Compare commits

..

47 Commits

Author SHA1 Message Date
Andrey Meshkov
1e36deeeda *(global): update translations 2019-12-24 12:15:27 +03:00
Andrey Meshkov
dfe47ab33b *: update translation script readme 2019-12-24 11:45:02 +03:00
Andrey Meshkov
670810c433 -(dnsforward): the issue with cache and custom upstreams
 Closes: https://github.com/AdguardTeam/AdGuardHome/issues/1301
2019-12-24 11:19:36 +03:00
Andrey Meshkov
3a077717ae *(home): do not set whois-info for manually created clients
 Closes: Do not set WhoisInfo for manually created clients
2019-12-23 20:02:06 +03:00
Andrey Meshkov
cdd55139fa *(dnsforward): cache upstream instances
 Closes: https://github.com/AdguardTeam/AdGuardHome/issues/1296
2019-12-23 19:31:27 +03:00
Andrey Meshkov
3dd91cf179 -: add one more test 2019-12-23 17:12:50 +03:00
Andrey Meshkov
6bf512f96f -(home): fix searching clients by mac address 2019-12-23 16:59:02 +03:00
Andrey Meshkov
9b93d43ac6 -(home): fix deadlock in clients 2019-12-23 16:27:24 +03:00
Andrey Meshkov
ec7a62e123 -(home): fix duplicate check when adding a new ClientHost 2019-12-23 16:20:12 +03:00
Andrey Meshkov
b4f4111609 -(dnsfilter): match DNS response against filtering rules only
Supposedly, this will fix #1290
2019-12-23 15:59:49 +03:00
Andrey Meshkov
7c0cf641db Merge: -(dnsforward): fix client settings for CNAME matching
fix #1274

* commit '5077f1a2b31056e184156ce9602e2428c7676acb':
  -(dnsforward): fix client settings for CNAME matching
2019-12-23 15:36:14 +03:00
Andrey Meshkov
87a82f5e60 Merge: -(global): fixing the installation flow on windows
* commit 'abbf8fb87b747700ecced07c9ca0b8e1f997ece1':
  -(global): fixing the installation flow on windows
2019-12-23 15:21:29 +03:00
Andrey Meshkov
abbf8fb87b -(global): fixing the installation flow on windows
There could be a bug caused by the lack of SO_REUSEPORT
2019-12-23 14:57:10 +03:00
Ildar Kamalov
c5537968b1 Merge: + DNS Rewrites: support wildcard domain name
Closes #922

* commit '5ba45b91c9cfde84fc0866e6b3fca679504975e7':
  * client: fix render field for DNS settings
  + client: handle wildcard domains
  + DNS Rewrites: support wildcard domain name
2019-12-23 14:02:21 +03:00
Andrey Meshkov
5077f1a2b3 -(dnsforward): fix client settings for CNAME matching
 Closes: https://github.com/AdguardTeam/AdGuardHome/issues/1274
2019-12-23 13:36:59 +03:00
Andrey Meshkov
482e9fd6f3 Merge: - client: sort clients table by requests count by default
fix #1253

* commit 'ca79fc98f5d43a5ca1955e8b0056edc3a6ee9b61':
  change code style in reduce
  - client: sort clients table by requests count by default
2019-12-23 12:35:07 +03:00
Ildar Kamalov
5ba45b91c9 * client: fix render field for DNS settings 2019-12-20 17:02:42 +03:00
Ildar Kamalov
c9478592a2 + client: handle wildcard domains 2019-12-20 16:48:27 +03:00
Simon Zolin
8685584bf5 + DNS Rewrites: support wildcard domain name 2019-12-20 16:45:58 +03:00
Artem Baskal
ca79fc98f5 change code style in reduce 2019-12-20 16:22:38 +03:00
Andrey Meshkov
28096d6966 Merge: + client: add clients forms validation and cache findClients function
* commit '2e493e0226d3f22941fd09eed44ebb67a4d2874a':
  + client: add clients forms validation and cache findClients function
2019-12-20 16:15:04 +03:00
Artem Baskal
2e493e0226 + client: add clients forms validation and cache findClients function 2019-12-20 16:07:03 +03:00
Artem Baskal
cddf3ca01e Merge: - client: delete whois column in the static clients table
Close #1252

* commit 'c2f1b2d8f5c9814c3328555fc364d1360518c872':
  - client: delete whois column in the statis cients table
2019-12-20 16:02:27 +03:00
Ildar Kamalov
24e8ef6b32 Merge: DHCP: show static leases all the time
Closes #1023

* commit '60cbb93488ec3fa1ca720d88e79ae48130ac9461':
  + client: show DHCP static leases all the time
  * dhcp: now static leases functionality works before DHCP is started
2019-12-20 15:18:23 +03:00
Simon Zolin
ceab5d4c41 Merge: + DNS: Allow DOH queries via unencrypted HTTP
Close #1276

* commit '91c3149ee2dc902a5081345431f586ae72362963':
  + allow_unencrypted_doh: add test
  + DNS: Allow DOH queries via unencrypted HTTP (e.g. for reverse proxying)
2019-12-20 15:14:41 +03:00
Simon Zolin
91c3149ee2 + allow_unencrypted_doh: add test 2019-12-20 14:50:54 +03:00
Artem Baskal
57c031c1c6 - client: sort clients table by requests count by default 2019-12-20 14:15:57 +03:00
Krombel
ec8fe0b40c + DNS: Allow DOH queries via unencrypted HTTP (e.g. for reverse proxying) 2019-12-20 12:22:43 +03:00
Andrey Meshkov
32f780366e -(global): docker image - dirty version 2019-12-19 19:50:12 +03:00
Andrey Meshkov
041ea65d14 Merge: - DNS: configuration settings were not applied until full restart
* commit 'd65cdd4544efdecb9bbe36e411c0bbcb6475ba94':
  - DNS: configuration settings were not applied until full restart
2019-12-19 15:03:13 +03:00
Simon Zolin
d65cdd4544 - DNS: configuration settings were not applied until full restart 2019-12-19 14:49:15 +03:00
Andrey Meshkov
cde15afd90 *: use npm ci for more reliable builds 2019-12-19 13:28:08 +03:00
Andrey Meshkov
00fabb0ecf Merge: + client: add X-DNS-Prefetch-Control meta tag
* commit '073643537612437430c7035cda679a758ca94d13':
  + client: add meta tag to index.html
  + client: add X-DNS-Prefetch-Control meta tag
2019-12-19 12:16:13 +03:00
Ildar Kamalov
0736435376 + client: add meta tag to index.html 2019-12-19 12:13:15 +03:00
Andrey Meshkov
f6976f3c7e Merge: - DNS: set RecursionAvailable flag in response message
* commit '4540a4e94ad204fc1cba9e15b95ce9b684ed2335':
  - DNS: set RecursionAvailable flag in response message
2019-12-19 12:09:32 +03:00
Simon Zolin
4540a4e94a - DNS: set RecursionAvailable flag in response message 2019-12-19 11:52:21 +03:00
Artem Baskal
c2f1b2d8f5 - client: delete whois column in the statis cients table 2019-12-19 11:15:58 +03:00
Ildar Kamalov
bf410c81ae + client: add X-DNS-Prefetch-Control meta tag 2019-12-19 10:23:04 +03:00
Ildar Kamalov
60cbb93488 + client: show DHCP static leases all the time 2019-12-18 13:40:05 +03:00
Andrey Meshkov
b54bf94697 Merge: - client: hide dns is starting message by default
* commit '7fade498b910a2492b2e214f0b2a706b51548b34':
  - client: add setDnsRunningStatus action
  - client: save in store dnsStatus even if running false
  - client: hide dns is starting message by default
2019-12-17 22:35:34 +03:00
Artem Baskal
7fade498b9 - client: add setDnsRunningStatus action 2019-12-17 18:54:28 +03:00
Artem Baskal
39640d8190 - client: save in store dnsStatus even if running false 2019-12-17 17:46:59 +03:00
Artem Baskal
242e5e136f - client: hide dns is starting message by default 2019-12-17 16:15:44 +03:00
Simon Zolin
b9c0b55356 * dhcp: now static leases functionality works before DHCP is started 2019-12-17 15:59:05 +03:00
Simon Zolin
b105f20837 Merge: - DNS: fix slow response to /status and /access/list requests
Close #1264

* commit '8521635f63e9570a4e75033533dec8180e7f130a':
  - DNS: fix slow response to /status and /access/list requests
2019-12-17 15:11:48 +03:00
Simon Zolin
8521635f63 - DNS: fix slow response to /status and /access/list requests 2019-12-17 13:09:03 +03:00
Simon Zolin
04de9d0f7b Merge: - DNS: "custom_ip" blocking mode didn't work after app restart
Close #1262

Squashed commit of the following:

commit bacd683ef5b52e275323a3c07b370ca08702403e
Author: Simon Zolin <s.zolin@adguard.com>
Date:   Mon Dec 16 17:00:49 2019 +0300

    fix

commit 3d4f9626460de3e13a621f2b8e535e9e0939e2bb
Author: Simon Zolin <s.zolin@adguard.com>
Date:   Mon Dec 16 16:54:23 2019 +0300

    fix

commit bf924bf90e9b705883bec88f8d7af11c39c1f322
Author: Simon Zolin <s.zolin@adguard.com>
Date:   Mon Dec 16 16:45:41 2019 +0300

    add test

commit 43338ea3645a025d69dd838bc732344255960bed
Author: Simon Zolin <s.zolin@adguard.com>
Date:   Mon Dec 16 16:07:51 2019 +0300

    - DNS: "custom_ip" blocking mode didn't work after app restart

commit 220f32e713a95d2c67355c61e419dd09df9d42b2
Author: Simon Zolin <s.zolin@adguard.com>
Date:   Mon Dec 16 15:46:01 2019 +0300

    - first run: fix panic on stop in case initialization didn't complete

    e.g. when Stats module can't be initialized because of incompatible file system
2019-12-16 17:04:30 +03:00
82 changed files with 1438 additions and 456 deletions

View File

@@ -14,7 +14,7 @@ before_install:
- curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh| sh -s -- -b $(go env GOPATH)/bin v1.19.1
install:
- npm --prefix client install
- npm --prefix client ci
cache:
directories:

View File

@@ -916,6 +916,8 @@ Response:
...
]
`domain` can be an exact host name (`www.host.com`) or a wildcard (`*.host.com`).
### API: Add a rewrite entry

View File

@@ -14,7 +14,7 @@ all: build
build: $(TARGET)
client/node_modules: client/package.json client/package-lock.json
npm --prefix client install
npm --prefix client ci
touch client/node_modules
$(STATIC): $(JSFILES) client/node_modules

41
client/package-lock.json generated vendored
View File

@@ -5214,8 +5214,7 @@
"ansi-regex": {
"version": "2.1.1",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"aproba": {
"version": "1.2.0",
@@ -5236,14 +5235,12 @@
"balanced-match": {
"version": "1.0.0",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"brace-expansion": {
"version": "1.1.11",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@@ -5258,20 +5255,17 @@
"code-point-at": {
"version": "1.1.0",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"concat-map": {
"version": "0.0.1",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"console-control-strings": {
"version": "1.1.0",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"core-util-is": {
"version": "1.0.2",
@@ -5388,8 +5382,7 @@
"inherits": {
"version": "2.0.3",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"ini": {
"version": "1.3.5",
@@ -5401,7 +5394,6 @@
"version": "1.0.0",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"number-is-nan": "^1.0.0"
}
@@ -5416,7 +5408,6 @@
"version": "3.0.4",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"brace-expansion": "^1.1.7"
}
@@ -5424,14 +5415,12 @@
"minimist": {
"version": "0.0.8",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"minipass": {
"version": "2.3.5",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"safe-buffer": "^5.1.2",
"yallist": "^3.0.0"
@@ -5450,7 +5439,6 @@
"version": "0.5.1",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"minimist": "0.0.8"
}
@@ -5531,8 +5519,7 @@
"number-is-nan": {
"version": "1.0.1",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"object-assign": {
"version": "4.1.1",
@@ -5544,7 +5531,6 @@
"version": "1.4.0",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"wrappy": "1"
}
@@ -5630,8 +5616,7 @@
"safe-buffer": {
"version": "5.1.2",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"safer-buffer": {
"version": "2.1.2",
@@ -5667,7 +5652,6 @@
"version": "1.0.2",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
@@ -5687,7 +5671,6 @@
"version": "3.0.1",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"ansi-regex": "^2.0.0"
}
@@ -5731,14 +5714,12 @@
"wrappy": {
"version": "1.0.2",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"yallist": {
"version": "3.0.3",
"bundled": true,
"dev": true,
"optional": true
"dev": true
}
}
},

View File

@@ -5,6 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<meta name="google" content="notranslate">
<meta http-equiv="x-dns-prefetch-control" content="off">
<link rel="icon" type="image/png" href="favicon.png" sizes="48x48">
<title>AdGuard Home</title>
</head>

View File

@@ -11,7 +11,9 @@
"dhcp_found": "Вашата мрежа вече има активен DHCP сървър. Не е безопасно ползването на втори!",
"dhcp_leases": "DHCP раздадени адреси",
"dhcp_leases_not_found": "Няма намерени активни DHCP адреси",
"dhcp_config_saved": "Запиши конфигурацията на DHCP сървъра",
"form_error_required": "Задължително поле",
"form_error_ip_format": "Невалиден IPv4 адрес",
"form_error_positive": "Проверете дали е положително число",
"dhcp_form_gateway_input": "IP шлюз",
"dhcp_form_subnet_input": "Мрежова маска",
@@ -45,6 +47,7 @@
"disabled_protection": "Защитата е забранена",
"refresh_statics": "Обнови статистиката",
"dns_query": "DNS запитвания",
"blocked_by": "Блокирани от",
"stats_malware_phishing": "вируси/атаки",
"stats_adult": "сайтове за възрастни",
"stats_query_domain": "Най-отваряни страници",
@@ -55,6 +58,7 @@
"top_clients": "Най-активни IP адреси",
"no_clients_found": "Нямa намерени адреси",
"general_statistics": "Обща статисика",
"number_of_dns_query_24_hours": "Сума на DNS заявки за последните 24 часа",
"number_of_dns_query_blocked_24_hours": "Сума на блокирани DNS заявки от филтрите за реклама и местни",
"number_of_dns_query_blocked_24_hours_by_sec": "Сума на блокирани DNS заявки от AdGuard свързани със сигурността",
"number_of_dns_query_blocked_24_hours_adult": "Сума на блокирани сайтове за възрастни",
@@ -69,9 +73,11 @@
"use_adguard_parental": "Включи AdGuard Родителски Надзор",
"use_adguard_parental_hint": "Модул XXX в AdGuard Home ще провери дали страницата има материали за възвъстни. Използва се същия API за анонимност като при модула за Сигурност.",
"enforce_safe_search": "Включи Безопасно Търсене",
"enforce_save_search_hint": "AdGuard Home прилага Безопасно Търсене в следните търсачки и сайтове: Google, Youtube, Bing, и Yandex.",
"no_servers_specified": "Няма избрани услуги",
"general_settings": "Общи настройки",
"upstream_dns": "Главен DNS сървър",
"upstream_dns_hint": "Ако оставите празно, AdGuard Home ще използва <a href='https://1.1.1.1/' target='_blank'>Cloudflare DNS</a> за главен. Използвай tls:// представка за DNS използващи TLS връзка.",
"test_upstream_btn": "Тествай главния DNS",
"apply_btn": "Приложи",
"disabled_filtering_toast": "Забрани филтрирането",
@@ -155,6 +161,7 @@
"install_settings_dns_desc": "За да работи, ще трябва да настроите вашият рутер или устройства да ползват DNS сървър с адрес:",
"install_settings_all_interfaces": "Всички интерфейси",
"install_auth_title": "Удостоверяване",
"install_auth_desc": "Много е важно да зададете име и парола за достъп до вашия панел за администрация на AdGuard Home. Препоръчваме ви да зададете име и парола независимо че го ползвате само в къщи.",
"install_auth_username": "Потребител",
"install_auth_password": "Парола",
"install_auth_confirm": "Потвърдете паролата",

View File

@@ -24,6 +24,7 @@
"form_error_ip_format": "Neplatný formát IP",
"form_error_mac_format": "Neplatný formát MAC",
"form_error_positive": "Musí být větší než 0",
"form_error_negative": "Musí být rovno 0 nebo vyšší",
"dhcp_form_gateway_input": "IP brána",
"dhcp_form_subnet_input": "Maska podsítě",
"dhcp_form_range_title": "Rozsah IP adres",
@@ -187,6 +188,22 @@
"query_log_disabled": "Protokol dotazu je zakázán a lze jej nakonfigurovat v <0>nastavení</0>",
"query_log_strict_search": "Pro striktní vyhledávání použijte dvojité uvozovky",
"query_log_retention_confirm": "Opravdu chcete změnit uchovávání protokolu dotazů? Pokud snížíte hodnotu intervalu, některá data budou ztracena",
"dns_config": "Konfigurace serveru DNS",
"blocking_mode": "Režim blokování",
"nxdomain": "NXDOMAIN",
"null_ip": "Nulová IP",
"custom_ip": "Vlastní IP",
"blocking_ipv4": "Blokování IPv4",
"blocking_ipv6": "Blokování IPv6",
"form_enter_rate_limit": "Zadejte rychlostní limit",
"rate_limit": "Rychlostní limit",
"edns_enable": "Povolit klientskou podsíť EDNS",
"edns_cs_desc": "Pokud je povoleno, AdGuard Home bude odesílat podsítě klientů na servery DNS.",
"rate_limit_desc": "Počet požadavků za sekundu, které smí jeden klient provádět (0: neomezeno)",
"blocking_ipv4_desc": "IP adresa, která se má vrátit v případě blokovaného požadavku A",
"blocking_ipv6_desc": "IP adresa, která se má vrátit v případě blokovaného požadavku AAAA",
"blocking_mode_desc": "<0>NXDOMAIN Odpověď s kódem NXDOMAIN;</0> <0>Nulová IP Odpověď s nulovou IP adresou (0,0.0,0 pro A; :: pro AAAA);</0> <0>Vlastní IP adresa Odpověď s ručně nastavenou IP adresou.</0>",
"upstream_dns_client_desc": "Pokud toto pole ponecháte prázdné, AdGuard Home použije servery nakonfigurované v<0>nastavení DNS</0>.",
"source_label": "Zdroj",
"found_in_known_domain_db": "Nalezeno v databázi známých domén",
"category_label": "Kategorie",
@@ -354,7 +371,6 @@
"rewrite_desc": "Umožňuje snadno nakonfigurovat vlastní DNS odezvy pro konkrétní název domény.",
"rewrite_applied": "Aplikované pravidlo přesměrování",
"dns_rewrites": "Přesměrování DNS",
"form_domain": "Zadejte doménu",
"form_answer": "Zadejte IP adresu nebo název domény",
"form_error_domain_format": "Neplatný formát domény",
"form_error_answer_format": "Neplatný formát odpovědi",
@@ -409,5 +425,6 @@
"descr": "Popis",
"whois": "Whois",
"filtering_rules_learn_more": "<0>Další informace</0> o vytváření vlastních seznamů zakázaných hostitelů.",
"blocked_by_response": "Zakázáno s odpovědí CNAME nebo IP"
"blocked_by_response": "Zakázáno s odpovědí CNAME nebo IP",
"try_again": "Zkusit znovu"
}

View File

@@ -24,6 +24,7 @@
"form_error_ip_format": "Ugyldigt IP-format",
"form_error_mac_format": "Ugyldigt MAC-format",
"form_error_positive": "Skal være større end 0",
"form_error_negative": "Skal være lig med 0 eller større",
"dhcp_form_gateway_input": "Gateway IP",
"dhcp_form_subnet_input": "Subnet mask",
"dhcp_form_range_title": "Interval af IP-adresser",
@@ -187,6 +188,22 @@
"query_log_disabled": "Forespørgselsloggen er deaktiveret og kan konfigureres i <0>indstillinger</0>",
"query_log_strict_search": "Brug dobbelt anførselstegn til streng søgning",
"query_log_retention_confirm": "Er du sikker på, at du vil ændre opbevaring af forespørgselsloggen? Hvis du mindsker intervalværdien, vil nogle data gå tabt",
"dns_config": "DNS-serverkonfiguration",
"blocking_mode": "Blokeringstilstand",
"nxdomain": "NXDOMAIN",
"null_ip": "Null IP",
"custom_ip": "Tilpasset IP",
"blocking_ipv4": "IPv4-blokering",
"blocking_ipv6": "IPv6-blokering",
"form_enter_rate_limit": "Indtast hyppighedsgrænse",
"rate_limit": "Hyppighedsgrænse",
"edns_enable": "Aktiver EDNS Client Subnet",
"edns_cs_desc": "Hvis det er aktiveret, vil AdGuard Home sende klienters subnets til DNS-serverne.",
"rate_limit_desc": "Antallet af anmodninger pr. sekund, som en enkelt klient får lov til at fremsætte (0: ubegrænset)",
"blocking_ipv4_desc": "IP-adresse, der skal returneres for en blokeret A-anmodning",
"blocking_ipv6_desc": "IP-adresse, der skal returneres for en blokeret AAAA-anmodning",
"blocking_mode_desc": "<0>NXDOMAIN - Svar med NXDOMAIN-kode;</0> <0>Null IP - Svar med nul IP-adresse (0.0.0.0 for A, :: for AAAA);</0> <0>Brugerdefineret IP - Svar med en manuelt indstillet IP-adresse.</0>",
"upstream_dns_client_desc": "Hvis du lader dette felt være tomt, vil AdGuard Home bruge de servere, der er konfigureret i <0>DNS-indstillingerne</0>.",
"source_label": "Kilde",
"found_in_known_domain_db": "Fundet i databasen med kendte domæner.",
"category_label": "Kategori",
@@ -354,7 +371,6 @@
"rewrite_desc": "Gør det nemt at konfigurere det tilpassede DNS-svar for et specifikt domænenavn.",
"rewrite_applied": "Anvendt Omskrivningsregel",
"dns_rewrites": "DNS-omskrivninger",
"form_domain": "Indtast domæne",
"form_answer": "Indtast IP-adresser eller domænenavne",
"form_error_domain_format": "Ugyldigt domæneformat",
"form_error_answer_format": "Ugyldigt svarformat",
@@ -409,5 +425,6 @@
"descr": "Beskrivelse",
"whois": "Whois",
"filtering_rules_learn_more": "<0>Lær mere</0> om at oprette dine egne værtsblokeringslister.",
"blocked_by_response": "Blokeret af CNAME eller IP som svar"
"blocked_by_response": "Blokeret af CNAME eller IP som svar",
"try_again": "Prøv igen"
}

View File

@@ -17,9 +17,15 @@
"dhcp_leases": "DHCP-Leasingverträge",
"dhcp_static_leases": "DHCP statische Leases",
"dhcp_leases_not_found": "Keine DHCP-Leasingverträge gefunden\n",
"dhcp_config_saved": "Gespeicherte DHCP-Server-Konfiguration",
"form_error_required": "Pflichtfeld",
"form_error_ip4_format": "Ungültiges IPv4-Format",
"form_error_ip6_format": "Ungültiges IPv6-Format",
"form_error_ip_format": "Ungültiges IPv4-Format",
"form_error_mac_format": "Ungültiges MAC-Format",
"form_error_client_id_format": "Ungültiges Client-ID-Format",
"form_error_positive": "Muss größer als 0 sein.",
"form_error_negative": "Muss gleich oder größer als 0 (Null) sein",
"dhcp_form_gateway_input": "Gateway-IP",
"dhcp_form_subnet_input": "Subnetz-Maske",
"dhcp_form_range_title": "Bereich von IP-Adressen",
@@ -41,6 +47,7 @@
"dhcp_new_static_lease": "Neuer statischer Lease",
"dhcp_static_leases_not_found": "Keine statischen DHCP-Leases gefunden",
"dhcp_add_static_lease": "Statischen Lease hinzufügen",
"dhcp_reset": "Möchten Sie die DHCP-Konfiguration wirklich zurücksetzen?",
"delete_confirm": "Möchten Sie „{{key}}” wirklich löschen?",
"form_enter_hostname": "Gerätenamen eingeben",
"error_details": "Fehlerdetails",
@@ -94,13 +101,16 @@
"use_adguard_parental": "AdGuard Webservice für Kindersicherung verwenden",
"use_adguard_parental_hint": "AdGuard Home wird überprüfen, ob die Domain Inhalte hat, die nur für Erwachsene geeignet sind. Zum Schutz Ihrer Privatsphäre wird die gleiche API wie für den Webservice für Internet-Sicherheit verwendet.",
"enforce_safe_search": "SafeSearch erzwingen",
"enforce_save_search_hint": "AdGuard kann SafeSearch für folgende Suchmaschinen erzwingen: Google, Youtube, Bing und Yandex.",
"no_servers_specified": "Keine Server festgelegt",
"general_settings": "Allgemeine Einstellungen",
"dns_settings": "DNS-Einstellungen",
"encryption_settings": "Verschlüsselungseinstellungen",
"dhcp_settings": "DHCP-Einstellungen",
"upstream_dns": "Upstream-DNS-Server",
"upstream_dns_hint": "Wenn Sie dieses Feld leer lassen wird AdGuard Home <a href='https://1.1.1.1/' target='_blank'>Cloudflare DNS</a> als Upstream verwenden. Verwenden Sie das Präfix tls:// für DNS über TLS-Server.",
"test_upstream_btn": "Upstreams testen",
"upstreams": "Upstreams",
"apply_btn": "Anwenden",
"disabled_filtering_toast": "Filtern deaktiviert",
"enabled_filtering_toast": "Filtern aktiviert",
@@ -179,6 +189,22 @@
"query_log_disabled": "Das Abfrageprotokoll ist deaktiviert und kann in den <0>Einstellungen</0> konfiguriert werden.",
"query_log_strict_search": "Doppelte Anführungszeichen für die strikte Suche verwenden",
"query_log_retention_confirm": "Möchten Sie die Aufbewahrung des Abfrageprotokolls wirklich ändern? Wenn Sie den Zeitabstand verringern, gehen einige Daten verloren.",
"dns_config": "DNS-Serverkonfiguration",
"blocking_mode": "Sperrmodus",
"nxdomain": "NXDomain",
"null_ip": "Null-IP-Adresse",
"custom_ip": "Benutzerdefinierte IP",
"blocking_ipv4": "IPv4-Sperren",
"blocking_ipv6": "IPv6-Sperren",
"form_enter_rate_limit": "Begrenzungswert eingeben",
"rate_limit": "Begrenzungswert",
"edns_enable": "EDNS Client Subnetz aktivieren",
"edns_cs_desc": "Wenn aktiviert, sendet AdGuard Home die Subnetze der Clients an die DNS-Server.",
"rate_limit_desc": "Die Anzahl der Anfragen pro Sekunde, die ein einzelner Client stellen darf (0: unbegrenzt)",
"blocking_ipv4_desc": "IP-Adresse, die für eine gesperrte A-Anfrage zurückgegeben werden soll",
"blocking_ipv6_desc": "IP-Adresse, die für eine gesperrte AAAA-Anfrage zurückgegeben werden soll",
"blocking_mode_desc": "<0>NXDomain - Antwortet mit NXDomain-Code;</0> <0>Null-IP-Adresse - Antwortet mit Null-IP-Adresse (0.0.0.0 für A; :: für AAAA);</0> <0>Benutzerdefinierte IP - Antwortet mit einer manuell festgelegten IP-Adresse.</0>",
"upstream_dns_client_desc": "Wenn Sie dieses Feld leer lassen, verwendet AdGuard Home die Server, die in den <0>DNS-Einstellungen</0> konfiguriert sind.",
"source_label": "Quelle",
"found_in_known_domain_db": "In der Datenbank der bekannten Domains gefunden.",
"category_label": "Kategorie",
@@ -275,6 +301,8 @@
"update_announcement": "AdGuard Home {{version}} ist jetzt verfügbar! <0>Klicken Sie hier</0> für weitere Informationen.",
"setup_guide": "Einrichtungsassistent",
"dns_addresses": "DNS-Adressen",
"dns_start": "DNS-Server wird gestartet",
"dns_status_error": "Fehler bei Statusabfrage des DNS-Server",
"down": "Nicht erreichbar",
"fix": "Beheben",
"dns_providers": "Hier finden Sie eine <0>Liste der bekannten DNS-Anbieter</0> zur Auswahl.",
@@ -293,8 +321,11 @@
"client_edit": "Client bearbeiten",
"client_identifier": "Bezeichner",
"ip_address": "IP-Adresse",
"client_identifier_desc": "Clients können durch die IP-Adresse oder MAC-Adresse identifiziert werden. Bitte beachten Sie, dass die Verwendung der MAC-Adresse als Identifikator nur möglich ist, wenn AdGuard Home gleichzeitig auch ein <0>DHCP-Server</0> ist.",
"form_enter_ip": "IP-Adresse eingeben",
"form_enter_mac": "MAC-Adresse eingeben",
"form_enter_id": "Kennung eingeben",
"form_add_id": "Kennung hinzufügen",
"form_client_name": "Clientnamen eingeben",
"client_global_settings": "Allgemeine Einstellungen nutzen",
"client_deleted": "Client „{{key}}” erfolgreich entfernt",
@@ -395,5 +426,10 @@
"netname": "Netzwerkname",
"descr": "Beschreibung",
"whois": "Whois",
"filtering_rules_learn_more": "<0>Erfahren Sie mehr</0> über die Erstellung eigener Hosts-Blocklisten."
"filtering_rules_learn_more": "<0>Erfahren Sie mehr</0> über die Erstellung eigener Hosts-Blocklisten.",
"blocked_by_response": "Nach CNAME oder IP-Antwort blockiert",
"try_again": "Erneut versuchen",
"domain_desc": "Geben Sie den Domain-Namen oder den Platzhalter ein, der umgeschrieben werden soll.",
"example_rewrite_domain": "Antworten nur für diesen Domain-Namen umschreiben.",
"example_rewrite_wildcard": "Antworten nur für alle <0>example.org</0> Subdomains umschreiben."
}

View File

@@ -23,6 +23,7 @@
"form_error_ip6_format": "Invalid IPv6 format",
"form_error_ip_format": "Invalid IP format",
"form_error_mac_format": "Invalid MAC format",
"form_error_client_id_format": "Invalid client ID format",
"form_error_positive": "Must be greater than 0",
"form_error_negative": "Must be equal to 0 or greater",
"dhcp_form_gateway_input": "Gateway IP",
@@ -301,7 +302,7 @@
"setup_guide": "Setup guide",
"dns_addresses": "DNS addresses",
"dns_start": "DNS server is starting up",
"dns_status_error": "Error of getting DNS server status",
"dns_status_error": "Error checking the DNS server status",
"down": "Down",
"fix": "Fix",
"dns_providers": "Here is a <0>list of known DNS providers</0> to choose from.",
@@ -371,7 +372,7 @@
"rewrite_desc": "Allows to easily configure custom DNS response for a specific domain name.",
"rewrite_applied": "Applied Rewrite rule",
"dns_rewrites": "DNS rewrites",
"form_domain": "Enter domain",
"form_domain": "Enter domain name or wildcard",
"form_answer": "Enter IP address or domain name",
"form_error_domain_format": "Invalid domain format",
"form_error_answer_format": "Invalid answer format",
@@ -427,5 +428,8 @@
"whois": "Whois",
"filtering_rules_learn_more": "<0>Learn more</0> about creating your own hosts blocklists.",
"blocked_by_response": "Blocked by CNAME or IP in response",
"try_again": "Try again"
"try_again": "Try again",
"domain_desc": "Enter the domain name or wildcard you want to be rewritten.",
"example_rewrite_domain": "rewrite responses for this domain name only.",
"example_rewrite_wildcard": "rewrite responses for all <0>example.org</0> subdomains."
}

View File

@@ -12,7 +12,7 @@
"dhcp_description": "Si su router no proporciona la configuración DHCP, puede utilizar el propio servidor DHCP incorporado de AdGuard.",
"dhcp_enable": "Habilitar servidor DHCP",
"dhcp_disable": "Deshabilitar servidor DHCP",
"dhcp_not_found": "Es seguro habilitar el servidor DHCP incorporado. No se ha encontrado ningún servidor DHCP activo en la red, sin embargo le recomendamos que lo vuelva a comprobar manualmente, ya que nuestra prueba automática no ofrece actualmente una garantía del 100%.",
"dhcp_not_found": "Es seguro habilitar el servidor DHCP incorporado. No se ha encontrado ningún servidor DHCP activo en la red, sin embargo le recomendamos que lo vuelva a comprobar manualmente, ya que nuestra prueba automática no ofrece actualmente una garantía del 100 %.",
"dhcp_found": "Un servidor DHCP activo se encuentra en la red. No es seguro habilitar el servidor DHCP incorporado.",
"dhcp_leases": "Asignaciones DHCP",
"dhcp_static_leases": "Asignaciones DHCP estáticas",
@@ -24,6 +24,7 @@
"form_error_ip_format": "Formato IP no válido",
"form_error_mac_format": "Formato MAC no válido",
"form_error_positive": "Debe ser mayor que 0",
"form_error_negative": "Debe ser igual o mayor que 0",
"dhcp_form_gateway_input": "IP de puerta de enlace",
"dhcp_form_subnet_input": "Máscara de subred",
"dhcp_form_range_title": "Rango de direcciones IP",
@@ -141,7 +142,7 @@
"examples_title": "Ejemplos",
"example_meaning_filter_block": "bloquea el acceso al dominio ejemplo.org\ny a todos sus subdominios",
"example_meaning_filter_whitelist": "desbloquea el acceso al dominio ejemplo.org y a todos sus subdominios",
"example_meaning_host_block": "AdGuard Home regresará la dirección 127.0.0.1 para el dominio ejemplo.org (pero no para sus subdominios).",
"example_meaning_host_block": "AdGuard Home devolverá la dirección 127.0.0.1 para el dominio ejemplo.org (pero no para sus subdominios).",
"example_comment": "! Aquí va un comentario",
"example_comment_meaning": "solo un comentario",
"example_comment_hash": "# También un comentario",
@@ -187,11 +188,21 @@
"query_log_disabled": "El registro de consultas está deshabilitado y se puede configurar en la <0>configuración</0>",
"query_log_strict_search": "Usar comillas dobles para una búsqueda estricta",
"query_log_retention_confirm": "¿Está seguro de que desea cambiar la retención del registro de consultas? Si disminuye el valor del intervalo, se perderán algunos datos",
"dns_config": "Configuración del servidor DNS",
"blocking_mode": "Modo de bloqueo",
"nxdomain": "NXDOMAIN",
"null_ip": "IP nulo",
"custom_ip": "IP personalizada",
"blocking_ipv4": "Bloqueo de IPv4",
"blocking_ipv6": "Bloqueo de IPv6",
"form_enter_rate_limit": "Ingrese el límite de cantidad",
"rate_limit": "Límite de cantidad",
"edns_enable": "Habilitar subred de cliente EDNS",
"edns_cs_desc": "Si está habilitado, AdGuard Home enviará las subredes de los clientes a los servidores DNS.",
"rate_limit_desc": "Número de peticiones por segundo que un solo cliente puede hacer (0: ilimitado)",
"blocking_ipv4_desc": "Dirección IP devolverá una petición A bloqueada",
"blocking_ipv6_desc": "Dirección IP devolverá una petición AAAA bloqueada",
"blocking_mode_desc": "<0>NXDOMAIN - Responde con el código NXDOMAIN.</0> <0>IP nulo - Responde con una dirección IP cero (0.0.0.0 para A; :: para AAAA).</0> <0>IP personalizada - Responde con una dirección IP establecida manualmente.</0>",
"upstream_dns_client_desc": "Si mantiene este campo vacío, AdGuard Home utilizará los servidores configurados en la <0>configuración del DNS</0>.",
"source_label": "Fuente",
"found_in_known_domain_db": "Encontrado en la base de datos de dominios conocidos.",
@@ -360,7 +371,6 @@
"rewrite_desc": "Permite configurar fácilmente la respuesta DNS personalizada para un nombre de dominio específico.",
"rewrite_applied": "Regla de reescritura aplicada",
"dns_rewrites": "Reescrituras DNS",
"form_domain": "Ingrese el dominio",
"form_answer": "Ingrese la dirección IP o el nombre del dominio",
"form_error_domain_format": "Formato de dominio no válido",
"form_error_answer_format": "Formato de respuesta no válido",
@@ -415,5 +425,6 @@
"descr": "Descripción",
"whois": "Whois",
"filtering_rules_learn_more": "<0>Más información</0> sobre cómo crear tus propias listas para bloqueo de hosts.",
"blocked_by_response": "Bloqueado por CNAME o IP en respuesta"
"blocked_by_response": "Bloqueado por CNAME o IP en respuesta",
"try_again": "Volver a intentar"
}

View File

@@ -330,7 +330,6 @@
"rewrite_desc": "به آسانی اجازه پیکربندی پاسخ DNS دستی برای یک نام دامنه خاص را می دهد.",
"rewrite_applied": "دستور بازنویسی اِعمال شد",
"dns_rewrites": "بازنویسی های DNS",
"form_domain": "نام دامنه را وارد کنید",
"form_answer": "نام دامنه یا آدرس آی پی را وارد کنید",
"form_error_domain_format": "فرمت دامنه اشتباه است",
"form_error_answer_format": "فرمت پاسخ اشتباه است",

View File

@@ -30,6 +30,7 @@
"dhcp_ip_addresses": "Adresses IP",
"dhcp_table_hostname": "Nom de machine",
"dhcp_table_expires": "Expire le",
"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>. Nous allons automatiquement définir une adresse IP comme statique si vous appuyez sur le bouton Activer DHCP.",
"back": "Retour",
"dashboard": "Tableau de bord",
"settings": "Paramètres",
@@ -74,6 +75,7 @@
"no_servers_specified": "Pas de serveurs spécifiés",
"general_settings": "Paramètres généraux",
"upstream_dns": "Serveurs DNS upstream",
"upstream_dns_hint": "Si vous laissez ce champ vide, AdGuard Home va utiliser <a href='https://www.quad9.net/' target='_blank'>Quad9</a> comme upstream.",
"test_upstream_btn": "Tester les upstreams",
"apply_btn": "Appliquer",
"disabled_filtering_toast": "Filtrage désactivé",
@@ -116,7 +118,7 @@
"example_upstream_tcp": "DNS classique (au-dessus de TCP)",
"all_filters_up_to_date_toast": "Tous les filtres sont mis à jour",
"updated_upstream_dns_toast": "Les serveurs DNS upstream sont mis à jour",
"dns_test_ok_toast": "Les serveurs DNS spécifiés fonctionnent de manière correcte",
"dns_test_ok_toast": "Les serveurs DNS spécifiés fonctionnent correctement",
"dns_test_not_ok_toast": "Impossible d'utiliser le serveur \"{{key}}\": veuillez vérifier si le nom saisi est bien correct",
"unblock_btn": "Débloquer",
"block_btn": "Bloquer",
@@ -136,6 +138,8 @@
"updated_custom_filtering_toast": "Règles de filtrage d'utilisateur mises à jour",
"rule_removed_from_custom_filtering_toast": "Règle retirée des règles d'utilisateur",
"rule_added_to_custom_filtering_toast": "Règle ajoutée aux règles d'utilisateur",
"edns_cs_desc": "Si activé, AdGuard Home enverra les sous-réseaux des clients aux serveurs DNS.",
"upstream_dns_client_desc": "Si vous laissez ce champ vide, AdGuard Home utilisera les serveurs configurés dans les <0>paramètres DNS</0>.",
"found_in_known_domain_db": "Trouvé dans la base de données des domaines connus",
"category_label": "Catégorie",
"rule_label": "Règle",
@@ -155,5 +159,6 @@
"install_devices_windows_list_3": "Sur la partie gauche de l'écran, recherchez Modifier les paramètres de la carte et cliquez dessus.",
"install_devices_windows_list_4": "Sélectionnez votre connexion active, clic droit dessus et sélectionnez Propriétés.",
"install_devices_windows_list_5": "Recherchez la version du protocole Internet 4 (TCP/IP) dans la liste, sélectionnez-la puis cliquez à nouveau sur Propriétés.",
"access_blocked_desc": "Ne confondez pas ceci avec les filtres. AdGuard Home bloquera les requêtes DNS avec ces domaines dans la requête.",
"updates_version_equal": "AdGuard Home est à jour"
}
}

View File

@@ -24,6 +24,7 @@
"form_error_ip_format": "Nevažeći format IP adrese",
"form_error_mac_format": "Nevažeći MAC format",
"form_error_positive": "Mora biti veće od 0",
"form_error_negative": "Mora biti jednako ili veće od 0",
"dhcp_form_gateway_input": "Gateway IP",
"dhcp_form_subnet_input": "Subnet maskiranje",
"dhcp_form_range_title": "Raspon IP adresa",
@@ -187,6 +188,22 @@
"query_log_disabled": "Zapisnik upita je onemogućen i može se postaviti u <0>postavkama</0>",
"query_log_strict_search": "Koristite dvostruke navodnike za strogo pretraživanje",
"query_log_retention_confirm": "Jeste li sigurni da želite promijeniti zadržavanje zapisnika upita? Ako smanjite vrijednost intervala, neki će podaci biti izgubljeni",
"dns_config": "DNS postavke poslužitelja",
"blocking_mode": "Način blokiranja",
"nxdomain": "NXDOMAIN",
"null_ip": "Null IP",
"custom_ip": "Prilagođen IP",
"blocking_ipv4": "Blokiranje IPv4",
"blocking_ipv6": "Blokiranje IPv6",
"form_enter_rate_limit": "Unesite ograničenje",
"rate_limit": "Ograničenje",
"edns_enable": "Omogući EDNS Client Subnet",
"edns_cs_desc": "Ako je omogućeno, AdGuard Home će slati podmreže klijenata na DNS poslužitelje.",
"rate_limit_desc": "Broj zahtjeva u sekundi koji su dopušteni po jednom klijentu (0: neograničeno)",
"blocking_ipv4_desc": "Povratna IP adresa za blokirane A zahtjeve",
"blocking_ipv6_desc": "Povratna IP adresa za blokirane AAAA zahtjeve",
"blocking_mode_desc": "<0>NXDOMAIN Odgovor s NXDOMAIN kôdom;</0> <0>Null IP Odgovor s nuliranom IP adresom (0.0.0.0 za A; :: za AAAA);</0> <0>Prilagođeni IP - Odgovor s ručno postavljenom IP adresom.</0>",
"upstream_dns_client_desc": "Ako ovo polje ostane prazno, AdGuard Home će upotrijebiti poslužitelje postavljene u <0>DNS postavkama</0>.",
"source_label": "Izvor",
"found_in_known_domain_db": "Pronađeno u bazi poznatih domena.",
"category_label": "Kategorija",
@@ -354,7 +371,6 @@
"rewrite_desc": "Omogućuje jednostavno postavljanje prilagođenog DNS odgovora za određenu domenu.",
"rewrite_applied": "Primijenjena pravila prijepisa",
"dns_rewrites": "DNS prijepisi",
"form_domain": "Unesite domenu",
"form_answer": "Unesite IP adresu ili naziv domene",
"form_error_domain_format": "Nevažeći format domene",
"form_error_answer_format": "Nevažeći format odgovora",
@@ -409,5 +425,6 @@
"descr": "Opis",
"whois": "Whois",
"filtering_rules_learn_more": "<0>Saznajte više</0> o stvaranju vlastitog hosts popisa neželjenih.",
"blocked_by_response": "Blokirano od strane CNAME-a ili IP-a u odgovoru"
"blocked_by_response": "Blokirano od strane CNAME-a ili IP-a u odgovoru",
"try_again": "Pokušajte ponovno"
}

View File

@@ -17,7 +17,9 @@
"dhcp_leases": "DHCP leases",
"dhcp_static_leases": "DHCP static leases",
"dhcp_leases_not_found": "DHCP lease tidak ditemukan",
"dhcp_config_saved": "Pengaturan server DHCP tersimpan",
"form_error_required": "Kolom yang harus diisi",
"form_error_ip_format": "Format IPv4 tidak valid",
"form_error_mac_format": "Format MAC tidak valid",
"form_error_positive": "Harus lebih dari 0",
"dhcp_form_gateway_input": "IP gateway",
@@ -94,12 +96,14 @@
"use_adguard_parental": "Gunakan layanan web kontrol orang tua AdGuard",
"use_adguard_parental_hint": "AdGuard Home akan mengecek jika domain mengandung materi dewasa. Akan menggunakan API yang ramah privasi yang sama sebagai layanan web keamanan penjelajahan.",
"enforce_safe_search": "Paksa penelusuran aman",
"enforce_save_search_hint": "AdGuard Home dapat memaksa penelusuran aman pada mesin pencari berikut: Google, Youtube, Bing, dan Yandex.",
"no_servers_specified": "Sever tidak disebutkan",
"general_settings": "Pengaturan umum",
"dns_settings": "Pengaturan DNS",
"encryption_settings": "Pengaturan enkripsi",
"dhcp_settings": "Pengaturan DHCP",
"upstream_dns": "Server DNS hulu",
"upstream_dns_hint": "Jika Anda mengosongkan kolom ini, AdGuard Home akan menggunakan <a href='https://1.1.1.1/' target='_blank'>Cloudflare DNS</a> sebagai hulu. Gunakan tls:// untuk server DNS over TLS.",
"test_upstream_btn": "Uji hulu",
"apply_btn": "Terapkan",
"disabled_filtering_toast": "Penyaringan nonaktif",
@@ -137,6 +141,7 @@
"example_comment": "! Komentar di sini",
"example_comment_meaning": "hanya sebuah komentar",
"example_comment_hash": "Juga sebuah komentar",
"example_regex_meaning": "blokir akses ke domain yang cocok dengan <0>ekspresi reguler yang ditentukan</0>",
"example_upstream_regular": "DNS reguler (melalui UDP)",
"example_upstream_dot": "terenkripsi <a href='https://en.wikipedia.org/wiki/DNS_over_TLS' target='_blank'>DNS-over-TLS</a>",
"example_upstream_doh": "terenkripsi <a href='https://en.wikipedia.org/wiki/DNS_over_HTTPS' target='_blank'>DNS-over-HTTPS</a>",
@@ -169,6 +174,15 @@
"rule_added_to_custom_filtering_toast": "Aturan ditambah ke aturan penyaringan khusus",
"query_log_response_status": "Status: {{value}}",
"query_log_filtered": "Difilter oleh {{filter}}",
"query_log_confirm_clear": "Apakah Anda yakin ingin menghapus seluruh kueri log?",
"query_log_cleared": "Kueri log telah berhasil dihapus",
"query_log_clear": "Hapus kueri log",
"query_log_retention": "Retensi kueri log",
"query_log_enable": "Aktifkan log",
"query_log_configuration": "Konfigurasi log",
"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_retention_confirm": "Apakah Anda yakin ingin mengubah retensi kueri log? Jika Anda menurunkan nilai interval, beberapa data akan hilang",
"source_label": "Sumber",
"found_in_known_domain_db": "Ditemukan di database domain dikenal",
"category_label": "Kategori",
@@ -186,6 +200,7 @@
"install_settings_dns_desc": "Anda perlu mengkonfigurasi perangkat atau router anda untuk menggunakan server DNS berikut ini",
"install_settings_all_interfaces": "Semua antarmuka",
"install_auth_title": "Otentikasi",
"install_auth_desc": "Sangat disarankan untuk mengkonfigurasi otentikasi kata sandi ke antarmuka web admin AdGuard Home Anda. Meskipun hanya dapat diakses di jaringan lokal Anda, tetap penting untuk melindunginya dari akses tak terbatas.",
"install_auth_username": "Nama Pengguna",
"install_auth_password": "Kata Sandi",
"install_auth_confirm": "Konfirmasi kata sandi",
@@ -264,6 +279,7 @@
"update_announcement": "AdGuard Home {{version}} sekarang tersedia! <0>Klik di sini</0> untuk info lebih lanjut.",
"setup_guide": "Panduan Penyiapan",
"dns_addresses": "Alamat DNS",
"dns_status_error": "Kesalahan dalam mendapatkan status server DNS",
"down": "Padam",
"fix": "Perbaiki",
"dns_providers": "Berikut adalah <0>daftar penyedia DNS yang dikenal</0> untuk dipilih.",
@@ -282,6 +298,7 @@
"client_edit": "Ubah Klien",
"client_identifier": "Identifikasi",
"ip_address": "Alamat IP",
"client_identifier_desc": "Klien dapat diidentifikasi dengan alamat IP atau alamat MAC. Harap dicatat bahwa menggunakan MAC sebagai pengidentifikasi hanya dimungkinkan jika AdGuard Home juga merupakan <0>server DHCP</0>",
"form_enter_ip": "Masukkan IP",
"form_enter_mac": "Masukkan MAC",
"form_client_name": "Masukkan nama klien",
@@ -330,7 +347,6 @@
"rewrite_desc": "Memungkinkan untuk dengan mudah mengkonfigurasi respons DNS kustom untuk nama domain tertentu.",
"rewrite_applied": "Aturan Rewrite yang diterapkan",
"dns_rewrites": "DNS rewrite",
"form_domain": "Masukkan nama domain",
"form_answer": "Masaukan alamat IP atau nama domain",
"form_error_domain_format": "Nama domain tidak valid",
"form_error_answer_format": "Format jawaban tidak valid",
@@ -358,9 +374,31 @@
"domain": "Domain",
"answer": "Jawab",
"filter_added_successfully": "Filter telah berhasil ditambahkan",
"statistics_configuration": "Konfigurasi statistik",
"statistics_retention": "Statistik disimpan",
"statistics_retention_desc": "Jika Anda menurunkan nilai interval, beberapa data akan hilang",
"statistics_clear": " Hapus statistik",
"statistics_clear_confirm": "Apakah Anda yakin ingin menghapus statistik?",
"statistics_cleared": "Statistik berhasil dihapus"
"statistics_retention_confirm": "Apakah Anda yakin ingin mengubah retensi statistik? Jika Anda menurunkan nilai interval, beberapa data akan hilang",
"statistics_cleared": "Statistik berhasil dihapus",
"interval_hours": "{{count}} jam",
"interval_hours_plural": "{{count}} jam",
"filters_configuration": "Konfigurasi filter",
"filters_enable": "Aktifkan filter",
"filters_interval": "Interval pembaruan filter",
"disabled": "Tidak aktif",
"username_label": "Nama pengguna",
"username_placeholder": "Masukkan nama pengguna",
"password_label": "Kata sandi",
"password_placeholder": "Masukkan kata sandi",
"sign_in": "Masuk",
"sign_out": "Keluar",
"forgot_password": "Lupa kata sandi?",
"forgot_password_desc": "Ikuti <0>langkah-langkah ini</0> untuk membuat kata sandi baru untuk akun pengguna Anda.",
"location": "Lokasi",
"orgname": "Nama organisasi",
"netname": "Nama jaringan",
"descr": "Deskripsi",
"whois": "Whois",
"filtering_rules_learn_more": "<0>Pelajari lebih lanjut</0> tentang membuat daftar hitam host Anda sendiri."
}

View File

@@ -341,7 +341,6 @@
"rewrite_desc": "Consente di configurare facilmente la risposta DNS personalizzata per un nome di dominio specifico.",
"rewrite_applied": "Regola di riscrittura applicata",
"dns_rewrites": "Riscrittura DNS",
"form_domain": "Inserisci il dominio",
"form_answer": "Inserisci l'indirizzo IP o il nome del dominio",
"form_error_domain_format": "Formato del dominio non valido",
"form_error_answer_format": "Formato di risposta non valido",

View File

@@ -17,9 +17,14 @@
"dhcp_leases": "DHCP割り当て",
"dhcp_static_leases": "DHCP静的割り当て",
"dhcp_leases_not_found": "DHCP割当はありません",
"dhcp_config_saved": "DHCP設定の保存に成功しました",
"form_error_required": "必須項目",
"form_error_ip4_format": "IPv4フォーマットではありません",
"form_error_ip6_format": "IPv6フォーマットではありません",
"form_error_ip_format": "IPv4フォーマットではありません",
"form_error_mac_format": "MACフォーマットではありません",
"form_error_positive": "0より大きい必要があります",
"form_error_negative": "0以上である必要があります",
"dhcp_form_gateway_input": "ゲートウェイIP",
"dhcp_form_subnet_input": "サブネットマスク",
"dhcp_form_range_title": "IPアドレスの範囲",
@@ -41,6 +46,7 @@
"dhcp_new_static_lease": "新規静的割り当て",
"dhcp_static_leases_not_found": "DHCP静的割り当てはありません",
"dhcp_add_static_lease": "静的割り当てを追加する",
"dhcp_reset": "DHCP設定をリセットして良いですか",
"delete_confirm": "\"{{key}}\" を削除してもよろしいですか?",
"form_enter_hostname": "ホスト名を入力してください",
"error_details": "エラー詳細",
@@ -64,16 +70,22 @@
"disabled_protection": "保護を無効にしました",
"refresh_statics": "統計データを最新にする",
"dns_query": "DNSクエリ",
"blocked_by": "<0>フィルタにブロックされたDNSクエリ</0>",
"stats_malware_phishing": "ブロックされたマルウェア/フィッシング",
"stats_adult": "ブロックされたアダルトウェブサイト",
"stats_query_domain": "最も問合せされたドメイン",
"for_last_24_hours": "過去24時間以内",
"for_last_days": "過去{{count}}日間以内",
"for_last_days_plural": "過去{{count}}日間以内",
"no_domains_found": "ドメイン情報はありません",
"requests_count": "リクエスト数",
"top_blocked_domains": "最もブロックされたドメイン",
"top_clients": "トップクライアント",
"no_clients_found": "クライアント情報はありません",
"general_statistics": "全般的な統計",
"number_of_dns_query_days": "過去{{count}}日間に処理されたDNSクエリの数",
"number_of_dns_query_days_plural": "過去{{count}}日間に処理されたDNSクエリの数",
"number_of_dns_query_24_hours": "過去24時間に処理されたDNSクエリの数",
"number_of_dns_query_blocked_24_hours": "広告ブロックフィルタとhostsブロックリストによってブロックされたDNSリクエストの数",
"number_of_dns_query_blocked_24_hours_by_sec": "AdGuardブラウジングセキュリティモジュールによってブロックされたDNSリクエストの数",
"number_of_dns_query_blocked_24_hours_adult": "ブロックされたアダルトウェブサイトの数",
@@ -88,13 +100,16 @@
"use_adguard_parental": "AdGuardペアレンタルコントロール・ウェブサービスを使用する",
"use_adguard_parental_hint": "AdGuard Homeは、ドメインにアダルトコンテンツが含まれているかどうかを確認します。 ブラウジングセキュリティ・ウェブサービスと同じプライバシーに優しいAPIを使用します。",
"enforce_safe_search": "セーフサーチを強制する",
"enforce_save_search_hint": "AdGuard Homeは、Google、Youtube、Bing、DuckDuckGo、Yandexの検索エンジンでセーフサーチを強制できます。",
"no_servers_specified": "サーバが指定されていません",
"general_settings": "一般設定",
"dns_settings": "DNS設定",
"encryption_settings": "暗号化設定",
"dhcp_settings": "DHCP設定",
"upstream_dns": "上流DNSサーバ",
"upstream_dns_hint": "このフィールドを未入力のままにすると、AdGuard Homeは上流として<a href='https://1.1.1.1/' target='_blank'>Cloudflare DNS</a>を使用します。DNS over TLSサーバには、「tls://」プレフィックスを使用してください。",
"test_upstream_btn": "上流サーバをテストする",
"upstreams": "上流",
"apply_btn": "適用する",
"disabled_filtering_toast": "フィルタリングを無効にしました",
"enabled_filtering_toast": "フィルタリングを有効にしました",
@@ -131,6 +146,7 @@
"example_comment": "! ここにはコメントが入ります",
"example_comment_meaning": "ただのコメントです",
"example_comment_hash": "# ここもコメントです",
"example_regex_meaning": "指定の正規表現に一致するドメインへのアクセスをブロックします",
"example_upstream_regular": "通常のDNSUDPでの問い合わせ",
"example_upstream_dot": "暗号化されている <a href='https://en.wikipedia.org/wiki/DNS_over_TLS' target='_blank'>DNS-over-TLS</a>",
"example_upstream_doh": "暗号化されている <a href='https://en.wikipedia.org/wiki/DNS_over_HTTPS' target='_blank'>DNS-over-HTTPS</a>",
@@ -161,6 +177,33 @@
"updated_custom_filtering_toast": "カスタム・フィルタリングルールを更新しました",
"rule_removed_from_custom_filtering_toast": "ルールをカスタム・フィルタリングルールから除去しました",
"rule_added_to_custom_filtering_toast": "ルールをカスタム・フィルタリングルールに追加しました",
"query_log_response_status": "ステータス: {{value}}",
"query_log_filtered": "{{filter}}によるフィルタ",
"query_log_confirm_clear": "クエリ・ログ全体を消去してもよろしいですか?",
"query_log_cleared": "クエリ・ログの消去に成功しました",
"query_log_clear": "クエリ・ログを消去する",
"query_log_retention": "クエリ・ログの保持",
"query_log_enable": "ログを有効にする",
"query_log_configuration": "ログ設定",
"query_log_disabled": "クエリ・ログは無効になっており、<0>設定</0>で構成できます",
"query_log_strict_search": "完全一致検索には二重引用符を使用します",
"query_log_retention_confirm": "クエリ・ログの保持を変更してもよろしいですか? 期間を短くすると、一部のデータが失われます",
"dns_config": "DNSサーバ設定",
"blocking_mode": "ブロックモード",
"nxdomain": "NXDOMAIN",
"null_ip": "Null IP",
"custom_ip": "カスタムIP",
"blocking_ipv4": "ブロック中のIPv4",
"blocking_ipv6": "ブロック中のIPv6",
"form_enter_rate_limit": "頻度制限を入力してください",
"rate_limit": "頻度制限",
"edns_enable": "EDNSクライアント・サブネットを有効にする",
"edns_cs_desc": "有効にすると、AdGuard HomeはクライアントのサブネットをDNSサーバへ送信します。",
"rate_limit_desc": "単一のクライアントに許可される1秒あたりのリクエスト数0無制限",
"blocking_ipv4_desc": "ブロックされたAリクエストに対して応答されるIPアドレス",
"blocking_ipv6_desc": "ブロックされたAAAAリクエストに対して応答されるIPアドレス",
"blocking_mode_desc": "<0>NXDOMAIN - NXDOMAINコードで応答;</0> <0>Null IP - ゼロのIPアドレスで応答Aの場合は0.0.0.0; AAAAの場合は::;</0> <0>カスタムIP - 手動で設定されたIPアドレスで応答。</0>",
"upstream_dns_client_desc": "このフィールドを未入力のままにすると、AdGuard Homeは<0>DNS設定</0>で構成されたサーバを使用します。",
"source_label": "ソース",
"found_in_known_domain_db": "既知のドメインデータベースに見つかりました。",
"category_label": "カテゴリ",
@@ -178,6 +221,7 @@
"install_settings_dns_desc": "次のアドレスでDNSサーバを使用するようにあなたのデバイスまたはルータを設定する必要があります:",
"install_settings_all_interfaces": "すべてのインターフェイス",
"install_auth_title": "認証",
"install_auth_desc": "AdGuard Homeの管理ウェブインターフェースにパスワード認証を設定することを強くお勧めします。ローカルネットワークでのみアクセス可能であっても、制限のないアクセスから保護することは重要です。",
"install_auth_username": "ユーザ名",
"install_auth_password": "パスワード",
"install_auth_confirm": "パスワード(確認用)",
@@ -256,6 +300,8 @@
"update_announcement": "AdGuard Home {{version}}がリリースされました。詳しくは<0>こちらをクリック</0>してください。",
"setup_guide": "セットアップガイド",
"dns_addresses": "DNSアドレス",
"dns_start": "DNSサーバが起動処理中です",
"dns_status_error": "DNSサーバ・ステータスの取得エラー",
"down": "ダウン",
"fix": "改善",
"dns_providers": "こちらは、選択可能な<0>既知のDNSプロバイダの一覧</0>です。",
@@ -274,8 +320,11 @@
"client_edit": "クライアントの編集",
"client_identifier": "識別子",
"ip_address": "IPアドレス",
"client_identifier_desc": "クライアントはIPアドレスまたはMACアドレスで識別できます。AdGuard Homeが<0>DHCPサーバ</0>でもある場合にのみ、識別子としてMACを使用することが可能であることにご注意ください。",
"form_enter_ip": "IPアドレスを入力してください",
"form_enter_mac": "MACアドレスを入力してください",
"form_enter_id": "識別子を入力してください",
"form_add_id": "識別子を追加する",
"form_client_name": "クライアント名を入力してください",
"client_global_settings": "グローバル設定を使用する",
"client_deleted": "クライアント \"{{key}}\" の削除に成功しました",
@@ -322,7 +371,7 @@
"rewrite_desc": "特定のドメイン名に対するDNS応答を簡単にカスタマイズすることを可能にします。",
"rewrite_applied": "書き換えルールを適用済み",
"dns_rewrites": "DNS書き換え",
"form_domain": "ドメイン名を入力",
"form_domain": "ドメイン名を入力してください",
"form_answer": "IPアドレスかドメイン名を入力",
"form_error_domain_format": "ドメイン名のフォーマットが間違っています",
"form_error_answer_format": "応答フォーマットが間違っています",
@@ -335,5 +384,48 @@
"blocked_services_global": "ブロックするサービスに対しグローバル設定を使用する",
"blocked_service": "ブロックするサービス",
"block_all": "すべてブロック",
"unblock_all": "すべてのブロックを解除"
"unblock_all": "すべてのブロックを解除",
"encryption_certificate_path": "証明書のパスを入力してください",
"encryption_private_key_path": "秘密鍵のパスを入力してください",
"encryption_certificates_source_path": "証明書のパスを設定する",
"encryption_certificates_source_content": "証明書の内容をペーストする",
"encryption_key_source_path": "秘密鍵のパスを設定する",
"encryption_key_source_content": "秘密鍵の内容をペーストする",
"stats_params": "統計設定",
"config_successfully_saved": "設定の保存に成功しました",
"interval_24_hour": "24時間",
"interval_days": "{{count}}日",
"interval_days_plural": "{{count}}日",
"domain": "ドメイン",
"answer": "応答",
"filter_added_successfully": "フィルタの追加に成功しました",
"statistics_configuration": "統計設定",
"statistics_retention": "統計保持",
"statistics_retention_desc": "期間を短くすると、一部のデータが失われます",
"statistics_clear": "統計を消去する",
"statistics_clear_confirm": "統計を消去してもよろしいですか?",
"statistics_retention_confirm": "統計の保持を変更してもよろしいですか? 期間を短くすると、一部のデータが失われます",
"statistics_cleared": "統計の消去に成功しました",
"interval_hours": "{{count}}時間",
"interval_hours_plural": "{{count}}時間",
"filters_configuration": "フィルタ設定",
"filters_enable": "フィルタを有効にする",
"filters_interval": "フィルタの更新間隔",
"disabled": "無効",
"username_label": "ユーザ名",
"username_placeholder": "ユーザ名を入力してください",
"password_label": "パスワード",
"password_placeholder": "パスワードを入力して下さい",
"sign_in": "サインイン",
"sign_out": "サインアウト",
"forgot_password": "パスワードをお忘れですか?",
"forgot_password_desc": "<0>こちらの手順</0>に従って、新しいパスワードを作成してください。",
"location": "ロケーション",
"orgname": "組織名",
"netname": "ネットワーク名",
"descr": "説明",
"whois": "Whois",
"filtering_rules_learn_more": "独自のブラックリストの作成に関して<0>詳しく学習します</0>。",
"blocked_by_response": "応答されたCNAMEかIPアドレスによるブロック",
"try_again": "再試行する"
}

View File

@@ -17,7 +17,11 @@
"dhcp_leases": "DHCP 임대",
"dhcp_static_leases": "DHCP 고정 임대",
"dhcp_leases_not_found": "DHCP 임대를 찾을 수 없음",
"dhcp_config_saved": "DHCP 서버 설정 저장됨",
"form_error_required": "필수 필드",
"form_error_ip4_format": "잘못된 IPv4 형식",
"form_error_ip6_format": "잘못된 IPv6 형식",
"form_error_ip_format": "잘못된 IP 형식",
"form_error_mac_format": "잘못된 MAC 형식",
"form_error_positive": "0보다 커야 합니다",
"dhcp_form_gateway_input": "게이트웨이 IP",
@@ -41,6 +45,7 @@
"dhcp_new_static_lease": "새 고정 임대",
"dhcp_static_leases_not_found": "DHCP 고정 임대를 찾을 수 없음",
"dhcp_add_static_lease": "고정 임대 추가",
"dhcp_reset": "정말로 DHCP 설정을 초기화할까요?",
"delete_confirm": "\"{{key}}\"을 삭제하시겠습니까?",
"form_enter_hostname": "호스트 이름을 입력해주세요",
"error_details": "오류 상세 정보",
@@ -94,13 +99,16 @@
"use_adguard_parental": "AdGuard 자녀 보호 웹 서비스 사용",
"use_adguard_parental_hint": "AdGuard Home은 도메인에 성인 자료가 포함되어 있는지 확인합니다. 브라우징 보안 웹 서비스와 동일한 개인정보 보호 API를 사용함.",
"enforce_safe_search": "세이프서치 강제",
"enforce_save_search_hint": "AdGuard Home은 다음과 같은 검색엔진(구글, 유투브, 빙, 덕덕고, 얀덱스)에서 안전검색이 가능합니다.",
"no_servers_specified": "지정된 서버 없음",
"general_settings": "일반 설정",
"dns_settings": "DNS 설정",
"encryption_settings": "암호화 설정",
"dhcp_settings": "DHCP 설정",
"upstream_dns": "업스트림 DNS 서버",
"upstream_dns_hint": "이 항목을 비워 두면 AdGuard Home에서 <a href='https://1.1.1.1/' target='_blank'> Cloudflare DNS </a>를 업스트림으로 사용합니다.",
"test_upstream_btn": "업스트림 테스트",
"upstreams": "업스트림",
"apply_btn": "적용",
"disabled_filtering_toast": "필터링 비활성화됨",
"enabled_filtering_toast": "필터링 활성화됨",
@@ -275,6 +283,8 @@
"update_announcement": "AdGuard Home {{version}} 사용 가능합니다! <0>이곳</0>을 클릭하여 더 많은 정보를 확인하세요.",
"setup_guide": "설치 안내",
"dns_addresses": "DNS 주소",
"dns_start": "DNS 서버를 시작하고 있습니다",
"dns_status_error": "DNS 서버 상태를 가져오는 도중 오류가 발생했습니다",
"down": "다운로드",
"fix": "수정",
"dns_providers": "여기에 선택가능한 DNS 목록 </0>이 있습니다.",
@@ -293,8 +303,11 @@
"client_edit": "클라이언트 수정",
"client_identifier": "식별자",
"ip_address": "IP 주소",
"client_identifier_desc": "사용자는 IP 주소 또는 MAC 주소로 식별할 수 있지만 AdGuard Home이 <0>DHCP 서버인 </0> 경우에만 사용자는 MAC 주소로 식별할 수 있습니다.",
"form_enter_ip": "IP 입력",
"form_enter_mac": "MAC 입력",
"form_enter_id": "식별자 입력",
"form_add_id": "식별자 추가",
"form_client_name": "클라이언트 이름 입력",
"client_global_settings": "글로벌 설정 사용",
"client_deleted": "클라이언트 \"{{key}}\"가 정상적으로 삭제되었습니다",
@@ -341,7 +354,6 @@
"rewrite_desc": "특정 도메인 이름에 대한 사용자 지정 DNS 응답을 쉽게 구성할 수 있습니다.",
"rewrite_applied": "적용된 변경 규칙",
"dns_rewrites": "DNS 변경",
"form_domain": "도메인을 입력하세요",
"form_answer": "IP 주소 또는 도메인 이름을 입력하세요",
"form_error_domain_format": "도메인 형식이 잘못되었습니다",
"form_error_answer_format": "답변 형식이 잘못되었습니다. ",
@@ -395,5 +407,6 @@
"netname": "네트워크 이름",
"descr": "설명",
"whois": "후이즈",
"filtering_rules_learn_more": "차단 리스트를 직접 호스트하는 법을 <0>알아보세요</0>."
"filtering_rules_learn_more": "차단 리스트를 직접 호스트하는 법을 <0>알아보세요</0>.",
"blocked_by_response": "응답 중 차단된 CNAME 또는 IP"
}

View File

@@ -15,10 +15,13 @@
"dhcp_not_found": "Geen actieve DHCP servers gevonden op het netwerk. Het is VEILIG om de ingebouwde DHCP server in te schakelen",
"dhcp_found": "Actieve DHCP server(s) gevonden op het netwerk. het is NIET veilig om de ingebouwde DHCP server in te schakelen.",
"dhcp_leases": "DHCP lease overzicht",
"dhcp_static_leases": "DHCP statische lease",
"dhcp_leases_not_found": "Geen DHCP lease gevonden",
"dhcp_config_saved": "DHCP server configuratie opgeslagen",
"form_error_required": "Vereist veld",
"form_error_ip4_format": "Ongeldig IPv4 formaat",
"form_error_ip6_format": "Ongeldig IPv6 formaat",
"form_error_ip_format": "Ongeldig IPv4 formaat",
"form_error_mac_format": "Ongeldig MAC formaat.",
"form_error_positive": "Moet groter zijn dan 0",
"dhcp_form_gateway_input": "Gateway IP",
@@ -34,6 +37,11 @@
"dhcp_table_hostname": "Host naam",
"dhcp_table_expires": "Verloopt op",
"dhcp_warning": "Indien je de ingebouwde DHCP server wilt inschakelen, let dan op dat er geen andere actieve DHCP server aanwezig is. Dit kan de internet verbinding instabiel maken!.",
"dhcp_error": "We kunnen niet bepalen of er een andere DHCP server in het netwerk is.",
"dhcp_static_ip_error": "Om de DHCP server te gebruiken, moet een statisch IP-adres worden ingesteld. We hebben niet kunnen vaststellen of de netwerkinterface is geconfigureerd met een statisch IP-adres. Stel handmatig een statisch IP-adres in.",
"dhcp_dynamic_ip_found": "Om de DHCP server te gebruiken voor interface <0>{{interfaceName}}</0>, moet er een statisch IP-adres worden ingesteld. Uw huidige IP-adres is <0>{{ipAddress}}</0>. We stellen automatische dit IP-adres in, als statisch IP-adres, wanneer u op de knop DHCP inschakelen drukt.",
"dhcp_lease_added": "Statische uitgifte \"{{key}}\" met succes toegevoegd",
"dhcp_lease_deleted": "Statische uitgifte \"{{key}}\" met succes verwijderd",
"dhcp_new_static_lease": "Voeg static lease toe",
"dhcp_static_leases_not_found": "Geen DHCP static lease gevonden",
"dhcp_add_static_lease": "Voeg statische lease toe",
@@ -61,10 +69,12 @@
"disabled_protection": "Bescherming uitgeschakeld",
"refresh_statics": "Ververs statistieken",
"dns_query": "DNS-queries",
"blocked_by": "<0>Geblokkeerd door Filters</0>",
"stats_malware_phishing": "Geblokkeerde malware/phishing",
"stats_adult": "Geblokkeerde 18+ websites",
"stats_query_domain": "Meest bezochte domeinen",
"for_last_24_hours": "van de laatste 24-uur",
"for_last_days": "sinds de laatste {{count}} dagen",
"for_last_days_plural": "sinds de laatste {{count}} dagen",
"no_domains_found": "Geen domeinen gevonden",
"requests_count": "Verzoek teller",
@@ -72,6 +82,7 @@
"top_clients": "Top gebruikers",
"no_clients_found": "Geen gebruikers gevonden",
"general_statistics": "Generieke statistieken",
"number_of_dns_query_days": "Aantal verwerkte DNS aanvragen van de laatste {{count}} dagen",
"number_of_dns_query_days_plural": "Aantal verwerkte DNS aanvragen van de laatste {{count}} dagen",
"number_of_dns_query_24_hours": "Aantal verwerkte aanvragen van de laatste 24 uur",
"number_of_dns_query_blocked_24_hours": "Aantal geblokkeerde DNS aanvragen door advertentie blokkering en hosts blokkeerlijsten",
@@ -88,12 +99,14 @@
"use_adguard_parental": "Gebruik AdGuard Ouderlijk toezicht web service",
"use_adguard_parental_hint": "AdGuard Home controleert of het domein 18+ content bevat. Dit gebeurt dmv dezelfde privacy vriendelijke API als de Browsing Security web service.",
"enforce_safe_search": "Forceer Veilig Zoeken",
"enforce_save_search_hint": "AdGuard Home kan veilig zoeken forceren voor de volgende zoekmachines: Google, Youtube, Bing, en Yandex.",
"no_servers_specified": "Geen servers gespecificeerd",
"general_settings": "Generieke instellingen",
"dns_settings": "DNS Instellingen",
"encryption_settings": "Encryptie Instellingen",
"dhcp_settings": "DHCP Instellingen",
"upstream_dns": "Upstream DNS servers",
"upstream_dns_hint": "Indien je dit veld leeg laat, zal AdGuard Home <a href='https://1.1.1.1/' target='_blank'>Cloudflare DNS</a> gebruiken als upstream.",
"test_upstream_btn": "Test upstream",
"upstreams": "Upstreams",
"apply_btn": "Toepassen",
@@ -132,6 +145,7 @@
"example_comment": "! Hier komt een opmerking",
"example_comment_meaning": "zomaar een opmerking",
"example_comment_hash": "# Nog een opmerking",
"example_regex_meaning": "blokkeer de toegang tot de domeinen die overeenkomen met de opgegeven reguliere expressie",
"example_upstream_regular": "standaard DNS (over UDP)",
"example_upstream_dot": "versleutelde<a href='https://en.wikipedia.org/wiki/DNS_over_TLS' target='_blank'>DNS-over-TLS</a>",
"example_upstream_doh": "versleutelde<a href='https://en.wikipedia.org/wiki/DNS_over_HTTPS' target='_blank'>DNS-over-HTTPS</a>",
@@ -164,6 +178,16 @@
"rule_added_to_custom_filtering_toast": "Regel toegevoegd aan de aangepaste filterregels",
"query_log_response_status": "Status: {{value}}",
"query_log_filtered": "Gefilterd door {{filter}}",
"query_log_confirm_clear": "Weet u zeker dat u het hele query logboek wilt legen?",
"query_log_cleared": "Het query logboek is succesvol geleegd",
"query_log_clear": "Leeg query logs",
"query_log_retention": "Query logs bewaartermijn",
"query_log_enable": "Log bestanden inschakelen",
"query_log_configuration": "Logbestanden instellingen",
"query_log_disabled": "Het query logboek is uitgeschakeld en kan worden geconfigureerd in de <0>instellingen</0>",
"query_log_strict_search": "Gebruik dubbele aanhalingstekens voor strikt zoeken",
"query_log_retention_confirm": "Weet u zeker dat u de bewaartermijn van het query logboek wilt wijzigen? Als u de intervalwaarde verlaagt, gaan sommige gegevens verloren",
"custom_ip": "Aangepast IP",
"source_label": "Bron",
"found_in_known_domain_db": "Gevonden in de bekende domeingegevensbank.",
"category_label": "Categorie",
@@ -181,6 +205,7 @@
"install_settings_dns_desc": "U moet uw apparaten of router configureren om de DNS-server te gebruiken op de volgende adressen:",
"install_settings_all_interfaces": "Alle interfaces",
"install_auth_title": "Authenticatie",
"install_auth_desc": "Het wordt ten zeerste aanbevolen om wachtwoordverificatie te configureren voor de AdGuard Home admin webinterface. Zelfs als het alleen toegankelijk is in uw lokale netwerk, is het nog steeds belangrijk om het te beschermen tegen onbeperkte toegang.",
"install_auth_username": "Gebruikersnaam",
"install_auth_password": "Wachtwoord",
"install_auth_confirm": "Bevestig wachtwoord",
@@ -279,6 +304,7 @@
"client_edit": "Wijzig gebruiker",
"client_identifier": "Identificeer via",
"ip_address": "IP adres",
"client_identifier_desc": "Gebruikers kunnen worden geïdentificeerd door het IP-adres of MAC-adres. Houd er rekening mee dat het gebruik van MAC als ID alleen mogelijk is als AdGuard Home ook een <0> DHCP-server </0> is",
"form_enter_ip": "Vul IP in",
"form_enter_mac": "Vul MAC in",
"form_enter_id": "ID invoeren",
@@ -309,9 +335,26 @@
"setup_dns_privacy_1": "<0>DNS-over-TLS:</0> Gebruik <1>{{address}}</1> string.",
"setup_dns_privacy_2": "<0>DNS-over-HTTPS:</0> Gebruik <1>{{address}}</1> string.",
"setup_dns_privacy_3": "<0>Hou er rekening mee dat het beveiligde DNS protocol alleen beschikbaar is voor Android 9. U moet dus extra software installeren voor andere besturingssystemen.</0><0>Hier is een lijst van te gebruiken software.</0>",
"setup_dns_privacy_android_1": "Android 9 ondersteunt native DNS-over-TLS. Om het te configureren, ga naar Instellingen → Netwerk & internet → Geavanceerd → Privé DNS en voer daar uw domeinnaam in.",
"setup_dns_privacy_android_2": "<0>AdGuard voor Android</0>ondersteunt<1>DNS-over-HTTPS </1>en<1>DNS-over-TLS</1>.",
"setup_dns_privacy_android_3": "<0> Intra </0> voegt <1> DNS-over-HTTPS</1> ondersteuning toe aan Android.",
"setup_dns_privacy_ios_1": "<0>DNSCloak</0> ondersteunt <1> DNS-over-HTTPS </1>, maar om het te configureren om op uw eigen server te gebruiken moet er een <2> DNS-stempel </2> gegenereerd worden.",
"setup_dns_privacy_ios_2": "<0> AdGuard voor iOS </0> ondersteunt de instellingen <1> DNS-over-HTTPS </1> en <1> DNS-over-TLS </1>.",
"setup_dns_privacy_other_title": "Overig gebruik",
"setup_dns_privacy_other_1": "AdGuard Home kan op elk platform een veilige DNS-client zijn.",
"form_domain": "Vul domein in",
"setup_dns_privacy_other_2": "<0>dnsproxy</0> ondersteunt alle bekende beveiligde DNS-protocollen.",
"setup_dns_privacy_other_3": "<0> dnscrypt-proxy </0> ondersteunt <1> DNS-over-HTTPS </1>.",
"setup_dns_privacy_other_4": "<0> Mozilla Firefox </0> ondersteunt <1> DNS-over-HTTPS </1>.",
"setup_dns_privacy_other_5": "U vindt meer implementaties <0> hier </0> en <1> hier </1>.",
"setup_dns_notice": "Om <1> DNS-over-HTTPS </1> of <1> DNS-over-TLS </1> te gebruiken, moet u <0> Codering </0> configureren in de AdGuard Home-instellingen.",
"rewrite_added": "DNS-herschrijving voor \"{{key}}\" met succes toegevoegd",
"rewrite_deleted": "DNS-herschrijving voor \"{{key}}\" met succes verwijderd",
"rewrite_add": "DNS-herschrijving toevoegen",
"rewrite_not_found": "Geen DNS-herschrijving gevonden",
"rewrite_confirm_delete": "Bent u zeker dat u DNS-herschrijving \"{{key}}\" wilt verwijderen?",
"rewrite_desc": "Hiermee kunt u eenvoudig aangepaste DNS-antwoorden configureren voor een specifieke domeinnaam.",
"rewrite_applied": "Toegepaste herschrijf regel",
"dns_rewrites": "DNS herschrijvingen",
"form_answer": "Vul IP adres of domeinnaam in",
"form_error_domain_format": "Ongeldige domeinnaam",
"form_error_answer_format": "Ongeldig antwoord",
@@ -334,14 +377,38 @@
"stats_params": "Statistieken configuratie",
"config_successfully_saved": "Configuratie succesvol opgeslagen",
"interval_24_hour": "24 uur",
"interval_days": "{{count}} dagen",
"interval_days_plural": "{{count}} dagen",
"domain": "Domein",
"answer": "Antwoord",
"filter_added_successfully": "Het filter is succesvol toegevoegd",
"statistics_configuration": "Statistieken configuratie",
"statistics_retention": "Statistieken retentie",
"statistics_retention_desc": "Als je de interval waarde vermindert, zullen sommige gegevens verloren gaan",
"statistics_clear": " Statistieken wissen",
"statistics_clear_confirm": "Alle statistieken werkelijk wissen?",
"statistics_retention_confirm": "Weet u zeker dat u de bewaartermijn van de statistieken wilt wijzigen? Als u de intervalwaarde verlaagt, gaan sommige gegevens verloren",
"statistics_cleared": "Statistieken succesvol gewist",
"blocked_by_response": "Geblokkeerd door CNAME of IP als antwoord"
"interval_hours": "{{count}} uur",
"interval_hours_plural": "{{count}} uren",
"filters_configuration": "Filters instellingen",
"filters_enable": "Filters inschakelen",
"filters_interval": "Filters update frequentie",
"disabled": "Uitgeschakeld",
"username_label": "Gebruikersnaam",
"username_placeholder": "Voer gebruikersnaam in",
"password_label": "Wachtwoord",
"password_placeholder": "Voer wachtwoord in",
"sign_in": "Aanmelden",
"sign_out": "Afmelden",
"forgot_password": "Wachtwoord vergeten?",
"forgot_password_desc": "Volg <0>deze stappen</0> om een nieuw wachtwoord voor uw gebruikersaccount te maken.",
"location": "Locatie",
"orgname": "Naam organisatie",
"netname": "Netwerk naam",
"descr": "Beschrijving",
"whois": "Whois",
"filtering_rules_learn_more": "<0>Meer informatie</0> over het maken van uw eigen hosts-blocklists.",
"blocked_by_response": "Geblokkeerd door CNAME of IP als antwoord",
"try_again": "Probeer opnieuw"
}

View File

@@ -345,7 +345,6 @@
"rewrite_desc": "Lar deg enkelt konfigurere selvvalgte DNS-tilbakemeldinger for et spesifikt domenenavn.",
"rewrite_applied": "Benyttet omdirigeringsregelen",
"dns_rewrites": "DNS-omdirigeringer",
"form_domain": "Skriv inn domene",
"form_answer": "Skriv inn IP-adresse eller domenenavn",
"form_error_domain_format": "Ugyldig domeneformat",
"form_error_answer_format": "Ugyldig svarformat",

View File

@@ -330,7 +330,6 @@
"rewrite_desc": "Pozwala łatwo skonfigurować niestandardową odpowiedź DNS dla określonej nazwy domeny.",
"rewrite_applied": "Przepisano regułę",
"dns_rewrites": "Przepisywanie DNS",
"form_domain": "Wprowadź domenę",
"form_answer": "Wpisz adres IP lub nazwę domeny",
"form_error_domain_format": "Niepoprawny format domeny",
"form_error_answer_format": "Nieprawidłowy format odpowiedzi",

View File

@@ -24,6 +24,7 @@
"form_error_ip_format": "Formato de endereço IPv inválido",
"form_error_mac_format": "Formato do endereço MAC inválido",
"form_error_positive": "Deve ser maior que 0",
"form_error_negative": "Deve ser igual ou superior a 0",
"dhcp_form_gateway_input": "IP do gateway",
"dhcp_form_subnet_input": "Máscara de sub-rede",
"dhcp_form_range_title": "Faixa de endereços IP",
@@ -187,6 +188,22 @@
"query_log_disabled": "O registro de consulta está desativado e pode ser configurado em <0>configurações</0>",
"query_log_strict_search": "Use aspas duplas para uma pesquisa mais criteriosa",
"query_log_retention_confirm": "Você tem certeza de que deseja alterar o arquivamento do registro de consulta? Se diminuir o valor de intervalo, alguns dados serão perdidos",
"dns_config": "Configuração do servidor DNS",
"blocking_mode": "Modo de bloqueio",
"nxdomain": "NXDOMAIN",
"null_ip": "IP nulo",
"custom_ip": "IP personalizado",
"blocking_ipv4": "Bloqueando IPv4",
"blocking_ipv6": "Bloqueando IPv6",
"form_enter_rate_limit": "Insira a taxa limite",
"rate_limit": "Taxa limite",
"edns_enable": "Ativar a sub-rede do cliente EDNS",
"edns_cs_desc": "Se ativado, o AdGuard Home estará enviando as sub-redes dos clientes para os servidores DNS.",
"rate_limit_desc": "O número de solicitações por segundo que um único cliente pode fazer (0: ilimitado)",
"blocking_ipv4_desc": "Endereço de IP a ser retornado para uma solicitação bloqueada",
"blocking_ipv6_desc": "Endereço de IP a ser retornado para uma solicitação AAAA bloqueada",
"blocking_mode_desc": "<0>NXDOMAIN - Responde com o código NXDOMAIN;</0> <0>IP nulo - Responde com endereço IP zero (0.0.0.0.0 para A; :: para AAAA);</0><0>IP personalizado - Responde com um endereço de IP definido manualmente.</0>",
"upstream_dns_client_desc": "Se você mantiver este campo vazio, o AdGuard Home usará os servidores configurados nas configurações <0>DNS</0>.",
"source_label": "Fonte",
"found_in_known_domain_db": "Encontrado no banco de dados de domínios conhecidos.",
"category_label": "Categoria",
@@ -354,7 +371,6 @@
"rewrite_desc": "Permite configurar uma resposta personalizada do DNS para um nome de domínio específico.",
"rewrite_applied": "Regra de reescrita aplicada",
"dns_rewrites": "Reescritas de DNS",
"form_domain": "Digite o domínio",
"form_answer": "Digite o endereço de IP ou nome de domínio",
"form_error_domain_format": "Formato de domínio inválido",
"form_error_answer_format": "Formato de resposta inválido",
@@ -409,5 +425,6 @@
"descr": "Descrição",
"whois": "Whois",
"filtering_rules_learn_more": "<0>Saiba mais</0> sobre como criar as suas próprias listas negras de servidores.",
"blocked_by_response": "Bloqueado por CNAME ou IP na resposta"
"blocked_by_response": "Bloqueado por CNAME ou IP na resposta",
"try_again": "Tente novamente"
}

View File

@@ -24,6 +24,7 @@
"form_error_ip_format": "Formato de endereço IPv4 inválido",
"form_error_mac_format": "Formato do endereço MAC inválido",
"form_error_positive": "Deve ser maior que 0",
"form_error_negative": "Deve ser igual ou superior a 0",
"dhcp_form_gateway_input": "IP do gateway",
"dhcp_form_subnet_input": "Máscara de sub-rede",
"dhcp_form_range_title": "Faixa de endereços IP",
@@ -187,6 +188,22 @@
"query_log_disabled": "O registo de consulta está desactivado e pode ser configurado em <0>definições</0>",
"query_log_strict_search": "Usar aspas duplas para uma pesquisa rigorosa",
"query_log_retention_confirm": "Tem a certeza de que deseja alterar a retenção do registo de consulta? Se diminuir o valor do intervalo, alguns dados serão perdidos",
"dns_config": "Configuração do servidor DNS",
"blocking_mode": "Modo de bloqueio",
"nxdomain": "NXDOMAIN",
"null_ip": "IP nulo",
"custom_ip": "IP Personalizado",
"blocking_ipv4": "A bloquear IPv4",
"blocking_ipv6": "A bloquear IPv6",
"form_enter_rate_limit": "Insira o limite de taxa",
"rate_limit": "Limite de taxa",
"edns_enable": "Activar sub-rede do cliente EDNS",
"edns_cs_desc": "Se activado, o AdGuard Home enviará sub-redes dos clientes para os servidores DNS.",
"rate_limit_desc": "O número de solicitações por segundo que um único cliente pode fazer (0: ilimitado)",
"blocking_ipv4_desc": "Endereço IP a ser devolvido para uma solicitação A bloqueada",
"blocking_ipv6_desc": "Endereço IP a ser devolvido para uma solicitação AAAA bloqueada",
"blocking_mode_desc": "<0>NXDOMAIN Responda com o código NXDOMAIN;;</0> <0>IP nulo - responda com zero endereço IP (0.0.0.0 for A; :: for AAAA);</0> <0>IP personalizado - Responda com um endereço IP definido manualmente.</0>",
"upstream_dns_client_desc": "Se mantiver esse campo vazio, o AdGuard Home usará os servidores configurados nas <0>Definições de DNS</0>.",
"source_label": "Fonte",
"found_in_known_domain_db": "Encontrado no banco de dados de domínios conhecido.",
"category_label": "Categoria",
@@ -354,7 +371,6 @@
"rewrite_desc": "Permite configurar uma resposta personalizada do DNS para um nome de domínio específico.",
"rewrite_applied": "Regra de reescrita aplicada",
"dns_rewrites": "Reescritas de DNS",
"form_domain": "Inserir domínio",
"form_answer": "Insira o endereço de IP ou nome de domínio",
"form_error_domain_format": "Formato de domínio inválido",
"form_error_answer_format": "Formato de resposta inválido",
@@ -409,5 +425,6 @@
"descr": "Descrição",
"whois": "Whois",
"filtering_rules_learn_more": "<0>Saiba mais</0>sobre como criar as suas próprias listas negras de servidores.",
"blocked_by_response": "Bloqueado por CNAME ou IP em resposta"
"blocked_by_response": "Bloqueado por CNAME ou IP em resposta",
"try_again": "Tente novamente"
}

View File

@@ -23,6 +23,7 @@
"form_error_ip6_format": "Неверный формат IPv6",
"form_error_ip_format": "Неверный формат IP-адреса",
"form_error_mac_format": "Некорректный формат MAC",
"form_error_client_id_format": "Неверный формат ID клиента",
"form_error_positive": "Должно быть больше 0",
"form_error_negative": "Должно быть не меньше 0",
"dhcp_form_gateway_input": "IP-адрес шлюза",
@@ -75,12 +76,16 @@
"stats_adult": "Заблокированные \"взрослые\" сайты",
"stats_query_domain": "Часто запрашиваемые домены",
"for_last_24_hours": "за 24 часа",
"for_last_days": "за последний {{count}} день",
"for_last_days_plural": "за последние {{count}} дней",
"no_domains_found": "Домены не найдены",
"requests_count": "Количество запросов",
"top_blocked_domains": "Часто блокируемые домены",
"top_clients": "Частые клиенты",
"no_clients_found": "Клиентов не найдено",
"general_statistics": "Общая статистика",
"number_of_dns_query_days": "Количество DNS-запросов за последний {{count}} день",
"number_of_dns_query_days_plural": "Количество DNS запросов, обработанных за последние {{count}} дней",
"number_of_dns_query_24_hours": "Количество DNS-запросов за 24 часа",
"number_of_dns_query_blocked_24_hours": "Количество DNS-запросов, заблокированных фильтрами и блок-списками",
"number_of_dns_query_blocked_24_hours_by_sec": "Количество DNS-запросов, заблокированных модулем Антифишинга AdGuard",
@@ -105,6 +110,7 @@
"upstream_dns": "Upstream DNS-серверы",
"upstream_dns_hint": "Если вы оставите это поле пустым, то AdGuard Home использует <a href='https://www.quad9.net/' target='_blank'>Quad9</a> в качестве DNS сервера.",
"test_upstream_btn": "Тест upstream серверов",
"upstreams": "Upstreams",
"apply_btn": "Применить",
"disabled_filtering_toast": "Фильтрация выкл.",
"enabled_filtering_toast": "Фильтрация вкл.",
@@ -389,6 +395,8 @@
"stats_params": "Конфигурация статистики",
"config_successfully_saved": "Конфигурация успешно сохранена",
"interval_24_hour": "24 часа",
"interval_days": "{{count}} день",
"interval_days_plural": "{{count}} дней",
"domain": "Домен",
"answer": "Ответ",
"filter_added_successfully": "Фильтр успешно добавлен",
@@ -399,6 +407,8 @@
"statistics_clear_confirm": "Вы уверены, что хотите очистить статистику?",
"statistics_retention_confirm": "Вы уверены, что хотите изменить срок хранения статистики? При сокращении интервала данные могут быть утеряны",
"statistics_cleared": "Статистика успешно очищена",
"interval_hours": "{{count}} час",
"interval_hours_plural": "{{count}} часов",
"filters_configuration": "Настройка фильтров",
"filters_enable": "Включить фильтры",
"filters_interval": "Интервал обновления фильтров",
@@ -416,19 +426,10 @@
"netname": "Название сети",
"descr": "Описание",
"whois": "Whois",
"interval_hours_0": "{{count}} час",
"interval_hours_1": "{{count}} часа",
"interval_hours_2": "{{count}} часов",
"interval_days_0": "{{count}} день",
"interval_days_1": "{{count}} дня",
"interval_days_2": "{{count}} дней",
"for_last_days_0": "за последний {{count}} день",
"for_last_days_1": "за последние {{count}} дня",
"for_last_days_2": "за последние {{count}} дней",
"number_of_dns_query_days_0": "Количество DNS-запросов за {{count}} день",
"number_of_dns_query_days_1": "Количество DNS-запросов за {{count}} дня",
"number_of_dns_query_days_2": "Количество DNS-запросов за {{count}} дней",
"filtering_rules_learn_more": "<0>Узнайте больше</0> о создании собственных списков блокировки хостов.",
"blocked_by_response": "Заблокировано по CNAME или IP в ответе",
"try_again": "Попробовать еще раз"
"try_again": "Попробовать еще раз",
"domain_desc": "Введите имя или маску домены, который вы хотите перенаправить.",
"example_rewrite_domain": "перенаправляет только для этот домен.",
"example_rewrite_wildcard": "перенправляет все поддомены <0>example.org</0>."
}

View File

@@ -24,6 +24,7 @@
"form_error_ip_format": "Nesprávny formát IPv4",
"form_error_mac_format": "Nesprávny MAC formát",
"form_error_positive": "Musí byť väčšie ako 0",
"form_error_negative": "Musí byť číslo 0 alebo viac",
"dhcp_form_gateway_input": "IP brána",
"dhcp_form_subnet_input": "Maska podsiete",
"dhcp_form_range_title": "Rozsah IP adries",
@@ -99,7 +100,7 @@
"use_adguard_parental": "Použiť AdGuard službu Rodičovská kontrola",
"use_adguard_parental_hint": "AdGuard Home skontroluje, či doména obsahuje materiály pre dospelých. Používa rovnaké API priateľské k ochrane osobných údajov ako služba Bezpečného prehliadania.",
"enforce_safe_search": "Vynútiť bezpečné vyhľadávanie",
"enforce_save_search_hint": "AdGuard Home môže vynútiť bezpečné vyhľadávanie v nasledujúcich vyhľadávačoch: Google, Youtube, Bing, DuckDuckGo and Yandex.",
"enforce_save_search_hint": "AdGuard Home môže vynútiť bezpečné vyhľadávanie v nasledujúcich vyhľadávačoch: Google, Youtube, Bing, DuckDuckGo a Yandex.",
"no_servers_specified": "Neboli špecifikované žiadne servery",
"general_settings": "Všeobecné nastavenia",
"dns_settings": "Nastavenia DNS",
@@ -187,6 +188,22 @@
"query_log_disabled": "Protokol dopytov je vypnutý a možno ho nakonfigurovať v <0>nastaveniach</0>",
"query_log_strict_search": "Na prísne vyhľadávanie použite dvojité úvodzovky",
"query_log_retention_confirm": "Naozaj chcete zmeniť uchovávanie denníku dopytov? Ak znížite hodnotu intervalu, niektoré údaje sa stratia",
"dns_config": "Konfigurácia DNS servera",
"blocking_mode": "Spôsob blokovania",
"nxdomain": "NXDOMAIN",
"null_ip": "Nulová IP adresa",
"custom_ip": "Vlastná IP adresa",
"blocking_ipv4": "Blokovanie IPv4",
"blocking_ipv6": "Blokovanie IPv6",
"form_enter_rate_limit": "Zadajte rýchlostný limit",
"rate_limit": "Rýchlostný limit",
"edns_enable": "Povoliť klientsku podsiete EDNS",
"edns_cs_desc": "Ak je povolená, program AdGuard Home bude odosielať podsiete klientov na DNS servery.",
"rate_limit_desc": "Počet požiadaviek za sekundu, ktoré môže jeden klient vykonať (0: neobmedzene)",
"blocking_ipv4_desc": "IP adresa, ktorá sa má vrátiť v prípade blokovanej žiadosti A",
"blocking_ipv6_desc": "IP adresa, ktorá sa má vrátiť v prípade blokovanej žiadosti AAAA",
"blocking_mode_desc": "<0>NXDOMAIN - Odpoveď s kódom NXDOMAIN;</0> <0>Null IP - Odpoveď s nulovou IP adresou (0,0.0,0 pre A; :: pre AAAA);</0> <0>Vlastná IP adresa - Odpoveď s manuálne nastavenou IP adresou.</0>",
"upstream_dns_client_desc": "Ak ponecháte toto pole prázdne, AdGuard Home použije servery nakonfigurované v <0>nastaveniach DNS</0>.",
"source_label": "Zdroj",
"found_in_known_domain_db": "Nájdené v databáze známych domén.",
"category_label": "Kategória",
@@ -354,7 +371,6 @@
"rewrite_desc": "Umožňuje ľahko nakonfigurovať vlastnú odpoveď DNS pre konkrétny názov domény.",
"rewrite_applied": "Použilo sa pravidlo prepisovania",
"dns_rewrites": "DNS prepisovanie",
"form_domain": "Vložte meno domény",
"form_answer": "Zadajte IP adresu alebo názov domény",
"form_error_domain_format": "Neplatný formát domény",
"form_error_answer_format": "Neplatný formát odpovede",
@@ -409,5 +425,6 @@
"descr": "Popis",
"whois": "Whois",
"filtering_rules_learn_more": "<0>Viac informácií</0> o vytváraní vlastných zoznamov hostiteľov.",
"blocked_by_response": "Blokované pomocou CNAME alebo IP v odpovedi"
"blocked_by_response": "Blokované pomocou CNAME alebo IP v odpovedi",
"try_again": "Skúste znova"
}

View File

@@ -24,6 +24,7 @@
"form_error_ip_format": "Neveljaven format IP",
"form_error_mac_format": "Neveljaven MAC format",
"form_error_positive": "Mora biti večja od 0",
"form_error_negative": "Mora biti enako ali več kot 0",
"dhcp_form_gateway_input": "IP prehoda",
"dhcp_form_subnet_input": "Maska podomrežja",
"dhcp_form_range_title": "Razpon naslovov IP",
@@ -187,6 +188,22 @@
"query_log_disabled": "Dnevnik poizvedb je onemogočen in ga je mogoče konfigurirati v <0>nastavitvah</0>",
"query_log_strict_search": "Za strogo iskanje uporabite dvojne narekovaje",
"query_log_retention_confirm": "Ali ste prepričani, da želite spremeniti zadrževanje dnevnika poizvedb? Če zmanjšate vrednost intervala, bodo nekateri podatki izgubljeni",
"dns_config": "Konfiguracija strežnika DNS",
"blocking_mode": "Način zaviranja",
"nxdomain": "NXDOMAIN",
"null_ip": "Prazen IP",
"custom_ip": "IP po meri",
"blocking_ipv4": "Onemogočanje IPv4",
"blocking_ipv6": "Onemogočanje IPv6",
"form_enter_rate_limit": "Vnesite omejitev hitrosti",
"rate_limit": "Omejitev hitrosti",
"edns_enable": "Omogoči podmrežje odjemalcev EDNS",
"edns_cs_desc": "Če je omogočeno, bo AdGuard Home pošiljal podmrežja odjemalca na strežnike DNS.",
"rate_limit_desc": "Število zahtev na sekundo, ki jih lahko pošlje posamezni odjemalec (0: neomejeno)",
"blocking_ipv4_desc": "IP naslov, ki mora biti vrnjen za onemogočeno zahtevo A",
"blocking_ipv6_desc": "IP naslov, ki mora biti vrnjen za onemogočeno zahtevo AAAA",
"blocking_mode_desc": "<0>NXDOMAIN Odziva se s kodo NXDOMAIN;</0> <0>Prazen IP Odziva se z ničelnim naslovom IP (0,0.0,0 za A; :: za AAAA);</0> <0>IP po meri - Odziva se z ročno nastavljenim IP naslovom.</0>",
"upstream_dns_client_desc": "Če pustite to polje prazno, bo AdGuard Home uporabil strežnike, konfigurirane v <0>nastavitvah DNS</0>.",
"source_label": "Vir",
"found_in_known_domain_db": "Najdeno v zbirki podatkov znanih domen.",
"category_label": "Kategorija",
@@ -354,7 +371,6 @@
"rewrite_desc": "Omogoča enostavno konfiguriranje odgovora DNS po meri za določeno ime domene.",
"rewrite_applied": "Uporabljeno Pravilo za prepisovanje",
"dns_rewrites": "Prepisovanja NDS",
"form_domain": "Vnesi domeno",
"form_answer": "Vnesite IP naslov ali ime domene",
"form_error_domain_format": "Neveljavna oblika domene",
"form_error_answer_format": "Neveljavna oblika odgovora",
@@ -409,5 +425,6 @@
"descr": "Opis",
"whois": "Whois",
"filtering_rules_learn_more": "<0>Več o tem</0>, o ustvarjanju lastnih Seznamov nedovoljenih gostiteljev.",
"blocked_by_response": "Onemogočeno z CNAME ali IP v odgovoru"
"blocked_by_response": "Onemogočeno z CNAME ali IP v odgovoru",
"try_again": "Poskusi ponovno"
}

View File

@@ -371,7 +371,6 @@
"rewrite_desc": "Dozvoljava da jednostavno konfigurišete prilagođeni DNS odgovor za određeni domen.",
"rewrite_applied": "Primenjeno pravilo prepisivanja",
"dns_rewrites": "DNS prepisivanja",
"form_domain": "Unesite domen",
"form_answer": "Unesite IP adresu ili domen",
"form_error_domain_format": "Nevažeći format domena",
"form_error_answer_format": "Nevažeći format odgovora",

View File

@@ -17,7 +17,9 @@
"dhcp_leases": "DHCP-lease",
"dhcp_static_leases": "Statiska DHCP-leases",
"dhcp_leases_not_found": "Ingen DHCP-lease hittad",
"dhcp_config_saved": "Sparade inställningar för DHCP-servern",
"form_error_required": "Obligatoriskt fält",
"form_error_ip_format": "Ogiltigt IPv4-format",
"form_error_mac_format": "Ogiltigt MAC-format",
"form_error_positive": "Måste vara större än noll",
"dhcp_form_gateway_input": "Gateway-IP",
@@ -77,6 +79,8 @@
"top_clients": "Toppklienter",
"no_clients_found": "Inga hitatde klienter",
"general_statistics": "Allmän statistik",
"number_of_dns_query_days": "Ett antal DNS-förfrågningar utfördes under den senaste {{count}} dagen",
"number_of_dns_query_days_plural": "Ett antal DNS-förfrågningar utfördes under de senaste {{count}} dagarna",
"number_of_dns_query_24_hours": "Ett antal DNS-förfrågningar utfördes under de senaste 244 timamrna",
"number_of_dns_query_blocked_24_hours": "Ett antal DNS-förfrågningar blockerades av annonsfilter och värdens bloceringsklistor",
"number_of_dns_query_blocked_24_hours_by_sec": "Ett antal DNS-förfrågningar blockerades av AdGuards modul för surfsäkerhet",
@@ -92,12 +96,14 @@
"use_adguard_parental": "Använda AdGuards webbservice för färäldrakontroll",
"use_adguard_parental_hint": "AdGuard Home kommer att kontrollera domäner för innehåll av vuxenmaterial . Samma integritetsvänliga metod för API-lookup som tillämpas i webbservicens surfsäkerhet används.",
"enforce_safe_search": "Tillämpa Säker surf",
"enforce_save_search_hint": "AdGuard Home kan framtvinga säker surf i följande sökmoterer: Google, Youtube, Bing, och Yandex.",
"no_servers_specified": "Inga servrar angivna",
"general_settings": "Allmänna inställningar",
"dns_settings": "DNS-inställningar",
"encryption_settings": "Krypteringsinställningar",
"dhcp_settings": "DHCP-inställningar",
"upstream_dns": "Upstream DNS-servrar",
"upstream_dns_hint": "Om du låter fältet vara tomt kommer AdGuard Home att använda <a href='https://1.1.1.1/' target='_blank'>Cloudflare DNS</a> för uppström.",
"test_upstream_btn": "Testa uppströmmar",
"apply_btn": "Tillämpa",
"disabled_filtering_toast": "Filtrering bortkopplad",
@@ -291,6 +297,7 @@
"client_edit": "Redigera klient",
"client_identifier": "Identifikator",
"ip_address": "IP-adress",
"client_identifier_desc": "Klienter kan identifieras genom IP-adresser eller MAC-adresser. Notera att användning av MAC som identifierare bara är möjligt om AdGuard Home också är en <0>DHCP-server</0>",
"form_enter_ip": "Skriv in IP",
"form_enter_mac": "Skriv in MAC",
"form_client_name": "Skriv in klientnamn",
@@ -306,16 +313,41 @@
"access_title": "Åtkomstinställningar",
"access_desc": "Här kan du konfigurera åtkomstregler för AdGuard Homes DNS-server.",
"access_allowed_title": "Tillåtna klienter",
"access_allowed_desc": "En lista över CIDR eller IP-adresser. Om konfigurerad kommer AdGuard Home endast acceptera förfrågningar från dessa IP-adresser.",
"access_disallowed_title": "Otillåtna klienter",
"access_disallowed_desc": "En lista över CIDR eller IP-adresser. Om konfigurerad kommer AdGuard Home inte acceptera förfrågningar från dessa IP-adresser.",
"access_blocked_title": "Blockerade domäner",
"access_blocked_desc": "Ej att blandas ihop med filter. AdGuard Home kommer inte accepter DNS-förfrågningar innehållande dessa domäner.",
"access_settings_saved": "Åtkomstinställningar sparade",
"updates_checked": "Sökning efter uppdateringar genomförd",
"updates_version_equal": "AdGuard Home är uppdaterat",
"check_updates_now": "Sök efter uppdateringar nu",
"dns_privacy": "DNS-Integritet",
"setup_dns_privacy_1": "<0>DNS-över-TLS:</0> Använd: <1>{{address}}</1>",
"setup_dns_privacy_2": "<0>DNS-över-HTTPS:</0> Använd: <1>{{address}}</1>",
"setup_dns_privacy_3": "<0>Notera att krypterade DNS-protokoll endast stöds på Android 9. Du behöver därför installera ytterligare mjukvara för andra operativsystem.</0><0>Här är en lista över program du kan använda.</0>",
"setup_dns_privacy_android_1": "Android 9 har inbyggt stöd för DNS-över-TLS. Konfigurera och uppge domännamn under Inställningar → Nätverk & Internet → Avancerat → Privat DNS.",
"setup_dns_privacy_android_2": "<0>AdGuard för Android</0> stödjer <1>DNS-över-HTTPS</1> samt <1>DNS-över-TLS</1>.",
"setup_dns_privacy_android_3": "<0>Intra</0> lägger till stöd för <1>DNS-ÖVER-HTTPS</1> till Android.",
"setup_dns_privacy_ios_1": "<0>DNSCloak</0> stödjer <1>DNS-ÖVER-HTTPS</1>, men för konfigurering krävs att du använder dig egen server. Du behöver generera en <2>DNS-Stämpel</2> till programmet.",
"setup_dns_privacy_ios_2": "<0>AdGuard för iOS</0> stödjer <1>DNS-över-HTTPS</1> samt <1>DNS-över-TLS</1>.",
"setup_dns_privacy_other_title": "Andra implementeringar",
"setup_dns_privacy_other_1": "AdGuard Home kan själv vara en säker DNS-klient på alla plattformar.",
"setup_dns_privacy_other_2": "<0>dnsproxy</0> stödjer alla bekräftat säkra DNS-protokoll.",
"setup_dns_privacy_other_3": "<0>dnscrypt-proxy</0> stödjer <1>DNS-over-HTTPS</1>.",
"setup_dns_privacy_other_4": "<0>Mozilla Firefox</0> stödjer <1>DNS-over-HTTPS</1>.",
"setup_dns_privacy_other_5": "Du kan hitta fler implementeringar <0>här</0> och <1>här</1>.",
"setup_dns_notice": "För att kunna använda <1>DNS-över-HTTPS</1> eller <1>DNS-över-TLS</1>, behöver du <0>konfigurera Kryptering</0> i AdGuard Home-inställningar.",
"rewrite_added": "DNS-omskrivning för \"{{key}}\" lyckad",
"rewrite_deleted": "DNS-omskrivning för \"{{key}}\" har tagits bort",
"interval_24_hour": "24 timmar",
"domain": "Domän",
"answer": "Svar",
"statistics_retention_desc": "Om du minskar intervallet kommer viss data att gå förlorad",
"statistics_clear": " Rensa statistik",
"statistics_clear_confirm": "Är du säker på att du vill radera statistiken?",
"statistics_retention_confirm": "Är du säker på att du vill ändra retentionstiden för statistik? Om du minskar intervallet kommer viss data att gå förlorad",
"statistics_cleared": "Statistiken har rensats",
"interval_hours": "{{count}} timme",
"interval_hours_plural": "{{count}} timmar",
"filters_configuration": "Filterinställningar",

View File

@@ -17,9 +17,14 @@
"dhcp_leases": "DHCP kiralamaları",
"dhcp_static_leases": "Sabit DHCP kiralamaları",
"dhcp_leases_not_found": "DHCP kiralaması bulunamadı",
"dhcp_config_saved": "DHCP sunucusu ayarı kaydedildi",
"form_error_required": "Gerekli alan",
"form_error_ip4_format": "Geçersiz IPv4 formatı",
"form_error_ip6_format": "Geçersiz IPv6 formatı",
"form_error_ip_format": "Geçersiz IPv4 formatı",
"form_error_mac_format": "Geçersiz MAC biçimi",
"form_error_positive": "0'dan büyük olmalı",
"form_error_negative": "0 veya daha büyük olmalıdır",
"dhcp_form_gateway_input": "Ağ Geçidi IP'si",
"dhcp_form_subnet_input": "Alt Ağ Maskesi",
"dhcp_form_range_title": "IP adres aralığı",
@@ -41,6 +46,7 @@
"dhcp_new_static_lease": "Yeni sabit kiralama",
"dhcp_static_leases_not_found": "Sabit DHCP kiralaması bulunamadı",
"dhcp_add_static_lease": "Sabit kiralama ekle",
"dhcp_reset": "DHCP yapılandırmasını sıfırlamak istediğinizden emin misiniz?",
"delete_confirm": "\"{{key}}\" silmek istediğinizden emin misiniz?",
"form_enter_hostname": "Cihaz ismi girin",
"error_details": "Hata detayları",
@@ -94,13 +100,16 @@
"use_adguard_parental": "AdGuard ebeveyn kontrolü web hizmetini kullan",
"use_adguard_parental_hint": "AdGuard Home, alan adının yetişkin içerik bulundurup bulundurmadığını kontrol edecek. Gezinti güvenliği web hizmeti ile kullandığımız aynı gizlilik dostu API'yi kullanıyoruz.",
"enforce_safe_search": "Güvenli aramayı zorunlu kıl",
"enforce_save_search_hint": "AdGuard Home şu arama motorlarında güvenli aramayı zorunlu kılabilir: Google, Youtube, Bing, DuckDuckGo ve Yandex.",
"no_servers_specified": "Sunucu adresi girilmedi",
"general_settings": "Genel ayarlar",
"dns_settings": "DNS ayarları",
"encryption_settings": "Şifreleme ayarları",
"dhcp_settings": "DHCP ayarları",
"upstream_dns": "Üst DNS sunucusu",
"upstream_dns_hint": "Eğer bu alanı boş bırakırsanız AdGuard Home üst sunucu olarak <a href='https://1.1.1.1/' target='_blank'>Cloudflare DNS</a> adresini kullanacaktır.",
"test_upstream_btn": "Üst sunucuyu test et",
"upstreams": "Upstreams",
"apply_btn": "Uygula",
"disabled_filtering_toast": "Filtreleme devre dışı",
"enabled_filtering_toast": "Filtreleme çalışıyor",
@@ -137,6 +146,7 @@
"example_comment": "! Buraya bir yorum ekledim",
"example_comment_meaning": "yorum eklemek",
"example_comment_hash": "# Bir yorum daha ekledim",
"example_regex_meaning": "belirtilen düzenli ifadelerle eşleşen alan adlarına erişimi engelle",
"example_upstream_regular": "normal DNS (UDP üzerinden)",
"example_upstream_dot": "<0>DNS-over-TLS</0> şifrelemesi",
"example_upstream_doh": "<0>DNS-over-HTTPS</0> şifrelemesi",
@@ -169,6 +179,31 @@
"rule_added_to_custom_filtering_toast": "Kural isteğe bağlı filtreleme kurallarına eklendi",
"query_log_response_status": "Durum: {{value}}",
"query_log_filtered": "{{filter}} tarafından filtrelendi",
"query_log_confirm_clear": "Tüm sorgu günlüğünü temizlemek istediğinizden emin misiniz?",
"query_log_cleared": "Sorgu günlüğü başarıyla temizlendi",
"query_log_clear": "Sorgu kayıtlarını temizle",
"query_log_retention": "Sorgu kayıtlarının saklanması",
"query_log_enable": "Günlük kaydını etkinleştir",
"query_log_configuration": "Günlük yapılandırması",
"query_log_disabled": "Sorgu günlüğü devre dışı bırakıldı ve <0>ayarlar</0>da yapılandırılabilir",
"query_log_strict_search": "Katı arama için çift tırnak işareti kullanın",
"query_log_retention_confirm": "Sorgu günlüğü saklama süresini değiştirmek istediğinize emin misiniz? Aralık değerini azaltırsanız, bazı veriler kaybolacaktır",
"dns_config": "DNS sunucusu yapılandırması",
"blocking_mode": "Engelleme modu",
"nxdomain": "NXDOMAIN",
"null_ip": "Boş IP",
"custom_ip": "Özel IP",
"blocking_ipv4": "IPv4 engelleme",
"blocking_ipv6": "IPv6 engelleme",
"form_enter_rate_limit": "Sıklık limitini girin",
"rate_limit": "Sıklık limiti",
"edns_enable": "EDNS İstemci Alt Ağını Etkinleştir",
"edns_cs_desc": "Etkinleştirilirse, AdGuard Home, istemcilerin alt ağlarını DNS sunucularına gönderir.",
"rate_limit_desc": "Tek bir istemcinin saniye başına yapmasına izin verilen istek sayısı (0: sınırsız)",
"blocking_ipv4_desc": "Engellenen bir A isteği için geri döndürülecek IP adresi",
"blocking_ipv6_desc": "Engellenen bir AAAA isteği için geri döndürülecek IP adresi",
"blocking_mode_desc": "<0>NXDOMAIN NXDOMAIN ile yanıtla code;</0> <0> Boş IP Sıfır IP adresi ile cevap verin (A için 0.0.0.0; :: AAAA için); </0> <0> Özel IP - Elle ayarlanmış bir IP adresi ile cevap verin.</0>",
"upstream_dns_client_desc": "Bu alanı boş tutarsanız, AdGuard Home, <0>DNS ayarlarında</0> yapılandırılmış sunucuları kullanır.",
"source_label": "Kaynak",
"found_in_known_domain_db": "Bilinen alan adları veri tabanı içinde bulundu",
"category_label": "Kategori",
@@ -186,6 +221,7 @@
"install_settings_dns_desc": "Cihazlarınızı veya yönlendiricinizi şu adresteki DNS sunucusunu kullanması için ayarlamanız gerekecek:",
"install_settings_all_interfaces": "Tüm arayüzler",
"install_auth_title": "Kimlik Doğrulama",
"install_auth_desc": "AdAdGuard Home yönetici web arayüzüne erişim için kullanıcı adı ve şifresi oluşturmanız şiddetle tavsiye edilir. Sadece yerel ağınız erişilebilir olsa bile izinsiz giriş yapılmasını engellemek için şifrenizin olması önemlidir.",
"install_auth_username": "Kullanıcı adı",
"install_auth_password": "Şifre",
"install_auth_confirm": "Şifreyi onayla",
@@ -264,6 +300,8 @@
"update_announcement": "AdGuard Home {{version}} şu an yüklenmeye hazır! Daha fazla bilgi için <0>buraya tıklayın.</0>",
"setup_guide": "Kurulum rehberi",
"dns_addresses": "DNS adresleri",
"dns_start": "DNS sunucusu başlatılıyor",
"dns_status_error": "DNS sunucusu durumunu alma hatası",
"down": "kapalı",
"fix": "Düzelt",
"dns_providers": "Aralarından seçim yapabileceğiniz bilinen <0>DNS sağlayıcıların listesi</0>.",
@@ -282,8 +320,11 @@
"client_edit": "İstemciyi düzenle",
"client_identifier": "Tanımlayıcı",
"ip_address": "IP adresi",
"client_identifier_desc": "İstemciler IP adresleri veya MAC adresleri ile tanımlanabilir. Lütfen not edin, MAC adresi ile tanımlamayı kullanmak için AdGuard Home'un <0>DHCP Sunucusu</0> olması gerekir.",
"form_enter_ip": "IP Girin",
"form_enter_mac": "MAC Girin",
"form_enter_id": "Tanımlayıcı girin",
"form_add_id": "Tanımlayıcı ekle",
"form_client_name": "İstemci ismi girin",
"client_global_settings": "Genel ayarları kullan",
"client_deleted": "\"{{key}}\" istemcisi başarıyla silindi",
@@ -358,9 +399,33 @@
"domain": "Alan adı",
"answer": "Cevap",
"filter_added_successfully": "Filtre başarıyla eklendi",
"statistics_configuration": "İstatistik yapılandırması",
"statistics_retention": "İstatistikleri depolama",
"statistics_retention_desc": "Zaman değerini azaltırsanız bazı veriler kaybolacaktır",
"statistics_clear": " İstatistikleri temizle",
"statistics_clear_confirm": "İstatistikleri temizlemeyi istediğinizden emin misiniz?",
"statistics_cleared": "İstatistikler başarıyla temizlendi"
"statistics_retention_confirm": "İstatistik saklama süresini değiştirmek istediğinize emin misiniz? Aralık değerini azaltırsanız, bazı veriler kaybolacaktır",
"statistics_cleared": "İstatistikler başarıyla temizlendi",
"interval_hours": "{{count}} saat",
"interval_hours_plural": "{{count}} saat",
"filters_configuration": "Filtre yapılandırması",
"filters_enable": "Filtreleri etkinleştir",
"filters_interval": "Filtreleri güncelleme sıklığı",
"disabled": "Devre dışı",
"username_label": "Kullanıcı adı",
"username_placeholder": "Kullanıcı adını girin",
"password_label": "Parola",
"password_placeholder": "Parolayı girin",
"sign_in": "Oturum aç",
"sign_out": "Oturumu kapat",
"forgot_password": "Parolanızı mı unuttunuz?",
"forgot_password_desc": "Kullanıcı hesabınız için yeni bir parola oluşturmak için lütfen <0>bu adımları</0> takip edin.",
"location": "Konum",
"orgname": "Organizasyon adı",
"netname": "Ağ adı",
"descr": "Açıklama",
"whois": "Whois",
"filtering_rules_learn_more": "Ana makinelere dair kendi kara listelerinizi oluşturmakla alakalı <0>daha fazla bilgi edinin</0>.",
"blocked_by_response": "Cevap olarak CNAME veya IP tarafından engellendi",
"try_again": "Tekrar deneyin"
}

View File

@@ -1,4 +1,9 @@
{
"client_settings": "Cài đặt máy khách",
"example_upstream_reserved": "bạn có thể chỉ định DNS ngược tuyến <0>cho một tên miền cụ thể(hoặc nhiều)</0>",
"upstream_parallel": "Sử dụng truy vấn song song để tăng tốc độ giải quyết bằng cách truy vấn đồng thời tất cả các máy chủ ngược tuyến",
"bootstrap_dns": "Máy chủ DNS Bootstrap",
"bootstrap_dns_desc": "Máy chủ DNS Bootstrap được sử dụng để phân giải địa chỉ IP của bộ phân giải DoH/DoT mà bạn chỉ định là ngược tuyến.",
"check_dhcp_servers": "Kiểm tra máy chủ DHCP",
"save_config": "Lưu thiết lập",
"enabled_dhcp": "Máy chủ DHCP đã kích hoạt",
@@ -9,9 +14,23 @@
"dhcp_disable": "Tắt máy chủ DHCP",
"dhcp_not_found": "Không có máy chủ DHCP nào được tìm thấy trong mạng. Có thể bật máy chủ DHCP một cách an toàn",
"dhcp_found": "Đã tìm thấy máy chủ DHCP trong mạng. Có thể có rủi ro nếu kích hoạt máy chủ DHCP dựng sẵn",
"dhcp_leases": "Thuê DHCP",
"dhcp_static_leases": "Thuê DHCP tĩnh",
"dhcp_leases_not_found": "Không tìm thấy DHCP cho thuê",
"dhcp_config_saved": "Đã lưu cấu hình máy chủ DHCP",
"form_error_required": "Trường bắt buộc",
"form_error_ip_format": "Định dạng IPv4 không hợp lệ",
"form_error_mac_format": "Định dạng MAC không hợp lệ",
"form_error_positive": "Phải lớn hơn 0",
"dhcp_form_gateway_input": "Cổng IP",
"dhcp_form_subnet_input": "Mặt nạ mạng con",
"dhcp_form_range_title": "Phạm vi của địa chỉ IP",
"dhcp_form_range_end": "IP kết thúc",
"dhcp_form_lease_title": "Thời gian thuê DHCP (tính bằng giây)",
"dhcp_form_lease_input": "Thời hạn thuê",
"dhcp_interface_select": "Chọn một card mạng",
"dhcp_hardware_address": "Địa chỉ phần cứng",
"dhcp_warning": "Nếu bạn vẫn muốn bật máy chủ DHCP, hãy đảm bảo rằng không có máy chủ DHCP hoạt động nào khác trong mạng của bạn. Nếu không, nó có thể phá vỡ Internet cho các thiết bị được kết nối!",
"back": "Quay lại",
"dashboard": "Tổng quan",
"settings": "Cài đặt",
@@ -129,5 +148,9 @@
"category_label": "Thể loại",
"rule_label": "Quy tắc",
"filter_label": "Bộ lọc",
"unknown_filter": "Bộ lọc không rõ {{filterId}}"
"unknown_filter": "Bộ lọc không rõ {{filterId}}",
"encryption_chain_invalid": "Chuỗi chứng chỉ không hợp lệ",
"encryption_key_invalid": "Đây là khóa riêng {{type}} không hợp lệ",
"form_error_domain_format": "Định dạng tên miền không hợp lệ",
"form_error_answer_format": "Định dạng câu trả lời không hợp lệ"
}

View File

@@ -17,7 +17,11 @@
"dhcp_leases": "DHCP 租约",
"dhcp_static_leases": "DHCP 静态租约",
"dhcp_leases_not_found": "未检测到 DHCP 租约",
"dhcp_config_saved": "已保存 DHCP 服务器配置",
"form_error_required": "必填字段",
"form_error_ip4_format": "无效的 IPv4 格式",
"form_error_ip6_format": "无效的 IPv6 格式",
"form_error_ip_format": "无效的 IPv4 格式",
"form_error_mac_format": "无效的 MAC 格式",
"form_error_positive": "必须大于 0",
"dhcp_form_gateway_input": "网关 IP",
@@ -41,6 +45,7 @@
"dhcp_new_static_lease": "新建静态租约",
"dhcp_static_leases_not_found": "未找到 DHCP 静态租约",
"dhcp_add_static_lease": "添加静态租约",
"dhcp_reset": "您确定要重置DHCP设定么",
"delete_confirm": "您确定要删除 \"{{key}}\"",
"form_enter_hostname": "输入主机名称",
"error_details": "详细错误信息",
@@ -64,16 +69,22 @@
"disabled_protection": "保护已禁用",
"refresh_statics": "刷新状态",
"dns_query": "DNS查询",
"blocked_by": "已被过滤器拦截",
"stats_malware_phishing": "被拦截的恶意/钓鱼网站",
"stats_adult": "被拦截的成人网站",
"stats_query_domain": "请求域名排行",
"for_last_24_hours": "在过去 24 小时",
"for_last_days": "最后 {{value}} 天",
"for_last_days_plural": "最近 {{count}} 天",
"no_domains_found": "未找到域名",
"requests_count": "请求数",
"top_blocked_domains": "被拦截域名排行",
"top_clients": "客户端排行",
"no_clients_found": "未找到客户端",
"general_statistics": "概况统计",
"number_of_dns_query_days": "过去 {{count}} 天内 处理的DNS 查询总数",
"number_of_dns_query_days_plural": "在过去的 {{count}} 天内处理了多少个 DNS 查询",
"number_of_dns_query_24_hours": "过去 24 小时内处理的 DNS 请求总数",
"number_of_dns_query_blocked_24_hours": "被广告过滤器和 Hosts 拦截清单拦截的 DNS 请求总数",
"number_of_dns_query_blocked_24_hours_by_sec": "被 AdGuard 安全浏览模块拦截的 DNS 请求总数",
"number_of_dns_query_blocked_24_hours_adult": "被拦截的成人网站总数",
@@ -88,13 +99,16 @@
"use_adguard_parental": "使用 AdGuard 【家长控制】服务",
"use_adguard_parental_hint": "AdGuard Home 将使用与浏览安全服务相同的隐私性强的 API 来检查域名指向的网站是否包含成人内容。",
"enforce_safe_search": "强制安全搜索",
"enforce_save_search_hint": "AdGuard Home 将对以下搜索引擎强制启用安全搜索Google、YouTube、Bing 和 Yandex。",
"no_servers_specified": "未找到指定的服务器",
"general_settings": "常规设置",
"dns_settings": "DNS 设置",
"encryption_settings": "加密设置",
"dhcp_settings": "DHCP 设置",
"upstream_dns": "上游 DNS 服务器",
"upstream_dns_hint": "如果此处留空AdGuard Home 将会使用 <a href='https://1.1.1.1/' target='_blank'>Cloudflare DNS</a> 作为上游 DNS。如果想要使用使用 DNS over TLS请以 tls:// 为开头。",
"test_upstream_btn": "测试上游 DNS",
"upstreams": "上游服务器",
"apply_btn": "应用",
"disabled_filtering_toast": "过滤器已禁用",
"enabled_filtering_toast": "过滤器已启用",
@@ -131,6 +145,7 @@
"example_comment": "! 这是一行注释",
"example_comment_meaning": "只是一条注释",
"example_comment_hash": "# 这也是一行注释",
"example_regex_meaning": "阻止访问与<0>指定的正则表达式</0>匹配的域",
"example_upstream_regular": "常规 DNS基于 UDP",
"example_upstream_dot": "加密 <a href='https://en.wikipedia.org/wiki/DNS_over_TLS' target='_blank'>DNS-over-TLS</a>",
"example_upstream_doh": "加密 <a href='https://en.wikipedia.org/wiki/DNS_over_HTTPS' target='_blank'>DNS-over-HTTPS</a>",
@@ -161,6 +176,22 @@
"updated_custom_filtering_toast": "自定义过滤规则已更新",
"rule_removed_from_custom_filtering_toast": "规则已从自定义过滤规则列表中移除",
"rule_added_to_custom_filtering_toast": "规则已添加到自定义过滤规则列表中",
"query_log_response_status": "状态: {{value}}",
"query_log_filtered": "被 {{filter}} 过滤",
"query_log_confirm_clear": "你确定想要清除全部查询日志吗?",
"query_log_cleared": "查询日志已成功清除",
"query_log_clear": "清除查询日志",
"query_log_retention": "查询记录保留时间",
"query_log_enable": "启用日志",
"query_log_configuration": "日志配置",
"query_log_disabled": "查询日志已禁用,在<0>这些设置</0>中能配置它们",
"query_log_strict_search": "使用双引号进行严谨搜索",
"query_log_retention_confirm": "您确定要更改查询记录保留时间吗? 如果您减少间隔时间的值, 某些数据可能会丢失。",
"dns_config": "DNS服务设定",
"rate_limit": "速度限制",
"edns_enable": "使用客户端的子网地址EDNS)",
"edns_cs_desc": "启用后AdGuard Home将会向DNS服务器发送客户端的子网地址进行查询",
"rate_limit_desc": "每个客户端每秒钟查询次数的限制 (0不限制)",
"source_label": "源",
"found_in_known_domain_db": "成功在已知域名数据库中查询到",
"category_label": "类别",
@@ -178,6 +209,7 @@
"install_settings_dns_desc": "您将需要使用以下地址来设置您的设备或路由器的 DNS 服务器:",
"install_settings_all_interfaces": "所有接口",
"install_auth_title": "身份认证",
"install_auth_desc": "我们强烈建议您为 AdGuard Home 的网页管理界面配置密码。即使该页面只能通过您的本地网络访问,但避免它被不加限制地访问仍十分重要。",
"install_auth_username": "用户名",
"install_auth_password": "密码",
"install_auth_confirm": "确认密码",
@@ -256,6 +288,7 @@
"update_announcement": "AdGuard Home {{version}} 现已发布! <0>点击此处</0> 以获取详细信息。",
"setup_guide": "设置指导",
"dns_addresses": "DNS 地址",
"dns_start": "正在启动DNS服务",
"down": "下移",
"fix": "修复",
"dns_providers": "此为可从中选择的<0>已知 DNS 提供商列表</0>。",
@@ -274,6 +307,7 @@
"client_edit": "编辑客户端",
"client_identifier": "标识符",
"ip_address": "IP 地址",
"client_identifier_desc": "客户端可通过 IP 地址或 MAC 地址识别。请注意,如 AdGuard Home 也是 <0>DHCP 服务器</0>,则仅能将 MAC 用作标识符",
"form_enter_ip": "输入 IP",
"form_enter_mac": "输入 MAC",
"form_client_name": "输入客户端名称",
@@ -313,5 +347,67 @@
"setup_dns_privacy_other_3": "<0>dnscrypt-proxy</0> 支持 <1>DNS-over-HTTPS</1>。",
"setup_dns_privacy_other_4": "<0>Mozilla Firefox</0> 支持 <1>DNS-over-HTTPS</1>。",
"setup_dns_privacy_other_5": "您可以从 <0>这里</0> 和 <1>这里</1> 找到更多的实施方案。",
"setup_dns_notice": "为了使用 <1>DNS-over-HTTPS</1> 或者 <1>DNS-over-TLS</1> ,您需要在 AdGuard Home 设置中 <0>配置加密</0> 。"
"setup_dns_notice": "为了使用 <1>DNS-over-HTTPS</1> 或者 <1>DNS-over-TLS</1> ,您需要在 AdGuard Home 设置中 <0>配置加密</0> 。",
"rewrite_added": "已成功添加 \"{{key}}\" 的 DNS 重写",
"rewrite_deleted": "已成功删除 \"{{key}}\" 的 DNS 重写",
"rewrite_add": "添加 DNS 重写",
"rewrite_not_found": "未找到 DNS 重写",
"rewrite_confirm_delete": "您确定要删除 \"{{key}}\" 的 DNS 重写?",
"rewrite_desc": "可以轻松地配置特定的域名的自定义 DNS 响应。",
"rewrite_applied": "已应用的重写规则",
"dns_rewrites": "DNS 重写",
"form_answer": "输入 IP 地址或域名",
"form_error_domain_format": "无效的域格式",
"form_error_answer_format": "无效的响应格式",
"configure": "配置",
"main_settings": "主要设置",
"block_services": "阻止特定服务",
"blocked_services": "已阻止的服务",
"blocked_services_desc": "允许快速地阻止热门网站和服务。",
"blocked_services_saved": "已阻止服务的设置保存成功",
"blocked_services_global": "使用全局已阻止服务设置",
"blocked_service": "已阻止的服务",
"block_all": "阻止所有",
"unblock_all": "允许所有",
"encryption_certificate_path": "证书路径",
"encryption_private_key_path": "私钥路径",
"encryption_certificates_source_path": "设置证书路径",
"encryption_certificates_source_content": "粘贴证书内容",
"encryption_key_source_path": "设置私钥文件",
"encryption_key_source_content": "粘贴私钥内容",
"stats_params": "统计配置",
"config_successfully_saved": "配置保存成功",
"interval_24_hour": "24 小时",
"interval_days": "{{value}} 天",
"interval_days_plural": "{{count}} 天",
"domain": "域",
"answer": "应答",
"filter_added_successfully": "已成功添加过滤器",
"statistics_configuration": "统计配置",
"statistics_retention": "统计保留",
"statistics_retention_desc": "如果您减少该间隔的数值, 某些数据可能会丢失",
"statistics_clear": " 清除统计数据",
"statistics_clear_confirm": "您确定要清除统计数据?",
"statistics_retention_confirm": "您确定要更改统计记录保留时间吗? 如果您减少间隔时间的值, 某些数据可能会丢失。",
"statistics_cleared": "统计数据已成功清除",
"interval_hours": "{{count}} 小时",
"interval_hours_plural": "{{count}} 小时",
"filters_configuration": "过滤器配置",
"filters_enable": "启用过滤器",
"filters_interval": "过滤器更新间隔",
"disabled": "已禁用",
"username_label": "用户名",
"username_placeholder": "输入用户名",
"password_label": "密码",
"password_placeholder": "输入密码",
"sign_in": "登入",
"sign_out": "登出",
"forgot_password": "忘记密码?",
"forgot_password_desc": "请遵从<0>这些步骤</0>来为你的用户账号创建一个新密码",
"location": "地址",
"orgname": "机构名称",
"netname": "网络名称",
"descr": "描述",
"whois": "Whois",
"filtering_rules_learn_more": "<0>了解更多</0>关于如何创建您自己的主机hosts拦截清单。"
}

View File

@@ -24,6 +24,7 @@
"form_error_ip_format": "無效的 IP 格式",
"form_error_mac_format": "無效的媒體存取控制MAC格式",
"form_error_positive": "必須大於 0",
"form_error_negative": "必須等於 0 或更大",
"dhcp_form_gateway_input": "閘道 IP",
"dhcp_form_subnet_input": "子網路遮罩",
"dhcp_form_range_title": "IP 位址範圍",
@@ -187,6 +188,22 @@
"query_log_disabled": "查詢記錄被禁用並可在<0>設定</0>中被配置",
"query_log_strict_search": "使用雙引號於嚴謹的搜尋",
"query_log_retention_confirm": "您確定您想要更改查詢記錄保留嗎?如果您減少該間隔值,某些資料將被丟失",
"dns_config": "DNS 伺服器配置",
"blocking_mode": "封鎖模式",
"nxdomain": "不存在的網域NXDOMAIN",
"null_ip": "無效的 IP",
"custom_ip": "自訂的 IP",
"blocking_ipv4": "封鎖 IPv4",
"blocking_ipv6": "封鎖 IPv6",
"form_enter_rate_limit": "輸入速率限制",
"rate_limit": "速率限制",
"edns_enable": "啟用對於 DNS 的擴充機制EDNS用戶端子網路",
"edns_cs_desc": "如果被啟用AdGuard Home 將持續傳送用戶端的子網路到 DNS 伺服器。",
"rate_limit_desc": "單一的用戶端被允許傳送的每秒請求之數量0無限制的",
"blocking_ipv4_desc": "要被返回給已封鎖的 A 請求之 IP 位址",
"blocking_ipv6_desc": "要被返回給已封鎖的 AAAA 請求之 IP 位址",
"blocking_mode_desc": "<0>不存在的網域NXDOMAIN 以 NXDOMAIN 碼回覆;</0> <0>無效的 IP 以零值 IP 位址0.0.0.0 供 A:: 供 AAAA回覆</0> <0>自訂的 IP - 以手動地被設定的 IP 位址回覆。</0>",
"upstream_dns_client_desc": "如果您將該欄位留空AdGuard Home 將使用在 <0>DNS 設定</0>中被配置的伺服器。",
"source_label": "來源",
"found_in_known_domain_db": "在已知的域名資料庫中被發現。",
"category_label": "類別",
@@ -284,7 +301,7 @@
"setup_guide": "設置指南",
"dns_addresses": "DNS 位址",
"dns_start": "DNS 伺服器正在啟動",
"dns_status_error": "取得 DNS 伺服器狀態之錯誤",
"dns_status_error": "檢查 DNS 伺服器狀態出錯",
"down": "停止運作的",
"fix": "修復",
"dns_providers": "這裡是一個從中選擇之<0>已知的 DNS 供應商之清單</0>。",
@@ -354,7 +371,6 @@
"rewrite_desc": "允許輕易地配置自訂的 DNS 回應供特定的域名。",
"rewrite_applied": "已套用的改寫規則",
"dns_rewrites": "DNS 改寫",
"form_domain": "輸入網域",
"form_answer": "輸入 IP 位址或域名",
"form_error_domain_format": "無效的網域格式",
"form_error_answer_format": "無效的回應格式",
@@ -409,5 +425,6 @@
"descr": "說明",
"whois": "Whois",
"filtering_rules_learn_more": "<0>了解更多</0>有關創建您自己的主機hosts封鎖清單。",
"blocked_by_response": "被正規名稱CNAME或 IP 封鎖作為回應"
"blocked_by_response": "被正規名稱CNAME或 IP 封鎖作為回應",
"try_again": "再次嘗試"
}

View File

@@ -233,6 +233,7 @@ export const getProfile = () => async (dispatch) => {
export const dnsStatusRequest = createAction('DNS_STATUS_REQUEST');
export const dnsStatusFailure = createAction('DNS_STATUS_FAILURE');
export const dnsStatusSuccess = createAction('DNS_STATUS_SUCCESS');
export const setDnsRunningStatus = createAction('SET_DNS_RUNNING_STATUS');
export const getDnsStatus = () => async (dispatch) => {
dispatch(dnsStatusRequest());
@@ -242,15 +243,17 @@ export const getDnsStatus = () => async (dispatch) => {
dispatch(dnsStatusFailure());
window.location.reload(true);
};
const handleRequestSuccess = (response) => {
const dnsStatus = response.data;
const runningStatus = dnsStatus && dnsStatus.running;
const { running } = dnsStatus;
const runningStatus = dnsStatus && running;
if (runningStatus === true) {
dispatch(dnsStatusSuccess(dnsStatus));
dispatch(getVersion());
dispatch(getTlsStatus());
dispatch(getProfile());
} else {
dispatch(setDnsRunningStatus(running));
}
};

View File

@@ -5,20 +5,28 @@ import { addErrorToast, addSuccessToast } from './index';
import { normalizeLogs, getParamsForClientsSearch, addClientInfo } from '../helpers/helpers';
import { TABLE_DEFAULT_PAGE_SIZE } from '../helpers/constants';
const getLogsWithParams = async (config) => {
const { older_than, filter, ...values } = config;
const rawLogs = await apiClient.getQueryLog({ ...filter, older_than });
const { data, oldest } = rawLogs;
const logs = normalizeLogs(data);
const clientsParams = getParamsForClientsSearch(logs, 'client');
const clients = await apiClient.findClients(clientsParams);
const logsWithClientInfo = addClientInfo(logs, clients, 'client');
// Cache clients in closure
const getLogsWithParamsWrapper = () => {
let clients = {};
return async (config) => {
const { older_than, filter, ...values } = config;
const rawLogs = await apiClient.getQueryLog({ ...filter, older_than });
const { data, oldest } = rawLogs;
const logs = normalizeLogs(data);
const clientsParams = getParamsForClientsSearch(logs, 'client');
if (!Object.values(clientsParams).every(client => client in clients)) {
clients = await apiClient.findClients(clientsParams);
}
const logsWithClientInfo = addClientInfo(logs, clients, 'client');
return {
logs: logsWithClientInfo, oldest, older_than, filter, ...values,
return {
logs: logsWithClientInfo, oldest, older_than, filter, ...values,
};
};
};
const getLogsWithParams = getLogsWithParamsWrapper();
export const getAdditionalLogsRequest = createAction('GET_ADDITIONAL_LOGS_REQUEST');
export const getAdditionalLogsFailure = createAction('GET_ADDITIONAL_LOGS_FAILURE');
export const getAdditionalLogsSuccess = createAction('GET_ADDITIONAL_LOGS_SUCCESS');

View File

@@ -39,30 +39,38 @@ export const getStatsRequest = createAction('GET_STATS_REQUEST');
export const getStatsFailure = createAction('GET_STATS_FAILURE');
export const getStatsSuccess = createAction('GET_STATS_SUCCESS');
export const getStats = () => async (dispatch) => {
dispatch(getStatsRequest());
try {
const stats = await apiClient.getStats();
const normalizedTopClients = normalizeTopStats(stats.top_clients);
const clientsParams = getParamsForClientsSearch(normalizedTopClients, 'name');
const clients = await apiClient.findClients(clientsParams);
const topClientsWithInfo = addClientInfo(normalizedTopClients, clients, 'name');
// Cache clients in closure
const getStatsWrapper = () => {
let clients = {};
return () => async (dispatch) => {
dispatch(getStatsRequest());
try {
const stats = await apiClient.getStats();
const normalizedTopClients = normalizeTopStats(stats.top_clients);
const clientsParams = getParamsForClientsSearch(normalizedTopClients, 'name');
if (!Object.values(clientsParams).every(client => client in clients)) {
clients = await apiClient.findClients(clientsParams);
}
const topClientsWithInfo = addClientInfo(normalizedTopClients, clients, 'name');
const normalizedStats = {
...stats,
top_blocked_domains: normalizeTopStats(stats.top_blocked_domains),
top_clients: topClientsWithInfo,
top_queried_domains: normalizeTopStats(stats.top_queried_domains),
avg_processing_time: secondsToMilliseconds(stats.avg_processing_time),
};
const normalizedStats = {
...stats,
top_blocked_domains: normalizeTopStats(stats.top_blocked_domains),
top_clients: topClientsWithInfo,
top_queried_domains: normalizeTopStats(stats.top_queried_domains),
avg_processing_time: secondsToMilliseconds(stats.avg_processing_time),
};
dispatch(getStatsSuccess(normalizedStats));
} catch (error) {
dispatch(addErrorToast({ error }));
dispatch(getStatsFailure());
}
dispatch(getStatsSuccess(normalizedStats));
} catch (error) {
dispatch(addErrorToast({ error }));
dispatch(getStatsFailure());
}
};
};
export const getStats = getStatsWrapper();
export const resetStatsRequest = createAction('RESET_STATS_REQUEST');
export const resetStatsFailure = createAction('RESET_STATS_FAILURE');
export const resetStatsSuccess = createAction('RESET_STATS_SUCCESS');

View File

@@ -89,12 +89,13 @@ class App extends Component {
<LoadingBar className="loading-bar" updateTime={1000} />
<Route component={Header} />
<div className="container container--wrap">
{dashboard.processing && !dashboard.isCoreRunning && (
{dashboard.processing && <Loading />}
{!dashboard.isCoreRunning && (
<div className="row row-cards">
<div className="col-lg-12">
<Status reloadPage={this.reloadPage}
message="dns_start"
/>
/>
<Loading />
</div>
</div>

View File

@@ -4,7 +4,7 @@ import { Field, reduxForm } from 'redux-form';
import { withNamespaces, Trans } from 'react-i18next';
import flow from 'lodash/flow';
import { renderField } from '../../../helpers/form';
import { renderInputField } from '../../../helpers/form';
import { RESPONSE_FILTER } from '../../../helpers/constants';
import Tooltip from '../../ui/Tooltip';
@@ -65,7 +65,7 @@ const Form = (props) => {
<Field
id="filter_question_type"
name="filter_question_type"
component={renderField}
component={renderInputField}
type="text"
className="form-control"
placeholder={t('type_table_header')}

View File

@@ -11,15 +11,6 @@ import whoisCell from './whoisCell';
const COLUMN_MIN_WIDTH = 200;
class AutoClients extends Component {
getStats = (ip, stats) => {
if (stats) {
const statsForCurrentIP = stats.find(item => item.name === ip);
return statsForCurrentIP && statsForCurrentIP.count;
}
return '';
};
columns = [
{
Header: this.props.t('table_client'),
@@ -47,11 +38,12 @@ class AutoClients extends Component {
},
{
Header: this.props.t('requests_count'),
accessor: 'statistics',
accessor: row => this.props.normalizedTopClients[row.ip] || 0,
sortMethod: (a, b) => b - a,
id: 'statistics',
minWidth: COLUMN_MIN_WIDTH,
Cell: (row) => {
const clientIP = row.original.ip;
const clientStats = clientIP && this.getStats(clientIP, this.props.topClients);
const { value: clientStats } = row;
if (clientStats) {
return (
@@ -80,6 +72,12 @@ class AutoClients extends Component {
<ReactTable
data={autoClients || []}
columns={this.columns}
defaultSorted={[
{
id: 'statistics',
asc: true,
},
]}
className="-striped -highlight card-table-overflow"
showPagination={true}
defaultPageSize={10}
@@ -100,7 +98,7 @@ class AutoClients extends Component {
AutoClients.propTypes = {
t: PropTypes.func.isRequired,
autoClients: PropTypes.array.isRequired,
topClients: PropTypes.array.isRequired,
normalizedTopClients: PropTypes.object.isRequired,
};
export default withNamespaces()(AutoClients);

View File

@@ -9,8 +9,6 @@ import Card from '../../ui/Card';
import Modal from './Modal';
import WrapCell from './WrapCell';
import whoisCell from './whoisCell';
class ClientsTable extends Component {
handleFormAdd = (values) => {
this.props.addClient(values);
@@ -62,15 +60,6 @@ class ClientsTable extends Component {
};
};
getStats = (name, stats) => {
if (stats) {
const currentStats = stats.find(item => item.info && item.info.name === name);
return currentStats && currentStats.count;
}
return '';
};
handleDelete = (data) => {
// eslint-disable-next-line no-alert
if (window.confirm(this.props.t('client_confirm_delete', { key: data.name }))) {
@@ -138,13 +127,13 @@ class ClientsTable extends Component {
<div className="logs__row logs__row--icons">
{value && value.length > 0
? value.map(service => (
<svg
className="service__icon service__icon--table"
title={service}
key={service}
>
<use xlinkHref={`#service_${service}`} />
</svg>
<svg
className="service__icon service__icon--table"
title={service}
key={service}
>
<use xlinkHref={`#service_${service}`} />
</svg>
))
: ''}
</div>
@@ -169,19 +158,14 @@ class ClientsTable extends Component {
);
},
},
{
Header: this.props.t('whois'),
accessor: 'whois_info',
minWidth: 200,
Cell: whoisCell(this.props.t),
},
{
Header: this.props.t('requests_count'),
accessor: 'statistics',
id: 'statistics',
accessor: row => this.props.normalizedTopClients[row.name] || 0,
sortMethod: (a, b) => b - a,
minWidth: 120,
Cell: (row) => {
const { name } = row.original;
const clientStats = this.getStats(name, this.props.topClients);
const { value: clientStats } = row;
if (clientStats) {
return (
@@ -265,6 +249,12 @@ class ClientsTable extends Component {
<ReactTable
data={clients || []}
columns={this.columns}
defaultSorted={[
{
id: 'statistics',
asc: true,
},
]}
className="-striped -highlight card-table-overflow"
showPagination={true}
defaultPageSize={10}
@@ -304,7 +294,7 @@ class ClientsTable extends Component {
ClientsTable.propTypes = {
t: PropTypes.func.isRequired,
clients: PropTypes.array.isRequired,
topClients: PropTypes.array.isRequired,
normalizedTopClients: PropTypes.object.isRequired,
toggleClientModal: PropTypes.func.isRequired,
deleteClient: PropTypes.func.isRequired,
addClient: PropTypes.func.isRequired,

View File

@@ -10,7 +10,9 @@ import Tabs from '../../ui/Tabs';
import Examples from '../Dns/Upstream/Examples';
import { toggleAllServices } from '../../../helpers/helpers';
import {
renderField,
required,
clientId,
renderInputField,
renderGroupField,
renderSelectField,
renderServiceField,
@@ -40,38 +42,30 @@ const settingsCheckboxes = [
placeholder: 'enforce_safe_search',
},
];
const validate = (values) => {
const errors = {};
const { name, ids } = values;
if (!name || !name.length) {
errors.name = i18n.t('form_error_required');
}
errors.name = required(name);
if (ids && ids.length) {
const idArrayErrors = [];
ids.forEach((id, idx) => {
if (!id || !id.length) {
idArrayErrors[idx] = i18n.t('form_error_required');
}
idArrayErrors[idx] = required(id) || clientId(id);
});
if (idArrayErrors.length) {
errors.ids = idArrayErrors;
}
}
return errors;
};
const renderFields = (placeholder, buttonTitle) =>
const renderFieldsWrapper = (placeholder, buttonTitle) =>
function cell(row) {
const {
fields,
meta: { error },
} = row;
return (
<div className="form__group">
{fields.map((ip, index) => (
@@ -84,6 +78,7 @@ const renderFields = (placeholder, buttonTitle) =>
placeholder={placeholder}
isActionAvailable={index !== 0}
removeField={() => fields.remove(index)}
normalize={data => data && data.trim()}
/>
</div>
))}
@@ -97,11 +92,13 @@ const renderFields = (placeholder, buttonTitle) =>
<use xlinkHref="#plus" />
</svg>
</button>
{error && <div className="error">{error}</div>}
</div>
);
};
// Should create function outside of component to prevent component re-renders
const renderFields = renderFieldsWrapper(i18n.t('form_enter_id'), i18n.t('form_add_id'));
let Form = (props) => {
const {
t,
@@ -126,10 +123,11 @@ let Form = (props) => {
<Field
id="name"
name="name"
component={renderField}
component={renderInputField}
type="text"
className="form-control"
placeholder={t('form_client_name')}
normalize={data => data && data.trim()}
/>
</div>
@@ -155,7 +153,7 @@ let Form = (props) => {
<div className="form__group">
<FieldArray
name="ids"
component={renderFields(t('form_enter_id'), t('form_add_id'))}
component={renderFields}
/>
</div>
</div>

View File

@@ -33,7 +33,7 @@ class Clients extends Component {
<Fragment>
<ClientsTable
clients={dashboard.clients}
topClients={stats.topClients}
normalizedTopClients={stats.normalizedTopClients}
isModalOpen={clients.isModalOpen}
modalClientName={clients.modalClientName}
modalType={clients.modalType}
@@ -47,7 +47,7 @@ class Clients extends Component {
/>
<AutoClients
autoClients={dashboard.autoClients}
topClients={stats.topClients}
normalizedTopClients={stats.normalizedTopClients}
/>
</Fragment>
)}

View File

@@ -5,7 +5,7 @@ import { Field, reduxForm, formValueSelector } from 'redux-form';
import { Trans, withNamespaces } from 'react-i18next';
import flow from 'lodash/flow';
import { renderField, required, ipv4, isPositive, toNumber } from '../../../helpers/form';
import { renderInputField, required, ipv4, isPositive, toNumber } from '../../../helpers/form';
const renderInterfaces = (interfaces => (
Object.keys(interfaces).map((item) => {
@@ -116,8 +116,9 @@ let Form = (props) => {
<div className="form__group form__group--settings">
<label>{t('dhcp_form_gateway_input')}</label>
<Field
id="gateway_ip"
name="gateway_ip"
component={renderField}
component={renderInputField}
type="text"
className="form-control"
placeholder={t('dhcp_form_gateway_input')}
@@ -127,8 +128,9 @@ let Form = (props) => {
<div className="form__group form__group--settings">
<label>{t('dhcp_form_subnet_input')}</label>
<Field
id="subnet_mask"
name="subnet_mask"
component={renderField}
component={renderInputField}
type="text"
className="form-control"
placeholder={t('dhcp_form_subnet_input')}
@@ -144,8 +146,9 @@ let Form = (props) => {
</div>
<div className="col">
<Field
id="range_start"
name="range_start"
component={renderField}
component={renderInputField}
type="text"
className="form-control"
placeholder={t('dhcp_form_range_start')}
@@ -154,8 +157,9 @@ let Form = (props) => {
</div>
<div className="col">
<Field
id="range_end"
name="range_end"
component={renderField}
component={renderInputField}
type="text"
className="form-control"
placeholder={t('dhcp_form_range_end')}
@@ -168,7 +172,7 @@ let Form = (props) => {
<label>{t('dhcp_form_lease_title')}</label>
<Field
name="lease_duration"
component={renderField}
component={renderInputField}
type="number"
className="form-control"
placeholder={t('dhcp_form_lease_input')}

View File

@@ -4,7 +4,7 @@ import { Field, reduxForm } from 'redux-form';
import { Trans, withNamespaces } from 'react-i18next';
import flow from 'lodash/flow';
import { renderField, ipv4, mac, required } from '../../../../helpers/form';
import { renderInputField, ipv4, mac, required } from '../../../../helpers/form';
const Form = (props) => {
const {
@@ -24,7 +24,7 @@ const Form = (props) => {
<Field
id="mac"
name="mac"
component={renderField}
component={renderInputField}
type="text"
className="form-control"
placeholder={t('form_enter_mac')}
@@ -35,7 +35,7 @@ const Form = (props) => {
<Field
id="ip"
name="ip"
component={renderField}
component={renderInputField}
type="text"
className="form-control"
placeholder={t('form_enter_ip')}
@@ -46,7 +46,7 @@ const Form = (props) => {
<Field
id="hostname"
name="hostname"
component={renderField}
component={renderInputField}
type="text"
className="form-control"
placeholder={t('form_enter_hostname')}

View File

@@ -219,46 +219,44 @@ class Dhcp extends Component {
</div>
</Card>
{dhcp.config.enabled && (
<Fragment>
<Card
title={t('dhcp_leases')}
bodyType="card-body box-body--settings"
>
<div className="row">
<div className="col">
<Leases leases={dhcp.leases} />
</div>
<Card
title={t('dhcp_leases')}
bodyType="card-body box-body--settings"
>
<div className="row">
<div className="col">
<Leases leases={dhcp.leases} />
</div>
</Card>
<Card
title={t('dhcp_static_leases')}
bodyType="card-body box-body--settings"
>
<div className="row">
<div className="col-12">
<StaticLeases
staticLeases={dhcp.staticLeases}
isModalOpen={dhcp.isModalOpen}
addStaticLease={addStaticLease}
removeStaticLease={removeStaticLease}
toggleLeaseModal={toggleLeaseModal}
processingAdding={dhcp.processingAdding}
processingDeleting={dhcp.processingDeleting}
/>
</div>
<div className="col-12">
<button
type="button"
className="btn btn-success btn-standard mt-3"
onClick={() => toggleLeaseModal()}
>
<Trans>dhcp_add_static_lease</Trans>
</button>
</div>
</div>
</Card>
</Fragment>
</div>
</Card>
)}
<Card
title={t('dhcp_static_leases')}
bodyType="card-body box-body--settings"
>
<div className="row">
<div className="col-12">
<StaticLeases
staticLeases={dhcp.staticLeases}
isModalOpen={dhcp.isModalOpen}
addStaticLease={addStaticLease}
removeStaticLease={removeStaticLease}
toggleLeaseModal={toggleLeaseModal}
processingAdding={dhcp.processingAdding}
processingDeleting={dhcp.processingDeleting}
/>
</div>
<div className="col-12">
<button
type="button"
className="btn btn-success btn-standard mt-3"
onClick={() => toggleLeaseModal()}
>
<Trans>dhcp_add_static_lease</Trans>
</button>
</div>
</div>
</Card>
</Fragment>
)}
</Fragment>

View File

@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
import { Field, reduxForm } from 'redux-form';
import { Trans, withNamespaces } from 'react-i18next';
import flow from 'lodash/flow';
import { renderTextareaField } from '../../../../helpers/form';
const Form = (props) => {
const {
@@ -21,7 +22,7 @@ const Form = (props) => {
<Field
id="allowed_clients"
name="allowed_clients"
component="textarea"
component={renderTextareaField}
type="text"
className="form-control form-control--textarea"
disabled={processingSet}
@@ -37,7 +38,7 @@ const Form = (props) => {
<Field
id="disallowed_clients"
name="disallowed_clients"
component="textarea"
component={renderTextareaField}
type="text"
className="form-control form-control--textarea"
disabled={processingSet}
@@ -53,7 +54,7 @@ const Form = (props) => {
<Field
id="blocked_hosts"
name="blocked_hosts"
component="textarea"
component={renderTextareaField}
type="text"
className="form-control form-control--textarea"
disabled={processingSet}
@@ -81,6 +82,7 @@ Form.propTypes = {
initialValues: PropTypes.object.isRequired,
processingSet: PropTypes.bool.isRequired,
t: PropTypes.func.isRequired,
textarea: PropTypes.bool,
};
export default flow([withNamespaces(), reduxForm({ form: 'accessForm' })])(Form);

View File

@@ -6,7 +6,7 @@ import { Trans, withNamespaces } from 'react-i18next';
import flow from 'lodash/flow';
import {
renderField,
renderInputField,
renderRadioField,
renderSelectField,
required,
@@ -45,7 +45,7 @@ let Form = ({
<Field
name="ratelimit"
type="number"
component={renderField}
component={renderInputField}
className="form-control"
placeholder={t('form_enter_rate_limit')}
normalize={toNumber}
@@ -90,7 +90,7 @@ let Form = ({
</div>
<Field
name="blocking_ipv4"
component={renderField}
component={renderInputField}
className="form-control"
placeholder={t('form_enter_ip')}
validate={[ipv4, required]}
@@ -107,7 +107,7 @@ let Form = ({
</div>
<Field
name="blocking_ipv6"
component={renderField}
component={renderInputField}
className="form-control"
placeholder={t('form_enter_ip')}
validate={[ipv6, required]}

View File

@@ -4,7 +4,7 @@ import { Field, reduxForm } from 'redux-form';
import { Trans, withNamespaces } from 'react-i18next';
import flow from 'lodash/flow';
import { renderField, required, domain, answer } from '../../../../helpers/form';
import { renderInputField, required, domain, answer } from '../../../../helpers/form';
const Form = (props) => {
const {
@@ -20,22 +20,41 @@ const Form = (props) => {
return (
<form onSubmit={handleSubmit}>
<div className="modal-body">
<div className="form__desc form__desc--top">
<Trans>domain_desc</Trans>
</div>
<div className="form__group">
<Field
id="domain"
name="domain"
component={renderField}
component={renderInputField}
type="text"
className="form-control"
placeholder={t('form_domain')}
validate={[required, domain]}
/>
</div>
<Trans>examples_title</Trans>:
<ol className="leading-loose">
<li>
<code>example.org</code> <Trans>example_rewrite_domain</Trans>
</li>
<li>
<code>*.example.org</code> &nbsp;
<span>
<Trans components={[<code key="0">text</code>]}>
example_rewrite_wildcard
</Trans>
</span>
</li>
</ol>
<div className="form__group">
<Field
id="answer"
name="answer"
component={renderField}
component={renderInputField}
type="text"
className="form-control"
placeholder={t('form_answer')}

View File

@@ -6,7 +6,7 @@ import { Trans, withNamespaces } from 'react-i18next';
import flow from 'lodash/flow';
import {
renderField,
renderInputField,
renderSelectField,
renderRadioField,
toNumber,
@@ -117,7 +117,7 @@ let Form = (props) => {
<Field
id="server_name"
name="server_name"
component={renderField}
component={renderInputField}
type="text"
className="form-control"
placeholder={t('encryption_server_enter')}
@@ -154,7 +154,7 @@ let Form = (props) => {
<Field
id="port_https"
name="port_https"
component={renderField}
component={renderInputField}
type="number"
className="form-control"
placeholder={t('encryption_https')}
@@ -176,7 +176,7 @@ let Form = (props) => {
<Field
id="port_dns_over_tls"
name="port_dns_over_tls"
component={renderField}
component={renderInputField}
type="number"
className="form-control"
placeholder={t('encryption_dot')}
@@ -252,7 +252,7 @@ let Form = (props) => {
<Field
id="certificate_path"
name="certificate_path"
component={renderField}
component={renderInputField}
type="text"
className="form-control"
placeholder={t('encryption_certificate_path')}
@@ -321,7 +321,7 @@ let Form = (props) => {
<Field
id="private_key_path"
name="private_key_path"
component={renderField}
component={renderInputField}
type="text"
className="form-control"
placeholder={t('encryption_private_key_path')}

View File

@@ -1,8 +1,9 @@
export const R_URL_REQUIRES_PROTOCOL = /^https?:\/\/[^/\s]+(\/.*)?$/;
export const R_HOST = /^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\\-]*[A-Za-z0-9])$/;
export const R_IPV4 = /^(?:(?:^|\.)(?:2(?:5[0-5]|[0-4]\d)|1?\d?\d)){4}$/g;
export const R_IPV6 = /^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))/g;
export const R_MAC = /^((([a-fA-F0-9][a-fA-F0-9]+[-]){5}|([a-fA-F0-9][a-fA-F0-9]+[:]){5})([a-fA-F0-9][a-fA-F0-9])$)|(^([a-fA-F0-9][a-fA-F0-9][a-fA-F0-9][a-fA-F0-9]+[.]){2}([a-fA-F0-9][a-fA-F0-9][a-fA-F0-9][a-fA-F0-9]))$/g;
export const R_HOST = /^(\*\.)?([\w-]+\.)+[\w-]+$/;
export const R_IPV4 = /^(?:(?:^|\.)(?:2(?:5[0-5]|[0-4]\d)|1?\d?\d)){4}$/;
export const R_IPV6 = /^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/;
export const R_CIDR = /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$/;
export const R_MAC = /^((([a-fA-F0-9][a-fA-F0-9]+[-]){5}|([a-fA-F0-9][a-fA-F0-9]+[:]){5})([a-fA-F0-9][a-fA-F0-9])$)|(^([a-fA-F0-9][a-fA-F0-9][a-fA-F0-9][a-fA-F0-9]+[.]){2}([a-fA-F0-9][a-fA-F0-9][a-fA-F0-9][a-fA-F0-9]))$/;
export const STATS_NAMES = {
avg_processing_time: 'average_processing_time',

View File

@@ -1,33 +1,45 @@
import React, { Fragment } from 'react';
import { Trans } from 'react-i18next';
import PropTypes from 'prop-types';
import { R_IPV4, R_MAC, R_HOST, R_IPV6, R_CIDR, UNSAFE_PORTS } from '../helpers/constants';
import { R_IPV4, R_MAC, R_HOST, R_IPV6, UNSAFE_PORTS } from '../helpers/constants';
export const renderField = (props, elementType) => {
const {
input, id, className, placeholder, type, disabled,
autoComplete, meta: { touched, error },
} = props;
export const renderField = ({
input,
id,
className,
placeholder,
type,
disabled,
autoComplete,
meta: { touched, error },
}) => (
<Fragment>
<input
{...input}
id={id}
placeholder={placeholder}
type={type}
className={className}
disabled={disabled}
autoComplete={autoComplete}
/>
{!disabled &&
touched &&
(error && <span className="form__message form__message--error">{error}</span>)}
</Fragment>
);
const element = React.createElement(elementType, {
...input,
id,
className,
placeholder,
autoComplete,
disabled,
type,
});
return (
<Fragment>
{element}
{!disabled && touched && (error && <span className="form__message form__message--error">{error}</span>)}
</Fragment>
);
};
renderField.propTypes = {
id: PropTypes.string.isRequired,
input: PropTypes.object.isRequired,
meta: PropTypes.object.isRequired,
className: PropTypes.string,
placeholder: PropTypes.string,
type: PropTypes.string,
disabled: PropTypes.bool,
autoComplete: PropTypes.bool,
};
export const renderTextareaField = props => renderField(props, 'textarea');
export const renderInputField = props => renderField(props, 'input');
export const renderGroupField = ({
input,
@@ -53,7 +65,7 @@ export const renderGroupField = ({
autoComplete={autoComplete}
/>
{isActionAvailable &&
<span className="input-group-append">
<span className="input-group-append">
<button
type="button"
className="btn btn-secondary btn-icon"
@@ -66,10 +78,9 @@ export const renderGroupField = ({
</span>
}
</div>
{!disabled &&
touched &&
(error && <span className="form__message form__message--error">{error}</span>)}
touched &&
(error && <span className="form__message form__message--error">{error}</span>)}
</Fragment>
);
@@ -82,8 +93,8 @@ export const renderRadioField = ({
<span className="custom-control-label">{placeholder}</span>
</label>
{!disabled &&
touched &&
(error && <span className="form__message form__message--error">{error}</span>)}
touched &&
(error && <span className="form__message form__message--error">{error}</span>)}
</Fragment>
);
@@ -112,8 +123,8 @@ export const renderSelectField = ({
</span>
</label>
{!disabled &&
touched &&
(error && <span className="form__message form__message--error">{error}</span>)}
touched &&
(error && <span className="form__message form__message--error">{error}</span>)}
</Fragment>
);
@@ -141,52 +152,67 @@ export const renderServiceField = ({
</svg>
</label>
{!disabled &&
touched &&
(error && <span className="form__message form__message--error">{error}</span>)}
touched &&
(error && <span className="form__message form__message--error">{error}</span>)}
</Fragment>
);
// Validation functions
// If the value is valid, the validation function should return undefined.
// https://redux-form.com/6.6.3/examples/fieldlevelvalidation/
export const required = (value) => {
if (value || value === 0) {
return false;
const formattedValue = typeof value === 'string' ? value.trim() : value;
if (formattedValue || formattedValue === 0 || (formattedValue && formattedValue.length !== 0)) {
return undefined;
}
return <Trans>form_error_required</Trans>;
};
export const ipv4 = (value) => {
if (value && !new RegExp(R_IPV4).test(value)) {
if (value && !R_IPV4.test(value)) {
return <Trans>form_error_ip4_format</Trans>;
}
return false;
return undefined;
};
export const clientId = (value) => {
if (!value) {
return undefined;
}
const formattedValue = value ? value.trim() : value;
if (formattedValue && !(R_IPV4.test(formattedValue) || R_IPV6.test(formattedValue)
|| R_MAC.test(formattedValue) || R_CIDR.test(formattedValue))) {
return <Trans>form_error_client_id_format</Trans>;
}
return undefined;
};
export const ipv6 = (value) => {
if (value && !new RegExp(R_IPV6).test(value)) {
if (value && !R_IPV6.test(value)) {
return <Trans>form_error_ip6_format</Trans>;
}
return false;
return undefined;
};
export const ip = (value) => {
if (value && !new RegExp(R_IPV4).test(value) && !new RegExp(R_IPV6).test(value)) {
if (value && !R_IPV4.test(value) && !R_IPV6.test(value)) {
return <Trans>form_error_ip_format</Trans>;
}
return false;
return undefined;
};
export const mac = (value) => {
if (value && !new RegExp(R_MAC).test(value)) {
if (value && !R_MAC.test(value)) {
return <Trans>form_error_mac_format</Trans>;
}
return false;
return undefined;
};
export const isPositive = (value) => {
if ((value || value === 0) && value <= 0) {
return <Trans>form_error_positive</Trans>;
}
return false;
return undefined;
};
export const biggerOrEqualZero = (value) => {
@@ -200,42 +226,37 @@ export const port = (value) => {
if ((value || value === 0) && (value < 80 || value > 65535)) {
return <Trans>form_error_port_range</Trans>;
}
return false;
return undefined;
};
export const portTLS = (value) => {
if (value === 0) {
return false;
return undefined;
} else if (value && (value < 80 || value > 65535)) {
return <Trans>form_error_port_range</Trans>;
}
return false;
return undefined;
};
export const isSafePort = (value) => {
if (UNSAFE_PORTS.includes(value)) {
return <Trans>form_error_port_unsafe</Trans>;
}
return false;
return undefined;
};
export const domain = (value) => {
if (value && !new RegExp(R_HOST).test(value)) {
if (value && !R_HOST.test(value)) {
return <Trans>form_error_domain_format</Trans>;
}
return false;
return undefined;
};
export const answer = (value) => {
if (
value &&
(!new RegExp(R_IPV4).test(value) &&
!new RegExp(R_IPV6).test(value) &&
!new RegExp(R_HOST).test(value))
) {
if (value && (!R_IPV4.test(value) && !R_IPV6.test(value) && !R_HOST.test(value))) {
return <Trans>form_error_answer_format</Trans>;
}
return false;
return undefined;
};
export const toNumber = value => value && parseInt(value, 10);

View File

@@ -261,6 +261,13 @@ export const redirectToCurrentProtocol = (values, httpPort = 80) => {
export const normalizeTextarea = text => text && text.replace(/[;, ]/g, '\n').split('\n').filter(n => n);
export const normalizeTopClients = clients => clients.reduce((accumulator, clientObj) => {
const { name, count } = clientObj;
// eslint-disable-next-line no-param-reassign
accumulator[name] = count;
return accumulator;
}, {});
export const getClientInfo = (clients, ip) => {
const client = clients
.find(item => item.ip_addrs && item.ip_addrs.find(clientIp => clientIp === ip));

View File

@@ -6,7 +6,7 @@ import flow from 'lodash/flow';
import i18n from '../../i18n';
import Controls from './Controls';
import renderField from './renderField';
import { renderInputField } from '../../helpers/form';
const required = (value) => {
if (value || value === 0) {
@@ -48,7 +48,7 @@ const Auth = (props) => {
</label>
<Field
name="username"
component={renderField}
component={renderInputField}
type="text"
className="form-control"
placeholder={ t('install_auth_username_enter') }
@@ -62,7 +62,7 @@ const Auth = (props) => {
</label>
<Field
name="password"
component={renderField}
component={renderInputField}
type="password"
className="form-control"
placeholder={ t('install_auth_password_enter') }
@@ -76,7 +76,7 @@ const Auth = (props) => {
</label>
<Field
name="confirm_password"
component={renderField}
component={renderInputField}
type="password"
className="form-control"
placeholder={ t('install_auth_confirm') }

View File

@@ -7,9 +7,9 @@ import flow from 'lodash/flow';
import Controls from './Controls';
import AddressList from './AddressList';
import renderField from './renderField';
import { getInterfaceIp } from '../../helpers/helpers';
import { ALL_INTERFACES_IP } from '../../helpers/constants';
import { renderInputField } from '../../helpers/form';
const required = (value) => {
if (value || value === 0) {
@@ -133,7 +133,7 @@ class Settings extends Component {
</label>
<Field
name="web.port"
component={renderField}
component={renderInputField}
type="number"
className="form-control"
placeholder="80"
@@ -201,7 +201,7 @@ class Settings extends Component {
</label>
<Field
name="dns.port"
component={renderField}
component={renderInputField}
type="number"
className="form-control"
placeholder="80"

View File

@@ -1,19 +0,0 @@
import React, { Fragment } from 'react';
const renderField = ({
input, className, placeholder, type, disabled, autoComplete, meta: { touched, error },
}) => (
<Fragment>
<input
{...input}
placeholder={placeholder}
type={type}
className={className}
disabled={disabled}
autoComplete={autoComplete}
/>
{!disabled && touched && (error && <span className="form__message form__message--error">{error}</span>)}
</Fragment>
);
export default renderField;

View File

@@ -4,7 +4,7 @@ import { Field, reduxForm } from 'redux-form';
import { Trans, withNamespaces } from 'react-i18next';
import flow from 'lodash/flow';
import { renderField, required } from '../../helpers/form';
import { renderInputField, required } from '../../helpers/form';
const Form = (props) => {
const {
@@ -19,10 +19,11 @@ const Form = (props) => {
<Trans>username_label</Trans>
</label>
<Field
id="username1"
name="username"
type="text"
className="form-control"
component={renderField}
component={renderInputField}
placeholder={t('username_placeholder')}
autoComplete="username"
disabled={processing}
@@ -38,7 +39,7 @@ const Form = (props) => {
name="password"
type="password"
className="form-control"
component={renderField}
component={renderInputField}
placeholder={t('password_placeholder')}
autoComplete="current-password"
disabled={processing}

View File

@@ -58,12 +58,13 @@ const settings = handleActions(
const dashboard = handleActions(
{
[actions.setDnsRunningStatus]: (state, { payload }) =>
({ ...state, isCoreRunning: payload }),
[actions.dnsStatusRequest]: state => ({ ...state, processing: true }),
[actions.dnsStatusFailure]: state => ({ ...state, processing: false }),
[actions.dnsStatusSuccess]: (state, { payload }) => {
const {
version,
running,
dns_port: dnsPort,
dns_addresses: dnsAddresses,
upstream_dns: upstreamDns,
@@ -75,7 +76,7 @@ const dashboard = handleActions(
} = payload;
const newState = {
...state,
isCoreRunning: running,
isCoreRunning: true,
processing: false,
dnsVersion: version,
dnsPort,
@@ -187,7 +188,7 @@ const dashboard = handleActions(
},
{
processing: true,
isCoreRunning: false,
isCoreRunning: true,
processingVersion: true,
processingFiltering: true,
processingClients: true,

View File

@@ -1,4 +1,5 @@
import { handleActions } from 'redux-actions';
import { normalizeTopClients } from '../helpers/helpers';
import * as actions from '../actions/stats';
@@ -64,6 +65,7 @@ const stats = handleActions(
replacedSafebrowsing,
topBlockedDomains,
topClients,
normalizedTopClients: normalizeTopClients(topClients),
topQueriedDomains,
numBlockedFiltering,
numDnsQueries,

View File

@@ -100,9 +100,14 @@ func (s *Server) CheckConfig(config ServerConfig) error {
func Create(config ServerConfig) *Server {
s := Server{}
s.conf = config
s.conf.DBFilePath = filepath.Join(config.WorkDir, dbFilename)
if s.conf.HTTPRegister != nil {
s.registerHandlers()
}
// we can't delay database loading until DHCP server is started,
// because we need static leases functionality available beforehand
s.dbLoad()
return &s
}
@@ -112,7 +117,6 @@ func (s *Server) Init(config ServerConfig) error {
if err != nil {
return err
}
s.dbLoad()
return nil
}
@@ -178,7 +182,7 @@ func (s *Server) setConfig(config ServerConfig) error {
s.conf.WorkDir = oldconf.WorkDir
s.conf.HTTPRegister = oldconf.HTTPRegister
s.conf.ConfigModified = oldconf.ConfigModified
s.conf.DBFilePath = filepath.Join(config.WorkDir, dbFilename)
s.conf.DBFilePath = oldconf.DBFilePath
return nil
}
@@ -565,10 +569,6 @@ func (s *Server) handleDecline(p dhcp4.Packet, options dhcp4.Options) dhcp4.Pack
// AddStaticLease adds a static lease (thread-safe)
func (s *Server) AddStaticLease(l Lease) error {
if s.IPpool == nil {
return fmt.Errorf("DHCP server isn't started")
}
if len(l.IP) != 4 {
return fmt.Errorf("Invalid IP")
}
@@ -629,10 +629,6 @@ func (s *Server) rmLease(l Lease) error {
// RemoveStaticLease removes a static lease (thread-safe)
func (s *Server) RemoveStaticLease(l Lease) error {
if s.IPpool == nil {
return fmt.Errorf("DHCP server isn't started")
}
if len(l.IP) != 4 {
return fmt.Errorf("Invalid IP")
}
@@ -675,10 +671,6 @@ func (s *Server) StaticLeases() []Lease {
s.leasesLock.Lock()
defer s.leasesLock.Unlock()
if s.IPpool == nil {
s.dbLoad()
}
var result []Lease
for _, lease := range s.leases {
if lease.Expiry.Unix() == 1 {
@@ -717,9 +709,17 @@ func (s *Server) FindMACbyIP(ip net.IP) net.HardwareAddr {
s.leasesLock.RLock()
defer s.leasesLock.RUnlock()
ip4 := ip.To4()
if ip4 == nil {
return nil
}
for _, l := range s.leases {
if l.Expiry.Unix() > now && l.IP.Equal(ip) {
return l.HWAddr
if l.IP.Equal(ip4) {
unix := l.Expiry.Unix()
if unix > now || unix == leaseExpireStatic {
return l.HWAddr
}
}
}
return nil

View File

@@ -23,6 +23,7 @@ func check(t *testing.T, result bool, msg string) {
func TestDHCP(t *testing.T) {
var s = Server{}
s.conf.DBFilePath = dbFilename
defer func() { _ = os.Remove(dbFilename) }()
var p, p2 dhcp4.Packet
var hw net.HardwareAddr
var lease *Lease
@@ -185,7 +186,7 @@ func TestDB(t *testing.T) {
lease, _ = s.reserveLease(p)
lease.Expiry = time.Unix(4000000002, 0)
os.Remove("leases.db")
_ = os.Remove("leases.db")
s.dbStore()
s.reset()
@@ -198,7 +199,7 @@ func TestDB(t *testing.T) {
check(t, bytes.Equal(s.leases[1].IP, []byte{1, 1, 1, 2}), "leases[1].IP")
check(t, s.leases[1].Expiry.Unix() == 4000000002, "leases[1].Expiry")
os.Remove("leases.db")
_ = os.Remove("leases.db")
}
func TestIsValidSubnetMask(t *testing.T) {

View File

@@ -263,7 +263,17 @@ func (r Reason) Matched() bool {
return r != NotFilteredNotFound
}
// CheckHost tries to match host against rules, then safebrowsing and parental if they are enabled
// CheckHostRules tries to match the host against filtering rules only
func (d *Dnsfilter) CheckHostRules(host string, qtype uint16, setts *RequestFilteringSettings) (Result, error) {
if !setts.FilteringEnabled {
return Result{}, nil
}
return d.matchHost(host, qtype)
}
// CheckHost tries to match the host against filtering rules,
// then safebrowsing and parental if they are enabled
func (d *Dnsfilter) CheckHost(host string, qtype uint16, setts *RequestFilteringSettings) (Result, error) {
// sometimes DNS clients will try to resolve ".", which is a request to get root servers
if host == "" {
@@ -334,6 +344,13 @@ func (d *Dnsfilter) CheckHost(host string, qtype uint16, setts *RequestFiltering
return Result{}, nil
}
// Return TRUE of host name matches a wildcard pattern
func matchDomainWildcard(host, wildcard string) bool {
return len(wildcard) >= 2 &&
wildcard[0] == '*' && wildcard[1] == '.' &&
strings.HasSuffix(host, wildcard[1:])
}
// Process rewrites table
// . Find CNAME for a domain name
// . if found, set domain name to canonical name
@@ -347,7 +364,9 @@ func (d *Dnsfilter) processRewrites(host string, qtype uint16) Result {
for _, r := range d.Rewrites {
if r.Domain != host {
continue
if !matchDomainWildcard(host, r.Domain) {
continue
}
}
ip := net.ParseIP(r.Answer)
@@ -362,7 +381,9 @@ func (d *Dnsfilter) processRewrites(host string, qtype uint16) Result {
for _, r := range d.Rewrites {
if r.Domain != host {
continue
if !matchDomainWildcard(host, r.Domain) {
continue
}
}
ip := net.ParseIP(r.Answer)

View File

@@ -474,6 +474,60 @@ func TestClientSettings(t *testing.T) {
assert.True(t, r.IsFiltered && r.Reason == FilteredBlockedService)
}
func TestRewrites(t *testing.T) {
d := Dnsfilter{}
// CNAME, A, AAAA
d.Rewrites = []RewriteEntry{
RewriteEntry{"somecname", "somehost.com"},
RewriteEntry{"somehost.com", "0.0.0.0"},
RewriteEntry{"host.com", "1.2.3.4"},
RewriteEntry{"host.com", "1.2.3.5"},
RewriteEntry{"host.com", "1:2:3::4"},
RewriteEntry{"www.host.com", "host.com"},
}
r := d.processRewrites("host2.com", dns.TypeA)
assert.Equal(t, NotFilteredNotFound, r.Reason)
r = d.processRewrites("www.host.com", dns.TypeA)
assert.Equal(t, ReasonRewrite, r.Reason)
assert.Equal(t, "host.com", r.CanonName)
assert.True(t, len(r.IPList) == 2)
assert.True(t, r.IPList[0].Equal(net.ParseIP("1.2.3.4")))
assert.True(t, r.IPList[1].Equal(net.ParseIP("1.2.3.5")))
r = d.processRewrites("www.host.com", dns.TypeAAAA)
assert.Equal(t, ReasonRewrite, r.Reason)
assert.True(t, len(r.IPList) == 1)
assert.True(t, r.IPList[0].Equal(net.ParseIP("1:2:3::4")))
// wildcard
d.Rewrites = []RewriteEntry{
RewriteEntry{"*.host.com", "1.2.3.5"},
RewriteEntry{"host.com", "1.2.3.4"},
}
r = d.processRewrites("host.com", dns.TypeA)
assert.Equal(t, ReasonRewrite, r.Reason)
assert.True(t, r.IPList[0].Equal(net.ParseIP("1.2.3.4")))
r = d.processRewrites("www.host.com", dns.TypeA)
assert.Equal(t, ReasonRewrite, r.Reason)
assert.True(t, r.IPList[0].Equal(net.ParseIP("1.2.3.5")))
r = d.processRewrites("www.host2.com", dns.TypeA)
assert.Equal(t, NotFilteredNotFound, r.Reason)
// wildcard + CNAME
d.Rewrites = []RewriteEntry{
RewriteEntry{"*.host.com", "host.com"},
RewriteEntry{"host.com", "1.2.3.4"},
}
r = d.processRewrites("www.host.com", dns.TypeA)
assert.Equal(t, ReasonRewrite, r.Reason)
assert.Equal(t, "host.com", r.CanonName)
assert.True(t, r.IPList[0].Equal(net.ParseIP("1.2.3.4")))
}
// BENCHMARKS
func BenchmarkSafeBrowsing(b *testing.B) {

View File

@@ -119,13 +119,13 @@ type accessListJSON struct {
}
func (s *Server) handleAccessList(w http.ResponseWriter, r *http.Request) {
s.Lock()
s.RLock()
j := accessListJSON{
AllowedClients: s.conf.AllowedClients,
DisallowedClients: s.conf.DisallowedClients,
BlockedHosts: s.conf.BlockedHosts,
}
s.Unlock()
s.RUnlock()
w.Header().Set("Content-Type", "application/json")
err := json.NewEncoder(w).Encode(j)

View File

@@ -94,7 +94,7 @@ func stringArrayDup(a []string) []string {
// WriteDiskConfig - write configuration
func (s *Server) WriteDiskConfig(c *FilteringConfig) {
s.Lock()
s.RLock()
sc := s.conf.FilteringConfig
*c = sc
c.RatelimitWhitelist = stringArrayDup(sc.RatelimitWhitelist)
@@ -103,7 +103,7 @@ func (s *Server) WriteDiskConfig(c *FilteringConfig) {
c.DisallowedClients = stringArrayDup(sc.DisallowedClients)
c.BlockedHosts = stringArrayDup(sc.BlockedHosts)
c.UpstreamDNS = stringArrayDup(sc.UpstreamDNS)
s.Unlock()
s.RUnlock()
}
// FilteringConfig represents the DNS filtering configuration of AdGuard Home
@@ -113,7 +113,7 @@ type FilteringConfig struct {
FilterHandler func(clientAddr string, settings *dnsfilter.RequestFilteringSettings) `yaml:"-"`
// This callback function returns the list of upstream servers for a client specified by IP address
GetUpstreamsByClient func(clientAddr string) []string `yaml:"-"`
GetUpstreamsByClient func(clientAddr string) []upstream.Upstream `yaml:"-"`
ProtectionEnabled bool `yaml:"protection_enabled"` // whether or not use any of dnsfilter features
@@ -233,6 +233,13 @@ func (s *Server) startInternal() error {
func (s *Server) Prepare(config *ServerConfig) error {
if config != nil {
s.conf = *config
if s.conf.BlockingMode == "custom_ip" {
s.conf.BlockingIPAddrv4 = net.ParseIP(s.conf.BlockingIPv4)
s.conf.BlockingIPAddrv6 = net.ParseIP(s.conf.BlockingIPv6)
if s.conf.BlockingIPAddrv4 == nil || s.conf.BlockingIPAddrv6 == nil {
return fmt.Errorf("DNS: invalid custom blocking IP address specified")
}
}
}
if len(s.conf.UpstreamDNS) == 0 {
@@ -345,23 +352,6 @@ func (s *Server) IsRunning() bool {
return s.isRunning
}
// Restart - restart server
func (s *Server) Restart() error {
s.Lock()
defer s.Unlock()
log.Print("Start reconfiguring the server")
err := s.stopInternal()
if err != nil {
return errorx.Decorate(err, "could not reconfigure the server")
}
err = s.startInternal()
if err != nil {
return errorx.Decorate(err, "could not reconfigure the server")
}
return nil
}
// Reconfigure applies the new configuration to the DNS server
func (s *Server) Reconfigure(config *ServerConfig) error {
s.Lock()
@@ -475,13 +465,9 @@ func (s *Server) handleDNSRequest(p *proxy.Proxy, d *proxy.DNSContext) error {
if d.Addr != nil && s.conf.GetUpstreamsByClient != nil {
clientIP := ipFromAddr(d.Addr)
upstreams := s.conf.GetUpstreamsByClient(clientIP)
for _, us := range upstreams {
u, err := upstream.AddressToUpstream(us, upstream.Options{Timeout: 30 * time.Second})
if err != nil {
log.Error("upstream.AddressToUpstream: %s: %s", us, err)
continue
}
d.Upstreams = append(d.Upstreams, u)
if len(upstreams) > 0 {
log.Debug("Using custom upstreams for %s", clientIP)
d.Upstreams = upstreams
}
}
@@ -502,7 +488,7 @@ func (s *Server) handleDNSRequest(p *proxy.Proxy, d *proxy.DNSContext) error {
} else if res.Reason != dnsfilter.NotFilteredWhiteList {
origResp2 := d.Res
res, err = s.filterResponse(d)
res, err = s.filterDNSResponse(d)
if err != nil {
return err
}
@@ -603,22 +589,28 @@ func (s *Server) updateStats(d *proxy.DNSContext, elapsed time.Duration, res dns
s.stats.Update(e)
}
// filterDNSRequest applies the dnsFilter and sets d.Res if the request was filtered
func (s *Server) filterDNSRequest(d *proxy.DNSContext) (*dnsfilter.Result, error) {
if !s.conf.ProtectionEnabled || s.dnsFilter == nil {
return &dnsfilter.Result{}, nil
}
// getClientRequestFilteringSettings lookups client filtering settings
// using the client's IP address from the DNSContext
func (s *Server) getClientRequestFilteringSettings(d *proxy.DNSContext) *dnsfilter.RequestFilteringSettings {
setts := s.dnsFilter.GetConfig()
setts.FilteringEnabled = true
if s.conf.FilterHandler != nil {
clientAddr := ipFromAddr(d.Addr)
s.conf.FilterHandler(clientAddr, &setts)
}
return &setts
}
// filterDNSRequest applies the dnsFilter and sets d.Res if the request was filtered
func (s *Server) filterDNSRequest(d *proxy.DNSContext) (*dnsfilter.Result, error) {
if !s.conf.ProtectionEnabled || s.dnsFilter == nil {
return &dnsfilter.Result{}, nil
}
setts := s.getClientRequestFilteringSettings(d)
req := d.Req
host := strings.TrimSuffix(req.Question[0].Name, ".")
res, err := s.dnsFilter.CheckHost(host, d.Req.Question[0].Qtype, &setts)
res, err := s.dnsFilter.CheckHost(host, d.Req.Question[0].Qtype, setts)
if err != nil {
// Return immediately if there's an error
return nil, errorx.Decorate(err, "dnsfilter failed to check host '%s'", host)
@@ -628,8 +620,7 @@ func (s *Server) filterDNSRequest(d *proxy.DNSContext) (*dnsfilter.Result, error
d.Res = s.genDNSFilterMessage(d, &res)
} else if res.Reason == dnsfilter.ReasonRewrite && len(res.IPList) != 0 {
resp := dns.Msg{}
resp.SetReply(req)
resp := s.makeResponse(req)
name := host
if len(res.CanonName) != 0 {
@@ -642,7 +633,6 @@ func (s *Server) filterDNSRequest(d *proxy.DNSContext) (*dnsfilter.Result, error
a := s.genAAnswer(req, ip)
a.Hdr.Name = dns.Fqdn(name)
resp.Answer = append(resp.Answer, a)
} else if req.Question[0].Qtype == dns.TypeAAAA {
a := s.genAAAAAnswer(req, ip)
a.Hdr.Name = dns.Fqdn(name)
@@ -650,7 +640,7 @@ func (s *Server) filterDNSRequest(d *proxy.DNSContext) (*dnsfilter.Result, error
}
}
d.Res = &resp
d.Res = resp
}
return &res, err
@@ -658,7 +648,7 @@ func (s *Server) filterDNSRequest(d *proxy.DNSContext) (*dnsfilter.Result, error
// If response contains CNAME, A or AAAA records, we apply filtering to each canonical host name or IP address.
// If this is a match, we set a new response in d.Res and return.
func (s *Server) filterResponse(d *proxy.DNSContext) (*dnsfilter.Result, error) {
func (s *Server) filterDNSResponse(d *proxy.DNSContext) (*dnsfilter.Result, error) {
for _, a := range d.Res.Answer {
host := ""
@@ -686,9 +676,8 @@ func (s *Server) filterResponse(d *proxy.DNSContext) (*dnsfilter.Result, error)
s.RUnlock()
continue
}
setts := dnsfilter.RequestFilteringSettings{}
setts.FilteringEnabled = true
res, err := s.dnsFilter.CheckHost(host, d.Req.Question[0].Qtype, &setts)
setts := s.getClientRequestFilteringSettings(d)
res, err := s.dnsFilter.CheckHostRules(host, d.Req.Question[0].Qtype, setts)
s.RUnlock()
if err != nil {
@@ -704,6 +693,15 @@ func (s *Server) filterResponse(d *proxy.DNSContext) (*dnsfilter.Result, error)
return nil, nil
}
// Create a DNS response by DNS request and set necessary flags
func (s *Server) makeResponse(req *dns.Msg) *dns.Msg {
resp := dns.Msg{}
resp.SetReply(req)
resp.RecursionAvailable = true
resp.Compress = true
return &resp
}
// genDNSFilterMessage generates a DNS message corresponding to the filtering result
func (s *Server) genDNSFilterMessage(d *proxy.DNSContext, result *dnsfilter.Result) *dns.Msg {
m := d.Req
@@ -751,17 +749,15 @@ func (s *Server) genServerFailure(request *dns.Msg) *dns.Msg {
}
func (s *Server) genARecord(request *dns.Msg, ip net.IP) *dns.Msg {
resp := dns.Msg{}
resp.SetReply(request)
resp := s.makeResponse(request)
resp.Answer = append(resp.Answer, s.genAAnswer(request, ip))
return &resp
return resp
}
func (s *Server) genAAAARecord(request *dns.Msg, ip net.IP) *dns.Msg {
resp := dns.Msg{}
resp.SetReply(request)
resp := s.makeResponse(request)
resp.Answer = append(resp.Answer, s.genAAAAAnswer(request, ip))
return &resp
return resp
}
func (s *Server) genAAnswer(req *dns.Msg, ip net.IP) *dns.A {
@@ -797,9 +793,8 @@ func (s *Server) genResponseWithIP(req *dns.Msg, ip net.IP) *dns.Msg {
}
// empty response
resp := dns.Msg{}
resp.SetReply(req)
return &resp
resp := s.makeResponse(req)
return resp
}
func (s *Server) genBlockedHost(request *dns.Msg, newAddr string, d *proxy.DNSContext) *dns.Msg {
@@ -827,9 +822,7 @@ func (s *Server) genBlockedHost(request *dns.Msg, newAddr string, d *proxy.DNSCo
return s.genServerFailure(request)
}
resp := dns.Msg{}
resp.SetReply(request)
resp.Authoritative, resp.RecursionAvailable = true, true
resp := s.makeResponse(request)
if newContext.Res != nil {
for _, answer := range newContext.Res.Answer {
answer.Header().Name = request.Question[0].Name
@@ -837,7 +830,7 @@ func (s *Server) genBlockedHost(request *dns.Msg, newAddr string, d *proxy.DNSCo
}
}
return &resp
return resp
}
// Make a CNAME response

View File

@@ -121,7 +121,7 @@ func (s *Server) handleSetConfig(w http.ResponseWriter, r *http.Request) {
s.conf.ConfigModified()
if restart {
err = s.Restart()
err = s.Reconfigure(nil)
if err != nil {
httpError(r, w, http.StatusInternalServerError, "%s", err)
return
@@ -172,7 +172,7 @@ func (s *Server) handleSetUpstreamConfig(w http.ResponseWriter, r *http.Request)
s.Unlock()
s.conf.ConfigModified()
err = s.Restart()
err = s.Reconfigure(nil)
if err != nil {
httpError(r, w, http.StatusInternalServerError, "%s", err)
return

View File

@@ -384,6 +384,30 @@ func TestBlockCNAME(t *testing.T) {
_ = s.Stop()
}
func TestClientRulesForCNAMEMatching(t *testing.T) {
s := createTestServer(t)
testUpstm := &testUpstream{testCNAMEs, testIPv4, nil}
s.conf.FilterHandler = func(clientAddr string, settings *dnsfilter.RequestFilteringSettings) {
settings.FilteringEnabled = false
}
err := s.startWithUpstream(testUpstm)
assert.Nil(t, err)
addr := s.dnsProxy.Addr(proxy.ProtoUDP)
// 'badhost' has a canonical name 'null.example.org' which is blocked by filters:
// response is blocked
req := dns.Msg{}
req.Id = dns.Id()
req.Question = []dns.Question{
{Name: "badhost.", Qtype: dns.TypeA, Qclass: dns.ClassINET},
}
// However, in our case it should not be blocked
// as filtering is disabled on the client level
reply, err := dns.Exchange(&req, addr.String())
assert.Nil(t, err)
assert.Equal(t, dns.RcodeSuccess, reply.Rcode)
}
func TestNullBlockedRequest(t *testing.T) {
s := createTestServer(t)
s.conf.FilteringConfig.BlockingMode = "null_ip"
@@ -424,6 +448,55 @@ func TestNullBlockedRequest(t *testing.T) {
}
}
func TestBlockedCustomIP(t *testing.T) {
rules := "||nxdomain.example.org^\n||null.example.org^\n127.0.0.1 host.example.org\n@@||whitelist.example.org^\n||127.0.0.255\n"
filters := map[int]string{}
filters[0] = rules
c := dnsfilter.Config{}
f := dnsfilter.New(&c, filters)
s := NewServer(f, nil, nil)
conf := ServerConfig{}
conf.UDPListenAddr = &net.UDPAddr{Port: 0}
conf.TCPListenAddr = &net.TCPAddr{Port: 0}
conf.ProtectionEnabled = true
conf.BlockingMode = "custom_ip"
conf.BlockingIPv4 = "bad IP"
conf.UpstreamDNS = []string{"8.8.8.8:53", "8.8.4.4:53"}
err := s.Prepare(&conf)
assert.True(t, err != nil) // invalid BlockingIPv4
conf.BlockingIPv4 = "0.0.0.1"
conf.BlockingIPv6 = "::1"
err = s.Prepare(&conf)
assert.True(t, err == nil)
err = s.Start()
assert.True(t, err == nil, "%s", err)
addr := s.dnsProxy.Addr(proxy.ProtoUDP)
req := createTestMessageWithType("null.example.org.", dns.TypeA)
reply, err := dns.Exchange(req, addr.String())
assert.True(t, err == nil)
assert.True(t, len(reply.Answer) == 1)
a, ok := reply.Answer[0].(*dns.A)
assert.True(t, ok)
assert.True(t, a.A.String() == "0.0.0.1")
req = createTestMessageWithType("null.example.org.", dns.TypeAAAA)
reply, err = dns.Exchange(req, addr.String())
assert.True(t, err == nil)
assert.True(t, len(reply.Answer) == 1)
a6, ok := reply.Answer[0].(*dns.AAAA)
assert.True(t, ok)
assert.True(t, a6.AAAA.String() == "::1")
err = s.Stop()
if err != nil {
t.Fatalf("DNS server failed to stop: %s", err)
}
}
func TestBlockedByHosts(t *testing.T) {
s := createTestServer(t)
err := s.Start()
@@ -514,7 +587,11 @@ func TestBlockedBySafeBrowsing(t *testing.T) {
}
func createTestServer(t *testing.T) *Server {
rules := "||nxdomain.example.org^\n||null.example.org^\n127.0.0.1 host.example.org\n@@||whitelist.example.org^\n||127.0.0.255\n"
rules := `||nxdomain.example.org
||null.example.org^
127.0.0.1 host.example.org
@@||whitelist.example.org^
||127.0.0.255`
filters := map[int]string{}
filters[0] = rules
c := dnsfilter.Config{}
@@ -652,6 +729,16 @@ func createTestMessage(host string) *dns.Msg {
return &req
}
func createTestMessageWithType(host string, qtype uint16) *dns.Msg {
req := dns.Msg{}
req.Id = dns.Id()
req.RecursionDesired = true
req.Question = []dns.Question{
{Name: host, Qtype: qtype, Qclass: dns.ClassINET},
}
return &req
}
func assertGoogleAResponse(t *testing.T, reply *dns.Msg) {
assertResponse(t, reply, "8.8.8.8")
}

2
go.mod
View File

@@ -3,7 +3,7 @@ module github.com/AdguardTeam/AdGuardHome
go 1.13
require (
github.com/AdguardTeam/dnsproxy v0.23.2
github.com/AdguardTeam/dnsproxy v0.23.4
github.com/AdguardTeam/golibs v0.3.0
github.com/AdguardTeam/urlfilter v0.7.0
github.com/NYTimes/gziphandler v1.1.1

4
go.sum
View File

@@ -1,5 +1,5 @@
github.com/AdguardTeam/dnsproxy v0.23.2 h1:HbBzoe9Pssj4UjvbeBUPHz7cpCt/7/LpVKu4olhPcKk=
github.com/AdguardTeam/dnsproxy v0.23.2/go.mod h1:2qy8rpdfBzKgMPxkHmPdaNK4XZJ322v4KtVGI8s8Bn0=
github.com/AdguardTeam/dnsproxy v0.23.4 h1:23o1mOdORJrkuMgRHXVa1oO0zRf+lZ1K6l9HxdLr+Ig=
github.com/AdguardTeam/dnsproxy v0.23.4/go.mod h1:2qy8rpdfBzKgMPxkHmPdaNK4XZJ322v4KtVGI8s8Bn0=
github.com/AdguardTeam/golibs v0.2.4 h1:GUssokegKxKF13K67Pgl0ZGwqHjNN6X7sep5ik6ORdY=
github.com/AdguardTeam/golibs v0.2.4/go.mod h1:R3M+mAg3nWG4X4Hsag5eef/TckHFH12ZYhK7AzJc8+U=
github.com/AdguardTeam/golibs v0.3.0 h1:1zO8ulGEOdXDDM++Ap4sYfTsT/Z4tZBZtiWSA4ykcOU=

View File

@@ -14,6 +14,7 @@ import (
"github.com/AdguardTeam/AdGuardHome/dhcpd"
"github.com/AdguardTeam/AdGuardHome/dnsforward"
"github.com/AdguardTeam/dnsproxy/upstream"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/utils"
)
@@ -62,8 +63,14 @@ type clientsContainer struct {
list map[string]*Client // name -> client
idIndex map[string]*Client // IP -> client
ipHost map[string]*ClientHost // IP -> Hostname
lock sync.Mutex
// cache for Upstream instances that are used in the case
// when custom DNS servers are configured for a client
upstreamsCache map[string][]upstream.Upstream // name -> []Upstream
lock sync.Mutex
// dhcpServer is used for looking up clients IP addresses by MAC addresses
dhcpServer *dhcpd.Server
testing bool // if TRUE, this object is used for internal tests
@@ -78,6 +85,7 @@ func (clients *clientsContainer) Init(objects []clientObject, dhcpServer *dhcpd.
clients.list = make(map[string]*Client)
clients.idIndex = make(map[string]*Client)
clients.ipHost = make(map[string]*ClientHost)
clients.upstreamsCache = make(map[string][]upstream.Upstream)
clients.dhcpServer = dhcpServer
clients.addFromConfig(objects)
@@ -168,7 +176,7 @@ func (clients *clientsContainer) Exists(ip string, source clientSource) bool {
clients.lock.Lock()
defer clients.lock.Unlock()
_, ok := clients.idIndex[ip]
_, ok := clients.findByIP(ip)
if ok {
return true
}
@@ -185,14 +193,58 @@ func (clients *clientsContainer) Exists(ip string, source clientSource) bool {
// Find searches for a client by IP
func (clients *clientsContainer) Find(ip string) (Client, bool) {
clients.lock.Lock()
defer clients.lock.Unlock()
return clients.findByIP(ip)
}
// FindUpstreams looks for upstreams configured for the client
// If no client found for this IP, or if no custom upstreams are configured,
// this method returns nil
func (clients *clientsContainer) FindUpstreams(ip string) []upstream.Upstream {
clients.lock.Lock()
defer clients.lock.Unlock()
c, ok := clients.findByIP(ip)
if !ok {
return nil
}
if len(c.Upstreams) == 0 {
return nil
}
upstreams, ok := clients.upstreamsCache[c.Name]
if ok {
return upstreams
}
for _, us := range c.Upstreams {
u, err := upstream.AddressToUpstream(us, upstream.Options{Timeout: dnsforward.DefaultTimeout})
if err != nil {
log.Error("upstream.AddressToUpstream: %s: %s", us, err)
continue
}
upstreams = append(upstreams, u)
}
if len(upstreams) == 0 {
clients.upstreamsCache[c.Name] = nil
} else {
clients.upstreamsCache[c.Name] = upstreams
}
return upstreams
}
// Find searches for a client by IP (and does not lock anything)
func (clients *clientsContainer) findByIP(ip string) (Client, bool) {
ipAddr := net.ParseIP(ip)
if ipAddr == nil {
return Client{}, false
}
clients.lock.Lock()
defer clients.lock.Unlock()
c, ok := clients.idIndex[ip]
if ok {
return *c, true
@@ -350,6 +402,9 @@ func (clients *clientsContainer) Del(name string) bool {
// update Name index
delete(clients.list, name)
// update upstreams cache
delete(clients.upstreamsCache, name)
// update ID index
for _, id := range c.IDs {
delete(clients.idIndex, id)
@@ -413,10 +468,13 @@ func (clients *clientsContainer) Update(name string, c Client) error {
// update Name index
if old.Name != c.Name {
delete(clients.list, old.Name)
clients.list[c.Name] = old
}
// update upstreams cache
delete(clients.upstreamsCache, name)
delete(clients.upstreamsCache, old.Name)
*old = c
return nil
}
@@ -426,10 +484,9 @@ func (clients *clientsContainer) SetWhoisInfo(ip string, info [][]string) {
clients.lock.Lock()
defer clients.lock.Unlock()
c, ok := clients.idIndex[ip]
_, ok := clients.findByIP(ip)
if ok {
c.WhoisInfo = info
log.Debug("Clients: set WHOIS info for client %s: %v", c.Name, c.WhoisInfo)
log.Debug("Clients: client for %s is already created, ignore WHOIS info", ip)
return
}
@@ -440,6 +497,7 @@ func (clients *clientsContainer) SetWhoisInfo(ip string, info [][]string) {
return
}
// Create a ClientHost implicitly so that we don't do this check again
ch = &ClientHost{
Source: ClientSourceWHOIS,
}
@@ -455,8 +513,8 @@ func (clients *clientsContainer) AddHost(ip, host string, source clientSource) (
clients.lock.Lock()
defer clients.lock.Unlock()
// check index
_, ok := clients.idIndex[ip]
// check existing clients first
_, ok := clients.findByIP(ip)
if ok {
return false, nil
}
@@ -520,7 +578,6 @@ func (clients *clientsContainer) addFromHostsFile() {
// The command's output is:
// HOST (IP) at MAC on IFACE
func (clients *clientsContainer) addFromSystemARP() {
if runtime.GOOS == "windows" {
return
}

View File

@@ -257,7 +257,6 @@ func (clients *clientsContainer) handleFindClient(w http.ResponseWriter, r *http
}
cj := clientHostToJSON(ip, ch)
el[ip] = cj
} else {
cj := clientToJSON(&c)
el[ip] = cj

View File

@@ -1,7 +1,12 @@
package home
import (
"net"
"os"
"testing"
"time"
"github.com/AdguardTeam/AdGuardHome/dhcpd"
"github.com/stretchr/testify/assert"
)
@@ -162,13 +167,63 @@ func TestClientsWhois(t *testing.T) {
clients.SetWhoisInfo("1.1.1.1", whois)
assert.True(t, clients.ipHost["1.1.1.1"].WhoisInfo[0][1] == "orgname-val")
// set whois info on existing client
// Check that we cannot set whois info on existing client
c = Client{
IDs: []string{"1.1.1.2"},
Name: "client1",
}
_, _ = clients.Add(c)
clients.SetWhoisInfo("1.1.1.2", whois)
assert.True(t, clients.idIndex["1.1.1.2"].WhoisInfo[0][1] == "orgname-val")
assert.Nil(t, clients.idIndex["1.1.1.2"].WhoisInfo)
_ = clients.Del("client1")
}
func TestClientsAddExistingHost(t *testing.T) {
var c Client
clients := clientsContainer{}
clients.testing = true
clients.Init(nil, nil)
// some test variables
mac, _ := net.ParseMAC("aa:aa:aa:aa:aa:aa")
testIP := "1.2.3.4"
// add a client
c = Client{
IDs: []string{"1.1.1.1", "1:2:3::4", "aa:aa:aa:aa:aa:aa", "2.2.2.0/24"},
Name: "client1",
}
ok, err := clients.Add(c)
assert.True(t, ok)
assert.Nil(t, err)
// try adding a duplicate by IP
ok, err = clients.AddHost("1.1.1.1", "test", ClientSourceRDNS)
assert.False(t, ok)
assert.Nil(t, err)
// now some more complicated stuff
// first, init a DHCP server with a single static lease
config := dhcpd.ServerConfig{
DBFilePath: "leases.db",
}
defer func() { _ = os.Remove("leases.db") }()
clients.dhcpServer = dhcpd.Create(config)
err = clients.dhcpServer.AddStaticLease(dhcpd.Lease{
HWAddr: mac,
IP: net.ParseIP(testIP).To4(),
Hostname: "testhost",
Expiry: time.Now().Add(time.Hour),
})
assert.Nil(t, err)
// try adding a duplicate IP which for a Mac-based client
ok, err = clients.AddHost(testIP, "test", ClientSourceRDNS)
assert.False(t, ok)
assert.Nil(t, err)
// don't allow duplicates by CIDR
ok, err = clients.AddHost("2.2.2.2", "test", ClientSourceRDNS)
assert.False(t, ok)
assert.Nil(t, err)
}

View File

@@ -117,6 +117,9 @@ type tlsConfigSettings struct {
PortHTTPS int `yaml:"port_https" json:"port_https,omitempty"` // HTTPS port. If 0, HTTPS will be disabled
PortDNSOverTLS int `yaml:"port_dns_over_tls" json:"port_dns_over_tls,omitempty"` // DNS-over-TLS port. If 0, DOT will be disabled
// Allow DOH queries via unencrypted HTTP (e.g. for reverse proxying)
AllowUnencryptedDOH bool `yaml:"allow_unencrypted_doh" json:"allow_unencrypted_doh"`
dnsforward.TLSConfig `yaml:",inline" json:",inline"`
}

View File

@@ -144,7 +144,7 @@ func handleGetProfile(w http.ResponseWriter, r *http.Request) {
// DNS-over-HTTPS
// --------------
func handleDOH(w http.ResponseWriter, r *http.Request) {
if r.TLS == nil {
if !config.TLS.AllowUnencryptedDOH && r.TLS == nil {
httpError(w, http.StatusNotFound, "Not Found")
return
}

View File

@@ -7,6 +7,7 @@ import (
"net"
"net/http"
"os/exec"
"runtime"
"strconv"
"github.com/AdguardTeam/golibs/log"
@@ -117,6 +118,10 @@ func handleInstallCheckConfig(w http.ResponseWriter, r *http.Request) {
// Check if DNSStubListener is active
func checkDNSStubListener() bool {
if runtime.GOOS != "linux" {
return false
}
cmd := exec.Command("systemctl", "is-enabled", "systemd-resolved")
log.Tracef("executing %s %v", cmd.Path, cmd.Args)
_, err := cmd.Output()

View File

@@ -11,6 +11,7 @@ import (
"github.com/AdguardTeam/AdGuardHome/querylog"
"github.com/AdguardTeam/AdGuardHome/stats"
"github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/dnsproxy/upstream"
"github.com/AdguardTeam/golibs/log"
"github.com/joomcode/errorx"
)
@@ -70,6 +71,9 @@ func initDNSServer() error {
sessFilename := filepath.Join(baseDir, "sessions.db")
config.auth = InitAuth(sessFilename, config.Users, config.WebSessionTTLHours*60*60)
if config.auth == nil {
return fmt.Errorf("Couldn't initialize Auth module")
}
config.Users = nil
Context.rdns = InitRDNS(Context.dnsServer, &Context.clients)
@@ -175,18 +179,12 @@ func generateServerConfig() dnsforward.ServerConfig {
return newconfig
}
func getUpstreamsByClient(clientAddr string) []string {
c, ok := Context.clients.Find(clientAddr)
if !ok {
return []string{}
}
log.Debug("Using upstreams %v for client %s (IP: %s)", c.Upstreams, c.Name, clientAddr)
return c.Upstreams
func getUpstreamsByClient(clientAddr string) []upstream.Upstream {
return Context.clients.FindUpstreams(clientAddr)
}
// If a client has his own settings, apply them
func applyAdditionalFiltering(clientAddr string, setts *dnsfilter.RequestFilteringSettings) {
ApplyBlockedServices(setts, config.DNS.BlockedServices)
if len(clientAddr) == 0 {
@@ -254,6 +252,10 @@ func reconfigureDNSServer() error {
}
func stopDNSServer() error {
if !isRunning() {
return nil
}
err := Context.dnsServer.Stop()
if err != nil {
return errorx.Decorate(err, "Couldn't stop forwarding DNS server")

View File

@@ -242,6 +242,10 @@ func checkPortAvailable(host string, port int) error {
return err
}
ln.Close()
// It seems that net.Listener.Close() doesn't close file descriptors right away.
// We wait for some time and hope that this fd will be closed.
time.Sleep(100 * time.Millisecond)
return nil
}
@@ -251,6 +255,10 @@ func checkPacketPortAvailable(host string, port int) error {
return err
}
ln.Close()
// It seems that net.Listener.Close() doesn't close file descriptors right away.
// We wait for some time and hope that this fd will be closed.
time.Sleep(100 * time.Millisecond)
return err
}

View File

@@ -20,6 +20,8 @@ import (
"syscall"
"time"
"github.com/AdguardTeam/AdGuardHome/isdelve"
"github.com/AdguardTeam/AdGuardHome/dhcpd"
"github.com/AdguardTeam/AdGuardHome/dnsfilter"
"github.com/AdguardTeam/AdGuardHome/dnsforward"
@@ -284,7 +286,8 @@ func httpServerLoop() {
// and if not, ask and try to run as root
func requireAdminRights() {
admin, _ := haveAdminRights()
if admin {
if //noinspection ALL
admin || isdelve.Enabled {
return
}

View File

@@ -2,6 +2,7 @@ package home
import (
"context"
"encoding/base64"
"io/ioutil"
"net/http"
"os"
@@ -9,7 +10,9 @@ import (
"testing"
"time"
"github.com/AdguardTeam/dnsproxy/proxyutil"
"github.com/AdguardTeam/dnsproxy/upstream"
"github.com/miekg/dns"
"github.com/stretchr/testify/assert"
)
@@ -61,6 +64,7 @@ tls:
force_https: false
port_https: 443
port_dns_over_tls: 853
allow_unencrypted_doh: true
certificate_chain: ""
private_key: ""
certificate_path: ""
@@ -99,6 +103,7 @@ schema_version: 5
// . Start AGH instance
// . Check Web server
// . Check DNS server
// . Check DNS server with DOH
// . Wait until the filters are downloaded
// . Stop and cleanup
func TestHome(t *testing.T) {
@@ -131,12 +136,34 @@ func TestHome(t *testing.T) {
assert.Truef(t, err == nil, "%s", err)
assert.Equal(t, 200, resp.StatusCode)
// test DNS over UDP
r := upstream.NewResolver("127.0.0.1:5354", 3*time.Second)
addrs, err := r.LookupIPAddr(context.TODO(), "static.adguard.com")
assert.Truef(t, err == nil, "%s", err)
haveIP := len(addrs) != 0
assert.True(t, haveIP)
// test DNS over HTTP without encryption
req := dns.Msg{}
req.Id = dns.Id()
req.RecursionDesired = true
req.Question = []dns.Question{{Name: "static.adguard.com.", Qtype: dns.TypeA, Qclass: dns.ClassINET}}
buf, err := req.Pack()
assert.True(t, err == nil, "%s", err)
requestURL := "http://127.0.0.1:3000/dns-query?dns=" + base64.RawURLEncoding.EncodeToString(buf)
resp, err = http.DefaultClient.Get(requestURL)
assert.True(t, err == nil, "%s", err)
body, err := ioutil.ReadAll(resp.Body)
assert.True(t, err == nil, "%s", err)
assert.True(t, resp.StatusCode == http.StatusOK)
response := dns.Msg{}
err = response.Unpack(body)
assert.True(t, err == nil, "%s", err)
addrs = nil
proxyutil.AppendIPAddrs(&addrs, response.Answer)
haveIP = len(addrs) != 0
assert.True(t, haveIP)
for i := 1; ; i++ {
st, err := os.Stat(filepath.Join(dir, "data", "filters", "1.txt"))
if err == nil && st.Size() != 0 {

View File

@@ -16,6 +16,7 @@ const (
defaultServer = "whois.arin.net"
defaultPort = "43"
maxValueLength = 250
whoisTTL = 1 * 60 * 60 // 1 hour
)
// Whois - module context
@@ -205,8 +206,7 @@ func (w *Whois) Begin(ip string) {
// TTL expired
}
expire = make([]byte, 8)
const ttl = 1 * 60 * 60
binary.BigEndian.PutUint64(expire, now+ttl)
binary.BigEndian.PutUint64(expire, now+whoisTTL)
_ = w.ipAddrs.Set([]byte(ip), expire)
log.Debug("Whois: adding %s", ip)

5
isdelve/delve.go Normal file
View File

@@ -0,0 +1,5 @@
// +build delve
package isdelve
const Enabled = true

3
isdelve/doc.go Normal file
View File

@@ -0,0 +1,3 @@
// Package isdelve is for checking if we're debugging:
// https://stackoverflow.com/questions/47879070/how-can-i-see-if-the-goland-debugger-is-running-in-the-program
package isdelve

5
isdelve/nodelve.go Normal file
View File

@@ -0,0 +1,5 @@
// +build !delve
package isdelve
const Enabled = false

View File

@@ -9,3 +9,9 @@ TWOSKY_URI=<API URI> TWOSKY_CLIENT_ID=<PROJECT ID> node upload.js
```
After download you'll find the output locales in the `client/src/__locales/` folder.
Examples:
```
TWOSKY_URI=https://twosky.example/api/v1 TWOSKY_CLIENT_ID=adguardhome node download.js
TWOSKY_URI=https://twosky.example/api/v1 TWOSKY_CLIENT_ID=adguardhome node upload.js
```