all: sync with master; upd chlog

This commit is contained in:
Ainar Garipov 2023-02-15 16:53:29 +03:00
parent 80eb339896
commit 66b831072c
55 changed files with 24817 additions and 798 deletions

View file

@ -1,7 +1,7 @@
'name': 'build' 'name': 'build'
'env': 'env':
'GO_VERSION': '1.18.9' 'GO_VERSION': '1.19.6'
'NODE_VERSION': '14' 'NODE_VERSION': '14'
'on': 'on':

View file

@ -1,7 +1,7 @@
'name': 'lint' 'name': 'lint'
'env': 'env':
'GO_VERSION': '1.18.9' 'GO_VERSION': '1.19.6'
'on': 'on':
'push': 'push':

View file

@ -14,11 +14,11 @@ and this project adheres to
<!-- <!--
## [v0.108.0] - TBA ## [v0.108.0] - TBA
## [v0.107.24] - 2023-02-22 (APPROX.) ## [v0.107.25] - 2023-03-09 (APPROX.)
See also the [v0.107.24 GitHub milestone][ms-v0.107.24]. See also the [v0.107.25 GitHub milestone][ms-v0.107.25].
[ms-v0.107.24]: https://github.com/AdguardTeam/AdGuardHome/milestone/60?closed=1 [ms-v0.107.25]: https://github.com/AdguardTeam/AdGuardHome/milestone/61?closed=1
NOTE: Add new changes BELOW THIS COMMENT. NOTE: Add new changes BELOW THIS COMMENT.
--> -->
@ -29,6 +29,98 @@ NOTE: Add new changes ABOVE THIS COMMENT.
## [v0.107.24] - 2023-02-15
See also the [v0.107.24 GitHub milestone][ms-v0.107.24].
### Security
- Go version has been updated, both because Go 1.18 has reached end of life an
to prevent the possibility of exploiting the Go vulnerabilities fixed in [Go
1.19.6][go-1.19.6].
### Added
- The ability to disable statistics by using the new `statistics.enabled`
field. Previously it was necessary to set the `statistics_interval` to 0,
losing the previous value ([#1717], [#4299]).
- The ability to exclude domain names from the query log or statistics by using
the new `querylog.ignored` or `statistics.ignored` fields ([#1717], [#4299]).
The UI changes are coming in the upcoming releases.
### Changed
#### Configuration Changes
In this release, the schema version has changed from 14 to 16.
- Property `statistics_interval`, which in schema versions 15 and earlier used
to be a part of the `dns` object, is now a part of the `statistics` object:
```yaml
# BEFORE:
'dns':
# …
'statistics_interval': 1
# AFTER:
'statistics':
# …
'interval': 1
```
To rollback this change, move the property back into the `dns` object and
change the `schema_version` back to `15`.
- The fields `dns.querylog_enabled`, `dns.querylog_file_enabled`,
`dns.querylog_interval`, and `dns.querylog_size_memory` have been moved to the
new `querylog` object.
```yaml
# BEFORE:
'dns':
'querylog_enabled': true
'querylog_file_enabled': true
'querylog_interval': '2160h'
'querylog_size_memory': 1000
# AFTER:
'querylog':
'enabled': true
'file_enabled': true
'interval': '2160h'
'size_memory': 1000
```
To rollback this change, rename and move properties back into the `dns`
object, remove `querylog` object and `querylog.ignored` property, and change
the `schema_version` back to `14`.
### Deprecated
- Go 1.19 support. Future versions will require at least Go 1.20 to build.
### Fixed
- Setting the AD (Authenticated Data) flag on responses that have the DO (DNSSEC
OK) flag set but not the AD flag ([#5479]).
- Client names resolved via reverse DNS not being updated ([#4939]).
- The icon for League Of Legends on the Blocked services page ([#5433]).
### Removed
- Go 1.18 support, as it has reached end of life.
[#1717]: https://github.com/AdguardTeam/AdGuardHome/issues/1717
[#4299]: https://github.com/AdguardTeam/AdGuardHome/issues/4299
[#4939]: https://github.com/AdguardTeam/AdGuardHome/issues/4939
[#5433]: https://github.com/AdguardTeam/AdGuardHome/issues/5433
[#5479]: https://github.com/AdguardTeam/AdGuardHome/issues/5479
[go-1.19.6]: https://groups.google.com/g/golang-announce/c/V0aBFqaFs_E
[ms-v0.107.24]: https://github.com/AdguardTeam/AdGuardHome/milestone/60?closed=1
## [v0.107.23] - 2023-02-01 ## [v0.107.23] - 2023-02-01
See also the [v0.107.23 GitHub milestone][ms-v0.107.23]. See also the [v0.107.23 GitHub milestone][ms-v0.107.23].
@ -1031,7 +1123,6 @@ In this release, the schema version has changed from 10 to 12.
To rollback this change, convert the property back into days and change the To rollback this change, convert the property back into days and change the
`schema_version` back to `11`. `schema_version` back to `11`.
- Property `rlimit_nofile`, which in schema versions 10 and earlier used to be - Property `rlimit_nofile`, which in schema versions 10 and earlier used to be
on the top level, is now moved to the new `os` object: on the top level, is now moved to the new `os` object:
@ -1578,11 +1669,12 @@ See also the [v0.104.2 GitHub milestone][ms-v0.104.2].
<!-- <!--
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.24...HEAD [Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.25...HEAD
[v0.107.24]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.23...v0.107.24 [v0.107.25]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.24...v0.107.25
--> -->
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.23...HEAD [Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.24...HEAD
[v0.107.24]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.23...v0.107.24
[v0.107.23]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.22...v0.107.23 [v0.107.23]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.22...v0.107.23
[v0.107.22]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.21...v0.107.22 [v0.107.22]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.21...v0.107.22
[v0.107.21]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.20...v0.107.21 [v0.107.21]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.20...v0.107.21

View file

@ -81,12 +81,24 @@ code.
### <a href="#automated-install-linux-and-mac" id="automated-install-linux-and-mac" name="automated-install-linux-and-mac">Automated install (Unix)</a> ### <a href="#automated-install-linux-and-mac" id="automated-install-linux-and-mac" name="automated-install-linux-and-mac">Automated install (Unix)</a>
Run the following command in your terminal: To install with `curl` run the following command:
```sh ```sh
curl -s -S -L https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/master/scripts/install.sh | sh -s -- -v curl -s -S -L https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/master/scripts/install.sh | sh -s -- -v
``` ```
To install with `wget` run the following command:
```sh
wget --no-verbose -O - https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/master/scripts/install.sh | sh -s -- -v
```
To install with `fetch` run the following command:
```sh
fetch -o - https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/master/scripts/install.sh | sh -s -- -v
```
The script also accepts some options: The script also accepts some options:
* `-c <channel>` to use specified channel; * `-c <channel>` to use specified channel;
@ -249,7 +261,7 @@ Run `make init` to prepare the development environment.
You will need this to build AdGuard Home: You will need this to build AdGuard Home:
* [Go](https://golang.org/dl/) v1.18 or later; * [Go](https://golang.org/dl/) v1.19 or later;
* [Node.js](https://nodejs.org/en/download/) v10.16.2 or later; * [Node.js](https://nodejs.org/en/download/) v10.16.2 or later;
* [npm](https://www.npmjs.com/) v6.14 or later; * [npm](https://www.npmjs.com/) v6.14 or later;
* [yarn](https://yarnpkg.com/) v1.22.5 or later. * [yarn](https://yarnpkg.com/) v1.22.5 or later.

View file

@ -7,7 +7,7 @@
# Make sure to sync any changes with the branch overrides below. # Make sure to sync any changes with the branch overrides below.
'variables': 'variables':
'channel': 'edge' 'channel': 'edge'
'dockerGo': 'adguard/golang-ubuntu:5.4' 'dockerGo': 'adguard/golang-ubuntu:6.1'
'stages': 'stages':
- 'Build frontend': - 'Build frontend':
@ -223,6 +223,7 @@
channel="${bamboo.channel}" channel="${bamboo.channel}"
readonly channel readonly channel
case "$channel" case "$channel"
in in
('release') ('release')
@ -269,8 +270,10 @@
set -e -f -u -x set -e -f -u -x
export CHANNEL="${bamboo.channel}" channel="${bamboo.channel}"
if [ "$CHANNEL" != 'release' ] && [ "${CHANNEL}" != 'beta' ] readonly channel
if [ "$channel" != 'release' ] && [ "${channel}" != 'beta' ]
then then
echo "don't publish to GitHub Releases for this channel" echo "don't publish to GitHub Releases for this channel"
@ -323,7 +326,7 @@
# need to build a few of these. # need to build a few of these.
'variables': 'variables':
'channel': 'beta' 'channel': 'beta'
'dockerGo': 'adguard/golang-ubuntu:5.4' 'dockerGo': 'adguard/golang-ubuntu:6.1'
# release-vX.Y.Z branches are the branches from which the actual final release # release-vX.Y.Z branches are the branches from which the actual final release
# is built. # is built.
- '^release-v[0-9]+\.[0-9]+\.[0-9]+': - '^release-v[0-9]+\.[0-9]+\.[0-9]+':
@ -338,4 +341,4 @@
# are the ones that actually get released. # are the ones that actually get released.
'variables': 'variables':
'channel': 'release' 'channel': 'release'
'dockerGo': 'adguard/golang-ubuntu:5.4' 'dockerGo': 'adguard/golang-ubuntu:6.1'

View file

@ -5,7 +5,7 @@
'key': 'AHBRTSPECS' 'key': 'AHBRTSPECS'
'name': 'AdGuard Home - Build and run tests' 'name': 'AdGuard Home - Build and run tests'
'variables': 'variables':
'dockerGo': 'adguard/golang-ubuntu:5.4' 'dockerGo': 'adguard/golang-ubuntu:6.1'
'stages': 'stages':
- 'Tests': - 'Tests':

View file

@ -261,7 +261,7 @@
"query_log_configuration": "Налада часопіса", "query_log_configuration": "Налада часопіса",
"query_log_disabled": "Часопіс запытаў выключаны, яго можна ўключыць у <0>наладах</0>", "query_log_disabled": "Часопіс запытаў выключаны, яго можна ўключыць у <0>наладах</0>",
"query_log_strict_search": "Ужывайце падвойныя двукоссі для строгага пошуку", "query_log_strict_search": "Ужывайце падвойныя двукоссі для строгага пошуку",
"query_log_retention_confirm": "Вы ўпэўнены, што хочаце змяніць тэрмін захоўвання запытаў? Пры скарачэнні інтэрвалу дадзеныя могуць быць згублены", "query_log_retention_confirm": "Вы ўпэўнены, што хочаце змяніць тэрмін захоўвання запытаў? Пры памяншэнні інтэрвалу, некаторыя даныя могуць быць страчаны",
"anonymize_client_ip": "Ананімізацыя IP-адрасы кліента", "anonymize_client_ip": "Ананімізацыя IP-адрасы кліента",
"anonymize_client_ip_desc": "Не захоўвайце поўныя IP-адрасы гэтых удзельнікаў у часопісах або статыстыцы", "anonymize_client_ip_desc": "Не захоўвайце поўныя IP-адрасы гэтых удзельнікаў у часопісах або статыстыцы",
"dns_config": "Налады DNS-сервера", "dns_config": "Налады DNS-сервера",
@ -356,7 +356,7 @@
"install_devices_android_list_5": "Зараз можна змяніць палі «DNS 1» і «DNS 2». Увядзіце ў іх адрасы AdGuard Home.", "install_devices_android_list_5": "Зараз можна змяніць палі «DNS 1» і «DNS 2». Увядзіце ў іх адрасы AdGuard Home.",
"install_devices_ios_list_1": "Увайдзіце ў меню налад прылады.", "install_devices_ios_list_1": "Увайдзіце ў меню налад прылады.",
"install_devices_ios_list_2": "Абярыце пункт «Wi-Fi» (для мабільных сетак ручная наладка DNS немагчыма).", "install_devices_ios_list_2": "Абярыце пункт «Wi-Fi» (для мабільных сетак ручная наладка DNS немагчыма).",
"install_devices_ios_list_3": "Націсніце на назву сетцы, да якой прылада падлучана ў дадзены момант.", "install_devices_ios_list_3": "Націсніце на назву актыўнай у дадзены момант сеткі.",
"install_devices_ios_list_4": "У поле «DNS» увядзіце ўвядзіце адрасы AdGuard Home.", "install_devices_ios_list_4": "У поле «DNS» увядзіце ўвядзіце адрасы AdGuard Home.",
"get_started": "Паехалі", "get_started": "Паехалі",
"next": "Далей", "next": "Далей",
@ -517,10 +517,10 @@
"filter_updated": "Спіс паспяхова абноўлены", "filter_updated": "Спіс паспяхова абноўлены",
"statistics_configuration": "Канфігурацыя статыстыкі", "statistics_configuration": "Канфігурацыя статыстыкі",
"statistics_retention": "Захаванне статыстыкі", "statistics_retention": "Захаванне статыстыкі",
"statistics_retention_desc": "Калі вы зменшыце значэнне інтэрвалу, некаторыя дадзеныя могуць быць згублены", "statistics_retention_desc": "Калі вы паменшыце значэнне інтэрвалу, некаторыя даныя могуць быць страчаны",
"statistics_clear": "Ачысціць статыстыку", "statistics_clear": "Ачысціць статыстыку",
"statistics_clear_confirm": "Вы ўпэўнены, што хочаце ачысціць статыстыку?", "statistics_clear_confirm": "Вы ўпэўнены, што хочаце ачысціць статыстыку?",
"statistics_retention_confirm": "Вы ўпэўнены, што хочаце змяніць тэрмін захоўвання статыстыкі? Пры скарачэнні інтэрвалу дадзеныя могуць быць згублены", "statistics_retention_confirm": "Вы ўпэўнены, што хочаце змяніць тэрмін захоўвання статыстыкі? Пры памяншэнні інтэрвалу, некаторыя даныя могуць быць страчаны",
"statistics_cleared": "Статыстыка паспяхова вычышчана", "statistics_cleared": "Статыстыка паспяхова вычышчана",
"statistics_enable": "Уключыць статыстыку", "statistics_enable": "Уключыць статыстыку",
"interval_hours": "{{count}} гадзіна", "interval_hours": "{{count}} гадзіна",
@ -598,7 +598,7 @@
"show_blocked_responses": "Заблакавана", "show_blocked_responses": "Заблакавана",
"show_whitelisted_responses": "Белы спіс", "show_whitelisted_responses": "Белы спіс",
"show_processed_responses": "Апрацавана", "show_processed_responses": "Апрацавана",
"blocked_safebrowsing": "Заблакавана згодна базе дадзеных Safe Browsing", "blocked_safebrowsing": "Заблакіравана згодна з базай даных Safe Browsing",
"blocked_adult_websites": "Заблакавана Бацькоўскім кантролем", "blocked_adult_websites": "Заблакавана Бацькоўскім кантролем",
"blocked_threats": "Заблакавана пагроз", "blocked_threats": "Заблакавана пагроз",
"allowed": "Дазволены", "allowed": "Дазволены",
@ -639,7 +639,7 @@
"safe_browsing": "Бяспечны інтэрнэт", "safe_browsing": "Бяспечны інтэрнэт",
"served_from_cache": "{{value}} <i>(атрымана з кэша)</i>", "served_from_cache": "{{value}} <i>(атрымана з кэша)</i>",
"form_error_password_length": "Пароль павінен быць не менш за {{value}} сімвалаў", "form_error_password_length": "Пароль павінен быць не менш за {{value}} сімвалаў",
"anonymizer_notification": "<0>Заўвага:</0> Ананімізацыя IP уключана. Вы можаце адключыць яго ў <1>Агульных наладах</1> .", "anonymizer_notification": "<0>Заўвага:</0> Ананімізацыя IP уключана. Вы можаце адключыць яе ў <1>Агульных наладах</1>.",
"confirm_dns_cache_clear": "Вы ўпэўнены, што хочаце ачысціць кэш DNS?", "confirm_dns_cache_clear": "Вы ўпэўнены, што хочаце ачысціць кэш DNS?",
"cache_cleared": "Кэш DNS паспяхова ачышчаны", "cache_cleared": "Кэш DNS паспяхова ачышчаны",
"clear_cache": "Ачысціць кэш" "clear_cache": "Ачысціць кэш"

View file

@ -454,7 +454,7 @@
"updates_checked": "La nueva versión de AdGuard Home está disponible", "updates_checked": "La nueva versión de AdGuard Home está disponible",
"updates_version_equal": "AdGuard Home está actualizado", "updates_version_equal": "AdGuard Home está actualizado",
"check_updates_now": "Buscar actualizaciones ahora", "check_updates_now": "Buscar actualizaciones ahora",
"version_request_error": "La búsqueda de actualizaciones falló. Por favor revisa tu conexión a Internet.", "version_request_error": "Error buscar la actualización. Comprueba tu conexión a Internet.",
"dns_privacy": "DNS cifrado", "dns_privacy": "DNS cifrado",
"setup_dns_privacy_1": "<0>DNS mediante TLS:</0> Utiliza la cadena <1>{{address}}</1>.", "setup_dns_privacy_1": "<0>DNS mediante TLS:</0> Utiliza la cadena <1>{{address}}</1>.",
"setup_dns_privacy_2": "<0>DNS mediante HTTPS:</0> Utiliza la cadena <1>{{address}}</1>.", "setup_dns_privacy_2": "<0>DNS mediante HTTPS:</0> Utiliza la cadena <1>{{address}}</1>.",
@ -640,7 +640,7 @@
"served_from_cache": "{{value}} <i>(servido desde la caché)</i>", "served_from_cache": "{{value}} <i>(servido desde la caché)</i>",
"form_error_password_length": "La contraseña debe tener al menos {{value}} caracteres", "form_error_password_length": "La contraseña debe tener al menos {{value}} caracteres",
"anonymizer_notification": "<0>Nota:</0> La anonimización de IP está habilitada. Puedes deshabilitarla en <1>Configuración general</1>.", "anonymizer_notification": "<0>Nota:</0> La anonimización de IP está habilitada. Puedes deshabilitarla en <1>Configuración general</1>.",
"confirm_dns_cache_clear": "¿Estás seguro de que deseas borrar la caché de DNS?", "confirm_dns_cache_clear": "¿Estás seguro de que deseas borrar la caché DNS?",
"cache_cleared": "Caché DNS borrado con éxito", "cache_cleared": "Caché DNS borrado correctamente",
"clear_cache": "Borrar caché" "clear_cache": "Borrar caché"
} }

View file

@ -24,7 +24,7 @@
"unavailable_dhcp": "DHCP ei ole käytettävissä", "unavailable_dhcp": "DHCP ei ole käytettävissä",
"unavailable_dhcp_desc": "AdGuard Home ei voi suorittaa DHCP-palvelinta käyttöjärjestelmässäsi", "unavailable_dhcp_desc": "AdGuard Home ei voi suorittaa DHCP-palvelinta käyttöjärjestelmässäsi",
"dhcp_title": "DHCP-palvelin (kokeellinen!)", "dhcp_title": "DHCP-palvelin (kokeellinen!)",
"dhcp_description": "Jos reitittimessäsi ei ole DHCP-asetuksia, voit käyttää AdGuard Homen omaa sisäänrakennettua DHCP-palvelinta.", "dhcp_description": "Jollei reitittimesi tarjoa DHCP-asetuksia, voit käyttää AdGuard Homen omaa sisäänrakennettua DHCP-palvelinta.",
"dhcp_enable": "Ota DHCP-palvelin käyttöön", "dhcp_enable": "Ota DHCP-palvelin käyttöön",
"dhcp_disable": "Poista DHCP-palvelin käytöstä", "dhcp_disable": "Poista DHCP-palvelin käytöstä",
"dhcp_not_found": "On turvallista ottaa sisäänrakennettu DHCP-palvelin käyttöön, koska AdGuard Home ei havainnut verkossa muita aktiivisia DHCP-palvelimia. Suosittelemme, että varmistat tämän vielä itse, koska automaattinen tunnistus ei ole 100% varma.", "dhcp_not_found": "On turvallista ottaa sisäänrakennettu DHCP-palvelin käyttöön, koska AdGuard Home ei havainnut verkossa muita aktiivisia DHCP-palvelimia. Suosittelemme, että varmistat tämän vielä itse, koska automaattinen tunnistus ei ole 100% varma.",

View file

@ -128,7 +128,7 @@
"number_of_dns_query_days": "过去 {{count}} 天内处理的 DNS 查询总数", "number_of_dns_query_days": "过去 {{count}} 天内处理的 DNS 查询总数",
"number_of_dns_query_days_plural": "在过去的 {{count}} 天内处理了多少个 DNS 查询", "number_of_dns_query_days_plural": "在过去的 {{count}} 天内处理了多少个 DNS 查询",
"number_of_dns_query_24_hours": "过去 24 小时内处理的 DNS 请求总数", "number_of_dns_query_24_hours": "过去 24 小时内处理的 DNS 请求总数",
"number_of_dns_query_blocked_24_hours": "被广告过滤器和 Hosts 拦截清单阻止的 DNS 请求总数", "number_of_dns_query_blocked_24_hours": "被广告过滤器和 Hosts 黑名单阻止的 DNS 请求总数",
"number_of_dns_query_blocked_24_hours_by_sec": "被 AdGuard 安全浏览模块阻止的 DNS 请求总数", "number_of_dns_query_blocked_24_hours_by_sec": "被 AdGuard 安全浏览模块阻止的 DNS 请求总数",
"number_of_dns_query_blocked_24_hours_adult": "被阻止的成人网站总数", "number_of_dns_query_blocked_24_hours_adult": "被阻止的成人网站总数",
"enforced_save_search": "强制安全搜索", "enforced_save_search": "强制安全搜索",
@ -146,10 +146,10 @@
"no_servers_specified": "未找到指定的服务器", "no_servers_specified": "未找到指定的服务器",
"general_settings": "常规设置", "general_settings": "常规设置",
"dns_settings": "DNS 设置", "dns_settings": "DNS 设置",
"dns_blocklists": "DNS 拦截列表", "dns_blocklists": "DNS 黑名单",
"dns_allowlists": "DNS 允许列表", "dns_allowlists": "DNS 白名单",
"dns_blocklists_desc": "AdGuard Home将阻止匹配DNS拦截清单的域名", "dns_blocklists_desc": "AdGuard Home将阻止匹配DNS拦截清单的域名",
"dns_allowlists_desc": "来自DNS允许列表的域将被允许即使它们位于任意阻止列表中也是如此", "dns_allowlists_desc": "来自 DNS 白名单的域名将被允许,即使它们位于任意黑名单中也是如此。",
"custom_filtering_rules": "自定义过滤规则", "custom_filtering_rules": "自定义过滤规则",
"encryption_settings": "加密设置", "encryption_settings": "加密设置",
"dhcp_settings": "DHCP 设置", "dhcp_settings": "DHCP 设置",
@ -178,22 +178,22 @@
"delete_table_action": "删除", "delete_table_action": "删除",
"elapsed": "耗时", "elapsed": "耗时",
"filters_and_hosts_hint": "AdGuard Home 可以解析基础的 adblock 规则和 Hosts 语法。", "filters_and_hosts_hint": "AdGuard Home 可以解析基础的 adblock 规则和 Hosts 语法。",
"no_blocklist_added": "未添加阻止列表", "no_blocklist_added": "未添加黑名单",
"no_whitelist_added": "未添加允许列表", "no_whitelist_added": "未添加白名单",
"add_blocklist": "添加阻止列表", "add_blocklist": "添加黑名单",
"add_allowlist": "添加允许列表", "add_allowlist": "添加白名单",
"cancel_btn": "取消", "cancel_btn": "取消",
"enter_name_hint": "输入名称", "enter_name_hint": "输入名称",
"enter_url_or_path_hint": "请输入URL或列表的绝对路径", "enter_url_or_path_hint": "请输入URL或列表的绝对路径",
"check_updates_btn": "检查更新", "check_updates_btn": "检查更新",
"new_blocklist": "新封锁清单", "new_blocklist": "新封锁清单",
"new_allowlist": "新的允许清单", "new_allowlist": "新增白名单",
"edit_blocklist": "编辑阻止列表", "edit_blocklist": "编辑黑名单",
"edit_allowlist": "编辑允许列表", "edit_allowlist": "编辑白名单",
"choose_blocklist": "选择拦截列表", "choose_blocklist": "选择黑名单",
"choose_allowlist": "选择允许列表", "choose_allowlist": "选择白名单",
"enter_valid_blocklist": "输入有效的阻止列表URL", "enter_valid_blocklist": "输入有效的黑名单 URL",
"enter_valid_allowlist": "输入有效的允许列表URL", "enter_valid_allowlist": "输入有效的白名单 URL",
"form_error_url_format": "无效的 URL 格式", "form_error_url_format": "无效的 URL 格式",
"form_error_url_or_path_format": "无效的 URL 或列表的绝对路径", "form_error_url_or_path_format": "无效的 URL 或列表的绝对路径",
"custom_filter_rules": "自定义过滤器规则", "custom_filter_rules": "自定义过滤器规则",
@ -269,9 +269,9 @@
"dns_cache_config_desc": "您可以在此处配置 DNS 缓存", "dns_cache_config_desc": "您可以在此处配置 DNS 缓存",
"blocking_mode": "拦截模式", "blocking_mode": "拦截模式",
"default": "默认", "default": "默认",
"nxdomain": "无效域名", "nxdomain": "NXDOMAIN",
"refused": "REFUSED", "refused": "REFUSED",
"null_ip": "无效 IP", "null_ip": " IP",
"custom_ip": "自定义 IP", "custom_ip": "自定义 IP",
"blocking_ipv4": "拦截 IPv4", "blocking_ipv4": "拦截 IPv4",
"blocking_ipv6": "拦截 IPv6", "blocking_ipv6": "拦截 IPv6",
@ -296,7 +296,7 @@
"blocking_mode_default": "默认:被 Adblock 规则拦截时反应为零 IP 地址A记录0.0.0.0AAAA记录::);被/etc/hosts 规则拦截时反应为规则中指定 IP 地址", "blocking_mode_default": "默认:被 Adblock 规则拦截时反应为零 IP 地址A记录0.0.0.0AAAA记录::);被/etc/hosts 规则拦截时反应为规则中指定 IP 地址",
"blocking_mode_refused": "REFUSED以 REFUSED 码响应请求", "blocking_mode_refused": "REFUSED以 REFUSED 码响应请求",
"blocking_mode_nxdomain": "NXDOMAIN以NXDOMAIN码响应", "blocking_mode_nxdomain": "NXDOMAIN以NXDOMAIN码响应",
"blocking_mode_null_ip": "空IP以零IP地址响应(A记录 0.0.0.0AAAA记录 ::)", "blocking_mode_null_ip": "空 IP以零 IP 地址响应A 记录 0.0.0.0AAAA 记录 ::",
"blocking_mode_custom_ip": "自定IP以手动设置的IP地址响应", "blocking_mode_custom_ip": "自定IP以手动设置的IP地址响应",
"theme_auto": "自动", "theme_auto": "自动",
"theme_light": "浅色主题", "theme_light": "浅色主题",
@ -605,7 +605,7 @@
"filtered": "已过滤", "filtered": "已过滤",
"rewritten": "重写项", "rewritten": "重写项",
"safe_search": "安全搜索", "safe_search": "安全搜索",
"blocklist": "拦截列表", "blocklist": "黑名单",
"milliseconds_abbreviation": "毫秒", "milliseconds_abbreviation": "毫秒",
"cache_size": "缓存大小", "cache_size": "缓存大小",
"cache_size_desc": "DNS 缓存大小(单位:字节)。要关闭缓存,请留空。", "cache_size_desc": "DNS 缓存大小(单位:字节)。要关闭缓存,请留空。",
@ -626,7 +626,7 @@
"filter_category_general_desc": "在大多数设备上阻止跟踪和广告的列表", "filter_category_general_desc": "在大多数设备上阻止跟踪和广告的列表",
"filter_category_security_desc": "专用于拦截恶意软件、钓鱼或欺诈域名的列表", "filter_category_security_desc": "专用于拦截恶意软件、钓鱼或欺诈域名的列表",
"filter_category_regional_desc": "专注于区域广告和跟踪服务器的列表", "filter_category_regional_desc": "专注于区域广告和跟踪服务器的列表",
"filter_category_other_desc": "其他阻止列表", "filter_category_other_desc": "其他黑名单",
"setup_config_to_enable_dhcp_server": "设置配置以启用 DHCP 服务器", "setup_config_to_enable_dhcp_server": "设置配置以启用 DHCP 服务器",
"original_response": "原始响应", "original_response": "原始响应",
"click_to_view_queries": "点击查看查询", "click_to_view_queries": "点击查看查询",

View file

@ -53,7 +53,7 @@ export const STATUS_COLORS = {
export const REPOSITORY = { export const REPOSITORY = {
URL: 'https://github.com/AdguardTeam/AdGuardHome', URL: 'https://github.com/AdguardTeam/AdGuardHome',
TRACKERS_DB: TRACKERS_DB:
'https://github.com/AdguardTeam/AdGuardHome/tree/master/client/src/helpers/trackers/adguard.json', 'https://github.com/AdguardTeam/AdGuardHome/tree/master/client/src/helpers/trackers/trackers.json',
ISSUES: 'https://github.com/AdguardTeam/AdGuardHome/issues/new/choose', ISSUES: 'https://github.com/AdguardTeam/AdGuardHome/issues/new/choose',
}; };

View file

@ -160,12 +160,6 @@ export default {
"homepage": "https://github.com/DandelionSprout/adfilt", "homepage": "https://github.com/DandelionSprout/adfilt",
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_6.txt" "source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_6.txt"
}, },
"energized_spark": {
"name": "Energized Spark",
"categoryId": "general",
"homepage": "https://energized.pro/",
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_28.txt"
},
"hagezi_personal": { "hagezi_personal": {
"name": "HaGeZi Personal Black \u0026 White", "name": "HaGeZi Personal Black \u0026 White",
"categoryId": "general", "categoryId": "general",

View file

@ -1,6 +1,5 @@
import whotracksmeDb from './whotracksme.json';
import whotracksmeWebsites from './whotracksme_web.json'; import whotracksmeWebsites from './whotracksme_web.json';
import adguardDb from './adguard.json'; import trackersDb from './trackers.json';
import { REPOSITORY } from '../constants'; import { REPOSITORY } from '../constants';
/** /**
@ -39,6 +38,7 @@ const getWhotracksmeUrl = (trackerId) => {
/** /**
* Gets the source metadata for the specified tracker * Gets the source metadata for the specified tracker
*
* @param {TrackerData} trackerData tracker data * @param {TrackerData} trackerData tracker data
* @returns {source} source metadata or null if no matching tracker found * @returns {source} source metadata or null if no matching tracker found
*/ */
@ -64,14 +64,26 @@ export const getSourceData = (trackerData) => {
}; };
/** /**
* Gets tracker data in the specified database * Converts the JSON string source into numeric source for AdGuard Home
*
* @param {TrackerData} trackerData tracker data
* @returns {number} source number
*/
const convertSource = (sourceStr) => {
if (!sourceStr || sourceStr !== 'AdGuard') {
return sources.WHOTRACKSME;
}
return sources.ADGUARD;
};
/**
* Gets tracker data from the trackers database
* *
* @param {String} domainName domain name to check * @param {String} domainName domain name to check
* @param {*} trackersDb trackers database
* @param {number} source source ID
* @returns {TrackerData} tracker data or null if no matching tracker found * @returns {TrackerData} tracker data or null if no matching tracker found
*/ */
const getTrackerDataFromDb = (domainName, trackersDb, source) => { export const getTrackerData = (domainName) => {
if (!domainName) { if (!domainName) {
return null; return null;
} }
@ -88,7 +100,7 @@ const getTrackerDataFromDb = (domainName, trackersDb, source) => {
if (trackerId) { if (trackerId) {
const trackerData = trackersDb.trackers[trackerId]; const trackerData = trackersDb.trackers[trackerId];
const categoryName = trackersDb.categories[trackerData.categoryId]; const categoryName = trackersDb.categories[trackerData.categoryId];
trackerData.source = source; const source = convertSource(trackerData.source);
const sourceData = getSourceData(trackerData); const sourceData = getSourceData(trackerData);
return { return {
@ -105,22 +117,3 @@ const getTrackerDataFromDb = (domainName, trackersDb, source) => {
// No tracker found for the specified domain // No tracker found for the specified domain
return null; return null;
}; };
/**
* Gets tracker data from the trackers database
*
* @param {String} domainName domain name to check
* @returns {TrackerData} tracker data or null if no matching tracker found
*/
export const getTrackerData = (domainName) => {
if (!domainName) {
return null;
}
let data = getTrackerDataFromDb(domainName, adguardDb, sources.ADGUARD);
if (!data) {
data = getTrackerDataFromDb(domainName, whotracksmeDb, sources.WHOTRACKSME);
}
return data;
};

File diff suppressed because it is too large Load diff

39
go.mod
View file

@ -1,10 +1,10 @@
module github.com/AdguardTeam/AdGuardHome module github.com/AdguardTeam/AdGuardHome
go 1.18 go 1.19
require ( require (
// TODO(a.garipov): Return to a tagged version once DNS64 is in. // TODO(a.garipov): Use v0.48.0 when it's released.
github.com/AdguardTeam/dnsproxy v0.46.6-0.20230125113741-98cb8a899e49 github.com/AdguardTeam/dnsproxy v0.47.1-0.20230207130636-533058b17239
github.com/AdguardTeam/golibs v0.11.4 github.com/AdguardTeam/golibs v0.11.4
github.com/AdguardTeam/urlfilter v0.16.1 github.com/AdguardTeam/urlfilter v0.16.1
github.com/NYTimes/gziphandler v1.1.1 github.com/NYTimes/gziphandler v1.1.1
@ -19,27 +19,26 @@ require (
github.com/google/uuid v1.3.0 github.com/google/uuid v1.3.0
github.com/insomniacslk/dhcp v0.0.0-20221215072855-de60144f33f8 github.com/insomniacslk/dhcp v0.0.0-20221215072855-de60144f33f8
github.com/kardianos/service v1.2.2 github.com/kardianos/service v1.2.2
github.com/lucas-clemente/quic-go v0.31.1
github.com/mdlayher/ethernet v0.0.0-20220221185849-529eae5b6118 github.com/mdlayher/ethernet v0.0.0-20220221185849-529eae5b6118
github.com/mdlayher/netlink v1.7.1 github.com/mdlayher/netlink v1.7.1
// TODO(a.garipov): This package is deprecated; find a new one or use // TODO(a.garipov): This package is deprecated; find a new one or use
// our own code for that. Perhaps, use gopacket. // our own code for that. Perhaps, use gopacket.
github.com/mdlayher/raw v0.1.0 github.com/mdlayher/raw v0.1.0
github.com/miekg/dns v1.1.50 github.com/miekg/dns v1.1.50
github.com/quic-go/quic-go v0.32.0
github.com/stretchr/testify v1.8.1 github.com/stretchr/testify v1.8.1
github.com/ti-mo/netfilter v0.5.0 github.com/ti-mo/netfilter v0.5.0
go.etcd.io/bbolt v1.3.7 go.etcd.io/bbolt v1.3.7
golang.org/x/crypto v0.5.0 golang.org/x/crypto v0.6.0
golang.org/x/exp v0.0.0-20230131160201-f062dba9d201 golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb
golang.org/x/net v0.5.0 golang.org/x/net v0.7.0
golang.org/x/sys v0.4.0 golang.org/x/sys v0.5.0
gopkg.in/natefinch/lumberjack.v2 v2.0.0 gopkg.in/natefinch/lumberjack.v2 v2.2.1
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
howett.net/plist v1.0.0 howett.net/plist v1.0.0
) )
require ( require (
github.com/BurntSushi/toml v1.1.0 // indirect
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 // indirect github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 // indirect
github.com/ameshkov/dnsstamps v1.0.3 // indirect github.com/ameshkov/dnsstamps v1.0.3 // indirect
@ -48,20 +47,22 @@ require (
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
github.com/golang/mock v1.6.0 // indirect github.com/golang/mock v1.6.0 // indirect
github.com/google/pprof v0.0.0-20230131232505-5a9e8f65f08f // indirect github.com/google/pprof v0.0.0-20230207041349-798e818bf904 // indirect
github.com/josharian/native v1.1.0 // indirect github.com/josharian/native v1.1.0 // indirect
github.com/marten-seemann/qpack v0.3.0 // indirect
github.com/marten-seemann/qtls-go1-18 v0.1.4 // indirect
github.com/marten-seemann/qtls-go1-19 v0.1.2 // indirect
github.com/mdlayher/packet v1.1.1 // indirect github.com/mdlayher/packet v1.1.1 // indirect
github.com/mdlayher/socket v0.4.0 // indirect github.com/mdlayher/socket v0.4.0 // indirect
github.com/onsi/ginkgo/v2 v2.8.0 // indirect github.com/onsi/ginkgo/v2 v2.8.1 // indirect
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
github.com/pierrec/lz4/v4 v4.1.17 // indirect
github.com/pkg/errors v0.9.1 // indirect github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/u-root/uio v0.0.0-20221213070652-c3537552635f // indirect github.com/quic-go/qpack v0.4.0 // indirect
golang.org/x/mod v0.7.0 // indirect github.com/quic-go/qtls-go1-18 v0.2.0 // indirect
github.com/quic-go/qtls-go1-19 v0.2.0 // indirect
github.com/quic-go/qtls-go1-20 v0.1.0 // indirect
github.com/u-root/uio v0.0.0-20230215032506-9aa6f7e2d72c // indirect
golang.org/x/mod v0.8.0 // indirect
golang.org/x/sync v0.1.0 // indirect golang.org/x/sync v0.1.0 // indirect
golang.org/x/text v0.6.0 // indirect golang.org/x/text v0.7.0 // indirect
golang.org/x/tools v0.5.0 // indirect golang.org/x/tools v0.6.0 // indirect
) )

73
go.sum
View file

@ -1,5 +1,5 @@
github.com/AdguardTeam/dnsproxy v0.46.6-0.20230125113741-98cb8a899e49 h1:TDZsKB8BrKA2na6p5l20BvEu3MmgOWhIfTANz5laFuE= github.com/AdguardTeam/dnsproxy v0.47.1-0.20230207130636-533058b17239 h1:n1oOiywOvdeqWLto809bK1rK1EPDkpaSfT/r1OiCVaQ=
github.com/AdguardTeam/dnsproxy v0.46.6-0.20230125113741-98cb8a899e49/go.mod h1:ZEkTmTJ2XInT3aVy0mHtEnSWSclpHHj/9hfNXDuAk5k= github.com/AdguardTeam/dnsproxy v0.47.1-0.20230207130636-533058b17239/go.mod h1:+Sdi5ISrjDFbeCsKNqzcC1Ag7pJ5Hh9y+UBNb3dfqJ4=
github.com/AdguardTeam/golibs v0.4.0/go.mod h1:skKsDKIBB7kkFflLJBpfGX+G8QFTx0WKUzB6TIgtUj4= github.com/AdguardTeam/golibs v0.4.0/go.mod h1:skKsDKIBB7kkFflLJBpfGX+G8QFTx0WKUzB6TIgtUj4=
github.com/AdguardTeam/golibs v0.10.4/go.mod h1:rSfQRGHIdgfxriDDNgNJ7HmE5zRoURq8R+VdR81Zuzw= github.com/AdguardTeam/golibs v0.10.4/go.mod h1:rSfQRGHIdgfxriDDNgNJ7HmE5zRoURq8R+VdR81Zuzw=
github.com/AdguardTeam/golibs v0.11.4 h1:IltyvxwCTN+xxJF5sh6VadF8Zfbf8elgCm9dgijSVzM= github.com/AdguardTeam/golibs v0.11.4 h1:IltyvxwCTN+xxJF5sh6VadF8Zfbf8elgCm9dgijSVzM=
@ -7,8 +7,6 @@ github.com/AdguardTeam/golibs v0.11.4/go.mod h1:87bN2x4VsTritptE3XZg9l8T6gznWsIx
github.com/AdguardTeam/gomitmproxy v0.2.0/go.mod h1:Qdv0Mktnzer5zpdpi5rAwixNJzW2FN91LjKJCkVbYGU= github.com/AdguardTeam/gomitmproxy v0.2.0/go.mod h1:Qdv0Mktnzer5zpdpi5rAwixNJzW2FN91LjKJCkVbYGU=
github.com/AdguardTeam/urlfilter v0.16.1 h1:ZPi0rjqo8cQf2FVdzo6cqumNoHZx2KPXj2yZa1A5BBw= github.com/AdguardTeam/urlfilter v0.16.1 h1:ZPi0rjqo8cQf2FVdzo6cqumNoHZx2KPXj2yZa1A5BBw=
github.com/AdguardTeam/urlfilter v0.16.1/go.mod h1:46YZDOV1+qtdRDuhZKVPSSp7JWWes0KayqHrKAFBdEI= github.com/AdguardTeam/urlfilter v0.16.1/go.mod h1:46YZDOV1+qtdRDuhZKVPSSp7JWWes0KayqHrKAFBdEI=
github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I=
github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I= github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I=
github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA= github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA=
@ -57,8 +55,8 @@ github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
github.com/google/pprof v0.0.0-20230131232505-5a9e8f65f08f h1:gl1DCiSk+mrXXBGPm6CEeS2MkJuMVzAOrXg34oVj1QI= github.com/google/pprof v0.0.0-20230207041349-798e818bf904 h1:4/hN5RUoecvl+RmJRE2YxKWtnnQls6rQjjW5oV7qg2U=
github.com/google/pprof v0.0.0-20230131232505-5a9e8f65f08f/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo= github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8qtYCYyzA+8c/9qtqgA3qsXGYqCPKARAFg=
github.com/google/renameio v1.0.1 h1:Lh/jXZmvZxb0BBeSY5VKEfidcbcbenKjZFzM/q0fSeU= github.com/google/renameio v1.0.1 h1:Lh/jXZmvZxb0BBeSY5VKEfidcbcbenKjZFzM/q0fSeU=
github.com/google/renameio v1.0.1/go.mod h1:t/HQoYBZSsWSNK35C6CO/TpPLDVWvxOHboWUAweKUpk= github.com/google/renameio v1.0.1/go.mod h1:t/HQoYBZSsWSNK35C6CO/TpPLDVWvxOHboWUAweKUpk=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@ -85,14 +83,6 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lucas-clemente/quic-go v0.31.1 h1:O8Od7hfioqq0PMYHDyBkxU2aA7iZ2W9pjbrWuja2YR4=
github.com/lucas-clemente/quic-go v0.31.1/go.mod h1:0wFbizLgYzqHqtlyxyCaJKlE7bYgE6JQ+54TLd/Dq2g=
github.com/marten-seemann/qpack v0.3.0 h1:UiWstOgT8+znlkDPOg2+3rIuYXJ2CnGDkGUXN6ki6hE=
github.com/marten-seemann/qpack v0.3.0/go.mod h1:cGfKPBiP4a9EQdxCwEwI/GEeWAsjSekBvx/X8mh58+g=
github.com/marten-seemann/qtls-go1-18 v0.1.4 h1:ogomB+lWV3Vmwiu6RTwDVTMGx+9j7SEi98e8QB35Its=
github.com/marten-seemann/qtls-go1-18 v0.1.4/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4=
github.com/marten-seemann/qtls-go1-19 v0.1.2 h1:ZevAEqKXH0bZmoOBPiqX2h5rhQ7cbZi+X+rlq2JUbCE=
github.com/marten-seemann/qtls-go1-19 v0.1.2/go.mod h1:5HTDWtVudo/WFsHKRNuOhWlbdjrfs5JHrYb0wIJqGpI=
github.com/mdlayher/ethernet v0.0.0-20190606142754-0394541c37b7/go.mod h1:U6ZQobyTjI/tJyq2HG+i/dfSoFUt8/aZCM+GKtmFk/Y= github.com/mdlayher/ethernet v0.0.0-20190606142754-0394541c37b7/go.mod h1:U6ZQobyTjI/tJyq2HG+i/dfSoFUt8/aZCM+GKtmFk/Y=
github.com/mdlayher/ethernet v0.0.0-20220221185849-529eae5b6118 h1:2oDp6OOhLxQ9JBoUuysVz9UZ9uI6oLUbvAZu0x8o+vE= github.com/mdlayher/ethernet v0.0.0-20220221185849-529eae5b6118 h1:2oDp6OOhLxQ9JBoUuysVz9UZ9uI6oLUbvAZu0x8o+vE=
github.com/mdlayher/ethernet v0.0.0-20220221185849-529eae5b6118/go.mod h1:ZFUnHIVchZ9lJoWoEGUg8Q3M4U8aNNWA3CVSUTkW4og= github.com/mdlayher/ethernet v0.0.0-20220221185849-529eae5b6118/go.mod h1:ZFUnHIVchZ9lJoWoEGUg8Q3M4U8aNNWA3CVSUTkW4og=
@ -118,16 +108,29 @@ github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA=
github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/onsi/ginkgo/v2 v2.8.0 h1:pAM+oBNPrpXRs+E/8spkeGx9QgekbRVyr74EUvRVOUI= github.com/onsi/ginkgo/v2 v2.8.1 h1:xFTEVwOFa1D/Ty24Ws1npBWkDYEV9BqZrsDxVrVkrrU=
github.com/onsi/ginkgo/v2 v2.8.0/go.mod h1:6JsQiECmxCa3V5st74AL/AmsV482EDdVrGaVW6z3oYU= github.com/onsi/ginkgo/v2 v2.8.1/go.mod h1:N1/NbDngAFcSLdyZ+/aYTYGSlq9qMCS/cNKGJjy+csc=
github.com/onsi/gomega v1.25.0 h1:Vw7br2PCDYijJHSfBOWhov+8cAnUf8MfMaIOV323l6Y= github.com/onsi/gomega v1.26.0 h1:03cDLK28U6hWvCAns6NeydX3zIm4SF3ci69ulidS32Q=
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pierrec/lz4/v4 v4.1.17 h1:kV4Ip+/hUBC+8T6+2EgburRtkE9ef4nbY3f4dFhGjMc=
github.com/pierrec/lz4/v4 v4.1.17/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
github.com/quic-go/qtls-go1-18 v0.2.0 h1:5ViXqBZ90wpUcZS0ge79rf029yx0dYB0McyPJwqqj7U=
github.com/quic-go/qtls-go1-18 v0.2.0/go.mod h1:moGulGHK7o6O8lSPSZNoOwcLvJKJ85vVNc7oJFD65bc=
github.com/quic-go/qtls-go1-19 v0.2.0 h1:Cvn2WdhyViFUHoOqK52i51k4nDX8EwIh5VJiVM4nttk=
github.com/quic-go/qtls-go1-19 v0.2.0/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI=
github.com/quic-go/qtls-go1-20 v0.1.0 h1:d1PK3ErFy9t7zxKsG3NXBJXZjp/kMLoIb3y/kV54oAI=
github.com/quic-go/qtls-go1-20 v0.1.0/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM=
github.com/quic-go/quic-go v0.32.0 h1:lY02md31s1JgPiiyfqJijpu/UX/Iun304FI3yUqX7tA=
github.com/quic-go/quic-go v0.32.0/go.mod h1:/fCsKANhQIeD5l76c2JFU+07gVE3KaA0FP+0zMWwfwo=
github.com/shirou/gopsutil/v3 v3.21.8 h1:nKct+uP0TV8DjjNiHanKf8SAuub+GNsbrOtM9Nl9biA= github.com/shirou/gopsutil/v3 v3.21.8 h1:nKct+uP0TV8DjjNiHanKf8SAuub+GNsbrOtM9Nl9biA=
github.com/shirou/gopsutil/v3 v3.21.8/go.mod h1:YWp/H8Qs5fVmf17v7JNZzA0mPJ+mS2e9JdiUF9LlKzQ= github.com/shirou/gopsutil/v3 v3.21.8/go.mod h1:YWp/H8Qs5fVmf17v7JNZzA0mPJ+mS2e9JdiUF9LlKzQ=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
@ -151,23 +154,24 @@ github.com/tklauser/go-sysconf v0.3.9 h1:JeUVdAOWhhxVcU6Eqr/ATFHgXk/mmiItdKeJPev
github.com/tklauser/go-sysconf v0.3.9/go.mod h1:11DU/5sG7UexIrp/O6g35hrWzu0JxlwQ3LSFUzyeuhs= github.com/tklauser/go-sysconf v0.3.9/go.mod h1:11DU/5sG7UexIrp/O6g35hrWzu0JxlwQ3LSFUzyeuhs=
github.com/tklauser/numcpus v0.3.0 h1:ILuRUQBtssgnxw0XXIjKUC56fgnOrFoQQ/4+DeU2biQ= github.com/tklauser/numcpus v0.3.0 h1:ILuRUQBtssgnxw0XXIjKUC56fgnOrFoQQ/4+DeU2biQ=
github.com/tklauser/numcpus v0.3.0/go.mod h1:yFGUr7TUHQRAhyqBcEg0Ge34zDBAsIvJJcyE6boqnA8= github.com/tklauser/numcpus v0.3.0/go.mod h1:yFGUr7TUHQRAhyqBcEg0Ge34zDBAsIvJJcyE6boqnA8=
github.com/u-root/uio v0.0.0-20221213070652-c3537552635f h1:dpx1PHxYqAnXzbryJrWP1NQLzEjwcVgFLhkknuFQ7ww=
github.com/u-root/uio v0.0.0-20221213070652-c3537552635f/go.mod h1:IogEAUBXDEwX7oR/BMmCctShYs80ql4hF0ySdzGxf7E= github.com/u-root/uio v0.0.0-20221213070652-c3537552635f/go.mod h1:IogEAUBXDEwX7oR/BMmCctShYs80ql4hF0ySdzGxf7E=
github.com/u-root/uio v0.0.0-20230215032506-9aa6f7e2d72c h1:PHoGTnweZP+KIg/8Zc6+iOesrIF5yHkpb4GBDxHm7yE=
github.com/u-root/uio v0.0.0-20230215032506-9aa6f7e2d72c/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ= go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ=
go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE= golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
golang.org/x/exp v0.0.0-20230131160201-f062dba9d201 h1:BEABXpNXLEz0WxtA+6CQIz2xkg80e+1zrhWyMcq8VzE= golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb h1:PaBZQdo+iSDyHT053FjUCgZQ/9uqVwPOcl7KSWhKn6w=
golang.org/x/exp v0.0.0-20230131160201-f062dba9d201/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA= golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190419010253-1f3472d942ba/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190419010253-1f3472d942ba/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@ -184,8 +188,8 @@ golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLd
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210929193557-e81a3d93ecf6/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210929193557-e81a3d93ecf6/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw= golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
@ -218,24 +222,24 @@ golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18= golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k= golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.5.0 h1:+bSpV5HIeWkuvgaMfI3UmKRThoTA5ODJTUd8T17NO+4= golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
golang.org/x/tools v0.5.0/go.mod h1:N+Kgy78s5I24c24dU8OfWNEotWjutIs8SnJvn5IDq+k= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 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-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -244,12 +248,11 @@ google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscL
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg= gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View file

@ -9,7 +9,7 @@ import (
"github.com/AdguardTeam/dnsproxy/proxy" "github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/golibs/errors" "github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/netutil" "github.com/AdguardTeam/golibs/netutil"
"github.com/lucas-clemente/quic-go" "github.com/quic-go/quic-go"
) )
// ValidateClientID returns an error if id is not a valid ClientID. // ValidateClientID returns an error if id is not a valid ClientID.
@ -151,25 +151,7 @@ func (s *Server) clientIDFromDNSContext(pctx *proxy.DNSContext) (clientID string
func clientServerName(pctx *proxy.DNSContext, proto proxy.Proto) (srvName string, err error) { func clientServerName(pctx *proxy.DNSContext, proto proxy.Proto) (srvName string, err error) {
switch proto { switch proto {
case proxy.ProtoHTTPS: case proxy.ProtoHTTPS:
// github.com/lucas-clemente/quic-go seems to not populate the TLS srvName = pctx.HTTPRequest.TLS.ServerName
// field. So, if the request comes over HTTP/3, use the Host header
// value as the server name.
//
// See https://github.com/lucas-clemente/quic-go/issues/2879.
//
// TODO(a.garipov): Remove this crutch once they fix it.
r := pctx.HTTPRequest
if r.ProtoAtLeast(3, 0) {
var host string
host, err = netutil.SplitHost(r.Host)
if err != nil {
return "", fmt.Errorf("parsing host: %w", err)
}
srvName = host
} else if connState := r.TLS; connState != nil {
srvName = r.TLS.ServerName
}
case proxy.ProtoQUIC: case proxy.ProtoQUIC:
qConn := pctx.QUICConnection qConn := pctx.QUICConnection
conn, ok := qConn.(quicConnection) conn, ok := qConn.(quicConnection)

View file

@ -9,7 +9,7 @@ import (
"github.com/AdguardTeam/dnsproxy/proxy" "github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/golibs/testutil" "github.com/AdguardTeam/golibs/testutil"
"github.com/lucas-clemente/quic-go" "github.com/quic-go/quic-go"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@ -55,7 +55,6 @@ func TestServer_clientIDFromDNSContext(t *testing.T) {
wantClientID string wantClientID string
wantErrMsg string wantErrMsg string
strictSNI bool strictSNI bool
useHTTP3 bool
}{{ }{{
name: "udp", name: "udp",
proto: proxy.ProtoUDP, proto: proxy.ProtoUDP,
@ -64,7 +63,6 @@ func TestServer_clientIDFromDNSContext(t *testing.T) {
wantClientID: "", wantClientID: "",
wantErrMsg: "", wantErrMsg: "",
strictSNI: false, strictSNI: false,
useHTTP3: false,
}, { }, {
name: "tls_no_clientid", name: "tls_no_clientid",
proto: proxy.ProtoTLS, proto: proxy.ProtoTLS,
@ -73,7 +71,6 @@ func TestServer_clientIDFromDNSContext(t *testing.T) {
wantClientID: "", wantClientID: "",
wantErrMsg: "", wantErrMsg: "",
strictSNI: true, strictSNI: true,
useHTTP3: false,
}, { }, {
name: "tls_no_client_server_name", name: "tls_no_client_server_name",
proto: proxy.ProtoTLS, proto: proxy.ProtoTLS,
@ -83,7 +80,6 @@ func TestServer_clientIDFromDNSContext(t *testing.T) {
wantErrMsg: `clientid check: client server name "" ` + wantErrMsg: `clientid check: client server name "" ` +
`doesn't match host server name "example.com"`, `doesn't match host server name "example.com"`,
strictSNI: true, strictSNI: true,
useHTTP3: false,
}, { }, {
name: "tls_no_client_server_name_no_strict", name: "tls_no_client_server_name_no_strict",
proto: proxy.ProtoTLS, proto: proxy.ProtoTLS,
@ -92,7 +88,6 @@ func TestServer_clientIDFromDNSContext(t *testing.T) {
wantClientID: "", wantClientID: "",
wantErrMsg: "", wantErrMsg: "",
strictSNI: false, strictSNI: false,
useHTTP3: false,
}, { }, {
name: "tls_clientid", name: "tls_clientid",
proto: proxy.ProtoTLS, proto: proxy.ProtoTLS,
@ -101,7 +96,6 @@ func TestServer_clientIDFromDNSContext(t *testing.T) {
wantClientID: "cli", wantClientID: "cli",
wantErrMsg: "", wantErrMsg: "",
strictSNI: true, strictSNI: true,
useHTTP3: false,
}, { }, {
name: "tls_clientid_hostname_error", name: "tls_clientid_hostname_error",
proto: proxy.ProtoTLS, proto: proxy.ProtoTLS,
@ -111,7 +105,6 @@ func TestServer_clientIDFromDNSContext(t *testing.T) {
wantErrMsg: `clientid check: client server name "cli.example.net" ` + wantErrMsg: `clientid check: client server name "cli.example.net" ` +
`doesn't match host server name "example.com"`, `doesn't match host server name "example.com"`,
strictSNI: true, strictSNI: true,
useHTTP3: false,
}, { }, {
name: "tls_invalid_clientid", name: "tls_invalid_clientid",
proto: proxy.ProtoTLS, proto: proxy.ProtoTLS,
@ -121,7 +114,6 @@ func TestServer_clientIDFromDNSContext(t *testing.T) {
wantErrMsg: `clientid check: invalid clientid "!!!": ` + wantErrMsg: `clientid check: invalid clientid "!!!": ` +
`bad domain name label rune '!'`, `bad domain name label rune '!'`,
strictSNI: true, strictSNI: true,
useHTTP3: false,
}, { }, {
name: "tls_clientid_too_long", name: "tls_clientid_too_long",
proto: proxy.ProtoTLS, proto: proxy.ProtoTLS,
@ -133,7 +125,6 @@ func TestServer_clientIDFromDNSContext(t *testing.T) {
`pqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789": ` + `pqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789": ` +
`domain name label is too long: got 72, max 63`, `domain name label is too long: got 72, max 63`,
strictSNI: true, strictSNI: true,
useHTTP3: false,
}, { }, {
name: "quic_clientid", name: "quic_clientid",
proto: proxy.ProtoQUIC, proto: proxy.ProtoQUIC,
@ -142,7 +133,6 @@ func TestServer_clientIDFromDNSContext(t *testing.T) {
wantClientID: "cli", wantClientID: "cli",
wantErrMsg: "", wantErrMsg: "",
strictSNI: true, strictSNI: true,
useHTTP3: false,
}, { }, {
name: "tls_clientid_issue3437", name: "tls_clientid_issue3437",
proto: proxy.ProtoTLS, proto: proxy.ProtoTLS,
@ -152,7 +142,6 @@ func TestServer_clientIDFromDNSContext(t *testing.T) {
wantErrMsg: `clientid check: client server name "cli.myexample.com" ` + wantErrMsg: `clientid check: client server name "cli.myexample.com" ` +
`doesn't match host server name "example.com"`, `doesn't match host server name "example.com"`,
strictSNI: true, strictSNI: true,
useHTTP3: false,
}, { }, {
name: "tls_case", name: "tls_case",
proto: proxy.ProtoTLS, proto: proxy.ProtoTLS,
@ -161,7 +150,6 @@ func TestServer_clientIDFromDNSContext(t *testing.T) {
wantClientID: "insensitive", wantClientID: "insensitive",
wantErrMsg: ``, wantErrMsg: ``,
strictSNI: true, strictSNI: true,
useHTTP3: false,
}, { }, {
name: "quic_case", name: "quic_case",
proto: proxy.ProtoQUIC, proto: proxy.ProtoQUIC,
@ -170,7 +158,6 @@ func TestServer_clientIDFromDNSContext(t *testing.T) {
wantClientID: "insensitive", wantClientID: "insensitive",
wantErrMsg: ``, wantErrMsg: ``,
strictSNI: true, strictSNI: true,
useHTTP3: false,
}, { }, {
name: "https_no_clientid", name: "https_no_clientid",
proto: proxy.ProtoHTTPS, proto: proxy.ProtoHTTPS,
@ -179,7 +166,6 @@ func TestServer_clientIDFromDNSContext(t *testing.T) {
wantClientID: "", wantClientID: "",
wantErrMsg: "", wantErrMsg: "",
strictSNI: true, strictSNI: true,
useHTTP3: false,
}, { }, {
name: "https_clientid", name: "https_clientid",
proto: proxy.ProtoHTTPS, proto: proxy.ProtoHTTPS,
@ -188,16 +174,6 @@ func TestServer_clientIDFromDNSContext(t *testing.T) {
wantClientID: "cli", wantClientID: "cli",
wantErrMsg: "", wantErrMsg: "",
strictSNI: true, strictSNI: true,
useHTTP3: false,
}, {
name: "https_clientid_quic",
proto: proxy.ProtoHTTPS,
hostSrvName: "example.com",
cliSrvName: "cli.example.com",
wantClientID: "cli",
wantErrMsg: "",
strictSNI: true,
useHTTP3: true,
}} }}
for _, tc := range testCases { for _, tc := range testCases {
@ -219,7 +195,7 @@ func TestServer_clientIDFromDNSContext(t *testing.T) {
switch tc.proto { switch tc.proto {
case proxy.ProtoHTTPS: case proxy.ProtoHTTPS:
httpReq = newHTTPReq(tc.cliSrvName, tc.useHTTP3) httpReq = newHTTPReq(tc.cliSrvName)
case proxy.ProtoQUIC: case proxy.ProtoQUIC:
qconn = testQUICConnection{ qconn = testQUICConnection{
serverName: tc.cliSrvName, serverName: tc.cliSrvName,
@ -246,21 +222,11 @@ func TestServer_clientIDFromDNSContext(t *testing.T) {
} }
// newHTTPReq is a helper to create HTTP requests for tests. // newHTTPReq is a helper to create HTTP requests for tests.
func newHTTPReq(cliSrvName string, useHTTP3 bool) (r *http.Request) { func newHTTPReq(cliSrvName string) (r *http.Request) {
u := &url.URL{ u := &url.URL{
Path: "/dns-query", Path: "/dns-query",
} }
if useHTTP3 {
return &http.Request{
ProtoMajor: 3,
ProtoMinor: 0,
URL: u,
Host: cliSrvName,
TLS: &tls.ConnectionState{},
}
}
return &http.Request{ return &http.Request{
ProtoMajor: 1, ProtoMajor: 1,
ProtoMinor: 1, ProtoMinor: 1,

View file

@ -5,6 +5,7 @@ import (
"crypto/x509" "crypto/x509"
"fmt" "fmt"
"net" "net"
"net/netip"
"os" "os"
"sort" "sort"
"strings" "strings"
@ -225,7 +226,7 @@ type ServerConfig struct {
LocalPTRResolvers []string LocalPTRResolvers []string
// DNS64Prefixes is a slice of NAT64 prefixes to be used for DNS64. // DNS64Prefixes is a slice of NAT64 prefixes to be used for DNS64.
DNS64Prefixes []string DNS64Prefixes []netip.Prefix
// ResolveClients signals if the RDNS should resolve clients' addresses. // ResolveClients signals if the RDNS should resolve clients' addresses.
ResolveClients bool ResolveClients bool
@ -271,6 +272,8 @@ func (s *Server) createProxyConfig() (conf proxy.Config, err error) {
RequestHandler: s.handleDNSRequest, RequestHandler: s.handleDNSRequest,
EnableEDNSClientSubnet: srvConf.EnableEDNSClientSubnet, EnableEDNSClientSubnet: srvConf.EnableEDNSClientSubnet,
MaxGoroutines: int(srvConf.MaxGoroutines), MaxGoroutines: int(srvConf.MaxGoroutines),
UseDNS64: srvConf.UseDNS64,
DNS64Prefs: srvConf.DNS64Prefixes,
} }
if srvConf.CacheSize != 0 { if srvConf.CacheSize != 0 {

View file

@ -10,6 +10,8 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/dhcpd" "github.com/AdguardTeam/AdGuardHome/internal/dhcpd"
"github.com/AdguardTeam/AdGuardHome/internal/filtering" "github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/dnsproxy/proxy" "github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/dnsproxy/upstream"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log" "github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/netutil" "github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/stringutil" "github.com/AdguardTeam/golibs/stringutil"
@ -17,6 +19,9 @@ import (
) )
// To transfer information between modules // To transfer information between modules
//
// TODO(s.chzhen): Add lowercased, non-FQDN version of the hostname from the
// question of the request.
type dnsContext struct { type dnsContext struct {
proxyCtx *proxy.DNSContext proxyCtx *proxy.DNSContext
@ -419,7 +424,7 @@ func (s *Server) processDHCPHosts(dctx *dnsContext) (rc resultCode) {
} }
resp.Answer = append(resp.Answer, a) resp.Answer = append(resp.Answer, a)
case dns.TypeAAAA: case dns.TypeAAAA:
if len(s.dns64Prefs) > 0 { if s.dns64Pref != (netip.Prefix{}) {
// Respond with DNS64-mapped address for IPv4 host if DNS64 is // Respond with DNS64-mapped address for IPv4 host if DNS64 is
// enabled. // enabled.
aaaa := &dns.AAAA{ aaaa := &dns.AAAA{
@ -468,15 +473,6 @@ func (s *Server) processRestrictLocal(dctx *dnsContext) (rc resultCode) {
return resultCodeSuccess return resultCodeSuccess
} }
if s.shouldStripDNS64(ip) {
// Strip the prefix from the address to get the original IPv4.
ip = ip[nat64PrefixLen:]
// Treat a DNS64-prefixed address as a locally served one since those
// queries should never be sent to the global DNS.
dctx.unreversedReqIP = ip
}
// Restrict an access to local addresses for external clients. We also // Restrict an access to local addresses for external clients. We also
// assume that all the DHCP leases we give are locally served or at least // assume that all the DHCP leases we give are locally served or at least
// shouldn't be accessible externally. // shouldn't be accessible externally.
@ -655,13 +651,7 @@ func (s *Server) processUpstream(dctx *dnsContext) (rc resultCode) {
s.setCustomUpstream(pctx, dctx.clientID) s.setCustomUpstream(pctx, dctx.clientID)
origReqAD := false reqWantsDNSSEC := s.setReqAD(req)
if s.conf.EnableDNSSEC {
origReqAD = req.AuthenticatedData
if !req.AuthenticatedData {
req.AuthenticatedData = true
}
}
// Process the request further since it wasn't filtered. // Process the request further since it wasn't filtered.
prx := s.proxy() prx := s.proxy()
@ -671,23 +661,73 @@ func (s *Server) processUpstream(dctx *dnsContext) (rc resultCode) {
return resultCodeError return resultCodeError
} }
if dctx.err = prx.Resolve(pctx); dctx.err != nil { if err := prx.Resolve(pctx); err != nil {
return resultCodeError if errors.Is(err, upstream.ErrNoUpstreams) {
// Do not even put into querylog. Currently this happens either
// when the private resolvers enabled and the request is DNS64 PTR,
// or when the client isn't considered local by prx.
//
// TODO(e.burkov): Make proxy detect local client the same way as
// AGH does.
pctx.Res = s.genNXDomain(req)
return resultCodeFinish
} }
if s.performDNS64(prx, dctx) == resultCodeError { dctx.err = err
return resultCodeError return resultCodeError
} }
dctx.responseFromUpstream = true dctx.responseFromUpstream = true
dctx.responseAD = pctx.Res.AuthenticatedData dctx.responseAD = pctx.Res.AuthenticatedData
if s.conf.EnableDNSSEC && !origReqAD { s.setRespAD(pctx, reqWantsDNSSEC)
return resultCodeSuccess
}
// setReqAD changes the request based on the server settings. wantsDNSSEC is
// false if the response should be cleared of the AD bit.
//
// TODO(a.garipov, e.burkov): This should probably be done in module dnsproxy.
func (s *Server) setReqAD(req *dns.Msg) (wantsDNSSEC bool) {
if !s.conf.EnableDNSSEC {
return false
}
origReqAD := req.AuthenticatedData
req.AuthenticatedData = true
// Per [RFC 6840] says, validating resolvers should only set the AD bit when
// the response has the AD bit set and the request contained either a set DO
// bit or a set AD bit. So, if neither of these is true, clear the AD bits
// in [Server.setRespAD].
//
// [RFC 6840]: https://datatracker.ietf.org/doc/html/rfc6840#section-5.8
return origReqAD || hasDO(req)
}
// hasDO returns true if msg has EDNS(0) options and the DNSSEC OK flag is set
// in there.
//
// TODO(a.garipov): Move to golibs/dnsmsg when it's there.
func hasDO(msg *dns.Msg) (do bool) {
o := msg.IsEdns0()
if o == nil {
return false
}
return o.Do()
}
// setRespAD changes the request and response based on the server settings and
// the original request data.
func (s *Server) setRespAD(pctx *proxy.DNSContext, reqWantsDNSSEC bool) {
if s.conf.EnableDNSSEC && !reqWantsDNSSEC {
pctx.Req.AuthenticatedData = false pctx.Req.AuthenticatedData = false
pctx.Res.AuthenticatedData = false pctx.Res.AuthenticatedData = false
} }
return resultCodeSuccess
} }
// isDHCPClientHostQ returns true if q is from a request for a DHCP client // isDHCPClientHostQ returns true if q is from a request for a DHCP client

View file

@ -1,34 +1,10 @@
package dnsforward package dnsforward
import ( import (
"fmt"
"net" "net"
"net/netip" "net/netip"
"github.com/AdguardTeam/dnsproxy/proxy" "github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/mathutil"
"github.com/AdguardTeam/golibs/netutil"
"github.com/miekg/dns"
)
const (
// maxNAT64PrefixBitLen is the maximum length of a NAT64 prefix in bits.
// See https://datatracker.ietf.org/doc/html/rfc6147#section-5.2.
maxNAT64PrefixBitLen = 96
// nat64PrefixLen is the length of a NAT64 prefix in bytes.
nat64PrefixLen = net.IPv6len - net.IPv4len
// maxDNS64SynTTL is the maximum TTL for synthesized DNS64 responses with no
// SOA records in seconds.
//
// If the SOA RR was not delivered with the negative response to the AAAA
// query, then the DNS64 SHOULD use the TTL of the original A RR or 600
// seconds, whichever is shorter.
//
// See https://datatracker.ietf.org/doc/html/rfc6147#section-5.1.7.
maxDNS64SynTTL uint32 = 600
) )
// setupDNS64 initializes DNS64 settings, the NAT64 prefixes in particular. If // setupDNS64 initializes DNS64 settings, the NAT64 prefixes in particular. If
@ -38,227 +14,22 @@ const (
// is specified explicitly. Each prefix also validated to be a valid IPv6 // is specified explicitly. Each prefix also validated to be a valid IPv6
// CIDR with a maximum length of 96 bits. The first specified prefix is then // CIDR with a maximum length of 96 bits. The first specified prefix is then
// used to synthesize AAAA records. // used to synthesize AAAA records.
func (s *Server) setupDNS64() (err error) { func (s *Server) setupDNS64() {
if !s.conf.UseDNS64 { if !s.conf.UseDNS64 {
return nil return
} }
l := len(s.conf.DNS64Prefixes) if len(s.conf.DNS64Prefixes) == 0 {
if l == 0 { // dns64WellKnownPref is the default prefix to use in an algorithmic
s.dns64Prefs = []netip.Prefix{dns64WellKnownPref} // mapping for DNS64.
return nil
}
prefs := make([]netip.Prefix, 0, l)
for i, pref := range s.conf.DNS64Prefixes {
var p netip.Prefix
p, err = netip.ParsePrefix(pref)
if err != nil {
return fmt.Errorf("prefix at index %d: %w", i, err)
}
addr := p.Addr()
if !addr.Is6() {
return fmt.Errorf("prefix at index %d: %q is not an IPv6 prefix", i, pref)
}
if p.Bits() > maxNAT64PrefixBitLen {
return fmt.Errorf("prefix at index %d: %q is too long for DNS64", i, pref)
}
prefs = append(prefs, p.Masked())
}
s.dns64Prefs = prefs
return nil
}
// checkDNS64 checks if DNS64 should be performed. It returns a DNS64 request
// to resolve or nil if DNS64 is not desired. It also filters resp to not
// contain any NAT64 excluded addresses in the answer section, if needed. Both
// req and resp must not be nil.
// //
// See https://datatracker.ietf.org/doc/html/rfc6147. // See https://datatracker.ietf.org/doc/html/rfc6052#section-2.1.
func (s *Server) checkDNS64(req, resp *dns.Msg) (dns64Req *dns.Msg) { dns64WellKnownPref := netip.MustParsePrefix("64:ff9b::/96")
if len(s.dns64Prefs) == 0 {
return nil
}
q := req.Question[0] s.dns64Pref = dns64WellKnownPref
if q.Qtype != dns.TypeAAAA || q.Qclass != dns.ClassINET { } else {
// DNS64 operation for classes other than IN is undefined, and a DNS64 s.dns64Pref = s.conf.DNS64Prefixes[0]
// MUST behave as though no DNS64 function is configured.
return nil
} }
rcode := resp.Rcode
if rcode == dns.RcodeNameError {
// A result with RCODE=3 (Name Error) is handled according to normal DNS
// operation (which is normally to return the error to the client).
return nil
}
if rcode == dns.RcodeSuccess {
// If resolver receives an answer with at least one AAAA record
// containing an address outside any of the excluded range(s), then it
// by default SHOULD build an answer section for a response including
// only the AAAA record(s) that do not contain any of the addresses
// inside the excluded ranges.
var hasAnswers bool
if resp.Answer, hasAnswers = s.filterNAT64Answers(resp.Answer); hasAnswers {
return nil
}
// Any other RCODE is treated as though the RCODE were 0 and the answer
// section were empty.
}
return &dns.Msg{
MsgHdr: dns.MsgHdr{
Id: dns.Id(),
RecursionDesired: req.RecursionDesired,
AuthenticatedData: req.AuthenticatedData,
CheckingDisabled: req.CheckingDisabled,
},
Question: []dns.Question{{
Name: req.Question[0].Name,
Qtype: dns.TypeA,
Qclass: dns.ClassINET,
}},
}
}
// filterNAT64Answers filters out AAAA records that are within one of NAT64
// exclusion prefixes. hasAnswers is true if the filtered slice contains at
// least a single AAAA answer not within the prefixes or a CNAME.
func (s *Server) filterNAT64Answers(rrs []dns.RR) (filtered []dns.RR, hasAnswers bool) {
filtered = make([]dns.RR, 0, len(rrs))
for _, ans := range rrs {
switch ans := ans.(type) {
case *dns.AAAA:
addr, err := netutil.IPToAddrNoMapped(ans.AAAA)
if err != nil {
log.Error("dnsforward: bad AAAA record: %s", err)
continue
}
if s.withinDNS64(addr) {
// Filter the record.
continue
}
filtered, hasAnswers = append(filtered, ans), true
case *dns.CNAME, *dns.DNAME:
// If the response contains a CNAME or a DNAME, then the CNAME or
// DNAME chain is followed until the first terminating A or AAAA
// record is reached.
//
// Just treat CNAME and DNAME responses as passable answers since
// AdGuard Home doesn't follow any of these chains except the
// dnsrewrite-defined ones.
filtered, hasAnswers = append(filtered, ans), true
default:
filtered = append(filtered, ans)
}
}
return filtered, hasAnswers
}
// synthDNS64 synthesizes a DNS64 response using the original response as a
// basis and modifying it with data from resp. It returns true if the response
// was actually modified.
func (s *Server) synthDNS64(origReq, origResp, resp *dns.Msg) (ok bool) {
if len(resp.Answer) == 0 {
// If there is an empty answer, then the DNS64 responds to the original
// querying client with the answer the DNS64 received to the original
// (initiator's) query.
return false
}
// The Time to Live (TTL) field is set to the minimum of the TTL of the
// original A RR and the SOA RR for the queried domain. If the original
// response contains no SOA records, the minimum of the TTL of the original
// A RR and [maxDNS64SynTTL] should be used. See [maxDNS64SynTTL].
soaTTL := maxDNS64SynTTL
for _, rr := range origResp.Ns {
if hdr := rr.Header(); hdr.Rrtype == dns.TypeSOA && hdr.Name == origReq.Question[0].Name {
soaTTL = hdr.Ttl
break
}
}
newAns := make([]dns.RR, 0, len(resp.Answer))
for _, ans := range resp.Answer {
rr := s.synthRR(ans, soaTTL)
if rr == nil {
// The error should have already been logged.
return false
}
newAns = append(newAns, rr)
}
origResp.Answer = newAns
origResp.Ns = resp.Ns
origResp.Extra = resp.Extra
return true
}
// dns64WellKnownPref is the default prefix to use in an algorithmic mapping for
// DNS64. See https://datatracker.ietf.org/doc/html/rfc6052#section-2.1.
var dns64WellKnownPref = netip.MustParsePrefix("64:ff9b::/96")
// withinDNS64 checks if ip is within one of the configured DNS64 prefixes.
//
// TODO(e.burkov): We actually using bytes of only the first prefix from the
// set to construct the answer, so consider using some implementation of a
// prefix set for the rest.
func (s *Server) withinDNS64(ip netip.Addr) (ok bool) {
for _, n := range s.dns64Prefs {
if n.Contains(ip) {
return true
}
}
return false
}
// shouldStripDNS64 returns true if DNS64 is enabled and ip has either one of
// custom DNS64 prefixes or the Well-Known one. This is intended to be used
// with PTR requests.
//
// The requirement is to match any Pref64::/n used at the site, and not merely
// the locally configured Pref64::/n. This is because end clients could ask for
// a PTR record matching an address received through a different (site-provided)
// DNS64.
//
// See https://datatracker.ietf.org/doc/html/rfc6147#section-5.3.1.
func (s *Server) shouldStripDNS64(ip net.IP) (ok bool) {
if len(s.dns64Prefs) == 0 {
return false
}
addr, err := netutil.IPToAddr(ip, netutil.AddrFamilyIPv6)
if err != nil {
return false
}
switch {
case s.withinDNS64(addr):
log.Debug("dnsforward: %s is within DNS64 custom prefix set", ip)
case dns64WellKnownPref.Contains(addr):
log.Debug("dnsforward: %s is within DNS64 well-known prefix", ip)
default:
return false
}
return true
} }
// mapDNS64 maps ip to IPv6 address using configured DNS64 prefix. ip must be a // mapDNS64 maps ip to IPv6 address using configured DNS64 prefix. ip must be a
@ -267,79 +38,12 @@ func (s *Server) shouldStripDNS64(ip net.IP) (ok bool) {
func (s *Server) mapDNS64(ip netip.Addr) (mapped net.IP) { func (s *Server) mapDNS64(ip netip.Addr) (mapped net.IP) {
// Don't mask the address here since it should have already been masked on // Don't mask the address here since it should have already been masked on
// initialization stage. // initialization stage.
pref := s.dns64Prefs[0].Addr().As16() pref := s.dns64Pref.Masked().Addr().As16()
ipData := ip.As4() ipData := ip.As4()
mapped = make(net.IP, net.IPv6len) mapped = make(net.IP, net.IPv6len)
copy(mapped[:nat64PrefixLen], pref[:]) copy(mapped[:proxy.NAT64PrefixLength], pref[:])
copy(mapped[nat64PrefixLen:], ipData[:]) copy(mapped[proxy.NAT64PrefixLength:], ipData[:])
return mapped return mapped
} }
// performDNS64 processes the current state of dctx assuming that it has already
// been tried to resolve, checks if it contains any acceptable response, and if
// it doesn't, performs DNS64 request and the following synthesis. It returns
// the [resultCodeError] if there was an error set to dctx.
func (s *Server) performDNS64(prx *proxy.Proxy, dctx *dnsContext) (rc resultCode) {
pctx := dctx.proxyCtx
req := pctx.Req
dns64Req := s.checkDNS64(req, pctx.Res)
if dns64Req == nil {
return resultCodeSuccess
}
log.Debug("dnsforward: received an empty AAAA response, checking DNS64")
origReq := pctx.Req
origResp := pctx.Res
origUps := pctx.Upstream
pctx.Req = dns64Req
defer func() { pctx.Req = origReq }()
if dctx.err = prx.Resolve(pctx); dctx.err != nil {
return resultCodeError
}
dns64Resp := pctx.Res
pctx.Res = origResp
if dns64Resp != nil && s.synthDNS64(origReq, pctx.Res, dns64Resp) {
log.Debug("dnsforward: synthesized AAAA response for %q", origReq.Question[0].Name)
} else {
pctx.Upstream = origUps
}
return resultCodeSuccess
}
// synthRR synthesizes a DNS64 resource record in compliance with RFC 6147. If
// rr is not an A record, it's returned as is. A records are modified to become
// a DNS64-synthesized AAAA records, and the TTL is set according to the
// original TTL of a record and soaTTL. It returns nil on invalid A records.
func (s *Server) synthRR(rr dns.RR, soaTTL uint32) (result dns.RR) {
aResp, ok := rr.(*dns.A)
if !ok {
return rr
}
addr, err := netutil.IPToAddr(aResp.A, netutil.AddrFamilyIPv4)
if err != nil {
log.Error("dnsforward: bad A record: %s", err)
return nil
}
aaaa := &dns.AAAA{
Hdr: dns.RR_Header{
Name: aResp.Hdr.Name,
Rrtype: dns.TypeAAAA,
Class: aResp.Hdr.Class,
Ttl: mathutil.Min(aResp.Hdr.Ttl, soaTTL),
},
AAAA: s.mapDNS64(addr),
}
return aaaa
}

View file

@ -15,6 +15,16 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
// maxDNS64SynTTL is the maximum TTL for synthesized DNS64 responses with no SOA
// records in seconds.
//
// If the SOA RR was not delivered with the negative response to the AAAA query,
// then the DNS64 SHOULD use the TTL of the original A RR or 600 seconds,
// whichever is shorter.
//
// See https://datatracker.ietf.org/doc/html/rfc6147#section-5.1.7.
const maxDNS64SynTTL uint32 = 600
// newRR is a helper that creates a new dns.RR with the given name, qtype, ttl // newRR is a helper that creates a new dns.RR with the given name, qtype, ttl
// and value. It fails the test if the qtype is not supported or the type of // and value. It fails the test if the qtype is not supported or the type of
// value doesn't match the qtype. // value doesn't match the qtype.

View file

@ -80,10 +80,17 @@ type Server struct {
privateNets netutil.SubnetSet privateNets netutil.SubnetSet
localResolvers *proxy.Proxy localResolvers *proxy.Proxy
sysResolvers aghnet.SystemResolvers sysResolvers aghnet.SystemResolvers
// recDetector is a cache for recursive requests. It is used to detect
// and prevent recursive requests only for private upstreams.
//
// See https://github.com/adguardTeam/adGuardHome/issues/3185#issuecomment-851048135.
recDetector *recursionDetector recDetector *recursionDetector
// dns64Prefix is the set of NAT64 prefixes used for DNS64 handling. // dns64Pref is the NAT64 prefix used for DNS64 response mapping. The major
dns64Prefs []netip.Prefix // part of DNS64 happens inside the [proxy] package, but there still are
// some places where response mapping is needed (e.g. DHCP).
dns64Pref netip.Prefix
// anonymizer masks the client's IP addresses if needed. // anonymizer masks the client's IP addresses if needed.
anonymizer *aghnet.IPMut anonymizer *aghnet.IPMut
@ -246,8 +253,8 @@ func (s *Server) Resolve(host string) ([]net.IPAddr, error) {
// RDNSExchanger is a resolver for clients' addresses. // RDNSExchanger is a resolver for clients' addresses.
type RDNSExchanger interface { type RDNSExchanger interface {
// Exchange tries to resolve the ip in a suitable way, e.g. either as // Exchange tries to resolve the ip in a suitable way, i.e. either as local
// local or as external. // or as external.
Exchange(ip net.IP) (host string, err error) Exchange(ip net.IP) (host string, err error)
// ResolvesPrivatePTR returns true if the RDNSExchanger is able to // ResolvesPrivatePTR returns true if the RDNSExchanger is able to
@ -256,13 +263,13 @@ type RDNSExchanger interface {
} }
const ( const (
// rDNSEmptyAnswerErr is returned by Exchange method when the answer // ErrRDNSNoData is returned by [RDNSExchanger.Exchange] when the answer
// section of respond is empty. // section of response is either NODATA or has no PTR records.
rDNSEmptyAnswerErr errors.Error = "the answer section is empty" ErrRDNSNoData errors.Error = "no ptr data in response"
// rDNSNotPTRErr is returned by Exchange method when the response is not // ErrRDNSFailed is returned by [RDNSExchanger.Exchange] if the received
// of PTR type. // response is not a NOERROR or NXDOMAIN.
rDNSNotPTRErr errors.Error = "the response is not a ptr" ErrRDNSFailed errors.Error = "failed to resolve ptr"
) )
// type check // type check
@ -317,18 +324,25 @@ func (s *Server) Exchange(ip net.IP) (host string, err error) {
return "", err return "", err
} }
// Distinguish between NODATA response and a failed request.
resp := ctx.Res resp := ctx.Res
if len(resp.Answer) == 0 { if resp.Rcode != dns.RcodeSuccess && resp.Rcode != dns.RcodeNameError {
return "", fmt.Errorf("lookup for %q: %w", arpa, rDNSEmptyAnswerErr) return "", fmt.Errorf(
} "received %s response: %w",
dns.RcodeToString[resp.Rcode],
ptr, ok := resp.Answer[0].(*dns.PTR) ErrRDNSFailed,
if !ok { )
return "", fmt.Errorf("type checking: %w", rDNSNotPTRErr)
} }
for _, ans := range resp.Answer {
ptr, ok := ans.(*dns.PTR)
if ok {
return strings.TrimSuffix(ptr.Ptr, "."), nil return strings.TrimSuffix(ptr.Ptr, "."), nil
} }
}
return "", ErrRDNSNoData
}
// ResolvesPrivatePTR implements the RDNSExchanger interface for *Server. // ResolvesPrivatePTR implements the RDNSExchanger interface for *Server.
func (s *Server) ResolvesPrivatePTR() (ok bool) { func (s *Server) ResolvesPrivatePTR() (ok bool) {
@ -477,6 +491,8 @@ func (s *Server) Prepare(conf *ServerConfig) (err error) {
return fmt.Errorf("preparing proxy: %w", err) return fmt.Errorf("preparing proxy: %w", err)
} }
s.setupDNS64()
err = s.prepareInternalProxy() err = s.prepareInternalProxy()
if err != nil { if err != nil {
return fmt.Errorf("preparing internal proxy: %w", err) return fmt.Errorf("preparing internal proxy: %w", err)
@ -493,18 +509,18 @@ func (s *Server) Prepare(conf *ServerConfig) (err error) {
s.registerHandlers() s.registerHandlers()
err = s.setupDNS64() // TODO(e.burkov): Remove once the local resolvers logic moved to dnsproxy.
if err != nil {
return fmt.Errorf("preparing DNS64: %w", err)
}
s.dnsProxy = &proxy.Proxy{Config: proxyConfig}
err = s.setupResolvers(s.conf.LocalPTRResolvers) err = s.setupResolvers(s.conf.LocalPTRResolvers)
if err != nil { if err != nil {
return fmt.Errorf("setting up resolvers: %w", err) return fmt.Errorf("setting up resolvers: %w", err)
} }
if s.conf.UsePrivateRDNS {
proxyConfig.PrivateRDNSUpstreamConfig = s.localResolvers.UpstreamConfig
}
s.dnsProxy = &proxy.Proxy{Config: proxyConfig}
s.recDetector.clear() s.recDetector.clear()
return nil return nil

View file

@ -89,9 +89,14 @@ func createTestServer(
s.serverLock.Lock() s.serverLock.Lock()
defer s.serverLock.Unlock() defer s.serverLock.Unlock()
// TODO(e.burkov): Try to move it higher.
if localUps != nil { if localUps != nil {
s.localResolvers.UpstreamConfig.Upstreams = []upstream.Upstream{localUps} ups := []upstream.Upstream{localUps}
s.localResolvers.UpstreamConfig.Upstreams = ups
s.conf.UsePrivateRDNS = true s.conf.UsePrivateRDNS = true
s.dnsProxy.PrivateRDNSUpstreamConfig = &proxy.UpstreamConfig{
Upstreams: ups,
}
} }
return s return s
@ -1216,6 +1221,9 @@ func TestServer_Exchange(t *testing.T) {
errUpstream := aghtest.NewErrorUpstream() errUpstream := aghtest.NewErrorUpstream()
nonPtrUpstream := aghtest.NewBlockUpstream("some-host", true) nonPtrUpstream := aghtest.NewBlockUpstream("some-host", true)
refusingUpstream := aghtest.NewUpstreamMock(func(req *dns.Msg) (resp *dns.Msg, err error) {
return new(dns.Msg).SetRcode(req, dns.RcodeRefused), nil
})
srv := &Server{ srv := &Server{
recDetector: newRecursionDetector(0, 1), recDetector: newRecursionDetector(0, 1),
@ -1260,15 +1268,21 @@ func TestServer_Exchange(t *testing.T) {
}, { }, {
name: "empty_answer_error", name: "empty_answer_error",
want: "", want: "",
wantErr: rDNSEmptyAnswerErr, wantErr: ErrRDNSNoData,
locUpstream: locUpstream, locUpstream: locUpstream,
req: net.IP{192, 168, 1, 2}, req: net.IP{192, 168, 1, 2},
}, { }, {
name: "not_ptr_error", name: "invalid_answer",
want: "", want: "",
wantErr: rDNSNotPTRErr, wantErr: ErrRDNSNoData,
locUpstream: nonPtrUpstream, locUpstream: nonPtrUpstream,
req: localIP, req: localIP,
}, {
name: "refused",
want: "",
wantErr: ErrRDNSFailed,
locUpstream: refusingUpstream,
req: localIP,
}} }}
for _, tc := range testCases { for _, tc := range testCases {

View file

@ -22,9 +22,11 @@ func (s *Server) processQueryLogsAndStats(dctx *dnsContext) (rc resultCode) {
shouldLog := true shouldLog := true
msg := pctx.Req msg := pctx.Req
q := msg.Question[0]
host := strings.ToLower(strings.TrimSuffix(q.Name, "."))
// don't log ANY request if refuseAny is enabled // don't log ANY request if refuseAny is enabled
if len(msg.Question) >= 1 && msg.Question[0].Qtype == dns.TypeANY && s.conf.RefuseAny { if q.Qtype == dns.TypeANY && s.conf.RefuseAny {
shouldLog = false shouldLog = false
} }
@ -41,11 +43,20 @@ func (s *Server) processQueryLogsAndStats(dctx *dnsContext) (rc resultCode) {
// Synchronize access to s.queryLog and s.stats so they won't be suddenly // Synchronize access to s.queryLog and s.stats so they won't be suddenly
// uninitialized while in use. This can happen after proxy server has been // uninitialized while in use. This can happen after proxy server has been
// stopped, but its workers haven't yet exited. // stopped, but its workers haven't yet exited.
if shouldLog && s.queryLog != nil { if shouldLog &&
s.queryLog != nil &&
s.queryLog.ShouldLog(host, q.Qtype, q.Qclass) {
s.logQuery(dctx, pctx, elapsed, ip) s.logQuery(dctx, pctx, elapsed, ip)
} else {
log.Debug(
"dnsforward: request %s %s from %s ignored; not logging",
dns.Type(q.Qtype),
host,
ip,
)
} }
if s.stats != nil { if s.stats != nil && s.stats.ShouldCount(host, q.Qtype, q.Qclass) {
s.updateStats(dctx, elapsed, *dctx.result, ip) s.updateStats(dctx, elapsed, *dctx.result, ip)
} }

View file

@ -16,34 +16,44 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
// testQueryLog is a simple querylog.QueryLog implementation for tests. // testQueryLog is a simple [querylog.QueryLog] implementation for tests.
type testQueryLog struct { type testQueryLog struct {
// QueryLog is embedded here simply to make testQueryLog // QueryLog is embedded here simply to make testQueryLog
// a querylog.QueryLog without actually implementing all methods. // a [querylog.QueryLog] without actually implementing all methods.
querylog.QueryLog querylog.QueryLog
lastParams *querylog.AddParams lastParams *querylog.AddParams
} }
// Add implements the querylog.QueryLog interface for *testQueryLog. // Add implements the [querylog.QueryLog] interface for *testQueryLog.
func (l *testQueryLog) Add(p *querylog.AddParams) { func (l *testQueryLog) Add(p *querylog.AddParams) {
l.lastParams = p l.lastParams = p
} }
// testStats is a simple stats.Stats implementation for tests. // ShouldLog implements the [querylog.QueryLog] interface for *testQueryLog.
func (l *testQueryLog) ShouldLog(string, uint16, uint16) bool {
return true
}
// testStats is a simple [stats.Interface] implementation for tests.
type testStats struct { type testStats struct {
// Stats is embedded here simply to make testStats a stats.Stats without // Stats is embedded here simply to make testStats a [stats.Interface]
// actually implementing all methods. // without actually implementing all methods.
stats.Interface stats.Interface
lastEntry stats.Entry lastEntry stats.Entry
} }
// Update implements the stats.Stats interface for *testStats. // Update implements the [stats.Interface] interface for *testStats.
func (l *testStats) Update(e stats.Entry) { func (l *testStats) Update(e stats.Entry) {
l.lastEntry = e l.lastEntry = e
} }
// ShouldCount implements the [stats.Interface] interface for *testStats.
func (l *testStats) ShouldCount(string, uint16, uint16) bool {
return true
}
func TestProcessQueryLogsAndStats(t *testing.T) { func TestProcessQueryLogsAndStats(t *testing.T) {
testCases := []struct { testCases := []struct {
name string name string

View file

@ -1283,7 +1283,7 @@ var blockedServices = []blockedService{{
}, { }, {
ID: "leagueoflegends", ID: "leagueoflegends",
Name: "League of Legends", Name: "League of Legends",
IconSVG: []byte("<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 30 30\" width=\"60px\" height=\"60px\"><path d=\"M 7 4 L 9 7.25 L 9 22.75 L 6.875 26 L 21.957031 26 L 25 22 L 14 22 L 14 4 L 7 4 z M 16 4.0507812 L 16 6.0585938 C 20.493 6.5575937 24 10.375 24 15 C 24 16.849 23.438516 18.569 22.478516 20 L 24.785156 20 C 25.556156 18.498 26 16.801 26 15 C 26 9.272 21.598 4.5577812 16 4.0507812 z M 6.8730469 7.6113281 C 5.0940469 9.5663281 4 12.155 4 15 C 4 17.837 5.0884219 20.418094 6.8574219 22.371094 L 7 22.154297 L 7 19.105469 C 6.365 17.872469 6 16.479 6 15 C 6 13.521 6.365 12.127531 7 10.894531 L 7 7.8164062 L 6.8730469 7.6113281 z\"/></svg>"), IconSVG: []byte("<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" viewBox=\"0 0 30 30\"><path d=\"M 7 4 L 9 7.25 L 9 22.75 L 6.875 26 L 21.957031 26 L 25 22 L 14 22 L 14 4 L 7 4 z M 16 4.0507812 L 16 6.0585938 C 20.493 6.5575937 24 10.375 24 15 C 24 16.849 23.438516 18.569 22.478516 20 L 24.785156 20 C 25.556156 18.498 26 16.801 26 15 C 26 9.272 21.598 4.5577812 16 4.0507812 z M 6.8730469 7.6113281 C 5.0940469 9.5663281 4 12.155 4 15 C 4 17.837 5.0884219 20.418094 6.8574219 22.371094 L 7 22.154297 L 7 19.105469 C 6.365 17.872469 6 16.479 6 15 C 6 13.521 6.365 12.127531 7 10.894531 L 7 7.8164062 L 6.8730469 7.6113281 z\"/></svg>"),
Rules: []string{ Rules: []string{
"||leagueoflegends.co.kr^", "||leagueoflegends.co.kr^",
"||leagueoflegends.com^", "||leagueoflegends.com^",
@ -1307,9 +1307,7 @@ var blockedServices = []blockedService{{
Rules: []string{ Rules: []string{
"||aus.social^", "||aus.social^",
"||awscommunity.social^", "||awscommunity.social^",
"||cupoftea.social^",
"||cyberplace.social^", "||cyberplace.social^",
"||defcon.social^",
"||det.social^", "||det.social^",
"||fosstodon.org^", "||fosstodon.org^",
"||glasgow.social^", "||glasgow.social^",
@ -1335,7 +1333,6 @@ var blockedServices = []blockedService{{
"||mastodon.au^", "||mastodon.au^",
"||mastodon.bida.im^", "||mastodon.bida.im^",
"||mastodon.com.tr^", "||mastodon.com.tr^",
"||mastodon.eus^",
"||mastodon.green^", "||mastodon.green^",
"||mastodon.ie^", "||mastodon.ie^",
"||mastodon.iriseden.eu^", "||mastodon.iriseden.eu^",
@ -1343,13 +1340,15 @@ var blockedServices = []blockedService{{
"||mastodon.nu^", "||mastodon.nu^",
"||mastodon.nz^", "||mastodon.nz^",
"||mastodon.online^", "||mastodon.online^",
"||mastodon.online^",
"||mastodon.scot^", "||mastodon.scot^",
"||mastodon.sdf.org^", "||mastodon.sdf.org^",
"||mastodon.social^", "||mastodon.social^",
"||mastodon.social^",
"||mastodon.top^", "||mastodon.top^",
"||mastodon.uno^", "||mastodon.uno^",
"||mastodon.world^", "||mastodon.world^",
"||mastodon.zaclys.com^", "||mastodon.xyz^",
"||mastodonapp.uk^", "||mastodonapp.uk^",
"||mastodonners.nl^", "||mastodonners.nl^",
"||mastodont.cat^", "||mastodont.cat^",
@ -1361,9 +1360,10 @@ var blockedServices = []blockedService{{
"||mindly.social^", "||mindly.social^",
"||mstdn.ca^", "||mstdn.ca^",
"||mstdn.jp^", "||mstdn.jp^",
"||mstdn.party^",
"||mstdn.social^", "||mstdn.social^",
"||muenchen.social^", "||muenchen.social^",
"||muenster.im^", "||nerdculture.de^",
"||newsie.social^", "||newsie.social^",
"||noc.social^", "||noc.social^",
"||norden.social^", "||norden.social^",

View file

@ -67,15 +67,18 @@ func (c *Client) closeUpstreams() (err error) {
type clientSource uint type clientSource uint
// Client sources. The order determines the priority. // Clients information sources. The order determines the priority.
const ( const (
ClientSourceWHOIS clientSource = iota ClientSourceNone clientSource = iota
ClientSourceWHOIS
ClientSourceARP ClientSourceARP
ClientSourceRDNS ClientSourceRDNS
ClientSourceDHCP ClientSourceDHCP
ClientSourceHostsFile ClientSourceHostsFile
ClientSourcePersistent
) )
// type check
var _ fmt.Stringer = clientSource(0) var _ fmt.Stringer = clientSource(0)
// String returns a human-readable name of cs. // String returns a human-readable name of cs.
@ -96,6 +99,7 @@ func (cs clientSource) String() (s string) {
} }
} }
// type check
var _ encoding.TextMarshaler = clientSource(0) var _ encoding.TextMarshaler = clientSource(0)
// MarshalText implements encoding.TextMarshaler for the clientSource. // MarshalText implements encoding.TextMarshaler for the clientSource.
@ -332,23 +336,24 @@ func (clients *clientsContainer) onDHCPLeaseChanged(flags int) {
} }
} }
// exists checks if client with this IP address already exists. // clientSource checks if client with this IP address already exists and returns
func (clients *clientsContainer) exists(ip netip.Addr, source clientSource) (ok bool) { // the source which updated it last. It returns [ClientSourceNone] if the
// client doesn't exist.
func (clients *clientsContainer) clientSource(ip netip.Addr) (src clientSource) {
clients.lock.Lock() clients.lock.Lock()
defer clients.lock.Unlock() defer clients.lock.Unlock()
_, ok = clients.findLocked(ip.String()) _, ok := clients.findLocked(ip.String())
if ok { if ok {
return true return ClientSourcePersistent
} }
rc, ok := clients.ipToRC[ip] rc, ok := clients.ipToRC[ip]
if !ok { if !ok {
return false return ClientSourceNone
} }
// Return false if the new source has higher priority. return rc.Source
return source <= rc.Source
} }
func toQueryLogWHOIS(wi *RuntimeClientWHOISInfo) (cw *querylog.ClientWHOIS) { func toQueryLogWHOIS(wi *RuntimeClientWHOISInfo) (cw *querylog.ClientWHOIS) {

View file

@ -67,9 +67,9 @@ func TestClients(t *testing.T) {
assert.Equal(t, "client2", c.Name) assert.Equal(t, "client2", c.Name)
assert.False(t, clients.exists(cliNoneIP, ClientSourceHostsFile)) assert.Equal(t, clients.clientSource(cliNoneIP), ClientSourceNone)
assert.True(t, clients.exists(cli1IP, ClientSourceHostsFile)) assert.Equal(t, clients.clientSource(cli1IP), ClientSourcePersistent)
assert.True(t, clients.exists(cli2IP, ClientSourceHostsFile)) assert.Equal(t, clients.clientSource(cli2IP), ClientSourcePersistent)
}) })
t.Run("add_fail_name", func(t *testing.T) { t.Run("add_fail_name", func(t *testing.T) {
@ -127,8 +127,8 @@ func TestClients(t *testing.T) {
}) })
require.NoError(t, err) require.NoError(t, err)
assert.False(t, clients.exists(cliOldIP, ClientSourceHostsFile)) assert.Equal(t, clients.clientSource(cliOldIP), ClientSourceNone)
assert.True(t, clients.exists(cliNewIP, ClientSourceHostsFile)) assert.Equal(t, clients.clientSource(cliNewIP), ClientSourcePersistent)
err = clients.Update("client1", &Client{ err = clients.Update("client1", &Client{
IDs: []string{cliNew}, IDs: []string{cliNew},
@ -157,7 +157,7 @@ func TestClients(t *testing.T) {
ok := clients.Del("client1-renamed") ok := clients.Del("client1-renamed")
require.True(t, ok) require.True(t, ok)
assert.False(t, clients.exists(netip.MustParseAddr("1.1.1.2"), ClientSourceHostsFile)) assert.Equal(t, clients.clientSource(netip.MustParseAddr("1.1.1.2")), ClientSourceNone)
}) })
t.Run("del_fail", func(t *testing.T) { t.Run("del_fail", func(t *testing.T) {
@ -176,18 +176,18 @@ func TestClients(t *testing.T) {
ok = clients.AddHost(ip, "host3", ClientSourceHostsFile) ok = clients.AddHost(ip, "host3", ClientSourceHostsFile)
assert.True(t, ok) assert.True(t, ok)
assert.True(t, clients.exists(ip, ClientSourceHostsFile)) assert.Equal(t, clients.clientSource(ip), ClientSourceHostsFile)
}) })
t.Run("dhcp_replaces_arp", func(t *testing.T) { t.Run("dhcp_replaces_arp", func(t *testing.T) {
ip := netip.MustParseAddr("1.2.3.4") ip := netip.MustParseAddr("1.2.3.4")
ok := clients.AddHost(ip, "from_arp", ClientSourceARP) ok := clients.AddHost(ip, "from_arp", ClientSourceARP)
assert.True(t, ok) assert.True(t, ok)
assert.True(t, clients.exists(ip, ClientSourceARP)) assert.Equal(t, clients.clientSource(ip), ClientSourceARP)
ok = clients.AddHost(ip, "from_dhcp", ClientSourceDHCP) ok = clients.AddHost(ip, "from_dhcp", ClientSourceDHCP)
assert.True(t, ok) assert.True(t, ok)
assert.True(t, clients.exists(ip, ClientSourceDHCP)) assert.Equal(t, clients.clientSource(ip), ClientSourceDHCP)
}) })
t.Run("addhost_fail", func(t *testing.T) { t.Run("addhost_fail", func(t *testing.T) {

View file

@ -6,6 +6,7 @@ import (
"net/netip" "net/netip"
"os" "os"
"path/filepath" "path/filepath"
"sort"
"sync" "sync"
"github.com/AdguardTeam/AdGuardHome/internal/aghalg" "github.com/AdguardTeam/AdGuardHome/internal/aghalg"
@ -114,6 +115,8 @@ type configuration struct {
DNS dnsConfig `yaml:"dns"` DNS dnsConfig `yaml:"dns"`
TLS tlsConfigSettings `yaml:"tls"` TLS tlsConfigSettings `yaml:"tls"`
QueryLog queryLogConfig `yaml:"querylog"`
Stats statsConfig `yaml:"statistics"`
// Filters reflects the filters from [filtering.Config]. It's cloned to the // Filters reflects the filters from [filtering.Config]. It's cloned to the
// config used in the filtering module at the startup. Afterwards it's // config used in the filtering module at the startup. Afterwards it's
@ -147,20 +150,6 @@ type dnsConfig struct {
BindHosts []netip.Addr `yaml:"bind_hosts"` BindHosts []netip.Addr `yaml:"bind_hosts"`
Port int `yaml:"port"` Port int `yaml:"port"`
// StatsInterval is the time interval for flushing statistics to the disk in
// days.
StatsInterval uint32 `yaml:"statistics_interval"`
// QueryLogEnabled defines if the query log is enabled.
QueryLogEnabled bool `yaml:"querylog_enabled"`
// QueryLogFileEnabled defines, if the query log is written to the file.
QueryLogFileEnabled bool `yaml:"querylog_file_enabled"`
// QueryLogInterval is the interval for query log's files rotation.
QueryLogInterval timeutil.Duration `yaml:"querylog_interval"`
// QueryLogMemSize is the number of entries kept in memory before they are
// flushed to disk.
QueryLogMemSize uint32 `yaml:"querylog_size_memory"`
// AnonymizeClientIP defines if clients' IP addresses should be anonymized // AnonymizeClientIP defines if clients' IP addresses should be anonymized
// in query log and statistics. // in query log and statistics.
AnonymizeClientIP bool `yaml:"anonymize_client_ip"` AnonymizeClientIP bool `yaml:"anonymize_client_ip"`
@ -188,7 +177,7 @@ type dnsConfig struct {
UseDNS64 bool `yaml:"use_dns64"` UseDNS64 bool `yaml:"use_dns64"`
// DNS64Prefixes is the list of NAT64 prefixes to be used for DNS64. // DNS64Prefixes is the list of NAT64 prefixes to be used for DNS64.
DNS64Prefixes []string `yaml:"dns64_prefixes"` DNS64Prefixes []netip.Prefix `yaml:"dns64_prefixes"`
// ServeHTTP3 defines if HTTP/3 is be allowed for incoming requests. // ServeHTTP3 defines if HTTP/3 is be allowed for incoming requests.
// //
@ -228,6 +217,37 @@ type tlsConfigSettings struct {
dnsforward.TLSConfig `yaml:",inline" json:",inline"` dnsforward.TLSConfig `yaml:",inline" json:",inline"`
} }
type queryLogConfig struct {
// Enabled defines if the query log is enabled.
Enabled bool `yaml:"enabled"`
// FileEnabled defines, if the query log is written to the file.
FileEnabled bool `yaml:"file_enabled"`
// Interval is the interval for query log's files rotation.
Interval timeutil.Duration `yaml:"interval"`
// MemSize is the number of entries kept in memory before they are
// flushed to disk.
MemSize uint32 `yaml:"size_memory"`
// Ignored is the list of host names, which should not be written to
// log.
Ignored []string `yaml:"ignored"`
}
type statsConfig struct {
// Enabled defines if the statistics are enabled.
Enabled bool `yaml:"enabled"`
// Interval is the time interval for flushing statistics to the disk in
// days.
Interval uint32 `yaml:"interval"`
// Ignored is the list of host names, which should not be counted.
Ignored []string `yaml:"ignored"`
}
// config is the global configuration structure. // config is the global configuration structure.
// //
// TODO(a.garipov, e.burkov): This global is awful and must be removed. // TODO(a.garipov, e.burkov): This global is awful and must be removed.
@ -240,11 +260,6 @@ var config = &configuration{
DNS: dnsConfig{ DNS: dnsConfig{
BindHosts: []netip.Addr{netip.IPv4Unspecified()}, BindHosts: []netip.Addr{netip.IPv4Unspecified()},
Port: defaultPortDNS, Port: defaultPortDNS,
StatsInterval: 1,
QueryLogEnabled: true,
QueryLogFileEnabled: true,
QueryLogInterval: timeutil.Duration{Duration: 90 * timeutil.Day},
QueryLogMemSize: 1000,
FilteringConfig: dnsforward.FilteringConfig{ FilteringConfig: dnsforward.FilteringConfig{
ProtectionEnabled: true, // whether or not use any of filtering features ProtectionEnabled: true, // whether or not use any of filtering features
BlockingMode: dnsforward.BlockingModeDefault, BlockingMode: dnsforward.BlockingModeDefault,
@ -282,6 +297,18 @@ var config = &configuration{
PortDNSOverTLS: defaultPortTLS, // needs to be passed through to dnsproxy PortDNSOverTLS: defaultPortTLS, // needs to be passed through to dnsproxy
PortDNSOverQUIC: defaultPortQUIC, PortDNSOverQUIC: defaultPortQUIC,
}, },
QueryLog: queryLogConfig{
Enabled: true,
FileEnabled: true,
Interval: timeutil.Duration{Duration: 90 * timeutil.Day},
MemSize: 1000,
Ignored: []string{},
},
Stats: statsConfig{
Enabled: true,
Interval: 1,
Ignored: []string{},
},
// NOTE: Keep these parameters in sync with the one put into // NOTE: Keep these parameters in sync with the one put into
// client/src/helpers/filters/filters.js by scripts/vetted-filters. // client/src/helpers/filters/filters.js by scripts/vetted-filters.
// //
@ -458,19 +485,24 @@ func (c *configuration) write() (err error) {
} }
if Context.stats != nil { if Context.stats != nil {
sdc := stats.DiskConfig{} statsConf := stats.Config{}
Context.stats.WriteDiskConfig(&sdc) Context.stats.WriteDiskConfig(&statsConf)
config.DNS.StatsInterval = sdc.Interval config.Stats.Interval = statsConf.LimitDays
config.Stats.Enabled = statsConf.Enabled
config.Stats.Ignored = statsConf.Ignored.Values()
sort.Strings(config.Stats.Ignored)
} }
if Context.queryLog != nil { if Context.queryLog != nil {
dc := querylog.Config{} dc := querylog.Config{}
Context.queryLog.WriteDiskConfig(&dc) Context.queryLog.WriteDiskConfig(&dc)
config.DNS.QueryLogEnabled = dc.Enabled
config.DNS.QueryLogFileEnabled = dc.FileEnabled
config.DNS.QueryLogInterval = timeutil.Duration{Duration: dc.RotationIvl}
config.DNS.QueryLogMemSize = dc.MemSize
config.DNS.AnonymizeClientIP = dc.AnonymizeClientIP config.DNS.AnonymizeClientIP = dc.AnonymizeClientIP
config.QueryLog.Enabled = dc.Enabled
config.QueryLog.FileEnabled = dc.FileEnabled
config.QueryLog.Interval = timeutil.Duration{Duration: dc.RotationIvl}
config.QueryLog.MemSize = dc.MemSize
config.QueryLog.Ignored = dc.Ignored.Values()
sort.Strings(config.QueryLog.Ignored)
} }
if Context.filters != nil { if Context.filters != nil {

View file

@ -20,7 +20,7 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/version" "github.com/AdguardTeam/AdGuardHome/internal/version"
"github.com/AdguardTeam/golibs/errors" "github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log" "github.com/AdguardTeam/golibs/log"
"github.com/lucas-clemente/quic-go/http3" "github.com/quic-go/quic-go/http3"
) )
// getAddrsResponse is the response for /install/get_addresses endpoint. // getAddrsResponse is the response for /install/get_addresses endpoint.

View file

@ -7,6 +7,7 @@ import (
"net/url" "net/url"
"os" "os"
"path/filepath" "path/filepath"
"strings"
"github.com/AdguardTeam/AdGuardHome/internal/aghalg" "github.com/AdguardTeam/AdGuardHome/internal/aghalg"
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp" "github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
@ -20,6 +21,7 @@ import (
"github.com/AdguardTeam/golibs/errors" "github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log" "github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/netutil" "github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/stringutil"
"github.com/ameshkov/dnscrypt/v2" "github.com/ameshkov/dnscrypt/v2"
yaml "gopkg.in/yaml.v3" yaml "gopkg.in/yaml.v3"
) )
@ -51,10 +53,18 @@ func initDNS() (err error) {
statsConf := stats.Config{ statsConf := stats.Config{
Filename: filepath.Join(baseDir, "stats.db"), Filename: filepath.Join(baseDir, "stats.db"),
LimitDays: config.DNS.StatsInterval, LimitDays: config.Stats.Interval,
ConfigModified: onConfigModified, ConfigModified: onConfigModified,
HTTPRegister: httpRegister, HTTPRegister: httpRegister,
Enabled: config.Stats.Enabled,
} }
set, err := nonDupEmptyHostNames(config.Stats.Ignored)
if err != nil {
return fmt.Errorf("statistics: ignored list: %w", err)
}
statsConf.Ignored = set
Context.stats, err = stats.New(statsConf) Context.stats, err = stats.New(statsConf)
if err != nil { if err != nil {
return fmt.Errorf("init stats: %w", err) return fmt.Errorf("init stats: %w", err)
@ -66,12 +76,19 @@ func initDNS() (err error) {
HTTPRegister: httpRegister, HTTPRegister: httpRegister,
FindClient: Context.clients.findMultiple, FindClient: Context.clients.findMultiple,
BaseDir: baseDir, BaseDir: baseDir,
RotationIvl: config.DNS.QueryLogInterval.Duration,
MemSize: config.DNS.QueryLogMemSize,
Enabled: config.DNS.QueryLogEnabled,
FileEnabled: config.DNS.QueryLogFileEnabled,
AnonymizeClientIP: config.DNS.AnonymizeClientIP, AnonymizeClientIP: config.DNS.AnonymizeClientIP,
RotationIvl: config.QueryLog.Interval.Duration,
MemSize: config.QueryLog.MemSize,
Enabled: config.QueryLog.Enabled,
FileEnabled: config.QueryLog.FileEnabled,
} }
set, err = nonDupEmptyHostNames(config.QueryLog.Ignored)
if err != nil {
return fmt.Errorf("querylog: ignored list: %w", err)
}
conf.Ignored = set
Context.queryLog = querylog.New(conf) Context.queryLog = querylog.New(conf)
Context.filters, err = filtering.New(config.DNS.DnsfilterConf, nil) Context.filters, err = filtering.New(config.DNS.DnsfilterConf, nil)
@ -515,3 +532,27 @@ func closeDNSServer() {
log.Debug("all dns modules are closed") log.Debug("all dns modules are closed")
} }
// nonDupEmptyHostNames returns nil and error, if list has duplicate or empty
// host name. Otherwise returns a set, which contains lowercase host names
// without dot at the end, and nil error.
func nonDupEmptyHostNames(list []string) (set *stringutil.Set, err error) {
set = stringutil.NewSet()
for _, v := range list {
host := strings.ToLower(strings.TrimSuffix(v, "."))
// TODO(a.garipov): Think about ignoring empty (".") names in
// the future.
if host == "" {
return nil, errors.Error("host name is empty")
}
if set.Has(host) {
return nil, fmt.Errorf("duplicate host name %q", host)
}
set.Add(host)
}
return set, nil
}

View file

@ -8,6 +8,7 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/dnsforward" "github.com/AdguardTeam/AdGuardHome/internal/dnsforward"
"github.com/AdguardTeam/golibs/cache" "github.com/AdguardTeam/golibs/cache"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log" "github.com/AdguardTeam/golibs/log"
) )
@ -16,10 +17,6 @@ type RDNS struct {
exchanger dnsforward.RDNSExchanger exchanger dnsforward.RDNSExchanger
clients *clientsContainer clients *clientsContainer
// usePrivate is used to store the state of current private RDNS resolving
// settings and to react to it's changes.
usePrivate uint32
// ipCh used to pass client's IP to rDNS workerLoop. // ipCh used to pass client's IP to rDNS workerLoop.
ipCh chan netip.Addr ipCh chan netip.Addr
@ -28,13 +25,21 @@ type RDNS struct {
// address will be resolved once again. If the address couldn't be // address will be resolved once again. If the address couldn't be
// resolved, cache prevents further attempts to resolve it for some time. // resolved, cache prevents further attempts to resolve it for some time.
ipCache cache.Cache ipCache cache.Cache
// usePrivate stores the state of current private reverse-DNS resolving
// settings.
usePrivate atomic.Bool
} }
// Default rDNS values. // Default AdGuard Home reverse DNS values.
const ( const (
defaultRDNSCacheSize = 10000 revDNSCacheSize = 10000
defaultRDNSCacheTTL = 1 * 60 * 60
defaultRDNSIPChSize = 256 // TODO(e.burkov): Make these values configurable.
revDNSCacheTTL = 24 * 60 * 60
revDNSFailureCacheTTL = 1 * 60 * 60
revDNSQueueSize = 256
) )
// NewRDNS creates and returns initialized RDNS. // NewRDNS creates and returns initialized RDNS.
@ -48,14 +53,13 @@ func NewRDNS(
clients: clients, clients: clients,
ipCache: cache.New(cache.Config{ ipCache: cache.New(cache.Config{
EnableLRU: true, EnableLRU: true,
MaxCount: defaultRDNSCacheSize, MaxCount: revDNSCacheSize,
}), }),
ipCh: make(chan netip.Addr, defaultRDNSIPChSize), ipCh: make(chan netip.Addr, revDNSQueueSize),
}
if usePrivate {
rDNS.usePrivate = 1
} }
rDNS.usePrivate.Store(usePrivate)
go rDNS.workerLoop() go rDNS.workerLoop()
return rDNS return rDNS
@ -68,12 +72,8 @@ func NewRDNS(
// approach since only unresolved locally-served addresses should be removed. // approach since only unresolved locally-served addresses should be removed.
// Implement when improving the cache. // Implement when improving the cache.
func (r *RDNS) ensurePrivateCache() { func (r *RDNS) ensurePrivateCache() {
var usePrivate uint32 usePrivate := r.exchanger.ResolvesPrivatePTR()
if r.exchanger.ResolvesPrivatePTR() { if r.usePrivate.CompareAndSwap(!usePrivate, usePrivate) {
usePrivate = 1
}
if atomic.CompareAndSwapUint32(&r.usePrivate, 1-usePrivate, usePrivate) {
r.ipCache.Clear() r.ipCache.Clear()
} }
} }
@ -84,25 +84,28 @@ func (r *RDNS) isCached(ip netip.Addr) (ok bool) {
ipBytes := ip.AsSlice() ipBytes := ip.AsSlice()
now := uint64(time.Now().Unix()) now := uint64(time.Now().Unix())
if expire := r.ipCache.Get(ipBytes); len(expire) != 0 { if expire := r.ipCache.Get(ipBytes); len(expire) != 0 {
if binary.BigEndian.Uint64(expire) > now { return binary.BigEndian.Uint64(expire) > now
return true
} }
}
// The cache entry either expired or doesn't exist.
ttl := make([]byte, 8)
binary.BigEndian.PutUint64(ttl, now+defaultRDNSCacheTTL)
r.ipCache.Set(ipBytes, ttl)
return false return false
} }
// cache caches the ip address for ttl seconds.
func (r *RDNS) cache(ip netip.Addr, ttl uint64) {
ipData := ip.AsSlice()
ttlData := [8]byte{}
binary.BigEndian.PutUint64(ttlData[:], uint64(time.Now().Unix())+ttl)
r.ipCache.Set(ipData, ttlData[:])
}
// Begin adds the ip to the resolving queue if it is not cached or already // Begin adds the ip to the resolving queue if it is not cached or already
// resolved. // resolved.
func (r *RDNS) Begin(ip netip.Addr) { func (r *RDNS) Begin(ip netip.Addr) {
r.ensurePrivateCache() r.ensurePrivateCache()
if r.isCached(ip) || r.clients.exists(ip, ClientSourceRDNS) { if r.isCached(ip) || r.clients.clientSource(ip) > ClientSourceRDNS {
return return
} }
@ -120,15 +123,21 @@ func (r *RDNS) workerLoop() {
defer log.OnPanic("rdns") defer log.OnPanic("rdns")
for ip := range r.ipCh { for ip := range r.ipCh {
ttl := uint64(revDNSCacheTTL)
host, err := r.exchanger.Exchange(ip.AsSlice()) host, err := r.exchanger.Exchange(ip.AsSlice())
if err != nil { if err != nil {
log.Debug("rdns: resolving %q: %s", ip, err) log.Debug("rdns: resolving %q: %s", ip, err)
if errors.Is(err, dnsforward.ErrRDNSFailed) {
continue // Cache failure for a less time.
} else if host == "" { ttl = revDNSFailureCacheTTL
continue }
} }
r.cache(ip, ttl)
if host != "" {
_ = r.clients.AddHost(ip, host, ClientSourceRDNS) _ = r.clients.AddHost(ip, host, ClientSourceRDNS)
} }
} }
}

View file

@ -76,7 +76,7 @@ func TestRDNS_Begin(t *testing.T) {
ipCache := cache.New(cache.Config{ ipCache := cache.New(cache.Config{
EnableLRU: true, EnableLRU: true,
MaxCount: defaultRDNSCacheSize, MaxCount: revDNSCacheSize,
}) })
ttl := make([]byte, binary.Size(uint64(0))) ttl := make([]byte, binary.Size(uint64(0)))
binary.BigEndian.PutUint64(ttl, uint64(time.Now().Add(100*time.Hour).Unix())) binary.BigEndian.PutUint64(ttl, uint64(time.Now().Add(100*time.Hour).Unix()))
@ -153,7 +153,7 @@ func TestRDNS_ensurePrivateCache(t *testing.T) {
ipCache := cache.New(cache.Config{ ipCache := cache.New(cache.Config{
EnableLRU: true, EnableLRU: true,
MaxCount: defaultRDNSCacheSize, MaxCount: revDNSCacheSize,
}) })
ex := &rDNSExchanger{ ex := &rDNSExchanger{
@ -204,21 +204,25 @@ func TestRDNS_WorkerLoop(t *testing.T) {
cliIP netip.Addr cliIP netip.Addr
wantLog string wantLog string
name string name string
wantClientSource clientSource
}{{ }{{
ups: locUpstream, ups: locUpstream,
cliIP: localIP, cliIP: localIP,
wantLog: "", wantLog: "",
name: "all_good", name: "all_good",
wantClientSource: ClientSourceRDNS,
}, { }, {
ups: errUpstream, ups: errUpstream,
cliIP: netip.MustParseAddr("192.168.1.2"), cliIP: netip.MustParseAddr("192.168.1.2"),
wantLog: `rdns: resolving "192.168.1.2": test upstream error`, wantLog: `rdns: resolving "192.168.1.2": test upstream error`,
name: "resolve_error", name: "resolve_error",
wantClientSource: ClientSourceNone,
}, { }, {
ups: locUpstream, ups: locUpstream,
cliIP: netip.MustParseAddr("2a00:1450:400c:c06::93"), cliIP: netip.MustParseAddr("2a00:1450:400c:c06::93"),
wantLog: "", wantLog: "",
name: "ipv6_good", name: "ipv6_good",
wantClientSource: ClientSourceRDNS,
}} }}
for _, tc := range testCases { for _, tc := range testCases {
@ -237,6 +241,10 @@ func TestRDNS_WorkerLoop(t *testing.T) {
}, },
clients: cc, clients: cc,
ipCh: ch, ipCh: ch,
ipCache: cache.New(cache.Config{
EnableLRU: true,
MaxCount: revDNSCacheSize,
}),
} }
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
@ -253,11 +261,9 @@ func TestRDNS_WorkerLoop(t *testing.T) {
if tc.wantLog != "" { if tc.wantLog != "" {
assert.Contains(t, w.String(), tc.wantLog) assert.Contains(t, w.String(), tc.wantLog)
return
} }
assert.True(t, cc.exists(tc.cliIP, ClientSourceRDNS)) assert.Equal(t, tc.wantClientSource, cc.clientSource(tc.cliIP))
}) })
} }
} }

View file

@ -22,7 +22,7 @@ import (
) )
// currentSchemaVersion is the current schema version. // currentSchemaVersion is the current schema version.
const currentSchemaVersion = 14 const currentSchemaVersion = 16
// These aliases are provided for convenience. // These aliases are provided for convenience.
type ( type (
@ -87,6 +87,8 @@ func upgradeConfigSchema(oldVersion int, diskConf yobj) (err error) {
upgradeSchema11to12, upgradeSchema11to12,
upgradeSchema12to13, upgradeSchema12to13,
upgradeSchema13to14, upgradeSchema13to14,
upgradeSchema14to15,
upgradeSchema15to16,
} }
n := 0 n := 0
@ -802,6 +804,107 @@ func upgradeSchema13to14(diskConf yobj) (err error) {
return nil return nil
} }
// upgradeSchema14to15 performs the following changes:
//
// # BEFORE:
// 'dns':
// 'querylog_enabled': true
// 'querylog_file_enabled': true
// 'querylog_interval': '2160h'
// 'querylog_size_memory': 1000
//
// # AFTER:
// 'querylog':
// 'enabled': true
// 'file_enabled': true
// 'interval': '2160h'
// 'size_memory': 1000
// 'ignored': []
func upgradeSchema14to15(diskConf yobj) (err error) {
log.Printf("Upgrade yaml: 14 to 15")
diskConf["schema_version"] = 15
dnsVal, ok := diskConf["dns"]
if !ok {
return nil
}
dns, ok := dnsVal.(yobj)
if !ok {
return fmt.Errorf("unexpected type of dns: %T", dnsVal)
}
type temp struct {
from string
to string
val any
}
replaces := []temp{
{from: "querylog_enabled", to: "enabled", val: true},
{from: "querylog_file_enabled", to: "file_enabled", val: true},
{from: "querylog_interval", to: "interval", val: "2160h"},
{from: "querylog_size_memory", to: "size_memory", val: 1000},
}
qlog := map[string]any{
"ignored": []any{},
}
for _, r := range replaces {
v, has := dns[r.from]
if !has {
v = r.val
}
delete(dns, r.from)
qlog[r.to] = v
}
diskConf["querylog"] = qlog
return nil
}
// upgradeSchema15to16 performs the following changes:
//
// # BEFORE:
// 'dns':
// 'statistics_interval': 1
//
// # AFTER:
// 'statistics':
// 'enabled': true
// 'interval': 1
// 'ignored': []
func upgradeSchema15to16(diskConf yobj) (err error) {
log.Printf("Upgrade yaml: 15 to 16")
diskConf["schema_version"] = 16
dnsVal, ok := diskConf["dns"]
if !ok {
return nil
}
dns, ok := dnsVal.(yobj)
if !ok {
return fmt.Errorf("unexpected type of dns: %T", dnsVal)
}
stats := map[string]any{
"enabled": true,
"interval": 1,
"ignored": []any{},
}
k := "statistics_interval"
v, has := dns[k]
if has {
stats["enabled"] = v != 0
stats["interval"] = v
}
delete(dns, k)
diskConf["statistics"] = stats
return nil
}
// TODO(a.garipov): Replace with log.Output when we port it to our logging // TODO(a.garipov): Replace with log.Output when we port it to our logging
// package. // package.
func funcName() string { func funcName() string {

View file

@ -640,3 +640,110 @@ func TestUpgradeSchema13to14(t *testing.T) {
}) })
} }
} }
func TestUpgradeSchema14to15(t *testing.T) {
const newSchemaVer = 15
defaultWantObj := yobj{
"querylog": map[string]any{
"enabled": true,
"file_enabled": true,
"interval": "2160h",
"size_memory": 1000,
"ignored": []any{},
},
"dns": map[string]any{},
"schema_version": newSchemaVer,
}
testCases := []struct {
in yobj
want yobj
name string
}{{
in: yobj{
"dns": map[string]any{
"querylog_enabled": true,
"querylog_file_enabled": true,
"querylog_interval": "2160h",
"querylog_size_memory": 1000,
},
},
want: defaultWantObj,
name: "basic",
}, {
in: yobj{
"dns": map[string]any{},
},
want: defaultWantObj,
name: "default_values",
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
err := upgradeSchema14to15(tc.in)
require.NoError(t, err)
assert.Equal(t, tc.want, tc.in)
})
}
}
func TestUpgradeSchema15to16(t *testing.T) {
const newSchemaVer = 16
defaultWantObj := yobj{
"statistics": map[string]any{
"enabled": true,
"interval": 1,
"ignored": []any{},
},
"dns": map[string]any{},
"schema_version": newSchemaVer,
}
testCases := []struct {
in yobj
want yobj
name string
}{{
in: yobj{
"dns": map[string]any{
"statistics_interval": 1,
},
},
want: defaultWantObj,
name: "basic",
}, {
in: yobj{
"dns": map[string]any{},
},
want: defaultWantObj,
name: "default_values",
}, {
in: yobj{
"dns": map[string]any{
"statistics_interval": 0,
},
},
want: yobj{
"statistics": map[string]any{
"enabled": false,
"interval": 0,
"ignored": []any{},
},
"dns": map[string]any{},
"schema_version": newSchemaVer,
},
name: "stats_disabled",
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
err := upgradeSchema15to16(tc.in)
require.NoError(t, err)
assert.Equal(t, tc.want, tc.in)
})
}
}

View file

@ -15,8 +15,8 @@ import (
"github.com/AdguardTeam/golibs/log" "github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/netutil" "github.com/AdguardTeam/golibs/netutil"
"github.com/NYTimes/gziphandler" "github.com/NYTimes/gziphandler"
"github.com/lucas-clemente/quic-go" "github.com/quic-go/quic-go"
"github.com/lucas-clemente/quic-go/http3" "github.com/quic-go/quic-go/http3"
"golang.org/x/net/http2" "golang.org/x/net/http2"
"golang.org/x/net/http2/h2c" "golang.org/x/net/http2/h2c"
) )

View file

@ -44,6 +44,9 @@ func (l *queryLog) initWeb() {
} }
func (l *queryLog) handleQueryLog(w http.ResponseWriter, r *http.Request) { func (l *queryLog) handleQueryLog(w http.ResponseWriter, r *http.Request) {
l.lock.Lock()
defer l.lock.Unlock()
params, err := l.parseSearchParams(r) params, err := l.parseSearchParams(r)
if err != nil { if err != nil {
aghhttp.Error(r, w, http.StatusBadRequest, "failed to parse params: %s", err) aghhttp.Error(r, w, http.StatusBadRequest, "failed to parse params: %s", err)

View file

@ -247,3 +247,13 @@ func (l *queryLog) Add(params *AddParams) {
}() }()
} }
} }
// ShouldLog returns true if request for the host should be logged.
func (l *queryLog) ShouldLog(host string, _, _ uint16) bool {
return !l.isIgnored(host)
}
// isIgnored returns true if the host is in the Ignored list.
func (l *queryLog) isIgnored(host string) bool {
return l.conf.Ignored.Has(host)
}

View file

@ -10,6 +10,7 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/filtering" "github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/dnsproxy/proxyutil" "github.com/AdguardTeam/dnsproxy/proxyutil"
"github.com/AdguardTeam/golibs/stringutil"
"github.com/AdguardTeam/golibs/testutil" "github.com/AdguardTeam/golibs/testutil"
"github.com/AdguardTeam/golibs/timeutil" "github.com/AdguardTeam/golibs/timeutil"
"github.com/miekg/dns" "github.com/miekg/dns"
@ -249,6 +250,48 @@ func TestQueryLogFileDisabled(t *testing.T) {
assert.Equal(t, "example2.org", ll[1].QHost) assert.Equal(t, "example2.org", ll[1].QHost)
} }
func TestQueryLogShouldLog(t *testing.T) {
const (
ignored1 = "ignor.ed"
ignored2 = "ignored.to"
)
set := stringutil.NewSet(ignored1, ignored2)
l := newQueryLog(Config{
Enabled: true,
RotationIvl: timeutil.Day,
MemSize: 100,
BaseDir: t.TempDir(),
Ignored: set,
})
testCases := []struct {
name string
host string
wantLog bool
}{{
name: "log",
host: "example.com",
wantLog: true,
}, {
name: "no_log_ignored_1",
host: ignored1,
wantLog: false,
}, {
name: "no_log_ignored_2",
host: ignored2,
wantLog: false,
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
res := l.ShouldLog(tc.host, dns.TypeA, dns.ClassINET)
assert.Equal(t, tc.wantLog, res)
})
}
}
func addEntry(l *queryLog, host string, answerStr, client net.IP) { func addEntry(l *queryLog, host string, answerStr, client net.IP) {
q := dns.Msg{ q := dns.Msg{
Question: []dns.Question{{ Question: []dns.Question{{

View file

@ -10,6 +10,7 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/filtering" "github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/golibs/errors" "github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log" "github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/stringutil"
"github.com/AdguardTeam/golibs/timeutil" "github.com/AdguardTeam/golibs/timeutil"
"github.com/miekg/dns" "github.com/miekg/dns"
) )
@ -26,6 +27,9 @@ type QueryLog interface {
// WriteDiskConfig - write configuration // WriteDiskConfig - write configuration
WriteDiskConfig(c *Config) WriteDiskConfig(c *Config)
// ShouldLog returns true if request for the host should be logged.
ShouldLog(host string, qType, qClass uint16) bool
} }
// Config is the query log configuration structure. // Config is the query log configuration structure.
@ -71,6 +75,10 @@ type Config struct {
// AnonymizeClientIP tells if the query log should anonymize clients' IP // AnonymizeClientIP tells if the query log should anonymize clients' IP
// addresses. // addresses.
AnonymizeClientIP bool AnonymizeClientIP bool
// Ignored is the list of host names, which should not be written to
// log.
Ignored *stringutil.Set
} }
// AddParams is the parameters for adding an entry. // AddParams is the parameters for adding an entry.

View file

@ -155,6 +155,9 @@ func (l *queryLog) periodicRotate() {
// checkAndRotate rotates log files if those are older than the specified // checkAndRotate rotates log files if those are older than the specified
// rotation interval. // rotation interval.
func (l *queryLog) checkAndRotate() { func (l *queryLog) checkAndRotate() {
l.lock.Lock()
defer l.lock.Unlock()
oldest, err := l.readFileFirstTimeValue() oldest, err := l.readFileFirstTimeValue()
if err != nil && !errors.Is(err, os.ErrNotExist) { if err != nil && !errors.Is(err, os.ErrNotExist) {
log.Error("querylog: reading oldest record for rotation: %s", err) log.Error("querylog: reading oldest record for rotation: %s", err)

View file

@ -263,6 +263,10 @@ func (l *queryLog) readNextEntry(
e = &logEntry{} e = &logEntry{}
decodeLogEntry(e, line) decodeLogEntry(e, line)
if l.isIgnored(e.QHost) {
return nil, ts, nil
}
e.client, err = l.client(e.ClientID, e.IP.String(), cache) e.client, err = l.client(e.ClientID, e.IP.String(), cache)
if err != nil { if err != nil {
log.Error( log.Error(

View file

@ -5,7 +5,6 @@ package stats
import ( import (
"encoding/json" "encoding/json"
"net/http" "net/http"
"sync/atomic"
"time" "time"
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp" "github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
@ -41,10 +40,11 @@ type StatsResp struct {
// handleStats handles requests to the GET /control/stats endpoint. // handleStats handles requests to the GET /control/stats endpoint.
func (s *StatsCtx) handleStats(w http.ResponseWriter, r *http.Request) { func (s *StatsCtx) handleStats(w http.ResponseWriter, r *http.Request) {
limit := atomic.LoadUint32(&s.limitHours) s.lock.Lock()
defer s.lock.Unlock()
start := time.Now() start := time.Now()
resp, ok := s.getData(limit) resp, ok := s.getData(s.limitHours)
log.Debug("stats: prepared data in %v", time.Since(start)) log.Debug("stats: prepared data in %v", time.Since(start))
if !ok { if !ok {
@ -65,7 +65,13 @@ type configResp struct {
// handleStatsInfo handles requests to the GET /control/stats_info endpoint. // handleStatsInfo handles requests to the GET /control/stats_info endpoint.
func (s *StatsCtx) handleStatsInfo(w http.ResponseWriter, r *http.Request) { func (s *StatsCtx) handleStatsInfo(w http.ResponseWriter, r *http.Request) {
resp := configResp{IntervalDays: atomic.LoadUint32(&s.limitHours) / 24} s.lock.Lock()
defer s.lock.Unlock()
resp := configResp{IntervalDays: s.limitHours / 24}
if !s.enabled {
resp.IntervalDays = 0
}
_ = aghhttp.WriteJSONResponse(w, r, resp) _ = aghhttp.WriteJSONResponse(w, r, resp)
} }

View file

@ -15,16 +15,10 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp" "github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
"github.com/AdguardTeam/golibs/errors" "github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log" "github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/stringutil"
"go.etcd.io/bbolt" "go.etcd.io/bbolt"
) )
// DiskConfig is the configuration structure that is stored in file.
type DiskConfig struct {
// Interval is the number of days for which the statistics are collected
// before flushing to the database.
Interval uint32 `yaml:"statistics_interval"`
}
// checkInterval returns true if days is valid to be used as statistics // checkInterval returns true if days is valid to be used as statistics
// retention interval. The valid values are 0, 1, 7, 30 and 90. // retention interval. The valid values are 0, 1, 7, 30 and 90.
func checkInterval(days uint32) (ok bool) { func checkInterval(days uint32) (ok bool) {
@ -51,6 +45,12 @@ type Config struct {
// LimitDays is the maximum number of days to collect statistics into the // LimitDays is the maximum number of days to collect statistics into the
// current unit. // current unit.
LimitDays uint32 LimitDays uint32
// Enabled tells if the statistics are enabled.
Enabled bool
// Ignored is the list of host names, which should not be counted.
Ignored *stringutil.Set
} }
// Interface is the statistics interface to be used by other packages. // Interface is the statistics interface to be used by other packages.
@ -68,30 +68,22 @@ type Interface interface {
TopClientsIP(limit uint) []netip.Addr TopClientsIP(limit uint) []netip.Addr
// WriteDiskConfig puts the Interface's configuration to the dc. // WriteDiskConfig puts the Interface's configuration to the dc.
WriteDiskConfig(dc *DiskConfig) WriteDiskConfig(dc *Config)
// ShouldCount returns true if request for the host should be counted.
ShouldCount(host string, qType, qClass uint16) bool
} }
// StatsCtx collects the statistics and flushes it to the database. Its default // StatsCtx collects the statistics and flushes it to the database. Its default
// flushing interval is one hour. // flushing interval is one hour.
//
// TODO(e.burkov): Use atomic.Pointer for accessing db in go1.19.
type StatsCtx struct { type StatsCtx struct {
// limitHours is the maximum number of hours to collect statistics into the
// current unit.
//
// It is of type uint32 to be accessed by atomic. It's arranged at the
// beginning of the structure to keep 64-bit alignment.
limitHours uint32
// currMu protects curr. // currMu protects curr.
currMu *sync.RWMutex currMu *sync.RWMutex
// curr is the actual statistics collection result. // curr is the actual statistics collection result.
curr *unit curr *unit
// dbMu protects db.
dbMu *sync.Mutex
// db is the opened statistics database, if any. // db is the opened statistics database, if any.
db *bbolt.DB db atomic.Pointer[bbolt.DB]
// unitIDGen is the function that generates an identifier for the current // unitIDGen is the function that generates an identifier for the current
// unit. It's here for only testing purposes. // unit. It's here for only testing purposes.
@ -106,6 +98,21 @@ type StatsCtx struct {
// filename is the name of database file. // filename is the name of database file.
filename string filename string
// lock protects all the fields below.
lock sync.Mutex
// enabled tells if the statistics are enabled.
enabled bool
// limitHours is the maximum number of hours to collect statistics into the
// current unit.
//
// TODO(s.chzhen): Rewrite to use time.Duration.
limitHours uint32
// ignored is the list of host names, which should not be counted.
ignored *stringutil.Set
} }
// New creates s from conf and properly initializes it. Don't use s before // New creates s from conf and properly initializes it. Don't use s before
@ -114,11 +121,12 @@ func New(conf Config) (s *StatsCtx, err error) {
defer withRecovered(&err) defer withRecovered(&err)
s = &StatsCtx{ s = &StatsCtx{
enabled: conf.Enabled,
currMu: &sync.RWMutex{}, currMu: &sync.RWMutex{},
dbMu: &sync.Mutex{},
filename: conf.Filename, filename: conf.Filename,
configModified: conf.ConfigModified, configModified: conf.ConfigModified,
httpRegister: conf.HTTPRegister, httpRegister: conf.HTTPRegister,
ignored: conf.Ignored,
} }
if s.limitHours = conf.LimitDays * 24; !checkInterval(conf.LimitDays) { if s.limitHours = conf.LimitDays * 24; !checkInterval(conf.LimitDays) {
s.limitHours = 24 s.limitHours = 24
@ -137,7 +145,7 @@ func New(conf Config) (s *StatsCtx, err error) {
var udb *unitDB var udb *unitDB
id := s.unitIDGen() id := s.unitIDGen()
tx, err := s.db.Begin(true) tx, err := s.db.Load().Begin(true)
if err != nil { if err != nil {
return nil, fmt.Errorf("stats: opening a transaction: %w", err) return nil, fmt.Errorf("stats: opening a transaction: %w", err)
} }
@ -191,7 +199,7 @@ func (s *StatsCtx) Start() {
func (s *StatsCtx) Close() (err error) { func (s *StatsCtx) Close() (err error) {
defer func() { err = errors.Annotate(err, "stats: closing: %w") }() defer func() { err = errors.Annotate(err, "stats: closing: %w") }()
db := s.swapDatabase(nil) db := s.db.Swap(nil)
if db == nil { if db == nil {
return nil return nil
} }
@ -220,7 +228,10 @@ func (s *StatsCtx) Close() (err error) {
// Update implements the Interface interface for *StatsCtx. // Update implements the Interface interface for *StatsCtx.
func (s *StatsCtx) Update(e Entry) { func (s *StatsCtx) Update(e Entry) {
if atomic.LoadUint32(&s.limitHours) == 0 { s.lock.Lock()
defer s.lock.Unlock()
if !s.enabled || s.limitHours == 0 {
return return
} }
@ -248,14 +259,22 @@ func (s *StatsCtx) Update(e Entry) {
} }
// WriteDiskConfig implements the Interface interface for *StatsCtx. // WriteDiskConfig implements the Interface interface for *StatsCtx.
func (s *StatsCtx) WriteDiskConfig(dc *DiskConfig) { func (s *StatsCtx) WriteDiskConfig(dc *Config) {
dc.Interval = atomic.LoadUint32(&s.limitHours) / 24 s.lock.Lock()
defer s.lock.Unlock()
dc.LimitDays = s.limitHours / 24
dc.Enabled = s.enabled
dc.Ignored = s.ignored
} }
// TopClientsIP implements the [Interface] interface for *StatsCtx. // TopClientsIP implements the [Interface] interface for *StatsCtx.
func (s *StatsCtx) TopClientsIP(maxCount uint) (ips []netip.Addr) { func (s *StatsCtx) TopClientsIP(maxCount uint) (ips []netip.Addr) {
limit := atomic.LoadUint32(&s.limitHours) s.lock.Lock()
if limit == 0 { defer s.lock.Unlock()
limit := s.limitHours
if !s.enabled || limit == 0 {
return nil return nil
} }
@ -284,25 +303,6 @@ func (s *StatsCtx) TopClientsIP(maxCount uint) (ips []netip.Addr) {
return ips return ips
} }
// database returns the database if it's opened. It's safe for concurrent use.
func (s *StatsCtx) database() (db *bbolt.DB) {
s.dbMu.Lock()
defer s.dbMu.Unlock()
return s.db
}
// swapDatabase swaps the database with another one and returns it. It's safe
// for concurrent use.
func (s *StatsCtx) swapDatabase(with *bbolt.DB) (old *bbolt.DB) {
s.dbMu.Lock()
defer s.dbMu.Unlock()
old, s.db = s.db, with
return old
}
// deleteOldUnits walks the buckets available to tx and deletes old units. It // deleteOldUnits walks the buckets available to tx and deletes old units. It
// returns the number of deletions performed. // returns the number of deletions performed.
func deleteOldUnits(tx *bbolt.Tx, firstID uint32) (deleted int) { func deleteOldUnits(tx *bbolt.Tx, firstID uint32) (deleted int) {
@ -358,10 +358,7 @@ func (s *StatsCtx) openDB() (err error) {
// Use defer to unlock the mutex as soon as possible. // Use defer to unlock the mutex as soon as possible.
defer log.Debug("stats: database opened") defer log.Debug("stats: database opened")
s.dbMu.Lock() s.db.Store(db)
defer s.dbMu.Unlock()
s.db = db
return nil return nil
} }
@ -369,6 +366,9 @@ func (s *StatsCtx) openDB() (err error) {
func (s *StatsCtx) flush() (cont bool, sleepFor time.Duration) { func (s *StatsCtx) flush() (cont bool, sleepFor time.Duration) {
id := s.unitIDGen() id := s.unitIDGen()
s.lock.Lock()
defer s.lock.Unlock()
s.currMu.Lock() s.currMu.Lock()
defer s.currMu.Unlock() defer s.currMu.Unlock()
@ -377,12 +377,12 @@ func (s *StatsCtx) flush() (cont bool, sleepFor time.Duration) {
return false, 0 return false, 0
} }
limit := atomic.LoadUint32(&s.limitHours) limit := s.limitHours
if limit == 0 || ptr.id == id { if limit == 0 || ptr.id == id {
return true, time.Second return true, time.Second
} }
db := s.database() db := s.db.Load()
if db == nil { if db == nil {
return true, 0 return true, 0
} }
@ -437,21 +437,30 @@ func (s *StatsCtx) periodicFlush() {
} }
func (s *StatsCtx) setLimit(limitDays int) { func (s *StatsCtx) setLimit(limitDays int) {
atomic.StoreUint32(&s.limitHours, uint32(24*limitDays)) s.lock.Lock()
if limitDays == 0 { defer s.lock.Unlock()
if limitDays != 0 {
s.enabled = true
s.limitHours = uint32(24 * limitDays)
log.Debug("stats: set limit: %d days", limitDays)
return
}
s.enabled = false
log.Debug("stats: disabled")
if err := s.clear(); err != nil { if err := s.clear(); err != nil {
log.Error("stats: %s", err) log.Error("stats: %s", err)
} }
} }
log.Debug("stats: set limit: %d days", limitDays)
}
// Reset counters and clear database // Reset counters and clear database
func (s *StatsCtx) clear() (err error) { func (s *StatsCtx) clear() (err error) {
defer func() { err = errors.Annotate(err, "clearing: %w") }() defer func() { err = errors.Annotate(err, "clearing: %w") }()
db := s.swapDatabase(nil) db := s.db.Swap(nil)
if db != nil { if db != nil {
var tx *bbolt.Tx var tx *bbolt.Tx
tx, err = db.Begin(true) tx, err = db.Begin(true)
@ -495,7 +504,7 @@ func (s *StatsCtx) clear() (err error) {
} }
func (s *StatsCtx) loadUnits(limit uint32) (units []*unitDB, firstID uint32) { func (s *StatsCtx) loadUnits(limit uint32) (units []*unitDB, firstID uint32) {
db := s.database() db := s.db.Load()
if db == nil { if db == nil {
return nil, 0 return nil, 0
} }
@ -547,3 +556,13 @@ func (s *StatsCtx) loadUnits(limit uint32) (units []*unitDB, firstID uint32) {
return units, firstID return units, firstID
} }
// ShouldCount returns true if request for the host should be counted.
func (s *StatsCtx) ShouldCount(host string, _, _ uint16) bool {
return !s.isIgnored(host)
}
// isIgnored returns true if the host is in the Ignored list.
func (s *StatsCtx) isIgnored(host string) bool {
return s.ignored.Has(host)
}

View file

@ -47,8 +47,6 @@ func TestStats_races(t *testing.T) {
startTime := time.Now() startTime := time.Now()
testutil.CleanupAndRequireSuccess(t, s.Close) testutil.CleanupAndRequireSuccess(t, s.Close)
type signal = struct{}
writeFunc := func(start, fin *sync.WaitGroup, waitCh <-chan unit, i int) { writeFunc := func(start, fin *sync.WaitGroup, waitCh <-chan unit, i int) {
e := Entry{ e := Entry{
Domain: fmt.Sprintf("example-%d.org", i), Domain: fmt.Sprintf("example-%d.org", i),

View file

@ -53,6 +53,7 @@ func TestStats(t *testing.T) {
conf := stats.Config{ conf := stats.Config{
Filename: filepath.Join(t.TempDir(), "stats.db"), Filename: filepath.Join(t.TempDir(), "stats.db"),
LimitDays: 1, LimitDays: 1,
Enabled: true,
UnitID: constUnitID, UnitID: constUnitID,
HTTPRegister: func(_, url string, handler http.HandlerFunc) { HTTPRegister: func(_, url string, handler http.HandlerFunc) {
handlers[url] = handler handlers[url] = handler
@ -158,6 +159,7 @@ func TestLargeNumbers(t *testing.T) {
conf := stats.Config{ conf := stats.Config{
Filename: filepath.Join(t.TempDir(), "stats.db"), Filename: filepath.Join(t.TempDir(), "stats.db"),
LimitDays: 1, LimitDays: 1,
Enabled: true,
UnitID: func() (id uint32) { return atomic.LoadUint32(&curHour) }, UnitID: func() (id uint32) { return atomic.LoadUint32(&curHour) },
HTTPRegister: func(_, url string, handler http.HandlerFunc) { handlers[url] = handler }, HTTPRegister: func(_, url string, handler http.HandlerFunc) { handlers[url] = handler },
} }

View file

@ -10,6 +10,7 @@ import (
"github.com/AdguardTeam/golibs/errors" "github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log" "github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/stringutil"
"go.etcd.io/bbolt" "go.etcd.io/bbolt"
) )
@ -341,13 +342,15 @@ type pairsGetter func(u *unitDB) (pairs []countPair)
// topsCollector collects statistics about highest values from the given *unitDB // topsCollector collects statistics about highest values from the given *unitDB
// slice using pg to retrieve data. // slice using pg to retrieve data.
func topsCollector(units []*unitDB, max int, pg pairsGetter) []map[string]uint64 { func topsCollector(units []*unitDB, max int, ignored *stringutil.Set, pg pairsGetter) []map[string]uint64 {
m := map[string]uint64{} m := map[string]uint64{}
for _, u := range units { for _, u := range units {
for _, cp := range pg(u) { for _, cp := range pg(u) {
if !ignored.Has(cp.Name) {
m[cp.Name] += cp.Count m[cp.Name] += cp.Count
} }
} }
}
a2 := convertMapToSlice(m, max) a2 := convertMapToSlice(m, max)
return convertTopSlice(a2) return convertTopSlice(a2)
@ -408,9 +411,9 @@ func (s *StatsCtx) getData(limit uint32) (StatsResp, bool) {
BlockedFiltering: statsCollector(units, firstID, timeUnit, func(u *unitDB) (num uint64) { return u.NResult[RFiltered] }), BlockedFiltering: statsCollector(units, firstID, timeUnit, func(u *unitDB) (num uint64) { return u.NResult[RFiltered] }),
ReplacedSafebrowsing: statsCollector(units, firstID, timeUnit, func(u *unitDB) (num uint64) { return u.NResult[RSafeBrowsing] }), ReplacedSafebrowsing: statsCollector(units, firstID, timeUnit, func(u *unitDB) (num uint64) { return u.NResult[RSafeBrowsing] }),
ReplacedParental: statsCollector(units, firstID, timeUnit, func(u *unitDB) (num uint64) { return u.NResult[RParental] }), ReplacedParental: statsCollector(units, firstID, timeUnit, func(u *unitDB) (num uint64) { return u.NResult[RParental] }),
TopQueried: topsCollector(units, maxDomains, func(u *unitDB) (pairs []countPair) { return u.Domains }), TopQueried: topsCollector(units, maxDomains, s.ignored, func(u *unitDB) (pairs []countPair) { return u.Domains }),
TopBlocked: topsCollector(units, maxDomains, func(u *unitDB) (pairs []countPair) { return u.BlockedDomains }), TopBlocked: topsCollector(units, maxDomains, s.ignored, func(u *unitDB) (pairs []countPair) { return u.BlockedDomains }),
TopClients: topsCollector(units, maxClients, func(u *unitDB) (pairs []countPair) { return u.Clients }), TopClients: topsCollector(units, maxClients, nil, func(u *unitDB) (pairs []countPair) { return u.Clients }),
} }
// Total counters: // Total counters:

View file

@ -1,6 +1,6 @@
module github.com/AdguardTeam/AdGuardHome/internal/tools module github.com/AdguardTeam/AdGuardHome/internal/tools
go 1.18 go 1.19
require ( require (
github.com/fzipp/gocyclo v0.6.0 github.com/fzipp/gocyclo v0.6.0
@ -8,10 +8,10 @@ require (
github.com/gordonklaus/ineffassign v0.0.0-20230107090616-13ace0543b28 github.com/gordonklaus/ineffassign v0.0.0-20230107090616-13ace0543b28
github.com/kisielk/errcheck v1.6.3 github.com/kisielk/errcheck v1.6.3
github.com/kyoh86/looppointer v0.2.1 github.com/kyoh86/looppointer v0.2.1
github.com/securego/gosec/v2 v2.14.0 github.com/securego/gosec/v2 v2.15.0
golang.org/x/tools v0.5.1-0.20230117180257-8aba49bb5ea2 golang.org/x/tools v0.6.0
golang.org/x/vuln v0.0.0-20230130175424-dd534eeddf33 golang.org/x/vuln v0.0.0-20230213165600-1a019b0c7f30
honnef.co/go/tools v0.3.3 honnef.co/go/tools v0.4.1
mvdan.cc/gofumpt v0.4.0 mvdan.cc/gofumpt v0.4.0
mvdan.cc/unparam v0.0.0-20230125043941-70a0ce6e7b95 mvdan.cc/unparam v0.0.0-20230125043941-70a0ce6e7b95
) )
@ -24,10 +24,10 @@ require (
github.com/kyoh86/nolint v0.0.1 // indirect github.com/kyoh86/nolint v0.0.1 // indirect
github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354 // indirect github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
golang.org/x/exp v0.0.0-20230131160201-f062dba9d201 // indirect golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb // indirect
golang.org/x/exp/typeparams v0.0.0-20230131160201-f062dba9d201 // indirect golang.org/x/exp/typeparams v0.0.0-20230213192124-5e25df0256eb // indirect
golang.org/x/mod v0.7.0 // indirect golang.org/x/mod v0.8.0 // indirect
golang.org/x/sync v0.1.0 // indirect golang.org/x/sync v0.1.0 // indirect
golang.org/x/sys v0.4.0 // indirect golang.org/x/sys v0.5.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
) )

View file

@ -7,6 +7,7 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
github.com/fzipp/gocyclo v0.6.0 h1:lsblElZG7d3ALtGMx9fmxeTKZaLLpU8mET09yN4BBLo= github.com/fzipp/gocyclo v0.6.0 h1:lsblElZG7d3ALtGMx9fmxeTKZaLLpU8mET09yN4BBLo=
github.com/fzipp/gocyclo v0.6.0/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlyaLkLoA= github.com/fzipp/gocyclo v0.6.0/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlyaLkLoA=
github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
github.com/golangci/misspell v0.4.0 h1:KtVB/hTK4bbL/S6bs64rYyk8adjmh1BygbBiaAiX+a0= github.com/golangci/misspell v0.4.0 h1:KtVB/hTK4bbL/S6bs64rYyk8adjmh1BygbBiaAiX+a0=
github.com/golangci/misspell v0.4.0/go.mod h1:W6O/bwV6lGDxUCChm2ykw9NQdd5bYd1Xkjo88UcWyJc= github.com/golangci/misspell v0.4.0/go.mod h1:W6O/bwV6lGDxUCChm2ykw9NQdd5bYd1Xkjo88UcWyJc=
github.com/google/go-cmdtest v0.4.1-0.20220921163831-55ab3332a786 h1:rcv+Ippz6RAtvaGgKxc+8FQIpxHgsF+HBzPyYL2cyVU= github.com/google/go-cmdtest v0.4.1-0.20220921163831-55ab3332a786 h1:rcv+Ippz6RAtvaGgKxc+8FQIpxHgsF+HBzPyYL2cyVU=
@ -29,14 +30,14 @@ github.com/kyoh86/nolint v0.0.1 h1:GjNxDEkVn2wAxKHtP7iNTrRxytRZ1wXxLV5j4XzGfRU=
github.com/kyoh86/nolint v0.0.1/go.mod h1:1ZiZZ7qqrZ9dZegU96phwVcdQOMKIqRzFJL3ewq9gtI= github.com/kyoh86/nolint v0.0.1/go.mod h1:1ZiZZ7qqrZ9dZegU96phwVcdQOMKIqRzFJL3ewq9gtI=
github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354 h1:4kuARK6Y6FxaNu/BnU2OAaLF86eTVhP2hjTB6iMvItA= github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354 h1:4kuARK6Y6FxaNu/BnU2OAaLF86eTVhP2hjTB6iMvItA=
github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354/go.mod h1:KSVJerMDfblTH7p5MZaTt+8zaT2iEk3AkVb9PQdZuE8= github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354/go.mod h1:KSVJerMDfblTH7p5MZaTt+8zaT2iEk3AkVb9PQdZuE8=
github.com/onsi/ginkgo/v2 v2.3.1 h1:8SbseP7qM32WcvE6VaN6vfXxv698izmsJ1UQX9ve7T8= github.com/onsi/ginkgo/v2 v2.8.0 h1:pAM+oBNPrpXRs+E/8spkeGx9QgekbRVyr74EUvRVOUI=
github.com/onsi/gomega v1.22.1 h1:pY8O4lBfsHKZHM/6nrxkhVPUznOlIu3quZcKP/M20KI= github.com/onsi/gomega v1.26.0 h1:03cDLK28U6hWvCAns6NeydX3zIm4SF3ci69ulidS32Q=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e h1:aoZm08cpOy4WuID//EZDgcC4zIxODThtZNPirFr42+A= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e h1:aoZm08cpOy4WuID//EZDgcC4zIxODThtZNPirFr42+A=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 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.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/securego/gosec/v2 v2.14.0 h1:U1hfs0oBackChXA72plCYVA4cOlQ4gO+209dHiSNZbI= github.com/securego/gosec/v2 v2.15.0 h1:v4Ym7FF58/jlykYmmhZ7mTm7FQvN/setNm++0fgIAtw=
github.com/securego/gosec/v2 v2.14.0/go.mod h1:Ff03zEi5NwSOfXj9nFpBfhbWTtROCkg9N+9goggrYn4= github.com/securego/gosec/v2 v2.15.0/go.mod h1:VOjTrZOkUtSDt2QLSJmQBMWnvwiQPEjg0l+5juIqGk8=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/testify v1.1.4/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.1.4/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
@ -53,22 +54,22 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/exp v0.0.0-20230131160201-f062dba9d201 h1:BEABXpNXLEz0WxtA+6CQIz2xkg80e+1zrhWyMcq8VzE= golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb h1:PaBZQdo+iSDyHT053FjUCgZQ/9uqVwPOcl7KSWhKn6w=
golang.org/x/exp v0.0.0-20230131160201-f062dba9d201/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/exp/typeparams v0.0.0-20230131160201-f062dba9d201 h1:O1QcdQUR9htWjzzsXVFPX+RJ3n1P/u/5bsQR8dbs5BY= golang.org/x/exp/typeparams v0.0.0-20230213192124-5e25df0256eb h1:WGs/bGIWYyAY5PVgGGMXqGGCxSJz4fpoUExb/vgqNCU=
golang.org/x/exp/typeparams v0.0.0-20230131160201-f062dba9d201/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/exp/typeparams v0.0.0-20230213192124-5e25df0256eb/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA= golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 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-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw= golang.org/x/net v0.6.0 h1:L4ZwwTvKW9gr0ZMS1yrHD9GZhIuVjOBBnaKH+SPQK0Q=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/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.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -83,35 +84,33 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18= golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 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.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY= golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 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.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20201007032633-0806396f153e/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= golang.org/x/tools v0.0.0-20201007032633-0806396f153e/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
golang.org/x/tools v0.5.1-0.20230117180257-8aba49bb5ea2 h1:v0FhRDmSCNH/0EurAT6T8KRY4aNuUhz6/WwBMxG+gvQ= golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
golang.org/x/tools v0.5.1-0.20230117180257-8aba49bb5ea2/go.mod h1:N+Kgy78s5I24c24dU8OfWNEotWjutIs8SnJvn5IDq+k= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/vuln v0.0.0-20230130175424-dd534eeddf33 h1:je2aB5nnlseeGvJy5clg6EyC3jjbbCNsRDroC3qQJsA= golang.org/x/vuln v0.0.0-20230213165600-1a019b0c7f30 h1:Q4B8LhSjZGto/+P5REBb4N51ec2H4efRqjBIeJ6nv/Y=
golang.org/x/vuln v0.0.0-20230130175424-dd534eeddf33/go.mod h1:cBP4HMKv0X+x96j8IJWCKk0eqpakBmmHjKGSSC0NaYE= golang.org/x/vuln v0.0.0-20230213165600-1a019b0c7f30/go.mod h1:cBP4HMKv0X+x96j8IJWCKk0eqpakBmmHjKGSSC0NaYE=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 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-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.3.3 h1:oDx7VAwstgpYpb3wv0oxiZlxY+foCpRAwY7Vk6XpAgA= honnef.co/go/tools v0.4.1 h1:HPeloSr0mLOEMOkhT9Au5aeki44kvP6ka3v1xIsM6Zo=
honnef.co/go/tools v0.3.3/go.mod h1:jzwdWgg7Jdq75wlfblQxO4neNaFFSvgc1tD5Wv8U0Yw= honnef.co/go/tools v0.4.1/go.mod h1:36ZgoUOrqOk1GxwHhyryEkq8FQWkUO2xGuSMhUCcdvA=
mvdan.cc/gofumpt v0.4.0 h1:JVf4NN1mIpHogBj7ABpgOyZc65/UUOkKQFkoURsz4MM= mvdan.cc/gofumpt v0.4.0 h1:JVf4NN1mIpHogBj7ABpgOyZc65/UUOkKQFkoURsz4MM=
mvdan.cc/gofumpt v0.4.0/go.mod h1:PljLOHDeZqgS8opHRKLzp2It2VBuSdteAgqUfzMTxlQ= mvdan.cc/gofumpt v0.4.0/go.mod h1:PljLOHDeZqgS8opHRKLzp2It2VBuSdteAgqUfzMTxlQ=
mvdan.cc/unparam v0.0.0-20230125043941-70a0ce6e7b95 h1:n/xhncJPSt0YzfOhnyn41XxUdrWQNgmLBG72FE27Fqw= mvdan.cc/unparam v0.0.0-20230125043941-70a0ce6e7b95 h1:n/xhncJPSt0YzfOhnyn41XxUdrWQNgmLBG72FE27Fqw=

View file

@ -205,7 +205,7 @@ code from [the repo][companiesrepo].
### Usage ### Usage
```sh ```sh
( cd scripts/companiesdb && sh ./download.sh ) sh ./scripts/companiesdb/download.sh
``` ```
[companiesrepo]: https://github.com/AdguardTeam/companiesdb [companiesrepo]: https://github.com/AdguardTeam/companiesdb

View file

@ -4,11 +4,9 @@ set -e -f -u -x
# This script syncs companies DB that we bundle with AdGuard Home. The source # This script syncs companies DB that we bundle with AdGuard Home. The source
# for this database is https://github.com/AdguardTeam/companiesdb. # for this database is https://github.com/AdguardTeam/companiesdb.
#
trackers_url='https://raw.githubusercontent.com/AdguardTeam/companiesdb/main/dist/trackers.json'
output='./client/src/helpers/trackers/trackers.json'
readonly trackers_url output
whotracksme='https://raw.githubusercontent.com/AdguardTeam/companiesdb/main/dist/whotracksme.json' curl -o "$output" -v "$trackers_url"
adguard='https://raw.githubusercontent.com/AdguardTeam/companiesdb/main/dist/adguard.json'
base_path='../../client/src/helpers/trackers'
readonly whotracksme adguard base_path
curl -o "${base_path}/whotracksme.json" -v "$whotracksme"
curl -o "${base_path}/adguard.json" -v "$adguard"

View file

@ -338,6 +338,18 @@ download_wget() {
wget --no-verbose -O "$wget_output" "$1" wget --no-verbose -O "$wget_output" "$1"
} }
# download_fetch uses fetch(1) to download a file. The first argument is the
# URL. The second argument is optional and is the output file.
download_fetch() {
fetch_output="${2:-}"
if [ "$fetch_output" = '' ]
then
fetch -o '-' "$1"
else
fetch -o "$fetch_output" "$1"
fi
}
# Function set_download_func sets the appropriate function for downloading # Function set_download_func sets the appropriate function for downloading
# files. # files.
set_download_func() { set_download_func() {
@ -348,6 +360,9 @@ set_download_func() {
elif is_command 'wget' elif is_command 'wget'
then then
download_func='download_wget' download_func='download_wget'
elif is_command 'fetch'
then
download_func='download_fetch'
else else
error_exit "either curl or wget is required to install AdGuard Home via this script" error_exit "either curl or wget is required to install AdGuard Home via this script"
fi fi

View file

@ -227,8 +227,9 @@ gocyclo --over 13 ./internal/dhcpd ./internal/filtering/ ./internal/home/
# Apply stricter standards to new or somewhat refactored code. # Apply stricter standards to new or somewhat refactored code.
gocyclo --over 10 ./internal/aghio/ ./internal/aghnet/ ./internal/aghos/\ gocyclo --over 10 ./internal/aghio/ ./internal/aghnet/ ./internal/aghos/\
./internal/aghtest/ ./internal/dnsforward/ ./internal/filtering/rewrite/\ ./internal/aghtest/ ./internal/dnsforward/ ./internal/filtering/rewrite/\
./internal/stats/ ./internal/tools/ ./internal/updater/ ./internal/version/\ ./internal/stats/ ./internal/tools/ ./internal/updater/\
./scripts/vetted-filters/ ./main.go ./internal/version/ ./scripts/blocked-services/ ./scripts/vetted-filters/\
./main.go
ineffassign ./... ineffassign ./...

View file

@ -40,7 +40,7 @@ readonly go
count_flags='--count=1' count_flags='--count=1'
cover_flags='--coverprofile=./coverage.txt' cover_flags='--coverprofile=./coverage.txt'
shuffle_flags='--shuffle=on' shuffle_flags='--shuffle=on'
timeout_flags="${TIMEOUT_FLAGS:---timeout=30s}" timeout_flags="${TIMEOUT_FLAGS:---timeout=90s}"
readonly count_flags cover_flags shuffle_flags timeout_flags readonly count_flags cover_flags shuffle_flags timeout_flags
"$go" test "$count_flags" "$cover_flags" "$race_flags" "$shuffle_flags" "$timeout_flags"\ "$go" test "$count_flags" "$cover_flags" "$race_flags" "$shuffle_flags" "$timeout_flags"\