all: sync with master; upd chlog

This commit is contained in:
Ainar Garipov 2023-10-11 17:31:41 +03:00
parent 258eecc55b
commit 760d466b38
139 changed files with 39736 additions and 18364 deletions

View file

@ -1,8 +1,8 @@
'name': 'build' 'name': 'build'
'env': 'env':
'GO_VERSION': '1.20.8' 'GO_VERSION': '1.20.10'
'NODE_VERSION': '14' 'NODE_VERSION': '16'
'on': 'on':
'push': 'push':

View file

@ -1,7 +1,7 @@
'name': 'lint' 'name': 'lint'
'env': 'env':
'GO_VERSION': '1.20.8' 'GO_VERSION': '1.20.10'
'on': 'on':
'push': 'push':

View file

@ -14,11 +14,11 @@ and this project adheres to
<!-- <!--
## [v0.108.0] - TBA ## [v0.108.0] - TBA
## [v0.107.39] - 2023-09-27 (APPROX.) ## [v0.107.40] - 2023-10-09 (APPROX.)
See also the [v0.107.39 GitHub milestone][ms-v0.107.39]. See also the [v0.107.40 GitHub milestone][ms-v0.107.40].
[ms-v0.107.39]: https://github.com/AdguardTeam/AdGuardHome/milestone/74?closed=1 [ms-v0.107.40]: https://github.com/AdguardTeam/AdGuardHome/milestone/75?closed=1
NOTE: Add new changes BELOW THIS COMMENT. NOTE: Add new changes BELOW THIS COMMENT.
--> -->
@ -29,6 +29,50 @@ NOTE: Add new changes ABOVE THIS COMMENT.
## [v0.107.39] - 2023-10-11
See also the [v0.107.39 GitHub milestone][ms-v0.107.39].
### Security
- Go version has been updated to prevent the possibility of exploiting the
CVE-2023-39323 and CVE-2023-39325 Go vulnerabilities fixed in
[Go 1.20.9][go-1.20.9] and [Go 1.20.10][go-1.20.10].
### Added
- Ability to edit static leases on *DHCP settings* page ([#1700]).
- Ability to specify for how long clients should cache a filtered response,
using the *Blocked response TTL* field on the *DNS settings* page ([#4569]).
### Changed
- ipset entries are updated more often ([#6233]).
- Node.JS 16 is now required to build the frontend.
### Fixed
- Incorrect domain-specific upstream matching for `DS` queries ([#6156]).
- Improper validation of password length ([#6280]).
- Wrong algorithm for filtering self addresses from the list of private upstream
DNS servers ([#6231]).
- An accidental change in DNS rewrite priority ([#6226]).
[#1700]: https://github.com/AdguardTeam/AdGuardHome/issues/1700
[#4569]: https://github.com/AdguardTeam/AdGuardHome/issues/4569
[#6156]: https://github.com/AdguardTeam/AdGuardHome/issues/6156
[#6226]: https://github.com/AdguardTeam/AdGuardHome/issues/6226
[#6231]: https://github.com/AdguardTeam/AdGuardHome/issues/6231
[#6233]: https://github.com/AdguardTeam/AdGuardHome/issues/6233
[#6280]: https://github.com/AdguardTeam/AdGuardHome/issues/6280
[go-1.20.9]: https://groups.google.com/g/golang-announce/c/XBa1oHDevAo/m/desYyx3qAgAJ
[go-1.20.10]: https://groups.google.com/g/golang-announce/c/iNNxDTCjZvo/m/UDd7VKQuAAAJ
[ms-v0.107.39]: https://github.com/AdguardTeam/AdGuardHome/milestone/74?closed=1
## [v0.107.38] - 2023-09-11 ## [v0.107.38] - 2023-09-11
See also the [v0.107.38 GitHub milestone][ms-v0.107.38]. See also the [v0.107.38 GitHub milestone][ms-v0.107.38].
@ -2487,11 +2531,12 @@ See also the [v0.104.2 GitHub milestone][ms-v0.104.2].
<!-- <!--
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.39...HEAD [Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.40...HEAD
[v0.107.39]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.38...v0.107.39 [v0.107.40]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.39...v0.107.40
--> -->
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.38...HEAD [Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.39...HEAD
[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 [v0.107.38]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.37...v0.107.38
[v0.107.37]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.36...v0.107.37 [v0.107.37]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.36...v0.107.37
[v0.107.36]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.35...v0.107.36 [v0.107.36]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.35...v0.107.36

View file

@ -24,6 +24,7 @@ CHANNEL = development
CLIENT_DIR = client CLIENT_DIR = client
COMMIT = $$( git rev-parse --short HEAD ) COMMIT = $$( git rev-parse --short HEAD )
DIST_DIR = dist DIST_DIR = dist
GOAMD64 = v1
GOPROXY = https://goproxy.cn|https://proxy.golang.org|direct GOPROXY = https://goproxy.cn|https://proxy.golang.org|direct
GOSUMDB = sum.golang.google.cn GOSUMDB = sum.golang.google.cn
GPG_KEY = devteam@adguard.com GPG_KEY = devteam@adguard.com
@ -61,6 +62,7 @@ ENV = env\
GPG_KEY_PASSPHRASE='$(GPG_KEY_PASSPHRASE)'\ GPG_KEY_PASSPHRASE='$(GPG_KEY_PASSPHRASE)'\
DIST_DIR='$(DIST_DIR)'\ DIST_DIR='$(DIST_DIR)'\
GO="$(GO.MACRO)"\ GO="$(GO.MACRO)"\
GOAMD64="$(GOAMD64)"\
GOPROXY='$(GOPROXY)'\ GOPROXY='$(GOPROXY)'\
GOSUMDB='$(GOSUMDB)'\ GOSUMDB='$(GOSUMDB)'\
PATH="$${PWD}/bin:$$( "$(GO.MACRO)" env GOPATH )/bin:$${PATH}"\ PATH="$${PWD}/bin:$$( "$(GO.MACRO)" env GOPATH )/bin:$${PATH}"\

View file

@ -262,8 +262,8 @@ Run `make init` to prepare the development environment.
You will need this to build AdGuard Home: You will need this to build AdGuard Home:
* [Go](https://golang.org/dl/) v1.20 or later; * [Go](https://golang.org/dl/) v1.20 or later;
* [Node.js](https://nodejs.org/en/download/) v10.16.2 or later; * [Node.js](https://nodejs.org/en/download/) v16 or later;
* [npm](https://www.npmjs.com/) v6.14 or later; * [npm](https://www.npmjs.com/) v8 or later;
* [yarn](https://yarnpkg.com/) v1.22.5 or later. * [yarn](https://yarnpkg.com/) v1.22.5 or later.

View file

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

View file

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

View file

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

25137
client/package-lock.json generated vendored

File diff suppressed because it is too large Load diff

1
client/package.json vendored
View file

@ -42,7 +42,6 @@
"redux-actions": "^2.6.5", "redux-actions": "^2.6.5",
"redux-form": "^8.3.5", "redux-form": "^8.3.5",
"redux-thunk": "^2.3.0", "redux-thunk": "^2.3.0",
"string-length": "^5.0.1",
"timezones-list": "^3.0.2", "timezones-list": "^3.0.2",
"url-polyfill": "^1.1.9" "url-polyfill": "^1.1.9"
}, },

View file

@ -278,6 +278,7 @@
"custom_ip": "عنوان IP مخصص", "custom_ip": "عنوان IP مخصص",
"blocking_ipv4": "حجب عنوان IPv4", "blocking_ipv4": "حجب عنوان IPv4",
"blocking_ipv6": "حجب عنوان IPv6", "blocking_ipv6": "حجب عنوان IPv6",
"blocked_response_ttl": "زمن حظر الاستجابة",
"dnscrypt": "DNSCrypt", "dnscrypt": "DNSCrypt",
"dns_over_https": "DNS-over-HTTPS", "dns_over_https": "DNS-over-HTTPS",
"dns_over_tls": "DNS-over-TLS", "dns_over_tls": "DNS-over-TLS",
@ -598,7 +599,7 @@
"all_queries": "كافة الاستفسارات", "all_queries": "كافة الاستفسارات",
"show_blocked_responses": "حظر", "show_blocked_responses": "حظر",
"show_whitelisted_responses": "القائمة البيضاء", "show_whitelisted_responses": "القائمة البيضاء",
"show_processed_responses": "المعالجة", "show_processed_responses": "معالجة",
"blocked_safebrowsing": "محظور بواسطة التصفح الآمن", "blocked_safebrowsing": "محظور بواسطة التصفح الآمن",
"blocked_adult_websites": "محظور بواسطة الرقابة الأبوية", "blocked_adult_websites": "محظور بواسطة الرقابة الأبوية",
"blocked_threats": "التهديدات المحظورة", "blocked_threats": "التهديدات المحظورة",

View file

@ -73,7 +73,9 @@
"dhcp_dynamic_ip_found": "Ваша сістэма выкарыстоўвае дынамічны IP-адрас для інтэрфейсу <0>{{interfaceName}}</0>. Каб выкарыстоўваць DHCP-сервер трэба ўсталяваць статычны IP-адрас. Ваш бягучы IP-адрас <0>{{ipAddress}}</0>. Мы аўтаматычна ўсталюем яго як статычны, калі вы націснеце кнопку «Ўключыць DHCP».", "dhcp_dynamic_ip_found": "Ваша сістэма выкарыстоўвае дынамічны IP-адрас для інтэрфейсу <0>{{interfaceName}}</0>. Каб выкарыстоўваць DHCP-сервер трэба ўсталяваць статычны IP-адрас. Ваш бягучы IP-адрас <0>{{ipAddress}}</0>. Мы аўтаматычна ўсталюем яго як статычны, калі вы націснеце кнопку «Ўключыць DHCP».",
"dhcp_lease_added": "Статычная арэнда «{{key}}» паспяхова дададзена", "dhcp_lease_added": "Статычная арэнда «{{key}}» паспяхова дададзена",
"dhcp_lease_deleted": "Статычная арэнда «{{key}}» паспяхова выдалена", "dhcp_lease_deleted": "Статычная арэнда «{{key}}» паспяхова выдалена",
"dhcp_lease_updated": "Статычная арэнда \"{{key}}\" паспяхова абноўлена",
"dhcp_new_static_lease": "Новая статычная арэнда", "dhcp_new_static_lease": "Новая статычная арэнда",
"dhcp_edit_static_lease": "Рэдагаваць статычную арэнду",
"dhcp_static_leases_not_found": "Не знойдзена статычных арэнд DHCP", "dhcp_static_leases_not_found": "Не знойдзена статычных арэнд DHCP",
"dhcp_add_static_lease": "Дадаць статычную арэнду", "dhcp_add_static_lease": "Дадаць статычную арэнду",
"dhcp_reset_leases": "Скінуць усё арэнды", "dhcp_reset_leases": "Скінуць усё арэнды",
@ -174,6 +176,7 @@
"enabled_parental_toast": "Уключаны бацькоўскі кантроль", "enabled_parental_toast": "Уключаны бацькоўскі кантроль",
"disabled_safe_search_toast": "Адключаны бяспечны пошук", "disabled_safe_search_toast": "Адключаны бяспечны пошук",
"enabled_save_search_toast": "Уключаны бяспечны пошук", "enabled_save_search_toast": "Уключаны бяспечны пошук",
"updated_save_search_toast": "Налады бяспечнага пошуку абноўлены",
"enabled_table_header": "УКЛ.", "enabled_table_header": "УКЛ.",
"name_table_header": "Імя", "name_table_header": "Імя",
"list_url_table_header": "URL-адрас спіса", "list_url_table_header": "URL-адрас спіса",
@ -282,6 +285,9 @@
"custom_ip": "Свой IP", "custom_ip": "Свой IP",
"blocking_ipv4": "Блакаванне IPv4", "blocking_ipv4": "Блакаванне IPv4",
"blocking_ipv6": "Блакаванне IPv6", "blocking_ipv6": "Блакаванне IPv6",
"blocked_response_ttl": "TTL заблакаванага адказу",
"blocked_response_ttl_desc": "Паказвае, на працягу колькіх секунд кліенты павінны кэшаваць адфільтраваць адказ",
"form_enter_blocked_response_ttl": "Увядзіце TTL заблакіраванага адказу (у секундах)",
"dnscrypt": "DNSCrypt", "dnscrypt": "DNSCrypt",
"dns_over_https": "DNS-over-HTTPS", "dns_over_https": "DNS-over-HTTPS",
"dns_over_tls": "DNS-over-TLS", "dns_over_tls": "DNS-over-TLS",
@ -297,6 +303,8 @@
"rate_limit": "Ограничение скорости", "rate_limit": "Ограничение скорости",
"edns_enable": "Уключыць адпраўленне EDNS Client Subnet", "edns_enable": "Уключыць адпраўленне EDNS Client Subnet",
"edns_cs_desc": "Дадайце опцыю кліенцкай падсеткі EDNS (ECS) да запытаў вышэй па плыні і запісвайце значэнні, адпраўленыя кліентамі, у журнал запытаў.", "edns_cs_desc": "Дадайце опцыю кліенцкай падсеткі EDNS (ECS) да запытаў вышэй па плыні і запісвайце значэнні, адпраўленыя кліентамі, у журнал запытаў.",
"edns_use_custom_ip": "Выкарыстоўваць указаны IP для DNS",
"edns_use_custom_ip_desc": "Дазволіць выкарыстоўваць уласны IP для DNS",
"rate_limit_desc": "Абмежаванне на колькасць запытаў у секунду для кожнага кліента (0 — неабмежавана)", "rate_limit_desc": "Абмежаванне на колькасць запытаў у секунду для кожнага кліента (0 — неабмежавана)",
"blocking_ipv4_desc": "IP-адрас, што вяртаецца пры блакаванню A-запыту", "blocking_ipv4_desc": "IP-адрас, што вяртаецца пры блакаванню A-запыту",
"blocking_ipv6_desc": "IP-адрас, што вяртаецца пры блакаванню AAAA-запыту", "blocking_ipv6_desc": "IP-адрас, што вяртаецца пры блакаванню AAAA-запыту",
@ -532,6 +540,8 @@
"statistics_retention_confirm": "Вы ўпэўнены, што хочаце змяніць тэрмін захоўвання статыстыкі? Пры памяншэнні інтэрвалу, некаторыя даныя могуць быць страчаны", "statistics_retention_confirm": "Вы ўпэўнены, што хочаце змяніць тэрмін захоўвання статыстыкі? Пры памяншэнні інтэрвалу, некаторыя даныя могуць быць страчаны",
"statistics_cleared": "Статыстыка паспяхова вычышчана", "statistics_cleared": "Статыстыка паспяхова вычышчана",
"statistics_enable": "Уключыць статыстыку", "statistics_enable": "Уключыць статыстыку",
"ignore_domains": "Ігнаруемыя дамены (парадкова)",
"ignore_domains_title": "Ігнаруемыя дамены",
"ignore_domains_desc_stats": "Запыты, якія адпавядаюць гэтым правілам, не запісваюцца ў статыстыку", "ignore_domains_desc_stats": "Запыты, якія адпавядаюць гэтым правілам, не запісваюцца ў статыстыку",
"ignore_domains_desc_query": "Запыты, якія адпавядаюць гэтым правілам, не запісваюцца ў часопіс запытаў", "ignore_domains_desc_query": "Запыты, якія адпавядаюць гэтым правілам, не запісваюцца ў часопіс запытаў",
"interval_hours": "{{count}} гадзіна", "interval_hours": "{{count}} гадзіна",
@ -649,13 +659,32 @@
"parental_control": "Бацькоўскі кантроль", "parental_control": "Бацькоўскі кантроль",
"safe_browsing": "Бяспечны інтэрнэт", "safe_browsing": "Бяспечны інтэрнэт",
"served_from_cache": "{{value}} <i>(атрымана з кэша)</i>", "served_from_cache": "{{value}} <i>(атрымана з кэша)</i>",
"form_error_password_length": "Пароль павінен быць не менш за {{value}} сімвалаў", "form_error_password_length": "Пароль павінен утрымліваць ад {{min}} да {{max}} сімвалаў",
"anonymizer_notification": "<0>Заўвага:</0> Ананімізацыя IP уключана. Вы можаце адключыць яе ў <1>Агульных наладах</1>.", "anonymizer_notification": "<0>Заўвага:</0> Ананімізацыя IP уключана. Вы можаце адключыць яе ў <1>Агульных наладах</1>.",
"confirm_dns_cache_clear": "Вы ўпэўнены, што хочаце ачысціць кэш DNS?", "confirm_dns_cache_clear": "Вы ўпэўнены, што хочаце ачысціць кэш DNS?",
"cache_cleared": "Кэш DNS паспяхова ачышчаны", "cache_cleared": "Кэш DNS паспяхова ачышчаны",
"clear_cache": "Ачысціць кэш", "clear_cache": "Ачысціць кэш",
"make_static": "Зрабіць статычнай",
"theme_auto_desc": "Аўто (на аснове каляровай схемы вашага прылады)",
"theme_dark_desc": "Цёмная тэма", "theme_dark_desc": "Цёмная тэма",
"theme_light_desc": "Светлая тэма", "theme_light_desc": "Светлая тэма",
"disable_for_seconds": "На {{count}} секунд",
"disable_for_seconds_plural": "На {{count}} секунд",
"disable_for_minutes": "На {{count}} хвіліну",
"disable_for_minutes_plural": "На {{count}} хвілін",
"disable_for_hours": "На {{count}} гадзін",
"disable_for_hours_plural": "На {{count}} гадзін",
"disable_until_tomorrow": "Да заўтра",
"disable_notify_for_seconds": "Адключыць абарону на {{count}} секунд",
"disable_notify_for_seconds_plural": "Адключыць абарону на {{count}} секунд",
"disable_notify_for_minutes": "Адключыць абарону на {{count}} хвіліну",
"disable_notify_for_minutes_plural": "Адключыць абарону на {{count}} хвілін",
"disable_notify_for_hours": "Адключыць абарону на {{count}} гадзін",
"disable_notify_for_hours_plural": "Адключыць абарону на {{count}} гадзін",
"disable_notify_until_tomorrow": "Адключыць абарону да заўтра",
"enable_protection_timer": "Абарона будзе ўключана ў {{time}}",
"custom_retention_input": "Увядзіце тэрмін захоўвання ў гадзінах",
"custom_rotation_input": "Увядзіце частату ратацыі ў гадзінах",
"protection_section_label": "Ахова", "protection_section_label": "Ахова",
"log_and_stats_section_label": "Журнал запытаў і статыстыка", "log_and_stats_section_label": "Журнал запытаў і статыстыка",
"ignore_query_log": "Ігнараваць гэтага кліента ў журнале запытаў", "ignore_query_log": "Ігнараваць гэтага кліента ў журнале запытаў",

View file

@ -73,7 +73,9 @@
"dhcp_dynamic_ip_found": "Váš systém používá konfiguraci dynamické IP adresy pro rozhraní <0>{{interfaceName}}</0>. Pro použití serveru DHCP musí být nastavena statická IP adresa. Vaše aktuální IP adresa je <0>{{ipAddress}}</0>. AdGuard Home automaticky nastaví tuto IP adresu jako statickou, pokud stisknete tlačítko \"Zapnout DHCP server\".", "dhcp_dynamic_ip_found": "Váš systém používá konfiguraci dynamické IP adresy pro rozhraní <0>{{interfaceName}}</0>. Pro použití serveru DHCP musí být nastavena statická IP adresa. Vaše aktuální IP adresa je <0>{{ipAddress}}</0>. AdGuard Home automaticky nastaví tuto IP adresu jako statickou, pokud stisknete tlačítko \"Zapnout DHCP server\".",
"dhcp_lease_added": "Statický pronájem \"{{key}}\" byl úspěšně přidán", "dhcp_lease_added": "Statický pronájem \"{{key}}\" byl úspěšně přidán",
"dhcp_lease_deleted": "Statický pronájem \"{{key}}\" byl úspěšně odstraněn", "dhcp_lease_deleted": "Statický pronájem \"{{key}}\" byl úspěšně odstraněn",
"dhcp_lease_updated": "Statický pronájem \"{{key}}\" byl úspěšně aktualizován",
"dhcp_new_static_lease": "Nový statický pronájem", "dhcp_new_static_lease": "Nový statický pronájem",
"dhcp_edit_static_lease": "Upravit statický pronájem",
"dhcp_static_leases_not_found": "Nebyly nalezeny žádné statické pronájmy DHCP", "dhcp_static_leases_not_found": "Nebyly nalezeny žádné statické pronájmy DHCP",
"dhcp_add_static_lease": "Přidat statický pronájem", "dhcp_add_static_lease": "Přidat statický pronájem",
"dhcp_reset_leases": "Resetovat všechny pronájmy", "dhcp_reset_leases": "Resetovat všechny pronájmy",
@ -283,6 +285,9 @@
"custom_ip": "Vlastní IP", "custom_ip": "Vlastní IP",
"blocking_ipv4": "Blokování IPv4", "blocking_ipv4": "Blokování IPv4",
"blocking_ipv6": "Blokování IPv6", "blocking_ipv6": "Blokování IPv6",
"blocked_response_ttl": "TTL blokované odezvy",
"blocked_response_ttl_desc": "Určuje, na kolik sekund by měli klienti ukládat filtrovanou odezvu do mezipaměti",
"form_enter_blocked_response_ttl": "Zadejte TTL blokované odezvy (v sekundách)",
"dnscrypt": "DNSCrypt", "dnscrypt": "DNSCrypt",
"dns_over_https": "DNS skrze HTTPS", "dns_over_https": "DNS skrze HTTPS",
"dns_over_tls": "DNS skrze TLS", "dns_over_tls": "DNS skrze TLS",
@ -538,7 +543,7 @@
"ignore_domains": "Ignorované domény (oddělené novým řádkem)", "ignore_domains": "Ignorované domény (oddělené novým řádkem)",
"ignore_domains_title": "Ignorované domény", "ignore_domains_title": "Ignorované domény",
"ignore_domains_desc_stats": "Dotazy odpovídající těmto pravidlům se do statistik nezapisují", "ignore_domains_desc_stats": "Dotazy odpovídající těmto pravidlům se do statistik nezapisují",
"ignore_domains_desc_query": "Dotazy odpovídající těmto pravidlům se do protokolu dotazů nezapisují", "ignore_domains_desc_query": "Dotazy odpovídající těmto pravidlům se do záznamu dotazů nezapisují",
"interval_hours": "Hodiny: {{count}}", "interval_hours": "Hodiny: {{count}}",
"interval_hours_plural": "Hodiny: {{count}}", "interval_hours_plural": "Hodiny: {{count}}",
"filters_configuration": "Konfigurace filtrů", "filters_configuration": "Konfigurace filtrů",
@ -654,7 +659,7 @@
"parental_control": "Rodičovská ochrana", "parental_control": "Rodičovská ochrana",
"safe_browsing": "Bezpečné prohlížení", "safe_browsing": "Bezpečné prohlížení",
"served_from_cache": "{{value}} <i>(převzato z mezipaměti)</i>", "served_from_cache": "{{value}} <i>(převzato z mezipaměti)</i>",
"form_error_password_length": "Heslo musí být alespoň {{value}} znaků dlouhé", "form_error_password_length": "Heslo musí obsahovat od {{min}} do {{max}} znaků",
"anonymizer_notification": "<0>Poznámka:</0> Anonymizace IP je zapnuta. Můžete ji vypnout v <1>Obecných nastaveních</1>.", "anonymizer_notification": "<0>Poznámka:</0> Anonymizace IP je zapnuta. Můžete ji vypnout v <1>Obecných nastaveních</1>.",
"confirm_dns_cache_clear": "Opravdu chcete vymazat mezipaměť DNS?", "confirm_dns_cache_clear": "Opravdu chcete vymazat mezipaměť DNS?",
"cache_cleared": "Mezipaměť DNS úspěšně vymazána", "cache_cleared": "Mezipaměť DNS úspěšně vymazána",

View file

@ -73,7 +73,9 @@
"dhcp_dynamic_ip_found": "Dit system bruger en dynamisk IP-adresseopsætning til interface <0>{{interfaceName}}</0>. For at kunne bruge DHCP-serveren skal en statisk IP-adresse indstilles. Din aktuelle IP-adresse er <0>{{ipAddress}}</0>. AdGuard Home vil automatisk indstille denne IP-adresse som din statiske hvis du trykker på knappen \"Aktivér DHCP-server\".", "dhcp_dynamic_ip_found": "Dit system bruger en dynamisk IP-adresseopsætning til interface <0>{{interfaceName}}</0>. For at kunne bruge DHCP-serveren skal en statisk IP-adresse indstilles. Din aktuelle IP-adresse er <0>{{ipAddress}}</0>. AdGuard Home vil automatisk indstille denne IP-adresse som din statiske hvis du trykker på knappen \"Aktivér DHCP-server\".",
"dhcp_lease_added": "Statisk lease \"{{key}}\" tilføjet", "dhcp_lease_added": "Statisk lease \"{{key}}\" tilføjet",
"dhcp_lease_deleted": "Statisk lease \"{{key}}\" slettet", "dhcp_lease_deleted": "Statisk lease \"{{key}}\" slettet",
"dhcp_lease_updated": "Statisk tildeling \"{{key}}\" hermed opdateret",
"dhcp_new_static_lease": "Ny statisk lease", "dhcp_new_static_lease": "Ny statisk lease",
"dhcp_edit_static_lease": "Redigér statisk tildeling",
"dhcp_static_leases_not_found": "Intet DHCP statisk leases fundet", "dhcp_static_leases_not_found": "Intet DHCP statisk leases fundet",
"dhcp_add_static_lease": "Tilføj statisk lease", "dhcp_add_static_lease": "Tilføj statisk lease",
"dhcp_reset_leases": "Nulstil alle gyldighedsperioder", "dhcp_reset_leases": "Nulstil alle gyldighedsperioder",
@ -283,6 +285,9 @@
"custom_ip": "Tilpasset IP", "custom_ip": "Tilpasset IP",
"blocking_ipv4": "IPv4-blokering", "blocking_ipv4": "IPv4-blokering",
"blocking_ipv6": "IPv6-blokering", "blocking_ipv6": "IPv6-blokering",
"blocked_response_ttl": "Blokeret svar TTL",
"blocked_response_ttl_desc": "Angiver, i hvor mange sekunder klienterne skal cache-lagre et filtreret svar",
"form_enter_blocked_response_ttl": "Angiv blokeringssvar TTL (sekunder)",
"dnscrypt": "DNSCrypt", "dnscrypt": "DNSCrypt",
"dns_over_https": "DNS-over-HTTPS", "dns_over_https": "DNS-over-HTTPS",
"dns_over_tls": "DNS-over-TLS", "dns_over_tls": "DNS-over-TLS",
@ -654,7 +659,7 @@
"parental_control": "Forældrekontrol", "parental_control": "Forældrekontrol",
"safe_browsing": "Sikker Browsing", "safe_browsing": "Sikker Browsing",
"served_from_cache": "{{value}} <i>(leveret fra cache)</i>", "served_from_cache": "{{value}} <i>(leveret fra cache)</i>",
"form_error_password_length": "Adgangskoden skal udgøre mindst {{value}} tegn.", "form_error_password_length": "Adgangskode skal udgøre fra {{min}} til {{max}} tegn",
"anonymizer_notification": "<0>Bemærk:</0> IP-anonymisering er aktiveret. Det kan deaktiveres via <1>Generelle indstillinger</1>.", "anonymizer_notification": "<0>Bemærk:</0> IP-anonymisering er aktiveret. Det kan deaktiveres via <1>Generelle indstillinger</1>.",
"confirm_dns_cache_clear": "Sikker på, at DNS-cache skal ryddes?", "confirm_dns_cache_clear": "Sikker på, at DNS-cache skal ryddes?",
"cache_cleared": "DNS-cache hermed ryddet", "cache_cleared": "DNS-cache hermed ryddet",

View file

@ -6,14 +6,14 @@
"parallel_requests": "Paralleles Abfragen", "parallel_requests": "Paralleles Abfragen",
"load_balancing": "Lastverteilung", "load_balancing": "Lastverteilung",
"load_balancing_desc": "Einen Server nach dem anderen abfragen. AdGuard Home verwendet den gewichteten Zufallsalgorithmus, um den Server so auszuwählen, dass der schnellste Server häufiger verwendet wird.", "load_balancing_desc": "Einen Server nach dem anderen abfragen. AdGuard Home verwendet den gewichteten Zufallsalgorithmus, um den Server so auszuwählen, dass der schnellste Server häufiger verwendet wird.",
"bootstrap_dns": "Bootstrap DNS-Server", "bootstrap_dns": "Bootstrap-DNS-Server",
"bootstrap_dns_desc": "IP-Adressen der DNS-Server, die zum Auflösen der IP-Adressen von DoH/DoT Upstream-Servern verwendet werden, die Sie angegeben haben. Kommentare sind nicht erlaubt.", "bootstrap_dns_desc": "IP-Adressen der DNS-Server, die zum Auflösen der IP-Adressen von DoH/DoT Upstream-Servern verwendet werden, die Sie angegeben haben. Kommentare sind nicht erlaubt.",
"fallback_dns_title": "Fallback-DNS-Server", "fallback_dns_title": "Fallback-DNS-Server",
"fallback_dns_desc": "Liste der Fallback-DNS-Server, die verwendet werden, wenn die Upstream-DNS-Server nicht antworten. Die Syntax ist die gleiche wie im Hauptfeld für Upstream-Server oben.", "fallback_dns_desc": "Liste der Fallback-DNS-Server, die verwendet werden, wenn die Upstream-DNS-Server nicht antworten. Die Syntax ist die gleiche wie im Hauptfeld für Upstream-Server oben.",
"fallback_dns_placeholder": "Geben Sie einen Fallback-DNS-Server pro Zeile ein", "fallback_dns_placeholder": "Geben Sie einen Fallback-DNS-Server pro Zeile ein",
"local_ptr_title": "Private inverse DNS-Server", "local_ptr_title": "Private inverse DNS-Server",
"local_ptr_desc": "Die DNS-Server, die AdGuard Home für lokale PTR-Abfragen verwendet. Diese Server werden verwendet, um die Hostnamen von Clients mit privaten IP-Adressen, z. B. „192.168.12.34“, per inverse DNS-Anfragen aufzulösen. Wenn nicht festgelegt, verwendet AdGuard Home die Adressen der Standard-DNS-Auflöser Ihres Betriebssystems mit Ausnahme der Adressen von AdGuard Home selbst.", "local_ptr_desc": "Die DNS-Server, die AdGuard Home für lokale PTR-Abfragen verwendet. Diese Server werden verwendet, um die Hostnamen von Clients mit privaten IP-Adressen, z. B. „192.168.12.34“, per inverse DNS-Anfragen aufzulösen. Wenn nicht festgelegt, verwendet AdGuard Home die Adressen der Standard-DNS-Auflöser Ihres Betriebssystems mit Ausnahme der Adressen von AdGuard Home selbst.",
"local_ptr_default_resolver": "Standardmäßig verwendet AdGuard Home die folgenden Invers-DNS-Resolver: {{ip}}.", "local_ptr_default_resolver": "Standardmäßig verwendet AdGuard Home die folgenden inversen DNS-Resolver: {{ip}}.",
"local_ptr_no_default_resolver": "AdGuard Home konnte keine geeigneten privaten Invers-DNS-Resolver für dieses System ermitteln.", "local_ptr_no_default_resolver": "AdGuard Home konnte keine geeigneten privaten Invers-DNS-Resolver für dieses System ermitteln.",
"local_ptr_placeholder": "Geben Sie eine IP-Adresse pro Zeile ein", "local_ptr_placeholder": "Geben Sie eine IP-Adresse pro Zeile ein",
"resolve_clients_title": "Hostnamenauflösung der Clients aktivieren", "resolve_clients_title": "Hostnamenauflösung der Clients aktivieren",
@ -73,7 +73,9 @@
"dhcp_dynamic_ip_found": "Ihr System verwendet die dynamische Konfiguration der IP-Adresse für die Schnittstelle <0>{{interfaceName}}</0>. Um den DHCP-Server nutzen zu können, muss eine statische IP-Adresse festgelegt werden. Ihre aktuelle IP-Adresse ist <0>{{ipAddress}}</0>. Diese IP-Adresse wird automatisch als statisch festgelegt, sobald Sie auf die Schaltfläche „DHCP-Server aktivieren“ klicken.", "dhcp_dynamic_ip_found": "Ihr System verwendet die dynamische Konfiguration der IP-Adresse für die Schnittstelle <0>{{interfaceName}}</0>. Um den DHCP-Server nutzen zu können, muss eine statische IP-Adresse festgelegt werden. Ihre aktuelle IP-Adresse ist <0>{{ipAddress}}</0>. Diese IP-Adresse wird automatisch als statisch festgelegt, sobald Sie auf die Schaltfläche „DHCP-Server aktivieren“ klicken.",
"dhcp_lease_added": "Statische Zuweisung „{{key}}“ erfolgreich hinzugefügt", "dhcp_lease_added": "Statische Zuweisung „{{key}}“ erfolgreich hinzugefügt",
"dhcp_lease_deleted": "Statische Zuweisung „{{key}}“ erfolgreich entfernt", "dhcp_lease_deleted": "Statische Zuweisung „{{key}}“ erfolgreich entfernt",
"dhcp_lease_updated": "Statische Zuweisung „{{key}}“ erfolgreich aktualisiert",
"dhcp_new_static_lease": "Neue statische Zuweisung", "dhcp_new_static_lease": "Neue statische Zuweisung",
"dhcp_edit_static_lease": "Statische Zuweisung bearbeiten",
"dhcp_static_leases_not_found": "Keine statischen DHCP-Zuweisungen gefunden", "dhcp_static_leases_not_found": "Keine statischen DHCP-Zuweisungen gefunden",
"dhcp_add_static_lease": "Statische Zuweisung hinzufügen", "dhcp_add_static_lease": "Statische Zuweisung hinzufügen",
"dhcp_reset_leases": "Alle Zuweisungen zurücksetzen", "dhcp_reset_leases": "Alle Zuweisungen zurücksetzen",
@ -283,6 +285,9 @@
"custom_ip": "Benutzerdefinierte IP", "custom_ip": "Benutzerdefinierte IP",
"blocking_ipv4": "IPv4-Sperren", "blocking_ipv4": "IPv4-Sperren",
"blocking_ipv6": "IPv6-Sperren", "blocking_ipv6": "IPv6-Sperren",
"blocked_response_ttl": "Gültigkeitsdauer der blockierten Antwort",
"blocked_response_ttl_desc": "Gibt an, wie viele Sekunden lang die Clients eine gefilterte Antwort zwischenspeichern sollen",
"form_enter_blocked_response_ttl": "Geben Sie die Gültigkeitsdauer für blockierte Antworten ein (in Sekunden)",
"dnscrypt": "DNSCrypt", "dnscrypt": "DNSCrypt",
"dns_over_https": "DNS-over-HTTPS", "dns_over_https": "DNS-over-HTTPS",
"dns_over_tls": "DNS-over-TLS", "dns_over_tls": "DNS-over-TLS",
@ -296,7 +301,7 @@
"plain_dns": "Einfaches DNS", "plain_dns": "Einfaches DNS",
"form_enter_rate_limit": "Begrenzungswert eingeben", "form_enter_rate_limit": "Begrenzungswert eingeben",
"rate_limit": "Begrenzungswert", "rate_limit": "Begrenzungswert",
"edns_enable": "EDNS Client Subnetz aktivieren", "edns_enable": "EDNS-Client-Subnetz aktivieren",
"edns_cs_desc": "Die Option EDNS Client Subnetz (ECS) zu Upstream-Anfragen hinzufügen und die von Clients gesendeten Werte protokollieren.", "edns_cs_desc": "Die Option EDNS Client Subnetz (ECS) zu Upstream-Anfragen hinzufügen und die von Clients gesendeten Werte protokollieren.",
"edns_use_custom_ip": "Benutzerdefinierte IP für EDNS verwenden", "edns_use_custom_ip": "Benutzerdefinierte IP für EDNS verwenden",
"edns_use_custom_ip_desc": "Benutzerdefinierte IP für EDNS zulassen", "edns_use_custom_ip_desc": "Benutzerdefinierte IP für EDNS zulassen",
@ -322,16 +327,16 @@
"known_tracker": "Bekannte Tracker", "known_tracker": "Bekannte Tracker",
"install_welcome_title": "Willkommen bei AdGuard Home!", "install_welcome_title": "Willkommen bei AdGuard Home!",
"install_welcome_desc": "AdGuard Home ist ein netzwerkweiter Werbung- und Tracking sperrender DNS-Server. Sein Zweck ist es, Ihnen die Kontrolle über Ihr gesamtes Netzwerk und alle Ihre Geräte zu ermöglichen, und es ist nicht erforderlich, eine clientseitige Anwendung zu verwenden.", "install_welcome_desc": "AdGuard Home ist ein netzwerkweiter Werbung- und Tracking sperrender DNS-Server. Sein Zweck ist es, Ihnen die Kontrolle über Ihr gesamtes Netzwerk und alle Ihre Geräte zu ermöglichen, und es ist nicht erforderlich, eine clientseitige Anwendung zu verwenden.",
"install_settings_title": "Admin Weboberfläche", "install_settings_title": "Admin-Weboberfläche",
"install_settings_listen": "Netzwerk-Schnittstelle\n", "install_settings_listen": "Netzwerk-Schnittstelle\n",
"install_settings_port": "Port", "install_settings_port": "Port",
"install_settings_interface_link": "Ihre AdGuard Home Admin-Weboberfläche ist unter den folgenden Adressen verfügbar:", "install_settings_interface_link": "Ihre AdGuard-Home-Admin-Weboberfläche ist unter den folgenden Adressen verfügbar:",
"form_error_port": "Geben Sie eine gültige Portnummer ein", "form_error_port": "Geben Sie eine gültige Portnummer ein",
"install_settings_dns": "DNS-Server", "install_settings_dns": "DNS-Server",
"install_settings_dns_desc": "Sie müssen Ihre Geräte oder Ihren Router so konfigurieren, dass er den DNS-Server unter den folgenden Adressen verwendet:", "install_settings_dns_desc": "Sie müssen Ihre Geräte oder Ihren Router so konfigurieren, dass er den DNS-Server unter den folgenden Adressen verwendet:",
"install_settings_all_interfaces": "Alle Schnittstellen", "install_settings_all_interfaces": "Alle Schnittstellen",
"install_auth_title": "Authentifizierung", "install_auth_title": "Authentifizierung",
"install_auth_desc": "Die Passwort-Authentifizierung für Ihre AdGuard Home Admin-Web-Oberfläche muss konfiguriert werden. Auch wenn AdGuard Home nur in Ihrem lokalen Netzwerk zugänglich ist, ist es dennoch wichtig, es vor unberechtigtem Zugriff zu schützen.", "install_auth_desc": "Die Passwort-Authentifizierung für Ihre AdGuard-Home-Admin-Web-Oberfläche muss konfiguriert werden. Auch wenn AdGuard Home nur in Ihrem lokalen Netzwerk zugänglich ist, so ist es dennoch wichtig, es vor unberechtigtem Zugriff zu schützen.",
"install_auth_username": "Benutzername", "install_auth_username": "Benutzername",
"install_auth_password": "Passwort", "install_auth_password": "Passwort",
"install_auth_confirm": "Passwort bestätigen", "install_auth_confirm": "Passwort bestätigen",
@ -344,7 +349,7 @@
"install_submit_desc": "Die Einrichtung ist abgeschlossen und Sie können mit der Verwendung von AdGuard Home beginnen.", "install_submit_desc": "Die Einrichtung ist abgeschlossen und Sie können mit der Verwendung von AdGuard Home beginnen.",
"install_devices_router": "Router", "install_devices_router": "Router",
"install_devices_router_desc": "Diese Einrichtung deckt automatisch alle an Ihren Heimrouter angeschlossenen Geräte ab, und Sie müssen nicht jedes einzelne davon manuell konfigurieren.", "install_devices_router_desc": "Diese Einrichtung deckt automatisch alle an Ihren Heimrouter angeschlossenen Geräte ab, und Sie müssen nicht jedes einzelne davon manuell konfigurieren.",
"install_devices_address": "Der AdGuard Home DNS-Server belauscht die folgenden Adressen", "install_devices_address": "Der AdGuard-Home-DNS-Server lauscht unter folgenden Adressen",
"install_devices_router_list_1": "Öffnen Sie die Einstellungen für Ihren Router. In der Regel können Sie über eine URL (z. B. http://192.168.0.1/ oder http://192.168.1.1) von Ihrem Browser aus darauf zugreifen. Möglicherweise werden Sie aufgefordert, ein Passwort einzugeben. Wenn Sie sich nicht mehr daran erinnern, können Sie das Passwort oft durch Drücken einer Taste am Router selbst zurücksetzen, aber seien Sie sich bewusst, dass Sie bei dieser Vorgehensweise wahrscheinlich die gesamte Routerkonfiguration verlieren. Wenn für die Einrichtung Ihres Routers eine App erforderlich ist, installieren Sie bitte die App auf Ihrem mobilen Endgerät oder PC und verwenden Sie sie für den Zugriff auf die Einstellungen des Routers.", "install_devices_router_list_1": "Öffnen Sie die Einstellungen für Ihren Router. In der Regel können Sie über eine URL (z. B. http://192.168.0.1/ oder http://192.168.1.1) von Ihrem Browser aus darauf zugreifen. Möglicherweise werden Sie aufgefordert, ein Passwort einzugeben. Wenn Sie sich nicht mehr daran erinnern, können Sie das Passwort oft durch Drücken einer Taste am Router selbst zurücksetzen, aber seien Sie sich bewusst, dass Sie bei dieser Vorgehensweise wahrscheinlich die gesamte Routerkonfiguration verlieren. Wenn für die Einrichtung Ihres Routers eine App erforderlich ist, installieren Sie bitte die App auf Ihrem mobilen Endgerät oder PC und verwenden Sie sie für den Zugriff auf die Einstellungen des Routers.",
"install_devices_router_list_2": "Wechseln Sie zu den DHCP/DNS-Einstellungen. Suchen sie dort nach einem Eintrag „DNS“ und einem Bereich, welches zwei oder drei Zahlengruppen zulässt, die jeweils in vier Blöcke von ein bis drei Ziffern unterteilt sind.", "install_devices_router_list_2": "Wechseln Sie zu den DHCP/DNS-Einstellungen. Suchen sie dort nach einem Eintrag „DNS“ und einem Bereich, welches zwei oder drei Zahlengruppen zulässt, die jeweils in vier Blöcke von ein bis drei Ziffern unterteilt sind.",
"install_devices_router_list_3": "Geben Sie dort Ihre AdGuard Home Server-Adressen ein.", "install_devices_router_list_3": "Geben Sie dort Ihre AdGuard Home Server-Adressen ein.",
@ -537,8 +542,8 @@
"statistics_enable": "Statistiken aktivieren", "statistics_enable": "Statistiken aktivieren",
"ignore_domains": "Ignorierte Domains (durch Zeilenumbruch getrennt)", "ignore_domains": "Ignorierte Domains (durch Zeilenumbruch getrennt)",
"ignore_domains_title": "Ignorierte Domains", "ignore_domains_title": "Ignorierte Domains",
"ignore_domains_desc_stats": "Anfragen, die diesen Regeln entsprechen, werden nicht in die Statistik aufgenommen", "ignore_domains_desc_stats": "Abfragen, die diesen Regeln entsprechen, werden nicht in die Statistik aufgenommen",
"ignore_domains_desc_query": "Anfragen, die diesen Regeln entsprechen, werden nicht in das Anfragenprotokoll aufgenommen", "ignore_domains_desc_query": "Abfragen, die diesen Regeln entsprechen, werden nicht in das Anfragenprotokoll aufgenommen",
"interval_hours": "{{count}} Stunde", "interval_hours": "{{count}} Stunde",
"interval_hours_plural": "{{count}} Stunden", "interval_hours_plural": "{{count}} Stunden",
"filters_configuration": "Filterkonfiguration", "filters_configuration": "Filterkonfiguration",
@ -640,7 +645,7 @@
"filter_category_regional": "Regional", "filter_category_regional": "Regional",
"filter_category_other": "Weitere", "filter_category_other": "Weitere",
"filter_category_general_desc": "Listen, die Tracking und Werbung auf den meisten Geräten sperren", "filter_category_general_desc": "Listen, die Tracking und Werbung auf den meisten Geräten sperren",
"filter_category_security_desc": "Listen, die auf das Sperren von Malware, Phishing- oder Scam-Domains spezialisiert sind", "filter_category_security_desc": "Listen, die auf das Sperren von Malware-, Phishing- oder Scam-Domains spezialisiert sind",
"filter_category_regional_desc": "Listen, die sich auf regionale Werbeanzeigen und Tracking-Server konzentrieren", "filter_category_regional_desc": "Listen, die sich auf regionale Werbeanzeigen und Tracking-Server konzentrieren",
"filter_category_other_desc": "Weitere Sperrlisten", "filter_category_other_desc": "Weitere Sperrlisten",
"setup_config_to_enable_dhcp_server": "Einrichten der Konfiguration zur Aktivierung des DHCP-Servers", "setup_config_to_enable_dhcp_server": "Einrichten der Konfiguration zur Aktivierung des DHCP-Servers",
@ -654,7 +659,7 @@
"parental_control": "Kindersicherung", "parental_control": "Kindersicherung",
"safe_browsing": "Internetsicherheit", "safe_browsing": "Internetsicherheit",
"served_from_cache": "{{value}} <i>(aus dem Cache abgerufen)</i>", "served_from_cache": "{{value}} <i>(aus dem Cache abgerufen)</i>",
"form_error_password_length": "Das Passwort muss mindestens {{value}} Zeichen enthalten", "form_error_password_length": "Das Passwort muss zwischen {{min}} und {{max}} Zeichen enthalten",
"anonymizer_notification": "<0>Hinweis:</0> Die IP-Anonymisierung ist aktiviert. Sie können sie in den <1>Allgemeinen Einstellungen</1> deaktivieren.", "anonymizer_notification": "<0>Hinweis:</0> Die IP-Anonymisierung ist aktiviert. Sie können sie in den <1>Allgemeinen Einstellungen</1> deaktivieren.",
"confirm_dns_cache_clear": "Möchten Sie den DNS-Cache wirklich leeren?", "confirm_dns_cache_clear": "Möchten Sie den DNS-Cache wirklich leeren?",
"cache_cleared": "DNS-Cache erfolgreich geleert", "cache_cleared": "DNS-Cache erfolgreich geleert",

View file

@ -73,7 +73,9 @@
"dhcp_dynamic_ip_found": "Your system uses dynamic IP address configuration for interface <0>{{interfaceName}}</0>. In order to use DHCP server, a static IP address must be set. Your current IP address is <0>{{ipAddress}}</0>. AdGuard Home will automatically set this IP address as static if you press the \"Enable DHCP server\" button.", "dhcp_dynamic_ip_found": "Your system uses dynamic IP address configuration for interface <0>{{interfaceName}}</0>. In order to use DHCP server, a static IP address must be set. Your current IP address is <0>{{ipAddress}}</0>. AdGuard Home will automatically set this IP address as static if you press the \"Enable DHCP server\" button.",
"dhcp_lease_added": "Static lease \"{{key}}\" successfully added", "dhcp_lease_added": "Static lease \"{{key}}\" successfully added",
"dhcp_lease_deleted": "Static lease \"{{key}}\" successfully deleted", "dhcp_lease_deleted": "Static lease \"{{key}}\" successfully deleted",
"dhcp_lease_updated": "Static lease \"{{key}}\" successfully updated",
"dhcp_new_static_lease": "New static lease", "dhcp_new_static_lease": "New static lease",
"dhcp_edit_static_lease": "Edit static lease",
"dhcp_static_leases_not_found": "No DHCP static leases found", "dhcp_static_leases_not_found": "No DHCP static leases found",
"dhcp_add_static_lease": "Add static lease", "dhcp_add_static_lease": "Add static lease",
"dhcp_reset_leases": "Reset all leases", "dhcp_reset_leases": "Reset all leases",
@ -283,6 +285,9 @@
"custom_ip": "Custom IP", "custom_ip": "Custom IP",
"blocking_ipv4": "Blocking IPv4", "blocking_ipv4": "Blocking IPv4",
"blocking_ipv6": "Blocking IPv6", "blocking_ipv6": "Blocking IPv6",
"blocked_response_ttl": "Blocked response TTL",
"blocked_response_ttl_desc": "Specifies for how many seconds the clients should cache a filtered response",
"form_enter_blocked_response_ttl": "Enter blocked response TTL (seconds)",
"dnscrypt": "DNSCrypt", "dnscrypt": "DNSCrypt",
"dns_over_https": "DNS-over-HTTPS", "dns_over_https": "DNS-over-HTTPS",
"dns_over_tls": "DNS-over-TLS", "dns_over_tls": "DNS-over-TLS",
@ -654,7 +659,7 @@
"parental_control": "Parental Control", "parental_control": "Parental Control",
"safe_browsing": "Safe Browsing", "safe_browsing": "Safe Browsing",
"served_from_cache": "{{value}} <i>(served from cache)</i>", "served_from_cache": "{{value}} <i>(served from cache)</i>",
"form_error_password_length": "Password must be at least {{value}} characters long", "form_error_password_length": "Password must be {{min}} to {{max}} characters long",
"anonymizer_notification": "<0>Note:</0> IP anonymization is enabled. You can disable it in <1>General settings</1>.", "anonymizer_notification": "<0>Note:</0> IP anonymization is enabled. You can disable it in <1>General settings</1>.",
"confirm_dns_cache_clear": "Are you sure you want to clear DNS cache?", "confirm_dns_cache_clear": "Are you sure you want to clear DNS cache?",
"cache_cleared": "DNS cache successfully cleared", "cache_cleared": "DNS cache successfully cleared",

View file

@ -73,7 +73,9 @@
"dhcp_dynamic_ip_found": "Tu sistema utiliza la configuración de dirección IP dinámica para la interfaz <0>{{interfaceName}}</0>. Para poder utilizar el servidor DHCP se debe establecer una dirección IP estática. Tu dirección IP actual es <0>{{ipAddress}}</0>. AdGuard Home establecerá automáticamente esta dirección IP como estática si presionas el botón \"Habilitar servidor DHCP\".", "dhcp_dynamic_ip_found": "Tu sistema utiliza la configuración de dirección IP dinámica para la interfaz <0>{{interfaceName}}</0>. Para poder utilizar el servidor DHCP se debe establecer una dirección IP estática. Tu dirección IP actual es <0>{{ipAddress}}</0>. AdGuard Home establecerá automáticamente esta dirección IP como estática si presionas el botón \"Habilitar servidor DHCP\".",
"dhcp_lease_added": "Asignación estática \"{{key}}\" añadido correctamente", "dhcp_lease_added": "Asignación estática \"{{key}}\" añadido correctamente",
"dhcp_lease_deleted": "Asignación estática \"{{key}}\" eliminado correctamente", "dhcp_lease_deleted": "Asignación estática \"{{key}}\" eliminado correctamente",
"dhcp_lease_updated": "Asignación estática \"{{key}}\" actualizado correctamente",
"dhcp_new_static_lease": "Nueva asignación estática", "dhcp_new_static_lease": "Nueva asignación estática",
"dhcp_edit_static_lease": "Editar asignación estática",
"dhcp_static_leases_not_found": "No se han encontrado asignaciones DHCP estáticas", "dhcp_static_leases_not_found": "No se han encontrado asignaciones DHCP estáticas",
"dhcp_add_static_lease": "Añadir asignación estática", "dhcp_add_static_lease": "Añadir asignación estática",
"dhcp_reset_leases": "Restablecer todas las asignaciones", "dhcp_reset_leases": "Restablecer todas las asignaciones",
@ -283,6 +285,9 @@
"custom_ip": "IP personalizada", "custom_ip": "IP personalizada",
"blocking_ipv4": "Bloqueo de IPv4", "blocking_ipv4": "Bloqueo de IPv4",
"blocking_ipv6": "Bloqueo de IPv6", "blocking_ipv6": "Bloqueo de IPv6",
"blocked_response_ttl": "Respuesta TTL bloqueada",
"blocked_response_ttl_desc": "Especifica durante cuántos segundos los clientes deben almacenar en cache una respuesta filtrada",
"form_enter_blocked_response_ttl": "Ingresa el TTL de respuesta bloqueada (segundos)",
"dnscrypt": "DNSCrypt", "dnscrypt": "DNSCrypt",
"dns_over_https": "DNS mediante HTTPS", "dns_over_https": "DNS mediante HTTPS",
"dns_over_tls": "DNS mediante TLS", "dns_over_tls": "DNS mediante TLS",
@ -654,7 +659,7 @@
"parental_control": "Control parental", "parental_control": "Control parental",
"safe_browsing": "Navegación segura", "safe_browsing": "Navegación segura",
"served_from_cache": "{{value}} <i>(servido desde la caché)</i>", "served_from_cache": "{{value}} <i>(servido desde la caché)</i>",
"form_error_password_length": "La contraseña debe tener al menos {{value}} caracteres", "form_error_password_length": "La contraseña debe tener entre {{min}} y {{max}} caracteres",
"anonymizer_notification": "<0>Nota:</0> La anonimización de IP está habilitada. Puedes deshabilitarla en <1>Configuración general</1>.", "anonymizer_notification": "<0>Nota:</0> La anonimización de IP está habilitada. Puedes deshabilitarla en <1>Configuración general</1>.",
"confirm_dns_cache_clear": "¿Estás seguro de que deseas borrar la caché DNS?", "confirm_dns_cache_clear": "¿Estás seguro de que deseas borrar la caché DNS?",
"cache_cleared": "Caché DNS borrado correctamente", "cache_cleared": "Caché DNS borrado correctamente",

View file

@ -271,6 +271,9 @@
"custom_ip": "آی پی دستی", "custom_ip": "آی پی دستی",
"blocking_ipv4": "مسدودسازی IPv4", "blocking_ipv4": "مسدودسازی IPv4",
"blocking_ipv6": "مسدودسازی IPv6", "blocking_ipv6": "مسدودسازی IPv6",
"blocked_response_ttl": "TTL پاسخ مسدود شده",
"blocked_response_ttl_desc": "مشخص می کند که کلاینت ها برای چند ثانیه یک پاسخ فیلتر شده را در حافظه پنهان نگه دارند",
"form_enter_blocked_response_ttl": "پاسخ مسدود شده TTL (ثانیه) را وارد کنید",
"dnscrypt": "DNSCrypt", "dnscrypt": "DNSCrypt",
"dns_over_https": "DNS-over-HTTPS", "dns_over_https": "DNS-over-HTTPS",
"dns_over_tls": "DNS-over-TLS", "dns_over_tls": "DNS-over-TLS",

View file

@ -73,7 +73,9 @@
"dhcp_dynamic_ip_found": "Järjestelmäsi käyttää verkkosovittimelle <0>{{interfaceName}}</0> dynaamista IP-osoitetta. Jotta voit käyttää DHCP-palvelinta, on sovittimelle määritettävä kiinteä IP-osoite. Nykyinen IP-osoitteesi on <0>{{ipAddress}}</0>. Tämä osoite määritetään automaattisesti kiinteäksi, jos painat \"Ota DHCP-palvelin käyttöön\" -painiketta.", "dhcp_dynamic_ip_found": "Järjestelmäsi käyttää verkkosovittimelle <0>{{interfaceName}}</0> dynaamista IP-osoitetta. Jotta voit käyttää DHCP-palvelinta, on sovittimelle määritettävä kiinteä IP-osoite. Nykyinen IP-osoitteesi on <0>{{ipAddress}}</0>. Tämä osoite määritetään automaattisesti kiinteäksi, jos painat \"Ota DHCP-palvelin käyttöön\" -painiketta.",
"dhcp_lease_added": "Kiinteä laina \"{{key}}\" lisättiin", "dhcp_lease_added": "Kiinteä laina \"{{key}}\" lisättiin",
"dhcp_lease_deleted": "Kiinteä laina \"{{key}}\" poistettiin", "dhcp_lease_deleted": "Kiinteä laina \"{{key}}\" poistettiin",
"dhcp_lease_updated": "Kiinteä laina \"{{key}}\" päivitettiin",
"dhcp_new_static_lease": "Uusi kiinteä laina", "dhcp_new_static_lease": "Uusi kiinteä laina",
"dhcp_edit_static_lease": "Muokkaa kiinteää laina",
"dhcp_static_leases_not_found": "Kiinteitä DHCP-lainoja ei löytynyt", "dhcp_static_leases_not_found": "Kiinteitä DHCP-lainoja ei löytynyt",
"dhcp_add_static_lease": "Lisää kiinteä laina", "dhcp_add_static_lease": "Lisää kiinteä laina",
"dhcp_reset_leases": "Tyhjennä kaikki lainat", "dhcp_reset_leases": "Tyhjennä kaikki lainat",
@ -283,6 +285,9 @@
"custom_ip": "Mukautettu IP-osoite", "custom_ip": "Mukautettu IP-osoite",
"blocking_ipv4": "IPv4-esto", "blocking_ipv4": "IPv4-esto",
"blocking_ipv6": "IPv6-esto", "blocking_ipv6": "IPv6-esto",
"blocked_response_ttl": "Estetyn vastauksen elinaika",
"blocked_response_ttl_desc": "Määrittää montako sekuntia päätteiden tulee puskuroida suodatettuja vastauksia.",
"form_enter_blocked_response_ttl": "Syötä estetyn vastauksen elinaika (sekuntia)",
"dnscrypt": "DNSCrypt", "dnscrypt": "DNSCrypt",
"dns_over_https": "DNS-over-HTTPS", "dns_over_https": "DNS-over-HTTPS",
"dns_over_tls": "DNS-over-TLS", "dns_over_tls": "DNS-over-TLS",
@ -611,7 +616,7 @@
"dnssec_enable_desc": "Määritä DNSSEC-lippu ulos lähteville DNS-pyynnöille ja tarkasta tulos (vaatii DNSSEC-yhteensopivan resolverin).", "dnssec_enable_desc": "Määritä DNSSEC-lippu ulos lähteville DNS-pyynnöille ja tarkasta tulos (vaatii DNSSEC-yhteensopivan resolverin).",
"validated_with_dnssec": "DNSSEC-vahvistettu", "validated_with_dnssec": "DNSSEC-vahvistettu",
"all_queries": "Kaikki pyynnöt", "all_queries": "Kaikki pyynnöt",
"show_blocked_responses": "Estetyt", "show_blocked_responses": "Estetty",
"show_whitelisted_responses": "Sallitut", "show_whitelisted_responses": "Sallitut",
"show_processed_responses": "Käsitelty", "show_processed_responses": "Käsitelty",
"blocked_safebrowsing": "Turvallisen selauksen estämät", "blocked_safebrowsing": "Turvallisen selauksen estämät",
@ -654,7 +659,7 @@
"parental_control": "Lapsilukko", "parental_control": "Lapsilukko",
"safe_browsing": "Turvallinen selaus", "safe_browsing": "Turvallinen selaus",
"served_from_cache": "{{value}} <i>(jaettu välimuistista)</i>", "served_from_cache": "{{value}} <i>(jaettu välimuistista)</i>",
"form_error_password_length": "Salasanan on oltava ainakin {{value}} merkkiä", "form_error_password_length": "Salasanan on oltava {{min}} - {{max}} merkkiä pitkä",
"anonymizer_notification": "<0>Huomioi:</0> IP-osoitteen anonymisointi on käytössä. Voit poistaa sen käytöstä <1>Yleisistä asetuksista</1>.", "anonymizer_notification": "<0>Huomioi:</0> IP-osoitteen anonymisointi on käytössä. Voit poistaa sen käytöstä <1>Yleisistä asetuksista</1>.",
"confirm_dns_cache_clear": "Haluatko varmasti tyhjentää DNS-välimuistin?", "confirm_dns_cache_clear": "Haluatko varmasti tyhjentää DNS-välimuistin?",
"cache_cleared": "DNS-välimuistin tyhjennys onnistui", "cache_cleared": "DNS-välimuistin tyhjennys onnistui",

View file

@ -73,7 +73,9 @@
"dhcp_dynamic_ip_found": "Votre système utilise une configuration d'adresses IP dynamiques pour l'interface <0>{{interfaceName}}</0>. Pour utiliser un serveur DHCP, une adresse IP statique est requise. Votre adresse IP actuelle est <0>{{ipAddress}}</0>. AdGuard Home va automatiquement définir cette adresse IP comme statique si vous appuyez sur le bouton « Activer le serveur DHCP ».", "dhcp_dynamic_ip_found": "Votre système utilise une configuration d'adresses IP dynamiques pour l'interface <0>{{interfaceName}}</0>. Pour utiliser un serveur DHCP, une adresse IP statique est requise. Votre adresse IP actuelle est <0>{{ipAddress}}</0>. AdGuard Home va automatiquement définir cette adresse IP comme statique si vous appuyez sur le bouton « Activer le serveur DHCP ».",
"dhcp_lease_added": "« {{key}} » de bail statique ajoutée", "dhcp_lease_added": "« {{key}} » de bail statique ajoutée",
"dhcp_lease_deleted": "« {{key}} » de bail statique supprimée", "dhcp_lease_deleted": "« {{key}} » de bail statique supprimée",
"dhcp_lease_updated": "Bail statique « {{key}} » mis à jour correctement",
"dhcp_new_static_lease": "Nouveau bail statique", "dhcp_new_static_lease": "Nouveau bail statique",
"dhcp_edit_static_lease": "Modifier le bail statique",
"dhcp_static_leases_not_found": "Aucun bail statique DHCP trouvé", "dhcp_static_leases_not_found": "Aucun bail statique DHCP trouvé",
"dhcp_add_static_lease": "Ajoutez un bail statique", "dhcp_add_static_lease": "Ajoutez un bail statique",
"dhcp_reset_leases": "Réinitialiser tous les baux", "dhcp_reset_leases": "Réinitialiser tous les baux",
@ -283,6 +285,9 @@
"custom_ip": "IP personnalisée", "custom_ip": "IP personnalisée",
"blocking_ipv4": "Blocage IPv4", "blocking_ipv4": "Blocage IPv4",
"blocking_ipv6": "Blocage IPv6", "blocking_ipv6": "Blocage IPv6",
"blocked_response_ttl": "Réponse bloquée TTL",
"blocked_response_ttl_desc": "Spécifie pendant combien de secondes les clients doivent mettre en cache une réponse filtrée",
"form_enter_blocked_response_ttl": "Saisir le TTL de la réponse bloquée (secondes)",
"dnscrypt": "DNSCrypt", "dnscrypt": "DNSCrypt",
"dns_over_https": "DNS-over-HTTPS", "dns_over_https": "DNS-over-HTTPS",
"dns_over_tls": "DNS-over-TLS", "dns_over_tls": "DNS-over-TLS",
@ -319,7 +324,7 @@
"rule_label": "Règle(s)", "rule_label": "Règle(s)",
"list_label": "Liste", "list_label": "Liste",
"unknown_filter": "Filtre inconnu {{filterId}}", "unknown_filter": "Filtre inconnu {{filterId}}",
"known_tracker": "Pisteur connu", "known_tracker": "Traqueur connu",
"install_welcome_title": "Bienvenue sur AdGuard Home !", "install_welcome_title": "Bienvenue sur AdGuard Home !",
"install_welcome_desc": "AdGuard Home est un seveur DNS pour bloquer les pubs et traceurs sur tout un réseau. Son but est de vous donner le contrôle sur l'ensemble de votre réseau et tous vos appareils sans programme côté client supplémentaire.", "install_welcome_desc": "AdGuard Home est un seveur DNS pour bloquer les pubs et traceurs sur tout un réseau. Son but est de vous donner le contrôle sur l'ensemble de votre réseau et tous vos appareils sans programme côté client supplémentaire.",
"install_settings_title": "Interface web administrateur", "install_settings_title": "Interface web administrateur",
@ -537,8 +542,8 @@
"statistics_enable": "Activer les statistiques", "statistics_enable": "Activer les statistiques",
"ignore_domains": "Domaines ignorés (séparés par une nouvelle ligne)", "ignore_domains": "Domaines ignorés (séparés par une nouvelle ligne)",
"ignore_domains_title": "Domaines ignorés", "ignore_domains_title": "Domaines ignorés",
"ignore_domains_desc_stats": "Les requêtes pour ces domaines ne sont pas écrites dans les statistiques", "ignore_domains_desc_stats": "Les requêtes correspondantes à ces règles ne sont pas écrites dans les statistiques",
"ignore_domains_desc_query": "Les requêtes pour ces domaines ne sont pas écrites dans le journal des requêtes", "ignore_domains_desc_query": "Les requêtes correspondantes à ces règles ne sont pas écrites dans le journal des requêtes",
"interval_hours": "{{count}} heure", "interval_hours": "{{count}} heure",
"interval_hours_plural": "{{count}} heures", "interval_hours_plural": "{{count}} heures",
"filters_configuration": "Configuration des filtres", "filters_configuration": "Configuration des filtres",
@ -587,7 +592,7 @@
"filtered_custom_rules": "Filtré par des règles de filtrage personnalisées", "filtered_custom_rules": "Filtré par des règles de filtrage personnalisées",
"choose_from_list": "Choisissez dans la liste", "choose_from_list": "Choisissez dans la liste",
"add_custom_list": "Ajouter une liste personnalisée", "add_custom_list": "Ajouter une liste personnalisée",
"host_whitelisted": "Lhôte est sur liste blanche", "host_whitelisted": "Lhôte est autorisé",
"check_ip": "Adresses IP : {{ip}}", "check_ip": "Adresses IP : {{ip}}",
"check_cname": "CNAME : {{cname}}", "check_cname": "CNAME : {{cname}}",
"check_reason": "Raison : {{reason}}", "check_reason": "Raison : {{reason}}",
@ -612,12 +617,12 @@
"validated_with_dnssec": "Validé avec DNSSEC", "validated_with_dnssec": "Validé avec DNSSEC",
"all_queries": "Toutes les requêtes", "all_queries": "Toutes les requêtes",
"show_blocked_responses": "Bloqué", "show_blocked_responses": "Bloqué",
"show_whitelisted_responses": "Ajouté à la liste blanche", "show_whitelisted_responses": "Autorisée",
"show_processed_responses": "Traité", "show_processed_responses": "Traité",
"blocked_safebrowsing": "Navigation sécurisée bloquée", "blocked_safebrowsing": "Bloqué par la Navigation sécurisée",
"blocked_adult_websites": "Bloqué par le Contrôle Parental", "blocked_adult_websites": "Bloqué par le Contrôle Parental",
"blocked_threats": "Menaces bloquées", "blocked_threats": "Menaces bloquées",
"allowed": "Autorisé", "allowed": "Autorisées",
"filtered": "Filtré", "filtered": "Filtré",
"rewritten": "Réécrit", "rewritten": "Réécrit",
"safe_search": "Recherche Sécurisée", "safe_search": "Recherche Sécurisée",
@ -639,9 +644,9 @@
"filter_category_security": "Sécurité", "filter_category_security": "Sécurité",
"filter_category_regional": "Régional", "filter_category_regional": "Régional",
"filter_category_other": "Autre", "filter_category_other": "Autre",
"filter_category_general_desc": "Listes qui bloquent le pistage et la publicité sur la plupart des appareils", "filter_category_general_desc": "Listes qui bloquent le suivi et la publicité sur la plupart des appareils",
"filter_category_security_desc": "Listes créées exprès pour bloquer les logiciels malveillants, des domaines hameçonneurs ou frauduleux", "filter_category_security_desc": "Listes créées exprès pour bloquer les logiciels malveillants, des domaines hameçonneurs ou frauduleux",
"filter_category_regional_desc": "Listes axées sur les annonces régionales et les serveurs de pistage", "filter_category_regional_desc": "Listes axées sur les annonces régionales et les serveurs de suivi",
"filter_category_other_desc": "Autres listes noires", "filter_category_other_desc": "Autres listes noires",
"setup_config_to_enable_dhcp_server": "Configurer les paramètres pour activer le serveur DHCP", "setup_config_to_enable_dhcp_server": "Configurer les paramètres pour activer le serveur DHCP",
"original_response": "Réponse originale", "original_response": "Réponse originale",
@ -654,7 +659,7 @@
"parental_control": "Contrôle parental", "parental_control": "Contrôle parental",
"safe_browsing": "Navigation sécurisée", "safe_browsing": "Navigation sécurisée",
"served_from_cache": "{{value}} <i>(depuis le cache)</i>", "served_from_cache": "{{value}} <i>(depuis le cache)</i>",
"form_error_password_length": "Le mot de passe doit comporter au moins {{value}} caractères", "form_error_password_length": "Le mot de passe doit comporter entre {{min}} et {{max}}  caractères",
"anonymizer_notification": "<0>Note :</0> L'anonymisation IP est activée. Vous pouvez la désactiver dans les <1>paramètres généraux</1>.", "anonymizer_notification": "<0>Note :</0> L'anonymisation IP est activée. Vous pouvez la désactiver dans les <1>paramètres généraux</1>.",
"confirm_dns_cache_clear": "Voulez-vous vraiment vider le cache DNS ?", "confirm_dns_cache_clear": "Voulez-vous vraiment vider le cache DNS ?",
"cache_cleared": "Le cache DNS a été vidé", "cache_cleared": "Le cache DNS a été vidé",

View file

@ -73,7 +73,9 @@
"dhcp_dynamic_ip_found": "Vaš sustav koristi postavke dinamičke IP adrese za sučelje <0>{{interfaceName}}</0>. Za korištenje DHCP poslužitelja mora se postaviti statička IP adresa. Vaša trenutna IP adresa je <0>{{ipAddress}}</0>. AdGuard Home automatski će postaviti ovu IP adresu kao statičnu ako pritisnete gumb \"Omogući DHCP\".", "dhcp_dynamic_ip_found": "Vaš sustav koristi postavke dinamičke IP adrese za sučelje <0>{{interfaceName}}</0>. Za korištenje DHCP poslužitelja mora se postaviti statička IP adresa. Vaša trenutna IP adresa je <0>{{ipAddress}}</0>. AdGuard Home automatski će postaviti ovu IP adresu kao statičnu ako pritisnete gumb \"Omogući DHCP\".",
"dhcp_lease_added": "Statični lease \"{{key}}\" je uspješno dodan", "dhcp_lease_added": "Statični lease \"{{key}}\" je uspješno dodan",
"dhcp_lease_deleted": "Statični lease \"{{key}}\" je uspješno uklonjen", "dhcp_lease_deleted": "Statični lease \"{{key}}\" je uspješno uklonjen",
"dhcp_lease_updated": "Statični lease \"{{key}}\" uspješno ažuriran",
"dhcp_new_static_lease": "Novi static lease", "dhcp_new_static_lease": "Novi static lease",
"dhcp_edit_static_lease": "Uredi statični lease",
"dhcp_static_leases_not_found": "Nisu pronađeni statični DHCP leases", "dhcp_static_leases_not_found": "Nisu pronađeni statični DHCP leases",
"dhcp_add_static_lease": "Dodaj static lease", "dhcp_add_static_lease": "Dodaj static lease",
"dhcp_reset_leases": "Ponovno postavljanje svih najmova", "dhcp_reset_leases": "Ponovno postavljanje svih najmova",
@ -283,6 +285,9 @@
"custom_ip": "Prilagođen IP", "custom_ip": "Prilagođen IP",
"blocking_ipv4": "Blokiranje IPv4", "blocking_ipv4": "Blokiranje IPv4",
"blocking_ipv6": "Blokiranje IPv6", "blocking_ipv6": "Blokiranje IPv6",
"blocked_response_ttl": "TTL blokiranog odgovora",
"blocked_response_ttl_desc": "Određuje koliko sekundi bi klijenti trebali keširati filtrirani odgovor",
"form_enter_blocked_response_ttl": "Unesite TTL blokiranog odgovora (sekunde)",
"dnscrypt": "DNSCrypt", "dnscrypt": "DNSCrypt",
"dns_over_https": "DNS-over-HTTPS", "dns_over_https": "DNS-over-HTTPS",
"dns_over_tls": "DNS-over-TLS", "dns_over_tls": "DNS-over-TLS",
@ -654,7 +659,7 @@
"parental_control": "Roditeljska zaštita", "parental_control": "Roditeljska zaštita",
"safe_browsing": "Sigurno surfanje", "safe_browsing": "Sigurno surfanje",
"served_from_cache": "{{value}} <i>(dohvaćeno iz predmemorije)</i>", "served_from_cache": "{{value}} <i>(dohvaćeno iz predmemorije)</i>",
"form_error_password_length": "Lozinka mora imati najmanje {{value}} znakova", "form_error_password_length": "Lozinka mora sadržavati od {{min}} do {{max}} znakova",
"anonymizer_notification": "<0>Napomena:</0>IP anonimizacija je omogućena. Možete ju onemogućiti u <1>općim postavkama</1>.", "anonymizer_notification": "<0>Napomena:</0>IP anonimizacija je omogućena. Možete ju onemogućiti u <1>općim postavkama</1>.",
"confirm_dns_cache_clear": "Jeste li sigurni da želite očistiti DNS predmemoriju?", "confirm_dns_cache_clear": "Jeste li sigurni da želite očistiti DNS predmemoriju?",
"cache_cleared": "DNS predmemorija je uspješno izbrisana", "cache_cleared": "DNS predmemorija je uspješno izbrisana",

View file

@ -73,7 +73,9 @@
"dhcp_dynamic_ip_found": "A rendszer dinamikus IP-cím konfigurációt használ az <0>{{interfaceName}}</0> interfészhez. A DHCP szerver használatához statikus IP-címet kell beállítani. Jelenlegi IP-címe: <0>{{ipAddress}}</0>. Automatikusan beállítjuk ezt az IP címet statikusnak, ha rányom a DHCP engedélyezése gombra.", "dhcp_dynamic_ip_found": "A rendszer dinamikus IP-cím konfigurációt használ az <0>{{interfaceName}}</0> interfészhez. A DHCP szerver használatához statikus IP-címet kell beállítani. Jelenlegi IP-címe: <0>{{ipAddress}}</0>. Automatikusan beállítjuk ezt az IP címet statikusnak, ha rányom a DHCP engedélyezése gombra.",
"dhcp_lease_added": "Statikus bérlet \"{{key}}\" sikeresen hozzáadva", "dhcp_lease_added": "Statikus bérlet \"{{key}}\" sikeresen hozzáadva",
"dhcp_lease_deleted": "Statikus bérlet \"{{key}}\" sikeresen törölve", "dhcp_lease_deleted": "Statikus bérlet \"{{key}}\" sikeresen törölve",
"dhcp_lease_updated": "Statikus bérlet \"{{key}}\" sikeresen frissítve",
"dhcp_new_static_lease": "Új statikus bérlet", "dhcp_new_static_lease": "Új statikus bérlet",
"dhcp_edit_static_lease": "Statikus bérlet szerkesztése",
"dhcp_static_leases_not_found": "Nem találhatóak statikus DHCP bérletek", "dhcp_static_leases_not_found": "Nem találhatóak statikus DHCP bérletek",
"dhcp_add_static_lease": "Statikus bérlet hozzáadása", "dhcp_add_static_lease": "Statikus bérlet hozzáadása",
"dhcp_reset_leases": "Bérletek alaphelyzetbe", "dhcp_reset_leases": "Bérletek alaphelyzetbe",
@ -283,6 +285,9 @@
"custom_ip": "Egyedi IP", "custom_ip": "Egyedi IP",
"blocking_ipv4": "IPv4 blokkolása", "blocking_ipv4": "IPv4 blokkolása",
"blocking_ipv6": "IPv6 blokkolása", "blocking_ipv6": "IPv6 blokkolása",
"blocked_response_ttl": "Tiltott válasz TTL-je",
"blocked_response_ttl_desc": "Meghatározza, hogy a klienseknek hány másodpercig kell gyorsítótárazniuk a szűrt választ",
"form_enter_blocked_response_ttl": "Írja be a blokkolt válasz TTL-jét (másodpercben)",
"dnscrypt": "DNSCrypt", "dnscrypt": "DNSCrypt",
"dns_over_https": "DNS-over-HTTPS", "dns_over_https": "DNS-over-HTTPS",
"dns_over_tls": "DNS-over-TLS", "dns_over_tls": "DNS-over-TLS",
@ -654,7 +659,7 @@
"parental_control": "Szülői felügyelet", "parental_control": "Szülői felügyelet",
"safe_browsing": "Biztonságos böngészés", "safe_browsing": "Biztonságos böngészés",
"served_from_cache": "{{value}} <i>(gyorsítótárból kiszolgálva)</i>", "served_from_cache": "{{value}} <i>(gyorsítótárból kiszolgálva)</i>",
"form_error_password_length": "A jelszó legalább {{value}} karakter hosszú kell, hogy legyen", "form_error_password_length": "A jelszó legyen {{min}} és {{max}} karakter között",
"anonymizer_notification": "<0>Megjegyzés:</0> Az IP anonimizálás engedélyezve van. Az <1>Általános beállításoknál letilthatja</1> .", "anonymizer_notification": "<0>Megjegyzés:</0> Az IP anonimizálás engedélyezve van. Az <1>Általános beállításoknál letilthatja</1> .",
"confirm_dns_cache_clear": "Biztos benne, hogy törölni szeretné a DNS-gyorsítótárat?", "confirm_dns_cache_clear": "Biztos benne, hogy törölni szeretné a DNS-gyorsítótárat?",
"cache_cleared": "A DNS gyorsítótár sikeresen törlődött", "cache_cleared": "A DNS gyorsítótár sikeresen törlődött",

View file

@ -8,11 +8,14 @@
"load_balancing_desc": "Permintaan satu server pada satu waktu. AdGuard Home akan menggunakan algoritma acak tertimbang untuk memilih server sehingga server tercepat akan lebih sering digunakan.", "load_balancing_desc": "Permintaan satu server pada satu waktu. AdGuard Home akan menggunakan algoritma acak tertimbang untuk memilih server sehingga server tercepat akan lebih sering digunakan.",
"bootstrap_dns": "Server DNS bootstrap", "bootstrap_dns": "Server DNS bootstrap",
"bootstrap_dns_desc": "Alamat IP server DNS yang digunakan untuk menyelesaikan alamat IP resolver DoH/DoT yang Anda tentukan sebagai upstream. Komentar tidak diizinkan.", "bootstrap_dns_desc": "Alamat IP server DNS yang digunakan untuk menyelesaikan alamat IP resolver DoH/DoT yang Anda tentukan sebagai upstream. Komentar tidak diizinkan.",
"fallback_dns_title": "Server DNS cadangan",
"fallback_dns_desc": "Daftar server DNS cadangan yang digunakan ketika server DNS hulu tidak merespons. Sintaksnya sama dengan bidang hulu utama di atas.",
"fallback_dns_placeholder": "Masukkan satu server DNS cadangan per baris",
"local_ptr_title": "Server pembalik DNS pribadi", "local_ptr_title": "Server pembalik DNS pribadi",
"local_ptr_desc": "Server DNS yang digunakan AdGuard Home untuk kueri PTR lokal. Server ini digunakan untuk menyelesaikan nama host klien dengan alamat IP pribadi, misalnya \"192.168.12.34\", menggunakan DNS terbalik. Jika tidak disetel, AdGuard Home menggunakan alamat resolver DNS default OS Anda kecuali untuk alamat AdGuard Home itu sendiri.", "local_ptr_desc": "Server DNS yang digunakan AdGuard Home untuk kueri PTR lokal. Server ini digunakan untuk menyelesaikan nama host klien dengan alamat IP pribadi, misalnya \"192.168.12.34\", menggunakan DNS terbalik. Jika tidak disetel, AdGuard Home menggunakan alamat resolver DNS default OS Anda kecuali untuk alamat AdGuard Home itu sendiri.",
"local_ptr_default_resolver": "Secara bawaan, AdGuard Home menggunakan pemecah DNS terbalik: {{ip}}.", "local_ptr_default_resolver": "Secara bawaan, AdGuard Home menggunakan pemecah DNS terbalik: {{ip}}.",
"local_ptr_no_default_resolver": "AdGuard Home tidak dapat menentukan pemecah DNS terbalik yang sesuai untuk sistem ini.", "local_ptr_no_default_resolver": "AdGuard Home tidak dapat menentukan pemecah DNS terbalik yang sesuai untuk sistem ini.",
"local_ptr_placeholder": "Masukkan satu alamat server per baris", "local_ptr_placeholder": "Masukkan satu alamat IP per baris",
"resolve_clients_title": "Aktifkan resolusi hostname klien", "resolve_clients_title": "Aktifkan resolusi hostname klien",
"resolve_clients_desc": "Menyelesaikan alamat IP klien secara terbalik ke nama host mereka dengan mengirimkan kueri PTR ke resolver yang sesuai (server DNS pribadi untuk klien lokal, server upstream untuk klien dengan alamat IP publik).", "resolve_clients_desc": "Menyelesaikan alamat IP klien secara terbalik ke nama host mereka dengan mengirimkan kueri PTR ke resolver yang sesuai (server DNS pribadi untuk klien lokal, server upstream untuk klien dengan alamat IP publik).",
"use_private_ptr_resolvers_title": "Gunakan server pembalik DNS pribadi", "use_private_ptr_resolvers_title": "Gunakan server pembalik DNS pribadi",
@ -70,7 +73,9 @@
"dhcp_dynamic_ip_found": "Sistem Anda menggunakan konfigurasi alamat IP dinamis untuk antarmuka <0>{{interfaceName}}</0>. Untuk menggunakan server DHCP, alamat IP statis harus ditetapkan. Alamat IP Anda saat ini adalah <0>{{ipAddress}}</0>. AdGuard Home akan secara otomatis menetapkan alamat IP ini sebagai statis jika Anda menekan tombol Aktifkan DHCP.", "dhcp_dynamic_ip_found": "Sistem Anda menggunakan konfigurasi alamat IP dinamis untuk antarmuka <0>{{interfaceName}}</0>. Untuk menggunakan server DHCP, alamat IP statis harus ditetapkan. Alamat IP Anda saat ini adalah <0>{{ipAddress}}</0>. AdGuard Home akan secara otomatis menetapkan alamat IP ini sebagai statis jika Anda menekan tombol Aktifkan DHCP.",
"dhcp_lease_added": "Static lease \"{{key}}\" berhasil ditambahkan", "dhcp_lease_added": "Static lease \"{{key}}\" berhasil ditambahkan",
"dhcp_lease_deleted": "Static lease \"{{key}}\" berhasil dihapus", "dhcp_lease_deleted": "Static lease \"{{key}}\" berhasil dihapus",
"dhcp_lease_updated": "Static lease \"{{key}}\" berhasil diperbarui",
"dhcp_new_static_lease": "Static lease baru", "dhcp_new_static_lease": "Static lease baru",
"dhcp_edit_static_lease": "Mengedit static lease",
"dhcp_static_leases_not_found": "DHCP static lease tidak ditemukan", "dhcp_static_leases_not_found": "DHCP static lease tidak ditemukan",
"dhcp_add_static_lease": "Tambah static lease", "dhcp_add_static_lease": "Tambah static lease",
"dhcp_reset_leases": "Atur ulang semua kontrak", "dhcp_reset_leases": "Atur ulang semua kontrak",
@ -136,6 +141,7 @@
"enforced_save_search": "Paksa pencarian aman", "enforced_save_search": "Paksa pencarian aman",
"number_of_dns_query_to_safe_search": "Jumlah perminataan DNS ke mesin pencari yang dipaksa 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_processing_time": "Rata-rata waktu pemrosesan",
"processing_time": "Waktu pemrosesan",
"average_processing_time_hint": "Rata-rata waktu dalam milidetik untuk pemrosesan sebuah permintaan DNS", "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", "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>.", "filters_block_toggle_hint": "Anda dapat menyiapkan aturan pemblokiran di pengaturan <a>Penyaringan</a>.",
@ -170,6 +176,7 @@
"enabled_parental_toast": "Kontrol orang tua diaktifkan", "enabled_parental_toast": "Kontrol orang tua diaktifkan",
"disabled_safe_search_toast": "Pencarian aman dinonaktifkan", "disabled_safe_search_toast": "Pencarian aman dinonaktifkan",
"enabled_save_search_toast": "Pencarian aman diaktifkan", "enabled_save_search_toast": "Pencarian aman diaktifkan",
"updated_save_search_toast": "Pengaturan Pencarian Aman telah diperbarui",
"enabled_table_header": "Diaktifkan", "enabled_table_header": "Diaktifkan",
"name_table_header": "Nama", "name_table_header": "Nama",
"list_url_table_header": "Daftar URL", "list_url_table_header": "Daftar URL",
@ -259,12 +266,12 @@
"query_log_cleared": "Kueri log telah berhasil dihapus", "query_log_cleared": "Kueri log telah berhasil dihapus",
"query_log_updated": "Log permintaan telah berhasil diperbarui", "query_log_updated": "Log permintaan telah berhasil diperbarui",
"query_log_clear": "Hapus kueri log", "query_log_clear": "Hapus kueri log",
"query_log_retention": "Retensi kueri log", "query_log_retention": "Rotasi kueri log",
"query_log_enable": "Aktifkan log", "query_log_enable": "Aktifkan log",
"query_log_configuration": "Konfigurasi log", "query_log_configuration": "Konfigurasi log",
"query_log_disabled": "Kueri log dinonaktifkan dan dapat dikonfigurasi di <0>pengaturan</0>", "query_log_disabled": "Kueri log dinonaktifkan dan dapat dikonfigurasi di <0>pengaturan</0>",
"query_log_strict_search": "Gunakan tanda kutip ganda untuk pencarian ketat", "query_log_strict_search": "Gunakan tanda kutip ganda untuk pencarian ketat",
"query_log_retention_confirm": "Apakah Anda yakin ingin mengubah retensi kueri log? Jika Anda menurunkan nilai interval, beberapa data akan hilang", "query_log_retention_confirm": "Apakah Anda yakin ingin mengubah rotasi kueri log? Jika Anda menurunkan nilai interval, beberapa data akan hilang",
"anonymize_client_ip": "Anonim IP klien", "anonymize_client_ip": "Anonim IP klien",
"anonymize_client_ip_desc": "Jangan simpan alamat lengkap IP klien dalam log dan statistik", "anonymize_client_ip_desc": "Jangan simpan alamat lengkap IP klien dalam log dan statistik",
"dns_config": "Konfigurasi server DNS", "dns_config": "Konfigurasi server DNS",
@ -278,6 +285,9 @@
"custom_ip": "Custom IP", "custom_ip": "Custom IP",
"blocking_ipv4": "Blokiran IPv4", "blocking_ipv4": "Blokiran IPv4",
"blocking_ipv6": "Blokiran IPv6", "blocking_ipv6": "Blokiran IPv6",
"blocked_response_ttl": "Respon TLL diblokir",
"blocked_response_ttl_desc": "Menentukan berapa detik klien harus menyimpan respons yang difilter dalam cache",
"form_enter_blocked_response_ttl": "Masukkan TTL respons yang diblokir (detik)",
"dnscrypt": "DNSCrypt", "dnscrypt": "DNSCrypt",
"dns_over_https": "DNS-over-HTTPS", "dns_over_https": "DNS-over-HTTPS",
"dns_over_tls": "DNS-over-TLS", "dns_over_tls": "DNS-over-TLS",
@ -293,6 +303,8 @@
"rate_limit": "Batas nilai", "rate_limit": "Batas nilai",
"edns_enable": "Aktifkan EDNS Klien Subnet", "edns_enable": "Aktifkan EDNS Klien Subnet",
"edns_cs_desc": "Tambahkan opsi EDNS Client Subnet (ECS) ke permintaan upstream dan catat nilai yang dikirim oleh klien di log kueri.", "edns_cs_desc": "Tambahkan opsi EDNS Client Subnet (ECS) ke permintaan upstream dan catat nilai yang dikirim oleh klien di log kueri.",
"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_desc": "Jumlah permintaan per detik yang diperbolehkan untuk satu klien. Atur ke 0 untuk tidak terbatas.",
"blocking_ipv4_desc": "Alamat IP akan dikembalikan untuk permintaan A yang diblokir", "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_ipv6_desc": "Alamat IP akan dipulihkan untuk permintaan AAAA yang diblokir",
@ -396,7 +408,7 @@
"encryption_issuer": "Penerbit", "encryption_issuer": "Penerbit",
"encryption_hostnames": "Nama host", "encryption_hostnames": "Nama host",
"encryption_reset": "Anda yakin ingin mengatur ulang pengaturan enkripsi?", "encryption_reset": "Anda yakin ingin mengatur ulang pengaturan enkripsi?",
"encryption_warning": "Perhatian", "encryption_warning": "Peringatan",
"topline_expiring_certificate": "Sertifikat SSL Anda hampir kedaluwarsa. Perbarui <0>Pengaturan enkripsi</0>.", "topline_expiring_certificate": "Sertifikat SSL Anda hampir kedaluwarsa. Perbarui <0>Pengaturan enkripsi</0>.",
"topline_expired_certificate": "Sertifikat SSL Anda kedaluwarsa. Perbarui <0>Pengaturan enkripsi</0>.", "topline_expired_certificate": "Sertifikat SSL Anda kedaluwarsa. Perbarui <0>Pengaturan enkripsi</0>.",
"form_error_port_range": "Masukkan nomor port di kisaran 80-65535", "form_error_port_range": "Masukkan nomor port di kisaran 80-65535",
@ -444,7 +456,7 @@
"client_confirm_delete": "Apakah anda yakin ingin menghapus klien \"{{key}}\"?", "client_confirm_delete": "Apakah anda yakin ingin menghapus klien \"{{key}}\"?",
"list_confirm_delete": "Anda yakin ingin menghapus daftar ini?", "list_confirm_delete": "Anda yakin ingin menghapus daftar ini?",
"auto_clients_title": "Klien (waktu berjalan)", "auto_clients_title": "Klien (waktu berjalan)",
"auto_clients_desc": "Perangkat yang tidak ada dalam daftar klien Persisten yang mungkin masih menggunakan AdGuard Home", "auto_clients_desc": "Informasi tentang alamat IP perangkat yang menggunakan atau mungkin menggunakan AdGuard Home. Informasi ini dikumpulkan dari beberapa sumber, termasuk file host, reverse DNS, dll.",
"access_title": "Pengaturan akses", "access_title": "Pengaturan akses",
"access_desc": "Disini anda dapat mengatur aturan akses untuk server AdGuard Home DNS", "access_desc": "Disini anda dapat mengatur aturan akses untuk server AdGuard Home DNS",
"access_allowed_title": "Klien yang diizinkan", "access_allowed_title": "Klien yang diizinkan",
@ -457,6 +469,7 @@
"updates_checked": "Versi baru AdGuard Home tersedia\n", "updates_checked": "Versi baru AdGuard Home tersedia\n",
"updates_version_equal": "AdGuard Home sudah tebaru", "updates_version_equal": "AdGuard Home sudah tebaru",
"check_updates_now": "Periksa pembaruan sekarang", "check_updates_now": "Periksa pembaruan sekarang",
"version_request_error": "Pemeriksaan pembaruan gagal. Harap periksa koneksi internet anda.",
"dns_privacy": "DNS Privasi", "dns_privacy": "DNS Privasi",
"setup_dns_privacy_1": "<0>DNS-over-TLS:</0> Memakai <1>{{address}}</1> string.", "setup_dns_privacy_1": "<0>DNS-over-TLS:</0> Memakai <1>{{address}}</1> string.",
"setup_dns_privacy_2": "<0>DNS-over-TLS:</0> Memakai <1>{{address}}</1> string.", "setup_dns_privacy_2": "<0>DNS-over-TLS:</0> Memakai <1>{{address}}</1> string.",
@ -527,6 +540,8 @@
"statistics_retention_confirm": "Apakah Anda yakin ingin mengubah retensi statistik? Jika Anda menurunkan nilai interval, beberapa data akan hilang", "statistics_retention_confirm": "Apakah Anda yakin ingin mengubah retensi statistik? Jika Anda menurunkan nilai interval, beberapa data akan hilang",
"statistics_cleared": "Statistik berhasil dihapus", "statistics_cleared": "Statistik berhasil dihapus",
"statistics_enable": "Aktifkan statistik", "statistics_enable": "Aktifkan statistik",
"ignore_domains": "Domain yang diabaikan (dipisahkan oleh baris baru)",
"ignore_domains_title": "Domain yang diabaikan",
"ignore_domains_desc_stats": "Kueri yang cocok dengan aturan ini tidak ditulis ke statistik", "ignore_domains_desc_stats": "Kueri yang cocok dengan aturan ini tidak ditulis ke statistik",
"ignore_domains_desc_query": "Kueri yang cocok dengan aturan ini tidak ditulis ke log kueri", "ignore_domains_desc_query": "Kueri yang cocok dengan aturan ini tidak ditulis ke log kueri",
"interval_hours": "{{count}} jam", "interval_hours": "{{count}} jam",
@ -644,13 +659,36 @@
"parental_control": "Kontrol Orang Tua", "parental_control": "Kontrol Orang Tua",
"safe_browsing": "Penjelajahan Aman", "safe_browsing": "Penjelajahan Aman",
"served_from_cache": "{{value}} <i>(disajikan dari cache)</i>", "served_from_cache": "{{value}} <i>(disajikan dari cache)</i>",
"form_error_password_length": "Kata sandi harus minimal {{value}} karakter", "form_error_password_length": "Kata sandi harus terdiri dari {{min}} hingga {{max}}",
"anonymizer_notification": "<0>Catatan:</0> Anonimisasi IP diaktifkan. Anda dapat menonaktifkannya di <1>Pengaturan umum</1> .", "anonymizer_notification": "<0>Catatan:</0> Anonimisasi IP diaktifkan. Anda dapat menonaktifkannya di <1>Pengaturan umum</1> .",
"confirm_dns_cache_clear": "Apakah Anda yakin ingin menghapus cache DNS?", "confirm_dns_cache_clear": "Apakah Anda yakin ingin menghapus cache DNS?",
"cache_cleared": "Cache DNS berhasil dibersihkan", "cache_cleared": "Cache DNS berhasil dibersihkan",
"clear_cache": "Hapus cache", "clear_cache": "Hapus cache",
"make_static": "Jadikan statis",
"theme_auto_desc": "Otomatis (berdasarkan skema warna perangkat anda)",
"theme_dark_desc": "Tema gelap",
"theme_light_desc": "Tema terang", "theme_light_desc": "Tema terang",
"disable_for_seconds": "Untuk {{count}} detik",
"disable_for_seconds_plural": "Untuk {{count}} detik",
"disable_for_minutes": "Untuk {{count}} menit",
"disable_for_minutes_plural": "Untuk {{count}} menit",
"disable_for_hours": "Untuk {{count}} jam",
"disable_for_hours_plural": "Untuk {{count}} jam",
"disable_until_tomorrow": "Sampai besok",
"disable_notify_for_seconds": "Hentikan perlindungan selama {{count}} detik",
"disable_notify_for_seconds_plural": "Hentikan perlindungan selama {{count}} detik",
"disable_notify_for_minutes": "Hentikan perlindungan selama {{count}} menit",
"disable_notify_for_minutes_plural": "Hentikan perlindungan selama {{count}} menit",
"disable_notify_for_hours": "Hentikan perlindungan selama {{count}} jam",
"disable_notify_for_hours_plural": "Hentikan perlindungan selama {{count}} jam",
"disable_notify_until_tomorrow": "Hentikan perlindungan sampai besok",
"enable_protection_timer": "Perlindungan akan diaktifkan dalam {{time}}",
"custom_retention_input": "Masukkan retensi dalam hitungan jam",
"custom_rotation_input": "Masukkan rotasi dalam hitungan jam",
"protection_section_label": "Perlindungan", "protection_section_label": "Perlindungan",
"log_and_stats_section_label": "Log kueri dan statistik",
"ignore_query_log": "Abaikan klien ini di log kueri",
"ignore_statistics": "Abaikan klien ini di statistik",
"schedule_services": "Menjeda pemblokiran layanan", "schedule_services": "Menjeda pemblokiran layanan",
"schedule_services_desc": "Mengonfigurasi jadwal jeda filter pemblokiran layanan", "schedule_services_desc": "Mengonfigurasi jadwal jeda filter pemblokiran layanan",
"schedule_services_desc_client": "Mengonfigurasi jadwal jeda filter pemblokiran layanan untuk klien ini", "schedule_services_desc_client": "Mengonfigurasi jadwal jeda filter pemblokiran layanan untuk klien ini",
@ -665,6 +703,17 @@
"schedule_new": "Jadwal baru", "schedule_new": "Jadwal baru",
"schedule_edit": "Edit jadwal", "schedule_edit": "Edit jadwal",
"schedule_save": "Simpan jadwal", "schedule_save": "Simpan jadwal",
"schedule_add": "Tambahkan jadwal",
"schedule_remove": "Hapus jadwal",
"schedule_from": "Dari",
"schedule_to": "Hingga",
"sunday": "Minggu",
"monday": "Senin",
"tuesday": "Selasa",
"wednesday": "Rabu",
"thursday": "Kamis",
"friday": "Jumat",
"saturday": "Sabtu",
"sunday_short": "Ming", "sunday_short": "Ming",
"monday_short": "Sen", "monday_short": "Sen",
"tuesday_short": "Sel", "tuesday_short": "Sel",

View file

@ -73,7 +73,9 @@
"dhcp_dynamic_ip_found": "Il tuo sistema utilizza una configurazione di indirizzi IP dinamici per l'interfaccia <0>{{interfaceName}}</0>. Per poter utilizzare un server DHCP, è necessario impostare un indirizzo IP statico. Il tuo indirizzo IP attuale è <0>{{ipAddress}}</0>. AdGuard Home imposterà automaticamente questo indirizzo come statico quando cliccherai il pulsante \"Attiva server DHCP\".", "dhcp_dynamic_ip_found": "Il tuo sistema utilizza una configurazione di indirizzi IP dinamici per l'interfaccia <0>{{interfaceName}}</0>. Per poter utilizzare un server DHCP, è necessario impostare un indirizzo IP statico. Il tuo indirizzo IP attuale è <0>{{ipAddress}}</0>. AdGuard Home imposterà automaticamente questo indirizzo come statico quando cliccherai il pulsante \"Attiva server DHCP\".",
"dhcp_lease_added": "Lease statici \"{{key}}\" aggiunti correttamente", "dhcp_lease_added": "Lease statici \"{{key}}\" aggiunti correttamente",
"dhcp_lease_deleted": "Lease statico \"{{key}}\" eliminato correttamente", "dhcp_lease_deleted": "Lease statico \"{{key}}\" eliminato correttamente",
"dhcp_lease_updated": "Locazione statica \"{{key}}\" aggiornata con successo",
"dhcp_new_static_lease": "Nuovo lease statico", "dhcp_new_static_lease": "Nuovo lease statico",
"dhcp_edit_static_lease": "Modifica locazione statica",
"dhcp_static_leases_not_found": "Non è stato trovato nessun leases statico DHCP", "dhcp_static_leases_not_found": "Non è stato trovato nessun leases statico DHCP",
"dhcp_add_static_lease": "Aggiungi lease statico", "dhcp_add_static_lease": "Aggiungi lease statico",
"dhcp_reset_leases": "Reimposta tutti i temporanei", "dhcp_reset_leases": "Reimposta tutti i temporanei",
@ -283,6 +285,9 @@
"custom_ip": "IP personalizzato", "custom_ip": "IP personalizzato",
"blocking_ipv4": "Blocca IPv4", "blocking_ipv4": "Blocca IPv4",
"blocking_ipv6": "Blocca IPv6", "blocking_ipv6": "Blocca IPv6",
"blocked_response_ttl": "Risposta TTL bloccata",
"blocked_response_ttl_desc": "Specifica per quanti secondi i client devono tenere nella cache una risposta filtrata",
"form_enter_blocked_response_ttl": "Inserisci tempo di vita (TTL) della risposta bloccata (secondi)",
"dnscrypt": "DNSCrypt", "dnscrypt": "DNSCrypt",
"dns_over_https": "DNS su HTTPS", "dns_over_https": "DNS su HTTPS",
"dns_over_tls": "DNS su TLS", "dns_over_tls": "DNS su TLS",
@ -537,8 +542,8 @@
"statistics_enable": "Attiva statistiche", "statistics_enable": "Attiva statistiche",
"ignore_domains": "Domini ignorati (separati da nuova riga)", "ignore_domains": "Domini ignorati (separati da nuova riga)",
"ignore_domains_title": "Domini ignorati", "ignore_domains_title": "Domini ignorati",
"ignore_domains_desc_stats": "Le richieste per questi domini non vengono scritte nelle statistiche", "ignore_domains_desc_stats": "Le richieste che corrispondono a queste regole non vengono scritte nelle statistiche",
"ignore_domains_desc_query": "Le richieste per questi domini non vengono scritte nel registro delle richieste", "ignore_domains_desc_query": "Le richieste che corrispondono a queste regole non vengono scritte nel registro delle richieste",
"interval_hours": "{{count}} ora", "interval_hours": "{{count}} ora",
"interval_hours_plural": "{{count}} ore", "interval_hours_plural": "{{count}} ore",
"filters_configuration": "Configurazione filtri", "filters_configuration": "Configurazione filtri",
@ -654,7 +659,7 @@
"parental_control": "Controllo Parentale", "parental_control": "Controllo Parentale",
"safe_browsing": "Navigazione Sicura", "safe_browsing": "Navigazione Sicura",
"served_from_cache": "{{value}} <i>(fornito dalla cache)</i>", "served_from_cache": "{{value}} <i>(fornito dalla cache)</i>",
"form_error_password_length": "La password deve contenere almeno {{value}} caratteri", "form_error_password_length": "La password deve contenere da {{min}} a {{max}} caratteri",
"anonymizer_notification": "<0>Attenzione:</0> L'anonimizzazione dell'IP è abilitata. Puoi disabilitarla in <1>Impostazioni generali</1>.", "anonymizer_notification": "<0>Attenzione:</0> L'anonimizzazione dell'IP è abilitata. Puoi disabilitarla in <1>Impostazioni generali</1>.",
"confirm_dns_cache_clear": "Sei sicuro di voler cancellare la cache DNS?", "confirm_dns_cache_clear": "Sei sicuro di voler cancellare la cache DNS?",
"cache_cleared": "Cache DNS è stata cancellata correttamente", "cache_cleared": "Cache DNS è stata cancellata correttamente",

View file

@ -73,7 +73,9 @@
"dhcp_dynamic_ip_found": "お使いのシステムは、インターフェース<0>{{interfaceName}}</0>用に動的IPアドレス構成を使用しています。DHCPサーバを使用するには、静的IPアドレスで設定する必要があります。あなたの現在のIPアドレスは<0>{{ipAddress}}</0>です。「DHCPサーバを有効にする」ボタンを押すと、AdGuard Homeは自動的にこのIPアドレスを静的IPアドレスとして設定します。", "dhcp_dynamic_ip_found": "お使いのシステムは、インターフェース<0>{{interfaceName}}</0>用に動的IPアドレス構成を使用しています。DHCPサーバを使用するには、静的IPアドレスで設定する必要があります。あなたの現在のIPアドレスは<0>{{ipAddress}}</0>です。「DHCPサーバを有効にする」ボタンを押すと、AdGuard Homeは自動的にこのIPアドレスを静的IPアドレスとして設定します。",
"dhcp_lease_added": "静的リース \"{{key}}\" の追加が完了しました。", "dhcp_lease_added": "静的リース \"{{key}}\" の追加が完了しました。",
"dhcp_lease_deleted": "静的リース \"{{key}}\" の削除が完了しました。", "dhcp_lease_deleted": "静的リース \"{{key}}\" の削除が完了しました。",
"dhcp_lease_updated": "静的リース \"{{key}}\" の更新に成功しました。",
"dhcp_new_static_lease": "新規静的割り当て", "dhcp_new_static_lease": "新規静的割り当て",
"dhcp_edit_static_lease": "静的リースを編集",
"dhcp_static_leases_not_found": "DHCP静的割り当てはありません", "dhcp_static_leases_not_found": "DHCP静的割り当てはありません",
"dhcp_add_static_lease": "静的割り当てを追加する", "dhcp_add_static_lease": "静的割り当てを追加する",
"dhcp_reset_leases": "すべてのリースをリセットする", "dhcp_reset_leases": "すべてのリースをリセットする",
@ -283,6 +285,9 @@
"custom_ip": "カスタムIP", "custom_ip": "カスタムIP",
"blocking_ipv4": "ブロック中のIPv4", "blocking_ipv4": "ブロック中のIPv4",
"blocking_ipv6": "ブロック中のIPv6", "blocking_ipv6": "ブロック中のIPv6",
"blocked_response_ttl": "Blocked Response TTLブロック済み応答のTTL",
"blocked_response_ttl_desc": "フィルタリングされた応答をクライアントがキャッシュしておく時間(秒)を指定します。",
"form_enter_blocked_response_ttl": "ブロック済み応答のTTL秒単位を入力してください",
"dnscrypt": "DNSCrypt", "dnscrypt": "DNSCrypt",
"dns_over_https": "DNS-over-HTTPS", "dns_over_https": "DNS-over-HTTPS",
"dns_over_tls": "DNS-over-TLS", "dns_over_tls": "DNS-over-TLS",
@ -654,7 +659,7 @@
"parental_control": "ペアレンタルコントロール", "parental_control": "ペアレンタルコントロール",
"safe_browsing": "セーフブラウジング", "safe_browsing": "セーフブラウジング",
"served_from_cache": "{{value}} <i>(キャッシュから応答)</i>", "served_from_cache": "{{value}} <i>(キャッシュから応答)</i>",
"form_error_password_length": "パスワードは{{value}}文字以上にしてください", "form_error_password_length": "パスワードの長さは{{min}}〜{{max}}文字にしてください。",
"anonymizer_notification": "【<0>注意</0>】IPの匿名化が有効になっています。 <1>一般設定</1>で無効にできます。", "anonymizer_notification": "【<0>注意</0>】IPの匿名化が有効になっています。 <1>一般設定</1>で無効にできます。",
"confirm_dns_cache_clear": "DNS キャッシュをクリアしてもよろしいですか?", "confirm_dns_cache_clear": "DNS キャッシュをクリアしてもよろしいですか?",
"cache_cleared": "DNSキャッシュのクリア完了です。", "cache_cleared": "DNSキャッシュのクリア完了です。",

View file

@ -73,7 +73,9 @@
"dhcp_dynamic_ip_found": "시스템은 <0>{{interfaceName}}</0> 인터페이스에 동적 IP 주소를 사용합니다. DHCP 서버를 사용하려면 고정 IP 주소를 설정해야 합니다. 현재 IP 주소는 <0>{{ipAddress}}</0>입니다. 'DHCP 서버 활성화' 버튼을 누르면 AdGuard Home이 이 IP 주소를 고정 IP 주소로 자동 설정합니다.", "dhcp_dynamic_ip_found": "시스템은 <0>{{interfaceName}}</0> 인터페이스에 동적 IP 주소를 사용합니다. DHCP 서버를 사용하려면 고정 IP 주소를 설정해야 합니다. 현재 IP 주소는 <0>{{ipAddress}}</0>입니다. 'DHCP 서버 활성화' 버튼을 누르면 AdGuard Home이 이 IP 주소를 고정 IP 주소로 자동 설정합니다.",
"dhcp_lease_added": "'{{key}}' 고정 임대 정상적으로 추가되었습니다", "dhcp_lease_added": "'{{key}}' 고정 임대 정상적으로 추가되었습니다",
"dhcp_lease_deleted": "'{{key}}' 고정 임대 정상적으로 삭제되었습니다", "dhcp_lease_deleted": "'{{key}}' 고정 임대 정상적으로 삭제되었습니다",
"dhcp_lease_updated": "'{{key}}' 고정 임대 정상적으로 업데이트되었습니다.",
"dhcp_new_static_lease": "새 고정 임대", "dhcp_new_static_lease": "새 고정 임대",
"dhcp_edit_static_lease": "고정 임대 수정",
"dhcp_static_leases_not_found": "DHCP 고정 임대를 찾을 수 없음", "dhcp_static_leases_not_found": "DHCP 고정 임대를 찾을 수 없음",
"dhcp_add_static_lease": "고정 임대 추가", "dhcp_add_static_lease": "고정 임대 추가",
"dhcp_reset_leases": "모든 임대 초기화", "dhcp_reset_leases": "모든 임대 초기화",
@ -283,6 +285,9 @@
"custom_ip": "사용자 지정 IP", "custom_ip": "사용자 지정 IP",
"blocking_ipv4": "IPv4 차단", "blocking_ipv4": "IPv4 차단",
"blocking_ipv6": "IPv6 차단", "blocking_ipv6": "IPv6 차단",
"blocked_response_ttl": "차단된 TTL 응답",
"blocked_response_ttl_desc": "클라이언트가 필터링된 응답을 캐시해야 하는 시간(초)을 지정합니다.",
"form_enter_blocked_response_ttl": "차단된 응답 TTL(초)을 입력하세요.",
"dnscrypt": "DNSCrypt", "dnscrypt": "DNSCrypt",
"dns_over_https": "DNS-over-HTTPS", "dns_over_https": "DNS-over-HTTPS",
"dns_over_tls": "DNS-over-TLS", "dns_over_tls": "DNS-over-TLS",
@ -403,7 +408,7 @@
"encryption_issuer": "발행자", "encryption_issuer": "발행자",
"encryption_hostnames": "호스트 이름", "encryption_hostnames": "호스트 이름",
"encryption_reset": "암호화 설정을 재설정하시겠습니까?", "encryption_reset": "암호화 설정을 재설정하시겠습니까?",
"encryption_warning": "경고", "encryption_warning": "주의",
"topline_expiring_certificate": "SSL 인증서가 곧 만료됩니다. 업데이트<0> 암호화 설정</0>.", "topline_expiring_certificate": "SSL 인증서가 곧 만료됩니다. 업데이트<0> 암호화 설정</0>.",
"topline_expired_certificate": "SSL 인증서가 만료되었습니다. 업데이트<0> 암호화 설정</0>.", "topline_expired_certificate": "SSL 인증서가 만료되었습니다. 업데이트<0> 암호화 설정</0>.",
"form_error_port_range": "80-65535 범위의 포트 번호를 입력하세요", "form_error_port_range": "80-65535 범위의 포트 번호를 입력하세요",
@ -654,7 +659,7 @@
"parental_control": "자녀 보호", "parental_control": "자녀 보호",
"safe_browsing": "세이프 브라우징", "safe_browsing": "세이프 브라우징",
"served_from_cache": "{{value}} <i>(캐시에서 제공)</i>", "served_from_cache": "{{value}} <i>(캐시에서 제공)</i>",
"form_error_password_length": "비밀번호는 {{value}}자 이상이어야 합니다", "form_error_password_length": "비밀번호는 {{min}}~{{max}}자 길이여야 합니다.",
"anonymizer_notification": "<0>참고:</0> IP 익명화가 활성화되었습니다. <1>일반 설정</1>에서 비활성화할 수 있습니다.", "anonymizer_notification": "<0>참고:</0> IP 익명화가 활성화되었습니다. <1>일반 설정</1>에서 비활성화할 수 있습니다.",
"confirm_dns_cache_clear": "정말로 DNS 캐시를 지우시겠습니까?", "confirm_dns_cache_clear": "정말로 DNS 캐시를 지우시겠습니까?",
"cache_cleared": "DNS 캐시를 성공적으로 지웠습니다", "cache_cleared": "DNS 캐시를 성공적으로 지웠습니다",

View file

@ -73,7 +73,9 @@
"dhcp_dynamic_ip_found": "Je systeem gebruikt dynamische IP-adres configuratie voor interface <0>{{interfaceName}}</0>. Om de DHCP server te gebruiken moet er een statisch IP-adres worden ingesteld. Je huidige IP-adres is <0>{{ipAddress}}</0>. AdGuard Home zal automatisch dit IP-adres als statisch IP-adres instellen wanneer je op de knop \"DHCP inschakelen\" drukt.", "dhcp_dynamic_ip_found": "Je systeem gebruikt dynamische IP-adres configuratie voor interface <0>{{interfaceName}}</0>. Om de DHCP server te gebruiken moet er een statisch IP-adres worden ingesteld. Je huidige IP-adres is <0>{{ipAddress}}</0>. AdGuard Home zal automatisch dit IP-adres als statisch IP-adres instellen wanneer je op de knop \"DHCP inschakelen\" drukt.",
"dhcp_lease_added": "Statische uitgifte \"{{key}}\" met succes toegevoegd", "dhcp_lease_added": "Statische uitgifte \"{{key}}\" met succes toegevoegd",
"dhcp_lease_deleted": "Statische uitgifte \"{{key}}\" met succes verwijderd", "dhcp_lease_deleted": "Statische uitgifte \"{{key}}\" met succes verwijderd",
"dhcp_lease_updated": "Statische lease \"{{key}}\" succesvol bijgewerkt",
"dhcp_new_static_lease": "Voeg static lease toe", "dhcp_new_static_lease": "Voeg static lease toe",
"dhcp_edit_static_lease": "Statische lease bewerken",
"dhcp_static_leases_not_found": "Geen DHCP static lease gevonden", "dhcp_static_leases_not_found": "Geen DHCP static lease gevonden",
"dhcp_add_static_lease": "Voeg statische lease toe", "dhcp_add_static_lease": "Voeg statische lease toe",
"dhcp_reset_leases": "Alle leases resetten", "dhcp_reset_leases": "Alle leases resetten",
@ -283,6 +285,9 @@
"custom_ip": "Aangepast IP", "custom_ip": "Aangepast IP",
"blocking_ipv4": "Blokkeren IP4", "blocking_ipv4": "Blokkeren IP4",
"blocking_ipv6": "Blokkeren IP6", "blocking_ipv6": "Blokkeren IP6",
"blocked_response_ttl": "Geblokkeerde reactie TTL",
"blocked_response_ttl_desc": "Hiermee geef je op hoeveel seconden de clients een gefilterd antwoord in de cache moeten opslaan",
"form_enter_blocked_response_ttl": "Voer geblokkeerd antwoord TTL in (seconden)",
"dnscrypt": "DNSCrypt", "dnscrypt": "DNSCrypt",
"dns_over_https": "DNS-via-HTTPS", "dns_over_https": "DNS-via-HTTPS",
"dns_over_tls": "DNS-via-TLS", "dns_over_tls": "DNS-via-TLS",
@ -654,7 +659,7 @@
"parental_control": "Ouderlijk toezicht", "parental_control": "Ouderlijk toezicht",
"safe_browsing": "Veilig browsen", "safe_browsing": "Veilig browsen",
"served_from_cache": "{{value}} <i>(geleverd vanuit cache)</i>", "served_from_cache": "{{value}} <i>(geleverd vanuit cache)</i>",
"form_error_password_length": "Wachtwoord moet minimaal {{value}} tekens lang zijn", "form_error_password_length": "Wachtwoord moet {{min}} tot {{max}} tekens lang zijn",
"anonymizer_notification": "<0>Opmerking:</0> IP-anonimisering is ingeschakeld. Je kunt het uitschakelen in <1>Algemene instellingen</1>.", "anonymizer_notification": "<0>Opmerking:</0> IP-anonimisering is ingeschakeld. Je kunt het uitschakelen in <1>Algemene instellingen</1>.",
"confirm_dns_cache_clear": "Weet je zeker dat je de DNS-cache wilt wissen?", "confirm_dns_cache_clear": "Weet je zeker dat je de DNS-cache wilt wissen?",
"cache_cleared": "DNS-cache succesvol gewist", "cache_cleared": "DNS-cache succesvol gewist",

View file

@ -73,7 +73,9 @@
"dhcp_dynamic_ip_found": "Twój system używa dynamicznej konfiguracji adresu IP dla interfejsu <0>{{interfaceName}}</0>. Aby można było korzystać z serwera DHCP, należy ustawić statyczny adres IP. Twój obecny adres IP to <0>{{ipAddress}}</0>. AdGuard Home automatycznie ustawi ten adres IP jako statyczny, jeśli naciśniesz przycisk \"Włącz serwer DHCP\".", "dhcp_dynamic_ip_found": "Twój system używa dynamicznej konfiguracji adresu IP dla interfejsu <0>{{interfaceName}}</0>. Aby można było korzystać z serwera DHCP, należy ustawić statyczny adres IP. Twój obecny adres IP to <0>{{ipAddress}}</0>. AdGuard Home automatycznie ustawi ten adres IP jako statyczny, jeśli naciśniesz przycisk \"Włącz serwer DHCP\".",
"dhcp_lease_added": "Dzierżawa statyczna \"{{key}}\" pomyślnie dodana", "dhcp_lease_added": "Dzierżawa statyczna \"{{key}}\" pomyślnie dodana",
"dhcp_lease_deleted": "Dzierżawa statyczna \"{{key}}\" pomyślnie usunięta", "dhcp_lease_deleted": "Dzierżawa statyczna \"{{key}}\" pomyślnie usunięta",
"dhcp_lease_updated": "Dzierżawa statyczna \"{{key}}\" pomyślnie zaktualizowana",
"dhcp_new_static_lease": "Nowa dzierżawa statyczna", "dhcp_new_static_lease": "Nowa dzierżawa statyczna",
"dhcp_edit_static_lease": "Edytuj dzierżawę statyczną",
"dhcp_static_leases_not_found": "Nie znaleziono statycznych dzierżaw DHCP", "dhcp_static_leases_not_found": "Nie znaleziono statycznych dzierżaw DHCP",
"dhcp_add_static_lease": "Dodaj dzierżawę statyczną", "dhcp_add_static_lease": "Dodaj dzierżawę statyczną",
"dhcp_reset_leases": "Zresetuj wszystkie dzierżawy", "dhcp_reset_leases": "Zresetuj wszystkie dzierżawy",
@ -283,6 +285,9 @@
"custom_ip": "Niestandardowy adres IP", "custom_ip": "Niestandardowy adres IP",
"blocking_ipv4": "Blokowanie IPv4", "blocking_ipv4": "Blokowanie IPv4",
"blocking_ipv6": "Blokowanie IPv6", "blocking_ipv6": "Blokowanie IPv6",
"blocked_response_ttl": "TTL zablokowanej odpowiedzi",
"blocked_response_ttl_desc": "Określa, przez ile sekund klienci powinni buforować przefiltrowaną odpowiedź",
"form_enter_blocked_response_ttl": "Wprowadź TTL zablokowanej odpowiedzi (sekundy)",
"dnscrypt": "DNSCrypt", "dnscrypt": "DNSCrypt",
"dns_over_https": "DNS-over-HTTPS", "dns_over_https": "DNS-over-HTTPS",
"dns_over_tls": "DNS-over-TLS", "dns_over_tls": "DNS-over-TLS",
@ -403,7 +408,7 @@
"encryption_issuer": "Zgłaszający", "encryption_issuer": "Zgłaszający",
"encryption_hostnames": "Nazwy hostów", "encryption_hostnames": "Nazwy hostów",
"encryption_reset": "Czy na pewno chcesz zresetować ustawienia szyfrowania?", "encryption_reset": "Czy na pewno chcesz zresetować ustawienia szyfrowania?",
"encryption_warning": "Uwaga", "encryption_warning": "Ostrzeżenie",
"topline_expiring_certificate": "Twój certyfikat SSL wkrótce wygaśnie. Zaktualizuj <0>Ustawienia szyfrowania</0>.", "topline_expiring_certificate": "Twój certyfikat SSL wkrótce wygaśnie. Zaktualizuj <0>Ustawienia szyfrowania</0>.",
"topline_expired_certificate": "Twój certyfikat SSL wygasł. Zaktualizuj <0>Ustawienia szyfrowania</0>.", "topline_expired_certificate": "Twój certyfikat SSL wygasł. Zaktualizuj <0>Ustawienia szyfrowania</0>.",
"form_error_port_range": "Wpisz numer portu z zakresu 80-65535", "form_error_port_range": "Wpisz numer portu z zakresu 80-65535",
@ -654,7 +659,7 @@
"parental_control": "Kontrola rodzicielska", "parental_control": "Kontrola rodzicielska",
"safe_browsing": "Bezpieczne przeglądanie", "safe_browsing": "Bezpieczne przeglądanie",
"served_from_cache": "{{value}} <i>(podawane z pamięci podręcznej)</i>", "served_from_cache": "{{value}} <i>(podawane z pamięci podręcznej)</i>",
"form_error_password_length": "Hasło musi mieć co najmniej {{value}} znaków", "form_error_password_length": "Hasło musi zawierać od {{min}} do {{max}} znaków",
"anonymizer_notification": "<0>Uwaga:</0> Anonimizacja IP jest włączona. Możesz ją wyłączyć w <1>Ustawieniach ogólnych</1>.", "anonymizer_notification": "<0>Uwaga:</0> Anonimizacja IP jest włączona. Możesz ją wyłączyć w <1>Ustawieniach ogólnych</1>.",
"confirm_dns_cache_clear": "Czy na pewno chcesz wyczyścić pamięć podręczną DNS?", "confirm_dns_cache_clear": "Czy na pewno chcesz wyczyścić pamięć podręczną DNS?",
"cache_cleared": "Pamięć podręczna DNS została pomyślnie wyczyszczona", "cache_cleared": "Pamięć podręczna DNS została pomyślnie wyczyszczona",

View file

@ -73,7 +73,9 @@
"dhcp_dynamic_ip_found": "Seu sistema usa a configuração de endereço IP dinâmico para a interface <0>{{interfaceName}}</0>. Para usar o servidor DHCP, você deve definir um endereço de IP estático. Seu endereço IP atual é <0> {{ipAddress}} </ 0>. AdGuard Home irá definir automaticamente este endereço IP como estático se você pressionar o botão \"Ativar servidor DHCP\".", "dhcp_dynamic_ip_found": "Seu sistema usa a configuração de endereço IP dinâmico para a interface <0>{{interfaceName}}</0>. Para usar o servidor DHCP, você deve definir um endereço de IP estático. Seu endereço IP atual é <0> {{ipAddress}} </ 0>. AdGuard Home irá definir automaticamente este endereço IP como estático se você pressionar o botão \"Ativar servidor DHCP\".",
"dhcp_lease_added": "Concessão estática \"{{key}}\" adicionada com sucesso", "dhcp_lease_added": "Concessão estática \"{{key}}\" adicionada com sucesso",
"dhcp_lease_deleted": "Concessão estática \"{{key}}\" excluída com sucesso", "dhcp_lease_deleted": "Concessão estática \"{{key}}\" excluída com sucesso",
"dhcp_lease_updated": "Concessão estática \"{{key}}\" atualizada com sucesso",
"dhcp_new_static_lease": "Nova concessão estática", "dhcp_new_static_lease": "Nova concessão estática",
"dhcp_edit_static_lease": "Editar concessão estática",
"dhcp_static_leases_not_found": "Nenhuma concessão DHCP estática foi encontrada", "dhcp_static_leases_not_found": "Nenhuma concessão DHCP estática foi encontrada",
"dhcp_add_static_lease": "Adicionar nova concessão estática", "dhcp_add_static_lease": "Adicionar nova concessão estática",
"dhcp_reset_leases": "Redefinir todas as concessões", "dhcp_reset_leases": "Redefinir todas as concessões",
@ -283,6 +285,9 @@
"custom_ip": "IP personalizado", "custom_ip": "IP personalizado",
"blocking_ipv4": "Bloqueando IPv4", "blocking_ipv4": "Bloqueando IPv4",
"blocking_ipv6": "Bloqueando IPv6", "blocking_ipv6": "Bloqueando IPv6",
"blocked_response_ttl": "Resposta bloqueada TTL",
"blocked_response_ttl_desc": "Especifica por quantos segundos os clientes devem armazenar em cache uma resposta filtrada",
"form_enter_blocked_response_ttl": "Insira o TTL da resposta bloqueada (segundos)",
"dnscrypt": "DNSCrypt", "dnscrypt": "DNSCrypt",
"dns_over_https": "DNS-sobre-HTTPS", "dns_over_https": "DNS-sobre-HTTPS",
"dns_over_tls": "DNS-sobre-TLS", "dns_over_tls": "DNS-sobre-TLS",
@ -654,7 +659,7 @@
"parental_control": "Controle parental", "parental_control": "Controle parental",
"safe_browsing": "Navegação segura", "safe_browsing": "Navegação segura",
"served_from_cache": "{{value}} <i>(servido do cache)</i>", "served_from_cache": "{{value}} <i>(servido do cache)</i>",
"form_error_password_length": "A senha deve ter pelo menos {{value}} caracteres", "form_error_password_length": "A senha deve ter entre {{min}} e {{max}} caracteres",
"anonymizer_notification": "<0>Observação:</0> A anonimização de IP está ativada. Você pode desativá-lo em <1>Configurações gerais</1>.", "anonymizer_notification": "<0>Observação:</0> A anonimização de IP está ativada. Você pode desativá-lo em <1>Configurações gerais</1>.",
"confirm_dns_cache_clear": "Tem certeza de que deseja limpar o cache DNS?", "confirm_dns_cache_clear": "Tem certeza de que deseja limpar o cache DNS?",
"cache_cleared": "Cache DNS limpo com sucesso", "cache_cleared": "Cache DNS limpo com sucesso",

View file

@ -73,7 +73,9 @@
"dhcp_dynamic_ip_found": "O seu sistema usa a configuração de endereço IP dinâmico para a interface <0>{{interfaceName}}</0>. Para usar o servidor DHCP, deve definir um endereço de IP estático. O seu endereço IP atual é <0> {{ipAddress}} </ 0>. AdGuard Home irá definir automaticamente este endereço IP como estático se pressionar o botão \"Ativar servidor DHCP\".", "dhcp_dynamic_ip_found": "O seu sistema usa a configuração de endereço IP dinâmico para a interface <0>{{interfaceName}}</0>. Para usar o servidor DHCP, deve definir um endereço de IP estático. O seu endereço IP atual é <0> {{ipAddress}} </ 0>. AdGuard Home irá definir automaticamente este endereço IP como estático se pressionar o botão \"Ativar servidor DHCP\".",
"dhcp_lease_added": "Concessão estática \"{{key}}\" adicionada com sucesso", "dhcp_lease_added": "Concessão estática \"{{key}}\" adicionada com sucesso",
"dhcp_lease_deleted": "Concessão estática \"{{key}}\" excluída com sucesso", "dhcp_lease_deleted": "Concessão estática \"{{key}}\" excluída com sucesso",
"dhcp_lease_updated": "Concessão estática \"{{key}}\" atualizada com sucesso",
"dhcp_new_static_lease": "Nova concessão estática", "dhcp_new_static_lease": "Nova concessão estática",
"dhcp_edit_static_lease": "Editar concessão estática",
"dhcp_static_leases_not_found": "Nenhuma concessão DHCP estática foi encontrada", "dhcp_static_leases_not_found": "Nenhuma concessão DHCP estática foi encontrada",
"dhcp_add_static_lease": "Adicionar nova concessão estática", "dhcp_add_static_lease": "Adicionar nova concessão estática",
"dhcp_reset_leases": "Repor todas as concessões", "dhcp_reset_leases": "Repor todas as concessões",
@ -283,6 +285,9 @@
"custom_ip": "IP Personalizado", "custom_ip": "IP Personalizado",
"blocking_ipv4": "A bloquear IPv4", "blocking_ipv4": "A bloquear IPv4",
"blocking_ipv6": "A bloquear IPv6", "blocking_ipv6": "A bloquear IPv6",
"blocked_response_ttl": "Resposta bloqueada TTL",
"blocked_response_ttl_desc": "Especifica por quantos segundos os clientes devem armazenar em cache uma resposta filtrada",
"form_enter_blocked_response_ttl": "Insira o TTL da resposta bloqueada (segundos)",
"dnscrypt": "DNSCrypt", "dnscrypt": "DNSCrypt",
"dns_over_https": "DNS-sobre-HTTPS", "dns_over_https": "DNS-sobre-HTTPS",
"dns_over_tls": "DNS-sobre-TLS", "dns_over_tls": "DNS-sobre-TLS",
@ -403,7 +408,7 @@
"encryption_issuer": "Emissor", "encryption_issuer": "Emissor",
"encryption_hostnames": "Nomes dos servidores", "encryption_hostnames": "Nomes dos servidores",
"encryption_reset": "Tem a certeza de que deseja repor a definição de criptografia?", "encryption_reset": "Tem a certeza de que deseja repor a definição de criptografia?",
"encryption_warning": "Aviso", "encryption_warning": "Cuidado",
"topline_expiring_certificate": "O seu certificado SSL está prestes a expirar. Atualize as suas <0>definições de criptografia</0>.", "topline_expiring_certificate": "O seu certificado SSL está prestes a expirar. Atualize as suas <0>definições de criptografia</0>.",
"topline_expired_certificate": "O seu certificado SSL está expirado. Atualize as suas <0>definições de criptografia</0>.", "topline_expired_certificate": "O seu certificado SSL está expirado. Atualize as suas <0>definições de criptografia</0>.",
"form_error_port_range": "Digite um numero de porta entre 80 e 65535", "form_error_port_range": "Digite um numero de porta entre 80 e 65535",
@ -654,7 +659,7 @@
"parental_control": "Controlo parental", "parental_control": "Controlo parental",
"safe_browsing": "Navegação segura", "safe_browsing": "Navegação segura",
"served_from_cache": "{{value}} <i>(servido do cache)</i>", "served_from_cache": "{{value}} <i>(servido do cache)</i>",
"form_error_password_length": "A palavra-passe deve ter pelo menos {{value}} caracteres", "form_error_password_length": "A palavra-passe deve ter {{min}} a {{max}} caracteres",
"anonymizer_notification": "<0>Observação:</0> A anonimização de IP está ativada. Você pode desativá-la em <1>Definições gerais</1>.", "anonymizer_notification": "<0>Observação:</0> A anonimização de IP está ativada. Você pode desativá-la em <1>Definições gerais</1>.",
"confirm_dns_cache_clear": "Tem certeza de que quer limpar a cache DNS?", "confirm_dns_cache_clear": "Tem certeza de que quer limpar a cache DNS?",
"cache_cleared": "O cache DNS foi apagado com sucesso", "cache_cleared": "O cache DNS foi apagado com sucesso",

View file

@ -73,7 +73,9 @@
"dhcp_dynamic_ip_found": "Sistemul dvs. folosește configurația dinamică a adreselor IP pentru interfața <0>{{interfaceName}}</0>. Pentru a utiliza serverul DHCP, trebuie setată o adresă IP statică. Adresa IP curentă este <0>{{ipAddress}}</0>. AdGuard Home o va configura automat ca adresă IP statică, dacă apăsați butonul \"Activați serverul DHCP\".", "dhcp_dynamic_ip_found": "Sistemul dvs. folosește configurația dinamică a adreselor IP pentru interfața <0>{{interfaceName}}</0>. Pentru a utiliza serverul DHCP, trebuie setată o adresă IP statică. Adresa IP curentă este <0>{{ipAddress}}</0>. AdGuard Home o va configura automat ca adresă IP statică, dacă apăsați butonul \"Activați serverul DHCP\".",
"dhcp_lease_added": "\"{{key}}\" statică închiriată adăugată cu succes", "dhcp_lease_added": "\"{{key}}\" statică închiriată adăugată cu succes",
"dhcp_lease_deleted": "\"{{key}}\" statică închiriată eliminată cu succes", "dhcp_lease_deleted": "\"{{key}}\" statică închiriată eliminată cu succes",
"dhcp_lease_updated": "\"{{key}}\" statică închiriată actualizată cu succes",
"dhcp_new_static_lease": "Închiriere statică nouă", "dhcp_new_static_lease": "Închiriere statică nouă",
"dhcp_edit_static_lease": "Editați închiriere statică",
"dhcp_static_leases_not_found": "Nu s-au găsit închirieri statice DHCP", "dhcp_static_leases_not_found": "Nu s-au găsit închirieri statice DHCP",
"dhcp_add_static_lease": "Adăugați închiriere statică", "dhcp_add_static_lease": "Adăugați închiriere statică",
"dhcp_reset_leases": "Resetați toate închirierile", "dhcp_reset_leases": "Resetați toate închirierile",
@ -283,6 +285,9 @@
"custom_ip": "IP personalizat", "custom_ip": "IP personalizat",
"blocking_ipv4": "Blocarea IPv4", "blocking_ipv4": "Blocarea IPv4",
"blocking_ipv6": "Blocarea IPv6", "blocking_ipv6": "Blocarea IPv6",
"blocked_response_ttl": "Răspuns blocat TTL",
"blocked_response_ttl_desc": "Specifică pentru câte secunde trebuie să memoreze clienții un răspuns filtrat",
"form_enter_blocked_response_ttl": "Introduceți răspunsul blocat TTL (secunde)",
"dnscrypt": "DNSCrypt", "dnscrypt": "DNSCrypt",
"dns_over_https": "DNS-over-HTTPS", "dns_over_https": "DNS-over-HTTPS",
"dns_over_tls": "DNS-over-TLS", "dns_over_tls": "DNS-over-TLS",
@ -403,7 +408,7 @@
"encryption_issuer": "Emitent", "encryption_issuer": "Emitent",
"encryption_hostnames": "Nume de host", "encryption_hostnames": "Nume de host",
"encryption_reset": "Sunteți sigur că doriți să resetați setările de criptare?", "encryption_reset": "Sunteți sigur că doriți să resetați setările de criptare?",
"encryption_warning": "Avertisment", "encryption_warning": "Atenție",
"topline_expiring_certificate": "Certificatul dvs. SSL este pe cale să expire. Actualizați <0>Setările de criptare</0>.", "topline_expiring_certificate": "Certificatul dvs. SSL este pe cale să expire. Actualizați <0>Setările de criptare</0>.",
"topline_expired_certificate": "Certificatul dvs. SSL a expirat. Actualizați <0>Setările de criptare</0>.", "topline_expired_certificate": "Certificatul dvs. SSL a expirat. Actualizați <0>Setările de criptare</0>.",
"form_error_port_range": "Introduceți valoarea portului între 80-65535", "form_error_port_range": "Introduceți valoarea portului între 80-65535",
@ -654,7 +659,7 @@
"parental_control": "Control Parental", "parental_control": "Control Parental",
"safe_browsing": "Navigare în siguranță", "safe_browsing": "Navigare în siguranță",
"served_from_cache": "{{value}} <i>(furnizat din cache)</i>", "served_from_cache": "{{value}} <i>(furnizat din cache)</i>",
"form_error_password_length": "Parola trebuie să aibă cel puțin {{value}} caractere", "form_error_password_length": "Parola trebuie să aibă între {{min}} și {{max}} caractere",
"anonymizer_notification": "<0>Nota:</0> Anonimizarea IP este activată. Puteți să o dezactivați în <1>Setări generale</1>.", "anonymizer_notification": "<0>Nota:</0> Anonimizarea IP este activată. Puteți să o dezactivați în <1>Setări generale</1>.",
"confirm_dns_cache_clear": "Sunteți sigur că doriți să ștergeți memoria cache DNS?", "confirm_dns_cache_clear": "Sunteți sigur că doriți să ștergeți memoria cache DNS?",
"cache_cleared": "Cache-ul DNS a fost golit cu succes", "cache_cleared": "Cache-ul DNS a fost golit cu succes",

View file

@ -73,7 +73,9 @@
"dhcp_dynamic_ip_found": "Ваша система использует динамический IP-адрес для интерфейса <0>{{interfaceName}}</0>. Чтобы использовать DHCP-сервер, необходимо установить статический IP-адрес. Ваш текущий IP-адрес <0>{{ipAddress}}</0>. Мы автоматически установим его как статический, если вы нажмёте кнопку «Включить DHCP-сервер».", "dhcp_dynamic_ip_found": "Ваша система использует динамический IP-адрес для интерфейса <0>{{interfaceName}}</0>. Чтобы использовать DHCP-сервер, необходимо установить статический IP-адрес. Ваш текущий IP-адрес <0>{{ipAddress}}</0>. Мы автоматически установим его как статический, если вы нажмёте кнопку «Включить DHCP-сервер».",
"dhcp_lease_added": "Статическая аренда «{{key}}» успешно добавлена", "dhcp_lease_added": "Статическая аренда «{{key}}» успешно добавлена",
"dhcp_lease_deleted": "Статическая аренда «{{key}}» успешно удалена", "dhcp_lease_deleted": "Статическая аренда «{{key}}» успешно удалена",
"dhcp_lease_updated": "Статическая аренда «{{key}}» успешно обновлена",
"dhcp_new_static_lease": "Новая статическая аренда", "dhcp_new_static_lease": "Новая статическая аренда",
"dhcp_edit_static_lease": "Редактирование статической аренды",
"dhcp_static_leases_not_found": "Не найдено статических аренд DHCP", "dhcp_static_leases_not_found": "Не найдено статических аренд DHCP",
"dhcp_add_static_lease": "Добавить статическую аренду", "dhcp_add_static_lease": "Добавить статическую аренду",
"dhcp_reset_leases": "Сбросить все аренды", "dhcp_reset_leases": "Сбросить все аренды",
@ -283,6 +285,9 @@
"custom_ip": "Свой IP", "custom_ip": "Свой IP",
"blocking_ipv4": "Блокировка IPv4", "blocking_ipv4": "Блокировка IPv4",
"blocking_ipv6": "Блокировка IPv6", "blocking_ipv6": "Блокировка IPv6",
"blocked_response_ttl": "TTL заблокированного ответа",
"blocked_response_ttl_desc": "Указывает, в течение скольких секунд клиенты должны кешировать отфильтрованный ответ",
"form_enter_blocked_response_ttl": "Введите TTL заблокированного ответа (в секундах)",
"dnscrypt": "DNSCrypt", "dnscrypt": "DNSCrypt",
"dns_over_https": "DNS-over-HTTPS", "dns_over_https": "DNS-over-HTTPS",
"dns_over_tls": "DNS-over-TLS", "dns_over_tls": "DNS-over-TLS",
@ -654,7 +659,7 @@
"parental_control": "Родительский контроль", "parental_control": "Родительский контроль",
"safe_browsing": "Безопасный интернет", "safe_browsing": "Безопасный интернет",
"served_from_cache": "{{value}} <i>(получено из кеша)</i>", "served_from_cache": "{{value}} <i>(получено из кеша)</i>",
"form_error_password_length": "Пароль должен быть длиной не меньше {{value}} символов", "form_error_password_length": "Пароль должен содержать от {{min}} до {{max}} символов",
"anonymizer_notification": "<0>Внимание:</0> включена анонимизация IP-адресов. Вы можете отключить её в разделе <1>Основные настройки</1>.", "anonymizer_notification": "<0>Внимание:</0> включена анонимизация IP-адресов. Вы можете отключить её в разделе <1>Основные настройки</1>.",
"confirm_dns_cache_clear": "Вы уверены, что хотите очистить кеш DNS?", "confirm_dns_cache_clear": "Вы уверены, что хотите очистить кеш DNS?",
"cache_cleared": "Кеш DNS успешно очищен", "cache_cleared": "Кеш DNS успешно очищен",

View file

@ -73,7 +73,9 @@
"dhcp_dynamic_ip_found": "Váš systém používa pre rozhranie <0>{{interfaceName}}</0> dynamickú konfiguráciu IP adresy. Aby bolo možné používať DHCP server, musí byť nastavená statická IP adresa. Vaša aktuálna IP adresa je <0>{{ipAddress}}</0>. ak Ak stlačíte tlačidlo \"Povoliť DHCP server\", AdGuard Home automaticky nastaví túto IP adresu ako statickú.", "dhcp_dynamic_ip_found": "Váš systém používa pre rozhranie <0>{{interfaceName}}</0> dynamickú konfiguráciu IP adresy. Aby bolo možné používať DHCP server, musí byť nastavená statická IP adresa. Vaša aktuálna IP adresa je <0>{{ipAddress}}</0>. ak Ak stlačíte tlačidlo \"Povoliť DHCP server\", AdGuard Home automaticky nastaví túto IP adresu ako statickú.",
"dhcp_lease_added": "Statický \"{{key}}\" prenájmu bol úspešne pridaný", "dhcp_lease_added": "Statický \"{{key}}\" prenájmu bol úspešne pridaný",
"dhcp_lease_deleted": "Statický \"{{key}}\" prenájmu bol úspešne vymazaný", "dhcp_lease_deleted": "Statický \"{{key}}\" prenájmu bol úspešne vymazaný",
"dhcp_lease_updated": "Statický prenájom \"{{key}}\" bol úspešne aktualizovaný",
"dhcp_new_static_lease": "Nový statický prenájom", "dhcp_new_static_lease": "Nový statický prenájom",
"dhcp_edit_static_lease": "Upraviť statický prenájom",
"dhcp_static_leases_not_found": "Nebol nájdený žiadny statický DHCP prenájom", "dhcp_static_leases_not_found": "Nebol nájdený žiadny statický DHCP prenájom",
"dhcp_add_static_lease": "Pridať statický prenájom", "dhcp_add_static_lease": "Pridať statický prenájom",
"dhcp_reset_leases": "Resetovať všetky prenájmy", "dhcp_reset_leases": "Resetovať všetky prenájmy",
@ -283,6 +285,9 @@
"custom_ip": "Vlastná IP adresa", "custom_ip": "Vlastná IP adresa",
"blocking_ipv4": "Blokovanie IPv4", "blocking_ipv4": "Blokovanie IPv4",
"blocking_ipv6": "Blokovanie IPv6", "blocking_ipv6": "Blokovanie IPv6",
"blocked_response_ttl": "Blokovaná odozva TTL",
"blocked_response_ttl_desc": "Určuje, na koľko sekúnd by mali klienti uložiť filtrovanú odozvu do vyrovnávacej pamäte",
"form_enter_blocked_response_ttl": "Zadajte TTL blokovanej odozve (sekundy)",
"dnscrypt": "DNSCrypt", "dnscrypt": "DNSCrypt",
"dns_over_https": "DNS-over-HTTPS", "dns_over_https": "DNS-over-HTTPS",
"dns_over_tls": "DNS-over-TLS", "dns_over_tls": "DNS-over-TLS",
@ -654,7 +659,7 @@
"parental_control": "Rodičovská kontrola", "parental_control": "Rodičovská kontrola",
"safe_browsing": "Bezpečné prehliadanie", "safe_browsing": "Bezpečné prehliadanie",
"served_from_cache": "{{value}} <i>(prevzatá z cache pamäte)</i>", "served_from_cache": "{{value}} <i>(prevzatá z cache pamäte)</i>",
"form_error_password_length": "Heslo musí mať dĺžku aspoň {{value}} znakov", "form_error_password_length": "Heslo musí mať od {{min}} do {{max}} znakov",
"anonymizer_notification": "<0>Poznámka:</0> Anonymizácia IP je zapnutá. Môžete ju vypnúť vo <1>Všeobecných nastaveniach</1>.", "anonymizer_notification": "<0>Poznámka:</0> Anonymizácia IP je zapnutá. Môžete ju vypnúť vo <1>Všeobecných nastaveniach</1>.",
"confirm_dns_cache_clear": "Naozaj chcete vymazať vyrovnávaciu pamäť DNS?", "confirm_dns_cache_clear": "Naozaj chcete vymazať vyrovnávaciu pamäť DNS?",
"cache_cleared": "Vyrovnávacia pamäť DNS bola úspešne vymazaná", "cache_cleared": "Vyrovnávacia pamäť DNS bola úspešne vymazaná",

View file

@ -73,7 +73,9 @@
"dhcp_dynamic_ip_found": "Vaš sistem uporablja dinamično nastavitev naslova IP kartice <0>{{interfaceName}}</0>. Za uporabo strežnika DHCP morate nastaviti statični naslov IP. Vaš trenutni naslov IP je<0>{{ipAddress}}</0>. AdGuard Home bo samodejno nastavil ta naslov IP kot statičen, če pritisnete gumb 'Omogoči strežnik DHCP'.", "dhcp_dynamic_ip_found": "Vaš sistem uporablja dinamično nastavitev naslova IP kartice <0>{{interfaceName}}</0>. Za uporabo strežnika DHCP morate nastaviti statični naslov IP. Vaš trenutni naslov IP je<0>{{ipAddress}}</0>. AdGuard Home bo samodejno nastavil ta naslov IP kot statičen, če pritisnete gumb 'Omogoči strežnik DHCP'.",
"dhcp_lease_added": "Statičen najem \"{{key}}\" je uspešno dodan", "dhcp_lease_added": "Statičen najem \"{{key}}\" je uspešno dodan",
"dhcp_lease_deleted": "Statičen najem \"{{key}}\" je uspešno izbrisan", "dhcp_lease_deleted": "Statičen najem \"{{key}}\" je uspešno izbrisan",
"dhcp_lease_updated": "Statični najem \"{{key}}\" je uspešno posodobljen",
"dhcp_new_static_lease": "Nov statični najem", "dhcp_new_static_lease": "Nov statični najem",
"dhcp_edit_static_lease": "Uredi statični najem",
"dhcp_static_leases_not_found": "Ni najdenih statičnih najemov DHCP", "dhcp_static_leases_not_found": "Ni najdenih statičnih najemov DHCP",
"dhcp_add_static_lease": "Dodaj statičen najem", "dhcp_add_static_lease": "Dodaj statičen najem",
"dhcp_reset_leases": "Ponastavi vse najeme", "dhcp_reset_leases": "Ponastavi vse najeme",
@ -283,6 +285,9 @@
"custom_ip": "IP po meri", "custom_ip": "IP po meri",
"blocking_ipv4": "Onemogočanje IPv4", "blocking_ipv4": "Onemogočanje IPv4",
"blocking_ipv6": "Onemogočanje IPv6", "blocking_ipv6": "Onemogočanje IPv6",
"blocked_response_ttl": "Zaviran odziv TTL",
"blocked_response_ttl_desc": "Določa, koliko sekund naj odjemalci predpomnijo filtrirane odgovore",
"form_enter_blocked_response_ttl": "Vnesite TTL zaviranega odgovora (sekunde)",
"dnscrypt": "DNSCrypt", "dnscrypt": "DNSCrypt",
"dns_over_https": "DNS-prek-HTTPS", "dns_over_https": "DNS-prek-HTTPS",
"dns_over_tls": "DNS-prek-TLS", "dns_over_tls": "DNS-prek-TLS",
@ -654,7 +659,7 @@
"parental_control": "Starševski nadzor", "parental_control": "Starševski nadzor",
"safe_browsing": "Varno brskanje", "safe_browsing": "Varno brskanje",
"served_from_cache": "{{value}} <i>(postreženo iz predpomnilnika)</i>", "served_from_cache": "{{value}} <i>(postreženo iz predpomnilnika)</i>",
"form_error_password_length": "Geslo mora vsebovati najmanj {{value}} znakov", "form_error_password_length": "Geslo mora vsebovati od {{min}} do {{max}} znakov",
"anonymizer_notification": "<0>Opomba:</0> Anonimizacija IP je omogočena. Onemogočite ga lahko v <1>Splošnih nastavitvah</1>.", "anonymizer_notification": "<0>Opomba:</0> Anonimizacija IP je omogočena. Onemogočite ga lahko v <1>Splošnih nastavitvah</1>.",
"confirm_dns_cache_clear": "Ali ste prepričani, da želite počistiti predpomnilnik DNS?", "confirm_dns_cache_clear": "Ali ste prepričani, da želite počistiti predpomnilnik DNS?",
"cache_cleared": "Predpomnilnik DNS je bil uspešno počiščen", "cache_cleared": "Predpomnilnik DNS je bil uspešno počiščen",

View file

@ -73,7 +73,9 @@
"dhcp_dynamic_ip_found": "Vaš sistem koristi dinamičku IP adresu za okruženje <0>{{interfaceName}}</0>. Kako biste koristili DHCP server, morate podesiti statičku IP adresu. Vaša trenutna IP adresa je <0>{{ipAddress}}</0>. Automatski ćemo podesiti ovu IP adresu kao statičku ako pritisnete Uključi DHCP dugme.", "dhcp_dynamic_ip_found": "Vaš sistem koristi dinamičku IP adresu za okruženje <0>{{interfaceName}}</0>. Kako biste koristili DHCP server, morate podesiti statičku IP adresu. Vaša trenutna IP adresa je <0>{{ipAddress}}</0>. Automatski ćemo podesiti ovu IP adresu kao statičku ako pritisnete Uključi DHCP dugme.",
"dhcp_lease_added": "Statičko iznajmljivanje \"{{key}}\" uspešno dodato", "dhcp_lease_added": "Statičko iznajmljivanje \"{{key}}\" uspešno dodato",
"dhcp_lease_deleted": "Statičko iznajmljivanje lease \"{{key}}\" uspešno izbrisano", "dhcp_lease_deleted": "Statičko iznajmljivanje lease \"{{key}}\" uspešno izbrisano",
"dhcp_lease_updated": "Statičko iznajmljivanje \"{{key}}\" ažurirano",
"dhcp_new_static_lease": "Novo statičko iznajmljivanje", "dhcp_new_static_lease": "Novo statičko iznajmljivanje",
"dhcp_edit_static_lease": "Uređivanje statičkog iznajmljivanja",
"dhcp_static_leases_not_found": "Nisu pronađena statička DHCP iznajmljivanja", "dhcp_static_leases_not_found": "Nisu pronađena statička DHCP iznajmljivanja",
"dhcp_add_static_lease": "Dodaj statičko iznajmljivanje", "dhcp_add_static_lease": "Dodaj statičko iznajmljivanje",
"dhcp_reset_leases": "Resetuj sva unajmljivanja", "dhcp_reset_leases": "Resetuj sva unajmljivanja",
@ -283,6 +285,9 @@
"custom_ip": "Prilagođeni IP", "custom_ip": "Prilagođeni IP",
"blocking_ipv4": "Blokiranje IPv4", "blocking_ipv4": "Blokiranje IPv4",
"blocking_ipv6": "Blokiranje IPv6", "blocking_ipv6": "Blokiranje IPv6",
"blocked_response_ttl": "TTL blokiranog odgovora",
"blocked_response_ttl_desc": "Određuje koliko sekundi klijenti treba da keširaju filtrirani odgovor",
"form_enter_blocked_response_ttl": "Unesite TTL blokiranog odgovora (sekunde)",
"dnscrypt": "DNSCrypt", "dnscrypt": "DNSCrypt",
"dns_over_https": "DNS-over-HTTPS", "dns_over_https": "DNS-over-HTTPS",
"dns_over_tls": "DNS-over-TLS", "dns_over_tls": "DNS-over-TLS",
@ -654,7 +659,7 @@
"parental_control": "Roditeljska kontrola", "parental_control": "Roditeljska kontrola",
"safe_browsing": "Sigurno pregledanje", "safe_browsing": "Sigurno pregledanje",
"served_from_cache": "{{value}} <i>(posluženo iz predmemorije)</i>", "served_from_cache": "{{value}} <i>(posluženo iz predmemorije)</i>",
"form_error_password_length": "Lozinka mora imati najmanje {{value}} znakova", "form_error_password_length": "Lozinka mora imati od {{min}} do {{max}} znakova",
"anonymizer_notification": "<0>Nota:</0> IP prepoznavanje je omogućeno. Možete ga onemogućiti u opštim <1>postavkama</1>.", "anonymizer_notification": "<0>Nota:</0> IP prepoznavanje je omogućeno. Možete ga onemogućiti u opštim <1>postavkama</1>.",
"confirm_dns_cache_clear": "Želite li zaista da obrišite DNS keš?", "confirm_dns_cache_clear": "Želite li zaista da obrišite DNS keš?",
"cache_cleared": "DNS keš je uspešno očišćen", "cache_cleared": "DNS keš je uspešno očišćen",

View file

@ -73,7 +73,9 @@
"dhcp_dynamic_ip_found": "Din enhet använder en dynamisk IP-adress för gränssnittet <0>{{interfaceName}}</0>. För att kunna använda DHCP-servern behövs en statisk IP-adress. Din nuvarande IP-adress är <0>{{ipAddress}}</0>. AdGuard Home kommer automatiskt att göra denna IP-adress statisk om du trycker på knappen \"Aktivera DHCP\".", "dhcp_dynamic_ip_found": "Din enhet använder en dynamisk IP-adress för gränssnittet <0>{{interfaceName}}</0>. För att kunna använda DHCP-servern behövs en statisk IP-adress. Din nuvarande IP-adress är <0>{{ipAddress}}</0>. AdGuard Home kommer automatiskt att göra denna IP-adress statisk om du trycker på knappen \"Aktivera DHCP\".",
"dhcp_lease_added": "Statisk lease \"{{key}}\" har lagts till", "dhcp_lease_added": "Statisk lease \"{{key}}\" har lagts till",
"dhcp_lease_deleted": "Statisk lease \"{{key}}\" har raderats", "dhcp_lease_deleted": "Statisk lease \"{{key}}\" har raderats",
"dhcp_lease_updated": "Statiskt lease \"{{key}}\" har uppdaterats",
"dhcp_new_static_lease": "Ny statisk lease", "dhcp_new_static_lease": "Ny statisk lease",
"dhcp_edit_static_lease": "Redigera statiskt lease",
"dhcp_static_leases_not_found": "Inga statiska DHCP-leases hittade", "dhcp_static_leases_not_found": "Inga statiska DHCP-leases hittade",
"dhcp_add_static_lease": "Lägg till statisk lease", "dhcp_add_static_lease": "Lägg till statisk lease",
"dhcp_reset_leases": "Återställ alla leasingavtal", "dhcp_reset_leases": "Återställ alla leasingavtal",
@ -174,6 +176,7 @@
"enabled_parental_toast": "Föräldrakontroll inkopplat", "enabled_parental_toast": "Föräldrakontroll inkopplat",
"disabled_safe_search_toast": "Säker webbsökning bortkopplat", "disabled_safe_search_toast": "Säker webbsökning bortkopplat",
"enabled_save_search_toast": "Säker webbsökning inkopplat", "enabled_save_search_toast": "Säker webbsökning inkopplat",
"updated_save_search_toast": "Inställningarna för Säker sökning uppdaterade",
"enabled_table_header": "Inkopplat", "enabled_table_header": "Inkopplat",
"name_table_header": "Namn", "name_table_header": "Namn",
"list_url_table_header": "Lista URL", "list_url_table_header": "Lista URL",
@ -282,6 +285,9 @@
"custom_ip": "Eget IP", "custom_ip": "Eget IP",
"blocking_ipv4": "Blockera IPv4", "blocking_ipv4": "Blockera IPv4",
"blocking_ipv6": "Blockera IPv6", "blocking_ipv6": "Blockera IPv6",
"blocked_response_ttl": "TTL för blockerat svar",
"blocked_response_ttl_desc": "Anger hur många sekunder klienterna ska cache ett filtrerat svar",
"form_enter_blocked_response_ttl": "Ange TTL för blockerat svar (sekunder)",
"dnscrypt": "DNSCrypt", "dnscrypt": "DNSCrypt",
"dns_over_https": "DNS-over-HTTPS", "dns_over_https": "DNS-over-HTTPS",
"dns_over_tls": "DNS-over-TLS", "dns_over_tls": "DNS-over-TLS",
@ -653,7 +659,7 @@
"parental_control": "Föräldrakontroll", "parental_control": "Föräldrakontroll",
"safe_browsing": "Säker surfning", "safe_browsing": "Säker surfning",
"served_from_cache": "{{value}} <i>(levereras från cache)</i>", "served_from_cache": "{{value}} <i>(levereras från cache)</i>",
"form_error_password_length": "Lösenordet måste vara minst {{value}} tecken långt", "form_error_password_length": "Lösenordet måste vara {{min}} till {{max}} tecken långt",
"anonymizer_notification": "<0>Observera:</0> IP-anonymisering är aktiverad. Du kan inaktivera den i <1>Allmänna inställningar</1>.", "anonymizer_notification": "<0>Observera:</0> IP-anonymisering är aktiverad. Du kan inaktivera den i <1>Allmänna inställningar</1>.",
"confirm_dns_cache_clear": "Är du säker på att du vill rensa DNS-cache?", "confirm_dns_cache_clear": "Är du säker på att du vill rensa DNS-cache?",
"cache_cleared": "DNS-cacheminnet har rensats", "cache_cleared": "DNS-cacheminnet har rensats",
@ -669,8 +675,19 @@
"disable_for_hours": "I {{count}} timme", "disable_for_hours": "I {{count}} timme",
"disable_for_hours_plural": "I {{count}} timmar", "disable_for_hours_plural": "I {{count}} timmar",
"disable_until_tomorrow": "Tills imorgon", "disable_until_tomorrow": "Tills imorgon",
"disable_notify_for_seconds": "Inaktivera skyddet i {{count}} sekund",
"disable_notify_for_seconds_plural": "Inaktivera skyddet i {{count}} sekunder",
"disable_notify_for_minutes": "Inaktivera skyddet i {{count}} minut",
"disable_notify_for_minutes_plural": "Inaktivera skyddet i {{count}} minuter",
"disable_notify_for_hours": "Inaktivera skyddet i {{count}} timme",
"disable_notify_for_hours_plural": "Inaktivera skyddet i {{count}} timmar",
"disable_notify_until_tomorrow": "Inaktivera skyddet tills imorgon",
"enable_protection_timer": "Skyddet kommer att aktiveras i {{time}}",
"custom_retention_input": "Ange retention i timmar",
"custom_rotation_input": "Ange rotation i timmar",
"protection_section_label": "Skydd", "protection_section_label": "Skydd",
"log_and_stats_section_label": "Förfrågningslogg och statistik", "log_and_stats_section_label": "Förfrågningslogg och statistik",
"ignore_query_log": "Ignorera den här klienten i frågeloggen",
"ignore_statistics": "Ignorera denna kund i statistiken", "ignore_statistics": "Ignorera denna kund i statistiken",
"schedule_services": "Pausa blockering av tjänst", "schedule_services": "Pausa blockering av tjänst",
"schedule_services_desc": "Konfigurera pausschemat för det tjänsteblockerande filtret", "schedule_services_desc": "Konfigurera pausschemat för det tjänsteblockerande filtret",

View file

@ -73,7 +73,9 @@
"dhcp_dynamic_ip_found": "Sisteminiz, <0>{{interfaceName}}</0> arayüzü için dinamik IP adresi yapılandırması kullanıyor. DHCP sunucusunu kullanmak için sabit bir IP adresi ayarlanmalıdır. Geçerli olan IP adresiniz <0>{{ipAddress}}</0>. \"DHCP sunucusunu etkinleştir\" düğmesine basarsanız, AdGuard Home bu IP adresini otomatik bir şekilde sabit olarak ayarlayacaktır.", "dhcp_dynamic_ip_found": "Sisteminiz, <0>{{interfaceName}}</0> arayüzü için dinamik IP adresi yapılandırması kullanıyor. DHCP sunucusunu kullanmak için sabit bir IP adresi ayarlanmalıdır. Geçerli olan IP adresiniz <0>{{ipAddress}}</0>. \"DHCP sunucusunu etkinleştir\" düğmesine basarsanız, AdGuard Home bu IP adresini otomatik bir şekilde sabit olarak ayarlayacaktır.",
"dhcp_lease_added": "Sabit kiralama \"{{key}}\" başarıyla eklendi", "dhcp_lease_added": "Sabit kiralama \"{{key}}\" başarıyla eklendi",
"dhcp_lease_deleted": "Sabit kiralama \"{{key}}\" başarıyla silindi", "dhcp_lease_deleted": "Sabit kiralama \"{{key}}\" başarıyla silindi",
"dhcp_lease_updated": "Statik kiralama \"{{key}}\" başarıyla güncellendi",
"dhcp_new_static_lease": "Yeni sabit kiralama", "dhcp_new_static_lease": "Yeni sabit kiralama",
"dhcp_edit_static_lease": "Statik kiralamayı düzenle",
"dhcp_static_leases_not_found": "Sabit DHCP kiralaması bulunamadı", "dhcp_static_leases_not_found": "Sabit DHCP kiralaması bulunamadı",
"dhcp_add_static_lease": "Sabit kiralama ekle", "dhcp_add_static_lease": "Sabit kiralama ekle",
"dhcp_reset_leases": "Tüm kiralamaları sıfırla", "dhcp_reset_leases": "Tüm kiralamaları sıfırla",
@ -90,7 +92,7 @@
"client_details": "İstemci ayrıntıları", "client_details": "İstemci ayrıntıları",
"details": "Ayrıntılar", "details": "Ayrıntılar",
"back": "Geri", "back": "Geri",
"dashboard": "Ana Sayfa", "dashboard": "Pano",
"settings": "Ayarlar", "settings": "Ayarlar",
"filters": "Filtreler", "filters": "Filtreler",
"filter": "Filtre", "filter": "Filtre",
@ -283,6 +285,9 @@
"custom_ip": "Özel IP", "custom_ip": "Özel IP",
"blocking_ipv4": "IPv4 engelleme", "blocking_ipv4": "IPv4 engelleme",
"blocking_ipv6": "IPv6 engelleme", "blocking_ipv6": "IPv6 engelleme",
"blocked_response_ttl": "Engellenen yanıt kullanım süresi",
"blocked_response_ttl_desc": "İstemcilerin filtrelenmiş bir yanıtı kaç saniye süreyle önbelleğe alması gerektiğini belirtir",
"form_enter_blocked_response_ttl": "Engellenen yanıt kullanım süresini girin (saniye)",
"dnscrypt": "DNSCrypt", "dnscrypt": "DNSCrypt",
"dns_over_https": "DNS-over-HTTPS", "dns_over_https": "DNS-over-HTTPS",
"dns_over_tls": "DNS-over-TLS", "dns_over_tls": "DNS-over-TLS",
@ -370,7 +375,7 @@
"install_devices_ios_list_4": "DNS alanına AdGuard Home sunucunuzun adreslerini girin.", "install_devices_ios_list_4": "DNS alanına AdGuard Home sunucunuzun adreslerini girin.",
"get_started": "Başlayın", "get_started": "Başlayın",
"next": "Sonraki", "next": "Sonraki",
"open_dashboard": "Ana Sayfayı Aç", "open_dashboard": "Panoyu Aç",
"install_saved": "Başarıyla kaydedildi", "install_saved": "Başarıyla kaydedildi",
"encryption_title": "Şifreleme", "encryption_title": "Şifreleme",
"encryption_desc": "DNS ve yönetici web arayüzü için şifreleme (HTTPS/TLS) desteği", "encryption_desc": "DNS ve yönetici web arayüzü için şifreleme (HTTPS/TLS) desteği",
@ -654,7 +659,7 @@
"parental_control": "Ebeveyn Denetimi", "parental_control": "Ebeveyn Denetimi",
"safe_browsing": "Güvenli Gezinti", "safe_browsing": "Güvenli Gezinti",
"served_from_cache": "{{value}} <i>(önbellekten kullanıldı)</i>", "served_from_cache": "{{value}} <i>(önbellekten kullanıldı)</i>",
"form_error_password_length": "Parola en az {{value}} karakter uzunluğunda olmalıdır", "form_error_password_length": "Parola {{min}} ila {{max}} karakter uzunluğunda olmalıdır",
"anonymizer_notification": "<0>Not:</0> IP anonimleştirme etkinleştirildi. Bunu <1>Genel ayarlardan</1> devre dışı bırakabilirsiniz.", "anonymizer_notification": "<0>Not:</0> IP anonimleştirme etkinleştirildi. Bunu <1>Genel ayarlardan</1> devre dışı bırakabilirsiniz.",
"confirm_dns_cache_clear": "DNS önbelleğini temizlemek istediğinizden emin misiniz?", "confirm_dns_cache_clear": "DNS önbelleğini temizlemek istediğinizden emin misiniz?",
"cache_cleared": "DNS önbelleği başarıyla temizlendi", "cache_cleared": "DNS önbelleği başarıyla temizlendi",

View file

@ -73,7 +73,9 @@
"dhcp_dynamic_ip_found": "Ваша система використовує конфігурацію з динамічною IP-адресою для інтерфейсу <0>{{interfaceName}}</0>. Для використання DHCP-сервера необхідно встановити статичну IP-адресу. Ваша поточна IP-адреса <0>{{ipAddress}}</0>. Ми автоматично встановимо цю IP-адресу як статичну, якщо ви натиснете кнопку «Увімкнути DHCP-сервер».", "dhcp_dynamic_ip_found": "Ваша система використовує конфігурацію з динамічною IP-адресою для інтерфейсу <0>{{interfaceName}}</0>. Для використання DHCP-сервера необхідно встановити статичну IP-адресу. Ваша поточна IP-адреса <0>{{ipAddress}}</0>. Ми автоматично встановимо цю IP-адресу як статичну, якщо ви натиснете кнопку «Увімкнути DHCP-сервер».",
"dhcp_lease_added": "Статичну оренду «{{key}}» успішно додано", "dhcp_lease_added": "Статичну оренду «{{key}}» успішно додано",
"dhcp_lease_deleted": "Статичну оренду «{{key}}» успішно видалено", "dhcp_lease_deleted": "Статичну оренду «{{key}}» успішно видалено",
"dhcp_lease_updated": "Статичну оренду \"{{key}}\" успішно оновлено",
"dhcp_new_static_lease": "Нова статична оренда", "dhcp_new_static_lease": "Нова статична оренда",
"dhcp_edit_static_lease": "Редагувати статичну оренду",
"dhcp_static_leases_not_found": "Не знайдено статичних оренд DHCP", "dhcp_static_leases_not_found": "Не знайдено статичних оренд DHCP",
"dhcp_add_static_lease": "Додати статичну оренду", "dhcp_add_static_lease": "Додати статичну оренду",
"dhcp_reset_leases": "Скинути всі аренди", "dhcp_reset_leases": "Скинути всі аренди",
@ -283,6 +285,9 @@
"custom_ip": "Власний IP", "custom_ip": "Власний IP",
"blocking_ipv4": "Блокування IPv4", "blocking_ipv4": "Блокування IPv4",
"blocking_ipv6": "Блокування IPv6", "blocking_ipv6": "Блокування IPv6",
"blocked_response_ttl": "TTL заблокованої відповіді",
"blocked_response_ttl_desc": "Вказує, скільки секунд клієнти повинні кешувати відфільтровану відповідь",
"form_enter_blocked_response_ttl": "Введіть TTL заблокованої відповіді (секунди)",
"dnscrypt": "DNSCrypt", "dnscrypt": "DNSCrypt",
"dns_over_https": "DNS-over-HTTPS", "dns_over_https": "DNS-over-HTTPS",
"dns_over_tls": "DNS-over-TLS", "dns_over_tls": "DNS-over-TLS",
@ -654,7 +659,7 @@
"parental_control": "Батьківський контроль", "parental_control": "Батьківський контроль",
"safe_browsing": "Безпечний перегляд", "safe_browsing": "Безпечний перегляд",
"served_from_cache": "{{value}} <i>(отримано з кешу)</i>", "served_from_cache": "{{value}} <i>(отримано з кешу)</i>",
"form_error_password_length": "Пароль мусить мати принаймні {{value}} символів", "form_error_password_length": "Пароль має містити від {{min}} до {{max}} символів",
"anonymizer_notification": "<0>Примітка:</0> IP-анонімізацію ввімкнено. Ви можете вимкнути його в <1>Загальні налаштування</1> .", "anonymizer_notification": "<0>Примітка:</0> IP-анонімізацію ввімкнено. Ви можете вимкнути його в <1>Загальні налаштування</1> .",
"confirm_dns_cache_clear": "Ви впевнені, що бажаєте очистити кеш DNS?", "confirm_dns_cache_clear": "Ви впевнені, що бажаєте очистити кеш DNS?",
"cache_cleared": "Кеш DNS успішно очищено", "cache_cleared": "Кеш DNS успішно очищено",

View file

@ -73,7 +73,9 @@
"dhcp_dynamic_ip_found": "Hệ thống của bạn sử dụng cấu hình địa chỉ IP động cho giao diện <0>{{interfaceName}}</0>. Để sử dụng máy chủ DHCP, phải đặt địa chỉ IP tĩnh. Địa chỉ IP hiện tại của bạn là <0>{{ipAddress}}</0>. Chúng tôi sẽ tự động đặt địa chỉ IP này thành tĩnh nếu bạn nhấn nút Bật DHCP.", "dhcp_dynamic_ip_found": "Hệ thống của bạn sử dụng cấu hình địa chỉ IP động cho giao diện <0>{{interfaceName}}</0>. Để sử dụng máy chủ DHCP, phải đặt địa chỉ IP tĩnh. Địa chỉ IP hiện tại của bạn là <0>{{ipAddress}}</0>. Chúng tôi sẽ tự động đặt địa chỉ IP này thành tĩnh nếu bạn nhấn nút Bật DHCP.",
"dhcp_lease_added": "Cho thuê tĩnh \"{{key}}\" đã được thêm thành công", "dhcp_lease_added": "Cho thuê tĩnh \"{{key}}\" đã được thêm thành công",
"dhcp_lease_deleted": "Cho thuê tĩnh \"{{key}}\" đã xóa thành công", "dhcp_lease_deleted": "Cho thuê tĩnh \"{{key}}\" đã xóa thành công",
"dhcp_lease_updated": "Cho thuê tĩnh \"{{key}}\" được cập nhật thành công",
"dhcp_new_static_lease": "Cho thuê tĩnh mới", "dhcp_new_static_lease": "Cho thuê tĩnh mới",
"dhcp_edit_static_lease": "Chỉnh sửa hợp đồng thuê tĩnh",
"dhcp_static_leases_not_found": "Không tìm thấy DHCP cho thuê tĩnh", "dhcp_static_leases_not_found": "Không tìm thấy DHCP cho thuê tĩnh",
"dhcp_add_static_lease": "Thêm thuê tĩnh", "dhcp_add_static_lease": "Thêm thuê tĩnh",
"dhcp_reset_leases": "Đặt lại tất cả các hợp đồng thuê", "dhcp_reset_leases": "Đặt lại tất cả các hợp đồng thuê",
@ -283,6 +285,9 @@
"custom_ip": "IP tuỳ chỉnh", "custom_ip": "IP tuỳ chỉnh",
"blocking_ipv4": "Chặn IPv4", "blocking_ipv4": "Chặn IPv4",
"blocking_ipv6": "Chặn IPv6", "blocking_ipv6": "Chặn IPv6",
"blocked_response_ttl": "Chặn phản hồi TTL",
"blocked_response_ttl_desc": "Chỉ định trong bao nhiêu giây máy khách sẽ lưu vào bộ đệm một phản hồi đã được lọc",
"form_enter_blocked_response_ttl": "Nhập phản hồi bị chặn TTL (giây)",
"dnscrypt": "DNSCrypt", "dnscrypt": "DNSCrypt",
"dns_over_https": "DNS-over-HTTPS", "dns_over_https": "DNS-over-HTTPS",
"dns_over_tls": "DNS-over-TLS", "dns_over_tls": "DNS-over-TLS",
@ -654,7 +659,7 @@
"parental_control": "Quản lý của phụ huynh", "parental_control": "Quản lý của phụ huynh",
"safe_browsing": "Duyệt web an toàn", "safe_browsing": "Duyệt web an toàn",
"served_from_cache": "{{value}} <i>(được phục vụ từ bộ nhớ cache)</i>", "served_from_cache": "{{value}} <i>(được phục vụ từ bộ nhớ cache)</i>",
"form_error_password_length": "Mật khẩu phải có ít nhất {{value}} ký tự", "form_error_password_length": "Mật khẩu phải dài từ {{min}} đến {{max}} ký tự",
"anonymizer_notification": "<0> Lưu ý:</0> Tính năng ẩn danh IP được bật. Bạn có thể tắt nó trong <1> Cài đặt chung</1>.", "anonymizer_notification": "<0> Lưu ý:</0> Tính năng ẩn danh IP được bật. Bạn có thể tắt nó trong <1> Cài đặt chung</1>.",
"confirm_dns_cache_clear": "Bạn có chắc chắn muốn xóa bộ đệm ẩn DNS không?", "confirm_dns_cache_clear": "Bạn có chắc chắn muốn xóa bộ đệm ẩn DNS không?",
"cache_cleared": "Đã xóa thành công bộ đệm DNS", "cache_cleared": "Đã xóa thành công bộ đệm DNS",

View file

@ -7,9 +7,9 @@
"load_balancing": "负载均衡", "load_balancing": "负载均衡",
"load_balancing_desc": "一次查询一台服务器。AdGuard Home 将使用加权随机算法来选择服务器,以便更常使用最快的服务器。", "load_balancing_desc": "一次查询一台服务器。AdGuard Home 将使用加权随机算法来选择服务器,以便更常使用最快的服务器。",
"bootstrap_dns": "Bootstrap DNS 服务器", "bootstrap_dns": "Bootstrap DNS 服务器",
"bootstrap_dns_desc": "DNS 服务器的 IP 地址,用于解析指定为上游的 DoH/DoT 解析器的 IP 地址。不允许评论。", "bootstrap_dns_desc": "DNS 服务器的 IP 地址,用于解析指定为上游的 DoH/DoT 解析器的 IP 地址。不允许添加注释。",
"fallback_dns_title": "后备 DNS 服务器", "fallback_dns_title": "后备 DNS 服务器",
"fallback_dns_desc": "当上游 DNS 服务器没有响应时使用的后备 DNS 服务器列表。语法与上面的主要上游字段相同。", "fallback_dns_desc": "当上游 DNS 服务器没有响应时使用的后备 DNS 服务器列表。语法与上面的主要上游字段相同。",
"fallback_dns_placeholder": "每行输入一个后备 DNS 服务器", "fallback_dns_placeholder": "每行输入一个后备 DNS 服务器",
"local_ptr_title": "私人反向 DNS 服务器", "local_ptr_title": "私人反向 DNS 服务器",
"local_ptr_desc": "AdGuard Home 用于本地 PTR 查询的 DNS 服务器。这些服务器将使用反向 DNS 解析具有私人 IP 地址的客户机的主机名,比如 \"192.168.12.34\"。如果没有设置,除非是 AdGuard Home 里设置的地址AdGuard Home 都将自动使用您的操作系统的默认 DNS 解析器。", "local_ptr_desc": "AdGuard Home 用于本地 PTR 查询的 DNS 服务器。这些服务器将使用反向 DNS 解析具有私人 IP 地址的客户机的主机名,比如 \"192.168.12.34\"。如果没有设置,除非是 AdGuard Home 里设置的地址AdGuard Home 都将自动使用您的操作系统的默认 DNS 解析器。",
@ -58,7 +58,7 @@
"dhcp_form_subnet_input": "子网掩码", "dhcp_form_subnet_input": "子网掩码",
"dhcp_form_range_title": "IP 地址范围", "dhcp_form_range_title": "IP 地址范围",
"dhcp_form_range_start": "起始 IP 地址", "dhcp_form_range_start": "起始 IP 地址",
"dhcp_form_range_end": "末尾 IP 地址", "dhcp_form_range_end": "结束 IP 地址",
"dhcp_form_lease_title": "DHCP 租约时间(秒)", "dhcp_form_lease_title": "DHCP 租约时间(秒)",
"dhcp_form_lease_input": "租期", "dhcp_form_lease_input": "租期",
"dhcp_interface_select": "选择 DHCP 接口", "dhcp_interface_select": "选择 DHCP 接口",
@ -67,13 +67,15 @@
"ip": "IP 地址", "ip": "IP 地址",
"dhcp_table_hostname": "主机名", "dhcp_table_hostname": "主机名",
"dhcp_table_expires": "到期", "dhcp_table_expires": "到期",
"dhcp_warning": "如果想要启用内置的 DHCP 服务器,请确保在当前网络中没有其它起作用的 DHCP 服务器。否则,此操作可能会破坏已连接设备的网络连接!", "dhcp_warning": "如果用户想要启用内置的 DHCP 服务器,请确保在当前网络中没有其它起作用的 DHCP 服务器。否则,此操作可能会中断已连接设备的网络连接!",
"dhcp_error": "AdGuard Home 无法确定在当前网络中是否存在其它 DHCP 服务器", "dhcp_error": "AdGuard Home 无法确定在当前网络中是否存在其它 DHCP 服务器",
"dhcp_static_ip_error": "要使用 DHCP 服务器,则必须设置静态 IP 地址。AdGuard Home 无法确定此网络接口是否已被配置为使用静态 IP 地址。请手动为此网络接口设置静态 IP 地址。", "dhcp_static_ip_error": "要使用 DHCP 服务器,则必须设置静态 IP 地址。AdGuard Home 无法确定此网络接口是否已被配置为使用静态 IP 地址。请手动为此网络接口设置静态 IP 地址。",
"dhcp_dynamic_ip_found": "您的系统对网络接口 <0>{{interfaceName}}</0> 使用了动态 IP 地址配置。要使用 DHCP 服务器,则必须对此网络接口使用静态 IP 地址配置。此网络接口当前的 IP 地址为 <0>{{ipAddress}}</0>。如您点击「启用 DHCP 服务器」按钮AdGuard Home 将自动修改此网络接口以使用静态 IP 地址。", "dhcp_dynamic_ip_found": "您的系统对网络接口 <0>{{interfaceName}}</0> 使用了动态 IP 地址配置。要使用 DHCP 服务器,则必须对此网络接口使用静态 IP 地址配置。此网络接口当前的 IP 地址为 <0>{{ipAddress}}</0>。如您点击「启用 DHCP 服务器」按钮AdGuard Home 将自动修改此网络接口以使用静态 IP 地址。",
"dhcp_lease_added": "静态租约 \"{{key}}\" 已成功添加", "dhcp_lease_added": "静态租约 \"{{key}}\" 已成功添加",
"dhcp_lease_deleted": "静态租约 \"{{key}}\" 已成功删除", "dhcp_lease_deleted": "静态租约 \"{{key}}\" 已成功删除",
"dhcp_lease_updated": "静态租约 “{{key}}” 已成功更新",
"dhcp_new_static_lease": "新建静态租约", "dhcp_new_static_lease": "新建静态租约",
"dhcp_edit_static_lease": "编辑静态租约",
"dhcp_static_leases_not_found": "未找到 DHCP 静态租约", "dhcp_static_leases_not_found": "未找到 DHCP 静态租约",
"dhcp_add_static_lease": "添加静态租约", "dhcp_add_static_lease": "添加静态租约",
"dhcp_reset_leases": "重置所有租约", "dhcp_reset_leases": "重置所有租约",
@ -112,7 +114,7 @@
"disable_protection": "禁用保护", "disable_protection": "禁用保护",
"disabled_protection": "保护已禁用", "disabled_protection": "保护已禁用",
"refresh_statics": "刷新统计数据", "refresh_statics": "刷新统计数据",
"dns_query": "DNS查询", "dns_query": "DNS 查询",
"blocked_by": "<0>已被过滤器拦截</0>", "blocked_by": "<0>已被过滤器拦截</0>",
"stats_malware_phishing": "被拦截的恶意/钓鱼网站", "stats_malware_phishing": "被拦截的恶意/钓鱼网站",
"stats_adult": "被拦截的成人网站", "stats_adult": "被拦截的成人网站",
@ -154,7 +156,7 @@
"dns_settings": "DNS 设置", "dns_settings": "DNS 设置",
"dns_blocklists": "DNS 黑名单", "dns_blocklists": "DNS 黑名单",
"dns_allowlists": "DNS 白名单", "dns_allowlists": "DNS 白名单",
"dns_blocklists_desc": "AdGuard Home将阻止匹配DNS拦截清单的域名", "dns_blocklists_desc": "AdGuard Home 将阻止 DNS 黑名单里的域名",
"dns_allowlists_desc": "来自 DNS 白名单的域名将被允许,即使它们位于任意黑名单中也是如此。", "dns_allowlists_desc": "来自 DNS 白名单的域名将被允许,即使它们位于任意黑名单中也是如此。",
"custom_filtering_rules": "自定义过滤规则", "custom_filtering_rules": "自定义过滤规则",
"encryption_settings": "加密设置", "encryption_settings": "加密设置",
@ -162,7 +164,7 @@
"upstream_dns": "上游 DNS 服务器", "upstream_dns": "上游 DNS 服务器",
"upstream_dns_help": "每行输入一个服务器地址。<a>了解更多</a>关于配置上游 DNS 服务器的内容", "upstream_dns_help": "每行输入一个服务器地址。<a>了解更多</a>关于配置上游 DNS 服务器的内容",
"upstream_dns_configured_in_file": "配置路径{{path}}", "upstream_dns_configured_in_file": "配置路径{{path}}",
"test_upstream_btn": "测试上游 DNS", "test_upstream_btn": "测试上游",
"upstreams": "上游服务器", "upstreams": "上游服务器",
"upstream": "上游服务器", "upstream": "上游服务器",
"apply_btn": "应用", "apply_btn": "应用",
@ -207,9 +209,9 @@
"custom_filter_rules": "自定义过滤器规则", "custom_filter_rules": "自定义过滤器规则",
"custom_filter_rules_hint": "请确保每行只输入一条规则。你可以输入符合 adblock 语法或 Hosts 语法的规则。", "custom_filter_rules_hint": "请确保每行只输入一条规则。你可以输入符合 adblock 语法或 Hosts 语法的规则。",
"system_host_files": "系统主机文件", "system_host_files": "系统主机文件",
"examples_title": "例", "examples_title": "例",
"example_meaning_filter_block": "阻止 example.org 域名及其所有子域名;", "example_meaning_filter_block": "阻止 example.org 域名及其所有子域名;",
"example_meaning_filter_whitelist": "解除 example.org 及其所有子域名的封锁", "example_meaning_filter_whitelist": "解除对 example.org 及其所有子域名的拦截",
"example_meaning_host_block": "对 example.org不包括它的子域名以 127.0.0.1 作为响应;", "example_meaning_host_block": "对 example.org不包括它的子域名以 127.0.0.1 作为响应;",
"example_comment": "! 这是一行注释。", "example_comment": "! 这是一行注释。",
"example_comment_meaning": "只是一条注释;", "example_comment_meaning": "只是一条注释;",
@ -220,7 +222,7 @@
"example_upstream_udp": "常规 DNS通过 UDP、主机名", "example_upstream_udp": "常规 DNS通过 UDP、主机名",
"example_upstream_dot": "加密 <0>DNS-over-TLS</0>", "example_upstream_dot": "加密 <0>DNS-over-TLS</0>",
"example_upstream_doh": "加密 <0>DNS-over-HTTPS</0>", "example_upstream_doh": "加密 <0>DNS-over-HTTPS</0>",
"example_upstream_doh3": "带有强制 <0>HTTP/3</0> 的加密 DNS-over-HTTPS并且没有回退到 HTTP/2 或更低版本;", "example_upstream_doh3": "带有强制 <0>HTTP/3</0> 的加密 DNS-over-HTTPS不会回退到 HTTP/2 或更低版本;",
"example_upstream_doq": "加密 <0>DNS-over-QUIC</0>", "example_upstream_doq": "加密 <0>DNS-over-QUIC</0>",
"example_upstream_sdns": "<1>DNSCrypt</1> 的 <0>DNS Stamps</0> 或者 <2>DNS-over-HTTPS</2> 解析器;", "example_upstream_sdns": "<1>DNSCrypt</1> 的 <0>DNS Stamps</0> 或者 <2>DNS-over-HTTPS</2> 解析器;",
"example_upstream_tcp": "常规 DNS基于 TCP ", "example_upstream_tcp": "常规 DNS基于 TCP ",
@ -260,7 +262,7 @@
"rule_added_to_custom_filtering_toast": "规则已添加到自定义过滤规则列表中 {{rule}}", "rule_added_to_custom_filtering_toast": "规则已添加到自定义过滤规则列表中 {{rule}}",
"query_log_response_status": "状态: {{value}}", "query_log_response_status": "状态: {{value}}",
"query_log_filtered": "被 {{filter}} 过滤", "query_log_filtered": "被 {{filter}} 过滤",
"query_log_confirm_clear": "确定想要清除全部查询日志吗?", "query_log_confirm_clear": "确定想要清除全部查询日志吗?",
"query_log_cleared": "查询日志已成功清除", "query_log_cleared": "查询日志已成功清除",
"query_log_updated": "已成功更新查询日志", "query_log_updated": "已成功更新查询日志",
"query_log_clear": "清除查询日志", "query_log_clear": "清除查询日志",
@ -268,12 +270,12 @@
"query_log_enable": "启用日志", "query_log_enable": "启用日志",
"query_log_configuration": "日志配置", "query_log_configuration": "日志配置",
"query_log_disabled": "查询日志已禁用,在<0>这些设置</0>中能配置它们", "query_log_disabled": "查询日志已禁用,在<0>这些设置</0>中能配置它们",
"query_log_strict_search": "使用双引号进行严谨搜索", "query_log_strict_search": "使用双引号进行精确搜索",
"query_log_retention_confirm": "确定要更改查询记录保留时间吗?如果减少时间间隔数值,某些数据可能会丢失", "query_log_retention_confirm": "确定要更改查询记录保留时间吗?如果减少时间间隔数值,可能会丢失某些数据",
"anonymize_client_ip": "匿名化客户端IP", "anonymize_client_ip": "匿名化客户端 IP",
"anonymize_client_ip_desc": "不要在日志和统计信息中保存客户端的完整 IP 地址", "anonymize_client_ip_desc": "不要在日志和统计信息中保存客户端的完整 IP 地址",
"dns_config": "DNS 服务配置", "dns_config": "DNS 服务配置",
"dns_cache_config": "DNS缓存配置", "dns_cache_config": "DNS 缓存配置",
"dns_cache_config_desc": "您可以在此处配置 DNS 缓存", "dns_cache_config_desc": "您可以在此处配置 DNS 缓存",
"blocking_mode": "拦截模式", "blocking_mode": "拦截模式",
"default": "默认", "default": "默认",
@ -283,6 +285,9 @@
"custom_ip": "自定义 IP", "custom_ip": "自定义 IP",
"blocking_ipv4": "拦截 IPv4", "blocking_ipv4": "拦截 IPv4",
"blocking_ipv6": "拦截 IPv6", "blocking_ipv6": "拦截 IPv6",
"blocked_response_ttl": "屏蔽的 TTL 应答",
"blocked_response_ttl_desc": "指定客户端应缓存已过滤响应的秒数",
"form_enter_blocked_response_ttl": "输入拦截的 TTL 应答(秒)",
"dnscrypt": "DNSCrypt", "dnscrypt": "DNSCrypt",
"dns_over_https": "DNS-over-HTTPS", "dns_over_https": "DNS-over-HTTPS",
"dns_over_tls": "DNS-over-TLS", "dns_over_tls": "DNS-over-TLS",
@ -293,7 +298,7 @@
"download_mobileconfig_doh": "下载适用于 DNS-over-HTTPS 的 .mobileconfig", "download_mobileconfig_doh": "下载适用于 DNS-over-HTTPS 的 .mobileconfig",
"download_mobileconfig_dot": "下载适用于 DNS-over-TLS 的 .mobileconfig", "download_mobileconfig_dot": "下载适用于 DNS-over-TLS 的 .mobileconfig",
"download_mobileconfig": "下载配置文件", "download_mobileconfig": "下载配置文件",
"plain_dns": "无加密DNS", "plain_dns": "无加密 DNS",
"form_enter_rate_limit": "输入限制速率", "form_enter_rate_limit": "输入限制速率",
"rate_limit": "速度限制", "rate_limit": "速度限制",
"edns_enable": "启用 EDNS 客户端子网", "edns_enable": "启用 EDNS 客户端子网",
@ -303,11 +308,11 @@
"rate_limit_desc": "每个客户端每秒钟查询次数的限制。设置为 0 意味着不限制。", "rate_limit_desc": "每个客户端每秒钟查询次数的限制。设置为 0 意味着不限制。",
"blocking_ipv4_desc": "拦截 A 记录请求返回的 IP 地址", "blocking_ipv4_desc": "拦截 A 记录请求返回的 IP 地址",
"blocking_ipv6_desc": "拦截 AAAA 记录请求返回的 IP 地址", "blocking_ipv6_desc": "拦截 AAAA 记录请求返回的 IP 地址",
"blocking_mode_default": "默认:被 Adblock 规则拦截时反应为零 IP 地址A记录0.0.0.0AAAA记录::);被/etc/hosts 规则拦截时反应为规则中指定 IP 地址", "blocking_mode_default": "默认:被 Adblock 规则拦截时反应为零 IP 地址A记录0.0.0.0AAAA记录::);被 /etc/hosts 规则拦截时反应为规则中指定 IP 地址",
"blocking_mode_refused": "REFUSED以 REFUSED 码响应请求", "blocking_mode_refused": "REFUSED以 REFUSED 码响应请求",
"blocking_mode_nxdomain": "NXDOMAINNXDOMAIN码响应", "blocking_mode_nxdomain": "NXDOMAIN NXDOMAIN 码响应",
"blocking_mode_null_ip": "空 IP以零 IP 地址响应A 记录 0.0.0.0AAAA 记录 ::", "blocking_mode_null_ip": "空 IP以零 IP 地址响应A 记录 0.0.0.0AAAA 记录 ::",
"blocking_mode_custom_ip": "自定IP以手动设置的IP地址响应", "blocking_mode_custom_ip": "自定IP以手动设置的 IP 地址响应",
"theme_auto": "自动", "theme_auto": "自动",
"theme_light": "浅色主题", "theme_light": "浅色主题",
"theme_dark": "深色主题", "theme_dark": "深色主题",
@ -331,7 +336,7 @@
"install_settings_dns_desc": "您将需要使用以下地址来设置您的设备或路由器的 DNS 服务器:", "install_settings_dns_desc": "您将需要使用以下地址来设置您的设备或路由器的 DNS 服务器:",
"install_settings_all_interfaces": "所有接口", "install_settings_all_interfaces": "所有接口",
"install_auth_title": "身份认证", "install_auth_title": "身份认证",
"install_auth_desc": "需要对 AdGuard Home 的网页管理界面配置密码认证。即使该 AdGuard Home 只能通过您的本地网络访问,避免 AdGuard Home 被不受限制地访问依旧十分重要。", "install_auth_desc": "需要对 AdGuard Home 的网页管理界面配置密码认证。即使 AdGuard Home 只能通过本地网络访问,为它添加访问限制依旧十分重要。",
"install_auth_username": "用户名", "install_auth_username": "用户名",
"install_auth_password": "密码", "install_auth_password": "密码",
"install_auth_confirm": "确认密码", "install_auth_confirm": "确认密码",
@ -459,12 +464,12 @@
"access_disallowed_title": "不允许的客户端", "access_disallowed_title": "不允许的客户端",
"access_disallowed_desc": "CIDR、IP 地址或<a>客户端 ID</a> 的列表。如果已配置,则 AdGuard Home 将丢弃来自这些客户端的请求。如果允许的客户端已配置,此字段将会被忽略。", "access_disallowed_desc": "CIDR、IP 地址或<a>客户端 ID</a> 的列表。如果已配置,则 AdGuard Home 将丢弃来自这些客户端的请求。如果允许的客户端已配置,此字段将会被忽略。",
"access_blocked_title": "不允许的域名", "access_blocked_title": "不允许的域名",
"access_blocked_desc": "不要将此功能与过滤器混淆。AdGuard Home 将排除匹配这些域的 DNS 查询并且这些查询将不会在查询日志中显示。在此可以明确指定域名、通配符wildcard和网址过滤的规则例如 \"example.org\"、\"*.example.org\" 或 \"||example.org^\"。", "access_blocked_desc": "不要将此功能与过滤器混淆。AdGuard Home 将排除匹配这些的 DNS 查询并且这些查询将不会在查询日志中显示。在此可以明确指定域名、通配符wildcard和网址过滤的规则例如 \"example.org\"、\"*.example.org\" 或 \"||example.org^\"。",
"access_settings_saved": "访问设置保存成功", "access_settings_saved": "访问设置保存成功",
"updates_checked": "AdGuard Home 的新版本现在可用", "updates_checked": "AdGuard Home 的新版本现在可用",
"updates_version_equal": "AdGuard Home已经是最新版本", "updates_version_equal": "AdGuard Home已经是最新版本",
"check_updates_now": "立即检查更新", "check_updates_now": "立即检查更新",
"version_request_error": "检查更新失败。请检查您的因特网连接。", "version_request_error": "检查更新失败。请检查互联网连接。",
"dns_privacy": "DNS 隐私", "dns_privacy": "DNS 隐私",
"setup_dns_privacy_1": "<0>DNS-over-TLS:</0> 使用 <1>{{address}}</1> 字符串。", "setup_dns_privacy_1": "<0>DNS-over-TLS:</0> 使用 <1>{{address}}</1> 字符串。",
"setup_dns_privacy_2": "<0>DNS-over-HTTPS:</0> 使用 <1>{{address}}</1> 字符串。", "setup_dns_privacy_2": "<0>DNS-over-HTTPS:</0> 使用 <1>{{address}}</1> 字符串。",
@ -492,7 +497,7 @@
"rewrite_confirm_delete": "您确定要删除 \"{{key}}\" 的 DNS 重写?", "rewrite_confirm_delete": "您确定要删除 \"{{key}}\" 的 DNS 重写?",
"rewrite_desc": "可以轻松地为特定域名配置自定义 DNS 响应。", "rewrite_desc": "可以轻松地为特定域名配置自定义 DNS 响应。",
"rewrite_applied": " 重定向规则已应用", "rewrite_applied": " 重定向规则已应用",
"rewrite_hosts_applied": "根据hosts文件规则已被重写", "rewrite_hosts_applied": "根据 hosts 文件规则已被重写",
"dns_rewrites": "DNS 重写", "dns_rewrites": "DNS 重写",
"form_domain": "输入域", "form_domain": "输入域",
"form_answer": "输入 IP 地址或域名", "form_answer": "输入 IP 地址或域名",
@ -531,12 +536,12 @@
"statistics_retention": "统计保留", "statistics_retention": "统计保留",
"statistics_retention_desc": "如果您减少该间隔的数值, 某些数据可能会丢失", "statistics_retention_desc": "如果您减少该间隔的数值, 某些数据可能会丢失",
"statistics_clear": " 清除统计数据", "statistics_clear": " 清除统计数据",
"statistics_clear_confirm": "确定要清除统计数据?", "statistics_clear_confirm": "确定要清除统计数据?",
"statistics_retention_confirm": "您确定要更改统计记录保留时间吗? 如果您减少间隔时间的值, 某些数据可能会丢失。", "statistics_retention_confirm": "您确定要更改统计记录保留时间吗? 如果您减少间隔时间的值, 某些数据可能会丢失。",
"statistics_cleared": "统计数据已成功清除", "statistics_cleared": "统计数据已成功清除",
"statistics_enable": "启用统计数据", "statistics_enable": "启用统计数据",
"ignore_domains": "忽略的域(以换行符分隔)", "ignore_domains": "忽略的(以换行符分隔)",
"ignore_domains_title": "被忽略的域", "ignore_domains_title": "被忽略的",
"ignore_domains_desc_stats": "匹配这些规则的查询不在统计信息", "ignore_domains_desc_stats": "匹配这些规则的查询不在统计信息",
"ignore_domains_desc_query": "匹配这些规则的查询不在查询日志", "ignore_domains_desc_query": "匹配这些规则的查询不在查询日志",
"interval_hours": "{{count}} 小时", "interval_hours": "{{count}} 小时",
@ -559,24 +564,24 @@
"network": "网络", "network": "网络",
"descr": "描述", "descr": "描述",
"whois": "WHOIS", "whois": "WHOIS",
"filtering_rules_learn_more": "<0>了解更多</0>关于创建自己的hosts清单。", "filtering_rules_learn_more": "<0>了解更多</0>关于创建自己的 hosts 清单。",
"blocked_by_response": "因响应的CNAME或IP被屏蔽", "blocked_by_response": "因响应的 CNAME 或 IP 被屏蔽",
"blocked_by_cname_or_ip": "按CNAME或IP拦截", "blocked_by_cname_or_ip": "按 CNAME 或 IP 拦截",
"try_again": "重试", "try_again": "重试",
"domain_desc": "输入您要重写的域名或通配符。", "domain_desc": "输入您要重写的域名或通配符。",
"example_rewrite_domain": "仅重写此域名的响应。", "example_rewrite_domain": "仅重写此域名的响应。",
"example_rewrite_wildcard": "重写所有<0>example.org</0> 子域的响应。", "example_rewrite_wildcard": "重写所有<0>example.org</0> 子域的响应。",
"rewrite_ip_address": "IP地址:在 A或AAAA响应中使用这个IP", "rewrite_ip_address": "IP 地址:在 A 或 AAAA 响应中使用这个 IP",
"rewrite_domain_name": "域名 添加一个CNAME记录", "rewrite_domain_name": "域名:添加一个 CNAME 记录",
"rewrite_A": "<0>A</0>:特殊值,保持来自上游的<0>A</0>记录", "rewrite_A": "<0>A</0>:特殊值,保持来自上游的<0>A</0>记录",
"rewrite_AAAA": "<0>AAAA</0>:特殊值,保持来自上游的<0>AAAA</0>记录", "rewrite_AAAA": "<0>AAAA</0>:特殊值,保持来自上游的<0>AAAA</0>记录",
"disable_ipv6": "禁用 IPv6 地址的解析", "disable_ipv6": "禁用 IPv6 地址的解析",
"disable_ipv6_desc": "删除对 IPv6 地址(类型 AAAA的所有 DNS 查询,并从 HTTPS 响应中删除 IPv6 提示。", "disable_ipv6_desc": "丢弃对 IPv6 地址(类型 AAAA的所有 DNS 查询,并从 HTTPS 响应中删除 IPv6 相关的信息。",
"fastest_addr": "最快的 IP 地址", "fastest_addr": "最快的 IP 地址",
"fastest_addr_desc": "查询所有 DNS 服务器并返回所有响应中速度最快的 IP 地址。因 AdGuard Home 必须等待全部 DNS 服务器均有所回应,因而会降低 DNS 查询的速度,但同时,此举将会改善总体的连接。", "fastest_addr_desc": "查询所有 DNS 服务器并返回所有响应中速度最快的 IP 地址。因 AdGuard Home 必须等待全部 DNS 服务器响应,这会降低 DNS 查询的速度,但此举将会在总体上改善连接速度。",
"autofix_warning_text": "若您单击「修复」AdGuardHome 将会配置您的系统以使用 AdGuardHome 的 DNS 服务器。", "autofix_warning_text": "若您单击「修复」AdGuard Home 将会配置您的系统以使用 AdGuard Home 的 DNS 服务器。",
"autofix_warning_list": "其将会进行如下工作:<0>停用系统DNSStubListener</0><0>设置DNS服务器地址为127.0.0.1</0><0>将/etc/resolv.conf的符号链接目标替换为/run/systemd/resolv/resolv.conf</0><0>停止DNSStubListener重新加载系统解析服务</0>", "autofix_warning_list": "其将会进行如下工作:<0>停用系统DNSStubListener</0><0>设置DNS服务器地址为127.0.0.1</0><0>将/etc/resolv.conf的符号链接目标替换为/run/systemd/resolv/resolv.conf</0><0>停止DNSStubListener重新加载系统解析服务</0>",
"autofix_warning_result": "因此,默认情况下所有来自系统的DNS请求都将由AdGuardHome处理。", "autofix_warning_result": "因此,默认情况下所有来自系统的 DNS 请求都将由 AdGuard Home 处理。",
"tags_title": "标签", "tags_title": "标签",
"tags_desc": "您可以选择与客户端对应的标记。标签可以包含在过滤规则中,并允许您更准确地应用它们。<0>了解更多</0>。", "tags_desc": "您可以选择与客户端对应的标记。标签可以包含在过滤规则中,并允许您更准确地应用它们。<0>了解更多</0>。",
"form_select_tags": "选择客户端标签", "form_select_tags": "选择客户端标签",
@ -594,22 +599,22 @@
"check_service": "服务名称:{{service}}", "check_service": "服务名称:{{service}}",
"service_name": "服务名称", "service_name": "服务名称",
"check_not_found": "未在您的筛选列表中找到", "check_not_found": "未在您的筛选列表中找到",
"client_confirm_block": "确定要阻止客户端 \"{{ip}}\"?", "client_confirm_block": "确定要阻止客户端 \"{{ip}}\"?",
"client_confirm_unblock": "您确定要解除对客户端 \"{{ip}}\" 的封锁吗?", "client_confirm_unblock": "您确定要解除对客户端 \"{{ip}}\" 的封锁吗?",
"client_blocked": "客户端 \"{{ip}}\" 被成功拦截", "client_blocked": "客户端 \"{{ip}}\" 被成功拦截",
"client_unblocked": "成功解锁客户端 \"{{ip}}\"", "client_unblocked": "成功解除对 \"{{ip}}\" 客户端的封锁",
"static_ip": "静态IP地址", "static_ip": "静态IP地址",
"static_ip_desc": "AdGuard Home 是一个服务器,所以它需要一个静态IP地址才能正常工作。否则在某些情况下你的路由器可能会给这个设备分配一个不同的IP地址。", "static_ip_desc": "AdGuard Home 是一个服务器,所以它需要一个静态 IP 地址才能正常工作。否则,在某些情况下,路由器可能会给这个设备分配一个不同的 IP 地址。",
"set_static_ip": "设置一个静态IP", "set_static_ip": "设置一个静态 IP",
"install_static_ok": "好消息!静态 IP 地址已经配置", "install_static_ok": "好消息!静态 IP 地址已经配置",
"install_static_error": "AdGuard Home 无法为这个网络接口自动配置它。请参阅如何手动完成此操作的说明。", "install_static_error": "AdGuard Home 无法为这个网络接口自动配置它。请参阅如何手动完成此操作的说明。",
"install_static_configure": "AdGuard Home 检测到一个动态 IP 地址 <0>{{ip}}</0> 被使用。您想把它作为您的静态地址吗?", "install_static_configure": "AdGuard Home 检测到一个动态 IP 地址 <0>{{ip}}</0> 被使用。您想把它作为您的静态地址吗?",
"confirm_static_ip": "AdGuard Home 将把{{ip}} 配置为您的静态IP地址。您想要继续吗", "confirm_static_ip": "AdGuard Home 将把 {{ip}} 配置为静态 IP 地址。您想要继续吗?",
"list_updated": "{{count}} 列表已更新", "list_updated": "{{count}} 列表已更新",
"list_updated_plural": "{{count}} 条列表已更新", "list_updated_plural": "{{count}} 条列表已更新",
"dnssec_enable": "启用DNSSEC", "dnssec_enable": "启用 DNSSEC",
"dnssec_enable_desc": "在发出 DNS 查询中设置 DNSSEC 标志并检查结果(需要启用 DNSSEC 的解析器)。", "dnssec_enable_desc": "在发出 DNS 查询中设置 DNSSEC 标志并检查结果(需要启用 DNSSEC 的解析器)。",
"validated_with_dnssec": "通过DNSSEC验证", "validated_with_dnssec": "通过 DNSSEC 验证",
"all_queries": "所有查询记录", "all_queries": "所有查询记录",
"show_blocked_responses": "已拦截", "show_blocked_responses": "已拦截",
"show_whitelisted_responses": "已列入白名单", "show_whitelisted_responses": "已列入白名单",
@ -624,9 +629,9 @@
"blocklist": "黑名单", "blocklist": "黑名单",
"milliseconds_abbreviation": "毫秒", "milliseconds_abbreviation": "毫秒",
"cache_size": "缓存大小", "cache_size": "缓存大小",
"cache_size_desc": "DNS 缓存大小(单位:字节)。要关闭缓存,请留空。", "cache_size_desc": "DNS 缓存大小(单位:字节)。要关闭缓存,请留空。",
"cache_ttl_min_override": "覆盖最小TTL值", "cache_ttl_min_override": "覆盖最小 TTL 值",
"cache_ttl_max_override": "覆盖最大TTL值", "cache_ttl_max_override": "覆盖最大 TTL 值",
"enter_cache_size": "输入缓存大小(字节)", "enter_cache_size": "输入缓存大小(字节)",
"enter_cache_ttl_min_override": "输入最小 TTL 值(秒)", "enter_cache_ttl_min_override": "输入最小 TTL 值(秒)",
"enter_cache_ttl_max_override": "输入最大 TTL 值(秒)", "enter_cache_ttl_max_override": "输入最大 TTL 值(秒)",
@ -654,7 +659,7 @@
"parental_control": "家长控制", "parental_control": "家长控制",
"safe_browsing": "安全浏览", "safe_browsing": "安全浏览",
"served_from_cache": "{{value}}<i>(由缓存提供)</i>", "served_from_cache": "{{value}}<i>(由缓存提供)</i>",
"form_error_password_length": "密码必须至少有 {{value}} 个字符", "form_error_password_length": "密码长度必须为 {{min}} 到 {{max}} 个字符",
"anonymizer_notification": "<0>注意:</0> IP 匿名化已启用。您可以在<1>常规设置</1>中禁用它。", "anonymizer_notification": "<0>注意:</0> IP 匿名化已启用。您可以在<1>常规设置</1>中禁用它。",
"confirm_dns_cache_clear": "您确定要清除 DNS 缓存吗?", "confirm_dns_cache_clear": "您确定要清除 DNS 缓存吗?",
"cache_cleared": "已成功清除 DNS 缓存", "cache_cleared": "已成功清除 DNS 缓存",
@ -685,9 +690,9 @@
"ignore_query_log": "在查询日志中忽略此客户端", "ignore_query_log": "在查询日志中忽略此客户端",
"ignore_statistics": "在统计数据中忽略此客户端", "ignore_statistics": "在统计数据中忽略此客户端",
"schedule_services": "暂停服务拦截", "schedule_services": "暂停服务拦截",
"schedule_services_desc": "配置服务拦截过滤器的暂停计划", "schedule_services_desc": "配置服务拦截过滤器的暂停计划",
"schedule_services_desc_client": "为此客户端配置服务拦截过滤器的暂停计划", "schedule_services_desc_client": "为此客户端配置服务拦截过滤器的暂停计划",
"schedule_desc": "安排已拦截服务禁用的时间", "schedule_desc": "安排暂停拦截服务的时间段",
"schedule_invalid_select": "开始时间必须在结束时间之前", "schedule_invalid_select": "开始时间必须在结束时间之前",
"schedule_select_days": "选择日", "schedule_select_days": "选择日",
"schedule_timezone": "选择时区", "schedule_timezone": "选择时区",

View file

@ -73,7 +73,9 @@
"dhcp_dynamic_ip_found": "您的系統使用動態 IP 位址配置供介面 <0>{{interfaceName}}</0>。為了使用動態主機設定協定DHCP伺服器靜態 IP 位址必須被設定。您現行的 IP 位址為 <0>{{ipAddress}}</0>。如果您按\"啟用 DHCP 伺服器\" 按鈕AdGuard Home 將自動地設定此 IP 位址作為靜態。", "dhcp_dynamic_ip_found": "您的系統使用動態 IP 位址配置供介面 <0>{{interfaceName}}</0>。為了使用動態主機設定協定DHCP伺服器靜態 IP 位址必須被設定。您現行的 IP 位址為 <0>{{ipAddress}}</0>。如果您按\"啟用 DHCP 伺服器\" 按鈕AdGuard Home 將自動地設定此 IP 位址作為靜態。",
"dhcp_lease_added": "靜態租約 \"{{key}}\" 被成功地加入", "dhcp_lease_added": "靜態租約 \"{{key}}\" 被成功地加入",
"dhcp_lease_deleted": "靜態租約 \"{{key}}\" 被成功地刪除", "dhcp_lease_deleted": "靜態租約 \"{{key}}\" 被成功地刪除",
"dhcp_lease_updated": "「{{key}}」的靜態租約已成功更新",
"dhcp_new_static_lease": "新的靜態租約", "dhcp_new_static_lease": "新的靜態租約",
"dhcp_edit_static_lease": "編輯靜態租約",
"dhcp_static_leases_not_found": "無已發現之動態主機設定協定DHCP靜態租約", "dhcp_static_leases_not_found": "無已發現之動態主機設定協定DHCP靜態租約",
"dhcp_add_static_lease": "新增靜態租約", "dhcp_add_static_lease": "新增靜態租約",
"dhcp_reset_leases": "重置所有的租約", "dhcp_reset_leases": "重置所有的租約",
@ -283,6 +285,9 @@
"custom_ip": "自訂的 IP", "custom_ip": "自訂的 IP",
"blocking_ipv4": "封鎖 IPv4", "blocking_ipv4": "封鎖 IPv4",
"blocking_ipv6": "封鎖 IPv6", "blocking_ipv6": "封鎖 IPv6",
"blocked_response_ttl": "已封鎖的回應之存活時間TTL",
"blocked_response_ttl_desc": "指定客戶端應將過濾後的回應存入快取的秒數",
"form_enter_blocked_response_ttl": "請輸入已封鎖回應的存活時間(秒)",
"dnscrypt": "DNSCrypt", "dnscrypt": "DNSCrypt",
"dns_over_https": "DNS-over-HTTPS", "dns_over_https": "DNS-over-HTTPS",
"dns_over_tls": "DNS-over-TLS", "dns_over_tls": "DNS-over-TLS",
@ -654,7 +659,7 @@
"parental_control": "家長控制", "parental_control": "家長控制",
"safe_browsing": "安全瀏覽", "safe_browsing": "安全瀏覽",
"served_from_cache": "{{value}} <i>(由快取提供)</i>", "served_from_cache": "{{value}} <i>(由快取提供)</i>",
"form_error_password_length": "密碼必須為至少長 {{value}} 個字元", "form_error_password_length": "密碼長度必須為 {{min}} 到 {{max}} 個字符",
"anonymizer_notification": "<0>注意:</0>IP 匿名化被啟用。您可在<1>一般設定</1>中禁用它。", "anonymizer_notification": "<0>注意:</0>IP 匿名化被啟用。您可在<1>一般設定</1>中禁用它。",
"confirm_dns_cache_clear": "您確定您想要清除 DNS 快取嗎?", "confirm_dns_cache_clear": "您確定您想要清除 DNS 快取嗎?",
"cache_cleared": "DNS 快取被成功地清除", "cache_cleared": "DNS 快取被成功地清除",

View file

@ -660,6 +660,24 @@ export const removeStaticLease = (config) => async (dispatch) => {
} }
}; };
export const updateStaticLeaseRequest = createAction('UPDATE_STATIC_LEASE_REQUEST');
export const updateStaticLeaseFailure = createAction('UPDATE_STATIC_LEASE_FAILURE');
export const updateStaticLeaseSuccess = createAction('UPDATE_STATIC_LEASE_SUCCESS');
export const updateStaticLease = (config) => async (dispatch) => {
dispatch(updateStaticLeaseRequest());
try {
await apiClient.updateStaticLease(config);
dispatch(updateStaticLeaseSuccess(config));
dispatch(addSuccessToast(i18next.t('dhcp_lease_updated', { key: config.hostname || config.ip })));
dispatch(toggleLeaseModal());
dispatch(getDhcpStatus());
} catch (error) {
dispatch(addErrorToast({ error }));
dispatch(updateStaticLeaseFailure());
}
};
export const removeToast = createAction('REMOVE_TOAST'); export const removeToast = createAction('REMOVE_TOAST');
export const toggleBlocking = ( export const toggleBlocking = (

View file

@ -274,6 +274,8 @@ class Api {
DHCP_REMOVE_STATIC_LEASE = { path: 'dhcp/remove_static_lease', method: 'POST' }; DHCP_REMOVE_STATIC_LEASE = { path: 'dhcp/remove_static_lease', method: 'POST' };
DHCP_UPDATE_STATIC_LEASE = { path: 'dhcp/update_static_lease', method: 'POST' };
DHCP_RESET = { path: 'dhcp/reset', method: 'POST' }; DHCP_RESET = { path: 'dhcp/reset', method: 'POST' };
DHCP_LEASES_RESET = { path: 'dhcp/reset_leases', method: 'POST' }; DHCP_LEASES_RESET = { path: 'dhcp/reset_leases', method: 'POST' };
@ -320,6 +322,14 @@ class Api {
return this.makeRequest(path, method, parameters); return this.makeRequest(path, method, parameters);
} }
updateStaticLease(config) {
const { path, method } = this.DHCP_UPDATE_STATIC_LEASE;
const parameters = {
data: config,
};
return this.makeRequest(path, method, parameters);
}
resetDhcp() { resetDhcp() {
const { path, method } = this.DHCP_RESET; const { path, method } = this.DHCP_RESET;
return this.makeRequest(path, method); return this.makeRequest(path, method);

View file

@ -4,6 +4,7 @@ import ReactTable from 'react-table';
import { withTranslation } from 'react-i18next'; import { withTranslation } from 'react-i18next';
import { sortIp } from '../../../helpers/helpers'; import { sortIp } from '../../../helpers/helpers';
import { MODAL_TYPE } from '../../../helpers/constants'; import { MODAL_TYPE } from '../../../helpers/constants';
import { LocalStorageHelper, LOCAL_STORAGE_KEYS } from '../../../helpers/localStorageHelper';
class Table extends Component { class Table extends Component {
cellWrap = ({ value }) => ( cellWrap = ({ value }) => (
@ -85,7 +86,8 @@ class Table extends Component {
loading={processing || processingAdd || processingDelete} loading={processing || processingAdd || processingDelete}
className="-striped -highlight card-table-overflow" className="-striped -highlight card-table-overflow"
showPagination showPagination
defaultPageSize={10} defaultPageSize={LocalStorageHelper.getItem(LOCAL_STORAGE_KEYS.REWRITES_PAGE_SIZE) || 10}
onPageSizeChange={(size) => LocalStorageHelper.setItem(LOCAL_STORAGE_KEYS.REWRITES_PAGE_SIZE, size)}
minRows={5} minRows={5}
ofText="/" ofText="/"
previousText={t('previous_btn')} previousText={t('previous_btn')}

View file

@ -6,6 +6,7 @@ import CellWrap from '../ui/CellWrap';
import { MODAL_TYPE } from '../../helpers/constants'; import { MODAL_TYPE } from '../../helpers/constants';
import { formatDetailedDateTime } from '../../helpers/helpers'; import { formatDetailedDateTime } from '../../helpers/helpers';
import { isValidAbsolutePath } from '../../helpers/form'; import { isValidAbsolutePath } from '../../helpers/form';
import { LOCAL_STORAGE_KEYS, LocalStorageHelper } from '../../helpers/localStorageHelper';
class Table extends Component { class Table extends Component {
getDateCell = (row) => CellWrap(row, formatDetailedDateTime); getDateCell = (row) => CellWrap(row, formatDetailedDateTime);
@ -126,12 +127,17 @@ class Table extends Component {
loading, filters, t, whitelist, loading, filters, t, whitelist,
} = this.props; } = this.props;
const localStorageKey = whitelist
? LOCAL_STORAGE_KEYS.ALLOWLIST_PAGE_SIZE
: LOCAL_STORAGE_KEYS.BLOCKLIST_PAGE_SIZE;
return ( return (
<ReactTable <ReactTable
data={filters} data={filters}
columns={this.columns} columns={this.columns}
showPagination showPagination
defaultPageSize={10} defaultPageSize={LocalStorageHelper.getItem(localStorageKey) || 10}
onPageSizeChange={(size) => LocalStorageHelper.setItem(localStorageKey, size)}
loading={loading} loading={loading}
minRows={6} minRows={6}
ofText="/" ofText="/"

View file

@ -9,6 +9,7 @@ import CellWrap from '../../ui/CellWrap';
import whoisCell from './whoisCell'; import whoisCell from './whoisCell';
import LogsSearchLink from '../../ui/LogsSearchLink'; import LogsSearchLink from '../../ui/LogsSearchLink';
import { sortIp } from '../../../helpers/helpers'; import { sortIp } from '../../../helpers/helpers';
import { LocalStorageHelper, LOCAL_STORAGE_KEYS } from '../../../helpers/localStorageHelper';
const COLUMN_MIN_WIDTH = 200; const COLUMN_MIN_WIDTH = 200;
@ -85,7 +86,10 @@ class AutoClients extends Component {
]} ]}
className="-striped -highlight card-table-overflow" className="-striped -highlight card-table-overflow"
showPagination showPagination
defaultPageSize={10} defaultPageSize={LocalStorageHelper.getItem(LOCAL_STORAGE_KEYS.AUTO_CLIENTS_PAGE_SIZE) || 10}
onPageSizeChange={(size) => (
LocalStorageHelper.setItem(LOCAL_STORAGE_KEYS.AUTO_CLIENTS_PAGE_SIZE, size)
)}
minRows={5} minRows={5}
ofText="/" ofText="/"
previousText={t('previous_btn')} previousText={t('previous_btn')}

View file

@ -19,6 +19,7 @@ import Card from '../../../ui/Card';
import CellWrap from '../../../ui/CellWrap'; import CellWrap from '../../../ui/CellWrap';
import LogsSearchLink from '../../../ui/LogsSearchLink'; import LogsSearchLink from '../../../ui/LogsSearchLink';
import Modal from '../Modal'; import Modal from '../Modal';
import { LocalStorageHelper, LOCAL_STORAGE_KEYS } from '../../../../helpers/localStorageHelper';
const ClientsTable = ({ const ClientsTable = ({
clients, clients,
@ -342,7 +343,10 @@ const ClientsTable = ({
]} ]}
className="-striped -highlight card-table-overflow" className="-striped -highlight card-table-overflow"
showPagination showPagination
defaultPageSize={10} defaultPageSize={LocalStorageHelper.getItem(LOCAL_STORAGE_KEYS.CLIENTS_PAGE_SIZE) || 10}
onPageSizeChange={(size) => (
LocalStorageHelper.setItem(LOCAL_STORAGE_KEYS.CLIENTS_PAGE_SIZE, size)
)}
minRows={5} minRows={5}
ofText="/" ofText="/"
previousText={t('previous_btn')} previousText={t('previous_btn')}

View file

@ -22,6 +22,7 @@ const Form = ({
submitting, submitting,
processingAdding, processingAdding,
cidr, cidr,
isEdit,
}) => { }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const dispatch = useDispatch(); const dispatch = useDispatch();
@ -45,6 +46,7 @@ const Form = ({
placeholder={t('form_enter_mac')} placeholder={t('form_enter_mac')}
normalize={normalizeMac} normalize={normalizeMac}
validate={[validateRequiredValue, validateMac]} validate={[validateRequiredValue, validateMac]}
disabled={isEdit}
/> />
</div> </div>
<div className="form__group"> <div className="form__group">
@ -112,6 +114,7 @@ Form.propTypes = {
submitting: PropTypes.bool.isRequired, submitting: PropTypes.bool.isRequired,
processingAdding: PropTypes.bool.isRequired, processingAdding: PropTypes.bool.isRequired,
cidr: PropTypes.string.isRequired, cidr: PropTypes.string.isRequired,
isEdit: PropTypes.bool,
}; };
export default reduxForm({ form: FORM_NAME.LEASE })(Form); export default reduxForm({ form: FORM_NAME.LEASE })(Form);

View file

@ -5,9 +5,11 @@ import ReactModal from 'react-modal';
import { shallowEqual, useDispatch, useSelector } from 'react-redux'; import { shallowEqual, useDispatch, useSelector } from 'react-redux';
import Form from './Form'; import Form from './Form';
import { toggleLeaseModal } from '../../../../actions'; import { toggleLeaseModal } from '../../../../actions';
import { MODAL_TYPE } from '../../../../helpers/constants';
const Modal = ({ const Modal = ({
isModalOpen, isModalOpen,
modalType,
handleSubmit, handleSubmit,
processingAdding, processingAdding,
cidr, cidr,
@ -32,7 +34,11 @@ const Modal = ({
<div className="modal-content"> <div className="modal-content">
<div className="modal-header"> <div className="modal-header">
<h4 className="modal-title"> <h4 className="modal-title">
{modalType === MODAL_TYPE.EDIT_LEASE ? (
<Trans>dhcp_edit_static_lease</Trans>
) : (
<Trans>dhcp_new_static_lease</Trans> <Trans>dhcp_new_static_lease</Trans>
)}
</h4> </h4>
<button type="button" className="close" onClick={toggleModal}> <button type="button" className="close" onClick={toggleModal}>
<span className="sr-only">Close</span> <span className="sr-only">Close</span>
@ -53,6 +59,7 @@ const Modal = ({
cidr={cidr} cidr={cidr}
rangeStart={rangeStart} rangeStart={rangeStart}
rangeEnd={rangeEnd} rangeEnd={rangeEnd}
isEdit={modalType === MODAL_TYPE.EDIT_LEASE}
/> />
</div> </div>
</ReactModal> </ReactModal>
@ -61,6 +68,7 @@ const Modal = ({
Modal.propTypes = { Modal.propTypes = {
isModalOpen: PropTypes.bool.isRequired, isModalOpen: PropTypes.bool.isRequired,
modalType: PropTypes.string.isRequired,
handleSubmit: PropTypes.func.isRequired, handleSubmit: PropTypes.func.isRequired,
processingAdding: PropTypes.bool.isRequired, processingAdding: PropTypes.bool.isRequired,
cidr: PropTypes.string.isRequired, cidr: PropTypes.string.isRequired,

View file

@ -3,10 +3,15 @@ import PropTypes from 'prop-types';
import ReactTable from 'react-table'; import ReactTable from 'react-table';
import { Trans, useTranslation } from 'react-i18next'; import { Trans, useTranslation } from 'react-i18next';
import { useDispatch } from 'react-redux'; import { useDispatch } from 'react-redux';
import { LEASES_TABLE_DEFAULT_PAGE_SIZE } from '../../../../helpers/constants'; import { LEASES_TABLE_DEFAULT_PAGE_SIZE, MODAL_TYPE } from '../../../../helpers/constants';
import { sortIp } from '../../../../helpers/helpers'; import { sortIp } from '../../../../helpers/helpers';
import Modal from './Modal'; import Modal from './Modal';
import { addStaticLease, removeStaticLease } from '../../../../actions'; import {
addStaticLease,
removeStaticLease,
toggleLeaseModal,
updateStaticLease,
} from '../../../../actions';
const cellWrap = ({ value }) => ( const cellWrap = ({ value }) => (
<div className="logs__row o-hidden"> <div className="logs__row o-hidden">
@ -18,8 +23,10 @@ const cellWrap = ({ value }) => (
const StaticLeases = ({ const StaticLeases = ({
isModalOpen, isModalOpen,
modalType,
processingAdding, processingAdding,
processingDeleting, processingDeleting,
processingUpdating,
staticLeases, staticLeases,
cidr, cidr,
rangeStart, rangeStart,
@ -31,7 +38,12 @@ const StaticLeases = ({
const handleSubmit = (data) => { const handleSubmit = (data) => {
const { mac, ip, hostname } = data; const { mac, ip, hostname } = data;
if (modalType === MODAL_TYPE.EDIT_LEASE) {
dispatch(updateStaticLease({ mac, ip, hostname }));
} else {
dispatch(addStaticLease({ mac, ip, hostname })); dispatch(addStaticLease({ mac, ip, hostname }));
}
}; };
const handleDelete = (ip, mac, hostname = '') => { const handleDelete = (ip, mac, hostname = '') => {
@ -80,19 +92,35 @@ const StaticLeases = ({
Cell: (row) => { Cell: (row) => {
const { ip, mac, hostname } = row.original; const { ip, mac, hostname } = row.original;
return <div className="logs__row logs__row--center"> return (
<div className="logs__row logs__row--center">
<button <button
type="button" type="button"
className="btn btn-icon btn-icon--green btn-outline-secondary btn-sm" className="btn btn-icon btn-outline-primary btn-sm mr-2"
title={t('delete_table_action')} onClick={() => dispatch(toggleLeaseModal({
disabled={processingDeleting} type: MODAL_TYPE.EDIT_LEASE,
onClick={() => handleDelete(ip, mac, hostname)} config: { ip, mac, hostname },
}))}
disabled={processingUpdating}
title={t('edit_table_action')}
> >
<svg className="icons"> <svg className="icons icon12">
<use xlinkHref="#delete"/> <use xlinkHref="#edit" />
</svg> </svg>
</button> </button>
</div>; <button
type="button"
className="btn btn-icon btn-outline-secondary btn-sm"
onClick={() => handleDelete(ip, mac, hostname)}
disabled={processingDeleting}
title={t('delete_table_action')}
>
<svg className="icons icon12">
<use xlinkHref="#delete" />
</svg>
</button>
</div>
);
}, },
}, },
]} ]}
@ -105,6 +133,7 @@ const StaticLeases = ({
/> />
<Modal <Modal
isModalOpen={isModalOpen} isModalOpen={isModalOpen}
modalType={modalType}
handleSubmit={handleSubmit} handleSubmit={handleSubmit}
processingAdding={processingAdding} processingAdding={processingAdding}
cidr={cidr} cidr={cidr}
@ -119,8 +148,10 @@ const StaticLeases = ({
StaticLeases.propTypes = { StaticLeases.propTypes = {
staticLeases: PropTypes.array.isRequired, staticLeases: PropTypes.array.isRequired,
isModalOpen: PropTypes.bool.isRequired, isModalOpen: PropTypes.bool.isRequired,
modalType: PropTypes.string.isRequired,
processingAdding: PropTypes.bool.isRequired, processingAdding: PropTypes.bool.isRequired,
processingDeleting: PropTypes.bool.isRequired, processingDeleting: PropTypes.bool.isRequired,
processingUpdating: PropTypes.bool.isRequired,
cidr: PropTypes.string.isRequired, cidr: PropTypes.string.isRequired,
rangeStart: PropTypes.string, rangeStart: PropTypes.string,
rangeEnd: PropTypes.string, rangeEnd: PropTypes.string,

View file

@ -49,6 +49,7 @@ const Dhcp = () => {
isModalOpen, isModalOpen,
processingAdding, processingAdding,
processingDeleting, processingDeleting,
processingUpdating,
processingDhcp, processingDhcp,
v4, v4,
v6, v6,
@ -56,6 +57,7 @@ const Dhcp = () => {
enabled, enabled,
dhcp_available, dhcp_available,
interfaces, interfaces,
modalType,
} = useSelector((state) => state.dhcp, shallowEqual); } = useSelector((state) => state.dhcp, shallowEqual);
const interface_name = useSelector( const interface_name = useSelector(
@ -273,8 +275,11 @@ const Dhcp = () => {
<StaticLeases <StaticLeases
staticLeases={staticLeases} staticLeases={staticLeases}
isModalOpen={isModalOpen} isModalOpen={isModalOpen}
toggleModal={toggleModal}
modalType={modalType}
processingAdding={processingAdding} processingAdding={processingAdding}
processingDeleting={processingDeleting} processingDeleting={processingDeleting}
processingUpdating={processingUpdating}
cidr={cidr} cidr={cidr}
rangeStart={dhcp?.values?.v4?.range_start} rangeStart={dhcp?.values?.v4?.range_start}
rangeEnd={dhcp?.values?.v4?.range_end} rangeEnd={dhcp?.values?.v4?.range_end}

View file

@ -68,7 +68,7 @@ const Form = ({
return <form onSubmit={handleSubmit}> return <form onSubmit={handleSubmit}>
<div className="row"> <div className="row">
<div className="col-12 col-sm-6"> <div className="col-12 col-md-7">
<div className="form__group form__group--settings"> <div className="form__group form__group--settings">
<label htmlFor="ratelimit" <label htmlFor="ratelimit"
className="form__label form__label--with-desc"> className="form__label form__label--with-desc">
@ -178,6 +178,28 @@ const Form = ({
</div>)} </div>)}
</> </>
)} )}
<div className="col-12 col-md-7">
<div className="form__group form__group--settings">
<label htmlFor="blocked_response_ttl"
className="form__label form__label--with-desc">
<Trans>blocked_response_ttl</Trans>
</label>
<div className="form__desc form__desc--top">
<Trans>blocked_response_ttl_desc</Trans>
</div>
<Field
name="blocked_response_ttl"
type="number"
component={renderInputField}
className="form-control"
placeholder={t('form_enter_blocked_response_ttl')}
normalize={toNumber}
validate={validateRequiredValue}
min={UINT32_RANGE.MIN}
max={UINT32_RANGE.MAX}
/>
</div>
</div>
</div> </div>
<button <button
type="submit" type="submit"

View file

@ -13,6 +13,7 @@ const Config = () => {
ratelimit, ratelimit,
blocking_ipv4, blocking_ipv4,
blocking_ipv6, blocking_ipv6,
blocked_response_ttl,
edns_cs_enabled, edns_cs_enabled,
edns_cs_use_custom, edns_cs_use_custom,
edns_cs_custom_ip, edns_cs_custom_ip,
@ -38,6 +39,7 @@ const Config = () => {
blocking_mode, blocking_mode,
blocking_ipv4, blocking_ipv4,
blocking_ipv6, blocking_ipv6,
blocked_response_ttl,
edns_cs_enabled, edns_cs_enabled,
disable_ipv6, disable_ipv6,
dnssec_enabled, dnssec_enabled,

View file

@ -34,7 +34,7 @@ const validate = (values) => {
return errors; return errors;
}; };
const clearFields = (change, setTlsConfig, t) => { const clearFields = (change, setTlsConfig, validateTlsConfig, t) => {
const fields = { const fields = {
private_key: '', private_key: '',
certificate_chain: '', certificate_chain: '',
@ -53,6 +53,7 @@ const clearFields = (change, setTlsConfig, t) => {
Object.keys(fields) Object.keys(fields)
.forEach((field) => change(field, fields[field])); .forEach((field) => change(field, fields[field]));
setTlsConfig(fields); setTlsConfig(fields);
validateTlsConfig(fields);
} }
}; };
@ -102,6 +103,7 @@ let Form = (props) => {
subject, subject,
warning_validation, warning_validation,
setTlsConfig, setTlsConfig,
validateTlsConfig,
certificateSource, certificateSource,
privateKeySource, privateKeySource,
privateKeySaved, privateKeySaved,
@ -419,7 +421,7 @@ let Form = (props) => {
type="button" type="button"
className="btn btn-secondary btn-standart" className="btn btn-secondary btn-standart"
disabled={submitting || processingConfig} disabled={submitting || processingConfig}
onClick={() => clearFields(change, setTlsConfig, t)} onClick={() => clearFields(change, setTlsConfig, validateTlsConfig, t)}
> >
<Trans>reset_settings</Trans> <Trans>reset_settings</Trans>
</button> </button>
@ -455,6 +457,7 @@ Form.propTypes = {
subject: PropTypes.string, subject: PropTypes.string,
t: PropTypes.func.isRequired, t: PropTypes.func.isRequired,
setTlsConfig: PropTypes.func.isRequired, setTlsConfig: PropTypes.func.isRequired,
validateTlsConfig: PropTypes.func.isRequired,
certificateSource: PropTypes.string, certificateSource: PropTypes.string,
privateKeySource: PropTypes.string, privateKeySource: PropTypes.string,
privateKeySaved: PropTypes.bool, privateKeySaved: PropTypes.bool,

View file

@ -116,6 +116,7 @@ class Encryption extends Component {
onSubmit={this.handleFormSubmit} onSubmit={this.handleFormSubmit}
onChange={this.handleFormChange} onChange={this.handleFormChange}
setTlsConfig={this.props.setTlsConfig} setTlsConfig={this.props.setTlsConfig}
validateTlsConfig={this.props.validateTlsConfig}
{...this.props.encryption} {...this.props.encryption}
/> />
</Card> </Card>

View file

@ -62,7 +62,7 @@ class LogsConfig extends Component {
interval, interval,
customInterval, customInterval,
anonymize_client_ip, anonymize_client_ip,
ignored: ignored.join('\n'), ignored: ignored?.join('\n'),
}} }}
onSubmit={this.handleFormSubmit} onSubmit={this.handleFormSubmit}
processing={processing} processing={processing}

View file

@ -59,7 +59,7 @@ class StatsConfig extends Component {
interval, interval,
customInterval, customInterval,
enabled, enabled,
ignored: ignored.join('\n'), ignored: ignored?.join('\n'),
}} }}
onSubmit={this.handleFormSubmit} onSubmit={this.handleFormSubmit}
processing={processing} processing={processing}

View file

@ -20,7 +20,7 @@
} }
.dashboard .card-table-overflow--limited { .dashboard .card-table-overflow--limited {
max-height: 18rem; max-height: 19rem;
} }
.card-actions { .card-actions {

View file

@ -27,6 +27,7 @@ export const R_WIN_ABSOLUTE_PATH = /^([a-zA-Z]:)?(\\|\/)(?:[^\\/:*?"<>|\x00]+\\)
export const R_CLIENT_ID = /^[a-z0-9-]{1,63}$/; export const R_CLIENT_ID = /^[a-z0-9-]{1,63}$/;
export const MIN_PASSWORD_LENGTH = 8; export const MIN_PASSWORD_LENGTH = 8;
export const MAX_PASSWORD_LENGTH = 72;
export const HTML_PAGES = { export const HTML_PAGES = {
INSTALL: '/install.html', INSTALL: '/install.html',
@ -175,6 +176,7 @@ export const MODAL_TYPE = {
CHOOSE_FILTERING_LIST: 'CHOOSE_FILTERING_LIST', CHOOSE_FILTERING_LIST: 'CHOOSE_FILTERING_LIST',
ADD_REWRITE: 'ADD_REWRITE', ADD_REWRITE: 'ADD_REWRITE',
EDIT_REWRITE: 'EDIT_REWRITE', EDIT_REWRITE: 'EDIT_REWRITE',
EDIT_LEASE: 'EDIT_LEASE',
}; };
export const CLIENT_ID = { export const CLIENT_ID = {
@ -551,6 +553,4 @@ export const DISABLE_PROTECTION_TIMINGS = {
TOMORROW: 24 * 60 * 60 * 1000, TOMORROW: 24 * 60 * 60 * 1000,
}; };
export const LOCAL_STORAGE_THEME_KEY = 'account_theme';
export const LOCAL_TIMEZONE_VALUE = 'Local'; export const LOCAL_TIMEZONE_VALUE = 'Local';

View file

@ -220,12 +220,6 @@ export default {
"homepage": "https://github.com/hoshsadiq/adblock-nocoin-list/", "homepage": "https://github.com/hoshsadiq/adblock-nocoin-list/",
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_8.txt" "source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_8.txt"
}, },
"notracking_hosts_blocklists": {
"name": "The NoTracking blocklist",
"categoryId": "general",
"homepage": "https://github.com/notracking/hosts-blocklists",
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_32.txt"
},
"oisd_basic": { "oisd_basic": {
"name": "OISD Blocklist Small", "name": "OISD Blocklist Small",
"categoryId": "general", "categoryId": "general",
@ -286,6 +280,12 @@ export default {
"homepage": "https://github.com/mitchellkrogza/The-Big-List-of-Hacked-Malware-Web-Sites", "homepage": "https://github.com/mitchellkrogza/The-Big-List-of-Hacked-Malware-Web-Sites",
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_9.txt" "source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_9.txt"
}, },
"ublock_badware_risks": {
"name": "uBlock₀ filters Badware risks",
"categoryId": "security",
"homepage": "https://github.com/uBlockOrigin/uAssets",
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_50.txt"
},
"urlhaus_filter_online": { "urlhaus_filter_online": {
"name": "Malicious URL Blocklist (URLHaus)", "name": "Malicious URL Blocklist (URLHaus)",
"categoryId": "security", "categoryId": "security",

View file

@ -26,8 +26,8 @@ import {
STANDARD_WEB_PORT, STANDARD_WEB_PORT,
SPECIAL_FILTER_ID, SPECIAL_FILTER_ID,
THEMES, THEMES,
LOCAL_STORAGE_THEME_KEY,
} from './constants'; } from './constants';
import { LOCAL_STORAGE_KEYS, LocalStorageHelper } from './localStorageHelper';
/** /**
* @param time {string} The time to format * @param time {string} The time to format
@ -680,37 +680,13 @@ export const setHtmlLangAttr = (language) => {
window.document.documentElement.lang = language; window.document.documentElement.lang = language;
}; };
/**
* Set local storage field
*
* @param {string} key
* @param {string} value
*/
export const setStorageItem = (key, value) => {
if (window.localStorage) {
window.localStorage.setItem(key, value);
}
};
/**
* Get local storage field
*
* @param {string} key
*/
export const getStorageItem = (key) => (window.localStorage
? window.localStorage.getItem(key)
: null);
/** /**
* Set local storage theme field * Set local storage theme field
* *
* @param {string} theme * @param {string} theme
*/ */
export const setTheme = (theme) => { export const setTheme = (theme) => {
setStorageItem(LOCAL_STORAGE_THEME_KEY, theme); LocalStorageHelper.setItem(LOCAL_STORAGE_KEYS.THEME, theme);
}; };
/** /**
@ -719,7 +695,7 @@ export const setTheme = (theme) => {
* @returns {string} * @returns {string}
*/ */
export const getTheme = () => getStorageItem(LOCAL_STORAGE_THEME_KEY) || THEMES.light; export const getTheme = () => LocalStorageHelper.getItem(LOCAL_STORAGE_KEYS.THEME) || THEMES.light;
/** /**
* Sets UI theme. * Sets UI theme.

View file

@ -0,0 +1,44 @@
export const LOCAL_STORAGE_KEYS = {
THEME: 'account_theme',
BLOCKLIST_PAGE_SIZE: 'blocklist_page_size',
ALLOWLIST_PAGE_SIZE: 'allowlist_page_size',
CLIENTS_PAGE_SIZE: 'clients_page_size',
REWRITES_PAGE_SIZE: 'rewrites_page_size',
AUTO_CLIENTS_PAGE_SIZE: 'auto_clients_page_size',
};
export const LocalStorageHelper = {
setItem(key, value) {
try {
localStorage.setItem(key, JSON.stringify(value));
} catch (error) {
console.error(`Error setting ${key} in local storage: ${error.message}`);
}
},
getItem(key) {
try {
const item = localStorage.getItem(key);
return item ? JSON.parse(item) : null;
} catch (error) {
console.error(`Error getting ${key} from local storage: ${error.message}`);
return null;
}
},
removeItem(key) {
try {
localStorage.removeItem(key);
} catch (error) {
console.error(`Error removing ${key} from local storage: ${error.message}`);
}
},
clear() {
try {
localStorage.clear();
} catch (error) {
console.error(`Error clearing local storage: ${error.message}`);
}
},
};

View file

@ -1,5 +1,5 @@
{ {
"timeUpdated": "2023-09-01T00:09:16.088Z", "timeUpdated": "2023-10-08T00:09:45.691Z",
"categories": { "categories": {
"0": "audio_video_player", "0": "audio_video_player",
"1": "comments", "1": "comments",
@ -4069,6 +4069,13 @@
"url": "https://www.canddi.com/", "url": "https://www.canddi.com/",
"companyId": "canddi" "companyId": "canddi"
}, },
"canonical": {
"name": "Canonical",
"categoryId": 8,
"url": "https://canonical.com/",
"companyId": "canonical",
"source": "AdGuard"
},
"canvas": { "canvas": {
"name": "Canvas", "name": "Canvas",
"categoryId": 2, "categoryId": 2,
@ -7583,6 +7590,13 @@
"url": "http://www.game-advertising-online.com/", "url": "http://www.game-advertising-online.com/",
"companyId": "game_advertising_online" "companyId": "game_advertising_online"
}, },
"gameanalytics": {
"name": "GameAnalytics",
"categoryId": 101,
"url": "https://gameanalytics.com/",
"companyId": "mobvista",
"source": "AdGuard"
},
"gamedistribution.com": { "gamedistribution.com": {
"name": "Gamedistribution.com", "name": "Gamedistribution.com",
"categoryId": 8, "categoryId": 8,
@ -9065,7 +9079,8 @@
"name": "Imgur", "name": "Imgur",
"categoryId": 8, "categoryId": 8,
"url": "https://imgur.com/", "url": "https://imgur.com/",
"companyId": "imgur" "companyId": "medialab",
"source": "AdGuard"
}, },
"imho_vi": { "imho_vi": {
"name": "imho vi", "name": "imho vi",
@ -10001,7 +10016,7 @@
"name": "Kik", "name": "Kik",
"categoryId": 7, "categoryId": 7,
"url": "https://kik.com/", "url": "https://kik.com/",
"companyId": "kik", "companyId": "medialab",
"source": "AdGuard" "source": "AdGuard"
}, },
"king.com": { "king.com": {
@ -10184,6 +10199,13 @@
"url": "https://www.launchbit.com/", "url": "https://www.launchbit.com/",
"companyId": "launchbit" "companyId": "launchbit"
}, },
"launchpad": {
"name": "Launchpad",
"categoryId": 8,
"url": "https://launchpad.net/",
"companyId": "canonical",
"source": "AdGuard"
},
"layer-ad.org": { "layer-ad.org": {
"name": "Layer-ADS.net", "name": "Layer-ADS.net",
"categoryId": 4, "categoryId": 4,
@ -12835,7 +12857,7 @@
"name": "OPPO", "name": "OPPO",
"categoryId": 101, "categoryId": 101,
"url": "https://www.oppo.com/", "url": "https://www.oppo.com/",
"companyId": "oppo", "companyId": "bbk",
"source": "AdGuard" "source": "AdGuard"
}, },
"opta.net": { "opta.net": {
@ -12934,6 +12956,13 @@
"url": "http://www.ora.tv/", "url": "http://www.ora.tv/",
"companyId": "ora.tv" "companyId": "ora.tv"
}, },
"oracle_infinity": {
"name": "Oracle Infinity Behavioral Intelligence",
"categoryId": 6,
"url": "https://www.oracle.com/au/cx/marketing/digital-intelligence/",
"companyId": "oracle",
"source": "AdGuard"
},
"oracle_live_help": { "oracle_live_help": {
"name": "Oracle Live Help", "name": "Oracle Live Help",
"categoryId": 2, "categoryId": 2,
@ -14501,8 +14530,9 @@
"reddit": { "reddit": {
"name": "Reddit", "name": "Reddit",
"categoryId": 7, "categoryId": 7,
"url": "http://reddit.com", "url": "https://www.reddit.com",
"companyId": "reddit" "companyId": "advance",
"source": "AdGuard"
}, },
"redhelper": { "redhelper": {
"name": "RedHelper", "name": "RedHelper",
@ -16113,6 +16143,13 @@
"url": "https://www.snapchat.com/", "url": "https://www.snapchat.com/",
"companyId": "snap_technologies" "companyId": "snap_technologies"
}, },
"snapcraft": {
"name": "Snapcraft",
"categoryId": 8,
"url": "https://snapcraft.io",
"companyId": "canonical",
"source": "AdGuard"
},
"snigelweb": { "snigelweb": {
"name": "SnigelWeb, Inc.", "name": "SnigelWeb, Inc.",
"categoryId": 4, "categoryId": 4,
@ -17894,7 +17931,7 @@
"name": "Ubuntu", "name": "Ubuntu",
"categoryId": 8, "categoryId": 8,
"url": "https://ubuntu.com/", "url": "https://ubuntu.com/",
"companyId": "ubuntu", "companyId": "canonical",
"source": "AdGuard" "source": "AdGuard"
}, },
"ucfunnel": { "ucfunnel": {
@ -18754,6 +18791,13 @@
"url": "http://vpscash.nl/home", "url": "http://vpscash.nl/home",
"companyId": "vps_cash" "companyId": "vps_cash"
}, },
"vs": {
"name": "Visual Studio",
"categoryId": 8,
"url": "https://visualstudio.microsoft.com",
"companyId": "microsoft",
"source": "AdGuard"
},
"vscode": { "vscode": {
"name": "Visual Studio Code", "name": "Visual Studio Code",
"categoryId": 8, "categoryId": 8,
@ -19081,6 +19125,13 @@
"companyId": "meta", "companyId": "meta",
"source": "AdGuard" "source": "AdGuard"
}, },
"whisper": {
"name": "Whisper",
"categoryId": 7,
"url": "https://whisper.sh/",
"companyId": "medialab",
"source": "AdGuard"
},
"whos.amung.us": { "whos.amung.us": {
"name": "Whos.amung.us", "name": "Whos.amung.us",
"categoryId": 6, "categoryId": 6,
@ -20190,6 +20241,7 @@
"adtidy.org": "adguard", "adtidy.org": "adguard",
"agrd.io": "adguard", "agrd.io": "adguard",
"adguard.app": "adguard", "adguard.app": "adguard",
"adguard.info": "adguard",
"adguard.io": "adguard", "adguard.io": "adguard",
"adguard.org": "adguard", "adguard.org": "adguard",
"adguard-dns.com": "adguard_dns", "adguard-dns.com": "adguard_dns",
@ -20969,6 +21021,7 @@
"camakaroda.com": "camakaroda.com", "camakaroda.com": "camakaroda.com",
"s.edkay.com": "campus_explorer", "s.edkay.com": "campus_explorer",
"canddi.com": "canddi", "canddi.com": "canddi",
"canonical.com": "canonical",
"canvas.net": "canvas", "canvas.net": "canvas",
"canvasnetwork.com": "canvas", "canvasnetwork.com": "canvas",
"du11hjcvx0uqb.cloudfront.net": "canvas", "du11hjcvx0uqb.cloudfront.net": "canvas",
@ -21784,6 +21837,7 @@
"angsrvr.com": "fyber", "angsrvr.com": "fyber",
"fyber.com": "fyber", "fyber.com": "fyber",
"game-advertising-online.com": "game_advertising_online", "game-advertising-online.com": "game_advertising_online",
"gameanalytics.com": "gameanalytics",
"gamedistribution.com": "gamedistribution.com", "gamedistribution.com": "gamedistribution.com",
"gamerdna.com": "gamerdna", "gamerdna.com": "gamerdna",
"gannett-cdn.com": "gannett", "gannett-cdn.com": "gannett",
@ -22589,9 +22643,11 @@
"sa-as.com": "kickfire", "sa-as.com": "kickfire",
"sniff.visistat.com": "kickfire", "sniff.visistat.com": "kickfire",
"stats.visistat.com": "kickfire", "stats.visistat.com": "kickfire",
"kik.com": "kik",
"apikik.com": "kik", "apikik.com": "kik",
"kik-gateway-use1.meetme.com": "kik",
"kik-live.com": "kik", "kik-live.com": "kik",
"kik-stream.meetme.com": "kik",
"kik.com": "kik",
"king.com": "king.com", "king.com": "king.com",
"midasplayer.com": "king_com", "midasplayer.com": "king_com",
"kinja-img.com": "kinja.com", "kinja-img.com": "kinja.com",
@ -22633,6 +22689,8 @@
"events.launchdarkly.com": "launch_darkly", "events.launchdarkly.com": "launch_darkly",
"launchdarkly.com": "launch_darkly", "launchdarkly.com": "launch_darkly",
"launchbit.com": "launchbit", "launchbit.com": "launchbit",
"launchpad.net": "launchpad",
"launchpadcontent.net": "launchpad",
"layer-ad.org": "layer-ad.org", "layer-ad.org": "layer-ad.org",
"ph-live.slatic.net": "lazada", "ph-live.slatic.net": "lazada",
"slatic.net": "lazada", "slatic.net": "lazada",
@ -23073,6 +23131,7 @@
"e-msedge.net": "msedge", "e-msedge.net": "msedge",
"l-msedge.net": "msedge", "l-msedge.net": "msedge",
"s-msedge.net": "msedge", "s-msedge.net": "msedge",
"t-msedge.net": "msedge",
"msn.com": "msn", "msn.com": "msn",
"s-msn.com": "msn", "s-msn.com": "msn",
"musculahq.appspot.com": "muscula", "musculahq.appspot.com": "muscula",
@ -23171,6 +23230,7 @@
"d1ros97qkrwjf5.cloudfront.net": "new_relic", "d1ros97qkrwjf5.cloudfront.net": "new_relic",
"newrelic.com": "new_relic", "newrelic.com": "new_relic",
"nr-data.net": "new_relic", "nr-data.net": "new_relic",
"codestream.com": "new_relic",
"newscgp.com": "newscgp.com", "newscgp.com": "newscgp.com",
"nmcdn.us": "newsmax", "nmcdn.us": "newsmax",
"newstogram.com": "newstogram", "newstogram.com": "newstogram",
@ -23315,6 +23375,7 @@
"optincollect.com": "optinproject.com", "optincollect.com": "optinproject.com",
"volvelle.tech": "optomaton", "volvelle.tech": "optomaton",
"ora.tv": "ora.tv", "ora.tv": "ora.tv",
"oracleinfinity.io": "oracle_infinity",
"instantservice.com": "oracle_live_help", "instantservice.com": "oracle_live_help",
"ts.istrack.com": "oracle_live_help", "ts.istrack.com": "oracle_live_help",
"rightnowtech.com": "oracle_rightnow", "rightnowtech.com": "oracle_rightnow",
@ -23603,6 +23664,7 @@
"rcsmediagroup.it": "rcs.it", "rcsmediagroup.it": "rcs.it",
"d335luupugsy2.cloudfront.net": "rd_station", "d335luupugsy2.cloudfront.net": "rd_station",
"rea-group.com": "rea_group", "rea-group.com": "rea_group",
"reastatic.net": "rea_group",
"d12ulf131zb0yj.cloudfront.net": "reachforce", "d12ulf131zb0yj.cloudfront.net": "reachforce",
"reachforce.com": "reachforce", "reachforce.com": "reachforce",
"reachjunction.com": "reachjunction", "reachjunction.com": "reachjunction",
@ -24079,6 +24141,8 @@
"snapengage.com": "snap_engage", "snapengage.com": "snap_engage",
"sc-static.net": "snapchat", "sc-static.net": "snapchat",
"snapchat.com": "snapchat", "snapchat.com": "snapchat",
"snapcraft.io": "snapcraft",
"snapcraftcontent.com": "snapcraft",
"h-bid.com": "snigelweb", "h-bid.com": "snigelweb",
"eu2.snoobi.eu": "snoobi", "eu2.snoobi.eu": "snoobi",
"snoobi.com": "snoobi_analytics", "snoobi.com": "snoobi_analytics",
@ -24482,11 +24546,6 @@
"tyroodr.com": "tyroo", "tyroodr.com": "tyroo",
"tzetze.it": "tzetze", "tzetze.it": "tzetze",
"ubersetzung-app.com": "ubersetzung-app.com", "ubersetzung-app.com": "ubersetzung-app.com",
"canonical.com": "ubuntu",
"launchpad.net": "ubuntu",
"launchpadcontent.net": "ubuntu",
"snapcraft.io": "ubuntu",
"snapcraftcontent.com": "ubuntu",
"ubuntu.com": "ubuntu", "ubuntu.com": "ubuntu",
"ubuntucompanyservices.co.za": "ubuntu", "ubuntucompanyservices.co.za": "ubuntu",
"aralego.net": "ucfunnel", "aralego.net": "ucfunnel",
@ -24667,6 +24726,7 @@
"voxus-targeting-voxusmidia.netdna-ssl.com": "voxus", "voxus-targeting-voxusmidia.netdna-ssl.com": "voxus",
"c-dsp.vpadn.com": "vpon", "c-dsp.vpadn.com": "vpon",
"tools.vpscash.nl": "vpscash", "tools.vpscash.nl": "vpscash",
"vsassets.io": "vs",
"exp-tas.com": "vscode", "exp-tas.com": "vscode",
"vscode-unpkg.net": "vscode", "vscode-unpkg.net": "vscode",
"v0cdn.net": "vscode", "v0cdn.net": "vscode",
@ -24745,6 +24805,8 @@
"whatsbroadcast.com": "whatbroadcast", "whatsbroadcast.com": "whatbroadcast",
"whatsapp.net": "whatsapp", "whatsapp.net": "whatsapp",
"whatsapp.com": "whatsapp", "whatsapp.com": "whatsapp",
"whisper.onelink.me": "whisper",
"whisper.sh": "whisper",
"amung.us": "whos.amung.us", "amung.us": "whos.amung.us",
"whoson.com": "whoson", "whoson.com": "whoson",
"api.wibbitz.com": "wibbitz", "api.wibbitz.com": "wibbitz",

View file

@ -1,5 +1,4 @@
import i18next from 'i18next'; import i18next from 'i18next';
import stringLength from 'string-length';
import { import {
MAX_PORT, MAX_PORT,
@ -14,6 +13,7 @@ import {
UNSAFE_PORTS, UNSAFE_PORTS,
R_CLIENT_ID, R_CLIENT_ID,
R_DOMAIN, R_DOMAIN,
MAX_PASSWORD_LENGTH,
MIN_PASSWORD_LENGTH, MIN_PASSWORD_LENGTH,
} from './constants'; } from './constants';
import { ip4ToInt, isValidAbsolutePath } from './form'; import { ip4ToInt, isValidAbsolutePath } from './form';
@ -325,14 +325,33 @@ export const validateIpv4InCidr = (valueIp, allValues) => {
return undefined; return undefined;
}; };
/**
* @param value {string}
* @returns {number}
*/
const utf8StringLength = (value) => {
const encoder = new TextEncoder();
const view = encoder.encode(value);
return view.length;
};
/** /**
* @param value {string} * @param value {string}
* @returns {Function} * @returns {Function}
*/ */
export const validatePasswordLength = (value) => { export const validatePasswordLength = (value) => {
if (value && stringLength(value) < MIN_PASSWORD_LENGTH) { if (value) {
return i18next.t('form_error_password_length', { value: MIN_PASSWORD_LENGTH }); const length = utf8StringLength(value);
if (length < MIN_PASSWORD_LENGTH || length > MAX_PASSWORD_LENGTH) {
// TODO: Make the i18n clearer with regards to bytes vs. characters.
return i18next.t('form_error_password_length', {
min: MIN_PASSWORD_LENGTH,
max: MAX_PASSWORD_LENGTH,
});
} }
}
return undefined; return undefined;
}; };

View file

@ -128,7 +128,8 @@ const dhcp = handleActions(
const newState = { const newState = {
...state, ...state,
isModalOpen: !state.isModalOpen, isModalOpen: !state.isModalOpen,
leaseModalConfig: payload, modalType: payload?.type || '',
leaseModalConfig: payload?.config,
}; };
return newState; return newState;
}, },
@ -175,6 +176,16 @@ const dhcp = handleActions(
}; };
return newState; return newState;
}, },
[actions.updateStaticLeaseRequest]: (state) => ({ ...state, processingUpdating: true }),
[actions.updateStaticLeaseFailure]: (state) => ({ ...state, processingUpdating: false }),
[actions.updateStaticLeaseSuccess]: (state) => {
const newState = {
...state,
processingUpdating: false,
};
return newState;
},
}, },
{ {
processing: true, processing: true,
@ -184,6 +195,7 @@ const dhcp = handleActions(
processingConfig: false, processingConfig: false,
processingAdding: false, processingAdding: false,
processingDeleting: false, processingDeleting: false,
processingUpdating: false,
enabled: false, enabled: false,
interface_name: '', interface_name: '',
check: null, check: null,
@ -202,6 +214,7 @@ const dhcp = handleActions(
staticLeases: [], staticLeases: [],
isModalOpen: false, isModalOpen: false,
leaseModalConfig: undefined, leaseModalConfig: undefined,
modalType: '',
dhcp_available: false, dhcp_available: false,
}, },
); );

View file

@ -49,6 +49,7 @@ const dnsConfig = handleActions(
ratelimit: 20, ratelimit: 20,
blocking_ipv4: DEFAULT_BLOCKING_IPV4, blocking_ipv4: DEFAULT_BLOCKING_IPV4,
blocking_ipv6: DEFAULT_BLOCKING_IPV6, blocking_ipv6: DEFAULT_BLOCKING_IPV6,
blocked_response_ttl: 10,
edns_cs_enabled: false, edns_cs_enabled: false,
disable_ipv6: false, disable_ipv6: false,
dnssec_enabled: false, dnssec_enabled: false,

16
go.mod
View file

@ -3,8 +3,8 @@ module github.com/AdguardTeam/AdGuardHome
go 1.20 go 1.20
require ( require (
github.com/AdguardTeam/dnsproxy v0.54.0 github.com/AdguardTeam/dnsproxy v0.56.1
github.com/AdguardTeam/golibs v0.15.0 github.com/AdguardTeam/golibs v0.17.1
github.com/AdguardTeam/urlfilter v0.17.0 github.com/AdguardTeam/urlfilter v0.17.0
github.com/NYTimes/gziphandler v1.1.1 github.com/NYTimes/gziphandler v1.1.1
github.com/ameshkov/dnscrypt/v2 v2.2.7 github.com/ameshkov/dnscrypt/v2 v2.2.7
@ -17,7 +17,7 @@ require (
github.com/google/gopacket v1.1.19 github.com/google/gopacket v1.1.19
github.com/google/renameio/v2 v2.0.0 github.com/google/renameio/v2 v2.0.0
github.com/google/uuid v1.3.1 github.com/google/uuid v1.3.1
github.com/insomniacslk/dhcp v0.0.0-20230906122924-c71a6be05968 github.com/insomniacslk/dhcp v0.0.0-20230908212754-65c27093e38a
github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86 github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86
github.com/kardianos/service v1.2.2 github.com/kardianos/service v1.2.2
github.com/mdlayher/ethernet v0.0.0-20220221185849-529eae5b6118 github.com/mdlayher/ethernet v0.0.0-20220221185849-529eae5b6118
@ -26,8 +26,8 @@ require (
// TODO(a.garipov): This package is deprecated; find a new one or use our // TODO(a.garipov): This package is deprecated; find a new one or use our
// own code for that. Perhaps, use gopacket. // own code for that. Perhaps, use gopacket.
github.com/mdlayher/raw v0.1.0 github.com/mdlayher/raw v0.1.0
github.com/miekg/dns v1.1.55 github.com/miekg/dns v1.1.56
github.com/quic-go/quic-go v0.38.1 github.com/quic-go/quic-go v0.39.0
github.com/stretchr/testify v1.8.4 github.com/stretchr/testify v1.8.4
github.com/ti-mo/netfilter v0.5.0 github.com/ti-mo/netfilter v0.5.0
go.etcd.io/bbolt v1.3.7 go.etcd.io/bbolt v1.3.7
@ -47,10 +47,9 @@ require (
github.com/beefsack/go-rate v0.0.0-20220214233405-116f4ca011a0 // indirect github.com/beefsack/go-rate v0.0.0-20220214233405-116f4ca011a0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/golang/mock v1.6.0 // indirect github.com/google/pprof v0.0.0-20230912144702-c363fe2c2ed8 // indirect
github.com/google/pprof v0.0.0-20230906154834-20cde9067b3b // indirect
github.com/mdlayher/socket v0.5.0 // indirect github.com/mdlayher/socket v0.5.0 // indirect
github.com/onsi/ginkgo/v2 v2.12.0 // indirect github.com/onsi/ginkgo/v2 v2.12.1 // indirect
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
github.com/pierrec/lz4/v4 v4.1.18 // indirect github.com/pierrec/lz4/v4 v4.1.18 // indirect
github.com/pkg/errors v0.9.1 // indirect github.com/pkg/errors v0.9.1 // indirect
@ -58,6 +57,7 @@ require (
github.com/quic-go/qpack v0.4.0 // indirect github.com/quic-go/qpack v0.4.0 // indirect
github.com/quic-go/qtls-go1-20 v0.3.4 // indirect github.com/quic-go/qtls-go1-20 v0.3.4 // indirect
github.com/u-root/uio v0.0.0-20230305220412-3e8cd9d6bf63 // 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.12.0 // indirect golang.org/x/mod v0.12.0 // indirect
golang.org/x/sync v0.3.0 // indirect golang.org/x/sync v0.3.0 // indirect
golang.org/x/text v0.13.0 // indirect golang.org/x/text v0.13.0 // indirect

41
go.sum
View file

@ -1,7 +1,7 @@
github.com/AdguardTeam/dnsproxy v0.54.0 h1:OgSitM/EKrMMOi+guWZNwaU1cqRqJKWgR3l3fPWWayI= github.com/AdguardTeam/dnsproxy v0.56.1 h1:QltfyWO7k4mxWERCEYDzkQnKzvZX/zkneWjbuJ0TU6o=
github.com/AdguardTeam/dnsproxy v0.54.0/go.mod h1:tG/treaQekcKnugYoKOfm8vt3JGi6CliWta0MkQr15U= github.com/AdguardTeam/dnsproxy v0.56.1/go.mod h1:fqmehcE3cHFNqKbWQpIjGk7GqBy7ur1v5At499lFjRc=
github.com/AdguardTeam/golibs v0.15.0 h1:yOv/fdVkJIOWKr0NlUXAE9RA0DK9GKiBbiGzq47vY7o= github.com/AdguardTeam/golibs v0.17.1 h1:j3Ehhld5GI/amcHYG+CF0sJ4OOzAQ06BY3N/iBYJZ1M=
github.com/AdguardTeam/golibs v0.15.0/go.mod h1:66ZLs8P7nk/3IfKroQ1rqtieLk+5eXYXMBKXlVL7KeI= github.com/AdguardTeam/golibs v0.17.1/go.mod h1:DKhCIXHcUYtBhU8ibTLKh1paUL96n5zhQBlx763sj+U=
github.com/AdguardTeam/urlfilter v0.17.0 h1:tUzhtR9wMx704GIP3cibsDQJrixlMHfwoQbYJfPdFow= github.com/AdguardTeam/urlfilter v0.17.0 h1:tUzhtR9wMx704GIP3cibsDQJrixlMHfwoQbYJfPdFow=
github.com/AdguardTeam/urlfilter v0.17.0/go.mod h1:bbuZjPUzm/Ip+nz5qPPbwIP+9rZyQbQad8Lt/0fCulU= github.com/AdguardTeam/urlfilter v0.17.0/go.mod h1:bbuZjPUzm/Ip+nz5qPPbwIP+9rZyQbQad8Lt/0fCulU=
github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I= github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I=
@ -33,8 +33,6 @@ github.com/go-ping/ping v1.1.0 h1:3MCGhVX4fyEUuhsfwPrsEdQw6xspHkv5zHsiSoDFZYw=
github.com/go-ping/ping v1.1.0/go.mod h1:xIFjORFzTxqIV/tDVGO4eDy/bLuSyawEeojSm3GfRGk= github.com/go-ping/ping v1.1.0/go.mod h1:xIFjORFzTxqIV/tDVGO4eDy/bLuSyawEeojSm3GfRGk=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
@ -43,16 +41,16 @@ github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
github.com/google/pprof v0.0.0-20230906154834-20cde9067b3b h1:AIwVPxYWRaz7GetyrRj3ShUL5hr7WeuqF52G0g8Hne8= github.com/google/pprof v0.0.0-20230912144702-c363fe2c2ed8 h1:gpptm606MZYGaMHMsB4Srmb6EbW/IVHnt04rcMXnkBQ=
github.com/google/pprof v0.0.0-20230906154834-20cde9067b3b/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/google/pprof v0.0.0-20230912144702-c363fe2c2ed8/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 h1:UifI23ZTGY8Tt29JbYFiuyIU3eX+RNFtUwefq9qAhxg=
github.com/google/renameio/v2 v2.0.0/go.mod h1:BtmJXm5YlszgC+TD4HOEEUFgkJP3nLxehU6hfe7jRt4= 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.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= 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/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/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714 h1:/jC7qQFrv8CrSJVmaolDVOxTfS9kc36uB6H40kdbQq8=
github.com/insomniacslk/dhcp v0.0.0-20230906122924-c71a6be05968 h1:uBiv5/8x42J7myumCdFuDOc5HnEXRK6eOtefwvE6+TQ= github.com/insomniacslk/dhcp v0.0.0-20230908212754-65c27093e38a h1:S33o3djA1nPRd+d/bf7jbbXytXuK/EoXow7+aa76grQ=
github.com/insomniacslk/dhcp v0.0.0-20230906122924-c71a6be05968/go.mod h1:zmdm3sTSDP3vOOX3CEWRkkRHtKr1DxBx+J1OQFoDQQs= github.com/insomniacslk/dhcp v0.0.0-20230908212754-65c27093e38a/go.mod h1:zmdm3sTSDP3vOOX3CEWRkkRHtKr1DxBx+J1OQFoDQQs=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/josharian/native v1.0.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/josharian/native v1.0.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
@ -75,11 +73,11 @@ github.com/mdlayher/raw v0.1.0/go.mod h1:yXnxvs6c0XoF/aK52/H5PjsVHmWBCFfZUfoh/Y5
github.com/mdlayher/socket v0.2.1/go.mod h1:QLlNPkFR88mRUNQIzRBMfXxwKal8H7u1h3bL1CV+f0E= github.com/mdlayher/socket v0.2.1/go.mod h1:QLlNPkFR88mRUNQIzRBMfXxwKal8H7u1h3bL1CV+f0E=
github.com/mdlayher/socket v0.5.0 h1:ilICZmJcQz70vrWVes1MFera4jGiWNocSkykwwoy3XI= github.com/mdlayher/socket v0.5.0 h1:ilICZmJcQz70vrWVes1MFera4jGiWNocSkykwwoy3XI=
github.com/mdlayher/socket v0.5.0/go.mod h1:WkcBFfvyG8QENs5+hfQPl1X6Jpd2yeLIYgrGFmJiJxI= github.com/mdlayher/socket v0.5.0/go.mod h1:WkcBFfvyG8QENs5+hfQPl1X6Jpd2yeLIYgrGFmJiJxI=
github.com/miekg/dns v1.1.55 h1:GoQ4hpsj0nFLYe+bWiCToyrBEJXkQfOOIvFGFy0lEgo= github.com/miekg/dns v1.1.56 h1:5imZaSeoRNvpM9SzWNhEcP9QliKiz20/dA2QabIGVnE=
github.com/miekg/dns v1.1.55/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= 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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/onsi/ginkgo/v2 v2.12.0 h1:UIVDowFPwpg6yMUpPjGkYvf06K3RAiJXUhCxEwQVHRI= github.com/onsi/ginkgo/v2 v2.12.1 h1:uHNEO1RP2SpuZApSkel9nEh1/Mu+hmQe7Q+Pepg5OYA=
github.com/onsi/ginkgo/v2 v2.12.0/go.mod h1:ZNEzXISYlqpb8S36iN71ifqLi3vVD1rVJGvWRCJOUpQ= github.com/onsi/ginkgo/v2 v2.12.1/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o=
github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= 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 h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
@ -96,8 +94,8 @@ 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/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
github.com/quic-go/qtls-go1-20 v0.3.4 h1:MfFAPULvst4yoMgY9QmtpYmfij/em7O8UUi+bNVm7Cg= 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/qtls-go1-20 v0.3.4/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
github.com/quic-go/quic-go v0.38.1 h1:M36YWA5dEhEeT+slOu/SwMEucbYd0YFidxG3KlGPZaE= github.com/quic-go/quic-go v0.39.0 h1:AgP40iThFMY0bj8jGxROhw3S0FMGa8ryqsmi9tBH3So=
github.com/quic-go/quic-go v0.38.1/go.mod h1:ijnZM7JsFIkp4cRyjxJNIzdSfCLmUMg9wdyhGmg+SN4= github.com/quic-go/quic-go v0.39.0/go.mod h1:T09QsDQWjLiQ74ZmacDfqZmhY/NLnw5BC40MANNNZ1Q=
github.com/shirou/gopsutil/v3 v3.23.7 h1:C+fHO8hfIppoJ1WdsVm1RoI0RwXoNdfTK7yWXV0wVj4= github.com/shirou/gopsutil/v3 v3.23.7 h1:C+fHO8hfIppoJ1WdsVm1RoI0RwXoNdfTK7yWXV0wVj4=
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@ -113,10 +111,11 @@ github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+Kd
github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms= github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms=
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 h1:YcojQL98T/OO+rybuzn2+5KrD5dBwXIvYBvQ2cD3Avg=
github.com/u-root/uio v0.0.0-20230305220412-3e8cd9d6bf63/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264= github.com/u-root/uio v0.0.0-20230305220412-3e8cd9d6bf63/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw=
go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ= go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ=
go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
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-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck=
@ -125,7 +124,6 @@ golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjs
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@ -134,7 +132,6 @@ golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 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-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.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -148,8 +145,6 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@ -163,15 +158,11 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= 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/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-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=

View file

@ -0,0 +1,102 @@
package aghalg
import (
"github.com/AdguardTeam/golibs/errors"
)
// RingBuffer is the implementation of ring buffer data structure.
type RingBuffer[T any] struct {
buf []T
cur int
full bool
}
// NewRingBuffer initializes the new instance of ring buffer. size must be
// greater or equal to zero.
func NewRingBuffer[T any](size int) (rb *RingBuffer[T]) {
if size < 0 {
panic(errors.Error("ring buffer: size must be greater or equal to zero"))
}
return &RingBuffer[T]{
buf: make([]T, size),
}
}
// Append appends an element to the buffer.
func (rb *RingBuffer[T]) Append(e T) {
if len(rb.buf) == 0 {
return
}
rb.buf[rb.cur] = e
rb.cur = (rb.cur + 1) % cap(rb.buf)
if rb.cur == 0 {
rb.full = true
}
}
// Range calls cb for each element of the buffer. If cb returns false it stops.
func (rb *RingBuffer[T]) Range(cb func(T) (cont bool)) {
before, after := rb.splitCur()
for _, e := range before {
if !cb(e) {
return
}
}
for _, e := range after {
if !cb(e) {
return
}
}
}
// ReverseRange calls cb for each element of the buffer in reverse order. If
// cb returns false it stops.
func (rb *RingBuffer[T]) ReverseRange(cb func(T) (cont bool)) {
before, after := rb.splitCur()
for i := len(after) - 1; i >= 0; i-- {
if !cb(after[i]) {
return
}
}
for i := len(before) - 1; i >= 0; i-- {
if !cb(before[i]) {
return
}
}
}
// splitCur splits the buffer in two, before and after current position in
// chronological order. If buffer is not full, after is nil.
func (rb *RingBuffer[T]) splitCur() (before, after []T) {
if len(rb.buf) == 0 {
return nil, nil
}
cur := rb.cur
if !rb.full {
return rb.buf[:cur], nil
}
return rb.buf[cur:], rb.buf[:cur]
}
// Len returns a length of the buffer.
func (rb *RingBuffer[T]) Len() (l int) {
if !rb.full {
return rb.cur
}
return cap(rb.buf)
}
// Clear clears the buffer.
func (rb *RingBuffer[T]) Clear() {
rb.full = false
rb.cur = 0
}

View file

@ -0,0 +1,173 @@
package aghalg_test
import (
"testing"
"github.com/AdguardTeam/AdGuardHome/internal/aghalg"
"github.com/stretchr/testify/assert"
"golang.org/x/exp/slices"
)
// elements is a helper function that returns n elements of the buffer.
func elements(b *aghalg.RingBuffer[int], n int, reverse bool) (es []int) {
fn := b.Range
if reverse {
fn = b.ReverseRange
}
i := 0
fn(func(e int) (cont bool) {
if i >= n {
return false
}
es = append(es, e)
i++
return true
})
return es
}
func TestNewRingBuffer(t *testing.T) {
t.Run("success_and_clear", func(t *testing.T) {
b := aghalg.NewRingBuffer[int](5)
for i := 0; i < 10; i++ {
b.Append(i)
}
assert.Equal(t, []int{5, 6, 7, 8, 9}, elements(b, b.Len(), false))
b.Clear()
assert.Zero(t, b.Len())
})
t.Run("negative_size", func(t *testing.T) {
assert.PanicsWithError(t, "ring buffer: size must be greater or equal to zero", func() {
aghalg.NewRingBuffer[int](-5)
})
})
t.Run("zero", func(t *testing.T) {
b := aghalg.NewRingBuffer[int](0)
for i := 0; i < 10; i++ {
b.Append(i)
assert.Equal(t, 0, b.Len())
assert.Empty(t, elements(b, b.Len(), false))
assert.Empty(t, elements(b, b.Len(), true))
}
})
t.Run("single", func(t *testing.T) {
b := aghalg.NewRingBuffer[int](1)
for i := 0; i < 10; i++ {
b.Append(i)
assert.Equal(t, 1, b.Len())
assert.Equal(t, []int{i}, elements(b, b.Len(), false))
assert.Equal(t, []int{i}, elements(b, b.Len(), true))
}
})
}
func TestRingBuffer_Range(t *testing.T) {
const size = 5
b := aghalg.NewRingBuffer[int](size)
testCases := []struct {
name string
want []int
count int
length int
}{{
name: "three",
count: 3,
length: 3,
want: []int{0, 1, 2},
}, {
name: "ten",
count: 10,
length: size,
want: []int{5, 6, 7, 8, 9},
}, {
name: "hundred",
count: 100,
length: size,
want: []int{95, 96, 97, 98, 99},
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
for i := 0; i < tc.count; i++ {
b.Append(i)
}
bufLen := b.Len()
assert.Equal(t, tc.length, bufLen)
want := tc.want
assert.Equal(t, want, elements(b, bufLen, false))
assert.Equal(t, want[:len(want)-1], elements(b, bufLen-1, false))
assert.Equal(t, want[:len(want)/2], elements(b, bufLen/2, false))
want = want[:cap(want)]
slices.Reverse(want)
assert.Equal(t, want, elements(b, bufLen, true))
assert.Equal(t, want[:len(want)-1], elements(b, bufLen-1, true))
assert.Equal(t, want[:len(want)/2], elements(b, bufLen/2, true))
})
}
}
func TestRingBuffer_Range_increment(t *testing.T) {
const size = 5
b := aghalg.NewRingBuffer[int](size)
testCases := []struct {
name string
want []int
}{{
name: "one",
want: []int{0},
}, {
name: "two",
want: []int{0, 1},
}, {
name: "three",
want: []int{0, 1, 2},
}, {
name: "four",
want: []int{0, 1, 2, 3},
}, {
name: "five",
want: []int{0, 1, 2, 3, 4},
}, {
name: "six",
want: []int{1, 2, 3, 4, 5},
}, {
name: "seven",
want: []int{2, 3, 4, 5, 6},
}, {
name: "eight",
want: []int{3, 4, 5, 6, 7},
}, {
name: "nine",
want: []int{4, 5, 6, 7, 8},
}, {
name: "ten",
want: []int{5, 6, 7, 8, 9},
}}
for i, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
b.Append(i)
assert.Equal(t, tc.want, elements(b, b.Len(), false))
slices.Reverse(tc.want)
assert.Equal(t, tc.want, elements(b, b.Len(), true))
})
}
}

View file

@ -1,60 +0,0 @@
// Package aghio contains extensions for io package's types and methods
package aghio
import (
"fmt"
"io"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/mathutil"
)
// LimitReachedError records the limit and the operation that caused it.
type LimitReachedError struct {
Limit int64
}
// Error implements the [error] interface for *LimitReachedError.
//
// TODO(a.garipov): Think about error string format.
func (lre *LimitReachedError) Error() string {
return fmt.Sprintf("attempted to read more than %d bytes", lre.Limit)
}
// limitedReader is a wrapper for [io.Reader] limiting the input and dealing
// with errors package.
type limitedReader struct {
r io.Reader
limit int64
n int64
}
// Read implements the [io.Reader] interface.
func (lr *limitedReader) Read(p []byte) (n int, err error) {
if lr.n == 0 {
return 0, &LimitReachedError{
Limit: lr.limit,
}
}
p = p[:mathutil.Min(lr.n, int64(len(p)))]
n, err = lr.r.Read(p)
lr.n -= int64(n)
return n, err
}
// LimitReader wraps Reader to make it's Reader stop with ErrLimitReached after
// n bytes read.
func LimitReader(r io.Reader, n int64) (limited io.Reader, err error) {
if n < 0 {
return nil, errors.Error("limit must be non-negative")
}
return &limitedReader{
r: r,
limit: n,
n: n,
}, nil
}

View file

@ -1,96 +0,0 @@
package aghio_test
import (
"io"
"strings"
"testing"
"github.com/AdguardTeam/AdGuardHome/internal/aghio"
"github.com/AdguardTeam/golibs/testutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestLimitReader(t *testing.T) {
testCases := []struct {
wantErrMsg string
name string
n int64
}{{
wantErrMsg: "",
name: "positive",
n: 1,
}, {
wantErrMsg: "",
name: "zero",
n: 0,
}, {
wantErrMsg: "limit must be non-negative",
name: "negative",
n: -1,
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
_, err := aghio.LimitReader(nil, tc.n)
testutil.AssertErrorMsg(t, tc.wantErrMsg, err)
})
}
}
func TestLimitedReader_Read(t *testing.T) {
testCases := []struct {
err error
name string
rStr string
limit int64
want int
}{{
err: nil,
name: "perfectly_match",
rStr: "abc",
limit: 3,
want: 3,
}, {
err: io.EOF,
name: "eof",
rStr: "",
limit: 3,
want: 0,
}, {
err: &aghio.LimitReachedError{
Limit: 0,
},
name: "limit_reached",
rStr: "abc",
limit: 0,
want: 0,
}, {
err: nil,
name: "truncated",
rStr: "abc",
limit: 2,
want: 2,
}}
for _, tc := range testCases {
readCloser := io.NopCloser(strings.NewReader(tc.rStr))
lreader, err := aghio.LimitReader(readCloser, tc.limit)
require.NoError(t, err)
require.NotNil(t, lreader)
t.Run(tc.name, func(t *testing.T) {
buf := make([]byte, tc.limit+1)
n, rerr := lreader.Read(buf)
require.Equal(t, rerr, tc.err)
assert.Equal(t, tc.want, n)
})
}
}
func TestLimitedReader_LimitReachedError(t *testing.T) {
testutil.AssertErrorMsg(t, "attempted to read more than 0 bytes", &aghio.LimitReachedError{
Limit: 0,
})
}

View file

@ -12,12 +12,12 @@ import (
// listenPacketReusable announces on the local network address additionally // listenPacketReusable announces on the local network address additionally
// configuring the socket to have a reusable binding. // configuring the socket to have a reusable binding.
func listenPacketReusable(ifaceName, network, address string) (c net.PacketConn, err error) { func listenPacketReusable(ifaceName, network, address string) (c net.PacketConn, err error) {
var port int var port uint16
_, port, err = netutil.SplitHostPort(address) _, port, err = netutil.SplitHostPort(address)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// TODO(e.burkov): Inspect nclient4.NewRawUDPConn and implement here. // TODO(e.burkov): Inspect nclient4.NewRawUDPConn and implement here.
return nclient4.NewRawUDPConn(ifaceName, port) return nclient4.NewRawUDPConn(ifaceName, int(port))
} }

View file

@ -9,6 +9,7 @@ import (
"io" "io"
"net" "net"
"net/netip" "net/netip"
"net/url"
"syscall" "syscall"
"github.com/AdguardTeam/AdGuardHome/internal/aghos" "github.com/AdguardTeam/AdGuardHome/internal/aghos"
@ -263,7 +264,7 @@ func IsAddrInUse(err error) (ok bool) {
// CollectAllIfacesAddrs returns the slice of all network interfaces IP // CollectAllIfacesAddrs returns the slice of all network interfaces IP
// addresses without port number. // addresses without port number.
func CollectAllIfacesAddrs() (addrs []string, err error) { func CollectAllIfacesAddrs() (addrs []netip.Addr, err error) {
var ifaceAddrs []net.Addr var ifaceAddrs []net.Addr
ifaceAddrs, err = netInterfaceAddrs() ifaceAddrs, err = netInterfaceAddrs()
if err != nil { if err != nil {
@ -271,19 +272,41 @@ func CollectAllIfacesAddrs() (addrs []string, err error) {
} }
for _, addr := range ifaceAddrs { for _, addr := range ifaceAddrs {
cidr := addr.String() var p netip.Prefix
var ip net.IP p, err = netip.ParsePrefix(addr.String())
ip, _, err = net.ParseCIDR(cidr)
if err != nil { if err != nil {
return nil, fmt.Errorf("parsing cidr: %w", err) // Don't wrap the error since it's informative enough as is.
return nil, err
} }
addrs = append(addrs, ip.String()) addrs = append(addrs, p.Addr())
} }
return addrs, nil return addrs, nil
} }
// ParseAddrPort parses an [netip.AddrPort] from s, which should be either a
// valid IP, optionally with port, or a valid URL with plain IP address. The
// defaultPort is used if s doesn't contain port number.
func ParseAddrPort(s string, defaultPort uint16) (ipp netip.AddrPort, err error) {
u, err := url.Parse(s)
if err == nil && u.Host != "" {
s = u.Host
}
ipp, err = netip.ParseAddrPort(s)
if err != nil {
ip, parseErr := netip.ParseAddr(s)
if parseErr != nil {
return ipp, errors.Join(err, parseErr)
}
return netip.AddrPortFrom(ip, defaultPort), nil
}
return ipp, nil
}
// BroadcastFromPref calculates the broadcast IP address for p. // BroadcastFromPref calculates the broadcast IP address for p.
func BroadcastFromPref(p netip.Prefix) (bc netip.Addr) { func BroadcastFromPref(p netip.Prefix) (bc netip.Addr) {
bc = p.Addr().Unmap() bc = p.Addr().Unmap()

View file

@ -230,7 +230,7 @@ func TestCollectAllIfacesAddrs(t *testing.T) {
name string name string
wantErrMsg string wantErrMsg string
addrs []net.Addr addrs []net.Addr
wantAddrs []string wantAddrs []netip.Addr
}{{ }{{
name: "success", name: "success",
wantErrMsg: ``, wantErrMsg: ``,
@ -241,10 +241,13 @@ func TestCollectAllIfacesAddrs(t *testing.T) {
IP: net.IP{4, 3, 2, 1}, IP: net.IP{4, 3, 2, 1},
Mask: net.CIDRMask(16, netutil.IPv4BitLen), Mask: net.CIDRMask(16, netutil.IPv4BitLen),
}}, }},
wantAddrs: []string{"1.2.3.4", "4.3.2.1"}, wantAddrs: []netip.Addr{
netip.MustParseAddr("1.2.3.4"),
netip.MustParseAddr("4.3.2.1"),
},
}, { }, {
name: "not_cidr", name: "not_cidr",
wantErrMsg: `parsing cidr: invalid CIDR address: 1.2.3.4`, wantErrMsg: `netip.ParsePrefix("1.2.3.4"): no '/'`,
addrs: []net.Addr{&net.IPAddr{ addrs: []net.Addr{&net.IPAddr{
IP: net.IP{1, 2, 3, 4}, IP: net.IP{1, 2, 3, 4},
}}, }},
@ -269,12 +272,11 @@ func TestCollectAllIfacesAddrs(t *testing.T) {
t.Run("internal_error", func(t *testing.T) { t.Run("internal_error", func(t *testing.T) {
const errAddrs errors.Error = "can't get addresses" const errAddrs errors.Error = "can't get addresses"
const wantErrMsg string = `getting interfaces addresses: ` + string(errAddrs)
substNetInterfaceAddrs(t, func() ([]net.Addr, error) { return nil, errAddrs }) substNetInterfaceAddrs(t, func() ([]net.Addr, error) { return nil, errAddrs })
_, err := CollectAllIfacesAddrs() _, err := CollectAllIfacesAddrs()
testutil.AssertErrorMsg(t, wantErrMsg, err) assert.ErrorIs(t, err, errAddrs)
}) })
} }

View file

@ -2,10 +2,16 @@ package aghnet_test
import ( import (
"io/fs" "io/fs"
"net"
"net/netip"
"net/url"
"os" "os"
"testing" "testing"
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/testutil" "github.com/AdguardTeam/golibs/testutil"
"github.com/stretchr/testify/assert"
) )
func TestMain(m *testing.M) { func TestMain(m *testing.M) {
@ -14,3 +20,76 @@ func TestMain(m *testing.M) {
// testdata is the filesystem containing data for testing the package. // testdata is the filesystem containing data for testing the package.
var testdata fs.FS = os.DirFS("./testdata") var testdata fs.FS = os.DirFS("./testdata")
func TestParseAddrPort(t *testing.T) {
const defaultPort = 1
v4addr := netip.MustParseAddr("1.2.3.4")
testCases := []struct {
name string
input string
wantErrMsg string
want netip.AddrPort
}{{
name: "success_ip",
input: v4addr.String(),
wantErrMsg: "",
want: netip.AddrPortFrom(v4addr, defaultPort),
}, {
name: "success_ip_port",
input: netutil.JoinHostPort(v4addr.String(), 5),
wantErrMsg: "",
want: netip.AddrPortFrom(v4addr, 5),
}, {
name: "success_url",
input: (&url.URL{
Scheme: "tcp",
Host: v4addr.String(),
}).String(),
wantErrMsg: "",
want: netip.AddrPortFrom(v4addr, defaultPort),
}, {
name: "success_url_port",
input: (&url.URL{
Scheme: "tcp",
Host: netutil.JoinHostPort(v4addr.String(), 5),
}).String(),
wantErrMsg: "",
want: netip.AddrPortFrom(v4addr, 5),
}, {
name: "error_invalid_ip",
input: "256.256.256.256",
wantErrMsg: `not an ip:port
ParseAddr("256.256.256.256"): IPv4 field has value >255`,
want: netip.AddrPort{},
}, {
name: "error_invalid_port",
input: net.JoinHostPort(v4addr.String(), "-5"),
wantErrMsg: `invalid port "-5" parsing "1.2.3.4:-5"
ParseAddr("1.2.3.4:-5"): unexpected character (at ":-5")`,
want: netip.AddrPort{},
}, {
name: "error_invalid_url",
input: "tcp:://1.2.3.4",
wantErrMsg: `invalid port "//1.2.3.4" parsing "tcp:://1.2.3.4"
ParseAddr("tcp:://1.2.3.4"): each colon-separated field must have at least ` +
`one digit (at "tcp:://1.2.3.4")`,
want: netip.AddrPort{},
}, {
name: "empty",
input: "",
want: netip.AddrPort{},
wantErrMsg: `not an ip:port
ParseAddr(""): unable to parse IP`,
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
ap, err := aghnet.ParseAddrPort(tc.input, defaultPort)
testutil.AssertErrorMsg(t, tc.wantErrMsg, err)
assert.Equal(t, tc.want, ap)
})
}
}

View file

@ -1,36 +0,0 @@
package aghnet
// DefaultRefreshIvl is the default period of time between refreshing cached
// addresses.
// const DefaultRefreshIvl = 5 * time.Minute
// HostGenFunc is the signature for functions generating fake hostnames. The
// implementation must be safe for concurrent use.
type HostGenFunc func() (host string)
// SystemResolvers helps to work with local resolvers' addresses provided by OS.
type SystemResolvers interface {
// Get returns the slice of local resolvers' addresses. It must be safe for
// concurrent use.
Get() (rs []string)
// refresh refreshes the local resolvers' addresses cache. It must be safe
// for concurrent use.
refresh() (err error)
}
// NewSystemResolvers returns a SystemResolvers with the cache refresh rate
// defined by refreshIvl. It disables auto-refreshing if refreshIvl is 0. If
// nil is passed for hostGenFunc, the default generator will be used.
func NewSystemResolvers(
hostGenFunc HostGenFunc,
) (sr SystemResolvers, err error) {
sr = newSystemResolvers(hostGenFunc)
// Fill cache.
err = sr.refresh()
if err != nil {
return nil, err
}
return sr, nil
}

View file

@ -1,146 +0,0 @@
//go:build !windows
package aghnet
import (
"context"
"fmt"
"net"
"strings"
"sync"
"time"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/stringutil"
)
// defaultHostGen is the default method of generating host for Refresh.
func defaultHostGen() (host string) {
// TODO(e.burkov): Use strings.Builder.
return fmt.Sprintf("test%d.org", time.Now().UnixNano())
}
// systemResolvers is a default implementation of SystemResolvers interface.
type systemResolvers struct {
// addrsLock protects addrs.
addrsLock sync.RWMutex
// addrs is the set that contains cached local resolvers' addresses.
addrs *stringutil.Set
// resolver is used to fetch the resolvers' addresses.
resolver *net.Resolver
// hostGenFunc generates hosts to resolve.
hostGenFunc HostGenFunc
}
const (
// errBadAddrPassed is returned when dialFunc can't parse an IP address.
errBadAddrPassed errors.Error = "the passed string is not a valid IP address"
// errFakeDial is an error which dialFunc is expected to return.
errFakeDial errors.Error = "this error signals the successful dialFunc work"
// errUnexpectedHostFormat is returned by validateDialedHost when the host has
// more than one percent sign.
errUnexpectedHostFormat errors.Error = "unexpected host format"
)
// refresh implements the SystemResolvers interface for *systemResolvers.
func (sr *systemResolvers) refresh() (err error) {
defer func() { err = errors.Annotate(err, "systemResolvers: %w") }()
_, err = sr.resolver.LookupHost(context.Background(), sr.hostGenFunc())
dnserr := &net.DNSError{}
if errors.As(err, &dnserr) && dnserr.Err == errFakeDial.Error() {
return nil
}
return err
}
func newSystemResolvers(hostGenFunc HostGenFunc) (sr SystemResolvers) {
if hostGenFunc == nil {
hostGenFunc = defaultHostGen
}
s := &systemResolvers{
resolver: &net.Resolver{
PreferGo: true,
},
hostGenFunc: hostGenFunc,
addrs: stringutil.NewSet(),
}
s.resolver.Dial = s.dialFunc
return s
}
// validateDialedHost validated the host used by resolvers in dialFunc.
func validateDialedHost(host string) (err error) {
defer func() { err = errors.Annotate(err, "parsing %q: %w", host) }()
parts := strings.Split(host, "%")
switch len(parts) {
case 1:
// host
case 2:
// Remove the zone and check the IP address part.
host = parts[0]
default:
return errUnexpectedHostFormat
}
if _, err = netutil.ParseIP(host); err != nil {
return errBadAddrPassed
}
return nil
}
// dockerEmbeddedDNS is the address of Docker's embedded DNS server.
//
// See
// https://github.com/moby/moby/blob/v1.12.0/docs/userguide/networking/dockernetworks.md.
const dockerEmbeddedDNS = "127.0.0.11"
// dialFunc gets the resolver's address and puts it into internal cache.
func (sr *systemResolvers) dialFunc(_ context.Context, _, address string) (_ net.Conn, err error) {
// Just validate the passed address is a valid IP.
var host string
host, err = netutil.SplitHost(address)
if err != nil {
// TODO(e.burkov): Maybe use a structured errBadAddrPassed to
// allow unwrapping of the real error.
return nil, fmt.Errorf("%s: %w", err, errBadAddrPassed)
}
// Exclude Docker's embedded DNS server, as it may cause recursion if
// the container is set as the host system's default DNS server.
//
// See https://github.com/AdguardTeam/AdGuardHome/issues/3064.
//
// TODO(a.garipov): Perhaps only do this when we are in the container?
// Maybe use an environment variable?
if host == dockerEmbeddedDNS {
return nil, errFakeDial
}
err = validateDialedHost(host)
if err != nil {
return nil, fmt.Errorf("validating dialed host: %w", err)
}
sr.addrsLock.Lock()
defer sr.addrsLock.Unlock()
sr.addrs.Add(host)
return nil, errFakeDial
}
func (sr *systemResolvers) Get() (rs []string) {
sr.addrsLock.RLock()
defer sr.addrsLock.RUnlock()
return sr.addrs.Values()
}

View file

@ -1,81 +0,0 @@
//go:build !windows
package aghnet
import (
"context"
"testing"
"github.com/AdguardTeam/golibs/testutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func createTestSystemResolversImpl(
t *testing.T,
hostGenFunc HostGenFunc,
) (imp *systemResolvers) {
t.Helper()
sr := createTestSystemResolvers(t, hostGenFunc)
return testutil.RequireTypeAssert[*systemResolvers](t, sr)
}
func TestSystemResolvers_Refresh(t *testing.T) {
t.Run("expected_error", func(t *testing.T) {
sr := createTestSystemResolvers(t, nil)
assert.NoError(t, sr.refresh())
})
t.Run("unexpected_error", func(t *testing.T) {
_, err := NewSystemResolvers(func() string {
return "127.0.0.1::123"
})
assert.Error(t, err)
})
}
func TestSystemResolvers_DialFunc(t *testing.T) {
imp := createTestSystemResolversImpl(t, nil)
testCases := []struct {
want error
name string
address string
}{{
want: errFakeDial,
name: "valid_ipv4",
address: "127.0.0.1",
}, {
want: errFakeDial,
name: "valid_ipv6_port",
address: "[::1]:53",
}, {
want: errFakeDial,
name: "valid_ipv6_zone_port",
address: "[::1%lo0]:53",
}, {
want: errBadAddrPassed,
name: "invalid_split_host",
address: "127.0.0.1::123",
}, {
want: errUnexpectedHostFormat,
name: "invalid_ipv6_zone_port",
address: "[::1%%lo0]:53",
}, {
want: errBadAddrPassed,
name: "invalid_parse_ip",
address: "not-ip",
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
conn, err := imp.dialFunc(context.Background(), "", tc.address)
require.Nil(t, conn)
assert.ErrorIs(t, err, tc.want)
})
}
}

View file

@ -1,37 +0,0 @@
package aghnet
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func createTestSystemResolvers(
t *testing.T,
hostGenFunc HostGenFunc,
) (sr SystemResolvers) {
t.Helper()
var err error
sr, err = NewSystemResolvers(hostGenFunc)
require.NoError(t, err)
require.NotNil(t, sr)
return sr
}
func TestSystemResolvers_Get(t *testing.T) {
sr := createTestSystemResolvers(t, nil)
var rs []string
require.NotPanics(t, func() {
rs = sr.Get()
})
assert.NotEmpty(t, rs)
}
// TODO(e.burkov): Write tests for refreshWithTicker.
//
// See https://github.com/AdguardTeam/AdGuardHome/issues/2846.

View file

@ -1,163 +0,0 @@
//go:build windows
package aghnet
import (
"bufio"
"fmt"
"io"
"net"
"os/exec"
"strings"
"sync"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
)
// systemResolvers implementation differs for Windows since Go's resolver
// doesn't work there.
//
// See https://github.com/golang/go/issues/33097.
type systemResolvers struct {
// addrs is the slice of cached local resolvers' addresses.
addrs []string
addrsLock sync.RWMutex
}
func newSystemResolvers(_ HostGenFunc) (sr SystemResolvers) {
return &systemResolvers{}
}
func (sr *systemResolvers) Get() (rs []string) {
sr.addrsLock.RLock()
defer sr.addrsLock.RUnlock()
addrs := sr.addrs
rs = make([]string, len(addrs))
copy(rs, addrs)
return rs
}
// writeExit writes "exit" to w and closes it. It is supposed to be run in
// a goroutine.
func writeExit(w io.WriteCloser) {
defer log.OnPanic("systemResolvers: writeExit")
defer func() {
derr := w.Close()
if derr != nil {
log.Error("systemResolvers: writeExit: closing: %s", derr)
}
}()
_, err := io.WriteString(w, "exit")
if err != nil {
log.Error("systemResolvers: writeExit: writing: %s", err)
}
}
// scanAddrs scans the DNS addresses from nslookup's output. The expected
// output of nslookup looks like this:
//
// Default Server: 192-168-1-1.qualified.domain.ru
// Address: 192.168.1.1
func scanAddrs(s *bufio.Scanner) (addrs []string) {
for s.Scan() {
line := strings.TrimSpace(s.Text())
fields := strings.Fields(line)
if len(fields) != 2 || fields[0] != "Address:" {
continue
}
// If the address contains port then it is separated with '#'.
ipPort := strings.Split(fields[1], "#")
if len(ipPort) == 0 {
continue
}
addr := ipPort[0]
if net.ParseIP(addr) == nil {
log.Debug("systemResolvers: %q is not a valid ip", addr)
continue
}
addrs = append(addrs, addr)
}
return addrs
}
// getAddrs gets local resolvers' addresses from OS in a special Windows way.
//
// TODO(e.burkov): This whole function needs more detailed research on getting
// local resolvers addresses on Windows. We execute the external command for
// now that is not the most accurate way.
func (sr *systemResolvers) getAddrs() (addrs []string, err error) {
var cmdPath string
cmdPath, err = exec.LookPath("nslookup.exe")
if err != nil {
return nil, fmt.Errorf("looking up cmd path: %w", err)
}
cmd := exec.Command(cmdPath)
var stdin io.WriteCloser
stdin, err = cmd.StdinPipe()
if err != nil {
return nil, fmt.Errorf("getting the command's stdin pipe: %w", err)
}
var stdout io.ReadCloser
stdout, err = cmd.StdoutPipe()
if err != nil {
return nil, fmt.Errorf("getting the command's stdout pipe: %w", err)
}
go writeExit(stdin)
err = cmd.Start()
if err != nil {
return nil, fmt.Errorf("start command executing: %w", err)
}
s := bufio.NewScanner(stdout)
addrs = scanAddrs(s)
err = cmd.Wait()
if err != nil {
return nil, fmt.Errorf("executing the command: %w", err)
}
err = s.Err()
if err != nil {
return nil, fmt.Errorf("scanning output: %w", err)
}
// Don't close StdoutPipe since Wait do it for us in ¿most? cases.
//
// See go doc os/exec.Cmd.StdoutPipe.
return addrs, nil
}
func (sr *systemResolvers) refresh() (err error) {
defer func() { err = errors.Annotate(err, "systemResolvers: %w") }()
got, err := sr.getAddrs()
if err != nil {
return fmt.Errorf("can't get addresses: %w", err)
}
if len(got) == 0 {
return nil
}
sr.addrsLock.Lock()
defer sr.addrsLock.Unlock()
sr.addrs = got
return nil
}

View file

@ -1,7 +0,0 @@
//go:build windows
package aghnet
// TODO(e.burkov): Write tests for Windows implementation.
//
// See https://github.com/AdguardTeam/AdGuardHome/issues/2846.

View file

@ -4,7 +4,7 @@ import (
"bytes" "bytes"
"testing" "testing"
"github.com/AdguardTeam/AdGuardHome/internal/aghio" "github.com/AdguardTeam/golibs/ioutil"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -72,11 +72,10 @@ func TestLargestLabeled(t *testing.T) {
} }
t.Run("scanner_fail", func(t *testing.T) { t.Run("scanner_fail", func(t *testing.T) {
lr, err := aghio.LimitReader(bytes.NewReader([]byte{1, 2, 3}), 0) lr := ioutil.LimitReader(bytes.NewReader([]byte{1, 2, 3}), 0)
require.NoError(t, err)
target := &aghio.LimitReachedError{} target := &ioutil.LimitError{}
_, _, err = parsePSOutput(lr, "", nil) _, _, err := parsePSOutput(lr, "", nil)
require.ErrorAs(t, err, &target) require.ErrorAs(t, err, &target)
assert.EqualValues(t, 0, target.Limit) assert.EqualValues(t, 0, target.Limit)

View file

@ -4,10 +4,14 @@ package aghtest
import ( import (
"crypto/sha256" "crypto/sha256"
"io" "io"
"net/http"
"net/http/httptest"
"net/netip" "net/netip"
"net/url"
"testing" "testing"
"github.com/AdguardTeam/golibs/log" "github.com/AdguardTeam/golibs/log"
"github.com/stretchr/testify/require"
) )
const ( const (
@ -51,3 +55,19 @@ func HostToIPs(host string) (ipv4, ipv6 netip.Addr) {
return netip.AddrFrom4([4]byte(hash[:4])), netip.AddrFrom16([16]byte(hash[4:20])) return netip.AddrFrom4([4]byte(hash[:4])), netip.AddrFrom16([16]byte(hash[4:20]))
} }
// StartHTTPServer is a helper that starts the HTTP server, which is configured
// to return data on every request, and returns the client and server URL.
func StartHTTPServer(t testing.TB, data []byte) (c *http.Client, u *url.URL) {
t.Helper()
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
_, _ = w.Write(data)
}))
t.Cleanup(srv.Close)
u, err := url.Parse(srv.URL)
require.NoError(t, err)
return srv.Client(), u
}

View file

@ -57,6 +57,9 @@ type DHCPServer interface {
// RemoveStaticLease - remove a static lease // RemoveStaticLease - remove a static lease
RemoveStaticLease(l *Lease) (err error) RemoveStaticLease(l *Lease) (err error)
// UpdateStaticLease updates IP, hostname of the lease.
UpdateStaticLease(l *Lease) (err error)
// FindMACbyIP returns a MAC address by the IP address of its lease, if // FindMACbyIP returns a MAC address by the IP address of its lease, if
// there is one. // there is one.
FindMACbyIP(ip netip.Addr) (mac net.HardwareAddr) FindMACbyIP(ip netip.Addr) (mac net.HardwareAddr)

View file

@ -5,6 +5,7 @@ package dhcpd
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io"
"net" "net"
"net/http" "net/http"
"net/netip" "net/netip"
@ -290,12 +291,12 @@ func (s *server) handleDHCPSetConfigV6(
func (s *server) createServers(conf *dhcpServerConfigJSON) (srv4, srv6 DHCPServer, err error) { func (s *server) createServers(conf *dhcpServerConfigJSON) (srv4, srv6 DHCPServer, err error) {
srv4, v4Enabled, err := s.handleDHCPSetConfigV4(conf) srv4, v4Enabled, err := s.handleDHCPSetConfigV4(conf)
if err != nil { if err != nil {
return nil, nil, fmt.Errorf("bad dhcpv4 configuration: %s", err) return nil, nil, fmt.Errorf("bad dhcpv4 configuration: %w", err)
} }
srv6, v6Enabled, err := s.handleDHCPSetConfigV6(conf) srv6, v6Enabled, err := s.handleDHCPSetConfigV6(conf)
if err != nil { if err != nil {
return nil, nil, fmt.Errorf("bad dhcpv6 configuration: %s", err) return nil, nil, fmt.Errorf("bad dhcpv6 configuration: %w", err)
} }
if conf.Enabled == aghalg.NBTrue && !v4Enabled && !v6Enabled { if conf.Enabled == aghalg.NBTrue && !v4Enabled && !v6Enabled {
@ -424,7 +425,7 @@ func newNetInterfaceJSON(iface net.Interface) (out *netInterfaceJSON, err error)
addrs, err := iface.Addrs() addrs, err := iface.Addrs()
if err != nil { if err != nil {
return nil, fmt.Errorf( return nil, fmt.Errorf(
"failed to get addresses for interface %s: %s", "failed to get addresses for interface %s: %w",
iface.Name, iface.Name,
err, err,
) )
@ -590,82 +591,78 @@ func setOtherDHCPResult(ifaceName string, result *dhcpSearchResult) {
} }
} }
func (s *server) handleDHCPAddStaticLease(w http.ResponseWriter, r *http.Request) { // 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 *Lease, err error) {
l := &leaseStatic{} l := &leaseStatic{}
err := json.NewDecoder(r.Body).Decode(l) err = json.NewDecoder(r).Decode(l)
if err != nil { if err != nil {
aghhttp.Error(r, w, http.StatusBadRequest, "json.Decode: %s", err) return nil, nil, fmt.Errorf("decoding json: %w", err)
return
} }
if !l.IP.IsValid() { if !l.IP.IsValid() {
aghhttp.Error(r, w, http.StatusBadRequest, "invalid IP") return nil, nil, errors.Error("invalid ip")
return
} }
l.IP = l.IP.Unmap() l.IP = l.IP.Unmap()
var srv DHCPServer lease, err = l.toLease()
if l.IP.Is4() { if err != nil {
return nil, nil, fmt.Errorf("parsing: %w", err)
}
if lease.IP.Is4() {
srv = s.srv4 srv = s.srv4
} else { } else {
srv = s.srv6 srv = s.srv6
} }
lease, err := l.toLease() return srv, lease, nil
if err != nil { }
aghhttp.Error(r, w, http.StatusBadRequest, "parsing: %s", err)
return // handleDHCPAddStaticLease is the handler for the POST
} // /control/dhcp/add_static_lease HTTP API.
func (s *server) handleDHCPAddStaticLease(w http.ResponseWriter, r *http.Request) {
err = srv.AddStaticLease(lease) srv, lease, err := s.parseLease(r.Body)
if err != nil { if err != nil {
aghhttp.Error(r, w, http.StatusBadRequest, "%s", err) aghhttp.Error(r, w, http.StatusBadRequest, "%s", err)
return return
} }
if err = srv.AddStaticLease(lease); err != nil {
aghhttp.Error(r, w, http.StatusBadRequest, "%s", err)
}
} }
// handleDHCPRemoveStaticLease is the handler for the POST
// /control/dhcp/remove_static_lease HTTP API.
func (s *server) handleDHCPRemoveStaticLease(w http.ResponseWriter, r *http.Request) { func (s *server) handleDHCPRemoveStaticLease(w http.ResponseWriter, r *http.Request) {
l := &leaseStatic{} srv, lease, err := s.parseLease(r.Body)
err := json.NewDecoder(r.Body).Decode(l)
if err != nil {
aghhttp.Error(r, w, http.StatusBadRequest, "json.Decode: %s", err)
return
}
if !l.IP.IsValid() {
aghhttp.Error(r, w, http.StatusBadRequest, "invalid IP")
return
}
l.IP = l.IP.Unmap()
var srv DHCPServer
if l.IP.Is4() {
srv = s.srv4
} else {
srv = s.srv6
}
lease, err := l.toLease()
if err != nil {
aghhttp.Error(r, w, http.StatusBadRequest, "parsing: %s", err)
return
}
err = srv.RemoveStaticLease(lease)
if err != nil { if err != nil {
aghhttp.Error(r, w, http.StatusBadRequest, "%s", err) aghhttp.Error(r, w, http.StatusBadRequest, "%s", err)
return return
} }
if err = srv.RemoveStaticLease(lease); err != nil {
aghhttp.Error(r, w, http.StatusBadRequest, "%s", err)
}
}
// handleDHCPUpdateStaticLease is the handler for the POST
// /control/dhcp/update_static_lease HTTP API.
func (s *server) handleDHCPUpdateStaticLease(w http.ResponseWriter, r *http.Request) {
srv, lease, err := s.parseLease(r.Body)
if err != nil {
aghhttp.Error(r, w, http.StatusBadRequest, "%s", err)
return
}
if err = srv.UpdateStaticLease(lease); err != nil {
aghhttp.Error(r, w, http.StatusBadRequest, "%s", err)
}
} }
func (s *server) handleReset(w http.ResponseWriter, r *http.Request) { func (s *server) handleReset(w http.ResponseWriter, r *http.Request) {
@ -729,6 +726,7 @@ func (s *server) registerHandlers() {
s.conf.HTTPRegister(http.MethodPost, "/control/dhcp/find_active_dhcp", s.handleDHCPFindActiveServer) s.conf.HTTPRegister(http.MethodPost, "/control/dhcp/find_active_dhcp", s.handleDHCPFindActiveServer)
s.conf.HTTPRegister(http.MethodPost, "/control/dhcp/add_static_lease", s.handleDHCPAddStaticLease) s.conf.HTTPRegister(http.MethodPost, "/control/dhcp/add_static_lease", s.handleDHCPAddStaticLease)
s.conf.HTTPRegister(http.MethodPost, "/control/dhcp/remove_static_lease", s.handleDHCPRemoveStaticLease) s.conf.HTTPRegister(http.MethodPost, "/control/dhcp/remove_static_lease", s.handleDHCPRemoveStaticLease)
s.conf.HTTPRegister(http.MethodPost, "/control/dhcp/update_static_lease", s.handleDHCPUpdateStaticLease)
s.conf.HTTPRegister(http.MethodPost, "/control/dhcp/reset", s.handleReset) s.conf.HTTPRegister(http.MethodPost, "/control/dhcp/reset", s.handleReset)
s.conf.HTTPRegister(http.MethodPost, "/control/dhcp/reset_leases", s.handleResetLeases) s.conf.HTTPRegister(http.MethodPost, "/control/dhcp/reset_leases", s.handleResetLeases)
} }

View file

@ -0,0 +1,319 @@
//go:build darwin || freebsd || linux || openbsd
package dhcpd
import (
"bytes"
"encoding/json"
"net/http"
"net/http/httptest"
"net/netip"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// defaultResponse is a helper that returns the response with default
// configuration.
func defaultResponse() *dhcpStatusResponse {
conf4 := defaultV4ServerConf()
conf4.LeaseDuration = 86400
resp := &dhcpStatusResponse{
V4: *conf4,
V6: V6ServerConf{},
Leases: []*leaseDynamic{},
StaticLeases: []*leaseStatic{},
Enabled: true,
}
return resp
}
// handleLease is the helper function that calls handler with provided static
// lease as body and returns modified response recorder.
func handleLease(t *testing.T, lease *leaseStatic, handler http.HandlerFunc) (w *httptest.ResponseRecorder) {
t.Helper()
w = httptest.NewRecorder()
b := &bytes.Buffer{}
err := json.NewEncoder(b).Encode(lease)
require.NoError(t, err)
var r *http.Request
r, err = http.NewRequest(http.MethodPost, "", b)
require.NoError(t, err)
handler(w, r)
return w
}
// checkStatus is a helper that asserts the response of
// [*server.handleDHCPStatus].
func checkStatus(t *testing.T, s *server, want *dhcpStatusResponse) {
w := httptest.NewRecorder()
b := &bytes.Buffer{}
err := json.NewEncoder(b).Encode(&want)
require.NoError(t, err)
r, err := http.NewRequest(http.MethodPost, "", b)
require.NoError(t, err)
s.handleDHCPStatus(w, r)
assert.Equal(t, http.StatusOK, w.Code)
assert.JSONEq(t, b.String(), w.Body.String())
}
func TestServer_handleDHCPStatus(t *testing.T) {
const (
staticName = "static-client"
staticMAC = "aa:aa:aa:aa:aa:aa"
)
staticIP := netip.MustParseAddr("192.168.10.10")
staticLease := &leaseStatic{
HWAddr: staticMAC,
IP: staticIP,
Hostname: staticName,
}
s, err := Create(&ServerConfig{
Enabled: true,
Conf4: *defaultV4ServerConf(),
DataDir: t.TempDir(),
ConfigModified: func() {},
})
require.NoError(t, err)
ok := t.Run("status", func(t *testing.T) {
resp := defaultResponse()
checkStatus(t, s, resp)
})
require.True(t, ok)
ok = t.Run("add_static_lease", func(t *testing.T) {
w := handleLease(t, staticLease, s.handleDHCPAddStaticLease)
assert.Equal(t, http.StatusOK, w.Code)
resp := defaultResponse()
resp.StaticLeases = []*leaseStatic{staticLease}
checkStatus(t, s, resp)
})
require.True(t, ok)
ok = t.Run("add_invalid_lease", func(t *testing.T) {
w := handleLease(t, staticLease, s.handleDHCPAddStaticLease)
assert.Equal(t, http.StatusBadRequest, w.Code)
})
require.True(t, ok)
ok = t.Run("remove_static_lease", func(t *testing.T) {
w := handleLease(t, staticLease, s.handleDHCPRemoveStaticLease)
assert.Equal(t, http.StatusOK, w.Code)
resp := defaultResponse()
checkStatus(t, s, resp)
})
require.True(t, ok)
ok = t.Run("set_config", func(t *testing.T) {
w := httptest.NewRecorder()
resp := defaultResponse()
resp.Enabled = false
b := &bytes.Buffer{}
err = json.NewEncoder(b).Encode(&resp)
require.NoError(t, err)
var r *http.Request
r, err = http.NewRequest(http.MethodPost, "", b)
require.NoError(t, err)
s.handleDHCPSetConfig(w, r)
assert.Equal(t, http.StatusOK, w.Code)
checkStatus(t, s, resp)
})
require.True(t, ok)
}
func TestServer_HandleUpdateStaticLease(t *testing.T) {
const (
leaseV4Name = "static-client-v4"
leaseV4MAC = "44:44:44:44:44:44"
leaseV6Name = "static-client-v6"
leaseV6MAC = "66:66:66:66:66:66"
)
leaseV4IP := netip.MustParseAddr("192.168.10.10")
leaseV6IP := netip.MustParseAddr("2001::6")
const (
leaseV4Pos = iota
leaseV6Pos
)
leases := []*leaseStatic{
leaseV4Pos: {
HWAddr: leaseV4MAC,
IP: leaseV4IP,
Hostname: leaseV4Name,
},
leaseV6Pos: {
HWAddr: leaseV6MAC,
IP: leaseV6IP,
Hostname: leaseV6Name,
},
}
s, err := Create(&ServerConfig{
Enabled: true,
Conf4: *defaultV4ServerConf(),
Conf6: V6ServerConf{},
DataDir: t.TempDir(),
ConfigModified: func() {},
})
require.NoError(t, err)
for _, l := range leases {
w := handleLease(t, l, s.handleDHCPAddStaticLease)
assert.Equal(t, http.StatusOK, w.Code)
}
testCases := []struct {
name string
pos int
lease *leaseStatic
}{{
name: "update_v4_name",
pos: leaseV4Pos,
lease: &leaseStatic{
HWAddr: leaseV4MAC,
IP: leaseV4IP,
Hostname: "updated-client-v4",
},
}, {
name: "update_v4_ip",
pos: leaseV4Pos,
lease: &leaseStatic{
HWAddr: leaseV4MAC,
IP: netip.MustParseAddr("192.168.10.200"),
Hostname: "updated-client-v4",
},
}, {
name: "update_v6_name",
pos: leaseV6Pos,
lease: &leaseStatic{
HWAddr: leaseV6MAC,
IP: leaseV6IP,
Hostname: "updated-client-v6",
},
}, {
name: "update_v6_ip",
pos: leaseV6Pos,
lease: &leaseStatic{
HWAddr: leaseV6MAC,
IP: netip.MustParseAddr("2001::666"),
Hostname: "updated-client-v6",
},
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
w := handleLease(t, tc.lease, s.handleDHCPUpdateStaticLease)
assert.Equal(t, http.StatusOK, w.Code)
resp := defaultResponse()
leases[tc.pos] = tc.lease
resp.StaticLeases = leases
checkStatus(t, s, resp)
})
}
}
func TestServer_HandleUpdateStaticLease_validation(t *testing.T) {
const (
leaseV4Name = "static-client-v4"
leaseV4MAC = "44:44:44:44:44:44"
anotherV4Name = "another-client-v4"
anotherV4MAC = "55:55:55:55:55:55"
)
leaseV4IP := netip.MustParseAddr("192.168.10.10")
anotherV4IP := netip.MustParseAddr("192.168.10.20")
leases := []*leaseStatic{{
HWAddr: leaseV4MAC,
IP: leaseV4IP,
Hostname: leaseV4Name,
}, {
HWAddr: anotherV4MAC,
IP: anotherV4IP,
Hostname: anotherV4Name,
}}
s, err := Create(&ServerConfig{
Enabled: true,
Conf4: *defaultV4ServerConf(),
Conf6: V6ServerConf{},
DataDir: t.TempDir(),
ConfigModified: func() {},
})
require.NoError(t, err)
for _, l := range leases {
w := handleLease(t, l, s.handleDHCPAddStaticLease)
assert.Equal(t, http.StatusOK, w.Code)
}
testCases := []struct {
lease *leaseStatic
name string
want string
}{{
name: "v4_unknown_mac",
lease: &leaseStatic{
HWAddr: "aa:aa:aa:aa:aa:aa",
IP: leaseV4IP,
Hostname: leaseV4Name,
},
want: "dhcpv4: updating static lease: can't find lease aa:aa:aa:aa:aa:aa\n",
}, {
name: "update_v4_same_ip",
lease: &leaseStatic{
HWAddr: leaseV4MAC,
IP: anotherV4IP,
Hostname: leaseV4Name,
},
want: "dhcpv4: updating static lease: ip address is not unique\n",
}, {
name: "update_v4_same_name",
lease: &leaseStatic{
HWAddr: leaseV4MAC,
IP: leaseV4IP,
Hostname: anotherV4Name,
},
want: "dhcpv4: updating static lease: hostname is not unique\n",
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
w := handleLease(t, tc.lease, s.handleDHCPUpdateStaticLease)
assert.Equal(t, http.StatusBadRequest, w.Code)
assert.Equal(t, tc.want, w.Body.String())
})
}
}

View file

@ -1,159 +0,0 @@
//go:build darwin || freebsd || linux || openbsd
package dhcpd
import (
"bytes"
"encoding/json"
"net/http"
"net/http/httptest"
"net/netip"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestServer_handleDHCPStatus(t *testing.T) {
const (
staticName = "static-client"
staticMAC = "aa:aa:aa:aa:aa:aa"
)
staticIP := netip.MustParseAddr("192.168.10.10")
staticLease := &leaseStatic{
HWAddr: staticMAC,
IP: staticIP,
Hostname: staticName,
}
s, err := Create(&ServerConfig{
Enabled: true,
Conf4: *defaultV4ServerConf(),
DataDir: t.TempDir(),
ConfigModified: func() {},
})
require.NoError(t, err)
// checkStatus is a helper that asserts the response of
// [*server.handleDHCPStatus].
checkStatus := func(t *testing.T, want *dhcpStatusResponse) {
w := httptest.NewRecorder()
var req *http.Request
req, err = http.NewRequest(http.MethodGet, "", nil)
require.NoError(t, err)
b := &bytes.Buffer{}
err = json.NewEncoder(b).Encode(&want)
require.NoError(t, err)
s.handleDHCPStatus(w, req)
assert.Equal(t, http.StatusOK, w.Code)
assert.JSONEq(t, b.String(), w.Body.String())
}
// defaultResponse is a helper that returns the response with default
// configuration.
defaultResponse := func() *dhcpStatusResponse {
conf4 := defaultV4ServerConf()
conf4.LeaseDuration = 86400
resp := &dhcpStatusResponse{
V4: *conf4,
V6: V6ServerConf{},
Leases: []*leaseDynamic{},
StaticLeases: []*leaseStatic{},
Enabled: true,
}
return resp
}
ok := t.Run("status", func(t *testing.T) {
resp := defaultResponse()
checkStatus(t, resp)
})
require.True(t, ok)
ok = t.Run("add_static_lease", func(t *testing.T) {
w := httptest.NewRecorder()
b := &bytes.Buffer{}
err = json.NewEncoder(b).Encode(staticLease)
require.NoError(t, err)
var r *http.Request
r, err = http.NewRequest(http.MethodPost, "", b)
require.NoError(t, err)
s.handleDHCPAddStaticLease(w, r)
assert.Equal(t, http.StatusOK, w.Code)
resp := defaultResponse()
resp.StaticLeases = []*leaseStatic{staticLease}
checkStatus(t, resp)
})
require.True(t, ok)
ok = t.Run("add_invalid_lease", func(t *testing.T) {
w := httptest.NewRecorder()
b := &bytes.Buffer{}
err = json.NewEncoder(b).Encode(&leaseStatic{})
require.NoError(t, err)
var r *http.Request
r, err = http.NewRequest(http.MethodPost, "", b)
require.NoError(t, err)
s.handleDHCPAddStaticLease(w, r)
assert.Equal(t, http.StatusBadRequest, w.Code)
})
require.True(t, ok)
ok = t.Run("remove_static_lease", func(t *testing.T) {
w := httptest.NewRecorder()
b := &bytes.Buffer{}
err = json.NewEncoder(b).Encode(staticLease)
require.NoError(t, err)
var r *http.Request
r, err = http.NewRequest(http.MethodPost, "", b)
require.NoError(t, err)
s.handleDHCPRemoveStaticLease(w, r)
assert.Equal(t, http.StatusOK, w.Code)
resp := defaultResponse()
checkStatus(t, resp)
})
require.True(t, ok)
ok = t.Run("set_config", func(t *testing.T) {
w := httptest.NewRecorder()
resp := defaultResponse()
resp.Enabled = false
b := &bytes.Buffer{}
err = json.NewEncoder(b).Encode(&resp)
require.NoError(t, err)
var r *http.Request
r, err = http.NewRequest(http.MethodPost, "", b)
require.NoError(t, err)
s.handleDHCPSetConfig(w, r)
assert.Equal(t, http.StatusOK, w.Code)
checkStatus(t, resp)
})
require.True(t, ok)
}

View file

@ -43,6 +43,7 @@ func (s *server) registerHandlers() {
s.conf.HTTPRegister(http.MethodPost, "/control/dhcp/find_active_dhcp", s.notImplemented) s.conf.HTTPRegister(http.MethodPost, "/control/dhcp/find_active_dhcp", s.notImplemented)
s.conf.HTTPRegister(http.MethodPost, "/control/dhcp/add_static_lease", s.notImplemented) s.conf.HTTPRegister(http.MethodPost, "/control/dhcp/add_static_lease", s.notImplemented)
s.conf.HTTPRegister(http.MethodPost, "/control/dhcp/remove_static_lease", s.notImplemented) s.conf.HTTPRegister(http.MethodPost, "/control/dhcp/remove_static_lease", s.notImplemented)
s.conf.HTTPRegister(http.MethodPost, "/control/dhcp/update_static_lease", s.notImplemented)
s.conf.HTTPRegister(http.MethodPost, "/control/dhcp/reset", s.notImplemented) s.conf.HTTPRegister(http.MethodPost, "/control/dhcp/reset", s.notImplemented)
s.conf.HTTPRegister(http.MethodPost, "/control/dhcp/reset_leases", s.notImplemented) s.conf.HTTPRegister(http.MethodPost, "/control/dhcp/reset_leases", s.notImplemented)
} }

View file

@ -19,6 +19,7 @@ func (winServer) GetLeases(_ GetLeasesFlags) (leases []*Lease) { return nil }
func (winServer) getLeasesRef() []*Lease { return nil } func (winServer) getLeasesRef() []*Lease { return nil }
func (winServer) AddStaticLease(_ *Lease) (err error) { return nil } func (winServer) AddStaticLease(_ *Lease) (err error) { return nil }
func (winServer) RemoveStaticLease(_ *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) FindMACbyIP(_ netip.Addr) (mac net.HardwareAddr) { return nil }
func (winServer) WriteDiskConfig4(_ *V4ServerConf) {} func (winServer) WriteDiskConfig4(_ *V4ServerConf) {}
func (winServer) WriteDiskConfig6(_ *V6ServerConf) {} func (winServer) WriteDiskConfig6(_ *V6ServerConf) {}

View file

@ -148,6 +148,9 @@ func (s *v4Server) ResetLeases(leases []*Lease) (err error) {
return nil return nil
} }
s.leasesLock.Lock()
defer s.leasesLock.Unlock()
s.leasedOffsets = newBitSet() s.leasedOffsets = newBitSet()
s.hostsIndex = make(map[string]*Lease, len(leases)) s.hostsIndex = make(map[string]*Lease, len(leases))
s.ipIndex = make(map[netip.Addr]*Lease, len(leases)) s.ipIndex = make(map[netip.Addr]*Lease, len(leases))
@ -182,16 +185,13 @@ func (s *v4Server) isBlocklisted(l *Lease) (ok bool) {
return false return false
} }
ok = true
for _, b := range l.HWAddr { for _, b := range l.HWAddr {
if b != 0 { if b != 0 {
ok = false return false
break
} }
} }
return ok return true
} }
// GetLeases returns the list of current DHCP leases. It is safe for concurrent // GetLeases returns the list of current DHCP leases. It is safe for concurrent
@ -309,9 +309,15 @@ func (s *v4Server) rmDynamicLease(lease *Lease) (err error) {
return nil return nil
} }
// ErrDupHostname is returned by addLease when the added lease has a not empty const (
// non-unique hostname. // ErrDupHostname is returned by addLease, validateStaticLease when the
const ErrDupHostname = errors.Error("hostname is not unique") // modified lease has a not empty non-unique hostname.
ErrDupHostname = errors.Error("hostname is not unique")
// ErrDupIP is returned by addLease, validateStaticLease when the modified
// lease has a non-unique IP address.
ErrDupIP = errors.Error("ip address is not unique")
)
// addLease adds a dynamic or static lease. // addLease adds a dynamic or static lease.
func (s *v4Server) addLease(l *Lease) (err error) { func (s *v4Server) addLease(l *Lease) (err error) {
@ -428,6 +434,81 @@ func (s *v4Server) AddStaticLease(l *Lease) (err error) {
return nil return nil
} }
// UpdateStaticLease updates IP, hostname of the static lease.
func (s *v4Server) UpdateStaticLease(l *Lease) (err error) {
defer func() {
if err != nil {
err = errors.Annotate(err, "dhcpv4: updating static lease: %w")
return
}
s.conf.notify(LeaseChangedDBStore)
s.conf.notify(LeaseChangedRemovedStatic)
}()
s.leasesLock.Lock()
defer s.leasesLock.Unlock()
found := s.findLease(l.HWAddr)
if found == nil {
return fmt.Errorf("can't find lease %s", l.HWAddr)
}
err = s.validateStaticLease(l)
if err != nil {
return err
}
err = s.rmLease(found)
if err != nil {
return fmt.Errorf("removing previous lease for %s (%s): %w", l.IP, l.HWAddr, err)
}
err = s.addLease(l)
if err != nil {
return fmt.Errorf("adding updated static lease for %s (%s): %w", l.IP, l.HWAddr, err)
}
return nil
}
// validateStaticLease returns an error if the static lease is invalid.
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.
return err
}
err = netutil.ValidateHostname(hostname)
if err != nil {
return fmt.Errorf("validating hostname: %w", err)
}
dup, ok := s.hostsIndex[hostname]
if ok && !bytes.Equal(dup.HWAddr, l.HWAddr) {
return ErrDupHostname
}
dup, ok = s.ipIndex[l.IP]
if ok && !bytes.Equal(dup.HWAddr, l.HWAddr) {
return ErrDupIP
}
l.Hostname = hostname
if gwIP := s.conf.GatewayIP; gwIP == l.IP {
return fmt.Errorf("can't assign the gateway IP %q to the lease", gwIP)
}
if sn := s.conf.subnet; !sn.Contains(l.IP) {
return fmt.Errorf("subnet %s does not contain the ip %q", sn, l.IP)
}
return nil
}
// updateStaticLease safe removes dynamic lease with the same properties and // updateStaticLease safe removes dynamic lease with the same properties and
// then adds a static lease l. // then adds a static lease l.
func (s *v4Server) updateStaticLease(l *Lease) (err error) { func (s *v4Server) updateStaticLease(l *Lease) (err error) {

View file

@ -90,6 +90,9 @@ func (s *v6Server) IPByHost(host string) (ip netip.Addr) {
func (s *v6Server) ResetLeases(leases []*Lease) (err error) { func (s *v6Server) ResetLeases(leases []*Lease) (err error) {
defer func() { err = errors.Annotate(err, "dhcpv6: %w") }() defer func() { err = errors.Annotate(err, "dhcpv6: %w") }()
s.leasesLock.Lock()
defer s.leasesLock.Unlock()
s.leases = nil s.leases = nil
for _, l := range leases { for _, l := range leases {
ip := net.IP(l.IP.AsSlice()) ip := net.IP(l.IP.AsSlice())
@ -232,6 +235,37 @@ func (s *v6Server) AddStaticLease(l *Lease) (err error) {
return nil return nil
} }
// UpdateStaticLease updates IP, hostname of the static lease.
func (s *v6Server) UpdateStaticLease(l *Lease) (err error) {
defer func() {
if err != nil {
err = errors.Annotate(err, "dhcpv6: updating static lease: %w")
return
}
s.conf.notify(LeaseChangedDBStore)
s.conf.notify(LeaseChangedRemovedStatic)
}()
s.leasesLock.Lock()
defer s.leasesLock.Unlock()
found := s.findLease(l.HWAddr)
if found == nil {
return fmt.Errorf("can't find lease %s", l.HWAddr)
}
err = s.rmLease(found)
if err != nil {
return fmt.Errorf("removing previous lease for %s (%s): %w", l.IP, l.HWAddr, err)
}
s.addLease(l)
return nil
}
// RemoveStaticLease removes a static lease. It is safe for concurrent use. // RemoveStaticLease removes a static lease. It is safe for concurrent use.
func (s *v6Server) RemoveStaticLease(l *Lease) (err error) { func (s *v6Server) RemoveStaticLease(l *Lease) (err error) {
defer func() { err = errors.Annotate(err, "dhcpv6: %w") }() defer func() { err = errors.Annotate(err, "dhcpv6: %w") }()
@ -283,16 +317,14 @@ func (s *v6Server) rmLease(lease *Lease) (err error) {
return fmt.Errorf("lease not found") return fmt.Errorf("lease not found")
} }
// Find lease by MAC // Find lease by MAC.
func (s *v6Server) findLease(mac net.HardwareAddr) *Lease { func (s *v6Server) findLease(mac net.HardwareAddr) (lease *Lease) {
s.leasesLock.Lock()
defer s.leasesLock.Unlock()
for i := range s.leases { for i := range s.leases {
if bytes.Equal(mac, s.leases[i].HWAddr) { if bytes.Equal(mac, s.leases[i].HWAddr) {
return s.leases[i] return s.leases[i]
} }
} }
return nil return nil
} }
@ -474,7 +506,14 @@ func (s *v6Server) process(msg *dhcpv6.Message, req, resp dhcpv6.DHCPv6) bool {
return false return false
} }
lease := s.findLease(mac) var lease *Lease
func() {
s.leasesLock.Lock()
defer s.leasesLock.Unlock()
lease = s.findLease(mac)
}()
if lease == nil { if lease == nil {
log.Debug("dhcpv6: no lease for: %s", mac) log.Debug("dhcpv6: no lease for: %s", mac)

View file

@ -12,10 +12,12 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/aghalg" "github.com/AdguardTeam/AdGuardHome/internal/aghalg"
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp" "github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
"github.com/AdguardTeam/AdGuardHome/internal/aghtls" "github.com/AdguardTeam/AdGuardHome/internal/aghtls"
"github.com/AdguardTeam/AdGuardHome/internal/client" "github.com/AdguardTeam/AdGuardHome/internal/client"
"github.com/AdguardTeam/AdGuardHome/internal/filtering" "github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/dnsproxy/proxy" "github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/dnsproxy/upstream"
"github.com/AdguardTeam/golibs/errors" "github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log" "github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/netutil" "github.com/AdguardTeam/golibs/netutil"
@ -392,6 +394,122 @@ func (s *Server) prepareIpsetListSettings() (err error) {
return s.ipset.init(ipsets) return s.ipset.init(ipsets)
} }
// collectListenAddr adds addrPort to addrs. It also adds its port to
// unspecPorts if its address is unspecified.
func collectListenAddr(
addrPort netip.AddrPort,
addrs map[netip.AddrPort]unit,
unspecPorts map[uint16]unit,
) {
if addrPort == (netip.AddrPort{}) {
return
}
addrs[addrPort] = unit{}
if addrPort.Addr().IsUnspecified() {
unspecPorts[addrPort.Port()] = unit{}
}
}
// collectDNSAddrs returns configured set of listening addresses. It also
// returns a set of ports of each unspecified listening address.
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))
unspecPorts = map[uint16]unit{}
for _, laddr := range conf.TCPListenAddrs {
collectListenAddr(laddr.AddrPort(), addrs, unspecPorts)
}
for _, laddr := range conf.UDPListenAddrs {
collectListenAddr(laddr.AddrPort(), addrs, unspecPorts)
}
return addrs, unspecPorts
}
// defaultPlainDNSPort is the default port for plain DNS.
const defaultPlainDNSPort uint16 = 53
// 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 (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 || !m(addr) {
// Don't filter out the upstream if it either cannot be parsed, or
// does not match um.
return false
}
errs = append(errs, u.Close())
return true
}
upsConf.Upstreams = slices.DeleteFunc(upsConf.Upstreams, delFunc)
for d, ups := range upsConf.DomainReservedUpstreams {
upsConf.DomainReservedUpstreams[d] = slices.DeleteFunc(ups, delFunc)
}
for d, ups := range upsConf.SpecifiedDomainUpstreams {
upsConf.SpecifiedDomainUpstreams[d] = slices.DeleteFunc(ups, delFunc)
}
return errors.Join(errs...)
}
// ourAddrsMatcher returns a matcher that matches all the configured listening
// addresses.
func (conf *ServerConfig) ourAddrsMatcher() (m addrPortMatcher, err error) {
addrs, unspecPorts := conf.collectDNSAddrs()
if len(addrs) == 0 {
log.Debug("dnsforward: no listen addresses")
// Match no addresses.
return func(_ netip.AddrPort) (ok bool) { return false }, nil
}
if len(unspecPorts) == 0 {
log.Debug("dnsforward: filtering out addresses %s", addrs)
m = func(a netip.AddrPort) (ok bool) {
_, ok = addrs[a]
return ok
}
} else {
var ifaceAddrs []netip.Addr
ifaceAddrs, err = aghnet.CollectAllIfacesAddrs()
if err != nil {
// Don't wrap the error since it's informative enough as is.
return nil, err
}
log.Debug("dnsforward: filtering out addresses %s on ports %d", ifaceAddrs, unspecPorts)
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 // prepareTLS - prepares TLS configuration for the DNS proxy
func (s *Server) prepareTLS(proxyConfig *proxy.Config) (err error) { func (s *Server) prepareTLS(proxyConfig *proxy.Config) (err error) {
if len(s.conf.CertificateChainData) == 0 || len(s.conf.PrivateKeyData) == 0 { if len(s.conf.CertificateChainData) == 0 || len(s.conf.PrivateKeyData) == 0 {

View file

@ -25,8 +25,10 @@ import (
"github.com/AdguardTeam/golibs/errors" "github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log" "github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/netutil" "github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/netutil/sysresolv"
"github.com/AdguardTeam/golibs/stringutil" "github.com/AdguardTeam/golibs/stringutil"
"github.com/miekg/dns" "github.com/miekg/dns"
"golang.org/x/exp/slices"
) )
// DefaultTimeout is the default upstream timeout // DefaultTimeout is the default upstream timeout
@ -72,6 +74,11 @@ type DHCP interface {
Enabled() (ok bool) Enabled() (ok bool)
} }
type SystemResolvers interface {
// Addrs returns the list of system resolvers' addresses.
Addrs() (addrs []netip.AddrPort)
}
// Server is the main way to start a DNS server. // Server is the main way to start a DNS server.
// //
// Example: // Example:
@ -126,7 +133,7 @@ type Server struct {
// sysResolvers used to fetch system resolvers to use by default for private // sysResolvers used to fetch system resolvers to use by default for private
// PTR resolving. // PTR resolving.
sysResolvers aghnet.SystemResolvers sysResolvers SystemResolvers
// recDetector is a cache for recursive requests. It is used to detect // recDetector is a cache for recursive requests. It is used to detect
// and prevent recursive requests only for private upstreams. // and prevent recursive requests only for private upstreams.
@ -225,9 +232,7 @@ func NewServer(p DNSCreateParams) (s *Server, err error) {
anonymizer: p.Anonymizer, anonymizer: p.Anonymizer,
} }
// TODO(e.burkov): Enable the refresher after the actual implementation s.sysResolvers, err = sysresolv.NewSystemResolvers(nil, defaultPlainDNSPort)
// passes the public testing.
s.sysResolvers, err = aghnet.NewSystemResolvers(nil)
if err != nil { if err != nil {
return nil, fmt.Errorf("initializing system resolvers: %w", err) return nil, fmt.Errorf("initializing system resolvers: %w", err)
} }
@ -439,73 +444,28 @@ func (s *Server) startLocked() error {
// faster than ordinary upstreams. // faster than ordinary upstreams.
const defaultLocalTimeout = 1 * time.Second const defaultLocalTimeout = 1 * time.Second
// collectDNSIPAddrs returns IP addresses the server is listening on without
// port numbers. For internal use only.
func (s *Server) collectDNSIPAddrs() (addrs []string, err error) {
addrs = make([]string, len(s.conf.TCPListenAddrs)+len(s.conf.UDPListenAddrs))
var i int
var ip net.IP
for _, addr := range s.conf.TCPListenAddrs {
if addr == nil {
continue
}
if ip = addr.IP; ip.IsUnspecified() {
return aghnet.CollectAllIfacesAddrs()
}
addrs[i] = ip.String()
i++
}
for _, addr := range s.conf.UDPListenAddrs {
if addr == nil {
continue
}
if ip = addr.IP; ip.IsUnspecified() {
return aghnet.CollectAllIfacesAddrs()
}
addrs[i] = ip.String()
i++
}
return addrs[:i], nil
}
func (s *Server) filterOurDNSAddrs(addrs []string) (filtered []string, err error) {
var ourAddrs []string
ourAddrs, err = s.collectDNSIPAddrs()
if err != nil {
return nil, err
}
ourAddrsSet := stringutil.NewSet(ourAddrs...)
log.Debug("dnsforward: filtering out %s", ourAddrsSet.String())
// TODO(e.burkov): The approach of subtracting sets of strings is not
// really applicable here since in case of listening on all network
// interfaces we should check the whole interface's network to cut off
// all the loopback addresses as well.
return stringutil.FilterOut(addrs, ourAddrsSet.Has), nil
}
// setupLocalResolvers initializes the resolvers for local addresses. For // setupLocalResolvers initializes the resolvers for local addresses. For
// internal use only. // internal use only.
func (s *Server) setupLocalResolvers() (err error) { func (s *Server) setupLocalResolvers() (err error) {
bootstraps := s.conf.BootstrapDNS matcher, err := s.conf.ourAddrsMatcher()
resolvers := s.conf.LocalPTRResolvers if err != nil {
// Don't wrap the error because it's informative enough as is.
if len(resolvers) == 0 { return err
resolvers = s.sysResolvers.Get()
bootstraps = nil
} else {
resolvers = stringutil.FilterOut(resolvers, IsCommentOrEmpty)
} }
resolvers, err = s.filterOurDNSAddrs(resolvers) bootstraps := s.conf.BootstrapDNS
if err != nil { resolvers := s.conf.LocalPTRResolvers
return err 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) log.Debug("dnsforward: upstreams to resolve ptr for local addresses: %v", resolvers)
@ -514,13 +474,18 @@ func (s *Server) setupLocalResolvers() (err error) {
Bootstrap: bootstraps, Bootstrap: bootstraps,
Timeout: defaultLocalTimeout, Timeout: defaultLocalTimeout,
// TODO(e.burkov): Should we verify server's certificates? // TODO(e.burkov): Should we verify server's certificates?
PreferIPv6: s.conf.BootstrapPreferIPv6, PreferIPv6: s.conf.BootstrapPreferIPv6,
}) })
if err != nil { if err != nil {
return fmt.Errorf("preparing private upstreams: %w", err) return fmt.Errorf("preparing private upstreams: %w", err)
} }
if filterConfig {
if err = matcher.filterOut(uc); err != nil {
return fmt.Errorf("filtering private upstreams: %w", err)
}
}
s.localResolvers = &proxy.Proxy{ s.localResolvers = &proxy.Proxy{
Config: proxy.Config{ Config: proxy.Config{
UpstreamConfig: uc, UpstreamConfig: uc,

View file

@ -65,6 +65,9 @@ type jsonDNSConfig struct {
// UpstreamMode defines the way DNS requests are constructed. // UpstreamMode defines the way DNS requests are constructed.
UpstreamMode *string `json:"upstream_mode"` UpstreamMode *string `json:"upstream_mode"`
// BlockedResponseTTL is the TTL for blocked responses.
BlockedResponseTTL *uint32 `json:"blocked_response_ttl"`
// CacheSize in bytes. // CacheSize in bytes.
CacheSize *uint32 `json:"cache_size"` CacheSize *uint32 `json:"cache_size"`
@ -115,6 +118,7 @@ func (s *Server) getDNSConfig() (c *jsonDNSConfig) {
bootstraps := stringutil.CloneSliceOrEmpty(s.conf.BootstrapDNS) bootstraps := stringutil.CloneSliceOrEmpty(s.conf.BootstrapDNS)
fallbacks := stringutil.CloneSliceOrEmpty(s.conf.FallbackDNS) fallbacks := stringutil.CloneSliceOrEmpty(s.conf.FallbackDNS)
blockingMode, blockingIPv4, blockingIPv6 := s.dnsFilter.BlockingMode() blockingMode, blockingIPv4, blockingIPv6 := s.dnsFilter.BlockingMode()
blockedResponseTTL := s.dnsFilter.BlockedResponseTTL()
ratelimit := s.conf.Ratelimit ratelimit := s.conf.Ratelimit
customIP := s.conf.EDNSClientSubnet.CustomIP customIP := s.conf.EDNSClientSubnet.CustomIP
@ -138,9 +142,9 @@ func (s *Server) getDNSConfig() (c *jsonDNSConfig) {
upstreamMode = "parallel" upstreamMode = "parallel"
} }
defLocalPTRUps, err := s.filterOurDNSAddrs(s.sysResolvers.Get()) defPTRUps, err := s.defaultLocalPTRUpstreams()
if err != nil { if err != nil {
log.Debug("getting dns configuration: %s", err) log.Error("dnsforward: %s", err)
} }
return &jsonDNSConfig{ return &jsonDNSConfig{
@ -158,6 +162,7 @@ func (s *Server) getDNSConfig() (c *jsonDNSConfig) {
EDNSCSUseCustom: &useCustom, EDNSCSUseCustom: &useCustom,
DNSSECEnabled: &enableDNSSEC, DNSSECEnabled: &enableDNSSEC,
DisableIPv6: &aaaaDisabled, DisableIPv6: &aaaaDisabled,
BlockedResponseTTL: &blockedResponseTTL,
CacheSize: &cacheSize, CacheSize: &cacheSize,
CacheMinTTL: &cacheMinTTL, CacheMinTTL: &cacheMinTTL,
CacheMaxTTL: &cacheMaxTTL, CacheMaxTTL: &cacheMaxTTL,
@ -166,11 +171,29 @@ func (s *Server) getDNSConfig() (c *jsonDNSConfig) {
ResolveClients: &resolveClients, ResolveClients: &resolveClients,
UsePrivateRDNS: &usePrivateRDNS, UsePrivateRDNS: &usePrivateRDNS,
LocalPTRUpstreams: &localPTRUpstreams, LocalPTRUpstreams: &localPTRUpstreams,
DefaultLocalPTRUpstreams: defLocalPTRUps, DefaultLocalPTRUpstreams: defPTRUps,
DisabledUntil: protectionDisabledUntil, DisabledUntil: protectionDisabledUntil,
} }
} }
// 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.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)
ups = make([]string, 0, len(sysResolvers))
for _, r := range sysResolvers {
ups = append(ups, r.String())
}
return ups, nil
}
// handleGetConfig handles requests to the GET /control/dns_info endpoint. // handleGetConfig handles requests to the GET /control/dns_info endpoint.
func (s *Server) handleGetConfig(w http.ResponseWriter, r *http.Request) { func (s *Server) handleGetConfig(w http.ResponseWriter, r *http.Request) {
resp := s.getDNSConfig() resp := s.getDNSConfig()
@ -204,7 +227,7 @@ func (req *jsonDNSConfig) checkBootstrap() (err error) {
return errors.Error("empty") return errors.Error("empty")
} }
if _, err = upstream.NewResolver(b, nil); err != nil { if _, err = upstream.NewUpstreamResolver(b, nil); err != nil {
return err return err
} }
} }
@ -321,6 +344,10 @@ func (s *Server) setConfig(dc *jsonDNSConfig) (shouldRestart bool) {
s.dnsFilter.SetBlockingMode(*dc.BlockingMode, dc.BlockingIPv4, dc.BlockingIPv6) s.dnsFilter.SetBlockingMode(*dc.BlockingMode, dc.BlockingIPv4, dc.BlockingIPv6)
} }
if dc.BlockedResponseTTL != nil {
s.dnsFilter.SetBlockedResponseTTL(*dc.BlockedResponseTTL)
}
if dc.ProtectionEnabled != nil { if dc.ProtectionEnabled != nil {
s.dnsFilter.SetProtectionEnabled(*dc.ProtectionEnabled) s.dnsFilter.SetProtectionEnabled(*dc.ProtectionEnabled)
} }

View file

@ -28,17 +28,12 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
// fakeSystemResolvers is a mock aghnet.SystemResolvers implementation for // emptySysResolvers is an empty [SystemResolvers] implementation that always
// tests. // returns nil.
type fakeSystemResolvers struct { type emptySysResolvers struct{}
// SystemResolvers is embedded here simply to make *fakeSystemResolvers
// an aghnet.SystemResolvers without actually implementing all methods.
aghnet.SystemResolvers
}
// Get implements the aghnet.SystemResolvers interface for *fakeSystemResolvers. // Addrs implements the aghnet.SystemResolvers interface for emptySysResolvers.
// It always returns nil. func (emptySysResolvers) Addrs() (addrs []netip.AddrPort) {
func (fsr *fakeSystemResolvers) Get() (rs []string) {
return nil return nil
} }
@ -60,6 +55,7 @@ func TestDNSForwardHTTP_handleGetConfig(t *testing.T) {
filterConf := &filtering.Config{ filterConf := &filtering.Config{
ProtectionEnabled: true, ProtectionEnabled: true,
BlockingMode: filtering.BlockingModeDefault, BlockingMode: filtering.BlockingModeDefault,
BlockedResponseTTL: 10,
SafeBrowsingEnabled: true, SafeBrowsingEnabled: true,
SafeBrowsingCacheSize: 1000, SafeBrowsingCacheSize: 1000,
SafeSearchConf: filtering.SafeSearchConfig{Enabled: true}, SafeSearchConf: filtering.SafeSearchConfig{Enabled: true},
@ -78,7 +74,7 @@ func TestDNSForwardHTTP_handleGetConfig(t *testing.T) {
ConfigModified: func() {}, ConfigModified: func() {},
} }
s := createTestServer(t, filterConf, forwardConf, nil) s := createTestServer(t, filterConf, forwardConf, nil)
s.sysResolvers = &fakeSystemResolvers{} s.sysResolvers = &emptySysResolvers{}
require.NoError(t, s.Start()) require.NoError(t, s.Start())
testutil.CleanupAndRequireSuccess(t, s.Stop) testutil.CleanupAndRequireSuccess(t, s.Stop)
@ -137,6 +133,7 @@ func TestDNSForwardHTTP_handleSetConfig(t *testing.T) {
filterConf := &filtering.Config{ filterConf := &filtering.Config{
ProtectionEnabled: true, ProtectionEnabled: true,
BlockingMode: filtering.BlockingModeDefault, BlockingMode: filtering.BlockingModeDefault,
BlockedResponseTTL: 10,
SafeBrowsingEnabled: true, SafeBrowsingEnabled: true,
SafeBrowsingCacheSize: 1000, SafeBrowsingCacheSize: 1000,
SafeSearchConf: filtering.SafeSearchConfig{Enabled: true}, SafeSearchConf: filtering.SafeSearchConfig{Enabled: true},
@ -154,7 +151,7 @@ func TestDNSForwardHTTP_handleSetConfig(t *testing.T) {
ConfigModified: func() {}, ConfigModified: func() {},
} }
s := createTestServer(t, filterConf, forwardConf, nil) s := createTestServer(t, filterConf, forwardConf, nil)
s.sysResolvers = &fakeSystemResolvers{} s.sysResolvers = &emptySysResolvers{}
defaultConf := s.conf defaultConf := s.conf
@ -229,6 +226,9 @@ func TestDNSForwardHTTP_handleSetConfig(t *testing.T) {
}, { }, {
name: "fallbacks", name: "fallbacks",
wantSet: "", wantSet: "",
}, {
name: "blocked_response_ttl",
wantSet: "",
}} }}
var data map[string]struct { var data map[string]struct {
@ -480,7 +480,7 @@ func TestServer_HandleTestUpstreamDNS(t *testing.T) {
hostsListener := newLocalUpstreamListener(t, 0, goodHandler) hostsListener := newLocalUpstreamListener(t, 0, goodHandler)
hostsUps := (&url.URL{ hostsUps := (&url.URL{
Scheme: "tcp", Scheme: "tcp",
Host: netutil.JoinHostPort(upstreamHost, int(hostsListener.Port())), Host: netutil.JoinHostPort(upstreamHost, hostsListener.Port()),
}).String() }).String()
hc, err := aghnet.NewHostsContainer( hc, err := aghnet.NewHostsContainer(

View file

@ -6,8 +6,8 @@ import (
"os" "os"
"strings" "strings"
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
"github.com/AdguardTeam/AdGuardHome/internal/aghos" "github.com/AdguardTeam/AdGuardHome/internal/aghos"
"github.com/AdguardTeam/AdGuardHome/internal/ipset"
"github.com/AdguardTeam/golibs/errors" "github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log" "github.com/AdguardTeam/golibs/log"
"github.com/miekg/dns" "github.com/miekg/dns"
@ -15,14 +15,14 @@ import (
// ipsetCtx is the ipset context. ipsetMgr can be nil. // ipsetCtx is the ipset context. ipsetMgr can be nil.
type ipsetCtx struct { type ipsetCtx struct {
ipsetMgr aghnet.IpsetManager ipsetMgr ipset.Manager
} }
// init initializes the ipset context. It is not safe for concurrent use. // init initializes the ipset context. It is not safe for concurrent use.
// //
// TODO(a.garipov): Rewrite into a simple constructor? // TODO(a.garipov): Rewrite into a simple constructor?
func (c *ipsetCtx) init(ipsetConf []string) (err error) { func (c *ipsetCtx) init(ipsetConf []string) (err error) {
c.ipsetMgr, err = aghnet.NewIpsetManager(ipsetConf) c.ipsetMgr, err = ipset.NewManager(ipsetConf)
if errors.Is(err, os.ErrInvalid) || errors.Is(err, os.ErrPermission) { if errors.Is(err, os.ErrInvalid) || errors.Is(err, os.ErrPermission) {
// ipset cannot currently be initialized if the server was installed // ipset cannot currently be initialized if the server was installed
// from Snap or when the user or the binary doesn't have the required // from Snap or when the user or the binary doesn't have the required

View file

@ -114,3 +114,74 @@ func TestIpsetCtx_process(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
}) })
} }
func TestIpsetCtx_SkipIpsetProcessing(t *testing.T) {
req4 := createTestMessage("example.com")
resp4 := &dns.Msg{
Answer: []dns.RR{&dns.A{
A: net.IP{1, 2, 3, 4},
}},
}
m := &fakeIpsetMgr{}
ictx := &ipsetCtx{
ipsetMgr: m,
}
testCases := []struct {
dctx *dnsContext
name string
want bool
}{{
name: "basic",
want: false,
dctx: &dnsContext{
proxyCtx: &proxy.DNSContext{
Req: req4,
Res: resp4,
},
responseFromUpstream: true,
},
}, {
name: "rewrite",
want: true,
dctx: &dnsContext{
proxyCtx: &proxy.DNSContext{
Req: req4,
Res: resp4,
},
responseFromUpstream: false,
},
}, {
name: "empty_req",
want: true,
dctx: &dnsContext{
proxyCtx: &proxy.DNSContext{
Req: nil,
Res: resp4,
},
responseFromUpstream: true,
},
}, {
name: "empty_res",
want: true,
dctx: &dnsContext{
proxyCtx: &proxy.DNSContext{
Req: req4,
Res: nil,
},
responseFromUpstream: true,
},
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
got := ictx.skipIpsetProcessing(tc.dctx)
assert.Equal(t, tc.want, got)
})
}
}

View file

@ -20,6 +20,7 @@
"blocking_mode": "default", "blocking_mode": "default",
"blocking_ipv4": "", "blocking_ipv4": "",
"blocking_ipv6": "", "blocking_ipv6": "",
"blocked_response_ttl": 10,
"edns_cs_enabled": false, "edns_cs_enabled": false,
"dnssec_enabled": false, "dnssec_enabled": false,
"disable_ipv6": false, "disable_ipv6": false,
@ -55,6 +56,7 @@
"blocking_mode": "default", "blocking_mode": "default",
"blocking_ipv4": "", "blocking_ipv4": "",
"blocking_ipv6": "", "blocking_ipv6": "",
"blocked_response_ttl": 10,
"edns_cs_enabled": false, "edns_cs_enabled": false,
"dnssec_enabled": false, "dnssec_enabled": false,
"disable_ipv6": false, "disable_ipv6": false,
@ -90,6 +92,7 @@
"blocking_mode": "default", "blocking_mode": "default",
"blocking_ipv4": "", "blocking_ipv4": "",
"blocking_ipv6": "", "blocking_ipv6": "",
"blocked_response_ttl": 10,
"edns_cs_enabled": false, "edns_cs_enabled": false,
"dnssec_enabled": false, "dnssec_enabled": false,
"disable_ipv6": false, "disable_ipv6": false,

View file

@ -25,6 +25,7 @@
"blocking_mode": "default", "blocking_mode": "default",
"blocking_ipv4": "", "blocking_ipv4": "",
"blocking_ipv6": "", "blocking_ipv6": "",
"blocked_response_ttl": 10,
"edns_cs_enabled": false, "edns_cs_enabled": false,
"dnssec_enabled": false, "dnssec_enabled": false,
"disable_ipv6": false, "disable_ipv6": false,
@ -62,6 +63,7 @@
"blocking_mode": "default", "blocking_mode": "default",
"blocking_ipv4": "", "blocking_ipv4": "",
"blocking_ipv6": "", "blocking_ipv6": "",
"blocked_response_ttl": 10,
"edns_cs_enabled": false, "edns_cs_enabled": false,
"dnssec_enabled": false, "dnssec_enabled": false,
"disable_ipv6": false, "disable_ipv6": false,
@ -100,6 +102,7 @@
"blocking_mode": "refused", "blocking_mode": "refused",
"blocking_ipv4": "", "blocking_ipv4": "",
"blocking_ipv6": "", "blocking_ipv6": "",
"blocked_response_ttl": 10,
"edns_cs_enabled": false, "edns_cs_enabled": false,
"dnssec_enabled": false, "dnssec_enabled": false,
"disable_ipv6": false, "disable_ipv6": false,
@ -138,6 +141,7 @@
"blocking_mode": "default", "blocking_mode": "default",
"blocking_ipv4": "", "blocking_ipv4": "",
"blocking_ipv6": "", "blocking_ipv6": "",
"blocked_response_ttl": 10,
"edns_cs_enabled": false, "edns_cs_enabled": false,
"dnssec_enabled": false, "dnssec_enabled": false,
"disable_ipv6": false, "disable_ipv6": false,
@ -176,6 +180,7 @@
"blocking_mode": "default", "blocking_mode": "default",
"blocking_ipv4": "", "blocking_ipv4": "",
"blocking_ipv6": "", "blocking_ipv6": "",
"blocked_response_ttl": 10,
"edns_cs_enabled": false, "edns_cs_enabled": false,
"dnssec_enabled": false, "dnssec_enabled": false,
"disable_ipv6": false, "disable_ipv6": false,
@ -214,6 +219,7 @@
"blocking_mode": "default", "blocking_mode": "default",
"blocking_ipv4": "", "blocking_ipv4": "",
"blocking_ipv6": "", "blocking_ipv6": "",
"blocked_response_ttl": 10,
"edns_cs_enabled": true, "edns_cs_enabled": true,
"dnssec_enabled": false, "dnssec_enabled": false,
"disable_ipv6": false, "disable_ipv6": false,
@ -254,6 +260,7 @@
"blocking_mode": "default", "blocking_mode": "default",
"blocking_ipv4": "", "blocking_ipv4": "",
"blocking_ipv6": "", "blocking_ipv6": "",
"blocked_response_ttl": 10,
"edns_cs_enabled": true, "edns_cs_enabled": true,
"dnssec_enabled": false, "dnssec_enabled": false,
"disable_ipv6": false, "disable_ipv6": false,
@ -294,6 +301,7 @@
"blocking_mode": "default", "blocking_mode": "default",
"blocking_ipv4": "", "blocking_ipv4": "",
"blocking_ipv6": "", "blocking_ipv6": "",
"blocked_response_ttl": 10,
"edns_cs_enabled": false, "edns_cs_enabled": false,
"dnssec_enabled": false, "dnssec_enabled": false,
"disable_ipv6": false, "disable_ipv6": false,
@ -332,6 +340,7 @@
"blocking_mode": "default", "blocking_mode": "default",
"blocking_ipv4": "", "blocking_ipv4": "",
"blocking_ipv6": "", "blocking_ipv6": "",
"blocked_response_ttl": 10,
"edns_cs_enabled": false, "edns_cs_enabled": false,
"dnssec_enabled": true, "dnssec_enabled": true,
"disable_ipv6": false, "disable_ipv6": false,
@ -370,6 +379,7 @@
"blocking_mode": "default", "blocking_mode": "default",
"blocking_ipv4": "", "blocking_ipv4": "",
"blocking_ipv6": "", "blocking_ipv6": "",
"blocked_response_ttl": 10,
"edns_cs_enabled": false, "edns_cs_enabled": false,
"dnssec_enabled": false, "dnssec_enabled": false,
"disable_ipv6": false, "disable_ipv6": false,
@ -408,6 +418,7 @@
"blocking_mode": "default", "blocking_mode": "default",
"blocking_ipv4": "", "blocking_ipv4": "",
"blocking_ipv6": "", "blocking_ipv6": "",
"blocked_response_ttl": 10,
"edns_cs_enabled": false, "edns_cs_enabled": false,
"dnssec_enabled": false, "dnssec_enabled": false,
"disable_ipv6": false, "disable_ipv6": false,
@ -446,6 +457,7 @@
"blocking_mode": "default", "blocking_mode": "default",
"blocking_ipv4": "", "blocking_ipv4": "",
"blocking_ipv6": "", "blocking_ipv6": "",
"blocked_response_ttl": 10,
"edns_cs_enabled": false, "edns_cs_enabled": false,
"dnssec_enabled": false, "dnssec_enabled": false,
"disable_ipv6": false, "disable_ipv6": false,
@ -486,6 +498,7 @@
"blocking_mode": "default", "blocking_mode": "default",
"blocking_ipv4": "", "blocking_ipv4": "",
"blocking_ipv6": "", "blocking_ipv6": "",
"blocked_response_ttl": 10,
"edns_cs_enabled": false, "edns_cs_enabled": false,
"dnssec_enabled": false, "dnssec_enabled": false,
"disable_ipv6": false, "disable_ipv6": false,
@ -526,6 +539,7 @@
"blocking_mode": "default", "blocking_mode": "default",
"blocking_ipv4": "", "blocking_ipv4": "",
"blocking_ipv6": "", "blocking_ipv6": "",
"blocked_response_ttl": 10,
"edns_cs_enabled": false, "edns_cs_enabled": false,
"dnssec_enabled": false, "dnssec_enabled": false,
"disable_ipv6": false, "disable_ipv6": false,
@ -565,6 +579,7 @@
"blocking_mode": "default", "blocking_mode": "default",
"blocking_ipv4": "", "blocking_ipv4": "",
"blocking_ipv6": "", "blocking_ipv6": "",
"blocked_response_ttl": 10,
"edns_cs_enabled": false, "edns_cs_enabled": false,
"dnssec_enabled": false, "dnssec_enabled": false,
"disable_ipv6": false, "disable_ipv6": false,
@ -603,6 +618,7 @@
"blocking_mode": "default", "blocking_mode": "default",
"blocking_ipv4": "", "blocking_ipv4": "",
"blocking_ipv6": "", "blocking_ipv6": "",
"blocked_response_ttl": 10,
"edns_cs_enabled": false, "edns_cs_enabled": false,
"dnssec_enabled": false, "dnssec_enabled": false,
"disable_ipv6": false, "disable_ipv6": false,
@ -643,6 +659,7 @@
"blocking_mode": "default", "blocking_mode": "default",
"blocking_ipv4": "", "blocking_ipv4": "",
"blocking_ipv6": "", "blocking_ipv6": "",
"blocked_response_ttl": 10,
"edns_cs_enabled": false, "edns_cs_enabled": false,
"dnssec_enabled": false, "dnssec_enabled": false,
"disable_ipv6": false, "disable_ipv6": false,
@ -686,6 +703,7 @@
"blocking_mode": "default", "blocking_mode": "default",
"blocking_ipv4": "", "blocking_ipv4": "",
"blocking_ipv6": "", "blocking_ipv6": "",
"blocked_response_ttl": 10,
"edns_cs_enabled": false, "edns_cs_enabled": false,
"dnssec_enabled": false, "dnssec_enabled": false,
"disable_ipv6": false, "disable_ipv6": false,
@ -724,6 +742,7 @@
"blocking_mode": "default", "blocking_mode": "default",
"blocking_ipv4": "", "blocking_ipv4": "",
"blocking_ipv6": "", "blocking_ipv6": "",
"blocked_response_ttl": 10,
"edns_cs_enabled": false, "edns_cs_enabled": false,
"dnssec_enabled": false, "dnssec_enabled": false,
"disable_ipv6": false, "disable_ipv6": false,
@ -766,6 +785,46 @@
"blocking_mode": "default", "blocking_mode": "default",
"blocking_ipv4": "", "blocking_ipv4": "",
"blocking_ipv6": "", "blocking_ipv6": "",
"blocked_response_ttl": 10,
"edns_cs_enabled": false,
"dnssec_enabled": false,
"disable_ipv6": false,
"upstream_mode": "",
"cache_size": 0,
"cache_ttl_min": 0,
"cache_ttl_max": 0,
"cache_optimistic": false,
"resolve_clients": false,
"use_private_ptr_resolvers": false,
"local_ptr_upstreams": [],
"edns_cs_use_custom": false,
"edns_cs_custom_ip": ""
}
},
"blocked_response_ttl": {
"req": {
"blocked_response_ttl": 11
},
"want": {
"upstream_dns": [
"8.8.8.8:53",
"8.8.4.4:53"
],
"upstream_dns_file": "",
"bootstrap_dns": [
"9.9.9.10",
"149.112.112.10",
"2620:fe::10",
"2620:fe::fe:10"
],
"fallback_dns": [],
"protection_enabled": true,
"protection_disabled_until": null,
"ratelimit": 0,
"blocking_mode": "default",
"blocking_ipv4": "",
"blocking_ipv6": "",
"blocked_response_ttl": 11,
"edns_cs_enabled": false, "edns_cs_enabled": false,
"dnssec_enabled": false, "dnssec_enabled": false,
"disable_ipv6": false, "disable_ipv6": false,

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