mirror of
https://github.com/AdguardTeam/AdGuardHome.git
synced 2024-11-15 09:58:42 -07:00
all: sync with master; upd chlog
This commit is contained in:
parent
80eb339896
commit
66b831072c
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@ -1,7 +1,7 @@
|
||||
'name': 'build'
|
||||
|
||||
'env':
|
||||
'GO_VERSION': '1.18.9'
|
||||
'GO_VERSION': '1.19.6'
|
||||
'NODE_VERSION': '14'
|
||||
|
||||
'on':
|
||||
|
2
.github/workflows/lint.yml
vendored
2
.github/workflows/lint.yml
vendored
@ -1,7 +1,7 @@
|
||||
'name': 'lint'
|
||||
|
||||
'env':
|
||||
'GO_VERSION': '1.18.9'
|
||||
'GO_VERSION': '1.19.6'
|
||||
|
||||
'on':
|
||||
'push':
|
||||
|
106
CHANGELOG.md
106
CHANGELOG.md
@ -14,11 +14,11 @@ and this project adheres to
|
||||
<!--
|
||||
## [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.
|
||||
-->
|
||||
@ -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
|
||||
|
||||
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
|
||||
`schema_version` back to `11`.
|
||||
|
||||
- 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:
|
||||
|
||||
@ -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
|
||||
[v0.107.24]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.23...v0.107.24
|
||||
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.25...HEAD
|
||||
[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.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
|
||||
|
16
README.md
16
README.md
@ -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>
|
||||
|
||||
Run the following command in your terminal:
|
||||
To install with `curl` run the following command:
|
||||
|
||||
```sh
|
||||
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:
|
||||
|
||||
* `-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:
|
||||
|
||||
* [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;
|
||||
* [npm](https://www.npmjs.com/) v6.14 or later;
|
||||
* [yarn](https://yarnpkg.com/) v1.22.5 or later.
|
||||
|
@ -7,7 +7,7 @@
|
||||
# Make sure to sync any changes with the branch overrides below.
|
||||
'variables':
|
||||
'channel': 'edge'
|
||||
'dockerGo': 'adguard/golang-ubuntu:5.4'
|
||||
'dockerGo': 'adguard/golang-ubuntu:6.1'
|
||||
|
||||
'stages':
|
||||
- 'Build frontend':
|
||||
@ -223,6 +223,7 @@
|
||||
|
||||
channel="${bamboo.channel}"
|
||||
readonly channel
|
||||
|
||||
case "$channel"
|
||||
in
|
||||
('release')
|
||||
@ -269,8 +270,10 @@
|
||||
|
||||
set -e -f -u -x
|
||||
|
||||
export CHANNEL="${bamboo.channel}"
|
||||
if [ "$CHANNEL" != 'release' ] && [ "${CHANNEL}" != 'beta' ]
|
||||
channel="${bamboo.channel}"
|
||||
readonly channel
|
||||
|
||||
if [ "$channel" != 'release' ] && [ "${channel}" != 'beta' ]
|
||||
then
|
||||
echo "don't publish to GitHub Releases for this channel"
|
||||
|
||||
@ -323,7 +326,7 @@
|
||||
# need to build a few of these.
|
||||
'variables':
|
||||
'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
|
||||
# is built.
|
||||
- '^release-v[0-9]+\.[0-9]+\.[0-9]+':
|
||||
@ -338,4 +341,4 @@
|
||||
# are the ones that actually get released.
|
||||
'variables':
|
||||
'channel': 'release'
|
||||
'dockerGo': 'adguard/golang-ubuntu:5.4'
|
||||
'dockerGo': 'adguard/golang-ubuntu:6.1'
|
||||
|
@ -5,7 +5,7 @@
|
||||
'key': 'AHBRTSPECS'
|
||||
'name': 'AdGuard Home - Build and run tests'
|
||||
'variables':
|
||||
'dockerGo': 'adguard/golang-ubuntu:5.4'
|
||||
'dockerGo': 'adguard/golang-ubuntu:6.1'
|
||||
|
||||
'stages':
|
||||
- 'Tests':
|
||||
|
@ -261,7 +261,7 @@
|
||||
"query_log_configuration": "Налада часопіса",
|
||||
"query_log_disabled": "Часопіс запытаў выключаны, яго можна ўключыць у <0>наладах</0>",
|
||||
"query_log_strict_search": "Ужывайце падвойныя двукоссі для строгага пошуку",
|
||||
"query_log_retention_confirm": "Вы ўпэўнены, што хочаце змяніць тэрмін захоўвання запытаў? Пры скарачэнні інтэрвалу дадзеныя могуць быць згублены",
|
||||
"query_log_retention_confirm": "Вы ўпэўнены, што хочаце змяніць тэрмін захоўвання запытаў? Пры памяншэнні інтэрвалу, некаторыя даныя могуць быць страчаны",
|
||||
"anonymize_client_ip": "Ананімізацыя IP-адрасы кліента",
|
||||
"anonymize_client_ip_desc": "Не захоўвайце поўныя IP-адрасы гэтых удзельнікаў у часопісах або статыстыцы",
|
||||
"dns_config": "Налады DNS-сервера",
|
||||
@ -356,7 +356,7 @@
|
||||
"install_devices_android_list_5": "Зараз можна змяніць палі «DNS 1» і «DNS 2». Увядзіце ў іх адрасы AdGuard Home.",
|
||||
"install_devices_ios_list_1": "Увайдзіце ў меню налад прылады.",
|
||||
"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.",
|
||||
"get_started": "Паехалі",
|
||||
"next": "Далей",
|
||||
@ -517,10 +517,10 @@
|
||||
"filter_updated": "Спіс паспяхова абноўлены",
|
||||
"statistics_configuration": "Канфігурацыя статыстыкі",
|
||||
"statistics_retention": "Захаванне статыстыкі",
|
||||
"statistics_retention_desc": "Калі вы зменшыце значэнне інтэрвалу, некаторыя дадзеныя могуць быць згублены",
|
||||
"statistics_retention_desc": "Калі вы паменшыце значэнне інтэрвалу, некаторыя даныя могуць быць страчаны",
|
||||
"statistics_clear": "Ачысціць статыстыку",
|
||||
"statistics_clear_confirm": "Вы ўпэўнены, што хочаце ачысціць статыстыку?",
|
||||
"statistics_retention_confirm": "Вы ўпэўнены, што хочаце змяніць тэрмін захоўвання статыстыкі? Пры скарачэнні інтэрвалу дадзеныя могуць быць згублены",
|
||||
"statistics_retention_confirm": "Вы ўпэўнены, што хочаце змяніць тэрмін захоўвання статыстыкі? Пры памяншэнні інтэрвалу, некаторыя даныя могуць быць страчаны",
|
||||
"statistics_cleared": "Статыстыка паспяхова вычышчана",
|
||||
"statistics_enable": "Уключыць статыстыку",
|
||||
"interval_hours": "{{count}} гадзіна",
|
||||
@ -598,7 +598,7 @@
|
||||
"show_blocked_responses": "Заблакавана",
|
||||
"show_whitelisted_responses": "Белы спіс",
|
||||
"show_processed_responses": "Апрацавана",
|
||||
"blocked_safebrowsing": "Заблакавана згодна базе дадзеных Safe Browsing",
|
||||
"blocked_safebrowsing": "Заблакіравана згодна з базай даных Safe Browsing",
|
||||
"blocked_adult_websites": "Заблакавана Бацькоўскім кантролем",
|
||||
"blocked_threats": "Заблакавана пагроз",
|
||||
"allowed": "Дазволены",
|
||||
@ -639,7 +639,7 @@
|
||||
"safe_browsing": "Бяспечны інтэрнэт",
|
||||
"served_from_cache": "{{value}} <i>(атрымана з кэша)</i>",
|
||||
"form_error_password_length": "Пароль павінен быць не менш за {{value}} сімвалаў",
|
||||
"anonymizer_notification": "<0>Заўвага:</0> Ананімізацыя IP уключана. Вы можаце адключыць яго ў <1>Агульных наладах</1> .",
|
||||
"anonymizer_notification": "<0>Заўвага:</0> Ананімізацыя IP уключана. Вы можаце адключыць яе ў <1>Агульных наладах</1>.",
|
||||
"confirm_dns_cache_clear": "Вы ўпэўнены, што хочаце ачысціць кэш DNS?",
|
||||
"cache_cleared": "Кэш DNS паспяхова ачышчаны",
|
||||
"clear_cache": "Ачысціць кэш"
|
||||
|
@ -454,7 +454,7 @@
|
||||
"updates_checked": "La nueva versión de AdGuard Home está disponible",
|
||||
"updates_version_equal": "AdGuard Home está actualizado",
|
||||
"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",
|
||||
"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>.",
|
||||
@ -640,7 +640,7 @@
|
||||
"served_from_cache": "{{value}} <i>(servido desde la caché)</i>",
|
||||
"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>.",
|
||||
"confirm_dns_cache_clear": "¿Estás seguro de que deseas borrar la caché de DNS?",
|
||||
"cache_cleared": "Caché DNS borrado con éxito",
|
||||
"confirm_dns_cache_clear": "¿Estás seguro de que deseas borrar la caché DNS?",
|
||||
"cache_cleared": "Caché DNS borrado correctamente",
|
||||
"clear_cache": "Borrar caché"
|
||||
}
|
||||
|
@ -24,7 +24,7 @@
|
||||
"unavailable_dhcp": "DHCP ei ole käytettävissä",
|
||||
"unavailable_dhcp_desc": "AdGuard Home ei voi suorittaa DHCP-palvelinta käyttöjärjestelmässäsi",
|
||||
"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_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.",
|
||||
|
@ -16,7 +16,7 @@
|
||||
"resolve_clients_title": "启用客户端的 IP 地址的反向解析",
|
||||
"resolve_clients_desc": "通过发送 PTR 查询到对应的解析器 (本地客户端的私人 DNS 服务器,公共 IP 客户端的上游服务器) 将 IP 地址反向解析成其客户端主机名。",
|
||||
"use_private_ptr_resolvers_title": "使用私人反向 DNS 解析器",
|
||||
"use_private_ptr_resolvers_desc": "使用这些上游服务器对本地服务的地址执行反向 DNS 查找。 如果禁用,则 AdGuard Home会以 NXDOMAIN 响应所有此类PTR请求,从 DHCP、/ etc / hosts 等获知的客户端除外。",
|
||||
"use_private_ptr_resolvers_desc": "使用这些上游服务器对本地服务的地址执行反向 DNS 查找。 如果禁用,则 AdGuard Home 会以 NXDOMAIN 响应所有此类 PTR 请求,从 DHCP、/etc/hosts 等获知的客户端除外。",
|
||||
"check_dhcp_servers": "检查 DHCP 服务器",
|
||||
"save_config": "保存配置",
|
||||
"enabled_dhcp": "DHCP 服务器已启用",
|
||||
@ -128,7 +128,7 @@
|
||||
"number_of_dns_query_days": "过去 {{count}} 天内处理的 DNS 查询总数",
|
||||
"number_of_dns_query_days_plural": "在过去的 {{count}} 天内处理了多少个 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_adult": "被阻止的成人网站总数",
|
||||
"enforced_save_search": "强制安全搜索",
|
||||
@ -146,10 +146,10 @@
|
||||
"no_servers_specified": "未找到指定的服务器",
|
||||
"general_settings": "常规设置",
|
||||
"dns_settings": "DNS 设置",
|
||||
"dns_blocklists": "DNS 拦截列表",
|
||||
"dns_allowlists": "DNS 允许列表",
|
||||
"dns_blocklists": "DNS 黑名单",
|
||||
"dns_allowlists": "DNS 白名单",
|
||||
"dns_blocklists_desc": "AdGuard Home将阻止匹配DNS拦截清单的域名",
|
||||
"dns_allowlists_desc": "来自DNS允许列表的域将被允许,即使它们位于任意阻止列表中也是如此",
|
||||
"dns_allowlists_desc": "来自 DNS 白名单的域名将被允许,即使它们位于任意黑名单中也是如此。",
|
||||
"custom_filtering_rules": "自定义过滤规则",
|
||||
"encryption_settings": "加密设置",
|
||||
"dhcp_settings": "DHCP 设置",
|
||||
@ -178,22 +178,22 @@
|
||||
"delete_table_action": "删除",
|
||||
"elapsed": "耗时",
|
||||
"filters_and_hosts_hint": "AdGuard Home 可以解析基础的 adblock 规则和 Hosts 语法。",
|
||||
"no_blocklist_added": "未添加阻止列表",
|
||||
"no_whitelist_added": "未添加允许列表",
|
||||
"add_blocklist": "添加阻止列表",
|
||||
"add_allowlist": "添加允许列表",
|
||||
"no_blocklist_added": "未添加黑名单",
|
||||
"no_whitelist_added": "未添加白名单",
|
||||
"add_blocklist": "添加黑名单",
|
||||
"add_allowlist": "添加白名单",
|
||||
"cancel_btn": "取消",
|
||||
"enter_name_hint": "输入名称",
|
||||
"enter_url_or_path_hint": "请输入URL或列表的绝对路径",
|
||||
"check_updates_btn": "检查更新",
|
||||
"new_blocklist": "新封锁清单",
|
||||
"new_allowlist": "新的允许清单",
|
||||
"edit_blocklist": "编辑阻止列表",
|
||||
"edit_allowlist": "编辑允许列表",
|
||||
"choose_blocklist": "选择拦截列表",
|
||||
"choose_allowlist": "选择允许列表",
|
||||
"enter_valid_blocklist": "输入有效的阻止列表URL",
|
||||
"enter_valid_allowlist": "输入有效的允许列表URL",
|
||||
"new_allowlist": "新增白名单",
|
||||
"edit_blocklist": "编辑黑名单",
|
||||
"edit_allowlist": "编辑白名单",
|
||||
"choose_blocklist": "选择黑名单",
|
||||
"choose_allowlist": "选择白名单",
|
||||
"enter_valid_blocklist": "输入有效的黑名单 URL",
|
||||
"enter_valid_allowlist": "输入有效的白名单 URL",
|
||||
"form_error_url_format": "无效的 URL 格式",
|
||||
"form_error_url_or_path_format": "无效的 URL 或列表的绝对路径",
|
||||
"custom_filter_rules": "自定义过滤器规则",
|
||||
@ -269,9 +269,9 @@
|
||||
"dns_cache_config_desc": "您可以在此处配置 DNS 缓存",
|
||||
"blocking_mode": "拦截模式",
|
||||
"default": "默认",
|
||||
"nxdomain": "无效域名",
|
||||
"nxdomain": "NXDOMAIN",
|
||||
"refused": "REFUSED",
|
||||
"null_ip": "无效 IP",
|
||||
"null_ip": "空 IP",
|
||||
"custom_ip": "自定义 IP",
|
||||
"blocking_ipv4": "拦截 IPv4",
|
||||
"blocking_ipv6": "拦截 IPv6",
|
||||
@ -296,7 +296,7 @@
|
||||
"blocking_mode_default": "默认:被 Adblock 规则拦截时反应为零 IP 地址(A记录:0.0.0.0;AAAA记录:::);被/etc/hosts 规则拦截时反应为规则中指定 IP 地址",
|
||||
"blocking_mode_refused": "REFUSED:以 REFUSED 码响应请求",
|
||||
"blocking_mode_nxdomain": "NXDOMAIN:以NXDOMAIN码响应",
|
||||
"blocking_mode_null_ip": "空IP:以零IP地址响应(A记录 0.0.0.0;AAAA记录 ::)",
|
||||
"blocking_mode_null_ip": "空 IP:以零 IP 地址响应(A 记录 0.0.0.0;AAAA 记录 ::)",
|
||||
"blocking_mode_custom_ip": "自定IP:以手动设置的IP地址响应",
|
||||
"theme_auto": "自动",
|
||||
"theme_light": "浅色主题",
|
||||
@ -605,7 +605,7 @@
|
||||
"filtered": "已过滤",
|
||||
"rewritten": "重写项",
|
||||
"safe_search": "安全搜索",
|
||||
"blocklist": "拦截列表",
|
||||
"blocklist": "黑名单",
|
||||
"milliseconds_abbreviation": "毫秒",
|
||||
"cache_size": "缓存大小",
|
||||
"cache_size_desc": "DNS 缓存大小(单位:字节)。要关闭缓存,请留空。",
|
||||
@ -626,7 +626,7 @@
|
||||
"filter_category_general_desc": "在大多数设备上阻止跟踪和广告的列表",
|
||||
"filter_category_security_desc": "专用于拦截恶意软件、钓鱼或欺诈域名的列表",
|
||||
"filter_category_regional_desc": "专注于区域广告和跟踪服务器的列表",
|
||||
"filter_category_other_desc": "其他阻止列表",
|
||||
"filter_category_other_desc": "其他黑名单",
|
||||
"setup_config_to_enable_dhcp_server": "设置配置以启用 DHCP 服务器",
|
||||
"original_response": "原始响应",
|
||||
"click_to_view_queries": "点击查看查询",
|
||||
|
@ -53,7 +53,7 @@ export const STATUS_COLORS = {
|
||||
export const REPOSITORY = {
|
||||
URL: 'https://github.com/AdguardTeam/AdGuardHome',
|
||||
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',
|
||||
};
|
||||
|
||||
|
@ -160,12 +160,6 @@ export default {
|
||||
"homepage": "https://github.com/DandelionSprout/adfilt",
|
||||
"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": {
|
||||
"name": "HaGeZi Personal Black \u0026 White",
|
||||
"categoryId": "general",
|
||||
|
@ -1,6 +1,5 @@
|
||||
import whotracksmeDb from './whotracksme.json';
|
||||
import whotracksmeWebsites from './whotracksme_web.json';
|
||||
import adguardDb from './adguard.json';
|
||||
import trackersDb from './trackers.json';
|
||||
import { REPOSITORY } from '../constants';
|
||||
|
||||
/**
|
||||
@ -39,6 +38,7 @@ const getWhotracksmeUrl = (trackerId) => {
|
||||
|
||||
/**
|
||||
* Gets the source metadata for the specified tracker
|
||||
*
|
||||
* @param {TrackerData} trackerData tracker data
|
||||
* @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 {*} trackersDb trackers database
|
||||
* @param {number} source source ID
|
||||
* @returns {TrackerData} tracker data or null if no matching tracker found
|
||||
*/
|
||||
const getTrackerDataFromDb = (domainName, trackersDb, source) => {
|
||||
export const getTrackerData = (domainName) => {
|
||||
if (!domainName) {
|
||||
return null;
|
||||
}
|
||||
@ -88,7 +100,7 @@ const getTrackerDataFromDb = (domainName, trackersDb, source) => {
|
||||
if (trackerId) {
|
||||
const trackerData = trackersDb.trackers[trackerId];
|
||||
const categoryName = trackersDb.categories[trackerData.categoryId];
|
||||
trackerData.source = source;
|
||||
const source = convertSource(trackerData.source);
|
||||
const sourceData = getSourceData(trackerData);
|
||||
|
||||
return {
|
||||
@ -105,22 +117,3 @@ const getTrackerDataFromDb = (domainName, trackersDb, source) => {
|
||||
// No tracker found for the specified domain
|
||||
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;
|
||||
};
|
||||
|
23750
client/src/helpers/trackers/trackers.json
Normal file
23750
client/src/helpers/trackers/trackers.json
Normal file
File diff suppressed because it is too large
Load Diff
39
go.mod
39
go.mod
@ -1,10 +1,10 @@
|
||||
module github.com/AdguardTeam/AdGuardHome
|
||||
|
||||
go 1.18
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
// TODO(a.garipov): Return to a tagged version once DNS64 is in.
|
||||
github.com/AdguardTeam/dnsproxy v0.46.6-0.20230125113741-98cb8a899e49
|
||||
// TODO(a.garipov): Use v0.48.0 when it's released.
|
||||
github.com/AdguardTeam/dnsproxy v0.47.1-0.20230207130636-533058b17239
|
||||
github.com/AdguardTeam/golibs v0.11.4
|
||||
github.com/AdguardTeam/urlfilter v0.16.1
|
||||
github.com/NYTimes/gziphandler v1.1.1
|
||||
@ -19,27 +19,26 @@ require (
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/insomniacslk/dhcp v0.0.0-20221215072855-de60144f33f8
|
||||
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/netlink v1.7.1
|
||||
// TODO(a.garipov): This package is deprecated; find a new one or use
|
||||
// our own code for that. Perhaps, use gopacket.
|
||||
github.com/mdlayher/raw v0.1.0
|
||||
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/ti-mo/netfilter v0.5.0
|
||||
go.etcd.io/bbolt v1.3.7
|
||||
golang.org/x/crypto v0.5.0
|
||||
golang.org/x/exp v0.0.0-20230131160201-f062dba9d201
|
||||
golang.org/x/net v0.5.0
|
||||
golang.org/x/sys v0.4.0
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0
|
||||
golang.org/x/crypto v0.6.0
|
||||
golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb
|
||||
golang.org/x/net v0.7.0
|
||||
golang.org/x/sys v0.5.0
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
howett.net/plist v1.0.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v1.1.0 // indirect
|
||||
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect
|
||||
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 // 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/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // 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/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/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/pierrec/lz4/v4 v4.1.17 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/u-root/uio v0.0.0-20221213070652-c3537552635f // indirect
|
||||
golang.org/x/mod v0.7.0 // indirect
|
||||
github.com/quic-go/qpack v0.4.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/text v0.6.0 // indirect
|
||||
golang.org/x/tools v0.5.0 // indirect
|
||||
golang.org/x/text v0.7.0 // indirect
|
||||
golang.org/x/tools v0.6.0 // indirect
|
||||
)
|
||||
|
73
go.sum
73
go.sum
@ -1,5 +1,5 @@
|
||||
github.com/AdguardTeam/dnsproxy v0.46.6-0.20230125113741-98cb8a899e49 h1:TDZsKB8BrKA2na6p5l20BvEu3MmgOWhIfTANz5laFuE=
|
||||
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 h1:n1oOiywOvdeqWLto809bK1rK1EPDkpaSfT/r1OiCVaQ=
|
||||
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.10.4/go.mod h1:rSfQRGHIdgfxriDDNgNJ7HmE5zRoURq8R+VdR81Zuzw=
|
||||
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/urlfilter v0.16.1 h1:ZPi0rjqo8cQf2FVdzo6cqumNoHZx2KPXj2yZa1A5BBw=
|
||||
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/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
|
||||
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/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
|
||||
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-20230131232505-5a9e8f65f08f/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
|
||||
github.com/google/pprof v0.0.0-20230207041349-798e818bf904 h1:4/hN5RUoecvl+RmJRE2YxKWtnnQls6rQjjW5oV7qg2U=
|
||||
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/go.mod h1:t/HQoYBZSsWSNK35C6CO/TpPLDVWvxOHboWUAweKUpk=
|
||||
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.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
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-20220221185849-529eae5b6118 h1:2oDp6OOhLxQ9JBoUuysVz9UZ9uI6oLUbvAZu0x8o+vE=
|
||||
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/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/onsi/ginkgo/v2 v2.8.0 h1:pAM+oBNPrpXRs+E/8spkeGx9QgekbRVyr74EUvRVOUI=
|
||||
github.com/onsi/ginkgo/v2 v2.8.0/go.mod h1:6JsQiECmxCa3V5st74AL/AmsV482EDdVrGaVW6z3oYU=
|
||||
github.com/onsi/gomega v1.25.0 h1:Vw7br2PCDYijJHSfBOWhov+8cAnUf8MfMaIOV323l6Y=
|
||||
github.com/onsi/ginkgo/v2 v2.8.1 h1:xFTEVwOFa1D/Ty24Ws1npBWkDYEV9BqZrsDxVrVkrrU=
|
||||
github.com/onsi/ginkgo/v2 v2.8.1/go.mod h1:N1/NbDngAFcSLdyZ+/aYTYGSlq9qMCS/cNKGJjy+csc=
|
||||
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/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.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/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/go.mod h1:YWp/H8Qs5fVmf17v7JNZzA0mPJ+mS2e9JdiUF9LlKzQ=
|
||||
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/numcpus v0.3.0 h1:ILuRUQBtssgnxw0XXIjKUC56fgnOrFoQQ/4+DeU2biQ=
|
||||
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-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=
|
||||
go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ=
|
||||
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-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
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.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
|
||||
golang.org/x/exp v0.0.0-20230131160201-f062dba9d201 h1:BEABXpNXLEz0WxtA+6CQIz2xkg80e+1zrhWyMcq8VzE=
|
||||
golang.org/x/exp v0.0.0-20230131160201-f062dba9d201/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||
golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
|
||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||
golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb h1:PaBZQdo+iSDyHT053FjUCgZQ/9uqVwPOcl7KSWhKn6w=
|
||||
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/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.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA=
|
||||
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
|
||||
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-20190404232315-eb5bcb51f2a3/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-20210726213435-c6fcb2dbf985/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.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
|
||||
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
||||
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-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
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-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.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
|
||||
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
||||
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/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.3/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.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k=
|
||||
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
|
||||
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-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-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.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.5.0 h1:+bSpV5HIeWkuvgaMfI3UmKRThoTA5ODJTUd8T17NO+4=
|
||||
golang.org/x/tools v0.5.0/go.mod h1:N+Kgy78s5I24c24dU8OfWNEotWjutIs8SnJvn5IDq+k=
|
||||
golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
|
||||
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-20191011141410-1b5146add898/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 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/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
|
||||
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.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.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.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
@ -9,7 +9,7 @@ import (
|
||||
"github.com/AdguardTeam/dnsproxy/proxy"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"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.
|
||||
@ -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) {
|
||||
switch proto {
|
||||
case proxy.ProtoHTTPS:
|
||||
// github.com/lucas-clemente/quic-go seems to not populate the TLS
|
||||
// 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
|
||||
}
|
||||
srvName = pctx.HTTPRequest.TLS.ServerName
|
||||
case proxy.ProtoQUIC:
|
||||
qConn := pctx.QUICConnection
|
||||
conn, ok := qConn.(quicConnection)
|
||||
|
@ -9,7 +9,7 @@ import (
|
||||
|
||||
"github.com/AdguardTeam/dnsproxy/proxy"
|
||||
"github.com/AdguardTeam/golibs/testutil"
|
||||
"github.com/lucas-clemente/quic-go"
|
||||
"github.com/quic-go/quic-go"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@ -55,7 +55,6 @@ func TestServer_clientIDFromDNSContext(t *testing.T) {
|
||||
wantClientID string
|
||||
wantErrMsg string
|
||||
strictSNI bool
|
||||
useHTTP3 bool
|
||||
}{{
|
||||
name: "udp",
|
||||
proto: proxy.ProtoUDP,
|
||||
@ -64,7 +63,6 @@ func TestServer_clientIDFromDNSContext(t *testing.T) {
|
||||
wantClientID: "",
|
||||
wantErrMsg: "",
|
||||
strictSNI: false,
|
||||
useHTTP3: false,
|
||||
}, {
|
||||
name: "tls_no_clientid",
|
||||
proto: proxy.ProtoTLS,
|
||||
@ -73,7 +71,6 @@ func TestServer_clientIDFromDNSContext(t *testing.T) {
|
||||
wantClientID: "",
|
||||
wantErrMsg: "",
|
||||
strictSNI: true,
|
||||
useHTTP3: false,
|
||||
}, {
|
||||
name: "tls_no_client_server_name",
|
||||
proto: proxy.ProtoTLS,
|
||||
@ -83,7 +80,6 @@ func TestServer_clientIDFromDNSContext(t *testing.T) {
|
||||
wantErrMsg: `clientid check: client server name "" ` +
|
||||
`doesn't match host server name "example.com"`,
|
||||
strictSNI: true,
|
||||
useHTTP3: false,
|
||||
}, {
|
||||
name: "tls_no_client_server_name_no_strict",
|
||||
proto: proxy.ProtoTLS,
|
||||
@ -92,7 +88,6 @@ func TestServer_clientIDFromDNSContext(t *testing.T) {
|
||||
wantClientID: "",
|
||||
wantErrMsg: "",
|
||||
strictSNI: false,
|
||||
useHTTP3: false,
|
||||
}, {
|
||||
name: "tls_clientid",
|
||||
proto: proxy.ProtoTLS,
|
||||
@ -101,7 +96,6 @@ func TestServer_clientIDFromDNSContext(t *testing.T) {
|
||||
wantClientID: "cli",
|
||||
wantErrMsg: "",
|
||||
strictSNI: true,
|
||||
useHTTP3: false,
|
||||
}, {
|
||||
name: "tls_clientid_hostname_error",
|
||||
proto: proxy.ProtoTLS,
|
||||
@ -111,7 +105,6 @@ func TestServer_clientIDFromDNSContext(t *testing.T) {
|
||||
wantErrMsg: `clientid check: client server name "cli.example.net" ` +
|
||||
`doesn't match host server name "example.com"`,
|
||||
strictSNI: true,
|
||||
useHTTP3: false,
|
||||
}, {
|
||||
name: "tls_invalid_clientid",
|
||||
proto: proxy.ProtoTLS,
|
||||
@ -121,7 +114,6 @@ func TestServer_clientIDFromDNSContext(t *testing.T) {
|
||||
wantErrMsg: `clientid check: invalid clientid "!!!": ` +
|
||||
`bad domain name label rune '!'`,
|
||||
strictSNI: true,
|
||||
useHTTP3: false,
|
||||
}, {
|
||||
name: "tls_clientid_too_long",
|
||||
proto: proxy.ProtoTLS,
|
||||
@ -133,7 +125,6 @@ func TestServer_clientIDFromDNSContext(t *testing.T) {
|
||||
`pqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789": ` +
|
||||
`domain name label is too long: got 72, max 63`,
|
||||
strictSNI: true,
|
||||
useHTTP3: false,
|
||||
}, {
|
||||
name: "quic_clientid",
|
||||
proto: proxy.ProtoQUIC,
|
||||
@ -142,7 +133,6 @@ func TestServer_clientIDFromDNSContext(t *testing.T) {
|
||||
wantClientID: "cli",
|
||||
wantErrMsg: "",
|
||||
strictSNI: true,
|
||||
useHTTP3: false,
|
||||
}, {
|
||||
name: "tls_clientid_issue3437",
|
||||
proto: proxy.ProtoTLS,
|
||||
@ -152,7 +142,6 @@ func TestServer_clientIDFromDNSContext(t *testing.T) {
|
||||
wantErrMsg: `clientid check: client server name "cli.myexample.com" ` +
|
||||
`doesn't match host server name "example.com"`,
|
||||
strictSNI: true,
|
||||
useHTTP3: false,
|
||||
}, {
|
||||
name: "tls_case",
|
||||
proto: proxy.ProtoTLS,
|
||||
@ -161,7 +150,6 @@ func TestServer_clientIDFromDNSContext(t *testing.T) {
|
||||
wantClientID: "insensitive",
|
||||
wantErrMsg: ``,
|
||||
strictSNI: true,
|
||||
useHTTP3: false,
|
||||
}, {
|
||||
name: "quic_case",
|
||||
proto: proxy.ProtoQUIC,
|
||||
@ -170,7 +158,6 @@ func TestServer_clientIDFromDNSContext(t *testing.T) {
|
||||
wantClientID: "insensitive",
|
||||
wantErrMsg: ``,
|
||||
strictSNI: true,
|
||||
useHTTP3: false,
|
||||
}, {
|
||||
name: "https_no_clientid",
|
||||
proto: proxy.ProtoHTTPS,
|
||||
@ -179,7 +166,6 @@ func TestServer_clientIDFromDNSContext(t *testing.T) {
|
||||
wantClientID: "",
|
||||
wantErrMsg: "",
|
||||
strictSNI: true,
|
||||
useHTTP3: false,
|
||||
}, {
|
||||
name: "https_clientid",
|
||||
proto: proxy.ProtoHTTPS,
|
||||
@ -188,16 +174,6 @@ func TestServer_clientIDFromDNSContext(t *testing.T) {
|
||||
wantClientID: "cli",
|
||||
wantErrMsg: "",
|
||||
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 {
|
||||
@ -219,7 +195,7 @@ func TestServer_clientIDFromDNSContext(t *testing.T) {
|
||||
|
||||
switch tc.proto {
|
||||
case proxy.ProtoHTTPS:
|
||||
httpReq = newHTTPReq(tc.cliSrvName, tc.useHTTP3)
|
||||
httpReq = newHTTPReq(tc.cliSrvName)
|
||||
case proxy.ProtoQUIC:
|
||||
qconn = testQUICConnection{
|
||||
serverName: tc.cliSrvName,
|
||||
@ -246,21 +222,11 @@ func TestServer_clientIDFromDNSContext(t *testing.T) {
|
||||
}
|
||||
|
||||
// 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{
|
||||
Path: "/dns-query",
|
||||
}
|
||||
|
||||
if useHTTP3 {
|
||||
return &http.Request{
|
||||
ProtoMajor: 3,
|
||||
ProtoMinor: 0,
|
||||
URL: u,
|
||||
Host: cliSrvName,
|
||||
TLS: &tls.ConnectionState{},
|
||||
}
|
||||
}
|
||||
|
||||
return &http.Request{
|
||||
ProtoMajor: 1,
|
||||
ProtoMinor: 1,
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/netip"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
@ -225,7 +226,7 @@ type ServerConfig struct {
|
||||
LocalPTRResolvers []string
|
||||
|
||||
// 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 bool
|
||||
@ -271,6 +272,8 @@ func (s *Server) createProxyConfig() (conf proxy.Config, err error) {
|
||||
RequestHandler: s.handleDNSRequest,
|
||||
EnableEDNSClientSubnet: srvConf.EnableEDNSClientSubnet,
|
||||
MaxGoroutines: int(srvConf.MaxGoroutines),
|
||||
UseDNS64: srvConf.UseDNS64,
|
||||
DNS64Prefs: srvConf.DNS64Prefixes,
|
||||
}
|
||||
|
||||
if srvConf.CacheSize != 0 {
|
||||
|
@ -10,6 +10,8 @@ import (
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/dhcpd"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
||||
"github.com/AdguardTeam/dnsproxy/proxy"
|
||||
"github.com/AdguardTeam/dnsproxy/upstream"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/AdguardTeam/golibs/netutil"
|
||||
"github.com/AdguardTeam/golibs/stringutil"
|
||||
@ -17,6 +19,9 @@ import (
|
||||
)
|
||||
|
||||
// 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 {
|
||||
proxyCtx *proxy.DNSContext
|
||||
|
||||
@ -419,7 +424,7 @@ func (s *Server) processDHCPHosts(dctx *dnsContext) (rc resultCode) {
|
||||
}
|
||||
resp.Answer = append(resp.Answer, a)
|
||||
case dns.TypeAAAA:
|
||||
if len(s.dns64Prefs) > 0 {
|
||||
if s.dns64Pref != (netip.Prefix{}) {
|
||||
// Respond with DNS64-mapped address for IPv4 host if DNS64 is
|
||||
// enabled.
|
||||
aaaa := &dns.AAAA{
|
||||
@ -468,15 +473,6 @@ func (s *Server) processRestrictLocal(dctx *dnsContext) (rc resultCode) {
|
||||
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
|
||||
// assume that all the DHCP leases we give are locally served or at least
|
||||
// shouldn't be accessible externally.
|
||||
@ -655,13 +651,7 @@ func (s *Server) processUpstream(dctx *dnsContext) (rc resultCode) {
|
||||
|
||||
s.setCustomUpstream(pctx, dctx.clientID)
|
||||
|
||||
origReqAD := false
|
||||
if s.conf.EnableDNSSEC {
|
||||
origReqAD = req.AuthenticatedData
|
||||
if !req.AuthenticatedData {
|
||||
req.AuthenticatedData = true
|
||||
}
|
||||
}
|
||||
reqWantsDNSSEC := s.setReqAD(req)
|
||||
|
||||
// Process the request further since it wasn't filtered.
|
||||
prx := s.proxy()
|
||||
@ -671,23 +661,73 @@ func (s *Server) processUpstream(dctx *dnsContext) (rc resultCode) {
|
||||
return resultCodeError
|
||||
}
|
||||
|
||||
if dctx.err = prx.Resolve(pctx); dctx.err != nil {
|
||||
return resultCodeError
|
||||
}
|
||||
if err := prx.Resolve(pctx); err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
dctx.err = err
|
||||
|
||||
if s.performDNS64(prx, dctx) == resultCodeError {
|
||||
return resultCodeError
|
||||
}
|
||||
|
||||
dctx.responseFromUpstream = true
|
||||
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.Res.AuthenticatedData = false
|
||||
}
|
||||
|
||||
return resultCodeSuccess
|
||||
}
|
||||
|
||||
// isDHCPClientHostQ returns true if q is from a request for a DHCP client
|
||||
|
@ -1,34 +1,10 @@
|
||||
package dnsforward
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/netip"
|
||||
|
||||
"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
|
||||
@ -38,227 +14,22 @@ const (
|
||||
// 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
|
||||
// used to synthesize AAAA records.
|
||||
func (s *Server) setupDNS64() (err error) {
|
||||
func (s *Server) setupDNS64() {
|
||||
if !s.conf.UseDNS64 {
|
||||
return nil
|
||||
return
|
||||
}
|
||||
|
||||
l := len(s.conf.DNS64Prefixes)
|
||||
if l == 0 {
|
||||
s.dns64Prefs = []netip.Prefix{dns64WellKnownPref}
|
||||
if len(s.conf.DNS64Prefixes) == 0 {
|
||||
// dns64WellKnownPref is the default prefix to use in an algorithmic
|
||||
// mapping for DNS64.
|
||||
//
|
||||
// See https://datatracker.ietf.org/doc/html/rfc6052#section-2.1.
|
||||
dns64WellKnownPref := netip.MustParsePrefix("64:ff9b::/96")
|
||||
|
||||
return nil
|
||||
s.dns64Pref = dns64WellKnownPref
|
||||
} else {
|
||||
s.dns64Pref = s.conf.DNS64Prefixes[0]
|
||||
}
|
||||
|
||||
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.
|
||||
func (s *Server) checkDNS64(req, resp *dns.Msg) (dns64Req *dns.Msg) {
|
||||
if len(s.dns64Prefs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
q := req.Question[0]
|
||||
if q.Qtype != dns.TypeAAAA || q.Qclass != dns.ClassINET {
|
||||
// DNS64 operation for classes other than IN is undefined, and a DNS64
|
||||
// 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
|
||||
@ -267,79 +38,12 @@ func (s *Server) shouldStripDNS64(ip net.IP) (ok bool) {
|
||||
func (s *Server) mapDNS64(ip netip.Addr) (mapped net.IP) {
|
||||
// Don't mask the address here since it should have already been masked on
|
||||
// initialization stage.
|
||||
pref := s.dns64Prefs[0].Addr().As16()
|
||||
pref := s.dns64Pref.Masked().Addr().As16()
|
||||
ipData := ip.As4()
|
||||
|
||||
mapped = make(net.IP, net.IPv6len)
|
||||
copy(mapped[:nat64PrefixLen], pref[:])
|
||||
copy(mapped[nat64PrefixLen:], ipData[:])
|
||||
copy(mapped[:proxy.NAT64PrefixLength], pref[:])
|
||||
copy(mapped[proxy.NAT64PrefixLength:], ipData[:])
|
||||
|
||||
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
|
||||
}
|
||||
|
@ -15,6 +15,16 @@ import (
|
||||
"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
|
||||
// and value. It fails the test if the qtype is not supported or the type of
|
||||
// value doesn't match the qtype.
|
||||
|
@ -80,10 +80,17 @@ type Server struct {
|
||||
privateNets netutil.SubnetSet
|
||||
localResolvers *proxy.Proxy
|
||||
sysResolvers aghnet.SystemResolvers
|
||||
recDetector *recursionDetector
|
||||
|
||||
// dns64Prefix is the set of NAT64 prefixes used for DNS64 handling.
|
||||
dns64Prefs []netip.Prefix
|
||||
// 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
|
||||
|
||||
// dns64Pref is the NAT64 prefix used for DNS64 response mapping. The major
|
||||
// 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 *aghnet.IPMut
|
||||
@ -246,8 +253,8 @@ func (s *Server) Resolve(host string) ([]net.IPAddr, error) {
|
||||
|
||||
// RDNSExchanger is a resolver for clients' addresses.
|
||||
type RDNSExchanger interface {
|
||||
// Exchange tries to resolve the ip in a suitable way, e.g. either as
|
||||
// local or as external.
|
||||
// Exchange tries to resolve the ip in a suitable way, i.e. either as local
|
||||
// or as external.
|
||||
Exchange(ip net.IP) (host string, err error)
|
||||
|
||||
// ResolvesPrivatePTR returns true if the RDNSExchanger is able to
|
||||
@ -256,13 +263,13 @@ type RDNSExchanger interface {
|
||||
}
|
||||
|
||||
const (
|
||||
// rDNSEmptyAnswerErr is returned by Exchange method when the answer
|
||||
// section of respond is empty.
|
||||
rDNSEmptyAnswerErr errors.Error = "the answer section is empty"
|
||||
// ErrRDNSNoData is returned by [RDNSExchanger.Exchange] when the answer
|
||||
// section of response is either NODATA or has no PTR records.
|
||||
ErrRDNSNoData errors.Error = "no ptr data in response"
|
||||
|
||||
// rDNSNotPTRErr is returned by Exchange method when the response is not
|
||||
// of PTR type.
|
||||
rDNSNotPTRErr errors.Error = "the response is not a ptr"
|
||||
// ErrRDNSFailed is returned by [RDNSExchanger.Exchange] if the received
|
||||
// response is not a NOERROR or NXDOMAIN.
|
||||
ErrRDNSFailed errors.Error = "failed to resolve ptr"
|
||||
)
|
||||
|
||||
// type check
|
||||
@ -317,17 +324,24 @@ func (s *Server) Exchange(ip net.IP) (host string, err error) {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Distinguish between NODATA response and a failed request.
|
||||
resp := ctx.Res
|
||||
if len(resp.Answer) == 0 {
|
||||
return "", fmt.Errorf("lookup for %q: %w", arpa, rDNSEmptyAnswerErr)
|
||||
if resp.Rcode != dns.RcodeSuccess && resp.Rcode != dns.RcodeNameError {
|
||||
return "", fmt.Errorf(
|
||||
"received %s response: %w",
|
||||
dns.RcodeToString[resp.Rcode],
|
||||
ErrRDNSFailed,
|
||||
)
|
||||
}
|
||||
|
||||
ptr, ok := resp.Answer[0].(*dns.PTR)
|
||||
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.
|
||||
@ -477,6 +491,8 @@ func (s *Server) Prepare(conf *ServerConfig) (err error) {
|
||||
return fmt.Errorf("preparing proxy: %w", err)
|
||||
}
|
||||
|
||||
s.setupDNS64()
|
||||
|
||||
err = s.prepareInternalProxy()
|
||||
if err != nil {
|
||||
return fmt.Errorf("preparing internal proxy: %w", err)
|
||||
@ -493,18 +509,18 @@ func (s *Server) Prepare(conf *ServerConfig) (err error) {
|
||||
|
||||
s.registerHandlers()
|
||||
|
||||
err = s.setupDNS64()
|
||||
if err != nil {
|
||||
return fmt.Errorf("preparing DNS64: %w", err)
|
||||
}
|
||||
|
||||
s.dnsProxy = &proxy.Proxy{Config: proxyConfig}
|
||||
|
||||
// TODO(e.burkov): Remove once the local resolvers logic moved to dnsproxy.
|
||||
err = s.setupResolvers(s.conf.LocalPTRResolvers)
|
||||
if err != nil {
|
||||
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()
|
||||
|
||||
return nil
|
||||
|
@ -89,9 +89,14 @@ func createTestServer(
|
||||
s.serverLock.Lock()
|
||||
defer s.serverLock.Unlock()
|
||||
|
||||
// TODO(e.burkov): Try to move it higher.
|
||||
if localUps != nil {
|
||||
s.localResolvers.UpstreamConfig.Upstreams = []upstream.Upstream{localUps}
|
||||
ups := []upstream.Upstream{localUps}
|
||||
s.localResolvers.UpstreamConfig.Upstreams = ups
|
||||
s.conf.UsePrivateRDNS = true
|
||||
s.dnsProxy.PrivateRDNSUpstreamConfig = &proxy.UpstreamConfig{
|
||||
Upstreams: ups,
|
||||
}
|
||||
}
|
||||
|
||||
return s
|
||||
@ -1216,6 +1221,9 @@ func TestServer_Exchange(t *testing.T) {
|
||||
|
||||
errUpstream := aghtest.NewErrorUpstream()
|
||||
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{
|
||||
recDetector: newRecursionDetector(0, 1),
|
||||
@ -1260,15 +1268,21 @@ func TestServer_Exchange(t *testing.T) {
|
||||
}, {
|
||||
name: "empty_answer_error",
|
||||
want: "",
|
||||
wantErr: rDNSEmptyAnswerErr,
|
||||
wantErr: ErrRDNSNoData,
|
||||
locUpstream: locUpstream,
|
||||
req: net.IP{192, 168, 1, 2},
|
||||
}, {
|
||||
name: "not_ptr_error",
|
||||
name: "invalid_answer",
|
||||
want: "",
|
||||
wantErr: rDNSNotPTRErr,
|
||||
wantErr: ErrRDNSNoData,
|
||||
locUpstream: nonPtrUpstream,
|
||||
req: localIP,
|
||||
}, {
|
||||
name: "refused",
|
||||
want: "",
|
||||
wantErr: ErrRDNSFailed,
|
||||
locUpstream: refusingUpstream,
|
||||
req: localIP,
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
|
@ -22,9 +22,11 @@ func (s *Server) processQueryLogsAndStats(dctx *dnsContext) (rc resultCode) {
|
||||
|
||||
shouldLog := true
|
||||
msg := pctx.Req
|
||||
q := msg.Question[0]
|
||||
host := strings.ToLower(strings.TrimSuffix(q.Name, "."))
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
@ -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
|
||||
// uninitialized while in use. This can happen after proxy server has been
|
||||
// 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)
|
||||
} 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)
|
||||
}
|
||||
|
||||
|
@ -16,34 +16,44 @@ import (
|
||||
"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 {
|
||||
// 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
|
||||
|
||||
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) {
|
||||
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 {
|
||||
// Stats is embedded here simply to make testStats a stats.Stats without
|
||||
// actually implementing all methods.
|
||||
// Stats is embedded here simply to make testStats a [stats.Interface]
|
||||
// without actually implementing all methods.
|
||||
stats.Interface
|
||||
|
||||
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) {
|
||||
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) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
|
@ -1283,7 +1283,7 @@ var blockedServices = []blockedService{{
|
||||
}, {
|
||||
ID: "leagueoflegends",
|
||||
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{
|
||||
"||leagueoflegends.co.kr^",
|
||||
"||leagueoflegends.com^",
|
||||
@ -1307,9 +1307,7 @@ var blockedServices = []blockedService{{
|
||||
Rules: []string{
|
||||
"||aus.social^",
|
||||
"||awscommunity.social^",
|
||||
"||cupoftea.social^",
|
||||
"||cyberplace.social^",
|
||||
"||defcon.social^",
|
||||
"||det.social^",
|
||||
"||fosstodon.org^",
|
||||
"||glasgow.social^",
|
||||
@ -1335,7 +1333,6 @@ var blockedServices = []blockedService{{
|
||||
"||mastodon.au^",
|
||||
"||mastodon.bida.im^",
|
||||
"||mastodon.com.tr^",
|
||||
"||mastodon.eus^",
|
||||
"||mastodon.green^",
|
||||
"||mastodon.ie^",
|
||||
"||mastodon.iriseden.eu^",
|
||||
@ -1343,13 +1340,15 @@ var blockedServices = []blockedService{{
|
||||
"||mastodon.nu^",
|
||||
"||mastodon.nz^",
|
||||
"||mastodon.online^",
|
||||
"||mastodon.online^",
|
||||
"||mastodon.scot^",
|
||||
"||mastodon.sdf.org^",
|
||||
"||mastodon.social^",
|
||||
"||mastodon.social^",
|
||||
"||mastodon.top^",
|
||||
"||mastodon.uno^",
|
||||
"||mastodon.world^",
|
||||
"||mastodon.zaclys.com^",
|
||||
"||mastodon.xyz^",
|
||||
"||mastodonapp.uk^",
|
||||
"||mastodonners.nl^",
|
||||
"||mastodont.cat^",
|
||||
@ -1361,9 +1360,10 @@ var blockedServices = []blockedService{{
|
||||
"||mindly.social^",
|
||||
"||mstdn.ca^",
|
||||
"||mstdn.jp^",
|
||||
"||mstdn.party^",
|
||||
"||mstdn.social^",
|
||||
"||muenchen.social^",
|
||||
"||muenster.im^",
|
||||
"||nerdculture.de^",
|
||||
"||newsie.social^",
|
||||
"||noc.social^",
|
||||
"||norden.social^",
|
||||
|
@ -67,15 +67,18 @@ func (c *Client) closeUpstreams() (err error) {
|
||||
|
||||
type clientSource uint
|
||||
|
||||
// Client sources. The order determines the priority.
|
||||
// Clients information sources. The order determines the priority.
|
||||
const (
|
||||
ClientSourceWHOIS clientSource = iota
|
||||
ClientSourceNone clientSource = iota
|
||||
ClientSourceWHOIS
|
||||
ClientSourceARP
|
||||
ClientSourceRDNS
|
||||
ClientSourceDHCP
|
||||
ClientSourceHostsFile
|
||||
ClientSourcePersistent
|
||||
)
|
||||
|
||||
// type check
|
||||
var _ fmt.Stringer = clientSource(0)
|
||||
|
||||
// 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)
|
||||
|
||||
// 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.
|
||||
func (clients *clientsContainer) exists(ip netip.Addr, source clientSource) (ok bool) {
|
||||
// clientSource checks if client with this IP address already exists and returns
|
||||
// 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()
|
||||
defer clients.lock.Unlock()
|
||||
|
||||
_, ok = clients.findLocked(ip.String())
|
||||
_, ok := clients.findLocked(ip.String())
|
||||
if ok {
|
||||
return true
|
||||
return ClientSourcePersistent
|
||||
}
|
||||
|
||||
rc, ok := clients.ipToRC[ip]
|
||||
if !ok {
|
||||
return false
|
||||
return ClientSourceNone
|
||||
}
|
||||
|
||||
// Return false if the new source has higher priority.
|
||||
return source <= rc.Source
|
||||
return rc.Source
|
||||
}
|
||||
|
||||
func toQueryLogWHOIS(wi *RuntimeClientWHOISInfo) (cw *querylog.ClientWHOIS) {
|
||||
|
@ -67,9 +67,9 @@ func TestClients(t *testing.T) {
|
||||
|
||||
assert.Equal(t, "client2", c.Name)
|
||||
|
||||
assert.False(t, clients.exists(cliNoneIP, ClientSourceHostsFile))
|
||||
assert.True(t, clients.exists(cli1IP, ClientSourceHostsFile))
|
||||
assert.True(t, clients.exists(cli2IP, ClientSourceHostsFile))
|
||||
assert.Equal(t, clients.clientSource(cliNoneIP), ClientSourceNone)
|
||||
assert.Equal(t, clients.clientSource(cli1IP), ClientSourcePersistent)
|
||||
assert.Equal(t, clients.clientSource(cli2IP), ClientSourcePersistent)
|
||||
})
|
||||
|
||||
t.Run("add_fail_name", func(t *testing.T) {
|
||||
@ -127,8 +127,8 @@ func TestClients(t *testing.T) {
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.False(t, clients.exists(cliOldIP, ClientSourceHostsFile))
|
||||
assert.True(t, clients.exists(cliNewIP, ClientSourceHostsFile))
|
||||
assert.Equal(t, clients.clientSource(cliOldIP), ClientSourceNone)
|
||||
assert.Equal(t, clients.clientSource(cliNewIP), ClientSourcePersistent)
|
||||
|
||||
err = clients.Update("client1", &Client{
|
||||
IDs: []string{cliNew},
|
||||
@ -157,7 +157,7 @@ func TestClients(t *testing.T) {
|
||||
ok := clients.Del("client1-renamed")
|
||||
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) {
|
||||
@ -176,18 +176,18 @@ func TestClients(t *testing.T) {
|
||||
ok = clients.AddHost(ip, "host3", ClientSourceHostsFile)
|
||||
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) {
|
||||
ip := netip.MustParseAddr("1.2.3.4")
|
||||
ok := clients.AddHost(ip, "from_arp", ClientSourceARP)
|
||||
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)
|
||||
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) {
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"net/netip"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"sync"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghalg"
|
||||
@ -112,8 +113,10 @@ type configuration struct {
|
||||
// An active session is automatically refreshed once a day.
|
||||
WebSessionTTLHours uint32 `yaml:"web_session_ttl"`
|
||||
|
||||
DNS dnsConfig `yaml:"dns"`
|
||||
TLS tlsConfigSettings `yaml:"tls"`
|
||||
DNS dnsConfig `yaml:"dns"`
|
||||
TLS tlsConfigSettings `yaml:"tls"`
|
||||
QueryLog queryLogConfig `yaml:"querylog"`
|
||||
Stats statsConfig `yaml:"statistics"`
|
||||
|
||||
// Filters reflects the filters from [filtering.Config]. It's cloned to the
|
||||
// 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"`
|
||||
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
|
||||
// in query log and statistics.
|
||||
AnonymizeClientIP bool `yaml:"anonymize_client_ip"`
|
||||
@ -188,7 +177,7 @@ type dnsConfig struct {
|
||||
UseDNS64 bool `yaml:"use_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.
|
||||
//
|
||||
@ -228,6 +217,37 @@ type tlsConfigSettings struct {
|
||||
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.
|
||||
//
|
||||
// TODO(a.garipov, e.burkov): This global is awful and must be removed.
|
||||
@ -238,13 +258,8 @@ var config = &configuration{
|
||||
AuthBlockMin: 15,
|
||||
WebSessionTTLHours: 30 * 24,
|
||||
DNS: dnsConfig{
|
||||
BindHosts: []netip.Addr{netip.IPv4Unspecified()},
|
||||
Port: defaultPortDNS,
|
||||
StatsInterval: 1,
|
||||
QueryLogEnabled: true,
|
||||
QueryLogFileEnabled: true,
|
||||
QueryLogInterval: timeutil.Duration{Duration: 90 * timeutil.Day},
|
||||
QueryLogMemSize: 1000,
|
||||
BindHosts: []netip.Addr{netip.IPv4Unspecified()},
|
||||
Port: defaultPortDNS,
|
||||
FilteringConfig: dnsforward.FilteringConfig{
|
||||
ProtectionEnabled: true, // whether or not use any of filtering features
|
||||
BlockingMode: dnsforward.BlockingModeDefault,
|
||||
@ -282,6 +297,18 @@ var config = &configuration{
|
||||
PortDNSOverTLS: defaultPortTLS, // needs to be passed through to dnsproxy
|
||||
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
|
||||
// client/src/helpers/filters/filters.js by scripts/vetted-filters.
|
||||
//
|
||||
@ -458,19 +485,24 @@ func (c *configuration) write() (err error) {
|
||||
}
|
||||
|
||||
if Context.stats != nil {
|
||||
sdc := stats.DiskConfig{}
|
||||
Context.stats.WriteDiskConfig(&sdc)
|
||||
config.DNS.StatsInterval = sdc.Interval
|
||||
statsConf := stats.Config{}
|
||||
Context.stats.WriteDiskConfig(&statsConf)
|
||||
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 {
|
||||
dc := querylog.Config{}
|
||||
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.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 {
|
||||
|
@ -20,7 +20,7 @@ import (
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/version"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"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.
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghalg"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
|
||||
@ -20,6 +21,7 @@ import (
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/AdguardTeam/golibs/netutil"
|
||||
"github.com/AdguardTeam/golibs/stringutil"
|
||||
"github.com/ameshkov/dnscrypt/v2"
|
||||
yaml "gopkg.in/yaml.v3"
|
||||
)
|
||||
@ -51,10 +53,18 @@ func initDNS() (err error) {
|
||||
|
||||
statsConf := stats.Config{
|
||||
Filename: filepath.Join(baseDir, "stats.db"),
|
||||
LimitDays: config.DNS.StatsInterval,
|
||||
LimitDays: config.Stats.Interval,
|
||||
ConfigModified: onConfigModified,
|
||||
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)
|
||||
if err != nil {
|
||||
return fmt.Errorf("init stats: %w", err)
|
||||
@ -66,12 +76,19 @@ func initDNS() (err error) {
|
||||
HTTPRegister: httpRegister,
|
||||
FindClient: Context.clients.findMultiple,
|
||||
BaseDir: baseDir,
|
||||
RotationIvl: config.DNS.QueryLogInterval.Duration,
|
||||
MemSize: config.DNS.QueryLogMemSize,
|
||||
Enabled: config.DNS.QueryLogEnabled,
|
||||
FileEnabled: config.DNS.QueryLogFileEnabled,
|
||||
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.filters, err = filtering.New(config.DNS.DnsfilterConf, nil)
|
||||
@ -515,3 +532,27 @@ func closeDNSServer() {
|
||||
|
||||
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
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/dnsforward"
|
||||
"github.com/AdguardTeam/golibs/cache"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
)
|
||||
|
||||
@ -16,10 +17,6 @@ type RDNS struct {
|
||||
exchanger dnsforward.RDNSExchanger
|
||||
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 chan netip.Addr
|
||||
|
||||
@ -28,13 +25,21 @@ type RDNS struct {
|
||||
// address will be resolved once again. If the address couldn't be
|
||||
// resolved, cache prevents further attempts to resolve it for some time.
|
||||
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 (
|
||||
defaultRDNSCacheSize = 10000
|
||||
defaultRDNSCacheTTL = 1 * 60 * 60
|
||||
defaultRDNSIPChSize = 256
|
||||
revDNSCacheSize = 10000
|
||||
|
||||
// TODO(e.burkov): Make these values configurable.
|
||||
revDNSCacheTTL = 24 * 60 * 60
|
||||
revDNSFailureCacheTTL = 1 * 60 * 60
|
||||
|
||||
revDNSQueueSize = 256
|
||||
)
|
||||
|
||||
// NewRDNS creates and returns initialized RDNS.
|
||||
@ -48,14 +53,13 @@ func NewRDNS(
|
||||
clients: clients,
|
||||
ipCache: cache.New(cache.Config{
|
||||
EnableLRU: true,
|
||||
MaxCount: defaultRDNSCacheSize,
|
||||
MaxCount: revDNSCacheSize,
|
||||
}),
|
||||
ipCh: make(chan netip.Addr, defaultRDNSIPChSize),
|
||||
}
|
||||
if usePrivate {
|
||||
rDNS.usePrivate = 1
|
||||
ipCh: make(chan netip.Addr, revDNSQueueSize),
|
||||
}
|
||||
|
||||
rDNS.usePrivate.Store(usePrivate)
|
||||
|
||||
go rDNS.workerLoop()
|
||||
|
||||
return rDNS
|
||||
@ -68,12 +72,8 @@ func NewRDNS(
|
||||
// approach since only unresolved locally-served addresses should be removed.
|
||||
// Implement when improving the cache.
|
||||
func (r *RDNS) ensurePrivateCache() {
|
||||
var usePrivate uint32
|
||||
if r.exchanger.ResolvesPrivatePTR() {
|
||||
usePrivate = 1
|
||||
}
|
||||
|
||||
if atomic.CompareAndSwapUint32(&r.usePrivate, 1-usePrivate, usePrivate) {
|
||||
usePrivate := r.exchanger.ResolvesPrivatePTR()
|
||||
if r.usePrivate.CompareAndSwap(!usePrivate, usePrivate) {
|
||||
r.ipCache.Clear()
|
||||
}
|
||||
}
|
||||
@ -84,25 +84,28 @@ func (r *RDNS) isCached(ip netip.Addr) (ok bool) {
|
||||
ipBytes := ip.AsSlice()
|
||||
now := uint64(time.Now().Unix())
|
||||
if expire := r.ipCache.Get(ipBytes); len(expire) != 0 {
|
||||
if binary.BigEndian.Uint64(expire) > now {
|
||||
return true
|
||||
}
|
||||
return binary.BigEndian.Uint64(expire) > now
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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
|
||||
// resolved.
|
||||
func (r *RDNS) Begin(ip netip.Addr) {
|
||||
r.ensurePrivateCache()
|
||||
|
||||
if r.isCached(ip) || r.clients.exists(ip, ClientSourceRDNS) {
|
||||
if r.isCached(ip) || r.clients.clientSource(ip) > ClientSourceRDNS {
|
||||
return
|
||||
}
|
||||
|
||||
@ -120,15 +123,21 @@ func (r *RDNS) workerLoop() {
|
||||
defer log.OnPanic("rdns")
|
||||
|
||||
for ip := range r.ipCh {
|
||||
ttl := uint64(revDNSCacheTTL)
|
||||
|
||||
host, err := r.exchanger.Exchange(ip.AsSlice())
|
||||
if err != nil {
|
||||
log.Debug("rdns: resolving %q: %s", ip, err)
|
||||
|
||||
continue
|
||||
} else if host == "" {
|
||||
continue
|
||||
if errors.Is(err, dnsforward.ErrRDNSFailed) {
|
||||
// Cache failure for a less time.
|
||||
ttl = revDNSFailureCacheTTL
|
||||
}
|
||||
}
|
||||
|
||||
_ = r.clients.AddHost(ip, host, ClientSourceRDNS)
|
||||
r.cache(ip, ttl)
|
||||
|
||||
if host != "" {
|
||||
_ = r.clients.AddHost(ip, host, ClientSourceRDNS)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -76,7 +76,7 @@ func TestRDNS_Begin(t *testing.T) {
|
||||
|
||||
ipCache := cache.New(cache.Config{
|
||||
EnableLRU: true,
|
||||
MaxCount: defaultRDNSCacheSize,
|
||||
MaxCount: revDNSCacheSize,
|
||||
})
|
||||
ttl := make([]byte, binary.Size(uint64(0)))
|
||||
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{
|
||||
EnableLRU: true,
|
||||
MaxCount: defaultRDNSCacheSize,
|
||||
MaxCount: revDNSCacheSize,
|
||||
})
|
||||
|
||||
ex := &rDNSExchanger{
|
||||
@ -200,25 +200,29 @@ func TestRDNS_WorkerLoop(t *testing.T) {
|
||||
errUpstream := aghtest.NewErrorUpstream()
|
||||
|
||||
testCases := []struct {
|
||||
ups upstream.Upstream
|
||||
cliIP netip.Addr
|
||||
wantLog string
|
||||
name string
|
||||
ups upstream.Upstream
|
||||
cliIP netip.Addr
|
||||
wantLog string
|
||||
name string
|
||||
wantClientSource clientSource
|
||||
}{{
|
||||
ups: locUpstream,
|
||||
cliIP: localIP,
|
||||
wantLog: "",
|
||||
name: "all_good",
|
||||
ups: locUpstream,
|
||||
cliIP: localIP,
|
||||
wantLog: "",
|
||||
name: "all_good",
|
||||
wantClientSource: ClientSourceRDNS,
|
||||
}, {
|
||||
ups: errUpstream,
|
||||
cliIP: netip.MustParseAddr("192.168.1.2"),
|
||||
wantLog: `rdns: resolving "192.168.1.2": test upstream error`,
|
||||
name: "resolve_error",
|
||||
ups: errUpstream,
|
||||
cliIP: netip.MustParseAddr("192.168.1.2"),
|
||||
wantLog: `rdns: resolving "192.168.1.2": test upstream error`,
|
||||
name: "resolve_error",
|
||||
wantClientSource: ClientSourceNone,
|
||||
}, {
|
||||
ups: locUpstream,
|
||||
cliIP: netip.MustParseAddr("2a00:1450:400c:c06::93"),
|
||||
wantLog: "",
|
||||
name: "ipv6_good",
|
||||
ups: locUpstream,
|
||||
cliIP: netip.MustParseAddr("2a00:1450:400c:c06::93"),
|
||||
wantLog: "",
|
||||
name: "ipv6_good",
|
||||
wantClientSource: ClientSourceRDNS,
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
@ -237,6 +241,10 @@ func TestRDNS_WorkerLoop(t *testing.T) {
|
||||
},
|
||||
clients: cc,
|
||||
ipCh: ch,
|
||||
ipCache: cache.New(cache.Config{
|
||||
EnableLRU: true,
|
||||
MaxCount: revDNSCacheSize,
|
||||
}),
|
||||
}
|
||||
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
@ -253,11 +261,9 @@ func TestRDNS_WorkerLoop(t *testing.T) {
|
||||
|
||||
if 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))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ import (
|
||||
)
|
||||
|
||||
// currentSchemaVersion is the current schema version.
|
||||
const currentSchemaVersion = 14
|
||||
const currentSchemaVersion = 16
|
||||
|
||||
// These aliases are provided for convenience.
|
||||
type (
|
||||
@ -87,6 +87,8 @@ func upgradeConfigSchema(oldVersion int, diskConf yobj) (err error) {
|
||||
upgradeSchema11to12,
|
||||
upgradeSchema12to13,
|
||||
upgradeSchema13to14,
|
||||
upgradeSchema14to15,
|
||||
upgradeSchema15to16,
|
||||
}
|
||||
|
||||
n := 0
|
||||
@ -802,6 +804,107 @@ func upgradeSchema13to14(diskConf yobj) (err error) {
|
||||
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
|
||||
// package.
|
||||
func funcName() string {
|
||||
|
@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -15,8 +15,8 @@ import (
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/AdguardTeam/golibs/netutil"
|
||||
"github.com/NYTimes/gziphandler"
|
||||
"github.com/lucas-clemente/quic-go"
|
||||
"github.com/lucas-clemente/quic-go/http3"
|
||||
"github.com/quic-go/quic-go"
|
||||
"github.com/quic-go/quic-go/http3"
|
||||
"golang.org/x/net/http2"
|
||||
"golang.org/x/net/http2/h2c"
|
||||
)
|
||||
|
@ -44,6 +44,9 @@ func (l *queryLog) initWeb() {
|
||||
}
|
||||
|
||||
func (l *queryLog) handleQueryLog(w http.ResponseWriter, r *http.Request) {
|
||||
l.lock.Lock()
|
||||
defer l.lock.Unlock()
|
||||
|
||||
params, err := l.parseSearchParams(r)
|
||||
if err != nil {
|
||||
aghhttp.Error(r, w, http.StatusBadRequest, "failed to parse params: %s", err)
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
||||
"github.com/AdguardTeam/dnsproxy/proxyutil"
|
||||
"github.com/AdguardTeam/golibs/stringutil"
|
||||
"github.com/AdguardTeam/golibs/testutil"
|
||||
"github.com/AdguardTeam/golibs/timeutil"
|
||||
"github.com/miekg/dns"
|
||||
@ -249,6 +250,48 @@ func TestQueryLogFileDisabled(t *testing.T) {
|
||||
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) {
|
||||
q := dns.Msg{
|
||||
Question: []dns.Question{{
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/AdguardTeam/golibs/stringutil"
|
||||
"github.com/AdguardTeam/golibs/timeutil"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
@ -26,6 +27,9 @@ type QueryLog interface {
|
||||
|
||||
// WriteDiskConfig - write configuration
|
||||
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.
|
||||
@ -71,6 +75,10 @@ type Config struct {
|
||||
// AnonymizeClientIP tells if the query log should anonymize clients' IP
|
||||
// addresses.
|
||||
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.
|
||||
|
@ -155,6 +155,9 @@ func (l *queryLog) periodicRotate() {
|
||||
// checkAndRotate rotates log files if those are older than the specified
|
||||
// rotation interval.
|
||||
func (l *queryLog) checkAndRotate() {
|
||||
l.lock.Lock()
|
||||
defer l.lock.Unlock()
|
||||
|
||||
oldest, err := l.readFileFirstTimeValue()
|
||||
if err != nil && !errors.Is(err, os.ErrNotExist) {
|
||||
log.Error("querylog: reading oldest record for rotation: %s", err)
|
||||
|
@ -263,6 +263,10 @@ func (l *queryLog) readNextEntry(
|
||||
e = &logEntry{}
|
||||
decodeLogEntry(e, line)
|
||||
|
||||
if l.isIgnored(e.QHost) {
|
||||
return nil, ts, nil
|
||||
}
|
||||
|
||||
e.client, err = l.client(e.ClientID, e.IP.String(), cache)
|
||||
if err != nil {
|
||||
log.Error(
|
||||
|
@ -5,7 +5,6 @@ package stats
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
|
||||
@ -41,10 +40,11 @@ type StatsResp struct {
|
||||
|
||||
// handleStats handles requests to the GET /control/stats endpoint.
|
||||
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()
|
||||
resp, ok := s.getData(limit)
|
||||
resp, ok := s.getData(s.limitHours)
|
||||
log.Debug("stats: prepared data in %v", time.Since(start))
|
||||
|
||||
if !ok {
|
||||
@ -65,7 +65,13 @@ type configResp struct {
|
||||
|
||||
// handleStatsInfo handles requests to the GET /control/stats_info endpoint.
|
||||
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)
|
||||
}
|
||||
|
||||
|
@ -15,16 +15,10 @@ import (
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/AdguardTeam/golibs/stringutil"
|
||||
"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
|
||||
// retention interval. The valid values are 0, 1, 7, 30 and 90.
|
||||
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
|
||||
// current unit.
|
||||
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.
|
||||
@ -68,30 +68,22 @@ type Interface interface {
|
||||
TopClientsIP(limit uint) []netip.Addr
|
||||
|
||||
// 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
|
||||
// flushing interval is one hour.
|
||||
//
|
||||
// TODO(e.burkov): Use atomic.Pointer for accessing db in go1.19.
|
||||
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 *sync.RWMutex
|
||||
// curr is the actual statistics collection result.
|
||||
curr *unit
|
||||
|
||||
// dbMu protects db.
|
||||
dbMu *sync.Mutex
|
||||
// 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
|
||||
// unit. It's here for only testing purposes.
|
||||
@ -106,6 +98,21 @@ type StatsCtx struct {
|
||||
|
||||
// filename is the name of database file.
|
||||
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
|
||||
@ -114,11 +121,12 @@ func New(conf Config) (s *StatsCtx, err error) {
|
||||
defer withRecovered(&err)
|
||||
|
||||
s = &StatsCtx{
|
||||
enabled: conf.Enabled,
|
||||
currMu: &sync.RWMutex{},
|
||||
dbMu: &sync.Mutex{},
|
||||
filename: conf.Filename,
|
||||
configModified: conf.ConfigModified,
|
||||
httpRegister: conf.HTTPRegister,
|
||||
ignored: conf.Ignored,
|
||||
}
|
||||
if s.limitHours = conf.LimitDays * 24; !checkInterval(conf.LimitDays) {
|
||||
s.limitHours = 24
|
||||
@ -137,7 +145,7 @@ func New(conf Config) (s *StatsCtx, err error) {
|
||||
var udb *unitDB
|
||||
id := s.unitIDGen()
|
||||
|
||||
tx, err := s.db.Begin(true)
|
||||
tx, err := s.db.Load().Begin(true)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("stats: opening a transaction: %w", err)
|
||||
}
|
||||
@ -191,7 +199,7 @@ func (s *StatsCtx) Start() {
|
||||
func (s *StatsCtx) Close() (err error) {
|
||||
defer func() { err = errors.Annotate(err, "stats: closing: %w") }()
|
||||
|
||||
db := s.swapDatabase(nil)
|
||||
db := s.db.Swap(nil)
|
||||
if db == nil {
|
||||
return nil
|
||||
}
|
||||
@ -220,7 +228,10 @@ func (s *StatsCtx) Close() (err error) {
|
||||
|
||||
// Update implements the Interface interface for *StatsCtx.
|
||||
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
|
||||
}
|
||||
|
||||
@ -248,14 +259,22 @@ func (s *StatsCtx) Update(e Entry) {
|
||||
}
|
||||
|
||||
// WriteDiskConfig implements the Interface interface for *StatsCtx.
|
||||
func (s *StatsCtx) WriteDiskConfig(dc *DiskConfig) {
|
||||
dc.Interval = atomic.LoadUint32(&s.limitHours) / 24
|
||||
func (s *StatsCtx) WriteDiskConfig(dc *Config) {
|
||||
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.
|
||||
func (s *StatsCtx) TopClientsIP(maxCount uint) (ips []netip.Addr) {
|
||||
limit := atomic.LoadUint32(&s.limitHours)
|
||||
if limit == 0 {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
limit := s.limitHours
|
||||
if !s.enabled || limit == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -284,25 +303,6 @@ func (s *StatsCtx) TopClientsIP(maxCount uint) (ips []netip.Addr) {
|
||||
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
|
||||
// returns the number of deletions performed.
|
||||
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.
|
||||
defer log.Debug("stats: database opened")
|
||||
|
||||
s.dbMu.Lock()
|
||||
defer s.dbMu.Unlock()
|
||||
|
||||
s.db = db
|
||||
s.db.Store(db)
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -369,6 +366,9 @@ func (s *StatsCtx) openDB() (err error) {
|
||||
func (s *StatsCtx) flush() (cont bool, sleepFor time.Duration) {
|
||||
id := s.unitIDGen()
|
||||
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
s.currMu.Lock()
|
||||
defer s.currMu.Unlock()
|
||||
|
||||
@ -377,12 +377,12 @@ func (s *StatsCtx) flush() (cont bool, sleepFor time.Duration) {
|
||||
return false, 0
|
||||
}
|
||||
|
||||
limit := atomic.LoadUint32(&s.limitHours)
|
||||
limit := s.limitHours
|
||||
if limit == 0 || ptr.id == id {
|
||||
return true, time.Second
|
||||
}
|
||||
|
||||
db := s.database()
|
||||
db := s.db.Load()
|
||||
if db == nil {
|
||||
return true, 0
|
||||
}
|
||||
@ -437,21 +437,30 @@ func (s *StatsCtx) periodicFlush() {
|
||||
}
|
||||
|
||||
func (s *StatsCtx) setLimit(limitDays int) {
|
||||
atomic.StoreUint32(&s.limitHours, uint32(24*limitDays))
|
||||
if limitDays == 0 {
|
||||
if err := s.clear(); err != nil {
|
||||
log.Error("stats: %s", err)
|
||||
}
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
if limitDays != 0 {
|
||||
s.enabled = true
|
||||
s.limitHours = uint32(24 * limitDays)
|
||||
log.Debug("stats: set limit: %d days", limitDays)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
log.Debug("stats: set limit: %d days", limitDays)
|
||||
s.enabled = false
|
||||
log.Debug("stats: disabled")
|
||||
|
||||
if err := s.clear(); err != nil {
|
||||
log.Error("stats: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Reset counters and clear database
|
||||
func (s *StatsCtx) clear() (err error) {
|
||||
defer func() { err = errors.Annotate(err, "clearing: %w") }()
|
||||
|
||||
db := s.swapDatabase(nil)
|
||||
db := s.db.Swap(nil)
|
||||
if db != nil {
|
||||
var tx *bbolt.Tx
|
||||
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) {
|
||||
db := s.database()
|
||||
db := s.db.Load()
|
||||
if db == nil {
|
||||
return nil, 0
|
||||
}
|
||||
@ -547,3 +556,13 @@ func (s *StatsCtx) loadUnits(limit uint32) (units []*unitDB, firstID uint32) {
|
||||
|
||||
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)
|
||||
}
|
||||
|
@ -47,8 +47,6 @@ func TestStats_races(t *testing.T) {
|
||||
startTime := time.Now()
|
||||
testutil.CleanupAndRequireSuccess(t, s.Close)
|
||||
|
||||
type signal = struct{}
|
||||
|
||||
writeFunc := func(start, fin *sync.WaitGroup, waitCh <-chan unit, i int) {
|
||||
e := Entry{
|
||||
Domain: fmt.Sprintf("example-%d.org", i),
|
||||
|
@ -53,6 +53,7 @@ func TestStats(t *testing.T) {
|
||||
conf := stats.Config{
|
||||
Filename: filepath.Join(t.TempDir(), "stats.db"),
|
||||
LimitDays: 1,
|
||||
Enabled: true,
|
||||
UnitID: constUnitID,
|
||||
HTTPRegister: func(_, url string, handler http.HandlerFunc) {
|
||||
handlers[url] = handler
|
||||
@ -158,6 +159,7 @@ func TestLargeNumbers(t *testing.T) {
|
||||
conf := stats.Config{
|
||||
Filename: filepath.Join(t.TempDir(), "stats.db"),
|
||||
LimitDays: 1,
|
||||
Enabled: true,
|
||||
UnitID: func() (id uint32) { return atomic.LoadUint32(&curHour) },
|
||||
HTTPRegister: func(_, url string, handler http.HandlerFunc) { handlers[url] = handler },
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/AdguardTeam/golibs/stringutil"
|
||||
"go.etcd.io/bbolt"
|
||||
)
|
||||
|
||||
@ -341,11 +342,13 @@ type pairsGetter func(u *unitDB) (pairs []countPair)
|
||||
|
||||
// topsCollector collects statistics about highest values from the given *unitDB
|
||||
// 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{}
|
||||
for _, u := range units {
|
||||
for _, cp := range pg(u) {
|
||||
m[cp.Name] += cp.Count
|
||||
if !ignored.Has(cp.Name) {
|
||||
m[cp.Name] += cp.Count
|
||||
}
|
||||
}
|
||||
}
|
||||
a2 := convertMapToSlice(m, max)
|
||||
@ -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] }),
|
||||
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] }),
|
||||
TopQueried: topsCollector(units, maxDomains, func(u *unitDB) (pairs []countPair) { return u.Domains }),
|
||||
TopBlocked: topsCollector(units, maxDomains, func(u *unitDB) (pairs []countPair) { return u.BlockedDomains }),
|
||||
TopClients: topsCollector(units, maxClients, func(u *unitDB) (pairs []countPair) { return u.Clients }),
|
||||
TopQueried: topsCollector(units, maxDomains, s.ignored, func(u *unitDB) (pairs []countPair) { return u.Domains }),
|
||||
TopBlocked: topsCollector(units, maxDomains, s.ignored, func(u *unitDB) (pairs []countPair) { return u.BlockedDomains }),
|
||||
TopClients: topsCollector(units, maxClients, nil, func(u *unitDB) (pairs []countPair) { return u.Clients }),
|
||||
}
|
||||
|
||||
// Total counters:
|
||||
|
@ -1,6 +1,6 @@
|
||||
module github.com/AdguardTeam/AdGuardHome/internal/tools
|
||||
|
||||
go 1.18
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
github.com/fzipp/gocyclo v0.6.0
|
||||
@ -8,10 +8,10 @@ require (
|
||||
github.com/gordonklaus/ineffassign v0.0.0-20230107090616-13ace0543b28
|
||||
github.com/kisielk/errcheck v1.6.3
|
||||
github.com/kyoh86/looppointer v0.2.1
|
||||
github.com/securego/gosec/v2 v2.14.0
|
||||
golang.org/x/tools v0.5.1-0.20230117180257-8aba49bb5ea2
|
||||
golang.org/x/vuln v0.0.0-20230130175424-dd534eeddf33
|
||||
honnef.co/go/tools v0.3.3
|
||||
github.com/securego/gosec/v2 v2.15.0
|
||||
golang.org/x/tools v0.6.0
|
||||
golang.org/x/vuln v0.0.0-20230213165600-1a019b0c7f30
|
||||
honnef.co/go/tools v0.4.1
|
||||
mvdan.cc/gofumpt v0.4.0
|
||||
mvdan.cc/unparam v0.0.0-20230125043941-70a0ce6e7b95
|
||||
)
|
||||
@ -24,10 +24,10 @@ require (
|
||||
github.com/kyoh86/nolint v0.0.1 // indirect
|
||||
github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354 // 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/typeparams v0.0.0-20230131160201-f062dba9d201 // indirect
|
||||
golang.org/x/mod v0.7.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb // indirect
|
||||
golang.org/x/exp/typeparams v0.0.0-20230213192124-5e25df0256eb // indirect
|
||||
golang.org/x/mod v0.8.0 // indirect
|
||||
golang.org/x/sync v0.1.0 // indirect
|
||||
golang.org/x/sys v0.4.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
golang.org/x/sys v0.5.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
@ -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/fzipp/gocyclo v0.6.0 h1:lsblElZG7d3ALtGMx9fmxeTKZaLLpU8mET09yN4BBLo=
|
||||
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/go.mod h1:W6O/bwV6lGDxUCChm2ykw9NQdd5bYd1Xkjo88UcWyJc=
|
||||
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/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/onsi/ginkgo/v2 v2.3.1 h1:8SbseP7qM32WcvE6VaN6vfXxv698izmsJ1UQX9ve7T8=
|
||||
github.com/onsi/gomega v1.22.1 h1:pY8O4lBfsHKZHM/6nrxkhVPUznOlIu3quZcKP/M20KI=
|
||||
github.com/onsi/ginkgo/v2 v2.8.0 h1:pAM+oBNPrpXRs+E/8spkeGx9QgekbRVyr74EUvRVOUI=
|
||||
github.com/onsi/gomega v1.26.0 h1:03cDLK28U6hWvCAns6NeydX3zIm4SF3ci69ulidS32Q=
|
||||
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/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
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.14.0/go.mod h1:Ff03zEi5NwSOfXj9nFpBfhbWTtROCkg9N+9goggrYn4=
|
||||
github.com/securego/gosec/v2 v2.15.0 h1:v4Ym7FF58/jlykYmmhZ7mTm7FQvN/setNm++0fgIAtw=
|
||||
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.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
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-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
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-20230131160201-f062dba9d201/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-20230131160201-f062dba9d201/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
|
||||
golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb h1:PaBZQdo+iSDyHT053FjUCgZQ/9uqVwPOcl7KSWhKn6w=
|
||||
golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||
golang.org/x/exp/typeparams v0.0.0-20230213192124-5e25df0256eb h1:WGs/bGIWYyAY5PVgGGMXqGGCxSJz4fpoUExb/vgqNCU=
|
||||
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.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.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA=
|
||||
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
|
||||
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-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-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-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-20200625203802-6e8e738ad208/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-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.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
|
||||
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
||||
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/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.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.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-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.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
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.5.1-0.20230117180257-8aba49bb5ea2/go.mod h1:N+Kgy78s5I24c24dU8OfWNEotWjutIs8SnJvn5IDq+k=
|
||||
golang.org/x/vuln v0.0.0-20230130175424-dd534eeddf33 h1:je2aB5nnlseeGvJy5clg6EyC3jjbbCNsRDroC3qQJsA=
|
||||
golang.org/x/vuln v0.0.0-20230130175424-dd534eeddf33/go.mod h1:cBP4HMKv0X+x96j8IJWCKk0eqpakBmmHjKGSSC0NaYE=
|
||||
golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/vuln v0.0.0-20230213165600-1a019b0c7f30 h1:Q4B8LhSjZGto/+P5REBb4N51ec2H4efRqjBIeJ6nv/Y=
|
||||
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-20191011141410-1b5146add898/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/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.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
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.3.3/go.mod h1:jzwdWgg7Jdq75wlfblQxO4neNaFFSvgc1tD5Wv8U0Yw=
|
||||
honnef.co/go/tools v0.4.1 h1:HPeloSr0mLOEMOkhT9Au5aeki44kvP6ka3v1xIsM6Zo=
|
||||
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/go.mod h1:PljLOHDeZqgS8opHRKLzp2It2VBuSdteAgqUfzMTxlQ=
|
||||
mvdan.cc/unparam v0.0.0-20230125043941-70a0ce6e7b95 h1:n/xhncJPSt0YzfOhnyn41XxUdrWQNgmLBG72FE27Fqw=
|
||||
|
@ -205,7 +205,7 @@ code from [the repo][companiesrepo].
|
||||
### Usage
|
||||
|
||||
```sh
|
||||
( cd scripts/companiesdb && sh ./download.sh )
|
||||
sh ./scripts/companiesdb/download.sh
|
||||
```
|
||||
|
||||
[companiesrepo]: https://github.com/AdguardTeam/companiesdb
|
||||
|
@ -4,11 +4,9 @@ set -e -f -u -x
|
||||
|
||||
# This script syncs companies DB that we bundle with AdGuard Home. The source
|
||||
# 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'
|
||||
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"
|
||||
curl -o "$output" -v "$trackers_url"
|
||||
|
@ -338,6 +338,18 @@ download_wget() {
|
||||
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
|
||||
# files.
|
||||
set_download_func() {
|
||||
@ -348,6 +360,9 @@ set_download_func() {
|
||||
elif is_command 'wget'
|
||||
then
|
||||
download_func='download_wget'
|
||||
elif is_command 'fetch'
|
||||
then
|
||||
download_func='download_fetch'
|
||||
else
|
||||
error_exit "either curl or wget is required to install AdGuard Home via this script"
|
||||
fi
|
||||
|
@ -227,8 +227,9 @@ gocyclo --over 13 ./internal/dhcpd ./internal/filtering/ ./internal/home/
|
||||
# Apply stricter standards to new or somewhat refactored code.
|
||||
gocyclo --over 10 ./internal/aghio/ ./internal/aghnet/ ./internal/aghos/\
|
||||
./internal/aghtest/ ./internal/dnsforward/ ./internal/filtering/rewrite/\
|
||||
./internal/stats/ ./internal/tools/ ./internal/updater/ ./internal/version/\
|
||||
./scripts/vetted-filters/ ./main.go
|
||||
./internal/stats/ ./internal/tools/ ./internal/updater/\
|
||||
./internal/version/ ./scripts/blocked-services/ ./scripts/vetted-filters/\
|
||||
./main.go
|
||||
|
||||
ineffassign ./...
|
||||
|
||||
|
@ -40,7 +40,7 @@ readonly go
|
||||
count_flags='--count=1'
|
||||
cover_flags='--coverprofile=./coverage.txt'
|
||||
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
|
||||
|
||||
"$go" test "$count_flags" "$cover_flags" "$race_flags" "$shuffle_flags" "$timeout_flags"\
|
||||
|
Loading…
Reference in New Issue
Block a user