Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e73605c4c5 | ||
|
|
c7017d49aa | ||
|
|
191d3bde49 | ||
|
|
18876a8e5c | ||
|
|
aa4a0d9880 | ||
|
|
d03d731d65 | ||
|
|
33b58a42fe | ||
|
|
2e9e708647 | ||
|
|
8ad22841ab | ||
|
|
32cf02264c | ||
|
|
0e8445b38f | ||
|
|
cb27ecd6c0 | ||
|
|
535220b3df | ||
|
|
7b9cfa94f8 | ||
|
|
b3f2e88e9c | ||
|
|
aa7a8d45e4 | ||
|
|
49cdef3d6a | ||
|
|
fecd146552 |
57
CHANGELOG.md
57
CHANGELOG.md
@@ -23,7 +23,7 @@ and this project adheres to
|
||||
### Added
|
||||
|
||||
- Support for Discovery of Designated Resolvers (DDR) according to the [RFC
|
||||
draft][ddr-draft-06] ([#4463]).
|
||||
draft][ddr-draft] ([#4463]).
|
||||
- `windows/arm64` support ([#3057]).
|
||||
|
||||
### Deprecated
|
||||
@@ -33,16 +33,52 @@ and this project adheres to
|
||||
[#2993]: https://github.com/AdguardTeam/AdGuardHome/issues/2993
|
||||
[#3057]: https://github.com/AdguardTeam/AdGuardHome/issues/3057
|
||||
|
||||
[ddr-draft-06]: https://www.ietf.org/archive/id/draft-ietf-add-ddr-06.html
|
||||
[ddr-draft]: https://datatracker.ietf.org/doc/html/draft-ietf-add-ddr-08
|
||||
|
||||
|
||||
|
||||
<!--
|
||||
## [v0.107.8] - 2022-07-12 (APPROX.)
|
||||
## [v0.107.9] - 2022-08-23 (APPROX.)
|
||||
-->
|
||||
|
||||
|
||||
|
||||
## [v0.107.8] - 2022-07-13
|
||||
|
||||
See also the [v0.107.8 GitHub milestone][ms-v0.107.8].
|
||||
|
||||
### Security
|
||||
|
||||
- Go version was updated to prevent the possibility of exploiting the
|
||||
CVE-2022-1705, CVE-2022-32148, CVE-2022-30631, and other Go vulnerabilities
|
||||
fixed in [Go 1.17.12][go-1.17.12].
|
||||
|
||||
<!--
|
||||
TODO(a.garipov): Use the above format in all similar announcements below.
|
||||
-->
|
||||
|
||||
### Fixed
|
||||
|
||||
- DHCP lease validation incorrectly letting users assign the IP address of the
|
||||
gateway as the address of the lease ([#4698]).
|
||||
- Updater no longer expects a hardcoded name for `AdGuardHome` executable
|
||||
([#4219]).
|
||||
- Inconsistent names of runtime clients from hosts files ([#4683]).
|
||||
- PTR requests for addresses leased by DHCP will now be resolved into hostnames
|
||||
under `dhcp.local_domain_name` ([#4699]).
|
||||
- Broken service installation on OpenWrt ([#4677]).
|
||||
|
||||
[#4219]: https://github.com/AdguardTeam/AdGuardHome/issues/4219
|
||||
[#4677]: https://github.com/AdguardTeam/AdGuardHome/issues/4677
|
||||
[#4683]: https://github.com/AdguardTeam/AdGuardHome/issues/4683
|
||||
[#4698]: https://github.com/AdguardTeam/AdGuardHome/issues/4698
|
||||
[#4699]: https://github.com/AdguardTeam/AdGuardHome/issues/4699
|
||||
|
||||
[go-1.17.12]: https://groups.google.com/g/golang-announce/c/nqrv9fbR0zE
|
||||
[ms-v0.107.8]: https://github.com/AdguardTeam/AdGuardHome/milestone/44?closed=1
|
||||
|
||||
|
||||
|
||||
## [v0.107.7] - 2022-06-06
|
||||
|
||||
See also the [v0.107.7 GitHub milestone][ms-v0.107.7].
|
||||
@@ -51,7 +87,7 @@ See also the [v0.107.7 GitHub milestone][ms-v0.107.7].
|
||||
|
||||
- Go version was updated to prevent the possibility of exploiting the
|
||||
[CVE-2022-29526], [CVE-2022-30634], [CVE-2022-30629], [CVE-2022-30580], and
|
||||
[CVE-2022-29804] vulnerabilities.
|
||||
[CVE-2022-29804] Go vulnerabilities.
|
||||
- Enforced password strength policy ([#3503]).
|
||||
|
||||
### Added
|
||||
@@ -206,7 +242,7 @@ See also the [v0.107.6 GitHub milestone][ms-v0.107.6].
|
||||
|
||||
- `User-Agent` HTTP header removed from outgoing DNS-over-HTTPS requests.
|
||||
- Go version was updated to prevent the possibility of exploiting the
|
||||
[CVE-2022-24675], [CVE-2022-27536], and [CVE-2022-28327] vulnerabilities.
|
||||
[CVE-2022-24675], [CVE-2022-27536], and [CVE-2022-28327] Go vulnerabilities.
|
||||
|
||||
### Added
|
||||
|
||||
@@ -261,7 +297,7 @@ were resolved.
|
||||
### Security
|
||||
|
||||
- Go version was updated to prevent the possibility of exploiting the
|
||||
[CVE-2022-24921] vulnerability.
|
||||
[CVE-2022-24921] Go vulnerability.
|
||||
|
||||
[CVE-2022-24921]: https://www.cvedetails.com/cve/CVE-2022-24921
|
||||
|
||||
@@ -274,7 +310,7 @@ See also the [v0.107.4 GitHub milestone][ms-v0.107.4].
|
||||
### Security
|
||||
|
||||
- Go version was updated to prevent the possibility of exploiting the
|
||||
[CVE-2022-23806], [CVE-2022-23772], and [CVE-2022-23773] vulnerabilities.
|
||||
[CVE-2022-23806], [CVE-2022-23772], and [CVE-2022-23773] Go vulnerabilities.
|
||||
|
||||
### Fixed
|
||||
|
||||
@@ -1010,11 +1046,12 @@ See also the [v0.104.2 GitHub milestone][ms-v0.104.2].
|
||||
|
||||
|
||||
<!--
|
||||
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.8...HEAD
|
||||
[v0.107.8]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.7...v0.107.8
|
||||
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.9...HEAD
|
||||
[v0.107.9]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.8...v0.107.9
|
||||
-->
|
||||
|
||||
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.7...HEAD
|
||||
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.8...HEAD
|
||||
[v0.107.8]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.7...v0.107.8
|
||||
[v0.107.7]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.6...v0.107.7
|
||||
[v0.107.6]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.5...v0.107.6
|
||||
[v0.107.5]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.4...v0.107.5
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
# Make sure to sync any changes with the branch overrides below.
|
||||
'variables':
|
||||
'channel': 'edge'
|
||||
'dockerGo': 'adguard/golang-ubuntu:4.3'
|
||||
'dockerGo': 'adguard/golang-ubuntu:4.5'
|
||||
|
||||
'stages':
|
||||
- 'Make release':
|
||||
@@ -285,7 +285,7 @@
|
||||
# need to build a few of these.
|
||||
'variables':
|
||||
'channel': 'beta'
|
||||
'dockerGo': 'adguard/golang-ubuntu:4.3'
|
||||
'dockerGo': 'adguard/golang-ubuntu:4.5'
|
||||
# release-vX.Y.Z branches are the branches from which the actual final release
|
||||
# is built.
|
||||
- '^release-v[0-9]+\.[0-9]+\.[0-9]+':
|
||||
@@ -300,4 +300,4 @@
|
||||
# are the ones that actually get released.
|
||||
'variables':
|
||||
'channel': 'release'
|
||||
'dockerGo': 'adguard/golang-ubuntu:4.3'
|
||||
'dockerGo': 'adguard/golang-ubuntu:4.5'
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
'key': 'AHBRTSPECS'
|
||||
'name': 'AdGuard Home - Build and run tests'
|
||||
'variables':
|
||||
'dockerGo': 'adguard/golang-ubuntu:4.3'
|
||||
'dockerGo': 'adguard/golang-ubuntu:4.5'
|
||||
|
||||
'stages':
|
||||
- 'Tests':
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
"form_error_server_name": "Няслушнае імя сервера",
|
||||
"form_error_subnet": "Падсетка «{{cidr}}» не ўтрымвае IP-адраса «{{ip}}»",
|
||||
"form_error_positive": "Павінна быць больш 0",
|
||||
"form_error_gateway_ip": "Арэнда не можа мець IP-адрас шлюза",
|
||||
"out_of_range_error": "Павінна быць па-за дыяпазонам «{{start}}»-«{{end}}»",
|
||||
"lower_range_start_error": "Павінна быць менш за пачатак дыяпазону",
|
||||
"greater_range_start_error": "Павінна быць больш за пачатак дыяпазону",
|
||||
@@ -445,7 +446,7 @@
|
||||
"access_blocked_title": "Заблакаваныя дамены",
|
||||
"access_blocked_desc": "Не блытайце гэта з фільтрамі. AdGuard Home будзе ігнараваць DNS-запыты з гэтымі даменамі.",
|
||||
"access_settings_saved": "Налады доступу паспяхова захаваны",
|
||||
"updates_checked": "Праверка абнаўленняў прайшла паспяхова",
|
||||
"updates_checked": "Даступная новая версія AdGuard Home",
|
||||
"updates_version_equal": "Версія AdGuard Home актуальная",
|
||||
"check_updates_now": "Праверыць абнаўленні",
|
||||
"dns_privacy": "Зашыфраваны DNS",
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
"form_error_server_name": "Neplatný název serveru",
|
||||
"form_error_subnet": "Podsíť \"{{cidr}}\" neobsahuje IP adresu \"{{ip}}\"",
|
||||
"form_error_positive": "Musí být větší než 0",
|
||||
"form_error_gateway_ip": "Pronájem nemůže mít IP adresu brány",
|
||||
"out_of_range_error": "Musí být mimo rozsah \"{{start}}\"-\"{{end}}\"",
|
||||
"lower_range_start_error": "Musí být menší než začátek rozsahu",
|
||||
"greater_range_start_error": "Musí být větší než začátek rozsahu",
|
||||
@@ -445,7 +446,7 @@
|
||||
"access_blocked_title": "Blokované domény",
|
||||
"access_blocked_desc": "Nezaměňujte to s filtry. AdGuard Home zruší dotazy DNS odpovídající těmto doménám a tyto dotazy se neobjeví ani v protokolu dotazů. Zde můžete určit přesné názvy domén, zástupné znaky a pravidla filtrování URL adres, např. \"example.org\", \"*.example.org\" nebo \"||example.org^\".",
|
||||
"access_settings_saved": "Nastavení přístupu bylo úspěšně uloženo",
|
||||
"updates_checked": "Aktualizace úspěšně zkontrolovány",
|
||||
"updates_checked": "Nová verze AdGuard Home je k dispozici\n",
|
||||
"updates_version_equal": "AdGuard Home je aktuální",
|
||||
"check_updates_now": "Zkontrolovat aktualizace nyní",
|
||||
"dns_privacy": "Soukromí DNS",
|
||||
|
||||
@@ -445,7 +445,7 @@
|
||||
"access_blocked_title": "Ikke tilladte domæner",
|
||||
"access_blocked_desc": "Ikke at forveksle med filtre. AdGuard Home dropper DNS-forespørgsler matchende disse domæner, ej heller vil forespørgslerne optræde i forespørgselsloggen. Der kan angives præcise domænenavne, jokertegn eller URL-filterregler, f.eks. \"eksempel.org\", \"*.eksempel.org\", \"||eksempel.org^\" eller tilsvarende.",
|
||||
"access_settings_saved": "Adgangsindstillinger gemt",
|
||||
"updates_checked": "Opdateringstjek foretaget",
|
||||
"updates_checked": "En ny version af AdGuard Home er tilgængelig\n",
|
||||
"updates_version_equal": "AdGuard Home er opdateret",
|
||||
"check_updates_now": "Søg efter opdateringer nu",
|
||||
"dns_privacy": "DNS-fortrolighed",
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
"form_error_server_name": "Ungültiger Servername",
|
||||
"form_error_subnet": "Subnetz „{{cidr}}“ enthält nicht die IP-Adresse „{{ip}}“",
|
||||
"form_error_positive": "Muss größer als 0 sein",
|
||||
"form_error_gateway_ip": "Lease kann nicht die IP-Adresse des Gateways haben",
|
||||
"out_of_range_error": "Muss außerhalb des Bereichs „{{start}}“-„{{end}}“ liegen",
|
||||
"lower_range_start_error": "Muss niedriger als der Bereichsbeginn sein",
|
||||
"greater_range_start_error": "Muss größer als der Bereichsbeginn sein",
|
||||
@@ -445,7 +446,7 @@
|
||||
"access_blocked_title": "Nicht zugelassene Domains",
|
||||
"access_blocked_desc": "Verwechseln Sie dies nicht mit Filtern. AdGuard Home verwirft DNS-Abfragen, die mit diesen Domänen übereinstimmen, und diese Abfragen erscheinen nicht einmal im Abfrageprotokoll. Hier können Sie die genauen Domain-Namen, Wildcards und URL-Filter-Regeln angeben, z.B. 'beispiel.org', '*.beispiel.org' oder '||beispiel.org^'.",
|
||||
"access_settings_saved": "Zugriffseinstellungen erfolgreich gespeichert",
|
||||
"updates_checked": "Erfolgreich auf Aktualisierungen geprüft",
|
||||
"updates_checked": "Neue Version von AdGuard Home ist jetzt verfügbar",
|
||||
"updates_version_equal": "AdGuard Home ist aktuell",
|
||||
"check_updates_now": "Jetzt nach Aktualisierungen suchen",
|
||||
"dns_privacy": "DNS-Datenschutz",
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
"form_error_server_name": "Invalid server name",
|
||||
"form_error_subnet": "Subnet \"{{cidr}}\" does not contain the IP address \"{{ip}}\"",
|
||||
"form_error_positive": "Must be greater than 0",
|
||||
"form_error_gateway_ip": "Lease can't have the IP address of the gateway",
|
||||
"out_of_range_error": "Must be out of range \"{{start}}\"-\"{{end}}\"",
|
||||
"lower_range_start_error": "Must be lower than range start",
|
||||
"greater_range_start_error": "Must be greater than range start",
|
||||
@@ -445,7 +446,7 @@
|
||||
"access_blocked_title": "Disallowed domains",
|
||||
"access_blocked_desc": "Not to be confused with filters. AdGuard Home drops DNS queries matching these domains, and these queries don't even appear in the query log. You can specify exact domain names, wildcards, or URL filter rules, e.g. \"example.org\", \"*.example.org\", or \"||example.org^\" correspondingly.",
|
||||
"access_settings_saved": "Access settings successfully saved",
|
||||
"updates_checked": "Updates successfully checked",
|
||||
"updates_checked": "A new version of AdGuard Home is available",
|
||||
"updates_version_equal": "AdGuard Home is up-to-date",
|
||||
"check_updates_now": "Check for updates now",
|
||||
"dns_privacy": "DNS Privacy",
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
"form_error_server_name": "Nombre de servidor no válido",
|
||||
"form_error_subnet": "La subred \"{{cidr}}\" no contiene la dirección IP \"{{ip}}\"",
|
||||
"form_error_positive": "Debe ser mayor que 0",
|
||||
"form_error_gateway_ip": "Asignación no puede tener la dirección IP de la puerta de enlace",
|
||||
"out_of_range_error": "Debe estar fuera del rango \"{{start}}\"-\"{{end}}\"",
|
||||
"lower_range_start_error": "Debe ser inferior que el inicio de rango",
|
||||
"greater_range_start_error": "Debe ser mayor que el inicio de rango",
|
||||
@@ -445,7 +446,7 @@
|
||||
"access_blocked_title": "Dominios no permitidos",
|
||||
"access_blocked_desc": "No debe confundirse con filtros. AdGuard Home descartará las consultas DNS que coincidan con estos dominios, y estas consultas ni siquiera aparecerán en el registro de consultas. Puedes especificar nombres de dominio exactos, comodines o reglas de filtrado de URL, por ejemplo: \"ejemplo.org\", \"*.ejemplo.org\" o \"||ejemplo.org^\" correspondientemente.",
|
||||
"access_settings_saved": "Configuración de acceso guardado correctamente",
|
||||
"updates_checked": "Actualizaciones comprobadas correctamente",
|
||||
"updates_checked": "La nueva versión de AdGuard Home está disponible",
|
||||
"updates_version_equal": "AdGuard Home está actualizado",
|
||||
"check_updates_now": "Buscar actualizaciones ahora",
|
||||
"dns_privacy": "DNS cifrado",
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
"bootstrap_dns": "خودراه انداز سرورهای DNS",
|
||||
"bootstrap_dns_desc": "خودراه انداز سرورهای DNS برای تفکیک آدرس آی پی تفکیک کننده های DoH/DoT که شما بعنوان جریان ارسالی تعیین کردید استفاده میشود.",
|
||||
"local_ptr_title": "سرورهای خصوصی DNS",
|
||||
"local_ptr_desc": "سرور یا سرور های DNS ای که AdGuard Home برای درخواست های منابع محلی ارائه شده مورد استفاده قرار خواهد داد. برای مثال، این سرور برای تعیین نام های سرویس دهنده برای سرویس گیرنده با آدرس های آی پی خصوصی مورد استفاده قرار خواهد گرفت. اگر تعیین نشود،AdGuard Home به طور خودکار از تعیین کننده ی DNS پیش فرض شما استفاده خواهد کرد.",
|
||||
"local_ptr_default_resolver": "به طور پیش فرض، AdGuard Home از تعیین کننده های DNS معکوس زیر استفاده می کند: {{ip}}.",
|
||||
"local_ptr_no_default_resolver": "AdGuard Home نتوانست برای این دستگاه تعیین کننده های DNS معکوس محرمانه مناسب را معین کند.",
|
||||
"local_ptr_placeholder": "در هر خط یک آدرس سرور را وارد کنید",
|
||||
@@ -321,7 +320,6 @@
|
||||
"install_devices_android_list_5": "گروه مقادیر DNS 1 و DNS 2 را به آدرس سرور AdGuard Home خود تغییر دهید.",
|
||||
"install_devices_ios_list_1": "از صفحه خانه،تنظیمات را فشار دهید.",
|
||||
"install_devices_ios_list_2": "وای فای را از منوی چپ انتخاب کنید (پیکربندی DNS دستی برای ارتباط موبایلی غیرممکن است).",
|
||||
"install_devices_ios_list_3": "روی نام شبکه فعال فعلی کلیک کنید.",
|
||||
"install_devices_ios_list_4": "در فیلد DNS آدرس سرور AdGuard Home را وارد کنید",
|
||||
"get_started": "شروع به کار",
|
||||
"next": "بعدی",
|
||||
@@ -413,7 +411,7 @@
|
||||
"access_blocked_title": "دامنه های مسدود شده",
|
||||
"access_blocked_desc": "این را با فیلتر ها به اشتباه نگیرید.AdGuard Home جستار DNS را با این دامنه ها در جستار سوال ها نمی پذیرد.",
|
||||
"access_settings_saved": "تنظیمات دسترسی با موفقیت ذخیره شد",
|
||||
"updates_checked": "بروز رسانی با موفقیت بررسی شد",
|
||||
"updates_checked": "نسخه جدیدی از AdGuard Home در دسترس است",
|
||||
"updates_version_equal": "AdGuard Home بروز است",
|
||||
"check_updates_now": "حالا بررسی برای بروز رسانی",
|
||||
"dns_privacy": "حریم خصوصی DNS",
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
"form_error_server_name": "Virheellinen palvelimen nimi",
|
||||
"form_error_subnet": "Aliverkko \"{{cidr}}\" ei sisällä IP-osoitetta \"{{ip}}\"",
|
||||
"form_error_positive": "Oltava suurempi kuin 0",
|
||||
"form_error_gateway_ip": "Lainalla ei voi olla yhdyskäytävän IP-osoitetta",
|
||||
"out_of_range_error": "Oltava alueen \"{{start}}\" - \"{{end}}\" ulkopuolella",
|
||||
"lower_range_start_error": "Oltava alueen aloitusarvoa pienempi",
|
||||
"greater_range_start_error": "Oltava alueen aloitusarvoa suurempi",
|
||||
@@ -70,7 +71,7 @@
|
||||
"dhcp_error": "AdGuard Home ei voinut tunnistaa, onko verkossa toista aktiivista DHCP-palvelinta",
|
||||
"dhcp_static_ip_error": "Jotta DHCP-palvelinta voidaan käyttää, on määritettävä kiinteä IP-osoite. AdGuard Home ei voinut tunnistaa, onko tälle verkkosovittimelle määritetty IP-osoite kiinteä. Määritä kiinteä IP-osoite itse.",
|
||||
"dhcp_dynamic_ip_found": "Järjestelmäsi käyttää verkkosovittimelle <0>{{interfaceName}}</0> dynaamista IP-osoitetta. Jotta voit käyttää DHCP-palvelinta, on sovittimelle määritettävä kiinteä IP-osoite. Nykyinen IP-osoitteesi on <0>{{ipAddress}}</0>. Tämä osoite määritetään automaattisesti kiinteäksi, jos painat \"Ota DHCP-palvelin käyttöön\" -painiketta.",
|
||||
"dhcp_lease_added": "Kiinteä laina \"{{key}}\" on lisätty",
|
||||
"dhcp_lease_added": "Kiinteä laina \"{{key}}\" lisättiin",
|
||||
"dhcp_lease_deleted": "Kiinteä laina \"{{key}}\" poistettiin",
|
||||
"dhcp_new_static_lease": "Uusi kiinteä laina",
|
||||
"dhcp_static_leases_not_found": "Kiinteitä DHCP-lainoja ei löytynyt",
|
||||
@@ -344,12 +345,12 @@
|
||||
"install_devices_macos_list_2": "Paina \"Verkko\".",
|
||||
"install_devices_macos_list_3": "Valitse listan ensimmäinen yhteys ja paina \"Lisävalinnat\".",
|
||||
"install_devices_macos_list_4": "Valitse DNS-välilehti ja syötä AdGuard Home -palvelimesi osoitteet.",
|
||||
"install_devices_android_list_1": "Napauta Android-laitteesi aloitusnäytöstä tai sovellusvalikosta \"Asetukset\".",
|
||||
"install_devices_android_list_2": "Napauta \"Yhteydet\" ja sitten \"Wi-Fi\". Näytetään kaikki käytettävissä olevat langattomat verkot (mobiiliverkolle ei ole mahdollista määrittää omaa DNS-palvelinta).",
|
||||
"install_devices_android_list_3": "Napauta yhdistetyn verkon vieressä olevaa asetuskuvaketta tai paina verkkoa pitkään ja valitse \"Muokkaa verkkoa\".",
|
||||
"install_devices_android_list_4": "Saatat joutua napauttamaan \"Lisäasetukset\" nähdäksesi lisää valintoja. Muuttaaksesi DNS-asetuksia, on \"IP-asetukset\" -kohdan \"DHCP\" -valinta vaihdettava \"Staattinen\" -valintaan.",
|
||||
"install_devices_android_list_1": "Paina Android-laitteesi aloitusnäytöstä tai sovellusvalikosta \"Asetukset\".",
|
||||
"install_devices_android_list_2": "Paina \"Yhteydet\" ja sitten \"Wi-Fi\". Kaikki käytettävissä olevat langattomat verkot näytetään (mobiiliverkolle ei ole mahdollista määrittää omaa DNS-palvelinta).",
|
||||
"install_devices_android_list_3": "Paina yhdistetyn verkon vieressä olevaa asetuskuvaketta tai paina verkkoa pitkään ja valitse \"Muokkaa verkkoa\".",
|
||||
"install_devices_android_list_4": "Saatat joutua painamaan \"Lisäasetukset\" nähdäksesi enemmän valintoja. Muuttaaksesi DNS-asetuksia, on \"IP-asetukset\" -kohdan \"DHCP\" -valinta vaihdettava \"Staattinen\" -valintaan.",
|
||||
"install_devices_android_list_5": "Syötä \"DNS 1\" ja \"DNS 2\" -kenttiin AdGuard Home -palvelimesi osoitteet.",
|
||||
"install_devices_ios_list_1": "Napauta aloitusnäytöstä \"Asetukset\".",
|
||||
"install_devices_ios_list_1": "Paina aloitusnäytöstä \"Asetukset\".",
|
||||
"install_devices_ios_list_2": "Valitse vasemmalta \"Wi-Fi\" (mobiiliverkolle ei ole mahdollista määrittää omaa DNS-palvelinta).",
|
||||
"install_devices_ios_list_3": "Valitse tällä hetkellä aktiivinen verkko.",
|
||||
"install_devices_ios_list_4": "Syötä \"DNS\" -kenttään AdGuard Home -palvelimesi osoitteet.",
|
||||
@@ -445,7 +446,7 @@
|
||||
"access_blocked_title": "Kielletyt verkkotunnukset",
|
||||
"access_blocked_desc": "Ei pidä sekoittaa suodattimiin. AdGuard Home hylkää näiden verkkotunnusten DNS-pyynnöt, eivätkä nämä pyynnöt näy edes pyyntöhistoriassa. Tähän voidaan syöttää tarkkoja verkkotunnuksia, jokerimerkkejä tai URL-suodatussääntöjä, kuten \"example.org\", \"*.example.org\" tai \"||example.org^\".",
|
||||
"access_settings_saved": "Käytön asetukset tallennettiin",
|
||||
"updates_checked": "Päivitykset tarkastettiin",
|
||||
"updates_checked": "Uusi versio AdGuard Home -ohjelmasta on saatavana\n",
|
||||
"updates_version_equal": "AdGuard Home on ajan tasalla",
|
||||
"check_updates_now": "Tarkista päivitykset nyt",
|
||||
"dns_privacy": "DNS-tietosuoja",
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
"form_error_server_name": "Nom de serveur invalide",
|
||||
"form_error_subnet": "Le sous-réseau « {{cidr}} » ne contient pas l'adresse IP « {{ip}} »",
|
||||
"form_error_positive": "Doit être supérieur à 0",
|
||||
"form_error_gateway_ip": "Le bail ne peut pas avoir d'adresse IP de la passerelle",
|
||||
"out_of_range_error": "Doit être hors plage « {{start}} » - « {{end}} »",
|
||||
"lower_range_start_error": "Doit être inférieur au début de plage",
|
||||
"greater_range_start_error": "Doit être supérieur au début de plage",
|
||||
@@ -445,7 +446,7 @@
|
||||
"access_blocked_title": "Domaines interdits",
|
||||
"access_blocked_desc": "A ne pas confondre avec les filtres. AdGuard Home rejette les requêtes DNS correspondant à ces domaines, et ces requêtes n'apparaissent même pas dans le journal des requêtes. Vous pouvez spécifier des noms de domaine exacts, des caractères génériques ou des règles de filtrage d'URL, par exemple « exemple.org », « *.exemple.org » ou « ||example.org^ » de manière correspondante.",
|
||||
"access_settings_saved": "Paramètres d'accès enregistrés avec succès",
|
||||
"updates_checked": "Mises à jour vérifiées",
|
||||
"updates_checked": "Une nouvelle version de AdGuard Home est disponible",
|
||||
"updates_version_equal": "AdGuard Home est à jour",
|
||||
"check_updates_now": "Vérifier les mises à jour",
|
||||
"dns_privacy": "Confidentialité DNS",
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
"form_error_server_name": "Nevažeće ime poslužitelja",
|
||||
"form_error_subnet": "Podmrežu \"{{cidr}}\" ne sadrži IP adresu \"{{ip}}\"",
|
||||
"form_error_positive": "Mora biti veće od 0",
|
||||
"form_error_gateway_ip": "Najam ne može imati IP adresu pristupnika",
|
||||
"out_of_range_error": "Mora biti izvan ranga \"{{start}}\"-\"{{end}}\"",
|
||||
"lower_range_start_error": "Mora biti niže od početnog ranga",
|
||||
"greater_range_start_error": "Mora biti veće od krajnjeg ranga",
|
||||
@@ -445,7 +446,7 @@
|
||||
"access_blocked_title": "Nedopuštene domene",
|
||||
"access_blocked_desc": "Ne smije se miješati s filterima. AdGuard Home ispušta DNS upite koji odgovaraju tim domenama, a ti se upiti čak i ne pojavljuju u zapisniku upita. Možete navesti točne nazive domena, zamjenske znakove ili pravila filtriranja URL-a, npr || example.org example.org. example.org^\" u skladu s tim.",
|
||||
"access_settings_saved": "Postavke pristupa su uspješno spremljene",
|
||||
"updates_checked": "Uspješna provjera ažuriranja",
|
||||
"updates_checked": "Dostupna je nova verzija AdGuard Home-a",
|
||||
"updates_version_equal": "AdGuard Home je ažuriran",
|
||||
"check_updates_now": "Provjeri ažuriranja sada",
|
||||
"dns_privacy": "DNS privatnost",
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
"form_error_server_name": "Érvénytelen szervernév",
|
||||
"form_error_subnet": "A(z) \"{{cidr}}\" alhálózat nem tartalmazza a(z) \"{{ip}}\" IP címet",
|
||||
"form_error_positive": "0-nál nagyobbnak kell lennie",
|
||||
"form_error_gateway_ip": "A bérleti szerződés nem tartalmazhatja az átjáró IP-címét",
|
||||
"out_of_range_error": "A következő tartományon kívül legyen: \"{{start}}\"-\"{{end}}\"",
|
||||
"lower_range_start_error": "Kisebb legyen, mint a tartomány kezdete",
|
||||
"greater_range_start_error": "Nagyobbnak kell lennie, mint a tartomány kezdete",
|
||||
@@ -445,7 +446,7 @@
|
||||
"access_blocked_title": "Nem engedélyezett domainek",
|
||||
"access_blocked_desc": "Ne keverje össze ezt a szűrőkkel. Az AdGuard Home az összes DNS kérést el fogja dobni, ami ezekkel a domainekkel megegyezik, és ezek a lekérések nem is fognak megjelenni a lekérdezési naplóban sem. Megadhatja a pontos domain neveket, a helyettesítő karaktereket vagy az URL szűrési szabályokat, pl. ennek megfelelően \"example.org\", \"*.example.org\", vagy \"||example.org^\".",
|
||||
"access_settings_saved": "A hozzáférési beállítások sikeresen mentésre kerültek",
|
||||
"updates_checked": "A frissítések sikeresen ellenőrizve lettek",
|
||||
"updates_checked": "Elérhető az AdGuard Home új verziója",
|
||||
"updates_version_equal": "Az AdGuard Home naprakész",
|
||||
"check_updates_now": "Frissítések ellenőrzése most",
|
||||
"dns_privacy": "DNS Adatvédelem",
|
||||
|
||||
@@ -445,7 +445,7 @@
|
||||
"access_blocked_title": "Domain yang diblokir",
|
||||
"access_blocked_desc": "Jangan bingung dengan filter. AdGuard Home menghapus kueri DNS yang cocok dengan domain ini, dan kueri ini bahkan tidak muncul di log kueri. Anda dapat menentukan nama domain, karakter pengganti, atau aturan filter URL yang tepat, mis. \"example.org\", \"*.example.org\", atau \"||example.org^\" yang sesuai.",
|
||||
"access_settings_saved": "Pengaturan akses berhasil disimpan",
|
||||
"updates_checked": "Pembaruan berhasil dicek",
|
||||
"updates_checked": "Versi baru AdGuard Home tersedia\n",
|
||||
"updates_version_equal": "AdGuard Home sudah tebaru",
|
||||
"check_updates_now": "Periksa pembaruan sekarang",
|
||||
"dns_privacy": "DNS Privasi",
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
"form_error_server_name": "Nome server non valido",
|
||||
"form_error_subnet": "Il subnet \"{{cidr}}\" non contiene l'indirizzo IP \"{{ip}}\"",
|
||||
"form_error_positive": "Deve essere maggiore di 0",
|
||||
"form_error_gateway_ip": "Il leasing non può avere l'indirizzo IP del gateway",
|
||||
"out_of_range_error": "Deve essere fuori intervallo \"{{start}}\"-\"{{end}}\"",
|
||||
"lower_range_start_error": "Deve essere inferiore dell'intervallo di inizio",
|
||||
"greater_range_start_error": "Deve essere maggiore dell'intervallo di inizio",
|
||||
@@ -445,7 +446,7 @@
|
||||
"access_blocked_title": "Domini bloccati",
|
||||
"access_blocked_desc": "Da non confondere con i filtri. AdGuard Home eliminerà le richieste DNS corrispondenti a questi domini e queste richieste non verranno visualizzate nel relativo registro. Puoi specificare nomi di dominio esatti, caratteri jolly o regole di filtraggio URL, ad esempio \"esempio.org\", \"*.esempio.org\" o \"||esempio.org^\".",
|
||||
"access_settings_saved": "Impostazioni di accesso salvate correttamente",
|
||||
"updates_checked": "Verifica aggiornamenti riuscita",
|
||||
"updates_checked": "Nuova versione di AdGuard Home è disponibile",
|
||||
"updates_version_equal": "AdGuard Home è aggiornato",
|
||||
"check_updates_now": "Ricerca aggiornamenti ora",
|
||||
"dns_privacy": "Privacy DNS",
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
"form_error_server_name": "サーバー名が無効です",
|
||||
"form_error_subnet": "IPアドレス「{{ip}}」がサブネット「{{cidr}}」に含まれていません",
|
||||
"form_error_positive": "0より大きい値でなければなりません",
|
||||
"form_error_gateway_ip": "リースはゲートウェイのIPアドレスになっていることができません",
|
||||
"out_of_range_error": "\"{{start}}\"〜\"{{end}}\" の範囲外である必要があります",
|
||||
"lower_range_start_error": "範囲開始よりも低い値である必要があります",
|
||||
"greater_range_start_error": "範囲開始値より大きい値でなければなりません",
|
||||
@@ -445,7 +446,7 @@
|
||||
"access_blocked_title": "拒否するドメイン",
|
||||
"access_blocked_desc": "こちらをフィルタと混同しないでください。AdGuard Homeは、ここで入力されたドメインに一致するDNSクエリをドロップし、そういったクエリはクエリログにも表示されません。ここでは、「example.org」、「*.example.org」、「 ||example.org^ 」など、特定のドメイン名、ワイルドカード、URLフィルタルールを入力できます。",
|
||||
"access_settings_saved": "アクセス設定の保存に成功しました",
|
||||
"updates_checked": "アップデートの確認に成功しました",
|
||||
"updates_checked": "AdGuard Homeの新バージョンが利用可能です。",
|
||||
"updates_version_equal": "AdGuard Homeは既に最新です",
|
||||
"check_updates_now": "今すぐアップデートを確認する",
|
||||
"dns_privacy": "DNSプライバシー",
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
"form_error_server_name": "유효하지 않은 서버 이름",
|
||||
"form_error_subnet": "서브넷 \"{{cidr}}\"에 \"{{ip}}\" IP 주소가 없습니다",
|
||||
"form_error_positive": "0보다 커야 합니다",
|
||||
"form_error_gateway_ip": "임대는 게이트웨이의 IP 주소를 가질 수 없습니다",
|
||||
"out_of_range_error": "\"{{start}}\"-\"{{end}}\" 범위 밖이어야 합니다",
|
||||
"lower_range_start_error": "범위 시작보다 작은 값이어야 합니다",
|
||||
"greater_range_start_error": "범위 시작보다 큰 값이어야 합니다",
|
||||
@@ -445,7 +446,7 @@
|
||||
"access_blocked_title": "차단된 도메인",
|
||||
"access_blocked_desc": "이 기능을 필터와 혼동하지 마세요. AdGuard Home은 이 도메인에 대한 DNS 요청을 무시합니다. 여기에서는 'example.org' '*. example.org', '|| example.org ^'와 같은 특정 도메인 이름, 와일드 카드, URL 필터 규칙을 지정할 수 있습니다.",
|
||||
"access_settings_saved": "액세스 설정이 성공적으로 저장되었습니다.",
|
||||
"updates_checked": "업데이트가 성공적으로 확인되었습니다",
|
||||
"updates_checked": "AdGuard Home의 새 버전을 사용할 수 있습니다",
|
||||
"updates_version_equal": "AdGuard Home 최신 상태입니다.",
|
||||
"check_updates_now": "지금 업데이트 확인",
|
||||
"dns_privacy": "DNS 프라이버시",
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
"form_error_server_name": "Ongeldige servernaam",
|
||||
"form_error_subnet": "Subnet “{{cidr}}” bevat niet het IP-adres “{{ip}}”",
|
||||
"form_error_positive": "Moet groter zijn dan 0",
|
||||
"form_error_gateway_ip": "Lease kan niet het IP-adres van de gateway hebben",
|
||||
"out_of_range_error": "Moet buiten bereik zijn \"{{start}}\"-\"{{end}}\"",
|
||||
"lower_range_start_error": "Moet lager zijn dan begin reeks",
|
||||
"greater_range_start_error": "Moet groter zijn dan begin reeks",
|
||||
@@ -408,7 +409,7 @@
|
||||
"manual_update": "<a>Volg deze stappen</a> om handmatig bij te werken.",
|
||||
"processing_update": "Even geduld, AdGuard Home wordt bijgewerkt",
|
||||
"clients_title": "Permanente clients",
|
||||
"clients_desc": "Permanente client-records configureren voor apparaten verboden met AdGuard Home",
|
||||
"clients_desc": "Permanente client-records configureren voor apparaten verbonden met AdGuard Home",
|
||||
"settings_global": "Globaal",
|
||||
"settings_custom": "Aangepast",
|
||||
"table_client": "Gebruiker",
|
||||
@@ -445,7 +446,7 @@
|
||||
"access_blocked_title": "Niet toegelaten domeinen",
|
||||
"access_blocked_desc": "Verwar dit niet met filters. AdGuard Home zal deze DNS-zoekopdrachten niet uitvoeren die deze domeinen in de zoekopdracht bevatten. Hier kan je de exacte domeinnamen, wildcards en URL-filter-regels specifiëren, bijv. \"example.org\", \"*.example.org\" of \"||example.org^\".",
|
||||
"access_settings_saved": "Toegangsinstellingen succesvol opgeslagen",
|
||||
"updates_checked": "Met succes op updates gecontroleerd",
|
||||
"updates_checked": "Een nieuwe versie van AdGuard Home is beschikbaar\n",
|
||||
"updates_version_equal": "AdGuard Home is actueel",
|
||||
"check_updates_now": "Controleer op updates",
|
||||
"dns_privacy": "DNS Privacy",
|
||||
|
||||
@@ -104,7 +104,7 @@
|
||||
"stats_adult": "Blokkerte voksennettsteder",
|
||||
"stats_query_domain": "Mest forespurte domener",
|
||||
"for_last_24_hours": "de siste 24 timene",
|
||||
"for_last_days": "det siste døgnet",
|
||||
"for_last_days": "for den siste {{count}} dagen",
|
||||
"for_last_days_plural": "de siste {{count}} dagene",
|
||||
"stats_disabled": "Statistikkene har blitt skrudd av. Du kan skru den på fra <0>innstillingssiden</0>.",
|
||||
"stats_disabled_short": "Statistikkene har blitt skrudd av",
|
||||
@@ -114,7 +114,7 @@
|
||||
"top_clients": "Vanligste klienter",
|
||||
"no_clients_found": "Ingen klienter ble funnet",
|
||||
"general_statistics": "Generelle statistikker",
|
||||
"number_of_dns_query_days": "Antall DNS-forespørsler som ble behandlet det siste døgnet",
|
||||
"number_of_dns_query_days": "Antall DNS-spørringer behandlet for de siste {{count}} dagene",
|
||||
"number_of_dns_query_days_plural": "Antall DNS-forespørsler som ble behandlet de siste {{count}} dagene",
|
||||
"number_of_dns_query_24_hours": "Antall DNS-forespørsler som ble behandlet de siste 24 timene",
|
||||
"number_of_dns_query_blocked_24_hours": "Antall DNS-forespørsler som ble blokkert av adblock-filtre, hosts-lister, og domene-lister",
|
||||
@@ -429,7 +429,7 @@
|
||||
"access_blocked_title": "Blokkerte domener",
|
||||
"access_blocked_desc": "Ikke forveksle dette med filtre. AdGuard Home vil nekte å behandle DNS-forespørsler som har disse domenene, og disse forespørslene dukker ikke engang opp i forespørselsloggen. Du kan spesifisere nøyaktige domene navn, jokertegn, eller URL-filterregler, f.eks. «example.org», «*.example.log» eller «||example.org^» derav.",
|
||||
"access_settings_saved": "Tilgangsinnstillingene ble vellykket lagret",
|
||||
"updates_checked": "Oppdateringene ble vellykket sett etter",
|
||||
"updates_checked": "En ny versjon av AdGuard Home er tilgjengelig",
|
||||
"updates_version_equal": "AdGuard Home er fullt oppdatert",
|
||||
"check_updates_now": "Se etter oppdateringer nå",
|
||||
"dns_privacy": "DNS-privatliv",
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
"form_error_server_name": "Nieprawidłowa nazwa serwera",
|
||||
"form_error_subnet": "Podsieć \"{{cidr}}\" nie zawiera adresu IP \"{{ip}}\"",
|
||||
"form_error_positive": "Musi być większa niż 0",
|
||||
"form_error_gateway_ip": "Lease nie może mieć adresu IP bramy",
|
||||
"out_of_range_error": "Musi być spoza zakresu \"{{start}}\"-\"{{end}}\"",
|
||||
"lower_range_start_error": "Musi być niższy niż początek zakresu",
|
||||
"greater_range_start_error": "Musi być większy niż początek zakresu",
|
||||
@@ -117,7 +118,7 @@
|
||||
"stats_adult": "Zablokowane witryny dla dorosłych",
|
||||
"stats_query_domain": "Najczęściej wyszukiwane domeny",
|
||||
"for_last_24_hours": "przez ostatnie 24 godziny",
|
||||
"for_last_days": "z ostatniego dnia",
|
||||
"for_last_days": "za ostatni dzień {{count}}",
|
||||
"for_last_days_plural": "z ostatnich {{count}} dni",
|
||||
"stats_disabled": "Statystyki zostały wyłączone. Można je włączyć na <0>stronie ustawień</0>.",
|
||||
"stats_disabled_short": "Statystyki zostały wyłączone",
|
||||
@@ -445,7 +446,7 @@
|
||||
"access_blocked_title": "Niedozwolone domeny",
|
||||
"access_blocked_desc": "Nie należy ich mylić z filtrami. AdGuard Home usuwa zapytania DNS pasujące do tych domen, a zapytania te nie pojawiają się nawet w dzienniku zapytań. Możesz określić dokładne nazwy domen, symbole wieloznaczne lub reguły filtrowania adresów URL, np. \"example.org\", \"*.example.org\" lub \"||example.org^\".",
|
||||
"access_settings_saved": "Ustawienia dostępu zostały pomyślnie zapisane",
|
||||
"updates_checked": "Aktualizacje pomyślnie sprawdzone",
|
||||
"updates_checked": "Dostępna jest nowa wersja programu AdGuard Home\n",
|
||||
"updates_version_equal": "AdGuard Home jest aktualny",
|
||||
"check_updates_now": "Sprawdź aktualizacje teraz",
|
||||
"dns_privacy": "Prywatny DNS",
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
"form_error_server_name": "Nome de servidor inválido",
|
||||
"form_error_subnet": "A sub-rede \"{{cidr}}\" não contém o endereço IP \"{{ip}}\"",
|
||||
"form_error_positive": "Deve ser maior que 0",
|
||||
"form_error_gateway_ip": "A concessão não pode ter o endereço IP do gateway",
|
||||
"out_of_range_error": "Deve estar fora do intervalo \"{{start}}\"-\"{{end}}\"",
|
||||
"lower_range_start_error": "Deve ser inferior ao início do intervalo",
|
||||
"greater_range_start_error": "Deve ser maior que o início do intervalo",
|
||||
@@ -445,7 +446,7 @@
|
||||
"access_blocked_title": "Domínios bloqueados",
|
||||
"access_blocked_desc": "Não deve ser confundido com filtros. O AdGuard Home elimina as consultas DNS que correspondem a esses domínios, e essas consultas nem aparecem no registro de consultas. Você pode especificar nomes de domínio exatos, caracteres curinga ou regras de filtro de URL, por exemplo \"exemplo.org\", \"*.exemplo.org\", ou \"||exemplo.org^\" correspondentemente.",
|
||||
"access_settings_saved": "Configurações de acesso foram salvas com sucesso",
|
||||
"updates_checked": "Atualizações verificadas com sucesso",
|
||||
"updates_checked": "Uma nova versão do AdGuard Home está disponível\n",
|
||||
"updates_version_equal": "O AdGuard Home está atualizado.",
|
||||
"check_updates_now": "Verificar atualizações",
|
||||
"dns_privacy": "Privacidade de DNS",
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
"form_error_server_name": "Nome de servidor inválido",
|
||||
"form_error_subnet": "A sub-rede \"{{cidr}}\" não contém o endereço IP \"{{ip}}\"",
|
||||
"form_error_positive": "Deve ser maior que 0",
|
||||
"form_error_gateway_ip": "A concessão não pode ter o endereço IP do gateway",
|
||||
"out_of_range_error": "Deve estar fora do intervalo \"{{start}}\"-\"{{end}}\"",
|
||||
"lower_range_start_error": "Deve ser inferior ao início do intervalo",
|
||||
"greater_range_start_error": "Deve ser maior que o início do intervalo",
|
||||
@@ -445,7 +446,7 @@
|
||||
"access_blocked_title": "Domínios bloqueados",
|
||||
"access_blocked_desc": "Não deve ser confundido com filtros. O AdGuard Home elimina as consultas DNS que correspondem a esses domínios, e essas consultas nem aparecem no registro de consultas. Você pode especificar nomes de domínio exatos, caracteres curinga ou regras de filtro de URL, por exemplo \"exemplo.org\", \"*.exemplo.org\", ou \"||exemplo.org^\" correspondentemente.",
|
||||
"access_settings_saved": "Definições de acesso foram guardadas com sucesso",
|
||||
"updates_checked": "Atualizações verificadas com sucesso",
|
||||
"updates_checked": "Uma nova versão do AdGuard Home está disponível\n",
|
||||
"updates_version_equal": "O AdGuard Home está atualizado",
|
||||
"check_updates_now": "Verificar atualizações",
|
||||
"dns_privacy": "Privacidade de DNS",
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
"form_error_server_name": "Nume de server nevalid",
|
||||
"form_error_subnet": "Subrețeaua „{{cidr}}” nu conține adresa IP „{{ip}}”",
|
||||
"form_error_positive": "Trebuie să fie mai mare de 0",
|
||||
"form_error_gateway_ip": "Locația nu poate avea adresa IP a gateway-ului",
|
||||
"out_of_range_error": "Trebuie să fie în afara intervalului „{{start}}”-„{{end}}”",
|
||||
"lower_range_start_error": "Trebuie să fie mai mică decât începutul intervalului",
|
||||
"greater_range_start_error": "Trebuie să fie mai mare decât începutul intervalului",
|
||||
@@ -445,7 +446,7 @@
|
||||
"access_blocked_title": "Domenii blocate",
|
||||
"access_blocked_desc": "A nu se confunda cu filtrele. AdGuard Home respinge cererile DNS pentru aceste domenii, iar aceste cereri nici măcar nu apar în jurnalul de solicitări. Puteți specifica nume exacte de domenii, metacaractere sau reguli de filtrare URL, cum ar fi \"example.org\", \"*.exemple.org\" sau \"||example.org^\" în mod corespunzător.",
|
||||
"access_settings_saved": "Setările de acces au fost salvate cu succes",
|
||||
"updates_checked": "Actualizările au fost verificate cu succes",
|
||||
"updates_checked": "Este disponibilă o nouă versiune de AdGuard Home\n",
|
||||
"updates_version_equal": "AdGuard Home este la zi",
|
||||
"check_updates_now": "Verificați actualizările acum",
|
||||
"dns_privacy": "Confidențialitate DNS",
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
"form_error_server_name": "Некорректное имя сервера",
|
||||
"form_error_subnet": "Подсеть «{{cidr}}» не содержит IP-адрес «{{ip}}»",
|
||||
"form_error_positive": "Должно быть больше 0",
|
||||
"form_error_gateway_ip": "Аренда не может иметь IP-адрес шлюза",
|
||||
"out_of_range_error": "Должно быть вне диапазона «{{start}}»-«{{end}}»",
|
||||
"lower_range_start_error": "Должно быть меньше начала диапазона",
|
||||
"greater_range_start_error": "Должно быть больше начала диапазона",
|
||||
@@ -66,7 +67,7 @@
|
||||
"ip": "IP-адрес",
|
||||
"dhcp_table_hostname": "Имя хоста",
|
||||
"dhcp_table_expires": "Истекает",
|
||||
"dhcp_warning": "Если вы все равно хотите включить DHCP-сервер, убедитесь, что в сети больше нет активных DHCP-серверов. Иначе это может сломать доступ в сеть для подключённых устройств!",
|
||||
"dhcp_warning": "Если вы всё равно хотите включить DHCP-сервер, убедитесь, что в сети больше нет активных DHCP-серверов. Иначе это может сломать доступ в сеть для подключённых устройств!",
|
||||
"dhcp_error": "AdGuard Home не смог определить присутствие других DHCP-серверов в сети",
|
||||
"dhcp_static_ip_error": "Чтобы использовать DHCP-сервер, должен быть установлен статический IP-адрес. AdGuard Home не смог определить, использует ли этот сетевой интерфейс статический IP-адрес. Пожалуйста, установите его вручную.",
|
||||
"dhcp_dynamic_ip_found": "Ваша система использует динамический IP-адрес для интерфейса <0>{{interfaceName}}</0>. Чтобы использовать DHCP-сервер, необходимо установить статический IP-адрес. Ваш текущий IP-адрес – <0>{{ipAddress}}</0>. Мы автоматически установим его как статический, если вы нажмёте кнопку «Включить DHCP-сервер».",
|
||||
@@ -163,8 +164,8 @@
|
||||
"apply_btn": "Применить",
|
||||
"disabled_filtering_toast": "Фильтрация выкл.",
|
||||
"enabled_filtering_toast": "Фильтрация вкл.",
|
||||
"disabled_safe_browsing_toast": "Антифишинг отключен",
|
||||
"enabled_safe_browsing_toast": "Антифишинг включен",
|
||||
"disabled_safe_browsing_toast": "Антифишинг отключён",
|
||||
"enabled_safe_browsing_toast": "Антифишинг включён",
|
||||
"disabled_parental_toast": "Родительский контроль выкл.",
|
||||
"enabled_parental_toast": "Родительский контроль вкл.",
|
||||
"disabled_safe_search_toast": "Безопасный поиск выкл.",
|
||||
@@ -179,7 +180,7 @@
|
||||
"edit_table_action": "Редактировать",
|
||||
"delete_table_action": "Удалить",
|
||||
"elapsed": "Затрачено",
|
||||
"filters_and_hosts_hint": "AdGuard Home распознает базовые правила блокировки и синтаксис файлов hosts.",
|
||||
"filters_and_hosts_hint": "AdGuard Home распознаёт базовые правила блокировки и синтаксис файлов hosts.",
|
||||
"no_blocklist_added": "Чёрные списки не добавлены",
|
||||
"no_whitelist_added": "Белые списки не добавлены",
|
||||
"add_blocklist": "Добавить чёрный список",
|
||||
@@ -328,10 +329,10 @@
|
||||
"install_submit_title": "Поздравляем!",
|
||||
"install_submit_desc": "Настройка завершена, AdGuard Home готов к использованию.",
|
||||
"install_devices_router": "Роутер",
|
||||
"install_devices_router_desc": "Эта настройка покроет все устройства, подключенные к вашему домашнему роутеру, и вам не нужно будет настраивать каждое вручную.",
|
||||
"install_devices_router_desc": "Эта настройка покроет все устройства, подключённые к вашему домашнему роутеру, и вам не нужно будет настраивать каждое вручную.",
|
||||
"install_devices_address": "DNS-сервер AdGuard Home доступен по следующим адресам",
|
||||
"install_devices_router_list_1": "Откройте настройки вашего роутера. Обычно вы можете открыть их в вашем браузере, например, http://192.168.0.1/ или http://192.168.1.1/. Вас могут попросить ввести пароль. Если вы не помните его, пароль часто можно сбросить, нажав на кнопку на самом роутере, но помните, что эта процедура может привести к потере всей конфигурации роутера. Если вашему роутеру необходимо приложение для настройки, установите его на свой телефон или ПК и воспользуйтесь им для настройки роутера.",
|
||||
"install_devices_router_list_2": "Найдите настройки DHCP или DNS. Найдите буквы «DNS» рядом с текстовым полем, в которое можно ввести два или три ряда цифр, разделенных на 4 группы от одной до трёх цифр.",
|
||||
"install_devices_router_list_2": "Найдите настройки DHCP или DNS. Найдите буквы «DNS» рядом с текстовым полем, в которое можно ввести два или три ряда цифр, разделённых на 4 группы от одной до трёх цифр.",
|
||||
"install_devices_router_list_3": "Введите туда адрес вашего AdGuard Home.",
|
||||
"install_devices_router_list_4": "Вы не можете установить собственный DNS-сервер на некоторых типах маршрутизаторов. В этом случае может помочь настройка AdGuard Home в качестве <0>DHCP-сервера</0>. В противном случае вам следует обратиться к руководству по настройке DNS-серверов для вашей конкретной модели маршрутизатора.",
|
||||
"install_devices_windows_list_1": "Откройте Панель управления через меню «Пуск» или через поиск Windows.",
|
||||
@@ -445,7 +446,7 @@
|
||||
"access_blocked_title": "Неразрешённые домены",
|
||||
"access_blocked_desc": "Не путать с фильтрами. AdGuard Home будет игнорировать DNS-запросы с этими доменами. Здесь вы можете уточнить точные имена доменов, шаблоны, правила URL-фильтрации, например, «example.org», «*.example.org» или «||example.org».",
|
||||
"access_settings_saved": "Настройки доступа успешно сохранены",
|
||||
"updates_checked": "Проверка обновлений прошла успешно",
|
||||
"updates_checked": "Доступна новая версия AdGuard Home",
|
||||
"updates_version_equal": "Версия AdGuard Home актуальна",
|
||||
"check_updates_now": "Проверить обновления",
|
||||
"dns_privacy": "Зашифрованный DNS",
|
||||
@@ -589,7 +590,7 @@
|
||||
"validated_with_dnssec": "Подтверждено с помощью DNSSEC",
|
||||
"all_queries": "Все запросы",
|
||||
"show_blocked_responses": "Заблокировано",
|
||||
"show_whitelisted_responses": "В белом списке",
|
||||
"show_whitelisted_responses": "Разрешённые",
|
||||
"show_processed_responses": "Обработан",
|
||||
"blocked_safebrowsing": "Заблокировано согласно базе данных Safe Browsing",
|
||||
"blocked_adult_websites": "Заблокировано Родительским контролем",
|
||||
|
||||
@@ -335,7 +335,7 @@
|
||||
"encryption_dot": "TLS-හරහා-ව.නා.ප. තොට",
|
||||
"encryption_dot_desc": "මෙම තොට වින්යාසගත කර ඇත්නම්, ඇඩ්ගාර්ඩ් හෝම් විසින් මෙම කවුළුව හරහා TLS-හරහා-ව.නා.ප. සේවාදායකයක් ධාවනය කරනු ඇත.",
|
||||
"encryption_doq": "QUIC-හරහා-ව.නා.ප. තොට",
|
||||
"encryption_doq_desc": "මෙම තොට වින්යාසගත කර ඇත්නම්, ඇඩ්ගාර්ඩ් හෝම් විසින් මෙම තොට හරහා QUIC-හරහා-ව.නා.ප. සේවාදායකයක් ධාවනය කරනු ඇත. එය පරීක්ෂාත්මක වන අතර විශ්වාසදායක නොවිය හැකිය. එසේම, මේ වන විට එයට සහාය දක්වන බොහෝ අනුග්රාහක නැත.",
|
||||
"encryption_doq_desc": "මෙම තොට වින්යාසගත කර ඇත්නම්, ඇඩ්ගාර්ඩ් හෝම් විසින් මෙම තොට හරහා QUIC-හරහා-ව.නා.ප. සේවාදායකයක් ධාවනය කරනු ඇත.",
|
||||
"encryption_certificates": "සහතික",
|
||||
"encryption_certificates_input": "ඔබගේ PEM-කේතනය කළ සහතික පිටපත් කර මෙහි අලවන්න.",
|
||||
"encryption_status": "තත්වය",
|
||||
@@ -406,7 +406,7 @@
|
||||
"access_disallowed_desc": "CIDR හෝ අ.ජා. කෙ. ලිපින ලැයිස්තුවක් වින්යාසගත කර ඇත්නම්, ඇඩ්ගාර්ඩ් හෝම් විසින් එම අ.ජා. කෙ. ලිපින වලින් ඉල්ලීම් අත්හරිනු ඇත.",
|
||||
"access_blocked_title": "නොඉඩ ලත් වසම්",
|
||||
"access_settings_saved": "ප්රවේශ වීමේ සැකසුම් සාර්ථකව සුරකින ලදි",
|
||||
"updates_checked": "යාවත්කාල සාර්ථකව පරික්ෂා කෙරිණි",
|
||||
"updates_checked": "ඇඩ්ගාර්ඩ් හෝම් හි නව අනුවාදයක් තිබේ",
|
||||
"updates_version_equal": "ඇඩ්ගාර්ඩ් හෝම් යාවත්කාලීනයි",
|
||||
"check_updates_now": "දැන් යාවත්කාල පරීක්ෂා කරන්න",
|
||||
"dns_privacy": "ව.නා.ප. රහස්යතා",
|
||||
@@ -414,12 +414,12 @@
|
||||
"setup_dns_privacy_android_2": "<1>HTTPS-හරහා-ව.නා.ප.</1> සහ <1>TLS-හරහා-ව.නා.ප.</1> සඳහා <0>ඇන්ඩ්රොයිඩ් සඳහා ඇඩ්ගාර්ඩ්</0> සහාය දක්වයි.",
|
||||
"setup_dns_privacy_other_title": "වෙනත් ක්රියාවට නැංවූ දෑ",
|
||||
"setup_dns_privacy_other_2": "<0>ඩීඑන්එස්ප්රොක්සි</0> දන්නා සියලුම ආරක්ෂිත ව.නා.ප. කෙටුම්පත් සඳහා සහාය දක්වයි.",
|
||||
"setup_dns_privacy_other_3": "<1>DNS-over-HTTPS</1> සඳහා <0>dnscrypt-පෙරකලාසිය</0> සහාය දක්වයි.",
|
||||
"setup_dns_privacy_other_4": "<1>DNS-over-HTTPS</1> සඳහා <0>මොසිල්ලා ෆයර්ෆොක්ස්</0> සහාය දක්වයි.",
|
||||
"setup_dns_privacy_other_5": "<0>මෙහි</0> සහ <1>මෙහි</1> තවත් ක්රියාවට නැංවූ දෑ ඔබට හමුවනු ඇත.",
|
||||
"setup_dns_privacy_ioc_mac": "අයිඕඑස් සහ මැක්ඕඑස් වින්යාසය",
|
||||
"setup_dns_notice": "ඔබට <1>DNS-over-HTTPS</1> හෝ <1>DNS-over-TLS</1> භාවිතයට ඇඩ්ගාර්ඩ් හෝම් සැකසුම් තුළ <0>සංකේතනය වින්යාසගත</0> කිරීමට ඇවැසිය.",
|
||||
"rewrite_added": "\"{{key}}\" සඳහා ව.නා.ප. නැවත ලිවීම සාර්ථකව එකතු කෙරිණි",
|
||||
"rewrite_deleted": "\"{{key}}\" සඳහා ව. නා. ප. නැවත ලිවීම සාර්ථකව ඉවත් කෙරිණි",
|
||||
"rewrite_add": "ව.නා.ප. නැවත ලිවීමක් එකතු කරන්න",
|
||||
"rewrite_not_found": "ව.නා.ප. නැවත ලිවීම් හමු නොවිණි",
|
||||
"rewrite_confirm_delete": "\"{{key}}\" සඳහා ව.නා.ප. නැවත ලිවීම ඉවත් කිරීමට අවශ්ය බව ඔබට විශ්වාසද?",
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
"form_error_server_name": "Neplatné meno servera",
|
||||
"form_error_subnet": "Podsieť \"{{cidr}}\" neobsahuje IP adresu \"{{ip}}\"",
|
||||
"form_error_positive": "Musí byť väčšie ako 0",
|
||||
"form_error_gateway_ip": "Prenájom nemôže mať IP adresu brány",
|
||||
"out_of_range_error": "Musí byť mimo rozsahu \"{{start}}\"-\"{{end}}\"",
|
||||
"lower_range_start_error": "Musí byť nižšie ako začiatok rozsahu",
|
||||
"greater_range_start_error": "Musí byť väčšie ako začiatok rozsahu",
|
||||
@@ -445,7 +446,7 @@
|
||||
"access_blocked_title": "Nepovolené domény",
|
||||
"access_blocked_desc": "Nesmie byť zamieňaná s filtrami. AdGuard Home zruší DNS dopyty, ktoré sa zhodujú s týmito doménami, a tieto dopyty sa nezobrazia ani v denníku dopytov. Môžete určiť presné názvy domén, zástupné znaky alebo pravidlá filtrovania URL adries, napr. \"example.org\", \"*.example.org\" alebo ||example.org^\" zodpovedajúcim spôsobom.",
|
||||
"access_settings_saved": "Nastavenia prístupu úspešne uložené",
|
||||
"updates_checked": "Aktualizácie úspešne skontrolované",
|
||||
"updates_checked": "K dispozícii je nová verzia aplikácie AdGuard Home\n",
|
||||
"updates_version_equal": "AdGuard Home je aktuálny",
|
||||
"check_updates_now": "Skontrolovať aktualizácie teraz",
|
||||
"dns_privacy": "DNS súkromie",
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
"form_error_server_name": "Neveljavno ime strežnika",
|
||||
"form_error_subnet": "Podomrežje \"{{cidr}}\" ne vsebuje naslova IP \"{{ip}}\"",
|
||||
"form_error_positive": "Mora biti večja od 0",
|
||||
"form_error_gateway_ip": "Najem ne more imeti naslova IP prehoda",
|
||||
"out_of_range_error": "Mora biti izven razpona \"{{start}}\"-\"{{end}}\"",
|
||||
"lower_range_start_error": "Mora biti manjši od začetka razpona",
|
||||
"greater_range_start_error": "Mora biti večji od začetka razpona",
|
||||
@@ -445,7 +446,7 @@
|
||||
"access_blocked_title": "Prepovedane domene",
|
||||
"access_blocked_desc": "Ne gre zamenjati s filtri. AdGuard Home spusti poizvedbe DNS, ki se ujemajo s temi domenami, in te poizvedbe se niti ne pojavijo v dnevniku poizvedb. Določite lahko natančna imena domen, nadomestne znake ali pravila filtriranja URL-jev, npr. ustrezno \"example.org\", \"*.example.org\" ali \"|| example.org ^\".",
|
||||
"access_settings_saved": "Nastavitve dostopa so uspešno shranjene",
|
||||
"updates_checked": "Posodobitve so uspešno preverjene",
|
||||
"updates_checked": "Na voljo je nova različica programa AdGuard Home\n",
|
||||
"updates_version_equal": "AdGuard Home je posodobljen",
|
||||
"check_updates_now": "Preveri obstoj posodobitev zdaj",
|
||||
"dns_privacy": "Zasebnost DNS",
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
"form_error_server_name": "Nevažeće ime servera",
|
||||
"form_error_subnet": "Subnet \"{{cidr}}\" ne sadrži IP adresu \"{{ip}}\"",
|
||||
"form_error_positive": "Mora biti veće od 0",
|
||||
"form_error_gateway_ip": "Zakup ne može imati IP adresu mrežnog prolaza",
|
||||
"out_of_range_error": "Mora biti izvan opsega \"{{start}}\"-\"{{end}}\"",
|
||||
"lower_range_start_error": "Mora biti manje od početnog opsega",
|
||||
"greater_range_start_error": "Mora biti veće od početnog opsega",
|
||||
@@ -445,7 +446,7 @@
|
||||
"access_blocked_title": "Blokirani domeni",
|
||||
"access_blocked_desc": "Da ne bude zabune sa filterima. AdGuard Home odustaje od DNS upita koji se podudaraju sa ovim domenima, a ovi upiti se čak i ne pojavljuju u evidenciji upita. Možete da navedete tačna imena domena, džoker znakove ili pravila URL filtera, npr. \"example.org\", \"*.example.org\" ili \"|| example.org^\" dopisno.",
|
||||
"access_settings_saved": "Postavke pristupa su uspešno sačuvane",
|
||||
"updates_checked": "Ažuriranja su uspešno proverena",
|
||||
"updates_checked": "Dostupna je nova verzija AdGuard Home-a",
|
||||
"updates_version_equal": "AdGuard Home je ažuriran na najnoviju verziju",
|
||||
"check_updates_now": "Proveri da li postoje ispravke",
|
||||
"dns_privacy": "DNS privatnost",
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
"form_error_server_name": "Ogiltigt servernamn",
|
||||
"form_error_subnet": "Subnätet \"{{cidr}}\" innehåller inte IP-adressen \"{{ip}}\"",
|
||||
"form_error_positive": "Måste vara större än noll",
|
||||
"form_error_gateway_ip": "Lease kan inte ha IP-adressen för gatewayen",
|
||||
"out_of_range_error": "Måste vara utanför intervallet \"{{start}}\"-\"{{end}}\"",
|
||||
"lower_range_start_error": "Måste vara lägre än starten på intervallet",
|
||||
"greater_range_start_error": "Måste vara högre än starten på intervallet",
|
||||
@@ -445,7 +446,7 @@
|
||||
"access_blocked_title": "Blockerade domäner",
|
||||
"access_blocked_desc": "Ej att förväxla med filter. AdGuard Home kastar DNS-frågor som matchar dessa domäner, och dessa frågor visas inte ens i frågeloggen. Du kan ange exakta domännamn, jokertecken eller URL-filterregler, t.ex. \"example.org\", \"*.example.org\" eller \"||example.org^\" på motsvarande sätt.",
|
||||
"access_settings_saved": "Åtkomstinställningar sparade",
|
||||
"updates_checked": "Sökning efter uppdateringar genomförd",
|
||||
"updates_checked": "En ny version av AdGuard Home är tillgänglig\n",
|
||||
"updates_version_equal": "AdGuard Home är uppdaterat",
|
||||
"check_updates_now": "Sök efter uppdateringar nu",
|
||||
"dns_privacy": "DNS-Integritet",
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
"form_error_server_name": "Geçersiz sunucu adı",
|
||||
"form_error_subnet": "\"{{cidr}}\" alt ağı, \"{{ip}}\" IP adresini içermiyor",
|
||||
"form_error_positive": "0'dan büyük olmalıdır",
|
||||
"form_error_gateway_ip": "Kiralama, ağ geçidinin IP adresine sahip olamaz",
|
||||
"out_of_range_error": "\"{{start}}\"-\"{{end}}\" aralığının dışında olmalıdır",
|
||||
"lower_range_start_error": "Başlangıç aralığından daha düşük olmalıdır",
|
||||
"greater_range_start_error": "Başlangıç aralığından daha büyük olmalıdır",
|
||||
@@ -108,7 +109,7 @@
|
||||
"privacy_policy": "Gizlilik Politikası",
|
||||
"enable_protection": "Korumayı etkinleştir",
|
||||
"enabled_protection": "Koruma etkileştirildi",
|
||||
"disable_protection": "Korumayı durdur",
|
||||
"disable_protection": "Korumayı devre dışı bırak",
|
||||
"disabled_protection": "Koruma durduruldu",
|
||||
"refresh_statics": "İstatistikleri yenile",
|
||||
"dns_query": "DNS Sorguları",
|
||||
@@ -238,7 +239,7 @@
|
||||
"empty_response_status": "Boş",
|
||||
"show_all_filter_type": "Tümünü göster",
|
||||
"show_filtered_type": "Filtrelenenleri göster",
|
||||
"no_logs_found": "Günlük kaydı bulunamadı",
|
||||
"no_logs_found": "Günlük bulunamadı",
|
||||
"refresh_btn": "Yenile",
|
||||
"previous_btn": "Önceki",
|
||||
"next_btn": "Sonraki",
|
||||
@@ -445,7 +446,7 @@
|
||||
"access_blocked_title": "İzin verilmeyen alan adları",
|
||||
"access_blocked_desc": "Bu işlem filtrelerle ilgili değildir. AdGuard Home, bu alan adlarından gelen DNS sorgularını yanıtsız bırakır ve bu sorgular sorgu günlüğünde görünmez. Tam alan adlarını, joker karakterleri veya URL filtre kurallarını belirtebilirsiniz, ör. \"example.org\", \"*.example.org\" veya \"||example.org^\".",
|
||||
"access_settings_saved": "Erişim ayarları başarıyla kaydedildi!",
|
||||
"updates_checked": "Güncelleme kontrolü başarılı",
|
||||
"updates_checked": "AdGuard Home'un yeni bir sürümü mevcut",
|
||||
"updates_version_equal": "AdGuard Home yazılımı güncel durumda",
|
||||
"check_updates_now": "Güncellemeleri şimdi denetle",
|
||||
"dns_privacy": "DNS Gizliliği",
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
"form_error_server_name": "Неправильна назва сервера",
|
||||
"form_error_subnet": "Підмережа «{{cidr}}» не містить IP-адресу «{{ip}}»",
|
||||
"form_error_positive": "Повинно бути більше за 0",
|
||||
"form_error_gateway_ip": "Оренда не може мати IP-адресу шлюзу",
|
||||
"out_of_range_error": "Не повинна бути в діапазоні «{{start}}»−«{{end}}»",
|
||||
"lower_range_start_error": "Має бути меншим за початкову адресу",
|
||||
"greater_range_start_error": "Має бути більшим за початкову адресу",
|
||||
@@ -77,7 +78,7 @@
|
||||
"dhcp_add_static_lease": "Додати статичну оренду",
|
||||
"dhcp_reset_leases": "Скинути всі аренди",
|
||||
"dhcp_reset_leases_confirm": "Ви дійсно хочете скинути усі аренди?",
|
||||
"dhcp_reset_leases_success": "Аренди DHCP успішно скинуто",
|
||||
"dhcp_reset_leases_success": "Оренду DHCP успішно скинуто",
|
||||
"dhcp_reset": "Ви дійсно хочете скинути DHCP-конфігурацію?",
|
||||
"country": "Країна",
|
||||
"city": "Місто",
|
||||
@@ -105,7 +106,7 @@
|
||||
"copyright": "Авторське право",
|
||||
"homepage": "Домашня сторінка",
|
||||
"report_an_issue": "Повідомити про проблему",
|
||||
"privacy_policy": "Політика приватності",
|
||||
"privacy_policy": "Політика конфіденційності",
|
||||
"enable_protection": "Увімкнути захист",
|
||||
"enabled_protection": "Захист увімкнено",
|
||||
"disable_protection": "Вимкнути захист",
|
||||
@@ -218,7 +219,7 @@
|
||||
"example_upstream_tcp": "звичайний DNS (через TCP);",
|
||||
"example_upstream_tcp_hostname": "звичайний DNS (поверх TCP, з назвою вузла);",
|
||||
"all_lists_up_to_date_toast": "Всі списки вже оновлені",
|
||||
"updated_upstream_dns_toast": "DNS-сервери оновлено",
|
||||
"updated_upstream_dns_toast": "DNS-сервери успішно збережено",
|
||||
"dns_test_ok_toast": "Вказані DNS сервери працюють правильно",
|
||||
"dns_test_not_ok_toast": "Сервер «{{key}}»: неможливо використати. Перевірте правильність введення",
|
||||
"unblock": "Дозволити",
|
||||
@@ -245,7 +246,7 @@
|
||||
"loading_table_status": "Завантаження...",
|
||||
"page_table_footer_text": "Сторінка",
|
||||
"rows_table_footer_text": "рядків",
|
||||
"updated_custom_filtering_toast": "Власні правила фільтрування збережено",
|
||||
"updated_custom_filtering_toast": "Власні правила фільтрування успішно збережено",
|
||||
"rule_removed_from_custom_filtering_toast": "Правило вилучено з власних правил фільтрування: {{rule}}",
|
||||
"rule_added_to_custom_filtering_toast": "Правило додано до власних правил фільтрування: {{rule}}",
|
||||
"query_log_response_status": "Стан: {{value}}",
|
||||
@@ -445,7 +446,7 @@
|
||||
"access_blocked_title": "Заборонені домени",
|
||||
"access_blocked_desc": "Не плутайте з фільтрами. AdGuard Home буде ігнорувати DNS-запити з цими доменами, такі запити навіть не будуть записані до журналу. Ви можете вказати точні доменні імена, замінні знаки та правила фільтрування URL-адрес, наприклад, 'example.org', '*.example.org' або '||example.org^' відповідно.",
|
||||
"access_settings_saved": "Налаштування доступу успішно збережено",
|
||||
"updates_checked": "Оновлення успішно перевірені",
|
||||
"updates_checked": "Доступна нова версія AdGuard Home",
|
||||
"updates_version_equal": "AdGuard Home останньої версії",
|
||||
"check_updates_now": "Перевірити наявність оновлень",
|
||||
"dns_privacy": "Конфіденційність DNS",
|
||||
@@ -514,7 +515,7 @@
|
||||
"statistics_clear": "Очистити статистику",
|
||||
"statistics_clear_confirm": "Ви впевнені, що хочете очистити статистику?",
|
||||
"statistics_retention_confirm": "Ви впевнені, що хочете змінити тривалість статистики? Якщо зменшити значення інтервалу, деякі дані будуть втрачені",
|
||||
"statistics_cleared": "Статистика успішно очищена",
|
||||
"statistics_cleared": "Статистику успішно очищено",
|
||||
"statistics_enable": "Увімкнути статистику",
|
||||
"interval_hours": "{{count}} година",
|
||||
"interval_hours_plural": "{{count}} годин(и)",
|
||||
@@ -591,7 +592,7 @@
|
||||
"show_blocked_responses": "Заблоковані",
|
||||
"show_whitelisted_responses": "Дозволені",
|
||||
"show_processed_responses": "Оброблені",
|
||||
"blocked_safebrowsing": "Заблоковано Безпечним переглядом",
|
||||
"blocked_safebrowsing": "Заблоковано модулем «Безпека перегляду»",
|
||||
"blocked_adult_websites": "Заблоковано «Батьківським контролем»",
|
||||
"blocked_threats": "Заблоковано загроз",
|
||||
"allowed": "Дозволено",
|
||||
|
||||
@@ -220,7 +220,7 @@
|
||||
"all_lists_up_to_date_toast": "Tất cả danh sách đã ở phiên bản mới nhất",
|
||||
"updated_upstream_dns_toast": "Các máy chủ thượng nguồn đã được lưu thành công",
|
||||
"dns_test_ok_toast": "Máy chủ DNS có thể sử dụng",
|
||||
"dns_test_not_ok_toast": "Máy chủ \"\"': không thể sử dụng, vui lòng kiểm tra lại",
|
||||
"dns_test_not_ok_toast": "Máy chủ \"{{key}}\"': không thể sử dụng, vui lòng kiểm tra lại",
|
||||
"unblock": "Bỏ chặn",
|
||||
"block": "Chặn",
|
||||
"disallow_this_client": "Không cho phép client này",
|
||||
@@ -445,7 +445,7 @@
|
||||
"access_blocked_title": "Tên miền bị chặn",
|
||||
"access_blocked_desc": "Đừng nhầm lẫn điều này với các bộ lọc. AdGuard Home sẽ bỏ các truy vấn DNS với các tên miền này trong câu hỏi của truy vấn.",
|
||||
"access_settings_saved": "Cài đặt truy cập đã lưu thành công",
|
||||
"updates_checked": "Đã kiểm tra thành công cập nhật",
|
||||
"updates_checked": "Phiên bản mới của AdGuard Home có sẵn",
|
||||
"updates_version_equal": "AdGuard Home đã được cập nhật",
|
||||
"check_updates_now": "Kiểm tra cập nhật ngay bây giờ",
|
||||
"dns_privacy": "DNS Riêng Tư",
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
"form_error_server_name": "无效的服务器名",
|
||||
"form_error_subnet": "子网 \"{{cidr}}\" 不包含 IP 地址 \"{{ip}}\"",
|
||||
"form_error_positive": "必须大于 0",
|
||||
"form_error_gateway_ip": "租约期限不能有网关的 IP 地址",
|
||||
"out_of_range_error": "必定超出了范围 \"{{start}}\"-\"{{end}}\"",
|
||||
"lower_range_start_error": "必须小于范围起始值",
|
||||
"greater_range_start_error": "必须大于范围起始值",
|
||||
@@ -445,7 +446,7 @@
|
||||
"access_blocked_title": "不允许的域名",
|
||||
"access_blocked_desc": "不要将此功能与过滤器混淆。AdGuard Home 将排除匹配这些网域的 DNS 查询,并且这些查询将不会在查询日志中显示。在此可以明确指定域名、通配符(wildcard)和网址过滤的规则,例如 \"example.org\"、\"*.example.org\" 或 \"||example.org^\"。",
|
||||
"access_settings_saved": "访问设置保存成功",
|
||||
"updates_checked": "检查更新成功",
|
||||
"updates_checked": "AdGuard Home 的新版本现在可用",
|
||||
"updates_version_equal": "AdGuard Home已经是最新版本",
|
||||
"check_updates_now": "立即检查更新",
|
||||
"dns_privacy": "DNS 隐私",
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
"form_error_server_name": "無效的伺服器名稱",
|
||||
"form_error_subnet": "子網路 \"{{cidr}}\" 不包含該 IP 位址 \"{{ip}}\"",
|
||||
"form_error_positive": "必須大於 0",
|
||||
"form_error_gateway_ip": "租約不能有閘道的 IP 位址",
|
||||
"out_of_range_error": "必須在\"{{start}}\"-\"{{end}}\"範圍之外",
|
||||
"lower_range_start_error": "必須低於起始範圍",
|
||||
"greater_range_start_error": "必須大於起始範圍",
|
||||
@@ -445,7 +446,7 @@
|
||||
"access_blocked_title": "未被允許的網域",
|
||||
"access_blocked_desc": "不要把這個和過濾器混淆。AdGuard Home 排除與這些網域相符的 DNS 查詢,且這些查詢甚至不會出現在查詢記錄中。您可相應地明確指定確切的域名、萬用字元(wildcard)或網址過濾器的規則,例如,\"example.org\"、\"*.example.org\" 或 \"||example.org^\"。",
|
||||
"access_settings_saved": "存取設定被成功地儲存",
|
||||
"updates_checked": "更新被成功地檢查",
|
||||
"updates_checked": "AdGuard Home 的新版本為可用的",
|
||||
"updates_version_equal": "AdGuard Home 為最新的",
|
||||
"check_updates_now": "立即檢查更新",
|
||||
"dns_privacy": "DNS 隱私",
|
||||
@@ -630,6 +631,6 @@
|
||||
"use_saved_key": "使用該先前已儲存的金鑰",
|
||||
"parental_control": "家長控制",
|
||||
"safe_browsing": "安全瀏覽",
|
||||
"served_from_cache": "{{value}} <i>(由快取提供)</i>",
|
||||
"served_from_cache": "{{value}} <i>(由快取提供)</i>",
|
||||
"form_error_password_length": "密碼必須為至少長 {{value}} 個字元"
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ export const getTlsStatus = () => async (dispatch) => {
|
||||
export const setTlsConfigRequest = createAction('SET_TLS_CONFIG_REQUEST');
|
||||
export const setTlsConfigFailure = createAction('SET_TLS_CONFIG_FAILURE');
|
||||
export const setTlsConfigSuccess = createAction('SET_TLS_CONFIG_SUCCESS');
|
||||
export const dnsStatusSuccess = createAction('DNS_STATUS_SUCCESS');
|
||||
|
||||
export const setTlsConfig = (config) => async (dispatch, getState) => {
|
||||
dispatch(setTlsConfigRequest());
|
||||
@@ -39,6 +40,12 @@ export const setTlsConfig = (config) => async (dispatch, getState) => {
|
||||
const response = await apiClient.setTlsConfig(values);
|
||||
response.certificate_chain = atob(response.certificate_chain);
|
||||
response.private_key = atob(response.private_key);
|
||||
|
||||
const dnsStatus = await apiClient.getGlobalStatus();
|
||||
if (dnsStatus) {
|
||||
dispatch(dnsStatusSuccess(dnsStatus));
|
||||
}
|
||||
|
||||
dispatch(setTlsConfigSuccess(response));
|
||||
dispatch(addSuccessToast('encryption_config_saved'));
|
||||
redirectToCurrentProtocol(response, httpPort);
|
||||
|
||||
@@ -29,6 +29,8 @@ class Table extends Component {
|
||||
Header: this.props.t('actions_table_header'),
|
||||
accessor: 'actions',
|
||||
maxWidth: 100,
|
||||
sortable: false,
|
||||
resizable: false,
|
||||
Cell: (value) => (
|
||||
<div className="logs__row logs__row--center">
|
||||
<button
|
||||
|
||||
@@ -36,6 +36,7 @@ class Table extends Component {
|
||||
Cell: this.renderCheckbox,
|
||||
width: 90,
|
||||
className: 'text-center',
|
||||
resizable: false,
|
||||
},
|
||||
{
|
||||
Header: <Trans>name_table_header</Trans>,
|
||||
@@ -77,12 +78,14 @@ class Table extends Component {
|
||||
},
|
||||
{
|
||||
Header: <Trans>actions_table_header</Trans>,
|
||||
accessor: 'url',
|
||||
accessor: 'actions',
|
||||
className: 'text-center',
|
||||
width: 100,
|
||||
sortable: false,
|
||||
resizable: false,
|
||||
Cell: (row) => {
|
||||
const { value } = row;
|
||||
const { original } = row;
|
||||
const { url } = original;
|
||||
const { t, toggleFilteringModal, handleDelete } = this.props;
|
||||
|
||||
return (
|
||||
@@ -93,7 +96,7 @@ class Table extends Component {
|
||||
title={t('edit_table_action')}
|
||||
onClick={() => toggleFilteringModal({
|
||||
type: MODAL_TYPE.EDIT_FILTERS,
|
||||
url: value,
|
||||
url,
|
||||
})
|
||||
}
|
||||
>
|
||||
@@ -104,7 +107,7 @@ class Table extends Component {
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-icon btn-outline-secondary btn-sm"
|
||||
onClick={() => handleDelete(value)}
|
||||
onClick={() => handleDelete(url)}
|
||||
title={t('delete_table_action')}
|
||||
>
|
||||
<svg className="icons">
|
||||
|
||||
@@ -338,10 +338,19 @@
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.logs__row--icons {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.logs__table .logs__row {
|
||||
border-bottom: 2px solid var(--gray-216);
|
||||
}
|
||||
|
||||
.logs__tag {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* QUERY_STATUS_COLORS */
|
||||
.logs__row--blue {
|
||||
background-color: var(--blue);
|
||||
|
||||
@@ -193,7 +193,7 @@ class ClientsTable extends Component {
|
||||
<div className="logs__row o-hidden">
|
||||
<span className="logs__text">
|
||||
{value.map((tag) => (
|
||||
<div key={tag} title={tag} className="small">
|
||||
<div key={tag} title={tag} className="logs__tag small">
|
||||
{tag}
|
||||
</div>
|
||||
))}
|
||||
@@ -225,6 +225,8 @@ class ClientsTable extends Component {
|
||||
Header: this.props.t('actions_table_header'),
|
||||
accessor: 'actions',
|
||||
maxWidth: 100,
|
||||
sortable: false,
|
||||
resizable: false,
|
||||
Cell: (row) => {
|
||||
const clientName = row.original.name;
|
||||
const {
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
validateMac,
|
||||
validateRequiredValue,
|
||||
validateIpv4InCidr,
|
||||
validateIpGateway,
|
||||
} from '../../../../helpers/validators';
|
||||
import { FORM_NAME } from '../../../../helpers/constants';
|
||||
import { toggleLeaseModal } from '../../../../actions';
|
||||
@@ -57,6 +58,7 @@ const Form = ({
|
||||
validateRequiredValue,
|
||||
validateIpv4,
|
||||
validateIpv4InCidr,
|
||||
validateIpGateway,
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
@@ -101,6 +103,7 @@ Form.propTypes = {
|
||||
ip: PropTypes.string.isRequired,
|
||||
hostname: PropTypes.string.isRequired,
|
||||
cidr: PropTypes.string.isRequired,
|
||||
gatewayIp: PropTypes.string,
|
||||
}),
|
||||
pristine: PropTypes.bool.isRequired,
|
||||
handleSubmit: PropTypes.func.isRequired,
|
||||
|
||||
@@ -13,6 +13,7 @@ const Modal = ({
|
||||
cidr,
|
||||
rangeStart,
|
||||
rangeEnd,
|
||||
gatewayIp,
|
||||
}) => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
@@ -42,6 +43,7 @@ const Modal = ({
|
||||
cidr,
|
||||
rangeStart,
|
||||
rangeEnd,
|
||||
gatewayIp,
|
||||
}}
|
||||
onSubmit={handleSubmit}
|
||||
processingAdding={processingAdding}
|
||||
@@ -61,6 +63,7 @@ Modal.propTypes = {
|
||||
cidr: PropTypes.string.isRequired,
|
||||
rangeStart: PropTypes.string,
|
||||
rangeEnd: PropTypes.string,
|
||||
gatewayIp: PropTypes.string,
|
||||
};
|
||||
|
||||
export default withTranslation()(Modal);
|
||||
|
||||
@@ -24,6 +24,7 @@ const StaticLeases = ({
|
||||
cidr,
|
||||
rangeStart,
|
||||
rangeEnd,
|
||||
gatewayIp,
|
||||
}) => {
|
||||
const [t] = useTranslation();
|
||||
const dispatch = useDispatch();
|
||||
@@ -70,6 +71,8 @@ const StaticLeases = ({
|
||||
Header: <Trans>actions_table_header</Trans>,
|
||||
accessor: 'actions',
|
||||
maxWidth: 150,
|
||||
sortable: false,
|
||||
resizable: false,
|
||||
// eslint-disable-next-line react/display-name
|
||||
Cell: (row) => {
|
||||
const { ip, mac, hostname } = row.original;
|
||||
@@ -104,6 +107,7 @@ const StaticLeases = ({
|
||||
cidr={cidr}
|
||||
rangeStart={rangeStart}
|
||||
rangeEnd={rangeEnd}
|
||||
gatewayIp={gatewayIp}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
@@ -117,6 +121,7 @@ StaticLeases.propTypes = {
|
||||
cidr: PropTypes.string.isRequired,
|
||||
rangeStart: PropTypes.string,
|
||||
rangeEnd: PropTypes.string,
|
||||
gatewayIp: PropTypes.string,
|
||||
};
|
||||
|
||||
cellWrap.propTypes = {
|
||||
|
||||
@@ -278,6 +278,7 @@ const Dhcp = () => {
|
||||
cidr={cidr}
|
||||
rangeStart={dhcp?.values?.v4?.range_start}
|
||||
rangeEnd={dhcp?.values?.v4?.range_end}
|
||||
gatewayIp={dhcp?.values?.v4?.gateway_ip}
|
||||
/>
|
||||
<div className="btn-list mt-2">
|
||||
<button
|
||||
|
||||
@@ -113,6 +113,7 @@
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
background-color: transparent;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.btn-icon--green {
|
||||
|
||||
@@ -339,3 +339,14 @@ export const validatePasswordLength = (value) => {
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param value {string}
|
||||
* @returns {Function}
|
||||
*/
|
||||
export const validateIpGateway = (value, allValues) => {
|
||||
if (value === allValues.gatewayIp) {
|
||||
return i18next.t('form_error_gateway_ip');
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
67
internal/aghalg/nullbool.go
Normal file
67
internal/aghalg/nullbool.go
Normal file
@@ -0,0 +1,67 @@
|
||||
package aghalg
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// NullBool is a nullable boolean. Use these in JSON requests and responses
|
||||
// instead of pointers to bool.
|
||||
type NullBool uint8
|
||||
|
||||
// NullBool values
|
||||
const (
|
||||
NBNull NullBool = iota
|
||||
NBTrue
|
||||
NBFalse
|
||||
)
|
||||
|
||||
// String implements the fmt.Stringer interface for NullBool.
|
||||
func (nb NullBool) String() (s string) {
|
||||
switch nb {
|
||||
case NBNull:
|
||||
return "null"
|
||||
case NBTrue:
|
||||
return "true"
|
||||
case NBFalse:
|
||||
return "false"
|
||||
}
|
||||
|
||||
return fmt.Sprintf("!invalid NullBool %d", uint8(nb))
|
||||
}
|
||||
|
||||
// BoolToNullBool converts a bool into a NullBool.
|
||||
func BoolToNullBool(cond bool) (nb NullBool) {
|
||||
if cond {
|
||||
return NBTrue
|
||||
}
|
||||
|
||||
return NBFalse
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ json.Marshaler = NBNull
|
||||
|
||||
// MarshalJSON implements the json.Marshaler interface for NullBool.
|
||||
func (nb NullBool) MarshalJSON() (b []byte, err error) {
|
||||
return []byte(nb.String()), nil
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ json.Unmarshaler = (*NullBool)(nil)
|
||||
|
||||
// UnmarshalJSON implements the json.Unmarshaler interface for *NullBool.
|
||||
func (nb *NullBool) UnmarshalJSON(b []byte) (err error) {
|
||||
if len(b) == 0 || bytes.Equal(b, []byte("null")) {
|
||||
*nb = NBNull
|
||||
} else if bytes.Equal(b, []byte("true")) {
|
||||
*nb = NBTrue
|
||||
} else if bytes.Equal(b, []byte("false")) {
|
||||
*nb = NBFalse
|
||||
} else {
|
||||
return fmt.Errorf("unmarshalling json data into aghalg.NullBool: bad value %q", b)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
113
internal/aghalg/nullbool_test.go
Normal file
113
internal/aghalg/nullbool_test.go
Normal file
@@ -0,0 +1,113 @@
|
||||
package aghalg_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghalg"
|
||||
"github.com/AdguardTeam/golibs/testutil"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestNullBool_MarshalJSON(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
wantErrMsg string
|
||||
want []byte
|
||||
in aghalg.NullBool
|
||||
}{{
|
||||
name: "null",
|
||||
wantErrMsg: "",
|
||||
want: []byte("null"),
|
||||
in: aghalg.NBNull,
|
||||
}, {
|
||||
name: "true",
|
||||
wantErrMsg: "",
|
||||
want: []byte("true"),
|
||||
in: aghalg.NBTrue,
|
||||
}, {
|
||||
name: "false",
|
||||
wantErrMsg: "",
|
||||
want: []byte("false"),
|
||||
in: aghalg.NBFalse,
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
got, err := tc.in.MarshalJSON()
|
||||
testutil.AssertErrorMsg(t, tc.wantErrMsg, err)
|
||||
|
||||
assert.Equal(t, tc.want, got)
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("json", func(t *testing.T) {
|
||||
in := &struct {
|
||||
A aghalg.NullBool
|
||||
}{
|
||||
A: aghalg.NBTrue,
|
||||
}
|
||||
|
||||
got, err := json.Marshal(in)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, []byte(`{"A":true}`), got)
|
||||
})
|
||||
}
|
||||
|
||||
func TestNullBool_UnmarshalJSON(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
wantErrMsg string
|
||||
data []byte
|
||||
want aghalg.NullBool
|
||||
}{{
|
||||
name: "empty",
|
||||
wantErrMsg: "",
|
||||
data: []byte{},
|
||||
want: aghalg.NBNull,
|
||||
}, {
|
||||
name: "null",
|
||||
wantErrMsg: "",
|
||||
data: []byte("null"),
|
||||
want: aghalg.NBNull,
|
||||
}, {
|
||||
name: "true",
|
||||
wantErrMsg: "",
|
||||
data: []byte("true"),
|
||||
want: aghalg.NBTrue,
|
||||
}, {
|
||||
name: "false",
|
||||
wantErrMsg: "",
|
||||
data: []byte("false"),
|
||||
want: aghalg.NBFalse,
|
||||
}, {
|
||||
name: "invalid",
|
||||
wantErrMsg: `unmarshalling json data into aghalg.NullBool: bad value "invalid"`,
|
||||
data: []byte("invalid"),
|
||||
want: aghalg.NBNull,
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
var got aghalg.NullBool
|
||||
err := got.UnmarshalJSON(tc.data)
|
||||
testutil.AssertErrorMsg(t, tc.wantErrMsg, err)
|
||||
|
||||
assert.Equal(t, tc.want, got)
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("json", func(t *testing.T) {
|
||||
want := aghalg.NBTrue
|
||||
var got struct {
|
||||
A aghalg.NullBool
|
||||
}
|
||||
|
||||
err := json.Unmarshal([]byte(`{"A":true}`), &got)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, want, got.A)
|
||||
})
|
||||
}
|
||||
@@ -198,7 +198,7 @@ func (hc *HostsContainer) Close() (err error) {
|
||||
}
|
||||
|
||||
// Upd returns the channel into which the updates are sent. The receivable
|
||||
// map's values are guaranteed to be of type of *stringutil.Set.
|
||||
// map's values are guaranteed to be of type of *HostsRecord.
|
||||
func (hc *HostsContainer) Upd() (updates <-chan *netutil.IPMap) {
|
||||
return hc.updates
|
||||
}
|
||||
@@ -290,7 +290,7 @@ func (hp *hostsParser) parseFile(r io.Reader) (patterns []string, cont bool, err
|
||||
continue
|
||||
}
|
||||
|
||||
hp.addPairs(ip, hosts)
|
||||
hp.addRecord(ip, hosts)
|
||||
}
|
||||
|
||||
return nil, true, s.Err()
|
||||
@@ -335,39 +335,66 @@ func (hp *hostsParser) parseLine(line string) (ip net.IP, hosts []string) {
|
||||
return ip, hosts
|
||||
}
|
||||
|
||||
// addPair puts the pair of ip and host to the rules builder if needed. For
|
||||
// each ip the first member of hosts will become the main one.
|
||||
func (hp *hostsParser) addPairs(ip net.IP, hosts []string) {
|
||||
// HostsRecord represents a single hosts file record.
|
||||
type HostsRecord struct {
|
||||
Aliases *stringutil.Set
|
||||
Canonical string
|
||||
}
|
||||
|
||||
// Equal returns true if all fields of rec are equal to field in other or they
|
||||
// both are nil.
|
||||
func (rec *HostsRecord) Equal(other *HostsRecord) (ok bool) {
|
||||
if rec == nil {
|
||||
return other == nil
|
||||
}
|
||||
|
||||
return rec.Canonical == other.Canonical && rec.Aliases.Equal(other.Aliases)
|
||||
}
|
||||
|
||||
// addRecord puts the record for the IP address to the rules builder if needed.
|
||||
// The first host is considered to be the canonical name for the IP address.
|
||||
// hosts must have at least one name.
|
||||
func (hp *hostsParser) addRecord(ip net.IP, hosts []string) {
|
||||
line := strings.Join(append([]string{ip.String()}, hosts...), " ")
|
||||
|
||||
var rec *HostsRecord
|
||||
v, ok := hp.table.Get(ip)
|
||||
if !ok {
|
||||
// This ip is added at the first time.
|
||||
v = stringutil.NewSet()
|
||||
hp.table.Set(ip, v)
|
||||
rec = &HostsRecord{
|
||||
Aliases: stringutil.NewSet(),
|
||||
}
|
||||
|
||||
rec.Canonical, hosts = hosts[0], hosts[1:]
|
||||
hp.addRules(ip, rec.Canonical, line)
|
||||
hp.table.Set(ip, rec)
|
||||
} else {
|
||||
rec, ok = v.(*HostsRecord)
|
||||
if !ok {
|
||||
log.Error("%s: adding pairs: unexpected type %T", hostsContainerPref, v)
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var set *stringutil.Set
|
||||
set, ok = v.(*stringutil.Set)
|
||||
if !ok {
|
||||
log.Debug("%s: adding pairs: unexpected value type %T", hostsContainerPref, v)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
processed := strings.Join(append([]string{ip.String()}, hosts...), " ")
|
||||
for _, h := range hosts {
|
||||
if set.Has(h) {
|
||||
for _, host := range hosts {
|
||||
if rec.Canonical == host || rec.Aliases.Has(host) {
|
||||
continue
|
||||
}
|
||||
|
||||
set.Add(h)
|
||||
rec.Aliases.Add(host)
|
||||
|
||||
rule, rulePtr := hp.writeRules(h, ip)
|
||||
hp.translations[rule], hp.translations[rulePtr] = processed, processed
|
||||
|
||||
log.Debug("%s: added ip-host pair %q-%q", hostsContainerPref, ip, h)
|
||||
hp.addRules(ip, host, line)
|
||||
}
|
||||
}
|
||||
|
||||
// addRules adds rules and rule translations for the line.
|
||||
func (hp *hostsParser) addRules(ip net.IP, host, line string) {
|
||||
rule, rulePtr := hp.writeRules(host, ip)
|
||||
hp.translations[rule], hp.translations[rulePtr] = line, line
|
||||
|
||||
log.Debug("%s: added ip-host pair %q-%q", hostsContainerPref, ip, host)
|
||||
}
|
||||
|
||||
// writeRules writes the actual rule for the qtype and the PTR for the host-ip
|
||||
// pair into internal builders.
|
||||
func (hp *hostsParser) writeRules(host string, ip net.IP) (rule, rulePtr string) {
|
||||
@@ -417,6 +444,7 @@ func (hp *hostsParser) writeRules(host string, ip net.IP) (rule, rulePtr string)
|
||||
}
|
||||
|
||||
// equalSet returns true if the internal hosts table just parsed equals target.
|
||||
// target's values must be of type *HostsRecord.
|
||||
func (hp *hostsParser) equalSet(target *netutil.IPMap) (ok bool) {
|
||||
if target == nil {
|
||||
// hp.table shouldn't appear nil since it's initialized on each refresh.
|
||||
@@ -427,22 +455,35 @@ func (hp *hostsParser) equalSet(target *netutil.IPMap) (ok bool) {
|
||||
return false
|
||||
}
|
||||
|
||||
hp.table.Range(func(ip net.IP, b interface{}) (cont bool) {
|
||||
// ok is set to true if the target doesn't contain ip or if the
|
||||
// appropriate hosts set isn't equal to the checked one.
|
||||
if a, hasIP := target.Get(ip); !hasIP {
|
||||
ok = true
|
||||
} else if hosts, aok := a.(*stringutil.Set); aok {
|
||||
ok = !hosts.Equal(b.(*stringutil.Set))
|
||||
hp.table.Range(func(ip net.IP, recVal interface{}) (cont bool) {
|
||||
var targetVal interface{}
|
||||
targetVal, ok = target.Get(ip)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
// Continue only if maps has no discrepancies.
|
||||
return !ok
|
||||
var rec *HostsRecord
|
||||
rec, ok = recVal.(*HostsRecord)
|
||||
if !ok {
|
||||
log.Error("%s: comparing: unexpected type %T", hostsContainerPref, recVal)
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
var targetRec *HostsRecord
|
||||
targetRec, ok = targetVal.(*HostsRecord)
|
||||
if !ok {
|
||||
log.Error("%s: comparing: target: unexpected type %T", hostsContainerPref, targetVal)
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
ok = rec.Equal(targetRec)
|
||||
|
||||
return ok
|
||||
})
|
||||
|
||||
// Return true if every value from the IP map has no discrepancies with the
|
||||
// appropriate one from the target.
|
||||
return !ok
|
||||
return ok
|
||||
}
|
||||
|
||||
// sendUpd tries to send the parsed data to the ch.
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghtest"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/netutil"
|
||||
"github.com/AdguardTeam/golibs/stringutil"
|
||||
"github.com/AdguardTeam/golibs/testutil"
|
||||
"github.com/AdguardTeam/urlfilter"
|
||||
@@ -159,31 +160,47 @@ func TestHostsContainer_refresh(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
testutil.CleanupAndRequireSuccess(t, hc.Close)
|
||||
|
||||
checkRefresh := func(t *testing.T, wantHosts *stringutil.Set) {
|
||||
upd, ok := <-hc.Upd()
|
||||
require.True(t, ok)
|
||||
require.NotNil(t, upd)
|
||||
checkRefresh := func(t *testing.T, want *HostsRecord) {
|
||||
t.Helper()
|
||||
|
||||
var ok bool
|
||||
var upd *netutil.IPMap
|
||||
select {
|
||||
case upd, ok = <-hc.Upd():
|
||||
require.True(t, ok)
|
||||
require.NotNil(t, upd)
|
||||
case <-time.After(1 * time.Second):
|
||||
t.Fatal("did not receive after 1s")
|
||||
}
|
||||
|
||||
assert.Equal(t, 1, upd.Len())
|
||||
|
||||
v, ok := upd.Get(ip)
|
||||
require.True(t, ok)
|
||||
|
||||
var set *stringutil.Set
|
||||
set, ok = v.(*stringutil.Set)
|
||||
require.True(t, ok)
|
||||
require.IsType(t, (*HostsRecord)(nil), v)
|
||||
|
||||
assert.True(t, set.Equal(wantHosts))
|
||||
rec, _ := v.(*HostsRecord)
|
||||
require.NotNil(t, rec)
|
||||
|
||||
assert.Truef(t, rec.Equal(want), "%+v != %+v", rec, want)
|
||||
}
|
||||
|
||||
t.Run("initial_refresh", func(t *testing.T) {
|
||||
checkRefresh(t, stringutil.NewSet("hostname"))
|
||||
checkRefresh(t, &HostsRecord{
|
||||
Aliases: stringutil.NewSet(),
|
||||
Canonical: "hostname",
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("second_refresh", func(t *testing.T) {
|
||||
testFS["dir/file2"] = &fstest.MapFile{Data: []byte(ipStr + ` alias` + nl)}
|
||||
eventsCh <- event{}
|
||||
checkRefresh(t, stringutil.NewSet("hostname", "alias"))
|
||||
|
||||
checkRefresh(t, &HostsRecord{
|
||||
Aliases: stringutil.NewSet("alias"),
|
||||
Canonical: "hostname",
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("double_refresh", func(t *testing.T) {
|
||||
@@ -363,10 +380,15 @@ func TestHostsContainer(t *testing.T) {
|
||||
require.NoError(t, fstest.TestFS(testdata, "etc_hosts"))
|
||||
|
||||
testCases := []struct {
|
||||
want []*rules.DNSRewrite
|
||||
name string
|
||||
req *urlfilter.DNSRequest
|
||||
name string
|
||||
want []*rules.DNSRewrite
|
||||
}{{
|
||||
req: &urlfilter.DNSRequest{
|
||||
Hostname: "simplehost",
|
||||
DNSType: dns.TypeA,
|
||||
},
|
||||
name: "simple",
|
||||
want: []*rules.DNSRewrite{{
|
||||
RCode: dns.RcodeSuccess,
|
||||
Value: net.IPv4(1, 0, 0, 1),
|
||||
@@ -376,27 +398,12 @@ func TestHostsContainer(t *testing.T) {
|
||||
Value: net.ParseIP("::1"),
|
||||
RRType: dns.TypeAAAA,
|
||||
}},
|
||||
name: "simple",
|
||||
req: &urlfilter.DNSRequest{
|
||||
Hostname: "simplehost",
|
||||
DNSType: dns.TypeA,
|
||||
},
|
||||
}, {
|
||||
want: []*rules.DNSRewrite{{
|
||||
RCode: dns.RcodeSuccess,
|
||||
Value: net.IPv4(1, 0, 0, 0),
|
||||
RRType: dns.TypeA,
|
||||
}, {
|
||||
RCode: dns.RcodeSuccess,
|
||||
Value: net.ParseIP("::"),
|
||||
RRType: dns.TypeAAAA,
|
||||
}},
|
||||
name: "hello_alias",
|
||||
req: &urlfilter.DNSRequest{
|
||||
Hostname: "hello.world",
|
||||
DNSType: dns.TypeA,
|
||||
},
|
||||
}, {
|
||||
name: "hello_alias",
|
||||
want: []*rules.DNSRewrite{{
|
||||
RCode: dns.RcodeSuccess,
|
||||
Value: net.IPv4(1, 0, 0, 0),
|
||||
@@ -406,26 +413,41 @@ func TestHostsContainer(t *testing.T) {
|
||||
Value: net.ParseIP("::"),
|
||||
RRType: dns.TypeAAAA,
|
||||
}},
|
||||
name: "other_line_alias",
|
||||
}, {
|
||||
req: &urlfilter.DNSRequest{
|
||||
Hostname: "hello.world.again",
|
||||
DNSType: dns.TypeA,
|
||||
},
|
||||
name: "other_line_alias",
|
||||
want: []*rules.DNSRewrite{{
|
||||
RCode: dns.RcodeSuccess,
|
||||
Value: net.IPv4(1, 0, 0, 0),
|
||||
RRType: dns.TypeA,
|
||||
}, {
|
||||
RCode: dns.RcodeSuccess,
|
||||
Value: net.ParseIP("::"),
|
||||
RRType: dns.TypeAAAA,
|
||||
}},
|
||||
}, {
|
||||
want: []*rules.DNSRewrite{},
|
||||
name: "hello_subdomain",
|
||||
req: &urlfilter.DNSRequest{
|
||||
Hostname: "say.hello",
|
||||
DNSType: dns.TypeA,
|
||||
},
|
||||
}, {
|
||||
name: "hello_subdomain",
|
||||
want: []*rules.DNSRewrite{},
|
||||
name: "hello_alias_subdomain",
|
||||
}, {
|
||||
req: &urlfilter.DNSRequest{
|
||||
Hostname: "say.hello.world",
|
||||
DNSType: dns.TypeA,
|
||||
},
|
||||
name: "hello_alias_subdomain",
|
||||
want: []*rules.DNSRewrite{},
|
||||
}, {
|
||||
req: &urlfilter.DNSRequest{
|
||||
Hostname: "for.testing",
|
||||
DNSType: dns.TypeA,
|
||||
},
|
||||
name: "lots_of_aliases",
|
||||
want: []*rules.DNSRewrite{{
|
||||
RCode: dns.RcodeSuccess,
|
||||
RRType: dns.TypeA,
|
||||
@@ -435,37 +457,37 @@ func TestHostsContainer(t *testing.T) {
|
||||
RRType: dns.TypeAAAA,
|
||||
Value: net.ParseIP("::2"),
|
||||
}},
|
||||
name: "lots_of_aliases",
|
||||
req: &urlfilter.DNSRequest{
|
||||
Hostname: "for.testing",
|
||||
DNSType: dns.TypeA,
|
||||
},
|
||||
}, {
|
||||
req: &urlfilter.DNSRequest{
|
||||
Hostname: "1.0.0.1.in-addr.arpa",
|
||||
DNSType: dns.TypePTR,
|
||||
},
|
||||
name: "reverse",
|
||||
want: []*rules.DNSRewrite{{
|
||||
RCode: dns.RcodeSuccess,
|
||||
RRType: dns.TypePTR,
|
||||
Value: "simplehost.",
|
||||
}},
|
||||
name: "reverse",
|
||||
req: &urlfilter.DNSRequest{
|
||||
Hostname: "1.0.0.1.in-addr.arpa",
|
||||
DNSType: dns.TypePTR,
|
||||
},
|
||||
}, {
|
||||
want: []*rules.DNSRewrite{},
|
||||
name: "non-existing",
|
||||
req: &urlfilter.DNSRequest{
|
||||
Hostname: "nonexisting",
|
||||
DNSType: dns.TypeA,
|
||||
},
|
||||
name: "non-existing",
|
||||
want: []*rules.DNSRewrite{},
|
||||
}, {
|
||||
want: nil,
|
||||
name: "bad_type",
|
||||
req: &urlfilter.DNSRequest{
|
||||
Hostname: "1.0.0.1.in-addr.arpa",
|
||||
DNSType: dns.TypeSRV,
|
||||
},
|
||||
name: "bad_type",
|
||||
want: nil,
|
||||
}, {
|
||||
req: &urlfilter.DNSRequest{
|
||||
Hostname: "domain",
|
||||
DNSType: dns.TypeA,
|
||||
},
|
||||
name: "issue_4216_4_6",
|
||||
want: []*rules.DNSRewrite{{
|
||||
RCode: dns.RcodeSuccess,
|
||||
RRType: dns.TypeA,
|
||||
@@ -475,12 +497,12 @@ func TestHostsContainer(t *testing.T) {
|
||||
RRType: dns.TypeAAAA,
|
||||
Value: net.ParseIP("::42"),
|
||||
}},
|
||||
name: "issue_4216_4_6",
|
||||
}, {
|
||||
req: &urlfilter.DNSRequest{
|
||||
Hostname: "domain",
|
||||
Hostname: "domain4",
|
||||
DNSType: dns.TypeA,
|
||||
},
|
||||
}, {
|
||||
name: "issue_4216_4",
|
||||
want: []*rules.DNSRewrite{{
|
||||
RCode: dns.RcodeSuccess,
|
||||
RRType: dns.TypeA,
|
||||
@@ -490,12 +512,12 @@ func TestHostsContainer(t *testing.T) {
|
||||
RRType: dns.TypeA,
|
||||
Value: net.IPv4(1, 3, 5, 7),
|
||||
}},
|
||||
name: "issue_4216_4",
|
||||
req: &urlfilter.DNSRequest{
|
||||
Hostname: "domain4",
|
||||
DNSType: dns.TypeA,
|
||||
},
|
||||
}, {
|
||||
req: &urlfilter.DNSRequest{
|
||||
Hostname: "domain6",
|
||||
DNSType: dns.TypeAAAA,
|
||||
},
|
||||
name: "issue_4216_6",
|
||||
want: []*rules.DNSRewrite{{
|
||||
RCode: dns.RcodeSuccess,
|
||||
RRType: dns.TypeAAAA,
|
||||
@@ -505,11 +527,6 @@ func TestHostsContainer(t *testing.T) {
|
||||
RRType: dns.TypeAAAA,
|
||||
Value: net.ParseIP("::31"),
|
||||
}},
|
||||
name: "issue_4216_6",
|
||||
req: &urlfilter.DNSRequest{
|
||||
Hostname: "domain6",
|
||||
DNSType: dns.TypeAAAA,
|
||||
},
|
||||
}}
|
||||
|
||||
stubWatcher := aghtest.FSWatcher{
|
||||
|
||||
@@ -19,7 +19,7 @@ type SystemResolvers interface {
|
||||
}
|
||||
|
||||
// NewSystemResolvers returns a SystemResolvers with the cache refresh rate
|
||||
// defined by refreshIvl. It disables auto-resfreshing if refreshIvl is 0. If
|
||||
// defined by refreshIvl. It disables auto-refreshing if refreshIvl is 0. If
|
||||
// nil is passed for hostGenFunc, the default generator will be used.
|
||||
func NewSystemResolvers(
|
||||
hostGenFunc HostGenFunc,
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghalg"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
@@ -145,7 +146,7 @@ type dhcpServerConfigJSON struct {
|
||||
V4 *v4ServerConfJSON `json:"v4"`
|
||||
V6 *v6ServerConfJSON `json:"v6"`
|
||||
InterfaceName string `json:"interface_name"`
|
||||
Enabled nullBool `json:"enabled"`
|
||||
Enabled aghalg.NullBool `json:"enabled"`
|
||||
}
|
||||
|
||||
func (s *Server) handleDHCPSetConfigV4(
|
||||
@@ -156,7 +157,7 @@ func (s *Server) handleDHCPSetConfigV4(
|
||||
}
|
||||
|
||||
v4Conf := v4JSONToServerConf(conf.V4)
|
||||
v4Conf.Enabled = conf.Enabled == nbTrue
|
||||
v4Conf.Enabled = conf.Enabled == aghalg.NBTrue
|
||||
if len(v4Conf.RangeStart) == 0 {
|
||||
v4Conf.Enabled = false
|
||||
}
|
||||
@@ -183,7 +184,7 @@ func (s *Server) handleDHCPSetConfigV6(
|
||||
}
|
||||
|
||||
v6Conf := v6JSONToServerConf(conf.V6)
|
||||
v6Conf.Enabled = conf.Enabled == nbTrue
|
||||
v6Conf.Enabled = conf.Enabled == aghalg.NBTrue
|
||||
if len(v6Conf.RangeStart) == 0 {
|
||||
v6Conf.Enabled = false
|
||||
}
|
||||
@@ -206,7 +207,7 @@ func (s *Server) handleDHCPSetConfigV6(
|
||||
|
||||
func (s *Server) handleDHCPSetConfig(w http.ResponseWriter, r *http.Request) {
|
||||
conf := &dhcpServerConfigJSON{}
|
||||
conf.Enabled = boolToNullBool(s.conf.Enabled)
|
||||
conf.Enabled = aghalg.BoolToNullBool(s.conf.Enabled)
|
||||
conf.InterfaceName = s.conf.InterfaceName
|
||||
|
||||
err := json.NewDecoder(r.Body).Decode(conf)
|
||||
@@ -230,7 +231,7 @@ func (s *Server) handleDHCPSetConfig(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if conf.Enabled == nbTrue && !v4Enabled && !v6Enabled {
|
||||
if conf.Enabled == aghalg.NBTrue && !v4Enabled && !v6Enabled {
|
||||
aghhttp.Error(r, w, http.StatusBadRequest, "dhcpv4 or dhcpv6 configuration must be complete")
|
||||
|
||||
return
|
||||
@@ -243,8 +244,8 @@ func (s *Server) handleDHCPSetConfig(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if conf.Enabled != nbNull {
|
||||
s.conf.Enabled = conf.Enabled == nbTrue
|
||||
if conf.Enabled != aghalg.NBNull {
|
||||
s.conf.Enabled = conf.Enabled == aghalg.NBTrue
|
||||
}
|
||||
|
||||
if conf.InterfaceName != "" {
|
||||
@@ -279,11 +280,11 @@ func (s *Server) handleDHCPSetConfig(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
type netInterfaceJSON struct {
|
||||
Name string `json:"name"`
|
||||
GatewayIP net.IP `json:"gateway_ip"`
|
||||
HardwareAddr string `json:"hardware_address"`
|
||||
Flags string `json:"flags"`
|
||||
GatewayIP net.IP `json:"gateway_ip"`
|
||||
Addrs4 []net.IP `json:"ipv4_addresses"`
|
||||
Addrs6 []net.IP `json:"ipv6_addresses"`
|
||||
Flags string `json:"flags"`
|
||||
}
|
||||
|
||||
func (s *Server) handleDHCPInterfaces(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -497,7 +498,6 @@ func (s *Server) handleDHCPAddStaticLease(w http.ResponseWriter, r *http.Request
|
||||
}
|
||||
|
||||
ip4 := l.IP.To4()
|
||||
|
||||
if ip4 == nil {
|
||||
l.IP = l.IP.To16()
|
||||
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
package dhcpd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// nullBool is a nullable boolean. Use these in JSON requests and responses
|
||||
// instead of pointers to bool.
|
||||
//
|
||||
// TODO(a.garipov): Inspect uses of *bool, move this type into some new package
|
||||
// if we need it somewhere else.
|
||||
type nullBool uint8
|
||||
|
||||
// nullBool values
|
||||
const (
|
||||
nbNull nullBool = iota
|
||||
nbTrue
|
||||
nbFalse
|
||||
)
|
||||
|
||||
// String implements the fmt.Stringer interface for nullBool.
|
||||
func (nb nullBool) String() (s string) {
|
||||
switch nb {
|
||||
case nbNull:
|
||||
return "null"
|
||||
case nbTrue:
|
||||
return "true"
|
||||
case nbFalse:
|
||||
return "false"
|
||||
}
|
||||
|
||||
return fmt.Sprintf("!invalid nullBool %d", uint8(nb))
|
||||
}
|
||||
|
||||
// boolToNullBool converts a bool into a nullBool.
|
||||
func boolToNullBool(cond bool) (nb nullBool) {
|
||||
if cond {
|
||||
return nbTrue
|
||||
}
|
||||
|
||||
return nbFalse
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the json.Unmarshaler interface for *nullBool.
|
||||
func (nb *nullBool) UnmarshalJSON(b []byte) (err error) {
|
||||
if len(b) == 0 || bytes.Equal(b, []byte("null")) {
|
||||
*nb = nbNull
|
||||
} else if bytes.Equal(b, []byte("true")) {
|
||||
*nb = nbTrue
|
||||
} else if bytes.Equal(b, []byte("false")) {
|
||||
*nb = nbFalse
|
||||
} else {
|
||||
return fmt.Errorf("invalid nullBool value %q", b)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
package dhcpd
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/AdguardTeam/golibs/testutil"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestNullBool_UnmarshalJSON(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
wantErrMsg string
|
||||
data []byte
|
||||
want nullBool
|
||||
}{{
|
||||
name: "empty",
|
||||
wantErrMsg: "",
|
||||
data: []byte{},
|
||||
want: nbNull,
|
||||
}, {
|
||||
name: "null",
|
||||
wantErrMsg: "",
|
||||
data: []byte("null"),
|
||||
want: nbNull,
|
||||
}, {
|
||||
name: "true",
|
||||
wantErrMsg: "",
|
||||
data: []byte("true"),
|
||||
want: nbTrue,
|
||||
}, {
|
||||
name: "false",
|
||||
wantErrMsg: "",
|
||||
data: []byte("false"),
|
||||
want: nbFalse,
|
||||
}, {
|
||||
name: "invalid",
|
||||
wantErrMsg: `invalid nullBool value "invalid"`,
|
||||
data: []byte("invalid"),
|
||||
want: nbNull,
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
var got nullBool
|
||||
err := got.UnmarshalJSON(tc.data)
|
||||
testutil.AssertErrorMsg(t, tc.wantErrMsg, err)
|
||||
|
||||
assert.Equal(t, tc.want, got)
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("json", func(t *testing.T) {
|
||||
want := nbTrue
|
||||
var got struct {
|
||||
A nullBool
|
||||
}
|
||||
|
||||
err := json.Unmarshal([]byte(`{"A":true}`), &got)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, want, got.A)
|
||||
})
|
||||
}
|
||||
@@ -333,12 +333,16 @@ func (s *v4Server) rmLease(lease *Lease) (err error) {
|
||||
return errors.Error("lease not found")
|
||||
}
|
||||
|
||||
// AddStaticLease adds a static lease. It is safe for concurrent use.
|
||||
// AddStaticLease implements the DHCPServer interface for *v4Server. It is safe
|
||||
// for concurrent use.
|
||||
func (s *v4Server) AddStaticLease(l *Lease) (err error) {
|
||||
defer func() { err = errors.Annotate(err, "dhcpv4: adding static lease: %w") }()
|
||||
|
||||
if ip4 := l.IP.To4(); ip4 == nil {
|
||||
ip := l.IP.To4()
|
||||
if ip == nil {
|
||||
return fmt.Errorf("invalid ip %q, only ipv4 is supported", l.IP)
|
||||
} else if gwIP := s.conf.GatewayIP; gwIP.Equal(ip) {
|
||||
return fmt.Errorf("can't assign the gateway IP %s to the lease", gwIP)
|
||||
}
|
||||
|
||||
l.Expiry = time.Unix(leaseExpireStatic, 0)
|
||||
@@ -377,7 +381,7 @@ func (s *v4Server) AddStaticLease(l *Lease) (err error) {
|
||||
if err != nil {
|
||||
err = fmt.Errorf(
|
||||
"removing dynamic leases for %s (%s): %w",
|
||||
l.IP,
|
||||
ip,
|
||||
l.HWAddr,
|
||||
err,
|
||||
)
|
||||
@@ -387,7 +391,7 @@ func (s *v4Server) AddStaticLease(l *Lease) (err error) {
|
||||
|
||||
err = s.addLease(l)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("adding static lease for %s (%s): %w", l.IP, l.HWAddr, err)
|
||||
err = fmt.Errorf("adding static lease for %s (%s): %w", ip, l.HWAddr, err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
package dhcpd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -16,6 +17,13 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var (
|
||||
DefaultRangeStart = net.IP{192, 168, 10, 100}
|
||||
DefaultRangeEnd = net.IP{192, 168, 10, 200}
|
||||
DefaultGatewayIP = net.IP{192, 168, 10, 1}
|
||||
DefaultSubnetMask = net.IP{255, 255, 255, 0}
|
||||
)
|
||||
|
||||
func notify4(flags uint32) {
|
||||
}
|
||||
|
||||
@@ -24,10 +32,10 @@ func notify4(flags uint32) {
|
||||
func defaultV4ServerConf() (conf V4ServerConf) {
|
||||
return V4ServerConf{
|
||||
Enabled: true,
|
||||
RangeStart: net.IP{192, 168, 10, 100},
|
||||
RangeEnd: net.IP{192, 168, 10, 200},
|
||||
GatewayIP: net.IP{192, 168, 10, 1},
|
||||
SubnetMask: net.IP{255, 255, 255, 0},
|
||||
RangeStart: DefaultRangeStart,
|
||||
RangeEnd: DefaultRangeEnd,
|
||||
GatewayIP: DefaultGatewayIP,
|
||||
SubnetMask: DefaultSubnetMask,
|
||||
notify: notify4,
|
||||
}
|
||||
}
|
||||
@@ -44,44 +52,86 @@ func defaultSrv(t *testing.T) (s DHCPServer) {
|
||||
return s
|
||||
}
|
||||
|
||||
func TestV4_AddRemove_static(t *testing.T) {
|
||||
func TestV4Server_AddRemove_static(t *testing.T) {
|
||||
s := defaultSrv(t)
|
||||
|
||||
ls := s.GetLeases(LeasesStatic)
|
||||
assert.Empty(t, ls)
|
||||
require.Empty(t, ls)
|
||||
|
||||
// Add static lease.
|
||||
l := &Lease{
|
||||
Hostname: "static-1.local",
|
||||
HWAddr: net.HardwareAddr{0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA},
|
||||
IP: net.IP{192, 168, 10, 150},
|
||||
testCases := []struct {
|
||||
lease *Lease
|
||||
name string
|
||||
wantErrMsg string
|
||||
}{{
|
||||
lease: &Lease{
|
||||
Hostname: "success.local",
|
||||
HWAddr: net.HardwareAddr{0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA},
|
||||
IP: net.IP{192, 168, 10, 150},
|
||||
},
|
||||
name: "success",
|
||||
wantErrMsg: "",
|
||||
}, {
|
||||
lease: &Lease{
|
||||
Hostname: "probably-router.local",
|
||||
HWAddr: net.HardwareAddr{0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA},
|
||||
IP: DefaultGatewayIP,
|
||||
},
|
||||
name: "with_gateway_ip",
|
||||
wantErrMsg: "dhcpv4: adding static lease: " +
|
||||
"can't assign the gateway IP 192.168.10.1 to the lease",
|
||||
}, {
|
||||
lease: &Lease{
|
||||
Hostname: "ip6.local",
|
||||
HWAddr: net.HardwareAddr{0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA},
|
||||
IP: net.ParseIP("ffff::1"),
|
||||
},
|
||||
name: "ipv6",
|
||||
wantErrMsg: `dhcpv4: adding static lease: ` +
|
||||
`invalid ip "ffff::1", only ipv4 is supported`,
|
||||
}, {
|
||||
lease: &Lease{
|
||||
Hostname: "bad-mac.local",
|
||||
HWAddr: net.HardwareAddr{0xAA, 0xAA},
|
||||
IP: net.IP{192, 168, 10, 150},
|
||||
},
|
||||
name: "bad_mac",
|
||||
wantErrMsg: `dhcpv4: adding static lease: bad mac address "aa:aa": ` +
|
||||
`bad mac address length 2, allowed: [6 8 20]`,
|
||||
}, {
|
||||
lease: &Lease{
|
||||
Hostname: "bad-lbl-.local",
|
||||
HWAddr: net.HardwareAddr{0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA},
|
||||
IP: net.IP{192, 168, 10, 150},
|
||||
},
|
||||
name: "bad_hostname",
|
||||
wantErrMsg: `dhcpv4: adding static lease: validating hostname: ` +
|
||||
`bad domain name "bad-lbl-.local": ` +
|
||||
`bad domain name label "bad-lbl-": bad domain name label rune '-'`,
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
err := s.AddStaticLease(tc.lease)
|
||||
testutil.AssertErrorMsg(t, tc.wantErrMsg, err)
|
||||
if tc.wantErrMsg != "" {
|
||||
return
|
||||
}
|
||||
|
||||
err = s.RemoveStaticLease(&Lease{
|
||||
IP: tc.lease.IP,
|
||||
HWAddr: tc.lease.HWAddr,
|
||||
})
|
||||
diffErrMsg := fmt.Sprintf("dhcpv4: lease for ip %s is different: %+v", tc.lease.IP, tc.lease)
|
||||
testutil.AssertErrorMsg(t, diffErrMsg, err)
|
||||
|
||||
// Remove static lease.
|
||||
err = s.RemoveStaticLease(tc.lease)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
ls = s.GetLeases(LeasesStatic)
|
||||
require.Emptyf(t, ls, "after %s", tc.name)
|
||||
}
|
||||
|
||||
err := s.AddStaticLease(l)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = s.AddStaticLease(l)
|
||||
assert.Error(t, err)
|
||||
|
||||
ls = s.GetLeases(LeasesStatic)
|
||||
require.Len(t, ls, 1)
|
||||
|
||||
assert.True(t, l.IP.Equal(ls[0].IP))
|
||||
assert.Equal(t, l.HWAddr, ls[0].HWAddr)
|
||||
assert.True(t, ls[0].IsStatic())
|
||||
|
||||
// Try to remove static lease.
|
||||
err = s.RemoveStaticLease(&Lease{
|
||||
IP: net.IP{192, 168, 10, 110},
|
||||
HWAddr: net.HardwareAddr{0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA},
|
||||
})
|
||||
assert.Error(t, err)
|
||||
|
||||
// Remove static lease.
|
||||
err = s.RemoveStaticLease(l)
|
||||
require.NoError(t, err)
|
||||
ls = s.GetLeases(LeasesStatic)
|
||||
assert.Empty(t, ls)
|
||||
}
|
||||
|
||||
func TestV4_AddReplace(t *testing.T) {
|
||||
|
||||
@@ -95,9 +95,9 @@ func (s *Server) handleDNSRequest(_ *proxy.Proxy, d *proxy.DNSContext) error {
|
||||
s.processRecursion,
|
||||
s.processInitial,
|
||||
s.processDetermineLocal,
|
||||
s.processInternalHosts,
|
||||
s.processDHCPHosts,
|
||||
s.processRestrictLocal,
|
||||
s.processInternalIPAddrs,
|
||||
s.processDHCPAddrs,
|
||||
s.processFilteringBeforeRequest,
|
||||
s.processLocalPTR,
|
||||
s.processUpstream,
|
||||
@@ -225,12 +225,10 @@ func (s *Server) onDHCPLeaseChanged(flags int) {
|
||||
)
|
||||
}
|
||||
|
||||
lowhost := strings.ToLower(l.Hostname)
|
||||
lowhost := strings.ToLower(l.Hostname + "." + s.localDomainSuffix)
|
||||
ip := netutil.CloneIP(l.IP)
|
||||
|
||||
ipToHost.Set(l.IP, lowhost)
|
||||
|
||||
ip := make(net.IP, 4)
|
||||
copy(ip, l.IP.To4())
|
||||
ipToHost.Set(ip, lowhost)
|
||||
hostToIP[lowhost] = ip
|
||||
}
|
||||
|
||||
@@ -279,11 +277,11 @@ func (s *Server) hostToIP(host string) (ip net.IP, ok bool) {
|
||||
return ip, true
|
||||
}
|
||||
|
||||
// processInternalHosts respond to A requests if the target hostname is known to
|
||||
// processDHCPHosts respond to A requests if the target hostname is known to
|
||||
// the server.
|
||||
//
|
||||
// TODO(a.garipov): Adapt to AAAA as well.
|
||||
func (s *Server) processInternalHosts(dctx *dnsContext) (rc resultCode) {
|
||||
func (s *Server) processDHCPHosts(dctx *dnsContext) (rc resultCode) {
|
||||
if !s.dhcpServer.Enabled() {
|
||||
return resultCodeSuccess
|
||||
}
|
||||
@@ -298,11 +296,10 @@ func (s *Server) processInternalHosts(dctx *dnsContext) (rc resultCode) {
|
||||
return resultCodeSuccess
|
||||
}
|
||||
|
||||
reqHost := strings.ToLower(q.Name)
|
||||
reqHost := strings.ToLower(q.Name[:len(q.Name)-1])
|
||||
// TODO(a.garipov): Move everything related to DHCP local domain to the DHCP
|
||||
// server.
|
||||
host := strings.TrimSuffix(reqHost, s.localDomainSuffix)
|
||||
if host == reqHost {
|
||||
if !strings.HasSuffix(reqHost, s.localDomainSuffix) {
|
||||
return resultCodeSuccess
|
||||
}
|
||||
|
||||
@@ -315,7 +312,7 @@ func (s *Server) processInternalHosts(dctx *dnsContext) (rc resultCode) {
|
||||
return resultCodeFinish
|
||||
}
|
||||
|
||||
ip, ok := s.hostToIP(host)
|
||||
ip, ok := s.hostToIP(reqHost)
|
||||
if !ok {
|
||||
// TODO(e.burkov): Inspect special cases when user want to apply some
|
||||
// rules handled by other processors to the hosts with TLD.
|
||||
@@ -372,7 +369,7 @@ func (s *Server) processRestrictLocal(ctx *dnsContext) (rc resultCode) {
|
||||
|
||||
// Restrict an access to local addresses for external clients. We also
|
||||
// assume that all the DHCP leases we give are locally-served or at least
|
||||
// don't need to be inaccessible externally.
|
||||
// don't need to be accessible externally.
|
||||
if !s.privateNets.Contains(ip) {
|
||||
log.Debug("dns: addr %s is not from locally-served network", ip)
|
||||
|
||||
@@ -429,7 +426,7 @@ func (s *Server) ipToHost(ip net.IP) (host string, ok bool) {
|
||||
|
||||
// Respond to PTR requests if the target IP is leased by our DHCP server and the
|
||||
// requestor is inside the local network.
|
||||
func (s *Server) processInternalIPAddrs(ctx *dnsContext) (rc resultCode) {
|
||||
func (s *Server) processDHCPAddrs(ctx *dnsContext) (rc resultCode) {
|
||||
d := ctx.proxyCtx
|
||||
if d.Res != nil {
|
||||
return resultCodeSuccess
|
||||
|
||||
@@ -58,7 +58,7 @@ func TestServer_ProcessDetermineLocal(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestServer_ProcessInternalHosts_localRestriction(t *testing.T) {
|
||||
func TestServer_ProcessDHCPHosts_localRestriction(t *testing.T) {
|
||||
knownIP := net.IP{1, 2, 3, 4}
|
||||
|
||||
testCases := []struct {
|
||||
@@ -99,7 +99,7 @@ func TestServer_ProcessInternalHosts_localRestriction(t *testing.T) {
|
||||
dhcpServer: &testDHCP{},
|
||||
localDomainSuffix: defaultLocalDomainSuffix,
|
||||
tableHostToIP: hostToIPTable{
|
||||
"example": knownIP,
|
||||
"example." + defaultLocalDomainSuffix: knownIP,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -121,7 +121,7 @@ func TestServer_ProcessInternalHosts_localRestriction(t *testing.T) {
|
||||
isLocalClient: tc.isLocalCli,
|
||||
}
|
||||
|
||||
res := s.processInternalHosts(dctx)
|
||||
res := s.processDHCPHosts(dctx)
|
||||
require.Equal(t, tc.wantRes, res)
|
||||
pctx := dctx.proxyCtx
|
||||
if tc.wantRes == resultCodeFinish {
|
||||
@@ -147,10 +147,10 @@ func TestServer_ProcessInternalHosts_localRestriction(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestServer_ProcessInternalHosts(t *testing.T) {
|
||||
func TestServer_ProcessDHCPHosts(t *testing.T) {
|
||||
const (
|
||||
examplecom = "example.com"
|
||||
examplelan = "example.lan"
|
||||
examplelan = "example." + defaultLocalDomainSuffix
|
||||
)
|
||||
|
||||
knownIP := net.IP{1, 2, 3, 4}
|
||||
@@ -199,41 +199,41 @@ func TestServer_ProcessInternalHosts(t *testing.T) {
|
||||
}, {
|
||||
name: "success_custom_suffix",
|
||||
host: "example.custom",
|
||||
suffix: ".custom.",
|
||||
suffix: "custom",
|
||||
wantIP: knownIP,
|
||||
wantRes: resultCodeSuccess,
|
||||
qtyp: dns.TypeA,
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
s := &Server{
|
||||
dhcpServer: &testDHCP{},
|
||||
localDomainSuffix: tc.suffix,
|
||||
tableHostToIP: hostToIPTable{
|
||||
"example." + tc.suffix: knownIP,
|
||||
},
|
||||
}
|
||||
|
||||
req := &dns.Msg{
|
||||
MsgHdr: dns.MsgHdr{
|
||||
Id: 1234,
|
||||
},
|
||||
Question: []dns.Question{{
|
||||
Name: dns.Fqdn(tc.host),
|
||||
Qtype: tc.qtyp,
|
||||
Qclass: dns.ClassINET,
|
||||
}},
|
||||
}
|
||||
|
||||
dctx := &dnsContext{
|
||||
proxyCtx: &proxy.DNSContext{
|
||||
Req: req,
|
||||
},
|
||||
isLocalClient: true,
|
||||
}
|
||||
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
s := &Server{
|
||||
dhcpServer: &testDHCP{},
|
||||
localDomainSuffix: tc.suffix,
|
||||
tableHostToIP: hostToIPTable{
|
||||
"example": knownIP,
|
||||
},
|
||||
}
|
||||
|
||||
req := &dns.Msg{
|
||||
MsgHdr: dns.MsgHdr{
|
||||
Id: 1234,
|
||||
},
|
||||
Question: []dns.Question{{
|
||||
Name: dns.Fqdn(tc.host),
|
||||
Qtype: tc.qtyp,
|
||||
Qclass: dns.ClassINET,
|
||||
}},
|
||||
}
|
||||
|
||||
dctx := &dnsContext{
|
||||
proxyCtx: &proxy.DNSContext{
|
||||
Req: req,
|
||||
},
|
||||
isLocalClient: true,
|
||||
}
|
||||
|
||||
res := s.processInternalHosts(dctx)
|
||||
res := s.processDHCPHosts(dctx)
|
||||
pctx := dctx.proxyCtx
|
||||
assert.Equal(t, tc.wantRes, res)
|
||||
if tc.wantRes == resultCodeFinish {
|
||||
|
||||
@@ -107,7 +107,7 @@ type Server struct {
|
||||
// when no suffix is provided.
|
||||
//
|
||||
// See the documentation for Server.localDomainSuffix.
|
||||
const defaultLocalDomainSuffix = ".lan."
|
||||
const defaultLocalDomainSuffix = "lan"
|
||||
|
||||
// DNSCreateParams are parameters to create a new server.
|
||||
type DNSCreateParams struct {
|
||||
@@ -120,17 +120,6 @@ type DNSCreateParams struct {
|
||||
LocalDomain string
|
||||
}
|
||||
|
||||
// domainNameToSuffix converts a domain name into a local domain suffix.
|
||||
func domainNameToSuffix(tld string) (suffix string) {
|
||||
l := len(tld) + 2
|
||||
b := make([]byte, l)
|
||||
b[0] = '.'
|
||||
copy(b[1:], tld)
|
||||
b[l-1] = '.'
|
||||
|
||||
return string(b)
|
||||
}
|
||||
|
||||
const (
|
||||
// recursionTTL is the time recursive request is cached for.
|
||||
recursionTTL = 1 * time.Second
|
||||
@@ -151,7 +140,7 @@ func NewServer(p DNSCreateParams) (s *Server, err error) {
|
||||
return nil, fmt.Errorf("local domain: %w", err)
|
||||
}
|
||||
|
||||
localDomainSuffix = domainNameToSuffix(p.LocalDomain)
|
||||
localDomainSuffix = p.LocalDomain
|
||||
}
|
||||
|
||||
if p.Anonymizer == nil {
|
||||
|
||||
@@ -1016,10 +1016,13 @@ func (d *testDHCP) Leases(flags dhcpd.GetLeasesFlags) (leases []*dhcpd.Lease) {
|
||||
func (d *testDHCP) SetOnLeaseChanged(onLeaseChanged dhcpd.OnLeaseChangedT) {}
|
||||
|
||||
func TestPTRResponseFromDHCPLeases(t *testing.T) {
|
||||
const localDomain = "lan"
|
||||
|
||||
s, err := NewServer(DNSCreateParams{
|
||||
DNSFilter: filtering.New(&filtering.Config{}, nil),
|
||||
DHCPServer: &testDHCP{},
|
||||
PrivateNets: netutil.SubnetSetFunc(netutil.IsLocallyServed),
|
||||
LocalDomain: localDomain,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -1033,14 +1036,13 @@ func TestPTRResponseFromDHCPLeases(t *testing.T) {
|
||||
|
||||
err = s.Start()
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Cleanup(s.Close)
|
||||
|
||||
addr := s.dnsProxy.Addr(proxy.ProtoUDP)
|
||||
req := createTestMessageWithType("34.12.168.192.in-addr.arpa.", dns.TypePTR)
|
||||
|
||||
resp, err := dns.Exchange(req, addr.String())
|
||||
require.NoError(t, err)
|
||||
require.NoErrorf(t, err, "%s", addr)
|
||||
|
||||
require.Len(t, resp.Answer, 1)
|
||||
|
||||
@@ -1049,7 +1051,7 @@ func TestPTRResponseFromDHCPLeases(t *testing.T) {
|
||||
|
||||
ptr, ok := resp.Answer[0].(*dns.PTR)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, "myhost.", ptr.Ptr)
|
||||
assert.Equal(t, dns.Fqdn("myhost."+localDomain), ptr.Ptr)
|
||||
}
|
||||
|
||||
func TestPTRResponseFromHosts(t *testing.T) {
|
||||
|
||||
@@ -743,8 +743,7 @@ func (clients *clientsContainer) AddHost(ip net.IP, host string, src clientSourc
|
||||
|
||||
// addHostLocked adds a new IP-hostname pairing. For internal use only.
|
||||
func (clients *clientsContainer) addHostLocked(ip net.IP, host string, src clientSource) (ok bool) {
|
||||
var rc *RuntimeClient
|
||||
rc, ok = clients.findRuntimeClientLocked(ip)
|
||||
rc, ok := clients.findRuntimeClientLocked(ip)
|
||||
if ok {
|
||||
if rc.Source > src {
|
||||
return false
|
||||
@@ -799,25 +798,20 @@ func (clients *clientsContainer) addFromHostsFile(hosts *netutil.IPMap) {
|
||||
|
||||
n := 0
|
||||
hosts.Range(func(ip net.IP, v interface{}) (cont bool) {
|
||||
hosts, ok := v.(*stringutil.Set)
|
||||
rec, ok := v.(*aghnet.HostsRecord)
|
||||
if !ok {
|
||||
log.Error("dns: bad type %T in ipToRC for %s", v, ip)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
hosts.Range(func(name string) (cont bool) {
|
||||
if clients.addHostLocked(ip, name, ClientSourceHostsFile) {
|
||||
n++
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
clients.addHostLocked(ip, rec.Canonical, ClientSourceHostsFile)
|
||||
n++
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
log.Debug("clients: added %d client aliases from system hosts-file", n)
|
||||
log.Debug("clients: added %d client aliases from system hosts file", n)
|
||||
}
|
||||
|
||||
// addFromSystemARP adds the IP-hostname pairings from the output of the arp -a
|
||||
|
||||
@@ -7,11 +7,11 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghalg"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/updater"
|
||||
@@ -117,7 +117,18 @@ func handleUpdate(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
err := Context.updater.Update()
|
||||
// Retain the current absolute path of the executable, since the updater is
|
||||
// likely to change the position current one to the backup directory.
|
||||
//
|
||||
// See https://github.com/AdguardTeam/AdGuardHome/issues/4735.
|
||||
execPath, err := os.Executable()
|
||||
if err != nil {
|
||||
aghhttp.Error(r, w, http.StatusInternalServerError, "getting path: %s", err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
err = Context.updater.Update()
|
||||
if err != nil {
|
||||
aghhttp.Error(r, w, http.StatusInternalServerError, "%s", err)
|
||||
|
||||
@@ -129,13 +140,10 @@ func handleUpdate(w http.ResponseWriter, r *http.Request) {
|
||||
f.Flush()
|
||||
}
|
||||
|
||||
// The background context is used because the underlying functions wrap
|
||||
// it with timeout and shut down the server, which handles current
|
||||
// request. It also should be done in a separate goroutine due to the
|
||||
// same reason.
|
||||
go func() {
|
||||
finishUpdate(context.Background())
|
||||
}()
|
||||
// The background context is used because the underlying functions wrap it
|
||||
// with timeout and shut down the server, which handles current request. It
|
||||
// also should be done in a separate goroutine for the same reason.
|
||||
go finishUpdate(context.Background(), execPath)
|
||||
}
|
||||
|
||||
// versionResponse is the response for /control/version.json endpoint.
|
||||
@@ -147,8 +155,8 @@ type versionResponse struct {
|
||||
// setAllowedToAutoUpdate sets CanAutoUpdate to true if AdGuard Home is actually
|
||||
// allowed to perform an automatic update by the OS.
|
||||
func (vr *versionResponse) setAllowedToAutoUpdate() (err error) {
|
||||
if vr.CanAutoUpdate == nil || !*vr.CanAutoUpdate {
|
||||
return
|
||||
if vr.CanAutoUpdate != aghalg.NBTrue {
|
||||
return nil
|
||||
}
|
||||
|
||||
tlsConf := &tlsConfigSettings{}
|
||||
@@ -162,7 +170,7 @@ func (vr *versionResponse) setAllowedToAutoUpdate() (err error) {
|
||||
}
|
||||
}
|
||||
|
||||
vr.CanAutoUpdate = &canUpdate
|
||||
vr.CanAutoUpdate = aghalg.BoolToNullBool(canUpdate)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -174,46 +182,46 @@ func tlsConfUsesPrivilegedPorts(c *tlsConfigSettings) (ok bool) {
|
||||
}
|
||||
|
||||
// finishUpdate completes an update procedure.
|
||||
func finishUpdate(ctx context.Context) {
|
||||
log.Info("Stopping all tasks")
|
||||
func finishUpdate(ctx context.Context, execPath string) {
|
||||
var err error
|
||||
|
||||
log.Info("stopping all tasks")
|
||||
|
||||
cleanup(ctx)
|
||||
cleanupAlways()
|
||||
|
||||
exeName := "AdGuardHome"
|
||||
if runtime.GOOS == "windows" {
|
||||
exeName = "AdGuardHome.exe"
|
||||
}
|
||||
curBinName := filepath.Join(Context.workDir, exeName)
|
||||
|
||||
if runtime.GOOS == "windows" {
|
||||
if Context.runningAsService {
|
||||
// Note:
|
||||
// we can't restart the service via "kardianos/service" package - it kills the process first
|
||||
// we can't start a new instance - Windows doesn't allow it
|
||||
// NOTE: We can't restart the service via "kardianos/service"
|
||||
// package, because it kills the process first we can't start a new
|
||||
// instance, because Windows doesn't allow it.
|
||||
//
|
||||
// TODO(a.garipov): Recheck the claim above.
|
||||
cmd := exec.Command("cmd", "/c", "net stop AdGuardHome & net start AdGuardHome")
|
||||
err := cmd.Start()
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
log.Fatalf("exec.Command() failed: %s", err)
|
||||
log.Fatalf("restarting: stopping: %s", err)
|
||||
}
|
||||
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
cmd := exec.Command(curBinName, os.Args[1:]...)
|
||||
log.Info("Restarting: %v", cmd.Args)
|
||||
cmd := exec.Command(execPath, os.Args[1:]...)
|
||||
log.Info("restarting: %q %q", execPath, os.Args[1:])
|
||||
cmd.Stdin = os.Stdin
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
err := cmd.Start()
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
log.Fatalf("exec.Command() failed: %s", err)
|
||||
log.Fatalf("restarting:: %s", err)
|
||||
}
|
||||
|
||||
os.Exit(0)
|
||||
} else {
|
||||
log.Info("Restarting: %v", os.Args)
|
||||
err := syscall.Exec(curBinName, os.Args, os.Environ())
|
||||
if err != nil {
|
||||
log.Fatalf("syscall.Exec() failed: %s", err)
|
||||
}
|
||||
// Unreachable code
|
||||
}
|
||||
|
||||
log.Info("restarting: %q %q", execPath, os.Args[1:])
|
||||
err = syscall.Exec(execPath, os.Args, os.Environ())
|
||||
if err != nil {
|
||||
log.Fatalf("restarting: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
83
internal/home/service_linux.go
Normal file
83
internal/home/service_linux.go
Normal file
@@ -0,0 +1,83 @@
|
||||
//go:build linux
|
||||
// +build linux
|
||||
|
||||
package home
|
||||
|
||||
import (
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghos"
|
||||
"github.com/kardianos/service"
|
||||
)
|
||||
|
||||
func chooseSystem() {
|
||||
sys := service.ChosenSystem()
|
||||
// By default, package service uses the SysV system if it cannot detect
|
||||
// anything other, but the update-rc.d fix should not be applied on OpenWrt,
|
||||
// so exclude it explicitly.
|
||||
//
|
||||
// See https://github.com/AdguardTeam/AdGuardHome/issues/4480 and
|
||||
// https://github.com/AdguardTeam/AdGuardHome/issues/4677.
|
||||
if sys.String() == "unix-systemv" && !aghos.IsOpenWrt() {
|
||||
service.ChooseSystem(sysvSystem{System: sys})
|
||||
}
|
||||
}
|
||||
|
||||
// sysvSystem is a wrapper for service.System that wraps the service.Service
|
||||
// while creating a new one.
|
||||
//
|
||||
// TODO(e.burkov): File a PR to github.com/kardianos/service.
|
||||
type sysvSystem struct {
|
||||
// System is expected to have an unexported type
|
||||
// *service.linuxSystemService.
|
||||
service.System
|
||||
}
|
||||
|
||||
// New returns a wrapped service.Service.
|
||||
func (sys sysvSystem) New(i service.Interface, c *service.Config) (s service.Service, err error) {
|
||||
s, err = sys.System.New(i, c)
|
||||
if err != nil {
|
||||
return s, err
|
||||
}
|
||||
|
||||
return sysvService{
|
||||
Service: s,
|
||||
name: c.Name,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// sysvService is a wrapper for a service.Service that also calls update-rc.d in
|
||||
// a proper way on installing and uninstalling.
|
||||
type sysvService struct {
|
||||
// Service is expected to have an unexported type *service.sysv.
|
||||
service.Service
|
||||
// name stores the name of the service to call updating script with it.
|
||||
name string
|
||||
}
|
||||
|
||||
// Install wraps service.Service.Install call with calling the updating script.
|
||||
func (svc sysvService) Install() (err error) {
|
||||
err = svc.Service.Install()
|
||||
if err != nil {
|
||||
// Don't wrap an error since it's informative enough as is.
|
||||
return err
|
||||
}
|
||||
|
||||
_, _, err = aghos.RunCommand("update-rc.d", svc.name, "defaults")
|
||||
|
||||
// Don't wrap an error since it's informative enough as is.
|
||||
return err
|
||||
}
|
||||
|
||||
// Uninstall wraps service.Service.Uninstall call with calling the updating
|
||||
// script.
|
||||
func (svc sysvService) Uninstall() (err error) {
|
||||
err = svc.Service.Uninstall()
|
||||
if err != nil {
|
||||
// Don't wrap an error since it's informative enough as is.
|
||||
return err
|
||||
}
|
||||
|
||||
_, _, err = aghos.RunCommand("update-rc.d", svc.name, "remove")
|
||||
|
||||
// Don't wrap an error since it's informative enough as is.
|
||||
return err
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
//go:build !openbsd
|
||||
// +build !openbsd
|
||||
//go:build !(openbsd || linux)
|
||||
// +build !openbsd,!linux
|
||||
|
||||
package home
|
||||
|
||||
// chooseSystem checks the current system detected and substitutes it with local
|
||||
// implementation if needed.
|
||||
func chooseSystem() {}
|
||||
|
||||
@@ -5,9 +5,9 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghalg"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghio"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
)
|
||||
@@ -17,11 +17,12 @@ const versionCheckPeriod = 8 * time.Hour
|
||||
|
||||
// VersionInfo contains information about a new version.
|
||||
type VersionInfo struct {
|
||||
CanAutoUpdate *bool `json:"can_autoupdate,omitempty"`
|
||||
NewVersion string `json:"new_version,omitempty"`
|
||||
Announcement string `json:"announcement,omitempty"`
|
||||
AnnouncementURL string `json:"announcement_url,omitempty"`
|
||||
SelfUpdateMinVersion string `json:"-"`
|
||||
NewVersion string `json:"new_version,omitempty"`
|
||||
Announcement string `json:"announcement,omitempty"`
|
||||
AnnouncementURL string `json:"announcement_url,omitempty"`
|
||||
// TODO(a.garipov): See if the frontend actually still cares about
|
||||
// nullability.
|
||||
CanAutoUpdate aghalg.NullBool `json:"can_autoupdate,omitempty"`
|
||||
}
|
||||
|
||||
// MaxResponseSize is responses on server's requests maximum length in bytes.
|
||||
@@ -67,15 +68,13 @@ func (u *Updater) VersionInfo(forceRecheck bool) (vi VersionInfo, err error) {
|
||||
}
|
||||
|
||||
func (u *Updater) parseVersionResponse(data []byte) (VersionInfo, error) {
|
||||
var canAutoUpdate bool
|
||||
info := VersionInfo{
|
||||
CanAutoUpdate: &canAutoUpdate,
|
||||
CanAutoUpdate: aghalg.NBFalse,
|
||||
}
|
||||
versionJSON := map[string]string{
|
||||
"version": "",
|
||||
"announcement": "",
|
||||
"announcement_url": "",
|
||||
"selfupdate_min_version": "",
|
||||
"version": "",
|
||||
"announcement": "",
|
||||
"announcement_url": "",
|
||||
}
|
||||
err := json.Unmarshal(data, &versionJSON)
|
||||
if err != nil {
|
||||
@@ -91,14 +90,9 @@ func (u *Updater) parseVersionResponse(data []byte) (VersionInfo, error) {
|
||||
info.NewVersion = versionJSON["version"]
|
||||
info.Announcement = versionJSON["announcement"]
|
||||
info.AnnouncementURL = versionJSON["announcement_url"]
|
||||
info.SelfUpdateMinVersion = versionJSON["selfupdate_min_version"]
|
||||
|
||||
packageURL, ok := u.downloadURL(versionJSON)
|
||||
if ok &&
|
||||
info.NewVersion != u.version &&
|
||||
strings.TrimPrefix(u.version, "v") >= strings.TrimPrefix(info.SelfUpdateMinVersion, "v") {
|
||||
canAutoUpdate = true
|
||||
}
|
||||
info.CanAutoUpdate = aghalg.BoolToNullBool(ok && info.NewVersion != u.version)
|
||||
|
||||
u.newVersion = info.NewVersion
|
||||
u.packageURL = packageURL
|
||||
|
||||
@@ -104,11 +104,19 @@ func NewUpdater(conf *Config) *Updater {
|
||||
}
|
||||
|
||||
// Update performs the auto-update.
|
||||
func (u *Updater) Update() error {
|
||||
func (u *Updater) Update() (err error) {
|
||||
u.mu.Lock()
|
||||
defer u.mu.Unlock()
|
||||
|
||||
err := u.prepare()
|
||||
log.Info("updater: updating")
|
||||
defer func() { log.Info("updater: finished; errors: %v", err) }()
|
||||
|
||||
execPath, err := os.Executable()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = u.prepare(filepath.Base(execPath))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -159,7 +167,8 @@ func (u *Updater) VersionCheckURL() (vcu string) {
|
||||
return u.versionCheckURL
|
||||
}
|
||||
|
||||
func (u *Updater) prepare() (err error) {
|
||||
// prepare fills all necessary fields in Updater object.
|
||||
func (u *Updater) prepare(exeName string) (err error) {
|
||||
u.updateDir = filepath.Join(u.workDir, fmt.Sprintf("agh-update-%s", u.newVersion))
|
||||
|
||||
_, pkgNameOnly := filepath.Split(u.packageURL)
|
||||
@@ -170,17 +179,21 @@ func (u *Updater) prepare() (err error) {
|
||||
u.packageName = filepath.Join(u.updateDir, pkgNameOnly)
|
||||
u.backupDir = filepath.Join(u.workDir, "agh-backup")
|
||||
|
||||
exeName := "AdGuardHome"
|
||||
updateExeName := "AdGuardHome"
|
||||
if u.goos == "windows" {
|
||||
exeName = "AdGuardHome.exe"
|
||||
updateExeName = "AdGuardHome.exe"
|
||||
}
|
||||
|
||||
u.backupExeName = filepath.Join(u.backupDir, exeName)
|
||||
u.updateExeName = filepath.Join(u.updateDir, exeName)
|
||||
u.updateExeName = filepath.Join(u.updateDir, updateExeName)
|
||||
|
||||
log.Info("Updating from %s to %s. URL:%s", version.Version(), u.newVersion, u.packageURL)
|
||||
log.Debug(
|
||||
"updater: updating from %s to %s using url: %s",
|
||||
version.Version(),
|
||||
u.newVersion,
|
||||
u.packageURL,
|
||||
)
|
||||
|
||||
// TODO(a.garipov): Use os.Args[0] instead?
|
||||
u.currentExeName = filepath.Join(u.workDir, exeName)
|
||||
_, err = os.Stat(u.currentExeName)
|
||||
if err != nil {
|
||||
@@ -194,7 +207,7 @@ func (u *Updater) unpack() error {
|
||||
var err error
|
||||
_, pkgNameOnly := filepath.Split(u.packageURL)
|
||||
|
||||
log.Debug("updater: unpacking the package")
|
||||
log.Debug("updater: unpacking package")
|
||||
if strings.HasSuffix(pkgNameOnly, ".zip") {
|
||||
u.unpackedFiles, err = zipFileUnpack(u.packageName, u.updateDir)
|
||||
if err != nil {
|
||||
@@ -229,7 +242,7 @@ func (u *Updater) check() error {
|
||||
}
|
||||
|
||||
func (u *Updater) backup() error {
|
||||
log.Debug("updater: backing up the current configuration")
|
||||
log.Debug("updater: backing up current configuration")
|
||||
_ = os.Mkdir(u.backupDir, 0o755)
|
||||
err := copyFile(u.confName, filepath.Join(u.backupDir, "AdGuardHome.yaml"))
|
||||
if err != nil {
|
||||
@@ -252,7 +265,7 @@ func (u *Updater) replace() error {
|
||||
return fmt.Errorf("copySupportingFiles(%s, %s) failed: %s", u.updateDir, u.workDir, err)
|
||||
}
|
||||
|
||||
log.Debug("updater: renaming: %s -> %s", u.currentExeName, u.backupExeName)
|
||||
log.Debug("updater: renaming: %s to %s", u.currentExeName, u.backupExeName)
|
||||
err = os.Rename(u.currentExeName, u.backupExeName)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -268,7 +281,7 @@ func (u *Updater) replace() error {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Debug("updater: renamed: %s -> %s", u.updateExeName, u.currentExeName)
|
||||
log.Debug("updater: renamed: %s to %s", u.updateExeName, u.currentExeName)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -297,7 +310,7 @@ func (u *Updater) downloadPackageFile(url, filename string) (err error) {
|
||||
return fmt.Errorf("http request failed: %w", err)
|
||||
}
|
||||
|
||||
log.Debug("updater: reading HTTP body")
|
||||
log.Debug("updater: reading http body")
|
||||
// This use of ReadAll is now safe, because we limited body's Reader.
|
||||
body, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
@@ -343,7 +356,7 @@ func tarGzFileUnpackOne(outDir string, tr *tar.Reader, hdr *tar.Header) (name st
|
||||
}
|
||||
|
||||
if hdr.Typeflag != tar.TypeReg {
|
||||
log.Debug("updater: %s: unknown file type %d, skipping", name, hdr.Typeflag)
|
||||
log.Info("updater: %s: unknown file type %d, skipping", name, hdr.Typeflag)
|
||||
|
||||
return "", nil
|
||||
}
|
||||
@@ -364,7 +377,7 @@ func tarGzFileUnpackOne(outDir string, tr *tar.Reader, hdr *tar.Header) (name st
|
||||
return "", fmt.Errorf("io.Copy(): %w", err)
|
||||
}
|
||||
|
||||
log.Tracef("updater: created file %s", outputName)
|
||||
log.Debug("updater: created file %q", outputName)
|
||||
|
||||
return name, nil
|
||||
}
|
||||
@@ -440,7 +453,7 @@ func zipFileUnpackOne(outDir string, zf *zip.File) (name string, err error) {
|
||||
return "", fmt.Errorf("os.Mkdir(%q): %w", outputName, err)
|
||||
}
|
||||
|
||||
log.Tracef("created directory %q", outputName)
|
||||
log.Debug("updater: created directory %q", outputName)
|
||||
|
||||
return "", nil
|
||||
}
|
||||
@@ -457,7 +470,7 @@ func zipFileUnpackOne(outDir string, zf *zip.File) (name string, err error) {
|
||||
return "", fmt.Errorf("io.Copy(): %w", err)
|
||||
}
|
||||
|
||||
log.Tracef("created file %s", outputName)
|
||||
log.Debug("updater: created file %q", outputName)
|
||||
|
||||
return name, nil
|
||||
}
|
||||
@@ -516,7 +529,7 @@ func copySupportingFiles(files []string, srcdir, dstdir string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Debug("updater: copied: %q -> %q", src, dst)
|
||||
log.Debug("updater: copied: %q to %q", src, dst)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghalg"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghtest"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/version"
|
||||
"github.com/AdguardTeam/golibs/testutil"
|
||||
@@ -92,10 +93,7 @@ func TestUpdateGetVersion(t *testing.T) {
|
||||
assert.Equal(t, "v0.103.0-beta.2", info.NewVersion)
|
||||
assert.Equal(t, "AdGuard Home v0.103.0-beta.2 is now available!", info.Announcement)
|
||||
assert.Equal(t, "https://github.com/AdguardTeam/AdGuardHome/internal/releases", info.AnnouncementURL)
|
||||
assert.Equal(t, "v0.0", info.SelfUpdateMinVersion)
|
||||
if assert.NotNil(t, info.CanAutoUpdate) {
|
||||
assert.True(t, *info.CanAutoUpdate)
|
||||
}
|
||||
assert.Equal(t, aghalg.NBTrue, info.CanAutoUpdate)
|
||||
|
||||
// check cached
|
||||
_, err = u.VersionInfo(false)
|
||||
@@ -133,7 +131,7 @@ func TestUpdate(t *testing.T) {
|
||||
u.newVersion = "v0.103.1"
|
||||
u.packageURL = fakeURL.String()
|
||||
|
||||
require.NoError(t, u.prepare())
|
||||
require.NoError(t, u.prepare("AdGuardHome"))
|
||||
|
||||
u.currentExeName = filepath.Join(wd, "AdGuardHome")
|
||||
|
||||
@@ -211,7 +209,7 @@ func TestUpdateWindows(t *testing.T) {
|
||||
u.newVersion = "v0.103.1"
|
||||
u.packageURL = fakeURL.String()
|
||||
|
||||
require.NoError(t, u.prepare())
|
||||
require.NoError(t, u.prepare("AdGuardHome.exe"))
|
||||
|
||||
u.currentExeName = filepath.Join(wd, "AdGuardHome.exe")
|
||||
|
||||
@@ -290,10 +288,7 @@ func TestUpdater_VersionInto_ARM(t *testing.T) {
|
||||
assert.Equal(t, "v0.103.0-beta.2", info.NewVersion)
|
||||
assert.Equal(t, "AdGuard Home v0.103.0-beta.2 is now available!", info.Announcement)
|
||||
assert.Equal(t, "https://github.com/AdguardTeam/AdGuardHome/internal/releases", info.AnnouncementURL)
|
||||
assert.Equal(t, "v0.0", info.SelfUpdateMinVersion)
|
||||
if assert.NotNil(t, info.CanAutoUpdate) {
|
||||
assert.True(t, *info.CanAutoUpdate)
|
||||
}
|
||||
assert.Equal(t, aghalg.NBTrue, info.CanAutoUpdate)
|
||||
}
|
||||
|
||||
func TestUpdater_VersionInto_MIPS(t *testing.T) {
|
||||
@@ -330,8 +325,5 @@ func TestUpdater_VersionInto_MIPS(t *testing.T) {
|
||||
assert.Equal(t, "v0.103.0-beta.2", info.NewVersion)
|
||||
assert.Equal(t, "AdGuard Home v0.103.0-beta.2 is now available!", info.Announcement)
|
||||
assert.Equal(t, "https://github.com/AdguardTeam/AdGuardHome/internal/releases", info.AnnouncementURL)
|
||||
assert.Equal(t, "v0.0", info.SelfUpdateMinVersion)
|
||||
if assert.NotNil(t, info.CanAutoUpdate) {
|
||||
assert.True(t, *info.CanAutoUpdate)
|
||||
}
|
||||
assert.Equal(t, aghalg.NBTrue, info.CanAutoUpdate)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# A docker file for scripts/make/build-docker.sh.
|
||||
|
||||
FROM alpine:3.13
|
||||
FROM alpine:3.16
|
||||
|
||||
ARG BUILD_DATE
|
||||
ARG VERSION
|
||||
@@ -21,8 +21,7 @@ LABEL\
|
||||
org.opencontainers.image.version=$VERSION
|
||||
|
||||
# Update certificates.
|
||||
RUN apk --no-cache --update add ca-certificates libcap tzdata && \
|
||||
rm -rf /var/cache/apk/* && \
|
||||
RUN apk --no-cache add ca-certificates libcap tzdata && \
|
||||
mkdir -p /opt/adguardhome/conf /opt/adguardhome/work && \
|
||||
chown -R nobody: /opt/adguardhome
|
||||
|
||||
|
||||
@@ -50,6 +50,11 @@ readonly docker_image_name
|
||||
|
||||
# Set DOCKER_OUTPUT to 'type=image,name=adguard/adguard-home,push=true' if you
|
||||
# want (and are allowed) to push to DockerHub.
|
||||
#
|
||||
# If you want to inspect the resulting image using commands like "docker image
|
||||
# ls", change type to docker and also set docker_platforms to a single platform.
|
||||
#
|
||||
# See https://github.com/docker/buildx/issues/166.
|
||||
docker_output="${DOCKER_OUTPUT:-type=image,name=${docker_image_name},push=false}"
|
||||
readonly docker_output
|
||||
|
||||
|
||||
@@ -363,6 +363,7 @@ else
|
||||
fi
|
||||
readonly announcement_url
|
||||
|
||||
# TODO(a.garipov): Remove "selfupdate_min_version" in future versions.
|
||||
rm -f "$version_json"
|
||||
echo "{
|
||||
\"version\": \"${version}\",
|
||||
|
||||
Reference in New Issue
Block a user