diff --git a/CHANGELOG.md b/CHANGELOG.md index d4abd1cd..a9e5a664 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,12 +18,51 @@ and this project adheres to + + + +## [v0.107.20] - 2022-12-07 See also the [v0.107.20 GitHub milestone][ms-v0.107.20]. +### Security + +- Go version has been updated to prevent the possibility of exploiting the + CVE-2022-41717 and CVE-2022-41720 Go vulnerability fixed in [Go + 1.18.9][go-1.18.9]. + +### Added + +- The ability to clear the DNS cache ([#5190]). + +### Changed + +- DHCP server initialization errors are now logged at debug level if the server + itself disabled ([#4944]). + +### Fixed + +- Wrong validation error messages on the DHCP configuration page ([#5208]). +- Slow upstream checks making the API unresponsive ([#5193]). +- The TLS initialization errors preventing AdGuard Home from starting ([#5189]). + Instead, AdGuard Home disables encryption and shows an error message on the + encryption settings page in the UI, which was the intended previous behavior. +- URLs of some vetter blocklists. + +[#4944]: https://github.com/AdguardTeam/AdGuardHome/issues/4944 +[#5189]: https://github.com/AdguardTeam/AdGuardHome/issues/5189 +[#5190]: https://github.com/AdguardTeam/AdGuardHome/issues/5190 +[#5193]: https://github.com/AdguardTeam/AdGuardHome/issues/5193 +[#5208]: https://github.com/AdguardTeam/AdGuardHome/issues/5208 + +[go-1.18.9]: https://groups.google.com/g/golang-announce/c/L_3rmdT0BMU [ms-v0.107.20]: https://github.com/AdguardTeam/AdGuardHome/milestone/56?closed=1 ---> @@ -1443,11 +1482,12 @@ See also the [v0.104.2 GitHub milestone][ms-v0.104.2]. -[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.19...HEAD +[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.20...HEAD +[v0.107.20]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.19...v0.107.20 [v0.107.19]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.18...v0.107.19 [v0.107.18]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.17...v0.107.18 [v0.107.17]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.16...v0.107.17 diff --git a/bamboo-specs/release.yaml b/bamboo-specs/release.yaml index 86cc16ec..a43dca2b 100644 --- a/bamboo-specs/release.yaml +++ b/bamboo-specs/release.yaml @@ -7,7 +7,7 @@ # Make sure to sync any changes with the branch overrides below. 'variables': 'channel': 'edge' - 'dockerGo': 'adguard/golang-ubuntu:5.3' + 'dockerGo': 'adguard/golang-ubuntu:5.4' 'stages': - 'Build frontend': @@ -322,7 +322,7 @@ # need to build a few of these. 'variables': 'channel': 'beta' - 'dockerGo': 'adguard/golang-ubuntu:5.3' + 'dockerGo': 'adguard/golang-ubuntu:5.4' # release-vX.Y.Z branches are the branches from which the actual final release # is built. - '^release-v[0-9]+\.[0-9]+\.[0-9]+': @@ -337,4 +337,4 @@ # are the ones that actually get released. 'variables': 'channel': 'release' - 'dockerGo': 'adguard/golang-ubuntu:5.3' + 'dockerGo': 'adguard/golang-ubuntu:5.4' diff --git a/bamboo-specs/test.yaml b/bamboo-specs/test.yaml index ac8c67fd..0ea2df9c 100644 --- a/bamboo-specs/test.yaml +++ b/bamboo-specs/test.yaml @@ -5,7 +5,7 @@ 'key': 'AHBRTSPECS' 'name': 'AdGuard Home - Build and run tests' 'variables': - 'dockerGo': 'adguard/golang-ubuntu:5.3' + 'dockerGo': 'adguard/golang-ubuntu:5.4' 'stages': - 'Tests': diff --git a/client/src/__locales/be.json b/client/src/__locales/be.json index 19e45a4b..ca92d807 100644 --- a/client/src/__locales/be.json +++ b/client/src/__locales/be.json @@ -393,7 +393,7 @@ "encryption_issuer": "Выдавец", "encryption_hostnames": "Імёны хастоў", "encryption_reset": "Вы ўпэўнены, што хочаце скінуць налады шыфравання?", - "encryption_warning": "Увага", + "encryption_warning": "Папярэджанне", "topline_expiring_certificate": "Ваш SSL-сертыфікат хутка мінае. Абновіце <0>Налады шыфравання.", "topline_expired_certificate": "Ваш SSL-сертыфікат мінуў. Абновіце <0>Налады шыфравання.", "form_error_port_range": "Увядзіце нумар порта з інтэрвалу 80-65535", @@ -638,5 +638,8 @@ "safe_browsing": "Бяспечны інтэрнэт", "served_from_cache": "{{value}} (атрымана з кэша)", "form_error_password_length": "Пароль павінен быць не менш за {{value}} сімвалаў", - "anonymizer_notification": "<0>Заўвага: Ананімізацыя IP уключана. Вы можаце адключыць яго ў <1>Агульных наладах ." + "anonymizer_notification": "<0>Заўвага: Ананімізацыя IP уключана. Вы можаце адключыць яго ў <1>Агульных наладах .", + "confirm_dns_cache_clear": "Вы ўпэўнены, што хочаце ачысціць кэш DNS?", + "cache_cleared": "Кэш DNS паспяхова ачышчаны", + "clear_cache": "Ачысціць кэш" } diff --git a/client/src/__locales/cs.json b/client/src/__locales/cs.json index 15f79692..ccb3033b 100644 --- a/client/src/__locales/cs.json +++ b/client/src/__locales/cs.json @@ -638,5 +638,8 @@ "safe_browsing": "Bezpečné prohlížení", "served_from_cache": "{{value}} (převzato z mezipaměti)", "form_error_password_length": "Heslo musí být alespoň {{value}} znaků dlouhé", - "anonymizer_notification": "<0>Poznámka: Anonymizace IP je zapnuta. Můžete ji vypnout v <1>Obecných nastaveních." + "anonymizer_notification": "<0>Poznámka: Anonymizace IP je zapnuta. Můžete ji vypnout v <1>Obecných nastaveních.", + "confirm_dns_cache_clear": "Opravdu chcete vymazat mezipaměť DNS?", + "cache_cleared": "Mezipaměť DNS úspěšně vymazána", + "clear_cache": "Vymazat mezipaměť" } diff --git a/client/src/__locales/da.json b/client/src/__locales/da.json index 7a999346..ae074b89 100644 --- a/client/src/__locales/da.json +++ b/client/src/__locales/da.json @@ -638,5 +638,8 @@ "safe_browsing": "Sikker Browsing", "served_from_cache": "{{value}} (leveret fra cache)", "form_error_password_length": "Adgangskoden skal udgøre mindst {{value}} tegn.", - "anonymizer_notification": "<0>Bemærk: IP-anonymisering er aktiveret. Det kan deaktiveres via <1>Generelle indstillinger." + "anonymizer_notification": "<0>Bemærk: IP-anonymisering er aktiveret. Det kan deaktiveres via <1>Generelle indstillinger.", + "confirm_dns_cache_clear": "Sikker på, at DNS-cache skal ryddes?", + "cache_cleared": "DNS-cache hermed ryddet", + "clear_cache": "Ryd cache" } diff --git a/client/src/__locales/de.json b/client/src/__locales/de.json index d1d45e6b..34cdfb09 100644 --- a/client/src/__locales/de.json +++ b/client/src/__locales/de.json @@ -638,5 +638,8 @@ "safe_browsing": "Internetsicherheit", "served_from_cache": "{{value}} (aus dem Cache abgerufen)", "form_error_password_length": "Das Passwort muss mindestens {{value}} Zeichen enthalten", - "anonymizer_notification": "<0>Hinweis: Die IP-Anonymisierung ist aktiviert. Sie können sie in den <1>Allgemeinen Einstellungen deaktivieren." + "anonymizer_notification": "<0>Hinweis: Die IP-Anonymisierung ist aktiviert. Sie können sie in den <1>Allgemeinen Einstellungen deaktivieren.", + "confirm_dns_cache_clear": "Möchten Sie den DNS-Cache wirklich leeren?", + "cache_cleared": "DNS-Cache erfolgreich geleert", + "clear_cache": "Cache leeren" } diff --git a/client/src/__locales/en.json b/client/src/__locales/en.json index 7ec9c779..1771afbd 100644 --- a/client/src/__locales/en.json +++ b/client/src/__locales/en.json @@ -37,8 +37,6 @@ "dhcp_ipv6_settings": "DHCP IPv6 Settings", "form_error_required": "Required field", "form_error_ip4_format": "Invalid IPv4 address", - "form_error_ip4_range_start_format": "Invalid IPv4 address of the range start", - "form_error_ip4_range_end_format": "Invalid IPv4 address of the range end", "form_error_ip4_gateway_format": "Invalid IPv4 address of the gateway", "form_error_ip6_format": "Invalid IPv6 address", "form_error_ip_format": "Invalid IP address", @@ -51,9 +49,8 @@ "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", - "greater_range_end_error": "Must be greater than range end", "subnet_error": "Addresses must be in one subnet", - "gateway_or_subnet_invalid": "Subnet mask invalid", + "gateway_or_subnet_invalid": "Invalid subnet mask", "dhcp_form_gateway_input": "Gateway IP", "dhcp_form_subnet_input": "Subnet mask", "dhcp_form_range_title": "Range of IP addresses", @@ -638,5 +635,8 @@ "safe_browsing": "Safe Browsing", "served_from_cache": "{{value}} (served from cache)", "form_error_password_length": "Password must be at least {{value}} characters long", - "anonymizer_notification": "<0>Note: IP anonymization is enabled. You can disable it in <1>General settings." + "anonymizer_notification": "<0>Note: IP anonymization is enabled. You can disable it in <1>General settings.", + "confirm_dns_cache_clear": "Are you sure you want to clear DNS cache?", + "cache_cleared": "DNS cache successfully cleared", + "clear_cache": "Clear cache" } diff --git a/client/src/__locales/es.json b/client/src/__locales/es.json index dc881214..5e3aedd3 100644 --- a/client/src/__locales/es.json +++ b/client/src/__locales/es.json @@ -638,5 +638,8 @@ "safe_browsing": "Navegación segura", "served_from_cache": "{{value}} (servido desde la caché)", "form_error_password_length": "La contraseña debe tener al menos {{value}} caracteres", - "anonymizer_notification": "<0>Nota: La anonimización de IP está habilitada. Puedes deshabilitarla en <1>Configuración general." + "anonymizer_notification": "<0>Nota: La anonimización de IP está habilitada. Puedes deshabilitarla en <1>Configuración general.", + "confirm_dns_cache_clear": "¿Estás seguro de que deseas borrar la caché de DNS?", + "cache_cleared": "Caché DNS borrado con éxito", + "clear_cache": "Borrar caché" } diff --git a/client/src/__locales/fi.json b/client/src/__locales/fi.json index fd02be0e..f0d4e8cf 100644 --- a/client/src/__locales/fi.json +++ b/client/src/__locales/fi.json @@ -638,5 +638,8 @@ "safe_browsing": "Turvallinen selaus", "served_from_cache": "{{value}} (jaettu välimuistista)", "form_error_password_length": "Salasanan on oltava ainakin {{value}} merkkiä", - "anonymizer_notification": "<0>Huomioi: IP-osoitteen anonymisointi on käytössä. Voit poistaa sen käytöstä <1>Yleisistä asetuksista." + "anonymizer_notification": "<0>Huomioi: IP-osoitteen anonymisointi on käytössä. Voit poistaa sen käytöstä <1>Yleisistä asetuksista.", + "confirm_dns_cache_clear": "Haluatko varmasti tyhjentää DNS-välimuistin?", + "cache_cleared": "DNS-välimuistin tyhjennys onnistui", + "clear_cache": "Tyhjennä välimuisti" } diff --git a/client/src/__locales/fr.json b/client/src/__locales/fr.json index 310f429b..ff4aed42 100644 --- a/client/src/__locales/fr.json +++ b/client/src/__locales/fr.json @@ -638,5 +638,8 @@ "safe_browsing": "Navigation sécurisée", "served_from_cache": "{{value}} (depuis le cache)", "form_error_password_length": "Le mot de passe doit comporter au moins {{value}} caractères", - "anonymizer_notification": "<0>Note : L'anonymisation IP est activée. Vous pouvez la désactiver dans les <1>paramètres généraux." + "anonymizer_notification": "<0>Note : L'anonymisation IP est activée. Vous pouvez la désactiver dans les <1>paramètres généraux.", + "confirm_dns_cache_clear": "Voulez-vous vraiment vider le cache DNS ?", + "cache_cleared": "Le cache DNS a été vidé", + "clear_cache": "Vider le cache" } diff --git a/client/src/__locales/hr.json b/client/src/__locales/hr.json index 24f9376a..b9477551 100644 --- a/client/src/__locales/hr.json +++ b/client/src/__locales/hr.json @@ -638,5 +638,8 @@ "safe_browsing": "Sigurno surfanje", "served_from_cache": "{{value}} (dohvaćeno iz predmemorije)", "form_error_password_length": "Lozinka mora imati najmanje {{value}} znakova", - "anonymizer_notification": "<0>Napomena:IP anonimizacija je omogućena. Možete ju onemogućiti u <1>općim postavkama." + "anonymizer_notification": "<0>Napomena:IP anonimizacija je omogućena. Možete ju onemogućiti u <1>općim postavkama.", + "confirm_dns_cache_clear": "Jeste li sigurni da želite očistiti DNS predmemoriju?", + "cache_cleared": "DNS predmemorija je uspješno izbrisana", + "clear_cache": "Očisti predmemoriju" } diff --git a/client/src/__locales/hu.json b/client/src/__locales/hu.json index bf408b34..534fd69e 100644 --- a/client/src/__locales/hu.json +++ b/client/src/__locales/hu.json @@ -638,5 +638,8 @@ "safe_browsing": "Biztonságos böngészés", "served_from_cache": "{{value}} (gyorsítótárból kiszolgálva)", "form_error_password_length": "A jelszó legalább {{value}} karakter hosszú kell, hogy legyen", - "anonymizer_notification": "<0>Megjegyzés: Az IP anonimizálás engedélyezve van. Az <1>Általános beállításoknál letilthatja ." + "anonymizer_notification": "<0>Megjegyzés: Az IP anonimizálás engedélyezve van. Az <1>Általános beállításoknál letilthatja .", + "confirm_dns_cache_clear": "Biztos benne, hogy törölni szeretné a DNS-gyorsítótárat?", + "cache_cleared": "A DNS gyorsítótár sikeresen törlődött", + "clear_cache": "Gyorsítótár törlése" } diff --git a/client/src/__locales/id.json b/client/src/__locales/id.json index f285d669..97530100 100644 --- a/client/src/__locales/id.json +++ b/client/src/__locales/id.json @@ -638,5 +638,8 @@ "safe_browsing": "Penjelajahan Aman", "served_from_cache": "{{value}} (disajikan dari cache)", "form_error_password_length": "Kata sandi harus minimal {{value}} karakter", - "anonymizer_notification": "<0>Catatan: Anonimisasi IP diaktifkan. Anda dapat menonaktifkannya di <1>Pengaturan umum ." + "anonymizer_notification": "<0>Catatan: Anonimisasi IP diaktifkan. Anda dapat menonaktifkannya di <1>Pengaturan umum .", + "confirm_dns_cache_clear": "Apakah Anda yakin ingin menghapus cache DNS?", + "cache_cleared": "Cache DNS berhasil dibersihkan", + "clear_cache": "Hapus cache" } diff --git a/client/src/__locales/it.json b/client/src/__locales/it.json index f71f8a40..128fe91b 100644 --- a/client/src/__locales/it.json +++ b/client/src/__locales/it.json @@ -638,5 +638,8 @@ "safe_browsing": "Navigazione Sicura", "served_from_cache": "{{value}} (fornito dalla cache)", "form_error_password_length": "La password deve contenere almeno {{value}} caratteri", - "anonymizer_notification": "<0>Attenzione: L'anonimizzazione dell'IP è abilitata. Puoi disabilitarla in <1>Impostazioni generali." + "anonymizer_notification": "<0>Attenzione: L'anonimizzazione dell'IP è abilitata. Puoi disabilitarla in <1>Impostazioni generali.", + "confirm_dns_cache_clear": "Sei sicuro di voler cancellare la cache DNS?", + "cache_cleared": "Cache DNS è stata cancellata correttamente", + "clear_cache": "Cancella cache" } diff --git a/client/src/__locales/ja.json b/client/src/__locales/ja.json index 398b11e8..2215358d 100644 --- a/client/src/__locales/ja.json +++ b/client/src/__locales/ja.json @@ -638,5 +638,8 @@ "safe_browsing": "セーフブラウジング", "served_from_cache": "{{value}} (キャッシュから応答)", "form_error_password_length": "パスワードは{{value}}文字以上にしてください", - "anonymizer_notification": "【<0>注意】IPの匿名化が有効になっています。 <1>一般設定で無効にできます。" + "anonymizer_notification": "【<0>注意】IPの匿名化が有効になっています。 <1>一般設定で無効にできます。", + "confirm_dns_cache_clear": "DNS キャッシュをクリアしてもよろしいですか?", + "cache_cleared": "DNSキャッシュのクリア完了です。", + "clear_cache": "キャッシュをクリアする" } diff --git a/client/src/__locales/ko.json b/client/src/__locales/ko.json index de317c8a..aff7452e 100644 --- a/client/src/__locales/ko.json +++ b/client/src/__locales/ko.json @@ -638,5 +638,8 @@ "safe_browsing": "세이프 브라우징", "served_from_cache": "{{value}} (캐시에서 제공)", "form_error_password_length": "비밀번호는 {{value}}자 이상이어야 합니다", - "anonymizer_notification": "<0>참고: IP 익명화가 활성화되었습니다. <1>일반 설정에서 비활성화할 수 있습니다." + "anonymizer_notification": "<0>참고: IP 익명화가 활성화되었습니다. <1>일반 설정에서 비활성화할 수 있습니다.", + "confirm_dns_cache_clear": "정말로 DNS 캐시를 지우시겠습니까?", + "cache_cleared": "DNS 캐시를 성공적으로 지웠습니다", + "clear_cache": "캐시 지우기" } diff --git a/client/src/__locales/nl.json b/client/src/__locales/nl.json index a7722884..93566aa8 100644 --- a/client/src/__locales/nl.json +++ b/client/src/__locales/nl.json @@ -638,5 +638,8 @@ "safe_browsing": "Veilig browsen", "served_from_cache": "{{value}} (geleverd vanuit cache)", "form_error_password_length": "Wachtwoord moet minimaal {{value}} tekens lang zijn", - "anonymizer_notification": "<0>Opmerking: IP-anonimisering is ingeschakeld. Je kunt het uitschakelen in <1>Algemene instellingen." + "anonymizer_notification": "<0>Opmerking: IP-anonimisering is ingeschakeld. Je kunt het uitschakelen in <1>Algemene instellingen.", + "confirm_dns_cache_clear": "Weet je zeker dat je de DNS-cache wilt wissen?", + "cache_cleared": "DNS-cache succesvol gewist", + "clear_cache": "Cache wissen" } diff --git a/client/src/__locales/pl.json b/client/src/__locales/pl.json index d175b408..508ed9c4 100644 --- a/client/src/__locales/pl.json +++ b/client/src/__locales/pl.json @@ -638,5 +638,8 @@ "safe_browsing": "Bezpieczne przeglądanie", "served_from_cache": "{{value}} (podawane z pamięci podręcznej)", "form_error_password_length": "Hasło musi mieć co najmniej {{value}} znaków", - "anonymizer_notification": "<0>Uwaga: Anonimizacja IP jest włączona. Możesz ją wyłączyć w <1>Ustawieniach ogólnych." + "anonymizer_notification": "<0>Uwaga: Anonimizacja IP jest włączona. Możesz ją wyłączyć w <1>Ustawieniach ogólnych.", + "confirm_dns_cache_clear": "Czy na pewno chcesz wyczyścić pamięć podręczną DNS?", + "cache_cleared": "Pamięć podręczna DNS została pomyślnie wyczyszczona", + "clear_cache": "Wyczyść pamięć podręczną" } diff --git a/client/src/__locales/pt-br.json b/client/src/__locales/pt-br.json index ea2da002..cd7428ee 100644 --- a/client/src/__locales/pt-br.json +++ b/client/src/__locales/pt-br.json @@ -638,5 +638,8 @@ "safe_browsing": "Navegação segura", "served_from_cache": "{{value}} (servido do cache)", "form_error_password_length": "A senha deve ter pelo menos {{value}} caracteres", - "anonymizer_notification": "<0>Observação: A anonimização de IP está ativada. Você pode desativá-lo em <1>Configurações gerais." + "anonymizer_notification": "<0>Observação: A anonimização de IP está ativada. Você pode desativá-lo em <1>Configurações gerais.", + "confirm_dns_cache_clear": "Tem certeza de que deseja limpar o cache DNS?", + "cache_cleared": "Cache DNS limpo com sucesso", + "clear_cache": "Limpar cache" } diff --git a/client/src/__locales/pt-pt.json b/client/src/__locales/pt-pt.json index 81c92867..9905392f 100644 --- a/client/src/__locales/pt-pt.json +++ b/client/src/__locales/pt-pt.json @@ -638,5 +638,8 @@ "safe_browsing": "Navegação segura", "served_from_cache": "{{value}} (servido do cache)", "form_error_password_length": "A palavra-passe deve ter pelo menos {{value}} caracteres", - "anonymizer_notification": "<0>Observação: A anonimização de IP está ativada. Você pode desativá-la em <1>Definições gerais." + "anonymizer_notification": "<0>Observação: A anonimização de IP está ativada. Você pode desativá-la em <1>Definições gerais.", + "confirm_dns_cache_clear": "Tem certeza de que quer limpar a cache DNS?", + "cache_cleared": "O cache DNS foi apagado com sucesso", + "clear_cache": "Limpar cache" } diff --git a/client/src/__locales/ro.json b/client/src/__locales/ro.json index 82c86901..195d3f5e 100644 --- a/client/src/__locales/ro.json +++ b/client/src/__locales/ro.json @@ -638,5 +638,8 @@ "safe_browsing": "Navigare în siguranță", "served_from_cache": "{{value}} (furnizat din cache)", "form_error_password_length": "Parola trebuie să aibă cel puțin {{value}} caractere", - "anonymizer_notification": "<0>Nota: Anonimizarea IP este activată. Puteți să o dezactivați în <1>Setări generale." + "anonymizer_notification": "<0>Nota: Anonimizarea IP este activată. Puteți să o dezactivați în <1>Setări generale.", + "confirm_dns_cache_clear": "Sunteți sigur că doriți să ștergeți memoria cache DNS?", + "cache_cleared": "Cache-ul DNS a fost golit cu succes", + "clear_cache": "Goliți memoria cache" } diff --git a/client/src/__locales/ru.json b/client/src/__locales/ru.json index c1a5119b..f712bd75 100644 --- a/client/src/__locales/ru.json +++ b/client/src/__locales/ru.json @@ -638,5 +638,8 @@ "safe_browsing": "Безопасный интернет", "served_from_cache": "{{value}} (получено из кеша)", "form_error_password_length": "Пароль должен быть длиной не меньше {{value}} символов", - "anonymizer_notification": "<0>Внимание: включена анонимизация IP-адресов. Вы можете отключить её в разделе <1>Основные настройки." + "anonymizer_notification": "<0>Внимание: включена анонимизация IP-адресов. Вы можете отключить её в разделе <1>Основные настройки.", + "confirm_dns_cache_clear": "Вы уверены, что хотите очистить кеш DNS?", + "cache_cleared": "Кеш DNS успешно очищен", + "clear_cache": "Очистить кеш" } diff --git a/client/src/__locales/sk.json b/client/src/__locales/sk.json index 473557c4..b44501e6 100644 --- a/client/src/__locales/sk.json +++ b/client/src/__locales/sk.json @@ -638,5 +638,8 @@ "safe_browsing": "Bezpečné prehliadanie", "served_from_cache": "{{value}} (prevzatá z cache pamäte)", "form_error_password_length": "Heslo musí mať dĺžku aspoň {{value}} znakov", - "anonymizer_notification": "<0>Poznámka: Anonymizácia IP je zapnutá. Môžete ju vypnúť vo <1>Všeobecných nastaveniach." + "anonymizer_notification": "<0>Poznámka: Anonymizácia IP je zapnutá. Môžete ju vypnúť vo <1>Všeobecných nastaveniach.", + "confirm_dns_cache_clear": "Naozaj chcete vymazať vyrovnávaciu pamäť DNS?", + "cache_cleared": "Vyrovnávacia pamäť DNS bola úspešne vymazaná", + "clear_cache": "Vymazať vyrovnávaciu pamäť" } diff --git a/client/src/__locales/sl.json b/client/src/__locales/sl.json index 7f854f0a..bb6a60c6 100644 --- a/client/src/__locales/sl.json +++ b/client/src/__locales/sl.json @@ -638,5 +638,8 @@ "safe_browsing": "Varno brskanje", "served_from_cache": "{{value}} (postreženo iz predpomnilnika)", "form_error_password_length": "Geslo mora vsebovati najmanj {{value}} znakov", - "anonymizer_notification": "<0>Opomba: Anonimizacija IP je omogočena. Onemogočite ga lahko v <1>Splošnih nastavitvah." + "anonymizer_notification": "<0>Opomba: Anonimizacija IP je omogočena. Onemogočite ga lahko v <1>Splošnih nastavitvah.", + "confirm_dns_cache_clear": "Ali ste prepričani, da želite počistiti predpomnilnik DNS?", + "cache_cleared": "Predpomnilnik DNS je bil uspešno počiščen", + "clear_cache": "Počisti predpomnilnik" } diff --git a/client/src/__locales/sr-cs.json b/client/src/__locales/sr-cs.json index 9b676752..53b0c3e5 100644 --- a/client/src/__locales/sr-cs.json +++ b/client/src/__locales/sr-cs.json @@ -638,5 +638,8 @@ "safe_browsing": "Sigurno pregledanje", "served_from_cache": "{{value}} (posluženo iz predmemorije)", "form_error_password_length": "Lozinka mora imati najmanje {{value}} znakova", - "anonymizer_notification": "<0>Nota: IP prepoznavanje je omogućeno. Možete ga onemogućiti u opštim <1>postavkama." + "anonymizer_notification": "<0>Nota: IP prepoznavanje je omogućeno. Možete ga onemogućiti u opštim <1>postavkama.", + "confirm_dns_cache_clear": "Želite li zaista da obrišite DNS keš?", + "cache_cleared": "DNS keš je uspešno očišćen", + "clear_cache": "Obriši keš memoriju" } diff --git a/client/src/__locales/sv.json b/client/src/__locales/sv.json index 50c12d07..7715211f 100644 --- a/client/src/__locales/sv.json +++ b/client/src/__locales/sv.json @@ -638,5 +638,8 @@ "safe_browsing": "Säker surfning", "served_from_cache": "{{value}} (levereras från cache)", "form_error_password_length": "Lösenordet måste vara minst {{value}} tecken långt", - "anonymizer_notification": "<0>Observera: IP-anonymisering är aktiverad. Du kan inaktivera den i <1>Allmänna inställningar." + "anonymizer_notification": "<0>Observera: IP-anonymisering är aktiverad. Du kan inaktivera den i <1>Allmänna inställningar.", + "confirm_dns_cache_clear": "Är du säker på att du vill rensa DNS-cache?", + "cache_cleared": "DNS-cacheminnet har rensats", + "clear_cache": "Rensa cache" } diff --git a/client/src/__locales/tr.json b/client/src/__locales/tr.json index a24028f1..5396eb52 100644 --- a/client/src/__locales/tr.json +++ b/client/src/__locales/tr.json @@ -638,5 +638,8 @@ "safe_browsing": "Güvenli Gezinti", "served_from_cache": "{{value}} (önbellekten kullanıldı)", "form_error_password_length": "Parola en az {{value}} karakter uzunluğunda olmalıdır", - "anonymizer_notification": "<0>Not: IP anonimleştirme etkinleştirildi. Bunu <1>Genel ayarlardan devre dışı bırakabilirsiniz." + "anonymizer_notification": "<0>Not: IP anonimleştirme etkinleştirildi. Bunu <1>Genel ayarlardan devre dışı bırakabilirsiniz.", + "confirm_dns_cache_clear": "DNS önbelleğini temizlemek istediğinizden emin misiniz?", + "cache_cleared": "DNS önbelleği başarıyla temizlendi", + "clear_cache": "Önbelleği temizle" } diff --git a/client/src/__locales/uk.json b/client/src/__locales/uk.json index 1041ae8d..797c769b 100644 --- a/client/src/__locales/uk.json +++ b/client/src/__locales/uk.json @@ -393,7 +393,7 @@ "encryption_issuer": "Видавець", "encryption_hostnames": "Назви вузлів", "encryption_reset": "Ви впевнені, що хочете скинути налаштування шифрування?", - "encryption_warning": "Увага", + "encryption_warning": "Попередження", "topline_expiring_certificate": "Ваш сертифікат SSL скоро закінчиться. Оновіть <0>Налаштування шифрування.", "topline_expired_certificate": "Термін дії вашого сертифіката SSL закінчився. Оновіть <0>Налаштування шифрування.", "form_error_port_range": "Введіть значення порту в діапазоні 80−65535", @@ -638,5 +638,8 @@ "safe_browsing": "Безпечний перегляд", "served_from_cache": "{{value}} (отримано з кешу)", "form_error_password_length": "Пароль мусить мати принаймні {{value}} символів", - "anonymizer_notification": "<0>Примітка: IP-анонімізацію ввімкнено. Ви можете вимкнути його в <1>Загальні налаштування ." + "anonymizer_notification": "<0>Примітка: IP-анонімізацію ввімкнено. Ви можете вимкнути його в <1>Загальні налаштування .", + "confirm_dns_cache_clear": "Ви впевнені, що бажаєте очистити кеш DNS?", + "cache_cleared": "Кеш DNS успішно очищено", + "clear_cache": "Очистити кеш" } diff --git a/client/src/__locales/vi.json b/client/src/__locales/vi.json index 7674d372..174c57b2 100644 --- a/client/src/__locales/vi.json +++ b/client/src/__locales/vi.json @@ -638,5 +638,8 @@ "safe_browsing": "Duyệt web an toàn", "served_from_cache": "{{value}} (được phục vụ từ bộ nhớ cache)", "form_error_password_length": "Mật khẩu phải có ít nhất {{value}} ký tự", - "anonymizer_notification": "<0> Lưu ý: Tính năng ẩn danh IP được bật. Bạn có thể tắt nó trong <1> Cài đặt chung." + "anonymizer_notification": "<0> Lưu ý: Tính năng ẩn danh IP được bật. Bạn có thể tắt nó trong <1> Cài đặt chung.", + "confirm_dns_cache_clear": "Bạn có chắc chắn muốn xóa bộ đệm ẩn DNS không?", + "cache_cleared": "Đã xóa thành công bộ đệm DNS", + "clear_cache": "Xóa bộ nhớ cache" } diff --git a/client/src/__locales/zh-cn.json b/client/src/__locales/zh-cn.json index 5a19d904..65ddd0cc 100644 --- a/client/src/__locales/zh-cn.json +++ b/client/src/__locales/zh-cn.json @@ -638,5 +638,8 @@ "safe_browsing": "安全浏览", "served_from_cache": "{{value}}(由缓存提供)", "form_error_password_length": "密码必须至少有 {{value}} 个字符", - "anonymizer_notification": "<0>注意: IP 匿名化已启用。您可以在<1>常规设置中禁用它。" + "anonymizer_notification": "<0>注意: IP 匿名化已启用。您可以在<1>常规设置中禁用它。", + "confirm_dns_cache_clear": "您确定要清除 DNS 缓存吗?", + "cache_cleared": "已成功清除 DNS 缓存", + "clear_cache": "清除缓存" } diff --git a/client/src/__locales/zh-tw.json b/client/src/__locales/zh-tw.json index e1ae9b0a..da8f815f 100644 --- a/client/src/__locales/zh-tw.json +++ b/client/src/__locales/zh-tw.json @@ -638,5 +638,8 @@ "safe_browsing": "安全瀏覽", "served_from_cache": "{{value}} (由快取提供)", "form_error_password_length": "密碼必須為至少長 {{value}} 個字元", - "anonymizer_notification": "<0>注意:IP 匿名化被啟用。您可在<1>一般設定中禁用它。" + "anonymizer_notification": "<0>注意:IP 匿名化被啟用。您可在<1>一般設定中禁用它。", + "confirm_dns_cache_clear": "您確定您想要清除 DNS 快取嗎?", + "cache_cleared": "DNS 快取被成功地清除", + "clear_cache": "清除快取" } diff --git a/client/src/actions/dnsConfig.js b/client/src/actions/dnsConfig.js index a0b93428..f8141fd5 100644 --- a/client/src/actions/dnsConfig.js +++ b/client/src/actions/dnsConfig.js @@ -1,4 +1,5 @@ import { createAction } from 'redux-actions'; +import i18next from 'i18next'; import apiClient from '../api/Api'; import { splitByNewLine } from '../helpers/helpers'; @@ -19,6 +20,22 @@ export const getDnsConfig = () => async (dispatch) => { } }; +export const clearDnsCacheRequest = createAction('CLEAR_DNS_CACHE_REQUEST'); +export const clearDnsCacheFailure = createAction('CLEAR_DNS_CACHE_FAILURE'); +export const clearDnsCacheSuccess = createAction('CLEAR_DNS_CACHE_SUCCESS'); + +export const clearDnsCache = () => async (dispatch) => { + dispatch(clearDnsCacheRequest()); + try { + const data = await apiClient.clearCache(); + dispatch(clearDnsCacheSuccess(data)); + dispatch(addSuccessToast(i18next.t('cache_cleared'))); + } catch (error) { + dispatch(addErrorToast({ error })); + dispatch(clearDnsCacheFailure()); + } +}; + export const setDnsConfigRequest = createAction('SET_DNS_CONFIG_REQUEST'); export const setDnsConfigFailure = createAction('SET_DNS_CONFIG_FAILURE'); export const setDnsConfigSuccess = createAction('SET_DNS_CONFIG_SUCCESS'); diff --git a/client/src/api/Api.js b/client/src/api/Api.js index bc030fa1..06f40c6c 100644 --- a/client/src/api/Api.js +++ b/client/src/api/Api.js @@ -593,6 +593,14 @@ class Api { }; return this.makeRequest(path, method, config); } + + // Cache + CLEAR_CACHE = { path: 'cache_clear', method: 'POST' }; + + clearCache() { + const { path, method } = this.CLEAR_CACHE; + return this.makeRequest(path, method); + } } const apiClient = new Api(); diff --git a/client/src/components/Settings/Dhcp/FormDHCPv4.js b/client/src/components/Settings/Dhcp/FormDHCPv4.js index cb371f9f..1c3d9c60 100644 --- a/client/src/components/Settings/Dhcp/FormDHCPv4.js +++ b/client/src/components/Settings/Dhcp/FormDHCPv4.js @@ -74,7 +74,6 @@ const FormDHCPv4 = ({ className="form-control" placeholder={t(ipv4placeholders.subnet_mask)} validate={[ - validateIpv4, validateRequired, validateGatewaySubnetMask, ]} @@ -97,7 +96,6 @@ const FormDHCPv4 = ({ placeholder={t(ipv4placeholders.range_start)} validate={[ validateIpv4, - validateGatewaySubnetMask, validateIpForGatewaySubnetMask, ]} disabled={!isInterfaceIncludesIpv4} @@ -113,7 +111,6 @@ const FormDHCPv4 = ({ validate={[ validateIpv4, validateIpv4RangeEnd, - validateGatewaySubnetMask, validateIpForGatewaySubnetMask, ]} disabled={!isInterfaceIncludesIpv4} diff --git a/client/src/components/Settings/Dns/Cache/Form.js b/client/src/components/Settings/Dns/Cache/Form.js index 5b0fe6a2..0f51e117 100644 --- a/client/src/components/Settings/Dns/Cache/Form.js +++ b/client/src/components/Settings/Dns/Cache/Form.js @@ -2,10 +2,12 @@ import React from 'react'; import PropTypes from 'prop-types'; import { Field, reduxForm } from 'redux-form'; import { Trans, useTranslation } from 'react-i18next'; -import { shallowEqual, useSelector } from 'react-redux'; +import { shallowEqual, useDispatch, useSelector } from 'react-redux'; + import { renderInputField, toNumber, CheckboxField } from '../../../../helpers/form'; import { CACHE_CONFIG_FIELDS, FORM_NAME, UINT32_RANGE } from '../../../../helpers/constants'; import { replaceZeroWithEmptyString } from '../../../../helpers/helpers'; +import { clearDnsCache } from '../../../../actions/dnsConfig'; const INPUTS_FIELDS = [ { @@ -32,6 +34,7 @@ const Form = ({ handleSubmit, submitting, invalid, }) => { const { t } = useTranslation(); + const dispatch = useDispatch(); const { processingSetConfig } = useSelector((state) => state.dnsConfig, shallowEqual); const { @@ -40,6 +43,12 @@ const Form = ({ const minExceedsMax = cache_ttl_min > cache_ttl_max; + const handleClearCache = () => { + if (window.confirm(t('confirm_dns_cache_clear'))) { + dispatch(clearDnsCache()); + } + }; + return
{INPUTS_FIELDS.map(({ @@ -97,6 +106,13 @@ const Form = ({ > save_btn + ; }; diff --git a/client/src/helpers/filters/filters.js b/client/src/helpers/filters/filters.js index 8be075e9..5a5ffad7 100644 --- a/client/src/helpers/filters/filters.js +++ b/client/src/helpers/filters/filters.js @@ -26,199 +26,199 @@ export default { "name": "1Hosts (Lite)", "categoryId": "general", "homepage": "https://badmojr.github.io/1Hosts/", - "source": "https://badmojr.gitlab.io/1hosts/Lite/adblock.txt" + "source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_24.txt" }, "CHN_adrules": { "name": "CHN: AdRules DNS List", "categoryId": "regional", "homepage": "https://github.com/Cats-Team/AdRules", - "source": "https://raw.githubusercontent.com/Cats-Team/AdRules/main/dns.txt" + "source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_29.txt" }, "CHN_anti_ad": { "name": "CHN: anti-AD", "categoryId": "regional", "homepage": "https://anti-ad.net/", - "source": "https://anti-ad.net/easylist.txt" + "source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_21.txt" }, "IDN_abpindo": { "name": "IDN: ABPindo", "categoryId": "regional", "homepage": "https://github.com/ABPindo/indonesianadblockrules", - "source": "https://raw.githubusercontent.com/ABPindo/indonesianadblockrules/master/subscriptions/aghome.txt" + "source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_22.txt" }, "IRN_unwanted_iranian_domains": { "name": "IRN: PersianBlocker list", "categoryId": "regional", "homepage": "https://github.com/MasterKia/PersianBlocker", - "source": "https://raw.githubusercontent.com/MasterKia/PersianBlocker/main/PersianBlockerHosts.txt" + "source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_19.txt" }, "ITA_filtri_dns": { "name": "ITA: Filtri-DNS", "categoryId": "regional", "homepage": "https://filtri-dns.ga/", - "source": "https://filtri-dns.ga/filtri.txt" + "source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_18.txt" }, "KOR_list_kr": { "name": "KOR: List-KR DNS", "categoryId": "regional", "homepage": "https://github.com/List-KR/List-KR", - "source": "https://github.com/List-KR/List-KR" + "source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_25.txt" }, "KOR_youslist": { "name": "KOR: YousList", "categoryId": "regional", "homepage": "https://github.com/yous/YousList", - "source": "https://raw.githubusercontent.com/yous/YousList/master/hosts.txt" + "source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_15.txt" }, "MKD_macedonian_pi_hole_blocklist": { "name": "MKD: Macedonian Pi-hole Blocklist", "categoryId": "regional", "homepage": "https://github.com/cchevy/macedonian-pi-hole-blocklist", - "source": "https://raw.githubusercontent.com/cchevy/macedonian-pi-hole-blocklist/master/hosts.txt" + "source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_20.txt" }, "NOR_dandelion_sprouts_anti_malware_list": { "name": "NOR: Dandelion Sprouts nordiske filtre", "categoryId": "regional", "homepage": "https://github.com/DandelionSprout/adfilt", - "source": "https://raw.githubusercontent.com/DandelionSprout/adfilt/master/NorwegianExperimentalList%20alternate%20versions/NordicFiltersAdGuardHome.txt" + "source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_13.txt" }, "POL_polish_filters_for_pi_hole": { "name": "POL: Polish filters for Pi hole", "categoryId": "regional", "homepage": "https://www.certyficate.it/", - "source": "https://raw.githubusercontent.com/MajkiIT/polish-ads-filter/master/polish-pihole-filters/hostfile.txt" + "source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_14.txt" }, "SWE_frellwit_swedish_hosts_file": { "name": "SWE: Frellwit's Swedish Hosts File", "categoryId": "regional", "homepage": "https://github.com/lassekongo83/Frellwits-filter-lists/", - "source": "https://raw.githubusercontent.com/lassekongo83/Frellwits-filter-lists/master/Frellwits-Swedish-Hosts-File.txt" + "source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_17.txt" }, "TUR_turk_adlist": { "name": "TUR: turk-adlist", "categoryId": "regional", "homepage": "https://github.com/bkrucarci/turk-adlist", - "source": "https://raw.githubusercontent.com/bkrucarci/turk-adlist/master/hosts" + "source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_26.txt" }, "VNM_abpvn": { "name": "VNM: ABPVN List", "categoryId": "regional", "homepage": "http://abpvn.com/", - "source": "https://abpvn.com/android/abpvn.txt" + "source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_16.txt" }, "adguard_dns_filter": { "name": "AdGuard DNS filter", "categoryId": "general", "homepage": "https://github.com/AdguardTeam/AdGuardSDNSFilter", - "source": "https://adguardteam.github.io/AdGuardSDNSFilter/Filters/filter.txt" + "source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_1.txt" }, "adway_default_blocklist": { "name": "AdAway Default Blocklist", "categoryId": "general", "homepage": "https://github.com/AdAway/adaway.github.io/", - "source": "https://adaway.org/hosts.txt" + "source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_2.txt" }, "curben_phishing_filter": { "name": "Phishing URL Blocklist (PhishTank and OpenPhish)", "categoryId": "security", "homepage": "https://gitlab.com/malware-filter/phishing-filter", - "source": "https://malware-filter.gitlab.io/malware-filter/phishing-filter-agh.txt" + "source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_30.txt" }, "dan_pollocks_list": { "name": "Dan Pollock's List", "categoryId": "general", "homepage": "https://someonewhocares.org/", - "source": "https://someonewhocares.org/hosts/zero/hosts" + "source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_4.txt" }, "dandelion_sprouts_anti_malware_list": { "name": "Dandelion Sprout's Anti-Malware List", "categoryId": "security", "homepage": "https://github.com/DandelionSprout/adfilt", - "source": "https://raw.githubusercontent.com/DandelionSprout/adfilt/master/Alternate%20versions%20Anti-Malware%20List/AntiMalwareAdGuardHome.txt" + "source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_12.txt" }, "dandelion_sprouts_game_console_adblock_list": { "name": "Dandelion Sprout's Game Console Adblock List", "categoryId": "other", "homepage": "https://github.com/DandelionSprout/adfilt", - "source": "https://raw.githubusercontent.com/DandelionSprout/adfilt/master/GameConsoleAdblockList.txt" + "source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_6.txt" }, "energized_spark": { "name": "Energized Spark", "categoryId": "general", "homepage": "https://energized.pro/", - "source": "https://block.energized.pro/spark/formats/filter" + "source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_28.txt" }, "nocoin_filter_list": { "name": "NoCoin Filter List", "categoryId": "security", "homepage": "https://github.com/hoshsadiq/adblock-nocoin-list/", - "source": "https://raw.githubusercontent.com/hoshsadiq/adblock-nocoin-list/master/hosts.txt" + "source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_8.txt" }, "notracking_hosts_blocklists": { "name": "The NoTracking blocklist", "categoryId": "general", "homepage": "https://github.com/notracking/hosts-blocklists", - "source": "https://raw.githubusercontent.com/notracking/hosts-blocklists/master/adblock/adblock.txt" + "source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_32.txt" }, "oisd_basic": { "name": "OISD Blocklist Basic", "categoryId": "general", "homepage": "https://oisd.nl/", - "source": "https://abp.oisd.nl/basic/" + "source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_5.txt" }, "oisd_full": { "name": "OISD Blocklist Full", "categoryId": "general", "homepage": "https://oisd.nl/", - "source": "https://abp.oisd.nl/" + "source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_27.txt" }, "perflyst_dandelion_sprout_smart_tv_blocklist_for_adguard_home": { "name": "Perflyst and Dandelion Sprout's Smart-TV Blocklist", "categoryId": "other", "homepage": "https://github.com/Perflyst/PiHoleBlocklist", - "source": "https://raw.githubusercontent.com/Perflyst/PiHoleBlocklist/master/SmartTV-AGH.txt" + "source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_7.txt" }, "peter_lowe_list": { "name": "Peter Lowe's Blocklist", "categoryId": "general", "homepage": "https://pgl.yoyo.org/adservers/", - "source": "https://pgl.yoyo.org/adservers/serverlist.php?hostformat=adblockplus\u0026showintro=1\u0026mimetype=plaintext" + "source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_3.txt" }, "scam_blocklist_by_durablenapkin": { "name": "Scam Blocklist by DurableNapkin", "categoryId": "security", "homepage": "https://github.com/durablenapkin/scamblocklist", - "source": "https://raw.githubusercontent.com/durablenapkin/scamblocklist/master/adguard.txt" + "source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_10.txt" }, "staklerware_indicators_list": { "name": "Stalkerware Indicators List", "categoryId": "security", "homepage": "https://github.com/AssoEchap/stalkerware-indicators", - "source": "https://raw.githubusercontent.com/AssoEchap/stalkerware-indicators/master/generated/hosts" + "source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_31.txt" }, "steven_blacks_list": { "name": "Steven Black's List", "categoryId": "general", "homepage": "https://github.com/StevenBlack/hosts", - "source": "https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts" + "source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_33.txt" }, "the_big_list_of_hacked_malware_web_sites": { "name": "The Big List of Hacked Malware Web Sites", "categoryId": "security", "homepage": "https://github.com/mitchellkrogza/The-Big-List-of-Hacked-Malware-Web-Sites", - "source": "https://raw.githubusercontent.com/mitchellkrogza/The-Big-List-of-Hacked-Malware-Web-Sites/master/hosts" + "source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_9.txt" }, "urlhaus_filter_online": { "name": "Malicious URL Blocklist (URLHaus)", "categoryId": "security", "homepage": "https://gitlab.com/malware-filter/urlhaus-filter", - "source": "https://malware-filter.gitlab.io/malware-filter/urlhaus-filter-agh.txt" + "source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_11.txt" }, "windowsspyblocker_hosts_spy_rules": { "name": "WindowsSpyBlocker - Hosts spy rules", "categoryId": "other", "homepage": "https://github.com/crazy-max/WindowsSpyBlocker", - "source": "https://raw.githubusercontent.com/crazy-max/WindowsSpyBlocker/master/data/hosts/spy.txt" + "source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_23.txt" } } } diff --git a/client/src/helpers/validators.js b/client/src/helpers/validators.js index ed274416..e7849639 100644 --- a/client/src/helpers/validators.js +++ b/client/src/helpers/validators.js @@ -77,11 +77,11 @@ export const validateNotInRange = (value, allValues) => { const { range_start, range_end } = allValues.v4; if (range_start && validateIpv4(range_start)) { - return 'form_error_ip4_range_start_format'; + return undefined; } if (range_end && validateIpv4(range_end)) { - return 'form_error_ip4_range_end_format'; + return undefined; } const isAboveMin = range_start && ip4ToInt(value) >= ip4ToInt(range_start); @@ -94,14 +94,6 @@ export const validateNotInRange = (value, allValues) => { }); } - if (!range_end && isAboveMin) { - return 'lower_range_start_error'; - } - - if (!range_start && isBelowMax) { - return 'greater_range_end_error'; - } - return undefined; }; @@ -118,7 +110,7 @@ export const validateGatewaySubnetMask = (_, allValues) => { const { subnet_mask, gateway_ip } = allValues.v4; if (validateIpv4(gateway_ip)) { - return 'form_error_ip4_gateway_format'; + return 'gateway_or_subnet_invalid'; } return parseSubnetMask(subnet_mask) ? undefined : 'gateway_or_subnet_invalid'; @@ -138,6 +130,10 @@ export const validateIpForGatewaySubnetMask = (value, allValues) => { gateway_ip, subnet_mask, } = allValues.v4; + if ((gateway_ip && validateIpv4(gateway_ip)) || (subnet_mask && validateIpv4(subnet_mask))) { + return undefined; + } + const subnetPrefix = parseSubnetMask(subnet_mask); if (!isIpInCidr(value, `${gateway_ip}/${subnetPrefix}`)) { diff --git a/go.mod b/go.mod index 3040b862..0e94bb90 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/AdguardTeam/AdGuardHome go 1.18 require ( - github.com/AdguardTeam/dnsproxy v0.46.2 + github.com/AdguardTeam/dnsproxy v0.46.4 github.com/AdguardTeam/golibs v0.11.3 github.com/AdguardTeam/urlfilter v0.16.0 github.com/NYTimes/gziphandler v1.1.1 @@ -18,7 +18,7 @@ require ( github.com/google/uuid v1.3.0 github.com/insomniacslk/dhcp v0.0.0-20221001123530-5308ebe5334c github.com/kardianos/service v1.2.2 - github.com/lucas-clemente/quic-go v0.29.2 + github.com/lucas-clemente/quic-go v0.31.0 github.com/mdlayher/ethernet v0.0.0-20220221185849-529eae5b6118 github.com/mdlayher/netlink v1.6.2 // TODO(a.garipov): This package is deprecated; find a new one or use @@ -47,16 +47,14 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect github.com/golang/mock v1.6.0 // indirect + github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect github.com/josharian/native v1.0.0 // indirect github.com/marten-seemann/qpack v0.3.0 // indirect github.com/marten-seemann/qtls-go1-18 v0.1.3 // indirect github.com/marten-seemann/qtls-go1-19 v0.1.1 // indirect github.com/mdlayher/packet v1.0.0 // indirect github.com/mdlayher/socket v0.2.3 // indirect - github.com/nxadm/tail v1.4.8 // indirect - github.com/onsi/ginkgo v1.16.5 // indirect github.com/onsi/ginkgo/v2 v2.5.0 // indirect - github.com/onsi/gomega v1.24.0 // indirect github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect @@ -65,5 +63,4 @@ require ( golang.org/x/sync v0.1.0 // indirect golang.org/x/text v0.4.0 // indirect golang.org/x/tools v0.2.0 // indirect - gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect ) diff --git a/go.sum b/go.sum index e8b95c59..091a84e7 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/AdguardTeam/dnsproxy v0.46.2 h1:ZUKM713Ts5meYQqk6cJkUBMCFSWqFPXTgjXkN4RI1Vo= -github.com/AdguardTeam/dnsproxy v0.46.2/go.mod h1:PAmRzFqls0E92XTglyY2ESAqMAzZJhHKErG1ZpRnpjA= +github.com/AdguardTeam/dnsproxy v0.46.4 h1:/+wnTG0q2TkGQyA1PeSsjv4/f5ZprGduKPSoOcG+rOU= +github.com/AdguardTeam/dnsproxy v0.46.4/go.mod h1:yYDMAH6ay2PxLcLtfVM3FUiyv/U9B/zYO+cIIssuJNU= github.com/AdguardTeam/golibs v0.4.0/go.mod h1:skKsDKIBB7kkFflLJBpfGX+G8QFTx0WKUzB6TIgtUj4= github.com/AdguardTeam/golibs v0.10.4/go.mod h1:rSfQRGHIdgfxriDDNgNJ7HmE5zRoURq8R+VdR81Zuzw= github.com/AdguardTeam/golibs v0.11.3 h1:Oif+REq2WLycQ2Xm3ZPmJdfftptss0HbGWbxdFaC310= @@ -25,6 +25,9 @@ github.com/beefsack/go-rate v0.0.0-20220214233405-116f4ca011a0 h1:0b2vaepXIfMsG+ github.com/beefsack/go-rate v0.0.0-20220214233405-116f4ca011a0/go.mod h1:6YNgTHLutezwnBvyneBbwvB8C82y3dcoOj5EQJIdGXA= github.com/bluele/gcache v0.0.2 h1:WcbfdXICg7G/DGBh1PFfcirkWOQV+v077yF1pSy3DGw= github.com/bluele/gcache v0.0.2/go.mod h1:m15KV+ECjptwSPxKhOhQoAFQVtUFjTVkc3H8o0t/fp0= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -34,8 +37,6 @@ github.com/digineo/go-ipset/v2 v2.2.1/go.mod h1:wBsNzJlZlABHUITkesrggFnZQtgW5wkq github.com/dimfeld/httptreemux/v5 v5.5.0 h1:p8jkiMrCuZ0CmhwYLcbNbl7DDo21fozhKHQ2PccwOFQ= github.com/dimfeld/httptreemux/v5 v5.5.0/go.mod h1:QeEylH57C0v3VO0tkKraVz9oD3Uu93CKPnTLbsidvSw= github.com/fanliao/go-promise v0.0.0-20141029170127-1890db352a72/go.mod h1:PjfxuH4FZdUyfMdtBio2lsRr1AKEaVPwelzuHuh8Lqc= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= @@ -47,13 +48,6 @@ github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8Wd github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -66,15 +60,17 @@ github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v1.0.1 h1:Lh/jXZmvZxb0BBeSY5VKEfidcbcbenKjZFzM/q0fSeU= github.com/google/renameio v1.0.1/go.mod h1:t/HQoYBZSsWSNK35C6CO/TpPLDVWvxOHboWUAweKUpk= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714 h1:/jC7qQFrv8CrSJVmaolDVOxTfS9kc36uB6H40kdbQq8= github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/insomniacslk/dhcp v0.0.0-20221001123530-5308ebe5334c h1:OCFM4+DXTWfNlyeoddrTwdup/ztkGSyAMR2UGcPckNQ= github.com/insomniacslk/dhcp v0.0.0-20221001123530-5308ebe5334c/go.mod h1:h+MxyHxRg9NH3terB1nfRIUaQEcI0XOVkdR9LNBlp8E= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= @@ -91,8 +87,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/lucas-clemente/quic-go v0.29.2 h1:O8Mt0O6LpvEW+wfC40vZdcw0DngwYzoxq5xULZNzSI8= -github.com/lucas-clemente/quic-go v0.29.2/go.mod h1:g6/h9YMmLuU54tL1gW25uIi3VlBp3uv+sBihplIuskE= +github.com/lucas-clemente/quic-go v0.31.0 h1:MfNp3fk0wjWRajw6quMFA3ap1AVtlU+2mtwmbVogB2M= +github.com/lucas-clemente/quic-go v0.31.0/go.mod h1:0wFbizLgYzqHqtlyxyCaJKlE7bYgE6JQ+54TLd/Dq2g= github.com/marten-seemann/qpack v0.3.0 h1:UiWstOgT8+znlkDPOg2+3rIuYXJ2CnGDkGUXN6ki6hE= github.com/marten-seemann/qpack v0.3.0/go.mod h1:cGfKPBiP4a9EQdxCwEwI/GEeWAsjSekBvx/X8mh58+g= github.com/marten-seemann/qtls-go1-18 v0.1.3 h1:R4H2Ks8P6pAtUagjFty2p7BVHn3XiwDAl7TTQf5h7TI= @@ -124,19 +120,9 @@ github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= -github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= -github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/ginkgo/v2 v2.5.0 h1:TRtrvv2vdQqzkwrQ1ke6vtXf7IK34RBUJafIy1wMwls= github.com/onsi/ginkgo/v2 v2.5.0/go.mod h1:Luc4sArBICYCS8THh8v3i3i5CuSZO+RaQRaJoeNwomw= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.24.0 h1:+0glovB9Jd6z3VR+ScSwQqXVTIfJcGA9UBM8yzQxhqg= -github.com/onsi/gomega v1.24.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -168,7 +154,6 @@ github.com/tklauser/numcpus v0.3.0/go.mod h1:yFGUr7TUHQRAhyqBcEg0Ge34zDBAsIvJJcy github.com/u-root/uio v0.0.0-20210528114334-82958018845c/go.mod h1:LpEX5FO/cB+WF4TYGY1V5qktpaZLkKkSegbr0V4eYXA= github.com/u-root/uio v0.0.0-20220204230159-dac05f7d2cb4 h1:hl6sK6aFgTLISijk6xIzeqnPzQcsLqqvL6vEfTPinME= github.com/u-root/uio v0.0.0-20220204230159-dac05f7d2cb4/go.mod h1:LpEX5FO/cB+WF4TYGY1V5qktpaZLkKkSegbr0V4eYXA= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= @@ -181,11 +166,9 @@ golang.org/x/exp v0.0.0-20221106115401-f9659909a136 h1:Fq7F/w7MAa1KJ5bt2aJ62ihqp golang.org/x/exp v0.0.0-20221106115401-f9659909a136/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I= golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190419010253-1f3472d942ba/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -195,10 +178,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201016165138-7b1cca2348c0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= @@ -208,14 +189,11 @@ golang.org/x/net v0.0.0-20210929193557-e81a3d93ecf6/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20220923203811-8be639271d50/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220923202941-7f9b1623fab7/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190322080309-f49334f85ddc/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -224,13 +202,10 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190418153312-f0ce4c0180be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606122018-79a91cf218c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -238,7 +213,6 @@ golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201017003518-b09fb700fbb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -269,7 +243,6 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE= @@ -278,26 +251,15 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/internal/aghnet/hostscontainer_windows.go b/internal/aghnet/hostscontainer_windows.go index 819ba5bb..7bbf7ac0 100644 --- a/internal/aghnet/hostscontainer_windows.go +++ b/internal/aghnet/hostscontainer_windows.go @@ -15,7 +15,7 @@ import ( func defaultHostsPaths() (paths []string) { sysDir, err := windows.GetSystemDirectory() if err != nil { - log.Error("getting system directory: %s", err) + log.Error("aghnet: getting system directory: %s", err) return []string{} } diff --git a/internal/aghos/os.go b/internal/aghos/os.go index 26201df2..67496c78 100644 --- a/internal/aghos/os.go +++ b/internal/aghos/os.go @@ -168,11 +168,11 @@ func IsOpenWrt() (ok bool) { return isOpenWrt() } -// RootDirFS returns the fs.FS rooted at the operating system's root. +// RootDirFS returns the [fs.FS] rooted at the operating system's root. On +// Windows it returns the fs.FS rooted at the volume of the system directory +// (usually, C:). func RootDirFS() (fsys fs.FS) { - // Use empty string since os.DirFS implicitly prepends a slash to it. This - // behavior is undocumented but it currently works. - return os.DirFS("") + return rootDirFS() } // NotifyReconfigureSignal notifies c on receiving reconfigure signals. diff --git a/internal/aghos/os_unix.go b/internal/aghos/os_unix.go index 7e04f0c0..6dcc5717 100644 --- a/internal/aghos/os_unix.go +++ b/internal/aghos/os_unix.go @@ -3,12 +3,17 @@ package aghos import ( + "io/fs" "os" "os/signal" "golang.org/x/sys/unix" ) +func rootDirFS() (fsys fs.FS) { + return os.DirFS("/") +} + func notifyReconfigureSignal(c chan<- os.Signal) { signal.Notify(c, unix.SIGHUP) } diff --git a/internal/aghos/os_windows.go b/internal/aghos/os_windows.go index 616fcf46..5568ef4c 100644 --- a/internal/aghos/os_windows.go +++ b/internal/aghos/os_windows.go @@ -3,13 +3,29 @@ package aghos import ( + "io/fs" "os" "os/signal" + "path/filepath" "syscall" + "github.com/AdguardTeam/golibs/log" "golang.org/x/sys/windows" ) +func rootDirFS() (fsys fs.FS) { + // TODO(a.garipov): Use a better way if golang/go#44279 is ever resolved. + sysDir, err := windows.GetSystemDirectory() + if err != nil { + log.Error("aghos: getting root filesystem: %s; using C:", err) + + // Assume that C: is the safe default. + return os.DirFS("C:") + } + + return os.DirFS(filepath.VolumeName(sysDir)) +} + func setRlimit(val uint64) (err error) { return Unsupported("setrlimit") } diff --git a/internal/dhcpd/config.go b/internal/dhcpd/config.go index 718d567f..8b255f76 100644 --- a/internal/dhcpd/config.go +++ b/internal/dhcpd/config.go @@ -137,14 +137,14 @@ func (c *V4ServerConf) Validate() (err error) { gatewayIP, err := ensureV4(c.GatewayIP, "address") if err != nil { - // Don't wrap an errors since it's informative enough as is and there is + // Don't wrap the error since it's informative enough as is and there is // an annotation deferred already. return err } subnetMask, err := ensureV4(c.SubnetMask, "subnet mask") if err != nil { - // Don't wrap an errors since it's informative enough as is and there is + // Don't wrap the error since it's informative enough as is and there is // an annotation deferred already. return err } @@ -155,20 +155,21 @@ func (c *V4ServerConf) Validate() (err error) { rangeStart, err := ensureV4(c.RangeStart, "address") if err != nil { - // Don't wrap an errors since it's informative enough as is and there is + // Don't wrap the error since it's informative enough as is and there is // an annotation deferred already. return err } + rangeEnd, err := ensureV4(c.RangeEnd, "address") if err != nil { - // Don't wrap an errors since it's informative enough as is and there is + // Don't wrap the error since it's informative enough as is and there is // an annotation deferred already. return err } c.ipRange, err = newIPRange(rangeStart.AsSlice(), rangeEnd.AsSlice()) if err != nil { - // Don't wrap an errors since it's informative enough as is and there is + // Don't wrap the error since it's informative enough as is and there is // an annotation deferred already. return err } diff --git a/internal/dhcpd/dhcpd.go b/internal/dhcpd/dhcpd.go index ea7725a9..3602b4db 100644 --- a/internal/dhcpd/dhcpd.go +++ b/internal/dhcpd/dhcpd.go @@ -219,8 +219,6 @@ var _ Interface = (*server)(nil) // Create initializes and returns the DHCP server handling both address // families. It also registers the corresponding HTTP API endpoints. -// -// TODO(e.burkov): Don't register handlers, see TODO on [aghhttp.RegisterFunc]. func Create(conf *ServerConfig) (s *server, err error) { s = &server{ conf: &ServerConfig{ @@ -237,6 +235,8 @@ func Create(conf *ServerConfig) (s *server, err error) { }, } + // TODO(e.burkov): Don't register handlers, see TODO on + // [aghhttp.RegisterFunc]. s.registerHandlers() v4conf := conf.Conf4 @@ -250,7 +250,7 @@ func Create(conf *ServerConfig) (s *server, err error) { return nil, fmt.Errorf("creating dhcpv4 srv: %w", err) } - log.Error("creating dhcpv4 srv: %s", err) + log.Debug("dhcpd: warning: creating dhcpv4 srv: %s", err) } v6conf := conf.Conf6 diff --git a/internal/dnsforward/http.go b/internal/dnsforward/http.go index 8879bb1a..5668573b 100644 --- a/internal/dnsforward/http.go +++ b/internal/dnsforward/http.go @@ -3,6 +3,7 @@ package dnsforward import ( "encoding/json" "fmt" + "io" "net" "net/http" "net/netip" @@ -565,6 +566,11 @@ type domainSpecificTestError struct { error } +// Error implements the [error] interface for domainSpecificTestError. +func (err domainSpecificTestError) Error() (msg string) { + return fmt.Sprintf("WARNING: %s", err.error) +} + // checkDNS checks the upstream server defined by upstreamConfigStr using // healthCheck for actually exchange messages. It uses bootstrap to resolve the // upstream's address. @@ -631,44 +637,54 @@ func (s *Server) handleTestUpstreamDNS(w http.ResponseWriter, r *http.Request) { result := map[string]string{} bootstraps := req.BootstrapDNS - timeout := s.conf.UpstreamTimeout - for _, host := range req.Upstreams { - err = checkDNS(host, bootstraps, timeout, checkDNSUpstreamExc) - if err != nil { - log.Info("%v", err) - result[host] = err.Error() - if _, ok := err.(domainSpecificTestError); ok { - result[host] = fmt.Sprintf("WARNING: %s", result[host]) - } - continue - } - - result[host] = "OK" + type upsCheckResult = struct { + res string + host string } - for _, host := range req.PrivateUpstreams { - err = checkDNS(host, bootstraps, timeout, checkPrivateUpstreamExc) - if err != nil { - log.Info("%v", err) - // TODO(e.burkov): If passed upstream have already written an error - // above, we rewriting the error for it. These cases should be - // handled properly instead. - result[host] = err.Error() - if _, ok := err.(domainSpecificTestError); ok { - result[host] = fmt.Sprintf("WARNING: %s", result[host]) - } + upsNum := len(req.Upstreams) + len(req.PrivateUpstreams) + resCh := make(chan upsCheckResult, upsNum) - continue + checkUps := func(ups string, healthCheck healthCheckFunc) { + res := upsCheckResult{ + host: ups, } + defer func() { resCh <- res }() - result[host] = "OK" + checkErr := checkDNS(ups, bootstraps, timeout, healthCheck) + if checkErr != nil { + res.res = checkErr.Error() + } else { + res.res = "OK" + } } + for _, ups := range req.Upstreams { + go checkUps(ups, checkDNSUpstreamExc) + } + for _, ups := range req.PrivateUpstreams { + go checkUps(ups, checkPrivateUpstreamExc) + } + + for i := 0; i < upsNum; i++ { + pair := <-resCh + // TODO(e.burkov): The upstreams used for both common and private + // resolving should be reported separately. + result[pair.host] = pair.res + } + close(resCh) + _ = aghhttp.WriteJSONResponse(w, r, result) } +// handleCacheClear is the handler for the POST /control/cache_clear HTTP API. +func (s *Server) handleCacheClear(w http.ResponseWriter, _ *http.Request) { + s.dnsProxy.ClearCache() + _, _ = io.WriteString(w, "OK") +} + // handleDoH is the DNS-over-HTTPs handler. // // Control flow: @@ -703,6 +719,8 @@ func (s *Server) registerHandlers() { s.conf.HTTPRegister(http.MethodGet, "/control/access/list", s.handleAccessList) s.conf.HTTPRegister(http.MethodPost, "/control/access/set", s.handleAccessSet) + s.conf.HTTPRegister(http.MethodPost, "/control/cache_clear", s.handleCacheClear) + // Register both versions, with and without the trailing slash, to // prevent a 301 Moved Permanently redirect when clients request the // path without the trailing slash. Those redirects break some clients. diff --git a/internal/dnsforward/http_test.go b/internal/dnsforward/http_test.go index 64ba21c4..5e0b8018 100644 --- a/internal/dnsforward/http_test.go +++ b/internal/dnsforward/http_test.go @@ -7,16 +7,20 @@ import ( "net" "net/http" "net/http/httptest" + "net/netip" + "net/url" "os" "path/filepath" "strings" "testing" + "time" "github.com/AdguardTeam/AdGuardHome/internal/aghhttp" "github.com/AdguardTeam/AdGuardHome/internal/aghnet" "github.com/AdguardTeam/AdGuardHome/internal/filtering" "github.com/AdguardTeam/golibs/netutil" "github.com/AdguardTeam/golibs/testutil" + "github.com/miekg/dns" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -392,3 +396,141 @@ func TestValidateUpstreamsPrivate(t *testing.T) { }) } } + +func newLocalUpstreamListener(t *testing.T, port int, handler dns.Handler) (real net.Addr) { + startCh := make(chan struct{}) + upsSrv := &dns.Server{ + Addr: netip.AddrPortFrom(netutil.IPv4Localhost(), uint16(port)).String(), + Net: "tcp", + Handler: handler, + NotifyStartedFunc: func() { close(startCh) }, + } + go func() { + t := testutil.PanicT{} + + err := upsSrv.ListenAndServe() + require.NoError(t, err) + }() + <-startCh + testutil.CleanupAndRequireSuccess(t, upsSrv.Shutdown) + + return upsSrv.Listener.Addr() +} + +func TestServer_handleTestUpstreaDNS(t *testing.T) { + goodHandler := dns.HandlerFunc(func(w dns.ResponseWriter, m *dns.Msg) { + err := w.WriteMsg(new(dns.Msg).SetReply(m)) + require.NoError(testutil.PanicT{}, err) + }) + badHandler := dns.HandlerFunc(func(w dns.ResponseWriter, _ *dns.Msg) { + err := w.WriteMsg(new(dns.Msg)) + require.NoError(testutil.PanicT{}, err) + }) + + goodUps := (&url.URL{ + Scheme: "tcp", + Host: newLocalUpstreamListener(t, 0, goodHandler).String(), + }).String() + badUps := (&url.URL{ + Scheme: "tcp", + Host: newLocalUpstreamListener(t, 0, badHandler).String(), + }).String() + + const upsTimeout = 100 * time.Millisecond + + srv := createTestServer(t, &filtering.Config{}, ServerConfig{ + UDPListenAddrs: []*net.UDPAddr{{}}, + TCPListenAddrs: []*net.TCPAddr{{}}, + UpstreamTimeout: upsTimeout, + }, nil) + startDeferStop(t, srv) + + testCases := []struct { + body map[string]any + wantResp map[string]any + name string + }{{ + body: map[string]any{ + "upstream_dns": []string{goodUps}, + }, + wantResp: map[string]any{ + goodUps: "OK", + }, + name: "success", + }, { + body: map[string]any{ + "upstream_dns": []string{badUps}, + }, + wantResp: map[string]any{ + badUps: `upstream "` + badUps + `" fails to exchange: ` + + `couldn't communicate with upstream: dns: id mismatch`, + }, + name: "broken", + }, { + body: map[string]any{ + "upstream_dns": []string{goodUps, badUps}, + }, + wantResp: map[string]any{ + goodUps: "OK", + badUps: `upstream "` + badUps + `" fails to exchange: ` + + `couldn't communicate with upstream: dns: id mismatch`, + }, + name: "both", + }} + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + reqBody, err := json.Marshal(tc.body) + require.NoError(t, err) + + w := httptest.NewRecorder() + r, err := http.NewRequest(http.MethodPost, "", bytes.NewReader(reqBody)) + require.NoError(t, err) + + srv.handleTestUpstreamDNS(w, r) + require.Equal(t, http.StatusOK, w.Code) + + resp := map[string]any{} + err = json.NewDecoder(w.Body).Decode(&resp) + require.NoError(t, err) + + assert.Equal(t, tc.wantResp, resp) + }) + } + + t.Run("timeout", func(t *testing.T) { + slowHandler := dns.HandlerFunc(func(w dns.ResponseWriter, m *dns.Msg) { + time.Sleep(upsTimeout * 2) + writeErr := w.WriteMsg(new(dns.Msg).SetReply(m)) + require.NoError(testutil.PanicT{}, writeErr) + }) + sleepyUps := (&url.URL{ + Scheme: "tcp", + Host: newLocalUpstreamListener(t, 0, slowHandler).String(), + }).String() + + req := map[string]any{ + "upstream_dns": []string{sleepyUps}, + } + reqBody, err := json.Marshal(req) + require.NoError(t, err) + + w := httptest.NewRecorder() + r, err := http.NewRequest(http.MethodPost, "", bytes.NewReader(reqBody)) + require.NoError(t, err) + + srv.handleTestUpstreamDNS(w, r) + require.Equal(t, http.StatusOK, w.Code) + + resp := map[string]any{} + err = json.NewDecoder(w.Body).Decode(&resp) + require.NoError(t, err) + + require.Contains(t, resp, sleepyUps) + require.IsType(t, "", resp[sleepyUps]) + sleepyRes, _ := resp[sleepyUps].(string) + + // TODO(e.burkov): Improve the format of an error in dnsproxy. + assert.True(t, strings.HasSuffix(sleepyRes, "i/o timeout")) + }) +} diff --git a/internal/filtering/filtering.go b/internal/filtering/filtering.go index a1de94cd..db6e1b17 100644 --- a/internal/filtering/filtering.go +++ b/internal/filtering/filtering.go @@ -33,6 +33,7 @@ import ( // The IDs of built-in filter lists. // // Keep in sync with client/src/helpers/constants.js. +// TODO(d.kolyshev): Add RewritesListID and don't forget to keep in sync. const ( CustomListID = -iota SysHostsListID diff --git a/internal/filtering/rewrite/item.go b/internal/filtering/rewrite/item.go new file mode 100644 index 00000000..d67798d7 --- /dev/null +++ b/internal/filtering/rewrite/item.go @@ -0,0 +1,73 @@ +package rewrite + +import ( + "fmt" + "net/netip" + "strings" + + "github.com/miekg/dns" +) + +// Item is a single DNS rewrite record. +type Item struct { + // Domain is the domain pattern for which this rewrite should work. + Domain string `yaml:"domain"` + + // Answer is the IP address, canonical name, or one of the special + // values: "A" or "AAAA". + Answer string `yaml:"answer"` +} + +// equal returns true if rw is equal to other. +func (rw *Item) equal(other *Item) (ok bool) { + if rw == nil { + return other == nil + } else if other == nil { + return false + } + + return *rw == *other +} + +// toRule converts rw to a filter rule. +func (rw *Item) toRule() (res string) { + if rw == nil { + return "" + } + + domain := strings.ToLower(rw.Domain) + + dType, exception := rw.rewriteParams() + dTypeKey := dns.TypeToString[dType] + if exception { + return fmt.Sprintf("@@||%s^$dnstype=%s,dnsrewrite", domain, dTypeKey) + } + + return fmt.Sprintf("|%s^$dnsrewrite=NOERROR;%s;%s", domain, dTypeKey, rw.Answer) +} + +// rewriteParams returns dns request type and exception flag for rw. +func (rw *Item) rewriteParams() (dType uint16, exception bool) { + switch rw.Answer { + case "AAAA": + return dns.TypeAAAA, true + case "A": + return dns.TypeA, true + default: + // Go on. + } + + addr, err := netip.ParseAddr(rw.Answer) + if err != nil { + // TODO(d.kolyshev): Validate rw.Answer as a domain name. + return dns.TypeCNAME, false + } + + if addr.Is4() { + dType = dns.TypeA + } else { + dType = dns.TypeAAAA + } + + return dType, false +} diff --git a/internal/filtering/rewrite/item_internal_test.go b/internal/filtering/rewrite/item_internal_test.go new file mode 100644 index 00000000..68d88223 --- /dev/null +++ b/internal/filtering/rewrite/item_internal_test.go @@ -0,0 +1,124 @@ +package rewrite + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestItem_equal(t *testing.T) { + const ( + testDomain = "example.org" + testAnswer = "1.1.1.1" + ) + + testItem := &Item{ + Domain: testDomain, + Answer: testAnswer, + } + + testCases := []struct { + name string + left *Item + right *Item + want bool + }{{ + name: "nil_left", + left: nil, + right: testItem, + want: false, + }, { + name: "nil_right", + left: testItem, + right: nil, + want: false, + }, { + name: "nils", + left: nil, + right: nil, + want: true, + }, { + name: "equal", + left: testItem, + right: testItem, + want: true, + }, { + name: "distinct", + left: testItem, + right: &Item{ + Domain: "other", + Answer: "other", + }, + want: false, + }} + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + res := tc.left.equal(tc.right) + assert.Equal(t, tc.want, res) + }) + } +} + +func TestItem_toRule(t *testing.T) { + const testDomain = "example.org" + + testCases := []struct { + name string + item *Item + want string + }{{ + name: "nil", + item: nil, + want: "", + }, { + name: "a_rule", + item: &Item{ + Domain: testDomain, + Answer: "1.1.1.1", + }, + want: "|example.org^$dnsrewrite=NOERROR;A;1.1.1.1", + }, { + name: "aaaa_rule", + item: &Item{ + Domain: testDomain, + Answer: "1:2:3::4", + }, + want: "|example.org^$dnsrewrite=NOERROR;AAAA;1:2:3::4", + }, { + name: "cname_rule", + item: &Item{ + Domain: testDomain, + Answer: "other.org", + }, + want: "|example.org^$dnsrewrite=NOERROR;CNAME;other.org", + }, { + name: "wildcard_rule", + item: &Item{ + Domain: "*.example.org", + Answer: "other.org", + }, + want: "|*.example.org^$dnsrewrite=NOERROR;CNAME;other.org", + }, { + name: "aaaa_exception", + item: &Item{ + Domain: testDomain, + Answer: "A", + }, + want: "@@||example.org^$dnstype=A,dnsrewrite", + }, { + name: "aaaa_exception", + item: &Item{ + Domain: testDomain, + Answer: "AAAA", + }, + want: "@@||example.org^$dnstype=AAAA,dnsrewrite", + }} + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + res := tc.item.toRule() + assert.Equal(t, tc.want, res) + }) + } +} diff --git a/internal/filtering/rewrite/storage.go b/internal/filtering/rewrite/storage.go new file mode 100644 index 00000000..0afb8b6e --- /dev/null +++ b/internal/filtering/rewrite/storage.go @@ -0,0 +1,241 @@ +// Package rewrite implements DNS Rewrites storage and request matching. +package rewrite + +import ( + "fmt" + "strings" + "sync" + + "github.com/AdguardTeam/golibs/log" + "github.com/AdguardTeam/golibs/stringutil" + "github.com/AdguardTeam/urlfilter" + "github.com/AdguardTeam/urlfilter/filterlist" + "github.com/AdguardTeam/urlfilter/rules" + "github.com/miekg/dns" + "golang.org/x/exp/slices" +) + +// Storage is a storage for rewrite rules. +type Storage interface { + // MatchRequest returns matching dnsrewrites for the specified request. + MatchRequest(dReq *urlfilter.DNSRequest) (rws []*rules.DNSRewrite) + + // Add adds item to the storage. + Add(item *Item) (err error) + + // Remove deletes item from the storage. + Remove(item *Item) (err error) + + // List returns all items from the storage. + List() (items []*Item) +} + +// DefaultStorage is the default storage for rewrite rules. +type DefaultStorage struct { + // mu protects items. + mu *sync.RWMutex + + // engine is the DNS filtering engine. + engine *urlfilter.DNSEngine + + // ruleList is the filtering rule ruleList used by the engine. + ruleList filterlist.RuleList + + // rewrites stores the rewrite entries from configuration. + rewrites []*Item + + // urlFilterID is the synthetic integer identifier for the urlfilter engine. + // + // TODO(a.garipov): Change the type to a string in module urlfilter and + // remove this crutch. + urlFilterID int +} + +// NewDefaultStorage returns new rewrites storage. listID is used as an +// identifier of the underlying rules list. rewrites must not be nil. +func NewDefaultStorage(listID int, rewrites []*Item) (s *DefaultStorage, err error) { + s = &DefaultStorage{ + mu: &sync.RWMutex{}, + urlFilterID: listID, + rewrites: rewrites, + } + + s.mu.Lock() + defer s.mu.Unlock() + + err = s.resetRules() + if err != nil { + return nil, err + } + + return s, nil +} + +// type check +var _ Storage = (*DefaultStorage)(nil) + +// MatchRequest implements the [Storage] interface for *DefaultStorage. +func (s *DefaultStorage) MatchRequest(dReq *urlfilter.DNSRequest) (rws []*rules.DNSRewrite) { + s.mu.RLock() + defer s.mu.RUnlock() + + rrules := s.rewriteRulesForReq(dReq) + if len(rrules) == 0 { + return nil + } + + // TODO(a.garipov): Check cnames for cycles on initialisation. + cnames := stringutil.NewSet() + host := dReq.Hostname + for len(rrules) > 0 && rrules[0].DNSRewrite != nil && rrules[0].DNSRewrite.NewCNAME != "" { + rule := rrules[0] + rwAns := rule.DNSRewrite.NewCNAME + + log.Debug("rewrite: cname for %s is %s", host, rwAns) + + if dReq.Hostname == rwAns { + // A request for the hostname itself is an exception rule. + // TODO(d.kolyshev): Check rewrite of a pattern onto itself. + + return nil + } + + if host == rwAns && isWildcard(rule.RuleText) { + // An "*.example.com → sub.example.com" rewrite matching in a loop. + // + // See https://github.com/AdguardTeam/AdGuardHome/issues/4016. + + return []*rules.DNSRewrite{rule.DNSRewrite} + } + + if cnames.Has(rwAns) { + log.Info("rewrite: cname loop for %q on %q", dReq.Hostname, rwAns) + + return nil + } + + cnames.Add(rwAns) + + drules := s.rewriteRulesForReq(&urlfilter.DNSRequest{ + Hostname: rwAns, + DNSType: dReq.DNSType, + }) + if drules != nil { + rrules = drules + } + + host = rwAns + } + + return s.collectDNSRewrites(rrules, dReq.DNSType) +} + +// collectDNSRewrites filters DNSRewrite by question type. +func (s *DefaultStorage) collectDNSRewrites( + rewrites []*rules.NetworkRule, + qtyp uint16, +) (rws []*rules.DNSRewrite) { + for _, rewrite := range rewrites { + dnsRewrite := rewrite.DNSRewrite + if matchesQType(dnsRewrite, qtyp) { + rws = append(rws, dnsRewrite) + } + } + + return rws +} + +// rewriteRulesForReq returns matching dnsrewrite rules. +func (s *DefaultStorage) rewriteRulesForReq(dReq *urlfilter.DNSRequest) (rules []*rules.NetworkRule) { + res, _ := s.engine.MatchRequest(dReq) + + return res.DNSRewrites() +} + +// Add implements the [Storage] interface for *DefaultStorage. +func (s *DefaultStorage) Add(item *Item) (err error) { + s.mu.Lock() + defer s.mu.Unlock() + + // TODO(d.kolyshev): Handle duplicate items. + s.rewrites = append(s.rewrites, item) + + return s.resetRules() +} + +// Remove implements the [Storage] interface for *DefaultStorage. +func (s *DefaultStorage) Remove(item *Item) (err error) { + s.mu.Lock() + defer s.mu.Unlock() + + arr := []*Item{} + + // TODO(d.kolyshev): Use slices.IndexFunc + slices.Delete? + for _, ent := range s.rewrites { + if ent.equal(item) { + log.Debug("rewrite: removed element: %s -> %s", ent.Domain, ent.Answer) + + continue + } + + arr = append(arr, ent) + } + s.rewrites = arr + + return s.resetRules() +} + +// List implements the [Storage] interface for *DefaultStorage. +func (s *DefaultStorage) List() (items []*Item) { + s.mu.RLock() + defer s.mu.RUnlock() + + return slices.Clone(s.rewrites) +} + +// resetRules resets the filtering rules. +func (s *DefaultStorage) resetRules() (err error) { + // TODO(a.garipov): Use strings.Builder. + var rulesText []string + for _, rewrite := range s.rewrites { + rulesText = append(rulesText, rewrite.toRule()) + } + + strList := &filterlist.StringRuleList{ + ID: s.urlFilterID, + RulesText: strings.Join(rulesText, "\n"), + IgnoreCosmetic: true, + } + + rs, err := filterlist.NewRuleStorage([]filterlist.RuleList{strList}) + if err != nil { + return fmt.Errorf("creating list storage: %w", err) + } + + s.ruleList = strList + s.engine = urlfilter.NewDNSEngine(rs) + + log.Info("rewrite: filter %d: reset %d rules", s.urlFilterID, s.engine.RulesCount) + + return nil +} + +// matchesQType returns true if dnsrewrite matches the question type qt. +func matchesQType(dnsrr *rules.DNSRewrite, qt uint16) (ok bool) { + // Add CNAMEs, since they match for all types requests. + if dnsrr.RRType == dns.TypeCNAME { + return true + } + + // Reject types other than A and AAAA. + if qt != dns.TypeA && qt != dns.TypeAAAA { + return false + } + + return dnsrr.RRType == qt +} + +// isWildcard returns true if pat is a wildcard domain pattern. +func isWildcard(pat string) (res bool) { + return strings.HasPrefix(pat, "|*.") +} diff --git a/internal/filtering/rewrite/storage_test.go b/internal/filtering/rewrite/storage_test.go new file mode 100644 index 00000000..4db06a4e --- /dev/null +++ b/internal/filtering/rewrite/storage_test.go @@ -0,0 +1,458 @@ +package rewrite + +import ( + "net" + "testing" + + "github.com/AdguardTeam/urlfilter" + "github.com/AdguardTeam/urlfilter/rules" + "github.com/miekg/dns" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestNewDefaultStorage(t *testing.T) { + items := []*Item{{ + Domain: "example.com", + Answer: "answer.com", + }} + + s, err := NewDefaultStorage(-1, items) + require.NoError(t, err) + + require.Len(t, s.List(), 1) +} + +func TestDefaultStorage_CRUD(t *testing.T) { + var items []*Item + + s, err := NewDefaultStorage(-1, items) + require.NoError(t, err) + require.Len(t, s.List(), 0) + + item := &Item{Domain: "example.com", Answer: "answer.com"} + + err = s.Add(item) + require.NoError(t, err) + + list := s.List() + require.Len(t, list, 1) + require.True(t, item.equal(list[0])) + + err = s.Remove(item) + require.NoError(t, err) + require.Len(t, s.List(), 0) +} + +func TestDefaultStorage_MatchRequest(t *testing.T) { + items := []*Item{{ + // This one and below are about CNAME, A and AAAA. + Domain: "somecname", + Answer: "somehost.com", + }, { + Domain: "somehost.com", + Answer: "0.0.0.0", + }, { + Domain: "host.com", + Answer: "1.2.3.4", + }, { + Domain: "host.com", + Answer: "1.2.3.5", + }, { + Domain: "host.com", + Answer: "1:2:3::4", + }, { + Domain: "www.host.com", + Answer: "host.com", + }, { + // This one is a wildcard. + Domain: "*.host.com", + Answer: "1.2.3.5", + }, { + // This one and below are about wildcard overriding. + Domain: "a.host.com", + Answer: "1.2.3.4", + }, { + // This one is about CNAME and wildcard interacting. + Domain: "*.host2.com", + Answer: "host.com", + }, { + // This one and below are about 2 level CNAME. + Domain: "b.host.com", + Answer: "somecname", + }, { + // This one and below are about 2 level CNAME and wildcard. + Domain: "b.host3.com", + Answer: "a.host3.com", + }, { + Domain: "a.host3.com", + Answer: "x.host.com", + }, { + Domain: "*.hostboth.com", + Answer: "1.2.3.6", + }, { + Domain: "*.hostboth.com", + Answer: "1234::5678", + }, { + Domain: "BIGHOST.COM", + Answer: "1.2.3.7", + }, { + Domain: "*.issue4016.com", + Answer: "sub.issue4016.com", + }} + + s, err := NewDefaultStorage(-1, items) + require.NoError(t, err) + + testCases := []struct { + name string + host string + wantDNSRewrites []*rules.DNSRewrite + dtyp uint16 + }{{ + name: "not_filtered_not_found", + host: "hoost.com", + wantDNSRewrites: nil, + dtyp: dns.TypeA, + }, { + name: "not_filtered_qtype", + host: "www.host.com", + wantDNSRewrites: nil, + dtyp: dns.TypeMX, + }, { + name: "rewritten_a", + host: "www.host.com", + wantDNSRewrites: []*rules.DNSRewrite{{ + Value: net.IP{1, 2, 3, 4}.To16(), + NewCNAME: "", + RCode: dns.RcodeSuccess, + RRType: dns.TypeA, + }, { + Value: net.IP{1, 2, 3, 5}.To16(), + NewCNAME: "", + RCode: dns.RcodeSuccess, + RRType: dns.TypeA, + }}, + dtyp: dns.TypeA, + }, { + name: "rewritten_aaaa", + host: "www.host.com", + wantDNSRewrites: []*rules.DNSRewrite{{ + Value: net.ParseIP("1:2:3::4"), + NewCNAME: "", + RCode: dns.RcodeSuccess, + RRType: dns.TypeAAAA, + }}, + dtyp: dns.TypeAAAA, + }, { + name: "wildcard_match", + host: "abc.host.com", + wantDNSRewrites: []*rules.DNSRewrite{{ + Value: net.IP{1, 2, 3, 5}.To16(), + NewCNAME: "", + RCode: dns.RcodeSuccess, + RRType: dns.TypeA, + }}, + dtyp: dns.TypeA, + //}, { + // TODO(d.kolyshev): This is about matching in urlfilter. + // name: "wildcard_override", + // host: "a.host.com", + // wantDNSRewrites: []*rules.DNSRewrite{{ + // Value: net.IP{1, 2, 3, 4}.To16(), + // NewCNAME: "", + // RCode: dns.RcodeSuccess, + // RRType: dns.TypeA, + // }}, + // dtyp: dns.TypeA, + }, { + name: "wildcard_cname_interaction", + host: "www.host2.com", + wantDNSRewrites: []*rules.DNSRewrite{{ + Value: net.IP{1, 2, 3, 4}.To16(), + NewCNAME: "", + RCode: dns.RcodeSuccess, + RRType: dns.TypeA, + }, { + Value: net.IP{1, 2, 3, 5}.To16(), + NewCNAME: "", + RCode: dns.RcodeSuccess, + RRType: dns.TypeA, + }}, + dtyp: dns.TypeA, + }, { + name: "two_cnames", + host: "b.host.com", + wantDNSRewrites: []*rules.DNSRewrite{{ + Value: net.IP{0, 0, 0, 0}.To16(), + NewCNAME: "", + RCode: dns.RcodeSuccess, + RRType: dns.TypeA, + }}, + dtyp: dns.TypeA, + }, { + name: "two_cnames_and_wildcard", + host: "b.host3.com", + wantDNSRewrites: []*rules.DNSRewrite{{ + Value: net.IP{1, 2, 3, 5}.To16(), + NewCNAME: "", + RCode: dns.RcodeSuccess, + RRType: dns.TypeA, + }}, + dtyp: dns.TypeA, + }, { + name: "issue3343", + host: "www.hostboth.com", + wantDNSRewrites: []*rules.DNSRewrite{{ + Value: net.ParseIP("1234::5678"), + NewCNAME: "", + RCode: dns.RcodeSuccess, + RRType: dns.TypeAAAA, + }}, + dtyp: dns.TypeAAAA, + }, { + name: "issue3351", + host: "bighost.com", + wantDNSRewrites: []*rules.DNSRewrite{{ + Value: net.IP{1, 2, 3, 7}.To16(), + NewCNAME: "", + RCode: dns.RcodeSuccess, + RRType: dns.TypeA, + }}, + dtyp: dns.TypeA, + }, { + name: "issue4008", + host: "somehost.com", + wantDNSRewrites: nil, + dtyp: dns.TypeHTTPS, + }, { + name: "issue4016", + host: "www.issue4016.com", + wantDNSRewrites: []*rules.DNSRewrite{{ + Value: nil, + NewCNAME: "sub.issue4016.com", + RCode: dns.RcodeSuccess, + RRType: dns.TypeNone, + }}, + dtyp: dns.TypeA, + }, { + name: "issue4016_self", + host: "sub.issue4016.com", + wantDNSRewrites: nil, + dtyp: dns.TypeA, + }} + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + dnsRewrites := s.MatchRequest(&urlfilter.DNSRequest{ + Hostname: tc.host, + DNSType: tc.dtyp, + }) + + assert.Equal(t, tc.wantDNSRewrites, dnsRewrites) + }) + } +} + +func TestDefaultStorage_MatchRequest_Levels(t *testing.T) { + // Exact host, wildcard L2, wildcard L3. + items := []*Item{{ + Domain: "host.com", + Answer: "1.1.1.1", + }, { + Domain: "*.host.com", + Answer: "2.2.2.2", + }, { + Domain: "*.sub.host.com", + Answer: "3.3.3.3", + }} + + s, err := NewDefaultStorage(-1, items) + require.NoError(t, err) + + testCases := []struct { + name string + host string + wantDNSRewrites []*rules.DNSRewrite + dtyp uint16 + }{{ + name: "exact_match", + host: "host.com", + wantDNSRewrites: []*rules.DNSRewrite{{ + Value: net.IP{1, 1, 1, 1}.To16(), + NewCNAME: "", + RCode: dns.RcodeSuccess, + RRType: dns.TypeA, + }}, + dtyp: dns.TypeA, + }, { + name: "l2_match", + host: "sub.host.com", + wantDNSRewrites: []*rules.DNSRewrite{{ + Value: net.IP{2, 2, 2, 2}.To16(), + NewCNAME: "", + RCode: dns.RcodeSuccess, + RRType: dns.TypeA, + }}, + dtyp: dns.TypeA, + //}, { + // TODO(d.kolyshev): This is about matching in urlfilter. + // name: "l3_match", + // host: "my.sub.host.com", + // wantDNSRewrites: []*rules.DNSRewrite{{ + // Value: net.IP{3, 3, 3, 3}.To16(), + // NewCNAME: "", + // RCode: dns.RcodeSuccess, + // RRType: dns.TypeA, + // }}, + // dtyp: dns.TypeA, + }} + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + dnsRewrites := s.MatchRequest(&urlfilter.DNSRequest{ + Hostname: tc.host, + DNSType: tc.dtyp, + }) + + assert.Equal(t, tc.wantDNSRewrites, dnsRewrites) + }) + } +} + +func TestDefaultStorage_MatchRequest_ExceptionCNAME(t *testing.T) { + // Wildcard and exception for a sub-domain. + items := []*Item{{ + Domain: "*.host.com", + Answer: "2.2.2.2", + }, { + Domain: "sub.host.com", + Answer: "sub.host.com", + }, { + Domain: "*.sub.host.com", + Answer: "*.sub.host.com", + }} + + s, err := NewDefaultStorage(-1, items) + require.NoError(t, err) + + testCases := []struct { + name string + host string + wantDNSRewrites []*rules.DNSRewrite + dtyp uint16 + }{{ + name: "match_subdomain", + host: "my.host.com", + wantDNSRewrites: []*rules.DNSRewrite{{ + Value: net.IP{2, 2, 2, 2}.To16(), + NewCNAME: "", + RCode: dns.RcodeSuccess, + RRType: dns.TypeA, + }}, + dtyp: dns.TypeA, + }, { + name: "exception_cname", + host: "sub.host.com", + wantDNSRewrites: nil, + dtyp: dns.TypeA, + //}, { + // TODO(d.kolyshev): This is about matching in urlfilter. + // name: "exception_wildcard", + // host: "my.sub.host.com", + // wantDNSRewrites: nil, + // dtyp: dns.TypeA, + }} + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + dnsRewrites := s.MatchRequest(&urlfilter.DNSRequest{ + Hostname: tc.host, + DNSType: tc.dtyp, + }) + + assert.Equal(t, tc.wantDNSRewrites, dnsRewrites) + }) + } +} + +func TestDefaultStorage_MatchRequest_ExceptionIP(t *testing.T) { + // Exception for AAAA record. + items := []*Item{{ + Domain: "host.com", + Answer: "1.2.3.4", + }, { + Domain: "host.com", + Answer: "AAAA", + }, { + Domain: "host2.com", + Answer: "::1", + }, { + Domain: "host2.com", + Answer: "A", + }, { + Domain: "host3.com", + Answer: "A", + }} + + s, err := NewDefaultStorage(-1, items) + require.NoError(t, err) + + testCases := []struct { + name string + host string + wantDNSRewrites []*rules.DNSRewrite + dtyp uint16 + }{{ + name: "match_A", + host: "host.com", + wantDNSRewrites: []*rules.DNSRewrite{{ + Value: net.IP{1, 2, 3, 4}.To16(), + NewCNAME: "", + RCode: dns.RcodeSuccess, + RRType: dns.TypeA, + }}, + dtyp: dns.TypeA, + }, { + name: "exception_AAAA_host.com", + host: "host.com", + wantDNSRewrites: nil, + dtyp: dns.TypeAAAA, + }, { + name: "exception_A_host2.com", + host: "host2.com", + wantDNSRewrites: nil, + dtyp: dns.TypeA, + }, { + name: "match_AAAA_host2.com", + host: "host2.com", + wantDNSRewrites: []*rules.DNSRewrite{{ + Value: net.ParseIP("::1"), + NewCNAME: "", + RCode: dns.RcodeSuccess, + RRType: dns.TypeAAAA, + }}, + dtyp: dns.TypeAAAA, + }, { + name: "exception_A_host3.com", + host: "host3.com", + wantDNSRewrites: nil, + dtyp: dns.TypeA, + }, { + name: "match_AAAA_host3.com", + host: "host3.com", + wantDNSRewrites: nil, + dtyp: dns.TypeAAAA, + }} + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + dnsRewrites := s.MatchRequest(&urlfilter.DNSRequest{ + Hostname: tc.host, + DNSType: tc.dtyp, + }) + + assert.Equal(t, tc.wantDNSRewrites, dnsRewrites) + }) + } +} diff --git a/internal/filtering/rewritehttp.go b/internal/filtering/rewritehttp.go new file mode 100644 index 00000000..752979fe --- /dev/null +++ b/internal/filtering/rewritehttp.go @@ -0,0 +1,93 @@ +package filtering + +import ( + "encoding/json" + "net/http" + + "github.com/AdguardTeam/AdGuardHome/internal/aghhttp" + "github.com/AdguardTeam/golibs/log" +) + +// TODO(d.kolyshev): Use [rewrite.Item] instead. +type rewriteEntryJSON struct { + Domain string `json:"domain"` + Answer string `json:"answer"` +} + +func (d *DNSFilter) handleRewriteList(w http.ResponseWriter, r *http.Request) { + arr := []*rewriteEntryJSON{} + + d.confLock.Lock() + for _, ent := range d.Config.Rewrites { + jsent := rewriteEntryJSON{ + Domain: ent.Domain, + Answer: ent.Answer, + } + arr = append(arr, &jsent) + } + d.confLock.Unlock() + + _ = aghhttp.WriteJSONResponse(w, r, arr) +} + +func (d *DNSFilter) handleRewriteAdd(w http.ResponseWriter, r *http.Request) { + rwJSON := rewriteEntryJSON{} + err := json.NewDecoder(r.Body).Decode(&rwJSON) + if err != nil { + aghhttp.Error(r, w, http.StatusBadRequest, "json.Decode: %s", err) + + return + } + + rw := &LegacyRewrite{ + Domain: rwJSON.Domain, + Answer: rwJSON.Answer, + } + + err = rw.normalize() + if err != nil { + // Shouldn't happen currently, since normalize only returns a non-nil + // error when a rewrite is nil, but be change-proof. + aghhttp.Error(r, w, http.StatusBadRequest, "normalizing: %s", err) + + return + } + + d.confLock.Lock() + d.Config.Rewrites = append(d.Config.Rewrites, rw) + d.confLock.Unlock() + log.Debug("rewrite: added element: %s -> %s [%d]", rw.Domain, rw.Answer, len(d.Config.Rewrites)) + + d.Config.ConfigModified() +} + +func (d *DNSFilter) handleRewriteDelete(w http.ResponseWriter, r *http.Request) { + jsent := rewriteEntryJSON{} + err := json.NewDecoder(r.Body).Decode(&jsent) + if err != nil { + aghhttp.Error(r, w, http.StatusBadRequest, "json.Decode: %s", err) + + return + } + + entDel := &LegacyRewrite{ + Domain: jsent.Domain, + Answer: jsent.Answer, + } + arr := []*LegacyRewrite{} + + d.confLock.Lock() + for _, ent := range d.Config.Rewrites { + if ent.equal(entDel) { + log.Debug("rewrite: removed element: %s -> %s", ent.Domain, ent.Answer) + + continue + } + + arr = append(arr, ent) + } + d.Config.Rewrites = arr + d.confLock.Unlock() + + d.Config.ConfigModified() +} diff --git a/internal/filtering/rewrites.go b/internal/filtering/rewrites.go index 675ab53e..057a9c4e 100644 --- a/internal/filtering/rewrites.go +++ b/internal/filtering/rewrites.go @@ -3,16 +3,12 @@ package filtering import ( - "encoding/json" "fmt" "net" - "net/http" "sort" "strings" - "github.com/AdguardTeam/AdGuardHome/internal/aghhttp" "github.com/AdguardTeam/golibs/errors" - "github.com/AdguardTeam/golibs/log" "github.com/miekg/dns" "golang.org/x/exp/slices" ) @@ -221,86 +217,3 @@ func max(a, b int) int { return b } - -type rewriteEntryJSON struct { - Domain string `json:"domain"` - Answer string `json:"answer"` -} - -func (d *DNSFilter) handleRewriteList(w http.ResponseWriter, r *http.Request) { - arr := []*rewriteEntryJSON{} - - d.confLock.Lock() - for _, ent := range d.Config.Rewrites { - jsent := rewriteEntryJSON{ - Domain: ent.Domain, - Answer: ent.Answer, - } - arr = append(arr, &jsent) - } - d.confLock.Unlock() - - _ = aghhttp.WriteJSONResponse(w, r, arr) -} - -func (d *DNSFilter) handleRewriteAdd(w http.ResponseWriter, r *http.Request) { - rwJSON := rewriteEntryJSON{} - err := json.NewDecoder(r.Body).Decode(&rwJSON) - if err != nil { - aghhttp.Error(r, w, http.StatusBadRequest, "json.Decode: %s", err) - - return - } - - rw := &LegacyRewrite{ - Domain: rwJSON.Domain, - Answer: rwJSON.Answer, - } - - err = rw.normalize() - if err != nil { - // Shouldn't happen currently, since normalize only returns a non-nil - // error when a rewrite is nil, but be change-proof. - aghhttp.Error(r, w, http.StatusBadRequest, "normalizing: %s", err) - - return - } - - d.confLock.Lock() - d.Config.Rewrites = append(d.Config.Rewrites, rw) - d.confLock.Unlock() - log.Debug("rewrite: added element: %s -> %s [%d]", rw.Domain, rw.Answer, len(d.Config.Rewrites)) - - d.Config.ConfigModified() -} - -func (d *DNSFilter) handleRewriteDelete(w http.ResponseWriter, r *http.Request) { - jsent := rewriteEntryJSON{} - err := json.NewDecoder(r.Body).Decode(&jsent) - if err != nil { - aghhttp.Error(r, w, http.StatusBadRequest, "json.Decode: %s", err) - - return - } - - entDel := &LegacyRewrite{ - Domain: jsent.Domain, - Answer: jsent.Answer, - } - arr := []*LegacyRewrite{} - - d.confLock.Lock() - for _, ent := range d.Config.Rewrites { - if ent.equal(entDel) { - log.Debug("rewrite: removed element: %s -> %s", ent.Domain, ent.Answer) - - continue - } - - arr = append(arr, ent) - } - d.Config.Rewrites = arr - d.confLock.Unlock() - - d.Config.ConfigModified() -} diff --git a/internal/filtering/servicelist.go b/internal/filtering/servicelist.go index f640097e..232976e8 100644 --- a/internal/filtering/servicelist.go +++ b/internal/filtering/servicelist.go @@ -251,14 +251,12 @@ var blockedServices = []blockedService{{ Name: "Mastodon", IconSVG: []byte(""), Rules: []string{ + "||aus.social^", "||awscommunity.social^", - "||colorid.es^", - "||dizl.de^", "||dju.social^", "||dresden.network^", "||fedibird.com^", "||fosstodon.org^", - "||freiburg.social^", "||glasgow.social^", "||h4.io^", "||hachyderm.io^", @@ -269,32 +267,30 @@ var blockedServices = []blockedService{{ "||ieji.de^", "||indieweb.social^", "||ioc.exchange^", - "||kfem.cat^", "||kolektiva.social^", - "||kurry.social^", - "||libretooth.gr^", "||livellosegreto.it^", "||lor.sh^", "||m.cmx.im^", - "||mast.dragon-fly.club^", + "||mas.to^", "||masto.ai^", "||masto.es^", "||masto.nobigtech.es^", "||masto.pt^", - "||mastodon-belgium.be^", "||mastodon.au^", "||mastodon.bida.im^", + "||mastodon.com.tr^", "||mastodon.eus^", "||mastodon.ie^", "||mastodon.iriseden.eu^", + "||mastodon.lol^", "||mastodon.nl^", "||mastodon.nu^", "||mastodon.nz^", "||mastodon.online^", "||mastodon.scot^", "||mastodon.sdf.org^", - "||mastodon.se^", "||mastodon.social^", + "||mastodon.top^", "||mastodon.uno^", "||mastodon.world^", "||mastodon.zaclys.com^", @@ -304,6 +300,8 @@ var blockedServices = []blockedService{{ "||mastodontti.fi^", "||mastouille.fr^", "||mathstodon.xyz^", + "||meow.social^", + "||metalhead.club^", "||mindly.social^", "||mstdn.ca^", "||mstdn.jp^", @@ -311,14 +309,13 @@ var blockedServices = []blockedService{{ "||mstdn.social^", "||muenchen.social^", "||muenster.im^", + "||nerdculture.de^", "||newsie.social^", "||noc.social^", "||norden.social^", "||nrw.social^", "||o3o.ca^", "||ohai.social^", - "||oslo.town^", - "||pettingzoo.co^", "||pewtix.com^", "||phpc.social^", "||piaille.fr^", @@ -329,18 +326,20 @@ var blockedServices = []blockedService{{ "||ruby.social^", "||ruhr.social^", "||sfba.social^", - "||snabelen.no^", + "||socel.net^", "||social.anoxinon.de^", "||social.cologne^", "||social.dev-wiki.de^", + "||social.linux.pizza^", "||social.politicaconciencia.org^", "||social.vivaldi.net^", - "||sociale.network^", + "||sself.co^", "||sueden.social^", "||techhub.social^", "||theblower.au^", "||tkz.one^", "||toot.aquilenet.fr^", + "||toot.community^", "||toot.funami.tech^", "||toot.wales^", "||troet.cafe^", @@ -350,6 +349,7 @@ var blockedServices = []blockedService{{ "||urbanists.social^", "||vocalodon.net^", "||wxw.moe^", + "||xarxa.cloud^", }, }, { ID: "minecraft", diff --git a/internal/home/home.go b/internal/home/home.go index 6ff698d3..3085d66c 100644 --- a/internal/home/home.go +++ b/internal/home/home.go @@ -514,7 +514,8 @@ func run(opts options, clientBuildFS fs.FS) { Context.tls, err = newTLSManager(config.TLS) if err != nil { - log.Fatalf("initializing tls: %s", err) + log.Error("initializing tls: %s", err) + onConfigModified() } Context.web, err = initWeb(opts, clientBuildFS) diff --git a/internal/home/tls.go b/internal/home/tls.go index c9086629..4be63d9d 100644 --- a/internal/home/tls.go +++ b/internal/home/tls.go @@ -40,7 +40,9 @@ type tlsManager struct { conf tlsConfigSettings } -// newTLSManager initializes the TLS configuration. +// newTLSManager initializes the manager of TLS configuration. m is always +// non-nil while any returned error indicates that the TLS configuration isn't +// valid. Thus TLS may be initialized later, e.g. via the web UI. func newTLSManager(conf tlsConfigSettings) (m *tlsManager, err error) { m = &tlsManager{ status: &tlsConfigStatus{}, @@ -50,7 +52,9 @@ func newTLSManager(conf tlsConfigSettings) (m *tlsManager, err error) { if m.conf.Enabled { err = m.load() if err != nil { - return nil, err + m.conf.Enabled = false + + return m, err } m.setCertFileTime() diff --git a/openapi/CHANGELOG.md b/openapi/CHANGELOG.md index a57f313c..cc30989d 100644 --- a/openapi/CHANGELOG.md +++ b/openapi/CHANGELOG.md @@ -6,6 +6,14 @@ +## v0.107.20: API Changes + +### `POST /control/cache_clear` + +* The new `POST /control/cache_clear` HTTP API allows clearing the DNS cache. + + + ## v0.107.17: API Changes ### `GET /control/blocked_services/services` is deprecated diff --git a/openapi/openapi.yaml b/openapi/openapi.yaml index 4fb68670..b3135477 100644 --- a/openapi/openapi.yaml +++ b/openapi/openapi.yaml @@ -94,6 +94,15 @@ 'responses': '200': 'description': 'OK' + '/cache_clear': + 'post': + 'tags': + - 'global' + 'operationId': 'cacheClear' + 'summary': 'Clear DNS cache' + 'responses': + '200': + 'description': 'OK' '/test_upstream_dns': 'post': 'tags': diff --git a/scripts/vetted-filters/main.go b/scripts/vetted-filters/main.go index d5647924..e0076878 100644 --- a/scripts/vetted-filters/main.go +++ b/scripts/vetted-filters/main.go @@ -74,7 +74,11 @@ func main() { Name: f.Name, CategoryID: cat, Homepage: f.Homepage, - Source: f.SourceURL, + // NOTE: The source URL in filters.json is not guaranteed to contain + // the URL of the filtering rule list. So, use our mirror for the + // vetted blocklists, which are mostly guaranteed to be valid and + // available lists. + Source: f.DownloadURL, } } @@ -114,11 +118,11 @@ type hlFilters struct { // hlFiltersFilter is the JSON structure for a filter in the Hostlists Registry. type hlFiltersFilter struct { - FilterID string `json:"filterId"` - Name string `json:"name"` - Homepage string `json:"homepage"` - SourceURL string `json:"sourceUrl"` - Tags []string `json:"tags"` + DownloadURL string `json:"downloadUrl"` + FilterID string `json:"filterId"` + Homepage string `json:"homepage"` + Name string `json:"name"` + Tags []string `json:"tags"` } // category returns the AdGuard Home category for this filter. If there is no