Compare commits

..

6 Commits

Author SHA1 Message Date
Eugene Burkov
24a62d0638 all: imp script, code 2023-11-09 13:02:05 +03:00
Eugene Burkov
f81a94eb94 all: bump go in ci 2023-11-09 12:38:10 +03:00
Eugene Burkov
ca898fe74e dnsforward: imp code, rm wg 2023-11-09 12:26:44 +03:00
Eugene Burkov
366ec81621 dnsforward: fix options 2023-11-08 18:46:02 +03:00
Eugene Burkov
f9ee511094 dnsforward: add tests, todo 2023-11-08 18:33:40 +03:00
Eugene Burkov
deedc490e1 dnsforward: fix upstream check endpoint 2023-11-08 17:51:34 +03:00
134 changed files with 2492 additions and 3948 deletions

View File

@@ -1,8 +1,7 @@
Before submitting a PR please make sure that:
1. You have discussed your solution in an issue and have got an
approval from a maintainer. See our
[contribution guide](https://github.com/AdguardTeam/AdGuardHome/blob/master/CONTRIBUTING.md).
approval from a maintainer.
2. This isn't a localization fix; please send those to our
[CrowdIn](https://crowdin.com/project/adguard-applications/en#/adguard-home)
@@ -14,8 +13,8 @@ Before submitting a PR please make sure that:
Add a short description here. The description should include:
1. Which issue this PR closes (`Closes #NNNN.`) or updates (`Updates
#NNNN.`). Please do not open PRs without filing an issue first.
#NNNN.`).
2. A short description of how the change achieves that.
Do not forget to remove these instructions!
Do not forget to remove these instructions.

View File

@@ -1,7 +1,7 @@
'name': 'build'
'env':
'GO_VERSION': '1.20.12'
'GO_VERSION': '1.20.11'
'NODE_VERSION': '16'
'on':

View File

@@ -1,7 +1,7 @@
'name': 'lint'
'env':
'GO_VERSION': '1.20.12'
'GO_VERSION': '1.20.11'
'on':
'push':

View File

@@ -14,119 +14,46 @@ and this project adheres to
<!--
## [v0.108.0] - TBA
## [v0.107.43] - 2023-12-20 (APPROX.)
## [v0.107.41] - 2023-11-01 (APPROX.)
See also the [v0.107.43 GitHub milestone][ms-v0.107.43].
See also the [v0.107.41 GitHub milestone][ms-v0.107.41].
[ms-v0.107.43]: https://github.com/AdguardTeam/AdGuardHome/milestone/78?closed=1
[ms-v0.107.41]: https://github.com/AdguardTeam/AdGuardHome/milestone/76?closed=1
NOTE: Add new changes BELOW THIS COMMENT.
-->
<!--
NOTE: Add new changes ABOVE THIS COMMENT.
-->
## [v0.107.42] - 2023-12-07
See also the [v0.107.42 GitHub milestone][ms-v0.107.42].
### Security
- Go version has been updated to prevent the possibility of exploiting the
CVE-2023-39326, CVE-2023-45283, and CVE-2023-45285 Go vulnerabilities fixed in
[Go 1.20.12][go-1.20.12].
### Added
- Ability to set client's custom DNS cache ([#6263]).
- Ability to disable plain-DNS serving through configuration file if an
encrypted protocol is already enabled ([#1660]).
- Ability to specify rate limiting settings in the Web UI ([#6369]).
### Changed
#### Configuration changes
- The new property `dns.serve_plain_dns` has been added to the configuration
file ([#1660]).
- The property `dns.bogus_nxdomain` is now validated more strictly.
- Added new properties `clients.persistent.*.upstreams_cache_enabled` and
`clients.persistent.*.upstreams_cache_size` that describe cache configuration
for each client's custom upstream configuration.
### Fixed
- `ipset` entries family validation ([#6420]).
- Pre-filling the *New static lease* window with data ([#6402]).
- Protection pause timer synchronization ([#5759]).
[#1660]: https://github.com/AdguardTeam/AdGuardHome/issues/1660
[#5759]: https://github.com/AdguardTeam/AdGuardHome/issues/5759
[#6263]: https://github.com/AdguardTeam/AdGuardHome/issues/6263
[#6369]: https://github.com/AdguardTeam/AdGuardHome/issues/6369
[#6402]: https://github.com/AdguardTeam/AdGuardHome/issues/6402
[#6420]: https://github.com/AdguardTeam/AdGuardHome/issues/6420
[go-1.20.12]: https://groups.google.com/g/golang-announce/c/iLGK3x6yuNo/m/z6MJ-eB0AQAJ
[ms-v0.107.42]: https://github.com/AdguardTeam/AdGuardHome/milestone/77?closed=1
## [v0.107.41] - 2023-11-13
See also the [v0.107.41 GitHub milestone][ms-v0.107.41].
### Security
- Go version has been updated to prevent the possibility of exploiting the
CVE-2023-45283 and CVE-2023-45284 Go vulnerabilities fixed in
[Go 1.20.11][go-1.20.11].
### Added
- Ability to specify subnet lengths for IPv4 and IPv6 addresses, used for rate
limiting requests, in the configuration file ([#6368]).
- Ability to specify multiple domain specific upstreams per line, e.g.
`[/domain1/../domain2/]upstream1 upstream2 .. upstreamN` ([#4977]).
### Changed
- Increased the height of the ready-to-use filter lists dialog ([#6358]).
- Improved logging of authentication failures ([#6357]).
#### Configuration changes
- New properties `dns.ratelimit_subnet_len_ipv4` and
`dns.ratelimit_subnet_len_ipv6` have been added to the configuration file
([#6368]).
- The height of ready-to-use filter lists has been increased ([#6358]).
- Improved authentication failure logging ([#6357]).
### Fixed
- Schedule timezone not being sent ([#6401]).
- Average request processing time calculation ([#6220]).
- Redundant truncation of long client names in the Top Clients table ([#6338]).
- Redundant shortening long client names in the Top Clients table ([#6338]).
- Scrolling column headers in the tables ([#6337]).
- `$important,dnsrewrite` rules not overriding allowlist rules ([#6204]).
- `$important,dnsrewrite` rules do not take precedence over allowlist rules
([#6204]).
- Dark mode DNS rewrite background ([#6329]).
- Issues with QUIC and HTTP/3 upstreams on Linux ([#6335]).
[#4977]: https://github.com/AdguardTeam/AdGuardHome/issues/4977
[#6204]: https://github.com/AdguardTeam/AdGuardHome/issues/6204
[#6220]: https://github.com/AdguardTeam/AdGuardHome/issues/6220
[#6329]: https://github.com/AdguardTeam/AdGuardHome/issues/6329
[#6335]: https://github.com/AdguardTeam/AdGuardHome/issues/6335
[#6337]: https://github.com/AdguardTeam/AdGuardHome/issues/6337
[#6338]: https://github.com/AdguardTeam/AdGuardHome/issues/6338
[#6357]: https://github.com/AdguardTeam/AdGuardHome/issues/6357
[#6358]: https://github.com/AdguardTeam/AdGuardHome/issues/6358
[#6368]: https://github.com/AdguardTeam/AdGuardHome/issues/6368
[#6401]: https://github.com/AdguardTeam/AdGuardHome/issues/6401
[go-1.20.11]: https://groups.google.com/g/golang-announce/c/4tU8LZfBFkY/m/d-jSKR_jBwAJ
[ms-v0.107.41]: https://github.com/AdguardTeam/AdGuardHome/milestone/76?closed=1
<!--
NOTE: Add new changes ABOVE THIS COMMENT.
-->
@@ -257,7 +184,7 @@ See also the [v0.107.37 GitHub milestone][ms-v0.107.37].
is returned if the blocking mode isn't set to `Null IP`. In previous versions
it returned NXDOMAIN response in such cases.
#### Configuration changes
#### Configuration Changes
In this release, the schema version has changed from 24 to 27.
@@ -467,7 +394,7 @@ See also the [v0.107.34 GitHub milestone][ms-v0.107.34].
- Improved CPU and RAM consumption during updates of filtering-rule lists.
#### Configuration changes
#### Configuration Changes
In this release, the schema version has changed from 23 to 24.
@@ -547,7 +474,7 @@ See also the [v0.107.33 GitHub milestone][ms-v0.107.33].
### Changed
#### Configuration changes
#### Configuration Changes
In this release, the schema version has changed from 20 to 23.
@@ -812,7 +739,7 @@ See also the [v0.107.28 GitHub milestone][ms-v0.107.28].
- ARPA domain names containing a subnet within private networks now also
considered private, behaving closer to [RFC 6761][rfc6761] ([#5567]).
#### Configuration changes
#### Configuration Changes
In this release, the schema version has changed from 17 to 20.
@@ -952,7 +879,7 @@ See also the [v0.107.26 GitHub milestone][ms-v0.107.26].
### Changed
#### Configuration changes
#### Configuration Changes
- Property `edns_client_subnet`, which in schema versions 16 and earlier used
to be a part of the `dns` object, is now part of the `dns.edns_client_subnet`
@@ -1041,7 +968,7 @@ See also the [v0.107.24 GitHub milestone][ms-v0.107.24].
### Changed
#### Configuration changes
#### Configuration Changes
In this release, the schema version has changed from 14 to 16.
@@ -1751,7 +1678,7 @@ See also the [v0.107.7 GitHub milestone][ms-v0.107.7].
- The `dns.bogus_nxdomain` property in the configuration file now supports CIDR
notation alongside IP addresses ([#1730]).
#### Configuration changes
#### Configuration Changes
In this release, the schema version has changed from 12 to 14.
@@ -2092,7 +2019,7 @@ See also the [v0.107.0 GitHub milestone][ms-v0.107.0].
file, together with the new `group` and `user` properties ([#2763]).
- Permissions on filter files are now `0o644` instead of `0o600` ([#3198]).
#### Configuration changes
#### Configuration Changes
In this release, the schema version has changed from 10 to 12.
@@ -2658,13 +2585,11 @@ See also the [v0.104.2 GitHub milestone][ms-v0.104.2].
<!--
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.43...HEAD
[v0.107.43]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.42...v0.107.43
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.41...HEAD
[v0.107.41]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.40...v0.107.41
-->
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.42...HEAD
[v0.107.42]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.41...v0.107.42
[v0.107.41]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.40...v0.107.41
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.40...HEAD
[v0.107.40]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.39...v0.107.40
[v0.107.39]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.38...v0.107.39
[v0.107.38]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.37...v0.107.38

View File

@@ -1,89 +0,0 @@
# Contributing to AdGuard Home
If you want to contribute to AdGuard Home by filing or commenting on an issue or
opening a pull request, please follow the instructions below.
## General recommendations
Please don't:
* post comments like “+1” or “this”. Use the :+1: reaction on the issue
instead, as this allows us to actually see the level of support for issues.
* file issues about localization errors or send localization updates as PRs.
We're using [CrowdIn] to manage our translations and we generally update
them before each Beta and Release build. You can learn more about
translating AdGuard products [in our Knowledge Base][kb-trans].
* file issues about a particular filtering-rule list misbehaving. These are
tracked through the [separate form for filtering issues][form].
* send updates to filtering-rule lists, such as the ones for the Blocked
Services feature or the list of approved filtering-rule lists. We update
them once before each Beta and Release build.
Please do:
* follow the template instructions and provide data for reproducing issues.
* write the title of your issue or pull request in English. Any language is
fine in the body, but it is important to keep the title in English to make
it easier for people and bots to look up duplicated issues.
[CrowdIn]: https://crowdin.com/project/adguard-applications/en#/adguard-home
[form]: https://link.adtidy.org/forward.html?action=report&app=home&from=github
[kb-trans]: https://kb.adguard.com/en/general/adguard-translations
## Issues
### Search first
Please make sure that the issue is not a duplicate or a question. If it's a
duplicate, please react to the original issue with a thumbs up. If it's a
question, please look through our [Wiki] and, if you haven't found the answer,
post it to the GitHub [Discussions] page.
[Discussions]: https://github.com/AdguardTeam/AdGuardHome/discussions/categories/q-a
[Wiki]: https://github.com/AdguardTeam/AdGuardHome/wiki
### Follow the issue template
Developers need to be able to reproduce the faulty behavior in order to fix an
issue, so please make sure that you follow the instructions in the issue
template carefully.
## Pull requests
### Discuss your changes first
Please discuss your changes by opening an issue. The maintainers should
evaluate your proposal, and it's generally better if that's done before any code
is written.
### Review your changes for style
We have a set of [code guidelines][hacking] that we expect the code to follow.
Please make sure you follow it.
[hacking]: https://github.com/AdguardTeam/CodeGuidelines/blob/master/Go/Go.md
### Test your changes
Make sure that it passes linters and tests by running the corresponding Make
targets. For backend changes, it's `make go-check`. For frontend, run
`make js-lint`.
Additionally, a manual test is often required. While we're constantly working
on improving our test suites, they're still not as good as we'd like them to be.

View File

@@ -7,7 +7,7 @@
# Make sure to sync any changes with the branch overrides below.
'variables':
'channel': 'edge'
'dockerGo': 'adguard/golang-ubuntu:7.6'
'dockerGo': 'adguard/golang-ubuntu:7.5'
'stages':
- 'Build frontend':
@@ -272,7 +272,7 @@
# need to build a few of these.
'variables':
'channel': 'beta'
'dockerGo': 'adguard/golang-ubuntu:7.6'
'dockerGo': 'adguard/golang-ubuntu:7.4'
# release-vX.Y.Z branches are the branches from which the actual final
# release is built.
- '^release-v[0-9]+\.[0-9]+\.[0-9]+':
@@ -287,4 +287,4 @@
# are the ones that actually get released.
'variables':
'channel': 'release'
'dockerGo': 'adguard/golang-ubuntu:7.6'
'dockerGo': 'adguard/golang-ubuntu:7.4'

View File

@@ -10,7 +10,7 @@
# Make sure to sync any changes with the branch overrides below.
'variables':
'channel': 'edge'
'dockerGo': 'adguard/golang-ubuntu:7.6'
'dockerGo': 'adguard/golang-ubuntu:7.5'
'snapcraftChannel': 'edge'
'stages':
@@ -191,7 +191,7 @@
# need to build a few of these.
'variables':
'channel': 'beta'
'dockerGo': 'adguard/golang-ubuntu:7.6'
'dockerGo': 'adguard/golang-ubuntu:7.4'
'snapcraftChannel': 'beta'
# release-vX.Y.Z branches are the branches from which the actual final
# release is built.
@@ -207,5 +207,5 @@
# are the ones that actually get released.
'variables':
'channel': 'release'
'dockerGo': 'adguard/golang-ubuntu:7.6'
'dockerGo': 'adguard/golang-ubuntu:7.4'
'snapcraftChannel': 'candidate'

View File

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

View File

@@ -1,7 +1,6 @@
{
"client_settings": "Налады кліентаў",
"example_upstream_reserved": "upstream <0>для канкрэтных даменаў</0>;",
"example_multiple_upstreams_reserved": "некалькі DNS-сервераў <0>для канкрэтных даменаў</0>;",
"example_upstream_comment": "каментар.",
"upstream_parallel": "Ужыць адначасныя запыты да ўсіх сервераў для паскарэння апрацоўкі запыту",
"parallel_requests": "Паралельныя запыты",
@@ -144,8 +143,7 @@
"enforced_save_search": "Ужыты бяспечны пошук",
"number_of_dns_query_to_safe_search": "Колькасць запытаў DNS для пошукавых сістэм, для якіх быў ужыты Бяспечны пошук",
"average_processing_time": "Сярэдні час апрацоўкі запыту",
"average_upstream_response_time": "Сярэдні час водгуку upstream-сервера",
"response_time": "Час водгуку",
"processing_time": "Час апрацоўкі",
"average_processing_time_hint": "Сярэдні час для апрацоўкі запыту DNS у мілісекундах",
"block_domain_use_filters_and_hosts": "Блакаваць дамены з выкарыстаннем фільтраў і файлаў хастоў",
"filters_block_toggle_hint": "Вы можаце наладзіць правілы блакавання ў «<a>Фільтрах</a>».",
@@ -310,16 +308,6 @@
"edns_use_custom_ip": "Выкарыстоўваць указаны IP для DNS",
"edns_use_custom_ip_desc": "Дазволіць выкарыстоўваць уласны IP для DNS",
"rate_limit_desc": "Абмежаванне на колькасць запытаў у секунду для кожнага кліента (0 — неабмежавана)",
"rate_limit_subnet_len_ipv4": "Даўжыня прэфікса падсеткі для адрасоў IPv4",
"rate_limit_subnet_len_ipv4_desc": "Даўжыня прэфікса падсеткі для адрасоў IPv4, якія выкарыстоўваюцца для абмежавання хуткасці. Значэнне па змаўчанні 24",
"rate_limit_subnet_len_ipv4_error": "Даўжыня прэфікса падсеткі IPv4 павінна быць ад 0 да 32",
"rate_limit_subnet_len_ipv6": "Даўжыня прэфікса падсеткі для адрасоў IPv6",
"rate_limit_subnet_len_ipv6_desc": "Даўжыня прэфікса падсеткі для адрасоў IPv6, якія выкарыстоўваюцца для абмежавання хуткасці. Значэнне па змаўчанні 56",
"rate_limit_subnet_len_ipv6_error": "Даўжыня прэфікса падсеткі IPv6 павінна быць ад 0 да 128",
"form_enter_rate_limit_subnet_len": "Увядзіце даўжыню прэфікса падсеткі для абмежавання хуткасці",
"rate_limit_whitelist": "Белы спіс з абмежаваннем хуткасці",
"rate_limit_whitelist_desc": "IP-адрасы выключаны з абмежавання хуткасці",
"rate_limit_whitelist_placeholder": "Увядзіце па адным адрасе на радок",
"blocking_ipv4_desc": "IP-адрас, што вяртаецца пры блакаванню A-запыту",
"blocking_ipv6_desc": "IP-адрас, што вяртаецца пры блакаванню AAAA-запыту",
"blocking_mode_default": "Стандартны: Адказвае з нулёвым IP-адрасам (0.0.0.0 для A; :: для AAAA), калі заблакавана правілам у стылі Adblock; адказвае з IP-адрасам, паказаным у правіле, калі заблакавана правілам у стылі /etc/hosts-style",
@@ -734,8 +722,5 @@
"wednesday_short": "Ср.",
"thursday_short": "Чц.",
"friday_short": "Пт.",
"saturday_short": "Сб.",
"upstream_dns_cache_configuration": "Канфігурацыя кэша upstream DNS-сервераў",
"enable_upstream_dns_cache": "Ўключыць кэшаванне для карыстацкай канфігурацыі upstream-сервераў гэтага кліента",
"dns_cache_size": "Памер кэша DNS, у байтах"
"saturday_short": "Сб."
}

View File

@@ -1,7 +1,6 @@
{
"client_settings": "Nastavení klienta",
"example_upstream_reserved": "odchozí DNS připojení <0>pro konkrétní doménu(y)</0>;",
"example_multiple_upstreams_reserved": "více odchozích připojení <0>pro konkrétní domény</0>;",
"example_upstream_comment": "komentář.",
"upstream_parallel": "Použijte paralelní požadavky na urychlení řešení simultánním dotazováním na všechny navazující servery.",
"parallel_requests": "Paralelní požadavky",
@@ -144,8 +143,7 @@
"enforced_save_search": "Vynucené bezpečné vyhledávání",
"number_of_dns_query_to_safe_search": "Počet požadavků DNS na vyhledávače, při kterých bylo vynucené bezpečné vyhledávání",
"average_processing_time": "Průměrný čas zpracování",
"average_upstream_response_time": "Průměrná doba odezvy odchozích připojení",
"response_time": "Čas odezvy",
"processing_time": "Doba zpracování",
"average_processing_time_hint": "Průměrný čas zpracování požadavků DNS v milisekundách",
"block_domain_use_filters_and_hosts": "Blokovat domény pomocí filtrů a seznamů adres",
"filters_block_toggle_hint": "Pravidla blokování můžete nastavit v nastavení <a>Filtry</a>.",
@@ -310,16 +308,6 @@
"edns_use_custom_ip": "Použít vlastní IP pro EDNS",
"edns_use_custom_ip_desc": "Povolit použití vlastní IP pro EDNS",
"rate_limit_desc": "Počet požadavků za sekundu, které smí jeden klient provádět (0: neomezeno)",
"rate_limit_subnet_len_ipv4": "Délka předpony podsítě pro adresy IPv4",
"rate_limit_subnet_len_ipv4_desc": "Délka předpony podsítě pro adresy IPv4 používané pro omezení rychlosti. Výchozí hodnota je 24",
"rate_limit_subnet_len_ipv4_error": "Délka předpony podsítě IPv4 by měla být mezi 0 a 32",
"rate_limit_subnet_len_ipv6": "Délka předpony podsítě pro adresy IPv6",
"rate_limit_subnet_len_ipv6_desc": "Délka předpony podsítě pro adresy IPv6 používané pro omezení rychlosti. Výchozí hodnota je 56",
"rate_limit_subnet_len_ipv6_error": "Délka předpony podsítě IPv6 by měla být mezi 0 a 128",
"form_enter_rate_limit_subnet_len": "Zadejte délku předpony podsítě pro omezení rychlosti",
"rate_limit_whitelist": "Seznam výjimek pro omezení rychlosti",
"rate_limit_whitelist_desc": "IP adresy vyloučené z omezení rychlosti",
"rate_limit_whitelist_placeholder": "Zadejte jednu IP adresu na řádek",
"blocking_ipv4_desc": "IP adresa, která se má vrátit v případě blokovaného požadavku typu A",
"blocking_ipv6_desc": "IP adresa, která se má vrátit v případě blokovaného požadavku typu AAAA",
"blocking_mode_default": "Výchozí: Odezva s nulovou IP adresou (0.0.0.0 pro A; :: pro AAAA), pokud je blokováno pravidlem ve stylu Adblock; odezva pomocí IP adresy uvedené v pravidle, pokud je blokováno pravidlem /etc/hosts-style",
@@ -734,8 +722,5 @@
"wednesday_short": "Středa",
"thursday_short": "Čtvrtek",
"friday_short": "Pátek",
"saturday_short": "Sobota",
"upstream_dns_cache_configuration": "Konfigurace mezipaměti odchozího DNS",
"enable_upstream_dns_cache": "Povolit ukládání do mezipaměti DNS pro vlastní konfiguraci odchozího připojení tohoto klienta",
"dns_cache_size": "Velikost mezipaměti DNS v bajtech"
"saturday_short": "Sobota"
}

View File

@@ -1,7 +1,6 @@
{
"client_settings": "Klientindstillinger",
"example_upstream_reserved": "en upstream <0>for bestemte domæner</0>;",
"example_multiple_upstreams_reserved": "flere upstreams <0>til bestemte domæner</0>;",
"example_upstream_comment": "en kommentaren.",
"upstream_parallel": "Brug parallelforespørgsler til at accelerere fortolkningen ved at forespørge alle upstream-servere samtidigt.",
"parallel_requests": "Parallelle forespørgsler",
@@ -144,8 +143,7 @@
"enforced_save_search": "Håndhævet sikker søgning",
"number_of_dns_query_to_safe_search": "Antallet af DNS-forespørgsler til søgemaskiner, hvor Sikker Søgning blev håndhævet",
"average_processing_time": "Gennemsnitlig behandlingstid",
"average_upstream_response_time": "Gennemsnitlig upstream-responstid",
"response_time": "Responstid",
"processing_time": "Behandlingstid",
"average_processing_time_hint": "Gennemsnitlig behandlingstid i millisekunder af DNS-forespørgsel",
"block_domain_use_filters_and_hosts": "Blokér domæner vha. filtre og værtsfiler",
"filters_block_toggle_hint": "Du kan opsætte blokeringsregler i <a>Filterindstillingerne</a>.",
@@ -310,16 +308,6 @@
"edns_use_custom_ip": "Brug tilpasset IP til EDNS",
"edns_use_custom_ip_desc": "Tillad brug af tilpasset IP til EDNS",
"rate_limit_desc": "Antallet af forespørgsler pr. sekund tilladt pr. klient (værdien 0 = ubegrænset)",
"rate_limit_subnet_len_ipv4": "Længde på undernetpræfiks for IPv4-adresser",
"rate_limit_subnet_len_ipv4_desc": "Længde på undernetpræfiks for IPv4-adresser til hastighedsbegrænsning. Standard er 24",
"rate_limit_subnet_len_ipv4_error": "Længden på IPv4-undernetpræfiks skal være mellem 0 og 32",
"rate_limit_subnet_len_ipv6": "Længde på undernetpræfiks for IPv6-adresser",
"rate_limit_subnet_len_ipv6_desc": "Længde på undernetpræfiks for IPv6-adresser til hastighedsbegrænsning. Standard er 56",
"rate_limit_subnet_len_ipv6_error": "Længden på IPv6-undernetpræfiks skal være mellem 0 og 128",
"form_enter_rate_limit_subnet_len": "Angiv længden på undernetpræfiks til hastighedsbegrænsning",
"rate_limit_whitelist": "Hvidliste til hastighedsbegrænsning",
"rate_limit_whitelist_desc": "IP-adresser undtaget fra hastighedsbegrænsning",
"rate_limit_whitelist_placeholder": "Angiv én IP-adresse pr. linje",
"blocking_ipv4_desc": "Returneret IP-adresse for en blokeret A-forespørgsel",
"blocking_ipv6_desc": "Returneret IP-adresse for en blokeret AAAA-forespørgsel",
"blocking_mode_default": "Standard: Svar med nul IP-adresse (0.0.0.0 for A; :: for AAAA), når blokeret af Adblock-lignende regel. Svar med IP-adressen angivet i reglen, når blokeret af /etc/hosts-lignende regel",
@@ -734,8 +722,5 @@
"wednesday_short": "Ons",
"thursday_short": "Tors",
"friday_short": "Fre",
"saturday_short": "Lør",
"upstream_dns_cache_configuration": "Upstream DNS-cacheopsætning",
"enable_upstream_dns_cache": "Aktivér DNS-cachelagring for denne klients tilpassede upstream-opsætning",
"dns_cache_size": "DNS-cachestørrelse i bytes"
"saturday_short": "Lør"
}

View File

@@ -1,7 +1,6 @@
{
"client_settings": "Client-Einstellungen",
"example_upstream_reserved": "ein Upstream <0>für bestimmte Domains</0>;",
"example_multiple_upstreams_reserved": "mehrere Upstreams <0>für bestimmte Domains</0>;",
"example_upstream_comment": "ein Kommentar.",
"upstream_parallel": "Parallele Abfragen verwenden, um das Auflösen zu beschleunigen, indem alle Upstream-Server gleichzeitig abgefragt werden.",
"parallel_requests": "Paralleles Abfragen",
@@ -144,8 +143,7 @@
"enforced_save_search": "Sichere Suche erzwungen",
"number_of_dns_query_to_safe_search": "Anzahl der DNS-Anfragen bei denen Sichere Suche für Suchanfragen erzwungen wurde",
"average_processing_time": "Durchschnittliche Bearbeitungsdauer",
"average_upstream_response_time": "Durchschnittliche Upstream-Antwortzeit",
"response_time": "Antwortzeit",
"processing_time": "Verarbeitungszeit",
"average_processing_time_hint": "Durchschnittliche Zeit in Millisekunden zur Bearbeitung von DNS-Anfragen",
"block_domain_use_filters_and_hosts": "Domains durch Filter und Host-Dateien sperren",
"filters_block_toggle_hint": "Sie können Blockierregeln in den <a>Filter</a>einstellungen erstellen.",
@@ -310,16 +308,6 @@
"edns_use_custom_ip": "Benutzerdefinierte IP für EDNS verwenden",
"edns_use_custom_ip_desc": "Benutzerdefinierte IP für EDNS zulassen",
"rate_limit_desc": "Die Anzahl der Anfragen pro Sekunde, die ein einzelner Client stellen darf. Das Setzen auf 0 bedeutet keine Begrenzung.",
"rate_limit_subnet_len_ipv4": "Länge des Subnetzpräfixes für IPv4-Adressen",
"rate_limit_subnet_len_ipv4_desc": "Subnetpräfixlänge für IPv4-Adressen, die für die Ratebegrenzung verwendet werden. Der Standardwert ist 24",
"rate_limit_subnet_len_ipv4_error": "Die Subnetzpräfixlänge für IPv4-Adressen sollte zwischen 0 und 32 liegen",
"rate_limit_subnet_len_ipv6": "Subnetzpräfixlänge für IPv6-Adressen",
"rate_limit_subnet_len_ipv6_desc": "Subnetpräfixlänge für IPv6-Adressen, die für die Ratebegrenzung verwendet werden. Der Standardwert ist 56",
"rate_limit_subnet_len_ipv6_error": "Die Subnetzpräfixlänge für IPv6-Adressen sollte zwischen 0 und 128 liegen",
"form_enter_rate_limit_subnet_len": "Geben Sie die Subnetzpräfixlänge für die Ratebegrenzung ein",
"rate_limit_whitelist": "Zulassungsliste für die Ratebegrenzung",
"rate_limit_whitelist_desc": "IP-Adressen, die von der Ratebegrenzung ausgeschlossen sind",
"rate_limit_whitelist_placeholder": "Geben Sie eine IP-Adresse pro Zeile ein",
"blocking_ipv4_desc": "IP-Adresse, die für eine gesperrte A-Anfrage zurückgegeben werden soll",
"blocking_ipv6_desc": "IP-Adresse, die für eine gesperrte AAAA-Anfrage zurückgegeben werden soll",
"blocking_mode_default": "Standard: Mit Null IP Adress (0.0.0.0 for A; :: for AAAA) antworten, wenn sie durch eine Regel im Adblock-Stil gesperrt sind; mit der in der Regel angegebenen IP-Adresse antworten, wenn sie durch eine Regel im /etc/hosts-Stil gesperrt wurde",
@@ -734,8 +722,5 @@
"wednesday_short": "Mi",
"thursday_short": "Do",
"friday_short": "Fr",
"saturday_short": "Sa",
"upstream_dns_cache_configuration": "Konfiguration des Upstream-DNS-Cache",
"enable_upstream_dns_cache": "Caching für die benutzerdefinierte Upstream-Server-Konfiguration dieses Clients aktivieren",
"dns_cache_size": "Größe des DNS-Cache, in Bytes"
"saturday_short": "Sa"
}

View File

@@ -144,8 +144,7 @@
"enforced_save_search": "Enforced safe search",
"number_of_dns_query_to_safe_search": "The number of DNS requests to search engines for which Safe Search was enforced",
"average_processing_time": "Average processing time",
"average_upstream_response_time": "Average upstream response time",
"response_time": "Response time",
"processing_time": "Processing time",
"average_processing_time_hint": "Average time in milliseconds on processing a DNS request",
"block_domain_use_filters_and_hosts": "Block domains using filters and hosts files",
"filters_block_toggle_hint": "You can setup blocking rules in the <a>Filters</a> settings.",
@@ -310,16 +309,6 @@
"edns_use_custom_ip": "Use custom IP for EDNS",
"edns_use_custom_ip_desc": "Allow to use custom IP for EDNS",
"rate_limit_desc": "The number of requests per second allowed per client. Setting it to 0 means no limit.",
"rate_limit_subnet_len_ipv4": "Subnet prefix length for IPv4 addresses",
"rate_limit_subnet_len_ipv4_desc": "Subnet prefix length for IPv4 addresses used for rate limiting. The default is 24",
"rate_limit_subnet_len_ipv4_error": "The IPv4 subnet prefix length should be between 0 and 32",
"rate_limit_subnet_len_ipv6": "Subnet prefix length for IPv6 addresses",
"rate_limit_subnet_len_ipv6_desc": "Subnet prefix length for IPv6 addresses used for rate limiting. The default is 56",
"rate_limit_subnet_len_ipv6_error": "The IPv6 subnet prefix length should be between 0 and 128",
"form_enter_rate_limit_subnet_len": "Enter subnet prefix length for rate limiting",
"rate_limit_whitelist": "Rate limiting allowlist",
"rate_limit_whitelist_desc": "IP addresses excluded from rate limiting",
"rate_limit_whitelist_placeholder": "Enter one IP address per line",
"blocking_ipv4_desc": "IP address to be returned for a blocked A request",
"blocking_ipv6_desc": "IP address to be returned for a blocked AAAA request",
"blocking_mode_default": "Default: Respond with zero IP address (0.0.0.0 for A; :: for AAAA) when blocked by Adblock-style rule; respond with the IP address specified in the rule when blocked by /etc/hosts-style rule",
@@ -734,8 +723,5 @@
"wednesday_short": "Wed",
"thursday_short": "Thu",
"friday_short": "Fri",
"saturday_short": "Sat",
"upstream_dns_cache_configuration": "Upstream DNS cache configuration",
"enable_upstream_dns_cache": "Enable DNS caching for this client's custom upstream configuration",
"dns_cache_size": "DNS cache size, in bytes"
"saturday_short": "Sat"
}

View File

@@ -1,7 +1,6 @@
{
"client_settings": "Configuración de clientes",
"example_upstream_reserved": "un DNS de subida <0>para un dominio específico</0>.",
"example_multiple_upstreams_reserved": "múltiples upstreams <0>para dominios específicos</0>;",
"example_upstream_comment": "un comentario.",
"upstream_parallel": "Usar consultas paralelas para acelerar la resolución al consultar simultáneamente a todos los servidores DNS de subida.",
"parallel_requests": "Consultas paralelas",
@@ -9,9 +8,9 @@
"load_balancing_desc": "Consulta un servidor DNS de subida a la vez. AdGuard Home utiliza su algoritmo aleatorio ponderado para elegir el servidor más rápido y sea utilizado con más frecuencia.",
"bootstrap_dns": "Servidores DNS de arranque",
"bootstrap_dns_desc": "Direcciones IP de servidores DNS utilizadas para resolver direcciones IP de los solucionadores DoH/DoT que especifiques como ascendentes. No se permiten comentarios.",
"fallback_dns_title": "Servidores DNS alternativos",
"fallback_dns_desc": "Lista de servidores DNS alternativos utilizados cuando los servidores DNS de subida no responden. La sintaxis es la misma que en el campo de los principales DNS de subida anterior.",
"fallback_dns_placeholder": "Ingresa un servidor DNS alternativo por línea",
"fallback_dns_title": "Servidores DNS de fallback",
"fallback_dns_desc": "La lista de DNS de fallback serán usadas cuando los servidores de upstream de DNS no respondan. La sintaxis es la misma que en los principales del campo anterior.",
"fallback_dns_placeholder": "Ingresa un servidor de DNS alternativo por línea",
"local_ptr_title": "Servidores DNS inversos y privados",
"local_ptr_desc": "Los servidores DNS que AdGuard Home utiliza para las consultas PTR locales. Estos servidores se utilizan para resolver las peticiones PTR de direcciones en rangos de IP privadas, por ejemplo \"192.168.12.34\", utilizando DNS inverso. Si no está establecido, AdGuard Home utilizará los resolutores DNS predeterminados de tu sistema operativo, excepto las direcciones del propio AdGuard Home.",
"local_ptr_default_resolver": "Por defecto, AdGuard Home utiliza los siguientes resolutores DNS inversos: {{ip}}.",
@@ -132,8 +131,8 @@
"top_clients": "Clientes más frecuentes",
"no_clients_found": "No se han encontrado clientes",
"general_statistics": "Estadísticas generales",
"top_upstreams": "DNS de subida más frecuentes",
"no_upstreams_data_found": "No se han encontrado datos de DNS de subida",
"top_upstreams": "Mejores upstreams",
"no_upstreams_data_found": "No se han encontrado datos de upstreams",
"number_of_dns_query_days": "Número de consultas DNS procesadas durante el último {{count}} día",
"number_of_dns_query_days_plural": "Número de consultas DNS procesadas durante los últimos {{count}} días",
"number_of_dns_query_hours": "Número de consultas DNS procesadas durante la última {{count}} hora",
@@ -144,8 +143,7 @@
"enforced_save_search": "Búsquedas seguras forzadas",
"number_of_dns_query_to_safe_search": "Número de peticiones DNS a los motores de búsqueda para los que se aplicó la búsqueda segura forzada",
"average_processing_time": "Tiempo promedio de procesamiento",
"average_upstream_response_time": "Tiempo promedio de respuesta upstream",
"response_time": "Tiempo de respuesta",
"processing_time": "Tiempo de procesamiento",
"average_processing_time_hint": "Tiempo promedio en milisegundos al procesar una petición DNS",
"block_domain_use_filters_and_hosts": "Bloquear dominios usando filtros y archivos hosts",
"filters_block_toggle_hint": "Puedes configurar las reglas de bloqueo en la configuración de <a>filtros</a>.",
@@ -170,7 +168,7 @@
"upstream_dns_configured_in_file": "Configurado en {{path}}",
"test_upstream_btn": "Probar DNS de subida",
"upstreams": "DNS de subida",
"upstream": "DNS de subida",
"upstream": "Upstream",
"apply_btn": "Aplicar",
"disabled_filtering_toast": "Filtrado deshabilitado",
"enabled_filtering_toast": "Filtrado habilitado",
@@ -310,16 +308,6 @@
"edns_use_custom_ip": "Usar IP personalizada para EDNS",
"edns_use_custom_ip_desc": "Permitir el uso de IP personalizadas para EDNS",
"rate_limit_desc": "Número de peticiones por segundo permitidas por cliente. Establecerlo en 0 significa que no hay límite.",
"rate_limit_subnet_len_ipv4": "Longitud del prefijo de subred para direcciones IPv4",
"rate_limit_subnet_len_ipv4_desc": "Longitud del prefijo de subred para direcciones IPv4 utilizadas para limitar la velocidad. El valor predeterminado es 24",
"rate_limit_subnet_len_ipv4_error": "La longitud del prefijo de subred IPv4 debe estar entre 0 y 32",
"rate_limit_subnet_len_ipv6": "Longitud del prefijo de subred para direcciones IPv6",
"rate_limit_subnet_len_ipv6_desc": "Longitud del prefijo de subred para direcciones IPv6 utilizadas para limitar la velocidad. El valor predeterminado es 56",
"rate_limit_subnet_len_ipv6_error": "La longitud del prefijo de subred IPv6 debe estar entre 0 y 128",
"form_enter_rate_limit_subnet_len": "Ingresa la longitud del prefijo de subred para limitar la velocidad",
"rate_limit_whitelist": "Lista de permitidos de limitación de velocidad",
"rate_limit_whitelist_desc": "Direcciones IP excluidas de la limitación de velocidad",
"rate_limit_whitelist_placeholder": "Ingresa una dirección IP por línea",
"blocking_ipv4_desc": "Dirección IP devolverá una petición A bloqueada",
"blocking_ipv6_desc": "Dirección IP devolverá una petición AAAA bloqueada",
"blocking_mode_default": "Predeterminado: Responde con dirección IP cero (0.0.0.0 para A; :: para AAAA) cuando está bloqueado por la regla de estilo Adblock; responde con la dirección IP especificada en la regla cuando está bloqueado por una regla de estilo /etc/hosts",
@@ -689,21 +677,21 @@
"disable_for_hours": "Por {{count}} hora",
"disable_for_hours_plural": "Por {{count}} horas",
"disable_until_tomorrow": "Hasta mañana",
"disable_notify_for_seconds": "Deshabilitar protección por {{count}} segundo",
"disable_notify_for_seconds_plural": "Deshabilitar protección por {{count}} segundos",
"disable_notify_for_minutes": "Deshabilitar protección por {{count}} minuto",
"disable_notify_for_minutes_plural": "Deshabilitar protección por {{count}} minutos",
"disable_notify_for_hours": "Deshabilitar protección por {{count}} hora",
"disable_notify_for_hours_plural": "Deshabilitar protección por {{count}} horas",
"disable_notify_until_tomorrow": "Deshabilitar protección hasta mañana",
"enable_protection_timer": "La protección se habilitará a las {{time}}",
"disable_notify_for_seconds": "Desactivar la protección por {{count}} segundo",
"disable_notify_for_seconds_plural": "Desactivar la protección por {{count}} segundos",
"disable_notify_for_minutes": "Desactivar la protección por {{count}} minuto",
"disable_notify_for_minutes_plural": "Desactivar la protección por {{count}} minutos",
"disable_notify_for_hours": "Desactivar la protección por {{count}} hora",
"disable_notify_for_hours_plural": "Desactivar la protección por {{count}} horas",
"disable_notify_until_tomorrow": "Desactivar la protección hasta mañana",
"enable_protection_timer": "La protección se activará en {{time}}",
"custom_retention_input": "Ingresa la retención en horas",
"custom_rotation_input": "Ingresa la rotación en horas",
"protection_section_label": "Protección",
"log_and_stats_section_label": "Registro de consultas y estadísticas",
"ignore_query_log": "Ignorar este cliente en el registro de consultas",
"ignore_statistics": "Ignorar este cliente en las estadísticas",
"schedule_services": "Pausar servicio de bloqueo",
"schedule_services": "Pausar el servicio de bloqueo",
"schedule_services_desc": "Configura el horario programado de pausa del servicio de bloqueo",
"schedule_services_desc_client": "Configurar el horario programado de pausa del bloqueo de servicio filtrado para este cliente",
"schedule_desc": "Establecer periodos de inactividad para servicios bloqueados",
@@ -713,7 +701,7 @@
"schedule_current_timezone": "Zona horaria actual: {{value}}",
"schedule_time_all_day": "Todo el dia",
"schedule_modal_description": "Este horario sustituirá cualquier horario existente para el mismo día de la semana. Cada día de la semana solo puede tener un periodo de inactividad.",
"schedule_modal_time_off": "Detener servicio de bloqueo:",
"schedule_modal_time_off": "Detener el servicio de bloqueo:",
"schedule_new": "Nuevo horario",
"schedule_edit": "Editar horario",
"schedule_save": "Guardar horario",
@@ -734,8 +722,5 @@
"wednesday_short": "Mié.",
"thursday_short": "Jue.",
"friday_short": "Vie.",
"saturday_short": "Sáb.",
"upstream_dns_cache_configuration": "Configuración de la caché DNS upstream",
"enable_upstream_dns_cache": "Habilitar el almacenamiento en caché de DNS para la configuración personalizada de este cliente",
"dns_cache_size": "Tamaño de la caché DNS, en bytes"
"saturday_short": "Sáb."
}

View File

@@ -139,6 +139,7 @@
"enforced_save_search": "جستجوی اَمن اجبار شده",
"number_of_dns_query_to_safe_search": "تعداد درخواست های DNS برای موتور جستجو که جستجوی اَمن اجبار شده",
"average_processing_time": "میانگین زمان پردازش",
"processing_time": "زمان پردازش",
"average_processing_time_hint": "زمان میانگین بر هزارم ثانیه در پردازش درخواست DNS",
"block_domain_use_filters_and_hosts": "مسدودسازی دامنه ها توسط فیلترها و فایل های میزبان",
"filters_block_toggle_hint": "میتوانید دستورات مسدودسازی را در تنظیمات <a>فیلترها</a> راه اندازی کنید.",

View File

@@ -1,7 +1,6 @@
{
"client_settings": "Päätelaiteasetukset",
"example_upstream_reserved": "ylävirta <0>tietyille verkkotunnuksille</0>;",
"example_multiple_upstreams_reserved": "useita ylävirtoja <0>tietyille verkkotunnuksille</0>;",
"example_upstream_comment": "kommentti.",
"upstream_parallel": "Käytä rinnakkaisia pyyntöjä ja nopeuta selvitystä käyttämällä kaikkia ylävirtapalvelimia samanaikaisesti.",
"parallel_requests": "Rinnakkaiset pyynnöt",
@@ -144,8 +143,7 @@
"enforced_save_search": "Turvallinen haku pakotettiin",
"number_of_dns_query_to_safe_search": "DNS-pyyntöjen määrä, joille turvallinen haku pakotettiin käyttöön",
"average_processing_time": "Keskimääräinen käsittelyaika",
"average_upstream_response_time": "Ylävirran keskimääräinen vasteaika",
"response_time": "Vasteaika",
"processing_time": "Käsittelyaika",
"average_processing_time_hint": "Keskimääräinen DNS-pyynnön käsittelyyn kulutettu aika millisekunteina",
"block_domain_use_filters_and_hosts": "Estä verkkotunnuksia suodattimilla ja hosts-tiedostoilla",
"filters_block_toggle_hint": "Voit määrittää estosääntöjä <a>suodatinasetuksissa</a>.",
@@ -310,16 +308,6 @@
"edns_use_custom_ip": "Käytä omaa IP-osoitetta EDNS:lle",
"edns_use_custom_ip_desc": "Salli oman IP-osoitteen käyttö EDNS-mekanismille.",
"rate_limit_desc": "Päätelaitteelle sallittu pyyntöjen enimmäismäärä sekunnissa. Arvo 0 tarkoittaa rajatonta.",
"rate_limit_subnet_len_ipv4": "IPv4-osoitteiden aliverkon etuliitteen pituus",
"rate_limit_subnet_len_ipv4_desc": "Aliverkon etuliitteen pituus IPv4-osoitteille, joita käytetään nopeuden rajoittamiseen. Oletusarvo on 24",
"rate_limit_subnet_len_ipv4_error": "IPv4-aliverkon etuliitteen pituuden tulee olla 032",
"rate_limit_subnet_len_ipv6": "IPv6-osoitteiden aliverkon etuliitteen pituus",
"rate_limit_subnet_len_ipv6_desc": "Aliverkon etuliitteen pituus IPv6-osoitteille, joita käytetään nopeuden rajoittamiseen. Oletusarvo on 56",
"rate_limit_subnet_len_ipv6_error": "IPv6-aliverkon etuliitteen pituuden tulee olla 0128",
"form_enter_rate_limit_subnet_len": "Anna aliverkon etuliitteen pituus nopeuden rajoittamista varten",
"rate_limit_whitelist": "Nopeutta rajoittava sallittu luettelo",
"rate_limit_whitelist_desc": "IP-osoitteet, jotka eivät kuulu nopeusrajoituksen piiriin",
"rate_limit_whitelist_placeholder": "Syötä yksi IP-osoite per rivi",
"blocking_ipv4_desc": "Estettyyn A-pyyntöön palautettava IP-osoite",
"blocking_ipv6_desc": "Estettyyn AAAA-pyyntöön palautettava IP-osoite",
"blocking_mode_default": "Oletus: Vastaa IP-nollaosoitteella (0.0.0.0 korvaa A; :: korvaa AAAA) kun estetään mainoseston säännöllä; vastaa säännön määrittämällä IP-osoitteella kun estetään /etc/hosts-tyyppisellä säännöllä",
@@ -734,8 +722,5 @@
"wednesday_short": "Ke",
"thursday_short": "To",
"friday_short": "Pe",
"saturday_short": "La",
"upstream_dns_cache_configuration": "Ylävirran DNS-välimuistin määritykset",
"enable_upstream_dns_cache": "Käytä DNS-välimuistia tämän päätelaitteen mukautetuissa ylävirtamäärityksissä",
"dns_cache_size": "DNS-välimuistin koko tavuina"
"saturday_short": "La"
}

View File

@@ -1,7 +1,6 @@
{
"client_settings": "Paramètres du client",
"example_upstream_reserved": "un amont <0>pour des domaines spécifiques</0> ;",
"example_multiple_upstreams_reserved": "plusieurs amonts <0>pour des domaines spécifiques</0> ;",
"example_upstream_comment": " un commentaire.",
"upstream_parallel": "Utilisez des requêtes parallèles pour accélérer la résolution en requêtant simultanément tous les serveurs en amont.",
"parallel_requests": "Requêtes en parallèle",
@@ -144,8 +143,7 @@
"enforced_save_search": "Recherche sécurisée forcée",
"number_of_dns_query_to_safe_search": "Le nombre de requêtes DNS faites avec la Recherche securisée",
"average_processing_time": "Temps moyen de traitement",
"average_upstream_response_time": "Temps de réponse moyen en amont",
"response_time": "Temps de réponse",
"processing_time": "Délai de traitement",
"average_processing_time_hint": "Temps moyen (en millisecondes) de traitement d'une requête DNS",
"block_domain_use_filters_and_hosts": "Bloquez les domaines à l'aide des filtres et fichiers hosts",
"filters_block_toggle_hint": "Vous pouvez configurer les règles de filtrage dans les paramètres des <a>Filtres</a>.",
@@ -310,16 +308,6 @@
"edns_use_custom_ip": "Utiliser une IP personnalisée pour EDNS",
"edns_use_custom_ip_desc": "Autoriser l'utilisation d'une adresse IP personnalisée pour EDNS",
"rate_limit_desc": "Le nombre de requêtes par seconde quun seul client est autorisé à faire. Le réglage 0 fait illimité.",
"rate_limit_subnet_len_ipv4": "Longueur du préfixe de sous-réseau pour les adresses IPv4",
"rate_limit_subnet_len_ipv4_desc": "Longueur du préfixe de sous-réseau pour les adresses IPv4 utilisé pour la limitation de vitesse. La valeur par défaut est 24",
"rate_limit_subnet_len_ipv4_error": "La longueur du préfixe du sous-réseau IPv4 doit être entre 0 et 32",
"rate_limit_subnet_len_ipv6": "Longueur du préfixe de sous-réseau pour les adresses IPv6",
"rate_limit_subnet_len_ipv6_desc": "Longueur du préfixe de sous-réseau pour les adresses IPv6 utilisé pour la limitation de débit. La valeur par défaut est 56",
"rate_limit_subnet_len_ipv6_error": "La longueur du préfixe du sous-réseau IPv6 doit être entre 0 et 128",
"form_enter_rate_limit_subnet_len": "Saisissez la longueur du préfixe de sous-réseau pour la limitation de débit",
"rate_limit_whitelist": "Liste d'autorisation de limitation de débit",
"rate_limit_whitelist_desc": "Adresses IP exclues de la limitation du débit",
"rate_limit_whitelist_placeholder": "Saisissez une adresse IP par ligne",
"blocking_ipv4_desc": "Adresse IP à renvoyer pour une demande A bloquée",
"blocking_ipv6_desc": "Adresse IP à renvoyer pour une demande AAAA bloquée",
"blocking_mode_default": "Par défaut : Répondre avec adresse IP zéro (0.0.0.0 pour A ; :: pour AAAA) lorsque bloqué par la règle de style Adblock ; répondre avec ladresse IP spécifiée dans la règle lorsque bloquée par la règle du style /etc/hosts",
@@ -734,8 +722,5 @@
"wednesday_short": "Mer.",
"thursday_short": "Jeu.",
"friday_short": "Ven.",
"saturday_short": "Sam.",
"upstream_dns_cache_configuration": "Configuration du cache DNS en amont",
"enable_upstream_dns_cache": "Activer la mise en cache pour la configuration personnalisée du serveur en amont de ce client",
"dns_cache_size": "Taille du cache DNS, en bytes"
"saturday_short": "Sam."
}

View File

@@ -1,7 +1,6 @@
{
"client_settings": "Postavke klijenta",
"example_upstream_reserved": "upstream <0>za određene domene</0>;",
"example_multiple_upstreams_reserved": "višestruke upstream poslužitelje <0>za određene domene</0>;",
"example_upstream_comment": "komentar.",
"upstream_parallel": "Koristi paralelne upite kako bi ubrzali rješavanje istovremenim ispitavanjem svih upstream poslužitelja.",
"parallel_requests": "Paralelni zahtjevi",
@@ -144,8 +143,7 @@
"enforced_save_search": "Omogućeno sigurno pretraživanje",
"number_of_dns_query_to_safe_search": "Broj DNS zahtjeva prema pretraživačima za koje je omogućeno Sigurno pretraživanje",
"average_processing_time": "Prosječno vrijeme obrade",
"average_upstream_response_time": "Prosječno vrijeme odziva upstream poslužitelja",
"response_time": "Vrijeme odziva",
"processing_time": "Vrijeme obrade",
"average_processing_time_hint": "Prosječno vrijeme u milisekundama za obradu DNS zahtjeva",
"block_domain_use_filters_and_hosts": "Blokiraj domene koristeći filtre ili hosts datoteke",
"filters_block_toggle_hint": "Pravila blokiranja možete postaviti u postavkama <a>filtara</a>.",
@@ -310,16 +308,6 @@
"edns_use_custom_ip": "Koristi prilagođeni IP za EDNS",
"edns_use_custom_ip_desc": "Dopusti korištenje prilagođenog IP-a za EDNS",
"rate_limit_desc": "Broj zahtjeva u sekundi koji su dopušteni po jednom klijentu. Postavljanje na 0 znači neograničeno.",
"rate_limit_subnet_len_ipv4": "Duljina prefiksa podmreže za IPv4 adrese",
"rate_limit_subnet_len_ipv4_desc": "Duljina prefiksa podmreže za IPv4 adrese koje se koriste za ograničavanje brzine. Zadana vrijednost je 24",
"rate_limit_subnet_len_ipv4_error": "Dužina IPv4 prefiksa podmreže trebala bi biti između 0 i 32",
"rate_limit_subnet_len_ipv6": "Duljina prefiksa podmreže za IPv6 adrese",
"rate_limit_subnet_len_ipv6_desc": "Duljina prefiksa podmreže za IPv6 adrese koje se koriste za ograničavanje brzine. Zadana vrijednost je 56",
"rate_limit_subnet_len_ipv6_error": "Dužina IPv6 prefiksa podmreže trebala bi biti između 0 i 128",
"form_enter_rate_limit_subnet_len": "Unesite duljinu prefiksa podmreže za ograničenje brzine",
"rate_limit_whitelist": "Popis dopuštenih za ograničavanje brzine",
"rate_limit_whitelist_desc": "IP adrese isključene iz ograničenja brzine",
"rate_limit_whitelist_placeholder": "Unesite jednu adresu poslužitelja po retku",
"blocking_ipv4_desc": "Povratna IP adresa za blokirane A zahtjeve",
"blocking_ipv6_desc": "Povratna IP adresa za blokirane AAAA zahtjeve",
"blocking_mode_default": "Zadano: Odgovori s nultom IP adresom (0.0.0.0 za A; :: za AAAA) kada ga blokira Adblock slično pravilo; odgovorite s IP adresom definiranom u pravilu kada je blokirano od /etc/hosts sličnog pravila",
@@ -734,8 +722,5 @@
"wednesday_short": "Sri",
"thursday_short": "Čet",
"friday_short": "Pet",
"saturday_short": "Sub",
"upstream_dns_cache_configuration": "Konfiguracija predmemoriranja upstream DNS poslužitelja",
"enable_upstream_dns_cache": "Uključite keširanje za korisničku konfiguraciju upstream servera ovog klijenta",
"dns_cache_size": "Veličina DNS predmemorije, u bajtovima"
"saturday_short": "Sub"
}

View File

@@ -1,7 +1,6 @@
{
"client_settings": "Kliens beállítások",
"example_upstream_reserved": "Megadhat egy DNS kiszolgálót <0>egy adott domainhez vagy domainekhez</0>",
"example_multiple_upstreams_reserved": "több upstream szerver <0>adott domainekhez</0>;",
"example_upstream_comment": "egy megjegyzés.",
"upstream_parallel": "Használjon párhuzamos lekéréseket a domainek feloldásának felgyorsításához az összes upstream kiszolgálóra való egyidejű lekérdezéssel.",
"parallel_requests": "Párhuzamos lekérések",
@@ -144,8 +143,7 @@
"enforced_save_search": "Kényszerített biztonságos keresés",
"number_of_dns_query_to_safe_search": "A biztonságos keresésre kényszerített DNS lekérdezések száma",
"average_processing_time": "Átlagos feldolgozási idő",
"average_upstream_response_time": "Átlagos upstream válaszidő",
"response_time": "Válaszidő",
"processing_time": "Feldolgozási idő",
"average_processing_time_hint": "A DNS lekérdezések feldolgozásához szükséges átlagos idő milliszekundumban",
"block_domain_use_filters_and_hosts": "Domainek blokkolása szűrők és hosztfájlok használatával",
"filters_block_toggle_hint": "A <a> szűrőbeállításoknál</a> megadhatja a blokkolási szabályokat.",
@@ -310,16 +308,6 @@
"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_subnet_len_ipv4": "Az IPv4-címek alhálózati előtagjának hossza",
"rate_limit_subnet_len_ipv4_desc": "A sebességkorlátozáshoz használt IPv4-címek alhálózati előtagjának hossza. Az alapértelmezett érték 24",
"rate_limit_subnet_len_ipv4_error": "Az IPv4 alhálózati előtag hosszának 0 és 32 között kell lennie",
"rate_limit_subnet_len_ipv6": "Az IPv6-címek alhálózati előtagjának hossza",
"rate_limit_subnet_len_ipv6_desc": "A sebességkorlátozáshoz használt IPv6-címek alhálózati előtagjának hossza. Az alapértelmezett érték 56",
"rate_limit_subnet_len_ipv6_error": "Az IPv6 alhálózati előtag hosszának 0 és 128 között kell lennie",
"form_enter_rate_limit_subnet_len": "Adja meg az alhálózati előtag hosszát a sebességkorlátozáshoz",
"rate_limit_whitelist": "Sebességkorlátozó engedélyezési lista",
"rate_limit_whitelist_desc": "A sebességkorlátozásból kizárt IP-címek",
"rate_limit_whitelist_placeholder": "Adjon meg egy IP-címet soronként",
"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_mode_default": "Alapértelmezés: Válaszoljon nulla IP-címmel (vagyis 0.0.0.0 az A-hoz, :: pedig az AAAA-hoz), amikor a blokkolás egy adblock-stílusú szabállyal történik; illetve válaszoljon egy, a szabály által meghatározott IP címmel, amikor a blokkolás egy /etc/hosts stílusú szabállyal történik",
@@ -734,8 +722,5 @@
"wednesday_short": "Szer",
"thursday_short": "Csüt",
"friday_short": "Pén",
"saturday_short": "Szom",
"upstream_dns_cache_configuration": "Upstream DNS gyorsítótár konfigurációja",
"enable_upstream_dns_cache": "A DNS gyorsítótárazásának engedélyezése az ügyfél egyéni upstream konfigurációjához",
"dns_cache_size": "DNS gyorsítótár mérete, bájtokban"
"saturday_short": "Szom"
}

View File

@@ -1,7 +1,6 @@
{
"client_settings": "Pengaturan klien",
"example_upstream_reserved": "upstream <0>untuk domain spesifik</0>;",
"example_multiple_upstreams_reserved": "beberapa server upstream <0>untuk domain spesifik</0>;",
"example_upstream_comment": "komentar.",
"upstream_parallel": "Gunakan kueri paralel untuk mempercepat resoluasi dengan menanyakan semua server upstream secara bersamaan",
"parallel_requests": "Permintaan paralel",
@@ -144,8 +143,7 @@
"enforced_save_search": "Paksa pencarian aman",
"number_of_dns_query_to_safe_search": "Jumlah perminataan DNS ke mesin pencari yang dipaksa Pencarian Aman",
"average_processing_time": "Rata-rata waktu pemrosesan",
"average_upstream_response_time": "Waktu respons server upstream rata-rata",
"response_time": "Waktu respons",
"processing_time": "Waktu pemrosesan",
"average_processing_time_hint": "Rata-rata waktu dalam milidetik untuk pemrosesan sebuah permintaan DNS",
"block_domain_use_filters_and_hosts": "Blokir domain menggunakan filter dan file hosts",
"filters_block_toggle_hint": "Anda dapat menyiapkan aturan pemblokiran di pengaturan <a>Penyaringan</a>.",
@@ -310,16 +308,6 @@
"edns_use_custom_ip": "Gunakan IP khusus untuk EDNS",
"edns_use_custom_ip_desc": "Izinkan untuk menggunakan IP kustom untuk EDNS",
"rate_limit_desc": "Jumlah permintaan per detik yang diperbolehkan untuk satu klien. Atur ke 0 untuk tidak terbatas.",
"rate_limit_subnet_len_ipv4": "Panjang awalan subnet untuk alamat IPv4",
"rate_limit_subnet_len_ipv4_desc": "Panjang awalan subnet untuk alamat IPv4 yang digunakan untuk pembatasan kecepatan. Standarnya adalah 24",
"rate_limit_subnet_len_ipv4_error": "Panjang awalan subnet IPv4 harus antara 0 dan 32",
"rate_limit_subnet_len_ipv6": "Panjang awalan subnet untuk alamat IPv6",
"rate_limit_subnet_len_ipv6_desc": "Panjang awalan subnet untuk alamat IPv6 yang digunakan untuk pembatasan kecepatan. Standarnya adalah 56",
"rate_limit_subnet_len_ipv6_error": "Panjang awalan subnet IPv6 harus antara 0 dan 128",
"form_enter_rate_limit_subnet_len": "Masukkan panjang awalan subnet untuk pembatasan kecepatan",
"rate_limit_whitelist": "Daftar pembatasan tarif yang diizinkan",
"rate_limit_whitelist_desc": "Alamat IP dikecualikan dari pembatasan tarif",
"rate_limit_whitelist_placeholder": "Masukkan satu alamat IP per baris",
"blocking_ipv4_desc": "Alamat IP akan dikembalikan untuk permintaan A yang diblokir",
"blocking_ipv6_desc": "Alamat IP akan dipulihkan untuk permintaan AAAA yang diblokir",
"blocking_mode_default": "Default: Tanggapi dengan alamat IP nol (0.0.0.0 untuk A; :: untuk AAAA) saat diblokir oleh aturan gaya Adblock; tanggapi dengan alamat IP yang ditentukan dalam aturan ketika diblokir oleh aturan gaya host /etc/",
@@ -734,8 +722,5 @@
"wednesday_short": "Rab",
"thursday_short": "Kam",
"friday_short": "Jum",
"saturday_short": "Sab",
"upstream_dns_cache_configuration": "Konfigurasi cache DNS upstream",
"enable_upstream_dns_cache": "Aktifkan cache DNS untuk konfigurasi upstream kustom klien ini",
"dns_cache_size": "Ukuran cache DNS, dalam byte"
"saturday_short": "Sab"
}

View File

@@ -1,7 +1,6 @@
{
"client_settings": "Impostazioni client",
"example_upstream_reserved": "un upstream <0>per specifici domini</0>;",
"example_multiple_upstreams_reserved": "upstream multipli <0>per domini specifici</0>;",
"example_upstream_comment": "un commento.",
"upstream_parallel": "Utilizza richieste parallele per accelerare la risoluzione interrogando simultaneamente tutti i server upstream.",
"parallel_requests": "Richieste parallele",
@@ -144,8 +143,7 @@
"enforced_save_search": "Ricerca sicura forzata",
"number_of_dns_query_to_safe_search": "Numero di richieste DNS dai motori di ricerca per i quali la Ricerca Sicura è stata forzata",
"average_processing_time": "Tempo di elaborazione medio",
"average_upstream_response_time": "Tempo medio di risposta upstream",
"response_time": "Tempo di risposta",
"processing_time": "Tempo di elaborazione",
"average_processing_time_hint": "Tempo medio in millisecondi per elaborare una richiesta DNS",
"block_domain_use_filters_and_hosts": "Blocca domini utilizzando filtri e file hosts",
"filters_block_toggle_hint": "Puoi impostare le regole di blocco nelle impostazioni dei <a>Filtri</a>.",
@@ -310,16 +308,6 @@
"edns_use_custom_ip": "Usa IP personalizzato per EDNS",
"edns_use_custom_ip_desc": "Consentire l'uso di un IP personalizzato per EDNS",
"rate_limit_desc": "Il numero di richieste al secondo consentite da un singolo client. Impostare questo valore a 0 rimuove le limitazioni.",
"rate_limit_subnet_len_ipv4": "Lunghezza prefisso di sottorete per indirizzi IPv4",
"rate_limit_subnet_len_ipv4_desc": "Lunghezza prefisso sottorete per indirizzi IPv4 usati per la limitazione della velocità. Valore predefinito 24",
"rate_limit_subnet_len_ipv4_error": "La lunghezza del prefisso di sottorete IPv4 deve essere compresa tra 0 e 32",
"rate_limit_subnet_len_ipv6": "Lunghezza prefisso di sottorete per indirizzi IPv6",
"rate_limit_subnet_len_ipv6_desc": "Lunghezza prefisso di sottorete per indirizzi IPv6 usati per la limitazione della velocità. Valore predefinito 56",
"rate_limit_subnet_len_ipv6_error": "La lunghezza del prefisso di sottorete IPv6 deve essere compresa tra 0 e 128",
"form_enter_rate_limit_subnet_len": "Inserisci lunghezza prefisso di sottorete per limitazione velocità",
"rate_limit_whitelist": "Lista consentita per limitazione velocità",
"rate_limit_whitelist_desc": "Indirizzi IP esclusi dalla limitazione della velocità",
"rate_limit_whitelist_placeholder": "Inserisci un indirizzo IP per riga",
"blocking_ipv4_desc": "Indirizzo IP per una richiesta DNS IPv4 bloccata",
"blocking_ipv6_desc": "Indirizzo IP restituito per una richiesta DNS IPv6 bloccata",
"blocking_mode_default": "Risponde con un indirizzo IP pari a zero (0.0.0.0 per A; :: per AAAA) quando bloccato da una regola in stile Blocca-annunci; risponde con l'indirizzo IP specificato nella regola quando bloccato da una regola in stile /etc/hosts",
@@ -734,8 +722,5 @@
"wednesday_short": "Mer",
"thursday_short": "Gio",
"friday_short": "Ven",
"saturday_short": "Sab",
"upstream_dns_cache_configuration": "Configurazione cache DNS upstream",
"enable_upstream_dns_cache": "Abilita cache DNS per la configurazione upstream personalizzata del client",
"dns_cache_size": "Dimensioni cache DNS (in byte)"
"saturday_short": "Sab"
}

View File

@@ -1,7 +1,6 @@
{
"client_settings": "クライアント設定",
"example_upstream_reserved": "<0>特定のドメイン</0>に対してDNSアップストリームを指定できます。",
"example_multiple_upstreams_reserved": "<0>特定ドメイン</0>のための複数のアップストリームサーバー;",
"example_upstream_comment": "コメントを追加できます。",
"upstream_parallel": "並列リクエストを使用する(同時にすべてのアップストリームサーバーに処理要求することで解決スピードが向上)",
"parallel_requests": "並列リクエスト",
@@ -144,8 +143,7 @@
"enforced_save_search": "強制されたセーフサーチ",
"number_of_dns_query_to_safe_search": "セーフサーチが強制適用された検索エンジンへのDNSリクエストの数",
"average_processing_time": "平均処理時間",
"average_upstream_response_time": "アップストリームの平均応答時間",
"response_time": "応答時間",
"processing_time": "処理時間",
"average_processing_time_hint": "DNSリクエストの処理にかかる平均時間ミリ秒単位",
"block_domain_use_filters_and_hosts": "フィルタとhostsファイルを使用してドメインをブロックする",
"filters_block_toggle_hint": "<a>フィルタ</a>の設定でブロックするルールを設定することができます。",
@@ -310,16 +308,6 @@
"edns_use_custom_ip": "EDNSにカスタムIPを使用する",
"edns_use_custom_ip_desc": "EDNS に対してカスタム IP の使用を許可します。",
"rate_limit_desc": "一つのクライアントに対して許可される1秒あたりのリクエスト数「0」に設定すると、制限なしになります",
"rate_limit_subnet_len_ipv4": "IPv4 アドレスのサブネットプレフィックス長",
"rate_limit_subnet_len_ipv4_desc": "rate limitingレート制限に使用される IPv4 アドレスのサブネットプレフィックス長です。デフォルト値は 24 です。",
"rate_limit_subnet_len_ipv4_error": "IPv4 サブネットプレフィックス長は0〜32の範囲内である必要があります。",
"rate_limit_subnet_len_ipv6": "IPv6 アドレスのサブネットプレフィックス長",
"rate_limit_subnet_len_ipv6_desc": "rate limitingレート制限に使用される IPv6 アドレスのサブネットプレフィックス長です。デフォルト値は 56 です。",
"rate_limit_subnet_len_ipv6_error": "IPv6 サブネットのプレフィックス長は0〜128の範囲内である必要があります。",
"form_enter_rate_limit_subnet_len": "rate limitingレート制限のためのサブネットプレフィックス長を入力してください",
"rate_limit_whitelist": "rate limitingレート制限の許可リスト",
"rate_limit_whitelist_desc": "rate limitingレート制限の対象から外すIPアドレスを指定できます。",
"rate_limit_whitelist_placeholder": "IPアドレスを1行に1つずづ入力してください。",
"blocking_ipv4_desc": "ブロックされたAリクエストに対して応答されるIPアドレス",
"blocking_ipv6_desc": "ブロックされたAAAAリクエストに対して応答されるIPアドレス",
"blocking_mode_default": "デフォルトAdblock系ルールによってブロックされると、ゼロIPアドレスAに対しては「0.0.0.0」、AAAAに対しては「::」)で応答します。/etc/hosts系ルールによってブロックされると、ルールにて指定されているIPアドレスで応答します。",
@@ -734,8 +722,5 @@
"wednesday_short": "水",
"thursday_short": "木",
"friday_short": "金",
"saturday_short": "土",
"upstream_dns_cache_configuration": "Upstream DNS cache configurationアップストリームDNSキャッシュの構成",
"enable_upstream_dns_cache": "このクライアントのカスタムアップストリーム構成に対してDNSキャッシュを有効にする",
"dns_cache_size": "DNSキャッシュサイズバイト単位"
"saturday_short": "土"
}

View File

@@ -1,7 +1,6 @@
{
"client_settings": "클라이언트 설정",
"example_upstream_reserved": "<0>특정 도메인에 대한</0> 업스트림;",
"example_multiple_upstreams_reserved": "<0>특정 도메인</0>에 대한 여러 업스트림",
"example_upstream_comment": "댓글.",
"upstream_parallel": "쿼리 처리 속도를 높이려면 모든 업스트림 서버에서 동시에 병렬 쿼리를 사용해주세요.",
"parallel_requests": "병렬 처리 요청",
@@ -144,8 +143,7 @@
"enforced_save_search": "세이프서치 강제",
"number_of_dns_query_to_safe_search": "세이프서치가 적용된 검색 엔진에 대해 DNS 요청 수",
"average_processing_time": "평균처리 시간",
"average_upstream_response_time": "평균 업스트림 응답 시간",
"response_time": "응답 시간",
"processing_time": "처리 시간",
"average_processing_time_hint": "DNS 요청 처리시 평균 시간(밀리초)",
"block_domain_use_filters_and_hosts": "필터 및 호스트 파일을 사용하여 도메인 차단",
"filters_block_toggle_hint": "차단규칙<a>필터</a>을 설정할 수 있습니다.",
@@ -310,16 +308,6 @@
"edns_use_custom_ip": "EDNS에 사용자 지정 IP 사용",
"edns_use_custom_ip_desc": "EDNS에 사용자 지정 IP 사용하도록 허용합니다.",
"rate_limit_desc": "단일 클라이언트에서 허용 가능한 초 당 요청 생성 숫자 (0: 무제한)",
"rate_limit_subnet_len_ipv4": "IPv4 주소의 서브넷 접두사 길이",
"rate_limit_subnet_len_ipv4_desc": "속도 제한에 사용되는 IPv4 주소의 서브넷 접두사 길이입니다. 기본값은 24입니다.",
"rate_limit_subnet_len_ipv4_error": "IPv4 서브넷 접두사 길이는 0에서 32 사이여야 합니다.",
"rate_limit_subnet_len_ipv6": "IPv6 주소의 서브넷 접두사 길이",
"rate_limit_subnet_len_ipv6_desc": "속도 제한에 사용되는 IPv6 주소의 서브넷 접두사 길이입니다. 기본값은 56입니다.",
"rate_limit_subnet_len_ipv6_error": "IPv6 서브넷 접두사 길이는 0에서 128 사이여야 합니다.",
"form_enter_rate_limit_subnet_len": "속도 제한을 위한 서브넷 접두사 길이를 입력하세요",
"rate_limit_whitelist": "속도 제한 허용 목록",
"rate_limit_whitelist_desc": "속도 제한에서 제외되는 IP 주소",
"rate_limit_whitelist_placeholder": "한 줄에 하나씩 IP 주소를 입력하세요.",
"blocking_ipv4_desc": "차단된 A 요청에 대해서 반환할 IP 주소",
"blocking_ipv6_desc": "차단된 AAAA 요청에 대해서 반환할 IP 주소",
"blocking_mode_default": "기본: Adblock 스타일 규칙에 의해 차단되면 제로 IP 주소(A는 0.0.0.0; AAAA는 ::)로 응답합니다; /etc/hosts 스타일 규칙에 의해 차단되면 규칙에 정의된 IP 주소로 응답합니다",
@@ -734,8 +722,5 @@
"wednesday_short": "수",
"thursday_short": "목",
"friday_short": "금",
"saturday_short": "토",
"upstream_dns_cache_configuration": "업스트림 DNS 캐시 설정",
"enable_upstream_dns_cache": "이 클라이언트의 사용자 지정 업스트림 설정에서 DNS 캐싱 사용",
"dns_cache_size": "DNS 캐시 크기(바이트)"
"saturday_short": "토"
}

View File

@@ -1,7 +1,6 @@
{
"client_settings": "Cliëntinstellingen",
"example_upstream_reserved": "een upstream <0>voor specifieke domeinen</0>;",
"example_multiple_upstreams_reserved": "meerdere upstreams <0>voor specifieke domeinen</0>;",
"example_upstream_comment": "een commentaar.",
"upstream_parallel": "Parallelle verzoeken gebruiken om te versnellen door gelijktijdig verzoeken te sturen naar alle upstream servers.",
"parallel_requests": "Parallelle verzoeken",
@@ -144,8 +143,7 @@
"enforced_save_search": "Geforceerd veilig zoeken",
"number_of_dns_query_to_safe_search": "Aantal DNS aanvragen in zoekmachines dmv geforceerd veilig zoeken",
"average_processing_time": "Gemiddelde procestijd",
"average_upstream_response_time": "Gemiddelde upstream responstijd",
"response_time": "Responsetijd",
"processing_time": "Verwerkingstijd",
"average_processing_time_hint": "Gemiddelde verwerkingstijd in milliseconden van een DNS aanvraag",
"block_domain_use_filters_and_hosts": "Domeinen blokkeren d.m.v. filters en host-bestanden",
"filters_block_toggle_hint": "Je kan blokkeringsregels toevoegen in de <a>Filters</a> instellingen.",
@@ -310,16 +308,6 @@
"edns_use_custom_ip": "Aangepast IP-adres gebruiken voor EDNS",
"edns_use_custom_ip_desc": "Toestaan om aangepast IP-adres voor EDNS te gebruiken",
"rate_limit_desc": "Het aantal verzoeken per seconde toegelaten per toestel. 0 betekent onbeperkt.",
"rate_limit_subnet_len_ipv4": "Lengte subnetvoorvoegsel voor IPv4-adressen",
"rate_limit_subnet_len_ipv4_desc": "Lengte subnetvoorvoegsel voor IPv4-adressen die worden gebruikt voor snelheidsbeperking. De standaardwaarde is 24",
"rate_limit_subnet_len_ipv4_error": "De lengte van het IPv4-subnetvoorvoegsel moet tussen 0 en 32 liggen",
"rate_limit_subnet_len_ipv6": "Lengte subnetvoorvoegsel voor IPv6-adressen",
"rate_limit_subnet_len_ipv6_desc": "Lengte subnetvoorvoegsel voor IPv6-adressen die worden gebruikt voor snelheidsbeperking. De standaardwaarde is 56",
"rate_limit_subnet_len_ipv6_error": "De lengte van het IPv6-subnetvoorvoegsel moet tussen 0 en 128 liggen",
"form_enter_rate_limit_subnet_len": "Voer de lengte van het subnetvoorvoegsel in voor snelheidsbeperking",
"rate_limit_whitelist": "Toelatingslijst voor snelheidsbeperking",
"rate_limit_whitelist_desc": "IP-adressen uitgesloten van snelheidsbeperking",
"rate_limit_whitelist_placeholder": "Voer één IP-adres per regel in",
"blocking_ipv4_desc": "IP-adres dat moet worden teruggegeven voor een geblokkeerd A-verzoek",
"blocking_ipv6_desc": "IP-adres dat moet worden teruggegeven voor een geblokkeerd A-verzoek",
"blocking_mode_default": "Standaard: Reageer met een nul IP adres (0.0.0.0 for A; :: voor AAAA) wanneer geblokkeerd door een Adblock-type regel; reageer met het IP-adres dat is opgegeven in de regel wanneer geblokkeerd door een /etc/hosts type regel",
@@ -734,8 +722,5 @@
"wednesday_short": "wo",
"thursday_short": "do",
"friday_short": "vr",
"saturday_short": "za",
"upstream_dns_cache_configuration": "Upstream DNS-cacheconfiguratie",
"enable_upstream_dns_cache": "DNS-caching inschakelen voor de aangepaste upstream-configuratie van deze client",
"dns_cache_size": "DNS-cachegrootte, in bytes"
"saturday_short": "za"
}

View File

@@ -128,6 +128,7 @@
"enforced_save_search": "Påtvungede barnevennlige søk",
"number_of_dns_query_to_safe_search": "Antall DNS-forespørsler til søkemotorer der \"Safe Search\" ble fremtvunget",
"average_processing_time": "Gjennomsnittlig behandlingstid",
"processing_time": "Behandlingstid",
"average_processing_time_hint": "Gjennomsnittstid for behandling av DNS-forespørsler i millisekunder",
"block_domain_use_filters_and_hosts": "Blokker domener ved hjelp av filtre, «hosts»-filer, og rå domener",
"filters_block_toggle_hint": "Du kan sette opp blokkeringsoppføringer i <a>Filtre</a>-innstillingene.",

View File

@@ -1,7 +1,6 @@
{
"client_settings": "Ustawienia klienta",
"example_upstream_reserved": "upstream <0>dla określonych domen</0>;",
"example_multiple_upstreams_reserved": "wiele serwerów nadrzędnych <0>dla konkretnej domeny</0>;",
"example_upstream_comment": "komentarz.",
"upstream_parallel": "Użyj zapytań równoległych, aby przyspieszyć rozwiązywanie przez jednoczesne wysyłanie zapytań do wszystkich serwerów nadrzędnych.",
"parallel_requests": "Równoległe żądania",
@@ -144,8 +143,7 @@
"enforced_save_search": "Wymuszone bezpieczne wyszukiwanie",
"number_of_dns_query_to_safe_search": "Liczba żądań DNS kierowanych do wyszukiwarek, dla których wymuszono Bezpieczne wyszukiwanie",
"average_processing_time": "Średni czas przetwarzania",
"average_upstream_response_time": "Średni czas odpowiedzi serwera nadrzędnego",
"response_time": "Czas odpowiedzi",
"processing_time": "Czas przetwarzania",
"average_processing_time_hint": "Średni czas przetwarzania żądania DNS liczony w milisekundach",
"block_domain_use_filters_and_hosts": "Zablokuj domeny za pomocą filtrów i plików host",
"filters_block_toggle_hint": "Możesz skonfigurować reguły blokowania w ustawieniach <a>Filtry</a>.",
@@ -310,16 +308,6 @@
"edns_use_custom_ip": "Użyj niestandardowego adresu IP dla EDNS",
"edns_use_custom_ip_desc": "Zezwól na użycie niestandardowego adresu IP dla EDNS",
"rate_limit_desc": "Liczba żądań na sekundę dozwolona na klienta. Ustawienie wartości 0 oznacza brak ograniczeń.",
"rate_limit_subnet_len_ipv4": "Długość maski podsieci dla adresów IPv4",
"rate_limit_subnet_len_ipv4_desc": "Długość maski podsieci dla adresów IPv4 używanych do ograniczania prędkości. Domyślnie jest to 24",
"rate_limit_subnet_len_ipv4_error": "Długość maski podsieci IPv4 powinna wynosić od 0 do 32",
"rate_limit_subnet_len_ipv6": "Długość prefiksu podsieci dla adresów IPv6",
"rate_limit_subnet_len_ipv6_desc": "Długość prefiksu podsieci dla adresów IPv6 używanych do ograniczania szybkości. Domyślnie jest to 56",
"rate_limit_subnet_len_ipv6_error": "Długość prefiksu podsieci IPv6 powinna wynosić od 0 do 128",
"form_enter_rate_limit_subnet_len": "Wprowadź długość prefiksu podsieci dla ograniczenia prędkości",
"rate_limit_whitelist": "Lista zezwoleń ograniczających prędkość",
"rate_limit_whitelist_desc": "Adresy IP wykluczone z ograniczania prędkości",
"rate_limit_whitelist_placeholder": "Wprowadź po jednym adresie IP w każdym wierszu",
"blocking_ipv4_desc": "Adres IP, który ma zostać zwrócony w przypadku zablokowanego żądania A",
"blocking_ipv6_desc": "Adres IP, który ma zostać zwrócony w przypadku zablokowanego żądania AAAA",
"blocking_mode_default": "Domyślna: Odpowiedz z zerowym adresem IP (0.0.0.0 dla A; :: dla AAAA) po zablokowaniu przez regułę Adblock; odpowiedź adresem IP wpisanym w regule, jeśli jest blokowany przez regułę w stylu /etc/hosts",
@@ -734,8 +722,5 @@
"wednesday_short": "Śro",
"thursday_short": "Czw",
"friday_short": "Pt",
"saturday_short": "Sob",
"upstream_dns_cache_configuration": "Konfiguracja pamięci podręcznej upstream serwerów DNS",
"enable_upstream_dns_cache": "Włącz pamięć podręczną dla niestandardowej konfiguracji serwera upstream tego klienta",
"dns_cache_size": "Rozmiar pamięci podręcznej DNS, w bajtach"
"saturday_short": "Sob"
}

View File

@@ -1,7 +1,6 @@
{
"client_settings": "Configurações do cliente",
"example_upstream_reserved": "um DNS primário <0>para o domínios especificos</0>;",
"example_multiple_upstreams_reserved": "múltiplos upstreams <0>para domínios específicos</0>;",
"example_upstream_comment": "um comentário.",
"upstream_parallel": "Usar consultas paralelas para acelerar a resolução consultando simultaneamente todos os servidores DNS primário",
"parallel_requests": "Solicitações paralelas",
@@ -144,8 +143,7 @@
"enforced_save_search": "Forçar pesquisa segura",
"number_of_dns_query_to_safe_search": "O número de solicitações de DNS para mecanismos de pesquisa para os quais a pesquisa segura foi aplicada",
"average_processing_time": "Tempo médio de processamento",
"average_upstream_response_time": "Tempo médio de resposta upstream",
"response_time": "Tempo de resposta",
"processing_time": "Tempo de processamento",
"average_processing_time_hint": "Tempo médio em milissegundos no processamento de uma solicitação DNS",
"block_domain_use_filters_and_hosts": "Bloquear domínios usando arquivos de filtros e hosts",
"filters_block_toggle_hint": "Você pode configurar as regras de bloqueio nas configurações de <a>Filtros</a>.",
@@ -303,23 +301,13 @@
"download_mobileconfig_dot": "BAixar .mobileconfig para DNS-sobre-TLS",
"download_mobileconfig": "Baixar arquivo de configuração",
"plain_dns": "DNS simples",
"form_enter_rate_limit": "Insira a velocidade limite",
"rate_limit": "Velocidade limite",
"form_enter_rate_limit": "Insira a taxa limite",
"rate_limit": "Taxa limite",
"edns_enable": "Ativar a sub-rede do cliente EDNS",
"edns_cs_desc": "Adicione a opção de sub-rede de cliente EDNS (ECS) às solicitações de servidor DNS primário e registre os valores enviados pelos clientes no registro de consulta.",
"edns_use_custom_ip": "Usar IP personalizado para EDNS",
"edns_use_custom_ip_desc": "Permitir o uso de IP personalizado para EDNS",
"rate_limit_desc": "O número de solicitações por segundo permitidas por cliente. Definir como 0 significa que não há limite.",
"rate_limit_subnet_len_ipv4": "Comprimento do prefixo de sub-rede para endereços IPv4",
"rate_limit_subnet_len_ipv4_desc": "Comprimento do prefixo de sub-rede para endereços IPv4 usados para limitação de velocidade. O padrão é 24",
"rate_limit_subnet_len_ipv4_error": "O comprimento do prefixo da sub-rede IPv4 deve estar entre 0 e 32",
"rate_limit_subnet_len_ipv6": "Comprimento do prefixo de sub-rede para endereços IPv6",
"rate_limit_subnet_len_ipv6_desc": "Comprimento do prefixo de sub-rede para endereços IPv6 usados para limitação de velocidade. O padrão é 56",
"rate_limit_subnet_len_ipv6_error": "O comprimento do prefixo da sub-rede IPv6 deve estar entre 0 e 128",
"form_enter_rate_limit_subnet_len": "Insira o comprimento do prefixo da sub-rede para limitação de taxa",
"rate_limit_whitelist": "Lista de permissões de limitação de velocidade",
"rate_limit_whitelist_desc": "Endereços IP excluídos da limitação de velocidade",
"rate_limit_whitelist_placeholder": "Insira um endereço IP por linha",
"blocking_ipv4_desc": "Endereço de IP a ser retornado para uma solicitação bloqueada",
"blocking_ipv6_desc": "Endereço de IP a ser retornado para uma solicitação AAAA bloqueada",
"blocking_mode_default": "Padrão: Responder com zero endereço IP (0.0.0.0 para A; :: para AAAA) quando bloqueado pela regra de estilo Adblock; responde com o endereço IP especificado na regra quando bloqueado pela regra /etc/hosts-style",
@@ -734,8 +722,5 @@
"wednesday_short": "Quar",
"thursday_short": "Qui",
"friday_short": "Sex",
"saturday_short": "Sab",
"upstream_dns_cache_configuration": "Configuração do cache de DNS upstream",
"enable_upstream_dns_cache": "Ativar o armazenamento em cache do DNS para a configuração de upstream personalizada deste cliente",
"dns_cache_size": "Tamanho do cache do DNS, em bytes"
"saturday_short": "Sab"
}

View File

@@ -1,7 +1,6 @@
{
"client_settings": "Definições do cliente",
"example_upstream_reserved": "Podes especificar o DNS primário <0>para domínio(s) especifico(s)</0>",
"example_multiple_upstreams_reserved": "múltiplos upstreams <0>para domínios específicos</0>;",
"example_upstream_comment": "um comentário.",
"upstream_parallel": "Usar consultas paralelas para acelerar a resolução consultando simultaneamente todos os servidores DNS",
"parallel_requests": "Solicitações paralelas",
@@ -144,8 +143,7 @@
"enforced_save_search": "Forçar pesquisa segura",
"number_of_dns_query_to_safe_search": "O número de solicitações de DNS para motores de busca para os quais a pesquisa segura foi aplicada",
"average_processing_time": "Tempo médio de processamento",
"average_upstream_response_time": "Tempo médio de resposta upstream",
"response_time": "Tempo de resposta",
"processing_time": "Tempo de processamento",
"average_processing_time_hint": "Tempo médio em milissegundos no processamento de uma solicitação DNS",
"block_domain_use_filters_and_hosts": "Bloquear domínios usando ficheiros de filtros e hosts",
"filters_block_toggle_hint": "Pode configurar as regras de bloqueio nas configurações de <a>Filtros</a>.",
@@ -303,23 +301,13 @@
"download_mobileconfig_dot": "Transferir .mobileconfig para DNS-sobre-TLS",
"download_mobileconfig": "Transferir ficheiro de configuração",
"plain_dns": "DNS simples",
"form_enter_rate_limit": "Insira o limite de velocidade",
"rate_limit": "Limite de velocidade",
"form_enter_rate_limit": "Insira o limite de taxa",
"rate_limit": "Limite de taxa",
"edns_enable": "Ativar a sub-rede do cliente EDNS",
"edns_cs_desc": "Adicione a opção de sub-rede de cliente EDNS (ECS) às solicitações de servidor DNS primário e registre os valores enviados pelos clientes no registo de consulta.",
"edns_use_custom_ip": "Usar IP personalizado para EDNS",
"edns_use_custom_ip_desc": "Permitir a utilização de IP personalizado para EDNS",
"rate_limit_desc": "O número de solicitações por segundo permitido por cliente. Configurando para 0 significa sem limite.",
"rate_limit_subnet_len_ipv4": "Comprimento do prefixo de sub-rede para endereços IPv4",
"rate_limit_subnet_len_ipv4_desc": "Comprimento do prefixo de sub-rede para endereços IPv4 usados para limitação de velocidade. O padrão é 24",
"rate_limit_subnet_len_ipv4_error": "O comprimento do prefixo da sub-rede IPv4 deve estar entre 0 e 32",
"rate_limit_subnet_len_ipv6": "Comprimento do prefixo de sub-rede para endereços IPv6",
"rate_limit_subnet_len_ipv6_desc": "Comprimento do prefixo de sub-rede para endereços IPv6 usados para limitação de velocidade. O padrão é 56",
"rate_limit_subnet_len_ipv6_error": "O comprimento do prefixo da sub-rede IPv6 deve situar-se entre 0 e 128",
"form_enter_rate_limit_subnet_len": "Introduza o comprimento do prefixo da sub-rede para limitação da velocidade",
"rate_limit_whitelist": "Lista de permissões de limitação de velocidade",
"rate_limit_whitelist_desc": "Endereços IP excluídos da limitação de velocidade",
"rate_limit_whitelist_placeholder": "Insira um endereço IP por linha",
"blocking_ipv4_desc": "Endereço IP a ser devolvido para uma solicitação A bloqueada",
"blocking_ipv6_desc": "Endereço IP a ser devolvido para uma solicitação AAAA bloqueada",
"blocking_mode_default": "Predefinido: Responder com zero endereço IP (0.0.0.0 para A; :: para AAAA) quando bloqueado pela regra de estilo Adblock; responde com o endereço IP especificado na regra quando bloqueado pela regra /etc/hosts-style",
@@ -734,8 +722,5 @@
"wednesday_short": "Quarta",
"thursday_short": "Quinta",
"friday_short": "Sexta",
"saturday_short": "Sábado",
"upstream_dns_cache_configuration": "Configuração da cache do DNS upstream",
"enable_upstream_dns_cache": "Ativar o armazenamento em cache do DNS para a configuração de upstream personalizada deste cliente",
"dns_cache_size": "Tamanho da cache DNS, em bytes"
"saturday_short": "Sábado"
}

View File

@@ -1,7 +1,6 @@
{
"client_settings": "Setări client",
"example_upstream_reserved": "un flux în amonte <0>pentru domenii specifice</0>;",
"example_multiple_upstreams_reserved": "mai mulți servere în amonte <0>pentru domenii specifice</0>;",
"example_upstream_comment": "un comentariu.",
"upstream_parallel": "Folosiți interogări paralele pentru a accelera rezolvarea, interogând simultan toate serverele în amonte.",
"parallel_requests": "Solicitări paralele",
@@ -144,8 +143,7 @@
"enforced_save_search": "Căutare protejată întărită",
"number_of_dns_query_to_safe_search": "Numărul de interogări DNS pe motoarele de căutare pentru care a fost impusă Căutarea Sigură",
"average_processing_time": "Timpul mediu de procesare",
"average_upstream_response_time": "Timpul mediu de răspuns al serverului în amonte",
"response_time": "Timp de răspuns",
"processing_time": "Timp de procesare",
"average_processing_time_hint": "Timp mediu în milisecunde la procesarea unei cereri DNS",
"block_domain_use_filters_and_hosts": "Blocați domenii folosind filtre și fișiere hosts",
"filters_block_toggle_hint": "Puteți configura regulile de blocare în setările <a>Filtre</a>.",
@@ -310,16 +308,6 @@
"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_subnet_len_ipv4": "Lungimea prefixului de subrețea pentru adrese IPv4",
"rate_limit_subnet_len_ipv4_desc": "Lungimea prefixului de subrețea pentru adresele IPv4 utilizate pentru limitarea ratei. Valoarea implicită este 24",
"rate_limit_subnet_len_ipv4_error": "Lungimea prefixului de subrețea IPv4 ar trebui să fie între 0 și 32",
"rate_limit_subnet_len_ipv6": "Lungimea prefixului de subrețea pentru adrese IPv6",
"rate_limit_subnet_len_ipv6_desc": "Lungimea prefixului de subrețea pentru adresele IPv6 utilizate pentru limitarea ratei. Valoarea implicită este 56",
"rate_limit_subnet_len_ipv6_error": "Lungimea prefixului de subrețea IPv6 ar trebui să fie între 0 și 128",
"form_enter_rate_limit_subnet_len": "Introduceți lungimea prefixului de subrețea pentru limitarea ratei",
"rate_limit_whitelist": "Lista permisă pentru limitarea ratei",
"rate_limit_whitelist_desc": "Adresele IP excluse de la limitarea ratei",
"rate_limit_whitelist_placeholder": "Introduceți o adresă IP per linie",
"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_mode_default": "Implicit: Răspunde cu adresa IP (0.0.0.0 for A; :: pentru AAAA) când sunt blocate de regulă tip Adblock; răspunde cu adresa IP specificată în regulă când sunt blocate de regula tip /etc/hosts",
@@ -734,8 +722,5 @@
"wednesday_short": "mi",
"thursday_short": "jo",
"friday_short": "vi",
"saturday_short": "sa",
"upstream_dns_cache_configuration": "Configurarea cache-ului DNS în amonte",
"enable_upstream_dns_cache": "Activați memoria cache DNS pentru configurația personalizată în amonte a acestui client",
"dns_cache_size": "Dimensiunea cache-ului DNS, în octeți"
"saturday_short": "sa"
}

View File

@@ -1,7 +1,6 @@
{
"client_settings": "Настройки клиентов",
"example_upstream_reserved": "DNS-сервер <0>для конкретных доменов</0>;",
"example_multiple_upstreams_reserved": "несколько DNS-серверов <0>для конкретных доменов</0>;",
"example_upstream_comment": "комментарий.",
"upstream_parallel": "Использовать параллельные запросы ко всем серверам одновременно для ускорения обработки запроса.",
"parallel_requests": "Параллельные запросы",
@@ -144,8 +143,7 @@
"enforced_save_search": "Применён безопасный поиск",
"number_of_dns_query_to_safe_search": "Количество запросов DNS для поисковых систем, для которых был применён Безопасный поиск",
"average_processing_time": "Среднее время обработки запроса",
"average_upstream_response_time": "Среднее время ответа upstream-сервера",
"response_time": "Время ответа",
"processing_time": "Время обработки",
"average_processing_time_hint": "Среднее время для обработки запроса DNS в миллисекундах",
"block_domain_use_filters_and_hosts": "Блокировать домены с использованием фильтров и файлов hosts",
"filters_block_toggle_hint": "Вы можете настроить правила блокировки в <a>«Фильтрах»</a>.",
@@ -310,16 +308,6 @@
"edns_use_custom_ip": "Использовать указанный IP для EDNS",
"edns_use_custom_ip_desc": "Разрешить использовать собственный IP для EDNS",
"rate_limit_desc": "Ограничение на количество запросов в секунду для каждого клиента (0 — неограниченно).",
"rate_limit_subnet_len_ipv4": "Длина префикса подсети для IPv4-адресов",
"rate_limit_subnet_len_ipv4_desc": "Длина префикса подсети для IPv4-адресов, используемых для ограничения скорости. По умолчанию 24",
"rate_limit_subnet_len_ipv4_error": "Длина префикса IPv4-подсетей должна составлять от 0 до 32",
"rate_limit_subnet_len_ipv6": "Длина префикса подсети для IPv6-адресов",
"rate_limit_subnet_len_ipv6_desc": "Длина префикса подсети для IPv6-адресов, используемых для ограничения скорости. По умолчанию 56",
"rate_limit_subnet_len_ipv6_error": "Длина префикса IPv6-подсетей должна составлять от 0 до 128",
"form_enter_rate_limit_subnet_len": "Введите длину префикса подсети для ограничения скорости",
"rate_limit_whitelist": "Белый список ограничения скорости",
"rate_limit_whitelist_desc": "IP-адреса, на которые не распространяется ограничение скорости",
"rate_limit_whitelist_placeholder": "Введите по одному адресу на строчку",
"blocking_ipv4_desc": "IP-адрес, возвращаемый при блокировке A-запроса",
"blocking_ipv6_desc": "IP-адрес, возвращаемый при блокировке AAAA-запроса",
"blocking_mode_default": "Стандартный: Отвечает с нулевым IP-адресом, (0.0.0.0 для A; :: для AAAA) когда заблокировано правилом в стиле Adblock; отвечает с IP-адресом, указанным в правиле, когда заблокировано правилом в стиле файлов hosts",
@@ -734,8 +722,5 @@
"wednesday_short": "Ср",
"thursday_short": "Чт",
"friday_short": "Пт",
"saturday_short": "Сб",
"upstream_dns_cache_configuration": "Конфигурация кеша upstream DNS-серверов",
"enable_upstream_dns_cache": "Включить кеширование для пользовательской конфигурации upstream-серверов этого клиента",
"dns_cache_size": "Размер DNS-кеша в байтах"
"saturday_short": "Сб"
}

View File

@@ -122,6 +122,7 @@
"enforced_save_search": "ආරක්‍ෂිත සෙවීම බලාත්මක කළ",
"number_of_dns_query_to_safe_search": "ආරක්‍ෂිත සෙවීම බලාත්මක කළ සෙවුම් යන්ත්‍ර සඳහා ව.නා.ප. ඉල්ලීම් ගණන",
"average_processing_time": "සාමාන්‍ය සැකසුම් කාලය",
"processing_time": "සැකසුම් කාලය",
"average_processing_time_hint": "ව.නා.ප. ඉල්ලීමක් සැකසීමේ සාමාන්‍ය කාලය මිලි තත්පර වලින්",
"block_domain_use_filters_and_hosts": "පෙරහන් හා සත්කාරක ගොනු භාවිතයෙන් වසම් අවහිර කරන්න",
"filters_block_toggle_hint": "ඔබට අවහිර කිරීමේ නීති <a>පෙරහන්</a> තුළ පිහිටුවිය හැකිය.",
@@ -646,20 +647,6 @@
"log_and_stats_section_label": "විමසුම් සටහන හා සංඛ්‍යාලේඛන",
"ignore_query_log": "විමසුම් සටහනට මෙම අනුග්‍රාහකය යොදන්න එපා",
"ignore_statistics": "සංඛ්‍යාලේඛනයට මෙම අනුග්‍රාහකය යොදන්න එපා",
"schedule_invalid_select": "ආරම්භක වේලාව අවසන් වේලාවට කලින් විය යුතුය",
"schedule_select_days": "දවස් තෝරන්න",
"schedule_timezone": "වේලා කලාපයක් තෝරන්න",
"schedule_current_timezone": "වත්මන් වේලා කලාපය: {{value}}",
"schedule_time_all_day": "දවස පුරාම",
"schedule_from": "සිට",
"schedule_to": "දක්වා",
"sunday": "ඉරිදා",
"monday": "සඳුදා",
"tuesday": "අඟහරුවාදා",
"wednesday": "බදාදා",
"thursday": "බ්‍රහස්පතින්දා",
"friday": "සිකුරාදා",
"saturday": "සෙනසුරාදා",
"sunday_short": "ඉරිදා",
"monday_short": "සඳුදා",
"tuesday_short": "අඟහ",

View File

@@ -1,7 +1,6 @@
{
"client_settings": "Nastavenie klienta",
"example_upstream_reserved": "upstream <0>pre konkrétne domény</0>;",
"example_multiple_upstreams_reserved": "viaceré upstreamy pre <0>konkrétne domény</0>;",
"example_upstream_comment": "komentár.",
"upstream_parallel": "Používať paralelné dopyty na zrýchlenie súčasným dopytovaním všetkých upstream serverov súčasne.",
"parallel_requests": "Paralelné dopyty",
@@ -144,8 +143,7 @@
"enforced_save_search": "Vynútené bezpečné vyhľadávanie",
"number_of_dns_query_to_safe_search": "Počet DNS dopytov na vyhľadávače, pri ktorých bolo vynútené bezpečné vyhľadávanie",
"average_processing_time": "Priemerný čas spracovania",
"average_upstream_response_time": "Priemerný čas odozvy upstreamu",
"response_time": "Čas odozvy",
"processing_time": "Doba spracovania",
"average_processing_time_hint": "Priemerný čas spracovania DNS dopytu v milisekundách",
"block_domain_use_filters_and_hosts": "Blokovať domény pomocou filtrov a zoznamov adries",
"filters_block_toggle_hint": "Pravidlá blokovania môžete nastaviť v nastaveniach <a>Filtre</a>.",
@@ -310,16 +308,6 @@
"edns_use_custom_ip": "Použiť vlastnú IP adresu pre EDNS",
"edns_use_custom_ip_desc": "Povoliť používanie vlastnej IP adresy pre EDNS",
"rate_limit_desc": "Počet požiadaviek za sekundu, ktoré môže jeden klient vykonať. Nastavenie na hodnotu 0 znamená neobmedzene.",
"rate_limit_subnet_len_ipv4": "Dĺžka prefixu podsiete pre adresy IPv4",
"rate_limit_subnet_len_ipv4_desc": "Dĺžka prefixu podsiete pre adresy IPv4 používané na obmedzenie rýchlosti. Predvolená hodnota je 24",
"rate_limit_subnet_len_ipv4_error": "Dĺžka prefixu podsiete IPv4 musí byť od 0 do 32",
"rate_limit_subnet_len_ipv6": "Dĺžka prefixu podsiete pre adresy IPv6",
"rate_limit_subnet_len_ipv6_desc": "Dĺžka prefixu podsiete pre adresy IPv6 používané na obmedzenie rýchlosti. Predvolená hodnota je 56",
"rate_limit_subnet_len_ipv6_error": "Dĺžka prefixu podsiete IPv6 musí byť od 0 do 128",
"form_enter_rate_limit_subnet_len": "Zadajte dĺžku prefixu podsiete pre obmedzenie rýchlosti",
"rate_limit_whitelist": "Zoznam povolení obmedzujúcich rýchlosť",
"rate_limit_whitelist_desc": "IP adresy vylúčené z obmedzenia rýchlosti",
"rate_limit_whitelist_placeholder": "Na každý riadok zadajte IP adresu jedného servera",
"blocking_ipv4_desc": "IP adresa, ktorá sa má vrátiť v prípade blokovanej žiadosti A",
"blocking_ipv6_desc": "IP adresa, ktorá sa má vrátiť v prípade blokovanej žiadosti AAAA",
"blocking_mode_default": "Predvolené: Odpovedať nulovou adresou IP (0,0.0.0 pre A; :: pre AAAA), keď je blokovaná pravidlom v štýle Adblock; odpovedať IP adresou uvedenou v pravidle, keď je blokovaná pravidlom v štýle /etc/hosts",
@@ -734,8 +722,5 @@
"wednesday_short": "Str",
"thursday_short": "Štr",
"friday_short": "Pia",
"saturday_short": "Sob",
"upstream_dns_cache_configuration": "Konfigurácia cache pamäte DNS pre upstream",
"enable_upstream_dns_cache": "Zapnúť ukladanie DNS do cache pamäte pre vlastnú konfiguráciu odosielania tohto klienta",
"dns_cache_size": "Veľkosť cache pamäte DNS v bajtoch"
"saturday_short": "Sob"
}

View File

@@ -1,7 +1,6 @@
{
"client_settings": "Nastavitve odjemalca",
"example_upstream_reserved": "gorvodni <0>za določene domene</0>;",
"example_multiple_upstreams_reserved": "več gorvodnih <0>za določene domene</0>;",
"example_upstream_comment": "komentar.",
"upstream_parallel": "Uporabite vzporedne zahteve za pospešitev reševanja s hkratnim poizvedovanjem vseh gorvodnih strežnikov.",
"parallel_requests": "Vzporedne zahteve",
@@ -144,8 +143,7 @@
"enforced_save_search": "Prisilno varno iskanje",
"number_of_dns_query_to_safe_search": "Število zahtev DNS za iskalnike, za katere je bilo uveljavljeno varno iskanje",
"average_processing_time": "Povprečni čas obdelave",
"average_upstream_response_time": "Povprečni gorvodni odzivni čas",
"response_time": "Odzivni čas",
"processing_time": "Čas obdelave",
"average_processing_time_hint": "Povprečni čas v milisekundah pri obdelavi zahteve DNS",
"block_domain_use_filters_and_hosts": "Onemogoči domene s filtri in gostiteljskimi datotekami",
"filters_block_toggle_hint": "Pravila zaviranja lahko nastavite v nastavitvah <a>Filtri</a>.",
@@ -310,16 +308,6 @@
"edns_use_custom_ip": "Uporabi IP po meri za EDNS",
"edns_use_custom_ip_desc": "Dovoli uporabo naslova IP po meri za EDNS",
"rate_limit_desc": "Dovoljeno število zahtev na sekundo na odjemalca. Nastavitev na 0 pomeni brez omejitve.",
"rate_limit_subnet_len_ipv4": "Dolžina predpone podomrežja za naslove IPv4",
"rate_limit_subnet_len_ipv4_desc": "Dolžina predpone podomrežja za naslove IPv4, ki se uporabljajo za omejevanje hitrosti. Privzeto je 24",
"rate_limit_subnet_len_ipv4_error": "Dolžina predpone podomrežja IPv4 mora biti med 0 in 32",
"rate_limit_subnet_len_ipv6": "Dolžina predpone podomrežja za naslove IPv4",
"rate_limit_subnet_len_ipv6_desc": "Dolžina predpone podomrežja za naslove IPv6, ki se uporabljajo za omejevanje hitrosti. Privzeta vrednost je 56",
"rate_limit_subnet_len_ipv6_error": "Dolžina podomrežne predpone IPv6 mora biti med 0 in 128",
"form_enter_rate_limit_subnet_len": "Vnesite dolžino predpone podomrežja za omejitev hitrosti",
"rate_limit_whitelist": "Seznam dovoljenih za omejevanje hitrosti",
"rate_limit_whitelist_desc": "Naslovi IP so izključeni iz omejitve hitrosti",
"rate_limit_whitelist_placeholder": "Vnesite en naslov IP na vrstico",
"blocking_ipv4_desc": "IP naslov, ki mora biti vrnjen za onemogočeno zahtevo A",
"blocking_ipv6_desc": "IP naslov, ki mora biti vrnjen za onemogočeno zahtevo AAAA",
"blocking_mode_default": "Privzeto: odgovori z ničelnim naslovom IP (0.0.0.0 za A; :: za AAAA), ko je onemogočen s pravilom v slogu Adblocka; odgovor z naslovom IP, določenim v pravilu, ko je onemogočen s pravilom /etc/hosts",
@@ -734,8 +722,5 @@
"wednesday_short": "Sre",
"thursday_short": "Čet",
"friday_short": "Pet",
"saturday_short": "Sob",
"upstream_dns_cache_configuration": "Nastavitve predpomnilnika gorvodnega DNS",
"enable_upstream_dns_cache": "Omogoči predpomnjenje nastavitev gorvodnega DNS po meri tega odjemalca",
"dns_cache_size": "Velikost predpomnilnika DNS, v bajtih"
"saturday_short": "Sob"
}

View File

@@ -1,7 +1,6 @@
{
"client_settings": "Postavke klijenta",
"example_upstream_reserved": "upstream <0>za određene domene</0>;",
"example_multiple_upstreams_reserved": "nekoliko DNS servera <0>za određene domene</0>;",
"example_upstream_comment": "komentar.",
"upstream_parallel": "Koristite paralelne upite da biste ubrzali rešavanje tako što ćete istovremeno ispitati sve uzvodne servere.",
"parallel_requests": "Paralelni zahtevi",
@@ -144,8 +143,7 @@
"enforced_save_search": "Nametni sigurno pretraživanje",
"number_of_dns_query_to_safe_search": "Broj DNS zahteva ka pretraživačima za koje je nametnuto sigurno pretraživanje",
"average_processing_time": "Prosečno vreme obrade",
"average_upstream_response_time": "Prosečno vreme odziva upstream-servera",
"response_time": "Vreme odziva",
"processing_time": "Vreme obrade",
"average_processing_time_hint": "Prosečno vreme u milisekundama za obradu DNS zahteva",
"block_domain_use_filters_and_hosts": "Blokiraj domene koristeći filtere i hosts datoteke",
"filters_block_toggle_hint": "Možete postaviti pravila blokiranja u <a>Filters</a> postavkama.",
@@ -310,16 +308,6 @@
"edns_use_custom_ip": "Koristi prilagođeni IP za EDNS",
"edns_use_custom_ip_desc": "Dozvoli korišćenje prilagođenog IP-a za EDNS",
"rate_limit_desc": "Broj zahteva u sekundi dozvoljen po klijentu. Postavljanje na 0 znači da nema ograničenja.",
"rate_limit_subnet_len_ipv4": "Dužina prefixa podmreže za IPv4 adrese",
"rate_limit_subnet_len_ipv4_desc": "Dužina prefixa podmreže za IPv4 adrese koje se koriste za ograničavanje brzine. Podrazumevano je 24",
"rate_limit_subnet_len_ipv4_error": "Dužina prefixa IPv4 podmreže treba da bude između 0 i 32",
"rate_limit_subnet_len_ipv6": "Dužina prefixa podmreže za IPv6 adrese",
"rate_limit_subnet_len_ipv6_desc": "Dužina prefixa podmreže za IPv6 adrese koje se koriste za ograničavanje brzine. Podrazumevano je 56",
"rate_limit_subnet_len_ipv6_error": "Dužina prefixa IPv6 podmreže treba da bude između 0 i 128",
"form_enter_rate_limit_subnet_len": "Unesite dužinu prefixa podmreže da biste ograničili brzinu",
"rate_limit_whitelist": "Lista dozvoljenih lista za ograničavanje brzine",
"rate_limit_whitelist_desc": "IP adrese koje nisu obuhvaćene ograničenjem brzine",
"rate_limit_whitelist_placeholder": "Unesite jednu IP adresu servera po redu",
"blocking_ipv4_desc": "IP adresa koja će biti vraćena za blokirane zahteve",
"blocking_ipv6_desc": "IP adresa koja će biti vraćena za blokirane AAAA zahteve",
"blocking_mode_default": "Podrazumevano: Odgovara sa REFUSED kada je blokirano od Adblock-style pravila; odgovara sa IP adresom koja je određena u pravilu kada je blokiran od /etc/hosts-style pravila",
@@ -734,8 +722,5 @@
"wednesday_short": "Sre",
"thursday_short": "Čet",
"friday_short": "Pet",
"saturday_short": "Sub",
"upstream_dns_cache_configuration": "Konfiguracija keša upstream DNS servera",
"enable_upstream_dns_cache": "Uključite keširanje za korisničku konfiguraciju upstream servera ovog klijenta",
"dns_cache_size": "Veličina DNS keša, u bajtovima"
"saturday_short": "Sub"
}

View File

@@ -1,8 +1,7 @@
{
"client_settings": "Klientinställningar",
"example_upstream_reserved": "uppström <0>för en specifik domän</0>;",
"example_multiple_upstreams_reserved": "flera uppströmsservrar <0>för specifika domäner</0>;",
"example_upstream_comment": "en kommentar.",
"example_upstream_comment": "du kan ange en kommentar.",
"upstream_parallel": "Använd parallella förfrågningar för att snabba upp dessa genom att fråga alla uppströmsservrar samtidigt.",
"parallel_requests": "Parallella förfrågningar",
"load_balancing": "Lastbalansering",
@@ -144,8 +143,7 @@
"enforced_save_search": "Aktivering av Säker surf",
"number_of_dns_query_to_safe_search": "Antalet DNS-förfrågningar mot sökmotorer där Säker surf tvingats",
"average_processing_time": "Genomsnittlig processtid",
"average_upstream_response_time": "Genomsnittlig svarstid uppströmsserver",
"response_time": "Svarstid",
"processing_time": "Bearbetningstid",
"average_processing_time_hint": "Genomsnittlig processtid i millisekunder för DNS-förfrågning",
"block_domain_use_filters_and_hosts": "Blockera domäner med filter- och värdfiler",
"filters_block_toggle_hint": "Du kan ställa in egna blockerings regler i <a>Filterinställningar</a>.",
@@ -310,15 +308,6 @@
"edns_use_custom_ip": "Använd anpassad IP för EDNS",
"edns_use_custom_ip_desc": "Tillåt att använda anpassad IP för EDNS",
"rate_limit_desc": "Antalet förfrågningar per sekund som tillåts per klient. Att sätta den till 0 innebär ingen gräns.",
"rate_limit_subnet_len_ipv4": "Prefixlängd för subnät för IPv4-adresser",
"rate_limit_subnet_len_ipv4_desc": "Subnätprefixlängd för IPv4-adresser som används för hastighetsbegränsning. Standard är 24",
"rate_limit_subnet_len_ipv4_error": "IPv4-subnätets prefixlängd ska vara mellan 0 och 32",
"rate_limit_subnet_len_ipv6": "Prefixlängd för subnät för IPv6-adresser",
"rate_limit_subnet_len_ipv6_desc": "Subnätprefixlängd för IPv6-adresser som används för hastighetsbegränsning. Standard är 56",
"rate_limit_subnet_len_ipv6_error": "IPv6-subnätets prefixlängd ska vara mellan 0 och 128",
"form_enter_rate_limit_subnet_len": "Ange subnätprefixlängd för hastighetsbegränsning",
"rate_limit_whitelist_desc": "IP-adresser uteslutna från hastighetsbegränsning",
"rate_limit_whitelist_placeholder": "Ange en IP-adress per rad",
"blocking_ipv4_desc": "IP adress som ska returneras för en blockerad A förfrågan",
"blocking_ipv6_desc": "IP adress som ska returneras för en blockerad AAAA förfrågan",
"blocking_mode_default": "Standard: Svara med noll IP-adress (0.0.0.0 för A; :: för AAAA) när det blockeras av regel i Adblock-stil; svara med IP-adressen som anges i regeln när den blockeras av regel i /etc/hosts-stil",
@@ -733,8 +722,5 @@
"wednesday_short": "Ons",
"thursday_short": "Tor",
"friday_short": "Fre",
"saturday_short": "Lör",
"upstream_dns_cache_configuration": "Konfiguration av uppströms DNS-cache",
"enable_upstream_dns_cache": "Aktivera DNS-cachelagring för den här klientens anpassade uppströmskonfiguration",
"dns_cache_size": "DNS-cachestorlek, i byte"
"saturday_short": "Lör"
}

View File

@@ -1,7 +1,6 @@
{
"client_settings": "İstemci ayarları",
"example_upstream_reserved": "<0>belirli alan adları</0> için bir üst sunucusu;",
"example_multiple_upstreams_reserved": "<0>belirli alanlar için</0> birden fazla üst kaynaklar;",
"example_upstream_comment": "bir yorum.",
"upstream_parallel": "Tüm üst sunucuları eş zamanlı sorgulayarak çözümlemeyi hızlandırmak için paralel sorgular kullanın.",
"parallel_requests": "Paralel istekler",
@@ -98,7 +97,7 @@
"filters": "Filtreler",
"filter": "Filtre",
"query_log": "Sorgu Günlüğü",
"compact": "Sık",
"compact": "Yoğun",
"nothing_found": "Hiçbir şey bulunamadı",
"faq": "SSS",
"version": "Sürüm",
@@ -144,8 +143,7 @@
"enforced_save_search": "Uygulanan güvenli arama",
"number_of_dns_query_to_safe_search": "Güvenli Aramanın uygulandığı arama motorlarına gönderilen DNS isteklerinin sayısı",
"average_processing_time": "Ortalama işlem süresi",
"average_upstream_response_time": "Ortalama üst kaynak yanıt süresi",
"response_time": "Yanıt süresi",
"processing_time": "İşlem süresi",
"average_processing_time_hint": "Bir DNS isteğinin milisaniye cinsinden ortalama işlem süresi",
"block_domain_use_filters_and_hosts": "Filtre ve hosts dosyalarını kullanarak alan adlarını engelle",
"filters_block_toggle_hint": "<a>Filtreler</a> ayarlarında engelleme kuralları oluşturabilirsiniz.",
@@ -182,7 +180,7 @@
"enabled_save_search_toast": "Güvenli Arama etkinleştirildi",
"updated_save_search_toast": "Güvenli Arama ayarları güncellendi",
"enabled_table_header": "Etkin",
"name_table_header": "Adı",
"name_table_header": "Ad",
"list_url_table_header": "Liste URL'si",
"rules_count_table_header": "Kural sayısı",
"last_time_updated_table_header": "Son güncelleme zamanı",
@@ -310,19 +308,9 @@
"edns_use_custom_ip": "EDNS için özel IP kullan",
"edns_use_custom_ip_desc": "EDNS için özel IP kullanımına izin ver",
"rate_limit_desc": "İstemci başına izin verilen saniyedeki istek sayısı. 0 olarak ayarlamak, sınır olmadığı anlamına gelir.",
"rate_limit_subnet_len_ipv4": "IPv4 adresleri için alt ağ önek uzunluğu",
"rate_limit_subnet_len_ipv4_desc": "Hız sınırlaması için kullanılan IPv4 adreslerinin alt ağ önek uzunluğu. Varsayılan 24'tür",
"rate_limit_subnet_len_ipv4_error": "IPv4 alt ağ önek uzunluğu 0 ile 32 arasında olmalıdır",
"rate_limit_subnet_len_ipv6": "IPv6 adresleri için alt ağ önek uzunluğu",
"rate_limit_subnet_len_ipv6_desc": "Hız sınırlaması için kullanılan IPv6 adreslerinin alt ağ önek uzunluğu. Varsayılan 56'tür",
"rate_limit_subnet_len_ipv6_error": "IPv6 alt ağ önek uzunluğu 0 ile 128 arasında olmalıdır",
"form_enter_rate_limit_subnet_len": "Hız sınırlaması için alt ağ önek uzunluğunu girin",
"rate_limit_whitelist": "Hız sınırlama izin listesi",
"rate_limit_whitelist_desc": "Hız sınırlamasından hariç tutulan IP adresleri",
"rate_limit_whitelist_placeholder": "Her satıra bir IP adresi girin",
"blocking_ipv4_desc": "Engellenen bir A isteği için geri döndürülecek IP adresi",
"blocking_ipv6_desc": "Engellenen bir AAAA isteği için geri döndürülecek IP adresi",
"blocking_mode_default": "Varsayılan: Reklam engelleme stili kuralı tarafından engellendiğinde sıfır IP adresiyle (A için 0.0.0.0; :: AAAA için) yanıt verin; /etc/hosts-tarzı kural tarafından engellendiğinde, kuralda belirtilen IP adresiyle yanıt verin",
"blocking_mode_default": "Varsayılan: Reklam engelleme tarzı kural tarafından engellendiğinde sıfır IP adresiyle (A için 0.0.0.0; :: AAAA için) yanıt verin; /etc/hosts-tarzı kural tarafından engellendiğinde, kuralda belirtilen IP adresiyle yanıt verin",
"blocking_mode_refused": "REFUSED: REFUSED koduyla yanıt verin",
"blocking_mode_nxdomain": "NXDOMAIN: NXDOMAIN koduyla yanıt verin",
"blocking_mode_null_ip": "Boş IP: Sıfır IP adresiyle yanıt verin (A için 0.0.0.0; :: AAAA için)",
@@ -447,7 +435,7 @@
"settings_global": "Genel",
"settings_custom": "Özel",
"table_client": "İstemci",
"table_name": "AdAdı",
"table_name": "Ad",
"save_btn": "Kaydet",
"client_add": "İstemci Ekle",
"client_new": "Yeni İstemci",
@@ -461,7 +449,7 @@
"form_enter_id": "Tanımlayıcı girin",
"form_add_id": "Tanımlayıcı ekle",
"form_client_name": "İstemci ismi girin",
"name": "Adı",
"name": "Ad",
"client_global_settings": "Genel ayarları kullan",
"client_deleted": "\"{{key}}\" istemcisi başarıyla silindi",
"client_added": "\"{{key}}\" istemcisi başarıyla eklendi",
@@ -734,8 +722,5 @@
"wednesday_short": "Çar",
"thursday_short": "Per",
"friday_short": "Cum",
"saturday_short": "Cmt",
"upstream_dns_cache_configuration": "Üst kaynak DNS önbellek yapılandırması",
"enable_upstream_dns_cache": "Bu istemcinin özel üst kaynak yapılandırması için DNS önbelleğe almayı etkinleştir",
"dns_cache_size": "DNS önbellek boyutu, bayt cinsinden"
"saturday_short": "Cmt"
}

View File

@@ -1,7 +1,6 @@
{
"client_settings": "Налаштування клієнта",
"example_upstream_reserved": "DNS-сервер <0>для певних доменів</0>;",
"example_multiple_upstreams_reserved": "кілька DNS-серверів <0>для конкретних доменів</0>;",
"example_upstream_comment": "коментар.",
"upstream_parallel": "Використовувати паралельні запити, щоб пришвидшити вирішення одночасною чергою всіх оригінальних серверів.",
"parallel_requests": "Паралельні запити",
@@ -144,8 +143,7 @@
"enforced_save_search": "Примусовий безпечний пошук",
"number_of_dns_query_to_safe_search": "Кількість DNS-запитів до пошукових систем, для яких примусово застосований безпечний пошук",
"average_processing_time": "Середній час обробки",
"average_upstream_response_time": "Середній час відгуку upstream-сервера",
"response_time": "Час відгуку",
"processing_time": "Час обробки",
"average_processing_time_hint": "Середній час обробки DNS запиту в мілісекундах",
"block_domain_use_filters_and_hosts": "Блокування доменів за допомогою фільтрів та hosts-файлів",
"filters_block_toggle_hint": "Ви можете налаштувати правила блокування в розділі <a>Фільтри</a>.",
@@ -310,16 +308,6 @@
"edns_use_custom_ip": "Використання користувацької IP-адреси для EDNS",
"edns_use_custom_ip_desc": "Дозволити використовувати користувацьку IP-адресу для EDNS",
"rate_limit_desc": "Кількість запитів в секунду, які може робити один клієнт. Встановлене значення «0» означатиме необмежену кількість.",
"rate_limit_subnet_len_ipv4": "Довжина префікса підмережі для адрес IPv4",
"rate_limit_subnet_len_ipv4_desc": "Довжина префікса підмережі для адрес IPv4, які використовуються для обмеження швидкості. Типовим значенням є 24",
"rate_limit_subnet_len_ipv4_error": "Довжина префікса підмережі IPv4 має бути від 0 до 32",
"rate_limit_subnet_len_ipv6": "Довжина префікса підмережі для адрес IPv6",
"rate_limit_subnet_len_ipv6_desc": "Довжина префікса підмережі для адрес IPv6, які використовуються для обмеження швидкості. Типовим значенням є 56",
"rate_limit_subnet_len_ipv6_error": "Довжина префікса підмережі IPv6 має бути від 0 до 128",
"form_enter_rate_limit_subnet_len": "Введіть довжину префікса підмережі для обмеження швидкості",
"rate_limit_whitelist": "Список дозволених обмежень швидкості",
"rate_limit_whitelist_desc": "IP-адреси, на які не поширюється обмеження швидкості",
"rate_limit_whitelist_placeholder": "Вводьте одну адресу на рядок",
"blocking_ipv4_desc": "IP-адреса, яку потрібно видати для заблокованого A запиту",
"blocking_ipv6_desc": "IP-адреса, яку потрібно видати для заблокованого АААА запиту",
"blocking_mode_default": "Усталено: відповідь із нульовою IP-адресою (0.0.0.0 для A; :: для AAAA), якщо заблоковано правилом у Adblock-стилі; відповідь зазначеною у правилі IP-адресою, якщо заблокувано правилом у hosts-стилі",
@@ -695,7 +683,7 @@
"disable_notify_for_minutes_plural": "Вимкнення захисту на {{count}} хвилин",
"disable_notify_for_hours": "Вимкнення захисту на {{count}} годину",
"disable_notify_for_hours_plural": "Вимкнення захисту на {{count}} годин",
"disable_notify_until_tomorrow": "Вимкнути захист до завтра",
"disable_notify_until_tomorrow": "Відключення захисту до завтра",
"enable_protection_timer": "Захист буде ввімкнено о {{time}}",
"custom_retention_input": "Введіть час в годинах",
"custom_rotation_input": "Введіть час в годинах",
@@ -713,7 +701,7 @@
"schedule_current_timezone": "Поточний часовий пояс: {{value}}",
"schedule_time_all_day": "Увесь день",
"schedule_modal_description": "Цей розклад замінить усі наявні розклади на той самий день тижня. Кожен день тижня може мати тільки один період бездіяльності.",
"schedule_modal_time_off": "Вимкнення блокування сервісів:",
"schedule_modal_time_off": "Блокування сервісів відключена:",
"schedule_new": "Новий розклад",
"schedule_edit": "Редагувати розклад",
"schedule_save": "Зберегти розклад",
@@ -734,8 +722,5 @@
"wednesday_short": "СР",
"thursday_short": "ЧТ",
"friday_short": "ПТ",
"saturday_short": "СБ",
"upstream_dns_cache_configuration": "Конфігурація кешу upstream DNS-серверів",
"enable_upstream_dns_cache": "Увімкнути кешування для користувацької конфігурації upstream-серверів цього клієнта",
"dns_cache_size": "Розмір кешу DNS, у байтах"
"saturday_short": "СБ"
}

View File

@@ -1,7 +1,6 @@
{
"client_settings": "Cài đặt thiết bị",
"example_upstream_reserved": "ngược dòng <0>cho các miền cụ thể</0>;",
"example_multiple_upstreams_reserved": "nhiều máy chủ thượng nguồn <0>cho các miền cụ thể</0>;",
"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",
"parallel_requests": "Yêu cầu song song",
@@ -144,8 +143,7 @@
"enforced_save_search": "Bắt buộc tìm kiếm an toàn",
"number_of_dns_query_to_safe_search": "Số yêu cầu DNS tới công cụ tìm kiếm đã chuyển thành tìm kiếm an toàn",
"average_processing_time": "Thời gian xử lý trung bình",
"average_upstream_response_time": "Thời gian phản hồi trung bình từ máy chủ thượng nguồn",
"response_time": "Thời gian đáp ứng",
"processing_time": "Thời gian xử lý",
"average_processing_time_hint": "Thời gian trung bình cho một yêu cầu DNS tính bằng mili giây",
"block_domain_use_filters_and_hosts": "Chặn tên miền sử dụng các bộ lọc và file hosts",
"filters_block_toggle_hint": "Bạn có thể thiết lập quy tắc chặn tại cài đặt <a>Bộ lọc</a>.",
@@ -310,16 +308,6 @@
"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_subnet_len_ipv4": "Độ dài tiền tố mạng con cho địa chỉ IPv4",
"rate_limit_subnet_len_ipv4_desc": "Độ dài tiền tố mạng con cho các địa chỉ IPv4 được sử dụng để giới hạn tốc độ. Mặc định là 24",
"rate_limit_subnet_len_ipv4_error": "Độ dài tiền tố mạng con IPv4 phải nằm trong khoảng từ 0 đến 32",
"rate_limit_subnet_len_ipv6": "Độ dài tiền tố mạng con cho địa chỉ IPv6",
"rate_limit_subnet_len_ipv6_desc": "Độ dài tiền tố mạng con cho các địa chỉ IPv6 được sử dụng để giới hạn tốc độ. Mặc định là 56",
"rate_limit_subnet_len_ipv6_error": "Độ dài tiền tố mạng con IPv6 phải nằm trong khoảng từ 0 đến 128",
"form_enter_rate_limit_subnet_len": "Nhập độ dài tiền tố mạng con để giới hạn tốc độ",
"rate_limit_whitelist": "Danh sách cho phép giới hạn tỷ lệ",
"rate_limit_whitelist_desc": "Địa chỉ IP bị loại trừ khỏi giới hạn tốc độ",
"rate_limit_whitelist_placeholder": "Nhập một địa chỉ IP trên mỗi dòng",
"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_mode_default": "Mặc định: Trả lời với NXDOMAIN khi bị chặn bởi quy tắc kiểu Adblock; phản hồi với địa chỉ IP được chỉ định trong quy tắc khi bị chặn bởi quy tắc / etc / hosts-style",
@@ -734,8 +722,5 @@
"wednesday_short": "Thứ 4",
"thursday_short": "Thứ 5",
"friday_short": "Thứ 6",
"saturday_short": "Thứ 7",
"upstream_dns_cache_configuration": "Cấu hình bộ nhớ đệm upstream của các máy chủ DNS",
"enable_upstream_dns_cache": "Bật bộ nhớ cache cho cấu hình ngược dòng của máy chủ upstream của khách hàng này",
"dns_cache_size": "Kích thước bộ nhớ cache DNS, tính bằng byte"
"saturday_short": "Thứ 7"
}

View File

@@ -1,7 +1,6 @@
{
"client_settings": "客户端设置",
"example_upstream_reserved": "指定为<0>特定域名</0>的上游服务器;",
"example_multiple_upstreams_reserved": "<0>特定域名</0>的多个上游服务器;",
"example_upstream_comment": "注释。",
"upstream_parallel": "使用并行请求以同时查询所有上游服务器来加快解析速度。",
"parallel_requests": "并行请求",
@@ -144,8 +143,7 @@
"enforced_save_search": "强制安全搜索",
"number_of_dns_query_to_safe_search": "启用强制安全搜索后对搜索引擎的 DNS 请求总数",
"average_processing_time": "平均处理时间",
"average_upstream_response_time": "上游服务器的平均响应时间",
"response_time": "响应时间",
"processing_time": "处理时间",
"average_processing_time_hint": "处理 DNS 请求的平均时间(毫秒)",
"block_domain_use_filters_and_hosts": "使用过滤器和 Hosts 文件以拦截指定域名",
"filters_block_toggle_hint": "你可以在 <a>过滤器</a> 设置中添加过滤规则。",
@@ -310,16 +308,6 @@
"edns_use_custom_ip": "为 EDNS 使用自定义 IP",
"edns_use_custom_ip_desc": "允许为 EDNS 使用自定义 IP",
"rate_limit_desc": "每个客户端每秒钟查询次数的限制。设置为 0 意味着不限制。",
"rate_limit_subnet_len_ipv4": "IPv4 地址子网前缀长度",
"rate_limit_subnet_len_ipv4_desc": "用于速率限制的 IPv4 地址子网前缀长度。默认为 24",
"rate_limit_subnet_len_ipv4_error": "IPv4 子网前缀长度应介于 0 到 32 之间",
"rate_limit_subnet_len_ipv6": "IPv6 地址子网前缀长度",
"rate_limit_subnet_len_ipv6_desc": "用于速率限制的 IPv6 地址子网前缀长度。默认为 56",
"rate_limit_subnet_len_ipv6_error": "IPv6 子网前缀长度应介于 0 到 128 之间",
"form_enter_rate_limit_subnet_len": "输入用于速率限制的子网前缀长度",
"rate_limit_whitelist": "速率限制白名单",
"rate_limit_whitelist_desc": "排除在速率限制之外的 IP 地址",
"rate_limit_whitelist_placeholder": "每行输入一个 IP 地址",
"blocking_ipv4_desc": "拦截 A 记录请求返回的 IP 地址",
"blocking_ipv6_desc": "拦截 AAAA 记录请求返回的 IP 地址",
"blocking_mode_default": "默认:被 Adblock 规则拦截时反应为零 IP 地址A记录0.0.0.0AAAA记录::);被 /etc/hosts 规则拦截时反应为规则中指定 IP 地址",
@@ -734,8 +722,5 @@
"wednesday_short": "周三",
"thursday_short": "周四",
"friday_short": "周五",
"saturday_short": "周六",
"upstream_dns_cache_configuration": "上游 DNS 缓存配置",
"enable_upstream_dns_cache": "为该客户端的自定义上游配置启用 DNS 缓存",
"dns_cache_size": "DNS 缓存大小,单位:字节"
"saturday_short": "周六"
}

View File

@@ -1,7 +1,6 @@
{
"client_settings": "用戶端設定",
"example_upstream_reserved": "<0>供特定的網域</0>之上游;",
"example_multiple_upstreams_reserved": "<0>特定網域</0>的多個上游伺服器;",
"example_upstream_comment": "註解。",
"upstream_parallel": "透過同時地查詢所有上游的伺服器,使用並行的查詢以加速解析。",
"parallel_requests": "並行的請求",
@@ -9,9 +8,9 @@
"load_balancing_desc": "每次查詢一個上游伺服器。AdGuard Home 使用它的加權隨機的演算法來選擇伺服器,以便最快的伺服器被更常使用。",
"bootstrap_dns": "自我啟動BootstrapDNS 伺服器",
"bootstrap_dns_desc": "DNS 伺服器的 IP 位址,用於解析您指定為上游伺服器的 DoH/DoT 解析器的 IP 位址。不允許註釋。",
"fallback_dns_title": "應變 DNS 伺服器",
"fallback_dns_desc": "當上游 DNS 伺服器未回覆時被使用的應變 DNS 伺服器清單。語法與上面主要上游欄位中的相同。",
"fallback_dns_placeholder": "每行輸入一個應變 DNS 伺服器",
"fallback_dns_title": "備援 DNS 伺服器",
"fallback_dns_desc": "當上游 DNS 伺服器未回應時使用的備援 DNS 伺服器清單。語法與上面主要上游欄位相同。",
"fallback_dns_placeholder": "每行輸入一個備援 DNS 伺服器",
"local_ptr_title": "私人反向的 DNS 伺服器",
"local_ptr_desc": "AdGuard Home 用於區域指標PTR查詢之 DNS 伺服器。這些伺服器被用於解析有關在私人 IP 範圍的位址之區域指標查詢,例如,\"192.168.12.34\",使用反向的 DNS。如果未被設定AdGuard Home 使用您的作業系統之預設 DNS 解析器的位址。",
"local_ptr_default_resolver": "預設下AdGuard Home 使用以下反向的 DNS 解析器:{{ip}}。",
@@ -132,7 +131,7 @@
"top_clients": "熱門用戶端",
"no_clients_found": "無已發現之用戶端",
"general_statistics": "一般的統計資料",
"top_upstreams": "熱門上游",
"top_upstreams": "經常請求的上游伺服器",
"no_upstreams_data_found": "找不到上游伺服器資料",
"number_of_dns_query_days": "在最近的 {{count}} 日內已處理的 DNS 查詢之數量",
"number_of_dns_query_days_plural": "在最近的 {{count}} 日內已處理的 DNS 查詢之數量",
@@ -144,8 +143,7 @@
"enforced_save_search": "已強制執行的安全搜尋",
"number_of_dns_query_to_safe_search": "安全搜尋已被強制執行之屬於搜尋引擎的 DNS 請求之數量",
"average_processing_time": "平均的處理時間",
"average_upstream_response_time": "平均的上游回應時間",
"response_time": "回應時間",
"processing_time": "處理時間",
"average_processing_time_hint": "在處理一項 DNS 請求時以毫秒ms計的平均時間",
"block_domain_use_filters_and_hosts": "透過過濾器和主機檔案封鎖網域",
"filters_block_toggle_hint": "您可在<a>過濾器</a>設定中設置封鎖規則。",
@@ -290,7 +288,7 @@
"blocking_ipv4": "封鎖 IPv4",
"blocking_ipv6": "封鎖 IPv6",
"blocked_response_ttl": "已封鎖的回應之存活時間TTL",
"blocked_response_ttl_desc": "對用戶端應快取受過濾的回應,指定多少秒數",
"blocked_response_ttl_desc": "指定客戶端應將過濾的回應存入快取的秒數",
"form_enter_blocked_response_ttl": "請輸入已封鎖回應的存活時間(秒)",
"dnscrypt": "DNSCrypt",
"dns_over_https": "DNS-over-HTTPS",
@@ -310,16 +308,6 @@
"edns_use_custom_ip": "為 EDNS 使用自訂的 IP",
"edns_use_custom_ip_desc": "允許為 EDNS 使用自訂的 IP",
"rate_limit_desc": "每個用戶端被允許的每秒請求之數量。設定它為 0 表示無限制。",
"rate_limit_subnet_len_ipv4": "IPv4 位址的子網路前綴長度",
"rate_limit_subnet_len_ipv4_desc": "用於速率限制的 IPv4 位址的子網路前綴長度。預設值為 24",
"rate_limit_subnet_len_ipv4_error": "IPv4 子網路前綴長度應在 0 至 32 之間",
"rate_limit_subnet_len_ipv6": "IPv6 位址的子網路前綴長度",
"rate_limit_subnet_len_ipv6_desc": "用於速率限制的 IPv6 位址的子網路前綴長度。預設值為 56",
"rate_limit_subnet_len_ipv6_error": "IPv6 子網路前綴長度應在 0 至 128 之間",
"form_enter_rate_limit_subnet_len": "輸入用於速率限制的子網路前綴長度",
"rate_limit_whitelist": "速率限制允許清單",
"rate_limit_whitelist_desc": "從速率限制中排除的 IP 位址",
"rate_limit_whitelist_placeholder": "每行輸入一個 IP 位址",
"blocking_ipv4_desc": "要被返回給已封鎖的 A 請求之 IP 位址",
"blocking_ipv6_desc": "要被返回給已封鎖的 AAAA 請求之 IP 位址",
"blocking_mode_default": "預設:當被 AdBlock 樣式的規則封鎖時,以零值 IP 位址0.0.0.0 供 A:: 供 AAAA回覆當被 /etc/hosts 樣式的規則封鎖時,以在該規則中之已明確指定的 IP 位址回覆",
@@ -734,8 +722,5 @@
"wednesday_short": "週三",
"thursday_short": "週四",
"friday_short": "週五",
"saturday_short": "週六",
"upstream_dns_cache_configuration": "上游 DNS 快取設定",
"enable_upstream_dns_cache": "啟用本用戶端自訂上游配置的 DNS 快取",
"dns_cache_size": "DNS 快取大小,單位:位元"
"saturday_short": "週六"
}

View File

@@ -62,10 +62,6 @@ export const setDnsConfig = (config) => async (dispatch) => {
data.upstream_dns = splitByNewLine(config.upstream_dns);
hasDnsSettings = true;
}
if (Object.prototype.hasOwnProperty.call(data, 'ratelimit_whitelist')) {
data.ratelimit_whitelist = splitByNewLine(config.ratelimit_whitelist);
hasDnsSettings = true;
}
await apiClient.setDnsConfig(data);

View File

@@ -338,40 +338,6 @@ export const getDnsStatus = () => async (dispatch) => {
}
};
export const timerStatusRequest = createAction('TIMER_STATUS_REQUEST');
export const timerStatusFailure = createAction('TIMER_STATUS_FAILURE');
export const timerStatusSuccess = createAction('TIMER_STATUS_SUCCESS');
export const getTimerStatus = () => async (dispatch) => {
dispatch(timerStatusRequest());
const handleRequestError = () => {
dispatch(addErrorToast({ error: 'dns_status_error' }));
dispatch(dnsStatusFailure());
window.location.reload(true);
};
const handleRequestSuccess = (response) => {
const dnsStatus = response.data;
if (dnsStatus.protection_disabled_duration === 0) {
dnsStatus.protection_disabled_duration = null;
}
const { running } = dnsStatus;
const runningStatus = dnsStatus && running;
if (runningStatus === true) {
dispatch(timerStatusSuccess(dnsStatus));
} else {
dispatch(setDnsRunningStatus(running));
}
};
try {
checkStatus(handleRequestSuccess, handleRequestError);
} catch (error) {
handleRequestError();
}
};
export const testUpstreamRequest = createAction('TEST_UPSTREAM_REQUEST');
export const testUpstreamFailure = createAction('TEST_UPSTREAM_FAILURE');
export const testUpstreamSuccess = createAction('TEST_UPSTREAM_SUCCESS');

View File

@@ -28,7 +28,7 @@ import {
} from '../../helpers/constants';
import { getLogsUrlParams, setHtmlLangAttr, setUITheme } from '../../helpers/helpers';
import Header from '../Header';
import { changeLanguage, getDnsStatus, getTimerStatus } from '../../actions';
import { changeLanguage, getDnsStatus } from '../../actions';
import Dashboard from '../../containers/Dashboard';
import SetupGuide from '../../containers/SetupGuide';
@@ -126,18 +126,6 @@ const App = () => {
useEffect(() => {
dispatch(getDnsStatus());
const handleVisibilityChange = () => {
if (document.visibilityState === 'visible') {
dispatch(getTimerStatus());
}
};
document.addEventListener('visibilitychange', handleVisibilityChange);
return () => {
document.removeEventListener('visibilitychange', handleVisibilityChange);
};
}, []);
const setLanguage = () => {

View File

@@ -38,7 +38,7 @@ const UpstreamAvgTime = ({
subtitle,
}) => (
<Card
title={t('average_upstream_response_time')}
title={t('average_processing_time')}
subtitle={subtitle}
bodyType="card-table"
refresh={refreshButton}
@@ -55,7 +55,7 @@ const UpstreamAvgTime = ({
Cell: DomainCell,
},
{
Header: <Trans>response_time</Trans>,
Header: <Trans>processing_time</Trans>,
accessor: 'count',
maxWidth: 190,
Cell: TimeCell,

View File

@@ -81,10 +81,6 @@ export const Modal = ({
};
});
if (timezone !== intialTimezone) {
newSchedule.time_zone = timezone;
}
onSubmit(newSchedule);
};

View File

@@ -79,10 +79,6 @@ const ClientsTable = ({
} else {
config.tags = [];
}
if (typeof values.upstreams_cache_size === 'string') {
config.upstreams_cache_size = 0;
}
}
if (modalType === MODAL_TYPE.EDIT_FILTERS) {

View File

@@ -12,13 +12,8 @@ import i18n from '../../../i18n';
import Tabs from '../../ui/Tabs';
import Examples from '../Dns/Upstream/Examples';
import { ScheduleForm } from '../../Filters/Services/ScheduleForm';
import { toggleAllServices, trimLinesAndRemoveEmpty, captitalizeWords } from '../../../helpers/helpers';
import {
toggleAllServices,
trimLinesAndRemoveEmpty,
captitalizeWords,
} from '../../../helpers/helpers';
import {
toNumber,
renderInputField,
renderGroupField,
CheckboxField,
@@ -26,7 +21,7 @@ import {
renderTextareaField,
} from '../../../helpers/form';
import { validateClientId, validateRequiredValue } from '../../../helpers/validators';
import { CLIENT_ID_LINK, FORM_NAME, UINT32_RANGE } from '../../../helpers/constants';
import { CLIENT_ID_LINK, FORM_NAME } from '../../../helpers/constants';
import './Service.css';
const settingsCheckboxes = [
@@ -312,35 +307,6 @@ let Form = (props) => {
normalizeOnBlur={trimLinesAndRemoveEmpty}
/>
<Examples />
<div className="form__label--bold mt-5 mb-3">
{t('upstream_dns_cache_configuration')}
</div>
<div className="form__group mb-2">
<Field
name="upstreams_cache_enabled"
type="checkbox"
component={CheckboxField}
placeholder={t('enable_upstream_dns_cache')}
/>
</div>
<div className="form__group form__group--settings">
<label
htmlFor="upstreams_cache_size"
className="form__label"
>
{t('dns_cache_size')}
</label>
<Field
name="upstreams_cache_size"
type="number"
component={renderInputField}
placeholder={t('enter_cache_size')}
className="form-control"
normalize={toNumber}
min={0}
max={UINT32_RANGE.MAX}
/>
</div>
</div>,
},
};

View File

@@ -27,7 +27,7 @@ class Leases extends Component {
<div className="logs__row logs__row--center">
<button
type="button"
className="btn btn-icon btn-icon--green btn-outline-success btn-sm"
className="btn btn-icon btn-icon--green btn-outline-secondary btn-sm"
title={t('make_static')}
onClick={this.convertToStatic(row)}
disabled={disabledLeasesButton}

View File

@@ -6,7 +6,6 @@ import { Trans, useTranslation } from 'react-i18next';
import {
renderInputField,
renderRadioField,
renderTextareaField,
CheckboxField,
toNumber,
} from '../../../../helpers/form';
@@ -15,10 +14,7 @@ import {
validateIpv6,
validateRequiredValue,
validateIp,
validateIPv4Subnet,
validateIPv6Subnet,
} from '../../../../helpers/validators';
import { removeEmptyLines } from '../../../../helpers/helpers';
import { BLOCKING_MODES, FORM_NAME, UINT32_RANGE } from '../../../../helpers/constants';
const checkboxes = [
@@ -94,69 +90,6 @@ const Form = ({
/>
</div>
</div>
<div className="col-12 col-md-7">
<div className="form__group form__group--settings">
<label htmlFor="ratelimit_subnet_len_ipv4"
className="form__label form__label--with-desc">
<Trans>rate_limit_subnet_len_ipv4</Trans>
</label>
<div className="form__desc form__desc--top">
<Trans>rate_limit_subnet_len_ipv4_desc</Trans>
</div>
<Field
name="ratelimit_subnet_len_ipv4"
type="number"
component={renderInputField}
className="form-control"
placeholder={t('form_enter_rate_limit_subnet_len')}
normalize={toNumber}
validate={[validateRequiredValue, validateIPv4Subnet]}
min={0}
max={32}
/>
</div>
</div>
<div className="col-12 col-md-7">
<div className="form__group form__group--settings">
<label htmlFor="ratelimit_subnet_len_ipv6"
className="form__label form__label--with-desc">
<Trans>rate_limit_subnet_len_ipv6</Trans>
</label>
<div className="form__desc form__desc--top">
<Trans>rate_limit_subnet_len_ipv6_desc</Trans>
</div>
<Field
name="ratelimit_subnet_len_ipv6"
type="number"
component={renderInputField}
className="form-control"
placeholder={t('form_enter_rate_limit_subnet_len')}
normalize={toNumber}
validate={[validateRequiredValue, validateIPv6Subnet]}
min={0}
max={128}
/>
</div>
</div>
<div className="col-12 col-md-7">
<div className="form__group form__group--settings">
<label htmlFor="ratelimit_whitelist"
className="form__label form__label--with-desc">
<Trans>rate_limit_whitelist</Trans>
</label>
<div className="form__desc form__desc--top">
<Trans>rate_limit_whitelist_desc</Trans>
</div>
<Field
name="ratelimit_whitelist"
component={renderTextareaField}
type="text"
className="form-control"
placeholder={t('rate_limit_whitelist_placeholder')}
normalizeOnBlur={removeEmptyLines}
/>
</div>
</div>
<div className="col-12">
<div className="form__group form__group--settings">
<Field

View File

@@ -11,9 +11,6 @@ const Config = () => {
const {
blocking_mode,
ratelimit,
ratelimit_subnet_len_ipv4,
ratelimit_subnet_len_ipv6,
ratelimit_whitelist,
blocking_ipv4,
blocking_ipv6,
blocked_response_ttl,
@@ -39,9 +36,6 @@ const Config = () => {
<Form
initialValues={{
ratelimit,
ratelimit_subnet_len_ipv4,
ratelimit_subnet_len_ipv6,
ratelimit_whitelist,
blocking_mode,
blocking_ipv4,
blocking_ipv6,

View File

@@ -26,10 +26,6 @@ export const R_WIN_ABSOLUTE_PATH = /^([a-zA-Z]:)?(\\|\/)(?:[^\\/:*?"<>|\x00]+\\)
export const R_CLIENT_ID = /^[a-z0-9-]{1,63}$/;
export const R_IPV4_SUBNET = /^([0-9]|[1-2][0-9]|3[0-2])?$/;
export const R_IPV6_SUBNET = /^([0-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8])?$/;
export const MIN_PASSWORD_LENGTH = 8;
export const MAX_PASSWORD_LENGTH = 72;

View File

@@ -190,12 +190,6 @@ export default {
"homepage": "https://github.com/hagezi/dns-blocklists#piracy",
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_46.txt"
},
"hagezi_encrypted_dns_vpn_tor_proxy_bypass": {
"name": "HaGeZi's Encrypted DNS/VPN/TOR/Proxy Bypass",
"categoryId": "security",
"homepage": "https://github.com/hagezi/dns-blocklists#bypass",
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_52.txt"
},
"hagezi_gambling_blocklist": {
"name": "HaGeZi's Gambling Blocklist",
"categoryId": "other",
@@ -208,30 +202,12 @@ export default {
"homepage": "https://github.com/hagezi/dns-blocklists",
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_34.txt"
},
"hagezi_pro": {
"name": "HaGeZi's Pro Blocklist",
"categoryId": "general",
"homepage": "https://github.com/hagezi/dns-blocklists",
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_48.txt"
},
"hagezi_pro++": {
"name": "HaGeZi's Pro++ Blocklist",
"categoryId": "general",
"homepage": "https://github.com/hagezi/dns-blocklists",
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_51.txt"
},
"hagezi_threat_intelligence_feeds": {
"name": "HaGeZi's Threat Intelligence Feeds",
"categoryId": "security",
"homepage": "https://github.com/hagezi/dns-blocklists",
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_44.txt"
},
"hagezi_ultimate": {
"name": "HaGeZi's Ultimate Blocklist",
"categoryId": "general",
"homepage": "https://github.com/hagezi/dns-blocklists",
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_49.txt"
},
"no_google": {
"name": "No Google",
"categoryId": "other",

View File

@@ -1,5 +1,5 @@
{
"timeUpdated": "2023-12-01T15:24:07.522Z",
"timeUpdated": "2023-10-15T12:13:01.838Z",
"categories": {
"0": "audio_video_player",
"1": "comments",
@@ -640,8 +640,7 @@
"name": "AdChina",
"categoryId": 4,
"url": "http://www.adchina.com/",
"companyId": null,
"source": "AdGuard"
"companyId": "alibaba"
},
"adcito": {
"name": "Adcito",
@@ -682,9 +681,8 @@
"adcolony": {
"name": "AdColony",
"categoryId": 4,
"url": "https://www.adcolony.com/history-of-adcolony/",
"companyId": "digital_turbine",
"source": "AdGuard"
"url": "thttp://www.admarvel.com/",
"companyId": "adcolony"
},
"adconion": {
"name": "Adconion",
@@ -1296,13 +1294,6 @@
"url": "http://www.demdex.com/",
"companyId": "adobe"
},
"adobe_developer": {
"name": "Adobe Developer",
"categoryId": 8,
"url": "https://developer.adobe.com/",
"companyId": "adobe",
"source": "AdGuard"
},
"adobe_dynamic_media": {
"name": "Adobe Dynamic Media",
"categoryId": 4,
@@ -1318,16 +1309,8 @@
"adobe_experience_cloud": {
"name": "Adobe Experience Cloud",
"categoryId": 6,
"url": "https://business.adobe.com/",
"companyId": "adobe",
"source": "AdGuard"
},
"adobe_experience_league": {
"name": "Adobe Experience League",
"categoryId": 6,
"url": "https://experienceleague.adobe.com/",
"companyId": "adobe",
"source": "AdGuard"
"url": "https://www.adobe.com/experience-cloud.html",
"companyId": "adobe"
},
"adobe_login": {
"name": "Adobe Login",
@@ -2289,29 +2272,27 @@
"name": "Alibaba",
"categoryId": 8,
"url": "http://www.alibaba.com/",
"companyId": "softbank",
"source": "AdGuard"
"companyId": "alibaba"
},
"alibaba_cloud": {
"name": "Alibaba Cloud",
"categoryId": 10,
"url": "https://www.alibabacloud.com/",
"companyId": "softbank",
"companyId": "alibaba",
"source": "AdGuard"
},
"alibaba_ucbrowser": {
"name": "UC Browser",
"categoryId": 8,
"url": "https://ucweb.com/",
"companyId": "softbank",
"companyId": "alibaba",
"source": "AdGuard"
},
"alipay.com": {
"name": "Alipay",
"categoryId": 2,
"url": "https://global.alipay.com/",
"companyId": "softbank",
"source": "AdGuard"
"url": "https://www.alipay.com/",
"companyId": "alibaba"
},
"alivechat": {
"name": "AliveChat",
@@ -3777,11 +3758,10 @@
"companyId": "branica"
},
"braze": {
"name": "Braze, Inc.",
"name": "Braze",
"categoryId": 6,
"url": "https://www.braze.com/",
"companyId": "braze",
"source": "AdGuard"
"companyId": "braze_inc"
},
"brealtime": {
"name": "EMX Digital",
@@ -4373,13 +4353,6 @@
"url": "http://chartbeat.com/",
"companyId": "chartbeat"
},
"chartboost": {
"name": "Chartboost",
"categoryId": 4,
"url": "http://chartboost.com/",
"companyId": "take-two",
"source": "AdGuard"
},
"chaser": {
"name": "Chaser",
"categoryId": 2,
@@ -7357,7 +7330,7 @@
"name": "Flurry",
"categoryId": 101,
"url": "http://www.flurry.com/",
"companyId": "apollo_global_management",
"companyId": "verizon",
"source": "AdGuard"
},
"flxone": {
@@ -12413,13 +12386,6 @@
"url": "http://www.nimblecommerce.com/",
"companyId": "nimblecommerce"
},
"nine_direct_digital": {
"name": "Nine Digital Direct",
"categoryId": 4,
"url": "https://ninedigitaldirect.com.au/",
"companyId": "nine_entertainment",
"source": "AdGuard"
},
"ninja_access_analysis": {
"name": "Ninja Access Analysis",
"categoryId": 6,
@@ -12545,7 +12511,7 @@
"name": "Network Time Protocol",
"categoryId": 5,
"url": "https://ntp.org/",
"companyId": "network_time_foundation",
"companyId": "ntppool",
"source": "AdGuard"
},
"nttcom_online_marketing_solutions": {
@@ -17073,8 +17039,7 @@
"name": "Taobao",
"categoryId": 4,
"url": "https://world.taobao.com/",
"companyId": "softbank",
"source": "AdGuard"
"companyId": "alibaba"
},
"tapad": {
"name": "Tapad",
@@ -19581,9 +19546,8 @@
"yahoo": {
"name": "Yahoo!",
"categoryId": 6,
"url": "https://yahoo.com/",
"companyId": "apollo_global_management",
"source": "AdGuard"
"url": "https://yahoo.com",
"companyId": "verizon"
},
"yahoo_ad_exchange": {
"name": "Yahoo! Ad Exchange",
@@ -19597,13 +19561,6 @@
"url": "https://developer.yahoo.com/analytics/",
"companyId": "verizon"
},
"yahoo_advertising": {
"name": "Yahoo! Advertising",
"categoryId": 4,
"url": "https://www.advertising.yahooinc.com/",
"companyId": "apollo_global_management",
"source": "AdGuard"
},
"yahoo_analytics": {
"name": "Yahoo! Analytics",
"categoryId": 6,
@@ -19634,13 +19591,6 @@
"url": "http://searchmarketing.yahoo.com",
"companyId": "verizon"
},
"yahoo_search": {
"name": "Yahoo! Search",
"categoryId": 4,
"url": "https://search.yahooinc.com/",
"companyId": "apollo_global_management",
"source": "AdGuard"
},
"yahoo_small_business": {
"name": "Yahoo! Small Business",
"categoryId": 4,
@@ -20278,7 +20228,6 @@
"ad-cloud.jp": "adcloud",
"admarvel.s3.amazonaws.com": "adcolony",
"ads.admarvel.com": "adcolony",
"adcolony.com": "adcolony",
"adrdgt.com": "adconion",
"amgdgt.com": "adconion",
"adcrowd.com": "adcrowd",
@@ -20433,7 +20382,6 @@
"demdex.net": "adobe_audience_manager",
"everestjs.net": "adobe_audience_manager",
"everesttech.net": "adobe_audience_manager",
"adobe.io": "adobe_developer",
"scene7.com": "adobe_dynamic_media",
"adobedtm.com": "adobe_dynamic_tag_management",
"2o7.net": "adobe_experience_cloud",
@@ -20443,7 +20391,6 @@
"nedstat.com": "adobe_experience_cloud",
"omtrdc.net": "adobe_experience_cloud",
"sitestat.com": "adobe_experience_cloud",
"adobedc.net": "adobe_experience_league",
"adobelogin.com": "adobe_login",
"adobetag.com": "adobe_tagmanager",
"typekit.com": "adobe_typekit",
@@ -20707,10 +20654,8 @@
"a2z.com": "amazon",
"aamazoncognito.com": "amazon",
"amazon-corp.com": "amazon",
"amazon-dss.com": "amazon",
"amazon.com.au": "amazon",
"amazon.com.mx": "amazon",
"amazon.dev": "amazon",
"amazon.in": "amazon",
"amazon.nl": "amazon",
"amazon.sa": "amazon",
@@ -20718,7 +20663,6 @@
"amazonbrowserapp.es": "amazon",
"amazoncrl.com": "amazon",
"firetvcaptiveportal.com": "amazon",
"ntp-fireos.com": "amazon",
"amazon-adsystem.com": "amazon_adsystem",
"serving-sys.com": "amazon_adsystem",
"sizmek.com": "amazon_adsystem",
@@ -20734,16 +20678,12 @@
"amazontrust.com": "amazon_cdn",
"associates-amazon.com": "amazon_cdn",
"cloudfront.net": "amazon_cloudfront",
"ota-cloudfront.net": "amazon_cloudfront",
"axx-eu.amazon-adsystem.com": "amazon_mobile_ads",
"amazonpay.com": "amazon_payments",
"payments-amazon.com": "amazon_payments",
"amazonpay.in": "amazon_payments",
"aiv-cdn.net": "amazon_video",
"aiv-delivery.net": "amazon_video",
"amazonvideo.com": "amazon_video",
"pv-cdn.net": "amazon_video",
"primevideo.com": "amazon_video",
"amazonaws.com": "amazon_web_services",
"amazonwebservices.com": "amazon_web_services",
"awsstatic.com": "amazon_web_services",
@@ -20890,8 +20830,6 @@
"ad.globe7.com": "axill",
"azadify.com": "azadify",
"azure.com": "azure",
"azure.net": "azure",
"azurefd.net": "azure",
"trafficmanager.net": "azure",
"blob.core.windows.net": "azure_blob_storage",
"azureedge.net": "azureedge.net",
@@ -21059,7 +20997,6 @@
"brandwire.tv": "brandwire.tv",
"branica.com": "branica",
"appboycdn.com": "braze",
"braze.com": "braze",
"brealtime.com": "brealtime",
"bridgetrack.com": "bridgetrack",
"brightcove.com": "brightcove",
@@ -21183,7 +21120,6 @@
"chaordicsystems.com": "chaordic",
"chartbeat.com": "chartbeat",
"chartbeat.net": "chartbeat",
"chartboost.com": "chartboost",
"chaser.ru": "chaser",
"cloud.chatbeacon.io": "chat_beacon",
"chatango.com": "chatango",
@@ -21993,9 +21929,7 @@
"githubassets.com": "github",
"githubusercontent.com": "github",
"ghcr.io": "github",
"github.blog": "github",
"github.dev": "github",
"octocaptcha.com": "github",
"githubapp.com": "github_apps",
"github.io": "github_pages",
"aff3.gittigidiyor.com": "gittigidiyor_affiliate_program",
@@ -23154,16 +23088,7 @@
"s-microsoft.com": "microsoft",
"trouter.io": "microsoft",
"windows.net": "microsoft",
"aka.ms": "microsoft",
"microsoftazuread-sso.com": "microsoft",
"bingapis.com": "microsoft",
"msauth.net": "microsoft",
"msauthimages.net": "microsoft",
"msftauth.net": "microsoft",
"msftstatic.com": "microsoft",
"msidentity.com": "microsoft",
"nelreports.net": "microsoft",
"windowscentral.com": "microsoft",
"analytics.live.com": "microsoft_analytics",
"a.clarity.ms": "microsoft_clarity",
"b.clarity.ms": "microsoft_clarity",
@@ -23248,14 +23173,10 @@
"mrpdata.com": "mrpdata",
"mrpdata.net": "mrpdata",
"mrskincash.com": "mrskincash",
"a-msedge.net": "msedge",
"b-msedge.net": "msedge",
"e-msedge.net": "msedge",
"k-msedge.net": "msedge",
"l-msedge.net": "msedge",
"s-msedge.net": "msedge",
"t-msedge.net": "msedge",
"wac-msedge.net": "msedge",
"msn.com": "msn",
"s-msn.com": "msn",
"musculahq.appspot.com": "muscula",
@@ -23373,7 +23294,6 @@
"ads.ngageinc.com": "ngage_inc.",
"nice264.com": "nice264.com",
"nimblecommerce.com": "nimblecommerce",
"nineanalytics.io": "nine_direct_digital",
"cho-chin.com": "ninja_access_analysis",
"donburako.com": "ninja_access_analysis",
"hishaku.com": "ninja_access_analysis",
@@ -25027,15 +24947,10 @@
"yahoo.com": "yahoo",
"yahooapis.com": "yahoo",
"yimg.com": "yahoo",
"oath.cloud": "yahoo",
"yahoo.net": "yahoo",
"yahooinc.com": "yahoo",
"yahoodns.net": "yahoo",
"ads.yahoo.com": "yahoo_ad_exchange",
"yads.yahoo.com": "yahoo_ad_exchange",
"yieldmanager.com": "yahoo_ad_exchange",
"pr-bh.ybp.yahoo.com": "yahoo_ad_manager",
"ads.yahoo.com": "yahoo_advertising",
"adtech.yahooinc.com": "yahoo_advertising",
"analytics.yahoo.com": "yahoo_analytics",
"np.lexity.com": "yahoo_commerce_central",
"storage-yahoo.jp": "yahoo_japan_retargeting",
@@ -25045,7 +24960,6 @@
"yjtag.jp": "yahoo_japan_retargeting",
"ov.yahoo.co.jp": "yahoo_overture",
"overture.com": "yahoo_overture",
"search.yahooinc.com": "yahoo_search",
"luminate.com": "yahoo_small_business",
"pixazza.com": "yahoo_small_business",
"awaps.yandex.ru": "yandex",

View File

@@ -15,8 +15,6 @@ import {
R_DOMAIN,
MAX_PASSWORD_LENGTH,
MIN_PASSWORD_LENGTH,
R_IPV4_SUBNET,
R_IPV6_SUBNET,
} from './constants';
import { ip4ToInt, isValidAbsolutePath } from './form';
import { isIpInCidr, parseSubnetMask } from './helpers';
@@ -367,25 +365,3 @@ export const validateIpGateway = (value, allValues) => {
}
return undefined;
};
/**
* @param value {string}
* @returns {Function}
*/
export const validateIPv4Subnet = (value) => {
if (!R_IPV4_SUBNET.test(value)) {
return i18next.t('rate_limit_subnet_len_ipv4_error');
}
return undefined;
};
/**
* @param value {string}
* @returns {Function}
*/
export const validateIPv6Subnet = (value) => {
if (!R_IPV6_SUBNET.test(value)) {
return i18next.t('rate_limit_subnet_len_ipv6_error');
}
return undefined;
};

View File

@@ -44,19 +44,6 @@ const dashboard = handleActions(
return newState;
},
[actions.timerStatusSuccess]: (state, { payload }) => {
const {
protection_enabled: protectionEnabled,
protection_disabled_duration: protectionDisabledDuration,
} = payload;
const newState = {
...state,
protectionEnabled,
protectionDisabledDuration,
};
return newState;
},
[actions.getVersionRequest]: (state) => ({
...state,

View File

@@ -128,7 +128,8 @@ const dhcp = handleActions(
const newState = {
...state,
isModalOpen: !state.isModalOpen,
leaseModalConfig: payload,
modalType: payload?.type || '',
leaseModalConfig: payload?.config,
};
return newState;
},

View File

@@ -18,7 +18,6 @@ const dnsConfig = handleActions(
fallback_dns,
bootstrap_dns,
local_ptr_upstreams,
ratelimit_whitelist,
...values
} = payload;
@@ -31,7 +30,6 @@ const dnsConfig = handleActions(
fallback_dns: (fallback_dns && fallback_dns.join('\n')) || '',
bootstrap_dns: (bootstrap_dns && bootstrap_dns.join('\n')) || '',
local_ptr_upstreams: (local_ptr_upstreams && local_ptr_upstreams.join('\n')) || '',
ratelimit_whitelist: (ratelimit_whitelist && ratelimit_whitelist.join('\n')) || '',
processingGetConfig: false,
};
},

45
go.mod
View File

@@ -3,21 +3,21 @@ module github.com/AdguardTeam/AdGuardHome
go 1.20
require (
github.com/AdguardTeam/dnsproxy v0.60.0
github.com/AdguardTeam/golibs v0.18.0
github.com/AdguardTeam/dnsproxy v0.56.3
github.com/AdguardTeam/golibs v0.17.2
github.com/AdguardTeam/urlfilter v0.17.3
github.com/NYTimes/gziphandler v1.1.1
github.com/ameshkov/dnscrypt/v2 v2.2.7
github.com/bluele/gcache v0.0.2
github.com/digineo/go-ipset/v2 v2.2.1
github.com/dimfeld/httptreemux/v5 v5.5.0
github.com/fsnotify/fsnotify v1.7.0
github.com/fsnotify/fsnotify v1.6.0
github.com/go-ping/ping v1.1.0
github.com/google/go-cmp v0.6.0
github.com/google/gopacket v1.1.19
github.com/google/renameio/v2 v2.0.0
github.com/google/uuid v1.4.0
github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2
github.com/google/uuid v1.3.1
github.com/insomniacslk/dhcp v0.0.0-20231016090811-6a2c8fbdcc1c
github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86
github.com/kardianos/service v1.2.2
github.com/mdlayher/ethernet v0.0.0-20220221185849-529eae5b6118
@@ -26,18 +26,18 @@ require (
// TODO(a.garipov): This package is deprecated; find a new one or use our
// own code for that. Perhaps, use gopacket.
github.com/mdlayher/raw v0.1.0
github.com/miekg/dns v1.1.57
github.com/quic-go/quic-go v0.40.0
github.com/miekg/dns v1.1.56
github.com/quic-go/quic-go v0.39.2
github.com/stretchr/testify v1.8.4
github.com/ti-mo/netfilter v0.5.1
go.etcd.io/bbolt v1.3.8
golang.org/x/crypto v0.16.0
golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb
golang.org/x/net v0.19.0
golang.org/x/sys v0.15.0
go.etcd.io/bbolt v1.3.7
golang.org/x/crypto v0.14.0
golang.org/x/exp v0.0.0-20231006140011-7918f672742d
golang.org/x/net v0.17.0
golang.org/x/sys v0.13.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1
gopkg.in/yaml.v3 v3.0.1
howett.net/plist v1.0.1
howett.net/plist v1.0.0
)
require (
@@ -47,20 +47,19 @@ require (
github.com/beefsack/go-rate v0.0.0-20220214233405-116f4ca011a0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/google/pprof v0.0.0-20231205033806-a5a03c77bf08 // indirect
// TODO(a.garipov): Upgrade to v0.5.0 once we switch to Go 1.21+.
github.com/mdlayher/socket v0.4.1 // indirect
github.com/onsi/ginkgo/v2 v2.13.2 // indirect
github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98 // indirect
github.com/mdlayher/socket v0.5.0 // indirect
github.com/onsi/ginkgo/v2 v2.13.0 // indirect
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
github.com/pierrec/lz4/v4 v4.1.19 // indirect
github.com/pierrec/lz4/v4 v4.1.18 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/quic-go/qpack v0.4.0 // indirect
github.com/quic-go/qtls-go1-20 v0.4.1 // indirect
github.com/quic-go/qtls-go1-20 v0.3.4 // indirect
github.com/u-root/uio v0.0.0-20230305220412-3e8cd9d6bf63 // indirect
go.uber.org/mock v0.3.0 // indirect
golang.org/x/mod v0.14.0 // indirect
golang.org/x/sync v0.5.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/tools v0.16.0 // indirect
golang.org/x/mod v0.13.0 // indirect
golang.org/x/sync v0.4.0 // indirect
golang.org/x/text v0.13.0 // indirect
golang.org/x/tools v0.14.0 // indirect
)

93
go.sum
View File

@@ -1,7 +1,7 @@
github.com/AdguardTeam/dnsproxy v0.60.0 h1:0gLYoFyWRhQ1MP6g6AqqZXL5/h2QM4FE1aybUZ5HGuE=
github.com/AdguardTeam/dnsproxy v0.60.0/go.mod h1:B7FvvTFQZBfey1cJXQo732EyCLX6xj4JqrciCawATzg=
github.com/AdguardTeam/golibs v0.18.0 h1:ckS2YK7t2Ub6UkXl0fnreVaM15Zb07Hh1gmFqttjpWg=
github.com/AdguardTeam/golibs v0.18.0/go.mod h1:DKhCIXHcUYtBhU8ibTLKh1paUL96n5zhQBlx763sj+U=
github.com/AdguardTeam/dnsproxy v0.56.3 h1:WP1FooLfZQPHEH2SuwMtJsOurDt32rubGx0OddcsKT0=
github.com/AdguardTeam/dnsproxy v0.56.3/go.mod h1:ZvkbM71HwpilgkCnTubDiR4Ba6x5Qvnhy2iasMWaTDM=
github.com/AdguardTeam/golibs v0.17.2 h1:vg6wHMjUKscnyPGRvxS5kAt7Uw4YxcJiITZliZ476W8=
github.com/AdguardTeam/golibs v0.17.2/go.mod h1:DKhCIXHcUYtBhU8ibTLKh1paUL96n5zhQBlx763sj+U=
github.com/AdguardTeam/urlfilter v0.17.3 h1:fg/ObbnO0Cv6aw0tW6N/ETDMhhNvmcUUOZ7HlmKC3rw=
github.com/AdguardTeam/urlfilter v0.17.3/go.mod h1:Jru7jFfeH2CoDf150uDs+rRYcZBzHHBz05r9REyDKyE=
github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I=
@@ -25,9 +25,9 @@ github.com/digineo/go-ipset/v2 v2.2.1 h1:k6skY+0fMqeUjjeWO/m5OuWPSZUAn7AucHMnQ1M
github.com/digineo/go-ipset/v2 v2.2.1/go.mod h1:wBsNzJlZlABHUITkesrggFnZQtgW5wkqw1uo8Qxe0VU=
github.com/dimfeld/httptreemux/v5 v5.5.0 h1:p8jkiMrCuZ0CmhwYLcbNbl7DDo21fozhKHQ2PccwOFQ=
github.com/dimfeld/httptreemux/v5 v5.5.0/go.mod h1:QeEylH57C0v3VO0tkKraVz9oD3Uu93CKPnTLbsidvSw=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ping/ping v1.1.0 h1:3MCGhVX4fyEUuhsfwPrsEdQw6xspHkv5zHsiSoDFZYw=
github.com/go-ping/ping v1.1.0/go.mod h1:xIFjORFzTxqIV/tDVGO4eDy/bLuSyawEeojSm3GfRGk=
@@ -41,16 +41,16 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
github.com/google/pprof v0.0.0-20231205033806-a5a03c77bf08 h1:PxlBVtIFHR/mtWk2i0gTEdCz+jBnqiuHNSki0epDbVs=
github.com/google/pprof v0.0.0-20231205033806-a5a03c77bf08/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98 h1:pUa4ghanp6q4IJHwE9RwLgmVFfReJN+KbQ8ExNEUUoQ=
github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
github.com/google/renameio/v2 v2.0.0 h1:UifI23ZTGY8Tt29JbYFiuyIU3eX+RNFtUwefq9qAhxg=
github.com/google/renameio/v2 v2.0.0/go.mod h1:BtmJXm5YlszgC+TD4HOEEUFgkJP3nLxehU6hfe7jRt4=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714 h1:/jC7qQFrv8CrSJVmaolDVOxTfS9kc36uB6H40kdbQq8=
github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2 h1:9K06NfxkBh25x56yVhWWlKFE8YpicaSfHwoV8SFbueA=
github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2/go.mod h1:3A9PQ1cunSDF/1rbTq99Ts4pVnycWg+vlPkfeD2NLFI=
github.com/insomniacslk/dhcp v0.0.0-20231016090811-6a2c8fbdcc1c h1:PgxFEySCI41sH0mB7/2XswdXbUykQsRUGod8Rn+NubM=
github.com/insomniacslk/dhcp v0.0.0-20231016090811-6a2c8fbdcc1c/go.mod h1:3A9PQ1cunSDF/1rbTq99Ts4pVnycWg+vlPkfeD2NLFI=
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.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
@@ -71,19 +71,19 @@ github.com/mdlayher/packet v1.1.2/go.mod h1:GEu1+n9sG5VtiRE4SydOmX5GTwyyYlteZiFU
github.com/mdlayher/raw v0.1.0 h1:K4PFMVy+AFsp0Zdlrts7yNhxc/uXoPVHi9RzRvtZF2Y=
github.com/mdlayher/raw v0.1.0/go.mod h1:yXnxvs6c0XoF/aK52/H5PjsVHmWBCFfZUfoh/Y5s9Sg=
github.com/mdlayher/socket v0.2.1/go.mod h1:QLlNPkFR88mRUNQIzRBMfXxwKal8H7u1h3bL1CV+f0E=
github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U=
github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA=
github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM=
github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk=
github.com/mdlayher/socket v0.5.0 h1:ilICZmJcQz70vrWVes1MFera4jGiWNocSkykwwoy3XI=
github.com/mdlayher/socket v0.5.0/go.mod h1:WkcBFfvyG8QENs5+hfQPl1X6Jpd2yeLIYgrGFmJiJxI=
github.com/miekg/dns v1.1.56 h1:5imZaSeoRNvpM9SzWNhEcP9QliKiz20/dA2QabIGVnE=
github.com/miekg/dns v1.1.56/go.mod h1:cRm6Oo2C8TY9ZS/TqsSrseAcncm74lfK5G+ikN2SWWY=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/onsi/ginkgo/v2 v2.13.2 h1:Bi2gGVkfn6gQcjNjZJVO8Gf0FHzMPf2phUei9tejVMs=
github.com/onsi/ginkgo/v2 v2.13.2/go.mod h1:XStQ8QcGwLyF4HdfcZB8SFOS/MWCgDuXMSBe6zrvLgM=
github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg=
github.com/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4=
github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o=
github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI=
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pierrec/lz4/v4 v4.1.19 h1:tYLzDnjDXh9qIxSTKHwXwOYmm9d887Y7Y1ZkyXYHAN4=
github.com/pierrec/lz4/v4 v4.1.19/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pierrec/lz4/v4 v4.1.18 h1:xaKrnTkyoqfh1YItXl56+6KJNVYWlEEPuAQW9xsplYQ=
github.com/pierrec/lz4/v4 v4.1.18/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -92,10 +92,10 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
github.com/quic-go/qtls-go1-20 v0.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs=
github.com/quic-go/qtls-go1-20 v0.4.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
github.com/quic-go/quic-go v0.40.0 h1:GYd1iznlKm7dpHD7pOVpUvItgMPo/jrMgDWZhMCecqw=
github.com/quic-go/quic-go v0.40.0/go.mod h1:PeN7kuVJ4xZbxSv/4OX6S1USOX8MJvydwpTx31vx60c=
github.com/quic-go/qtls-go1-20 v0.3.4 h1:MfFAPULvst4yoMgY9QmtpYmfij/em7O8UUi+bNVm7Cg=
github.com/quic-go/qtls-go1-20 v0.3.4/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
github.com/quic-go/quic-go v0.39.2 h1:hmwAf8zAHlvan0Y5PXxeeBFZEW17IW99sXLry8I2kjk=
github.com/quic-go/quic-go v0.39.2/go.mod h1:T09QsDQWjLiQ74ZmacDfqZmhY/NLnw5BC40MANNNZ1Q=
github.com/shirou/gopsutil/v3 v3.23.7 h1:C+fHO8hfIppoJ1WdsVm1RoI0RwXoNdfTK7yWXV0wVj4=
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@@ -112,32 +112,32 @@ github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYm
github.com/u-root/uio v0.0.0-20230305220412-3e8cd9d6bf63 h1:YcojQL98T/OO+rybuzn2+5KrD5dBwXIvYBvQ2cD3Avg=
github.com/u-root/uio v0.0.0-20230305220412-3e8cd9d6bf63/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264=
github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw=
go.etcd.io/bbolt v1.3.8 h1:xs88BrvEv273UsB79e0hcVrlUWmS0a8upikMFhSyAtA=
go.etcd.io/bbolt v1.3.8/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ=
go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
go.uber.org/mock v0.3.0 h1:3mUxI1No2/60yUYax92Pt8eNOEecx2D3lcXZh2NEZJo=
go.uber.org/mock v0.3.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY=
golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb h1:c0vyKkb6yr3KR7jEfJaOSv4lG7xPkbN6r52aJz1d8a8=
golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI=
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
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.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY=
golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
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-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
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.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ=
golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
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-20190322080309-f49334f85ddc/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -148,18 +148,19 @@ golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.4.1-0.20230131160137-e7d7f63158de/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.16.0 h1:GO788SKMRunPIBCXiQyo2AaexLstOrVhuAL5YwsckQM=
golang.org/x/tools v0.16.0/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0=
golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc=
golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg=
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=
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
@@ -171,5 +172,5 @@ gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
howett.net/plist v1.0.1 h1:37GdZ8tP09Q35o9ych3ehygcsL+HqKSwzctveSlarvM=
howett.net/plist v1.0.1/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g=
howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM=
howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g=

View File

@@ -0,0 +1,33 @@
// Package aghchan contains channel utilities.
package aghchan
import (
"fmt"
"time"
)
// Receive returns an error if it cannot receive a value form c before timeout
// runs out.
func Receive[T any](c <-chan T, timeout time.Duration) (v T, ok bool, err error) {
var zero T
timeoutCh := time.After(timeout)
select {
case <-timeoutCh:
// TODO(a.garipov): Consider implementing [errors.Aser] for
// os.ErrTimeout.
return zero, false, fmt.Errorf("did not receive after %s", timeout)
case v, ok = <-c:
return v, ok, nil
}
}
// MustReceive panics if it cannot receive a value form c before timeout runs
// out.
func MustReceive[T any](c <-chan T, timeout time.Duration) (v T, ok bool) {
v, ok, err := Receive(c, timeout)
if err != nil {
panic(err)
}
return v, ok
}

View File

@@ -12,12 +12,51 @@ import (
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/hostsfile"
"github.com/AdguardTeam/golibs/log"
"golang.org/x/exp/maps"
"golang.org/x/exp/slices"
)
// DefaultHostsPaths returns the slice of paths default for the operating system
// to files and directories which are containing the hosts database. The result
// is intended to be used within fs.FS so the initial slash is omitted.
func DefaultHostsPaths() (paths []string) {
return defaultHostsPaths()
}
// MatchAddr returns the records for the IP address.
func (hc *HostsContainer) MatchAddr(ip netip.Addr) (recs []*hostsfile.Record) {
cur := hc.current.Load()
if cur == nil {
return nil
}
return cur.addrs[ip]
}
// MatchName returns the records for the hostname.
func (hc *HostsContainer) MatchName(name string) (recs []*hostsfile.Record) {
cur := hc.current.Load()
if cur != nil {
recs = cur.names[name]
}
return recs
}
// hostsContainerPrefix is a prefix for logging and wrapping errors in
// HostsContainer's methods.
const hostsContainerPrefix = "hosts container"
// Hosts is a map of IP addresses to the records, as it primarily stored in the
// [HostsContainer]. It should not be accessed for writing since it may be read
// concurrently, users should clone it before modifying.
//
// The order of records for each address is preserved from original files, but
// the order of the addresses, being a map key, is not.
//
// TODO(e.burkov): Probably, this should be a sorted slice of records.
type Hosts map[netip.Addr][]*hostsfile.Record
// HostsContainer stores the relevant hosts database provided by the OS and
// processes both A/AAAA and PTR DNS requests for those.
type HostsContainer struct {
@@ -25,10 +64,10 @@ type HostsContainer struct {
done chan struct{}
// updates is the channel for receiving updated hosts.
updates chan *hostsfile.DefaultStorage
updates chan Hosts
// current is the last set of hosts parsed.
current atomic.Pointer[hostsfile.DefaultStorage]
current atomic.Pointer[hostsIndex]
// fsys is the working file system to read hosts files from.
fsys fs.FS
@@ -69,7 +108,7 @@ func NewHostsContainer(
hc = &HostsContainer{
done: make(chan struct{}, 1),
updates: make(chan *hostsfile.DefaultStorage, 1),
updates: make(chan Hosts, 1),
fsys: fsys,
watcher: w,
patterns: patterns,
@@ -102,33 +141,23 @@ func NewHostsContainer(
func (hc *HostsContainer) Close() (err error) {
log.Debug("%s: closing", hostsContainerPrefix)
err = errors.Annotate(hc.watcher.Close(), "closing fs watcher: %w")
err = hc.watcher.Close()
if err != nil {
err = fmt.Errorf("closing fs watcher: %w", err)
// Go on and close the container either way.
}
// Go on and close the container either way.
close(hc.done)
return err
}
// Upd returns the channel into which the updates are sent. The updates
// themselves must not be modified.
func (hc *HostsContainer) Upd() (updates <-chan *hostsfile.DefaultStorage) {
// Upd returns the channel into which the updates are sent.
func (hc *HostsContainer) Upd() (updates <-chan Hosts) {
return hc.updates
}
// type check
var _ hostsfile.Storage = (*HostsContainer)(nil)
// ByAddr implements the [hostsfile.Storage] interface for *HostsContainer.
func (hc *HostsContainer) ByAddr(addr netip.Addr) (names []string) {
return hc.current.Load().ByAddr(addr)
}
// ByName implements the [hostsfile.Storage] interface for *HostsContainer.
func (hc *HostsContainer) ByName(name string) (addrs []netip.Addr) {
return hc.current.Load().ByName(name)
}
// pathsToPatterns converts paths into patterns compatible with fs.Glob.
func pathsToPatterns(fsys fs.FS, paths []string) (patterns []string, err error) {
for i, p := range paths {
@@ -139,7 +168,7 @@ func pathsToPatterns(fsys fs.FS, paths []string) (patterns []string, err error)
continue
}
// Don't put a filename here since it's already added by [fs.Stat].
// Don't put a filename here since it's already added by fs.Stat.
return nil, fmt.Errorf("path at index %d: %w", i, err)
}
@@ -181,7 +210,7 @@ func (hc *HostsContainer) handleEvents() {
}
// sendUpd tries to send the parsed data to the ch.
func (hc *HostsContainer) sendUpd(recs *hostsfile.DefaultStorage) {
func (hc *HostsContainer) sendUpd(recs Hosts) {
log.Debug("%s: sending upd", hostsContainerPrefix)
ch := hc.updates
@@ -198,6 +227,67 @@ func (hc *HostsContainer) sendUpd(recs *hostsfile.DefaultStorage) {
}
}
// hostsIndex is a [hostsfile.Set] to enumerate all the records.
type hostsIndex struct {
// addrs maps IP addresses to the records.
addrs Hosts
// names maps hostnames to the records.
names map[string][]*hostsfile.Record
}
// walk is a file walking function for hostsIndex.
func (idx *hostsIndex) walk(r io.Reader) (patterns []string, cont bool, err error) {
return nil, true, hostsfile.Parse(idx, r, nil)
}
// type check
var _ hostsfile.Set = (*hostsIndex)(nil)
// Add implements the [hostsfile.Set] interface for *hostsIndex.
func (idx *hostsIndex) Add(rec *hostsfile.Record) {
idx.addrs[rec.Addr] = append(idx.addrs[rec.Addr], rec)
for _, name := range rec.Names {
idx.names[name] = append(idx.names[name], rec)
}
}
// type check
var _ hostsfile.HandleSet = (*hostsIndex)(nil)
// HandleInvalid implements the [hostsfile.HandleSet] interface for *hostsIndex.
func (idx *hostsIndex) HandleInvalid(src string, _ []byte, err error) {
lineErr := &hostsfile.LineError{}
if !errors.As(err, &lineErr) {
// Must not happen if idx passed to [hostsfile.Parse].
return
} else if errors.Is(lineErr, hostsfile.ErrEmptyLine) {
// Ignore empty lines.
return
}
log.Info("%s: warning: parsing %q: %s", hostsContainerPrefix, src, lineErr)
}
// equalRecs is an equality function for [*hostsfile.Record].
func equalRecs(a, b *hostsfile.Record) (ok bool) {
return a.Addr == b.Addr && a.Source == b.Source && slices.Equal(a.Names, b.Names)
}
// equalRecSlices is an equality function for slices of [*hostsfile.Record].
func equalRecSlices(a, b []*hostsfile.Record) (ok bool) { return slices.EqualFunc(a, b, equalRecs) }
// Equal returns true if indexes are equal.
func (idx *hostsIndex) Equal(other *hostsIndex) (ok bool) {
if idx == nil {
return other == nil
} else if other == nil {
return false
}
return maps.EqualFunc(idx.addrs, other.addrs, equalRecSlices)
}
// refresh gets the data from specified files and propagates the updates if
// needed.
//
@@ -205,21 +295,26 @@ func (hc *HostsContainer) sendUpd(recs *hostsfile.DefaultStorage) {
func (hc *HostsContainer) refresh() (err error) {
log.Debug("%s: refreshing", hostsContainerPrefix)
// The error is always nil here since no readers passed.
strg, _ := hostsfile.NewDefaultStorage()
_, err = aghos.FileWalker(func(r io.Reader) (patterns []string, cont bool, err error) {
// Don't wrap the error since it's already informative enough as is.
return nil, true, hostsfile.Parse(strg, r, nil)
}).Walk(hc.fsys, hc.patterns...)
var addrLen, nameLen int
last := hc.current.Load()
if last != nil {
addrLen, nameLen = len(last.addrs), len(last.names)
}
idx := &hostsIndex{
addrs: make(Hosts, addrLen),
names: make(map[string][]*hostsfile.Record, nameLen),
}
_, err = aghos.FileWalker(idx.walk).Walk(hc.fsys, hc.patterns...)
if err != nil {
// Don't wrap the error since it's informative enough as is.
return err
}
// TODO(e.burkov): Serialize updates using [time.Time].
if !hc.current.Load().Equal(strg) {
hc.current.Store(strg)
hc.sendUpd(strg)
// TODO(e.burkov): Serialize updates using time.
if !last.Equal(idx) {
hc.current.Store(idx)
hc.sendUpd(idx.addrs)
}
return nil

View File

@@ -0,0 +1,17 @@
//go:build linux
package aghnet
import (
"github.com/AdguardTeam/AdGuardHome/internal/aghos"
)
func defaultHostsPaths() (paths []string) {
paths = []string{"etc/hosts"}
if aghos.IsOpenWrt() {
paths = append(paths, "tmp/hosts")
}
return paths
}

View File

@@ -0,0 +1,7 @@
//go:build !(windows || linux)
package aghnet
func defaultHostsPaths() (paths []string) {
return []string{"etc/hosts"}
}

View File

@@ -3,11 +3,13 @@ package aghnet_test
import (
"net/netip"
"path"
"path/filepath"
"sync/atomic"
"testing"
"testing/fstest"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/aghchan"
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
"github.com/AdguardTeam/AdGuardHome/internal/aghtest"
"github.com/AdguardTeam/golibs/errors"
@@ -18,6 +20,139 @@ import (
"github.com/stretchr/testify/require"
)
// nl is a newline character.
const nl = "\n"
// Variables mirroring the etc_hosts file from testdata.
var (
addr1000 = netip.MustParseAddr("1.0.0.0")
addr1001 = netip.MustParseAddr("1.0.0.1")
addr1002 = netip.MustParseAddr("1.0.0.2")
addr1003 = netip.MustParseAddr("1.0.0.3")
addr1004 = netip.MustParseAddr("1.0.0.4")
addr1357 = netip.MustParseAddr("1.3.5.7")
addr4216 = netip.MustParseAddr("4.2.1.6")
addr7531 = netip.MustParseAddr("7.5.3.1")
addr0 = netip.MustParseAddr("::")
addr1 = netip.MustParseAddr("::1")
addr2 = netip.MustParseAddr("::2")
addr3 = netip.MustParseAddr("::3")
addr4 = netip.MustParseAddr("::4")
addr42 = netip.MustParseAddr("::42")
addr13 = netip.MustParseAddr("::13")
addr31 = netip.MustParseAddr("::31")
hostsSrc = "./" + filepath.Join("./testdata", "etc_hosts")
testHosts = map[netip.Addr][]*hostsfile.Record{
addr1000: {{
Addr: addr1000,
Source: hostsSrc,
Names: []string{"hello", "hello.world"},
}, {
Addr: addr1000,
Source: hostsSrc,
Names: []string{"hello.world.again"},
}, {
Addr: addr1000,
Source: hostsSrc,
Names: []string{"hello.world"},
}},
addr1001: {{
Addr: addr1001,
Source: hostsSrc,
Names: []string{"simplehost"},
}, {
Addr: addr1001,
Source: hostsSrc,
Names: []string{"simplehost"},
}},
addr1002: {{
Addr: addr1002,
Source: hostsSrc,
Names: []string{"a.whole", "lot.of", "aliases", "for.testing"},
}},
addr1003: {{
Addr: addr1003,
Source: hostsSrc,
Names: []string{"*"},
}},
addr1004: {{
Addr: addr1004,
Source: hostsSrc,
Names: []string{"*.com"},
}},
addr1357: {{
Addr: addr1357,
Source: hostsSrc,
Names: []string{"domain4", "domain4.alias"},
}},
addr7531: {{
Addr: addr7531,
Source: hostsSrc,
Names: []string{"domain4.alias", "domain4"},
}},
addr4216: {{
Addr: addr4216,
Source: hostsSrc,
Names: []string{"domain", "domain.alias"},
}},
addr0: {{
Addr: addr0,
Source: hostsSrc,
Names: []string{"hello", "hello.world"},
}, {
Addr: addr0,
Source: hostsSrc,
Names: []string{"hello.world.again"},
}, {
Addr: addr0,
Source: hostsSrc,
Names: []string{"hello.world"},
}},
addr1: {{
Addr: addr1,
Source: hostsSrc,
Names: []string{"simplehost"},
}, {
Addr: addr1,
Source: hostsSrc,
Names: []string{"simplehost"},
}},
addr2: {{
Addr: addr2,
Source: hostsSrc,
Names: []string{"a.whole", "lot.of", "aliases", "for.testing"},
}},
addr3: {{
Addr: addr3,
Source: hostsSrc,
Names: []string{"*"},
}},
addr4: {{
Addr: addr4,
Source: hostsSrc,
Names: []string{"*.com"},
}},
addr42: {{
Addr: addr42,
Source: hostsSrc,
Names: []string{"domain.alias", "domain"},
}},
addr13: {{
Addr: addr13,
Source: hostsSrc,
Names: []string{"domain6", "domain6.alias"},
}},
addr31: {{
Addr: addr31,
Source: hostsSrc,
Names: []string{"domain6.alias", "domain6"},
}},
}
)
func TestNewHostsContainer(t *testing.T) {
const dirname = "dir"
const filename = "file1"
@@ -132,21 +267,7 @@ func TestHostsContainer_refresh(t *testing.T) {
anotherIPStr := "1.2.3.4"
anotherIP := netip.MustParseAddr(anotherIPStr)
r1 := &hostsfile.Record{
Addr: ip,
Source: "file1",
Names: []string{"hostname"},
}
r2 := &hostsfile.Record{
Addr: anotherIP,
Source: "file2",
Names: []string{"alias"},
}
r1Data, _ := r1.MarshalText()
r2Data, _ := r2.MarshalText()
testFS := fstest.MapFS{"dir/file1": &fstest.MapFile{Data: r1Data}}
testFS := fstest.MapFS{"dir/file1": &fstest.MapFile{Data: []byte(ipStr + ` hostname` + nl)}}
// event is a convenient alias for an empty struct{} to emit test events.
type event = struct{}
@@ -168,47 +289,172 @@ func TestHostsContainer_refresh(t *testing.T) {
require.NoError(t, err)
testutil.CleanupAndRequireSuccess(t, hc.Close)
strg, _ := hostsfile.NewDefaultStorage()
strg.Add(r1)
checkRefresh := func(t *testing.T, want aghnet.Hosts) {
t.Helper()
upd, ok := aghchan.MustReceive(hc.Upd(), 1*time.Second)
require.True(t, ok)
assert.Equal(t, want, upd)
}
t.Run("initial_refresh", func(t *testing.T) {
upd, ok := testutil.RequireReceive(t, hc.Upd(), 1*time.Second)
require.True(t, ok)
assert.True(t, strg.Equal(upd))
checkRefresh(t, aghnet.Hosts{
ip: {{
Addr: ip,
Source: "file1",
Names: []string{"hostname"},
}},
})
})
strg.Add(r2)
t.Run("second_refresh", func(t *testing.T) {
testFS["dir/file2"] = &fstest.MapFile{Data: r2Data}
testFS["dir/file2"] = &fstest.MapFile{Data: []byte(anotherIPStr + ` alias` + nl)}
eventsCh <- event{}
upd, ok := testutil.RequireReceive(t, hc.Upd(), 1*time.Second)
require.True(t, ok)
assert.True(t, strg.Equal(upd))
checkRefresh(t, aghnet.Hosts{
ip: {{
Addr: ip,
Source: "file1",
Names: []string{"hostname"},
}},
anotherIP: {{
Addr: anotherIP,
Source: "file2",
Names: []string{"alias"},
}},
})
})
t.Run("double_refresh", func(t *testing.T) {
// Make a change once.
testFS["dir/file1"] = &fstest.MapFile{Data: []byte(ipStr + " alias\n")}
testFS["dir/file1"] = &fstest.MapFile{Data: []byte(ipStr + ` alias` + nl)}
eventsCh <- event{}
// Require the changes are written.
current, ok := testutil.RequireReceive(t, hc.Upd(), 1*time.Second)
require.True(t, ok)
require.Eventually(t, func() bool {
ips := hc.MatchName("hostname")
require.Empty(t, current.ByName("hostname"))
return len(ips) == 0
}, 5*time.Second, time.Second/2)
// Make a change again.
testFS["dir/file2"] = &fstest.MapFile{Data: []byte(ipStr + " hostname\n")}
testFS["dir/file2"] = &fstest.MapFile{Data: []byte(ipStr + ` hostname` + nl)}
eventsCh <- event{}
// Require the changes are written.
current, ok = testutil.RequireReceive(t, hc.Upd(), 1*time.Second)
require.True(t, ok)
require.Eventually(t, func() bool {
ips := hc.MatchName("hostname")
require.NotEmpty(t, current.ByName("hostname"))
return len(ips) > 0
}, 5*time.Second, time.Second/2)
assert.Len(t, hc.Upd(), 1)
})
}
func TestHostsContainer_MatchName(t *testing.T) {
require.NoError(t, fstest.TestFS(testdata, "etc_hosts"))
stubWatcher := aghtest.FSWatcher{
OnEvents: func() (e <-chan struct{}) { return nil },
OnAdd: func(name string) (err error) { return nil },
OnClose: func() (err error) { return nil },
}
testCases := []struct {
req string
name string
want []*hostsfile.Record
}{{
req: "simplehost",
name: "simple",
want: append(testHosts[addr1001], testHosts[addr1]...),
}, {
req: "hello.world",
name: "hello_alias",
want: []*hostsfile.Record{
testHosts[addr1000][0],
testHosts[addr1000][2],
testHosts[addr0][0],
testHosts[addr0][2],
},
}, {
req: "hello.world.again",
name: "other_line_alias",
want: []*hostsfile.Record{
testHosts[addr1000][1],
testHosts[addr0][1],
},
}, {
req: "say.hello",
name: "hello_subdomain",
want: nil,
}, {
req: "say.hello.world",
name: "hello_alias_subdomain",
want: nil,
}, {
req: "for.testing",
name: "lots_of_aliases",
want: append(testHosts[addr1002], testHosts[addr2]...),
}, {
req: "nonexistent.example",
name: "non-existing",
want: nil,
}, {
req: "domain",
name: "issue_4216_4_6",
want: append(testHosts[addr4216], testHosts[addr42]...),
}, {
req: "domain4",
name: "issue_4216_4",
want: append(testHosts[addr1357], testHosts[addr7531]...),
}, {
req: "domain6",
name: "issue_4216_6",
want: append(testHosts[addr13], testHosts[addr31]...),
}}
hc, err := aghnet.NewHostsContainer(testdata, &stubWatcher, "etc_hosts")
require.NoError(t, err)
testutil.CleanupAndRequireSuccess(t, hc.Close)
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
recs := hc.MatchName(tc.req)
assert.Equal(t, tc.want, recs)
})
}
}
func TestHostsContainer_MatchAddr(t *testing.T) {
require.NoError(t, fstest.TestFS(testdata, "etc_hosts"))
stubWatcher := aghtest.FSWatcher{
OnEvents: func() (e <-chan struct{}) { return nil },
OnAdd: func(name string) (err error) { return nil },
OnClose: func() (err error) { return nil },
}
hc, err := aghnet.NewHostsContainer(testdata, &stubWatcher, "etc_hosts")
require.NoError(t, err)
testutil.CleanupAndRequireSuccess(t, hc.Close)
testCases := []struct {
req netip.Addr
name string
want []*hostsfile.Record
}{{
req: netip.AddrFrom4([4]byte{1, 0, 0, 1}),
name: "reverse",
want: testHosts[addr1001],
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
recs := hc.MatchAddr(tc.req)
assert.Equal(t, tc.want, recs)
})
}
}

View File

@@ -0,0 +1,32 @@
//go:build windows
package aghnet
import (
"os"
"path"
"path/filepath"
"strings"
"github.com/AdguardTeam/golibs/log"
"golang.org/x/sys/windows"
)
func defaultHostsPaths() (paths []string) {
sysDir, err := windows.GetSystemDirectory()
if err != nil {
log.Error("aghnet: getting system directory: %s", err)
return []string{}
}
// Split all the elements of the path to join them afterwards. This is
// needed to make the Windows-specific path string returned by
// windows.GetSystemDirectory to be compatible with fs.FS.
pathElems := strings.Split(sysDir, string(os.PathSeparator))
if len(pathElems) > 0 && pathElems[0] == filepath.VolumeName(sysDir) {
pathElems = pathElems[1:]
}
return []string{path.Join(append(pathElems, "drivers/etc/hosts")...)}
}

View File

@@ -10,11 +10,9 @@ import (
"net"
"net/netip"
"net/url"
"strings"
"syscall"
"github.com/AdguardTeam/AdGuardHome/internal/aghos"
"github.com/AdguardTeam/dnsproxy/upstream"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
)
@@ -309,50 +307,6 @@ func ParseAddrPort(s string, defaultPort uint16) (ipp netip.AddrPort, err error)
return ipp, nil
}
// ParseSubnet parses s either as a CIDR prefix itself, or as an IP address,
// returning the corresponding single-IP CIDR prefix.
//
// TODO(e.burkov): Taken from dnsproxy, move to golibs.
func ParseSubnet(s string) (p netip.Prefix, err error) {
if strings.Contains(s, "/") {
p, err = netip.ParsePrefix(s)
if err != nil {
return netip.Prefix{}, err
}
} else {
var ip netip.Addr
ip, err = netip.ParseAddr(s)
if err != nil {
return netip.Prefix{}, err
}
p = netip.PrefixFrom(ip, ip.BitLen())
}
return p, nil
}
// ParseBootstraps returns the slice of upstream resolvers parsed from addrs.
// It additionally returns the closers for each resolver, that should be closed
// after use.
func ParseBootstraps(
addrs []string,
opts *upstream.Options,
) (boots []*upstream.UpstreamResolver, err error) {
boots = make([]*upstream.UpstreamResolver, 0, len(boots))
for i, b := range addrs {
var r *upstream.UpstreamResolver
r, err = upstream.NewUpstreamResolver(b, opts)
if err != nil {
return nil, fmt.Errorf("bootstrap at index %d: %w", i, err)
}
boots = append(boots, r)
}
return boots, nil
}
// BroadcastFromPref calculates the broadcast IP address for p.
func BroadcastFromPref(p netip.Prefix) (bc netip.Addr) {
bc = p.Addr().Unmap()

View File

@@ -1,9 +1,11 @@
package aghnet_test
import (
"io/fs"
"net"
"net/netip"
"net/url"
"os"
"testing"
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
@@ -16,6 +18,9 @@ func TestMain(m *testing.M) {
testutil.DiscardLogOutput(m)
}
// testdata is the filesystem containing data for testing the package.
var testdata fs.FS = os.DirFS("./testdata")
func TestParseAddrPort(t *testing.T) {
const defaultPort = 1

38
internal/aghnet/testdata/etc_hosts vendored Normal file
View File

@@ -0,0 +1,38 @@
#
# Test /etc/hosts file
#
1.0.0.1 simplehost
1.0.0.0 hello hello.world
# See https://github.com/AdguardTeam/AdGuardHome/issues/3846.
1.0.0.2 a.whole lot.of aliases for.testing
# See https://github.com/AdguardTeam/AdGuardHome/issues/3946.
1.0.0.3 *
1.0.0.4 *.com
# See https://github.com/AdguardTeam/AdGuardHome/issues/4079.
1.0.0.0 hello.world.again
# Duplicates of a main host and an alias.
1.0.0.1 simplehost
1.0.0.0 hello.world
# Same for IPv6.
::1 simplehost
:: hello hello.world
::2 a.whole lot.of aliases for.testing
::3 *
::4 *.com
:: hello.world.again
::1 simplehost
:: hello.world
# See https://github.com/AdguardTeam/AdGuardHome/issues/4216.
4.2.1.6 domain domain.alias
::42 domain.alias domain
1.3.5.7 domain4 domain4.alias
7.5.3.1 domain4.alias domain4
::13 domain6 domain6.alias
::31 domain6.alias domain6

1
internal/aghnet/testdata/ifaces vendored Normal file
View File

@@ -0,0 +1 @@
iface sample_name inet static

View File

@@ -0,0 +1,5 @@
# The "testdata" part is added here because the test is actually run from the
# parent directory. Real interface files usually contain only absolute paths.
source ./testdata/ifaces
source ./testdata/*

View File

@@ -11,7 +11,6 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/next/agh"
"github.com/AdguardTeam/AdGuardHome/internal/rdns"
"github.com/AdguardTeam/AdGuardHome/internal/whois"
"github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/dnsproxy/upstream"
"github.com/miekg/dns"
)
@@ -117,26 +116,6 @@ func (p *AddressUpdater) UpdateAddress(ip netip.Addr, host string, info *whois.I
p.OnUpdateAddress(ip, host, info)
}
// Package dnsforward
// ClientsContainer is a fake [dnsforward.ClientsContainer] implementation for
// tests.
type ClientsContainer struct {
OnUpstreamConfigByID func(
id string,
boot upstream.Resolver,
) (conf *proxy.CustomUpstreamConfig, err error)
}
// UpstreamConfigByID implements the [dnsforward.ClientsContainer] interface
// for *ClientsContainer.
func (c *ClientsContainer) UpstreamConfigByID(
id string,
boot upstream.Resolver,
) (conf *proxy.CustomUpstreamConfig, err error) {
return c.OnUpstreamConfigByID(id, boot)
}
// Package filtering
// Resolver is a fake [filtering.Resolver] implementation for tests.

View File

@@ -2,7 +2,6 @@ package aghtest_test
import (
"github.com/AdguardTeam/AdGuardHome/internal/aghtest"
"github.com/AdguardTeam/AdGuardHome/internal/dnsforward"
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
)
@@ -10,6 +9,3 @@ import (
// type check
var _ filtering.Resolver = (*aghtest.Resolver)(nil)
// type check
var _ dnsforward.ClientsContainer = (*aghtest.ClientsContainer)(nil)

View File

@@ -7,16 +7,11 @@ import (
"testing"
"github.com/AdguardTeam/AdGuardHome/internal/confmigrate"
"github.com/AdguardTeam/golibs/testutil"
"github.com/stretchr/testify/require"
"golang.org/x/crypto/bcrypt"
yaml "gopkg.in/yaml.v3"
)
func TestMain(m *testing.M) {
testutil.DiscardLogOutput(m)
}
// testdata is a virtual filesystem containing test data.
var testdata = os.DirFS("testdata")

View File

@@ -8,7 +8,6 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
"github.com/AdguardTeam/AdGuardHome/internal/dhcpsvc"
"github.com/AdguardTeam/golibs/errors"
)
@@ -50,16 +49,16 @@ type ServerConfig struct {
// DHCPServer - DHCP server interface
type DHCPServer interface {
// ResetLeases resets leases.
ResetLeases(leases []*dhcpsvc.Lease) (err error)
ResetLeases(leases []*Lease) (err error)
// GetLeases returns deep clones of the current leases.
GetLeases(flags GetLeasesFlags) (leases []*dhcpsvc.Lease)
GetLeases(flags GetLeasesFlags) (leases []*Lease)
// AddStaticLease - add a static lease
AddStaticLease(l *dhcpsvc.Lease) (err error)
AddStaticLease(l *Lease) (err error)
// RemoveStaticLease - remove a static lease
RemoveStaticLease(l *dhcpsvc.Lease) (err error)
RemoveStaticLease(l *Lease) (err error)
// UpdateStaticLease updates IP, hostname of the lease.
UpdateStaticLease(l *dhcpsvc.Lease) (err error)
UpdateStaticLease(l *Lease) (err error)
// FindMACbyIP returns a MAC address by the IP address of its lease, if
// there is one.
@@ -82,7 +81,7 @@ type DHCPServer interface {
Start() (err error)
// Stop - stop server
Stop() (err error)
getLeasesRef() []*dhcpsvc.Lease
getLeasesRef() []*Lease
}
// V4ServerConf - server configuration

View File

@@ -5,13 +5,9 @@ package dhcpd
import (
"encoding/json"
"fmt"
"net"
"net/netip"
"os"
"strings"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/dhcpsvc"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/google/renameio/v2/maybe"
@@ -32,60 +28,7 @@ type dataLeases struct {
Version int `json:"version"`
// Leases is the list containing stored DHCP leases.
Leases []*dbLease `json:"leases"`
}
// dbLease is the structure of stored lease.
type dbLease struct {
Expiry string `json:"expires"`
IP netip.Addr `json:"ip"`
Hostname string `json:"hostname"`
HWAddr string `json:"mac"`
IsStatic bool `json:"static"`
}
// fromLease converts *dhcpsvc.Lease to *dbLease.
func fromLease(l *dhcpsvc.Lease) (dl *dbLease) {
var expiryStr string
if !l.IsStatic {
// The front-end is waiting for RFC 3999 format of the time value. It
// also shouldn't got an Expiry field for static leases.
//
// See https://github.com/AdguardTeam/AdGuardHome/issues/2692.
expiryStr = l.Expiry.Format(time.RFC3339)
}
return &dbLease{
Expiry: expiryStr,
Hostname: l.Hostname,
HWAddr: l.HWAddr.String(),
IP: l.IP,
IsStatic: l.IsStatic,
}
}
// toLease converts *dbLease to *dhcpsvc.Lease.
func (dl *dbLease) toLease() (l *dhcpsvc.Lease, err error) {
mac, err := net.ParseMAC(dl.HWAddr)
if err != nil {
return nil, fmt.Errorf("parsing hardware address: %w", err)
}
expiry := time.Time{}
if !dl.IsStatic {
expiry, err = time.Parse(time.RFC3339, dl.Expiry)
if err != nil {
return nil, fmt.Errorf("parsing expiry time: %w", err)
}
}
return &dhcpsvc.Lease{
Expiry: expiry,
IP: dl.IP,
Hostname: dl.Hostname,
HWAddr: mac,
IsStatic: dl.IsStatic,
}, nil
Leases []*Lease `json:"leases"`
}
// dbLoad loads stored leases.
@@ -106,22 +49,15 @@ func (s *server) dbLoad() (err error) {
}
leases := dl.Leases
leases4 := []*dhcpsvc.Lease{}
leases6 := []*dhcpsvc.Lease{}
leases4 := []*Lease{}
leases6 := []*Lease{}
for _, l := range leases {
var lease *dhcpsvc.Lease
lease, err = l.toLease()
if err != nil {
log.Info("dhcp: invalid lease: %s", err)
continue
}
if lease.IP.Is4() {
leases4 = append(leases4, lease)
if l.IP.Is4() {
leases4 = append(leases4, l)
} else {
leases6 = append(leases6, lease)
leases6 = append(leases6, l)
}
}
@@ -137,12 +73,8 @@ func (s *server) dbLoad() (err error) {
}
}
log.Info(
"dhcp: loaded leases v4:%d v6:%d total-read:%d from DB",
len(leases4),
len(leases6),
len(leases),
)
log.Info("dhcp: loaded leases v4:%d v6:%d total-read:%d from DB",
len(leases4), len(leases6), len(leases))
return nil
}
@@ -151,26 +83,24 @@ func (s *server) dbLoad() (err error) {
func (s *server) dbStore() (err error) {
// Use an empty slice here as opposed to nil so that it doesn't write
// "null" into the database file if leases are empty.
leases := []*dbLease{}
leases := []*Lease{}
for _, l := range s.srv4.getLeasesRef() {
leases = append(leases, fromLease(l))
}
leases4 := s.srv4.getLeasesRef()
leases = append(leases, leases4...)
if s.srv6 != nil {
for _, l := range s.srv6.getLeasesRef() {
leases = append(leases, fromLease(l))
}
leases6 := s.srv6.getLeasesRef()
leases = append(leases, leases6...)
}
return writeDB(s.conf.dbFilePath, leases)
}
// writeDB writes leases to file at path.
func writeDB(path string, leases []*dbLease) (err error) {
func writeDB(path string, leases []*Lease) (err error) {
defer func() { err = errors.Annotate(err, "writing db: %w") }()
slices.SortFunc(leases, func(a, b *dbLease) (res int) {
slices.SortFunc(leases, func(a, b *Lease) (res int) {
return strings.Compare(a.Hostname, b.Hostname)
})

View File

@@ -2,6 +2,7 @@
package dhcpd
import (
"encoding/json"
"fmt"
"net"
"net/netip"
@@ -11,6 +12,7 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/dhcpsvc"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/timeutil"
"golang.org/x/exp/slices"
)
const (
@@ -27,6 +29,105 @@ const (
defaultBackoff time.Duration = 500 * time.Millisecond
)
// Lease contains the necessary information about a DHCP lease. It's used as is
// in the database, so don't change it until it's absolutely necessary, see
// [dataVersion].
//
// TODO(e.burkov): Unexport it and use [dhcpsvc.Lease].
type Lease struct {
// Expiry is the expiration time of the lease.
Expiry time.Time `json:"expires"`
// Hostname of the client.
Hostname string `json:"hostname"`
// HWAddr is the physical hardware address (MAC address).
HWAddr net.HardwareAddr `json:"mac"`
// IP is the IP address leased to the client.
IP netip.Addr `json:"ip"`
// IsStatic defines if the lease is static.
IsStatic bool `json:"static"`
}
// Clone returns a deep copy of l.
func (l *Lease) Clone() (clone *Lease) {
if l == nil {
return nil
}
return &Lease{
Expiry: l.Expiry,
Hostname: l.Hostname,
HWAddr: slices.Clone(l.HWAddr),
IP: l.IP,
IsStatic: l.IsStatic,
}
}
// IsBlocklisted returns true if the lease is blocklisted.
//
// TODO(a.garipov): Just make it a boolean field.
func (l *Lease) IsBlocklisted() (ok bool) {
if len(l.HWAddr) == 0 {
return false
}
for _, b := range l.HWAddr {
if b != 0 {
return false
}
}
return true
}
// MarshalJSON implements the json.Marshaler interface for Lease.
func (l Lease) MarshalJSON() ([]byte, error) {
var expiryStr string
if !l.IsStatic {
// The front-end is waiting for RFC 3999 format of the time
// value. It also shouldn't got an Expiry field for static
// leases.
//
// See https://github.com/AdguardTeam/AdGuardHome/issues/2692.
expiryStr = l.Expiry.Format(time.RFC3339)
}
type lease Lease
return json.Marshal(&struct {
HWAddr string `json:"mac"`
Expiry string `json:"expires,omitempty"`
lease
}{
HWAddr: l.HWAddr.String(),
Expiry: expiryStr,
lease: lease(l),
})
}
// UnmarshalJSON implements the json.Unmarshaler interface for *Lease.
func (l *Lease) UnmarshalJSON(data []byte) (err error) {
type lease Lease
aux := struct {
*lease
HWAddr string `json:"mac"`
}{
lease: (*lease)(l),
}
if err = json.Unmarshal(data, &aux); err != nil {
return err
}
l.HWAddr, err = net.ParseMAC(aux.HWAddr)
if err != nil {
return fmt.Errorf("couldn't parse MAC address: %w", err)
}
return nil
}
// OnLeaseChangedT is a callback for lease changes.
type OnLeaseChangedT func(flags int)
@@ -269,7 +370,19 @@ func (s *server) Stop() (err error) {
// Leases returns the list of active DHCP leases.
func (s *server) Leases() (leases []*dhcpsvc.Lease) {
return append(s.srv4.GetLeases(LeasesAll), s.srv6.GetLeases(LeasesAll)...)
ls := append(s.srv4.GetLeases(LeasesAll), s.srv6.GetLeases(LeasesAll)...)
leases = make([]*dhcpsvc.Lease, len(ls))
for i, l := range ls {
leases[i] = &dhcpsvc.Lease{
Expiry: l.Expiry,
Hostname: l.Hostname,
HWAddr: l.HWAddr,
IP: l.IP,
IsStatic: l.IsStatic,
}
}
return leases
}
// MACByIP returns a MAC address by the IP address of its lease, if there is
@@ -301,6 +414,6 @@ func (s *server) IPByHost(host string) (ip netip.Addr) {
}
// AddStaticLease - add static v4 lease
func (s *server) AddStaticLease(l *dhcpsvc.Lease) error {
func (s *server) AddStaticLease(l *Lease) error {
return s.srv4.AddStaticLease(l)
}

View File

@@ -9,7 +9,6 @@ import (
"testing"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/dhcpsvc"
"github.com/AdguardTeam/golibs/testutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -45,7 +44,7 @@ func TestDB(t *testing.T) {
s.srv6, err = v6Create(V6ServerConf{})
require.NoError(t, err)
leases := []*dhcpsvc.Lease{{
leases := []*Lease{{
Expiry: time.Now().Add(time.Hour),
Hostname: "static-1.local",
HWAddr: net.HardwareAddr{0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA},

View File

@@ -93,13 +93,13 @@ func leasesToStatic(leases []*dhcpsvc.Lease) (static []*leaseStatic) {
}
// toLease converts leaseStatic to Lease or returns error.
func (l *leaseStatic) toLease() (lease *dhcpsvc.Lease, err error) {
func (l *leaseStatic) toLease() (lease *Lease, err error) {
addr, err := net.ParseMAC(l.HWAddr)
if err != nil {
return nil, fmt.Errorf("couldn't parse MAC address: %w", err)
}
return &dhcpsvc.Lease{
return &Lease{
HWAddr: addr,
IP: l.IP,
Hostname: l.Hostname,
@@ -593,7 +593,7 @@ func setOtherDHCPResult(ifaceName string, result *dhcpSearchResult) {
// parseLease parses a lease from r. If there is no error returns DHCPServer
// and *Lease. r must be non-nil.
func (s *server) parseLease(r io.Reader) (srv DHCPServer, lease *dhcpsvc.Lease, err error) {
func (s *server) parseLease(r io.Reader) (srv DHCPServer, lease *Lease, err error) {
l := &leaseStatic{}
err = json.NewDecoder(r).Decode(l)
if err != nil {

View File

@@ -2,7 +2,6 @@ package dhcpd
import (
"encoding/json"
"fmt"
"net"
"net/netip"
"os"
@@ -26,9 +25,9 @@ const (
dbFilename = "leases.db"
)
// leaseJSON is the structure of stored lease in a legacy database.
// leaseJSON is the structure of stored lease.
//
// Deprecated: Use [dbLease].
// Deprecated: Use [Lease].
type leaseJSON struct {
HWAddr []byte `json:"mac"`
IP []byte `json:"ip"`
@@ -36,28 +35,13 @@ type leaseJSON struct {
Expiry int64 `json:"exp"`
}
// readOldDB reads the old database from the given path.
func readOldDB(path string) (leases []*leaseJSON, err error) {
// #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(path)
if errors.Is(err, os.ErrNotExist) {
// Nothing to migrate.
return nil, nil
} else if err != nil {
// Don't wrap the error since it's informative enough as is.
return nil, err
}
defer func() { err = errors.WithDeferred(err, file.Close()) }()
leases = []*leaseJSON{}
err = json.NewDecoder(file).Decode(&leases)
if err != nil {
return nil, fmt.Errorf("decoding old db: %w", err)
func normalizeIP(ip net.IP) net.IP {
ip4 := ip.To4()
if ip4 != nil {
return ip4
}
return leases, nil
return ip
}
// migrateDB migrates stored leases if necessary.
@@ -67,50 +51,59 @@ func migrateDB(conf *ServerConfig) (err error) {
oldLeasesPath := filepath.Join(conf.WorkDir, dbFilename)
dataDirPath := filepath.Join(conf.DataDir, dataFilename)
oldLeases, err := readOldDB(oldLeasesPath)
// #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)
if errors.Is(err, os.ErrNotExist) {
// Nothing to migrate.
return nil
} else if err != nil {
// Don't wrap the error since it's informative enough as is.
return err
}
ljs := []leaseJSON{}
err = json.NewDecoder(file).Decode(&ljs)
if err != nil {
// Don't wrap the error since it's informative enough as is.
return err
} else if oldLeases == nil {
// Nothing to migrate.
return nil
}
leases := make([]*dbLease, 0, len(oldLeases))
for _, l := range oldLeases {
l.IP = normalizeIP(l.IP)
ip, ok := netip.AddrFromSlice(l.IP)
err = file.Close()
if err != nil {
// Don't wrap the error since it's informative enough as is.
return err
}
leases := []*Lease{}
for _, lj := range ljs {
lj.IP = normalizeIP(lj.IP)
ip, ok := netip.AddrFromSlice(lj.IP)
if !ok {
log.Info("dhcp: invalid IP: %s", l.IP)
log.Info("dhcp: invalid IP: %s", lj.IP)
continue
}
leases = append(leases, &dbLease{
Expiry: time.Unix(l.Expiry, 0).Format(time.RFC3339),
Hostname: l.Hostname,
HWAddr: net.HardwareAddr(l.HWAddr).String(),
lease := &Lease{
Expiry: time.Unix(lj.Expiry, 0),
Hostname: lj.Hostname,
HWAddr: lj.HWAddr,
IP: ip,
IsStatic: l.Expiry == leaseExpireStatic,
})
IsStatic: lj.Expiry == leaseExpireStatic,
}
leases = append(leases, lease)
}
err = writeDB(dataDirPath, leases)
if err != nil {
// Don't wrap the error since an annotation deferred already.
// Don't wrap the error since it's informative enough as is.
return err
}
return os.Remove(oldLeasesPath)
}
// normalizeIP converts the given IP address to IPv4 if it's IPv4-mapped IPv6,
// or leaves it as is otherwise.
func normalizeIP(ip net.IP) (normalized net.IP) {
normalized = ip.To4()
if normalized != nil {
return normalized
}
return ip
}

View File

@@ -2,6 +2,7 @@ package dhcpd
import (
"encoding/json"
"net"
"net/netip"
"os"
"path/filepath"
@@ -26,16 +27,16 @@ func TestMigrateDB(t *testing.T) {
err := os.WriteFile(oldLeasesPath, []byte(testData), 0o644)
require.NoError(t, err)
wantLeases := []*dbLease{{
Expiry: time.Unix(1, 0).Format(time.RFC3339),
wantLeases := []*Lease{{
Expiry: time.Time{},
Hostname: "test1",
HWAddr: "11:22:33:44:55:66",
HWAddr: net.HardwareAddr{0x11, 0x22, 0x33, 0x44, 0x55, 0x66},
IP: netip.MustParseAddr("1.2.3.4"),
IsStatic: true,
}, {
Expiry: time.Unix(1231231231, 0).Format(time.RFC3339),
Expiry: time.Unix(1231231231, 0),
Hostname: "test2",
HWAddr: "66:55:44:33:22:11",
HWAddr: net.HardwareAddr{0x66, 0x55, 0x44, 0x33, 0x22, 0x11},
IP: netip.MustParseAddr("4.3.2.1"),
IsStatic: false,
}}
@@ -61,12 +62,12 @@ func TestMigrateDB(t *testing.T) {
leases := dl.Leases
for i, wantLease := range wantLeases {
assert.Equal(t, wantLease.Hostname, leases[i].Hostname)
assert.Equal(t, wantLease.HWAddr, leases[i].HWAddr)
assert.Equal(t, wantLease.IP, leases[i].IP)
assert.Equal(t, wantLease.IsStatic, leases[i].IsStatic)
for i, wl := range wantLeases {
assert.Equal(t, wl.Hostname, leases[i].Hostname)
assert.Equal(t, wl.HWAddr, leases[i].HWAddr)
assert.Equal(t, wl.IP, leases[i].IP)
assert.Equal(t, wl.IsStatic, leases[i].IsStatic)
require.Equal(t, wantLease.Expiry, leases[i].Expiry)
require.True(t, wl.Expiry.Equal(leases[i].Expiry))
}
}

View File

@@ -7,8 +7,6 @@ package dhcpd
import (
"net"
"net/netip"
"github.com/AdguardTeam/AdGuardHome/internal/dhcpsvc"
)
type winServer struct{}
@@ -16,19 +14,19 @@ type winServer struct{}
// type check
var _ DHCPServer = winServer{}
func (winServer) ResetLeases(_ []*dhcpsvc.Lease) (err error) { return nil }
func (winServer) GetLeases(_ GetLeasesFlags) (leases []*dhcpsvc.Lease) { return nil }
func (winServer) getLeasesRef() []*dhcpsvc.Lease { return nil }
func (winServer) AddStaticLease(_ *dhcpsvc.Lease) (err error) { return nil }
func (winServer) RemoveStaticLease(_ *dhcpsvc.Lease) (err error) { return nil }
func (winServer) UpdateStaticLease(_ *dhcpsvc.Lease) (err error) { return nil }
func (winServer) FindMACbyIP(_ netip.Addr) (mac net.HardwareAddr) { return nil }
func (winServer) WriteDiskConfig4(_ *V4ServerConf) {}
func (winServer) WriteDiskConfig6(_ *V6ServerConf) {}
func (winServer) Start() (err error) { return nil }
func (winServer) Stop() (err error) { return nil }
func (winServer) HostByIP(_ netip.Addr) (host string) { return "" }
func (winServer) IPByHost(_ string) (ip netip.Addr) { return netip.Addr{} }
func (winServer) ResetLeases(_ []*Lease) (err error) { return nil }
func (winServer) GetLeases(_ GetLeasesFlags) (leases []*Lease) { return nil }
func (winServer) getLeasesRef() []*Lease { return nil }
func (winServer) AddStaticLease(_ *Lease) (err error) { return nil }
func (winServer) RemoveStaticLease(_ *Lease) (err error) { return nil }
func (winServer) UpdateStaticLease(_ *Lease) (err error) { return nil }
func (winServer) FindMACbyIP(_ netip.Addr) (mac net.HardwareAddr) { return nil }
func (winServer) WriteDiskConfig4(_ *V4ServerConf) {}
func (winServer) WriteDiskConfig6(_ *V6ServerConf) {}
func (winServer) Start() (err error) { return nil }
func (winServer) Stop() (err error) { return nil }
func (winServer) HostByIP(_ netip.Addr) (host string) { return "" }
func (winServer) IPByHost(_ string) (ip netip.Addr) { return netip.Addr{} }
func v4Create(_ *V4ServerConf) (s DHCPServer, err error) { return winServer{}, nil }
func v6Create(_ V6ServerConf) (s DHCPServer, err error) { return winServer{}, nil }

View File

@@ -12,7 +12,6 @@ import (
"time"
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
"github.com/AdguardTeam/AdGuardHome/internal/dhcpsvc"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/netutil"
@@ -39,7 +38,7 @@ type v4Server struct {
// have intersections with [implicitOpts].
explicitOpts dhcpv4.Options
// leasesLock protects leases, hostsIndex, ipIndex, and leasedOffsets.
// leasesLock protects leases, leaseHosts, and leasedOffsets.
leasesLock sync.Mutex
// leasedOffsets contains offsets from conf.ipRange.start that have been
@@ -47,13 +46,13 @@ type v4Server struct {
leasedOffsets *bitSet
// leases contains all dynamic and static leases.
leases []*dhcpsvc.Lease
leases []*Lease
// hostsIndex is the set of all hostnames of all known DHCP clients.
hostsIndex map[string]*dhcpsvc.Lease
hostsIndex map[string]*Lease
// ipIndex is an index of leases by their IP addresses.
ipIndex map[netip.Addr]*dhcpsvc.Lease
ipIndex map[netip.Addr]*Lease
}
func (s *v4Server) enabled() (ok bool) {
@@ -142,7 +141,7 @@ func (s *v4Server) IPByHost(host string) (ip netip.Addr) {
}
// ResetLeases resets leases.
func (s *v4Server) ResetLeases(leases []*dhcpsvc.Lease) (err error) {
func (s *v4Server) ResetLeases(leases []*Lease) (err error) {
defer func() { err = errors.Annotate(err, "dhcpv4: %w") }()
if s.conf == nil {
@@ -153,8 +152,8 @@ func (s *v4Server) ResetLeases(leases []*dhcpsvc.Lease) (err error) {
defer s.leasesLock.Unlock()
s.leasedOffsets = newBitSet()
s.hostsIndex = make(map[string]*dhcpsvc.Lease, len(leases))
s.ipIndex = make(map[netip.Addr]*dhcpsvc.Lease, len(leases))
s.hostsIndex = make(map[string]*Lease, len(leases))
s.ipIndex = make(map[netip.Addr]*Lease, len(leases))
s.leases = nil
for _, l := range leases {
@@ -174,14 +173,14 @@ func (s *v4Server) ResetLeases(leases []*dhcpsvc.Lease) (err error) {
}
// getLeasesRef returns the actual leases slice. For internal use only.
func (s *v4Server) getLeasesRef() []*dhcpsvc.Lease {
func (s *v4Server) getLeasesRef() []*Lease {
return s.leases
}
// isBlocklisted returns true if this lease holds a blocklisted IP.
//
// TODO(a.garipov): Make a method of *Lease?
func (s *v4Server) isBlocklisted(l *dhcpsvc.Lease) (ok bool) {
func (s *v4Server) isBlocklisted(l *Lease) (ok bool) {
if len(l.HWAddr) == 0 {
return false
}
@@ -197,11 +196,11 @@ func (s *v4Server) isBlocklisted(l *dhcpsvc.Lease) (ok bool) {
// GetLeases returns the list of current DHCP leases. It is safe for concurrent
// use.
func (s *v4Server) GetLeases(flags GetLeasesFlags) (leases []*dhcpsvc.Lease) {
func (s *v4Server) GetLeases(flags GetLeasesFlags) (leases []*Lease) {
// The function shouldn't return nil, because zero-length slice behaves
// differently in cases like marshalling. Our front-end also requires
// a non-nil value in the response.
leases = []*dhcpsvc.Lease{}
leases = []*Lease{}
getDynamic := flags&LeasesDynamic != 0
getStatic := flags&LeasesStatic != 0
@@ -249,7 +248,7 @@ func (s *v4Server) FindMACbyIP(ip netip.Addr) (mac net.HardwareAddr) {
const defaultHwAddrLen = 6
// Add the specified IP to the black list for a time period
func (s *v4Server) blocklistLease(l *dhcpsvc.Lease) {
func (s *v4Server) blocklistLease(l *Lease) {
l.HWAddr = make(net.HardwareAddr, defaultHwAddrLen)
l.Hostname = ""
l.Expiry = time.Now().Add(s.conf.leaseTime)
@@ -285,7 +284,7 @@ func (s *v4Server) rmLeaseByIndex(i int) {
// Return error if a static lease is found
//
// TODO(s.chzhen): Refactor the code.
func (s *v4Server) rmDynamicLease(lease *dhcpsvc.Lease) (err error) {
func (s *v4Server) rmDynamicLease(lease *Lease) (err error) {
for i, l := range s.leases {
isStatic := l.IsStatic
@@ -321,7 +320,7 @@ const (
)
// addLease adds a dynamic or static lease.
func (s *v4Server) addLease(l *dhcpsvc.Lease) (err error) {
func (s *v4Server) addLease(l *Lease) (err error) {
r := s.conf.ipRange
leaseIP := net.IP(l.IP.AsSlice())
offset, inOffset := r.offset(leaseIP)
@@ -353,7 +352,7 @@ func (s *v4Server) addLease(l *dhcpsvc.Lease) (err error) {
}
// rmLease removes a lease with the same properties.
func (s *v4Server) rmLease(lease *dhcpsvc.Lease) (err error) {
func (s *v4Server) rmLease(lease *Lease) (err error) {
if len(s.leases) == 0 {
return nil
}
@@ -379,7 +378,7 @@ const ErrUnconfigured errors.Error = "server is unconfigured"
// AddStaticLease implements the DHCPServer interface for *v4Server. It is
// safe for concurrent use.
func (s *v4Server) AddStaticLease(l *dhcpsvc.Lease) (err error) {
func (s *v4Server) AddStaticLease(l *Lease) (err error) {
defer func() { err = errors.Annotate(err, "dhcpv4: adding static lease: %w") }()
if s.conf == nil {
@@ -436,7 +435,7 @@ func (s *v4Server) AddStaticLease(l *dhcpsvc.Lease) (err error) {
}
// UpdateStaticLease updates IP, hostname of the static lease.
func (s *v4Server) UpdateStaticLease(l *dhcpsvc.Lease) (err error) {
func (s *v4Server) UpdateStaticLease(l *Lease) (err error) {
defer func() {
if err != nil {
err = errors.Annotate(err, "dhcpv4: updating static lease: %w")
@@ -475,7 +474,7 @@ func (s *v4Server) UpdateStaticLease(l *dhcpsvc.Lease) (err error) {
}
// validateStaticLease returns an error if the static lease is invalid.
func (s *v4Server) validateStaticLease(l *dhcpsvc.Lease) (err error) {
func (s *v4Server) validateStaticLease(l *Lease) (err error) {
hostname, err := normalizeHostname(l.Hostname)
if err != nil {
// Don't wrap the error, because it's informative enough as is.
@@ -512,7 +511,7 @@ func (s *v4Server) validateStaticLease(l *dhcpsvc.Lease) (err error) {
// updateStaticLease safe removes dynamic lease with the same properties and
// then adds a static lease l.
func (s *v4Server) updateStaticLease(l *dhcpsvc.Lease) (err error) {
func (s *v4Server) updateStaticLease(l *Lease) (err error) {
s.leasesLock.Lock()
defer s.leasesLock.Unlock()
@@ -530,7 +529,7 @@ func (s *v4Server) updateStaticLease(l *dhcpsvc.Lease) (err error) {
}
// RemoveStaticLease removes a static lease. It is safe for concurrent use.
func (s *v4Server) RemoveStaticLease(l *dhcpsvc.Lease) (err error) {
func (s *v4Server) RemoveStaticLease(l *Lease) (err error) {
defer func() { err = errors.Annotate(err, "dhcpv4: %w") }()
if s.conf == nil {
@@ -607,7 +606,7 @@ func (s *v4Server) addrAvailable(target net.IP) (avail bool) {
}
// findLease finds a lease by its MAC-address.
func (s *v4Server) findLease(mac net.HardwareAddr) (l *dhcpsvc.Lease) {
func (s *v4Server) findLease(mac net.HardwareAddr) (l *Lease) {
for _, l = range s.leases {
if bytes.Equal(mac, l.HWAddr) {
return l
@@ -647,8 +646,8 @@ func (s *v4Server) findExpiredLease() int {
// reserveLease reserves a lease for a client by its MAC-address. It returns
// nil if it couldn't allocate a new lease.
func (s *v4Server) reserveLease(mac net.HardwareAddr) (l *dhcpsvc.Lease, err error) {
l = &dhcpsvc.Lease{HWAddr: slices.Clone(mac)}
func (s *v4Server) reserveLease(mac net.HardwareAddr) (l *Lease, err error) {
l = &Lease{HWAddr: slices.Clone(mac)}
nextIP := s.nextIP()
if nextIP == nil {
@@ -680,7 +679,7 @@ func (s *v4Server) reserveLease(mac net.HardwareAddr) (l *dhcpsvc.Lease, err err
// commitLease refreshes l's values. It takes the desired hostname into account
// when setting it into the lease, but generates a unique one if the provided
// can't be used.
func (s *v4Server) commitLease(l *dhcpsvc.Lease, hostname string) {
func (s *v4Server) commitLease(l *Lease, hostname string) {
prev := l.Hostname
hostname = s.validHostnameForClient(hostname, l.IP)
@@ -710,7 +709,7 @@ func (s *v4Server) commitLease(l *dhcpsvc.Lease, hostname string) {
// allocateLease allocates a new lease for the MAC address. If there are no IP
// addresses left, both l and err are nil.
func (s *v4Server) allocateLease(mac net.HardwareAddr) (l *dhcpsvc.Lease, err error) {
func (s *v4Server) allocateLease(mac net.HardwareAddr) (l *Lease, err error) {
for {
l, err = s.reserveLease(mac)
if err != nil {
@@ -729,7 +728,7 @@ func (s *v4Server) allocateLease(mac net.HardwareAddr) (l *dhcpsvc.Lease, err er
}
// handleDiscover is the handler for the DHCP Discover request.
func (s *v4Server) handleDiscover(req, resp *dhcpv4.DHCPv4) (l *dhcpsvc.Lease, err error) {
func (s *v4Server) handleDiscover(req, resp *dhcpv4.DHCPv4) (l *Lease, err error) {
mac := req.ClientHWAddr
defer s.conf.notify(LeaseChangedDBStore)
@@ -788,7 +787,7 @@ func OptionFQDN(fqdn string) (opt dhcpv4.Option) {
// checkLease checks if the pair of mac and ip is already leased. The mismatch
// is true when the existing lease has the same hardware address but differs in
// its IP address.
func (s *v4Server) checkLease(mac net.HardwareAddr, ip net.IP) (l *dhcpsvc.Lease, mismatch bool) {
func (s *v4Server) checkLease(mac net.HardwareAddr, ip net.IP) (lease *Lease, mismatch bool) {
s.leasesLock.Lock()
defer s.leasesLock.Unlock()
@@ -799,7 +798,7 @@ func (s *v4Server) checkLease(mac net.HardwareAddr, ip net.IP) (l *dhcpsvc.Lease
return nil, false
}
for _, l = range s.leases {
for _, l := range s.leases {
if !bytes.Equal(l.HWAddr, mac) {
continue
}
@@ -824,7 +823,7 @@ func (s *v4Server) handleSelecting(
req *dhcpv4.DHCPv4,
reqIP net.IP,
sid net.IP,
) (l *dhcpsvc.Lease, needsReply bool) {
) (l *Lease, needsReply bool) {
// Client inserts the address of the selected server in server identifier,
// ciaddr MUST be zero.
mac := req.ClientHWAddr
@@ -858,10 +857,7 @@ func (s *v4Server) handleSelecting(
}
// handleInitReboot handles the DHCPREQUEST generated during INIT-REBOOT state.
func (s *v4Server) handleInitReboot(
req *dhcpv4.DHCPv4,
reqIP net.IP,
) (l *dhcpsvc.Lease, needsReply bool) {
func (s *v4Server) handleInitReboot(req *dhcpv4.DHCPv4, reqIP net.IP) (l *Lease, needsReply bool) {
mac := req.ClientHWAddr
ip4 := reqIP.To4()
@@ -903,7 +899,7 @@ func (s *v4Server) handleInitReboot(
// handleRenew handles the DHCPREQUEST generated during RENEWING or REBINDING
// state.
func (s *v4Server) handleRenew(req *dhcpv4.DHCPv4) (l *dhcpsvc.Lease, needsReply bool) {
func (s *v4Server) handleRenew(req *dhcpv4.DHCPv4) (l *Lease, needsReply bool) {
mac := req.ClientHWAddr
// ciaddr MUST be filled in with client's IP address.
@@ -930,7 +926,7 @@ func (s *v4Server) handleRenew(req *dhcpv4.DHCPv4) (l *dhcpsvc.Lease, needsReply
// handleByRequestType handles the DHCPREQUEST according to the state during
// which it's generated by client.
func (s *v4Server) handleByRequestType(req *dhcpv4.DHCPv4) (lease *dhcpsvc.Lease, needsReply bool) {
func (s *v4Server) handleByRequestType(req *dhcpv4.DHCPv4) (lease *Lease, needsReply bool) {
reqIP, sid := req.RequestedIPAddress(), req.ServerIdentifier()
if sid != nil && !sid.IsUnspecified() {
@@ -954,7 +950,7 @@ func (s *v4Server) handleByRequestType(req *dhcpv4.DHCPv4) (lease *dhcpsvc.Lease
// handleRequest is the handler for a DHCPREQUEST message.
//
// See https://datatracker.ietf.org/doc/html/rfc2131#section-4.3.2.
func (s *v4Server) handleRequest(req, resp *dhcpv4.DHCPv4) (lease *dhcpsvc.Lease, needsReply bool) {
func (s *v4Server) handleRequest(req, resp *dhcpv4.DHCPv4) (lease *Lease, needsReply bool) {
lease, needsReply = s.handleByRequestType(req)
if lease == nil {
return nil, needsReply
@@ -1047,7 +1043,7 @@ func (s *v4Server) handleDecline(req, resp *dhcpv4.DHCPv4) (err error) {
}
// findLeaseForIP returns a lease for provided ip and mac.
func (s *v4Server) findLeaseForIP(ip net.IP, mac net.HardwareAddr) (l *dhcpsvc.Lease) {
func (s *v4Server) findLeaseForIP(ip net.IP, mac net.HardwareAddr) (l *Lease) {
netIP, ok := netip.AddrFromSlice(ip)
if !ok {
log.Info("dhcpv4: invalid IP: %s", ip)
@@ -1110,11 +1106,7 @@ func (s *v4Server) handleRelease(req, resp *dhcpv4.DHCPv4) (err error) {
}
// messageHandler describes a DHCPv4 message handler function.
type messageHandler func(
s *v4Server,
req *dhcpv4.DHCPv4,
resp *dhcpv4.DHCPv4,
) (rCode int, l *dhcpsvc.Lease, err error)
type messageHandler func(s *v4Server, req, resp *dhcpv4.DHCPv4) (rCode int, l *Lease, err error)
// messageHandlers is a map of handlers for various messages with message types
// keys.
@@ -1123,7 +1115,7 @@ var messageHandlers = map[dhcpv4.MessageType]messageHandler{
s *v4Server,
req *dhcpv4.DHCPv4,
resp *dhcpv4.DHCPv4,
) (rCode int, l *dhcpsvc.Lease, err error) {
) (rCode int, l *Lease, err error) {
l, err = s.handleDiscover(req, resp)
if err != nil {
return 0, nil, fmt.Errorf("handling discover: %s", err)
@@ -1139,7 +1131,7 @@ var messageHandlers = map[dhcpv4.MessageType]messageHandler{
s *v4Server,
req *dhcpv4.DHCPv4,
resp *dhcpv4.DHCPv4,
) (rCode int, l *dhcpsvc.Lease, err error) {
) (rCode int, l *Lease, err error) {
var toReply bool
l, toReply = s.handleRequest(req, resp)
if l == nil {
@@ -1157,7 +1149,7 @@ var messageHandlers = map[dhcpv4.MessageType]messageHandler{
s *v4Server,
req *dhcpv4.DHCPv4,
resp *dhcpv4.DHCPv4,
) (rCode int, l *dhcpsvc.Lease, err error) {
) (rCode int, l *Lease, err error) {
err = s.handleDecline(req, resp)
if err != nil {
return 0, nil, fmt.Errorf("handling decline: %s", err)
@@ -1169,7 +1161,7 @@ var messageHandlers = map[dhcpv4.MessageType]messageHandler{
s *v4Server,
req *dhcpv4.DHCPv4,
resp *dhcpv4.DHCPv4,
) (rCode int, l *dhcpsvc.Lease, err error) {
) (rCode int, l *Lease, err error) {
err = s.handleRelease(req, resp)
if err != nil {
return 0, nil, fmt.Errorf("handling release: %s", err)
@@ -1410,8 +1402,8 @@ func (s *v4Server) Stop() (err error) {
// Create DHCPv4 server
func v4Create(conf *V4ServerConf) (srv *v4Server, err error) {
s := &v4Server{
hostsIndex: map[string]*dhcpsvc.Lease{},
ipIndex: map[netip.Addr]*dhcpsvc.Lease{},
hostsIndex: map[string]*Lease{},
ipIndex: map[netip.Addr]*Lease{},
}
err = conf.Validate()

View File

@@ -11,7 +11,6 @@ import (
"time"
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
"github.com/AdguardTeam/AdGuardHome/internal/dhcpsvc"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/stringutil"
"github.com/AdguardTeam/golibs/testutil"
@@ -68,7 +67,7 @@ func TestV4Server_leasing(t *testing.T) {
s := defaultSrv(t)
t.Run("add_static", func(t *testing.T) {
err := s.AddStaticLease(&dhcpsvc.Lease{
err := s.AddStaticLease(&Lease{
Hostname: staticName,
HWAddr: staticMAC,
IP: staticIP,
@@ -77,7 +76,7 @@ func TestV4Server_leasing(t *testing.T) {
require.NoError(t, err)
t.Run("same_name", func(t *testing.T) {
err = s.AddStaticLease(&dhcpsvc.Lease{
err = s.AddStaticLease(&Lease{
Hostname: staticName,
HWAddr: anotherMAC,
IP: anotherIP,
@@ -91,7 +90,7 @@ func TestV4Server_leasing(t *testing.T) {
"dynamic leases for " + anotherIP.String() +
" (" + staticMAC.String() + "): static lease already exists"
err = s.AddStaticLease(&dhcpsvc.Lease{
err = s.AddStaticLease(&Lease{
Hostname: anotherName,
HWAddr: staticMAC,
IP: anotherIP,
@@ -105,7 +104,7 @@ func TestV4Server_leasing(t *testing.T) {
"dynamic leases for " + staticIP.String() +
" (" + anotherMAC.String() + "): static lease already exists"
err = s.AddStaticLease(&dhcpsvc.Lease{
err = s.AddStaticLease(&Lease{
Hostname: anotherName,
HWAddr: anotherMAC,
IP: staticIP,
@@ -209,11 +208,11 @@ func TestV4Server_AddRemove_static(t *testing.T) {
require.Empty(t, ls)
testCases := []struct {
lease *dhcpsvc.Lease
lease *Lease
name string
wantErrMsg string
}{{
lease: &dhcpsvc.Lease{
lease: &Lease{
Hostname: "success.local",
HWAddr: net.HardwareAddr{0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA},
IP: netip.MustParseAddr("192.168.10.150"),
@@ -221,7 +220,7 @@ func TestV4Server_AddRemove_static(t *testing.T) {
name: "success",
wantErrMsg: "",
}, {
lease: &dhcpsvc.Lease{
lease: &Lease{
Hostname: "probably-router.local",
HWAddr: net.HardwareAddr{0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA},
IP: DefaultGatewayIP,
@@ -230,7 +229,7 @@ func TestV4Server_AddRemove_static(t *testing.T) {
wantErrMsg: "dhcpv4: adding static lease: " +
`can't assign the gateway IP "192.168.10.1" to the lease`,
}, {
lease: &dhcpsvc.Lease{
lease: &Lease{
Hostname: "ip6.local",
HWAddr: net.HardwareAddr{0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA},
IP: netip.MustParseAddr("ffff::1"),
@@ -239,7 +238,7 @@ func TestV4Server_AddRemove_static(t *testing.T) {
wantErrMsg: `dhcpv4: adding static lease: ` +
`invalid IP "ffff::1": only IPv4 is supported`,
}, {
lease: &dhcpsvc.Lease{
lease: &Lease{
Hostname: "bad-mac.local",
HWAddr: net.HardwareAddr{0xAA, 0xAA},
IP: netip.MustParseAddr("192.168.10.150"),
@@ -248,7 +247,7 @@ func TestV4Server_AddRemove_static(t *testing.T) {
wantErrMsg: `dhcpv4: adding static lease: bad mac address "aa:aa": ` +
`bad mac address length 2, allowed: [6 8 20]`,
}, {
lease: &dhcpsvc.Lease{
lease: &Lease{
Hostname: "bad-lbl-.local",
HWAddr: net.HardwareAddr{0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA},
IP: netip.MustParseAddr("192.168.10.150"),
@@ -267,7 +266,7 @@ func TestV4Server_AddRemove_static(t *testing.T) {
return
}
err = s.RemoveStaticLease(&dhcpsvc.Lease{
err = s.RemoveStaticLease(&Lease{
IP: tc.lease.IP,
HWAddr: tc.lease.HWAddr,
})
@@ -290,7 +289,7 @@ func TestV4_AddReplace(t *testing.T) {
s, ok := sIface.(*v4Server)
require.True(t, ok)
dynLeases := []dhcpsvc.Lease{{
dynLeases := []Lease{{
Hostname: "dynamic-1.local",
HWAddr: net.HardwareAddr{0x11, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA},
IP: netip.MustParseAddr("192.168.10.150"),
@@ -305,7 +304,7 @@ func TestV4_AddReplace(t *testing.T) {
require.NoError(t, err)
}
stLeases := []*dhcpsvc.Lease{{
stLeases := []*Lease{{
Hostname: "static-1.local",
HWAddr: net.HardwareAddr{0x33, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA},
IP: netip.MustParseAddr("192.168.10.150"),
@@ -514,7 +513,7 @@ func TestV4StaticLease_Get(t *testing.T) {
s.conf.dnsIPAddrs = []netip.Addr{dnsAddr}
s.implicitOpts.Update(dhcpv4.OptDNS(dnsAddr.AsSlice()))
l := &dhcpsvc.Lease{
l := &Lease{
Hostname: "static-1.local",
HWAddr: net.HardwareAddr{0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA},
IP: netip.MustParseAddr("192.168.10.150"),
@@ -780,7 +779,7 @@ func TestV4Server_FindMACbyIP(t *testing.T) {
anotherMAC := net.HardwareAddr{0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB}
s := &v4Server{
leases: []*dhcpsvc.Lease{{
leases: []*Lease{{
Hostname: staticName,
HWAddr: staticMAC,
IP: staticIP,
@@ -792,11 +791,11 @@ func TestV4Server_FindMACbyIP(t *testing.T) {
IP: anotherIP,
}},
}
s.ipIndex = map[netip.Addr]*dhcpsvc.Lease{
s.ipIndex = map[netip.Addr]*Lease{
staticIP: s.leases[0],
anotherIP: s.leases[1],
}
s.hostsIndex = map[string]*dhcpsvc.Lease{
s.hostsIndex = map[string]*Lease{
staticName: s.leases[0],
anotherName: s.leases[1],
}
@@ -846,7 +845,7 @@ func TestV4Server_handleDecline(t *testing.T) {
s4, ok := s.(*v4Server)
require.True(t, ok)
s4.leases = []*dhcpsvc.Lease{{
s4.leases = []*Lease{{
Hostname: dynamicName,
HWAddr: dynamicMAC,
IP: dynamicIP,
@@ -888,7 +887,7 @@ func TestV4Server_handleRelease(t *testing.T) {
s4, ok := s.(*v4Server)
require.True(t, ok)
s4.leases = []*dhcpsvc.Lease{{
s4.leases = []*Lease{{
Hostname: dynamicName,
HWAddr: dynamicMAC,
IP: dynamicIP,

View File

@@ -11,7 +11,6 @@ import (
"time"
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
"github.com/AdguardTeam/AdGuardHome/internal/dhcpsvc"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/netutil"
@@ -32,7 +31,7 @@ type v6Server struct {
sid dhcpv6.DUID
srv *server6.Server
leases []*dhcpsvc.Lease
leases []*Lease
leasesLock sync.Mutex
ipAddrs [256]byte
}
@@ -88,7 +87,7 @@ func (s *v6Server) IPByHost(host string) (ip netip.Addr) {
}
// ResetLeases resets leases.
func (s *v6Server) ResetLeases(leases []*dhcpsvc.Lease) (err error) {
func (s *v6Server) ResetLeases(leases []*Lease) (err error) {
defer func() { err = errors.Annotate(err, "dhcpv6: %w") }()
s.leasesLock.Lock()
@@ -112,14 +111,12 @@ func (s *v6Server) ResetLeases(leases []*dhcpsvc.Lease) (err error) {
// GetLeases returns the list of current DHCP leases. It is safe for concurrent
// use.
func (s *v6Server) GetLeases(flags GetLeasesFlags) (leases []*dhcpsvc.Lease) {
func (s *v6Server) GetLeases(flags GetLeasesFlags) (leases []*Lease) {
// The function shouldn't return nil value because zero-length slice
// behaves differently in cases like marshalling. Our front-end also
// requires non-nil value in the response.
leases = []*dhcpsvc.Lease{}
leases = []*Lease{}
s.leasesLock.Lock()
defer s.leasesLock.Unlock()
for _, l := range s.leases {
if l.IsStatic {
if (flags & LeasesStatic) != 0 {
@@ -131,12 +128,12 @@ func (s *v6Server) GetLeases(flags GetLeasesFlags) (leases []*dhcpsvc.Lease) {
}
}
}
s.leasesLock.Unlock()
return leases
}
// getLeasesRef returns the actual leases slice. For internal use only.
func (s *v6Server) getLeasesRef() []*dhcpsvc.Lease {
func (s *v6Server) getLeasesRef() []*Lease {
return s.leases
}
@@ -177,7 +174,7 @@ func (s *v6Server) leaseRemoveSwapByIndex(i int) {
// Remove a dynamic lease with the same properties
// Return error if a static lease is found
func (s *v6Server) rmDynamicLease(lease *dhcpsvc.Lease) (err error) {
func (s *v6Server) rmDynamicLease(lease *Lease) (err error) {
for i := 0; i < len(s.leases); i++ {
l := s.leases[i]
@@ -207,7 +204,7 @@ func (s *v6Server) rmDynamicLease(lease *dhcpsvc.Lease) (err error) {
}
// AddStaticLease adds a static lease. It is safe for concurrent use.
func (s *v6Server) AddStaticLease(l *dhcpsvc.Lease) (err error) {
func (s *v6Server) AddStaticLease(l *Lease) (err error) {
defer func() { err = errors.Annotate(err, "dhcpv6: %w") }()
if !l.IP.Is6() {
@@ -239,7 +236,7 @@ func (s *v6Server) AddStaticLease(l *dhcpsvc.Lease) (err error) {
}
// UpdateStaticLease updates IP, hostname of the static lease.
func (s *v6Server) UpdateStaticLease(l *dhcpsvc.Lease) (err error) {
func (s *v6Server) UpdateStaticLease(l *Lease) (err error) {
defer func() {
if err != nil {
err = errors.Annotate(err, "dhcpv6: updating static lease: %w")
@@ -270,7 +267,7 @@ func (s *v6Server) UpdateStaticLease(l *dhcpsvc.Lease) (err error) {
}
// RemoveStaticLease removes a static lease. It is safe for concurrent use.
func (s *v6Server) RemoveStaticLease(l *dhcpsvc.Lease) (err error) {
func (s *v6Server) RemoveStaticLease(l *Lease) (err error) {
defer func() { err = errors.Annotate(err, "dhcpv6: %w") }()
if !l.IP.Is6() {
@@ -295,7 +292,7 @@ func (s *v6Server) RemoveStaticLease(l *dhcpsvc.Lease) (err error) {
}
// Add a lease
func (s *v6Server) addLease(l *dhcpsvc.Lease) {
func (s *v6Server) addLease(l *Lease) {
s.leases = append(s.leases, l)
ip := l.IP.As16()
s.ipAddrs[ip[15]] = 1
@@ -303,7 +300,7 @@ func (s *v6Server) addLease(l *dhcpsvc.Lease) {
}
// Remove a lease with the same properties
func (s *v6Server) rmLease(lease *dhcpsvc.Lease) (err error) {
func (s *v6Server) rmLease(lease *Lease) (err error) {
for i, l := range s.leases {
if l.IP == lease.IP {
if !bytes.Equal(l.HWAddr, lease.HWAddr) ||
@@ -321,7 +318,7 @@ func (s *v6Server) rmLease(lease *dhcpsvc.Lease) (err error) {
}
// Find lease by MAC.
func (s *v6Server) findLease(mac net.HardwareAddr) (lease *dhcpsvc.Lease) {
func (s *v6Server) findLease(mac net.HardwareAddr) (lease *Lease) {
for i := range s.leases {
if bytes.Equal(mac, s.leases[i].HWAddr) {
return s.leases[i]
@@ -359,8 +356,8 @@ func (s *v6Server) findFreeIP() net.IP {
}
// Reserve lease for MAC
func (s *v6Server) reserveLease(mac net.HardwareAddr) *dhcpsvc.Lease {
l := dhcpsvc.Lease{
func (s *v6Server) reserveLease(mac net.HardwareAddr) *Lease {
l := Lease{
HWAddr: make([]byte, len(mac)),
}
@@ -393,7 +390,7 @@ func (s *v6Server) reserveLease(mac net.HardwareAddr) *dhcpsvc.Lease {
return &l
}
func (s *v6Server) commitDynamicLease(l *dhcpsvc.Lease) {
func (s *v6Server) commitDynamicLease(l *Lease) {
l.Expiry = time.Now().Add(s.conf.leaseTime)
s.leasesLock.Lock()
@@ -441,7 +438,7 @@ func (s *v6Server) checkSID(msg *dhcpv6.Message) error {
}
// . IAAddress must be equal to the lease's IP
func (s *v6Server) checkIA(msg *dhcpv6.Message, lease *dhcpsvc.Lease) error {
func (s *v6Server) checkIA(msg *dhcpv6.Message, lease *Lease) error {
switch msg.Type() {
case dhcpv6.MessageTypeRequest,
dhcpv6.MessageTypeConfirm,
@@ -467,7 +464,7 @@ func (s *v6Server) checkIA(msg *dhcpv6.Message, lease *dhcpsvc.Lease) error {
}
// Store lease in DB (if necessary) and return lease life time
func (s *v6Server) commitLease(msg *dhcpv6.Message, lease *dhcpsvc.Lease) time.Duration {
func (s *v6Server) commitLease(msg *dhcpv6.Message, lease *Lease) time.Duration {
lifetime := s.conf.leaseTime
switch msg.Type() {
@@ -509,7 +506,7 @@ func (s *v6Server) process(msg *dhcpv6.Message, req, resp dhcpv6.DHCPv6) bool {
return false
}
var lease *dhcpsvc.Lease
var lease *Lease
func() {
s.leasesLock.Lock()
defer s.leasesLock.Unlock()

View File

@@ -8,7 +8,6 @@ import (
"testing"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/dhcpsvc"
"github.com/insomniacslk/dhcp/dhcpv6"
"github.com/insomniacslk/dhcp/iana"
"github.com/stretchr/testify/assert"
@@ -29,7 +28,7 @@ func TestV6_AddRemove_static(t *testing.T) {
require.Empty(t, s.GetLeases(LeasesStatic))
// Add static lease.
l := &dhcpsvc.Lease{
l := &Lease{
IP: netip.MustParseAddr("2001::1"),
HWAddr: net.HardwareAddr{0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA},
}
@@ -48,7 +47,7 @@ func TestV6_AddRemove_static(t *testing.T) {
assert.True(t, ls[0].IsStatic)
// Try to remove non-existent static lease.
err = s.RemoveStaticLease(&dhcpsvc.Lease{
err = s.RemoveStaticLease(&Lease{
IP: netip.MustParseAddr("2001::2"),
HWAddr: l.HWAddr,
})
@@ -73,7 +72,7 @@ func TestV6_AddReplace(t *testing.T) {
require.True(t, ok)
// Add dynamic leases.
dynLeases := []*dhcpsvc.Lease{{
dynLeases := []*Lease{{
IP: netip.MustParseAddr("2001::1"),
HWAddr: net.HardwareAddr{0x11, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA},
}, {
@@ -85,7 +84,7 @@ func TestV6_AddReplace(t *testing.T) {
s.addLease(l)
}
stLeases := []*dhcpsvc.Lease{{
stLeases := []*Lease{{
IP: netip.MustParseAddr("2001::1"),
HWAddr: net.HardwareAddr{0x33, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA},
}, {
@@ -127,7 +126,7 @@ func TestV6GetLease(t *testing.T) {
LinkLayerAddr: net.HardwareAddr{0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA},
}
l := &dhcpsvc.Lease{
l := &Lease{
IP: netip.MustParseAddr("2001::1"),
HWAddr: net.HardwareAddr{0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA},
}
@@ -325,7 +324,7 @@ func TestV6_FindMACbyIP(t *testing.T) {
anotherMAC := net.HardwareAddr{0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB}
s := &v6Server{
leases: []*dhcpsvc.Lease{{
leases: []*Lease{{
Hostname: staticName,
HWAddr: staticMAC,
IP: staticIP,
@@ -338,7 +337,7 @@ func TestV6_FindMACbyIP(t *testing.T) {
}},
}
s.leases = []*dhcpsvc.Lease{{
s.leases = []*Lease{{
Hostname: staticName,
HWAddr: staticMAC,
IP: staticIP,

View File

@@ -43,7 +43,7 @@ func (conf *Config) Validate() (err error) {
case !conf.Enabled:
return nil
case conf.ICMPTimeout < 0:
return newMustErr("icmp timeout", "be non-negative", conf.ICMPTimeout)
return fmt.Errorf("icmp timeout %s must be non-negative", conf.ICMPTimeout)
}
err = netutil.ValidateDomainName(conf.LocalDomainName)
@@ -68,9 +68,9 @@ func (conf *Config) Validate() (err error) {
return nil
}
// newMustErr returns an error that indicates that valName must be as must
// mustBeErr returns an error that indicates that valName must be as must
// describes.
func newMustErr(valName, must string, val fmt.Stringer) (err error) {
func mustBeErr(valName, must string, val fmt.Stringer) (err error) {
return fmt.Errorf("%s %s must %s", valName, val, must)
}

View File

@@ -10,13 +10,13 @@ import (
"time"
"github.com/AdguardTeam/AdGuardHome/internal/next/agh"
"golang.org/x/exp/slices"
)
// Lease is a DHCP lease.
//
// TODO(e.burkov): Consider moving it to [agh], since it also may be needed in
// [websvc].
// 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
@@ -34,21 +34,6 @@ type Lease struct {
IsStatic bool
}
// Clone returns a deep copy of l.
func (l *Lease) Clone() (clone *Lease) {
if l == nil {
return nil
}
return &Lease{
Expiry: l.Expiry,
Hostname: l.Hostname,
HWAddr: slices.Clone(l.HWAddr),
IP: l.IP,
IsStatic: l.IsStatic,
}
}
type Interface interface {
agh.ServiceWithConfig[*Config]
@@ -72,9 +57,6 @@ type Interface interface {
IPByHost(host string) (ip netip.Addr)
// Leases returns all the active DHCP leases.
//
// TODO(e.burkov): Consider implementing iterating methods with appropriate
// signatures instead of cloning the whole list.
Leases() (ls []*Lease)
// AddLease adds a new DHCP lease. It returns an error if the lease is
@@ -109,9 +91,6 @@ func (Empty) Shutdown(_ context.Context) (err error) { return nil }
// Config implements the [ServiceWithConfig] interface for Empty.
func (Empty) Config() (conf *Config) { return nil }
// type check
var _ Interface = Empty{}
// Enabled implements the [Interface] interface for Empty.
func (Empty) Enabled() (ok bool) { return false }
@@ -124,6 +103,9 @@ 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{} }
// type check
var _ Interface = Empty{}
// Leases implements the [Interface] interface for Empty.
func (Empty) Leases() (leases []*Lease) { return nil }

View File

@@ -25,9 +25,6 @@ type DHCPServer struct {
// interfaces6 is the set of IPv6 interfaces sorted by interface name.
interfaces6 []*iface6
// leases is the set of active DHCP leases.
leases []*Lease
// icmpTimeout is the timeout for checking another DHCP server's presence.
icmpTimeout time.Duration
}
@@ -78,23 +75,3 @@ func New(conf *Config) (srv *DHCPServer, err error) {
icmpTimeout: conf.ICMPTimeout,
}, nil
}
// type check
//
// TODO(e.burkov): Uncomment when the [Interface] interface is implemented.
// var _ Interface = (*DHCPServer)(nil)
// Enabled implements the [Interface] interface for *DHCPServer.
func (srv *DHCPServer) Enabled() (ok bool) {
return srv.enabled.Load()
}
// Leases implements the [Interface] interface for *DHCPServer.
func (srv *DHCPServer) Leases() (leases []*Lease) {
leases = make([]*Lease, 0, len(srv.leases))
for _, lease := range srv.leases {
leases = append(leases, lease.Clone())
}
return leases
}

View File

@@ -45,15 +45,15 @@ func (conf *IPv4Config) validate() (err error) {
case !conf.Enabled:
return nil
case !conf.GatewayIP.Is4():
return newMustErr("gateway ip", "be a valid ipv4", conf.GatewayIP)
return mustBeErr("gateway ip", "be a valid ipv4", conf.GatewayIP)
case !conf.SubnetMask.Is4():
return newMustErr("subnet mask", "be a valid ipv4 cidr mask", conf.SubnetMask)
return mustBeErr("subnet mask", "be a valid ipv4 cidr mask", conf.SubnetMask)
case !conf.RangeStart.Is4():
return newMustErr("range start", "be a valid ipv4", conf.RangeStart)
return mustBeErr("range start", "be a valid ipv4", conf.RangeStart)
case !conf.RangeEnd.Is4():
return newMustErr("range end", "be a valid ipv4", conf.RangeEnd)
return mustBeErr("range end", "be a valid ipv4", conf.RangeEnd)
case conf.LeaseDuration <= 0:
return newMustErr("lease duration", "be less than %d", conf.LeaseDuration)
return mustBeErr("lease duration", "be less than %d", conf.LeaseDuration)
default:
return nil
}

View File

@@ -27,19 +27,6 @@ import (
"golang.org/x/exp/slices"
)
// ClientsContainer provides information about preconfigured DNS clients.
type ClientsContainer interface {
// UpstreamConfigByID returns the custom upstream configuration for the
// client having id, using boot to initialize the one if necessary. It
// returns nil if there is no custom upstream configuration for the client.
// The id is expected to be either a string representation of an IP address
// or the ClientID.
UpstreamConfigByID(
id string,
boot upstream.Resolver,
) (conf *proxy.CustomUpstreamConfig, err error)
}
// Config represents the DNS filtering configuration of AdGuard Home. The zero
// Config is empty and ready for use.
type Config struct {
@@ -48,9 +35,10 @@ type Config struct {
// FilterHandler is an optional additional filtering callback.
FilterHandler func(cliAddr netip.Addr, clientID string, settings *filtering.Settings) `yaml:"-"`
// ClientsContainer stores the information about special handling of some
// DNS clients.
ClientsContainer ClientsContainer `yaml:"-"`
// GetCustomUpstreamByClient is a callback that returns upstreams
// configuration based on the client IP address or ClientID. It returns
// nil if there are no custom upstreams for the client.
GetCustomUpstreamByClient func(id string) (conf *proxy.UpstreamConfig, err error) `yaml:"-"`
// Anti-DNS amplification
@@ -58,16 +46,8 @@ type Config struct {
// (0 to disable).
Ratelimit uint32 `yaml:"ratelimit"`
// RatelimitSubnetLenIPv4 is a subnet length for IPv4 addresses used for
// rate limiting requests.
RatelimitSubnetLenIPv4 int `yaml:"ratelimit_subnet_len_ipv4"`
// RatelimitSubnetLenIPv6 is a subnet length for IPv6 addresses used for
// rate limiting requests.
RatelimitSubnetLenIPv6 int `yaml:"ratelimit_subnet_len_ipv6"`
// RatelimitWhitelist is the list of whitelisted client IP addresses.
RatelimitWhitelist []netip.Addr `yaml:"ratelimit_whitelist"`
RatelimitWhitelist []string `yaml:"ratelimit_whitelist"`
// RefuseAny, if true, refuse ANY requests.
RefuseAny bool `yaml:"refuse_any"`
@@ -289,19 +269,16 @@ type ServerConfig struct {
// UseHTTP3Upstreams defines if HTTP/3 is be allowed for DNS-over-HTTPS
// upstreams.
UseHTTP3Upstreams bool
// ServePlainDNS defines if plain DNS is allowed for incoming requests.
ServePlainDNS bool
}
// newProxyConfig creates and validates configuration for the main proxy.
func (s *Server) newProxyConfig() (conf *proxy.Config, err error) {
// createProxyConfig creates and validates configuration for the main proxy.
func (s *Server) createProxyConfig() (conf proxy.Config, err error) {
srvConf := s.conf
conf = &proxy.Config{
conf = proxy.Config{
UDPListenAddr: srvConf.UDPListenAddrs,
TCPListenAddr: srvConf.TCPListenAddrs,
HTTP3: srvConf.ServeHTTP3,
Ratelimit: int(srvConf.Ratelimit),
RatelimitSubnetLenIPv4: srvConf.RatelimitSubnetLenIPv4,
RatelimitSubnetLenIPv6: srvConf.RatelimitSubnetLenIPv6,
RatelimitWhitelist: srvConf.RatelimitWhitelist,
RefuseAny: srvConf.RefuseAny,
TrustedProxies: srvConf.TrustedProxies,
@@ -329,25 +306,27 @@ func (s *Server) newProxyConfig() (conf *proxy.Config, err error) {
}
setProxyUpstreamMode(
conf,
&conf,
srvConf.AllServers,
srvConf.FastestAddr,
srvConf.FastestTimeout.Duration,
)
conf.BogusNXDomain, err = parseBogusNXDOMAIN(srvConf.BogusNXDomain)
if err != nil {
return nil, fmt.Errorf("bogus_nxdomain: %w", err)
for i, s := range srvConf.BogusNXDomain {
var subnet *net.IPNet
subnet, err = netutil.ParseSubnet(s)
if err != nil {
log.Error("subnet at index %d: %s", i, err)
continue
}
conf.BogusNXDomain = append(conf.BogusNXDomain, subnet)
}
err = s.prepareTLS(conf)
err = s.prepareTLS(&conf)
if err != nil {
return nil, fmt.Errorf("validating tls: %w", err)
}
err = s.preparePlain(conf)
if err != nil {
return nil, fmt.Errorf("validating plain: %w", err)
return proxy.Config{}, fmt.Errorf("validating tls: %w", err)
}
if c := srvConf.DNSCryptConfig; c.Enabled {
@@ -358,27 +337,12 @@ func (s *Server) newProxyConfig() (conf *proxy.Config, err error) {
}
if conf.UpstreamConfig == nil || len(conf.UpstreamConfig.Upstreams) == 0 {
return nil, errors.Error("no default upstream servers configured")
return proxy.Config{}, errors.Error("no default upstream servers configured")
}
return conf, nil
}
// parseBogusNXDOMAIN parses the bogus NXDOMAIN strings into valid subnets.
func parseBogusNXDOMAIN(confBogusNXDOMAIN []string) (subnets []netip.Prefix, err error) {
for i, s := range confBogusNXDOMAIN {
var subnet netip.Prefix
subnet, err = aghnet.ParseSubnet(s)
if err != nil {
return nil, fmt.Errorf("subnet at index %d: %w", i, err)
}
subnets = append(subnets, subnet)
}
return subnets, nil
}
const defaultBlockedResponseTTL = 3600
// initDefaultSettings initializes default settings if nothing
@@ -449,7 +413,10 @@ func collectListenAddr(
// collectDNSAddrs returns configured set of listening addresses. It also
// returns a set of ports of each unspecified listening address.
func (conf *ServerConfig) collectDNSAddrs() (addrs mapAddrPortSet, unspecPorts map[uint16]unit) {
func (conf *ServerConfig) collectDNSAddrs() (
addrs map[netip.AddrPort]unit,
unspecPorts map[uint16]unit,
) {
// TODO(e.burkov): Perhaps, we shouldn't allocate as much memory, since the
// TCP and UDP listening addresses are currently the same.
addrs = make(map[netip.AddrPort]unit, len(conf.TCPListenAddrs)+len(conf.UDPListenAddrs))
@@ -469,64 +436,20 @@ func (conf *ServerConfig) collectDNSAddrs() (addrs mapAddrPortSet, unspecPorts m
// defaultPlainDNSPort is the default port for plain DNS.
const defaultPlainDNSPort uint16 = 53
// addrPortSet is a set of [netip.AddrPort] values.
type addrPortSet interface {
// Has returns true if addrPort is in the set.
Has(addrPort netip.AddrPort) (ok bool)
}
// type check
var _ addrPortSet = emptyAddrPortSet{}
// emptyAddrPortSet is the [addrPortSet] containing no values.
type emptyAddrPortSet struct{}
// Has implements the [addrPortSet] interface for [emptyAddrPortSet].
func (emptyAddrPortSet) Has(_ netip.AddrPort) (ok bool) { return false }
// mapAddrPortSet is the [addrPortSet] containing values of [netip.AddrPort] as
// keys of a map.
type mapAddrPortSet map[netip.AddrPort]unit
// type check
var _ addrPortSet = mapAddrPortSet{}
// Has implements the [addrPortSet] interface for [mapAddrPortSet].
func (m mapAddrPortSet) Has(addrPort netip.AddrPort) (ok bool) {
_, ok = m[addrPort]
return ok
}
// combinedAddrPortSet is the [addrPortSet] defined by some IP addresses along
// with ports, any combination of which is considered being in the set.
type combinedAddrPortSet struct {
// TODO(e.burkov): Use sorted slices in combination with binary search.
ports map[uint16]unit
addrs []netip.Addr
}
// type check
var _ addrPortSet = (*combinedAddrPortSet)(nil)
// Has implements the [addrPortSet] interface for [*combinedAddrPortSet].
func (m *combinedAddrPortSet) Has(addrPort netip.AddrPort) (ok bool) {
_, ok = m.ports[addrPort.Port()]
return ok && slices.Contains(m.addrs, addrPort.Addr())
}
// addrPortMatcher is a function that matches an IP address with port.
type addrPortMatcher func(addr netip.AddrPort) (ok bool)
// filterOut filters out all the upstreams that match um. It returns all the
// closing errors joined.
func filterOutAddrs(upsConf *proxy.UpstreamConfig, set addrPortSet) (err error) {
func (m addrPortMatcher) filterOut(upsConf *proxy.UpstreamConfig) (err error) {
var errs []error
delFunc := func(u upstream.Upstream) (ok bool) {
// TODO(e.burkov): We should probably consider the protocol of u to
// only filter out the listening addresses of the same protocol.
addr, parseErr := aghnet.ParseAddrPort(u.Address(), defaultPlainDNSPort)
if parseErr != nil || !set.Has(addr) {
if parseErr != nil || !m(addr) {
// Don't filter out the upstream if it either cannot be parsed, or
// does not match m.
// does not match um.
return false
}
@@ -546,20 +469,26 @@ func filterOutAddrs(upsConf *proxy.UpstreamConfig, set addrPortSet) (err error)
return errors.Join(errs...)
}
// ourAddrsSet returns an addrPortSet that contains all the configured listening
// ourAddrsMatcher returns a matcher that matches all the configured listening
// addresses.
func (conf *ServerConfig) ourAddrsSet() (m addrPortSet, err error) {
func (conf *ServerConfig) ourAddrsMatcher() (m addrPortMatcher, err error) {
addrs, unspecPorts := conf.collectDNSAddrs()
switch {
case len(addrs) == 0:
if len(addrs) == 0 {
log.Debug("dnsforward: no listen addresses")
return emptyAddrPortSet{}, nil
case len(unspecPorts) == 0:
// Match no addresses.
return func(_ netip.AddrPort) (ok bool) { return false }, nil
}
if len(unspecPorts) == 0 {
log.Debug("dnsforward: filtering out addresses %s", addrs)
return addrs, nil
default:
m = func(a netip.AddrPort) (ok bool) {
_, ok = addrs[a]
return ok
}
} else {
var ifaceAddrs []netip.Addr
ifaceAddrs, err = aghnet.CollectAllIfacesAddrs()
if err != nil {
@@ -569,11 +498,16 @@ func (conf *ServerConfig) ourAddrsSet() (m addrPortSet, err error) {
log.Debug("dnsforward: filtering out addresses %s on ports %d", ifaceAddrs, unspecPorts)
return &combinedAddrPortSet{
ports: unspecPorts,
addrs: ifaceAddrs,
}, nil
m = func(a netip.AddrPort) (ok bool) {
if _, ok = unspecPorts[a.Port()]; ok {
return slices.Contains(ifaceAddrs, a.Addr())
}
return false
}
}
return m, nil
}
// prepareTLS - prepares TLS configuration for the DNS proxy
@@ -630,7 +564,7 @@ func (s *Server) prepareTLS(proxyConfig *proxy.Config) (err error) {
// isWildcard returns true if host is a wildcard hostname.
func isWildcard(host string) (ok bool) {
return strings.HasPrefix(host, "*.")
return len(host) >= 2 && host[0] == '*' && host[1] == '.'
}
// matchesDomainWildcard returns true if host matches the domain wildcard
@@ -670,31 +604,6 @@ func (s *Server) onGetCertificate(ch *tls.ClientHelloInfo) (*tls.Certificate, er
return &s.conf.cert, nil
}
// preparePlain prepares the plain-DNS configuration for the DNS proxy.
// preparePlain assumes that prepareTLS has already been called.
func (s *Server) preparePlain(proxyConf *proxy.Config) (err error) {
if s.conf.ServePlainDNS {
proxyConf.UDPListenAddr = s.conf.UDPListenAddrs
proxyConf.TCPListenAddr = s.conf.TCPListenAddrs
return nil
}
lenEncrypted := len(proxyConf.DNSCryptTCPListenAddr) +
len(proxyConf.DNSCryptUDPListenAddr) +
len(proxyConf.HTTPSListenAddr) +
len(proxyConf.QUICListenAddr) +
len(proxyConf.TLSListenAddr)
if lenEncrypted == 0 {
// TODO(a.garipov): Support full disabling of all DNS.
return errors.Error("disabling plain dns requires at least one encrypted protocol")
}
log.Info("dnsforward: warning: plain dns is disabled")
return nil
}
// UpdatedProtectionStatus updates protection state, if the protection was
// disabled temporarily. Returns the updated state of protection.
func (s *Server) UpdatedProtectionStatus() (enabled bool, disabledUntil *time.Time) {

View File

@@ -1,349 +0,0 @@
package dnsforward
import (
"fmt"
"strings"
"sync"
"github.com/AdguardTeam/dnsproxy/upstream"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/miekg/dns"
"golang.org/x/exp/slices"
)
// upstreamConfigValidator parses the [*proxy.UpstreamConfig] and checks the
// actual DNS availability of each upstream.
type upstreamConfigValidator struct {
// general is the general upstream configuration.
general []*upstreamResult
// fallback is the fallback upstream configuration.
fallback []*upstreamResult
// private is the private upstream configuration.
private []*upstreamResult
}
// upstreamResult is a result of validation of an [upstream.Upstream] within an
// [proxy.UpstreamConfig].
type upstreamResult struct {
// server is the parsed upstream. It is nil when there was an error during
// parsing.
server upstream.Upstream
// err is the error either from parsing or from checking the upstream.
err error
// original is the piece of configuration that have either been turned to an
// upstream or caused an error.
original string
// isSpecific is true if the upstream is domain-specific.
isSpecific bool
}
// compare compares two [upstreamResult]s. It returns 0 if they are equal, -1
// if ur should be sorted before other, and 1 otherwise.
//
// TODO(e.burkov): Perhaps it makes sense to sort the results with errors near
// the end.
func (ur *upstreamResult) compare(other *upstreamResult) (res int) {
return strings.Compare(ur.original, other.original)
}
// newUpstreamConfigValidator parses the upstream configuration and returns a
// validator for it. cv already contains the parsed upstreams along with errors
// related.
func newUpstreamConfigValidator(
general []string,
fallback []string,
private []string,
opts *upstream.Options,
) (cv *upstreamConfigValidator) {
cv = &upstreamConfigValidator{}
for _, line := range general {
cv.general = cv.insertLineResults(cv.general, line, opts)
}
for _, line := range fallback {
cv.fallback = cv.insertLineResults(cv.fallback, line, opts)
}
for _, line := range private {
cv.private = cv.insertLineResults(cv.private, line, opts)
}
return cv
}
// insertLineResults parses line and inserts the result into s. It can insert
// multiple results as well as none.
func (cv *upstreamConfigValidator) insertLineResults(
s []*upstreamResult,
line string,
opts *upstream.Options,
) (result []*upstreamResult) {
upstreams, isSpecific, err := splitUpstreamLine(line)
if err != nil {
return cv.insert(s, &upstreamResult{
err: err,
original: line,
})
}
for _, upstreamAddr := range upstreams {
var res *upstreamResult
if upstreamAddr != "#" {
res = cv.parseUpstream(upstreamAddr, opts)
} else if !isSpecific {
res = &upstreamResult{
err: errNotDomainSpecific,
original: upstreamAddr,
}
} else {
continue
}
res.isSpecific = isSpecific
s = cv.insert(s, res)
}
return s
}
// insert inserts r into slice in a sorted order, except duplicates. slice must
// not be nil.
func (cv *upstreamConfigValidator) insert(
s []*upstreamResult,
r *upstreamResult,
) (result []*upstreamResult) {
i, has := slices.BinarySearchFunc(s, r, (*upstreamResult).compare)
if has {
log.Debug("dnsforward: duplicate configuration %q", r.original)
return s
}
return slices.Insert(s, i, r)
}
// parseUpstream parses addr and returns the result of parsing. It returns nil
// if the specified server points at the default upstream server which is
// validated separately.
func (cv *upstreamConfigValidator) parseUpstream(
addr string,
opts *upstream.Options,
) (r *upstreamResult) {
// Check if the upstream has a valid protocol prefix.
//
// TODO(e.burkov): Validate the domain name.
if proto, _, ok := strings.Cut(addr, "://"); ok {
if !slices.Contains(protocols, proto) {
return &upstreamResult{
err: fmt.Errorf("bad protocol %q", proto),
original: addr,
}
}
}
ups, err := upstream.AddressToUpstream(addr, opts)
return &upstreamResult{
server: ups,
err: err,
original: addr,
}
}
// check tries to exchange with each successfully parsed upstream and enriches
// the results with the healthcheck errors. It should not be called after the
// [upsConfValidator.close] method, since it makes no sense to check the closed
// upstreams.
func (cv *upstreamConfigValidator) check() {
const (
// testTLD is the special-use fully-qualified domain name for testing
// the DNS server reachability.
//
// See https://datatracker.ietf.org/doc/html/rfc6761#section-6.2.
testTLD = "test."
// inAddrARPATLD is the special-use fully-qualified domain name for PTR
// IP address resolution.
//
// See https://datatracker.ietf.org/doc/html/rfc1035#section-3.5.
inAddrARPATLD = "in-addr.arpa."
)
commonChecker := &healthchecker{
hostname: testTLD,
qtype: dns.TypeA,
ansEmpty: true,
}
arpaChecker := &healthchecker{
hostname: inAddrARPATLD,
qtype: dns.TypePTR,
ansEmpty: false,
}
wg := &sync.WaitGroup{}
wg.Add(len(cv.general) + len(cv.fallback) + len(cv.private))
for _, res := range cv.general {
go cv.checkSrv(res, wg, commonChecker)
}
for _, res := range cv.fallback {
go cv.checkSrv(res, wg, commonChecker)
}
for _, res := range cv.private {
go cv.checkSrv(res, wg, arpaChecker)
}
wg.Wait()
}
// checkSrv runs hc on the server from res, if any, and stores any occurred
// error in res. wg is always marked done in the end. It used to be called in
// a separate goroutine.
func (cv *upstreamConfigValidator) checkSrv(
res *upstreamResult,
wg *sync.WaitGroup,
hc *healthchecker,
) {
defer wg.Done()
if res.server == nil {
return
}
res.err = hc.check(res.server)
if res.err != nil && res.isSpecific {
res.err = domainSpecificTestError{Err: res.err}
}
}
// close closes all the upstreams that were successfully parsed. It enriches
// the results with deferred closing errors.
func (cv *upstreamConfigValidator) close() {
for _, slice := range [][]*upstreamResult{cv.general, cv.fallback, cv.private} {
for _, r := range slice {
if r.server != nil {
r.err = errors.WithDeferred(r.err, r.server.Close())
}
}
}
}
// status returns all the data collected during parsing, healthcheck, and
// closing of the upstreams. The returned map is keyed by the original upstream
// configuration piece and contains the corresponding error or "OK" if there was
// no error.
func (cv *upstreamConfigValidator) status() (results map[string]string) {
result := map[string]string{}
for _, res := range cv.general {
resultToStatus("general", res, result)
}
for _, res := range cv.fallback {
resultToStatus("fallback", res, result)
}
for _, res := range cv.private {
resultToStatus("private", res, result)
}
return result
}
// resultToStatus puts "OK" or an error message from res into resMap. section
// is the name of the upstream configuration section, i.e. "general",
// "fallback", or "private", and only used for logging.
//
// TODO(e.burkov): Currently, the HTTP handler expects that all the results are
// put together in a single map, which may lead to collisions, see AG-27539.
// Improve the results compilation.
func resultToStatus(section string, res *upstreamResult, resMap map[string]string) {
val := "OK"
if res.err != nil {
val = res.err.Error()
}
prevVal := resMap[res.original]
switch prevVal {
case "":
resMap[res.original] = val
case val:
log.Debug("dnsforward: duplicating %s config line %q", section, res.original)
default:
log.Debug(
"dnsforward: warning: %s config line %q (%v) had different result %v",
section,
val,
res.original,
prevVal,
)
}
}
// domainSpecificTestError is a wrapper for errors returned by checkDNS to mark
// the tested upstream domain-specific and therefore consider its errors
// non-critical.
//
// TODO(a.garipov): Some common mechanism of distinguishing between errors and
// warnings (non-critical errors) is desired.
type domainSpecificTestError struct {
// Err is the actual error occurred during healthcheck test.
Err error
}
// type check
var _ error = domainSpecificTestError{}
// Error implements the [error] interface for domainSpecificTestError.
func (err domainSpecificTestError) Error() (msg string) {
return fmt.Sprintf("WARNING: %s", err.Err)
}
// type check
var _ errors.Wrapper = domainSpecificTestError{}
// Unwrap implements the [errors.Wrapper] interface for domainSpecificTestError.
func (err domainSpecificTestError) Unwrap() (wrapped error) {
return err.Err
}
// healthchecker checks the upstream's status by exchanging with it.
type healthchecker struct {
// hostname is the name of the host to put into healthcheck DNS request.
hostname string
// qtype is the type of DNS request to use for healthcheck.
qtype uint16
// ansEmpty defines if the answer section within the response is expected to
// be empty.
ansEmpty bool
}
// check exchanges with u and validates the response.
func (h *healthchecker) check(u upstream.Upstream) (err error) {
req := &dns.Msg{
MsgHdr: dns.MsgHdr{
Id: dns.Id(),
RecursionDesired: true,
},
Question: []dns.Question{{
Name: h.hostname,
Qtype: h.qtype,
Qclass: dns.ClassINET,
}},
}
reply, err := u.Exchange(req)
if err != nil {
return fmt.Errorf("couldn't communicate with upstream: %w", err)
} else if h.ansEmpty && len(reply.Answer) > 0 {
return errWrongResponse
}
return nil
}

View File

@@ -4,8 +4,6 @@ import (
"context"
"fmt"
"net"
"net/netip"
"strconv"
"time"
"github.com/AdguardTeam/golibs/errors"
@@ -13,12 +11,10 @@ import (
)
// DialContext is an [aghnet.DialContextFunc] that uses s to resolve hostnames.
// addr should be a valid host:port address, where host could be a domain name
// or an IP address.
func (s *Server) DialContext(ctx context.Context, network, addr string) (conn net.Conn, err error) {
log.Debug("dnsforward: dialing %q for network %q", addr, network)
host, portStr, err := net.SplitHostPort(addr)
host, port, err := net.SplitHostPort(addr)
if err != nil {
return nil, err
}
@@ -32,24 +28,21 @@ func (s *Server) DialContext(ctx context.Context, network, addr string) (conn ne
return dialer.DialContext(ctx, network, addr)
}
port, err := strconv.Atoi(portStr)
if err != nil {
return nil, fmt.Errorf("invalid port %s: %w", portStr, err)
}
ips, err := s.Resolve(ctx, network, host)
addrs, err := s.Resolve(host)
if err != nil {
return nil, fmt.Errorf("resolving %q: %w", host, err)
} else if len(ips) == 0 {
}
log.Debug("dnsforward: resolving %q: %v", host, addrs)
if len(addrs) == 0 {
return nil, fmt.Errorf("no addresses for host %q", host)
}
log.Debug("dnsforward: resolved %q: %v", host, ips)
var dialErrs []error
for _, ip := range ips {
addrPort := netip.AddrPortFrom(ip, uint16(port))
conn, err = dialer.DialContext(ctx, network, addrPort.String())
for _, a := range addrs {
addr = net.JoinHostPort(a.String(), port)
conn, err = dialer.DialContext(ctx, network, addr)
if err != nil {
dialErrs = append(dialErrs, err)

View File

@@ -292,7 +292,6 @@ func TestServer_HandleDNSRequest_dns64(t *testing.T) {
Config: Config{
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
},
ServePlainDNS: true,
}, localUps)
t.Run(tc.name, func(t *testing.T) {

View File

@@ -2,9 +2,7 @@
package dnsforward
import (
"context"
"fmt"
"io"
"net"
"net/http"
"net/netip"
@@ -36,11 +34,6 @@ import (
// DefaultTimeout is the default upstream timeout
const DefaultTimeout = 10 * time.Second
// defaultLocalTimeout is the default timeout for resolving addresses from
// locally-served networks. It is assumed that local resolvers should work much
// faster than ordinary upstreams.
const defaultLocalTimeout = 1 * time.Second
// defaultClientIDCacheCount is the default count of items in the LRU ClientID
// cache. The assumption here is that there won't be more than this many
// requests between the BeforeRequestHandler stage and the actual processing.
@@ -115,7 +108,7 @@ type Server struct {
// stats is the statistics collector for client's DNS usage data.
stats stats.Interface
// access drops disallowed clients.
// access drops unallowed clients.
access *accessManager
// localDomainSuffix is the suffix used to detect internal hosts. It
@@ -142,21 +135,8 @@ type Server struct {
// PTR resolving.
sysResolvers SystemResolvers
// etcHosts contains the current data from the system's hosts files.
etcHosts upstream.Resolver
// bootstrap is the resolver for upstreams' hostnames.
bootstrap upstream.Resolver
// bootResolvers are the resolvers that should be used for
// bootstrapping along with [etcHosts].
//
// TODO(e.burkov): Use [proxy.UpstreamConfig] when it will implement the
// [upstream.Resolver] interface.
bootResolvers []*upstream.UpstreamResolver
// recDetector is a cache for recursive requests. It is used to detect and
// prevent recursive requests only for private upstreams.
// recDetector is a cache for recursive requests. It is used to detect
// and prevent recursive requests only for private upstreams.
//
// See https://github.com/adguardTeam/adGuardHome/issues/3185#issuecomment-851048135.
recDetector *recursionDetector
@@ -173,8 +153,8 @@ type Server struct {
// during the BeforeRequestHandler stage.
clientIDCache cache.Cache
// internalProxy resolves internal requests from the application itself. It
// isn't started and so no listen ports are required.
// DNS proxy instance for internal usage
// We don't Start() it and so no listen port is required.
internalProxy *proxy.Proxy
// isRunning is true if the DNS server is running.
@@ -205,7 +185,6 @@ type DNSCreateParams struct {
DHCPServer DHCP
PrivateNets netutil.SubnetSet
Anonymizer *aghnet.IPMut
EtcHosts *aghnet.HostsContainer
LocalDomain string
}
@@ -238,30 +217,19 @@ func NewServer(p DNSCreateParams) (s *Server, err error) {
if p.Anonymizer == nil {
p.Anonymizer = aghnet.NewIPMut(nil)
}
var etcHosts upstream.Resolver
if p.EtcHosts != nil {
etcHosts = upstream.NewHostsResolver(p.EtcHosts)
}
s = &Server{
dnsFilter: p.DNSFilter,
dhcpServer: p.DHCPServer,
stats: p.Stats,
queryLog: p.QueryLog,
privateNets: p.PrivateNets,
// TODO(e.burkov): Use some case-insensitive string comparison.
localDomainSuffix: strings.ToLower(localDomainSuffix),
etcHosts: etcHosts,
recDetector: newRecursionDetector(recursionTTL, cachedRecurrentReqNum),
clientIDCache: cache.New(cache.Config{
EnableLRU: true,
MaxCount: defaultClientIDCacheCount,
}),
anonymizer: p.Anonymizer,
conf: ServerConfig{
ServePlainDNS: true,
},
}
s.sysResolvers, err = sysresolv.NewSystemResolvers(nil, defaultPlainDNSPort)
@@ -269,6 +237,8 @@ func NewServer(p DNSCreateParams) (s *Server, err error) {
return nil, fmt.Errorf("initializing system resolvers: %w", err)
}
s.dhcpServer = p.DHCPServer
if runtime.GOARCH == "mips" || runtime.GOARCH == "mipsle" {
// Use plain DNS on MIPS, encryption is too slow
defaultDNS = defaultBootstrap
@@ -304,7 +274,7 @@ func (s *Server) WriteDiskConfig(c *Config) {
sc := s.conf.Config
*c = sc
c.RatelimitWhitelist = slices.Clone(sc.RatelimitWhitelist)
c.RatelimitWhitelist = stringutil.CloneSlice(sc.RatelimitWhitelist)
c.BootstrapDNS = stringutil.CloneSlice(sc.BootstrapDNS)
c.FallbackDNS = stringutil.CloneSlice(sc.FallbackDNS)
c.AllowedClients = stringutil.CloneSlice(sc.AllowedClients)
@@ -335,14 +305,15 @@ func (s *Server) AddrProcConfig() (c *client.DefaultAddrProcConfig) {
}
}
// Resolve gets IP addresses by host name from an upstream server. No
// request/response filtering is performed. Query log and Stats are not
// updated. This method may be called before [Server.Start].
func (s *Server) Resolve(ctx context.Context, net, host string) (addr []netip.Addr, err error) {
// Resolve - get IP addresses by host name from an upstream server.
// No request/response filtering is performed.
// Query log and Stats are not updated.
// This method may be called before Start().
func (s *Server) Resolve(host string) ([]net.IPAddr, error) {
s.serverLock.RLock()
defer s.serverLock.RUnlock()
return s.internalProxy.LookupNetIP(ctx, net, host)
return s.internalProxy.LookupIPAddr(host)
}
const (
@@ -383,8 +354,9 @@ func (s *Server) Exchange(ip netip.Addr) (host string, ttl time.Duration, err er
}
dctx := &proxy.DNSContext{
Proto: "udp",
Req: req,
Proto: "udp",
Req: req,
StartTime: time.Now(),
}
var resolver *proxy.Proxy
@@ -450,7 +422,7 @@ func hostFromPTR(resp *dns.Msg) (host string, ttl time.Duration, err error) {
return "", 0, ErrRDNSNoData
}
// Start starts the DNS server. It must only be called after [Server.Prepare].
// Start starts the DNS server.
func (s *Server) Start() error {
s.serverLock.Lock()
defer s.serverLock.Unlock()
@@ -458,42 +430,48 @@ func (s *Server) Start() error {
return s.startLocked()
}
// startLocked starts the DNS server without locking. s.serverLock is expected
// to be locked.
// startLocked starts the DNS server without locking. For internal use only.
func (s *Server) startLocked() error {
err := s.dnsProxy.Start()
if err == nil {
s.isRunning = true
}
return err
}
// setupLocalResolvers initializes the resolvers for local addresses. It
// assumes s.serverLock is locked or the Server not running.
func (s *Server) setupLocalResolvers(boot upstream.Resolver) (err error) {
set, err := s.conf.ourAddrsSet()
// defaultLocalTimeout is the default timeout for resolving addresses from
// locally-served networks. It is assumed that local resolvers should work much
// faster than ordinary upstreams.
const defaultLocalTimeout = 1 * time.Second
// setupLocalResolvers initializes the resolvers for local addresses. For
// internal use only.
func (s *Server) setupLocalResolvers() (err error) {
matcher, err := s.conf.ourAddrsMatcher()
if err != nil {
// Don't wrap the error because it's informative enough as is.
return err
}
bootstraps := s.conf.BootstrapDNS
resolvers := s.conf.LocalPTRResolvers
confNeedsFiltering := len(resolvers) > 0
if confNeedsFiltering {
resolvers = stringutil.FilterOut(resolvers, IsCommentOrEmpty)
} else {
sysResolvers := slices.DeleteFunc(slices.Clone(s.sysResolvers.Addrs()), set.Has)
filterConfig := false
if len(resolvers) == 0 {
sysResolvers := slices.DeleteFunc(s.sysResolvers.Addrs(), matcher)
resolvers = make([]string, 0, len(sysResolvers))
for _, r := range sysResolvers {
resolvers = append(resolvers, r.String())
}
} else {
resolvers = stringutil.FilterOut(resolvers, IsCommentOrEmpty)
filterConfig = true
}
log.Debug("dnsforward: upstreams to resolve ptr for local addresses: %v", resolvers)
uc, err := s.prepareUpstreamConfig(resolvers, nil, &upstream.Options{
Bootstrap: boot,
Bootstrap: bootstraps,
Timeout: defaultLocalTimeout,
// TODO(e.burkov): Should we verify server's certificates?
PreferIPv6: s.conf.BootstrapPreferIPv6,
@@ -502,9 +480,8 @@ func (s *Server) setupLocalResolvers(boot upstream.Resolver) (err error) {
return fmt.Errorf("preparing private upstreams: %w", err)
}
if confNeedsFiltering {
err = filterOutAddrs(uc, set)
if err != nil {
if filterConfig {
if err = matcher.filterOut(uc); err != nil {
return fmt.Errorf("filtering private upstreams: %w", err)
}
}
@@ -515,7 +492,6 @@ func (s *Server) setupLocalResolvers(boot upstream.Resolver) (err error) {
},
}
// TODO(e.burkov): Should we also consider the DNS64 usage?
if s.conf.UsePrivateRDNS &&
// Only set the upstream config if there are any upstreams. It's safe
// to put nil into [proxy.Config.PrivateRDNSUpstreamConfig].
@@ -542,19 +518,31 @@ func (s *Server) Prepare(conf *ServerConfig) (err error) {
s.initDefaultSettings()
boot, err := s.prepareInternalDNS()
err = s.prepareIpsetListSettings()
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return fmt.Errorf("preparing ipset settings: %w", err)
}
err = s.prepareUpstreamSettings()
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return err
}
proxyConfig, err := s.newProxyConfig()
var proxyConfig proxy.Config
proxyConfig, err = s.createProxyConfig()
if err != nil {
return fmt.Errorf("preparing proxy: %w", err)
}
s.setupDNS64()
err = s.prepareInternalProxy()
if err != nil {
return fmt.Errorf("preparing internal proxy: %w", err)
}
s.access, err = newAccessCtx(
s.conf.AllowedClients,
s.conf.DisallowedClients,
@@ -567,9 +555,9 @@ func (s *Server) Prepare(conf *ServerConfig) (err error) {
// Set the proxy here because [setupLocalResolvers] sets its values.
//
// TODO(e.burkov): Remove once the local resolvers logic moved to dnsproxy.
s.dnsProxy = &proxy.Proxy{Config: *proxyConfig}
s.dnsProxy = &proxy.Proxy{Config: proxyConfig}
err = s.setupLocalResolvers(boot)
err = s.setupLocalResolvers()
if err != nil {
return fmt.Errorf("setting up resolvers: %w", err)
}
@@ -588,38 +576,6 @@ func (s *Server) Prepare(conf *ServerConfig) (err error) {
return nil
}
// prepareInternalDNS initializes the internal state of s before initializing
// the primary DNS proxy instance. It assumes s.serverLock is locked or the
// Server not running.
func (s *Server) prepareInternalDNS() (boot upstream.Resolver, err error) {
err = s.prepareIpsetListSettings()
if err != nil {
return nil, fmt.Errorf("preparing ipset settings: %w", err)
}
s.bootstrap, s.bootResolvers, err = s.createBootstrap(s.conf.BootstrapDNS, &upstream.Options{
Timeout: DefaultTimeout,
HTTPVersions: UpstreamHTTPVersions(s.conf.UseHTTP3Upstreams),
})
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return nil, err
}
err = s.prepareUpstreamSettings(s.bootstrap)
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return s.bootstrap, err
}
err = s.prepareInternalProxy()
if err != nil {
return s.bootstrap, fmt.Errorf("preparing internal proxy: %w", err)
}
return s.bootstrap, nil
}
// setupFallbackDNS initializes the fallback DNS servers.
func (s *Server) setupFallbackDNS() (err error) {
fallbacks := s.conf.FallbackDNS
@@ -643,8 +599,7 @@ func (s *Server) setupFallbackDNS() (err error) {
return nil
}
// setupAddrProc initializes the address processor. It assumes s.serverLock is
// locked or the Server not running.
// setupAddrProc initializes the address processor. For internal use only.
func (s *Server) setupAddrProc() {
// TODO(a.garipov): This is a crutch for tests; remove.
if s.conf.AddrProcConf == nil {
@@ -733,8 +688,7 @@ func (s *Server) Stop() error {
return s.stopLocked()
}
// stopLocked stops the DNS server without locking. s.serverLock is expected to
// be locked.
// stopLocked stops the DNS server without locking. For internal use only.
func (s *Server) stopLocked() (err error) {
// TODO(e.burkov, a.garipov): Return critical errors, not just log them.
// This will require filtering all the non-critical errors in
@@ -747,11 +701,18 @@ func (s *Server) stopLocked() (err error) {
}
}
logCloserErr(s.internalProxy.UpstreamConfig, "dnsforward: closing internal resolvers: %s")
logCloserErr(s.localResolvers.UpstreamConfig, "dnsforward: closing local resolvers: %s")
if upsConf := s.internalProxy.UpstreamConfig; upsConf != nil {
err = upsConf.Close()
if err != nil {
log.Error("dnsforward: closing internal resolvers: %s", err)
}
}
for _, b := range s.bootResolvers {
logCloserErr(b, "dnsforward: closing bootstrap %s: %s", b.Address())
if upsConf := s.localResolvers.UpstreamConfig; upsConf != nil {
err = upsConf.Close()
if err != nil {
log.Error("dnsforward: closing local resolvers: %s", err)
}
}
s.isRunning = false
@@ -759,18 +720,6 @@ func (s *Server) stopLocked() (err error) {
return nil
}
// logCloserErr logs the error returned by c, if any.
func logCloserErr(c io.Closer, format string, args ...any) {
if c == nil {
return
}
err := c.Close()
if err != nil {
log.Error(format, append(args, err)...)
}
}
// IsRunning returns true if the DNS server is running.
func (s *Server) IsRunning() bool {
s.serverLock.RLock()

View File

@@ -54,10 +54,13 @@ const (
testMessagesCount = 10
)
// testClientAddrPort is the common net.Addr for tests.
// testClientAddr is the common net.Addr for tests.
//
// TODO(a.garipov): Use more.
var testClientAddrPort = netip.MustParseAddrPort("1.2.3.4:12345")
var testClientAddr net.Addr = &net.TCPAddr{
IP: net.IP{1, 2, 3, 4},
Port: 12345,
}
func startDeferStop(t *testing.T, s *Server) {
t.Helper()
@@ -179,7 +182,6 @@ func createTestTLS(t *testing.T, tlsConf TLSConfig) (s *Server, certPem []byte)
Config: Config{
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
},
ServePlainDNS: true,
}, nil)
tlsConf.CertificateChainData, tlsConf.PrivateKeyData = certPem, keyPem
@@ -307,7 +309,6 @@ func TestServer(t *testing.T) {
Config: Config{
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
},
ServePlainDNS: true,
}, nil)
s.conf.UpstreamConfig.Upstreams = []upstream.Upstream{newGoogleUpstream()}
startDeferStop(t, s)
@@ -346,7 +347,6 @@ func TestServer_timeout(t *testing.T) {
Config: Config{
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
},
ServePlainDNS: true,
}
s, err := NewServer(DNSCreateParams{DNSFilter: createTestDNSFilter(t)})
@@ -381,7 +381,6 @@ func TestServer_Prepare_fallbacks(t *testing.T) {
},
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
},
ServePlainDNS: true,
}
s, err := NewServer(DNSCreateParams{})
@@ -403,7 +402,6 @@ func TestServerWithProtectionDisabled(t *testing.T) {
Config: Config{
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
},
ServePlainDNS: true,
}, nil)
s.conf.UpstreamConfig.Upstreams = []upstream.Upstream{newGoogleUpstream()}
startDeferStop(t, s)
@@ -481,7 +479,6 @@ func TestServerRace(t *testing.T) {
UpstreamDNS: []string{"8.8.8.8:53", "8.8.4.4:53"},
},
ConfigModified: func() {},
ServePlainDNS: true,
}
s := createTestServer(t, filterConf, forwardConf, nil)
s.conf.UpstreamConfig.Upstreams = []upstream.Upstream{newGoogleUpstream()}
@@ -535,7 +532,6 @@ func TestSafeSearch(t *testing.T) {
Enabled: false,
},
},
ServePlainDNS: true,
}
s := createTestServer(t, filterConf, forwardConf, nil)
startDeferStop(t, s)
@@ -598,7 +594,6 @@ func TestInvalidRequest(t *testing.T) {
Enabled: false,
},
},
ServePlainDNS: true,
}, nil)
startDeferStop(t, s)
@@ -627,7 +622,6 @@ func TestBlockedRequest(t *testing.T) {
Enabled: false,
},
},
ServePlainDNS: true,
}
s := createTestServer(t, &filtering.Config{
ProtectionEnabled: true,
@@ -650,71 +644,45 @@ func TestBlockedRequest(t *testing.T) {
}
func TestServerCustomClientUpstream(t *testing.T) {
const defaultCacheSize = 1024 * 1024
var upsCalledCounter uint32
forwardConf := ServerConfig{
UDPListenAddrs: []*net.UDPAddr{{}},
TCPListenAddrs: []*net.TCPAddr{{}},
Config: Config{
CacheSize: defaultCacheSize,
EDNSClientSubnet: &EDNSClientSubnet{
Enabled: false,
},
},
ServePlainDNS: true,
}
s := createTestServer(t, &filtering.Config{
BlockingMode: filtering.BlockingModeDefault,
}, forwardConf, nil)
s.conf.GetCustomUpstreamByClient = func(_ string) (conf *proxy.UpstreamConfig, err error) {
ups := aghtest.NewUpstreamMock(func(req *dns.Msg) (resp *dns.Msg, err error) {
return aghalg.Coalesce(
aghtest.MatchedResponse(req, dns.TypeA, "host", "192.168.0.1"),
new(dns.Msg).SetRcode(req, dns.RcodeNameError),
), nil
})
ups := aghtest.NewUpstreamMock(func(req *dns.Msg) (resp *dns.Msg, err error) {
atomic.AddUint32(&upsCalledCounter, 1)
return aghalg.Coalesce(
aghtest.MatchedResponse(req, dns.TypeA, "host", "192.168.0.1"),
new(dns.Msg).SetRcode(req, dns.RcodeNameError),
), nil
})
customUpsConf := proxy.NewCustomUpstreamConfig(
&proxy.UpstreamConfig{
return &proxy.UpstreamConfig{
Upstreams: []upstream.Upstream{ups},
},
true,
defaultCacheSize,
forwardConf.EDNSClientSubnet.Enabled,
)
s.conf.ClientsContainer = &aghtest.ClientsContainer{
OnUpstreamConfigByID: func(
_ string,
_ upstream.Resolver,
) (conf *proxy.CustomUpstreamConfig, err error) {
return customUpsConf, nil
},
}, nil
}
startDeferStop(t, s)
addr := s.dnsProxy.Addr(proxy.ProtoUDP).String()
addr := s.dnsProxy.Addr(proxy.ProtoUDP)
// Send test request.
req := createTestMessage("host.")
reply, err := dns.Exchange(req, addr)
reply, err := dns.Exchange(req, addr.String())
require.NoError(t, err)
require.NotEmpty(t, reply.Answer)
require.Len(t, reply.Answer, 1)
assert.Equal(t, dns.RcodeSuccess, reply.Rcode)
assert.Equal(t, net.IP{192, 168, 0, 1}, reply.Answer[0].(*dns.A).A)
assert.Equal(t, uint32(1), atomic.LoadUint32(&upsCalledCounter))
require.NotEmpty(t, reply.Answer)
_, err = dns.Exchange(req, addr)
require.NoError(t, err)
assert.Equal(t, uint32(1), atomic.LoadUint32(&upsCalledCounter))
require.Len(t, reply.Answer, 1)
assert.Equal(t, net.IP{192, 168, 0, 1}, reply.Answer[0].(*dns.A).A)
}
// testCNAMEs is a map of names and CNAMEs necessary for the TestUpstream work.
@@ -740,7 +708,6 @@ func TestBlockCNAMEProtectionEnabled(t *testing.T) {
Enabled: false,
},
},
ServePlainDNS: true,
}, nil)
testUpstm := &aghtest.Upstream{
CName: testCNAMEs,
@@ -773,7 +740,6 @@ func TestBlockCNAME(t *testing.T) {
Enabled: false,
},
},
ServePlainDNS: true,
}
s := createTestServer(t, &filtering.Config{
ProtectionEnabled: true,
@@ -848,7 +814,6 @@ func TestClientRulesForCNAMEMatching(t *testing.T) {
Enabled: false,
},
},
ServePlainDNS: true,
}
s := createTestServer(t, &filtering.Config{
BlockingMode: filtering.BlockingModeDefault,
@@ -893,7 +858,6 @@ func TestNullBlockedRequest(t *testing.T) {
Enabled: false,
},
},
ServePlainDNS: true,
}
s := createTestServer(t, &filtering.Config{
ProtectionEnabled: true,
@@ -959,7 +923,6 @@ func TestBlockedCustomIP(t *testing.T) {
Enabled: false,
},
},
ServePlainDNS: true,
}
// Invalid BlockingIPv4.
@@ -1011,7 +974,6 @@ func TestBlockedByHosts(t *testing.T) {
Enabled: false,
},
},
ServePlainDNS: true,
}
s := createTestServer(t, &filtering.Config{
@@ -1062,7 +1024,6 @@ func TestBlockedBySafeBrowsing(t *testing.T) {
Enabled: false,
},
},
ServePlainDNS: true,
}
s := createTestServer(t, filterConf, forwardConf, nil)
startDeferStop(t, s)
@@ -1121,7 +1082,6 @@ func TestRewrite(t *testing.T) {
Enabled: false,
},
},
ServePlainDNS: true,
}))
ups := aghtest.NewUpstreamMock(func(req *dns.Msg) (resp *dns.Msg, err error) {

View File

@@ -40,7 +40,6 @@ func TestServer_FilterDNSRewrite(t *testing.T) {
Config: Config{
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
},
ServePlainDNS: true,
}, nil)
makeQ := func(qtype rules.RRType) (req *dns.Msg) {

View File

@@ -10,6 +10,7 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/urlfilter/rules"
"github.com/miekg/dns"
"golang.org/x/exp/slices"
@@ -27,7 +28,8 @@ func (s *Server) beforeRequestHandler(
return false, fmt.Errorf("getting clientid: %w", err)
}
blocked, _ := s.IsBlockedClient(pctx.Addr.Addr(), clientID)
addrPort := netutil.NetAddrToAddrPort(pctx.Addr)
blocked, _ := s.IsBlockedClient(addrPort.Addr(), clientID)
if blocked {
return s.preBlockedResponse(pctx)
}
@@ -58,7 +60,8 @@ func (s *Server) clientRequestFilteringSettings(dctx *dnsContext) (setts *filter
setts = s.dnsFilter.Settings()
setts.ProtectionEnabled = dctx.protectionEnabled
if s.conf.FilterHandler != nil {
s.conf.FilterHandler(dctx.proxyCtx.Addr.Addr(), dctx.clientID, setts)
addrPort := netutil.NetAddrToAddrPort(dctx.proxyCtx.Addr)
s.conf.FilterHandler(addrPort.Addr(), dctx.clientID, setts)
}
return setts

View File

@@ -35,7 +35,6 @@ func TestHandleDNSRequest_handleDNSRequest(t *testing.T) {
Enabled: false,
},
},
ServePlainDNS: true,
}
filters := []filtering.Filter{{
ID: 0, Data: []byte(rules),
@@ -187,7 +186,7 @@ func TestHandleDNSRequest_handleDNSRequest(t *testing.T) {
dctx := &proxy.DNSContext{
Proto: proxy.ProtoUDP,
Req: tc.req,
Addr: testClientAddrPort,
Addr: &net.UDPAddr{IP: net.IP{127, 0, 0, 1}, Port: 1},
}
t.Run(tc.name, func(t *testing.T) {
@@ -326,7 +325,7 @@ func TestHandleDNSRequest_filterDNSResponse(t *testing.T) {
Proto: proxy.ProtoUDP,
Req: tc.req,
Res: resp,
Addr: testClientAddrPort,
Addr: &net.UDPAddr{IP: net.IP{127, 0, 0, 1}, Port: 1},
}
dctx := &dnsContext{

View File

@@ -6,15 +6,21 @@ import (
"io"
"net/http"
"net/netip"
"strings"
"sync"
"sync/atomic"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/dnsproxy/upstream"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/stringutil"
"github.com/miekg/dns"
"golang.org/x/exp/maps"
"golang.org/x/exp/slices"
)
@@ -40,19 +46,8 @@ type jsonDNSConfig struct {
// ProtectionEnabled defines if protection is enabled.
ProtectionEnabled *bool `json:"protection_enabled"`
// Ratelimit is the number of requests per second allowed per client.
Ratelimit *uint32 `json:"ratelimit"`
// RatelimitSubnetLenIPv4 is a subnet length for IPv4 addresses used for
// rate limiting requests.
RatelimitSubnetLenIPv4 *int `json:"ratelimit_subnet_len_ipv4"`
// RatelimitSubnetLenIPv6 is a subnet length for IPv6 addresses used for
// rate limiting requests.
RatelimitSubnetLenIPv6 *int `json:"ratelimit_subnet_len_ipv6"`
// RatelimitWhitelist is a list of IP addresses excluded from rate limiting.
RatelimitWhitelist *[]netip.Addr `json:"ratelimit_whitelist"`
// RateLimit is the number of requests per second allowed per client.
RateLimit *uint32 `json:"ratelimit"`
// BlockingMode defines the way blocked responses are constructed.
BlockingMode *filtering.BlockingMode `json:"blocking_mode"`
@@ -127,9 +122,6 @@ func (s *Server) getDNSConfig() (c *jsonDNSConfig) {
blockingMode, blockingIPv4, blockingIPv6 := s.dnsFilter.BlockingMode()
blockedResponseTTL := s.dnsFilter.BlockedResponseTTL()
ratelimit := s.conf.Ratelimit
ratelimitSubnetLenIPv4 := s.conf.RatelimitSubnetLenIPv4
ratelimitSubnetLenIPv6 := s.conf.RatelimitSubnetLenIPv6
ratelimitWhitelist := append([]netip.Addr{}, s.conf.RatelimitWhitelist...)
customIP := s.conf.EDNSClientSubnet.CustomIP
enableEDNSClientSubnet := s.conf.EDNSClientSubnet.Enabled
@@ -166,10 +158,7 @@ func (s *Server) getDNSConfig() (c *jsonDNSConfig) {
BlockingMode: &blockingMode,
BlockingIPv4: blockingIPv4,
BlockingIPv6: blockingIPv6,
Ratelimit: &ratelimit,
RatelimitSubnetLenIPv4: &ratelimitSubnetLenIPv4,
RatelimitSubnetLenIPv6: &ratelimitSubnetLenIPv6,
RatelimitWhitelist: &ratelimitWhitelist,
RateLimit: &ratelimit,
EDNSCSCustomIP: customIP,
EDNSCSEnabled: &enableEDNSClientSubnet,
EDNSCSUseCustom: &useCustom,
@@ -192,13 +181,13 @@ func (s *Server) getDNSConfig() (c *jsonDNSConfig) {
// defaultLocalPTRUpstreams returns the list of default local PTR resolvers
// filtered of AdGuard Home's own DNS server addresses. It may appear empty.
func (s *Server) defaultLocalPTRUpstreams() (ups []string, err error) {
matcher, err := s.conf.ourAddrsSet()
matcher, err := s.conf.ourAddrsMatcher()
if err != nil {
// Don't wrap the error because it's informative enough as is.
return nil, err
}
sysResolvers := slices.DeleteFunc(s.sysResolvers.Addrs(), matcher.Has)
sysResolvers := slices.DeleteFunc(s.sysResolvers.Addrs(), matcher)
ups = make([]string, 0, len(sysResolvers))
for _, r := range sysResolvers {
ups = append(ups, r.String())
@@ -213,7 +202,6 @@ func (s *Server) handleGetConfig(w http.ResponseWriter, r *http.Request) {
aghhttp.WriteJSONResponseOK(w, r, resp)
}
// checkBlockingMode returns an error if blocking mode is invalid.
func (req *jsonDNSConfig) checkBlockingMode() (err error) {
if req.BlockingMode == nil {
return nil
@@ -222,21 +210,12 @@ func (req *jsonDNSConfig) checkBlockingMode() (err error) {
return validateBlockingMode(*req.BlockingMode, req.BlockingIPv4, req.BlockingIPv6)
}
// checkUpstreamsMode returns an error if the upstream mode is invalid.
func (req *jsonDNSConfig) checkUpstreamsMode() (err error) {
if req.UpstreamMode == nil {
return nil
}
func (req *jsonDNSConfig) checkUpstreamsMode() bool {
valid := []string{"", "fastest_addr", "parallel"}
mode := *req.UpstreamMode
if ok := slices.Contains([]string{"", "fastest_addr", "parallel"}, mode); !ok {
return fmt.Errorf("upstream_mode: incorrect value %q", mode)
}
return nil
return req.UpstreamMode == nil || stringutil.InSlice(valid, *req.UpstreamMode)
}
// checkBootstrap returns an error if any bootstrap address is invalid.
func (req *jsonDNSConfig) checkBootstrap() (err error) {
if req.Bootstraps == nil {
return nil
@@ -251,7 +230,6 @@ func (req *jsonDNSConfig) checkBootstrap() (err error) {
}
if _, err = upstream.NewUpstreamResolver(b, nil); err != nil {
// Don't wrap the error because it's informative enough as is.
return err
}
}
@@ -267,136 +245,67 @@ func (req *jsonDNSConfig) checkFallbacks() (err error) {
err = ValidateUpstreams(*req.Fallbacks)
if err != nil {
return fmt.Errorf("fallback servers: %w", err)
return fmt.Errorf("validating fallback servers: %w", err)
}
return nil
}
// validate returns an error if any field of req is invalid.
//
// TODO(s.chzhen): Parse, don't validate.
func (req *jsonDNSConfig) validate(privateNets netutil.SubnetSet) (err error) {
defer func() { err = errors.Annotate(err, "validating dns config: %w") }()
err = req.validateUpstreamDNSServers(privateNets)
if err != nil {
// Don't wrap the error since it's informative enough as is.
return err
}
err = req.checkRatelimitSubnetMaskLen()
if err != nil {
// Don't wrap the error since it's informative enough as is.
return err
}
err = req.checkBlockingMode()
if err != nil {
// Don't wrap the error since it's informative enough as is.
return err
}
err = req.checkUpstreamsMode()
if err != nil {
// Don't wrap the error since it's informative enough as is.
return err
}
err = req.checkCacheTTL()
if err != nil {
// Don't wrap the error since it's informative enough as is.
return err
}
return nil
}
// validateUpstreamDNSServers returns an error if any field of req is invalid.
func (req *jsonDNSConfig) validateUpstreamDNSServers(privateNets netutil.SubnetSet) (err error) {
if req.Upstreams != nil {
err = ValidateUpstreams(*req.Upstreams)
if err != nil {
return fmt.Errorf("upstream servers: %w", err)
return fmt.Errorf("validating upstream servers: %w", err)
}
}
if req.LocalPTRUpstreams != nil {
err = ValidateUpstreamsPrivate(*req.LocalPTRUpstreams, privateNets)
if err != nil {
return fmt.Errorf("private upstream servers: %w", err)
return fmt.Errorf("validating private upstream servers: %w", err)
}
}
err = req.checkBootstrap()
if err != nil {
// Don't wrap the error since it's informative enough as is.
return err
}
err = req.checkFallbacks()
if err != nil {
// Don't wrap the error since it's informative enough as is.
return err
}
return nil
}
// checkCacheTTL returns an error if the configuration of the cache TTL is
// invalid.
func (req *jsonDNSConfig) checkCacheTTL() (err error) {
if req.CacheMinTTL == nil && req.CacheMaxTTL == nil {
return nil
err = req.checkBlockingMode()
if err != nil {
return err
}
var minTTL, maxTTL uint32
switch {
case !req.checkUpstreamsMode():
return errors.Error("upstream_mode: incorrect value")
case !req.checkCacheTTL():
return errors.Error("cache_ttl_min must be less or equal than cache_ttl_max")
default:
return nil
}
}
func (req *jsonDNSConfig) checkCacheTTL() bool {
if req.CacheMinTTL == nil && req.CacheMaxTTL == nil {
return true
}
var min, max uint32
if req.CacheMinTTL != nil {
minTTL = *req.CacheMinTTL
min = *req.CacheMinTTL
}
if req.CacheMaxTTL != nil {
maxTTL = *req.CacheMaxTTL
max = *req.CacheMaxTTL
}
if minTTL <= maxTTL {
return nil
}
return errors.Error("cache_ttl_min must be less or equal than cache_ttl_max")
}
// checkRatelimitSubnetMaskLen returns an error if the length of the subnet mask
// for IPv4 or IPv6 addresses is invalid.
func (req *jsonDNSConfig) checkRatelimitSubnetMaskLen() (err error) {
err = checkInclusion(req.RatelimitSubnetLenIPv4, 0, netutil.IPv4BitLen)
if err != nil {
return fmt.Errorf("ratelimit_subnet_len_ipv4 is invalid: %w", err)
}
err = checkInclusion(req.RatelimitSubnetLenIPv6, 0, netutil.IPv6BitLen)
if err != nil {
return fmt.Errorf("ratelimit_subnet_len_ipv6 is invalid: %w", err)
}
return nil
}
// checkInclusion returns an error if a ptr is not nil and points to value,
// that not in the inclusive range between minN and maxN.
func checkInclusion(ptr *int, minN, maxN int) (err error) {
if ptr == nil {
return nil
}
n := *ptr
switch {
case n < minN:
return fmt.Errorf("value %d less than min %d", n, minN)
case n > maxN:
return fmt.Errorf("value %d greater than max %d", n, maxN)
}
return nil
return min <= max
}
// handleSetConfig handles requests to the POST /control/dns_config endpoint.
@@ -493,9 +402,6 @@ func (s *Server) setConfigRestartable(dc *jsonDNSConfig) (shouldRestart bool) {
setIfNotNil(&s.conf.CacheOptimistic, dc.CacheOptimistic),
setIfNotNil(&s.conf.AddrProcConf.UseRDNS, dc.ResolveClients),
setIfNotNil(&s.conf.UsePrivateRDNS, dc.UsePrivateRDNS),
setIfNotNil(&s.conf.RatelimitSubnetLenIPv4, dc.RatelimitSubnetLenIPv4),
setIfNotNil(&s.conf.RatelimitSubnetLenIPv6, dc.RatelimitSubnetLenIPv6),
setIfNotNil(&s.conf.RatelimitWhitelist, dc.RatelimitWhitelist),
} {
shouldRestart = shouldRestart || hasSet
if shouldRestart {
@@ -503,8 +409,8 @@ func (s *Server) setConfigRestartable(dc *jsonDNSConfig) (shouldRestart bool) {
}
}
if dc.Ratelimit != nil && s.conf.Ratelimit != *dc.Ratelimit {
s.conf.Ratelimit = *dc.Ratelimit
if dc.RateLimit != nil && s.conf.Ratelimit != *dc.RateLimit {
s.conf.Ratelimit = *dc.RateLimit
shouldRestart = true
}
@@ -519,11 +425,427 @@ type upstreamJSON struct {
PrivateUpstreams []string `json:"private_upstream"`
}
// closeBoots closes all the provided bootstrap servers and logs errors if any.
func closeBoots(boots []*upstream.UpstreamResolver) {
for _, c := range boots {
logCloserErr(c, "dnsforward: closing bootstrap %s: %s", c.Address())
// IsCommentOrEmpty returns true if s starts with a "#" character or is empty.
// This function is useful for filtering out non-upstream lines from upstream
// configs.
func IsCommentOrEmpty(s string) (ok bool) {
return len(s) == 0 || s[0] == '#'
}
// newUpstreamConfig validates upstreams and returns an appropriate upstream
// configuration or nil if it can't be built.
//
// TODO(e.burkov): Perhaps proxy.ParseUpstreamsConfig should validate upstreams
// slice already so that this function may be considered useless.
func newUpstreamConfig(upstreams []string) (conf *proxy.UpstreamConfig, err error) {
// No need to validate comments and empty lines.
upstreams = stringutil.FilterOut(upstreams, IsCommentOrEmpty)
if len(upstreams) == 0 {
// Consider this case valid since it means the default server should be
// used.
return nil, nil
}
err = validateUpstreamConfig(upstreams)
if err != nil {
// Don't wrap the error since it's informative enough as is.
return nil, err
}
conf, err = proxy.ParseUpstreamsConfig(
upstreams,
&upstream.Options{
Bootstrap: []string{},
Timeout: DefaultTimeout,
},
)
if err != nil {
// Don't wrap the error since it's informative enough as is.
return nil, err
} else if len(conf.Upstreams) == 0 {
return nil, errors.Error("no default upstreams specified")
}
return conf, nil
}
// validateUpstreamConfig validates each upstream from the upstream
// configuration and returns an error if any upstream is invalid.
//
// TODO(e.burkov): Move into aghnet or even into dnsproxy.
func validateUpstreamConfig(conf []string) (err error) {
for _, u := range conf {
var ups []string
var domains []string
ups, domains, err = separateUpstream(u)
if err != nil {
// Don't wrap the error since it's informative enough as is.
return err
}
for _, addr := range ups {
_, err = validateUpstream(addr, len(domains) > 0)
if err != nil {
return fmt.Errorf("validating upstream %q: %w", addr, err)
}
}
}
return nil
}
// ValidateUpstreams validates each upstream and returns an error if any
// upstream is invalid or if there are no default upstreams specified.
//
// TODO(e.burkov): Move into aghnet or even into dnsproxy.
func ValidateUpstreams(upstreams []string) (err error) {
_, err = newUpstreamConfig(upstreams)
return err
}
// ValidateUpstreamsPrivate validates each upstream and returns an error if any
// upstream is invalid or if there are no default upstreams specified. It also
// checks each domain of domain-specific upstreams for being ARPA pointing to
// a locally-served network. privateNets must not be nil.
func ValidateUpstreamsPrivate(upstreams []string, privateNets netutil.SubnetSet) (err error) {
conf, err := newUpstreamConfig(upstreams)
if err != nil {
return fmt.Errorf("creating config: %w", err)
}
if conf == nil {
return nil
}
keys := maps.Keys(conf.DomainReservedUpstreams)
slices.Sort(keys)
var errs []error
for _, domain := range keys {
var subnet netip.Prefix
subnet, err = extractARPASubnet(domain)
if err != nil {
errs = append(errs, err)
continue
}
if !privateNets.Contains(subnet.Addr().AsSlice()) {
errs = append(
errs,
fmt.Errorf("arpa domain %q should point to a locally-served network", domain),
)
}
}
return errors.Annotate(errors.Join(errs...), "checking domain-specific upstreams: %w")
}
var protocols = []string{
"h3://",
"https://",
"quic://",
"sdns://",
"tcp://",
"tls://",
"udp://",
}
// validateUpstream returns an error if u alongside with domains is not a valid
// upstream configuration. usesDefault is true if the upstream is
// domain-specific and is configured to point at the default upstream server
// which is validated separately. specific reflects if the upstream is
// domain-specific.
func validateUpstream(u string, specific bool) (usesDefault bool, err error) {
// The special server address '#' means that default server must be used.
if u == "#" && specific {
return true, nil
}
// Check if the upstream has a valid protocol prefix.
//
// TODO(e.burkov): Validate the domain name.
for _, proto := range protocols {
if strings.HasPrefix(u, proto) {
return false, nil
}
}
if proto, _, ok := strings.Cut(u, "://"); ok {
return false, fmt.Errorf("bad protocol %q", proto)
}
// Check if upstream is either an IP or IP with port.
if _, err = netip.ParseAddr(u); err == nil {
return false, nil
} else if _, err = netip.ParseAddrPort(u); err == nil {
return false, nil
}
return false, err
}
// separateUpstream returns the upstreams and the specified domains. domains
// is nil when the upstream is not domains-specific. Otherwise it may also be
// empty.
func separateUpstream(upstreamStr string) (upstreams, domains []string, err error) {
if !strings.HasPrefix(upstreamStr, "[/") {
return []string{upstreamStr}, nil, nil
}
defer func() { err = errors.Annotate(err, "bad upstream for domain %q: %w", upstreamStr) }()
parts := strings.Split(upstreamStr[2:], "/]")
switch len(parts) {
case 2:
// Go on.
case 1:
return nil, nil, errors.Error("missing separator")
default:
return nil, nil, errors.Error("duplicated separator")
}
for i, host := range strings.Split(parts[0], "/") {
if host == "" {
continue
}
err = netutil.ValidateDomainName(strings.TrimPrefix(host, "*."))
if err != nil {
return nil, nil, fmt.Errorf("domain at index %d: %w", i, err)
}
domains = append(domains, host)
}
return strings.Fields(parts[1]), domains, nil
}
// healthCheckFunc is a signature of function to check if upstream exchanges
// properly.
type healthCheckFunc func(u upstream.Upstream) (err error)
// checkExchange is a [healthCheckFunc] that checks if the DNS upstream
// exchanges correctly.
func checkExchange(u upstream.Upstream) (err error) {
// testTLD is the special-use fully-qualified domain name for testing the
// DNS server reachability.
//
// See https://datatracker.ietf.org/doc/html/rfc6761#section-6.2.
const testTLD = "test."
req := &dns.Msg{
MsgHdr: dns.MsgHdr{
Id: dns.Id(),
RecursionDesired: true,
},
Question: []dns.Question{{
Name: testTLD,
Qtype: dns.TypeA,
Qclass: dns.ClassINET,
}},
}
var reply *dns.Msg
reply, err = u.Exchange(req)
if err != nil {
return fmt.Errorf("couldn't communicate with upstream: %w", err)
} else if len(reply.Answer) != 0 {
return errors.Error("wrong response")
}
return nil
}
// checkPrivateExchange is a [healthCheckFunc] that checks if the upstream for
// resolving private addresses exchanges correctly.
//
// TODO(e.burkov): Think about testing the ip6.arpa. as well.
func checkPrivateExchange(u upstream.Upstream) (err error) {
// inAddrArpaTLD is the special-use fully-qualified domain name for PTR IP
// address resolution.
//
// See https://datatracker.ietf.org/doc/html/rfc1035#section-3.5.
const inAddrArpaTLD = "in-addr.arpa."
req := &dns.Msg{
MsgHdr: dns.MsgHdr{
Id: dns.Id(),
RecursionDesired: true,
},
Question: []dns.Question{{
Name: inAddrArpaTLD,
Qtype: dns.TypePTR,
Qclass: dns.ClassINET,
}},
}
if _, err = u.Exchange(req); err != nil {
return fmt.Errorf("couldn't communicate with upstream: %w", err)
}
return nil
}
// domainSpecificTestError is a wrapper for errors returned by checkDNS to mark
// the tested upstream domain-specific and therefore consider its errors
// non-critical.
//
// TODO(a.garipov): Some common mechanism of distinguishing between errors and
// warnings (non-critical errors) is desired.
type domainSpecificTestError struct {
error
}
// Error implements the [error] interface for domainSpecificTestError.
func (err domainSpecificTestError) Error() (msg string) {
return fmt.Sprintf("WARNING: %s", err.error)
}
// checkUpstreamAddr creates the upstream using opts and, possibly, information
// from system hosts files, then checks if the DNS upstream exchanges correctly.
// It returns an error if addr is not valid DNS upstream address or the upstream
// is not exchanging correctly.
//
// TODO(e.burkov): Remove the receiver.
func (s *Server) checkUpstreamAddr(
addr string,
specific bool,
basicOpts *upstream.Options,
check healthCheckFunc,
) (err error) {
usesDefault, err := validateUpstream(addr, specific)
if err != nil {
return fmt.Errorf("wrong upstream format: %w", err)
} else if usesDefault {
return nil
}
log.Debug("dnsforward: checking if upstream %q works", addr)
defer func() {
if err != nil && specific {
err = domainSpecificTestError{error: err}
}
}()
opts := &upstream.Options{
Bootstrap: basicOpts.Bootstrap,
Timeout: basicOpts.Timeout,
PreferIPv6: basicOpts.PreferIPv6,
}
// dnsFilter can be nil during application update.
//
// TODO(e.burkov): Remove when update dnsproxy.
if s.dnsFilter != nil {
recs := s.dnsFilter.EtcHostsRecords(extractUpstreamHost(addr))
for _, rec := range recs {
opts.ServerIPAddrs = append(opts.ServerIPAddrs, rec.Addr.AsSlice())
}
sortNetIPAddrs(opts.ServerIPAddrs, opts.PreferIPv6)
}
u, err := upstream.AddressToUpstream(addr, opts)
if err != nil {
return fmt.Errorf("creating upstream for %q: %w", addr, err)
}
defer func() { err = errors.WithDeferred(err, u.Close()) }()
return check(u)
}
// checkResult is a result of checking an upstream server.
type checkResult = struct {
// status is an error message if the upstream server is not working. It's
// nil for working upstreams.
status error
// address is the upstream server address as given in the request. It may
// appear to be a whole line if it's incorrect itself.
address string
}
// checkDNS parses an upstream configuration line using opts and checks if the
// specified upstreams are working using check. countWG is decremented when the
// expected number of results added to resNum, then results are sent to resCh.
//
// TODO(e.burkov): Remove the receiver.
func (s *Server) checkDNS(
line string,
opts *upstream.Options,
check healthCheckFunc,
countWG *sync.WaitGroup,
resNum *atomic.Int32,
resCh chan<- checkResult,
) {
defer log.OnPanic("dnsforward: checking upstreams")
addrs, domains, err := separateUpstream(line)
if err != nil {
resNum.Add(1)
countWG.Done()
resCh <- checkResult{
address: line,
status: fmt.Errorf("wrong upstream format: %w", err),
}
return
}
resNum.Add(int32(len(addrs)))
countWG.Done()
specific := len(domains) > 0
for _, addr := range addrs {
resCh <- checkResult{
address: addr,
status: s.checkUpstreamAddr(addr, specific, opts, check),
}
}
}
// check returns the mapping of upstream addresses to their check results.
func (s *Server) check(req *upstreamJSON, opts *upstream.Options) (result map[string]string) {
req.Upstreams = stringutil.FilterOut(req.Upstreams, IsCommentOrEmpty)
req.FallbackDNS = stringutil.FilterOut(req.FallbackDNS, IsCommentOrEmpty)
req.PrivateUpstreams = stringutil.FilterOut(req.PrivateUpstreams, IsCommentOrEmpty)
countWG := &sync.WaitGroup{}
countWG.Add(len(req.Upstreams) + len(req.FallbackDNS) + len(req.PrivateUpstreams))
resNum := &atomic.Int32{}
resCh := make(chan checkResult)
for _, addr := range req.Upstreams {
go s.checkDNS(addr, opts, checkExchange, countWG, resNum, resCh)
}
for _, addr := range req.FallbackDNS {
go s.checkDNS(addr, opts, checkExchange, countWG, resNum, resCh)
}
for _, addr := range req.PrivateUpstreams {
go s.checkDNS(addr, opts, checkPrivateExchange, countWG, resNum, resCh)
}
// Wait until all the servers are counted and enqueued.
countWG.Wait()
n := resNum.Load()
result = make(map[string]string, n)
for i := int32(0); i < n; i++ {
// TODO(e.burkov): Upstreams intended for different purposes should
// be distinguished in the result, even if specified equally.
res := <-resCh
if res.status != nil {
result[res.address] = res.status.Error()
} else {
result[res.address] = "OK"
}
}
return result
}
// handleTestUpstreamDNS handles requests to the POST /control/test_upstream_dns
@@ -537,30 +859,18 @@ func (s *Server) handleTestUpstreamDNS(w http.ResponseWriter, r *http.Request) {
return
}
req.Upstreams = stringutil.FilterOut(req.Upstreams, IsCommentOrEmpty)
req.FallbackDNS = stringutil.FilterOut(req.FallbackDNS, IsCommentOrEmpty)
req.PrivateUpstreams = stringutil.FilterOut(req.PrivateUpstreams, IsCommentOrEmpty)
req.BootstrapDNS = stringutil.FilterOut(req.BootstrapDNS, IsCommentOrEmpty)
bootstrapAddrs := stringutil.FilterOut(req.BootstrapDNS, IsCommentOrEmpty)
if len(bootstrapAddrs) == 0 {
bootstrapAddrs = defaultBootstrap
}
opts := &upstream.Options{
Bootstrap: bootstrapAddrs,
Timeout: s.conf.UpstreamTimeout,
PreferIPv6: s.conf.BootstrapPreferIPv6,
}
var boots []*upstream.UpstreamResolver
opts.Bootstrap, boots, err = s.createBootstrap(req.BootstrapDNS, opts)
if err != nil {
aghhttp.Error(r, w, http.StatusBadRequest, "Failed to parse bootstrap servers: %s", err)
return
}
defer closeBoots(boots)
cv := newUpstreamConfigValidator(req.Upstreams, req.FallbackDNS, req.PrivateUpstreams, opts)
cv.check()
cv.close()
aghhttp.WriteJSONResponseOK(w, r, cv.status())
aghhttp.WriteJSONResponseOK(w, r, s.check(req, opts))
}
// handleCacheClear is the handler for the POST /control/cache_clear HTTP API.

View File

@@ -20,7 +20,6 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
"github.com/AdguardTeam/AdGuardHome/internal/aghtest"
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/dnsproxy/upstream"
"github.com/AdguardTeam/golibs/httphdr"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/testutil"
@@ -73,14 +72,11 @@ func TestDNSForwardHTTP_handleGetConfig(t *testing.T) {
UDPListenAddrs: []*net.UDPAddr{},
TCPListenAddrs: []*net.TCPAddr{},
Config: Config{
UpstreamDNS: []string{"8.8.8.8:53", "8.8.4.4:53"},
FallbackDNS: []string{"9.9.9.10"},
RatelimitSubnetLenIPv4: 24,
RatelimitSubnetLenIPv6: 56,
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
UpstreamDNS: []string{"8.8.8.8:53", "8.8.4.4:53"},
FallbackDNS: []string{"9.9.9.10"},
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
},
ConfigModified: func() {},
ServePlainDNS: true,
}
s := createTestServer(t, filterConf, forwardConf, nil)
s.sysResolvers = &emptySysResolvers{}
@@ -154,13 +150,10 @@ func TestDNSForwardHTTP_handleSetConfig(t *testing.T) {
UDPListenAddrs: []*net.UDPAddr{},
TCPListenAddrs: []*net.TCPAddr{},
Config: Config{
UpstreamDNS: []string{"8.8.8.8:53", "8.8.4.4:53"},
RatelimitSubnetLenIPv4: 24,
RatelimitSubnetLenIPv6: 56,
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
UpstreamDNS: []string{"8.8.8.8:53", "8.8.4.4:53"},
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
},
ConfigModified: func() {},
ServePlainDNS: true,
}
s := createTestServer(t, filterConf, forwardConf, nil)
s.sysResolvers = &emptySysResolvers{}
@@ -186,18 +179,11 @@ func TestDNSForwardHTTP_handleSetConfig(t *testing.T) {
name: "blocking_mode_good",
wantSet: "",
}, {
name: "blocking_mode_bad",
wantSet: "validating dns config: " +
"blocking_ipv4 must be valid ipv4 on custom_ip blocking_mode",
name: "blocking_mode_bad",
wantSet: "blocking_ipv4 must be valid ipv4 on custom_ip blocking_mode",
}, {
name: "ratelimit",
wantSet: "",
}, {
name: "ratelimit_subnet_len",
wantSet: "",
}, {
name: "ratelimit_whitelist_not_ip",
wantSet: `decoding request: ParseAddr("not.ip"): unexpected character (at "not.ip")`,
}, {
name: "edns_cs_enabled",
wantSet: "",
@@ -220,26 +206,24 @@ func TestDNSForwardHTTP_handleSetConfig(t *testing.T) {
name: "upstream_mode_fastest_addr",
wantSet: "",
}, {
name: "upstream_dns_bad",
wantSet: `validating dns config: ` +
`upstream servers: validating upstream "!!!": not an ip:port`,
name: "upstream_dns_bad",
wantSet: `validating upstream servers: validating upstream "!!!": not an ip:port`,
}, {
name: "bootstraps_bad",
wantSet: `validating dns config: checking bootstrap a: invalid address: not a bootstrap: ` +
wantSet: `checking bootstrap a: invalid address: bootstrap a:53: ` +
`ParseAddr("a"): unable to parse IP`,
}, {
name: "cache_bad_ttl",
wantSet: `validating dns config: cache_ttl_min must be less or equal than cache_ttl_max`,
wantSet: `cache_ttl_min must be less or equal than cache_ttl_max`,
}, {
name: "upstream_mode_bad",
wantSet: `validating dns config: upstream_mode: incorrect value "somethingelse"`,
wantSet: `upstream_mode: incorrect value`,
}, {
name: "local_ptr_upstreams_good",
wantSet: "",
}, {
name: "local_ptr_upstreams_bad",
wantSet: `validating dns config: ` +
`private upstream servers: checking domain-specific upstreams: ` +
wantSet: `validating private upstream servers: checking domain-specific upstreams: ` +
`bad arpa domain name "non.arpa.": not a reversed ip network`,
}, {
name: "local_ptr_upstreams_null",
@@ -363,7 +347,7 @@ func TestValidateUpstreams(t *testing.T) {
set: []string{"123.3.7m"},
}, {
name: "invalid",
wantErr: `splitting upstream line "[/host.com]tls://dns.adguard.com": ` +
wantErr: `bad upstream for domain "[/host.com]tls://dns.adguard.com": ` +
`missing separator`,
set: []string{"[/host.com]tls://dns.adguard.com"},
}, {
@@ -389,7 +373,7 @@ func TestValidateUpstreams(t *testing.T) {
},
}, {
name: "bad_domain",
wantErr: `splitting upstream line "[/!/]8.8.8.8": domain at index 0: ` +
wantErr: `bad upstream for domain "[/!/]8.8.8.8": domain at index 0: ` +
`bad domain name "!": bad top-level domain name label "!": ` +
`bad top-level domain name label rune '!'`,
set: []string{"[/!/]8.8.8.8"},
@@ -477,15 +461,25 @@ func newLocalUpstreamListener(t *testing.T, port uint16, handler dns.Handler) (r
}
func TestServer_HandleTestUpstreamDNS(t *testing.T) {
hdlr := 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))
require.NoError(testutil.PanicT{}, err)
})
badHandler := dns.HandlerFunc(func(w dns.ResponseWriter, _ *dns.Msg) {
err := w.WriteMsg(new(dns.Msg))
require.NoError(testutil.PanicT{}, err)
})
ups := (&url.URL{
goodUps := (&url.URL{
Scheme: "tcp",
Host: newLocalUpstreamListener(t, 0, hdlr).String(),
Host: newLocalUpstreamListener(t, 0, goodHandler).String(),
}).String()
badUps := (&url.URL{
Scheme: "tcp",
Host: newLocalUpstreamListener(t, 0, badHandler).String(),
}).String()
goodAndBadUps := strings.Join([]string{goodUps, badUps}, " ")
const (
upsTimeout = 100 * time.Millisecond
@@ -494,7 +488,7 @@ func TestServer_HandleTestUpstreamDNS(t *testing.T) {
upstreamHost = "custom.localhost"
)
hostsListener := newLocalUpstreamListener(t, 0, hdlr)
hostsListener := newLocalUpstreamListener(t, 0, goodHandler)
hostsUps := (&url.URL{
Scheme: "tcp",
Host: netutil.JoinHostPort(upstreamHost, hostsListener.Port()),
@@ -525,9 +519,7 @@ func TestServer_HandleTestUpstreamDNS(t *testing.T) {
Config: Config{
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
},
ServePlainDNS: true,
}, nil)
srv.etcHosts = upstream.NewHostsResolver(hc)
startDeferStop(t, srv)
testCases := []struct {
@@ -535,6 +527,43 @@ func TestServer_HandleTestUpstreamDNS(t *testing.T) {
wantResp map[string]any
name string
}{{
body: map[string]any{
"upstream_dns": []string{goodUps},
},
wantResp: map[string]any{
goodUps: "OK",
},
name: "success",
}, {
body: map[string]any{
"upstream_dns": []string{badUps},
},
wantResp: map[string]any{
badUps: `couldn't communicate with upstream: exchanging with ` +
badUps + ` over tcp: dns: id mismatch`,
},
name: "broken",
}, {
body: map[string]any{
"upstream_dns": []string{goodUps, badUps},
},
wantResp: map[string]any{
goodUps: "OK",
badUps: `couldn't communicate with upstream: exchanging with ` +
badUps + ` over tcp: dns: id mismatch`,
},
name: "both",
}, {
body: map[string]any{
"upstream_dns": []string{"[/domain.example/]" + badUps},
},
wantResp: map[string]any{
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},
},
@@ -544,12 +573,63 @@ func TestServer_HandleTestUpstreamDNS(t *testing.T) {
name: "etc_hosts",
}, {
body: map[string]any{
"upstream_dns": []string{ups, "#this.is.comment"},
"fallback_dns": []string{goodUps},
},
wantResp: map[string]any{
ups: "OK",
goodUps: "OK",
},
name: "comment_mix",
name: "fallback_success",
}, {
body: map[string]any{
"fallback_dns": []string{badUps},
},
wantResp: map[string]any{
badUps: `couldn't communicate with upstream: exchanging with ` +
badUps + ` over tcp: dns: id mismatch`,
},
name: "fallback_broken",
}, {
body: map[string]any{
"fallback_dns": []string{goodUps, "#this.is.comment"},
},
wantResp: map[string]any{
goodUps: "OK",
},
name: "fallback_comment_mix",
}, {
body: map[string]any{
"upstream_dns": []string{"[/domain.example/]" + goodUps + " " + badUps},
},
wantResp: map[string]any{
goodUps: "OK",
badUps: `WARNING: couldn't communicate ` +
`with upstream: exchanging with ` + badUps + ` over tcp: ` +
`dns: id mismatch`,
},
name: "multiple_domain_specific_upstreams",
}, {
body: map[string]any{
"upstream_dns": []string{"[/domain.example/]/]1.2.3.4"},
},
wantResp: map[string]any{
"[/domain.example/]/]1.2.3.4": `wrong upstream format: ` +
`bad upstream for domain "[/domain.example/]/]1.2.3.4": ` +
`duplicated separator`,
},
name: "bad_specification",
}, {
body: map[string]any{
"upstream_dns": []string{"[/domain.example/]" + goodAndBadUps},
"fallback_dns": []string{"[/domain.example/]" + goodAndBadUps},
"private_upstream": []string{"[/domain.example/]" + goodAndBadUps},
},
wantResp: map[string]any{
goodUps: "OK",
badUps: `WARNING: couldn't communicate ` +
`with upstream: exchanging with ` + badUps + ` over tcp: ` +
`dns: id mismatch`,
},
name: "all_different",
}}
for _, tc := range testCases {

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