diff --git a/CHANGELOG.md b/CHANGELOG.md index 023125aa..2c64ec96 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,21 @@ See also the [v0.107.19 GitHub milestone][ms-v0.107.19]. [ms-v0.107.19]: https://github.com/AdguardTeam/AdGuardHome/milestone/55?closed=1 --> +### Added + +- The ability to block popular Mastodon instances + ([AdguardTeam/HostlistsRegistry#100]). +- The new `--update` command-line option, which allows updating AdGuard Home + silently ([#4223]). + +### Changed + +- Minor UI changes. + +[#4223]: https://github.com/AdguardTeam/AdGuardHome/issues/4223 + +[AdguardTeam/HostlistsRegistry#100]: https://github.com/AdguardTeam/HostlistsRegistry/pull/100 + ## [v0.107.18] - 2022-11-08 diff --git a/client/src/__locales/ar.json b/client/src/__locales/ar.json index 54076076..020b790c 100644 --- a/client/src/__locales/ar.json +++ b/client/src/__locales/ar.json @@ -392,6 +392,7 @@ "encryption_issuer": "المصدر", "encryption_hostnames": "اسم المستضيف", "encryption_reset": "هل أنت متأكد أنك تريد إعادة تعيين إعدادات التشفير؟", + "encryption_warning": "تحذير", "topline_expiring_certificate": "شهادة SSL الخاصة بك على وشك الانتهاء. قم بتحديث <0>إعدادات التشفير.", "topline_expired_certificate": "انتهت صلاحية شهادة SSL الخاصة بك. قم بتحديث <0>إعدادات التشفير.", "form_error_port_range": "أدخل رقم المنفذ في النطاق 80-65535", diff --git a/client/src/__locales/be.json b/client/src/__locales/be.json index af646119..19e45a4b 100644 --- a/client/src/__locales/be.json +++ b/client/src/__locales/be.json @@ -393,6 +393,7 @@ "encryption_issuer": "Выдавец", "encryption_hostnames": "Імёны хастоў", "encryption_reset": "Вы ўпэўнены, што хочаце скінуць налады шыфравання?", + "encryption_warning": "Увага", "topline_expiring_certificate": "Ваш SSL-сертыфікат хутка мінае. Абновіце <0>Налады шыфравання.", "topline_expired_certificate": "Ваш SSL-сертыфікат мінуў. Абновіце <0>Налады шыфравання.", "form_error_port_range": "Увядзіце нумар порта з інтэрвалу 80-65535", diff --git a/client/src/__locales/bg.json b/client/src/__locales/bg.json index a64b7620..d7808318 100644 --- a/client/src/__locales/bg.json +++ b/client/src/__locales/bg.json @@ -244,6 +244,7 @@ "encryption_issuer": "Изпълнител", "encryption_hostnames": "Имена на хоста", "encryption_reset": "Сигурни ли сте че искате да изтриете настройките за криптиране?", + "encryption_warning": "Внимание", "topline_expiring_certificate": "Вашият SSL сертификат изтича. Обнови <0>Настройки за криптиране.", "topline_expired_certificate": "Вашият SSL сертификат е изтекъл. Обнови <0>Настройки за криптиране.", "form_error_port_range": "Въведете порт в диапазона 80-65535", diff --git a/client/src/__locales/cs.json b/client/src/__locales/cs.json index 160b1046..15f79692 100644 --- a/client/src/__locales/cs.json +++ b/client/src/__locales/cs.json @@ -393,6 +393,7 @@ "encryption_issuer": "Vydavatel", "encryption_hostnames": "Názvy hostitelů", "encryption_reset": "Opravdu chcete obnovit nastavení šifrování?", + "encryption_warning": "Varování", "topline_expiring_certificate": "Váš SSL certifikát brzy vyprší. Aktualizujte <0>Nastavení šifrování.", "topline_expired_certificate": "Váš SSL certifikát vypršel. Aktualizujte <0>Nastavení šifrování.", "form_error_port_range": "Zadejte číslo portu v rozmezí 80-65535", diff --git a/client/src/__locales/da.json b/client/src/__locales/da.json index 941645d8..7a999346 100644 --- a/client/src/__locales/da.json +++ b/client/src/__locales/da.json @@ -393,6 +393,7 @@ "encryption_issuer": "Udsteder", "encryption_hostnames": "Værtsnavne", "encryption_reset": "Sikker på, at du vil nulstille krypteringsindstillingerne?", + "encryption_warning": "Advarsel", "topline_expiring_certificate": "Dit SSL-certifikat er ved at udløbe. Opdatér <0>Krypteringsindstillinger.", "topline_expired_certificate": "Dit SSL-certifikat er udløbet. Opdatér <0>Krypteringsindstillinger.", "form_error_port_range": "Angiv portnummer i intervallet 80-65535", diff --git a/client/src/__locales/de.json b/client/src/__locales/de.json index 25c4bd9e..d1d45e6b 100644 --- a/client/src/__locales/de.json +++ b/client/src/__locales/de.json @@ -393,6 +393,7 @@ "encryption_issuer": "Ausgestellt von", "encryption_hostnames": "Hostnamen", "encryption_reset": "Möchten Sie die Verschlüsselungseinstellungen wirklich zurücksetzen?", + "encryption_warning": "Warnhinweis", "topline_expiring_certificate": "Ihr SSL-Zertifikat läuft demnächst ab. Aktualisieren Sie Ihre <0>Verschlüsselungseinstellungen.", "topline_expired_certificate": "Ihr SSL-Zertifikat ist abgelaufen. Aktualisieren Sie Ihre <0>Verschlüsselungseinstellungen.", "form_error_port_range": "Geben Sie die Portnummer zwischen 80 und 65535 ein", diff --git a/client/src/__locales/en.json b/client/src/__locales/en.json index b986dea1..7ec9c779 100644 --- a/client/src/__locales/en.json +++ b/client/src/__locales/en.json @@ -393,6 +393,7 @@ "encryption_issuer": "Issuer", "encryption_hostnames": "Hostnames", "encryption_reset": "Are you sure you want to reset encryption settings?", + "encryption_warning": "Warning", "topline_expiring_certificate": "Your SSL certificate is about to expire. Update <0>Encryption settings.", "topline_expired_certificate": "Your SSL certificate is expired. Update <0>Encryption settings.", "form_error_port_range": "Enter port number in the range of 80-65535", diff --git a/client/src/__locales/es.json b/client/src/__locales/es.json index 8f8eaf11..dc881214 100644 --- a/client/src/__locales/es.json +++ b/client/src/__locales/es.json @@ -393,6 +393,7 @@ "encryption_issuer": "Emisor", "encryption_hostnames": "Nombres de hosts", "encryption_reset": "¿Estás seguro de que deseas restablecer la configuración de cifrado?", + "encryption_warning": "Advertencia", "topline_expiring_certificate": "Tu certificado SSL está a punto de expirar. Actualiza la <0>configuración de cifrado.", "topline_expired_certificate": "Tu certificado SSL ha expirado. Actualiza la <0>configuración de cifrado.", "form_error_port_range": "Ingresa el número del puerto en el rango de 80 a 65535", diff --git a/client/src/__locales/fa.json b/client/src/__locales/fa.json index 719ca49f..1a6949ae 100644 --- a/client/src/__locales/fa.json +++ b/client/src/__locales/fa.json @@ -361,6 +361,7 @@ "encryption_issuer": "صادر کننده", "encryption_hostnames": "نام میزبان", "encryption_reset": "آیا میخواهید تنظیمات رمزگُذاری به پیش فرض بازگردد؟", + "encryption_warning": "هشدار", "topline_expiring_certificate": "گواهینامه اِس اِس اِل شما در صدد انقضاء است. <0>تنظیمات رمزگُذاری را بروز رسانی کنید.", "topline_expired_certificate": "گواهینامه اِس اِس اِل شما منقضی شده است. <0>تنظیمات رمزگُذاری را بروز رسانی کنید.", "form_error_port_range": "مقدار پورت را در محدوده 80-65535 وارد کنید", diff --git a/client/src/__locales/fi.json b/client/src/__locales/fi.json index 831b1cc6..fd02be0e 100644 --- a/client/src/__locales/fi.json +++ b/client/src/__locales/fi.json @@ -393,6 +393,7 @@ "encryption_issuer": "Toimittaja", "encryption_hostnames": "Isäntänimet", "encryption_reset": "Haluatko varmasti palauttaa salausasetukset?", + "encryption_warning": "Varoitus", "topline_expiring_certificate": "SSL-varmenteesi on erääntymässä. Päivitä <0>Salausasetukset.", "topline_expired_certificate": "SSL-varmenteesi on erääntynyt. Päivitä <0>Salausasetukset.", "form_error_port_range": "Syötä portti väliltä 80-65535", @@ -542,8 +543,8 @@ "descr": "Kuvaus", "whois": "WHOIS", "filtering_rules_learn_more": "<0>Lue lisää omien hosts-listojesi luonnista.", - "blocked_by_response": "Vastauksen sisältämän CNAME:n tai IP:n estämä", - "blocked_by_cname_or_ip": "CNAME:n tai IP:n estämä", + "blocked_by_response": "Estetty vastauksen CNAME:n tai IP:n perusteella", + "blocked_by_cname_or_ip": "Estetty CNAME:n tai IP:n perusteella", "try_again": "Yritä uudelleen", "domain_desc": "Syötä korvattava verkkotunnus tai jokerimerkki.", "example_rewrite_domain": "korvaa vain tämän verkkotunnuksen vastaukset", diff --git a/client/src/__locales/fr.json b/client/src/__locales/fr.json index 3f840948..310f429b 100644 --- a/client/src/__locales/fr.json +++ b/client/src/__locales/fr.json @@ -393,6 +393,7 @@ "encryption_issuer": "Émetteur", "encryption_hostnames": "Noms d'hôte", "encryption_reset": "Voulez-vous vraiment réinitialiser les paramètres de chiffrement ?", + "encryption_warning": "Attention", "topline_expiring_certificate": "Votre certificat SSL est sur le point d'expirer. Mettez à jour vos <0>Paramètres de chiffrement.", "topline_expired_certificate": "Votre certificat SSL a expiré. Mettez à jour vos <0>Paramètres de chiffrement.", "form_error_port_range": "Saisissez une valeur de port entre 80 et 65535", diff --git a/client/src/__locales/hr.json b/client/src/__locales/hr.json index e3ec2290..24f9376a 100644 --- a/client/src/__locales/hr.json +++ b/client/src/__locales/hr.json @@ -393,6 +393,7 @@ "encryption_issuer": "Izdavač", "encryption_hostnames": "Nazivi računala", "encryption_reset": "Jeste li sigurni da želite poništiti postavke šifriranja?", + "encryption_warning": "Upozorenje", "topline_expiring_certificate": "Vaš SSL certifikat uskoro ističe. Ažurirajte <0>Postavke šifriranja.", "topline_expired_certificate": "Vaš SSL certifikat je istekao. Ažurirajte <0>Postavke šifriranja.", "form_error_port_range": "Unesite broj porta od 80 do 65536", diff --git a/client/src/__locales/hu.json b/client/src/__locales/hu.json index 1c1cd839..bf408b34 100644 --- a/client/src/__locales/hu.json +++ b/client/src/__locales/hu.json @@ -393,6 +393,7 @@ "encryption_issuer": "Kibocsátó", "encryption_hostnames": "Hosztnevek", "encryption_reset": "Biztosan visszaállítja a titkosítási beállításokat?", + "encryption_warning": "Figyelmeztetés", "topline_expiring_certificate": "Az SSL-tanúsítványa hamarosan lejár. Frissítse a <0>Titkosítási beállításokat.", "topline_expired_certificate": "Az SSL-tanúsítványa lejárt. Frissítse a <0>Titkosítási beállításokat.", "form_error_port_range": "Adjon meg egy portszámot a 80-65535 tartományon belül", diff --git a/client/src/__locales/id.json b/client/src/__locales/id.json index 93526063..f285d669 100644 --- a/client/src/__locales/id.json +++ b/client/src/__locales/id.json @@ -393,6 +393,7 @@ "encryption_issuer": "Penerbit", "encryption_hostnames": "Nama host", "encryption_reset": "Anda yakin ingin mengatur ulang pengaturan enkripsi?", + "encryption_warning": "Perhatian", "topline_expiring_certificate": "Sertifikat SSL Anda hampir kedaluwarsa. Perbarui <0>Pengaturan enkripsi.", "topline_expired_certificate": "Sertifikat SSL Anda kedaluwarsa. Perbarui <0>Pengaturan enkripsi.", "form_error_port_range": "Masukkan nomor port di kisaran 80-65535", diff --git a/client/src/__locales/it.json b/client/src/__locales/it.json index 499a8d81..f71f8a40 100644 --- a/client/src/__locales/it.json +++ b/client/src/__locales/it.json @@ -393,6 +393,7 @@ "encryption_issuer": "Emittente", "encryption_hostnames": "Nomi host", "encryption_reset": "Sei sicuro di voler ripristinare le impostazioni di crittografia?", + "encryption_warning": "Attenzione", "topline_expiring_certificate": "Il tuo certificato SSL sta per scadere. Aggiorna le<0> Impostazioni di crittografia .", "topline_expired_certificate": "Il tuo certificato SSL è scaduto. Aggiorna le <0> Impostazioni di crittografia .", "form_error_port_range": "Immettere il valore della porta nell'intervallo 80-65535", diff --git a/client/src/__locales/ja.json b/client/src/__locales/ja.json index e4faaa06..398b11e8 100644 --- a/client/src/__locales/ja.json +++ b/client/src/__locales/ja.json @@ -393,6 +393,7 @@ "encryption_issuer": "発行者", "encryption_hostnames": "ホスト名", "encryption_reset": "暗号化設定をリセットして良いですか?", + "encryption_warning": "警告", "topline_expiring_certificate": "SSL証明書は期限切れになります。<0>暗号化設定を更新します。", "topline_expired_certificate": "SSL証明書は期限切れです。<0>暗号化設定を更新します。", "form_error_port_range": "80〜65535 の範囲内でポート番号を入力してください", diff --git a/client/src/__locales/ko.json b/client/src/__locales/ko.json index 779f2f98..de317c8a 100644 --- a/client/src/__locales/ko.json +++ b/client/src/__locales/ko.json @@ -393,6 +393,7 @@ "encryption_issuer": "발행자", "encryption_hostnames": "호스트 이름", "encryption_reset": "암호화 설정을 재설정하시겠습니까?", + "encryption_warning": "경고", "topline_expiring_certificate": "SSL 인증서가 곧 만료됩니다. 업데이트<0> 암호화 설정.", "topline_expired_certificate": "SSL 인증서가 만료되었습니다. 업데이트<0> 암호화 설정.", "form_error_port_range": "80-65535 범위의 포트 번호를 입력하세요", diff --git a/client/src/__locales/nl.json b/client/src/__locales/nl.json index c21addcf..a7722884 100644 --- a/client/src/__locales/nl.json +++ b/client/src/__locales/nl.json @@ -393,6 +393,7 @@ "encryption_issuer": "Uitgever", "encryption_hostnames": "Hostnamen", "encryption_reset": "Ben je zeker dat je de encryptie instellingen wil resetten?", + "encryption_warning": "Waarschuwing", "topline_expiring_certificate": "Jouw SSL-certificaat vervalt binnenkort. Werk de <0>encryptie-instellingen bij.", "topline_expired_certificate": "Jouw SSL-certificaat is vervallen. Werk de <0>encryptie-instellingen bij.", "form_error_port_range": "Poortnummer invoeren tussen 80 en 65535", diff --git a/client/src/__locales/no.json b/client/src/__locales/no.json index ca9baf02..07499b86 100644 --- a/client/src/__locales/no.json +++ b/client/src/__locales/no.json @@ -373,6 +373,7 @@ "encryption_issuer": "Utsteder", "encryption_hostnames": "Vertsnavn", "encryption_reset": "Er du sikker på at du vil tilbakestille krypteringsinnstillingene?", + "encryption_warning": "Advarsel", "topline_expiring_certificate": "Ditt SSL-sertifikat er i ferd med å utløpe. Oppdater <0>Krypteringsinnstillinger.", "topline_expired_certificate": "SSL-sertifikatet har utløpt. Oppdater <0>Krypteringsinnstillinger.", "form_error_port_range": "Skriv inn et portnummer i området 80-65535", diff --git a/client/src/__locales/pl.json b/client/src/__locales/pl.json index 8e5ccb4b..d175b408 100644 --- a/client/src/__locales/pl.json +++ b/client/src/__locales/pl.json @@ -393,6 +393,7 @@ "encryption_issuer": "Zgłaszający", "encryption_hostnames": "Nazwy hostów", "encryption_reset": "Czy na pewno chcesz zresetować ustawienia szyfrowania?", + "encryption_warning": "Uwaga!", "topline_expiring_certificate": "Twój certyfikat SSL wkrótce wygaśnie. Zaktualizuj <0>Ustawienia szyfrowania.", "topline_expired_certificate": "Twój certyfikat SSL wygasł. Zaktualizuj <0>Ustawienia szyfrowania.", "form_error_port_range": "Wpisz numer portu z zakresu 80-65535", diff --git a/client/src/__locales/pt-br.json b/client/src/__locales/pt-br.json index ee8b5c1e..ea2da002 100644 --- a/client/src/__locales/pt-br.json +++ b/client/src/__locales/pt-br.json @@ -393,6 +393,7 @@ "encryption_issuer": "Emissor", "encryption_hostnames": "Nomes dos servidores", "encryption_reset": "Você tem certeza de que deseja redefinir a configuração de criptografia?", + "encryption_warning": "Aviso", "topline_expiring_certificate": "Seu certificado SSL está prestes a expirar. Atualize suas <0>configurações de criptografia", "topline_expired_certificate": "Seu certificado SSL está expirado. Atualize suas <0>configurações de criptografia", "form_error_port_range": "Digite um número de porta entre 80 e 65535", diff --git a/client/src/__locales/pt-pt.json b/client/src/__locales/pt-pt.json index b143afae..81c92867 100644 --- a/client/src/__locales/pt-pt.json +++ b/client/src/__locales/pt-pt.json @@ -393,6 +393,7 @@ "encryption_issuer": "Emissor", "encryption_hostnames": "Nomes dos servidores", "encryption_reset": "Tem a certeza de que deseja repor a definição de criptografia?", + "encryption_warning": "Aviso", "topline_expiring_certificate": "O seu certificado SSL está prestes a expirar. Atualize as suas <0>definições de criptografia.", "topline_expired_certificate": "O seu certificado SSL está expirado. Atualize as suas <0>definições de criptografia.", "form_error_port_range": "Digite um numero de porta entre 80 e 65535", diff --git a/client/src/__locales/ro.json b/client/src/__locales/ro.json index a8f94d39..82c86901 100644 --- a/client/src/__locales/ro.json +++ b/client/src/__locales/ro.json @@ -393,6 +393,7 @@ "encryption_issuer": "Emitent", "encryption_hostnames": "Nume de host", "encryption_reset": "Sunteți sigur că doriți să resetați setările de criptare?", + "encryption_warning": "Avertisment", "topline_expiring_certificate": "Certificatul dvs. SSL este pe cale să expire. Actualizați <0>Setările de criptare.", "topline_expired_certificate": "Certificatul dvs. SSL a expirat. Actualizați <0>Setările de criptare.", "form_error_port_range": "Introduceți valoarea portului între 80-65535", diff --git a/client/src/__locales/ru.json b/client/src/__locales/ru.json index 7cf3732f..c1a5119b 100644 --- a/client/src/__locales/ru.json +++ b/client/src/__locales/ru.json @@ -393,6 +393,7 @@ "encryption_issuer": "Издатель", "encryption_hostnames": "Имена хостов", "encryption_reset": "Вы уверены, что хотите сбросить настройки шифрования?", + "encryption_warning": "Предупреждение", "topline_expiring_certificate": "Ваш SSL-сертификат скоро истекает. Обновите <0>Настройки шифрования.", "topline_expired_certificate": "Ваш SSL-сертификат истёк. Обновите <0>Настройки шифрования.", "form_error_port_range": "Введите номер порта из интервала 80-65535", diff --git a/client/src/__locales/sk.json b/client/src/__locales/sk.json index 243ed299..473557c4 100644 --- a/client/src/__locales/sk.json +++ b/client/src/__locales/sk.json @@ -393,6 +393,7 @@ "encryption_issuer": "Vydavateľ", "encryption_hostnames": "Názvy hostiteľov", "encryption_reset": "Naozaj chcete obnoviť nastavenia šifrovania?", + "encryption_warning": "Varovanie", "topline_expiring_certificate": "Váš SSL certifikát čoskoro vyprší. Aktualizujte <0>Nastavenia šifrovania.", "topline_expired_certificate": "Váš SSL certifikát vypršal. Aktualizujte <0>Nastavenia šifrovania.", "form_error_port_range": "Zadajte číslo portu v rozsahu 80-65535", diff --git a/client/src/__locales/sl.json b/client/src/__locales/sl.json index 2c8436ae..7f854f0a 100644 --- a/client/src/__locales/sl.json +++ b/client/src/__locales/sl.json @@ -393,6 +393,7 @@ "encryption_issuer": "Izdajatelj", "encryption_hostnames": "Imena gostiteljev", "encryption_reset": "Ali ste prepričani, da želite ponastaviti nastavitve šifriranja?", + "encryption_warning": "Opozorilo", "topline_expiring_certificate": "Vaš e digitalno potrdilo SSL bo kmalu poteklol. Posodobite <0>Nastavitve šifriranja.", "topline_expired_certificate": "Vaše digitalno potrdilo SSL je poteklo. Posodobi <0>Nastavitve šifriranja.", "form_error_port_range": "Vnesite številko vrat v razponu med 80-65535", diff --git a/client/src/__locales/sr-cs.json b/client/src/__locales/sr-cs.json index 4bb3f011..9b676752 100644 --- a/client/src/__locales/sr-cs.json +++ b/client/src/__locales/sr-cs.json @@ -393,6 +393,7 @@ "encryption_issuer": "Izdavač", "encryption_hostnames": "Imena hostova", "encryption_reset": "Jeste li sigurni da želite dda resetujete postavke šifrovanja?", + "encryption_warning": "Upozorenje", "topline_expiring_certificate": "Vaš SSL sertifikat uskoro ističe. Ažurirajte <0>postavke šifrovanja.", "topline_expired_certificate": "Vaš SSL sertifikat je istekao. Ažurirajte <0>postavke šifrovanja.", "form_error_port_range": "Unesite vrednost porta u opsegu od 80-65535", diff --git a/client/src/__locales/sv.json b/client/src/__locales/sv.json index 2d3cbcaa..50c12d07 100644 --- a/client/src/__locales/sv.json +++ b/client/src/__locales/sv.json @@ -393,6 +393,7 @@ "encryption_issuer": "Utgivare", "encryption_hostnames": "Värdnamn", "encryption_reset": "Är du säker på att du vill återställa krypteringsinställningarna?", + "encryption_warning": "Varning", "topline_expiring_certificate": "Ditt SSL-certifikat håller på att gå ut. <0>Krypteringsinställningar.", "topline_expired_certificate": "Ditt SSL-certifikat har gått ut. Uppdatera <0>Krypteringsinställningar-", "form_error_port_range": "Ange ett portnummer inom värdena 80-65535", diff --git a/client/src/__locales/th.json b/client/src/__locales/th.json index c149c767..c7ccb39b 100644 --- a/client/src/__locales/th.json +++ b/client/src/__locales/th.json @@ -262,6 +262,7 @@ "encryption_issuer": "ผู้ออกใบรับรอง:", "encryption_hostnames": "ชื่อโฮส", "encryption_reset": "คุณแน่ใจนะว่าจะล้างค่าการเข้ารหัส?", + "encryption_warning": "คำเตือน", "topline_expiring_certificate": "ใบรับรอง SSL ของคุณกำลังจะหมดอายุ กรุณาอัปเดท <0>การตั้งค่าเข้ารหัส.", "topline_expired_certificate": "ใบรับรอง SSL ของคุณหมดอายุแล้ว กรุณาอัปเดท <0>การตั้งค่าเข้ารหัส.", "form_error_port_unsafe": "เป็นพอร์ทที่ไม่ปลอดภัย", diff --git a/client/src/__locales/tr.json b/client/src/__locales/tr.json index 791c397d..a24028f1 100644 --- a/client/src/__locales/tr.json +++ b/client/src/__locales/tr.json @@ -393,6 +393,7 @@ "encryption_issuer": "Sağlayan", "encryption_hostnames": "Ana makine adları", "encryption_reset": "Şifreleme ayarlarını sıfırlamak istediğinizden emin misiniz?", + "encryption_warning": "Uyarı", "topline_expiring_certificate": "SSL sertifikanızın süresi sona üzere. <0>Şifreleme ayarlarını güncelleyin.", "topline_expired_certificate": "SSL sertifikanızın süresi sona erdi. <0>Şifreleme ayarlarını güncelleyin.", "form_error_port_range": "80-65535 aralığında geçerli bir bağlantı noktası değeri girin", diff --git a/client/src/__locales/uk.json b/client/src/__locales/uk.json index d9f61146..1041ae8d 100644 --- a/client/src/__locales/uk.json +++ b/client/src/__locales/uk.json @@ -371,7 +371,7 @@ "encryption_redirect": "Автоматично перенаправляти на HTTPS", "encryption_redirect_desc": "Якщо встановлено, AdGuard Home автоматично перенаправить вас з HTTP на адреси HTTPS.", "encryption_https": "Порт HTTPS", - "encryption_https_desc": "Якщо HTTPS-порт налаштовано, інтерфейс адміністратора AdGuard Home буде доступний через HTTPS, а також DNS-over-HTTPS-сервер буде доступний за адресою /dns-query.", + "encryption_https_desc": "Якщо HTTPS-порт налаштовано, інтерфейс адміністратора AdGuard Home буде доступний через HTTPS, а також сервер DNS-over-HTTPS буде доступний за адресою '/dns-query'.", "encryption_dot": "Порт DNS-over-TLS", "encryption_dot_desc": "Якщо цей порт налаштовано, AdGuard Home запустить на цьому порту сервер DNS-over-TLS.", "encryption_doq": "Порт DNS-over-QUIC", @@ -393,6 +393,7 @@ "encryption_issuer": "Видавець", "encryption_hostnames": "Назви вузлів", "encryption_reset": "Ви впевнені, що хочете скинути налаштування шифрування?", + "encryption_warning": "Увага", "topline_expiring_certificate": "Ваш сертифікат SSL скоро закінчиться. Оновіть <0>Налаштування шифрування.", "topline_expired_certificate": "Термін дії вашого сертифіката SSL закінчився. Оновіть <0>Налаштування шифрування.", "form_error_port_range": "Введіть значення порту в діапазоні 80−65535", diff --git a/client/src/__locales/vi.json b/client/src/__locales/vi.json index 52020d80..7674d372 100644 --- a/client/src/__locales/vi.json +++ b/client/src/__locales/vi.json @@ -393,6 +393,7 @@ "encryption_issuer": "Phát hành", "encryption_hostnames": "Tên máy chủ", "encryption_reset": "Bạn có chắc chắn muốn đặt lại cài đặt mã hóa?", + "encryption_warning": "Cảnh báo", "topline_expiring_certificate": "Chứng chỉ SSL của bạn sắp hết hạn. Cập nhật <0>Cài đặt mã hóa.", "topline_expired_certificate": "Chứng chỉ SSL của bạn đã hết hạn. Cập nhật <0>Cài đặt mã hóa.", "form_error_port_range": "Nhập giá trị cổng trong phạm vi 80-65535", diff --git a/client/src/__locales/zh-cn.json b/client/src/__locales/zh-cn.json index cc22c96b..5a19d904 100644 --- a/client/src/__locales/zh-cn.json +++ b/client/src/__locales/zh-cn.json @@ -393,6 +393,7 @@ "encryption_issuer": "颁发者", "encryption_hostnames": "主机名", "encryption_reset": "您确定想要重置加密设置?", + "encryption_warning": "警告", "topline_expiring_certificate": "您的 SSL 证书即将过期。请更新 <0>加密设置 。", "topline_expired_certificate": "您的 SSL 证书已过期。请更新 <0>加密设置 。", "form_error_port_range": "输入 80 - 65535 范围内的端口值", diff --git a/client/src/__locales/zh-hk.json b/client/src/__locales/zh-hk.json index 99028a59..82a58118 100644 --- a/client/src/__locales/zh-hk.json +++ b/client/src/__locales/zh-hk.json @@ -381,6 +381,7 @@ "encryption_issuer": "簽發者", "encryption_hostnames": "主機名稱", "encryption_reset": "您確定要重設加密設定嗎?", + "encryption_warning": "警告", "topline_expiring_certificate": "您的 SSL 憑證即將到期。請前往<0>加密設定更新。", "topline_expired_certificate": "您的 SSL 憑證已到期。請前往<0>加密設定更新。", "form_error_port_range": "輸入範圍 80-65535 中的值", diff --git a/client/src/__locales/zh-tw.json b/client/src/__locales/zh-tw.json index 4a5913ba..e1ae9b0a 100644 --- a/client/src/__locales/zh-tw.json +++ b/client/src/__locales/zh-tw.json @@ -393,6 +393,7 @@ "encryption_issuer": "簽發者", "encryption_hostnames": "主機名稱", "encryption_reset": "您確定您想要重置加密設定嗎?", + "encryption_warning": "警告", "topline_expiring_certificate": "您的安全通訊端層(SSL)憑證即將到期。更新<0>加密設定。", "topline_expired_certificate": "您的安全通訊端層(SSL)憑證為已到期的。更新<0>加密設定。", "form_error_port_range": "輸入在 80-65535 之範圍內的連接埠號碼", diff --git a/client/src/components/Settings/Encryption/Form.js b/client/src/components/Settings/Encryption/Form.js index b94dd94b..9df440aa 100644 --- a/client/src/components/Settings/Encryption/Form.js +++ b/client/src/components/Settings/Encryption/Form.js @@ -56,6 +56,26 @@ const clearFields = (change, setTlsConfig, t) => { } }; +const validationMessage = (warningValidation, isWarning) => { + if (!warningValidation) { + return null; + } + + if (isWarning) { + return ( +
+

encryption_warning: {warningValidation}

+
+ ); + } + + return ( +
+

{warningValidation}

+
+ ); +}; + let Form = (props) => { const { t, @@ -95,6 +115,8 @@ let Form = (props) => { || !valid_cert || !valid_pair; + const isWarning = valid_key && valid_cert && valid_pair; + return (
@@ -382,11 +404,7 @@ let Form = (props) => { )}
- {warning_validation && ( -
-

{warning_validation}

-
- )} + {validationMessage(warning_validation, isWarning)}
diff --git a/internal/aghnet/net.go b/internal/aghnet/net.go index bdcc6e4f..b8c8c05e 100644 --- a/internal/aghnet/net.go +++ b/internal/aghnet/net.go @@ -31,12 +31,6 @@ var ( // the IP being static is available. const ErrNoStaticIPInfo errors.Error = "no information about static ip" -// IPv4Localhost returns 127.0.0.1, which returns true for [netip.Addr.Is4]. -func IPv4Localhost() (ip netip.Addr) { return netip.AddrFrom4([4]byte{127, 0, 0, 1}) } - -// IPv6Localhost returns ::1, which returns true for [netip.Addr.Is6]. -func IPv6Localhost() (ip netip.Addr) { return netip.AddrFrom16([16]byte{15: 1}) } - // IfaceHasStaticIP checks if interface is configured to have static IP address. // If it can't give a definitive answer, it returns false and an error for which // errors.Is(err, ErrNoStaticIPInfo) is true. diff --git a/internal/aghnet/net_test.go b/internal/aghnet/net_test.go index f4275c6c..6e9e612e 100644 --- a/internal/aghnet/net_test.go +++ b/internal/aghnet/net_test.go @@ -188,7 +188,7 @@ func TestBroadcastFromIPNet(t *testing.T) { } func TestCheckPort(t *testing.T) { - laddr := netip.AddrPortFrom(IPv4Localhost(), 0) + laddr := netip.AddrPortFrom(netutil.IPv4Localhost(), 0) t.Run("tcp_bound", func(t *testing.T) { l, err := net.Listen("tcp", laddr.String()) diff --git a/internal/dnsforward/clientid.go b/internal/dnsforward/clientid.go index 6a111c47..fb5eefda 100644 --- a/internal/dnsforward/clientid.go +++ b/internal/dnsforward/clientid.go @@ -23,16 +23,6 @@ func ValidateClientID(id string) (err error) { return nil } -// hasLabelSuffix returns true if s ends with suffix preceded by a dot. It's -// a helper function to prevent unnecessary allocations in code like: -// -// if strings.HasSuffix(s, "." + suffix) { /* … */ } -// -// s must be longer than suffix. -func hasLabelSuffix(s, suffix string) (ok bool) { - return strings.HasSuffix(s, suffix) && s[len(s)-len(suffix)-1] == '.' -} - // clientIDFromClientServerName extracts and validates a ClientID. hostSrvName // is the server name of the host. cliSrvName is the server name as sent by the // client. When strict is true, and client and host server name don't match, @@ -46,7 +36,7 @@ func clientIDFromClientServerName( return "", nil } - if !hasLabelSuffix(cliSrvName, hostSrvName) { + if !netutil.IsImmediateSubdomain(cliSrvName, hostSrvName) { if !strict { return "", nil } diff --git a/internal/dnsforward/dnsforward.go b/internal/dnsforward/dnsforward.go index 6d107153..53091e23 100644 --- a/internal/dnsforward/dnsforward.go +++ b/internal/dnsforward/dnsforward.go @@ -246,6 +246,7 @@ type RDNSExchanger interface { // Exchange tries to resolve the ip in a suitable way, e.g. either as // local or as external. Exchange(ip net.IP) (host string, err error) + // ResolvesPrivatePTR returns true if the RDNSExchanger is able to // resolve PTR requests for locally-served addresses. ResolvesPrivatePTR() (ok bool) @@ -261,6 +262,9 @@ const ( rDNSNotPTRErr errors.Error = "the response is not a ptr" ) +// type check +var _ RDNSExchanger = (*Server)(nil) + // Exchange implements the RDNSExchanger interface for *Server. func (s *Server) Exchange(ip net.IP) (host string, err error) { s.serverLock.RLock() @@ -675,21 +679,13 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { // IsBlockedClient returns true if the client is blocked by the current access // settings. -func (s *Server) IsBlockedClient(ip net.IP, clientID string) (blocked bool, rule string) { +func (s *Server) IsBlockedClient(ip netip.Addr, clientID string) (blocked bool, rule string) { s.serverLock.RLock() defer s.serverLock.RUnlock() blockedByIP := false - if ip != nil { - // TODO(a.garipov): Remove once we switch to netip.Addr more fully. - ipAddr, err := netutil.IPToAddrNoMapped(ip) - if err != nil { - log.Error("dnsforward: bad client ip %v: %s", ip, err) - - return false, "" - } - - blockedByIP, rule = s.access.isBlockedIP(ipAddr) + if ip != (netip.Addr{}) { + blockedByIP, rule = s.access.isBlockedIP(ip) } allowlistMode := s.access.allowlistMode() diff --git a/internal/dnsforward/filter.go b/internal/dnsforward/filter.go index 6f64d35d..f36fd52a 100644 --- a/internal/dnsforward/filter.go +++ b/internal/dnsforward/filter.go @@ -19,13 +19,13 @@ func (s *Server) beforeRequestHandler( _ *proxy.Proxy, pctx *proxy.DNSContext, ) (reply bool, err error) { - ip, _ := netutil.IPAndPortFromAddr(pctx.Addr) clientID, err := s.clientIDFromDNSContext(pctx) if err != nil { return false, fmt.Errorf("getting clientid: %w", err) } - blocked, _ := s.IsBlockedClient(ip, clientID) + addrPort := netutil.NetAddrToAddrPort(pctx.Addr) + blocked, _ := s.IsBlockedClient(addrPort.Addr(), clientID) if blocked { return s.preBlockedResponse(pctx) } diff --git a/internal/filtering/filter_test.go b/internal/filtering/filter_test.go index 99014bd0..81a7929a 100644 --- a/internal/filtering/filter_test.go +++ b/internal/filtering/filter_test.go @@ -11,7 +11,7 @@ import ( "testing" "time" - "github.com/AdguardTeam/AdGuardHome/internal/aghnet" + "github.com/AdguardTeam/golibs/netutil" "github.com/AdguardTeam/golibs/testutil" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -40,7 +40,7 @@ func serveFiltersLocally(t *testing.T, fltContent []byte) (ipp netip.AddrPort) { addr := l.Addr() require.IsType(t, new(net.TCPAddr), addr) - return netip.AddrPortFrom(aghnet.IPv4Localhost(), uint16(addr.(*net.TCPAddr).Port)) + return netip.AddrPortFrom(netutil.IPv4Localhost(), uint16(addr.(*net.TCPAddr).Port)) } func TestFilters(t *testing.T) { diff --git a/internal/filtering/servicelist.go b/internal/filtering/servicelist.go index 89b8510b..f640097e 100644 --- a/internal/filtering/servicelist.go +++ b/internal/filtering/servicelist.go @@ -246,6 +246,111 @@ var blockedServices = []blockedService{{ Rules: []string{ "||mail.ru^", }, +}, { + ID: "mastodon", + Name: "Mastodon", + IconSVG: []byte(""), + Rules: []string{ + "||awscommunity.social^", + "||colorid.es^", + "||dizl.de^", + "||dju.social^", + "||dresden.network^", + "||fedibird.com^", + "||fosstodon.org^", + "||freiburg.social^", + "||glasgow.social^", + "||h4.io^", + "||hachyderm.io^", + "||hessen.social^", + "||hispagatos.space^", + "||home.social^", + "||hostux.social^", + "||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^", + "||masto.ai^", + "||masto.es^", + "||masto.nobigtech.es^", + "||masto.pt^", + "||mastodon-belgium.be^", + "||mastodon.au^", + "||mastodon.bida.im^", + "||mastodon.eus^", + "||mastodon.ie^", + "||mastodon.iriseden.eu^", + "||mastodon.nl^", + "||mastodon.nu^", + "||mastodon.nz^", + "||mastodon.online^", + "||mastodon.scot^", + "||mastodon.sdf.org^", + "||mastodon.se^", + "||mastodon.social^", + "||mastodon.uno^", + "||mastodon.world^", + "||mastodon.zaclys.com^", + "||mastodonapp.uk^", + "||mastodont.cat^", + "||mastodontech.de^", + "||mastodontti.fi^", + "||mastouille.fr^", + "||mathstodon.xyz^", + "||mindly.social^", + "||mstdn.ca^", + "||mstdn.jp^", + "||mstdn.party^", + "||mstdn.social^", + "||muenchen.social^", + "||muenster.im^", + "||newsie.social^", + "||noc.social^", + "||norden.social^", + "||nrw.social^", + "||o3o.ca^", + "||ohai.social^", + "||oslo.town^", + "||pettingzoo.co^", + "||pewtix.com^", + "||phpc.social^", + "||piaille.fr^", + "||pol.social^", + "||qdon.space^", + "||ravenation.club^", + "||rollenspiel.social^", + "||ruby.social^", + "||ruhr.social^", + "||sfba.social^", + "||snabelen.no^", + "||social.anoxinon.de^", + "||social.cologne^", + "||social.dev-wiki.de^", + "||social.politicaconciencia.org^", + "||social.vivaldi.net^", + "||sociale.network^", + "||sueden.social^", + "||techhub.social^", + "||theblower.au^", + "||tkz.one^", + "||toot.aquilenet.fr^", + "||toot.funami.tech^", + "||toot.wales^", + "||troet.cafe^", + "||uiuxdev.social^", + "||union.place^", + "||universeodon.com^", + "||urbanists.social^", + "||vocalodon.net^", + "||wxw.moe^", + }, }, { ID: "minecraft", Name: "Minecraft", diff --git a/internal/home/clients.go b/internal/home/clients.go index fa230178..f46c616e 100644 --- a/internal/home/clients.go +++ b/internal/home/clients.go @@ -129,7 +129,7 @@ type RuntimeClientWHOISInfo struct { type clientsContainer struct { // TODO(a.garipov): Perhaps use a number of separate indices for - // different types (string, net.IP, and so on). + // different types (string, netip.Addr, and so on). list map[string]*Client // name -> client idIndex map[string]*Client // ID -> client @@ -333,7 +333,7 @@ func (clients *clientsContainer) onDHCPLeaseChanged(flags int) { } // exists checks if client with this IP address already exists. -func (clients *clientsContainer) exists(ip net.IP, source clientSource) (ok bool) { +func (clients *clientsContainer) exists(ip netip.Addr, source clientSource) (ok bool) { clients.lock.Lock() defer clients.lock.Unlock() @@ -342,7 +342,7 @@ func (clients *clientsContainer) exists(ip net.IP, source clientSource) (ok bool return true } - rc, ok := clients.findRuntimeClientLocked(ip) + rc, ok := clients.ipToRC[ip] if !ok { return false } @@ -371,7 +371,8 @@ func (clients *clientsContainer) findMultiple(ids []string) (c *querylog.Client, var artClient *querylog.Client var art bool for _, id := range ids { - c, art = clients.clientOrArtificial(net.ParseIP(id), id) + ip, _ := netip.ParseAddr(id) + c, art = clients.clientOrArtificial(ip, id) if art { artClient = c @@ -389,7 +390,7 @@ func (clients *clientsContainer) findMultiple(ids []string) (c *querylog.Client, // records about this client besides maybe whether or not it is blocked. c is // never nil. func (clients *clientsContainer) clientOrArtificial( - ip net.IP, + ip netip.Addr, id string, ) (c *querylog.Client, art bool) { defer func() { @@ -406,13 +407,6 @@ func (clients *clientsContainer) clientOrArtificial( }, false } - if ip == nil { - // Technically should never happen, but still. - return &querylog.Client{ - Name: "", - }, true - } - var rc *RuntimeClient rc, ok = clients.findRuntimeClient(ip) if ok { @@ -492,19 +486,20 @@ func (clients *clientsContainer) findLocked(id string) (c *Client, ok bool) { return c, true } - ip := net.ParseIP(id) - if ip == nil { + ip, err := netip.ParseAddr(id) + if err != nil { return nil, false } for _, c = range clients.list { for _, id := range c.IDs { - _, ipnet, err := net.ParseCIDR(id) + var n netip.Prefix + n, err = netip.ParsePrefix(id) if err != nil { continue } - if ipnet.Contains(ip) { + if n.Contains(ip) { return c, true } } @@ -514,19 +509,20 @@ func (clients *clientsContainer) findLocked(id string) (c *Client, ok bool) { return nil, false } - macFound := clients.dhcpServer.FindMACbyIP(ip) + macFound := clients.dhcpServer.FindMACbyIP(ip.AsSlice()) if macFound == nil { return nil, false } for _, c = range clients.list { for _, id := range c.IDs { - hwAddr, err := net.ParseMAC(id) + var mac net.HardwareAddr + mac, err = net.ParseMAC(id) if err != nil { continue } - if bytes.Equal(hwAddr, macFound) { + if bytes.Equal(mac, macFound) { return c, true } } @@ -535,32 +531,18 @@ func (clients *clientsContainer) findLocked(id string) (c *Client, ok bool) { return nil, false } -// findRuntimeClientLocked finds a runtime client by their IP address. For -// internal use only. -func (clients *clientsContainer) findRuntimeClientLocked(ip net.IP) (rc *RuntimeClient, ok bool) { - // TODO(a.garipov): Remove once we switch to netip.Addr more fully. - ipAddr, err := netutil.IPToAddrNoMapped(ip) - if err != nil { - log.Error("clients: bad client ip %v: %s", ip, err) - - return nil, false - } - - rc, ok = clients.ipToRC[ipAddr] - - return rc, ok -} - // findRuntimeClient finds a runtime client by their IP. -func (clients *clientsContainer) findRuntimeClient(ip net.IP) (rc *RuntimeClient, ok bool) { - if ip == nil { +func (clients *clientsContainer) findRuntimeClient(ip netip.Addr) (rc *RuntimeClient, ok bool) { + if ip == (netip.Addr{}) { return nil, false } clients.lock.Lock() defer clients.lock.Unlock() - return clients.findRuntimeClientLocked(ip) + rc, ok = clients.ipToRC[ip] + + return rc, ok } // check validates the client. @@ -578,14 +560,16 @@ func (clients *clientsContainer) check(c *Client) (err error) { for i, id := range c.IDs { // Normalize structured data. - var ip net.IP - var ipnet *net.IPNet - var mac net.HardwareAddr - if ip = net.ParseIP(id); ip != nil { + var ( + ip netip.Addr + n netip.Prefix + mac net.HardwareAddr + ) + + if ip, err = netip.ParseAddr(id); err == nil { c.IDs[i] = ip.String() - } else if ip, ipnet, err = net.ParseCIDR(id); err == nil { - ipnet.IP = ip - c.IDs[i] = ipnet.String() + } else if n, err = netip.ParsePrefix(id); err == nil { + c.IDs[i] = n.String() } else if mac, err = net.ParseMAC(id); err == nil { c.IDs[i] = mac.String() } else if err = dnsforward.ValidateClientID(id); err == nil { @@ -750,7 +734,7 @@ func (clients *clientsContainer) Update(name string, c *Client) (err error) { } // setWHOISInfo sets the WHOIS information for a client. -func (clients *clientsContainer) setWHOISInfo(ip net.IP, wi *RuntimeClientWHOISInfo) { +func (clients *clientsContainer) setWHOISInfo(ip netip.Addr, wi *RuntimeClientWHOISInfo) { clients.lock.Lock() defer clients.lock.Unlock() @@ -760,7 +744,7 @@ func (clients *clientsContainer) setWHOISInfo(ip net.IP, wi *RuntimeClientWHOISI return } - rc, ok := clients.findRuntimeClientLocked(ip) + rc, ok := clients.ipToRC[ip] if ok { rc.WHOISInfo = wi log.Debug("clients: set whois info for runtime client %s: %+v", rc.Host, wi) @@ -776,32 +760,22 @@ func (clients *clientsContainer) setWHOISInfo(ip net.IP, wi *RuntimeClientWHOISI rc.WHOISInfo = wi - // TODO(a.garipov): Remove once we switch to netip.Addr more fully. - ipAddr, err := netutil.IPToAddrNoMapped(ip) - if err != nil { - log.Error("clients: bad client ip %v: %s", ip, err) - - return - } - - clients.ipToRC[ipAddr] = rc + clients.ipToRC[ip] = rc log.Debug("clients: set whois info for runtime client with ip %s: %+v", ip, wi) } // AddHost adds a new IP-hostname pairing. The priorities of the sources are // taken into account. ok is true if the pairing was added. -func (clients *clientsContainer) AddHost(ip net.IP, host string, src clientSource) (ok bool, err error) { +func (clients *clientsContainer) AddHost( + ip netip.Addr, + host string, + src clientSource, +) (ok bool) { clients.lock.Lock() defer clients.lock.Unlock() - // TODO(a.garipov): Remove once we switch to netip.Addr more fully. - ipAddr, err := netutil.IPToAddrNoMapped(ip) - if err != nil { - return false, fmt.Errorf("adding host: %w", err) - } - - return clients.addHostLocked(ipAddr, host, src), nil + return clients.addHostLocked(ip, host, src) } // addHostLocked adds a new IP-hostname pairing. clients.lock is expected to be diff --git a/internal/home/clients_test.go b/internal/home/clients_test.go index 636971eb..ca98352a 100644 --- a/internal/home/clients_test.go +++ b/internal/home/clients_test.go @@ -22,8 +22,18 @@ func TestClients(t *testing.T) { clients.Init(nil, nil, nil, nil) t.Run("add_success", func(t *testing.T) { + var ( + cliNone = "1.2.3.4" + cli1 = "1.1.1.1" + cli2 = "2.2.2.2" + + cliNoneIP = netip.MustParseAddr(cliNone) + cli1IP = netip.MustParseAddr(cli1) + cli2IP = netip.MustParseAddr(cli2) + ) + c := &Client{ - IDs: []string{"1.1.1.1", "1:2:3::4", "aa:aa:aa:aa:aa:aa"}, + IDs: []string{cli1, "1:2:3::4", "aa:aa:aa:aa:aa:aa"}, Name: "client1", } @@ -33,7 +43,7 @@ func TestClients(t *testing.T) { assert.True(t, ok) c = &Client{ - IDs: []string{"2.2.2.2"}, + IDs: []string{cli2}, Name: "client2", } @@ -42,7 +52,7 @@ func TestClients(t *testing.T) { assert.True(t, ok) - c, ok = clients.Find("1.1.1.1") + c, ok = clients.Find(cli1) require.True(t, ok) assert.Equal(t, "client1", c.Name) @@ -52,14 +62,14 @@ func TestClients(t *testing.T) { assert.Equal(t, "client1", c.Name) - c, ok = clients.Find("2.2.2.2") + c, ok = clients.Find(cli2) require.True(t, ok) assert.Equal(t, "client2", c.Name) - assert.False(t, clients.exists(net.IP{1, 2, 3, 4}, ClientSourceHostsFile)) - assert.True(t, clients.exists(net.IP{1, 1, 1, 1}, ClientSourceHostsFile)) - assert.True(t, clients.exists(net.IP{2, 2, 2, 2}, ClientSourceHostsFile)) + assert.False(t, clients.exists(cliNoneIP, ClientSourceHostsFile)) + assert.True(t, clients.exists(cli1IP, ClientSourceHostsFile)) + assert.True(t, clients.exists(cli2IP, ClientSourceHostsFile)) }) t.Run("add_fail_name", func(t *testing.T) { @@ -103,23 +113,31 @@ func TestClients(t *testing.T) { }) t.Run("update_success", func(t *testing.T) { + var ( + cliOld = "1.1.1.1" + cliNew = "1.1.1.2" + + cliOldIP = netip.MustParseAddr(cliOld) + cliNewIP = netip.MustParseAddr(cliNew) + ) + err := clients.Update("client1", &Client{ - IDs: []string{"1.1.1.2"}, + IDs: []string{cliNew}, Name: "client1", }) require.NoError(t, err) - assert.False(t, clients.exists(net.IP{1, 1, 1, 1}, ClientSourceHostsFile)) - assert.True(t, clients.exists(net.IP{1, 1, 1, 2}, ClientSourceHostsFile)) + assert.False(t, clients.exists(cliOldIP, ClientSourceHostsFile)) + assert.True(t, clients.exists(cliNewIP, ClientSourceHostsFile)) err = clients.Update("client1", &Client{ - IDs: []string{"1.1.1.2"}, + IDs: []string{cliNew}, Name: "client1-renamed", UseOwnSettings: true, }) require.NoError(t, err) - c, ok := clients.Find("1.1.1.2") + c, ok := clients.Find(cliNew) require.True(t, ok) assert.Equal(t, "client1-renamed", c.Name) @@ -132,14 +150,14 @@ func TestClients(t *testing.T) { require.Len(t, c.IDs, 1) - assert.Equal(t, "1.1.1.2", c.IDs[0]) + assert.Equal(t, cliNew, c.IDs[0]) }) t.Run("del_success", func(t *testing.T) { ok := clients.Del("client1-renamed") require.True(t, ok) - assert.False(t, clients.exists(net.IP{1, 1, 1, 2}, ClientSourceHostsFile)) + assert.False(t, clients.exists(netip.MustParseAddr("1.1.1.2"), ClientSourceHostsFile)) }) t.Run("del_fail", func(t *testing.T) { @@ -148,45 +166,33 @@ func TestClients(t *testing.T) { }) t.Run("addhost_success", func(t *testing.T) { - ip := net.IP{1, 1, 1, 1} - - ok, err := clients.AddHost(ip, "host", ClientSourceARP) - require.NoError(t, err) - + ip := netip.MustParseAddr("1.1.1.1") + ok := clients.AddHost(ip, "host", ClientSourceARP) assert.True(t, ok) - ok, err = clients.AddHost(ip, "host2", ClientSourceARP) - require.NoError(t, err) - + ok = clients.AddHost(ip, "host2", ClientSourceARP) assert.True(t, ok) - ok, err = clients.AddHost(ip, "host3", ClientSourceHostsFile) - require.NoError(t, err) - + ok = clients.AddHost(ip, "host3", ClientSourceHostsFile) assert.True(t, ok) assert.True(t, clients.exists(ip, ClientSourceHostsFile)) }) t.Run("dhcp_replaces_arp", func(t *testing.T) { - ip := net.IP{1, 2, 3, 4} - - ok, err := clients.AddHost(ip, "from_arp", ClientSourceARP) - require.NoError(t, err) - + ip := netip.MustParseAddr("1.2.3.4") + ok := clients.AddHost(ip, "from_arp", ClientSourceARP) assert.True(t, ok) assert.True(t, clients.exists(ip, ClientSourceARP)) - ok, err = clients.AddHost(ip, "from_dhcp", ClientSourceDHCP) - require.NoError(t, err) - + ok = clients.AddHost(ip, "from_dhcp", ClientSourceDHCP) assert.True(t, ok) assert.True(t, clients.exists(ip, ClientSourceDHCP)) }) t.Run("addhost_fail", func(t *testing.T) { - ok, err := clients.AddHost(net.IP{1, 1, 1, 1}, "host1", ClientSourceRDNS) - require.NoError(t, err) + ip := netip.MustParseAddr("1.1.1.1") + ok := clients.AddHost(ip, "host1", ClientSourceRDNS) assert.False(t, ok) }) } @@ -203,7 +209,7 @@ func TestClientsWHOIS(t *testing.T) { t.Run("new_client", func(t *testing.T) { ip := netip.MustParseAddr("1.1.1.255") - clients.setWHOISInfo(ip.AsSlice(), whois) + clients.setWHOISInfo(ip, whois) rc := clients.ipToRC[ip] require.NotNil(t, rc) @@ -212,12 +218,10 @@ func TestClientsWHOIS(t *testing.T) { t.Run("existing_auto-client", func(t *testing.T) { ip := netip.MustParseAddr("1.1.1.1") - ok, err := clients.AddHost(ip.AsSlice(), "host", ClientSourceRDNS) - require.NoError(t, err) - + ok := clients.AddHost(ip, "host", ClientSourceRDNS) assert.True(t, ok) - clients.setWHOISInfo(ip.AsSlice(), whois) + clients.setWHOISInfo(ip, whois) rc := clients.ipToRC[ip] require.NotNil(t, rc) @@ -234,7 +238,7 @@ func TestClientsWHOIS(t *testing.T) { require.NoError(t, err) assert.True(t, ok) - clients.setWHOISInfo(ip.AsSlice(), whois) + clients.setWHOISInfo(ip, whois) rc := clients.ipToRC[ip] require.Nil(t, rc) @@ -249,7 +253,7 @@ func TestClientsAddExisting(t *testing.T) { clients.Init(nil, nil, nil, nil) t.Run("simple", func(t *testing.T) { - ip := net.IP{1, 1, 1, 1} + ip := netip.MustParseAddr("1.1.1.1") // Add a client. ok, err := clients.Add(&Client{ @@ -260,8 +264,7 @@ func TestClientsAddExisting(t *testing.T) { assert.True(t, ok) // Now add an auto-client with the same IP. - ok, err = clients.AddHost(ip, "test", ClientSourceRDNS) - require.NoError(t, err) + ok = clients.AddHost(ip, "test", ClientSourceRDNS) assert.True(t, ok) }) diff --git a/internal/home/clientshttp.go b/internal/home/clientshttp.go index 313fd998..d9bb0ea9 100644 --- a/internal/home/clientshttp.go +++ b/internal/home/clientshttp.go @@ -3,8 +3,8 @@ package home import ( "encoding/json" "fmt" - "net" "net/http" + "net/netip" "github.com/AdguardTeam/AdGuardHome/internal/aghhttp" ) @@ -47,8 +47,8 @@ type runtimeClientJSON struct { WHOISInfo *RuntimeClientWHOISInfo `json:"whois_info"` Name string `json:"name"` + IP netip.Addr `json:"ip"` Source clientSource `json:"source"` - IP net.IP `json:"ip"` } type clientListJSON struct { @@ -75,7 +75,7 @@ func (clients *clientsContainer) handleGetClients(w http.ResponseWriter, r *http Name: rc.Host, Source: rc.Source, - IP: ip.AsSlice(), + IP: ip, } data.RuntimeClients = append(data.RuntimeClients, cj) @@ -218,7 +218,7 @@ func (clients *clientsContainer) handleFindClient(w http.ResponseWriter, r *http break } - ip := net.ParseIP(idStr) + ip, _ := netip.ParseAddr(idStr) c, ok := clients.Find(idStr) var cj *clientJSON if !ok { @@ -240,7 +240,7 @@ func (clients *clientsContainer) handleFindClient(w http.ResponseWriter, r *http // findRuntime looks up the IP in runtime and temporary storages, like // /etc/hosts tables, DHCP leases, or blocklists. cj is guaranteed to be // non-nil. -func (clients *clientsContainer) findRuntime(ip net.IP, idStr string) (cj *clientJSON) { +func (clients *clientsContainer) findRuntime(ip netip.Addr, idStr string) (cj *clientJSON) { rc, ok := clients.findRuntimeClient(ip) if !ok { // It is still possible that the IP used to be in the runtime clients diff --git a/internal/home/control.go b/internal/home/control.go index 5e4e6df2..f9e7d4d2 100644 --- a/internal/home/control.go +++ b/internal/home/control.go @@ -71,9 +71,7 @@ func appendDNSAddrsWithIfaces(dst []string, src []netip.Addr) (res []string, err // on, including the addresses on all interfaces in cases of unspecified IPs. func collectDNSAddresses() (addrs []string, err error) { if hosts := config.DNS.BindHosts; len(hosts) == 0 { - addr := aghnet.IPv4Localhost() - - addrs = appendDNSAddrs(addrs, addr) + addrs = appendDNSAddrs(addrs, netutil.IPv4Localhost()) } else { addrs, err = appendDNSAddrsWithIfaces(addrs, hosts) if err != nil { diff --git a/internal/home/dns.go b/internal/home/dns.go index 7d40ed35..1980b252 100644 --- a/internal/home/dns.go +++ b/internal/home/dns.go @@ -8,6 +8,7 @@ import ( "os" "path/filepath" + "github.com/AdguardTeam/AdGuardHome/internal/aghalg" "github.com/AdguardTeam/AdGuardHome/internal/aghnet" "github.com/AdguardTeam/AdGuardHome/internal/dnsforward" "github.com/AdguardTeam/AdGuardHome/internal/filtering" @@ -150,8 +151,8 @@ func isRunning() bool { } func onDNSRequest(pctx *proxy.DNSContext) { - ip, _ := netutil.IPAndPortFromAddr(pctx.Addr) - if ip == nil { + ip := netutil.NetAddrToAddrPort(pctx.Addr).Addr() + if ip == (netip.Addr{}) { // This would be quite weird if we get here. return } @@ -160,7 +161,8 @@ func onDNSRequest(pctx *proxy.DNSContext) { if srcs.RDNS && !ip.IsLoopback() { Context.rdns.Begin(ip) } - if srcs.WHOIS && !netutil.IsSpecialPurpose(ip) { + + if srcs.WHOIS && !netutil.IsSpecialPurposeAddr(ip) { Context.whois.Begin(ip) } } @@ -193,11 +195,7 @@ func ipsToUDPAddrs(ips []netip.Addr, port int) (udpAddrs []*net.UDPAddr) { func generateServerConfig() (newConf dnsforward.ServerConfig, err error) { dnsConf := config.DNS - hosts := dnsConf.BindHosts - if len(hosts) == 0 { - hosts = []netip.Addr{aghnet.IPv4Localhost()} - } - + hosts := aghalg.CoalesceSlice(dnsConf.BindHosts, []netip.Addr{netutil.IPv4Localhost()}) newConf = dnsforward.ServerConfig{ UDPListenAddrs: ipsToUDPAddrs(hosts, dnsConf.Port), TCPListenAddrs: ipsToTCPAddrs(hosts, dnsConf.Port), @@ -400,15 +398,12 @@ func startDNSServer() error { const topClientsNumber = 100 // the number of clients to get for _, ip := range Context.stats.TopClientsIP(topClientsNumber) { - if ip == nil { - continue - } - srcs := config.Clients.Sources if srcs.RDNS && !ip.IsLoopback() { Context.rdns.Begin(ip) } - if srcs.WHOIS && !netutil.IsSpecialPurpose(ip) { + + if srcs.WHOIS && !netutil.IsSpecialPurposeAddr(ip) { Context.whois.Begin(ip) } } diff --git a/internal/home/home.go b/internal/home/home.go index 665c41ce..6ff698d3 100644 --- a/internal/home/home.go +++ b/internal/home/home.go @@ -542,6 +542,11 @@ func run(opts options, clientBuildFS fs.FS) { } } + // TODO(a.garipov): This could be made much earlier and could be done on + // the first run as well, but to achieve this we need to bypass requests + // over dnsforward resolver. + cmdlineUpdate(opts) + Context.web.Start() // wait indefinitely for other go-routines to complete their job @@ -576,7 +581,7 @@ func checkPermissions() { } // We should check if AdGuard Home is able to bind to port 53 - err := aghnet.CheckPort("tcp", netip.AddrPortFrom(aghnet.IPv4Localhost(), defaultPortDNS)) + err := aghnet.CheckPort("tcp", netip.AddrPortFrom(netutil.IPv4Localhost(), defaultPortDNS)) if err != nil { if errors.Is(err, os.ErrPermission) { log.Fatal(`Permission check failed. @@ -927,3 +932,37 @@ type jsonError struct { // Message is the error message, an opaque string. Message string `json:"message"` } + +// cmdlineUpdate updates current application and exits. +func cmdlineUpdate(opts options) { + if !opts.performUpdate { + return + } + + log.Info("starting update") + + if Context.firstRun { + log.Info("update not allowed on first run") + + os.Exit(0) + } + + _, err := Context.updater.VersionInfo(true) + if err != nil { + vcu := Context.updater.VersionCheckURL() + log.Error("getting version info from %s: %s", vcu, err) + + os.Exit(0) + } + + if Context.updater.NewVersion() == "" { + log.Info("no updates available") + + os.Exit(0) + } + + err = Context.updater.Update() + fatalOnError(err) + + os.Exit(0) +} diff --git a/internal/home/options.go b/internal/home/options.go index e14e26f6..befc78a2 100644 --- a/internal/home/options.go +++ b/internal/home/options.go @@ -47,6 +47,9 @@ type options struct { // disableUpdate, if set, makes AdGuard Home not check for updates. disableUpdate bool + // performUpdate, if set, updates AdGuard Home without GUI and exits. + performUpdate bool + // verbose shows if verbose logging is enabled. verbose bool @@ -221,6 +224,14 @@ var cmdLineOpts = []cmdLineOpt{{ description: "Don't check for updates.", longName: "no-check-update", shortName: "", +}, { + updateWithValue: nil, + updateNoValue: func(o options) (options, error) { o.performUpdate = true; return o, nil }, + effect: nil, + serialize: func(o options) (val string, ok bool) { return "", o.performUpdate }, + description: "Update application and exit.", + longName: "update", + shortName: "", }, { updateWithValue: nil, updateNoValue: nil, diff --git a/internal/home/options_test.go b/internal/home/options_test.go index 32b4243a..2b507535 100644 --- a/internal/home/options_test.go +++ b/internal/home/options_test.go @@ -103,6 +103,11 @@ func TestParseDisableUpdate(t *testing.T) { assert.True(t, testParseOK(t, "--no-check-update").disableUpdate, "--no-check-update is disable update") } +func TestParsePerformUpdate(t *testing.T) { + assert.False(t, testParseOK(t).performUpdate, "empty is not perform update") + assert.True(t, testParseOK(t, "--update").performUpdate, "--update is perform update") +} + // TODO(e.burkov): Remove after v0.108.0. func TestParseDisableMemoryOptimization(t *testing.T) { o, eff, err := parseCmdOpts("", []string{"--no-mem-optimization"}) @@ -169,6 +174,10 @@ func TestOptsToArgs(t *testing.T) { name: "disable_update", args: []string{"--no-check-update"}, opts: options{disableUpdate: true}, + }, { + name: "perform_update", + args: []string{"--update"}, + opts: options{performUpdate: true}, }, { name: "control_action", args: []string{"-s", "run"}, diff --git a/internal/home/rdns.go b/internal/home/rdns.go index e44000b3..c6ce0f59 100644 --- a/internal/home/rdns.go +++ b/internal/home/rdns.go @@ -2,7 +2,7 @@ package home import ( "encoding/binary" - "net" + "net/netip" "sync/atomic" "time" @@ -21,7 +21,7 @@ type RDNS struct { usePrivate uint32 // ipCh used to pass client's IP to rDNS workerLoop. - ipCh chan net.IP + ipCh chan netip.Addr // ipCache caches the IP addresses to be resolved by rDNS. The resolved // address stays here while it's inside clients. After leaving clients the @@ -50,7 +50,7 @@ func NewRDNS( EnableLRU: true, MaxCount: defaultRDNSCacheSize, }), - ipCh: make(chan net.IP, defaultRDNSIPChSize), + ipCh: make(chan netip.Addr, defaultRDNSIPChSize), } if usePrivate { rDNS.usePrivate = 1 @@ -80,9 +80,10 @@ func (r *RDNS) ensurePrivateCache() { // isCached returns true if ip is already cached and not expired yet. It also // caches it otherwise. -func (r *RDNS) isCached(ip net.IP) (ok bool) { +func (r *RDNS) isCached(ip netip.Addr) (ok bool) { + ipBytes := ip.AsSlice() now := uint64(time.Now().Unix()) - if expire := r.ipCache.Get(ip); len(expire) != 0 { + if expire := r.ipCache.Get(ipBytes); len(expire) != 0 { if binary.BigEndian.Uint64(expire) > now { return true } @@ -91,14 +92,14 @@ func (r *RDNS) isCached(ip net.IP) (ok bool) { // The cache entry either expired or doesn't exist. ttl := make([]byte, 8) binary.BigEndian.PutUint64(ttl, now+defaultRDNSCacheTTL) - r.ipCache.Set(ip, ttl) + r.ipCache.Set(ipBytes, ttl) return false } // Begin adds the ip to the resolving queue if it is not cached or already // resolved. -func (r *RDNS) Begin(ip net.IP) { +func (r *RDNS) Begin(ip netip.Addr) { r.ensurePrivateCache() if r.isCached(ip) || r.clients.exists(ip, ClientSourceRDNS) { @@ -107,9 +108,9 @@ func (r *RDNS) Begin(ip net.IP) { select { case r.ipCh <- ip: - log.Tracef("rdns: %q added to queue", ip) + log.Debug("rdns: %q added to queue", ip) default: - log.Tracef("rdns: queue is full") + log.Debug("rdns: queue is full") } } @@ -119,7 +120,7 @@ func (r *RDNS) workerLoop() { defer log.OnPanic("rdns") for ip := range r.ipCh { - host, err := r.exchanger.Exchange(ip) + host, err := r.exchanger.Exchange(ip.AsSlice()) if err != nil { log.Debug("rdns: resolving %q: %s", ip, err) @@ -128,8 +129,6 @@ func (r *RDNS) workerLoop() { continue } - // Don't handle any errors since AddHost doesn't return non-nil errors - // for now. - _, _ = r.clients.AddHost(ip, host, ClientSourceRDNS) + _ = r.clients.AddHost(ip, host, ClientSourceRDNS) } } diff --git a/internal/home/rdns_test.go b/internal/home/rdns_test.go index 9f90ce5a..8dc675b5 100644 --- a/internal/home/rdns_test.go +++ b/internal/home/rdns_test.go @@ -27,14 +27,14 @@ func TestRDNS_Begin(t *testing.T) { w := &bytes.Buffer{} aghtest.ReplaceLogWriter(t, w) - ip1234, ip1235 := net.IP{1, 2, 3, 4}, net.IP{1, 2, 3, 5} + ip1234, ip1235 := netip.MustParseAddr("1.2.3.4"), netip.MustParseAddr("1.2.3.5") testCases := []struct { cliIDIndex map[string]*Client - customChan chan net.IP + customChan chan netip.Addr name string wantLog string - req net.IP + ip netip.Addr wantCacheHit int wantCacheMiss int }{{ @@ -42,7 +42,7 @@ func TestRDNS_Begin(t *testing.T) { customChan: nil, name: "cached", wantLog: "", - req: ip1234, + ip: ip1234, wantCacheHit: 1, wantCacheMiss: 0, }, { @@ -50,7 +50,7 @@ func TestRDNS_Begin(t *testing.T) { customChan: nil, name: "not_cached", wantLog: "rdns: queue is full", - req: ip1235, + ip: ip1235, wantCacheHit: 0, wantCacheMiss: 1, }, { @@ -58,15 +58,15 @@ func TestRDNS_Begin(t *testing.T) { customChan: nil, name: "already_in_clients", wantLog: "", - req: ip1235, + ip: ip1235, wantCacheHit: 0, wantCacheMiss: 1, }, { cliIDIndex: map[string]*Client{}, - customChan: make(chan net.IP, 1), + customChan: make(chan netip.Addr, 1), name: "add_to_queue", wantLog: `rdns: "1.2.3.5" added to queue`, - req: ip1235, + ip: ip1235, wantCacheHit: 0, wantCacheMiss: 1, }} @@ -102,7 +102,7 @@ func TestRDNS_Begin(t *testing.T) { } t.Run(tc.name, func(t *testing.T) { - rdns.Begin(tc.req) + rdns.Begin(tc.ip) assert.Equal(t, tc.wantCacheHit, ipCache.Stats().Hit) assert.Equal(t, tc.wantCacheMiss, ipCache.Stats().Miss) assert.Contains(t, w.String(), tc.wantLog) @@ -179,8 +179,8 @@ func TestRDNS_WorkerLoop(t *testing.T) { w := &bytes.Buffer{} aghtest.ReplaceLogWriter(t, w) - localIP := net.IP{192, 168, 1, 1} - revIPv4, err := netutil.IPToReversedAddr(localIP) + localIP := netip.MustParseAddr("192.168.1.1") + revIPv4, err := netutil.IPToReversedAddr(localIP.AsSlice()) require.NoError(t, err) revIPv6, err := netutil.IPToReversedAddr(net.ParseIP("2a00:1450:400c:c06::93")) @@ -201,24 +201,24 @@ func TestRDNS_WorkerLoop(t *testing.T) { testCases := []struct { ups upstream.Upstream + cliIP netip.Addr wantLog string name string - cliIP net.IP }{{ ups: locUpstream, + cliIP: localIP, wantLog: "", name: "all_good", - cliIP: localIP, }, { ups: errUpstream, + cliIP: netip.MustParseAddr("192.168.1.2"), wantLog: `rdns: resolving "192.168.1.2": test upstream error`, name: "resolve_error", - cliIP: net.IP{192, 168, 1, 2}, }, { ups: locUpstream, + cliIP: netip.MustParseAddr("2a00:1450:400c:c06::93"), wantLog: "", name: "ipv6_good", - cliIP: net.ParseIP("2a00:1450:400c:c06::93"), }} for _, tc := range testCases { @@ -230,7 +230,7 @@ func TestRDNS_WorkerLoop(t *testing.T) { ipToRC: map[netip.Addr]*RuntimeClient{}, allTags: stringutil.NewSet(), } - ch := make(chan net.IP) + ch := make(chan netip.Addr) rdns := &RDNS{ exchanger: &rDNSExchanger{ ex: tc.ups, diff --git a/internal/home/tls.go b/internal/home/tls.go index 7fdd64d8..c9086629 100644 --- a/internal/home/tls.go +++ b/internal/home/tls.go @@ -513,6 +513,11 @@ func validateCertChain(certs []*x509.Certificate, srvName string) (err error) { return nil } +// errNoIPInCert is the error that is returned from [parseCertChain] if the leaf +// certificate doesn't contain IPs. +const errNoIPInCert errors.Error = `certificates has no IP addresses; ` + + `DNS-over-TLS won't be advertised via DDR` + // parseCertChain parses the certificate chain from raw data, and returns it. // If ok is true, the returned error, if any, is not critical. func parseCertChain(chain []byte) (parsedCerts []*x509.Certificate, ok bool, err error) { @@ -535,8 +540,7 @@ func parseCertChain(chain []byte) (parsedCerts []*x509.Certificate, ok bool, err log.Info("tls: number of certs: %d", len(parsedCerts)) if !aghtls.CertificateHasIP(parsedCerts[0]) { - err = errors.Error(`certificate has no IP addresses` + - `, this may cause issues with DNS-over-TLS clients`) + err = errNoIPInCert } return parsedCerts, true, err diff --git a/internal/home/whois.go b/internal/home/whois.go index c9834708..9ffee9e0 100644 --- a/internal/home/whois.go +++ b/internal/home/whois.go @@ -6,6 +6,7 @@ import ( "fmt" "io" "net" + "net/netip" "strings" "time" @@ -26,7 +27,7 @@ const ( // WHOIS - module context type WHOIS struct { clients *clientsContainer - ipChan chan net.IP + ipChan chan netip.Addr // dialContext specifies the dial function for creating unencrypted TCP // connections. @@ -51,7 +52,7 @@ func initWHOIS(clients *clientsContainer) *WHOIS { MaxCount: 10000, }), dialContext: customDialContext, - ipChan: make(chan net.IP, 255), + ipChan: make(chan netip.Addr, 255), } go w.workerLoop() @@ -192,7 +193,7 @@ func (w *WHOIS) queryAll(ctx context.Context, target string) (string, error) { } // Request WHOIS information -func (w *WHOIS) process(ctx context.Context, ip net.IP) (wi *RuntimeClientWHOISInfo) { +func (w *WHOIS) process(ctx context.Context, ip netip.Addr) (wi *RuntimeClientWHOISInfo) { resp, err := w.queryAll(ctx, ip.String()) if err != nil { log.Debug("whois: error: %s IP:%s", err, ip) @@ -220,24 +221,25 @@ func (w *WHOIS) process(ctx context.Context, ip net.IP) (wi *RuntimeClientWHOISI } // Begin - begin requesting WHOIS info -func (w *WHOIS) Begin(ip net.IP) { +func (w *WHOIS) Begin(ip netip.Addr) { + ipBytes := ip.AsSlice() now := uint64(time.Now().Unix()) - expire := w.ipAddrs.Get([]byte(ip)) + expire := w.ipAddrs.Get(ipBytes) if len(expire) != 0 { exp := binary.BigEndian.Uint64(expire) if exp > now { return } - // TTL expired } + expire = make([]byte, 8) binary.BigEndian.PutUint64(expire, now+whoisTTL) - _ = w.ipAddrs.Set([]byte(ip), expire) + _ = w.ipAddrs.Set(ipBytes, expire) log.Debug("whois: adding %s", ip) + select { case w.ipChan <- ip: - // default: log.Debug("whois: queue is full") } diff --git a/internal/stats/stats.go b/internal/stats/stats.go index e9397907..0ac8d9be 100644 --- a/internal/stats/stats.go +++ b/internal/stats/stats.go @@ -6,6 +6,7 @@ import ( "fmt" "io" "net" + "net/netip" "os" "sync" "sync/atomic" @@ -64,7 +65,7 @@ type Interface interface { // GetTopClientIP returns at most limit IP addresses corresponding to the // clients with the most number of requests. - TopClientsIP(limit uint) []net.IP + TopClientsIP(limit uint) []netip.Addr // WriteDiskConfig puts the Interface's configuration to the dc. WriteDiskConfig(dc *DiskConfig) @@ -107,8 +108,6 @@ type StatsCtx struct { filename string } -var _ Interface = &StatsCtx{} - // New creates s from conf and properly initializes it. Don't use s before // calling it's Start method. func New(conf Config) (s *StatsCtx, err error) { @@ -178,6 +177,9 @@ func withRecovered(orig *error) { *orig = errors.WithDeferred(*orig, err) } +// type check +var _ Interface = (*StatsCtx)(nil) + // Start implements the Interface interface for *StatsCtx. func (s *StatsCtx) Start() { s.initWeb() @@ -250,8 +252,8 @@ func (s *StatsCtx) WriteDiskConfig(dc *DiskConfig) { dc.Interval = atomic.LoadUint32(&s.limitHours) / 24 } -// TopClientsIP implements the Interface interface for *StatsCtx. -func (s *StatsCtx) TopClientsIP(maxCount uint) (ips []net.IP) { +// TopClientsIP implements the [Interface] interface for *StatsCtx. +func (s *StatsCtx) TopClientsIP(maxCount uint) (ips []netip.Addr) { limit := atomic.LoadUint32(&s.limitHours) if limit == 0 { return nil @@ -271,10 +273,10 @@ func (s *StatsCtx) TopClientsIP(maxCount uint) (ips []net.IP) { } a := convertMapToSlice(m, int(maxCount)) - ips = []net.IP{} + ips = []netip.Addr{} for _, it := range a { - ip := net.ParseIP(it.Name) - if ip != nil { + ip, err := netip.ParseAddr(it.Name) + if err == nil { ips = append(ips, ip) } } diff --git a/internal/stats/stats_test.go b/internal/stats/stats_test.go index bb2cc0d8..cd88cbaf 100644 --- a/internal/stats/stats_test.go +++ b/internal/stats/stats_test.go @@ -11,6 +11,7 @@ import ( "testing" "github.com/AdguardTeam/AdGuardHome/internal/stats" + "github.com/AdguardTeam/golibs/netutil" "github.com/AdguardTeam/golibs/testutil" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -45,7 +46,7 @@ func assertSuccessAndUnmarshal(t *testing.T, to any, handler http.Handler, req * } func TestStats(t *testing.T) { - cliIP := net.IP{127, 0, 0, 1} + cliIP := netutil.IPv4Localhost() cliIPStr := cliIP.String() handlers := map[string]http.Handler{} @@ -123,7 +124,7 @@ func TestStats(t *testing.T) { topClients := s.TopClientsIP(2) require.NotEmpty(t, topClients) - assert.True(t, cliIP.Equal(topClients[0])) + assert.Equal(t, cliIP, topClients[0]) }) t.Run("reset", func(t *testing.T) {