Compare commits

..

20 Commits

Author SHA1 Message Date
Dimitry Kolyshev
94458c5658 Merge remote-tracking branch 'origin/master' into 3389-querylog-export
# Conflicts:
#	CHANGELOG.md
2023-06-22 13:28:17 +04:00
Dimitry Kolyshev
6fea435d89 Merge remote-tracking branch 'origin/master' into 3389-querylog-export
# Conflicts:
#	CHANGELOG.md
2023-06-15 14:40:06 +04:00
Dimitry Kolyshev
05706bd7ea querylog: bench search 2023-06-15 14:38:44 +04:00
Dimitry Kolyshev
00327757e1 querylog: bench search 2023-06-14 12:07:16 +04:00
Dimitry Kolyshev
5f0e53ded7 home: write timeout middleware 2023-06-14 10:51:17 +04:00
Dimitry Kolyshev
5cd4ce766d Merge remote-tracking branch 'origin/master' into 3389-querylog-export 2023-06-14 10:08:11 +04:00
Dimitry Kolyshev
e695fd9885 querylog: timeout revert 2023-06-14 10:07:22 +04:00
Dimitry Kolyshev
c43053e7d2 querylog: timeout 2023-06-13 14:36:31 +04:00
Dimitry Kolyshev
86e25944b3 querylog: imp code 2023-06-13 13:08:15 +04:00
Dimitry Kolyshev
fd7260f6de Merge remote-tracking branch 'origin/master' into 3389-querylog-export
# Conflicts:
#	CHANGELOG.md
2023-06-13 10:38:17 +04:00
Dimitry Kolyshev
c591e46254 querylog: imp code 2023-06-02 15:26:04 +03:00
Dimitry Kolyshev
66d9ea7cca querylog: imp docs 2023-06-02 15:15:37 +03:00
Dimitry Kolyshev
dafc785845 querylog: imp code docs 2023-06-02 12:09:50 +03:00
Dimitry Kolyshev
e9b17891bb Merge remote-tracking branch 'origin/master' into 3389-querylog-export 2023-06-02 12:00:57 +03:00
Dimitry Kolyshev
0b27f048a7 querylog: imp code 2023-05-31 16:26:56 +03:00
Dimitry Kolyshev
649454e77b querylog: imp code 2023-05-31 15:11:33 +03:00
Dimitry Kolyshev
ca22d8524d all: imp docs 2023-05-31 15:11:15 +03:00
Dimitry Kolyshev
07f4f0474c all: imp docs 2023-05-31 12:06:13 +03:00
Dimitry Kolyshev
8813e135b6 querylog: export 2023-05-31 11:49:15 +03:00
Dimitry Kolyshev
f4f2c11eb9 all: export querylog docs 2023-05-29 13:29:41 +03:00
127 changed files with 2161 additions and 4410 deletions

5
.gitignore vendored
View File

@@ -16,13 +16,10 @@
/dist/ /dist/
/filtering/tests/filtering.TestLotsOfRules*.pprof /filtering/tests/filtering.TestLotsOfRules*.pprof
/filtering/tests/top-1m.csv /filtering/tests/top-1m.csv
/internal/next/AdGuardHome.yaml
/launchpad_credentials /launchpad_credentials
/querylog.json* /querylog.json*
/snapcraft_login /snapcraft_login
AdGuardHome AdGuardHome*
AdGuardHome.exe
AdGuardHome.yaml*
coverage.txt coverage.txt
node_modules/ node_modules/

View File

@@ -14,94 +14,32 @@ and this project adheres to
<!-- <!--
## [v0.108.0] - TBA ## [v0.108.0] - TBA
## [v0.107.34] - 2023-07-26 (APPROX.) ## [v0.107.33] - 2023-06-28 (APPROX.)
See also the [v0.107.34 GitHub milestone][ms-v0.107.34]. See also the [v0.107.33 GitHub milestone][ms-v0.107.33].
[ms-v0.107.34]: https://github.com/AdguardTeam/AdGuardHome/milestone/69?closed=1 [ms-v0.107.33]: https://github.com/AdguardTeam/AdGuardHome/milestone/68?closed=1
NOTE: Add new changes BELOW THIS COMMENT. NOTE: Add new changes BELOW THIS COMMENT.
--> -->
<!--
NOTE: Add new changes ABOVE THIS COMMENT.
-->
## [v0.107.33] - 2023-07-03
See also the [v0.107.33 GitHub milestone][ms-v0.107.33].
### Added ### Added
- The new command-line flag `--web-addr` is the address to serve the web UI on, - The new HTTP API, `GET /control/querylog/export`, which can be used to
in the host:port format. export query log items. See `openapi/openapi.yaml` for the full description
- The ability to set inactivity periods for filtering blocked services, both ([#3389]).
globally and per client, in the configuration file ([#951]). The UI changes - The ability to set inactivity periods for filtering blocked services in the
are coming in the upcoming releases. configuration file ([#951]). The UI changes are coming in the upcoming
releases.
- The ability to edit rewrite rules via `PUT /control/rewrite/update` HTTP API - The ability to edit rewrite rules via `PUT /control/rewrite/update` HTTP API
and the Web UI ([#1577]). ([#1577]).
### Changed ### Changed
#### Configuration Changes #### Configuration Changes
In this release, the schema version has changed from 20 to 23. In this release, the schema version has changed from 20 to 21.
- Properties `bind_host`, `bind_port`, and `web_session_ttl` which used to setup
web UI binding configuration, are now moved to a new object `http` containing
new properties `address` and `session_ttl`:
```yaml
# BEFORE:
'bind_host': '1.2.3.4'
'bind_port': 8080
'web_session_ttl': 720
# AFTER:
'http':
'address': '1.2.3.4:8080'
'session_ttl': '720h'
```
Note that the new `http.session_ttl` property is now a duration string. To
rollback this change, remove the new object `http`, set back `bind_host`,
`bind_port`, `web_session_ttl`, and change the `schema_version` back to `22`.
- Property `clients.persistent.blocked_services`, which in schema versions 21
and earlier used to be a list containing ids of blocked services, is now an
object containing ids and schedule for blocked services:
```yaml
# BEFORE:
'clients':
'persistent':
- 'name': 'client-name'
'blocked_services':
- id_1
- id_2
# AFTER:
'clients':
'persistent':
- 'name': client-name
'blocked_services':
'ids':
- id_1
- id_2
'schedule':
'time_zone': 'Local'
'sun':
'start': '0s'
'end': '24h'
'mon':
'start': '1h'
'end': '23h'
```
To rollback this change, replace `clients.persistent.blocked_services` object
with the list of ids of blocked services and change the `schema_version` back
to `21`.
- Property `dns.blocked_services`, which in schema versions 20 and earlier used - Property `dns.blocked_services`, which in schema versions 20 and earlier used
to be a list containing ids of blocked services, is now an object containing to be a list containing ids of blocked services, is now an object containing
ids and schedule for blocked services: ids and schedule for blocked services:
@@ -145,23 +83,8 @@ In this release, the schema version has changed from 20 to 23.
To rollback this change, replace `dns.blocked_services` object with the list To rollback this change, replace `dns.blocked_services` object with the list
of ids of blocked services and change the `schema_version` back to `20`. of ids of blocked services and change the `schema_version` back to `20`.
### Deprecated
- `HEALTHCHECK` and `ENTRYPOINT` sections in `Dockerfile` ([#5939]). They cause
a lot of issues, especially with tools like `docker-compose` and `podman`, and
will be removed in a future release.
- Flags `-h`, `--host`, `-p`, `--port` have been deprecated. The `-h` flag
will work as an alias for `--help`, instead of the deprecated `--host` in the
future releases.
### Fixed ### Fixed
- Ignoring of `/etc/hosts` file when resolving the hostnames of upstream DNS
servers ([#5902]).
- Excessive error logging when using DNS-over-QUIC ([#5285]).
- Inability to set `bind_host` in `AdGuardHome.yaml` in Docker ([#4231],
[#4235]).
- The blocklists can now be deleted properly ([#5700]).
- Queries with the question-section target `.`, for example `NS .`, are now - Queries with the question-section target `.`, for example `NS .`, are now
counted in the statistics and correctly shown in the query log ([#5910]). counted in the statistics and correctly shown in the query log ([#5910]).
- Safe Search not working with `AAAA` queries for domains that don't have `AAAA` - Safe Search not working with `AAAA` queries for domains that don't have `AAAA`
@@ -169,16 +92,13 @@ In this release, the schema version has changed from 20 to 23.
[#951]: https://github.com/AdguardTeam/AdGuardHome/issues/951 [#951]: https://github.com/AdguardTeam/AdGuardHome/issues/951
[#1577]: https://github.com/AdguardTeam/AdGuardHome/issues/1577 [#1577]: https://github.com/AdguardTeam/AdGuardHome/issues/1577
[#4231]: https://github.com/AdguardTeam/AdGuardHome/issues/4231 [#3389]: https://github.com/AdguardTeam/AdGuardHome/issues/3389
[#4235]: https://github.com/AdguardTeam/AdGuardHome/pull/4235
[#5285]: https://github.com/AdguardTeam/AdGuardHome/issues/5285
[#5700]: https://github.com/AdguardTeam/AdGuardHome/issues/5700
[#5902]: https://github.com/AdguardTeam/AdGuardHome/issues/5902
[#5910]: https://github.com/AdguardTeam/AdGuardHome/issues/5910 [#5910]: https://github.com/AdguardTeam/AdGuardHome/issues/5910
[#5913]: https://github.com/AdguardTeam/AdGuardHome/issues/5913 [#5913]: https://github.com/AdguardTeam/AdGuardHome/issues/5913
[#5939]: https://github.com/AdguardTeam/AdGuardHome/discussions/5939
[ms-v0.107.33]: https://github.com/AdguardTeam/AdGuardHome/milestone/68?closed=1 <!--
NOTE: Add new changes ABOVE THIS COMMENT.
-->
@@ -2160,12 +2080,11 @@ See also the [v0.104.2 GitHub milestone][ms-v0.104.2].
<!-- <!--
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.34...HEAD
[v0.107.34]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.33...v0.107.34
-->
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.33...HEAD [Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.33...HEAD
[v0.107.33]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.32...v0.107.33 [v0.107.33]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.32...v0.107.33
-->
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.32...HEAD
[v0.107.32]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.31...v0.107.32 [v0.107.32]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.31...v0.107.32
[v0.107.31]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.30...v0.107.31 [v0.107.31]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.30...v0.107.31
[v0.107.30]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.29...v0.107.30 [v0.107.30]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.29...v0.107.30

View File

@@ -475,9 +475,7 @@
"setup_dns_notice": "Каб выкарыстоўваць <1>DNS-over-HTTPS</1> ці <1>DNS-over-TLS</1>, вам патрэбна <0>наладзіць шыфраванне</0> у наладах AdGuard Home.", "setup_dns_notice": "Каб выкарыстоўваць <1>DNS-over-HTTPS</1> ці <1>DNS-over-TLS</1>, вам патрэбна <0>наладзіць шыфраванне</0> у наладах AdGuard Home.",
"rewrite_added": "Правіла перанакіравання DNS для «{{key}}» паспяхова дададзена", "rewrite_added": "Правіла перанакіравання DNS для «{{key}}» паспяхова дададзена",
"rewrite_deleted": "Правіла перанакіравання DNS для «{{key}}» паспяхова выдалена", "rewrite_deleted": "Правіла перанакіравання DNS для «{{key}}» паспяхова выдалена",
"rewrite_updated": "Перазапіс DNS паспяхова абноўлены",
"rewrite_add": "Дадаць правіла перанакіравання DNS", "rewrite_add": "Дадаць правіла перанакіравання DNS",
"rewrite_edit": "Рэдагаваць перазапіс DNS",
"rewrite_not_found": "Не знойдзена правілаў перанакіравання DNS", "rewrite_not_found": "Не знойдзена правілаў перанакіравання DNS",
"rewrite_confirm_delete": "Вы ўпэўнены, што хочаце выдаліць правіла перанакіравання DNS для «{{key}}»?", "rewrite_confirm_delete": "Вы ўпэўнены, што хочаце выдаліць правіла перанакіравання DNS для «{{key}}»?",
"rewrite_desc": "Дазваляе лёгка наладзіць карыстацкі DNS-адказ для пэўнага дамена.", "rewrite_desc": "Дазваляе лёгка наладзіць карыстацкі DNS-адказ для пэўнага дамена.",

View File

@@ -478,9 +478,7 @@
"setup_dns_notice": "Pro použití <1>DNS skrze HTTPS</1> nebo <1>DNS skrze TLS</1> potřebujete v nastaveních AdGuard Home <0>nakonfigurovat šifrování</0>.", "setup_dns_notice": "Pro použití <1>DNS skrze HTTPS</1> nebo <1>DNS skrze TLS</1> potřebujete v nastaveních AdGuard Home <0>nakonfigurovat šifrování</0>.",
"rewrite_added": "Přesměrování DNS pro „{{key}}“ úspěšně přidáno", "rewrite_added": "Přesměrování DNS pro „{{key}}“ úspěšně přidáno",
"rewrite_deleted": "Přesměrování DNS pro „{{key}}“ úspěšně smazáno", "rewrite_deleted": "Přesměrování DNS pro „{{key}}“ úspěšně smazáno",
"rewrite_updated": "Přesměrování DNS bylo úspěšně aktualizováno",
"rewrite_add": "Přidat přesměrování DNS", "rewrite_add": "Přidat přesměrování DNS",
"rewrite_edit": "Upravit přesměrování DNS",
"rewrite_not_found": "Přesměrování DNS nenalezeny", "rewrite_not_found": "Přesměrování DNS nenalezeny",
"rewrite_confirm_delete": "Jste si jisti, že chcete smazat přesměrování DNS pro „{{key}}“?", "rewrite_confirm_delete": "Jste si jisti, že chcete smazat přesměrování DNS pro „{{key}}“?",
"rewrite_desc": "Umožňuje snadno nakonfigurovat vlastní DNS odezvy pro konkrétní název domény.", "rewrite_desc": "Umožňuje snadno nakonfigurovat vlastní DNS odezvy pro konkrétní název domény.",

View File

@@ -478,9 +478,7 @@
"setup_dns_notice": "For at kunne bruge <1>DNS-over-HTTPS</1> eller <1>DNS-over-TLS</1>, skal du <0>opsætte Krypteringen</0> i AdGuard Homes indstillinger.", "setup_dns_notice": "For at kunne bruge <1>DNS-over-HTTPS</1> eller <1>DNS-over-TLS</1>, skal du <0>opsætte Krypteringen</0> i AdGuard Homes indstillinger.",
"rewrite_added": "DNS-omskrivning for \"{{key}}\" blev tilføjet", "rewrite_added": "DNS-omskrivning for \"{{key}}\" blev tilføjet",
"rewrite_deleted": "DNS-omskrivning for \"{{key}}\" blev slettet", "rewrite_deleted": "DNS-omskrivning for \"{{key}}\" blev slettet",
"rewrite_updated": "DNS-omskrivning hermed opdateret",
"rewrite_add": "Tilføj DNS-omskrivning", "rewrite_add": "Tilføj DNS-omskrivning",
"rewrite_edit": "Redigér DNS-omskrivning",
"rewrite_not_found": "Ingen DNS-omskrivninger fundet", "rewrite_not_found": "Ingen DNS-omskrivninger fundet",
"rewrite_confirm_delete": "Sikker på, at du vil slette DNS-omskrivning for \"{{key}}\"?", "rewrite_confirm_delete": "Sikker på, at du vil slette DNS-omskrivning for \"{{key}}\"?",
"rewrite_desc": "Gør det nemt at opsætte det tilpassede DNS-svar for et specifikt domænenavn.", "rewrite_desc": "Gør det nemt at opsætte det tilpassede DNS-svar for et specifikt domænenavn.",

View File

@@ -478,9 +478,7 @@
"setup_dns_notice": "Um <1>DNS-over-HTTTPS</1> oder <1>DNS-over-TLS</1> verwenden zu können, müssen Sie in den AdGuard Home Einstellungen die <0>Verschlüsselung konfigurieren</0>.", "setup_dns_notice": "Um <1>DNS-over-HTTTPS</1> oder <1>DNS-over-TLS</1> verwenden zu können, müssen Sie in den AdGuard Home Einstellungen die <0>Verschlüsselung konfigurieren</0>.",
"rewrite_added": "DNS-Umschreibung für „{{key}}“ erfolgreich hinzugefügt", "rewrite_added": "DNS-Umschreibung für „{{key}}“ erfolgreich hinzugefügt",
"rewrite_deleted": "DNS-Umschreibung für „{{key}}“ erfolgreich entfernt", "rewrite_deleted": "DNS-Umschreibung für „{{key}}“ erfolgreich entfernt",
"rewrite_updated": "DNS-Rewrite erfolgreich aktualisiert",
"rewrite_add": "DNS-Umschreibung hinzufügen", "rewrite_add": "DNS-Umschreibung hinzufügen",
"rewrite_edit": "DNS-Rewrite bearbeiten",
"rewrite_not_found": "Keine DNS-Umschreibungen gefunden", "rewrite_not_found": "Keine DNS-Umschreibungen gefunden",
"rewrite_confirm_delete": "Möchten Sie die DNS-Umschreibung für „{{key}}“ wirklich entfernen?", "rewrite_confirm_delete": "Möchten Sie die DNS-Umschreibung für „{{key}}“ wirklich entfernen?",
"rewrite_desc": "Ermöglicht die einfache Konfiguration der benutzerdefinierten DNS-Antwort für einen bestimmten Domainnamen.", "rewrite_desc": "Ermöglicht die einfache Konfiguration der benutzerdefinierten DNS-Antwort für einen bestimmten Domainnamen.",

View File

@@ -478,9 +478,7 @@
"setup_dns_notice": "In order to use <1>DNS-over-HTTPS</1> or <1>DNS-over-TLS</1>, you need to <0>configure Encryption</0> in AdGuard Home settings.", "setup_dns_notice": "In order to use <1>DNS-over-HTTPS</1> or <1>DNS-over-TLS</1>, you need to <0>configure Encryption</0> in AdGuard Home settings.",
"rewrite_added": "DNS rewrite for \"{{key}}\" successfully added", "rewrite_added": "DNS rewrite for \"{{key}}\" successfully added",
"rewrite_deleted": "DNS rewrite for \"{{key}}\" successfully deleted", "rewrite_deleted": "DNS rewrite for \"{{key}}\" successfully deleted",
"rewrite_updated": "DNS rewrite successfully updated",
"rewrite_add": "Add DNS rewrite", "rewrite_add": "Add DNS rewrite",
"rewrite_edit": "Edit DNS rewrite",
"rewrite_not_found": "No DNS rewrites found", "rewrite_not_found": "No DNS rewrites found",
"rewrite_confirm_delete": "Are you sure you want to delete DNS rewrite for \"{{key}}\"?", "rewrite_confirm_delete": "Are you sure you want to delete DNS rewrite for \"{{key}}\"?",
"rewrite_desc": "Allows to easily configure custom DNS response for a specific domain name.", "rewrite_desc": "Allows to easily configure custom DNS response for a specific domain name.",

View File

@@ -478,9 +478,7 @@
"setup_dns_notice": "Para utilizar <1>DNS mediante HTTPS</1> o <1>DNS mediante TLS</1>, debes <0>configurar el cifrado</0> en la configuración de AdGuard Home.", "setup_dns_notice": "Para utilizar <1>DNS mediante HTTPS</1> o <1>DNS mediante TLS</1>, debes <0>configurar el cifrado</0> en la configuración de AdGuard Home.",
"rewrite_added": "Reescritura DNS para \"{{key}}\" añadido correctamente", "rewrite_added": "Reescritura DNS para \"{{key}}\" añadido correctamente",
"rewrite_deleted": "Reescritura DNS para \"{{key}}\" eliminado correctamente", "rewrite_deleted": "Reescritura DNS para \"{{key}}\" eliminado correctamente",
"rewrite_updated": "Reconfiguración de DNS actualizada correctamente",
"rewrite_add": "Añadir reescritura DNS", "rewrite_add": "Añadir reescritura DNS",
"rewrite_edit": "Editar reconfiguración de DNS",
"rewrite_not_found": "No se han encontrado reescrituras DNS", "rewrite_not_found": "No se han encontrado reescrituras DNS",
"rewrite_confirm_delete": "¿Estás seguro de que deseas eliminar la reescritura DNS para \"{{key}}\"?", "rewrite_confirm_delete": "¿Estás seguro de que deseas eliminar la reescritura DNS para \"{{key}}\"?",
"rewrite_desc": "Permite configurar fácilmente la respuesta DNS personalizada para un nombre de dominio específico.", "rewrite_desc": "Permite configurar fácilmente la respuesta DNS personalizada para un nombre de dominio específico.",

View File

@@ -440,9 +440,7 @@
"setup_dns_notice": "به منظور استفاده از <1>DNS-over-HTTPS</1> یا <1>DNS-over-TLS</1>، شما نیاز به <0>پیکربندی رمزگذاری</0> در تنظیمات AdGuard Home دارید.", "setup_dns_notice": "به منظور استفاده از <1>DNS-over-HTTPS</1> یا <1>DNS-over-TLS</1>، شما نیاز به <0>پیکربندی رمزگذاری</0> در تنظیمات AdGuard Home دارید.",
"rewrite_added": "بازنویسی DNS برای \"{{key}}\" با موفقیت اضافه شد", "rewrite_added": "بازنویسی DNS برای \"{{key}}\" با موفقیت اضافه شد",
"rewrite_deleted": "بازنویسی DNS برای \"{{key}}\" با موفقیت حذف شد", "rewrite_deleted": "بازنویسی DNS برای \"{{key}}\" با موفقیت حذف شد",
"rewrite_updated": "بازنویسی DNS با موفقیت به روز شد",
"rewrite_add": "افزودن بازنویسی DNS", "rewrite_add": "افزودن بازنویسی DNS",
"rewrite_edit": "بازنویسی DNS را ویرایش کنید",
"rewrite_not_found": "بازنویسی DNS یافت نشد", "rewrite_not_found": "بازنویسی DNS یافت نشد",
"rewrite_confirm_delete": "آیا واقعا میخواهید بازنویسی DNS برای \"{{key}}\" را حذف کنید؟", "rewrite_confirm_delete": "آیا واقعا میخواهید بازنویسی DNS برای \"{{key}}\" را حذف کنید؟",
"rewrite_desc": "به آسانی اجازه پیکربندی پاسخ DNS دستی برای یک نام دامنه خاص را می دهد.", "rewrite_desc": "به آسانی اجازه پیکربندی پاسخ DNS دستی برای یک نام دامنه خاص را می دهد.",

View File

@@ -222,7 +222,7 @@
"all_lists_up_to_date_toast": "Kaikki listat ovat ajan tasalla", "all_lists_up_to_date_toast": "Kaikki listat ovat ajan tasalla",
"updated_upstream_dns_toast": "Ylävirtojen palvelimet tallennettiin", "updated_upstream_dns_toast": "Ylävirtojen palvelimet tallennettiin",
"dns_test_ok_toast": "Määritetyt DNS-palvelimet toimivat oikein", "dns_test_ok_toast": "Määritetyt DNS-palvelimet toimivat oikein",
"dns_test_not_ok_toast": "Palvelin \"{{key}}\": Ei voitu käyttää, tarkista oikeinkirjoitus", "dns_test_not_ok_toast": "Palvelin \"{{key}}\": ei voitu käyttää, tarkista sen oikeinkirjoitus",
"dns_test_warning_toast": "Datavuon \"{{key}}\" ei vastaa testipyyntöihin eikä välttämättä toimi kunnolla", "dns_test_warning_toast": "Datavuon \"{{key}}\" ei vastaa testipyyntöihin eikä välttämättä toimi kunnolla",
"unblock": "Salli", "unblock": "Salli",
"block": "Estä", "block": "Estä",
@@ -478,9 +478,7 @@
"setup_dns_notice": "<1>DNS-over-HTTPS</1> tai <1>DNS-over-TLS</1> -toteutuksia varten, on AdGuard Homen <0>Salausasetukset</0> määritettävä.", "setup_dns_notice": "<1>DNS-over-HTTPS</1> tai <1>DNS-over-TLS</1> -toteutuksia varten, on AdGuard Homen <0>Salausasetukset</0> määritettävä.",
"rewrite_added": "Kohteen \"{{key}}\" DNS-uudelleenohjaus lisättiin", "rewrite_added": "Kohteen \"{{key}}\" DNS-uudelleenohjaus lisättiin",
"rewrite_deleted": "Kohteen \"{{key}}\" DNS-uudelleenohjaus poistettiin", "rewrite_deleted": "Kohteen \"{{key}}\" DNS-uudelleenohjaus poistettiin",
"rewrite_updated": "DNS-uudelleenohjaukset päivitettiin",
"rewrite_add": "Lisää DNS-uudelleenohjaus", "rewrite_add": "Lisää DNS-uudelleenohjaus",
"rewrite_edit": "Muokkaa DNS-uudelleenohjausta",
"rewrite_not_found": "DNS-uudelleenohjauksia ei löytynyt", "rewrite_not_found": "DNS-uudelleenohjauksia ei löytynyt",
"rewrite_confirm_delete": "Haluatko varmasti poistaa DNS-uudelleenohjauksen kohteelle \"{{key}}\"?", "rewrite_confirm_delete": "Haluatko varmasti poistaa DNS-uudelleenohjauksen kohteelle \"{{key}}\"?",
"rewrite_desc": "Mahdollistaa oman DNS-vastauksen helpon määrityksen tietylle verkkotunnukselle.", "rewrite_desc": "Mahdollistaa oman DNS-vastauksen helpon määrityksen tietylle verkkotunnukselle.",

View File

@@ -478,9 +478,7 @@
"setup_dns_notice": "Pour utiliser le <1>DNS-over-HTTPS</1> ou le <1>DNS-over-TLS</1>, vous devez <0>configurer le Chiffrement</0> dans les paramètres de AdGuard Home.", "setup_dns_notice": "Pour utiliser le <1>DNS-over-HTTPS</1> ou le <1>DNS-over-TLS</1>, vous devez <0>configurer le Chiffrement</0> dans les paramètres de AdGuard Home.",
"rewrite_added": "Réécriture DNS pour « {{key}} » ajoutée", "rewrite_added": "Réécriture DNS pour « {{key}} » ajoutée",
"rewrite_deleted": "Réécriture DNS pour « {{key}} » supprimée", "rewrite_deleted": "Réécriture DNS pour « {{key}} » supprimée",
"rewrite_updated": "Réécriture DNS mise à jour",
"rewrite_add": "Ajouter une réécriture DNS", "rewrite_add": "Ajouter une réécriture DNS",
"rewrite_edit": "Modifier la réécriture DNS",
"rewrite_not_found": "Aucune réécriture DNS trouvée", "rewrite_not_found": "Aucune réécriture DNS trouvée",
"rewrite_confirm_delete": "Voulez-vous vraiment supprimer la réécriture DNS pour « {{key}} » ?", "rewrite_confirm_delete": "Voulez-vous vraiment supprimer la réécriture DNS pour « {{key}} » ?",
"rewrite_desc": "Permet de configurer facilement la réponse DNS personnalisée pour un nom de domaine spécifique.", "rewrite_desc": "Permet de configurer facilement la réponse DNS personnalisée pour un nom de domaine spécifique.",

View File

@@ -478,9 +478,7 @@
"setup_dns_notice": "Da biste koristili <1>DNS-over-HTTPS</1> ili <1>DNS-over-TLS</1>, morate <0>postaviti šifriranje</0> u AdGuard Home postavkama.", "setup_dns_notice": "Da biste koristili <1>DNS-over-HTTPS</1> ili <1>DNS-over-TLS</1>, morate <0>postaviti šifriranje</0> u AdGuard Home postavkama.",
"rewrite_added": "DNS prijepis za \"{{key}}\" je uspješno dodan", "rewrite_added": "DNS prijepis za \"{{key}}\" je uspješno dodan",
"rewrite_deleted": "DNS prijepis za \"{{key}}\" je uspješno uklonjen", "rewrite_deleted": "DNS prijepis za \"{{key}}\" je uspješno uklonjen",
"rewrite_updated": "Prepisivanje DNS-a uspješno ažurirano",
"rewrite_add": "Dodaj DNS prijepis", "rewrite_add": "Dodaj DNS prijepis",
"rewrite_edit": "Uredite prepisivanje DNS-a",
"rewrite_not_found": "Nema DNS prijepisa", "rewrite_not_found": "Nema DNS prijepisa",
"rewrite_confirm_delete": "Jeste li sigurni da želite ukloniti DNS prijepis za \"{{key}}\" klijenta?", "rewrite_confirm_delete": "Jeste li sigurni da želite ukloniti DNS prijepis za \"{{key}}\" klijenta?",
"rewrite_desc": "Omogućuje jednostavno postavljanje prilagođenog DNS odgovora za određenu domenu.", "rewrite_desc": "Omogućuje jednostavno postavljanje prilagođenog DNS odgovora za određenu domenu.",

View File

@@ -167,7 +167,6 @@
"enabled_parental_toast": "Szülői felügyelet engedélyezve", "enabled_parental_toast": "Szülői felügyelet engedélyezve",
"disabled_safe_search_toast": "Biztonságos keresés letiltva", "disabled_safe_search_toast": "Biztonságos keresés letiltva",
"enabled_save_search_toast": "Biztonságos keresés engedélyezve", "enabled_save_search_toast": "Biztonságos keresés engedélyezve",
"updated_save_search_toast": "A Biztonságos keresés beállításai frissítve",
"enabled_table_header": "Engedélyezve", "enabled_table_header": "Engedélyezve",
"name_table_header": "Név", "name_table_header": "Név",
"list_url_table_header": "Lista URL-je", "list_url_table_header": "Lista URL-je",
@@ -291,8 +290,6 @@
"rate_limit": "Kérések korlátozása", "rate_limit": "Kérések korlátozása",
"edns_enable": "EDNS kliens alhálózat engedélyezése", "edns_enable": "EDNS kliens alhálózat engedélyezése",
"edns_cs_desc": "Adja hozzá az EDNS Client Subnet beállítást (ECS) a felfelé irányuló kérésekhez, és naplózza a kliensek által küldött értékeket a lekérdezési naplóban.", "edns_cs_desc": "Adja hozzá az EDNS Client Subnet beállítást (ECS) a felfelé irányuló kérésekhez, és naplózza a kliensek által küldött értékeket a lekérdezési naplóban.",
"edns_use_custom_ip": "Használjon egyéni IP-címet az EDNS-hez",
"edns_use_custom_ip_desc": "Engedélyezze az egyéni IP-cím használatát az EDNS-hez",
"rate_limit_desc": "Maximálisan hány kérést küldhet egy kliens másodpercenkén. Ha 0-ra állítja, akkor nincs korlátozás.", "rate_limit_desc": "Maximálisan hány kérést küldhet egy kliens másodpercenkén. Ha 0-ra állítja, akkor nincs korlátozás.",
"blocking_ipv4_desc": "A blokkolt A kéréshez visszaadandó IP-cím", "blocking_ipv4_desc": "A blokkolt A kéréshez visszaadandó IP-cím",
"blocking_ipv6_desc": "A blokkolt AAAA kéréshez visszaadandó IP-cím", "blocking_ipv6_desc": "A blokkolt AAAA kéréshez visszaadandó IP-cím",
@@ -478,9 +475,7 @@
"setup_dns_notice": "Ahhoz, hogy a <1>DNS-over-HTTPS</1> vagy a <1>DNS-over-TLS</1> valamelyikét használja, muszáj <0>beállítania a titkosítást</0> az AdGuard Home beállításaiban.", "setup_dns_notice": "Ahhoz, hogy a <1>DNS-over-HTTPS</1> vagy a <1>DNS-over-TLS</1> valamelyikét használja, muszáj <0>beállítania a titkosítást</0> az AdGuard Home beállításaiban.",
"rewrite_added": "DNS átírás a(z) \"{{key}}\" kulcshoz sikeresen hozzáadva", "rewrite_added": "DNS átírás a(z) \"{{key}}\" kulcshoz sikeresen hozzáadva",
"rewrite_deleted": "DNS átírás a(z) \"{{key}}\" kulcshoz sikeresen törölve", "rewrite_deleted": "DNS átírás a(z) \"{{key}}\" kulcshoz sikeresen törölve",
"rewrite_updated": "A DNS újraírása sikeresen frissítve",
"rewrite_add": "DNS átírás hozzáadása", "rewrite_add": "DNS átírás hozzáadása",
"rewrite_edit": "DNS újraírás szerkesztése",
"rewrite_not_found": "Nem találhatók DNS átírások", "rewrite_not_found": "Nem találhatók DNS átírások",
"rewrite_confirm_delete": "Biztosan törölni szeretné a DNS átírást ehhez: \"{{key}}\"?", "rewrite_confirm_delete": "Biztosan törölni szeretné a DNS átírást ehhez: \"{{key}}\"?",
"rewrite_desc": "Lehetővé teszi, hogy egyszerűen beállítson egyéni DNS választ egy adott domain névhez.", "rewrite_desc": "Lehetővé teszi, hogy egyszerűen beállítson egyéni DNS választ egy adott domain névhez.",
@@ -528,10 +523,6 @@
"statistics_retention_confirm": "Biztos benne, hogy megváltoztatja a statisztika megőrzési idejét? Ha csökkentette az értéket, a megadottnál korábbi adatok elvesznek", "statistics_retention_confirm": "Biztos benne, hogy megváltoztatja a statisztika megőrzési idejét? Ha csökkentette az értéket, a megadottnál korábbi adatok elvesznek",
"statistics_cleared": "A statisztikák sikeresen vissza lettek állítva", "statistics_cleared": "A statisztikák sikeresen vissza lettek állítva",
"statistics_enable": "Statisztikák engedélyezése", "statistics_enable": "Statisztikák engedélyezése",
"ignore_domains": "Figyelmen kívül hagyott domainek (újsorral elválasztva)",
"ignore_domains_title": "Figyelmen kívül hagyott domainek",
"ignore_domains_desc_stats": "Az ezekre a tartományokra vonatkozó lekérdezések nem kerülnek a statisztikákba",
"ignore_domains_desc_query": "Az ezekhez a tartományokhoz tartozó lekérdezések nem kerülnek a lekérdezési naplóba",
"interval_hours": "{{count}} óra", "interval_hours": "{{count}} óra",
"interval_hours_plural": "{{count}} óra", "interval_hours_plural": "{{count}} óra",
"filters_configuration": "Szűrők beállításai", "filters_configuration": "Szűrők beállításai",
@@ -652,29 +643,5 @@
"confirm_dns_cache_clear": "Biztos benne, hogy törölni szeretné a DNS-gyorsítótárat?", "confirm_dns_cache_clear": "Biztos benne, hogy törölni szeretné a DNS-gyorsítótárat?",
"cache_cleared": "A DNS gyorsítótár sikeresen törlődött", "cache_cleared": "A DNS gyorsítótár sikeresen törlődött",
"clear_cache": "Gyorsítótár törlése", "clear_cache": "Gyorsítótár törlése",
"make_static": "Statikussá tétel", "protection_section_label": "Védelem"
"theme_auto_desc": "Automatikus (az eszköz színsémájától függően)",
"theme_dark_desc": "Sötét téma",
"theme_light_desc": "Világos téma",
"disable_for_seconds": "{{count}} másodpercig",
"disable_for_seconds_plural": "{{count}} másodpercig",
"disable_for_minutes": "{{count}} percig",
"disable_for_minutes_plural": "{{count}} percig",
"disable_for_hours": "{{count}} óráig",
"disable_for_hours_plural": "{{count}} óráig",
"disable_until_tomorrow": "Holnapig",
"disable_notify_for_seconds": "Kapcsolja ki a védelmet {{count}} másodpercre",
"disable_notify_for_seconds_plural": "Kapcsolja ki a védelmet {{count}} másodpercre",
"disable_notify_for_minutes": "Kapcsolja ki a védelmet {{count}} percre",
"disable_notify_for_minutes_plural": "Kapcsolja ki a védelmet {{count}} percre",
"disable_notify_for_hours": "Kapcsolja ki a védelmet {{count}} órára",
"disable_notify_for_hours_plural": "Kapcsolja ki a védelmet {{count}} órára",
"disable_notify_until_tomorrow": "Holnapig kapcsolja ki a védelmet",
"enable_protection_timer": "A védelem {{time}}-kor aktiválódik",
"custom_retention_input": "Adja meg a megőrzést órákban",
"custom_rotation_input": "Írja be a forgatást órákban",
"protection_section_label": "Védelem",
"log_and_stats_section_label": "Lekérdezési napló és statisztikák",
"ignore_query_log": "Figyelmen kívül hagyja ezt az ügyfelet a lekérdezési naplóban",
"ignore_statistics": "Hagyja figyelmen kívül ezt az ügyfelet a statisztikákban"
} }

View File

@@ -474,9 +474,7 @@
"setup_dns_notice": "Jikalau ingin menggunakan <1>DNS-over-HTTPS</1> atau <1>DNS-over-TLS</1>, Anda perlu <0>mengatur Enkripsi</0> pada pengaturan AdGuard Home.", "setup_dns_notice": "Jikalau ingin menggunakan <1>DNS-over-HTTPS</1> atau <1>DNS-over-TLS</1>, Anda perlu <0>mengatur Enkripsi</0> pada pengaturan AdGuard Home.",
"rewrite_added": "DNS rewrite untuk \"{{key}}\" berhasil ditambahkan", "rewrite_added": "DNS rewrite untuk \"{{key}}\" berhasil ditambahkan",
"rewrite_deleted": "DNS rewrite untuk \"{{key}}\" berhasil dihapus", "rewrite_deleted": "DNS rewrite untuk \"{{key}}\" berhasil dihapus",
"rewrite_updated": "Penulisan ulang DNS berhasil diperbarui",
"rewrite_add": "Tambah DNS rewrite", "rewrite_add": "Tambah DNS rewrite",
"rewrite_edit": "Edit penulisan ulang DNS",
"rewrite_not_found": "Tidak ada DNS rewrite ditemukan", "rewrite_not_found": "Tidak ada DNS rewrite ditemukan",
"rewrite_confirm_delete": "Apakah anda yakin ingin menghapus DNS rewrite untuk \"{{key}}\"?", "rewrite_confirm_delete": "Apakah anda yakin ingin menghapus DNS rewrite untuk \"{{key}}\"?",
"rewrite_desc": "Memungkinkan untuk dengan mudah mengkonfigurasi respons DNS kustom untuk nama domain tertentu.", "rewrite_desc": "Memungkinkan untuk dengan mudah mengkonfigurasi respons DNS kustom untuk nama domain tertentu.",

View File

@@ -478,9 +478,7 @@
"setup_dns_notice": "Per utilizzare <1>DNS su HTTPS</1> o <1>DNS su TLS</1>, è necessario <0>configurare la crittografia</0> nelle impostazioni di AdGuard Home.", "setup_dns_notice": "Per utilizzare <1>DNS su HTTPS</1> o <1>DNS su TLS</1>, è necessario <0>configurare la crittografia</0> nelle impostazioni di AdGuard Home.",
"rewrite_added": "Riscrittura DNS per \"{{key}}\" aggiunta correttamente", "rewrite_added": "Riscrittura DNS per \"{{key}}\" aggiunta correttamente",
"rewrite_deleted": "La riscrittura DNS per \"{{key}}\" è stata eliminata correttamente", "rewrite_deleted": "La riscrittura DNS per \"{{key}}\" è stata eliminata correttamente",
"rewrite_updated": "Riscrittura DNS aggiornata correttamente",
"rewrite_add": "Aggiungi la riscrittura DNS", "rewrite_add": "Aggiungi la riscrittura DNS",
"rewrite_edit": "Modifica della riscrittura DNS",
"rewrite_not_found": "Nessuna riscrittura DNS trovata", "rewrite_not_found": "Nessuna riscrittura DNS trovata",
"rewrite_confirm_delete": "Sei sicuro di voler cancellare la riscrittura DNS per \"{{key}}\"?", "rewrite_confirm_delete": "Sei sicuro di voler cancellare la riscrittura DNS per \"{{key}}\"?",
"rewrite_desc": "Consente di configurare facilmente la risposta DNS personalizzata per un nome di dominio specifico.", "rewrite_desc": "Consente di configurare facilmente la risposta DNS personalizzata per un nome di dominio specifico.",

View File

@@ -478,9 +478,7 @@
"setup_dns_notice": "<1>DNS-over-HTTPS</1>または<1>DNS-over-TLS</1>を使用するには、AdGuard Home 設定の<0>暗号化設定</0>が必要です。", "setup_dns_notice": "<1>DNS-over-HTTPS</1>または<1>DNS-over-TLS</1>を使用するには、AdGuard Home 設定の<0>暗号化設定</0>が必要です。",
"rewrite_added": "\"{{key}}\" のDNS書き換え情報を追加完了しました", "rewrite_added": "\"{{key}}\" のDNS書き換え情報を追加完了しました",
"rewrite_deleted": "\"{{key}}\" のDNS書き換え情報を削除完了しました", "rewrite_deleted": "\"{{key}}\" のDNS書き換え情報を削除完了しました",
"rewrite_updated": "DNS rewrite を更新完了しました。",
"rewrite_add": "DNS書き換え情報を追加する", "rewrite_add": "DNS書き換え情報を追加する",
"rewrite_edit": "DNS rewrite を編集する",
"rewrite_not_found": "DNS書き換え情報はありません", "rewrite_not_found": "DNS書き換え情報はありません",
"rewrite_confirm_delete": "\"{{key}}\" のDNS書き換え情報を削除してもよろしいですか", "rewrite_confirm_delete": "\"{{key}}\" のDNS書き換え情報を削除してもよろしいですか",
"rewrite_desc": "特定のドメイン名に対するDNS応答を簡単にカスタマイズすることを可能にします。", "rewrite_desc": "特定のドメイン名に対するDNS応答を簡単にカスタマイズすることを可能にします。",

View File

@@ -478,9 +478,7 @@
"setup_dns_notice": "<1>DNS-over-HTTPS</1> 또는 <1>DNS-over-TLS를</1> 사용하려면 AdGuard Home 설정에서 <0>암호화를 구성해야 합니다.</0>", "setup_dns_notice": "<1>DNS-over-HTTPS</1> 또는 <1>DNS-over-TLS를</1> 사용하려면 AdGuard Home 설정에서 <0>암호화를 구성해야 합니다.</0>",
"rewrite_added": "'{{key}}'에 대한 DNS 수정 정보를 성공적으로 추가 됩니다", "rewrite_added": "'{{key}}'에 대한 DNS 수정 정보를 성공적으로 추가 됩니다",
"rewrite_deleted": "'{{key}}'에 대한 DNS 수정 정보를 성공적으로 삭제 됩니다", "rewrite_deleted": "'{{key}}'에 대한 DNS 수정 정보를 성공적으로 삭제 됩니다",
"rewrite_updated": "DNS 다시 쓰기 업데이트 완료",
"rewrite_add": "DNS 변환 정보를 추가합니다", "rewrite_add": "DNS 변환 정보를 추가합니다",
"rewrite_edit": "DNS 다시 쓰기 편집",
"rewrite_not_found": "DNS 변경 정보를 찾을 수 없습니다", "rewrite_not_found": "DNS 변경 정보를 찾을 수 없습니다",
"rewrite_confirm_delete": "'{{key}}'에 대한 DNS 변경 정보를 삭제하시겠습니까?", "rewrite_confirm_delete": "'{{key}}'에 대한 DNS 변경 정보를 삭제하시겠습니까?",
"rewrite_desc": "특정 도메인 이름에 대한 사용자 지정 DNS 응답을 쉽게 구성할 수 있습니다.", "rewrite_desc": "특정 도메인 이름에 대한 사용자 지정 DNS 응답을 쉽게 구성할 수 있습니다.",

View File

@@ -478,9 +478,7 @@
"setup_dns_notice": "Om <1>DNS-via-HTTPS</1> of <1>DNS-via-TLS</1> te gebruiken, moet je <0>Versleuteling configureren</0> in de AdGuard Home instellingen.", "setup_dns_notice": "Om <1>DNS-via-HTTPS</1> of <1>DNS-via-TLS</1> te gebruiken, moet je <0>Versleuteling configureren</0> in de AdGuard Home instellingen.",
"rewrite_added": "DNS-herschrijving voor \"{{key}}\" met succes toegevoegd", "rewrite_added": "DNS-herschrijving voor \"{{key}}\" met succes toegevoegd",
"rewrite_deleted": "DNS-herschrijving voor \"{{key}}\" met succes verwijderd", "rewrite_deleted": "DNS-herschrijving voor \"{{key}}\" met succes verwijderd",
"rewrite_updated": "DNS-herschrijven succesvol bijgewerkt",
"rewrite_add": "DNS-herschrijving toevoegen", "rewrite_add": "DNS-herschrijving toevoegen",
"rewrite_edit": "DNS-herschrijven bewerken",
"rewrite_not_found": "Geen DNS-herschrijving gevonden", "rewrite_not_found": "Geen DNS-herschrijving gevonden",
"rewrite_confirm_delete": "Bent u zeker dat u DNS-herschrijving \"{{key}}\" wilt verwijderen?", "rewrite_confirm_delete": "Bent u zeker dat u DNS-herschrijving \"{{key}}\" wilt verwijderen?",
"rewrite_desc": "Hiermee kunt u eenvoudig aangepaste DNS-antwoorden configureren voor een specifieke domeinnaam.", "rewrite_desc": "Hiermee kunt u eenvoudig aangepaste DNS-antwoorden configureren voor een specifieke domeinnaam.",

View File

@@ -457,9 +457,7 @@
"setup_dns_notice": "For å benytte <1>DNS-over-HTTPS</1> eller <1>DNS-over-TLS</1>, må du <0>sette opp Kryptering</0> i AdGuard Home-innstillingene.", "setup_dns_notice": "For å benytte <1>DNS-over-HTTPS</1> eller <1>DNS-over-TLS</1>, må du <0>sette opp Kryptering</0> i AdGuard Home-innstillingene.",
"rewrite_added": "DNS-omdirigeringen for «{{key}}» ble vellykket lagt til", "rewrite_added": "DNS-omdirigeringen for «{{key}}» ble vellykket lagt til",
"rewrite_deleted": "DNS-omdirigeringen for «{{key}}» ble vellykket slettet", "rewrite_deleted": "DNS-omdirigeringen for «{{key}}» ble vellykket slettet",
"rewrite_updated": "DNS-omskriving ble oppdatert",
"rewrite_add": "Legg til DNS-omdirigering", "rewrite_add": "Legg til DNS-omdirigering",
"rewrite_edit": "Rediger DNS-omskriving",
"rewrite_not_found": "Ingen DNS-omdirigeringer ble funnet", "rewrite_not_found": "Ingen DNS-omdirigeringer ble funnet",
"rewrite_confirm_delete": "Er du sikker på at du vil slette DNS-omdirigeringen for «{{key}}»?", "rewrite_confirm_delete": "Er du sikker på at du vil slette DNS-omdirigeringen for «{{key}}»?",
"rewrite_desc": "Lar deg enkelt konfigurere selvvalgte DNS-tilbakemeldinger for et spesifikt domenenavn.", "rewrite_desc": "Lar deg enkelt konfigurere selvvalgte DNS-tilbakemeldinger for et spesifikt domenenavn.",

View File

@@ -478,9 +478,7 @@
"setup_dns_notice": "Aby skorzystać z <1>DNS-over-HTTPS</1> lub <1>DNS-over-TLS</1>, musisz w ustawieniach AdGuard Home <0>skonfigurować szyfrowanie</0>.", "setup_dns_notice": "Aby skorzystać z <1>DNS-over-HTTPS</1> lub <1>DNS-over-TLS</1>, musisz w ustawieniach AdGuard Home <0>skonfigurować szyfrowanie</0>.",
"rewrite_added": "Pomyślnie dodano przepisanie DNS dla „{{key}}”", "rewrite_added": "Pomyślnie dodano przepisanie DNS dla „{{key}}”",
"rewrite_deleted": "Przepisanie DNS dla „{{key}}” zostało pomyślnie usunięte", "rewrite_deleted": "Przepisanie DNS dla „{{key}}” zostało pomyślnie usunięte",
"rewrite_updated": "Pomyślnie zaktualizowano przepisywanie DNS",
"rewrite_add": "Dodaj przepisywanie DNS", "rewrite_add": "Dodaj przepisywanie DNS",
"rewrite_edit": "Edytuj przepisywanie DNS",
"rewrite_not_found": "Nie znaleziono przepisywania DNS", "rewrite_not_found": "Nie znaleziono przepisywania DNS",
"rewrite_confirm_delete": "Czy na pewno chcesz usunąć przepisywanie DNS dla „{{key}}”?", "rewrite_confirm_delete": "Czy na pewno chcesz usunąć przepisywanie DNS dla „{{key}}”?",
"rewrite_desc": "Pozwala łatwo skonfigurować niestandardową odpowiedź DNS dla określonej nazwy domeny.", "rewrite_desc": "Pozwala łatwo skonfigurować niestandardową odpowiedź DNS dla określonej nazwy domeny.",

View File

@@ -478,9 +478,7 @@
"setup_dns_notice": "Para usar o <1>DNS-sobre-HTTPS</1> ou <1>DNS-sobre-TLS</1>, você precisa <0>configurar a criptografia</0> nas configurações do AdGuard Home.", "setup_dns_notice": "Para usar o <1>DNS-sobre-HTTPS</1> ou <1>DNS-sobre-TLS</1>, você precisa <0>configurar a criptografia</0> nas configurações do AdGuard Home.",
"rewrite_added": "Reescrita de DNS para \"{{key}}\" adicionada com sucesso", "rewrite_added": "Reescrita de DNS para \"{{key}}\" adicionada com sucesso",
"rewrite_deleted": "Reescrita de DNS para \"{{key}}\" excluída com sucesso", "rewrite_deleted": "Reescrita de DNS para \"{{key}}\" excluída com sucesso",
"rewrite_updated": "Reconfiguração de DNS atualizada com êxito",
"rewrite_add": "Adicionar reescrita de DNS", "rewrite_add": "Adicionar reescrita de DNS",
"rewrite_edit": "Editar reconfiguração de DNS",
"rewrite_not_found": "Nenhuma reescrita de DNS foi encontrada", "rewrite_not_found": "Nenhuma reescrita de DNS foi encontrada",
"rewrite_confirm_delete": "Você tem certeza de que deseja excluir a reescrita de DNS para \"{{key}}\"?", "rewrite_confirm_delete": "Você tem certeza de que deseja excluir a reescrita de DNS para \"{{key}}\"?",
"rewrite_desc": "Permite configurar uma resposta personalizada do DNS para um nome de domínio específico.", "rewrite_desc": "Permite configurar uma resposta personalizada do DNS para um nome de domínio específico.",

View File

@@ -478,9 +478,7 @@
"setup_dns_notice": "Para usar o <1>DNS-sobre-HTTPS</1> ou <1>DNS-sobre-TLS</1>, precisa de <0>configurar a criptografia</0> nas configurações do AdGuard Home.", "setup_dns_notice": "Para usar o <1>DNS-sobre-HTTPS</1> ou <1>DNS-sobre-TLS</1>, precisa de <0>configurar a criptografia</0> nas configurações do AdGuard Home.",
"rewrite_added": "Reescrita de DNS para \"{{key}}\" adicionada com sucesso", "rewrite_added": "Reescrita de DNS para \"{{key}}\" adicionada com sucesso",
"rewrite_deleted": "Reescrita de DNS para \"{{key}}\" excluída com sucesso", "rewrite_deleted": "Reescrita de DNS para \"{{key}}\" excluída com sucesso",
"rewrite_updated": "Reedição de DNS atualizada com sucesso",
"rewrite_add": "Adicionar reescrita de DNS", "rewrite_add": "Adicionar reescrita de DNS",
"rewrite_edit": "Editar reedição de DNS",
"rewrite_not_found": "Nenhuma reescrita de DNS foi encontrada", "rewrite_not_found": "Nenhuma reescrita de DNS foi encontrada",
"rewrite_confirm_delete": "Tem a certeza de que deseja excluir a reescrita de DNS para \"{{key}}\"?", "rewrite_confirm_delete": "Tem a certeza de que deseja excluir a reescrita de DNS para \"{{key}}\"?",
"rewrite_desc": "Permite configurar uma resposta personalizada do DNS para um nome de domínio específico.", "rewrite_desc": "Permite configurar uma resposta personalizada do DNS para um nome de domínio específico.",

View File

@@ -167,7 +167,6 @@
"enabled_parental_toast": "Control Parental activat", "enabled_parental_toast": "Control Parental activat",
"disabled_safe_search_toast": "Căutare protejată dezactivată", "disabled_safe_search_toast": "Căutare protejată dezactivată",
"enabled_save_search_toast": "Căutare protejată activată", "enabled_save_search_toast": "Căutare protejată activată",
"updated_save_search_toast": "Setări Căutare sigură actualizate",
"enabled_table_header": "Activat", "enabled_table_header": "Activat",
"name_table_header": "Nume", "name_table_header": "Nume",
"list_url_table_header": "Lista URL", "list_url_table_header": "Lista URL",
@@ -257,12 +256,12 @@
"query_log_cleared": "Jurnalul de interogare a fost șters cu succes", "query_log_cleared": "Jurnalul de interogare a fost șters cu succes",
"query_log_updated": "Jurnalul de solicitări a fost actualizat cu succes", "query_log_updated": "Jurnalul de solicitări a fost actualizat cu succes",
"query_log_clear": "Curăță jurnalele", "query_log_clear": "Curăță jurnalele",
"query_log_retention": "Interogarea jurnalelor de rotație", "query_log_retention": "Retenție jurnale interogare",
"query_log_enable": "Activați jurnal", "query_log_enable": "Activați jurnal",
"query_log_configuration": "Configurația jurnalelor", "query_log_configuration": "Configurația jurnalelor",
"query_log_disabled": "Jurnalul de interogare este dezactivat și poate fi configurat în <0>setări</0>", "query_log_disabled": "Jurnalul de interogare este dezactivat și poate fi configurat în <0>setări</0>",
"query_log_strict_search": "Utilizați ghilimele duble pentru căutare strictă", "query_log_strict_search": "Utilizați ghilimele duble pentru căutare strictă",
"query_log_retention_confirm": "Sigur doriți să modificați rotația jurnalului de interogări? Dacă micșorați valoarea intervalului, unele date se vor pierde", "query_log_retention_confirm": "Sunteți sigur doriți să schimbați retenția jurnalului de interogare? Reducând valoarea intervalului, unele date vor fi pierdute",
"anonymize_client_ip": "Anonimizare client IP", "anonymize_client_ip": "Anonimizare client IP",
"anonymize_client_ip_desc": "Nu salvați adresa IP completă a clientului în jurnale și statistici", "anonymize_client_ip_desc": "Nu salvați adresa IP completă a clientului în jurnale și statistici",
"dns_config": "Configurația serverului DNS", "dns_config": "Configurația serverului DNS",
@@ -291,8 +290,6 @@
"rate_limit": "Limita ratei", "rate_limit": "Limita ratei",
"edns_enable": "Activați subrețeaua de clienți EDNS", "edns_enable": "Activați subrețeaua de clienți EDNS",
"edns_cs_desc": "Adaugă opțiunea EDNS Client Subnet (ECS) la solicitările în amonte și înregistrează valorile trimise de clienți în jurnalul de interogare.", "edns_cs_desc": "Adaugă opțiunea EDNS Client Subnet (ECS) la solicitările în amonte și înregistrează valorile trimise de clienți în jurnalul de interogare.",
"edns_use_custom_ip": "Utilizați IP personalizat pentru EDNS",
"edns_use_custom_ip_desc": "Permiteți utilizarea IP-ului personalizat pentru EDNS",
"rate_limit_desc": "Numărul de interogări pe secundă permise pe client. Setarea la 0 înseamnă că nu există limită.", "rate_limit_desc": "Numărul de interogări pe secundă permise pe client. Setarea la 0 înseamnă că nu există limită.",
"blocking_ipv4_desc": "Adresa IP de returnat pentru o cerere A de blocare", "blocking_ipv4_desc": "Adresa IP de returnat pentru o cerere A de blocare",
"blocking_ipv6_desc": "Adresa IP de returnat pentru o cerere AAAA de blocare", "blocking_ipv6_desc": "Adresa IP de returnat pentru o cerere AAAA de blocare",
@@ -478,9 +475,7 @@
"setup_dns_notice": "Pentru a utiliza <1>DNS-over-HTTPS</1> sau <1>DNS-over-TLS</1>, trebuie să <0>configurați Criptarea</0> în setările AdGuard Home.", "setup_dns_notice": "Pentru a utiliza <1>DNS-over-HTTPS</1> sau <1>DNS-over-TLS</1>, trebuie să <0>configurați Criptarea</0> în setările AdGuard Home.",
"rewrite_added": "Rescriere DNS pentru \"{{key}}\" adăugată cu succes", "rewrite_added": "Rescriere DNS pentru \"{{key}}\" adăugată cu succes",
"rewrite_deleted": "Rescriere DNS pentru \"{{key}}\" ștearsă cu succes", "rewrite_deleted": "Rescriere DNS pentru \"{{key}}\" ștearsă cu succes",
"rewrite_updated": "DNS rescrie actualizat cu succes",
"rewrite_add": "Adăugați rescriere DNS", "rewrite_add": "Adăugați rescriere DNS",
"rewrite_edit": "Editați rescrierea DNS",
"rewrite_not_found": "Nu s-au găsit rescrieri DNS", "rewrite_not_found": "Nu s-au găsit rescrieri DNS",
"rewrite_confirm_delete": "Sunteți sigur că doriți să ștergeți rescrierea DNS pentru \"{{key}}\"?", "rewrite_confirm_delete": "Sunteți sigur că doriți să ștergeți rescrierea DNS pentru \"{{key}}\"?",
"rewrite_desc": "Permite configurarea cu ușurință a răspunsului personalizat DNS pentru un nume de domeniu specific.", "rewrite_desc": "Permite configurarea cu ușurință a răspunsului personalizat DNS pentru un nume de domeniu specific.",
@@ -528,10 +523,6 @@
"statistics_retention_confirm": "Sunteți sigur că doriți să schimbați păstrarea statisticilor? Dacă reduceți valoarea intervalului, unele date vor fi pierdute", "statistics_retention_confirm": "Sunteți sigur că doriți să schimbați păstrarea statisticilor? Dacă reduceți valoarea intervalului, unele date vor fi pierdute",
"statistics_cleared": "Statisticile au fost șterse cu succes", "statistics_cleared": "Statisticile au fost șterse cu succes",
"statistics_enable": "Activați statisticile", "statistics_enable": "Activați statisticile",
"ignore_domains": "Domenii ignorate (separate prin linie nouă)",
"ignore_domains_title": "Domenii ignorate",
"ignore_domains_desc_stats": "Interogările pentru aceste domenii nu sunt scrise în statistici",
"ignore_domains_desc_query": "Interogările pentru aceste domenii nu sunt scrise în jurnalul de interogări",
"interval_hours": "{{count}} oră", "interval_hours": "{{count}} oră",
"interval_hours_plural": "{{count}} ore", "interval_hours_plural": "{{count}} ore",
"filters_configuration": "Configurația filtrelor", "filters_configuration": "Configurația filtrelor",
@@ -652,29 +643,5 @@
"confirm_dns_cache_clear": "Sunteți sigur că doriți să ștergeți memoria cache DNS?", "confirm_dns_cache_clear": "Sunteți sigur că doriți să ștergeți memoria cache DNS?",
"cache_cleared": "Cache-ul DNS a fost golit cu succes", "cache_cleared": "Cache-ul DNS a fost golit cu succes",
"clear_cache": "Goliți memoria cache", "clear_cache": "Goliți memoria cache",
"make_static": "Faceți static", "protection_section_label": "Protecție"
"theme_auto_desc": "Auto (pe baza schemei de culori a dispozitivului dvs.)",
"theme_dark_desc": "Temă întunecată",
"theme_light_desc": "Temă luminoasă",
"disable_for_seconds": "Timp de {{count}} secundă",
"disable_for_seconds_plural": "Timp de {{count}} secunde",
"disable_for_minutes": "Timp de {{count}} minut",
"disable_for_minutes_plural": "Timp de {{count}} minute",
"disable_for_hours": "Timp de {{count}} oră",
"disable_for_hours_plural": "Timp de {{count}} ore",
"disable_until_tomorrow": "Până mâine",
"disable_notify_for_seconds": "Dezactivați protecția timp de {{count}} secundă",
"disable_notify_for_seconds_plural": "Dezactivați protecția timp de {{count}} secunde",
"disable_notify_for_minutes": "Dezactivați protecția timp de {{count}} minut",
"disable_notify_for_minutes_plural": "Dezactivați protecția timp de {{count}} minute",
"disable_notify_for_hours": "Dezactivează protecția timp de {{count}} oră",
"disable_notify_for_hours_plural": "Dezactivați protecția timp de {{count}} ore",
"disable_notify_until_tomorrow": "Dezactivează protecția până mâine",
"enable_protection_timer": "Protecția va fi activată în {{time}}",
"custom_retention_input": "Introduceți reținerea în ore",
"custom_rotation_input": "Introduceți rotația în ore",
"protection_section_label": "Protecție",
"log_and_stats_section_label": "Jurnal de interogări și statistici",
"ignore_query_log": "Ignorați acest client în jurnalul de interogări",
"ignore_statistics": "Ignorați acest client în statistici"
} }

View File

@@ -478,9 +478,7 @@
"setup_dns_notice": "Чтобы использовать <1>DNS-over-HTTPS</1> или <1>DNS-over-TLS</1>, вам нужно <0>настроить шифрование</0> в настройках AdGuard Home.", "setup_dns_notice": "Чтобы использовать <1>DNS-over-HTTPS</1> или <1>DNS-over-TLS</1>, вам нужно <0>настроить шифрование</0> в настройках AdGuard Home.",
"rewrite_added": "Правило перезаписи DNS-запросов для «{{key}}» успешно добавлено", "rewrite_added": "Правило перезаписи DNS-запросов для «{{key}}» успешно добавлено",
"rewrite_deleted": "Правило перезаписи DNS-запросов для «{{key}}» успешно удалено", "rewrite_deleted": "Правило перезаписи DNS-запросов для «{{key}}» успешно удалено",
"rewrite_updated": "Правило перезаписи DNS-запросов успешно обновлено",
"rewrite_add": "Добавить правило перезаписи DNS-запросов", "rewrite_add": "Добавить правило перезаписи DNS-запросов",
"rewrite_edit": "Редактировать правило перезаписи DNS-запросов",
"rewrite_not_found": "Не найдено правил перезаписи DNS-запросов", "rewrite_not_found": "Не найдено правил перезаписи DNS-запросов",
"rewrite_confirm_delete": "Вы уверены, что хотите удалить правило перезаписи DNS-запросов для «{{key}}»?", "rewrite_confirm_delete": "Вы уверены, что хотите удалить правило перезаписи DNS-запросов для «{{key}}»?",
"rewrite_desc": "Позволяет легко настроить пользовательский DNS-ответ для определеннного домена.", "rewrite_desc": "Позволяет легко настроить пользовательский DNS-ответ для определеннного домена.",

View File

@@ -153,7 +153,6 @@
"enabled_parental_toast": "දෙමාපිය පාලනය සබල කෙරිණි", "enabled_parental_toast": "දෙමාපිය පාලනය සබල කෙරිණි",
"disabled_safe_search_toast": "ආරක්‍ෂිත සෙවුම අබල කෙරිණි", "disabled_safe_search_toast": "ආරක්‍ෂිත සෙවුම අබල කෙරිණි",
"enabled_save_search_toast": "ආරක්‍ෂිත සෙවුම සබල කෙරිණි", "enabled_save_search_toast": "ආරක්‍ෂිත සෙවුම සබල කෙරිණි",
"updated_save_search_toast": "ආරක්‍ෂිත සෙවුමේ සැකසුම් යාවත්කාල විය",
"enabled_table_header": "සබලයි", "enabled_table_header": "සබලයි",
"name_table_header": "නම", "name_table_header": "නම",
"list_url_table_header": "ඒ.ස.නි.(URL) ලැයිස්තුව", "list_url_table_header": "ඒ.ස.නි.(URL) ලැයිස්තුව",
@@ -238,12 +237,12 @@
"query_log_cleared": "විමසුම් සටහන සාර්ථකව හිස් කර ඇත", "query_log_cleared": "විමසුම් සටහන සාර්ථකව හිස් කර ඇත",
"query_log_updated": "විමසුම් සටහන සාර්ථකව යාවත්කාල කෙරිණි", "query_log_updated": "විමසුම් සටහන සාර්ථකව යාවත්කාල කෙරිණි",
"query_log_clear": "විමසුම් සටහන් හිස් කරන්න", "query_log_clear": "විමසුම් සටහන් හිස් කරන්න",
"query_log_retention": "විමසුම් සටහන් රඳවීම", "query_log_retention": "විමසුම් සටහන් රඳවා තබා ගැනීම",
"query_log_enable": "සටහන සබල කරන්න", "query_log_enable": "සටහන සබල කරන්න",
"query_log_configuration": "සටහන් වින්‍යාසය", "query_log_configuration": "සටහන් වින්‍යාසය",
"query_log_disabled": "විමසුම් සටහන අබල කර ඇති අතර එය <0>සැකසුම්</0> තුළ වින්‍යාසගත කළ හැකිය", "query_log_disabled": "විමසුම් සටහන අබල කර ඇති අතර එය <0>සැකසුම්</0> තුළ වින්‍යාසගත කළ හැකිය",
"query_log_strict_search": "ඉතා නිවැරදිව සෙවීමට ද්විත්ව උද්ධෘතය භාවිතා කරන්න", "query_log_strict_search": "ඉතා නිවැරදිව සෙවීමට ද්විත්ව උද්ධෘතය භාවිතා කරන්න",
"query_log_retention_confirm": "විමසුම් සටහන රඳවා තබා ගැනීම වෙනස් කිරීමට වුවමනා ද? ඔබ කාල පරතරයෙහි අගය අඩු කළහොත් සමහර දත්ත නැති වී යනු ඇත", "query_log_retention_confirm": "විමසුම් සටහන රඳවා තබා ගැනීම වෙනස් කිරීමට ඇවැසි බව ඔබට විශ්වාසද? ඔබ කාල පරතරයෙහි අගය අඩු කළහොත් සමහර දත්ත නැති වී යනු ඇත",
"anonymize_client_ip": "අනුග්‍රාහකයෙහි අ.ජා.කෙ. (IP) නිර්නාමික කරන්න", "anonymize_client_ip": "අනුග්‍රාහකයෙහි අ.ජා.කෙ. (IP) නිර්නාමික කරන්න",
"anonymize_client_ip_desc": "සටහන් සහ සංඛ්‍යාලේඛන තුළ අනුග්‍රාහකයේ පූර්ණ අ.ජා.කෙ. ලිපිනය සුරකින්න එපා", "anonymize_client_ip_desc": "සටහන් සහ සංඛ්‍යාලේඛන තුළ අනුග්‍රාහකයේ පූර්ණ අ.ජා.කෙ. ලිපිනය සුරකින්න එපා",
"dns_config": "ව.නා.ප. සේවාදායක වින්‍යාසය", "dns_config": "ව.නා.ප. සේවාදායක වින්‍යාසය",
@@ -271,8 +270,6 @@
"form_enter_rate_limit": "අනුපාත සීමාව ඇතුල් කරන්න", "form_enter_rate_limit": "අනුපාත සීමාව ඇතුල් කරන්න",
"rate_limit": "අනුපාත සීමාව", "rate_limit": "අනුපාත සීමාව",
"edns_enable": "EDNS අනුග්‍රාහක අනුජාලය සබල කරන්න", "edns_enable": "EDNS අනුග්‍රාහක අනුජාලය සබල කරන්න",
"edns_use_custom_ip": "EDNS සඳහා අභිරුචි අ.ජා.කෙ. යොදාගන්න",
"edns_use_custom_ip_desc": "EDNS සඳහා අභිරුචි අ.ජා.කෙ. භාවිතයට ඉඩදෙන්න",
"rate_limit_desc": "එක් අනුග්‍රාහකයකට ඉඩ දී ඇති තත්පරයට ඉල්ලීම් ගණන. එය 0 ලෙස සැකසීම යනුවෙන් අදහස් කරන්නේ සීමාවක් නැති බවයි.", "rate_limit_desc": "එක් අනුග්‍රාහකයකට ඉඩ දී ඇති තත්පරයට ඉල්ලීම් ගණන. එය 0 ලෙස සැකසීම යනුවෙන් අදහස් කරන්නේ සීමාවක් නැති බවයි.",
"blocking_ipv4_desc": "අවහිර කළ A ඉල්ලීමක් සඳහා ආපසු එවිය යුතු අ.ජා.කෙ. (IP) ලිපිනය", "blocking_ipv4_desc": "අවහිර කළ A ඉල්ලීමක් සඳහා ආපසු එවිය යුතු අ.ජා.කෙ. (IP) ලිපිනය",
"blocking_ipv6_desc": "අවහිර කළ AAAA ඉල්ලීමක් සඳහා ආපසු එවිය යුතු අ.ජා.කෙ. (IP) ලිපිනය", "blocking_ipv6_desc": "අවහිර කළ AAAA ඉල්ලීමක් සඳහා ආපසු එවිය යුතු අ.ජා.කෙ. (IP) ලිපිනය",
@@ -281,9 +278,6 @@
"blocking_mode_nxdomain": "නොපවතින වසම: NXDOMAIN කේතය සමඟ ප්‍රතිචාර දක්වයි", "blocking_mode_nxdomain": "නොපවතින වසම: NXDOMAIN කේතය සමඟ ප්‍රතිචාර දක්වයි",
"blocking_mode_null_ip": "අභිශූන්‍යය අ.ජා.කෙ.: ශුන්‍ය අ.ජා.කෙ. ලිපිනය සමඟ ප්‍රතිචාර දක්වයි (A සඳහා 0.0.0.0; AAAA සඳහා ::)", "blocking_mode_null_ip": "අභිශූන්‍යය අ.ජා.කෙ.: ශුන්‍ය අ.ජා.කෙ. ලිපිනය සමඟ ප්‍රතිචාර දක්වයි (A සඳහා 0.0.0.0; AAAA සඳහා ::)",
"blocking_mode_custom_ip": "අභිරුචි අන්තර්ජාල කෙටුම්පත: අතින් සැකසූ අ.ජා. කෙ. ලිපිනයක් සමඟ ප්‍රතිචාර දක්වයි", "blocking_mode_custom_ip": "අභිරුචි අන්තර්ජාල කෙටුම්පත: අතින් සැකසූ අ.ජා. කෙ. ලිපිනයක් සමඟ ප්‍රතිචාර දක්වයි",
"theme_auto": "ස්වයං",
"theme_light": "දීප්ත",
"theme_dark": "අඳුරු",
"upstream_dns_client_desc": "ඔබ මෙම ක්ෂේත්‍රය හිස්ව තබා ගන්නේ නම්, ඇඩ්ගාර්ඩ් හෝම් විසින් <0>ව.නා.ප. සැකසුම්</0> හි වින්‍යාසගත කර ඇති සේවාදායක භාවිතා කරනු ඇත.", "upstream_dns_client_desc": "ඔබ මෙම ක්ෂේත්‍රය හිස්ව තබා ගන්නේ නම්, ඇඩ්ගාර්ඩ් හෝම් විසින් <0>ව.නා.ප. සැකසුම්</0> හි වින්‍යාසගත කර ඇති සේවාදායක භාවිතා කරනු ඇත.",
"tracker_source": "ලුහුබැඳීම් මූලාශ්‍රය", "tracker_source": "ලුහුබැඳීම් මූලාශ්‍රය",
"source_label": "මූලාශ්‍රය", "source_label": "මූලාශ්‍රය",
@@ -376,7 +370,6 @@
"encryption_issuer": "නිකුත් කරන්නා", "encryption_issuer": "නිකුත් කරන්නා",
"encryption_hostnames": "ධාරක නාම", "encryption_hostnames": "ධාරක නාම",
"encryption_reset": "සංකේතාංකන සැකසුම් යළි පිහිටුවීමට අවශ්‍ය බව ඔබට විශ්වාස ද?", "encryption_reset": "සංකේතාංකන සැකසුම් යළි පිහිටුවීමට අවශ්‍ය බව ඔබට විශ්වාස ද?",
"encryption_warning": "අවවාදයයි",
"topline_expiring_certificate": "ඔබගේ SSL සහතිකය කල් ඉකුත්වීමට ආසන්න වී ඇත. <0>සංකේතන සැකසුම්</0> යාවත්කාල කරන්න.", "topline_expiring_certificate": "ඔබගේ SSL සහතිකය කල් ඉකුත්වීමට ආසන්න වී ඇත. <0>සංකේතන සැකසුම්</0> යාවත්කාල කරන්න.",
"topline_expired_certificate": "ඔබගේ SSL සහතිකය කල් ඉකුත් වී ඇත. <0>සංකේතන සැකසුම්</0> යාවත්කාල කරන්න.", "topline_expired_certificate": "ඔබගේ SSL සහතිකය කල් ඉකුත් වී ඇත. <0>සංකේතන සැකසුම්</0> යාවත්කාල කරන්න.",
"form_error_port_range": "80-65535 පරාසය හි තොටක අගයක් ඇතුල් කරන්න", "form_error_port_range": "80-65535 පරාසය හි තොටක අගයක් ඇතුල් කරන්න",
@@ -497,10 +490,8 @@
"statistics_clear": "සංඛ්‍යාලේඛන හිස් කරන්න", "statistics_clear": "සංඛ්‍යාලේඛන හිස් කරන්න",
"statistics_clear_confirm": "සංඛ්‍යාලේඛන ඉවත් කිරීමට වුවමනා ද?", "statistics_clear_confirm": "සංඛ්‍යාලේඛන ඉවත් කිරීමට වුවමනා ද?",
"statistics_retention_confirm": "සංඛ්‍යාලේඛන රඳවා තබා ගැනීම වෙනස් කිරීමට අවශ්‍ය බව ඔබට විශ්වාසද? ඔබ කාල පරතරයෙහි අගය අඩු කළහොත් සමහර දත්ත නැති වී යනු ඇත", "statistics_retention_confirm": "සංඛ්‍යාලේඛන රඳවා තබා ගැනීම වෙනස් කිරීමට අවශ්‍ය බව ඔබට විශ්වාසද? ඔබ කාල පරතරයෙහි අගය අඩු කළහොත් සමහර දත්ත නැති වී යනු ඇත",
"statistics_cleared": "සංඛ්‍යාලේඛන සාර්ථකව හිස් කෙරිණි", "statistics_cleared": "සංඛ්‍යාලේඛන සාර්ථකව ඉවත් කෙරිණි",
"statistics_enable": "සංඛ්‍යාලේඛන සබල කරන්න", "statistics_enable": "සංඛ්‍යාලේඛන සබල කරන්න",
"ignore_domains": "නොසලකන වසම් (පේළියකට එක බැගින්)",
"ignore_domains_title": "නොසලකන වසම්",
"interval_hours": "පැය {{count}}", "interval_hours": "පැය {{count}}",
"interval_hours_plural": "පැය {{count}}", "interval_hours_plural": "පැය {{count}}",
"filters_configuration": "පෙරහන් වින්‍යාසය", "filters_configuration": "පෙරහන් වින්‍යාසය",
@@ -610,31 +601,5 @@
"parental_control": "දෙමාපිය පාලනය", "parental_control": "දෙමාපිය පාලනය",
"safe_browsing": "ආරක්‍ෂිත පිරික්සුම", "safe_browsing": "ආරක්‍ෂිත පිරික්සුම",
"served_from_cache": "{{value}} <i>(නිහිතයෙන් ගැනිණි)</i>", "served_from_cache": "{{value}} <i>(නිහිතයෙන් ගැනිණි)</i>",
"form_error_password_length": "මුරපදය අවම වශයෙන් අකුරු {{value}} ක් දිගු විය යුතුමයි", "form_error_password_length": "මුරපදය අවම වශයෙන් අකුරු {{value}} ක් දිගු විය යුතුමයි"
"cache_cleared": "ව.නා.ප. නිහිතය හිස් කෙරිණි",
"clear_cache": "නිහිතය මකන්න",
"make_static": "ස්ථිතික කරන්න",
"theme_dark_desc": "අඳුරු තේමාව",
"theme_light_desc": "දීප්ත තේමාව",
"disable_for_seconds": "තත්පර {{count}} ක්",
"disable_for_seconds_plural": "තත්පර {{count}} ක්",
"disable_for_minutes": "විනාඩි {{count}} ක්",
"disable_for_minutes_plural": "විනාඩි {{count}} ක්",
"disable_for_hours": "පැය {{count}} ක්",
"disable_for_hours_plural": "පැය {{count}} ක්",
"disable_until_tomorrow": "හෙට වනතුරු",
"disable_notify_for_seconds": "තත්. {{count}} කට රැකවරණය අබල කරන්න",
"disable_notify_for_seconds_plural": "තත්. {{count}} කට රැකවරණය අබල කරන්න",
"disable_notify_for_minutes": "විනාඩි {{count}} කට රැකවරණය අබල කරන්න",
"disable_notify_for_minutes_plural": "විනාඩි {{count}} කට රැකවරණය අබල කරන්න",
"disable_notify_for_hours": "පැය {{count}} කට රැකවරණය අබල කරන්න",
"disable_notify_for_hours_plural": "පැය {{count}} කට රැකවරණය අබල කරන්න",
"disable_notify_until_tomorrow": "හෙට වනතුරු රැකවරණය අබල කරන්න",
"enable_protection_timer": "{{time}} න් රැකවරණය සබල කෙරේ",
"custom_retention_input": "රඳවා ගැනීම පැය වලින්",
"custom_rotation_input": "රඳවා ගැනීම පැය වලින්",
"protection_section_label": "රැකවරණය",
"log_and_stats_section_label": "විමසුම් සටහන හා සංඛ්‍යාලේඛන",
"ignore_query_log": "සටහනෙහි අනුග්‍රාහකය නොසලකන්න",
"ignore_statistics": "සංඛ්‍යාලේඛනයට අනුග්‍රාහකය නොසලකන්න"
} }

View File

@@ -478,9 +478,7 @@
"setup_dns_notice": "Pre použitie <1>DNS-over-HTTPS</1> alebo <1>DNS-over-TLS</1>, potrebujete v nastaveniach AdGuard Home <0>nakonfigurovať šifrovanie</0>.", "setup_dns_notice": "Pre použitie <1>DNS-over-HTTPS</1> alebo <1>DNS-over-TLS</1>, potrebujete v nastaveniach AdGuard Home <0>nakonfigurovať šifrovanie</0>.",
"rewrite_added": "DNS prepísanie pre \"{{key}}\" bolo úspešne pridané", "rewrite_added": "DNS prepísanie pre \"{{key}}\" bolo úspešne pridané",
"rewrite_deleted": "DNS prepísanie pre \"{{key}}\" bolo úspešne vymazané", "rewrite_deleted": "DNS prepísanie pre \"{{key}}\" bolo úspešne vymazané",
"rewrite_updated": "Prepísanie DNS bolo úspešne aktualizované",
"rewrite_add": "Pridať DNS prepísanie", "rewrite_add": "Pridať DNS prepísanie",
"rewrite_edit": "Upraviť prepísanie DNS",
"rewrite_not_found": "Neboli nájdené žiadne DNS prepísania", "rewrite_not_found": "Neboli nájdené žiadne DNS prepísania",
"rewrite_confirm_delete": "Naozaj chcete odstrániť prepísanie DNS pre \"{{key}}\"?", "rewrite_confirm_delete": "Naozaj chcete odstrániť prepísanie DNS pre \"{{key}}\"?",
"rewrite_desc": "Umožňuje ľahko nakonfigurovať vlastnú odpoveď DNS pre konkrétne meno domény.", "rewrite_desc": "Umožňuje ľahko nakonfigurovať vlastnú odpoveď DNS pre konkrétne meno domény.",

View File

@@ -478,9 +478,7 @@
"setup_dns_notice": "Za uporabo <1>DNS-prek-HTTPS</1> ali <1>DNS-prek-TLS</1>, morate <0>konfigurirati šifriranje</0> v nastavitvah AdGuard Home.", "setup_dns_notice": "Za uporabo <1>DNS-prek-HTTPS</1> ali <1>DNS-prek-TLS</1>, morate <0>konfigurirati šifriranje</0> v nastavitvah AdGuard Home.",
"rewrite_added": "Uspešno je dodano DNS prepisovanje za \"{{key}}\"", "rewrite_added": "Uspešno je dodano DNS prepisovanje za \"{{key}}\"",
"rewrite_deleted": "Uspešno je izbrisano DNS prepisovanje za \"{{key}}\"", "rewrite_deleted": "Uspešno je izbrisano DNS prepisovanje za \"{{key}}\"",
"rewrite_updated": "DNS prepisovanje uspešno posodobljen",
"rewrite_add": "Dodaj prepisovanje DNS", "rewrite_add": "Dodaj prepisovanje DNS",
"rewrite_edit": "Urejanje prepisa DNS",
"rewrite_not_found": "Ni bilo najdenih prepisovanj DNS", "rewrite_not_found": "Ni bilo najdenih prepisovanj DNS",
"rewrite_confirm_delete": "Ali ste prepričani, da želite izbrisati prepisovanje DNS za \"{{key}}\"?", "rewrite_confirm_delete": "Ali ste prepričani, da želite izbrisati prepisovanje DNS za \"{{key}}\"?",
"rewrite_desc": "Omogoča enostavno konfiguriranje odgovora DNS po meri za določeno ime domene.", "rewrite_desc": "Omogoča enostavno konfiguriranje odgovora DNS po meri za določeno ime domene.",

View File

@@ -475,9 +475,7 @@
"setup_dns_notice": "Kako biste koristili <1>DNS-over-HTTPS</1> ili <1>DNS-over-TLS</1>, potrebno je da <0>konfigurišete šifrovanje</0> u AdGuard Home postavkama.", "setup_dns_notice": "Kako biste koristili <1>DNS-over-HTTPS</1> ili <1>DNS-over-TLS</1>, potrebno je da <0>konfigurišete šifrovanje</0> u AdGuard Home postavkama.",
"rewrite_added": "DNS prepisivanje za \"{{key}}\" je uspešno dodato", "rewrite_added": "DNS prepisivanje za \"{{key}}\" je uspešno dodato",
"rewrite_deleted": "DNS prepisivanje za \"{{key}}\" uspešno izbrisano", "rewrite_deleted": "DNS prepisivanje za \"{{key}}\" uspešno izbrisano",
"rewrite_updated": "DNS ponovo napisao uspešno ažuriran",
"rewrite_add": "Dodaj DNS prepisivanje", "rewrite_add": "Dodaj DNS prepisivanje",
"rewrite_edit": "Uređivanje DNS prepravke",
"rewrite_not_found": "DNS prepisivanja nisu pronađena", "rewrite_not_found": "DNS prepisivanja nisu pronađena",
"rewrite_confirm_delete": "Jeste li sigurni da želite da izbrišete DNS prepisivanje za \"{{key}}\"?", "rewrite_confirm_delete": "Jeste li sigurni da želite da izbrišete DNS prepisivanje za \"{{key}}\"?",
"rewrite_desc": "Dozvoljava da jednostavno konfigurišete prilagođeni DNS odgovor za određeni domen.", "rewrite_desc": "Dozvoljava da jednostavno konfigurišete prilagođeni DNS odgovor za određeni domen.",

View File

@@ -475,9 +475,7 @@
"setup_dns_notice": "För att kunna använda <1>DNS-över-HTTPS</1> eller <1>DNS-över-TLS</1>, behöver du <0>konfigurera Kryptering</0> i AdGuard Home-inställningar.", "setup_dns_notice": "För att kunna använda <1>DNS-över-HTTPS</1> eller <1>DNS-över-TLS</1>, behöver du <0>konfigurera Kryptering</0> i AdGuard Home-inställningar.",
"rewrite_added": "DNS-omskrivning för \"{{key}}\" lyckad", "rewrite_added": "DNS-omskrivning för \"{{key}}\" lyckad",
"rewrite_deleted": "DNS-omskrivning för \"{{key}}\" har tagits bort", "rewrite_deleted": "DNS-omskrivning för \"{{key}}\" har tagits bort",
"rewrite_updated": "DNS-omskrivning har uppdaterats",
"rewrite_add": "Lägg till DNS omskrivning", "rewrite_add": "Lägg till DNS omskrivning",
"rewrite_edit": "Redigera DNS-omskrivning",
"rewrite_not_found": "Inga DNS omskrivningar hittades", "rewrite_not_found": "Inga DNS omskrivningar hittades",
"rewrite_confirm_delete": "Är du säker på att du vill ta bort DNS-omskrivningen för \"{{key}}\"?", "rewrite_confirm_delete": "Är du säker på att du vill ta bort DNS-omskrivningen för \"{{key}}\"?",
"rewrite_desc": "Gör det enkelt att konfigurera anpassat DNS svar för ett specifikt domännamn.", "rewrite_desc": "Gör det enkelt att konfigurera anpassat DNS svar för ett specifikt domännamn.",

View File

@@ -478,9 +478,7 @@
"setup_dns_notice": "<1>DNS-over-HTTPS</1> veya <1>DNS-over-TLS</1> protokolünü kullanmak için AdGuard Home üzerinde <0>Şifreleme ayarları</0> bölümünden ayarları yapmanız gerekir.", "setup_dns_notice": "<1>DNS-over-HTTPS</1> veya <1>DNS-over-TLS</1> protokolünü kullanmak için AdGuard Home üzerinde <0>Şifreleme ayarları</0> bölümünden ayarları yapmanız gerekir.",
"rewrite_added": "\"{{key}}\" için DNS yeniden yazımı başarıyla eklendi", "rewrite_added": "\"{{key}}\" için DNS yeniden yazımı başarıyla eklendi",
"rewrite_deleted": "\"{{key}}\" için DNS yeniden yazımı başarıyla silindi", "rewrite_deleted": "\"{{key}}\" için DNS yeniden yazımı başarıyla silindi",
"rewrite_updated": "DNS yeniden yazma başarıyla güncellendi",
"rewrite_add": "DNS yeniden yazımı ekle", "rewrite_add": "DNS yeniden yazımı ekle",
"rewrite_edit": "DNS yeniden yazmayı düzenle",
"rewrite_not_found": "DNS yeniden yazımı bulunamadı", "rewrite_not_found": "DNS yeniden yazımı bulunamadı",
"rewrite_confirm_delete": "\"{{key}}\" için DNS yeniden yazımını silmek istediğinize emin misiniz?", "rewrite_confirm_delete": "\"{{key}}\" için DNS yeniden yazımını silmek istediğinize emin misiniz?",
"rewrite_desc": "Belirli bir alan adı için özel DNS yanıtını kolayca yapılandırmanızı sağlar.", "rewrite_desc": "Belirli bir alan adı için özel DNS yanıtını kolayca yapılandırmanızı sağlar.",

View File

@@ -478,9 +478,7 @@
"setup_dns_notice": "Для використання <1>DNS-over-HTTPS</1> або <1>DNS-over-TLS</1>, вам потрібно <0>налаштувати Шифрування</0> в налаштуваннях AdGuard Home.", "setup_dns_notice": "Для використання <1>DNS-over-HTTPS</1> або <1>DNS-over-TLS</1>, вам потрібно <0>налаштувати Шифрування</0> в налаштуваннях AdGuard Home.",
"rewrite_added": "Перезапис DNS для «{{key}}» успішно додано", "rewrite_added": "Перезапис DNS для «{{key}}» успішно додано",
"rewrite_deleted": "Перезапис DNS для «{{key}}» успішно видалено", "rewrite_deleted": "Перезапис DNS для «{{key}}» успішно видалено",
"rewrite_updated": "Перезапис DNS успішно оновлено",
"rewrite_add": "Додати перезапис DNS", "rewrite_add": "Додати перезапис DNS",
"rewrite_edit": "Редагувати перезапис DNS",
"rewrite_not_found": "Перезаписів DNS не знайдено", "rewrite_not_found": "Перезаписів DNS не знайдено",
"rewrite_confirm_delete": "Ви впевнені, що хочете видалити перезапис DNS для «{{key}}»?", "rewrite_confirm_delete": "Ви впевнені, що хочете видалити перезапис DNS для «{{key}}»?",
"rewrite_desc": "Дозволяє легко налаштувати власну відповідь DNS для певного доменного імені.", "rewrite_desc": "Дозволяє легко налаштувати власну відповідь DNS для певного доменного імені.",

View File

@@ -1,5 +1,5 @@
{ {
"client_settings": "Cài đặt thiết bị", "client_settings": "Cài đặt máy khách",
"example_upstream_reserved": "ngược dòng <0>cho các miền cụ thể</0>;", "example_upstream_reserved": "ngược dòng <0>cho các miền cụ thể</0>;",
"example_upstream_comment": "một lời bình luận.", "example_upstream_comment": "một lời bình luận.",
"upstream_parallel": "Sử dụng truy vấn song song để tăng tốc độ giải quyết bằng cách truy vấn đồng thời tất cả các máy chủ ngược tuyến", "upstream_parallel": "Sử dụng truy vấn song song để tăng tốc độ giải quyết bằng cách truy vấn đồng thời tất cả các máy chủ ngược tuyến",
@@ -167,7 +167,6 @@
"enabled_parental_toast": "Đã bật quản lý của phụ huynh", "enabled_parental_toast": "Đã bật quản lý của phụ huynh",
"disabled_safe_search_toast": "Đã tắt tìm kiếm an toàn", "disabled_safe_search_toast": "Đã tắt tìm kiếm an toàn",
"enabled_save_search_toast": "Đã bật tìm kiếm an toàn", "enabled_save_search_toast": "Đã bật tìm kiếm an toàn",
"updated_save_search_toast": "Cài đặt Tìm kiếm an toàn đã được cập nhật",
"enabled_table_header": "Kích hoạt", "enabled_table_header": "Kích hoạt",
"name_table_header": "Tên", "name_table_header": "Tên",
"list_url_table_header": "URL bộ lọc", "list_url_table_header": "URL bộ lọc",
@@ -257,12 +256,12 @@
"query_log_cleared": "Nhật ký truy vấn đã được xóa thành công", "query_log_cleared": "Nhật ký truy vấn đã được xóa thành công",
"query_log_updated": "Cập nhật thành công nhật kí truy xuất", "query_log_updated": "Cập nhật thành công nhật kí truy xuất",
"query_log_clear": "Xóa nhật ký truy vấn", "query_log_clear": "Xóa nhật ký truy vấn",
"query_log_retention": "Xoay vòng nhật ký truy vấn", "query_log_retention": "Lưu giữ nhật ký truy vấn",
"query_log_enable": "Bật nhật ký", "query_log_enable": "Bật nhật ký",
"query_log_configuration": "Cấu hình nhật ký", "query_log_configuration": "Cấu hình nhật ký",
"query_log_disabled": "Nhật ký truy vấn bị vô hiệu hóa và có thể được định cấu hình trong <0>cài đặt</ 0>", "query_log_disabled": "Nhật ký truy vấn bị vô hiệu hóa và có thể được định cấu hình trong <0>cài đặt</ 0>",
"query_log_strict_search": "Sử dụng dấu ngoặc kép để tìm kiếm nghiêm ngặt", "query_log_strict_search": "Sử dụng dấu ngoặc kép để tìm kiếm nghiêm ngặt",
"query_log_retention_confirm": "Bạn có chắc chắn muốn thay đổi xoay vòng nhật ký truy vấn không? Nếu bạn giảm giá trị khoảng thời gian, một số dữ liệu sẽ bị mất", "query_log_retention_confirm": "Bạn có chắc chắn muốn thay đổi lưu giữ nhật ký truy vấn? Nếu bạn giảm giá trị khoảng, một số dữ liệu sẽ bị mất",
"anonymize_client_ip": "Ẩn danh IP khách", "anonymize_client_ip": "Ẩn danh IP khách",
"anonymize_client_ip_desc": "Không lưu địa chỉ IP đầy đủ của khách hàng trong nhật ký và thống kê", "anonymize_client_ip_desc": "Không lưu địa chỉ IP đầy đủ của khách hàng trong nhật ký và thống kê",
"dns_config": "Thiết lập máy chủ DNS", "dns_config": "Thiết lập máy chủ DNS",
@@ -291,8 +290,6 @@
"rate_limit": "Giới hạn yêu cầu", "rate_limit": "Giới hạn yêu cầu",
"edns_enable": "Bật mạng con EDNS Client", "edns_enable": "Bật mạng con EDNS Client",
"edns_cs_desc": "Thêm tùy chọn EDNS Client Subnet (ECS) vào các yêu cầu ngược dòng và ghi lại các giá trị được gửi bởi các máy khách trong nhật ký truy vấn.", "edns_cs_desc": "Thêm tùy chọn EDNS Client Subnet (ECS) vào các yêu cầu ngược dòng và ghi lại các giá trị được gửi bởi các máy khách trong nhật ký truy vấn.",
"edns_use_custom_ip": "Sử dụng địa chỉ IP tùy chỉnh cho EDNS",
"edns_use_custom_ip_desc": "Cho phép sử dụng địa chỉ IP tùy chỉnh cho EDNS",
"rate_limit_desc": "Số lượng yêu cầu mỗi giây mà một khách hàng được phép thực hiện (0: không giới hạn)", "rate_limit_desc": "Số lượng yêu cầu mỗi giây mà một khách hàng được phép thực hiện (0: không giới hạn)",
"blocking_ipv4_desc": "Địa chỉ IP được trả lại cho một yêu cầu A bị chặn", "blocking_ipv4_desc": "Địa chỉ IP được trả lại cho một yêu cầu A bị chặn",
"blocking_ipv6_desc": "Địa chỉ IP được trả lại cho một yêu cầu AAA bị chặn", "blocking_ipv6_desc": "Địa chỉ IP được trả lại cho một yêu cầu AAA bị chặn",
@@ -478,9 +475,7 @@
"setup_dns_notice": "Để sử dụng <1>DNS-over-HTTPS</1> hoặc <1>DNS-over-TLS</1>, bạn cần <0>định cấu hình Mã hóa</0> trong cài đặt AdGuard Home.", "setup_dns_notice": "Để sử dụng <1>DNS-over-HTTPS</1> hoặc <1>DNS-over-TLS</1>, bạn cần <0>định cấu hình Mã hóa</0> trong cài đặt AdGuard Home.",
"rewrite_added": "DNS viết lại cho \"{{key}}\" đã thêm thành công", "rewrite_added": "DNS viết lại cho \"{{key}}\" đã thêm thành công",
"rewrite_deleted": "DNS viết lại cho \"{{key}}\" đã xóa thành công", "rewrite_deleted": "DNS viết lại cho \"{{key}}\" đã xóa thành công",
"rewrite_updated": "Viết lại DNS được cập nhật thành công",
"rewrite_add": "Thêm DNS viết lại", "rewrite_add": "Thêm DNS viết lại",
"rewrite_edit": "Chỉnh sửa viết lại DNS",
"rewrite_not_found": "Không tìm thấy DNS viết lại", "rewrite_not_found": "Không tìm thấy DNS viết lại",
"rewrite_confirm_delete": "Bạn có chắc chắn muốn xóa DNS viết lại cho \"{{key}}\" không?", "rewrite_confirm_delete": "Bạn có chắc chắn muốn xóa DNS viết lại cho \"{{key}}\" không?",
"rewrite_desc": "Cho phép dễ dàng định cấu hình tùy chỉnh DNS phản hồi cho một tên miền cụ thể.", "rewrite_desc": "Cho phép dễ dàng định cấu hình tùy chỉnh DNS phản hồi cho một tên miền cụ thể.",
@@ -528,10 +523,6 @@
"statistics_retention_confirm": "Bạn có chắc chắn muốn thay đổi lưu giữ số liệu thống kê? Nếu bạn giảm giá trị khoảng, một số dữ liệu sẽ bị mất", "statistics_retention_confirm": "Bạn có chắc chắn muốn thay đổi lưu giữ số liệu thống kê? Nếu bạn giảm giá trị khoảng, một số dữ liệu sẽ bị mất",
"statistics_cleared": "Xoá thống kê thành công", "statistics_cleared": "Xoá thống kê thành công",
"statistics_enable": "Bật thống kê", "statistics_enable": "Bật thống kê",
"ignore_domains": "Các miền bị bỏ qua (cách nhau bởi dòng mới)",
"ignore_domains_title": "Các miền bị bỏ qua",
"ignore_domains_desc_stats": "Các truy vấn cho các miền này sẽ không được ghi vào thống kê",
"ignore_domains_desc_query": "Các truy vấn cho các miền này sẽ không được ghi vào nhật ký truy vấn",
"interval_hours": "{{count}} giờ", "interval_hours": "{{count}} giờ",
"interval_hours_plural": "{{count}} giờ", "interval_hours_plural": "{{count}} giờ",
"filters_configuration": "Cấu hình bộ lọc", "filters_configuration": "Cấu hình bộ lọc",
@@ -652,29 +643,5 @@
"confirm_dns_cache_clear": "Bạn có chắc chắn muốn xóa bộ đệm ẩn DNS không?", "confirm_dns_cache_clear": "Bạn có chắc chắn muốn xóa bộ đệm ẩn DNS không?",
"cache_cleared": "Đã xóa thành công bộ đệm DNS", "cache_cleared": "Đã xóa thành công bộ đệm DNS",
"clear_cache": "Xóa bộ nhớ cache", "clear_cache": "Xóa bộ nhớ cache",
"make_static": "Chuyển sang tĩnh", "protection_section_label": "Sự bảo vệ"
"theme_auto_desc": "Tự động (dựa trên chủ đề màu của thiết bị của bạn)",
"theme_dark_desc": "Chủ đề tối",
"theme_light_desc": "Chủ đề sáng",
"disable_for_seconds": "Trong {{count}} giây",
"disable_for_seconds_plural": "Trong {{count}} giây",
"disable_for_minutes": "Trong {{count}} phút",
"disable_for_minutes_plural": "Trong {{count}} phút",
"disable_for_hours": "Trong {{count}} giờ",
"disable_for_hours_plural": "Trong {{count}} giờ",
"disable_until_tomorrow": "Cho đến ngày mai",
"disable_notify_for_seconds": "Tắt bảo vệ trong {{count}} giây",
"disable_notify_for_seconds_plural": "Tắt bảo vệ trong {{count}} giây",
"disable_notify_for_minutes": "Tắt bảo vệ trong {{count}} phút",
"disable_notify_for_minutes_plural": "Tắt bảo vệ trong {{count}} phút",
"disable_notify_for_hours": "Tắt bảo vệ trong {{count}} giờ",
"disable_notify_for_hours_plural": "Tắt bảo vệ trong {{count}} giờ",
"disable_notify_until_tomorrow": "Vô hiệu hóa bảo vệ cho đến ngày mai",
"enable_protection_timer": "Bảo vệ sẽ được bật trong {{time}}",
"custom_retention_input": "Nhập thời gian giữ lại theo giờ",
"custom_rotation_input": "Nhập chu kỳ theo giờ",
"protection_section_label": "Sự bảo vệ",
"log_and_stats_section_label": "Nhật ký truy vấn và thống kê",
"ignore_query_log": "Bỏ qua máy khách này trong nhật ký truy vấn",
"ignore_statistics": "Bỏ qua máy khách này trong thống kê"
} }

View File

@@ -478,9 +478,7 @@
"setup_dns_notice": "为了使用 <1>DNS-over-HTTPS</1> 或者 <1>DNS-over-TLS</1> ,您需要在 AdGuard Home 设置中 <0>配置加密</0> 。", "setup_dns_notice": "为了使用 <1>DNS-over-HTTPS</1> 或者 <1>DNS-over-TLS</1> ,您需要在 AdGuard Home 设置中 <0>配置加密</0> 。",
"rewrite_added": "已成功添加 \"{{key}}\" 的 DNS 重写", "rewrite_added": "已成功添加 \"{{key}}\" 的 DNS 重写",
"rewrite_deleted": "已成功删除 \"{{key}}\" 的 DNS 重写", "rewrite_deleted": "已成功删除 \"{{key}}\" 的 DNS 重写",
"rewrite_updated": "DNS 重写已成功更新",
"rewrite_add": "添加 DNS 重写", "rewrite_add": "添加 DNS 重写",
"rewrite_edit": "编辑 DNS 重写",
"rewrite_not_found": "未找到 DNS 重写", "rewrite_not_found": "未找到 DNS 重写",
"rewrite_confirm_delete": "您确定要删除 \"{{key}}\" 的 DNS 重写?", "rewrite_confirm_delete": "您确定要删除 \"{{key}}\" 的 DNS 重写?",
"rewrite_desc": "可以轻松地为特定域名配置自定义 DNS 响应。", "rewrite_desc": "可以轻松地为特定域名配置自定义 DNS 响应。",

View File

@@ -48,7 +48,6 @@
"out_of_range_error": "必須介於 \"{{start}}\" - \"{{end}}\" 範圍之外", "out_of_range_error": "必須介於 \"{{start}}\" - \"{{end}}\" 範圍之外",
"lower_range_start_error": "必須小於起始值", "lower_range_start_error": "必須小於起始值",
"greater_range_start_error": "必須大於起始值", "greater_range_start_error": "必須大於起始值",
"gateway_or_subnet_invalid": "無效子網路",
"dhcp_form_gateway_input": "閘道 IP 位址", "dhcp_form_gateway_input": "閘道 IP 位址",
"dhcp_form_subnet_input": "子網路遮罩", "dhcp_form_subnet_input": "子網路遮罩",
"dhcp_form_range_title": "IP 位址範圍", "dhcp_form_range_title": "IP 位址範圍",
@@ -196,7 +195,6 @@
"form_error_url_or_path_format": "列表中含有的 URL 網址或絕對路徑", "form_error_url_or_path_format": "列表中含有的 URL 網址或絕對路徑",
"custom_filter_rules": "自訂過濾規則", "custom_filter_rules": "自訂過濾規則",
"custom_filter_rules_hint": "一行一條規則。您可以使用「adblock」語法或「hosts檔案」的語法。", "custom_filter_rules_hint": "一行一條規則。您可以使用「adblock」語法或「hosts檔案」的語法。",
"system_host_files": "系統 hosts 檔案",
"examples_title": "範例", "examples_title": "範例",
"example_meaning_filter_block": "封鎖對 example.org 網域及其所有子網域的存取", "example_meaning_filter_block": "封鎖對 example.org 網域及其所有子網域的存取",
"example_meaning_filter_whitelist": "解除對 example.org 網域及其所有子網域存取封鎖", "example_meaning_filter_whitelist": "解除對 example.org 網域及其所有子網域存取封鎖",
@@ -281,8 +279,6 @@
"rate_limit": "速率限制", "rate_limit": "速率限制",
"edns_enable": "啟用 EDNS Client Subnet", "edns_enable": "啟用 EDNS Client Subnet",
"edns_cs_desc": "傳送用戶端的子網路給 DNS 伺服器。", "edns_cs_desc": "傳送用戶端的子網路給 DNS 伺服器。",
"edns_use_custom_ip": "使用自訂 EDNS IP",
"edns_use_custom_ip_desc": "允許使用自訂 EDNS IP",
"rate_limit_desc": "限制單一裝置每秒發出的查詢次數(設定為 0 即表示無限制)", "rate_limit_desc": "限制單一裝置每秒發出的查詢次數(設定為 0 即表示無限制)",
"blocking_ipv4_desc": "回覆指定 IPv4 位址給被封鎖的網域的 A 紀錄查詢", "blocking_ipv4_desc": "回覆指定 IPv4 位址給被封鎖的網域的 A 紀錄查詢",
"blocking_ipv6_desc": "回覆指定 IPv6 位址給被封鎖的網域的 AAAA 紀錄查詢", "blocking_ipv6_desc": "回覆指定 IPv6 位址給被封鎖的網域的 AAAA 紀錄查詢",
@@ -291,9 +287,6 @@
"blocking_mode_nxdomain": "NXDOMAIN回應 NXDOMAIN 狀態碼", "blocking_mode_nxdomain": "NXDOMAIN回應 NXDOMAIN 狀態碼",
"blocking_mode_null_ip": "Null IP回應零值的 IP 位址A 紀錄回應 0.0.0.0 AAAA 紀錄回應 ::", "blocking_mode_null_ip": "Null IP回應零值的 IP 位址A 紀錄回應 0.0.0.0 AAAA 紀錄回應 ::",
"blocking_mode_custom_ip": "自訂 IP 位址:回應一個自訂的 IP 位址", "blocking_mode_custom_ip": "自訂 IP 位址:回應一個自訂的 IP 位址",
"theme_auto": "自動",
"theme_light": "明亮",
"theme_dark": "深色",
"upstream_dns_client_desc": "如果您將此欄位留白AdGuard Home 將使用 <0>DNS 設定</0> 內的設定的 DNS 伺服器。", "upstream_dns_client_desc": "如果您將此欄位留白AdGuard Home 將使用 <0>DNS 設定</0> 內的設定的 DNS 伺服器。",
"tracker_source": "追蹤器來源", "tracker_source": "追蹤器來源",
"source_label": "來源", "source_label": "來源",
@@ -404,7 +397,6 @@
"dns_providers": "下列為常見的<0> DNS 伺服器</0>。", "dns_providers": "下列為常見的<0> DNS 伺服器</0>。",
"update_now": "立即更新", "update_now": "立即更新",
"update_failed": "自動更新發生錯誤。請嘗試依照<a>以下步驟</a> 來手動更新。", "update_failed": "自動更新發生錯誤。請嘗試依照<a>以下步驟</a> 來手動更新。",
"manual_update": "請嘗試依照<a>下列步驟</a>來手動更新。",
"processing_update": "請稍候AdGuard Home 正在更新", "processing_update": "請稍候AdGuard Home 正在更新",
"clients_title": "用戶端", "clients_title": "用戶端",
"clients_desc": "對已連接到 AdGuard Home 的裝置進行設定", "clients_desc": "對已連接到 AdGuard Home 的裝置進行設定",
@@ -513,7 +505,6 @@
"statistics_clear_confirm": "您確定要清除統計資料嗎?", "statistics_clear_confirm": "您確定要清除統計資料嗎?",
"statistics_retention_confirm": "您確定要更改統計資料保存時間嗎?如果您縮短期限部分資料可能將會遺失", "statistics_retention_confirm": "您確定要更改統計資料保存時間嗎?如果您縮短期限部分資料可能將會遺失",
"statistics_cleared": "已清除統計資料", "statistics_cleared": "已清除統計資料",
"statistics_enable": "啟用統計數據",
"interval_hours": "{{count}} 小時", "interval_hours": "{{count}} 小時",
"interval_hours_plural": "{{count}} 小時", "interval_hours_plural": "{{count}} 小時",
"filters_configuration": "過濾器設定", "filters_configuration": "過濾器設定",
@@ -622,22 +613,5 @@
"original_response": "原始回應", "original_response": "原始回應",
"click_to_view_queries": "按一下以檢視查詢結果", "click_to_view_queries": "按一下以檢視查詢結果",
"port_53_faq_link": "連接埠 53 經常被「DNSStubListener」或「systemd-resolved」服務佔用。請閱讀下列有關解決<0>這個問題</0>的說明", "port_53_faq_link": "連接埠 53 經常被「DNSStubListener」或「systemd-resolved」服務佔用。請閱讀下列有關解決<0>這個問題</0>的說明",
"adg_will_drop_dns_queries": "AdGuard Home 將停止回應此用戶端的所有 DNS 查詢。", "adg_will_drop_dns_queries": "AdGuard Home 將停止回應此用戶端的所有 DNS 查詢。"
"safe_browsing": "安全瀏覽",
"served_from_cache": "{{value}} <i>(由快取回應)</i>",
"form_error_password_length": "密碼必須至少 {{value}} 個字元長度",
"theme_dark_desc": "深色主題",
"theme_light_desc": "淺色主題",
"disable_for_seconds": "{{count}} 秒",
"disable_for_seconds_plural": "{{count}} 秒",
"disable_for_minutes": "{{count}} 分鐘",
"disable_for_minutes_plural": "{{count}} 分鐘",
"disable_for_hours": "{{count}} 小時",
"disable_for_hours_plural": "{{count}} 小時",
"disable_until_tomorrow": "直到明天",
"disable_notify_for_seconds": "暫停防護 {{count}} 秒",
"disable_notify_for_seconds_plural": "暫停防護 {{count}} 秒",
"disable_notify_for_minutes": "暫停防護 {{count}} 分鐘",
"disable_notify_for_minutes_plural": "暫停防護 {{count}} 分鐘",
"disable_notify_for_hours": "暫停防護 {{count}} 小時"
} }

View File

@@ -478,9 +478,7 @@
"setup_dns_notice": "為了使用 <1>DNS-over-HTTPS</1> 或 <1>DNS-over-TLS</1>,您需要在 AdGuard Home 設定裡<0>配置加密</0>。", "setup_dns_notice": "為了使用 <1>DNS-over-HTTPS</1> 或 <1>DNS-over-TLS</1>,您需要在 AdGuard Home 設定裡<0>配置加密</0>。",
"rewrite_added": "對於 \"{{key}}\" 之 DNS 改寫被成功地加入", "rewrite_added": "對於 \"{{key}}\" 之 DNS 改寫被成功地加入",
"rewrite_deleted": "對於 \"{{key}}\" 之 DNS 改寫被成功地刪除", "rewrite_deleted": "對於 \"{{key}}\" 之 DNS 改寫被成功地刪除",
"rewrite_updated": "DNS 重寫已成功更新",
"rewrite_add": "新增 DNS 改寫", "rewrite_add": "新增 DNS 改寫",
"rewrite_edit": "編輯 DNS 重寫",
"rewrite_not_found": "無已發現之 DNS 改寫", "rewrite_not_found": "無已發現之 DNS 改寫",
"rewrite_confirm_delete": "您確定您想要刪除對於 \"{{key}}\" 之 DNS 改寫嗎?", "rewrite_confirm_delete": "您確定您想要刪除對於 \"{{key}}\" 之 DNS 改寫嗎?",
"rewrite_desc": "允許輕易地配置自訂的 DNS 回應供特定的域名。", "rewrite_desc": "允許輕易地配置自訂的 DNS 回應供特定的域名。",

View File

@@ -38,29 +38,6 @@ export const addRewrite = (config) => async (dispatch) => {
} }
}; };
export const updateRewriteRequest = createAction('UPDATE_REWRITE_REQUEST');
export const updateRewriteFailure = createAction('UPDATE_REWRITE_FAILURE');
export const updateRewriteSuccess = createAction('UPDATE_REWRITE_SUCCESS');
/**
* @param {Object} config
* @param {string} config.target - current DNS rewrite value
* @param {string} config.update - updated DNS rewrite value
*/
export const updateRewrite = (config) => async (dispatch) => {
dispatch(updateRewriteRequest());
try {
await apiClient.updateRewrite(config);
dispatch(updateRewriteSuccess());
dispatch(toggleRewritesModal());
dispatch(getRewritesList());
dispatch(addSuccessToast(i18next.t('rewrite_updated', { key: config.domain })));
} catch (error) {
dispatch(addErrorToast({ error }));
dispatch(updateRewriteFailure());
}
};
export const deleteRewriteRequest = createAction('DELETE_REWRITE_REQUEST'); export const deleteRewriteRequest = createAction('DELETE_REWRITE_REQUEST');
export const deleteRewriteFailure = createAction('DELETE_REWRITE_FAILURE'); export const deleteRewriteFailure = createAction('DELETE_REWRITE_FAILURE');
export const deleteRewriteSuccess = createAction('DELETE_REWRITE_SUCCESS'); export const deleteRewriteSuccess = createAction('DELETE_REWRITE_SUCCESS');

View File

@@ -455,8 +455,6 @@ class Api {
REWRITE_ADD = { path: 'rewrite/add', method: 'POST' }; REWRITE_ADD = { path: 'rewrite/add', method: 'POST' };
REWRITE_UPDATE = { path: 'rewrite/update', method: 'PUT' };
REWRITE_DELETE = { path: 'rewrite/delete', method: 'POST' }; REWRITE_DELETE = { path: 'rewrite/delete', method: 'POST' };
getRewritesList() { getRewritesList() {
@@ -472,14 +470,6 @@ class Api {
return this.makeRequest(path, method, parameters); return this.makeRequest(path, method, parameters);
} }
updateRewrite(config) {
const { path, method } = this.REWRITE_UPDATE;
const parameters = {
data: config,
};
return this.makeRequest(path, method, parameters);
}
deleteRewrite(config) { deleteRewrite(config) {
const { path, method } = this.REWRITE_DELETE; const { path, method } = this.REWRITE_DELETE;
const parameters = { const parameters = {

View File

@@ -6,7 +6,7 @@ import { shallowEqual, useSelector } from 'react-redux';
import Card from '../ui/Card'; import Card from '../ui/Card';
import { formatNumber } from '../../helpers/helpers'; import { formatNumber } from '../../helpers/helpers';
import LogsSearchLink from '../ui/LogsSearchLink'; import LogsSearchLink from '../ui/LogsSearchLink';
import { RESPONSE_FILTER, DAY } from '../../helpers/constants'; import { RESPONSE_FILTER } from '../../helpers/constants';
import Tooltip from '../ui/Tooltip'; import Tooltip from '../ui/Tooltip';
const Row = ({ const Row = ({
@@ -54,12 +54,12 @@ const Counters = ({ refreshButton, subtitle }) => {
avgProcessingTime, avgProcessingTime,
} = useSelector((state) => state.stats, shallowEqual); } = useSelector((state) => state.stats, shallowEqual);
const { t } = useTranslation(); const { t } = useTranslation();
const days = interval / DAY;
const rows = [ const rows = [
{ {
label: 'dns_query', label: 'dns_query',
count: numDnsQueries, count: numDnsQueries,
tooltipTitle: days === 1 ? 'number_of_dns_query_24_hours' : t('number_of_dns_query_days', { count: days }), tooltipTitle: interval === 1 ? 'number_of_dns_query_24_hours' : t('number_of_dns_query_days', { count: interval }),
response_status: RESPONSE_FILTER.ALL.QUERY, response_status: RESPONSE_FILTER.ALL.QUERY,
}, },
{ {

View File

@@ -105,7 +105,6 @@ Form.propTypes = {
submitting: PropTypes.bool.isRequired, submitting: PropTypes.bool.isRequired,
processingAdd: PropTypes.bool.isRequired, processingAdd: PropTypes.bool.isRequired,
t: PropTypes.func.isRequired, t: PropTypes.func.isRequired,
initialValues: PropTypes.object,
}; };
export default flow([ export default flow([

View File

@@ -3,7 +3,6 @@ import PropTypes from 'prop-types';
import { Trans, withTranslation } from 'react-i18next'; import { Trans, withTranslation } from 'react-i18next';
import ReactModal from 'react-modal'; import ReactModal from 'react-modal';
import { MODAL_TYPE } from '../../../helpers/constants';
import Form from './Form'; import Form from './Form';
const Modal = (props) => { const Modal = (props) => {
@@ -13,8 +12,6 @@ const Modal = (props) => {
toggleRewritesModal, toggleRewritesModal,
processingAdd, processingAdd,
processingDelete, processingDelete,
modalType,
currentRewrite,
} = props; } = props;
return ( return (
@@ -27,18 +24,13 @@ const Modal = (props) => {
<div className="modal-content"> <div className="modal-content">
<div className="modal-header"> <div className="modal-header">
<h4 className="modal-title"> <h4 className="modal-title">
{modalType === MODAL_TYPE.EDIT_REWRITE ? ( <Trans>rewrite_add</Trans>
<Trans>rewrite_edit</Trans>
) : (
<Trans>rewrite_add</Trans>
)}
</h4> </h4>
<button type="button" className="close" onClick={() => toggleRewritesModal()}> <button type="button" className="close" onClick={() => toggleRewritesModal()}>
<span className="sr-only">Close</span> <span className="sr-only">Close</span>
</button> </button>
</div> </div>
<Form <Form
initialValues={{ ...currentRewrite }}
onSubmit={handleSubmit} onSubmit={handleSubmit}
toggleRewritesModal={toggleRewritesModal} toggleRewritesModal={toggleRewritesModal}
processingAdd={processingAdd} processingAdd={processingAdd}
@@ -55,8 +47,6 @@ Modal.propTypes = {
toggleRewritesModal: PropTypes.func.isRequired, toggleRewritesModal: PropTypes.func.isRequired,
processingAdd: PropTypes.bool.isRequired, processingAdd: PropTypes.bool.isRequired,
processingDelete: PropTypes.bool.isRequired, processingDelete: PropTypes.bool.isRequired,
modalType: PropTypes.string.isRequired,
currentRewrite: PropTypes.object,
}; };
export default withTranslation()(Modal); export default withTranslation()(Modal);

View File

@@ -3,7 +3,6 @@ import PropTypes from 'prop-types';
import ReactTable from 'react-table'; import ReactTable from 'react-table';
import { withTranslation } from 'react-i18next'; import { withTranslation } from 'react-i18next';
import { sortIp } from '../../../helpers/helpers'; import { sortIp } from '../../../helpers/helpers';
import { MODAL_TYPE } from '../../../helpers/constants';
class Table extends Component { class Table extends Component {
cellWrap = ({ value }) => ( cellWrap = ({ value }) => (
@@ -32,44 +31,24 @@ class Table extends Component {
maxWidth: 100, maxWidth: 100,
sortable: false, sortable: false,
resizable: false, resizable: false,
Cell: (value) => { Cell: (value) => (
const currentRewrite = { <div className="logs__row logs__row--center">
answer: value.row.answer, <button
domain: value.row.domain, type="button"
}; className="btn btn-icon btn-icon--green btn-outline-secondary btn-sm"
onClick={() => this.props.handleDelete({
return ( answer: value.row.answer,
<div className="logs__row logs__row--center"> domain: value.row.domain,
<button })
type="button" }
className="btn btn-icon btn-outline-primary btn-sm mr-2" title={this.props.t('delete_table_action')}
onClick={() => { >
this.props.toggleRewritesModal({ <svg className="icons">
type: MODAL_TYPE.EDIT_REWRITE, <use xlinkHref="#delete" />
currentRewrite, </svg>
}); </button>
}} </div>
disabled={this.props.processingUpdate} ),
title={this.props.t('edit_table_action')}
>
<svg className="icons icon12">
<use xlinkHref="#edit" />
</svg>
</button>
<button
type="button"
className="btn btn-icon btn-outline-secondary btn-sm"
onClick={() => this.props.handleDelete(currentRewrite)}
title={this.props.t('delete_table_action')}
>
<svg className="icons">
<use xlinkHref="#delete" />
</svg>
</button>
</div>
);
},
}, },
]; ];
@@ -105,9 +84,7 @@ Table.propTypes = {
processing: PropTypes.bool.isRequired, processing: PropTypes.bool.isRequired,
processingAdd: PropTypes.bool.isRequired, processingAdd: PropTypes.bool.isRequired,
processingDelete: PropTypes.bool.isRequired, processingDelete: PropTypes.bool.isRequired,
processingUpdate: PropTypes.bool.isRequired,
handleDelete: PropTypes.func.isRequired, handleDelete: PropTypes.func.isRequired,
toggleRewritesModal: PropTypes.func.isRequired,
}; };
export default withTranslation()(Table); export default withTranslation()(Table);

View File

@@ -6,13 +6,16 @@ import Table from './Table';
import Modal from './Modal'; import Modal from './Modal';
import Card from '../../ui/Card'; import Card from '../../ui/Card';
import PageTitle from '../../ui/PageTitle'; import PageTitle from '../../ui/PageTitle';
import { MODAL_TYPE } from '../../../helpers/constants';
class Rewrites extends Component { class Rewrites extends Component {
componentDidMount() { componentDidMount() {
this.props.getRewritesList(); this.props.getRewritesList();
} }
handleSubmit = (values) => {
this.props.addRewrite(values);
};
handleDelete = (values) => { handleDelete = (values) => {
// eslint-disable-next-line no-alert // eslint-disable-next-line no-alert
if (window.confirm(this.props.t('rewrite_confirm_delete', { key: values.domain }))) { if (window.confirm(this.props.t('rewrite_confirm_delete', { key: values.domain }))) {
@@ -20,19 +23,6 @@ class Rewrites extends Component {
} }
}; };
handleSubmit = (values) => {
const { modalType, currentRewrite } = this.props.rewrites;
if (modalType === MODAL_TYPE.EDIT_REWRITE && currentRewrite) {
this.props.updateRewrite({
target: currentRewrite,
update: values,
});
} else {
this.props.addRewrite(values);
}
};
render() { render() {
const { const {
t, t,
@@ -46,9 +36,6 @@ class Rewrites extends Component {
processing, processing,
processingAdd, processingAdd,
processingDelete, processingDelete,
processingUpdate,
modalType,
currentRewrite,
} = rewrites; } = rewrites;
return ( return (
@@ -67,15 +54,13 @@ class Rewrites extends Component {
processing={processing} processing={processing}
processingAdd={processingAdd} processingAdd={processingAdd}
processingDelete={processingDelete} processingDelete={processingDelete}
processingUpdate={processingUpdate}
handleDelete={this.handleDelete} handleDelete={this.handleDelete}
toggleRewritesModal={toggleRewritesModal}
/> />
<button <button
type="button" type="button"
className="btn btn-success btn-standard mt-3" className="btn btn-success btn-standard mt-3"
onClick={() => toggleRewritesModal({ type: MODAL_TYPE.ADD_REWRITE })} onClick={() => toggleRewritesModal()}
disabled={processingAdd} disabled={processingAdd}
> >
<Trans>rewrite_add</Trans> <Trans>rewrite_add</Trans>
@@ -83,13 +68,10 @@ class Rewrites extends Component {
<Modal <Modal
isModalOpen={isModalOpen} isModalOpen={isModalOpen}
modalType={modalType}
toggleRewritesModal={toggleRewritesModal} toggleRewritesModal={toggleRewritesModal}
handleSubmit={this.handleSubmit} handleSubmit={this.handleSubmit}
processingAdd={processingAdd} processingAdd={processingAdd}
processingDelete={processingDelete} processingDelete={processingDelete}
processingUpdate={processingUpdate}
currentRewrite={currentRewrite}
/> />
</Fragment> </Fragment>
</Card> </Card>
@@ -104,7 +86,6 @@ Rewrites.propTypes = {
toggleRewritesModal: PropTypes.func.isRequired, toggleRewritesModal: PropTypes.func.isRequired,
addRewrite: PropTypes.func.isRequired, addRewrite: PropTypes.func.isRequired,
deleteRewrite: PropTypes.func.isRequired, deleteRewrite: PropTypes.func.isRequired,
updateRewrite: PropTypes.func.isRequired,
rewrites: PropTypes.object.isRequired, rewrites: PropTypes.object.isRequired,
}; };

View File

@@ -48,7 +48,6 @@ class Table extends Component {
Header: <Trans>list_url_table_header</Trans>, Header: <Trans>list_url_table_header</Trans>,
accessor: 'url', accessor: 'url',
minWidth: 180, minWidth: 180,
// eslint-disable-next-line react/prop-types
Cell: ({ value }) => ( Cell: ({ value }) => (
<div className="logs__row"> <div className="logs__row">
{isValidAbsolutePath(value) ? value {isValidAbsolutePath(value) ? value

View File

@@ -32,8 +32,6 @@ const ProtectionTimer = ({
}; };
ProtectionTimer.propTypes = { ProtectionTimer.propTypes = {
protectionDisabledDuration: PropTypes.number,
toggleProtectionSuccess: PropTypes.func.isRequired,
setProtectionTimerTime: PropTypes.func.isRequired, setProtectionTimerTime: PropTypes.func.isRequired,
}; };

View File

@@ -27,6 +27,7 @@ import {
} from '../../../helpers/constants'; } from '../../../helpers/constants';
import '../FormButton.css'; import '../FormButton.css';
const getIntervalTitle = (interval, t) => { const getIntervalTitle = (interval, t) => {
switch (interval) { switch (interval) {
case RETENTION_CUSTOM: case RETENTION_CUSTOM:

View File

@@ -7,6 +7,7 @@ import { Trans, withTranslation } from 'react-i18next';
import flow from 'lodash/flow'; import flow from 'lodash/flow';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { import {
renderRadioField, renderRadioField,
toNumber, toNumber,

View File

@@ -1,4 +1,3 @@
/* eslint-disable react/no-unknown-property */
import React from 'react'; import React from 'react';
import './Icons.css'; import './Icons.css';

View File

@@ -3,7 +3,6 @@ import {
getRewritesList, getRewritesList,
addRewrite, addRewrite,
deleteRewrite, deleteRewrite,
updateRewrite,
toggleRewritesModal, toggleRewritesModal,
} from '../actions/rewrites'; } from '../actions/rewrites';
import Rewrites from '../components/Filters/Rewrites'; import Rewrites from '../components/Filters/Rewrites';
@@ -18,7 +17,6 @@ const mapDispatchToProps = {
getRewritesList, getRewritesList,
addRewrite, addRewrite,
deleteRewrite, deleteRewrite,
updateRewrite,
toggleRewritesModal, toggleRewritesModal,
}; };

View File

@@ -173,8 +173,6 @@ export const MODAL_TYPE = {
ADD_FILTERS: 'ADD_FILTERS', ADD_FILTERS: 'ADD_FILTERS',
EDIT_FILTERS: 'EDIT_FILTERS', EDIT_FILTERS: 'EDIT_FILTERS',
CHOOSE_FILTERING_LIST: 'CHOOSE_FILTERING_LIST', CHOOSE_FILTERING_LIST: 'CHOOSE_FILTERING_LIST',
ADD_REWRITE: 'ADD_REWRITE',
EDIT_REWRITE: 'EDIT_REWRITE',
}; };
export const CLIENT_ID = { export const CLIENT_ID = {

View File

@@ -845,6 +845,7 @@ export const sortIp = (a, b) => {
} }
}; };
/** /**
* @param {number} filterId * @param {number} filterId
* @returns {string} * @returns {string}

File diff suppressed because it is too large Load Diff

View File

@@ -30,27 +30,7 @@ const rewrites = handleActions(
[actions.deleteRewriteFailure]: (state) => ({ ...state, processingDelete: false }), [actions.deleteRewriteFailure]: (state) => ({ ...state, processingDelete: false }),
[actions.deleteRewriteSuccess]: (state) => ({ ...state, processingDelete: false }), [actions.deleteRewriteSuccess]: (state) => ({ ...state, processingDelete: false }),
[actions.updateRewriteRequest]: (state) => ({ ...state, processingUpdate: true }), [actions.toggleRewritesModal]: (state) => {
[actions.updateRewriteFailure]: (state) => ({ ...state, processingUpdate: false }),
[actions.updateRewriteSuccess]: (state) => {
const newState = {
...state,
processingUpdate: false,
};
return newState;
},
[actions.toggleRewritesModal]: (state, { payload }) => {
if (payload) {
const newState = {
...state,
modalType: payload.type || '',
isModalOpen: !state.isModalOpen,
currentRewrite: payload.currentRewrite,
};
return newState;
}
const newState = { const newState = {
...state, ...state,
isModalOpen: !state.isModalOpen, isModalOpen: !state.isModalOpen,
@@ -62,10 +42,7 @@ const rewrites = handleActions(
processing: true, processing: true,
processingAdd: false, processingAdd: false,
processingDelete: false, processingDelete: false,
processingUpdate: false,
isModalOpen: false, isModalOpen: false,
modalType: '',
currentRewrite: {},
list: [], list: [],
}, },
); );

View File

@@ -82,5 +82,6 @@ CMD [ \
"/opt/adguardhome/AdGuardHome", \ "/opt/adguardhome/AdGuardHome", \
"--no-check-update", \ "--no-check-update", \
"-c", "/opt/adguardhome/conf/AdGuardHome.yaml", \ "-c", "/opt/adguardhome/conf/AdGuardHome.yaml", \
"-h", "0.0.0.0", \
"-w", "/opt/adguardhome/work" \ "-w", "/opt/adguardhome/work" \
] ]

View File

@@ -1,5 +1,13 @@
# Don't consider the HTTPS hostname since the enforced HTTPS redirection should # Don't consider the HTTPS hostname since the enforced HTTPS redirection should
# work if the SSL check skipped. See file docker/healthcheck.sh. # work if the SSL check skipped. See file docker/healthcheck.sh.
/^[^[:space:]]/ { is_http = /^http:/ } /^bind_host:/ { host = $2 }
/^[[:space:]]+address:/ { if (is_http) print "http://" $2 } /^bind_port:/ { port = $2 }
END {
if (match(host, ":")) {
print "http://[" host "]:" port
} else {
print "http://" host ":" port
}
}

19
go.mod
View File

@@ -3,8 +3,7 @@ module github.com/AdguardTeam/AdGuardHome
go 1.19 go 1.19
require ( require (
// TODO(a.garipov): Update to a tagged version when it's released. github.com/AdguardTeam/dnsproxy v0.50.2
github.com/AdguardTeam/dnsproxy v0.50.3-0.20230628054307-31e374065768
github.com/AdguardTeam/golibs v0.13.3 github.com/AdguardTeam/golibs v0.13.3
github.com/AdguardTeam/urlfilter v0.16.1 github.com/AdguardTeam/urlfilter v0.16.1
github.com/NYTimes/gziphandler v1.1.1 github.com/NYTimes/gziphandler v1.1.1
@@ -18,7 +17,7 @@ require (
github.com/google/gopacket v1.1.19 github.com/google/gopacket v1.1.19
github.com/google/renameio v1.0.1 github.com/google/renameio v1.0.1
github.com/google/uuid v1.3.0 github.com/google/uuid v1.3.0
github.com/insomniacslk/dhcp v0.0.0-20230612134759-b20c9ba983df github.com/insomniacslk/dhcp v0.0.0-20230516061539-49801966e6cb
github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86 github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86
github.com/kardianos/service v1.2.2 github.com/kardianos/service v1.2.2
github.com/mdlayher/ethernet v0.0.0-20220221185849-529eae5b6118 github.com/mdlayher/ethernet v0.0.0-20220221185849-529eae5b6118
@@ -27,13 +26,13 @@ require (
// TODO(a.garipov): This package is deprecated; find a new one or use our // TODO(a.garipov): This package is deprecated; find a new one or use our
// own code for that. Perhaps, use gopacket. // own code for that. Perhaps, use gopacket.
github.com/mdlayher/raw v0.1.0 github.com/mdlayher/raw v0.1.0
github.com/miekg/dns v1.1.55 github.com/miekg/dns v1.1.54
github.com/quic-go/quic-go v0.35.1 github.com/quic-go/quic-go v0.35.1
github.com/stretchr/testify v1.8.4 github.com/stretchr/testify v1.8.4
github.com/ti-mo/netfilter v0.5.0 github.com/ti-mo/netfilter v0.5.0
go.etcd.io/bbolt v1.3.7 go.etcd.io/bbolt v1.3.7
golang.org/x/crypto v0.10.0 golang.org/x/crypto v0.10.0
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1
golang.org/x/net v0.11.0 golang.org/x/net v0.11.0
golang.org/x/sys v0.9.0 golang.org/x/sys v0.9.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1 gopkg.in/natefinch/lumberjack.v2 v2.2.1
@@ -51,17 +50,17 @@ require (
github.com/golang/mock v1.6.0 // indirect github.com/golang/mock v1.6.0 // indirect
github.com/google/pprof v0.0.0-20230602150820-91b7bce49751 // indirect github.com/google/pprof v0.0.0-20230602150820-91b7bce49751 // indirect
github.com/mdlayher/socket v0.4.1 // indirect github.com/mdlayher/socket v0.4.1 // indirect
github.com/onsi/ginkgo/v2 v2.11.0 // indirect github.com/onsi/ginkgo/v2 v2.10.0 // indirect
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
github.com/pierrec/lz4/v4 v4.1.18 // indirect github.com/pierrec/lz4/v4 v4.1.17 // indirect
github.com/pkg/errors v0.9.1 // indirect github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/quic-go/qpack v0.4.0 // indirect github.com/quic-go/qpack v0.4.0 // indirect
github.com/quic-go/qtls-go1-19 v0.3.2 // indirect github.com/quic-go/qtls-go1-19 v0.3.2 // indirect
github.com/quic-go/qtls-go1-20 v0.2.2 // indirect github.com/quic-go/qtls-go1-20 v0.2.2 // indirect
github.com/u-root/uio v0.0.0-20230305220412-3e8cd9d6bf63 // indirect github.com/u-root/uio v0.0.0-20230305220412-3e8cd9d6bf63 // indirect
golang.org/x/mod v0.11.0 // indirect golang.org/x/mod v0.10.0 // indirect
golang.org/x/sync v0.3.0 // indirect golang.org/x/sync v0.2.0 // indirect
golang.org/x/text v0.10.0 // indirect golang.org/x/text v0.10.0 // indirect
golang.org/x/tools v0.10.0 // indirect golang.org/x/tools v0.9.3 // indirect
) )

38
go.sum
View File

@@ -1,5 +1,5 @@
github.com/AdguardTeam/dnsproxy v0.50.3-0.20230628054307-31e374065768 h1:5Ia6wA+tqAlTyzuaOVGSlHmb0osLWXeJUs3NxCuC4gA= github.com/AdguardTeam/dnsproxy v0.50.2 h1:p1471SsMZ6SMo7T51Olw4aNluahvMwSLMorwxYV18ts=
github.com/AdguardTeam/dnsproxy v0.50.3-0.20230628054307-31e374065768/go.mod h1:CQhZTkqC8X0ID6glrtyaxgqRRdiYfn1gJulC1cZ5Dn8= github.com/AdguardTeam/dnsproxy v0.50.2/go.mod h1:CQhZTkqC8X0ID6glrtyaxgqRRdiYfn1gJulC1cZ5Dn8=
github.com/AdguardTeam/golibs v0.4.0/go.mod h1:skKsDKIBB7kkFflLJBpfGX+G8QFTx0WKUzB6TIgtUj4= github.com/AdguardTeam/golibs v0.4.0/go.mod h1:skKsDKIBB7kkFflLJBpfGX+G8QFTx0WKUzB6TIgtUj4=
github.com/AdguardTeam/golibs v0.10.4/go.mod h1:rSfQRGHIdgfxriDDNgNJ7HmE5zRoURq8R+VdR81Zuzw= github.com/AdguardTeam/golibs v0.10.4/go.mod h1:rSfQRGHIdgfxriDDNgNJ7HmE5zRoURq8R+VdR81Zuzw=
github.com/AdguardTeam/golibs v0.13.3 h1:RT3QbzThtaLiFLkIUDS6/hlGEXrh0zYvdf4bd7UWpGo= github.com/AdguardTeam/golibs v0.13.3 h1:RT3QbzThtaLiFLkIUDS6/hlGEXrh0zYvdf4bd7UWpGo=
@@ -58,8 +58,8 @@ github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714 h1:/jC7qQFrv8CrSJVmaolDVOxTfS9kc36uB6H40kdbQq8= github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714 h1:/jC7qQFrv8CrSJVmaolDVOxTfS9kc36uB6H40kdbQq8=
github.com/insomniacslk/dhcp v0.0.0-20230612134759-b20c9ba983df h1:pF1MMIzEJzJ/MyI4bXYXVYyN8CJgoQ2PPKT2z3O/Cl4= github.com/insomniacslk/dhcp v0.0.0-20230516061539-49801966e6cb h1:6fDKEAXwe3rsfS4khW3EZ8kEqmSiV9szhMPcDrD+Y7Q=
github.com/insomniacslk/dhcp v0.0.0-20230612134759-b20c9ba983df/go.mod h1:7474bZ1YNCvarT6WFKie4kEET6J0KYRDC4XJqqXzQW4= github.com/insomniacslk/dhcp v0.0.0-20230516061539-49801966e6cb/go.mod h1:7474bZ1YNCvarT6WFKie4kEET6J0KYRDC4XJqqXzQW4=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/josharian/native v1.0.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/josharian/native v1.0.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
@@ -85,18 +85,18 @@ github.com/mdlayher/socket v0.2.1/go.mod h1:QLlNPkFR88mRUNQIzRBMfXxwKal8H7u1h3bL
github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U= github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U=
github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA= github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA=
github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
github.com/miekg/dns v1.1.55 h1:GoQ4hpsj0nFLYe+bWiCToyrBEJXkQfOOIvFGFy0lEgo= github.com/miekg/dns v1.1.54 h1:5jon9mWcb0sFJGpnI99tOMhCPyJ+RPVz5b63MQG0VWI=
github.com/miekg/dns v1.1.55/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= github.com/miekg/dns v1.1.54/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU= github.com/onsi/ginkgo/v2 v2.10.0 h1:sfUl4qgLdvkChZrWCYndY2EAu9BRIw1YphNAzy1VNWs=
github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM= github.com/onsi/ginkgo/v2 v2.10.0/go.mod h1:UDQOh5wbQUlMnkLfVaIUMtQ1Vus92oM+P2JX1aulgcE=
github.com/onsi/gomega v1.27.8 h1:gegWiwZjBsf2DgiSbf5hpokZ98JVDMcWkUiigk6/KXc= github.com/onsi/gomega v1.27.7 h1:fVih9JD6ogIiHUN6ePK7HJidyEDpWGVB5mzM7cWNXoU=
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pierrec/lz4/v4 v4.1.18 h1:xaKrnTkyoqfh1YItXl56+6KJNVYWlEEPuAQW9xsplYQ= github.com/pierrec/lz4/v4 v4.1.17 h1:kV4Ip+/hUBC+8T6+2EgburRtkE9ef4nbY3f4dFhGjMc=
github.com/pierrec/lz4/v4 v4.1.18/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pierrec/lz4/v4 v4.1.17/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -136,13 +136,13 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM= golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM=
golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME= golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc=
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU= golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk=
golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@@ -156,8 +156,8 @@ golang.org/x/net v0.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU=
golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ= golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190322080309-f49334f85ddc/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190322080309-f49334f85ddc/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -191,8 +191,8 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.10.0 h1:tvDr/iQoUqNdohiYm0LmmKcBk+q86lb9EprIUFhHHGg= golang.org/x/tools v0.9.3 h1:Gn1I8+64MsuTb/HpH+LmQtNas23LhUVr3rYZ0eKuaMM=
golang.org/x/tools v0.10.0/go.mod h1:UJwyiVBsOA2uwvK/e5OY3GTpDUJriEd+/YlqAwLPmyM= golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@@ -56,20 +56,15 @@ func (rm *requestMatcher) MatchRequest(
) (res *urlfilter.DNSResult, ok bool) { ) (res *urlfilter.DNSResult, ok bool) {
switch req.DNSType { switch req.DNSType {
case dns.TypeA, dns.TypeAAAA, dns.TypePTR: case dns.TypeA, dns.TypeAAAA, dns.TypePTR:
log.Debug( log.Debug("%s: handling the request for %s", hostsContainerPrefix, req.Hostname)
"%s: handling %s request for %s",
hostsContainerPrefix,
dns.Type(req.DNSType),
req.Hostname,
)
rm.stateLock.RLock()
defer rm.stateLock.RUnlock()
return rm.engine.MatchRequest(req)
default: default:
return nil, false return nil, false
} }
rm.stateLock.RLock()
defer rm.stateLock.RUnlock()
return rm.engine.MatchRequest(req)
} }
// Translate returns the source hosts-syntax rule for the generated dnsrewrite // Translate returns the source hosts-syntax rule for the generated dnsrewrite
@@ -101,8 +96,6 @@ const hostsContainerPrefix = "hosts container"
// HostsContainer stores the relevant hosts database provided by the OS and // HostsContainer stores the relevant hosts database provided by the OS and
// processes both A/AAAA and PTR DNS requests for those. // processes both A/AAAA and PTR DNS requests for those.
//
// TODO(e.burkov): Improve API and move to golibs.
type HostsContainer struct { type HostsContainer struct {
// requestMatcher matches the requests and translates the rules. It's // requestMatcher matches the requests and translates the rules. It's
// embedded to implement MatchRequest and Translate for *HostsContainer. // embedded to implement MatchRequest and Translate for *HostsContainer.

View File

@@ -25,8 +25,11 @@ func (s *bitSet) isSet(n uint64) (ok bool) {
var word uint64 var word uint64
word, ok = s.words[wordIdx] word, ok = s.words[wordIdx]
if !ok {
return false
}
return ok && word&(1<<bitIdx) != 0 return word&(1<<bitIdx) != 0
} }
// set sets or unsets a bit. // set sets or unsets a bit.

View File

@@ -249,30 +249,31 @@ func (c *dhcpConn) buildEtherPkt(payload []byte, peer *dhcpUnicastAddr) (pkt []b
func (s *v4Server) send(peer net.Addr, conn net.PacketConn, req, resp *dhcpv4.DHCPv4) { func (s *v4Server) send(peer net.Addr, conn net.PacketConn, req, resp *dhcpv4.DHCPv4) {
switch giaddr, ciaddr, mtype := req.GatewayIPAddr, req.ClientIPAddr, resp.MessageType(); { switch giaddr, ciaddr, mtype := req.GatewayIPAddr, req.ClientIPAddr, resp.MessageType(); {
case giaddr != nil && !giaddr.IsUnspecified(): case giaddr != nil && !giaddr.IsUnspecified():
// Send any return messages to the server port on the BOOTP relay agent // Send any return messages to the server port on the BOOTP
// whose address appears in giaddr. // relay agent whose address appears in giaddr.
peer = &net.UDPAddr{ peer = &net.UDPAddr{
IP: giaddr, IP: giaddr,
Port: dhcpv4.ServerPort, Port: dhcpv4.ServerPort,
} }
if mtype == dhcpv4.MessageTypeNak { if mtype == dhcpv4.MessageTypeNak {
// Set the broadcast bit in the DHCPNAK, so that the relay agent // Set the broadcast bit in the DHCPNAK, so that the relay agent
// broadcasts it to the client, because the client may not have a // broadcasts it to the client, because the client may not have
// correct network address or subnet mask, and the client may not be // a correct network address or subnet mask, and the client may not
// answering ARP requests. // be answering ARP requests.
resp.SetBroadcast() resp.SetBroadcast()
} }
case mtype == dhcpv4.MessageTypeNak: case mtype == dhcpv4.MessageTypeNak:
// Broadcast any DHCPNAK messages to 0xffffffff. // Broadcast any DHCPNAK messages to 0xffffffff.
case ciaddr != nil && !ciaddr.IsUnspecified(): case ciaddr != nil && !ciaddr.IsUnspecified():
// Unicast DHCPOFFER and DHCPACK messages to the address in ciaddr. // Unicast DHCPOFFER and DHCPACK messages to the address in
// ciaddr.
peer = &net.UDPAddr{ peer = &net.UDPAddr{
IP: ciaddr, IP: ciaddr,
Port: dhcpv4.ClientPort, Port: dhcpv4.ClientPort,
} }
case !req.IsBroadcast() && req.ClientHWAddr != nil: case !req.IsBroadcast() && req.ClientHWAddr != nil:
// Unicast DHCPOFFER and DHCPACK messages to the client's hardware // Unicast DHCPOFFER and DHCPACK messages to the client's
// address and yiaddr. // hardware address and yiaddr.
peer = &dhcpUnicastAddr{ peer = &dhcpUnicastAddr{
Addr: raw.Addr{HardwareAddr: req.ClientHWAddr}, Addr: raw.Addr{HardwareAddr: req.ClientHWAddr},
yiaddr: resp.YourIPAddr, yiaddr: resp.YourIPAddr,

View File

@@ -247,30 +247,31 @@ func (c *dhcpConn) buildEtherPkt(payload []byte, peer *dhcpUnicastAddr) (pkt []b
func (s *v4Server) send(peer net.Addr, conn net.PacketConn, req, resp *dhcpv4.DHCPv4) { func (s *v4Server) send(peer net.Addr, conn net.PacketConn, req, resp *dhcpv4.DHCPv4) {
switch giaddr, ciaddr, mtype := req.GatewayIPAddr, req.ClientIPAddr, resp.MessageType(); { switch giaddr, ciaddr, mtype := req.GatewayIPAddr, req.ClientIPAddr, resp.MessageType(); {
case giaddr != nil && !giaddr.IsUnspecified(): case giaddr != nil && !giaddr.IsUnspecified():
// Send any return messages to the server port on the BOOTP relay agent // Send any return messages to the server port on the BOOTP
// whose address appears in giaddr. // relay agent whose address appears in giaddr.
peer = &net.UDPAddr{ peer = &net.UDPAddr{
IP: giaddr, IP: giaddr,
Port: dhcpv4.ServerPort, Port: dhcpv4.ServerPort,
} }
if mtype == dhcpv4.MessageTypeNak { if mtype == dhcpv4.MessageTypeNak {
// Set the broadcast bit in the DHCPNAK, so that the relay agent // Set the broadcast bit in the DHCPNAK, so that the relay agent
// broadcasts it to the client, because the client may not have a // broadcasts it to the client, because the client may not have
// correct network address or subnet mask, and the client may not be // a correct network address or subnet mask, and the client may not
// answering ARP requests. // be answering ARP requests.
resp.SetBroadcast() resp.SetBroadcast()
} }
case mtype == dhcpv4.MessageTypeNak: case mtype == dhcpv4.MessageTypeNak:
// Broadcast any DHCPNAK messages to 0xffffffff. // Broadcast any DHCPNAK messages to 0xffffffff.
case ciaddr != nil && !ciaddr.IsUnspecified(): case ciaddr != nil && !ciaddr.IsUnspecified():
// Unicast DHCPOFFER and DHCPACK messages to the address in ciaddr. // Unicast DHCPOFFER and DHCPACK messages to the address in
// ciaddr.
peer = &net.UDPAddr{ peer = &net.UDPAddr{
IP: ciaddr, IP: ciaddr,
Port: dhcpv4.ClientPort, Port: dhcpv4.ClientPort,
} }
case !req.IsBroadcast() && req.ClientHWAddr != nil: case !req.IsBroadcast() && req.ClientHWAddr != nil:
// Unicast DHCPOFFER and DHCPACK messages to the client's hardware // Unicast DHCPOFFER and DHCPACK messages to the client's
// address and yiaddr. // hardware address and yiaddr.
peer = &dhcpUnicastAddr{ peer = &dhcpUnicastAddr{
Addr: packet.Addr{HardwareAddr: req.ClientHWAddr}, Addr: packet.Addr{HardwareAddr: req.ClientHWAddr},
yiaddr: resp.YourIPAddr, yiaddr: resp.YourIPAddr,

View File

@@ -28,9 +28,8 @@ const (
defaultBackoff time.Duration = 500 * time.Millisecond defaultBackoff time.Duration = 500 * time.Millisecond
) )
// Lease contains the necessary information about a DHCP lease. It's used as is // Lease contains the necessary information about a DHCP lease. It's used in
// in the database, so don't change it until it's absolutely necessary, see // various places. So don't change it without good reason.
// [dataVersion].
type Lease struct { type Lease struct {
// Expiry is the expiration time of the lease. // Expiry is the expiration time of the lease.
Expiry time.Time `json:"expires"` Expiry time.Time `json:"expires"`
@@ -42,6 +41,8 @@ type Lease struct {
HWAddr net.HardwareAddr `json:"mac"` HWAddr net.HardwareAddr `json:"mac"`
// IP is the IP address leased to the client. // IP is the IP address leased to the client.
//
// TODO(a.garipov): Migrate leases.db.
IP netip.Addr `json:"ip"` IP netip.Addr `json:"ip"`
// IsStatic defines if the lease is static. // IsStatic defines if the lease is static.

View File

@@ -51,9 +51,6 @@ func migrateDB(conf *ServerConfig) (err error) {
oldLeasesPath := filepath.Join(conf.WorkDir, dbFilename) oldLeasesPath := filepath.Join(conf.WorkDir, dbFilename)
dataDirPath := filepath.Join(conf.DataDir, dataFilename) dataDirPath := filepath.Join(conf.DataDir, dataFilename)
// #nosec G304 -- Trust this path, since it's taken from the old file name
// relative to the working directory and should generally be considered
// safe.
file, err := os.Open(oldLeasesPath) file, err := os.Open(oldLeasesPath)
if errors.Is(err, os.ErrNotExist) { if errors.Is(err, os.ErrNotExist) {
// Nothing to migrate. // Nothing to migrate.

View File

@@ -200,7 +200,7 @@ func createICMPv6RAPacket(params icmpv6RA) (data []byte, err error) {
func (ra *raCtx) Init() (err error) { func (ra *raCtx) Init() (err error) {
ra.stop.Store(0) ra.stop.Store(0)
ra.conn = nil ra.conn = nil
if !ra.raAllowSLAAC && !ra.raSLAACOnly { if !(ra.raAllowSLAAC || ra.raSLAACOnly) {
return nil return nil
} }

View File

@@ -1,86 +0,0 @@
package dhcpsvc
import (
"net/netip"
"time"
"github.com/google/gopacket/layers"
)
// Config is the configuration for the DHCP service.
type Config struct {
// Interfaces stores configurations of DHCP server specific for the network
// interface identified by its name.
Interfaces map[string]*InterfaceConfig
// LocalDomainName is the top-level domain name to use for resolving DHCP
// clients' hostnames.
LocalDomainName string
// ICMPTimeout is the timeout for checking another DHCP server's presence.
ICMPTimeout time.Duration
// Enabled is the state of the service, whether it is enabled or not.
Enabled bool
}
// InterfaceConfig is the configuration of a single DHCP interface.
type InterfaceConfig struct {
// IPv4 is the configuration of DHCP protocol for IPv4.
IPv4 *IPv4Config
// IPv6 is the configuration of DHCP protocol for IPv6.
IPv6 *IPv6Config
}
// IPv4Config is the interface-specific configuration for DHCPv4.
type IPv4Config struct {
// GatewayIP is the IPv4 address of the network's gateway. It is used as
// the default gateway for DHCP clients and also used in calculating the
// network-specific broadcast address.
GatewayIP netip.Addr
// SubnetMask is the IPv4 subnet mask of the network. It should be a valid
// IPv4 subnet mask (i.e. all 1s followed by all 0s).
SubnetMask netip.Addr
// RangeStart is the first address in the range to assign to DHCP clients.
RangeStart netip.Addr
// RangeEnd is the last address in the range to assign to DHCP clients.
RangeEnd netip.Addr
// Options is the list of DHCP options to send to DHCP clients.
Options layers.DHCPOptions
// LeaseDuration is the TTL of a DHCP lease.
LeaseDuration time.Duration
// Enabled is the state of the DHCPv4 service, whether it is enabled or not
// on the specific interface.
Enabled bool
}
// IPv6Config is the interface-specific configuration for DHCPv6.
type IPv6Config struct {
// RangeStart is the first address in the range to assign to DHCP clients.
RangeStart netip.Addr
// Options is the list of DHCP options to send to DHCP clients.
Options layers.DHCPOptions
// LeaseDuration is the TTL of a DHCP lease.
LeaseDuration time.Duration
// RASlaacOnly defines whether the DHCP clients should only use SLAAC for
// address assignment.
RASLAACOnly bool
// RAAllowSlaac defines whether the DHCP clients may use SLAAC for address
// assignment.
RAAllowSLAAC bool
// Enabled is the state of the DHCPv6 service, whether it is enabled or not
// on the specific interface.
Enabled bool
}

View File

@@ -1,120 +0,0 @@
// Package dhcpsvc contains the AdGuard Home DHCP service.
//
// TODO(e.burkov): Add tests.
package dhcpsvc
import (
"context"
"net"
"net/netip"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/next/agh"
)
// Lease is a DHCP lease.
//
// TODO(e.burkov): Consider it to [agh], since it also may be needed in
// [websvc]. Also think of implementing iterating methods with appropriate
// signatures.
type Lease struct {
// IP is the IP address leased to the client.
IP netip.Addr
// Expiry is the expiration time of the lease.
Expiry time.Time
// Hostname of the client.
Hostname string
// HWAddr is the physical hardware address (MAC address).
HWAddr net.HardwareAddr
// IsStatic defines if the lease is static.
IsStatic bool
}
type Interface interface {
agh.ServiceWithConfig[*Config]
// Enabled returns true if DHCP provides information about clients.
Enabled() (ok bool)
// HostByIP returns the hostname of the DHCP client with the given IP
// address. The address will be netip.Addr{} if there is no such client,
// due to an assumption that a DHCP client must always have an IP address.
HostByIP(ip netip.Addr) (host string)
// MACByIP returns the MAC address for the given IP address leased. It
// returns nil if there is no such client, due to an assumption that a DHCP
// client must always have a MAC address.
MACByIP(ip netip.Addr) (mac net.HardwareAddr)
// IPByHost returns the IP address of the DHCP client with the given
// hostname. The hostname will be an empty string if there is no such
// client, due to an assumption that a DHCP client must always have a
// hostname, either set by the client or assigned automatically.
IPByHost(host string) (ip netip.Addr)
// Leases returns all the DHCP leases.
Leases() (leases []*Lease)
// AddLease adds a new DHCP lease. It returns an error if the lease is
// invalid or already exists.
AddLease(l *Lease) (err error)
// EditLease changes an existing DHCP lease. It returns an error if there
// is no lease equal to old or if new is invalid or already exists.
EditLease(old, new *Lease) (err error)
// RemoveLease removes an existing DHCP lease. It returns an error if there
// is no lease equal to l.
RemoveLease(l *Lease) (err error)
// Reset removes all the DHCP leases.
Reset() (err error)
}
// Empty is an [Interface] implementation that does nothing.
type Empty struct{}
// type check
var _ Interface = Empty{}
// Start implements the [Service] interface for Empty.
func (Empty) Start() (err error) { return nil }
// Shutdown implements the [Service] interface for Empty.
func (Empty) Shutdown(_ context.Context) (err error) { return nil }
var _ agh.ServiceWithConfig[*Config] = Empty{}
// Config implements the [ServiceWithConfig] interface for Empty.
func (Empty) Config() (conf *Config) { return nil }
// Enabled implements the [Interface] interface for Empty.
func (Empty) Enabled() (ok bool) { return false }
// HostByIP implements the [Interface] interface for Empty.
func (Empty) HostByIP(_ netip.Addr) (host string) { return "" }
// MACByIP implements the [Interface] interface for Empty.
func (Empty) MACByIP(_ netip.Addr) (mac net.HardwareAddr) { return nil }
// IPByHost implements the [Interface] interface for Empty.
func (Empty) IPByHost(_ string) (ip netip.Addr) { return netip.Addr{} }
// Leases implements the [Interface] interface for Empty.
func (Empty) Leases() (leases []*Lease) { return nil }
// AddLease implements the [Interface] interface for Empty.
func (Empty) AddLease(_ *Lease) (err error) { return nil }
// EditLease implements the [Interface] interface for Empty.
func (Empty) EditLease(_, _ *Lease) (err error) { return nil }
// RemoveLease implements the [Interface] interface for Empty.
func (Empty) RemoveLease(_ *Lease) (err error) { return nil }
// Reset implements the [Interface] interface for Empty.
func (Empty) Reset() (err error) { return nil }

View File

@@ -15,6 +15,7 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/aghtls" "github.com/AdguardTeam/AdGuardHome/internal/aghtls"
"github.com/AdguardTeam/AdGuardHome/internal/filtering" "github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/dnsproxy/proxy" "github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/dnsproxy/upstream"
"github.com/AdguardTeam/golibs/errors" "github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log" "github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/netutil" "github.com/AdguardTeam/golibs/netutil"
@@ -435,6 +436,102 @@ func (s *Server) initDefaultSettings() {
} }
} }
// UpstreamHTTPVersions returns the HTTP versions for upstream configuration
// depending on configuration.
func UpstreamHTTPVersions(http3 bool) (v []upstream.HTTPVersion) {
if !http3 {
return upstream.DefaultHTTPVersions
}
return []upstream.HTTPVersion{
upstream.HTTPVersion3,
upstream.HTTPVersion2,
upstream.HTTPVersion11,
}
}
// prepareUpstreamSettings - prepares upstream DNS server settings
func (s *Server) prepareUpstreamSettings() error {
// We're setting a customized set of RootCAs. The reason is that Go default
// mechanism of loading TLS roots does not always work properly on some
// routers so we're loading roots manually and pass it here.
//
// See [aghtls.SystemRootCAs].
upstream.RootCAs = s.conf.TLSv12Roots
upstream.CipherSuites = s.conf.TLSCiphers
// Load upstreams either from the file, or from the settings
var upstreams []string
if s.conf.UpstreamDNSFileName != "" {
data, err := os.ReadFile(s.conf.UpstreamDNSFileName)
if err != nil {
return fmt.Errorf("reading upstream from file: %w", err)
}
upstreams = stringutil.SplitTrimmed(string(data), "\n")
log.Debug("dns: using %d upstream servers from file %s", len(upstreams), s.conf.UpstreamDNSFileName)
} else {
upstreams = s.conf.UpstreamDNS
}
httpVersions := UpstreamHTTPVersions(s.conf.UseHTTP3Upstreams)
upstreams = stringutil.FilterOut(upstreams, IsCommentOrEmpty)
upstreamConfig, err := proxy.ParseUpstreamsConfig(
upstreams,
&upstream.Options{
Bootstrap: s.conf.BootstrapDNS,
Timeout: s.conf.UpstreamTimeout,
HTTPVersions: httpVersions,
PreferIPv6: s.conf.BootstrapPreferIPv6,
},
)
if err != nil {
return fmt.Errorf("parsing upstream config: %w", err)
}
if len(upstreamConfig.Upstreams) == 0 {
log.Info("warning: no default upstream servers specified, using %v", defaultDNS)
var uc *proxy.UpstreamConfig
uc, err = proxy.ParseUpstreamsConfig(
defaultDNS,
&upstream.Options{
Bootstrap: s.conf.BootstrapDNS,
Timeout: s.conf.UpstreamTimeout,
HTTPVersions: httpVersions,
PreferIPv6: s.conf.BootstrapPreferIPv6,
},
)
if err != nil {
return fmt.Errorf("parsing default upstreams: %w", err)
}
upstreamConfig.Upstreams = uc.Upstreams
}
s.conf.UpstreamConfig = upstreamConfig
return nil
}
// setProxyUpstreamMode sets the upstream mode and related settings in conf
// based on provided parameters.
func setProxyUpstreamMode(
conf *proxy.Config,
allServers bool,
fastestAddr bool,
fastestTimeout time.Duration,
) {
if allServers {
conf.UpstreamMode = proxy.UModeParallel
} else if fastestAddr {
conf.UpstreamMode = proxy.UModeFastestAddr
conf.FastestPingTimeout = fastestTimeout
} else {
conf.UpstreamMode = proxy.UModeLoadBalance
}
}
// prepareIpsetListSettings reads and prepares the ipset configuration either // prepareIpsetListSettings reads and prepares the ipset configuration either
// from a file or from the data in the configuration file. // from a file or from the data in the configuration file.
func (s *Server) prepareIpsetListSettings() (err error) { func (s *Server) prepareIpsetListSettings() (err error) {
@@ -443,7 +540,6 @@ func (s *Server) prepareIpsetListSettings() (err error) {
return s.ipset.init(s.conf.IpsetList) return s.ipset.init(s.conf.IpsetList)
} }
// #nosec G304 -- Trust the path explicitly given by the user.
data, err := os.ReadFile(fn) data, err := os.ReadFile(fn)
if err != nil { if err != nil {
return err return err

View File

@@ -145,13 +145,10 @@ func (s *Server) handleDNSRequest(_ *proxy.Proxy, pctx *proxy.DNSContext) error
// processRecursion checks the incoming request and halts its handling by // processRecursion checks the incoming request and halts its handling by
// answering NXDOMAIN if s has tried to resolve it recently. // answering NXDOMAIN if s has tried to resolve it recently.
func (s *Server) processRecursion(dctx *dnsContext) (rc resultCode) { func (s *Server) processRecursion(dctx *dnsContext) (rc resultCode) {
log.Debug("dnsforward: started processing recursion")
defer log.Debug("dnsforward: finished processing recursion")
pctx := dctx.proxyCtx pctx := dctx.proxyCtx
if msg := pctx.Req; msg != nil && s.recDetector.check(*msg) { if msg := pctx.Req; msg != nil && s.recDetector.check(*msg) {
log.Debug("dnsforward: recursion detected resolving %q", msg.Question[0].Name) log.Debug("recursion detected resolving %q", msg.Question[0].Name)
pctx.Res = s.genNXDomain(pctx.Req) pctx.Res = s.genNXDomain(pctx.Req)
return resultCodeFinish return resultCodeFinish
@@ -161,13 +158,10 @@ func (s *Server) processRecursion(dctx *dnsContext) (rc resultCode) {
} }
// processInitial terminates the following processing for some requests if // processInitial terminates the following processing for some requests if
// needed and enriches dctx with some client-specific information. // needed and enriches the ctx with some client-specific information.
// //
// TODO(e.burkov): Decompose into less general processors. // TODO(e.burkov): Decompose into less general processors.
func (s *Server) processInitial(dctx *dnsContext) (rc resultCode) { func (s *Server) processInitial(dctx *dnsContext) (rc resultCode) {
log.Debug("dnsforward: started processing initial")
defer log.Debug("dnsforward: finished processing initial")
pctx := dctx.proxyCtx pctx := dctx.proxyCtx
q := pctx.Req.Question[0] q := pctx.Req.Question[0]
qt := q.Qtype qt := q.Qtype
@@ -288,9 +282,6 @@ func (s *Server) onDHCPLeaseChanged(flags int) {
// //
// See https://www.ietf.org/archive/id/draft-ietf-add-ddr-10.html. // See https://www.ietf.org/archive/id/draft-ietf-add-ddr-10.html.
func (s *Server) processDDRQuery(dctx *dnsContext) (rc resultCode) { func (s *Server) processDDRQuery(dctx *dnsContext) (rc resultCode) {
log.Debug("dnsforward: started processing ddr")
defer log.Debug("dnsforward: finished processing ddr")
if !s.conf.HandleDDR { if !s.conf.HandleDDR {
return resultCodeSuccess return resultCodeSuccess
} }
@@ -384,9 +375,6 @@ func (s *Server) makeDDRResponse(req *dns.Msg) (resp *dns.Msg) {
// processDetermineLocal determines if the client's IP address is from locally // processDetermineLocal determines if the client's IP address is from locally
// served network and saves the result into the context. // served network and saves the result into the context.
func (s *Server) processDetermineLocal(dctx *dnsContext) (rc resultCode) { func (s *Server) processDetermineLocal(dctx *dnsContext) (rc resultCode) {
log.Debug("dnsforward: started processing local detection")
defer log.Debug("dnsforward: finished processing local detection")
rc = resultCodeSuccess rc = resultCodeSuccess
var ip net.IP var ip net.IP
@@ -417,9 +405,6 @@ func (s *Server) dhcpHostToIP(host string) (ip netip.Addr, ok bool) {
// //
// TODO(a.garipov): Adapt to AAAA as well. // TODO(a.garipov): Adapt to AAAA as well.
func (s *Server) processDHCPHosts(dctx *dnsContext) (rc resultCode) { func (s *Server) processDHCPHosts(dctx *dnsContext) (rc resultCode) {
log.Debug("dnsforward: started processing dhcp hosts")
defer log.Debug("dnsforward: finished processing dhcp hosts")
pctx := dctx.proxyCtx pctx := dctx.proxyCtx
req := pctx.Req req := pctx.Req
q := req.Question[0] q := req.Question[0]
@@ -559,9 +544,6 @@ func extractARPASubnet(domain string) (pref netip.Prefix, err error) {
// processRestrictLocal responds with NXDOMAIN to PTR requests for IP addresses // processRestrictLocal responds with NXDOMAIN to PTR requests for IP addresses
// in locally served network from external clients. // in locally served network from external clients.
func (s *Server) processRestrictLocal(dctx *dnsContext) (rc resultCode) { func (s *Server) processRestrictLocal(dctx *dnsContext) (rc resultCode) {
log.Debug("dnsforward: started processing local restriction")
defer log.Debug("dnsforward: finished processing local restriction")
pctx := dctx.proxyCtx pctx := dctx.proxyCtx
req := pctx.Req req := pctx.Req
q := req.Question[0] q := req.Question[0]
@@ -631,9 +613,6 @@ func (s *Server) ipToDHCPHost(ip netip.Addr) (host string, ok bool) {
// processDHCPAddrs responds to PTR requests if the target IP is leased by the // processDHCPAddrs responds to PTR requests if the target IP is leased by the
// DHCP server. // DHCP server.
func (s *Server) processDHCPAddrs(dctx *dnsContext) (rc resultCode) { func (s *Server) processDHCPAddrs(dctx *dnsContext) (rc resultCode) {
log.Debug("dnsforward: started processing dhcp addrs")
defer log.Debug("dnsforward: finished processing dhcp addrs")
pctx := dctx.proxyCtx pctx := dctx.proxyCtx
if pctx.Res != nil { if pctx.Res != nil {
return resultCodeSuccess return resultCodeSuccess
@@ -679,9 +658,6 @@ func (s *Server) processDHCPAddrs(dctx *dnsContext) (rc resultCode) {
// processLocalPTR responds to PTR requests if the target IP is detected to be // processLocalPTR responds to PTR requests if the target IP is detected to be
// inside the local network and the query was not answered from DHCP. // inside the local network and the query was not answered from DHCP.
func (s *Server) processLocalPTR(dctx *dnsContext) (rc resultCode) { func (s *Server) processLocalPTR(dctx *dnsContext) (rc resultCode) {
log.Debug("dnsforward: started processing local ptr")
defer log.Debug("dnsforward: finished processing local ptr")
pctx := dctx.proxyCtx pctx := dctx.proxyCtx
if pctx.Res != nil { if pctx.Res != nil {
return resultCodeSuccess return resultCodeSuccess
@@ -716,9 +692,6 @@ func (s *Server) processLocalPTR(dctx *dnsContext) (rc resultCode) {
// Apply filtering logic // Apply filtering logic
func (s *Server) processFilteringBeforeRequest(ctx *dnsContext) (rc resultCode) { func (s *Server) processFilteringBeforeRequest(ctx *dnsContext) (rc resultCode) {
log.Debug("dnsforward: started processing filtering before req")
defer log.Debug("dnsforward: finished processing filtering before req")
if ctx.proxyCtx.Res != nil { if ctx.proxyCtx.Res != nil {
// Go on since the response is already set. // Go on since the response is already set.
return resultCodeSuccess return resultCodeSuccess
@@ -752,9 +725,6 @@ func ipStringFromAddr(addr net.Addr) (ipStr string) {
// processUpstream passes request to upstream servers and handles the response. // processUpstream passes request to upstream servers and handles the response.
func (s *Server) processUpstream(dctx *dnsContext) (rc resultCode) { func (s *Server) processUpstream(dctx *dnsContext) (rc resultCode) {
log.Debug("dnsforward: started processing upstream")
defer log.Debug("dnsforward: finished processing upstream")
pctx := dctx.proxyCtx pctx := dctx.proxyCtx
req := pctx.Req req := pctx.Req
q := req.Question[0] q := req.Question[0]
@@ -901,9 +871,6 @@ func (s *Server) setCustomUpstream(pctx *proxy.DNSContext, clientID string) {
// Apply filtering logic after we have received response from upstream servers // Apply filtering logic after we have received response from upstream servers
func (s *Server) processFilteringAfterResponse(dctx *dnsContext) (rc resultCode) { func (s *Server) processFilteringAfterResponse(dctx *dnsContext) (rc resultCode) {
log.Debug("dnsforward: started processing filtering after resp")
defer log.Debug("dnsforward: finished processing filtering after resp")
pctx := dctx.proxyCtx pctx := dctx.proxyCtx
switch res := dctx.result; res.Reason { switch res := dctx.result; res.Reason {
case filtering.NotFilteredAllowList: case filtering.NotFilteredAllowList:

View File

@@ -48,33 +48,12 @@ var webRegistered bool
// hostToIPTable is a convenient type alias for tables of host names to an IP // hostToIPTable is a convenient type alias for tables of host names to an IP
// address. // address.
//
// TODO(e.burkov): Use the [DHCP] interface instead.
type hostToIPTable = map[string]netip.Addr type hostToIPTable = map[string]netip.Addr
// ipToHostTable is a convenient type alias for tables of IP addresses to their // ipToHostTable is a convenient type alias for tables of IP addresses to their
// host names. For example, for use with PTR queries. // host names. For example, for use with PTR queries.
//
// TODO(e.burkov): Use the [DHCP] interface instead.
type ipToHostTable = map[netip.Addr]string type ipToHostTable = map[netip.Addr]string
// DHCP is an interface for accessing DHCP lease data needed in this package.
type DHCP interface {
// HostByIP returns the hostname of the DHCP client with the given IP
// address. The address will be netip.Addr{} if there is no such client,
// due to an assumption that a DHCP client must always have an IP address.
HostByIP(ip netip.Addr) (host string)
// IPByHost returns the IP address of the DHCP client with the given
// hostname. The hostname will be an empty string if there is no such
// client, due to an assumption that a DHCP client must always have a
// hostname, either set by the client or assigned automatically.
IPByHost(host string) (ip netip.Addr)
// Enabled returns true if DHCP provides information about clients.
Enabled() (ok bool)
}
// Server is the main way to start a DNS server. // Server is the main way to start a DNS server.
// //
// Example: // Example:
@@ -236,7 +215,7 @@ func (s *Server) Close() {
s.dnsProxy = nil s.dnsProxy = nil
if err := s.ipset.close(); err != nil { if err := s.ipset.close(); err != nil {
log.Error("dnsforward: closing ipset: %s", err) log.Error("closing ipset: %s", err)
} }
} }
@@ -464,17 +443,21 @@ func (s *Server) setupResolvers(localAddrs []string) (err error) {
return err return err
} }
log.Debug("dnsforward: upstreams to resolve ptr for local addresses: %v", localAddrs) log.Debug("upstreams to resolve PTR for local addresses: %v", localAddrs)
upsConfig, err := s.prepareUpstreamConfig(localAddrs, nil, &upstream.Options{ var upsConfig *proxy.UpstreamConfig
Bootstrap: bootstraps, upsConfig, err = proxy.ParseUpstreamsConfig(
Timeout: defaultLocalTimeout, localAddrs,
// TODO(e.burkov): Should we verify server's certificates? &upstream.Options{
Bootstrap: bootstraps,
Timeout: defaultLocalTimeout,
// TODO(e.burkov): Should we verify server's certificates?
PreferIPv6: s.conf.BootstrapPreferIPv6, PreferIPv6: s.conf.BootstrapPreferIPv6,
}) },
)
if err != nil { if err != nil {
return fmt.Errorf("parsing private upstreams: %w", err) return fmt.Errorf("parsing upstreams: %w", err)
} }
s.localResolvers = &proxy.Proxy{ s.localResolvers = &proxy.Proxy{
@@ -506,8 +489,7 @@ func (s *Server) Prepare(conf *ServerConfig) (err error) {
err = s.prepareUpstreamSettings() err = s.prepareUpstreamSettings()
if err != nil { if err != nil {
// Don't wrap the error, because it's informative enough as is. return fmt.Errorf("preparing upstream settings: %w", err)
return err
} }
var proxyConfig proxy.Config var proxyConfig proxy.Config
@@ -674,9 +656,7 @@ func (s *Server) Reconfigure(conf *ServerConfig) error {
s.serverLock.Lock() s.serverLock.Lock()
defer s.serverLock.Unlock() defer s.serverLock.Unlock()
log.Info("dnsforward: starting reconfiguring server") log.Print("Start reconfiguring the server")
defer log.Info("dnsforward: finished reconfiguring server")
err := s.stopLocked() err := s.stopLocked()
if err != nil { if err != nil {
return fmt.Errorf("could not reconfigure the server: %w", err) return fmt.Errorf("could not reconfigure the server: %w", err)
@@ -728,13 +708,13 @@ func (s *Server) IsBlockedClient(ip netip.Addr, clientID string) (blocked bool,
// Allow if at least one of the checks allows in allowlist mode, but block // Allow if at least one of the checks allows in allowlist mode, but block
// if at least one of the checks blocks in blocklist mode. // if at least one of the checks blocks in blocklist mode.
if allowlistMode && blockedByIP && blockedByClientID { if allowlistMode && blockedByIP && blockedByClientID {
log.Debug("dnsforward: client %v (id %q) is not in access allowlist", ip, clientID) log.Debug("client %v (id %q) is not in access allowlist", ip, clientID)
// Return now without substituting the empty rule for the // Return now without substituting the empty rule for the
// clientID because the rule can't be empty here. // clientID because the rule can't be empty here.
return true, rule return true, rule
} else if !allowlistMode && (blockedByIP || blockedByClientID) { } else if !allowlistMode && (blockedByIP || blockedByClientID) {
log.Debug("dnsforward: client %v (id %q) is in access blocklist", ip, clientID) log.Debug("client %v (id %q) is in access blocklist", ip, clientID)
blocked = true blocked = true
} }

View File

@@ -633,70 +633,61 @@ func (err domainSpecificTestError) Error() (msg string) {
return fmt.Sprintf("WARNING: %s", err.error) return fmt.Sprintf("WARNING: %s", err.error)
} }
// parseUpstreamLine parses line and creates the [upstream.Upstream] using opts // checkDNS checks the upstream server defined by upstreamConfigStr using
// and information from [s.dnsFilter.EtcHosts]. It returns an error if the line // healthCheck for actually exchange messages. It uses bootstrap to resolve the
// is not a valid upstream line, see [upstream.AddressToUpstream]. It's a // upstream's address.
// caller's responsibility to close u. func checkDNS(
func (s *Server) parseUpstreamLine( upstreamConfigStr string,
line string, bootstrap []string,
opts *upstream.Options, bootstrapPrefIPv6 bool,
) (u upstream.Upstream, specific bool, err error) { timeout time.Duration,
// Separate upstream from domains list. healthCheck healthCheckFunc,
upstreamAddr, domains, err := separateUpstream(line) ) (err error) {
if err != nil { if IsCommentOrEmpty(upstreamConfigStr) {
return nil, false, fmt.Errorf("wrong upstream format: %w", err) return nil
} }
specific = len(domains) > 0 // Separate upstream from domains list.
upstreamAddr, domains, err := separateUpstream(upstreamConfigStr)
if err != nil {
return fmt.Errorf("wrong upstream format: %w", err)
}
useDefault, err := validateUpstream(upstreamAddr, domains) useDefault, err := validateUpstream(upstreamAddr, domains)
if err != nil { if err != nil {
return nil, specific, fmt.Errorf("wrong upstream format: %w", err) return fmt.Errorf("wrong upstream format: %w", err)
} else if useDefault { } else if useDefault {
return nil, specific, nil return nil
}
if len(bootstrap) == 0 {
bootstrap = defaultBootstrap
} }
log.Debug("dnsforward: checking if upstream %q works", upstreamAddr) log.Debug("dnsforward: checking if upstream %q works", upstreamAddr)
opts = &upstream.Options{ u, err := upstream.AddressToUpstream(upstreamAddr, &upstream.Options{
Bootstrap: opts.Bootstrap, Bootstrap: bootstrap,
Timeout: opts.Timeout, Timeout: timeout,
PreferIPv6: opts.PreferIPv6, PreferIPv6: bootstrapPrefIPv6,
} })
if s.dnsFilter != nil && s.dnsFilter.EtcHosts != nil {
resolved := s.resolveUpstreamHost(extractUpstreamHost(upstreamAddr))
sortNetIPAddrs(resolved, opts.PreferIPv6)
opts.ServerIPAddrs = resolved
}
u, err = upstream.AddressToUpstream(upstreamAddr, opts)
if err != nil { if err != nil {
return nil, specific, fmt.Errorf("creating upstream for %q: %w", upstreamAddr, err) return fmt.Errorf("failed to choose upstream for %q: %w", upstreamAddr, err)
}
return u, specific, nil
}
func (s *Server) checkDNS(line string, opts *upstream.Options, check healthCheckFunc) (err error) {
if IsCommentOrEmpty(line) {
return nil
}
var u upstream.Upstream
var specific bool
defer func() {
if err != nil && specific {
err = domainSpecificTestError{error: err}
}
}()
u, specific, err = s.parseUpstreamLine(line, opts)
if err != nil || u == nil {
return err
} }
defer func() { err = errors.WithDeferred(err, u.Close()) }() defer func() { err = errors.WithDeferred(err, u.Close()) }()
return check(u) if err = healthCheck(u); err != nil {
err = fmt.Errorf("upstream %q fails to exchange: %w", upstreamAddr, err)
if domains != nil {
return domainSpecificTestError{error: err}
}
return err
}
log.Debug("dnsforward: upstream %q is ok", upstreamAddr)
return nil
} }
func (s *Server) handleTestUpstreamDNS(w http.ResponseWriter, r *http.Request) { func (s *Server) handleTestUpstreamDNS(w http.ResponseWriter, r *http.Request) {
@@ -708,54 +699,47 @@ func (s *Server) handleTestUpstreamDNS(w http.ResponseWriter, r *http.Request) {
return return
} }
opts := &upstream.Options{ result := map[string]string{}
Bootstrap: req.BootstrapDNS, bootstraps := req.BootstrapDNS
Timeout: s.conf.UpstreamTimeout, bootstrapPrefIPv6 := s.conf.BootstrapPreferIPv6
PreferIPv6: s.conf.BootstrapPreferIPv6, timeout := s.conf.UpstreamTimeout
}
if len(opts.Bootstrap) == 0 {
opts.Bootstrap = defaultBootstrap
}
type upsCheckResult = struct { type upsCheckResult = struct {
err error res string
host string host string
} }
req.Upstreams = stringutil.FilterOut(req.Upstreams, IsCommentOrEmpty)
req.PrivateUpstreams = stringutil.FilterOut(req.PrivateUpstreams, IsCommentOrEmpty)
upsNum := len(req.Upstreams) + len(req.PrivateUpstreams) upsNum := len(req.Upstreams) + len(req.PrivateUpstreams)
result := make(map[string]string, upsNum)
resCh := make(chan upsCheckResult, upsNum) resCh := make(chan upsCheckResult, upsNum)
checkUps := func(ups string, healthCheck healthCheckFunc) {
res := upsCheckResult{
host: ups,
}
defer func() { resCh <- res }()
checkErr := checkDNS(ups, bootstraps, bootstrapPrefIPv6, timeout, healthCheck)
if checkErr != nil {
res.res = checkErr.Error()
} else {
res.res = "OK"
}
}
for _, ups := range req.Upstreams { for _, ups := range req.Upstreams {
go func(ups string) { go checkUps(ups, checkDNSUpstreamExc)
resCh <- upsCheckResult{
host: ups,
err: s.checkDNS(ups, opts, checkDNSUpstreamExc),
}
}(ups)
} }
for _, ups := range req.PrivateUpstreams { for _, ups := range req.PrivateUpstreams {
go func(ups string) { go checkUps(ups, checkPrivateUpstreamExc)
resCh <- upsCheckResult{
host: ups,
err: s.checkDNS(ups, opts, checkPrivateUpstreamExc),
}
}(ups)
} }
for i := 0; i < upsNum; i++ { for i := 0; i < upsNum; i++ {
pair := <-resCh
// TODO(e.burkov): The upstreams used for both common and private // TODO(e.burkov): The upstreams used for both common and private
// resolving should be reported separately. // resolving should be reported separately.
pair := <-resCh result[pair.host] = pair.res
if pair.err != nil {
result[pair.host] = pair.err.Error()
} else {
result[pair.host] = "OK"
}
} }
close(resCh)
_ = aghhttp.WriteJSONResponse(w, r, result) _ = aghhttp.WriteJSONResponse(w, r, result)
} }

View File

@@ -13,12 +13,10 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
"testing" "testing"
"testing/fstest"
"time" "time"
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp" "github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
"github.com/AdguardTeam/AdGuardHome/internal/aghnet" "github.com/AdguardTeam/AdGuardHome/internal/aghnet"
"github.com/AdguardTeam/AdGuardHome/internal/aghtest"
"github.com/AdguardTeam/AdGuardHome/internal/filtering" "github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/golibs/httphdr" "github.com/AdguardTeam/golibs/httphdr"
"github.com/AdguardTeam/golibs/netutil" "github.com/AdguardTeam/golibs/netutil"
@@ -282,10 +280,6 @@ func TestIsCommentOrEmpty(t *testing.T) {
} }
func TestValidateUpstreams(t *testing.T) { func TestValidateUpstreams(t *testing.T) {
const sdnsStamp = `sdns://AQMAAAAAAAAAFDE3Ni4xMDMuMTMwLjEzMDo1NDQzINErR_J` +
`S3PLCu_iZEIbq95zkSV2LFsigxDIuUso_OQhzIjIuZG5zY3J5cHQuZGVmYXVsdC5uczE` +
`uYWRndWFyZC5jb20`
testCases := []struct { testCases := []struct {
name string name string
wantErr string wantErr string
@@ -306,7 +300,7 @@ func TestValidateUpstreams(t *testing.T) {
"[//]tls://1.1.1.1", "[//]tls://1.1.1.1",
"[/www.host.com/]#", "[/www.host.com/]#",
"[/host.com/google.com/]8.8.8.8", "[/host.com/google.com/]8.8.8.8",
"[/host/]" + sdnsStamp, "[/host/]sdns://AQMAAAAAAAAAFDE3Ni4xMDMuMTMwLjEzMDo1NDQzINErR_JS3PLCu_iZEIbq95zkSV2LFsigxDIuUso_OQhzIjIuZG5zY3J5cHQuZGVmYXVsdC5uczEuYWRndWFyZC5jb20",
}, },
}, { }, {
name: "with_default", name: "with_default",
@@ -316,7 +310,7 @@ func TestValidateUpstreams(t *testing.T) {
"[//]tls://1.1.1.1", "[//]tls://1.1.1.1",
"[/www.host.com/]#", "[/www.host.com/]#",
"[/host.com/google.com/]8.8.8.8", "[/host.com/google.com/]8.8.8.8",
"[/host/]" + sdnsStamp, "[/host/]sdns://AQMAAAAAAAAAFDE3Ni4xMDMuMTMwLjEzMDo1NDQzINErR_JS3PLCu_iZEIbq95zkSV2LFsigxDIuUso_OQhzIjIuZG5zY3J5cHQuZGVmYXVsdC5uczEuYWRndWFyZC5jb20",
"8.8.8.8", "8.8.8.8",
}, },
}, { }, {
@@ -332,10 +326,9 @@ func TestValidateUpstreams(t *testing.T) {
wantErr: `validating upstream "123.3.7m": not an ip:port`, wantErr: `validating upstream "123.3.7m": not an ip:port`,
set: []string{"123.3.7m"}, set: []string{"123.3.7m"},
}, { }, {
name: "invalid", name: "invalid",
wantErr: `bad upstream for domain "[/host.com]tls://dns.adguard.com": ` + wantErr: `bad upstream for domain "[/host.com]tls://dns.adguard.com": missing separator`,
`missing separator`, set: []string{"[/host.com]tls://dns.adguard.com"},
set: []string{"[/host.com]tls://dns.adguard.com"},
}, { }, {
name: "invalid", name: "invalid",
wantErr: `validating upstream "[host.ru]#": not an ip:port`, wantErr: `validating upstream "[host.ru]#": not an ip:port`,
@@ -347,14 +340,14 @@ func TestValidateUpstreams(t *testing.T) {
"1.1.1.1", "1.1.1.1",
"tls://1.1.1.1", "tls://1.1.1.1",
"https://dns.adguard.com/dns-query", "https://dns.adguard.com/dns-query",
sdnsStamp, "sdns://AQMAAAAAAAAAFDE3Ni4xMDMuMTMwLjEzMDo1NDQzINErR_JS3PLCu_iZEIbq95zkSV2LFsigxDIuUso_OQhzIjIuZG5zY3J5cHQuZGVmYXVsdC5uczEuYWRndWFyZC5jb20",
"udp://dns.google", "udp://dns.google",
"udp://8.8.8.8", "udp://8.8.8.8",
"[/host.com/]1.1.1.1", "[/host.com/]1.1.1.1",
"[//]tls://1.1.1.1", "[//]tls://1.1.1.1",
"[/www.host.com/]#", "[/www.host.com/]#",
"[/host.com/google.com/]8.8.8.8", "[/host.com/google.com/]8.8.8.8",
"[/host/]" + sdnsStamp, "[/host/]sdns://AQMAAAAAAAAAFDE3Ni4xMDMuMTMwLjEzMDo1NDQzINErR_JS3PLCu_iZEIbq95zkSV2LFsigxDIuUso_OQhzIjIuZG5zY3J5cHQuZGVmYXVsdC5uczEuYWRndWFyZC5jb20",
"[/пример.рф/]8.8.8.8", "[/пример.рф/]8.8.8.8",
}, },
}, { }, {
@@ -425,28 +418,27 @@ func TestValidateUpstreamsPrivate(t *testing.T) {
} }
} }
func newLocalUpstreamListener(t *testing.T, port uint16, handler dns.Handler) (real netip.AddrPort) { func newLocalUpstreamListener(t *testing.T, port int, handler dns.Handler) (real net.Addr) {
t.Helper()
startCh := make(chan struct{}) startCh := make(chan struct{})
upsSrv := &dns.Server{ upsSrv := &dns.Server{
Addr: netip.AddrPortFrom(netutil.IPv4Localhost(), port).String(), Addr: netip.AddrPortFrom(netutil.IPv4Localhost(), uint16(port)).String(),
Net: "tcp", Net: "tcp",
Handler: handler, Handler: handler,
NotifyStartedFunc: func() { close(startCh) }, NotifyStartedFunc: func() { close(startCh) },
} }
go func() { go func() {
err := upsSrv.ListenAndServe() t := testutil.PanicT{}
require.NoError(testutil.PanicT{}, err)
}()
err := upsSrv.ListenAndServe()
require.NoError(t, err)
}()
<-startCh <-startCh
testutil.CleanupAndRequireSuccess(t, upsSrv.Shutdown) testutil.CleanupAndRequireSuccess(t, upsSrv.Shutdown)
return testutil.RequireTypeAssert[*net.TCPAddr](t, upsSrv.Listener.Addr()).AddrPort() return upsSrv.Listener.Addr()
} }
func TestServer_HandleTestUpstreamDNS(t *testing.T) { func TestServer_handleTestUpstreaDNS(t *testing.T) {
goodHandler := dns.HandlerFunc(func(w dns.ResponseWriter, m *dns.Msg) { goodHandler := dns.HandlerFunc(func(w dns.ResponseWriter, m *dns.Msg) {
err := w.WriteMsg(new(dns.Msg).SetReply(m)) err := w.WriteMsg(new(dns.Msg).SetReply(m))
require.NoError(testutil.PanicT{}, err) require.NoError(testutil.PanicT{}, err)
@@ -465,38 +457,9 @@ func TestServer_HandleTestUpstreamDNS(t *testing.T) {
Host: newLocalUpstreamListener(t, 0, badHandler).String(), Host: newLocalUpstreamListener(t, 0, badHandler).String(),
}).String() }).String()
const ( const upsTimeout = 100 * time.Millisecond
upsTimeout = 100 * time.Millisecond
hostsFileName = "hosts" srv := createTestServer(t, &filtering.Config{}, ServerConfig{
upstreamHost = "custom.localhost"
)
hostsListener := newLocalUpstreamListener(t, 0, goodHandler)
hostsUps := (&url.URL{
Scheme: "tcp",
Host: netutil.JoinHostPort(upstreamHost, int(hostsListener.Port())),
}).String()
hc, err := aghnet.NewHostsContainer(
filtering.SysHostsListID,
fstest.MapFS{
hostsFileName: &fstest.MapFile{
Data: []byte(hostsListener.Addr().String() + " " + upstreamHost),
},
},
&aghtest.FSWatcher{
OnEvents: func() (e <-chan struct{}) { return nil },
OnAdd: func(_ string) (err error) { return nil },
OnClose: func() (err error) { return nil },
},
hostsFileName,
)
require.NoError(t, err)
srv := createTestServer(t, &filtering.Config{
EtcHosts: hc,
}, ServerConfig{
UDPListenAddrs: []*net.UDPAddr{{}}, UDPListenAddrs: []*net.UDPAddr{{}},
TCPListenAddrs: []*net.TCPAddr{{}}, TCPListenAddrs: []*net.TCPAddr{{}},
UpstreamTimeout: upsTimeout, UpstreamTimeout: upsTimeout,
@@ -523,7 +486,8 @@ func TestServer_HandleTestUpstreamDNS(t *testing.T) {
"upstream_dns": []string{badUps}, "upstream_dns": []string{badUps},
}, },
wantResp: map[string]any{ wantResp: map[string]any{
badUps: `couldn't communicate with upstream: exchanging with ` + badUps: `upstream "` + badUps + `" fails to exchange: ` +
`couldn't communicate with upstream: exchanging with ` +
badUps + ` over tcp: dns: id mismatch`, badUps + ` over tcp: dns: id mismatch`,
}, },
name: "broken", name: "broken",
@@ -533,40 +497,20 @@ func TestServer_HandleTestUpstreamDNS(t *testing.T) {
}, },
wantResp: map[string]any{ wantResp: map[string]any{
goodUps: "OK", goodUps: "OK",
badUps: `couldn't communicate with upstream: exchanging with ` + badUps: `upstream "` + badUps + `" fails to exchange: ` +
`couldn't communicate with upstream: exchanging with ` +
badUps + ` over tcp: dns: id mismatch`, badUps + ` over tcp: dns: id mismatch`,
}, },
name: "both", name: "both",
}, {
body: map[string]any{
"upstream_dns": []string{"[/domain.example/]" + badUps},
},
wantResp: map[string]any{
"[/domain.example/]" + badUps: `WARNING: couldn't communicate ` +
`with upstream: exchanging with ` + badUps + ` over tcp: ` +
`dns: id mismatch`,
},
name: "domain_specific_error",
}, {
body: map[string]any{
"upstream_dns": []string{hostsUps},
},
wantResp: map[string]any{
hostsUps: "OK",
},
name: "etc_hosts",
}} }}
for _, tc := range testCases { for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
var reqBody []byte reqBody, err := json.Marshal(tc.body)
reqBody, err = json.Marshal(tc.body)
require.NoError(t, err) require.NoError(t, err)
w := httptest.NewRecorder() w := httptest.NewRecorder()
r, err := http.NewRequest(http.MethodPost, "", bytes.NewReader(reqBody))
var r *http.Request
r, err = http.NewRequest(http.MethodPost, "", bytes.NewReader(reqBody))
require.NoError(t, err) require.NoError(t, err)
srv.handleTestUpstreamDNS(w, r) srv.handleTestUpstreamDNS(w, r)
@@ -594,15 +538,11 @@ func TestServer_HandleTestUpstreamDNS(t *testing.T) {
req := map[string]any{ req := map[string]any{
"upstream_dns": []string{sleepyUps}, "upstream_dns": []string{sleepyUps},
} }
reqBody, err := json.Marshal(req)
var reqBody []byte
reqBody, err = json.Marshal(req)
require.NoError(t, err) require.NoError(t, err)
w := httptest.NewRecorder() w := httptest.NewRecorder()
r, err := http.NewRequest(http.MethodPost, "", bytes.NewReader(reqBody))
var r *http.Request
r, err = http.NewRequest(http.MethodPost, "", bytes.NewReader(reqBody))
require.NoError(t, err) require.NoError(t, err)
srv.handleTestUpstreamDNS(w, r) srv.handleTestUpstreamDNS(w, r)

View File

@@ -110,9 +110,6 @@ func ipsFromAnswer(ans []dns.RR) (ip4s, ip6s []net.IP) {
// process adds the resolved IP addresses to the domain's ipsets, if any. // process adds the resolved IP addresses to the domain's ipsets, if any.
func (c *ipsetCtx) process(dctx *dnsContext) (rc resultCode) { func (c *ipsetCtx) process(dctx *dnsContext) (rc resultCode) {
log.Debug("dnsforward: ipset: started processing")
defer log.Debug("dnsforward: ipset: finished processing")
if c.skipIpsetProcessing(dctx) { if c.skipIpsetProcessing(dctx) {
return resultCodeSuccess return resultCodeSuccess
} }
@@ -128,12 +125,12 @@ func (c *ipsetCtx) process(dctx *dnsContext) (rc resultCode) {
n, err := c.ipsetMgr.Add(host, ip4s, ip6s) n, err := c.ipsetMgr.Add(host, ip4s, ip6s)
if err != nil { if err != nil {
// Consider ipset errors non-critical to the request. // Consider ipset errors non-critical to the request.
log.Error("dnsforward: ipset: adding host ips: %s", err) log.Error("ipset: adding host ips: %s", err)
return resultCodeSuccess return resultCodeSuccess
} }
log.Debug("dnsforward: ipset: added %d new ipset entries", n) log.Debug("ipset: added %d new ipset entries", n)
return resultCodeSuccess return resultCodeSuccess
} }

View File

@@ -17,78 +17,60 @@ import (
// Write Stats data and logs // Write Stats data and logs
func (s *Server) processQueryLogsAndStats(dctx *dnsContext) (rc resultCode) { func (s *Server) processQueryLogsAndStats(dctx *dnsContext) (rc resultCode) {
log.Debug("dnsforward: started processing querylog and stats")
defer log.Debug("dnsforward: finished processing querylog and stats")
elapsed := time.Since(dctx.startTime) elapsed := time.Since(dctx.startTime)
pctx := dctx.proxyCtx pctx := dctx.proxyCtx
q := pctx.Req.Question[0] shouldLog := true
msg := pctx.Req
q := msg.Question[0]
host := strings.ToLower(strings.TrimSuffix(q.Name, ".")) host := strings.ToLower(strings.TrimSuffix(q.Name, "."))
// don't log ANY request if refuseAny is enabled
if q.Qtype == dns.TypeANY && s.conf.RefuseAny {
shouldLog = false
}
ip, _ := netutil.IPAndPortFromAddr(pctx.Addr) ip, _ := netutil.IPAndPortFromAddr(pctx.Addr)
ip = slices.Clone(ip) ip = slices.Clone(ip)
s.serverLock.RLock()
defer s.serverLock.RUnlock()
s.anonymizer.Load()(ip) s.anonymizer.Load()(ip)
log.Debug("dnsforward: client ip for stats and querylog: %s", ip) log.Debug("client ip: %s", ip)
ipStr := ip.String() ipStr := ip.String()
ids := []string{ipStr, dctx.clientID} ids := []string{ipStr, dctx.clientID}
qt, cl := q.Qtype, q.Qclass
// Synchronize access to s.queryLog and s.stats so they won't be suddenly // Synchronize access to s.queryLog and s.stats so they won't be suddenly
// uninitialized while in use. This can happen after proxy server has been // uninitialized while in use. This can happen after proxy server has been
// stopped, but its workers haven't yet exited. // stopped, but its workers haven't yet exited.
s.serverLock.RLock() if shouldLog &&
defer s.serverLock.RUnlock() s.queryLog != nil &&
// TODO(s.chzhen): Use dnsforward.dnsContext when it will start
if s.shouldLog(host, qt, cl, ids) { // containing persistent client.
s.queryLog.ShouldLog(host, q.Qtype, q.Qclass, ids) {
s.logQuery(dctx, pctx, elapsed, ip) s.logQuery(dctx, pctx, elapsed, ip)
} else { } else {
log.Debug( log.Debug(
"dnsforward: request %s %s %q from %s ignored; not adding to querylog", "dnsforward: request %s %s from %s ignored; not logging",
dns.Class(cl), dns.Type(q.Qtype),
dns.Type(qt),
host, host,
ip, ip,
) )
} }
if s.shouldCountStat(host, qt, cl, ids) { if s.stats != nil &&
// TODO(s.chzhen): Use dnsforward.dnsContext when it will start
// containing persistent client.
s.stats.ShouldCount(host, q.Qtype, q.Qclass, ids) {
s.updateStats(dctx, elapsed, *dctx.result, ipStr) s.updateStats(dctx, elapsed, *dctx.result, ipStr)
} else {
log.Debug(
"dnsforward: request %s %s %q from %s ignored; not counting in stats",
dns.Class(cl),
dns.Type(qt),
host,
ip,
)
} }
return resultCodeSuccess return resultCodeSuccess
} }
// shouldLog returns true if the query with the given data should be logged in
// the query log. s.serverLock is expected to be locked.
func (s *Server) shouldLog(host string, qt, cl uint16, ids []string) (ok bool) {
if qt == dns.TypeANY && s.conf.RefuseAny {
return false
}
// TODO(s.chzhen): Use dnsforward.dnsContext when it will start containing
// persistent client.
return s.queryLog != nil && s.queryLog.ShouldLog(host, qt, cl, ids)
}
// shouldCountStat returns true if the query with the given data should be
// counted in the statistics. s.serverLock is expected to be locked.
func (s *Server) shouldCountStat(host string, qt, cl uint16, ids []string) (ok bool) {
// TODO(s.chzhen): Use dnsforward.dnsContext when it will start containing
// persistent client.
return s.stats != nil && s.stats.ShouldCount(host, qt, cl, ids)
}
// logQuery pushes the request details into the query log. // logQuery pushes the request details into the query log.
func (s *Server) logQuery( func (s *Server) logQuery(
dctx *dnsContext, dctx *dnsContext,

View File

@@ -1,311 +0,0 @@
package dnsforward
import (
"bytes"
"fmt"
"net"
"net/url"
"os"
"strings"
"time"
"github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/dnsproxy/upstream"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/stringutil"
"github.com/AdguardTeam/urlfilter"
"github.com/miekg/dns"
"golang.org/x/exp/maps"
"golang.org/x/exp/slices"
)
// loadUpstreams parses upstream DNS servers from the configured file or from
// the configuration itself.
func (s *Server) loadUpstreams() (upstreams []string, err error) {
if s.conf.UpstreamDNSFileName == "" {
return stringutil.FilterOut(s.conf.UpstreamDNS, IsCommentOrEmpty), nil
}
var data []byte
data, err = os.ReadFile(s.conf.UpstreamDNSFileName)
if err != nil {
return nil, fmt.Errorf("reading upstream from file: %w", err)
}
upstreams = stringutil.SplitTrimmed(string(data), "\n")
log.Debug("dnsforward: got %d upstreams in %q", len(upstreams), s.conf.UpstreamDNSFileName)
return stringutil.FilterOut(upstreams, IsCommentOrEmpty), nil
}
// prepareUpstreamSettings sets upstream DNS server settings.
func (s *Server) prepareUpstreamSettings() (err error) {
// We're setting a customized set of RootCAs. The reason is that Go default
// mechanism of loading TLS roots does not always work properly on some
// routers so we're loading roots manually and pass it here.
//
// See [aghtls.SystemRootCAs].
upstream.RootCAs = s.conf.TLSv12Roots
upstream.CipherSuites = s.conf.TLSCiphers
// Load upstreams either from the file, or from the settings
var upstreams []string
upstreams, err = s.loadUpstreams()
if err != nil {
return fmt.Errorf("loading upstreams: %w", err)
}
s.conf.UpstreamConfig, err = s.prepareUpstreamConfig(upstreams, defaultDNS, &upstream.Options{
Bootstrap: s.conf.BootstrapDNS,
Timeout: s.conf.UpstreamTimeout,
HTTPVersions: UpstreamHTTPVersions(s.conf.UseHTTP3Upstreams),
PreferIPv6: s.conf.BootstrapPreferIPv6,
})
if err != nil {
return fmt.Errorf("preparing upstream config: %w", err)
}
return nil
}
// prepareUpstreamConfig sets upstream configuration based on upstreams and
// configuration of s.
func (s *Server) prepareUpstreamConfig(
upstreams []string,
defaultUpstreams []string,
opts *upstream.Options,
) (uc *proxy.UpstreamConfig, err error) {
uc, err = proxy.ParseUpstreamsConfig(upstreams, opts)
if err != nil {
return nil, fmt.Errorf("parsing upstream config: %w", err)
}
if len(uc.Upstreams) == 0 && defaultUpstreams != nil {
log.Info("dnsforward: warning: no default upstreams specified, using %v", defaultUpstreams)
var defaultUpstreamConfig *proxy.UpstreamConfig
defaultUpstreamConfig, err = proxy.ParseUpstreamsConfig(defaultUpstreams, opts)
if err != nil {
return nil, fmt.Errorf("parsing default upstreams: %w", err)
}
uc.Upstreams = defaultUpstreamConfig.Upstreams
}
if s.dnsFilter != nil && s.dnsFilter.EtcHosts != nil {
err = s.replaceUpstreamsWithHosts(uc, opts)
if err != nil {
return nil, fmt.Errorf("resolving upstreams with hosts: %w", err)
}
}
return uc, nil
}
// replaceUpstreamsWithHosts replaces unique upstreams with their resolved
// versions based on the system hosts file.
//
// TODO(e.burkov): This should be performed inside dnsproxy, which should
// actually consider /etc/hosts. See TODO on [aghnet.HostsContainer].
func (s *Server) replaceUpstreamsWithHosts(
upsConf *proxy.UpstreamConfig,
opts *upstream.Options,
) (err error) {
resolved := map[string]*upstream.Options{}
err = s.resolveUpstreamsWithHosts(resolved, upsConf.Upstreams, opts)
if err != nil {
return fmt.Errorf("resolving upstreams: %w", err)
}
hosts := maps.Keys(upsConf.DomainReservedUpstreams)
// TODO(e.burkov): Think of extracting sorted range into an util function.
slices.Sort(hosts)
for _, host := range hosts {
err = s.resolveUpstreamsWithHosts(resolved, upsConf.DomainReservedUpstreams[host], opts)
if err != nil {
return fmt.Errorf("resolving upstreams reserved for %s: %w", host, err)
}
}
hosts = maps.Keys(upsConf.SpecifiedDomainUpstreams)
slices.Sort(hosts)
for _, host := range hosts {
err = s.resolveUpstreamsWithHosts(resolved, upsConf.SpecifiedDomainUpstreams[host], opts)
if err != nil {
return fmt.Errorf("resolving upstreams specific for %s: %w", host, err)
}
}
return nil
}
// resolveUpstreamsWithHosts resolves the IP addresses of each of the upstreams
// and replaces those both in upstreams and resolved. Upstreams that failed to
// resolve are placed to resolved as-is. This function only returns error of
// upstreams closing.
func (s *Server) resolveUpstreamsWithHosts(
resolved map[string]*upstream.Options,
upstreams []upstream.Upstream,
opts *upstream.Options,
) (err error) {
for i := range upstreams {
u := upstreams[i]
addr := u.Address()
host := extractUpstreamHost(addr)
withIPs, ok := resolved[host]
if !ok {
ips := s.resolveUpstreamHost(host)
if len(ips) == 0 {
resolved[host] = nil
return nil
}
sortNetIPAddrs(ips, opts.PreferIPv6)
withIPs = opts.Clone()
withIPs.ServerIPAddrs = ips
resolved[host] = withIPs
} else if withIPs == nil {
continue
}
if err = u.Close(); err != nil {
return fmt.Errorf("closing upstream %s: %w", addr, err)
}
upstreams[i], err = upstream.AddressToUpstream(addr, withIPs)
if err != nil {
return fmt.Errorf("replacing upstream %s with resolved %s: %w", addr, host, err)
}
log.Debug("dnsforward: using %s for %s", withIPs.ServerIPAddrs, upstreams[i].Address())
}
return nil
}
// extractUpstreamHost returns the hostname of addr without port with an
// assumption that any address passed here has already been successfully parsed
// by [upstream.AddressToUpstream]. This function eesentially mirrors the logic
// of [upstream.AddressToUpstream], see TODO on [replaceUpstreamsWithHosts].
func extractUpstreamHost(addr string) (host string) {
var err error
if strings.Contains(addr, "://") {
var u *url.URL
u, err = url.Parse(addr)
if err != nil {
log.Debug("dnsforward: parsing upstream %s: %s", addr, err)
return addr
}
return u.Hostname()
}
// Probably, plain UDP upstream defined by address or address:port.
host, err = netutil.SplitHost(addr)
if err != nil {
return addr
}
return host
}
// resolveUpstreamHost returns the version of ups with IP addresses from the
// system hosts file placed into its options.
func (s *Server) resolveUpstreamHost(host string) (addrs []net.IP) {
req := &urlfilter.DNSRequest{
Hostname: host,
DNSType: dns.TypeA,
}
aRes, _ := s.dnsFilter.EtcHosts.MatchRequest(req)
req.DNSType = dns.TypeAAAA
aaaaRes, _ := s.dnsFilter.EtcHosts.MatchRequest(req)
var ips []net.IP
for _, rw := range append(aRes.DNSRewrites(), aaaaRes.DNSRewrites()...) {
dr := rw.DNSRewrite
if dr == nil || dr.Value == nil {
continue
}
if ip, ok := dr.Value.(net.IP); ok {
ips = append(ips, ip)
}
}
return ips
}
// sortNetIPAddrs sorts addrs in accordance with the protocol preferences.
// Invalid addresses are sorted near the end.
//
// TODO(e.burkov): This function taken from dnsproxy, which also already
// contains a few similar functions. Think of moving to golibs.
func sortNetIPAddrs(addrs []net.IP, preferIPv6 bool) {
l := len(addrs)
if l <= 1 {
return
}
slices.SortStableFunc(addrs, func(addrA, addrB net.IP) (sortsBefore bool) {
switch len(addrA) {
case net.IPv4len, net.IPv6len:
switch len(addrB) {
case net.IPv4len, net.IPv6len:
// Go on.
default:
return true
}
default:
return false
}
if aIs4, bIs4 := addrA.To4() != nil, addrB.To4() != nil; aIs4 != bIs4 {
if aIs4 {
return !preferIPv6
}
return preferIPv6
}
return bytes.Compare(addrA, addrB) < 0
})
}
// UpstreamHTTPVersions returns the HTTP versions for upstream configuration
// depending on configuration.
func UpstreamHTTPVersions(http3 bool) (v []upstream.HTTPVersion) {
if !http3 {
return upstream.DefaultHTTPVersions
}
return []upstream.HTTPVersion{
upstream.HTTPVersion3,
upstream.HTTPVersion2,
upstream.HTTPVersion11,
}
}
// setProxyUpstreamMode sets the upstream mode and related settings in conf
// based on provided parameters.
func setProxyUpstreamMode(
conf *proxy.Config,
allServers bool,
fastestAddr bool,
fastestTimeout time.Duration,
) {
if allServers {
conf.UpstreamMode = proxy.UModeParallel
} else if fastestAddr {
conf.UpstreamMode = proxy.UModeFastestAddr
conf.FastestPingTimeout = fastestTimeout
} else {
conf.UpstreamMode = proxy.UModeLoadBalance
}
}

View File

@@ -2,7 +2,6 @@ package filtering
import ( import (
"encoding/json" "encoding/json"
"fmt"
"net/http" "net/http"
"time" "time"
@@ -56,29 +55,11 @@ type BlockedServices struct {
IDs []string `yaml:"ids"` IDs []string `yaml:"ids"`
} }
// Clone returns a deep copy of blocked services. // BlockedSvcKnown returns true if a blocked service ID is known.
func (s *BlockedServices) Clone() (c *BlockedServices) { func BlockedSvcKnown(s string) (ok bool) {
if s == nil { _, ok = serviceRules[s]
return nil
}
return &BlockedServices{ return ok
Schedule: s.Schedule.Clone(),
IDs: slices.Clone(s.IDs),
}
}
// Validate returns an error if blocked services contain unknown service ID. s
// must not be nil.
func (s *BlockedServices) Validate() (err error) {
for _, id := range s.IDs {
_, ok := serviceRules[id]
if !ok {
return fmt.Errorf("unknown blocked-service %q", id)
}
}
return nil
} }
// ApplyBlockedServices - set blocked services settings for this DNS request // ApplyBlockedServices - set blocked services settings for this DNS request

View File

@@ -519,7 +519,7 @@ func (d *DNSFilter) matchSysHosts(
dnsres, _ := d.EtcHosts.MatchRequest(&urlfilter.DNSRequest{ dnsres, _ := d.EtcHosts.MatchRequest(&urlfilter.DNSRequest{
Hostname: host, Hostname: host,
SortedClientTags: setts.ClientTags, SortedClientTags: setts.ClientTags,
// TODO(e.burkov): Wait for urlfilter update to pass netip.Addr. // TODO(e.burkov): Wait for urlfilter update to pass net.IP.
ClientIP: setts.ClientIP.String(), ClientIP: setts.ClientIP.String(),
ClientName: setts.ClientName, ClientName: setts.ClientName,
DNSType: qtype, DNSType: qtype,
@@ -988,11 +988,17 @@ func New(c *Config, blockFilters []Filter) (d *DNSFilter, err error) {
} }
if d.BlockedServices != nil { if d.BlockedServices != nil {
err = d.BlockedServices.Validate() bsvcs := []string{}
for _, s := range d.BlockedServices.IDs {
if !BlockedSvcKnown(s) {
log.Debug("skipping unknown blocked-service %q", s)
if err != nil { continue
return nil, fmt.Errorf("filtering: %w", err) }
bsvcs = append(bsvcs, s)
} }
d.BlockedServices.IDs = bsvcs
} }
if blockFilters != nil { if blockFilters != nil {

View File

@@ -169,7 +169,7 @@ func (d *DNSFilter) handleFilteringRemoveURL(w http.ResponseWriter, r *http.Requ
deleted = (*filters)[delIdx] deleted = (*filters)[delIdx]
p := deleted.Path(d.DataDir) p := deleted.Path(d.DataDir)
err = os.Rename(p, p+".old") err = os.Rename(p, p+".old")
if err != nil && !errors.Is(err, os.ErrNotExist) { if err != nil {
log.Error("deleting filter %d: renaming file %q: %s", deleted.ID, p, err) log.Error("deleting filter %d: renaming file %q: %s", deleted.ID, p, err)
return return

View File

@@ -27,25 +27,6 @@ var blockedServices = []blockedService{{
"||9cache.com^", "||9cache.com^",
"||9gag.com^", "||9gag.com^",
}, },
}, {
ID: "activision_blizzard",
Name: "Activision Blizzard",
IconSVG: []byte("<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"-237 0 1572 1572\"><path d=\"m549.1.2 548.4 1571.4H798l-74.2-200H374.5l-74.3 200H.7zM626 1085.1l-83-274.3-82.9 274.3z\"/></svg>"),
Rules: []string{
"||activision.com^",
"||activisionblizzard.com^",
"||demonware.net^",
},
}, {
ID: "aliexpress",
Name: "AliExpress",
IconSVG: []byte("<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" viewBox=\"0 0 50 50\"><path d=\"M9 4C6.25 4 4 6.25 4 9v32c0 2.75 2.25 5 5 5h32c2.75 0 5-2.25 5-5V9c0-2.75-2.25-5-5-5H9zm0 2h32c1.668 0 3 1.332 3 3v3.38A3.973 3.973 0 0 0 41 11H9a3.973 3.973 0 0 0-3 1.38V9c0-1.668 1.332-3 3-3zm6 11a1 1 0 0 1 1 1c0 4.962 4.037 9 9 9s9-4.038 9-9a1 1 0 1 1 2 0c0 6.065-4.935 11-11 11s-11-4.935-11-11a1 1 0 0 1 1-1z\"/></svg>"),
Rules: []string{
"||ae-rus.net^",
"||ae-rus.ru^",
"||aliexpress.com^",
"||aliexpress.ru^",
},
}, { }, {
ID: "amazon", ID: "amazon",
Name: "Amazon", Name: "Amazon",
@@ -253,16 +234,6 @@ var blockedServices = []blockedService{{
"||z.cn^", "||z.cn^",
"||zappos^", "||zappos^",
}, },
}, {
ID: "battle_net",
Name: "Battle.net",
IconSVG: []byte("<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" viewBox=\"0 0 50 50\"><path d=\"M43.11 22.15s3.95.2 3.95-2.12c0-3.03-5.26-5.77-5.26-5.77s.83-1.74 1.34-2.72a37.3 37.3 0 0 0 2.09-5.65c.16-1.1-.09-1.44-.09-1.44-.35 2.34-4.17 9.09-4.47 9.32-3.72-1.75-8.83-2.23-8.83-2.23S26.84 1 22.13 1c-4.67 0-4.65 9.02-4.65 9.02s-1.32-2.56-2.97-2.56c-2.42 0-3.22 3.67-3.22 7.64a37.8 37.8 0 0 0-9.16 1.17c-.36.1-1.49.92-.97.82 1.04-.34 5.95-1.1 10.25-.72.24 3.77 2.44 8.68 2.44 8.68S9.13 31.9 9.13 36.78c0 1.29.56 3.64 3.95 3.64 2.84 0 6.03-1.7 6.63-2.06a6.33 6.33 0 0 0-.91 2.83c0 .54.31 2.06 2.5 2.06 2.82 0 5.96-2.16 5.96-2.16s2.96 4.93 5.5 7.2c.69.6 1.34.71 1.34.71s-2.52-2.43-5.84-8.68c3.08-1.9 6.3-6.4 6.3-6.4l3.3.01c4.6 0 11.11-.96 11.11-4.61 0-3.77-5.86-7.17-5.86-7.17Zm.52-2.26c0 1.33-1.27 1.3-1.27 1.3l-.97.08s-1.82-.97-2.93-1.41c0 0 1.72-2.65 2.12-3.4.3.18 3.05 1.9 3.05 3.43ZM24.43 6.3c2.15 0 5.23 5.1 5.23 5.1s-4.8-.44-8.76 1.89c.1-3.67 1.34-7 3.52-7Zm-8.56 4.13c.69 0 1.36.83 1.64 1.54 0 .47.24 3.2.24 3.2l-3.96-.16c0-3.57 1.4-4.58 2.08-4.58Zm-.4 24.8c-2.17 0-2.62-1.2-2.62-2.29 0-2.45 1.96-5.9 1.96-5.9s2.2 4.63 6.04 6.59a10.02 10.02 0 0 1-5.39 1.6Zm7.02 4.85c-1.52 0-1.7-.98-1.7-1.21 0-.7.55-1.54.55-1.54s2.55-1.73 2.71-1.91l1.89 3.52s-1.93 1.14-3.45 1.14Zm4.74-1.92c-.93-1.62-1.6-3.3-1.6-3.3s3.78.24 5.82-1.86a11.2 11.2 0 0 1-5.65 1.07c4.93-4.34 7.8-7.48 10.23-10.74a9.46 9.46 0 0 0-1.6-1.15c-1.46 1.76-7.16 7.86-12.45 10.88-6.69-3.64-8.09-14.38-8.23-16.6l3.65.34s-1.37 2.44-1.37 4.23c0 1.79.21 1.89.21 1.89s-.04-3.13 1.89-5.54c1.46 7.82 3 11.83 4.19 14.22.6-.25 1.74-.76 1.74-.76s-3.38-9.73-3.19-16.31a13.8 13.8 0 0 1 6.36-1.66c6.73 0 12.14 2.9 12.14 2.9l-2.12 2.95s-1.89-3.42-4.55-4.03c1.4 1.05 2.98 2.44 3.8 4.43a68.4 68.4 0 0 0-14.47-3.59c-.19.8-.17 1.94-.17 1.94s9.03 1.66 15.6 5.43c-.05 8.21-9 14.53-10.23 15.26Zm8.55-6.14s2.8-3.68 2.76-8.55c0 0 4.52 2.8 4.52 5.54 0 3.05-7.28 3-7.28 3Z\"/></svg>"),
Rules: []string{
"||battle.net^",
"||battlenet.com.cn^",
"||bnet.163.com^",
"||bnet.cn^",
},
}, { }, {
ID: "bilibili", ID: "bilibili",
Name: "Bilibili", Name: "Bilibili",
@@ -312,21 +283,6 @@ var blockedServices = []blockedService{{
"||mincdn.com^", "||mincdn.com^",
"||yo9.com^", "||yo9.com^",
}, },
}, {
ID: "blizzard_entertainment",
Name: "Blizzard Entertainment",
IconSVG: []byte("<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" viewBox=\"0 -32 128 128\"><path fill-rule=\"evenodd\" d=\"M105 2h3v1h2l2 1 1 1h3l1 1h4l1 1 2 2v1l1 3v4l1 2v6l-1 2v2l-1 3v2l-1 2v14l-1 2v1l-1 3-1 1h-3l-1 1h-6a5 5 0 0 0 1-6l2-1h-1l-1-3v-3a350 350 0 0 1 0-8l-1-3v-1l-1-1V9h1V6l-1-1-4-3Zm9 13v10h1v25a8 8 0 0 0 2-4l1-1 1-3V30l1-1v-2l1-1v-5l-1-2-2-3-1-1h-3Z\" clip-rule=\"evenodd\"/><path fill-rule=\"evenodd\" d=\"M101 24v1l2 1h1v2h1l1 2v5l1 2s0-1 0 0l1 7 1 2v7l-1 5h-2l-2-2-4-1 1-3 1-2a22 22 0 0 0-1-10l-1-4h-1l1-4-1-1-2-3v2l-1 1v3l1 6v4l1 1-1 3v4l1 1-1 2v4l-1-1a13 13 0 0 0-4-5l-2-2 2-5V27l-1-1v-4l-1-1v-5h-1a33 33 0 0 1 0-4l4-4h-2l-4-4h-1V3h10l2 1 2 1h1c2 0 2 1 3 2l2 3 1 3v1l-1 2v1a11 11 0 0 1-1 4l-4 3ZM96 9v13l1 1a3 3 0 0 0 1-1c1 0 2-1 2-3v-1l1-1v-3l-2-3-2-2h-1ZM26 3l1 1h1l2 3v5l1 1v2l-1 1v9l1 1 1 1-1 7v9l-1 1 1 1-1 1v8h3l1-1h7v-1h16v6l1 2h-6l-1-1h-2l-1-1H31a4 4 0 0 0-3-1l-1 1h-1l-1 1h-5l1-1a10 10 0 0 0 3-2v-9l1-1-1-1V35l1-1V21l-1-1v-4l1-1v-3l1-2-1-3h-1l-2-2-1-1 1-1h4Z\" clip-rule=\"evenodd\"/><path fill-rule=\"evenodd\" d=\"M84 60v-3l-1-2v-4l-3-2v-1l1-2a11 11 0 0 0 2-6l-1-1-3-2h-2v3l1 1h1l-1 2h-4l-2 1-2 1-1-2v-1l1-1 1-1 1-2v-5l1-1v-6l1-1v-3l1-1v-3l1-2 1-1-1-1 1-1 1-3 1-1V7l1-1c1-1 0-4 2-3l1 3 1 1 1 2v1l1 5 1 3v2l1 1v2l1 1v8l1 3v9l-1 1-2 5v3l-1 2v4l-1 1h-1Zm-4-36-1 1v2l-1 2v4l4 1h2v-7l-1-3-2-1-1-1v2Z\" clip-rule=\"evenodd\"/><path fill-rule=\"evenodd\" d=\"M77 4v1l-2 3v2l-1 2v1l-1 1-1 4v7h-1v2a5 5 0 0 1-1 2v2l-2 2v7l-1 2v2l-2 4v3-1h3v-1l3-1 1-1 3-2h3l1 1-1 1a3 3 0 0 0 0 1l1 1v5l-1 1h-7v-1h-2l-2 1h-4l-2 1-1-2v-2l1-1v-1l1-1-1-1 1-1v-2l1-2-1-2v-8l1-2 2-5-1-1 1-2v-1l1-1v-4l1-1 2-4v-2l1-1h-3V8h-1l-1 1-2 3-1 4h-1l-1-1v-2l1-1V4h16ZM32 4h9l1 2-3 2 1 2-1 1v13l1 2-1 2v6l-1 1v2l1 1v5l-1 1 1 2 1 1 2 1v2h-7l-2 1-1-1 3-2v-8a4 4 0 0 1 0-2l1-1v-3l-1-14v-2l1-1h-1V7l-2-1h-1l-1-1 1-1Zm12 0h14v15c-2 1-2 4-3 6v2c-1 0-3 1-2 4h-1l-2 3-1 2v3l-1 2-2 5h2l1-1h2l1-1c1-1 1-3 3-3l1-2 2-2h1l1 3h-1v1l-1 1v7h-8l-1 1-2-1h-3l-1-1 1-1v-3l-1-2 1-1-1-1 1-3v-2l1-2 1-3a7 7 0 0 1 2-4l1-4 2-2 2-3v-3h1l2-3V8l-3-1h-2l-1 1a3 3 0 0 0-2 3l-1 1v4l-1 1-1 1v-1l-1-1V4ZM17 22l1 1h1v3s0-1 0 0l2 1v5l1 2-1 8v3a6 6 0 0 1 0 2l-1 2-1 2-1 3-3 2-2 2-3 1-1-1-1 1H1l-1-1 2-1 1-4V26l1-1-1-3V11l1-1-1-1H2V8L1 7 0 6V5l1-1h15l1 1c2 0 3 1 3 2l1 3v6l-4 6Zm-6-11v9h1l1-2 2-1v-6h-1l-1-1h-2v1Zm0 19-1 1 1 2-1 3v9a2 2 0 0 0 0 1v6l-1 1 3-1 1-2h1v-4l1-4v-5l-1-1 1-3-1-1v-2s0 1 0 0v-1l-1-1a20 20 0 0 1-2-2v4Z\" clip-rule=\"evenodd\"/></svg>"),
Rules: []string{
"||battle.net^",
"||battlenet.com.cn^",
"||blizzard.cn^",
"||blizzardgames.cn^",
"||blz-contentstack.com^",
"||blzstatic.cn^",
"||bnet.163.com^",
"||bnet.cn^",
"||lizzard.com^",
},
}, { }, {
ID: "cloudflare", ID: "cloudflare",
Name: "CloudFlare", Name: "CloudFlare",
@@ -363,14 +319,6 @@ var blockedServices = []blockedService{{
"||warp.plus^", "||warp.plus^",
"||workers.dev^", "||workers.dev^",
}, },
}, {
ID: "clubhouse",
Name: "Clubhouse",
IconSVG: []byte("<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" viewBox=\"0 0 50 50\"><path d=\"M29.8 4a1 1 0 0 0-.92.7 1 1 0 0 0 .36 1.1 31.2 31.2 0 0 1 6 6.02 1 1 0 1 0 1.6-1.2 33.2 33.2 0 0 0-6.4-6.4A1 1 0 0 0 29.8 4Zm-7.16 1.06c-.46 0-.87.3-.99.74a1 1 0 0 0 .5 1.15 31.13 31.13 0 0 1 11.13 10.6 1 1 0 1 0 1.7-1.07A33.12 33.12 0 0 0 23.11 5.2a.96.96 0 0 0-.48-.14ZM14.5 7.01a3.42 3.42 0 0 0-3.27 2.28l-.26-.27A3.49 3.49 0 0 0 8.5 8.01c-.9 0-1.8.34-2.48 1.01a3.51 3.51 0 0 0-.57 4.17c-.52.15-1.01.42-1.43.84a3.52 3.52 0 0 0 0 4.94l.27.27c-.46.16-.9.41-1.27.79a3.52 3.52 0 0 0 0 4.94l.88.88 16.47 16.47a9.01 9.01 0 0 0 12.72 0l4.23-4.22a9.94 9.94 0 0 0 2.3-3.59l2.63-7.08a8.03 8.03 0 0 1 1.84-2.87l1.74-1.73 1-1a4.02 4.02 0 0 0 0-5.66 4.02 4.02 0 0 0-5.66 0l-1 1-.7.71-4.2 4.2a2.98 2.98 0 0 1-4.24 0L17.9 8.96l-.94-.94a3.49 3.49 0 0 0-2.47-1.01Zm0 1.98c.38 0 .76.15 1.06.45l.94.94 13.1 13.1a5.02 5.02 0 0 0 7.08 0l4.2-4.18.7-.71 1-1c.8-.8 2.05-.8 2.83 0 .8.79.8 2.04 0 2.83l-2.73 2.73a10.03 10.03 0 0 0-2.3 3.58l-2.63 7.08a8.02 8.02 0 0 1-1.84 2.87l-4.23 4.23a6.99 6.99 0 0 1-9.9 0L4.44 23.56a1.5 1.5 0 0 1 0-2.12c.59-.59 1.45-.55 2.08.08l.1.09 8.2 8.37a1 1 0 0 0 .97.29 1 1 0 0 0 .46-1.68l-9.52-9.73-.01-.01-1.28-1.29a1.5 1.5 0 0 1 0-2.12c.6-.6 1.47-.58 2.08.03l9.18 9.17a1 1 0 0 0 1.69-.43 1 1 0 0 0-.28-.98L9 14.13l-.06-.07-1.5-1.5c-.6-.6-.6-1.53 0-2.12a1.5 1.5 0 0 1 2.12 0L20.8 21.67a1 1 0 0 0 1.68-.44 1 1 0 0 0-.27-.97l-8.7-8.7-.06-.06a1.4 1.4 0 0 1-.01-2.06c.3-.3.68-.45 1.06-.45ZM4.23 32a1 1 0 0 0-.82 1.51c3 5.18 7.36 9.46 12.59 12.37a1 1 0 0 0 1.51-.89 1 1 0 0 0-.54-.86A31.16 31.16 0 0 1 5.15 32.5a1.01 1.01 0 0 0-.92-.51Z\"/></svg>"),
Rules: []string{
"||clubhouse.com^",
"||clubhouseapi.com^",
},
}, { }, {
ID: "crunchyroll", ID: "crunchyroll",
Name: "Crunchyroll", Name: "Crunchyroll",
@@ -778,18 +726,6 @@ var blockedServices = []blockedService{{
"||xxbay.com^", "||xxbay.com^",
"||yibei.org^", "||yibei.org^",
}, },
}, {
ID: "electronic_arts",
Name: "Electronic Arts",
IconSVG: []byte("<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" viewBox=\"0 0 1000 1000\"><path d=\"M500 1000C224.3 1000 0 775.7 0 500S224.3 0 500 0s500 224.3 500 500-224.3 500-500 500zm84.63-693.4H302.05l-42.87 68.9h282.25zm57.75.66L469.63 582.33H278.02l44.2-68.96h114.85l43.87-68.93h-265.5l-43.86 68.93h62.9L147.2 651.05h364.2L645.9 438.9l49.05 74.46h-44.23l-41.88 68.96H739.8l45.48 68.72h83.54z\"/></svg>"),
Rules: []string{
"||ea.com^",
"||eamobile.com^",
"||easports.com^",
"||nearpolar.com^",
"||swtor.com^",
"||tnt-ea.com^",
},
}, { }, {
ID: "epic_games", ID: "epic_games",
Name: "Epic Games", Name: "Epic Games",
@@ -1454,39 +1390,11 @@ var blockedServices = []blockedService{{
"||line-apps.com^", "||line-apps.com^",
"||line-cdn.net^", "||line-cdn.net^",
"||line-scdn.net^", "||line-scdn.net^",
"||line.biz^",
"||line.me^", "||line.me^",
"||line.naver.jp^", "||line.naver.jp^",
"||linecorp.com^", "||linecorp.com^",
"||linefriends.com.tw^",
"||linefriends.com^",
"||linegame.jp^",
"||linemobile.com^",
"||linemyshop.com^", "||linemyshop.com^",
"||lineshoppingseller.com^", "||lineshoppingseller.com^",
"||linetv.tw^",
},
}, {
ID: "linkedin",
Name: "LinkedIn",
IconSVG: []byte("<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" viewBox=\"0 0 50 50\"><path d=\"M41,4H9C6.24,4,4,6.24,4,9v32c0,2.76,2.24,5,5,5h32c2.76,0,5-2.24,5-5V9C46,6.24,43.76,4,41,4z M17,20v19h-6V20H17z M11,14.47c0-1.4,1.2-2.47,3-2.47s2.93,1.07,3,2.47c0,1.4-1.12,2.53-3,2.53C12.2,17,11,15.87,11,14.47z M39,39h-6c0,0,0-9.26,0-10 c0-2-1-4-3.5-4.04h-0.08C27,24.96,26,27.02,26,29c0,0.91,0,10,0,10h-6V20h6v2.56c0,0,1.93-2.56,5.81-2.56 c3.97,0,7.19,2.73,7.19,8.26V39z\"/></svg>"),
Rules: []string{
"||bizographics.com^",
"||cs1404.wpc.epsiloncdn.net^",
"||cs767.wpc.epsiloncdn.net^",
"||l-0005.dc-msedge.net^",
"||l-0005.l-dc-msedge.net^",
"||l-0005.l-msedge.net^",
"||l-0015.l-msedge.net^",
"||licdn.cn^",
"||licdn.com^",
"||linkedin.at^",
"||linkedin.be^",
"||linkedin.cn^",
"||linkedin.com^",
"||linkedin.nl^",
"||linkedin.qtlcdn.com^",
"||lnkd.in^",
}, },
}, { }, {
ID: "mail_ru", ID: "mail_ru",
@@ -1530,6 +1438,7 @@ var blockedServices = []blockedService{{
"||masto.pt^", "||masto.pt^",
"||mastodon.au^", "||mastodon.au^",
"||mastodon.bida.im^", "||mastodon.bida.im^",
"||mastodon.com.tr^",
"||mastodon.eus^", "||mastodon.eus^",
"||mastodon.green^", "||mastodon.green^",
"||mastodon.ie^", "||mastodon.ie^",
@@ -1545,7 +1454,7 @@ var blockedServices = []blockedService{{
"||mastodon.social^", "||mastodon.social^",
"||mastodon.uno^", "||mastodon.uno^",
"||mastodon.world^", "||mastodon.world^",
"||mastodon.zaclys.com^", "||mastodon.xyz^",
"||mastodonapp.uk^", "||mastodonapp.uk^",
"||mastodonners.nl^", "||mastodonners.nl^",
"||mastodont.cat^", "||mastodont.cat^",
@@ -1556,12 +1465,12 @@ var blockedServices = []blockedService{{
"||metalhead.club^", "||metalhead.club^",
"||mindly.social^", "||mindly.social^",
"||mstdn.ca^", "||mstdn.ca^",
"||mstdn.jp^",
"||mstdn.party^", "||mstdn.party^",
"||mstdn.plus^", "||mstdn.plus^",
"||mstdn.social^", "||mstdn.social^",
"||muenchen.social^", "||muenchen.social^",
"||muenster.im^", "||newsie.social^",
"||nerdculture.de^",
"||noc.social^", "||noc.social^",
"||norden.social^", "||norden.social^",
"||nrw.social^", "||nrw.social^",
@@ -1589,17 +1498,16 @@ var blockedServices = []blockedService{{
"||techhub.social^", "||techhub.social^",
"||theblower.au^", "||theblower.au^",
"||tkz.one^", "||tkz.one^",
"||todon.eu^",
"||toot.aquilenet.fr^", "||toot.aquilenet.fr^",
"||toot.community^", "||toot.community^",
"||toot.funami.tech^", "||toot.funami.tech^",
"||toot.io^", "||toot.io^",
"||toot.wales^", "||toot.wales^",
"||troet.cafe^", "||troet.cafe^",
"||twingyeo.kr^",
"||union.place^", "||union.place^",
"||universeodon.com^", "||universeodon.com^",
"||urbanists.social^", "||urbanists.social^",
"||wien.rocks^",
"||wxw.moe^", "||wxw.moe^",
}, },
}, { }, {
@@ -1642,44 +1550,6 @@ var blockedServices = []blockedService{{
"||nflxso.net^", "||nflxso.net^",
"||nflxvideo.net^", "||nflxvideo.net^",
}, },
}, {
ID: "nintendo",
Name: "Nintendo",
IconSVG: []byte("<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" viewBox=\"0 0 50 50\"><path d=\"M6 7v36h12.6V21.75l13 20.78.27.47H44V7H31.4v1l.04 20.22L18.5 7.47 18.22 7Zm2 2h9.1l14.5 23.22 1.84 3v-3.5L33.4 9H42v32h-9L18.44 17.75l-1.85-2.94V41H8Z\"/></svg>"),
Rules: []string{
"||nintendo-europe.com^",
"||nintendo.be^",
"||nintendo.co.jp^",
"||nintendo.co.uk^",
"||nintendo.com.au^",
"||nintendo.com^",
"||nintendo.de^",
"||nintendo.es^",
"||nintendo.eu^",
"||nintendo.fr^",
"||nintendo.it^",
"||nintendo.jp^",
"||nintendo.net^",
"||nintendo.nl^",
"||nintendoswitch.cn^",
"||nintendowifi.net^",
},
}, {
ID: "nvidia",
Name: "Nvidia",
IconSVG: []byte("<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" viewBox=\"0 0 48 48\"><path d=\"M20 8a2 2 0 0 0-2 2v2.55l.84-.05c10.76-.37 17.78 8.82 17.78 8.82s-8.05 9.8-16.44 9.8c-.73 0-1.47-.07-2.18-.19v-2.2c.73.23 1.52.35 2.3.35 5.88 0 11.35-7.6 11.35-7.6s-5.07-6.91-12.81-6.66l-.82.03v-2.3c-9.49.77-17.68 8.8-17.68 8.8S4.97 34.76 18 35.98v-2.44c.59.07 1.22.12 1.81.12 7.82 0 13.47-3.99 18.94-8.7.91.73 4.62 2.49 5.4 3.26-5.2 4.36-17.33 7.86-24.2 7.86-.66 0-1.32-.03-1.95-.1V38c0 1.1.9 2 2 2h25a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H20zm-2 6.86v2.82a11.8 11.8 0 0 1 1.57-.07c4.95 0 7.9 3.85 7.9 3.85l-4.03 3.39c-1.8-3.02-2.43-4.35-5.44-4.7v8.57c-4.06-1.38-5.4-6.14-5.4-6.14s2.37-2.83 5.38-2.46H18v-2.44a15.66 15.66 0 0 0-9.22 4.46s2 7.52 9.22 8.8v2.6c-9.56-1.17-12.82-11.7-12.82-11.7s4.27-6.3 12.82-6.97z\"/></svg>"),
Rules: []string{
"||geforce.com^",
"||geforcenow.com^",
"||nvidia.cn^",
"||nvidia.com.global.ogslb.com^",
"||nvidia.com^",
"||nvidia.eu^",
"||nvidia.partners^",
"||nvidiagrid.net^",
"||nvidianews.com^",
"||tegrazone.com^",
},
}, { }, {
ID: "ok", ID: "ok",
Name: "OK.ru", Name: "OK.ru",
@@ -1834,14 +1704,6 @@ var blockedServices = []blockedService{{
"||robloxcdn.com^", "||robloxcdn.com^",
"||robloxdev.cn^", "||robloxdev.cn^",
}, },
}, {
ID: "rockstar_games",
Name: "Rockstar games",
IconSVG: []byte("<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" viewBox=\"0 0 50 50\"><path d=\"M12 3c-4.96 0-9 4.04-9 9v26c0 4.96 4.04 9 9 9h26c4.96 0 9-4.04 9-9V12c0-4.96-4.04-9-9-9H12zm0 2h26c3.88 0 7 3.12 7 7v26c0 3.88-3.12 7-7 7H12c-3.88 0-7-3.12-7-7V12c0-3.88 3.12-7 7-7zm3.72 5a1 1 0 0 0-.97.79l-3.87 18a1 1 0 0 0 .98 1.21h4.27a1 1 0 0 0 .97-.79L18.47 23h2.07c.94 0 1.12.15 1.36.73.24.57.3 1.76.1 3.4-.08.68-.05 1.22.02 1.6v.03a1 1 0 0 0 .3.97l3.37 3.12-2.6 5.74a1 1 0 0 0 1.43 1.26l5.58-3.39 4.29 3.33a1 1 0 0 0 1.6-.98l-1.09-5.56 4.7-3.47a1 1 0 0 0-.6-1.8h-4.86l-.82-5.14a1 1 0 0 0-.98-.84 1 1 0 0 0-.88.51l-2.77 5a14.3 14.3 0 0 1 .06-2.83c.15-1.48.01-2.64-.18-3.45-.06-.28-.08-.25-.15-.45.3-.17.4-.13.77-.5.8-.8 1.6-2.18 1.75-4.26.17-2.26-.55-3.98-1.92-4.9C27.65 10.17 25.91 10 24 10h-8.28zm.81 2H24c1.75 0 3.13.25 3.9.77.76.52 1.18 1.27 1.05 3.1-.13 1.67-.69 2.51-1.17 3a2 2 0 0 1-.82.56 1 1 0 0 0-.6 1.44s.12.21.27.82c.14.6.26 1.53.13 2.79a14.24 14.24 0 0 0-.01 3.52h-2.76c-.01-.19-.04-.32 0-.62.22-1.78.25-3.21-.24-4.42A3.38 3.38 0 0 0 20.54 21h-2.87a1 1 0 0 0-.98.78L15.32 28H13.1l3.44-16zm2.76 1.03a1 1 0 0 0-.98.8l-.98 4.94a1 1 0 0 0 .98 1.2h4.47c.79 0 1.65-.12 2.44-.58a3.6 3.6 0 0 0 1.68-2.41 3.3 3.3 0 0 0-.72-2.92 3.35 3.35 0 0 0-2.47-1.03h-4.42zm.82 2h3.6c.41 0 .79.16 1 .4.22.22.36.52.23 1.15-.13.62-.36.88-.72 1.08a3 3 0 0 1-1.44.3h-3.25l.58-2.93zm11.7 10.99.49 3.11a1 1 0 0 0 .98.84h2.69l-2.76 2.05a1 1 0 0 0-.4 1l.7 3.56-2.73-2.12a1 1 0 0 0-1.13-.07l-3.4 2.07 1.56-3.44a1 1 0 0 0-.23-1.15L25.55 30H29a1 1 0 0 0 .88-.51l1.92-3.47z\"/></svg>"),
Rules: []string{
"||rockstargames.com^",
"||rsg.sc^",
},
}, { }, {
ID: "shopee", ID: "shopee",
Name: "Shopee", Name: "Shopee",
@@ -2078,16 +1940,6 @@ var blockedServices = []blockedService{{
"||twvid.com^", "||twvid.com^",
"||vine.co^", "||vine.co^",
}, },
}, {
ID: "ubisoft",
Name: "Ubisoft",
IconSVG: []byte("<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" viewBox=\"0 0 32 32\"><path d=\"M15.22 3C7.14 3 3.66 10.18 3.66 10.18l1.03.74s-1.3 2.45-1.26 5.6A12.5 12.5 0 0 0 16.08 29a12.5 12.5 0 0 0 12.49-12.46c0-9-6.98-13.54-13.35-13.54zm.07 2.2c6.3 0 11.2 5.07 11.2 10.98 0 6.27-4.71 10.62-10.2 10.62-4.04 0-7.69-3.08-7.69-7.3a5.8 5.8 0 0 1 2.75-5.03l.21.23a6.37 6.37 0 0 0-1.53 3.91c0 3.32 2.6 5.62 5.88 5.62 4.18 0 6.97-3.56 6.97-7.7 0-4.81-4.25-8.9-9.36-8.9a11.1 11.1 0 0 0-6.61 2.3l-.21-.2a10.07 10.07 0 0 1 8.59-4.54zM13.4 9.8c3.26 0 6.44 2.15 7.24 5.22l-.3.1a8.35 8.35 0 0 0-6.52-3.44c-5.08 0-7.75 4.62-7.36 8.47l-.3.12s-.56-1.24-.56-2.71a7.8 7.8 0 0 1 7.8-7.76zm2.15 5.33a2.77 2.77 0 0 1 2.78 2.74c0 1.23-.79 1.96-.79 1.96l.94.65s-.93 1.46-2.82 1.46a3.4 3.4 0 0 1-.1-6.8z\"/></svg>"),
Rules: []string{
"||ubi.com^",
"||ubisoft.com^",
"||ubisoft.org^",
"||ubisoftconnect.com^",
},
}, { }, {
ID: "valorant", ID: "valorant",
Name: "Valorant", Name: "Valorant",

View File

@@ -23,14 +23,12 @@ type Client struct {
safeSearchConf filtering.SafeSearchConfig safeSearchConf filtering.SafeSearchConfig
SafeSearch filtering.SafeSearch SafeSearch filtering.SafeSearch
// BlockedServices is the configuration of blocked services of a client.
BlockedServices *filtering.BlockedServices
Name string Name string
IDs []string IDs []string
Tags []string Tags []string
Upstreams []string BlockedServices []string
Upstreams []string
UseOwnSettings bool UseOwnSettings bool
FilteringEnabled bool FilteringEnabled bool
@@ -46,9 +44,9 @@ type Client struct {
func (c *Client) ShallowClone() (sh *Client) { func (c *Client) ShallowClone() (sh *Client) {
clone := *c clone := *c
clone.BlockedServices = c.BlockedServices.Clone()
clone.IDs = stringutil.CloneSlice(c.IDs) clone.IDs = stringutil.CloneSlice(c.IDs)
clone.Tags = stringutil.CloneSlice(c.Tags) clone.Tags = stringutil.CloneSlice(c.Tags)
clone.BlockedServices = stringutil.CloneSlice(c.BlockedServices)
clone.Upstreams = stringutil.CloneSlice(c.Upstreams) clone.Upstreams = stringutil.CloneSlice(c.Upstreams)
return &clone return &clone

View File

@@ -11,7 +11,6 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/aghnet" "github.com/AdguardTeam/AdGuardHome/internal/aghnet"
"github.com/AdguardTeam/AdGuardHome/internal/dhcpd" "github.com/AdguardTeam/AdGuardHome/internal/dhcpd"
"github.com/AdguardTeam/AdGuardHome/internal/dhcpsvc"
"github.com/AdguardTeam/AdGuardHome/internal/dnsforward" "github.com/AdguardTeam/AdGuardHome/internal/dnsforward"
"github.com/AdguardTeam/AdGuardHome/internal/filtering" "github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/AdGuardHome/internal/querylog" "github.com/AdguardTeam/AdGuardHome/internal/querylog"
@@ -25,23 +24,6 @@ import (
"golang.org/x/exp/slices" "golang.org/x/exp/slices"
) )
// DHCP is an interface for accessing DHCP lease data the [clientsContainer]
// needs.
type DHCP interface {
// Leases returns all the DHCP leases.
Leases() (leases []*dhcpsvc.Lease)
// HostByIP returns the hostname of the DHCP client with the given IP
// address. The address will be netip.Addr{} if there is no such client,
// due to an assumption that a DHCP client must always have an IP address.
HostByIP(ip netip.Addr) (host string)
// MACByIP returns the MAC address for the given IP address leased. It
// returns nil if there is no such client, due to an assumption that a DHCP
// client must always have a MAC address.
MACByIP(ip netip.Addr) (mac net.HardwareAddr)
}
// clientsContainer is the storage of all runtime and persistent clients. // clientsContainer is the storage of all runtime and persistent clients.
type clientsContainer struct { type clientsContainer struct {
// TODO(a.garipov): Perhaps use a number of separate indices for different // TODO(a.garipov): Perhaps use a number of separate indices for different
@@ -96,7 +78,7 @@ func (clients *clientsContainer) Init(
etcHosts *aghnet.HostsContainer, etcHosts *aghnet.HostsContainer,
arpdb aghnet.ARPDB, arpdb aghnet.ARPDB,
filteringConf *filtering.Config, filteringConf *filtering.Config,
) (err error) { ) {
if clients.list != nil { if clients.list != nil {
log.Fatal("clients.list != nil") log.Fatal("clients.list != nil")
} }
@@ -110,29 +92,23 @@ func (clients *clientsContainer) Init(
clients.dhcpServer = dhcpServer clients.dhcpServer = dhcpServer
clients.etcHosts = etcHosts clients.etcHosts = etcHosts
clients.arpdb = arpdb clients.arpdb = arpdb
err = clients.addFromConfig(objects, filteringConf) clients.addFromConfig(objects, filteringConf)
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return err
}
clients.safeSearchCacheSize = filteringConf.SafeSearchCacheSize clients.safeSearchCacheSize = filteringConf.SafeSearchCacheSize
clients.safeSearchCacheTTL = time.Minute * time.Duration(filteringConf.CacheTime) clients.safeSearchCacheTTL = time.Minute * time.Duration(filteringConf.CacheTime)
if clients.testing { if clients.testing {
return nil return
} }
clients.updateFromDHCP(true)
if clients.dhcpServer != nil { if clients.dhcpServer != nil {
clients.dhcpServer.SetOnLeaseChanged(clients.onDHCPLeaseChanged) clients.dhcpServer.SetOnLeaseChanged(clients.onDHCPLeaseChanged)
clients.onDHCPLeaseChanged(dhcpd.LeaseChangedAdded)
} }
if clients.etcHosts != nil { if clients.etcHosts != nil {
go clients.handleHostsUpdates() go clients.handleHostsUpdates()
} }
return nil
} }
func (clients *clientsContainer) handleHostsUpdates() { func (clients *clientsContainer) handleHostsUpdates() {
@@ -172,14 +148,12 @@ func (clients *clientsContainer) reloadARP() {
type clientObject struct { type clientObject struct {
SafeSearchConf filtering.SafeSearchConfig `yaml:"safe_search"` SafeSearchConf filtering.SafeSearchConfig `yaml:"safe_search"`
// BlockedServices is the configuration of blocked services of a client.
BlockedServices *filtering.BlockedServices `yaml:"blocked_services"`
Name string `yaml:"name"` Name string `yaml:"name"`
IDs []string `yaml:"ids"` Tags []string `yaml:"tags"`
Tags []string `yaml:"tags"` IDs []string `yaml:"ids"`
Upstreams []string `yaml:"upstreams"` BlockedServices []string `yaml:"blocked_services"`
Upstreams []string `yaml:"upstreams"`
UseGlobalSettings bool `yaml:"use_global_settings"` UseGlobalSettings bool `yaml:"use_global_settings"`
FilteringEnabled bool `yaml:"filtering_enabled"` FilteringEnabled bool `yaml:"filtering_enabled"`
@@ -193,10 +167,7 @@ type clientObject struct {
// addFromConfig initializes the clients container with objects from the // addFromConfig initializes the clients container with objects from the
// configuration file. // configuration file.
func (clients *clientsContainer) addFromConfig( func (clients *clientsContainer) addFromConfig(objects []*clientObject, filteringConf *filtering.Config) {
objects []*clientObject,
filteringConf *filtering.Config,
) (err error) {
for _, o := range objects { for _, o := range objects {
cli := &Client{ cli := &Client{
Name: o.Name, Name: o.Name,
@@ -217,7 +188,7 @@ func (clients *clientsContainer) addFromConfig(
if o.SafeSearchConf.Enabled { if o.SafeSearchConf.Enabled {
o.SafeSearchConf.CustomResolver = safeSearchResolver{} o.SafeSearchConf.CustomResolver = safeSearchResolver{}
err = cli.setSafeSearch( err := cli.setSafeSearch(
o.SafeSearchConf, o.SafeSearchConf,
filteringConf.SafeSearchCacheSize, filteringConf.SafeSearchCacheSize,
time.Minute*time.Duration(filteringConf.CacheTime), time.Minute*time.Duration(filteringConf.CacheTime),
@@ -229,13 +200,14 @@ func (clients *clientsContainer) addFromConfig(
} }
} }
err = o.BlockedServices.Validate() for _, s := range o.BlockedServices {
if err != nil { if filtering.BlockedSvcKnown(s) {
return fmt.Errorf("clients: init client blocked services %q: %w", cli.Name, err) cli.BlockedServices = append(cli.BlockedServices, s)
} else {
log.Info("clients: skipping unknown blocked service %q", s)
}
} }
cli.BlockedServices = o.BlockedServices.Clone()
for _, t := range o.Tags { for _, t := range o.Tags {
if clients.allTags.Has(t) { if clients.allTags.Has(t) {
cli.Tags = append(cli.Tags, t) cli.Tags = append(cli.Tags, t)
@@ -246,13 +218,11 @@ func (clients *clientsContainer) addFromConfig(
slices.Sort(cli.Tags) slices.Sort(cli.Tags)
_, err = clients.Add(cli) _, err := clients.Add(cli)
if err != nil { if err != nil {
log.Error("clients: adding clients %s: %s", cli.Name, err) log.Error("clients: adding clients %s: %s", cli.Name, err)
} }
} }
return nil
} }
// forConfig returns all currently known persistent clients as objects for the // forConfig returns all currently known persistent clients as objects for the
@@ -266,11 +236,10 @@ func (clients *clientsContainer) forConfig() (objs []*clientObject) {
o := &clientObject{ o := &clientObject{
Name: cli.Name, Name: cli.Name,
BlockedServices: cli.BlockedServices.Clone(), Tags: stringutil.CloneSlice(cli.Tags),
IDs: stringutil.CloneSlice(cli.IDs),
IDs: stringutil.CloneSlice(cli.IDs), BlockedServices: stringutil.CloneSlice(cli.BlockedServices),
Tags: stringutil.CloneSlice(cli.Tags), Upstreams: stringutil.CloneSlice(cli.Upstreams),
Upstreams: stringutil.CloneSlice(cli.Upstreams),
UseGlobalSettings: !cli.UseOwnSettings, UseGlobalSettings: !cli.UseOwnSettings,
FilteringEnabled: cli.FilteringEnabled, FilteringEnabled: cli.FilteringEnabled,
@@ -308,38 +277,15 @@ func (clients *clientsContainer) periodicUpdate() {
} }
} }
// onDHCPLeaseChanged is a callback for the DHCP server. It updates the list of
// runtime clients using the DHCP server's leases.
//
// TODO(e.burkov): Remove when switched to dhcpsvc.
func (clients *clientsContainer) onDHCPLeaseChanged(flags int) { func (clients *clientsContainer) onDHCPLeaseChanged(flags int) {
if clients.dhcpServer == nil || !config.Clients.Sources.DHCP { switch flags {
return case dhcpd.LeaseChangedAdded,
dhcpd.LeaseChangedAddedStatic,
dhcpd.LeaseChangedRemovedStatic:
clients.updateFromDHCP(true)
case dhcpd.LeaseChangedRemovedAll:
clients.updateFromDHCP(false)
} }
clients.lock.Lock()
defer clients.lock.Unlock()
clients.rmHostsBySrc(ClientSourceDHCP)
if flags == dhcpd.LeaseChangedRemovedAll {
return
}
leases := clients.dhcpServer.Leases(dhcpd.LeasesAll)
n := 0
for _, l := range leases {
if l.Hostname == "" {
continue
}
ok := clients.addHostLocked(l.IP, l.Hostname, ClientSourceDHCP)
if ok {
n++
}
}
log.Debug("clients: added %d client aliases from dhcp", n)
} }
// clientSource checks if client with this IP address already exists and returns // clientSource checks if client with this IP address already exists and returns
@@ -355,11 +301,11 @@ func (clients *clientsContainer) clientSource(ip netip.Addr) (src clientSource)
} }
rc, ok := clients.ipToRC[ip] rc, ok := clients.ipToRC[ip]
if ok { if !ok {
return rc.Source return ClientSourceNone
} }
return ClientSourceNone return rc.Source
} }
// findMultiple is a wrapper around Find to make it a valid client finder for // findMultiple is a wrapper around Find to make it a valid client finder for
@@ -520,11 +466,11 @@ func (clients *clientsContainer) findLocked(id string) (c *Client, ok bool) {
} }
} }
if clients.dhcpServer != nil { if clients.dhcpServer == nil {
return clients.findDHCP(ip) return nil, false
} }
return nil, false return clients.findDHCP(ip)
} }
// findDHCP searches for a client by its MAC, if the DHCP server is active and // findDHCP searches for a client by its MAC, if the DHCP server is active and
@@ -751,27 +697,28 @@ func (clients *clientsContainer) setWHOISInfo(ip netip.Addr, wi *whois.Info) {
_, ok := clients.findLocked(ip.String()) _, ok := clients.findLocked(ip.String())
if ok { if ok {
log.Debug("clients: client for %s is already created, ignore whois info", ip) log.Debug("clients: client for %s is already created, ignore whois info", ip)
return
}
rc, ok := clients.ipToRC[ip]
if ok {
rc.WHOIS = wi
log.Debug("clients: set whois info for runtime client %s: %+v", rc.Host, wi)
return return
} }
// TODO(e.burkov): Consider storing WHOIS information separately and // Create a RuntimeClient implicitly so that we don't do this check
// potentially get rid of [RuntimeClient]. // again.
rc, ok := clients.ipToRC[ip] rc = &RuntimeClient{
if !ok { Source: ClientSourceWHOIS,
// Create a RuntimeClient implicitly so that we don't do this check
// again.
rc = &RuntimeClient{
Source: ClientSourceWHOIS,
}
clients.ipToRC[ip] = rc
log.Debug("clients: set whois info for runtime client with ip %s: %+v", ip, wi)
} else {
log.Debug("clients: set whois info for runtime client %s: %+v", rc.Host, wi)
} }
rc.WHOIS = wi rc.WHOIS = wi
clients.ipToRC[ip] = rc
log.Debug("clients: set whois info for runtime client with ip %s: %+v", ip, wi)
} }
// AddHost adds a new IP-hostname pairing. The priorities of the sources are // AddHost adds a new IP-hostname pairing. The priorities of the sources are
@@ -795,19 +742,23 @@ func (clients *clientsContainer) addHostLocked(
src clientSource, src clientSource,
) (ok bool) { ) (ok bool) {
rc, ok := clients.ipToRC[ip] rc, ok := clients.ipToRC[ip]
if !ok { if ok {
if rc.Source > src {
return false
}
rc.Host = host
rc.Source = src
} else {
rc = &RuntimeClient{ rc = &RuntimeClient{
WHOIS: &whois.Info{}, Host: host,
Source: src,
WHOIS: &whois.Info{},
} }
clients.ipToRC[ip] = rc clients.ipToRC[ip] = rc
} else if src < rc.Source {
return false
} }
rc.Host = host
rc.Source = src
log.Debug("clients: added %s -> %q [%d]", ip, host, len(clients.ipToRC)) log.Debug("clients: added %s -> %q [%d]", ip, host, len(clients.ipToRC))
return true return true
@@ -876,6 +827,38 @@ func (clients *clientsContainer) addFromSystemARP() {
log.Debug("clients: added %d client aliases from arp neighborhood", added) log.Debug("clients: added %d client aliases from arp neighborhood", added)
} }
// updateFromDHCP adds the clients that have a non-empty hostname from the DHCP
// server.
func (clients *clientsContainer) updateFromDHCP(add bool) {
if clients.dhcpServer == nil || !config.Clients.Sources.DHCP {
return
}
clients.lock.Lock()
defer clients.lock.Unlock()
clients.rmHostsBySrc(ClientSourceDHCP)
if !add {
return
}
leases := clients.dhcpServer.Leases(dhcpd.LeasesAll)
n := 0
for _, l := range leases {
if l.Hostname == "" {
continue
}
ok := clients.addHostLocked(l.IP, l.Hostname, ClientSourceDHCP)
if ok {
n++
}
}
log.Debug("clients: added %d client aliases from dhcp", n)
}
// close gracefully closes all the client-specific upstream configurations of // close gracefully closes all the client-specific upstream configurations of
// the persistent clients. // the persistent clients.
func (clients *clientsContainer) close() (err error) { func (clients *clientsContainer) close() (err error) {

View File

@@ -16,19 +16,18 @@ import (
// newClientsContainer is a helper that creates a new clients container for // newClientsContainer is a helper that creates a new clients container for
// tests. // tests.
func newClientsContainer(t *testing.T) (c *clientsContainer) { func newClientsContainer() (c *clientsContainer) {
c = &clientsContainer{ c = &clientsContainer{
testing: true, testing: true,
} }
err := c.Init(nil, nil, nil, nil, &filtering.Config{}) c.Init(nil, nil, nil, nil, &filtering.Config{})
require.NoError(t, err)
return c return c
} }
func TestClients(t *testing.T) { func TestClients(t *testing.T) {
clients := newClientsContainer(t) clients := newClientsContainer()
t.Run("add_success", func(t *testing.T) { t.Run("add_success", func(t *testing.T) {
var ( var (
@@ -199,7 +198,7 @@ func TestClients(t *testing.T) {
} }
func TestClientsWHOIS(t *testing.T) { func TestClientsWHOIS(t *testing.T) {
clients := newClientsContainer(t) clients := newClientsContainer()
whois := &whois.Info{ whois := &whois.Info{
Country: "AU", Country: "AU",
Orgname: "Example Org", Orgname: "Example Org",
@@ -245,7 +244,7 @@ func TestClientsWHOIS(t *testing.T) {
} }
func TestClientsAddExisting(t *testing.T) { func TestClientsAddExisting(t *testing.T) {
clients := newClientsContainer(t) clients := newClientsContainer()
t.Run("simple", func(t *testing.T) { t.Run("simple", func(t *testing.T) {
ip := netip.MustParseAddr("1.1.1.1") ip := netip.MustParseAddr("1.1.1.1")
@@ -317,7 +316,7 @@ func TestClientsAddExisting(t *testing.T) {
} }
func TestClientsCustomUpstream(t *testing.T) { func TestClientsCustomUpstream(t *testing.T) {
clients := newClientsContainer(t) clients := newClientsContainer()
// Add client with upstreams. // Add client with upstreams.
ok, err := clients.Add(&Client{ ok, err := clients.Add(&Client{

View File

@@ -9,7 +9,6 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/aghalg" "github.com/AdguardTeam/AdGuardHome/internal/aghalg"
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp" "github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
"github.com/AdguardTeam/AdGuardHome/internal/filtering" "github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/AdGuardHome/internal/schedule"
"github.com/AdguardTeam/AdGuardHome/internal/whois" "github.com/AdguardTeam/AdGuardHome/internal/whois"
) )
@@ -119,24 +118,15 @@ func (clients *clientsContainer) jsonToClient(cj clientJSON, prev *Client) (c *C
} }
} }
weekly := schedule.EmptyWeekly()
if prev != nil {
weekly = prev.BlockedServices.Schedule.Clone()
}
c = &Client{ c = &Client{
safeSearchConf: safeSearchConf, safeSearchConf: safeSearchConf,
Name: cj.Name, Name: cj.Name,
BlockedServices: &filtering.BlockedServices{ IDs: cj.IDs,
Schedule: weekly, Tags: cj.Tags,
IDs: cj.BlockedServices, BlockedServices: cj.BlockedServices,
}, Upstreams: cj.Upstreams,
IDs: cj.IDs,
Tags: cj.Tags,
Upstreams: cj.Upstreams,
UseOwnSettings: !cj.UseGlobalSettings, UseOwnSettings: !cj.UseGlobalSettings,
FilteringEnabled: cj.FilteringEnabled, FilteringEnabled: cj.FilteringEnabled,
@@ -190,8 +180,7 @@ func clientToJSON(c *Client) (cj *clientJSON) {
SafeBrowsingEnabled: c.SafeBrowsingEnabled, SafeBrowsingEnabled: c.SafeBrowsingEnabled,
UseGlobalBlockedServices: !c.UseOwnBlockedServices, UseGlobalBlockedServices: !c.UseOwnBlockedServices,
BlockedServices: c.BlockedServices,
BlockedServices: c.BlockedServices.IDs,
Upstreams: c.Upstreams, Upstreams: c.Upstreams,

View File

@@ -91,17 +91,18 @@ type clientSourcesConfig struct {
HostsFile bool `yaml:"hosts"` HostsFile bool `yaml:"hosts"`
} }
// configuration is loaded from YAML. // configuration is loaded from YAML
// // field ordering is important -- yaml fields will mirror ordering from here
// Field ordering is important, YAML fields better not to be reordered, if it's
// not absolutely necessary.
type configuration struct { type configuration struct {
// Raw file data to avoid re-reading of configuration file // Raw file data to avoid re-reading of configuration file
// It's reset after config is parsed // It's reset after config is parsed
fileData []byte fileData []byte
// HTTPConfig is the block with http conf. // BindHost is the address for the web interface server to listen on.
HTTPConfig httpConfig `yaml:"http"` BindHost netip.Addr `yaml:"bind_host"`
// BindPort is the port for the web interface server to listen on.
BindPort int `yaml:"bind_port"`
// Users are the clients capable for accessing the web interface. // Users are the clients capable for accessing the web interface.
Users []webUser `yaml:"users"` Users []webUser `yaml:"users"`
// AuthAttempts is the maximum number of failed login attempts a user // AuthAttempts is the maximum number of failed login attempts a user
@@ -119,6 +120,10 @@ type configuration struct {
// DebugPProf defines if the profiling HTTP handler will listen on :6060. // DebugPProf defines if the profiling HTTP handler will listen on :6060.
DebugPProf bool `yaml:"debug_pprof"` DebugPProf bool `yaml:"debug_pprof"`
// TTL for a web session (in hours)
// An active session is automatically refreshed once a day.
WebSessionTTLHours uint32 `yaml:"web_session_ttl"`
DNS dnsConfig `yaml:"dns"` DNS dnsConfig `yaml:"dns"`
TLS tlsConfigSettings `yaml:"tls"` TLS tlsConfigSettings `yaml:"tls"`
QueryLog queryLogConfig `yaml:"querylog"` QueryLog queryLogConfig `yaml:"querylog"`
@@ -151,23 +156,7 @@ type configuration struct {
SchemaVersion int `yaml:"schema_version"` // keeping last so that users will be less tempted to change it -- used when upgrading between versions SchemaVersion int `yaml:"schema_version"` // keeping last so that users will be less tempted to change it -- used when upgrading between versions
} }
// httpConfig is a block with HTTP configuration params. // field ordering is important -- yaml fields will mirror ordering from here
//
// Field ordering is important, YAML fields better not to be reordered, if it's
// not absolutely necessary.
type httpConfig struct {
// Address is the address to serve the web UI on.
Address netip.AddrPort
// SessionTTL for a web session.
// An active session is automatically refreshed once a day.
SessionTTL timeutil.Duration `yaml:"session_ttl"`
}
// dnsConfig is a block with DNS configuration params.
//
// Field ordering is important, YAML fields better not to be reordered, if it's
// not absolutely necessary.
type dnsConfig struct { type dnsConfig struct {
BindHosts []netip.Addr `yaml:"bind_hosts"` BindHosts []netip.Addr `yaml:"bind_hosts"`
Port int `yaml:"port"` Port int `yaml:"port"`
@@ -272,12 +261,11 @@ type statsConfig struct {
// //
// TODO(a.garipov, e.burkov): This global is awful and must be removed. // TODO(a.garipov, e.burkov): This global is awful and must be removed.
var config = &configuration{ var config = &configuration{
AuthAttempts: 5, BindPort: 3000,
AuthBlockMin: 15, BindHost: netip.IPv4Unspecified(),
HTTPConfig: httpConfig{ AuthAttempts: 5,
Address: netip.AddrPortFrom(netip.IPv4Unspecified(), 3000), AuthBlockMin: 15,
SessionTTL: timeutil.Duration{Duration: 30 * timeutil.Day}, WebSessionTTLHours: 30 * 24,
},
DNS: dnsConfig{ DNS: dnsConfig{
BindHosts: []netip.Addr{netip.IPv4Unspecified()}, BindHosts: []netip.Addr{netip.IPv4Unspecified()},
Port: defaultPortDNS, Port: defaultPortDNS,
@@ -439,8 +427,8 @@ func readLogSettings() (ls *logSettings) {
// validateBindHosts returns error if any of binding hosts from configuration is // validateBindHosts returns error if any of binding hosts from configuration is
// not a valid IP address. // not a valid IP address.
func validateBindHosts(conf *configuration) (err error) { func validateBindHosts(conf *configuration) (err error) {
if !conf.HTTPConfig.Address.IsValid() { if !conf.BindHost.IsValid() {
return errors.Error("http.address is not a valid ip address") return errors.Error("bind_host is not a valid ip address")
} }
for i, addr := range conf.DNS.BindHosts { for i, addr := range conf.DNS.BindHosts {
@@ -474,7 +462,7 @@ func parseConfig() (err error) {
} }
tcpPorts := aghalg.UniqChecker[tcpPort]{} tcpPorts := aghalg.UniqChecker[tcpPort]{}
addPorts(tcpPorts, tcpPort(config.HTTPConfig.Address.Port())) addPorts(tcpPorts, tcpPort(config.BindPort))
udpPorts := aghalg.UniqChecker[udpPort]{} udpPorts := aghalg.UniqChecker[udpPort]{}
addPorts(udpPorts, udpPort(config.DNS.Port)) addPorts(udpPorts, udpPort(config.DNS.Port))

View File

@@ -103,7 +103,7 @@ type statusResponse struct {
Language string `json:"language"` Language string `json:"language"`
DNSAddrs []string `json:"dns_addresses"` DNSAddrs []string `json:"dns_addresses"`
DNSPort int `json:"dns_port"` DNSPort int `json:"dns_port"`
HTTPPort uint16 `json:"http_port"` HTTPPort int `json:"http_port"`
// ProtectionDisabledDuration is the duration of the protection pause in // ProtectionDisabledDuration is the duration of the protection pause in
// milliseconds. // milliseconds.
@@ -158,7 +158,7 @@ func handleStatus(w http.ResponseWriter, r *http.Request) {
Language: config.Language, Language: config.Language,
DNSAddrs: dnsAddrs, DNSAddrs: dnsAddrs,
DNSPort: config.DNS.Port, DNSPort: config.DNS.Port,
HTTPPort: config.HTTPConfig.Address.Port(), HTTPPort: config.BindPort,
ProtectionDisabledDuration: protectionDisabledDuration, ProtectionDisabledDuration: protectionDisabledDuration,
ProtectionEnabled: protectionEnabled, ProtectionEnabled: protectionEnabled,
IsRunning: isRunning(), IsRunning: isRunning(),

View File

@@ -96,9 +96,8 @@ type checkConfResp struct {
func (req *checkConfReq) validateWeb(tcpPorts aghalg.UniqChecker[tcpPort]) (err error) { func (req *checkConfReq) validateWeb(tcpPorts aghalg.UniqChecker[tcpPort]) (err error) {
defer func() { err = errors.Annotate(err, "validating ports: %w") }() defer func() { err = errors.Annotate(err, "validating ports: %w") }()
// TODO(a.garipov): Declare all port variables anywhere as uint16. portInt := req.Web.Port
reqPort := uint16(req.Web.Port) port := tcpPort(portInt)
port := tcpPort(reqPort)
addPorts(tcpPorts, port) addPorts(tcpPorts, port)
if err = tcpPorts.Validate(); err != nil { if err = tcpPorts.Validate(); err != nil {
// Reset the value for the port to 1 to make sure that validateDNS // Reset the value for the port to 1 to make sure that validateDNS
@@ -109,15 +108,15 @@ func (req *checkConfReq) validateWeb(tcpPorts aghalg.UniqChecker[tcpPort]) (err
return err return err
} }
switch reqPort { switch portInt {
case 0, config.HTTPConfig.Address.Port(): case 0, config.BindPort:
return nil return nil
default: default:
// Go on and check the port binding only if it's not zero or won't be // Go on and check the port binding only if it's not zero or won't be
// unbound after install. // unbound after install.
} }
return aghnet.CheckPort("tcp", netip.AddrPortFrom(req.Web.IP, reqPort)) return aghnet.CheckPort("tcp", netip.AddrPortFrom(req.Web.IP, uint16(portInt)))
} }
// validateDNS returns error if the DNS part of the initial configuration can't // validateDNS returns error if the DNS part of the initial configuration can't
@@ -128,11 +127,11 @@ func (req *checkConfReq) validateDNS(
) (canAutofix bool, err error) { ) (canAutofix bool, err error) {
defer func() { err = errors.Annotate(err, "validating ports: %w") }() defer func() { err = errors.Annotate(err, "validating ports: %w") }()
port := uint16(req.DNS.Port) port := req.DNS.Port
switch port { switch port {
case 0: case 0:
return false, nil return false, nil
case config.HTTPConfig.Address.Port(): case config.BindPort:
// Go on and only check the UDP port since the TCP one is already bound // Go on and only check the UDP port since the TCP one is already bound
// by AdGuard Home for web interface. // by AdGuard Home for web interface.
default: default:
@@ -319,7 +318,8 @@ type applyConfigReq struct {
// copyInstallSettings copies the installation parameters between two // copyInstallSettings copies the installation parameters between two
// configuration structures. // configuration structures.
func copyInstallSettings(dst, src *configuration) { func copyInstallSettings(dst, src *configuration) {
dst.HTTPConfig = src.HTTPConfig dst.BindHost = src.BindHost
dst.BindPort = src.BindPort
dst.DNS.BindHosts = src.DNS.BindHosts dst.DNS.BindHosts = src.DNS.BindHosts
dst.DNS.Port = src.DNS.Port dst.DNS.Port = src.DNS.Port
} }
@@ -413,7 +413,8 @@ func (web *webAPI) handleInstallConfigure(w http.ResponseWriter, r *http.Request
copyInstallSettings(curConfig, config) copyInstallSettings(curConfig, config)
Context.firstRun = false Context.firstRun = false
config.HTTPConfig.Address = netip.AddrPortFrom(req.Web.IP, uint16(req.Web.Port)) config.BindHost = req.Web.IP
config.BindPort = req.Web.Port
config.DNS.BindHosts = []netip.Addr{req.DNS.IP} config.DNS.BindHosts = []netip.Addr{req.DNS.IP}
config.DNS.Port = req.DNS.Port config.DNS.Port = req.DNS.Port
@@ -486,8 +487,7 @@ func decodeApplyConfigReq(r io.Reader) (req *applyConfigReq, restartHTTP bool, e
return nil, false, errors.Error("ports cannot be 0") return nil, false, errors.Error("ports cannot be 0")
} }
addrPort := config.HTTPConfig.Address restartHTTP = config.BindHost != req.Web.IP || config.BindPort != req.Web.Port
restartHTTP = addrPort.Addr() != req.Web.IP || int(addrPort.Port()) != req.Web.Port
if restartHTTP { if restartHTTP {
err = aghnet.CheckPort("tcp", netip.AddrPortFrom(req.Web.IP, uint16(req.Web.Port))) err = aghnet.CheckPort("tcp", netip.AddrPortFrom(req.Web.IP, uint16(req.Web.Port)))
if err != nil { if err != nil {

View File

@@ -157,9 +157,7 @@ func (vr *versionResponse) setAllowedToAutoUpdate() (err error) {
Context.tls.WriteDiskConfig(tlsConf) Context.tls.WriteDiskConfig(tlsConf)
canUpdate := true canUpdate := true
if tlsConfUsesPrivilegedPorts(tlsConf) || if tlsConfUsesPrivilegedPorts(tlsConf) || config.BindPort < 1024 || config.DNS.Port < 1024 {
config.HTTPConfig.Address.Port() < 1024 ||
config.DNS.Port < 1024 {
canUpdate, err = aghnet.CanBindPrivilegedPorts() canUpdate, err = aghnet.CanBindPrivilegedPorts()
if err != nil { if err != nil {
return fmt.Errorf("checking ability to bind privileged ports: %w", err) return fmt.Errorf("checking ability to bind privileged ports: %w", err)

View File

@@ -473,12 +473,12 @@ func applyAdditionalFiltering(clientIP net.IP, clientID string, setts *filtering
if c.UseOwnBlockedServices { if c.UseOwnBlockedServices {
// TODO(e.burkov): Get rid of this crutch. // TODO(e.burkov): Get rid of this crutch.
setts.ServicesRules = nil svcs := c.BlockedServices
svcs := c.BlockedServices.IDs if svcs == nil {
if !c.BlockedServices.Schedule.Contains(time.Now()) { svcs = []string{}
Context.filters.ApplyBlockedServicesList(setts, svcs)
log.Debug("%s: services for client %q set: %s", pref, c.Name, svcs)
} }
Context.filters.ApplyBlockedServicesList(setts, svcs)
log.Debug("%s: services for client %q set: %s", pref, c.Name, svcs)
} }
setts.ClientName = c.Name setts.ClientName = c.Name

View File

@@ -1,176 +0,0 @@
package home
import (
"net"
"testing"
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/AdGuardHome/internal/schedule"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestApplyAdditionalFiltering(t *testing.T) {
var err error
Context.filters, err = filtering.New(&filtering.Config{
BlockedServices: &filtering.BlockedServices{
Schedule: schedule.EmptyWeekly(),
},
}, nil)
require.NoError(t, err)
Context.clients.idIndex = map[string]*Client{
"default": {
UseOwnSettings: false,
safeSearchConf: filtering.SafeSearchConfig{Enabled: false},
FilteringEnabled: false,
SafeBrowsingEnabled: false,
ParentalEnabled: false,
},
"custom_filtering": {
UseOwnSettings: true,
safeSearchConf: filtering.SafeSearchConfig{Enabled: true},
FilteringEnabled: true,
SafeBrowsingEnabled: true,
ParentalEnabled: true,
},
"partial_custom_filtering": {
UseOwnSettings: true,
safeSearchConf: filtering.SafeSearchConfig{Enabled: true},
FilteringEnabled: true,
SafeBrowsingEnabled: false,
ParentalEnabled: false,
},
}
testCases := []struct {
name string
id string
FilteringEnabled assert.BoolAssertionFunc
SafeSearchEnabled assert.BoolAssertionFunc
SafeBrowsingEnabled assert.BoolAssertionFunc
ParentalEnabled assert.BoolAssertionFunc
}{{
name: "global_settings",
id: "default",
FilteringEnabled: assert.False,
SafeSearchEnabled: assert.False,
SafeBrowsingEnabled: assert.False,
ParentalEnabled: assert.False,
}, {
name: "custom_settings",
id: "custom_filtering",
FilteringEnabled: assert.True,
SafeSearchEnabled: assert.True,
SafeBrowsingEnabled: assert.True,
ParentalEnabled: assert.True,
}, {
name: "partial",
id: "partial_custom_filtering",
FilteringEnabled: assert.True,
SafeSearchEnabled: assert.True,
SafeBrowsingEnabled: assert.False,
ParentalEnabled: assert.False,
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
setts := &filtering.Settings{}
applyAdditionalFiltering(net.IP{1, 2, 3, 4}, tc.id, setts)
tc.FilteringEnabled(t, setts.FilteringEnabled)
tc.SafeSearchEnabled(t, setts.SafeSearchEnabled)
tc.SafeBrowsingEnabled(t, setts.SafeBrowsingEnabled)
tc.ParentalEnabled(t, setts.ParentalEnabled)
})
}
}
func TestApplyAdditionalFiltering_blockedServices(t *testing.T) {
filtering.InitModule()
var (
globalBlockedServices = []string{"ok"}
clientBlockedServices = []string{"ok", "mail_ru", "vk"}
invalidBlockedServices = []string{"invalid"}
err error
)
Context.filters, err = filtering.New(&filtering.Config{
BlockedServices: &filtering.BlockedServices{
Schedule: schedule.EmptyWeekly(),
IDs: globalBlockedServices,
},
}, nil)
require.NoError(t, err)
Context.clients.idIndex = map[string]*Client{
"default": {
UseOwnBlockedServices: false,
},
"no_services": {
BlockedServices: &filtering.BlockedServices{
Schedule: schedule.EmptyWeekly(),
},
UseOwnBlockedServices: true,
},
"services": {
BlockedServices: &filtering.BlockedServices{
Schedule: schedule.EmptyWeekly(),
IDs: clientBlockedServices,
},
UseOwnBlockedServices: true,
},
"invalid_services": {
BlockedServices: &filtering.BlockedServices{
Schedule: schedule.EmptyWeekly(),
IDs: invalidBlockedServices,
},
UseOwnBlockedServices: true,
},
"allow_all": {
BlockedServices: &filtering.BlockedServices{
Schedule: schedule.FullWeekly(),
IDs: clientBlockedServices,
},
UseOwnBlockedServices: true,
},
}
testCases := []struct {
name string
id string
wantLen int
}{{
name: "global_settings",
id: "default",
wantLen: len(globalBlockedServices),
}, {
name: "custom_settings",
id: "no_services",
wantLen: 0,
}, {
name: "custom_settings_block",
id: "services",
wantLen: len(clientBlockedServices),
}, {
name: "custom_settings_invalid",
id: "invalid_services",
wantLen: 0,
}, {
name: "custom_settings_inactive_schedule",
id: "allow_all",
wantLen: 0,
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
setts := &filtering.Settings{}
applyAdditionalFiltering(net.IP{1, 2, 3, 4}, tc.id, setts)
require.Len(t, setts.ServicesRules, tc.wantLen)
})
}
}

View File

@@ -355,43 +355,21 @@ func initContextClients() (err error) {
arpdb = aghnet.NewARPDB() arpdb = aghnet.NewARPDB()
} }
err = Context.clients.Init( Context.clients.Init(
config.Clients.Persistent, config.Clients.Persistent,
Context.dhcpServer, Context.dhcpServer,
Context.etcHosts, Context.etcHosts,
arpdb, arpdb,
config.DNS.DnsfilterConf, config.DNS.DnsfilterConf,
) )
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return err
}
return nil return nil
} }
// setupBindOpts overrides bind host/port from the opts. // setupBindOpts overrides bind host/port from the opts.
func setupBindOpts(opts options) (err error) { func setupBindOpts(opts options) (err error) {
bindAddr := opts.bindAddr
if bindAddr != (netip.AddrPort{}) {
config.HTTPConfig.Address = bindAddr
if config.HTTPConfig.Address.Port() != 0 {
err = checkPorts()
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return err
}
}
return nil
}
if opts.bindPort != 0 { if opts.bindPort != 0 {
config.HTTPConfig.Address = netip.AddrPortFrom( config.BindPort = opts.bindPort
config.HTTPConfig.Address.Addr(),
uint16(opts.bindPort),
)
err = checkPorts() err = checkPorts()
if err != nil { if err != nil {
@@ -401,10 +379,7 @@ func setupBindOpts(opts options) (err error) {
} }
if opts.bindHost.IsValid() { if opts.bindHost.IsValid() {
config.HTTPConfig.Address = netip.AddrPortFrom( config.BindHost = opts.bindHost
opts.bindHost,
config.HTTPConfig.Address.Port(),
)
} }
return nil return nil
@@ -488,7 +463,7 @@ func setupDNSFilteringConf(conf *filtering.Config) (err error) {
// checkPorts is a helper for ports validation in config. // checkPorts is a helper for ports validation in config.
func checkPorts() (err error) { func checkPorts() (err error) {
tcpPorts := aghalg.UniqChecker[tcpPort]{} tcpPorts := aghalg.UniqChecker[tcpPort]{}
addPorts(tcpPorts, tcpPort(config.HTTPConfig.Address.Port())) addPorts(tcpPorts, tcpPort(config.BindPort))
udpPorts := aghalg.UniqChecker[udpPort]{} udpPorts := aghalg.UniqChecker[udpPort]{}
addPorts(udpPorts, udpPort(config.DNS.Port)) addPorts(udpPorts, udpPort(config.DNS.Port))
@@ -528,8 +503,8 @@ func initWeb(opts options, clientBuildFS fs.FS) (web *webAPI, err error) {
webConf := webConfig{ webConf := webConfig{
firstRun: Context.firstRun, firstRun: Context.firstRun,
BindHost: config.HTTPConfig.Address.Addr(), BindHost: config.BindHost,
BindPort: int(config.HTTPConfig.Address.Port()), BindPort: config.BindPort,
ReadTimeout: readTimeout, ReadTimeout: readTimeout,
ReadHeaderTimeout: readHdrTimeout, ReadHeaderTimeout: readHdrTimeout,
@@ -665,8 +640,8 @@ func initUsers() (auth *Auth, err error) {
log.Info("authratelimiter is disabled") log.Info("authratelimiter is disabled")
} }
sessionTTL := config.HTTPConfig.SessionTTL.Seconds() sessionTTL := config.WebSessionTTLHours * 60 * 60
auth = InitAuth(sessFilename, config.Users, uint32(sessionTTL), rateLimiter) auth = InitAuth(sessFilename, config.Users, sessionTTL, rateLimiter)
if auth == nil { if auth == nil {
return nil, errors.Error("initializing auth module failed") return nil, errors.Error("initializing auth module failed")
} }
@@ -944,7 +919,7 @@ func printHTTPAddresses(proto string) {
Context.tls.WriteDiskConfig(&tlsConf) Context.tls.WriteDiskConfig(&tlsConf)
} }
port := int(config.HTTPConfig.Address.Port()) port := config.BindPort
if proto == aghhttp.SchemeHTTPS { if proto == aghhttp.SchemeHTTPS {
port = tlsConf.PortHTTPS port = tlsConf.PortHTTPS
} }
@@ -956,9 +931,9 @@ func printHTTPAddresses(proto string) {
return return
} }
bindHost := config.HTTPConfig.Address.Addr() bindhost := config.BindHost
if !bindHost.IsUnspecified() { if !bindhost.IsUnspecified() {
printWebAddrs(proto, bindHost.String(), port) printWebAddrs(proto, bindhost.String(), port)
return return
} }
@@ -969,14 +944,14 @@ func printHTTPAddresses(proto string) {
// That's weird, but we'll ignore it. // That's weird, but we'll ignore it.
// //
// TODO(e.burkov): Find out when it happens. // TODO(e.burkov): Find out when it happens.
printWebAddrs(proto, bindHost.String(), port) printWebAddrs(proto, bindhost.String(), port)
return return
} }
for _, iface := range ifaces { for _, iface := range ifaces {
for _, addr := range iface.Addresses { for _, addr := range iface.Addresses {
printWebAddrs(proto, addr.String(), port) printWebAddrs(proto, addr.String(), config.BindPort)
} }
} }
} }

View File

@@ -3,13 +3,13 @@ package home
import ( import (
"io" "io"
"net/http" "net/http"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/aghio" "github.com/AdguardTeam/AdGuardHome/internal/aghio"
"github.com/AdguardTeam/golibs/log" "github.com/AdguardTeam/golibs/log"
) )
// middlerware is a wrapper function signature. // middleware is a wrapper function signature.
type middleware func(http.Handler) http.Handler type middleware func(http.Handler) http.Handler
// withMiddlewares consequently wraps h with all the middlewares. // withMiddlewares consequently wraps h with all the middlewares.
@@ -75,3 +75,48 @@ func limitRequestBody(h http.Handler) (limited http.Handler) {
h.ServeHTTP(w, rr) h.ServeHTTP(w, rr)
}) })
} }
const (
// defaultWriteTimeout is the maximum duration before timing out writes of
// the response.
defaultWriteTimeout = 60 * time.Second
// longerWriteTimeout is the maximum duration before timing out for APIs
// expecting longer response requests.
longerWriteTimeout = 5 * time.Minute
)
// expectsLongTimeoutRequests shows if this request should use a bigger write
// timeout value. These are exceptions for poorly designed current APIs as
// well as APIs that are designed to expect large files and requests. Remove
// once the new, better APIs are up.
//
// TODO(d.kolyshev): This could be achieved with [http.NewResponseController]
// with go v1.20.
func expectsLongTimeoutRequests(r *http.Request) (ok bool) {
if r.Method != http.MethodGet {
return false
}
return r.URL.Path == "/control/querylog/export"
}
// addWriteTimeout wraps underlying handler h, adding a response write timeout.
func addWriteTimeout(h http.Handler) (limited http.Handler) {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var handler http.Handler
if expectsLongTimeoutRequests(r) {
handler = http.TimeoutHandler(h, longerWriteTimeout, "write timeout exceeded")
} else {
handler = http.TimeoutHandler(h, defaultWriteTimeout, "write timeout exceeded")
}
handler.ServeHTTP(w, r)
})
}
// limitHandler wraps underlying handler h with default limits, such as request
// body limit and write timeout.
func limitHandler(h http.Handler) (limited http.Handler) {
return limitRequestBody(addWriteTimeout(h))
}

View File

@@ -35,18 +35,11 @@ type options struct {
serviceControlAction string serviceControlAction string
// bindHost is the address on which to serve the HTTP UI. // bindHost is the address on which to serve the HTTP UI.
//
// Deprecated: Use bindAddr.
bindHost netip.Addr bindHost netip.Addr
// bindPort is the port on which to serve the HTTP UI. // bindPort is the port on which to serve the HTTP UI.
//
// Deprecated: Use bindAddr.
bindPort int bindPort int
// bindAddr is the address to serve the web UI on.
bindAddr netip.AddrPort
// checkConfig is true if the current invocation is only required to check // checkConfig is true if the current invocation is only required to check
// the configuration file and exit. // the configuration file and exit.
checkConfig bool checkConfig bool
@@ -154,10 +147,9 @@ var cmdLineOpts = []cmdLineOpt{{
return o.bindHost.String(), true return o.bindHost.String(), true
}, },
description: "Deprecated. Host address to bind HTTP server on. Use --web-addr. " + description: "Host address to bind HTTP server on.",
"The short -h will work as --help in the future.", longName: "host",
longName: "host", shortName: "h",
shortName: "h",
}, { }, {
updateWithValue: func(o options, v string) (options, error) { updateWithValue: func(o options, v string) (options, error) {
var err error var err error
@@ -182,23 +174,9 @@ var cmdLineOpts = []cmdLineOpt{{
return strconv.Itoa(o.bindPort), true return strconv.Itoa(o.bindPort), true
}, },
description: "Deprecated. Port to serve HTTP pages on. Use --web-addr.", description: "Port to serve HTTP pages on.",
longName: "port", longName: "port",
shortName: "p", shortName: "p",
}, {
updateWithValue: func(o options, v string) (oo options, err error) {
o.bindAddr, err = netip.ParseAddrPort(v)
return o, err
},
updateNoValue: nil,
effect: nil,
serialize: func(o options) (val string, ok bool) {
return o.bindAddr.String(), o.bindAddr.IsValid()
},
description: "Address to serve the web UI on, in the host:port format.",
longName: "web-addr",
shortName: "",
}, { }, {
updateWithValue: func(o options, v string) (options, error) { updateWithValue: func(o options, v string) (options, error) {
o.serviceControlAction = v o.serviceControlAction = v

View File

@@ -82,23 +82,6 @@ func TestParseBindPort(t *testing.T) {
testParseErr(t, "port too high", "-p", "18446744073709551617") // 2^64 + 1 testParseErr(t, "port too high", "-p", "18446744073709551617") // 2^64 + 1
} }
func TestParseBindAddr(t *testing.T) {
wantAddrPort := netip.MustParseAddrPort("1.2.3.4:8089")
assert.Zero(t, testParseOK(t).bindAddr, "empty is not web-addr")
assert.Equal(t, wantAddrPort, testParseOK(t, "--web-addr", "1.2.3.4:8089").bindAddr)
assert.Equal(t, netip.MustParseAddrPort("1.2.3.4:0"), testParseOK(t, "--web-addr", "1.2.3.4:0").bindAddr)
testParseParamMissing(t, "-web-addr")
testParseErr(t, "not an int", "--web-addr", "1.2.3.4:x")
testParseErr(t, "hex not supported", "--web-addr", "1.2.3.4:0x100")
testParseErr(t, "port negative", "--web-addr", "1.2.3.4:-1")
testParseErr(t, "port too high", "--web-addr", "1.2.3.4:65536")
testParseErr(t, "port too high", "--web-addr", "1.2.3.4:4294967297") // 2^32 + 1
testParseErr(t, "port too high", "--web-addr", "1.2.3.4:18446744073709551617") // 2^64 + 1
}
func TestParseLogfile(t *testing.T) { func TestParseLogfile(t *testing.T) {
assert.Equal(t, "", testParseOK(t).logFile, "empty is no log file") assert.Equal(t, "", testParseOK(t).logFile, "empty is no log file")
assert.Equal(t, "path", testParseOK(t, "-l", "path").logFile, "-l is log file") assert.Equal(t, "path", testParseOK(t, "-l", "path").logFile, "-l is log file")
@@ -179,10 +162,6 @@ func TestOptsToArgs(t *testing.T) {
name: "bind_port", name: "bind_port",
args: []string{"-p", "666"}, args: []string{"-p", "666"},
opts: options{bindPort: 666}, opts: options{bindPort: 666},
}, {
name: "web-addr",
args: []string{"--web-addr", "1.2.3.4:8080"},
opts: options{bindAddr: netip.MustParseAddrPort("1.2.3.4:8080")},
}, { }, {
name: "log_file", name: "log_file",
args: []string{"-l", "path"}, args: []string{"-l", "path"},

View File

@@ -228,7 +228,12 @@ func TestRDNS_WorkerLoop(t *testing.T) {
for _, tc := range testCases { for _, tc := range testCases {
w.Reset() w.Reset()
cc := newClientsContainer(t) cc := &clientsContainer{
list: map[string]*Client{},
idIndex: map[string]*Client{},
ipToRC: map[netip.Addr]*RuntimeClient{},
allTags: stringutil.NewSet(),
}
ch := make(chan netip.Addr) ch := make(chan netip.Addr)
rdns := &RDNS{ rdns := &RDNS{
exchanger: &rDNSExchanger{ exchanger: &rDNSExchanger{

View File

@@ -320,7 +320,7 @@ func (m *tlsManager) handleTLSValidate(w http.ResponseWriter, r *http.Request) {
if setts.Enabled { if setts.Enabled {
err = validatePorts( err = validatePorts(
tcpPort(config.HTTPConfig.Address.Port()), tcpPort(config.BindPort),
tcpPort(setts.PortHTTPS), tcpPort(setts.PortHTTPS),
tcpPort(setts.PortDNSOverTLS), tcpPort(setts.PortDNSOverTLS),
tcpPort(setts.PortDNSCrypt), tcpPort(setts.PortDNSCrypt),
@@ -407,7 +407,7 @@ func (m *tlsManager) handleTLSConfigure(w http.ResponseWriter, r *http.Request)
if req.Enabled { if req.Enabled {
err = validatePorts( err = validatePorts(
tcpPort(config.HTTPConfig.Address.Port()), tcpPort(config.BindPort),
tcpPort(req.PortHTTPS), tcpPort(req.PortHTTPS),
tcpPort(req.PortDNSOverTLS), tcpPort(req.PortDNSOverTLS),
tcpPort(req.PortDNSCrypt), tcpPort(req.PortDNSCrypt),

View File

@@ -3,7 +3,6 @@ package home
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"net/netip"
"net/url" "net/url"
"os" "os"
"path" "path"
@@ -23,7 +22,7 @@ import (
) )
// currentSchemaVersion is the current schema version. // currentSchemaVersion is the current schema version.
const currentSchemaVersion = 23 const currentSchemaVersion = 21
// These aliases are provided for convenience. // These aliases are provided for convenience.
type ( type (
@@ -96,8 +95,6 @@ func upgradeConfigSchema(oldVersion int, diskConf yobj) (err error) {
upgradeSchema18to19, upgradeSchema18to19,
upgradeSchema19to20, upgradeSchema19to20,
upgradeSchema20to21, upgradeSchema20to21,
upgradeSchema21to22,
upgradeSchema22to23,
} }
n := 0 n := 0
@@ -1182,149 +1179,6 @@ func upgradeSchema20to21(diskConf yobj) (err error) {
return nil return nil
} }
// upgradeSchema21to22 performs the following changes:
//
// # BEFORE:
// 'persistent':
// - 'name': 'client_name'
// 'blocked_services':
// - 'svc_name'
//
// # AFTER:
// 'persistent':
// - 'name': 'client_name'
// 'blocked_services':
// 'ids':
// - 'svc_name'
// 'schedule':
// 'time_zone': 'Local'
func upgradeSchema21to22(diskConf yobj) (err error) {
log.Println("Upgrade yaml: 21 to 22")
diskConf["schema_version"] = 22
const field = "blocked_services"
clientsVal, ok := diskConf["clients"]
if !ok {
return nil
}
clients, ok := clientsVal.(yobj)
if !ok {
return fmt.Errorf("unexpected type of clients: %T", clientsVal)
}
persistentVal, ok := clients["persistent"]
if !ok {
return nil
}
persistent, ok := persistentVal.([]any)
if !ok {
return fmt.Errorf("unexpected type of persistent clients: %T", persistentVal)
}
for i, val := range persistent {
var c yobj
c, ok = val.(yobj)
if !ok {
return fmt.Errorf("persistent client at index %d: unexpected type %T", i, val)
}
var blockedVal any
blockedVal, ok = c[field]
if !ok {
continue
}
var services yarr
services, ok = blockedVal.(yarr)
if !ok {
return fmt.Errorf(
"persistent client at index %d: unexpected type of blocked services: %T",
i,
blockedVal,
)
}
c[field] = yobj{
"ids": services,
"schedule": yobj{
"time_zone": "Local",
},
}
}
return nil
}
// upgradeSchema22to23 performs the following changes:
//
// # BEFORE:
// 'bind_host': '1.2.3.4'
// 'bind_port': 8080
// 'web_session_ttl': 720
//
// # AFTER:
// 'http':
// 'address': '1.2.3.4:8080'
// 'session_ttl': '720h'
func upgradeSchema22to23(diskConf yobj) (err error) {
log.Printf("Upgrade yaml: 22 to 23")
diskConf["schema_version"] = 23
bindHostVal, ok := diskConf["bind_host"]
if !ok {
return nil
}
bindHost, ok := bindHostVal.(string)
if !ok {
return fmt.Errorf("unexpected type of bind_host: %T", bindHostVal)
}
bindHostAddr, err := netip.ParseAddr(bindHost)
if err != nil {
return fmt.Errorf("invalid bind_host value: %s", bindHost)
}
bindPortVal, ok := diskConf["bind_port"]
if !ok {
return nil
}
bindPort, ok := bindPortVal.(int)
if !ok {
return fmt.Errorf("unexpected type of bind_port: %T", bindPortVal)
}
sessionTTLVal, ok := diskConf["web_session_ttl"]
if !ok {
return nil
}
sessionTTL, ok := sessionTTLVal.(int)
if !ok {
return fmt.Errorf("unexpected type of web_session_ttl: %T", sessionTTLVal)
}
addr := netip.AddrPortFrom(bindHostAddr, uint16(bindPort))
if !addr.IsValid() {
return fmt.Errorf("invalid address: %s", addr)
}
diskConf["http"] = yobj{
"address": addr.String(),
"session_ttl": timeutil.Duration{Duration: time.Duration(sessionTTL) * time.Hour}.String(),
}
delete(diskConf, "bind_host")
delete(diskConf, "bind_port")
delete(diskConf, "web_session_ttl")
return nil
}
// TODO(a.garipov): Replace with log.Output when we port it to our logging // TODO(a.garipov): Replace with log.Output when we port it to our logging
// package. // package.
func funcName() string { func funcName() string {

View File

@@ -1183,126 +1183,3 @@ func TestUpgradeSchema20to21(t *testing.T) {
}) })
} }
} }
func TestUpgradeSchema21to22(t *testing.T) {
const newSchemaVer = 22
testCases := []struct {
in yobj
want yobj
name string
}{{
in: yobj{
"clients": yobj{},
},
want: yobj{
"clients": yobj{},
"schema_version": newSchemaVer,
},
name: "nothing",
}, {
in: yobj{
"clients": yobj{
"persistent": []any{yobj{"name": "localhost", "blocked_services": yarr{}}},
},
},
want: yobj{
"clients": yobj{
"persistent": []any{yobj{
"name": "localhost",
"blocked_services": yobj{
"ids": yarr{},
"schedule": yobj{
"time_zone": "Local",
},
},
}},
},
"schema_version": newSchemaVer,
},
name: "no_services",
}, {
in: yobj{
"clients": yobj{
"persistent": []any{yobj{"name": "localhost", "blocked_services": yarr{"ok"}}},
},
},
want: yobj{
"clients": yobj{
"persistent": []any{yobj{
"name": "localhost",
"blocked_services": yobj{
"ids": yarr{"ok"},
"schedule": yobj{
"time_zone": "Local",
},
},
}},
},
"schema_version": newSchemaVer,
},
name: "services",
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
err := upgradeSchema21to22(tc.in)
require.NoError(t, err)
assert.Equal(t, tc.want, tc.in)
})
}
}
func TestUpgradeSchema22to23(t *testing.T) {
const newSchemaVer = 23
testCases := []struct {
in yobj
want yobj
name string
}{{
name: "empty",
in: yobj{},
want: yobj{
"schema_version": newSchemaVer,
},
}, {
name: "ok",
in: yobj{
"bind_host": "1.2.3.4",
"bind_port": 8081,
"web_session_ttl": 720,
},
want: yobj{
"http": yobj{
"address": "1.2.3.4:8081",
"session_ttl": "720h",
},
"schema_version": newSchemaVer,
},
}, {
name: "v6_address",
in: yobj{
"bind_host": "2001:db8::1",
"bind_port": 8081,
"web_session_ttl": 720,
},
want: yobj{
"http": yobj{
"address": "[2001:db8::1]:8081",
"session_ttl": "720h",
},
"schema_version": newSchemaVer,
},
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
err := upgradeSchema22to23(tc.in)
require.NoError(t, err)
assert.Equal(t, tc.want, tc.in)
})
}
}

View File

@@ -25,11 +25,13 @@ const (
// readTimeout is the maximum duration for reading the entire request, // readTimeout is the maximum duration for reading the entire request,
// including the body. // including the body.
readTimeout = 60 * time.Second readTimeout = 60 * time.Second
// readHdrTimeout is the amount of time allowed to read request headers. // readHdrTimeout is the amount of time allowed to read request headers.
readHdrTimeout = 60 * time.Second readHdrTimeout = 60 * time.Second
// writeTimeout is the maximum duration before timing out writes of the // writeTimeout is the maximum duration before timing out writes of the
// response. // response. This limit is overwritten by [addWriteTimeout] middleware.
writeTimeout = 60 * time.Second writeTimeout = 10 * time.Minute
) )
type webConfig struct { type webConfig struct {
@@ -119,9 +121,7 @@ func webCheckPortAvailable(port int) (ok bool) {
return true return true
} }
addrPort := netip.AddrPortFrom(config.HTTPConfig.Address.Addr(), uint16(port)) return aghnet.CheckPort("tcp", netip.AddrPortFrom(config.BindHost, uint16(port))) == nil
return aghnet.CheckPort("tcp", addrPort) == nil
} }
// tlsConfigChanged updates the TLS configuration and restarts the HTTPS server // tlsConfigChanged updates the TLS configuration and restarts the HTTPS server
@@ -171,7 +171,7 @@ func (web *webAPI) start() {
errs := make(chan error, 2) errs := make(chan error, 2)
// Use an h2c handler to support unencrypted HTTP/2, e.g. for proxies. // Use an h2c handler to support unencrypted HTTP/2, e.g. for proxies.
hdlr := h2c.NewHandler(withMiddlewares(Context.mux, limitRequestBody), &http2.Server{}) hdlr := h2c.NewHandler(withMiddlewares(Context.mux, limitHandler), &http2.Server{})
// Create a new instance, because the Web is not usable after Shutdown. // Create a new instance, because the Web is not usable after Shutdown.
hostStr := web.conf.BindHost.String() hostStr := web.conf.BindHost.String()
@@ -256,7 +256,7 @@ func (web *webAPI) tlsServerLoop() {
CipherSuites: Context.tlsCipherIDs, CipherSuites: Context.tlsCipherIDs,
MinVersion: tls.VersionTLS12, MinVersion: tls.VersionTLS12,
}, },
Handler: withMiddlewares(Context.mux, limitRequestBody), Handler: withMiddlewares(Context.mux, limitHandler),
ReadTimeout: web.conf.ReadTimeout, ReadTimeout: web.conf.ReadTimeout,
ReadHeaderTimeout: web.conf.ReadHeaderTimeout, ReadHeaderTimeout: web.conf.ReadHeaderTimeout,
WriteTimeout: web.conf.WriteTimeout, WriteTimeout: web.conf.WriteTimeout,
@@ -290,7 +290,7 @@ func (web *webAPI) mustStartHTTP3(address string) {
CipherSuites: Context.tlsCipherIDs, CipherSuites: Context.tlsCipherIDs,
MinVersion: tls.VersionTLS12, MinVersion: tls.VersionTLS12,
}, },
Handler: withMiddlewares(Context.mux, limitRequestBody), Handler: withMiddlewares(Context.mux, limitHandler),
} }
log.Debug("web: starting http/3 server") log.Debug("web: starting http/3 server")

View File

@@ -1,27 +0,0 @@
# This is a file showing example configuration for AdGuard Home.
#
# TODO(a.garipov): Move to the top level once the rewrite is over.
dns:
addresses:
- '0.0.0.0:53'
bootstrap_dns:
- '9.9.9.10'
- '149.112.112.10'
- '2620:fe::10'
- '2620:fe::fe:10'
upstream_dns:
- '8.8.8.8'
dns64_prefixes:
- '1234::/64'
upstream_timeout: 1s
bootstrap_prefer_ipv6: true
use_dns64: true
http:
addresses:
- '0.0.0.0:3000'
secure_addresses: []
timeout: 5s
force_https: true
log:
verbose: true

View File

@@ -24,7 +24,6 @@ enough.
### Fixed ### Fixed
- `--check-config` breaking the configuration file ([#4067]).
- Inconsistent application of `--work-dir/-w` ([#2598], [#2902]). - Inconsistent application of `--work-dir/-w` ([#2598], [#2902]).
- The order of `-v/--verbose` and `--version` being significant ([#2893]). - The order of `-v/--verbose` and `--version` being significant ([#2893]).
@@ -37,5 +36,4 @@ enough.
[#2598]: https://github.com/AdguardTeam/AdGuardHome/issues/2598 [#2598]: https://github.com/AdguardTeam/AdGuardHome/issues/2598
[#2893]: https://github.com/AdguardTeam/AdGuardHome/issues/2893 [#2893]: https://github.com/AdguardTeam/AdGuardHome/issues/2893
[#2902]: https://github.com/AdguardTeam/AdGuardHome/issues/2902 [#2902]: https://github.com/AdguardTeam/AdGuardHome/issues/2902
[#4067]: https://github.com/AdguardTeam/AdGuardHome/issues/4067
[#5676]: https://github.com/AdguardTeam/AdGuardHome/issues/5676 [#5676]: https://github.com/AdguardTeam/AdGuardHome/issues/5676

View File

@@ -16,9 +16,11 @@ import (
) )
// Main is the entry point of AdGuard Home. // Main is the entry point of AdGuard Home.
func Main(embeddedFrontend fs.FS) { func Main(frontend fs.FS) {
start := time.Now() start := time.Now()
// Initial Configuration
cmdName := os.Args[0] cmdName := os.Args[0]
opts, err := parseOptions(cmdName, os.Args[1:]) opts, err := parseOptions(cmdName, os.Args[1:])
exitCode, needExit := processOptions(opts, cmdName, err) exitCode, needExit := processOptions(opts, cmdName, err)
@@ -37,17 +39,9 @@ func Main(embeddedFrontend fs.FS) {
check(err) check(err)
} }
frontend, err := frontendFromOpts(opts, embeddedFrontend) // Web Service
check(err)
confMgrConf := &configmgr.Config{ confMgr, err := configmgr.New(opts.confFile, frontend, start)
Frontend: frontend,
WebAddr: opts.webAddr,
Start: start,
FileName: opts.confFile,
}
confMgr, err := newConfigMgr(confMgrConf)
check(err) check(err)
web := confMgr.Web() web := confMgr.Web()
@@ -59,8 +53,9 @@ func Main(embeddedFrontend fs.FS) {
check(err) check(err)
sigHdlr := newSignalHandler( sigHdlr := newSignalHandler(
confMgrConf, opts.confFile,
opts.pidFile, frontend,
start,
web, web,
dns, dns,
) )
@@ -78,15 +73,6 @@ func ctxWithDefaultTimeout() (ctx context.Context, cancel context.CancelFunc) {
return context.WithTimeout(context.Background(), defaultTimeout) return context.WithTimeout(context.Background(), defaultTimeout)
} }
// newConfigMgr returns a new configuration manager using defaultTimeout as the
// context timeout.
func newConfigMgr(c *configmgr.Config) (m *configmgr.Manager, err error) {
ctx, cancel := ctxWithDefaultTimeout()
defer cancel()
return configmgr.New(ctx, c)
}
// check is a simple error-checking helper. It must only be used within Main. // check is a simple error-checking helper. It must only be used within Main.
func check(err error) { func check(err error) {
if err != nil { if err != nil {

View File

@@ -1,18 +1,14 @@
package cmd package cmd
import ( import (
"encoding"
"flag" "flag"
"fmt" "fmt"
"io" "io"
"io/fs"
"net/netip" "net/netip"
"os" "os"
"strings" "strings"
"github.com/AdguardTeam/AdGuardHome/internal/next/configmgr"
"github.com/AdguardTeam/AdGuardHome/internal/version" "github.com/AdguardTeam/AdGuardHome/internal/version"
"github.com/AdguardTeam/golibs/log"
"golang.org/x/exp/slices" "golang.org/x/exp/slices"
) )
@@ -29,6 +25,8 @@ type options struct {
logFile string logFile string
// pidFile is the path to the file where to store the PID. // pidFile is the path to the file where to store the PID.
//
// TODO(a.garipov): Use.
pidFile string pidFile string
// serviceAction is the service control action to perform: // serviceAction is the service control action to perform:
@@ -51,12 +49,15 @@ type options struct {
// other configuration is read, so all relative paths are relative to it. // other configuration is read, so all relative paths are relative to it.
workDir string workDir string
// webAddr contains the address on which to serve the web UI. // webAddrs contains the addresses on which to serve the web UI.
webAddr netip.AddrPort //
// TODO(a.garipov): Use.
webAddrs []netip.AddrPort
// checkConfig, if true, instructs AdGuard Home to check the configuration // checkConfig, if true, instructs AdGuard Home to check the configuration
// file, optionally print an error message to stdout, and exit with a // file and exit with a corresponding exit code.
// corresponding exit code. //
// TODO(a.garipov): Use.
checkConfig bool checkConfig bool
// disableUpdate, if true, prevents AdGuard Home from automatically checking // disableUpdate, if true, prevents AdGuard Home from automatically checking
@@ -102,7 +103,7 @@ const (
pidFileIdx pidFileIdx
serviceActionIdx serviceActionIdx
workDirIdx workDirIdx
webAddrIdx webAddrsIdx
checkConfigIdx checkConfigIdx
disableUpdateIdx disableUpdateIdx
glinetModeIdx glinetModeIdx
@@ -128,8 +129,8 @@ type commandLineOption struct {
// AdGuard Home. // AdGuard Home.
var commandLineOptions = []*commandLineOption{ var commandLineOptions = []*commandLineOption{
confFileIdx: { confFileIdx: {
// TODO(a.garipov): Remove the directory when the new code is ready. // TODO(a.garipov): Remove the ".1" when the new code is ready.
defaultValue: "internal/next/AdGuardHome.yaml", defaultValue: "AdGuardHome.1.yaml",
description: "Path to the config file.", description: "Path to the config file.",
long: "config", long: "config",
short: "c", short: "c",
@@ -171,17 +172,18 @@ var commandLineOptions = []*commandLineOption{
valueType: "path", valueType: "path",
}, },
webAddrIdx: { webAddrsIdx: {
defaultValue: netip.AddrPort{}, defaultValue: []netip.AddrPort(nil),
description: `Address to serve the web UI on, in the host:port format.`, description: `Address(es) to serve the web UI on, in the host:port format. ` +
long: "web-addr", `Can be used multiple times.`,
short: "", long: "web-addr",
valueType: "host:port", short: "",
valueType: "host:port",
}, },
checkConfigIdx: { checkConfigIdx: {
defaultValue: false, defaultValue: false,
description: "Check configuration, print errors to stdout, and quit.", description: "Check configuration and quit.",
long: "check-config", long: "check-config",
short: "", short: "",
valueType: "", valueType: "",
@@ -256,7 +258,7 @@ func parseOptions(cmdName string, args []string) (opts *options, err error) {
pidFileIdx: &opts.pidFile, pidFileIdx: &opts.pidFile,
serviceActionIdx: &opts.serviceAction, serviceActionIdx: &opts.serviceAction,
workDirIdx: &opts.workDir, workDirIdx: &opts.workDir,
webAddrIdx: &opts.webAddr, webAddrsIdx: &opts.webAddrs,
checkConfigIdx: &opts.checkConfig, checkConfigIdx: &opts.checkConfig,
disableUpdateIdx: &opts.disableUpdate, disableUpdateIdx: &opts.disableUpdate,
glinetModeIdx: &opts.glinetMode, glinetModeIdx: &opts.glinetMode,
@@ -289,16 +291,23 @@ func addOption(flags *flag.FlagSet, fieldPtr any, o *commandLineOption) {
if o.short != "" { if o.short != "" {
flags.StringVar(fieldPtr, o.short, o.defaultValue.(string), o.description) flags.StringVar(fieldPtr, o.short, o.defaultValue.(string), o.description)
} }
case *[]netip.AddrPort:
flags.Func(o.long, o.description, func(s string) (err error) {
addr, err := netip.ParseAddrPort(s)
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return err
}
*fieldPtr = append(*fieldPtr, addr)
return nil
})
case *bool: case *bool:
flags.BoolVar(fieldPtr, o.long, o.defaultValue.(bool), o.description) flags.BoolVar(fieldPtr, o.long, o.defaultValue.(bool), o.description)
if o.short != "" { if o.short != "" {
flags.BoolVar(fieldPtr, o.short, o.defaultValue.(bool), o.description) flags.BoolVar(fieldPtr, o.short, o.defaultValue.(bool), o.description)
} }
case encoding.TextUnmarshaler:
flags.TextVar(fieldPtr, o.long, o.defaultValue.(encoding.TextMarshaler), o.description)
if o.short != "" {
flags.TextVar(fieldPtr, o.short, o.defaultValue.(encoding.TextMarshaler), o.description)
}
default: default:
panic(fmt.Errorf("unexpected field pointer type %T", fieldPtr)) panic(fmt.Errorf("unexpected field pointer type %T", fieldPtr))
} }
@@ -371,13 +380,13 @@ func processOptions(
) (exitCode int, needExit bool) { ) (exitCode int, needExit bool) {
if parseErr != nil { if parseErr != nil {
// Assume that usage has already been printed. // Assume that usage has already been printed.
return statusArgumentError, true return 2, true
} }
if opts.help { if opts.help {
usage(cmdName, os.Stdout) usage(cmdName, os.Stdout)
return statusSuccess, true return 0, true
} }
if opts.version { if opts.version {
@@ -387,32 +396,8 @@ func processOptions(
fmt.Printf("AdGuard Home %s\n", version.Version()) fmt.Printf("AdGuard Home %s\n", version.Version())
} }
return statusSuccess, true return 0, true
}
if opts.checkConfig {
err := configmgr.Validate(opts.confFile)
if err != nil {
_, _ = io.WriteString(os.Stdout, err.Error()+"\n")
return statusError, true
}
return statusSuccess, true
} }
return 0, false return 0, false
} }
// frontendFromOpts returns the frontend to use based on the options.
func frontendFromOpts(opts *options, embeddedFrontend fs.FS) (frontend fs.FS, err error) {
const frontendSubdir = "build/static"
if opts.localFrontend {
log.Info("warning: using local frontend files")
return os.DirFS(frontendSubdir), nil
}
return fs.Sub(embeddedFrontend, frontendSubdir)
}

Some files were not shown because too many files have changed in this diff Show More