Compare commits
16 Commits
v0.107.0-b
...
fix-stats-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
02834e6b7b | ||
|
|
b45162a0f2 | ||
|
|
ce0df255be | ||
|
|
b735c8167c | ||
|
|
2ac2ad0609 | ||
|
|
37a3666a2a | ||
|
|
5aed8db5cf | ||
|
|
e1e064db59 | ||
|
|
b1242ee93f | ||
|
|
5f3131c799 | ||
|
|
1714a986e3 | ||
|
|
6ac28ee8ee | ||
|
|
bf1263628a | ||
|
|
176a344aee | ||
|
|
48b0cefb29 | ||
|
|
857f876486 |
@@ -19,7 +19,7 @@
|
||||
"it": "Italiano",
|
||||
"ja": "日本語",
|
||||
"ko": "한국어",
|
||||
"nl": "Dutch",
|
||||
"nl": "Nederlands",
|
||||
"no": "Norsk",
|
||||
"pl": "Polski",
|
||||
"pt-br": "Português (BR)",
|
||||
|
||||
21
CHANGELOG.md
21
CHANGELOG.md
@@ -10,12 +10,13 @@ and this project adheres to
|
||||
## [Unreleased]
|
||||
|
||||
<!--
|
||||
## [v0.107.0] - 2021-09-14 (APPROX.)
|
||||
## [v0.107.0] - 2021-09-28 (APPROX.)
|
||||
-->
|
||||
|
||||
### Added
|
||||
|
||||
- Bootstrap DNS server IPs to the `mobileconfig` API responses ([#3568]).
|
||||
- DNS server IP addresses to the `mobileconfig` API responses ([#3568],
|
||||
[#3607]).
|
||||
- Setting the timeout for IP address pinging in the "Fastest IP address" mode
|
||||
through the new `fastest_timeout` field in the configuration file ([#1992]).
|
||||
- Static IP address detection on FreeBSD ([#3289]).
|
||||
@@ -49,10 +50,10 @@ and this project adheres to
|
||||
|
||||
- The `systemd` service script will now create the `/var/log` directory when it
|
||||
doesn't exist ([#3579]).
|
||||
- Items in allowed clients, disallowed clients, and blocked hosts lists must
|
||||
be unique ([#3419]).
|
||||
- Items in allowed clients, disallowed clients, and blocked hosts lists are now
|
||||
required to be unique ([#3419]).
|
||||
- The TLS private key previously saved as a string isn't shown in API responses
|
||||
any more ([#1898]).
|
||||
anymore ([#1898]).
|
||||
- Better OpenWrt detection ([#3435]).
|
||||
- DNS-over-HTTPS queries that come from HTTP proxies in the `trusted_proxies`
|
||||
list now use the real IP address of the client instead of the address of the
|
||||
@@ -115,7 +116,10 @@ In this release, the schema version has changed from 10 to 12.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Panic when upstream server responds with empty question section ([#3551]).
|
||||
- Adding an IP into only one of the matching ipsets on Linux ([#3638]).
|
||||
- Removal of temporary filter files ([#3567]).
|
||||
- Panic when an upstream server responds with an empty question section
|
||||
([#3551]).
|
||||
- 9GAG blocking ([#3564]).
|
||||
- DHCP now follows RFCs more closely when it comes to response sending and
|
||||
option selection ([#3443], [#3538]).
|
||||
@@ -194,8 +198,11 @@ In this release, the schema version has changed from 10 to 12.
|
||||
[#3538]: https://github.com/AdguardTeam/AdGuardHome/issues/3538
|
||||
[#3551]: https://github.com/AdguardTeam/AdGuardHome/issues/3551
|
||||
[#3564]: https://github.com/AdguardTeam/AdGuardHome/issues/3564
|
||||
[#3567]: https://github.com/AdguardTeam/AdGuardHome/issues/3567
|
||||
[#3568]: https://github.com/AdguardTeam/AdGuardHome/issues/3568
|
||||
[#3579]: https://github.com/AdguardTeam/AdGuardHome/issues/3579
|
||||
[#3607]: https://github.com/AdguardTeam/AdGuardHome/issues/3607
|
||||
[#3638]: https://github.com/AdguardTeam/AdGuardHome/issues/3638
|
||||
|
||||
|
||||
|
||||
@@ -399,7 +406,7 @@ In this release, the schema version has changed from 10 to 12.
|
||||
- The field `"range_start"` in the `GET /control/dhcp/status` HTTP API response
|
||||
is now correctly named again ([#2678]).
|
||||
- DHCPv6 server's `ra_slaac_only` and `ra_allow_slaac` settings aren't reset to
|
||||
`false` on update any more ([#2653]).
|
||||
`false` on update anymore ([#2653]).
|
||||
- The `Vary` header is now added along with `Access-Control-Allow-Origin` to
|
||||
prevent cache-related and other issues in browsers ([#2658]).
|
||||
- The request body size limit is now set for HTTPS requests as well.
|
||||
|
||||
@@ -218,6 +218,7 @@
|
||||
"reset_settings": "Изтрий всички настройки",
|
||||
"update_announcement": "Има нова AdGuard Home {{version}}! <0>Цъкни тук</0> за повече информация.",
|
||||
"disable_ipv6": "Изключете IPv6 протокола",
|
||||
"check_updates_now": "Провери за актуализации",
|
||||
"show_blocked_responses": "Блокирано",
|
||||
"port_53_faq_link": "Порт 53 често е зает от \"DNSStubListener\" или \"systemd-resolved\" услуги. Моля, прочетете <0>тази инструкция</0> как да решите това."
|
||||
}
|
||||
|
||||
@@ -306,7 +306,7 @@
|
||||
"install_settings_dns_desc": "Budete muset nakonfigurovat Vaše zařízení nebo router, aby používali DNS server na následujících adresách:",
|
||||
"install_settings_all_interfaces": "Všechna rozhraní",
|
||||
"install_auth_title": "Ověřování",
|
||||
"install_auth_desc": "Doporučujeme Vám nakonfigurovat v administrátorském webovém rozhraní AdGuard Home ověření Vaší identity heslem. I když je přístupné pouze ve Vaší lokální síti, je stále důležité chránit jej před neomezeným přístupem.",
|
||||
"install_auth_desc": "V administrátorském webovém rozhraní AdGuard Home musí být nastaveno ověřovací heslo. I když je AdGuard Home přístupný pouze v místní síti, je důležité jej chránit před neomezeným přístupem.",
|
||||
"install_auth_username": "Uživatelské jméno",
|
||||
"install_auth_password": "Heslo",
|
||||
"install_auth_confirm": "Potvrďte heslo",
|
||||
|
||||
@@ -306,7 +306,7 @@
|
||||
"install_settings_dns_desc": "Du skal opsætte dine enheder eller router til at bruge DNS-serveren på flg. adresser:",
|
||||
"install_settings_all_interfaces": "Alle grænseflader",
|
||||
"install_auth_title": "Godkendelse",
|
||||
"install_auth_desc": "Det anbefales stærkt at opsætte adgangskodegodkendelse på din AdGuard Home admin webgrænseflade. Selvom den kun er tilgængelig på dit lokalnetværk, er det stadig vigtigt at få den beskyttet mod ubegrænset adgang.",
|
||||
"install_auth_desc": "Adgangskodegodkendelse på din AdGuard Home admin-webflade skal opsættes. Selv hvis AdGuard Home kun er tilgængelig på lokalnetværket, er beskyttelse mod uautoriseret og ubegrænset adgang stadig vigtig.",
|
||||
"install_auth_username": "Brugernavn",
|
||||
"install_auth_password": "Adgangskode",
|
||||
"install_auth_confirm": "Bekræft adgangskode",
|
||||
|
||||
@@ -306,7 +306,7 @@
|
||||
"install_settings_dns_desc": "Sie müssen Ihre Geräte oder Ihren Router so konfigurieren, dass er den DNS-Server unter den folgenden Adressen verwendet:",
|
||||
"install_settings_all_interfaces": "Alle Schnittstellen",
|
||||
"install_auth_title": "Authentifizierung",
|
||||
"install_auth_desc": "Es wird dringend empfohlen, die Passwortauthentifizierung für Ihre AdGuard Home Administrator-Weboberfläche zu konfigurieren. Auch wenn es nur in Ihrem lokalen Netzwerk zugänglich ist, ist es dennoch wichtig, es vor unbefugtem Zugriff zu schützen.",
|
||||
"install_auth_desc": "Die Passwortauthentifizierung für Ihre AdGuard Home Administrator-Weboberfläche muss konfiguriert sein. Auch wenn AdGuard Home nur in Ihrem lokalen Netzwerk zugänglich ist, ist es dennoch wichtig, es vor unbefugtem Zugriff zu schützen.",
|
||||
"install_auth_username": "Benutzername",
|
||||
"install_auth_password": "Passwort",
|
||||
"install_auth_confirm": "Passwort bestätigen",
|
||||
|
||||
@@ -306,7 +306,7 @@
|
||||
"install_settings_dns_desc": "You will need to configure your devices or router to use the DNS server on the following addresses:",
|
||||
"install_settings_all_interfaces": "All interfaces",
|
||||
"install_auth_title": "Authentication",
|
||||
"install_auth_desc": "It is highly recommended to configure password authentication to your AdGuard Home admin web interface. Even if it is accessible only in your local network, it is still important to protect it from unrestricted access.",
|
||||
"install_auth_desc": "Password authentication to your AdGuard Home admin web interface must be configured. Even if AdGuard Home is accessible only in your local network, it is still important to protect it from unrestricted access.",
|
||||
"install_auth_username": "Username",
|
||||
"install_auth_password": "Password",
|
||||
"install_auth_confirm": "Confirm password",
|
||||
|
||||
@@ -306,7 +306,7 @@
|
||||
"install_settings_dns_desc": "Deberás configurar tus dispositivos o router para usar el servidor DNS en las siguientes direcciones:",
|
||||
"install_settings_all_interfaces": "Todas las interfaces",
|
||||
"install_auth_title": "Autenticación",
|
||||
"install_auth_desc": "Se recomienda encarecidamente configurar la autenticación por contraseña para la interfaz web de administración de AdGuard Home. Incluso si solo es accesible en tu red local, es importante que estés protegido contra el acceso no autorizado.",
|
||||
"install_auth_desc": "Debe configurarse la autenticación por contraseña para la interfaz web de administración de AdGuard Home. Incluso si AdGuard Home es accesible solo en tu red local, es importante protegerlo del acceso no autorizado.",
|
||||
"install_auth_username": "Usuario",
|
||||
"install_auth_password": "Contraseña",
|
||||
"install_auth_confirm": "Confirmar contraseña",
|
||||
|
||||
@@ -306,7 +306,7 @@
|
||||
"install_settings_dns_desc": "Vous devrez configurer vos appareils et votre routeur pour utiliser le serveur DNS sur les adresses suivantes :",
|
||||
"install_settings_all_interfaces": "Toutes les interfaces",
|
||||
"install_auth_title": "Authentification",
|
||||
"install_auth_desc": "Il est fortement recommandé de configurer un mot de passe pour accéder à l'interface web administrateur AdGuard Home. Même si elle est disponible que dans votre réseau local, cela reste important de se protéger contre des accès non désirés.",
|
||||
"install_auth_desc": "C'est nécessaire de configurer l'authentification par aide de mot de passe pour accéder à l'interface web administrateur de votre AdGuard Home. Même si AdGuard Home n'est accessible que dans votre réseau local, c'est important d'y restreindre accès aux tiers.",
|
||||
"install_auth_username": "Nom d'utilisateur",
|
||||
"install_auth_password": "Mot de passe",
|
||||
"install_auth_confirm": "Confirmer le mot de passe",
|
||||
@@ -613,7 +613,7 @@
|
||||
"click_to_view_queries": "Cliquez pour voir les requêtes",
|
||||
"port_53_faq_link": "Le port 53 est souvent occupé par les services « DNSStubListener » ou « systemd-resolved ». Veuillez lire <0>cette instruction</0> pour savoir comment résoudre ce problème.",
|
||||
"adg_will_drop_dns_queries": "AdGuard Home ignorera toutes les requêtes DNS de ce client.",
|
||||
"filter_allowlist": "AVERTISSEMENT : Cette action exclura également la règle « {{disallowed_rule}} » de la liste des clients autorisés.",
|
||||
"filter_allowlist": "ATTENTION : Cette action exclura également la règle « {{disallowed_rule}} » de la liste des clients autorisés.",
|
||||
"last_rule_in_allowlist": "Impossible d’interdire ce client, car l’exclusion de la règle « {{disallowed_rule}} » DÉSACTIVERA la liste des « clients autorisés ».",
|
||||
"experimental": "Expérimental",
|
||||
"use_saved_key": "Utiliser la clef précédemment enregistrée"
|
||||
|
||||
@@ -306,7 +306,7 @@
|
||||
"install_settings_dns_desc": "Sarà necessario configurare i dispositivi o il router per utilizzare il server DNS nei seguenti indirizzi:",
|
||||
"install_settings_all_interfaces": "Tutte le interfacce",
|
||||
"install_auth_title": "Autenticazione",
|
||||
"install_auth_desc": "Si consiglia vivamente di configurare l'autenticazione della password per l'interfaccia web di amministrazione di AdGuard Home. Anche se è accessibile solo nella rete locale, è comunque importante proteggerlo da accessi illimitati.",
|
||||
"install_auth_desc": "L\\'autenticazione con password sulla tua interfaccia web da amministratore di AdGuard Home dev\\'esser configurata. Anche se AdGuard Home è accessibile solo dalla tua rete locale, è comunque importante proteggerlo da accessi non limitati.",
|
||||
"install_auth_username": "Nome utente",
|
||||
"install_auth_password": "Password",
|
||||
"install_auth_confirm": "Conferma password",
|
||||
|
||||
@@ -306,7 +306,7 @@
|
||||
"install_settings_dns_desc": "次のアドレスでDNSサーバを使用するようにあなたのデバイスまたはルータを設定する必要があります:",
|
||||
"install_settings_all_interfaces": "すべてのインターフェイス",
|
||||
"install_auth_title": "認証",
|
||||
"install_auth_desc": "AdGuard Homeの管理ウェブインターフェースにパスワード認証を設定することを強くお勧めします。ローカルネットワークでのみアクセス可能であっても、制限のないアクセスから保護することは重要です。",
|
||||
"install_auth_desc": "AdGuard Homeの管理ウェブインターフェースにパスワード認証を設定する必要があります。AdGuard Homeがローカルネットワークでのみアクセス可能であっても、制限のないアクセスから保護することは重要です。",
|
||||
"install_auth_username": "ユーザ名",
|
||||
"install_auth_password": "パスワード",
|
||||
"install_auth_confirm": "パスワード(確認用)",
|
||||
@@ -503,6 +503,7 @@
|
||||
"statistics_clear_confirm": "統計を消去してもよろしいですか?",
|
||||
"statistics_retention_confirm": "統計の保持を変更してもよろしいですか? 期間を短くすると、一部のデータが失われます",
|
||||
"statistics_cleared": "統計の消去に成功しました",
|
||||
"statistics_enable": "統計を有効にする",
|
||||
"interval_hours": "{{count}}時間",
|
||||
"interval_hours_plural": "{{count}}時間",
|
||||
"filters_configuration": "フィルタ設定",
|
||||
@@ -612,7 +613,8 @@
|
||||
"click_to_view_queries": "クエリを表示するにはクリックしてください",
|
||||
"port_53_faq_link": "多くの場合、ポート53は \"DNSStubListener\" または \"systemd-resolved\" サービスによって利用されています。これを解決する方法については、<0>この手順</0>をお読みください。",
|
||||
"adg_will_drop_dns_queries": "AdGuard Homeは、このクライアントからすべてのDNSクエリを落とします。",
|
||||
"client_not_in_allowed_clients": "「許可されたクライアント」リストにないため、このクライアントは許可されていません。",
|
||||
"filter_allowlist": "【注意】このアクションは、許可されたクライアントのリストから「{{disallowed_rule}}」というルールも除外します。",
|
||||
"last_rule_in_allowlist": "ルール「{{disallowed_rule}}」を除外すると「許可されたクライアント」リストが無効になるため、このクライアントを拒否することはできません。",
|
||||
"experimental": "実験用",
|
||||
"use_saved_key": "以前に保存したキーを使用する"
|
||||
}
|
||||
|
||||
@@ -306,7 +306,7 @@
|
||||
"install_settings_dns_desc": "Je moet jouw apparaten of router configureren om de DNS-server te gebruiken op de volgende adressen:",
|
||||
"install_settings_all_interfaces": "Alle interfaces",
|
||||
"install_auth_title": "Authenticatie",
|
||||
"install_auth_desc": "Het wordt ten zeerste aanbevolen om wachtwoordverificatie te configureren voor de AdGuard Home admin webinterface. Zelfs als het alleen toegankelijk is in uw lokale netwerk, is het nog steeds belangrijk om het te beschermen tegen onbeperkte toegang.",
|
||||
"install_auth_desc": "Wachtwoordverificatie voor je AdGuard Home-beheerderswebinterface moet worden geconfigureerd. Zelfs als AdGuard Home alleen toegankelijk is in je lokale netwerk, is het nog steeds belangrijk om het te beschermen tegen onbeperkte toegang.",
|
||||
"install_auth_username": "Gebruikersnaam",
|
||||
"install_auth_password": "Wachtwoord",
|
||||
"install_auth_confirm": "Bevestig wachtwoord",
|
||||
|
||||
@@ -306,7 +306,7 @@
|
||||
"install_settings_dns_desc": "Konieczne będzie skonfigurowanie urządzenia lub routera do korzystania z serwera DNS pod następującymi adresami:",
|
||||
"install_settings_all_interfaces": "Wszystkie interfejsy",
|
||||
"install_auth_title": "Uwierzytelnianie",
|
||||
"install_auth_desc": "Zalecamy skonfigurowanie strony AdGuard Home Admin, aby zweryfikować swoją tożsamość za pomocą hasła. Chociaż jest dostępny tylko w sieci lokalnej, nadal ważne jest, aby chronić go przed nieograniczonym dostępem.",
|
||||
"install_auth_desc": "Należy skonfigurować uwierzytelnianie hasłem do interfejsu internetowego administratora AdGuard Home. Nawet jeśli AdGuard Home jest dostępny tylko w sieci lokalnej, nadal ważne jest, aby chronić go przed nieograniczonym dostępem.",
|
||||
"install_auth_username": "Nazwa użytkownika",
|
||||
"install_auth_password": "Hasło",
|
||||
"install_auth_confirm": "Potwierdź hasło",
|
||||
|
||||
@@ -306,7 +306,7 @@
|
||||
"install_settings_dns_desc": "Você precisa configurar seu dispositivo ou roteador para usar o servidor DNS nos seguintes endereços:",
|
||||
"install_settings_all_interfaces": "Todas interfaces",
|
||||
"install_auth_title": "Autenticação",
|
||||
"install_auth_desc": "É altamente recomendável configurar a autenticação por senha na interface web de administração do AdGuard Home. Mesmo que ela seja acessível somente em sua rede local, ainda assim é importante protegê-la contra acesso irrestrito.",
|
||||
"install_auth_desc": "A autenticação de senha para a interface da web de administrador do AdGuard Home deve ser configurada. Mesmo que o AdGuard Home esteja acessível apenas em sua rede local, ainda é importante protegê-la de acesso irrestrito.",
|
||||
"install_auth_username": "Nome de usuário",
|
||||
"install_auth_password": "Senha",
|
||||
"install_auth_confirm": "Confirmar senha",
|
||||
|
||||
@@ -99,10 +99,10 @@
|
||||
"homepage": "Página inicial",
|
||||
"report_an_issue": "Comunicar um problema",
|
||||
"privacy_policy": "Política de privacidade",
|
||||
"enable_protection": "Ativar protecção",
|
||||
"enabled_protection": "Ativar protecção",
|
||||
"disable_protection": "Desativar protecção",
|
||||
"disabled_protection": "Desativar protecção",
|
||||
"enable_protection": "Ativar proteção",
|
||||
"enabled_protection": "Ativar proteção",
|
||||
"disable_protection": "Desativar proteção",
|
||||
"disabled_protection": "Desativar proteção",
|
||||
"refresh_statics": "Repor estatísticas",
|
||||
"dns_query": "Consultas de DNS",
|
||||
"blocked_by": "<0>Bloqueado por filtros</0>",
|
||||
@@ -306,7 +306,7 @@
|
||||
"install_settings_dns_desc": "Precisa de configurar o seu dispositivo ou router para usar o servidor DNS nos seguintes endereços:",
|
||||
"install_settings_all_interfaces": "Todas as interfaces",
|
||||
"install_auth_title": "Autenticação",
|
||||
"install_auth_desc": "É altamente recomendável configurar a autenticação por palavra-passe para a sua interface web de administrador do AdGuard Home. Mesmo que seja acessível apenas na sua rede local, ainda assim é importante protegê-lo contra o acesso irrestrito.",
|
||||
"install_auth_desc": "A autenticação de palavra-passe para a interface da web de administrador do AdGuard Home deve ser configurada. Mesmo que o AdGuard Home esteja acessível apenas em sua rede local, ainda é importante protegê-la de acesso irrestrito.",
|
||||
"install_auth_username": "Nome do utilizador",
|
||||
"install_auth_password": "Palavra-passe",
|
||||
"install_auth_confirm": "Confirmar palavra-passe",
|
||||
|
||||
@@ -306,7 +306,7 @@
|
||||
"install_settings_dns_desc": "Вам будет нужно настроить свои устройства или роутер на использование DNS-сервера на одном из следующих адресов:",
|
||||
"install_settings_all_interfaces": "Все интерфейсы",
|
||||
"install_auth_title": "Авторизация",
|
||||
"install_auth_desc": "Настоятельно рекомендуется настроить аутентификацию паролем для веб-интерфейса AdGuard Home. Даже если он доступен только в вашей локальной сети, важно защитить его от неограниченного доступа.",
|
||||
"install_auth_desc": "Должна быть настроена аутентификация паролем для веб-интерфейса AdGuard Home. Даже если он доступен только в вашей локальной сети, важно защитить его от неограниченного доступа.",
|
||||
"install_auth_username": "Имя пользователя",
|
||||
"install_auth_password": "Пароль",
|
||||
"install_auth_confirm": "Подтвердить пароль",
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
"dhcp_enable": "ග.ධා.වි.කෙ. සේවාදායකය සබල කරන්න",
|
||||
"dhcp_disable": "ග.ධා.වි.කෙ. සේවාදායකය අබල කරන්න",
|
||||
"dhcp_config_saved": "ග.ධා.වි.කෙ. වින්යාසය සාර්ථකව සුරකින ලදි",
|
||||
"dhcp_ipv4_settings": "ග.ධා.වි.කෙ. අයිපීවී 4 සැකසුම්",
|
||||
"dhcp_ipv6_settings": "ග.ධා.වි.කෙ. අයිපීවී 6 සැකසුම්",
|
||||
"dhcp_ipv4_settings": "ග.ධා.වි.කෙ. අ.ජා.කෙ. 4 සැකසුම්",
|
||||
"dhcp_ipv6_settings": "ග.ධා.වි.කෙ. අ.ජා.කෙ. 6 සැකසුම්",
|
||||
"form_error_required": "අවශ්ය ක්ෂේත්රයකි",
|
||||
"form_error_ip4_format": "වලංගු නොවන IPv4 ආකෘතියකි",
|
||||
"form_error_ip6_format": "වලංගු නොවන IPv6 ආකෘතියකි",
|
||||
@@ -221,8 +221,8 @@
|
||||
"refused": "REFUSED",
|
||||
"null_ip": "අභිශූන්යය අ.ජා. කෙ.",
|
||||
"custom_ip": "අභිරුචි අ.ජා. කෙ.",
|
||||
"blocking_ipv4": "අයි.පී.වී.4 අවහිර කිරීම\n",
|
||||
"blocking_ipv6": "අයි.පී.වී.6 අවහිර කිරීම",
|
||||
"blocking_ipv4": "අ.ජා.කෙ.4 අවහිර කිරීම",
|
||||
"blocking_ipv6": "අ.ජා.කෙ.6 අවහිර කිරීම",
|
||||
"client_id": "අනුග්රාහකයේ හැඳුනුම",
|
||||
"client_id_placeholder": "අනුග්රාහකයේ හැඳුනුම යොදන්න",
|
||||
"download_mobileconfig": "වින්යාසගත ගොනුව බාගන්න",
|
||||
@@ -230,8 +230,8 @@
|
||||
"form_enter_rate_limit": "අනුපාත සීමාව ඇතුල් කරන්න",
|
||||
"rate_limit": "අනුපාත සීමාව",
|
||||
"rate_limit_desc": "එක් අනුග්රාහකයකට ඉඩ දී ඇති තත්පරයට ඉල්ලීම් ගණන. එය 0 ලෙස සැකසීම යනුවෙන් අදහස් කරන්නේ සීමාවක් නැති බවයි.",
|
||||
"blocking_ipv4_desc": "අවහිර කළ A ඉල්ලීමක් සඳහා ආපසු එවිය යුතු අ.ජා. කෙ. (IP) ලිපිනය",
|
||||
"blocking_ipv6_desc": "අවහිර කළ AAAA ඉල්ලීමක් සඳහා ආපසු එවිය යුතු අ.ජා. කෙ. (IP) ලිපිනය",
|
||||
"blocking_ipv4_desc": "අවහිර කළ A ඉල්ලීමක් සඳහා ආපසු එවිය යුතු අ.ජා.කෙ. (IP) ලිපිනය",
|
||||
"blocking_ipv6_desc": "අවහිර කළ AAAA ඉල්ලීමක් සඳහා ආපසු එවිය යුතු අ.ජා.කෙ. (IP) ලිපිනය",
|
||||
"blocking_mode_default": "පොදු: දැන්වීම් අවහිර කරන ආකාරයේ නීතියක් මගින් අවහිර කළ විට REFUSED සමඟ ප්රතිචාර දක්වයි; /etc/host-style ආකාරයේ නීතියක් මගින් අවහිර කළ විට නීතියේ දක්වා ඇති අ.ජා. කෙ. ලිපිනය සමඟ ප්රතිචාර දක්වයි",
|
||||
"blocking_mode_refused": "REFUSED: REFUSED කේතය සමඟ ප්රතිචාර දක්වයි",
|
||||
"blocking_mode_nxdomain": "නොපවතින වසම (NXDOMAIN): NXDOMAIN කේතය සමඟ ප්රතිචාර දක්වයි",
|
||||
@@ -465,7 +465,7 @@
|
||||
"rewrite_ip_address": "අ.ජා. කෙ. ලිපිනය: මෙම අ.ජා. කෙටුම්පත A හෝ AAAA ප්රතිචාරයකට භාවිතා කරන්න",
|
||||
"rewrite_domain_name": "වසම් නාමය: අන්. නා. (CNAME) වාර්තාවක් එක් කරන්න",
|
||||
"disable_ipv6": "IPv6 ලිපින විසඳීම අබල කරන්න",
|
||||
"disable_ipv6_desc": "අ.ජා. කෙ. අනු. 6 ලිපින (AAAA වර්ගය) සඳහා වන සියලුම ව.නා.ප. විමසුම් අතහැර දමනු ලැබේ.",
|
||||
"disable_ipv6_desc": "අ.ජා.කෙ. අනු. 6 ලිපින (AAAA වර්ගය) සඳහා වන සියලුම ව.නා.ප. විමසුම් අතහැර දමනු ලැබේ.",
|
||||
"fastest_addr": "වේගවත්ම අන්තර්ජාල කෙටුම්පත් (IP) ලිපිනය",
|
||||
"fastest_addr_desc": "සියලුම ව.නා.ප. සේවාදායකයන්ගෙන් විමසා සියලු ප්රතිචාර අතරින් වේගවත්ම අ.ජා. කෙ. ලිපිනය ලබා දෙයි. සියලුම ව.නා.ප. සේවාදායකයන්ගේ ප්රතිචාර සඳහා ඇඩ්ගාර්ඩ් හෝම් රැඳී සිටිය යුතු බැවින් මෙය ව.නා.ප. විමසුම් මන්දගාමී කරන නමුත් සමස්ත සම්බන්ධතාවය වැඩි දියුණු කරයි.",
|
||||
"autofix_warning_text": "ඔබ \"නිරාකරණය කරන්න\" බොත්තම එබුවහොත්, ඔබගේ පද්ධතිය ඇඩ්ගාර්ඩ් හෝම් ව.නා.ප. සේවාදායකය භාවිතා කිරීමට වින්යාසගත කරනු ඇත.",
|
||||
|
||||
@@ -208,7 +208,7 @@
|
||||
"example_upstream_sdns": "môžete použiť <0>DNS pečiatky</0> pre <1>DNSCrypt</1> alebo <2>DNS-over-HTTPS</2>",
|
||||
"example_upstream_tcp": "radová DNS (cez TCP)",
|
||||
"all_lists_up_to_date_toast": "Všetky zoznamy sú už aktuálne",
|
||||
"updated_upstream_dns_toast": "Aktualizované upstream DNS servery",
|
||||
"updated_upstream_dns_toast": "Upstream servery boli úspešne uložené",
|
||||
"dns_test_ok_toast": "Špecifikované DNS servery pracujú korektne",
|
||||
"dns_test_not_ok_toast": "Server \"{{key}}\": nemohol byť použitý, skontrolujte, či ste ho správne napísali",
|
||||
"unblock": "Odblokovať",
|
||||
@@ -235,7 +235,7 @@
|
||||
"loading_table_status": "Načítavam...",
|
||||
"page_table_footer_text": "Stránka",
|
||||
"rows_table_footer_text": "riadky",
|
||||
"updated_custom_filtering_toast": "Aktualizované vlastné filtračné pravidlá",
|
||||
"updated_custom_filtering_toast": "Vlastné pravidlá boli úspešne uložené",
|
||||
"rule_removed_from_custom_filtering_toast": "Pravidlo odstránené z vlastných filtračných pravidiel: {{rule}}",
|
||||
"rule_added_to_custom_filtering_toast": "Pravidlo pridané do vlastných filtračných pravidiel: {{rule}}",
|
||||
"query_log_response_status": "Stav: {{value}}",
|
||||
@@ -306,7 +306,7 @@
|
||||
"install_settings_dns_desc": "Budete musieť konfigurovať Vaše zariadenia alebo smerovač, aby používali DNS server na nasledujúcich adresách:",
|
||||
"install_settings_all_interfaces": "Všetky rozhrania",
|
||||
"install_auth_title": "Overenie identity",
|
||||
"install_auth_desc": "Odporúčame Vám nakonfigurovať na administrátorskom webovom rozhraní AdGuard Home overenie Vašej identity heslom. Aj keď je prístupné iba vo Vašej lokálnej sieti, je stále dôležité chrániť ho pred neobmedzeným prístupom.",
|
||||
"install_auth_desc": "Je potrebné nakonfigurovať autentifikáciu heslom do administrátorského webového rozhrania AdGuard Home. Aj keď je AdGuard Home prístupný iba vo Vašej lokálnej sieti, je stále dôležité chrániť ho pred neobmedzeným prístupom.",
|
||||
"install_auth_username": "Meno používateľa",
|
||||
"install_auth_password": "Heslo",
|
||||
"install_auth_confirm": "Potvrdenie hesla",
|
||||
|
||||
@@ -208,7 +208,7 @@
|
||||
"example_upstream_sdns": "lahko uporabite <0>DNS Žige</0> za reševalce <1>DNSCrypt</1> ali <2>DNS-prek-HTTPS</2>",
|
||||
"example_upstream_tcp": "redni DNS (nad TCP)",
|
||||
"all_lists_up_to_date_toast": "Vsi seznami so že posodobljeni",
|
||||
"updated_upstream_dns_toast": "Posodobljeni Zagonske strežnike DNS",
|
||||
"updated_upstream_dns_toast": "Gorvodni trežniki so uspešno shranjeni",
|
||||
"dns_test_ok_toast": "Navedeni strežniki DNS delujejo pravilno",
|
||||
"dns_test_not_ok_toast": "Ni mogoče uporabiti: strežnika \"{{key}}\". Preverite, ali ste ga pravilno napisali",
|
||||
"unblock": "Omogoči",
|
||||
@@ -235,7 +235,7 @@
|
||||
"loading_table_status": "Nalaganje...",
|
||||
"page_table_footer_text": "Stran",
|
||||
"rows_table_footer_text": "vrstic",
|
||||
"updated_custom_filtering_toast": "Posodobljena pravila filtriranja po meri",
|
||||
"updated_custom_filtering_toast": "Pravila po meri so uspešno shranjena",
|
||||
"rule_removed_from_custom_filtering_toast": "Pravilo je odstranjeno iz pravil filtriranja po meri: {{rule}}",
|
||||
"rule_added_to_custom_filtering_toast": "Pravilo je dodano pravilom filtriranja po meri: {{rule}}",
|
||||
"query_log_response_status": "Stanje: {{value}}",
|
||||
@@ -306,7 +306,7 @@
|
||||
"install_settings_dns_desc": "Vaše naprave ali usmerjevalnik boste morali konfigurirati za uporabo strežnika DNS na naslednjih naslovih:",
|
||||
"install_settings_all_interfaces": "Vsi vmesniki",
|
||||
"install_auth_title": "Preverjanje pristnosti",
|
||||
"install_auth_desc": "Zelo priporočljivo je, da konfigurirate overovitev pristnosti gesla za vaš skrbniški spletni vmesnik AdGuard Home. Tudi če je dostopen v vašem lokalnem omrežju, je še vedno pomembno, da ga zaščitite pred neomejenim dostopom.",
|
||||
"install_auth_desc": "Nastavljeno mora biti preverjanje pristnosti gesla za skrbniški spletni vmesnik AdGuard Home. Tudi če je AdGuard Home dostopen samo v vašem lokalnem omrežju, je še vedno pomembno, da ga zaščitite pred neomejenim dostopom.",
|
||||
"install_auth_username": "Uporabniško ime",
|
||||
"install_auth_password": "Geslo",
|
||||
"install_auth_confirm": "Potrdite geslo",
|
||||
@@ -503,6 +503,7 @@
|
||||
"statistics_clear_confirm": "Ali ste prepričani, da želite počistiti statistiko?",
|
||||
"statistics_retention_confirm": "Ali ste prepričani, da želite spremeniti zadrževanje statistike? Če zmanjšate vrednost intervala, bodo nekateri podatki izgubljeni",
|
||||
"statistics_cleared": "Statistika je bila uspešno počiščena",
|
||||
"statistics_enable": "Omogoči statistiko",
|
||||
"interval_hours": "{{count}} ur",
|
||||
"interval_hours_plural": "{{count}} ur",
|
||||
"filters_configuration": "Nastavitve filtrov",
|
||||
@@ -612,7 +613,8 @@
|
||||
"click_to_view_queries": "Kliknite za prikaz poizvedb",
|
||||
"port_53_faq_link": "Vrata 53 pogosto zasedajo storitve 'DNSStubListener' ali 'Sistemsko razrešene storitve'. Preberite <0>to navodilo</0> o tem, kako to rešiti.",
|
||||
"adg_will_drop_dns_queries": "AdGuard Home bo izpustil vse poizvedbe DNS iz tega odjemalca.",
|
||||
"client_not_in_allowed_clients": "Odjemalec ni dovoljen, ker ga ni na seznamu \"Dovoljeni odjemalci\".",
|
||||
"filter_allowlist": "OPOZORILO: S to akcijo bo pravilo \"{{disallowed_rule}}\" izključeno s seznama dovoljenih odjemalcev.",
|
||||
"last_rule_in_allowlist": "Tega odjemalca ni mogoče onemogočiti, ker izključitev pravila \"{{disallowed_rule}}\" bo ONEMOGOČILO seznam 'Dovoljeni odjemalci'.",
|
||||
"experimental": "Eksperimentalno",
|
||||
"use_saved_key": "Uporabi prej shranjeni ključ"
|
||||
}
|
||||
|
||||
@@ -306,7 +306,7 @@
|
||||
"install_settings_dns_desc": "Cihazlarınızı veya yönlendiricinizi şu adresteki DNS sunucusunu kullanması için ayarlamanız gerekecek:",
|
||||
"install_settings_all_interfaces": "Tüm arayüzler",
|
||||
"install_auth_title": "Kimlik Doğrulama",
|
||||
"install_auth_desc": "AdGuard Home yönetici web arayüzüne erişim için kullanıcı adı ve şifre yapılandırmanız önemle tavsiye edilir. Yalnızca yerel ağınızdan erişilebilir olsa bile izinsiz erişimden korumak yine de önemlidir.",
|
||||
"install_auth_desc": "AdGuard Home yönetim web arayüzü için şifre doğrulaması yapılandırılmalıdır. AdGuard Home'a yalnızca yerel ağınızdan erişilebilir olsa bile, onu sınırsız erişimden korumak yine de önemlidir.",
|
||||
"install_auth_username": "Kullanıcı adı",
|
||||
"install_auth_password": "Parola",
|
||||
"install_auth_confirm": "Parolayı onayla",
|
||||
|
||||
@@ -306,7 +306,7 @@
|
||||
"install_settings_dns_desc": "您将需要使用以下地址来设置您的设备或路由器的 DNS 服务器:",
|
||||
"install_settings_all_interfaces": "所有接口",
|
||||
"install_auth_title": "身份认证",
|
||||
"install_auth_desc": "我们强烈建议您为 AdGuard Home 的网页管理界面配置密码。即使该页面只能通过您的本地网络访问,但避免它被不加限制地访问仍十分重要。",
|
||||
"install_auth_desc": "您需要对 AdGuard Home 的网页管理界面配置密码认证。即使该 AdGuard Home 只能通过您的本地网络访问,避免 AdGuard Home 被不受限制地访问依旧十分重要。",
|
||||
"install_auth_username": "用户名",
|
||||
"install_auth_password": "密码",
|
||||
"install_auth_confirm": "确认密码",
|
||||
|
||||
@@ -306,7 +306,7 @@
|
||||
"install_settings_dns_desc": "您將需要配置您的裝置或路由器以使用於下列的位址上之 DNS 伺服器:",
|
||||
"install_settings_all_interfaces": "所有的介面",
|
||||
"install_auth_title": "驗證",
|
||||
"install_auth_desc": "配置屬於您的 AdGuard Home 管理員網路介面之密碼驗證是被非常建議的。即使它僅在您的區域網路中為可存取的,保護它免於不受限制的存取為仍然重要的。",
|
||||
"install_auth_desc": "往您的 AdGuard Home 管理員網路介面之密碼驗證必須被配置。即使 AdGuard Home 僅在您的區域網路中為可存取的,保護它免於不受限制的存取為仍然重要的。",
|
||||
"install_auth_username": "使用者名稱",
|
||||
"install_auth_password": "密碼",
|
||||
"install_auth_confirm": "確認密碼",
|
||||
|
||||
@@ -16,18 +16,31 @@ const Row = ({
|
||||
? <LogsSearchLink response_status={response_status}>{formatNumber(count)}</LogsSearchLink>
|
||||
: count;
|
||||
|
||||
return <tr key={label}>
|
||||
<td>
|
||||
<Trans components={translationComponents}>{label}</Trans>
|
||||
<Tooltip content={tooltipTitle} placement="top"
|
||||
className="tooltip-container tooltip-custom--narrow text-center">
|
||||
<svg className="icons icon--20 icon--lightgray ml-2">
|
||||
<use xlinkHref="#question" />
|
||||
</svg>
|
||||
</Tooltip>
|
||||
</td>
|
||||
<td className="text-right"><strong>{content}</strong></td>
|
||||
</tr>;
|
||||
return (
|
||||
<div className="counters__row" key={label}>
|
||||
<div className="counters__column">
|
||||
<span className="counters__title">
|
||||
<Trans components={translationComponents}>
|
||||
{label}
|
||||
</Trans>
|
||||
</span>
|
||||
<span className="counters__tooltip">
|
||||
<Tooltip
|
||||
content={tooltipTitle}
|
||||
placement="top"
|
||||
className="tooltip-container tooltip-custom--narrow text-center"
|
||||
>
|
||||
<svg className="icons icon--20 icon--lightgray ml-2">
|
||||
<use xlinkHref="#question" />
|
||||
</svg>
|
||||
</Tooltip>
|
||||
</span>
|
||||
</div>
|
||||
<div className="counters__column counters__column--value">
|
||||
<strong>{content}</strong>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Counters = ({ refreshButton, subtitle }) => {
|
||||
@@ -88,9 +101,9 @@ const Counters = ({ refreshButton, subtitle }) => {
|
||||
bodyType="card-table"
|
||||
refresh={refreshButton}
|
||||
>
|
||||
<table className="table card-table">
|
||||
<tbody>{rows.map(Row)}</tbody>
|
||||
</table>
|
||||
<div className="counters">
|
||||
{rows.map(Row)}
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -49,3 +49,39 @@
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.counters__row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
border-top: 1px solid #dee2e6;
|
||||
padding: 0.75rem 1.5rem;
|
||||
}
|
||||
|
||||
.counters__column--value {
|
||||
flex-shrink: 0;
|
||||
margin-left: 1.5rem;
|
||||
text-align: right;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
.counters__column {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.counters__title {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.counters__tooltip {
|
||||
display: block;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
|
||||
4
go.mod
4
go.mod
@@ -3,8 +3,8 @@ module github.com/AdguardTeam/AdGuardHome
|
||||
go 1.16
|
||||
|
||||
require (
|
||||
github.com/AdguardTeam/dnsproxy v0.39.5
|
||||
github.com/AdguardTeam/golibs v0.9.2
|
||||
github.com/AdguardTeam/dnsproxy v0.39.8
|
||||
github.com/AdguardTeam/golibs v0.9.3
|
||||
github.com/AdguardTeam/urlfilter v0.14.6
|
||||
github.com/NYTimes/gziphandler v1.1.1
|
||||
github.com/ameshkov/dnscrypt/v2 v2.2.2
|
||||
|
||||
7
go.sum
7
go.sum
@@ -9,12 +9,13 @@ dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D
|
||||
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
|
||||
github.com/AdguardTeam/dhcp v0.0.0-20210519141215-51808c73c0bf h1:gc042VRSIRSUzZ+Px6xQCRWNJZTaPkomisDfUZmoFNk=
|
||||
github.com/AdguardTeam/dhcp v0.0.0-20210519141215-51808c73c0bf/go.mod h1:TKl4jN3Voofo4UJIicyNhWGp/nlQqQkFxmwIFTvBkKI=
|
||||
github.com/AdguardTeam/dnsproxy v0.39.5 h1:SQorhRLR1241t6hy9CiAGZUjRULhsDJlPJTl0UGX8uw=
|
||||
github.com/AdguardTeam/dnsproxy v0.39.5/go.mod h1:eDpJKAdkHORRwAedjuERv+7SWlcz4cn+5uwrbUAWHRY=
|
||||
github.com/AdguardTeam/dnsproxy v0.39.8 h1:miRhkZBx/19Rs1o10r3QC0D0Zc2J2Id/cqXwfvLOyM0=
|
||||
github.com/AdguardTeam/dnsproxy v0.39.8/go.mod h1:eDpJKAdkHORRwAedjuERv+7SWlcz4cn+5uwrbUAWHRY=
|
||||
github.com/AdguardTeam/golibs v0.4.0/go.mod h1:skKsDKIBB7kkFflLJBpfGX+G8QFTx0WKUzB6TIgtUj4=
|
||||
github.com/AdguardTeam/golibs v0.4.2/go.mod h1:skKsDKIBB7kkFflLJBpfGX+G8QFTx0WKUzB6TIgtUj4=
|
||||
github.com/AdguardTeam/golibs v0.9.2 h1:H3BDFkaosxvb+UgFlNVyN66GZ+JglcZULnJ7z7PukyQ=
|
||||
github.com/AdguardTeam/golibs v0.9.2/go.mod h1:fCAMwPBJ8S7YMYbTWvYS+eeTLblP5E04IDtNAo7y7IY=
|
||||
github.com/AdguardTeam/golibs v0.9.3 h1:noeKHJEzrSwxzX0Zi3USM3cXf1qQV99SO772jet/uEY=
|
||||
github.com/AdguardTeam/golibs v0.9.3/go.mod h1:fCAMwPBJ8S7YMYbTWvYS+eeTLblP5E04IDtNAo7y7IY=
|
||||
github.com/AdguardTeam/gomitmproxy v0.2.0/go.mod h1:Qdv0Mktnzer5zpdpi5rAwixNJzW2FN91LjKJCkVbYGU=
|
||||
github.com/AdguardTeam/urlfilter v0.14.6 h1:emqoKZElooHACYehRBYENeKVN1a/rspxiqTIMYLuoIo=
|
||||
github.com/AdguardTeam/urlfilter v0.14.6/go.mod h1:klx4JbOfc4EaNb5lWLqOwfg+pVcyRukmoJRvO55lL5U=
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"sync"
|
||||
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/digineo/go-ipset/v2"
|
||||
"github.com/mdlayher/netlink"
|
||||
"github.com/ti-mo/netfilter"
|
||||
@@ -67,6 +68,15 @@ type ipsetProps struct {
|
||||
// unit is a convenient alias for struct{}.
|
||||
type unit = struct{}
|
||||
|
||||
// ipsInIpset is the type of a set of IP-address-to-ipset mappings.
|
||||
type ipsInIpset map[ipInIpsetEntry]unit
|
||||
|
||||
// ipInIpsetEntry is the type for entries in an ipsInIpset set.
|
||||
type ipInIpsetEntry struct {
|
||||
ipsetName string
|
||||
ipArr [net.IPv6len]byte
|
||||
}
|
||||
|
||||
// ipsetMgr is the Linux Netfilter ipset manager.
|
||||
type ipsetMgr struct {
|
||||
nameToIpset map[string]ipsetProps
|
||||
@@ -82,7 +92,7 @@ type ipsetMgr struct {
|
||||
// are either added to all corresponding ipsets or not. When that stops
|
||||
// being the case, for example if we add dynamic reconfiguration of
|
||||
// ipsets, this map will need to become a per-ipset-name one.
|
||||
addedIPs map[[16]byte]unit
|
||||
addedIPs ipsInIpset
|
||||
|
||||
ipv4Conn ipsetConn
|
||||
ipv6Conn ipsetConn
|
||||
@@ -199,7 +209,7 @@ func newIpsetMgrWithDialer(ipsetConf []string, dial ipsetDialer) (mgr IpsetManag
|
||||
|
||||
dial: dial,
|
||||
|
||||
addedIPs: make(map[[16]byte]unit),
|
||||
addedIPs: make(ipsInIpset),
|
||||
}
|
||||
|
||||
err = m.dialNetfilter(&netlink.Config{})
|
||||
@@ -265,16 +275,19 @@ func (m *ipsetMgr) addIPs(host string, set ipsetProps, ips []net.IP) (n int, err
|
||||
}
|
||||
|
||||
var entries []*ipset.Entry
|
||||
var newAddedIPs [][16]byte
|
||||
var newAddedEntries []ipInIpsetEntry
|
||||
for _, ip := range ips {
|
||||
var iparr [16]byte
|
||||
copy(iparr[:], ip.To16())
|
||||
if _, added := m.addedIPs[iparr]; added {
|
||||
e := ipInIpsetEntry{
|
||||
ipsetName: set.name,
|
||||
}
|
||||
copy(e.ipArr[:], ip.To16())
|
||||
|
||||
if _, added := m.addedIPs[e]; added {
|
||||
continue
|
||||
}
|
||||
|
||||
entries = append(entries, ipset.NewEntry(ipset.EntryIP(ip)))
|
||||
newAddedIPs = append(newAddedIPs, iparr)
|
||||
newAddedEntries = append(newAddedEntries, e)
|
||||
}
|
||||
|
||||
n = len(entries)
|
||||
@@ -299,8 +312,8 @@ func (m *ipsetMgr) addIPs(host string, set ipsetProps, ips []net.IP) (n int, err
|
||||
|
||||
// Only add these to the cache once we're sure that all of them were
|
||||
// actually sent to the ipset.
|
||||
for _, iparr := range newAddedIPs {
|
||||
m.addedIPs[iparr] = unit{}
|
||||
for _, e := range newAddedEntries {
|
||||
m.addedIPs[e] = unit{}
|
||||
}
|
||||
|
||||
return n, nil
|
||||
@@ -330,6 +343,8 @@ func (m *ipsetMgr) addToSets(
|
||||
return n, fmt.Errorf("unexpected family %s for ipset %q", set.family, set.name)
|
||||
}
|
||||
|
||||
log.Debug("ipset: added %d ips to set %s", nn, set.name)
|
||||
|
||||
n += nn
|
||||
}
|
||||
|
||||
@@ -346,6 +361,8 @@ func (m *ipsetMgr) Add(host string, ip4s, ip6s []net.IP) (n int, err error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
log.Debug("ipset: found %d sets", len(sets))
|
||||
|
||||
return m.addToSets(host, ip4s, ip6s, sets)
|
||||
}
|
||||
|
||||
|
||||
@@ -131,7 +131,7 @@ func (c *ipsetCtx) process(dctx *dnsContext) (rc resultCode) {
|
||||
return resultCodeSuccess
|
||||
}
|
||||
|
||||
log.Debug("ipset: added %d new ips", n)
|
||||
log.Debug("ipset: added %d new ipset entries", n)
|
||||
|
||||
return resultCodeSuccess
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ type fakeIpsetMgr struct {
|
||||
ip6s []net.IP
|
||||
}
|
||||
|
||||
// Add implements the aghnet.IpsetManager inteface for *fakeIpsetMgr.
|
||||
// Add implements the aghnet.IpsetManager interface for *fakeIpsetMgr.
|
||||
func (m *fakeIpsetMgr) Add(host string, ip4s, ip6s []net.IP) (n int, err error) {
|
||||
m.ip4s = append(m.ip4s, ip4s...)
|
||||
m.ip6s = append(m.ip6s, ip6s...)
|
||||
|
||||
@@ -255,10 +255,14 @@ func (d *DNSFilter) GetConfig() (s Settings) {
|
||||
// WriteDiskConfig - write configuration
|
||||
func (d *DNSFilter) WriteDiskConfig(c *Config) {
|
||||
d.confLock.Lock()
|
||||
defer d.confLock.Unlock()
|
||||
|
||||
*c = d.Config
|
||||
c.Rewrites = rewriteArrayDup(d.Config.Rewrites)
|
||||
// BlockedServices
|
||||
d.confLock.Unlock()
|
||||
c.Rewrites = cloneRewrites(c.Rewrites)
|
||||
}
|
||||
|
||||
func cloneRewrites(entries []RewriteEntry) (clone []RewriteEntry) {
|
||||
return append([]RewriteEntry(nil), entries...)
|
||||
}
|
||||
|
||||
// SetFilters - set new filters (synchronously or asynchronously)
|
||||
|
||||
@@ -27,8 +27,66 @@ type RewriteEntry struct {
|
||||
Type uint16 `yaml:"-"`
|
||||
}
|
||||
|
||||
func (r *RewriteEntry) equals(b RewriteEntry) bool {
|
||||
return r.Domain == b.Domain && r.Answer == b.Answer
|
||||
// equal returns true if the entry is considered equal to the other.
|
||||
func (e *RewriteEntry) equal(other RewriteEntry) (ok bool) {
|
||||
return e.Domain == other.Domain && e.Answer == other.Answer
|
||||
}
|
||||
|
||||
// matchesQType returns true if the entry matched qtype.
|
||||
func (e *RewriteEntry) matchesQType(qtype uint16) (ok bool) {
|
||||
// Add CNAMEs, since they match for all types requests.
|
||||
if e.Type == dns.TypeCNAME {
|
||||
return true
|
||||
}
|
||||
|
||||
// Reject types other than A and AAAA.
|
||||
if qtype != dns.TypeA && qtype != dns.TypeAAAA {
|
||||
return false
|
||||
}
|
||||
|
||||
// If the types match or the entry is set to allow only the other type,
|
||||
// include them.
|
||||
return e.Type == qtype || e.IP == nil
|
||||
}
|
||||
|
||||
// normalize makes sure that the a new or decoded entry is normalized with
|
||||
// regards to domain name case, IP length, and so on.
|
||||
func (e *RewriteEntry) normalize() {
|
||||
// TODO(a.garipov): Write a case-agnostic version of strings.HasSuffix
|
||||
// and use it in matchDomainWildcard instead of using strings.ToLower
|
||||
// everywhere.
|
||||
e.Domain = strings.ToLower(e.Domain)
|
||||
|
||||
switch e.Answer {
|
||||
case "AAAA":
|
||||
e.IP = nil
|
||||
e.Type = dns.TypeAAAA
|
||||
|
||||
return
|
||||
case "A":
|
||||
e.IP = nil
|
||||
e.Type = dns.TypeA
|
||||
|
||||
return
|
||||
default:
|
||||
// Go on.
|
||||
}
|
||||
|
||||
ip := net.ParseIP(e.Answer)
|
||||
if ip == nil {
|
||||
e.Type = dns.TypeCNAME
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
ip4 := ip.To4()
|
||||
if ip4 != nil {
|
||||
e.IP = ip4
|
||||
e.Type = dns.TypeA
|
||||
} else {
|
||||
e.IP = ip
|
||||
e.Type = dns.TypeAAAA
|
||||
}
|
||||
}
|
||||
|
||||
func isWildcard(host string) bool {
|
||||
@@ -78,48 +136,9 @@ func (a rewritesSorted) Less(i, j int) bool {
|
||||
return len(a[i].Domain) > len(a[j].Domain)
|
||||
}
|
||||
|
||||
// prepare prepares the a new or decoded entry.
|
||||
func (r *RewriteEntry) prepare() {
|
||||
// TODO(a.garipov): Write a case-agnostic version of strings.HasSuffix
|
||||
// and use it in matchDomainWildcard instead of using strings.ToLower
|
||||
// everywhere.
|
||||
r.Domain = strings.ToLower(r.Domain)
|
||||
|
||||
switch r.Answer {
|
||||
case "AAAA":
|
||||
r.IP = nil
|
||||
r.Type = dns.TypeAAAA
|
||||
|
||||
return
|
||||
case "A":
|
||||
r.IP = nil
|
||||
r.Type = dns.TypeA
|
||||
|
||||
return
|
||||
default:
|
||||
// Go on.
|
||||
}
|
||||
|
||||
ip := net.ParseIP(r.Answer)
|
||||
if ip == nil {
|
||||
r.Type = dns.TypeCNAME
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
ip4 := ip.To4()
|
||||
if ip4 != nil {
|
||||
r.IP = ip4
|
||||
r.Type = dns.TypeA
|
||||
} else {
|
||||
r.IP = ip
|
||||
r.Type = dns.TypeAAAA
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DNSFilter) prepareRewrites() {
|
||||
for i := range d.Rewrites {
|
||||
d.Rewrites[i].prepare()
|
||||
d.Rewrites[i].normalize()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,18 +146,15 @@ func (d *DNSFilter) prepareRewrites() {
|
||||
// CNAME, then A and AAAA; exact, then wildcard. If the host is matched
|
||||
// exactly, wildcard entries aren't returned. If the host matched by wildcards,
|
||||
// return the most specific for the question type.
|
||||
func findRewrites(a []RewriteEntry, host string, qtype uint16) []RewriteEntry {
|
||||
func findRewrites(entries []RewriteEntry, host string, qtype uint16) (matched []RewriteEntry) {
|
||||
rr := rewritesSorted{}
|
||||
for _, r := range a {
|
||||
if r.Domain != host && !matchDomainWildcard(host, r.Domain) {
|
||||
for _, e := range entries {
|
||||
if e.Domain != host && !matchDomainWildcard(host, e.Domain) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Return CNAMEs for all types requests, but only the
|
||||
// appropriate ones for A and AAAA.
|
||||
if r.Type == dns.TypeCNAME ||
|
||||
(r.Type == qtype && (qtype == dns.TypeA || qtype == dns.TypeAAAA)) {
|
||||
rr = append(rr, r)
|
||||
if e.matchesQType(qtype) {
|
||||
rr = append(rr, e)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,12 +185,6 @@ func max(a, b int) int {
|
||||
return b
|
||||
}
|
||||
|
||||
func rewriteArrayDup(a []RewriteEntry) []RewriteEntry {
|
||||
a2 := make([]RewriteEntry, len(a))
|
||||
copy(a2, a)
|
||||
return a2
|
||||
}
|
||||
|
||||
type rewriteEntryJSON struct {
|
||||
Domain string `json:"domain"`
|
||||
Answer string `json:"answer"`
|
||||
@@ -213,7 +223,7 @@ func (d *DNSFilter) handleRewriteAdd(w http.ResponseWriter, r *http.Request) {
|
||||
Domain: jsent.Domain,
|
||||
Answer: jsent.Answer,
|
||||
}
|
||||
ent.prepare()
|
||||
ent.normalize()
|
||||
d.confLock.Lock()
|
||||
d.Config.Rewrites = append(d.Config.Rewrites, ent)
|
||||
d.confLock.Unlock()
|
||||
@@ -238,7 +248,7 @@ func (d *DNSFilter) handleRewriteDelete(w http.ResponseWriter, r *http.Request)
|
||||
arr := []RewriteEntry{}
|
||||
d.confLock.Lock()
|
||||
for _, ent := range d.Config.Rewrites {
|
||||
if ent.equals(entDel) {
|
||||
if ent.equal(entDel) {
|
||||
log.Debug("Rewrites: removed element: %s -> %s", ent.Domain, ent.Answer)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -316,7 +316,7 @@ func TestRewritesExceptionIP(t *testing.T) {
|
||||
}, {
|
||||
name: "match_AAAA_host3.com",
|
||||
host: "host3.com",
|
||||
want: nil,
|
||||
want: []net.IP{},
|
||||
dtyp: dns.TypeAAAA,
|
||||
}}
|
||||
|
||||
|
||||
@@ -173,7 +173,7 @@ var config = &configuration{
|
||||
AuthBlockMin: 15,
|
||||
DNS: dnsConfig{
|
||||
BindHosts: []net.IP{{0, 0, 0, 0}},
|
||||
Port: 53,
|
||||
Port: defaultPortDNS,
|
||||
StatsInterval: 1,
|
||||
FilteringConfig: dnsforward.FilteringConfig{
|
||||
ProtectionEnabled: true, // whether or not use any of filtering features
|
||||
@@ -202,9 +202,9 @@ var config = &configuration{
|
||||
UsePrivateRDNS: true,
|
||||
},
|
||||
TLS: tlsConfigSettings{
|
||||
PortHTTPS: 443,
|
||||
PortDNSOverTLS: 853, // needs to be passed through to dnsproxy
|
||||
PortDNSOverQUIC: 784,
|
||||
PortHTTPS: defaultPortHTTPS,
|
||||
PortDNSOverTLS: defaultPortTLS, // needs to be passed through to dnsproxy
|
||||
PortDNSOverQUIC: defaultPortQUIC,
|
||||
},
|
||||
logSettings: logSettings{
|
||||
LogCompress: false,
|
||||
|
||||
@@ -39,7 +39,7 @@ func httpError(w http.ResponseWriter, code int, format string, args ...interface
|
||||
func appendDNSAddrs(dst []string, addrs ...net.IP) (res []string) {
|
||||
for _, addr := range addrs {
|
||||
var hostport string
|
||||
if config.DNS.Port != 53 {
|
||||
if config.DNS.Port != defaultPortDNS {
|
||||
hostport = netutil.JoinHostPort(addr.String(), config.DNS.Port)
|
||||
} else {
|
||||
hostport = addr.String()
|
||||
@@ -285,8 +285,6 @@ func preInstallHandler(handler http.Handler) http.Handler {
|
||||
return &preInstallHandlerStruct{handler}
|
||||
}
|
||||
|
||||
const defaultHTTPSPort = 443
|
||||
|
||||
// handleHTTPSRedirect redirects the request to HTTPS, if needed. If ok is
|
||||
// true, the middleware must continue handling the request.
|
||||
func handleHTTPSRedirect(w http.ResponseWriter, r *http.Request) (ok bool) {
|
||||
@@ -304,7 +302,7 @@ func handleHTTPSRedirect(w http.ResponseWriter, r *http.Request) (ok bool) {
|
||||
|
||||
if r.TLS == nil && web.forceHTTPS {
|
||||
hostPort := host
|
||||
if port := web.conf.PortHTTPS; port != defaultHTTPSPort {
|
||||
if port := web.conf.PortHTTPS; port != defaultPortHTTPS {
|
||||
hostPort = netutil.JoinHostPort(host, port)
|
||||
}
|
||||
|
||||
|
||||
@@ -254,13 +254,21 @@ func (f *Filtering) handleFilteringRefresh(w http.ResponseWriter, r *http.Reques
|
||||
return
|
||||
}
|
||||
|
||||
Context.controlLock.Unlock()
|
||||
flags := filterRefreshBlocklists
|
||||
if req.White {
|
||||
flags = filterRefreshAllowlists
|
||||
}
|
||||
resp.Updated, err = f.refreshFilters(flags|filterRefreshForce, false)
|
||||
Context.controlLock.Lock()
|
||||
func() {
|
||||
// Temporarily unlock the Context.controlLock because the
|
||||
// f.refreshFilters waits for it to be unlocked but it's
|
||||
// actually locked in ensure wrapper.
|
||||
//
|
||||
// TODO(e.burkov): Reconsider this messy syncing process.
|
||||
Context.controlLock.Unlock()
|
||||
defer Context.controlLock.Lock()
|
||||
|
||||
resp.Updated, err = f.refreshFilters(flags|filterRefreshForce, false)
|
||||
}()
|
||||
if err != nil {
|
||||
httpError(w, http.StatusInternalServerError, "%s", err)
|
||||
return
|
||||
|
||||
@@ -29,8 +29,8 @@ type getAddrsResponse struct {
|
||||
// handleInstallGetAddresses is the handler for /install/get_addresses endpoint.
|
||||
func (web *Web) handleInstallGetAddresses(w http.ResponseWriter, r *http.Request) {
|
||||
data := getAddrsResponse{}
|
||||
data.WebPort = 80
|
||||
data.DNSPort = 53
|
||||
data.WebPort = defaultPortHTTP
|
||||
data.DNSPort = defaultPortDNS
|
||||
|
||||
ifaces, err := aghnet.GetValidNetInterfacesForWeb()
|
||||
if err != nil {
|
||||
@@ -257,7 +257,8 @@ type applyConfigReq struct {
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
// Copy installation parameters between two configuration objects
|
||||
// copyInstallSettings copies the installation parameters between two
|
||||
// configuration structures.
|
||||
func copyInstallSettings(dst, src *configuration) {
|
||||
dst.BindHost = src.BindHost
|
||||
dst.BindPort = src.BindPort
|
||||
@@ -307,7 +308,7 @@ func (web *Web) handleInstallConfigure(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
var curConfig *configuration
|
||||
curConfig := &configuration{}
|
||||
copyInstallSettings(curConfig, config)
|
||||
|
||||
Context.firstRun = false
|
||||
@@ -552,8 +553,8 @@ type getAddrsResponseBeta struct {
|
||||
// functionality will appear in default handleInstallGetAddresses.
|
||||
func (web *Web) handleInstallGetAddressesBeta(w http.ResponseWriter, r *http.Request) {
|
||||
data := getAddrsResponseBeta{}
|
||||
data.WebPort = 80
|
||||
data.DNSPort = 53
|
||||
data.WebPort = defaultPortHTTP
|
||||
data.DNSPort = defaultPortDNS
|
||||
|
||||
ifaces, err := aghnet.GetValidNetInterfacesForWeb()
|
||||
if err != nil {
|
||||
|
||||
@@ -19,6 +19,15 @@ import (
|
||||
yaml "gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
// Default ports.
|
||||
const (
|
||||
defaultPortDNS = 53
|
||||
defaultPortHTTP = 80
|
||||
defaultPortHTTPS = 443
|
||||
defaultPortQUIC = 784
|
||||
defaultPortTLS = 853
|
||||
)
|
||||
|
||||
// Called by other modules when configuration is changed
|
||||
func onConfigModified() {
|
||||
_ = config.write()
|
||||
@@ -253,7 +262,7 @@ func getDNSEncryption() (de dnsEncryption) {
|
||||
hostname := tlsConf.ServerName
|
||||
if tlsConf.PortHTTPS != 0 {
|
||||
addr := hostname
|
||||
if tlsConf.PortHTTPS != 443 {
|
||||
if tlsConf.PortHTTPS != defaultPortHTTPS {
|
||||
addr = netutil.JoinHostPort(addr, tlsConf.PortHTTPS)
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/AdguardTeam/golibs/stringutil"
|
||||
)
|
||||
|
||||
var nextFilterID = time.Now().Unix() // semi-stable way to generate an unique ID
|
||||
@@ -535,26 +536,85 @@ func (f *Filtering) read(reader io.Reader, tmpFile *os.File, filter *filter) (in
|
||||
}
|
||||
}
|
||||
|
||||
// updateIntl returns true if filter update performed successfully.
|
||||
func (f *Filtering) updateIntl(filter *filter) (updated bool, err error) {
|
||||
updated = false
|
||||
log.Tracef("Downloading update for filter %d from %s", filter.ID, filter.URL)
|
||||
// finalizeUpdate closes and gets rid of temporary file f with filter's content
|
||||
// according to updated. It also saves new values of flt's name, rules number
|
||||
// and checksum if sucсeeded.
|
||||
func finalizeUpdate(
|
||||
f *os.File,
|
||||
flt *filter,
|
||||
updated bool,
|
||||
name string,
|
||||
rnum int,
|
||||
cs uint32,
|
||||
) (err error) {
|
||||
tmpFileName := f.Name()
|
||||
|
||||
tmpFile, err := os.CreateTemp(filepath.Join(Context.getDataDir(), filterDir), "")
|
||||
// Close the file before renaming it because it's required on Windows.
|
||||
//
|
||||
// See https://github.com/adguardTeam/adGuardHome/issues/1553.
|
||||
if err = f.Close(); err != nil {
|
||||
return fmt.Errorf("closing temporary file: %w", err)
|
||||
}
|
||||
|
||||
if !updated {
|
||||
log.Tracef("filter #%d from %s has no changes, skip", flt.ID, flt.URL)
|
||||
|
||||
return os.Remove(tmpFileName)
|
||||
}
|
||||
|
||||
log.Printf("saving filter %d contents to: %s", flt.ID, flt.Path())
|
||||
|
||||
if err = os.Rename(tmpFileName, flt.Path()); err != nil {
|
||||
return errors.WithDeferred(err, os.Remove(tmpFileName))
|
||||
}
|
||||
|
||||
flt.Name = stringutil.Coalesce(flt.Name, name)
|
||||
flt.checksum = cs
|
||||
flt.RulesCount = rnum
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// processUpdate copies filter's content from src to dst and returns the name,
|
||||
// rules number, and checksum for it. It also returns the number of bytes read
|
||||
// from src.
|
||||
func (f *Filtering) processUpdate(
|
||||
src io.Reader,
|
||||
dst *os.File,
|
||||
flt *filter,
|
||||
) (name string, rnum int, cs uint32, n int, err error) {
|
||||
if n, err = f.read(src, dst, flt); err != nil {
|
||||
return "", 0, 0, 0, err
|
||||
}
|
||||
|
||||
if _, err = dst.Seek(0, io.SeekStart); err != nil {
|
||||
return "", 0, 0, 0, err
|
||||
}
|
||||
|
||||
rnum, cs, name = f.parseFilterContents(dst)
|
||||
|
||||
return name, rnum, cs, n, nil
|
||||
}
|
||||
|
||||
// updateIntl updates the flt rewriting it's actual file. It returns true if
|
||||
// the actual update has been performed.
|
||||
func (f *Filtering) updateIntl(flt *filter) (ok bool, err error) {
|
||||
log.Tracef("downloading update for filter %d from %s", flt.ID, flt.URL)
|
||||
|
||||
var name string
|
||||
var rnum, n int
|
||||
var cs uint32
|
||||
|
||||
var tmpFile *os.File
|
||||
tmpFile, err = os.CreateTemp(filepath.Join(Context.getDataDir(), filterDir), "")
|
||||
if err != nil {
|
||||
return updated, err
|
||||
return false, err
|
||||
}
|
||||
defer func() {
|
||||
var derr error
|
||||
if tmpFile != nil {
|
||||
if derr = tmpFile.Close(); derr != nil {
|
||||
log.Printf("Couldn't close temporary file: %s", derr)
|
||||
}
|
||||
|
||||
tmpFileName := tmpFile.Name()
|
||||
if derr = os.Remove(tmpFileName); derr != nil {
|
||||
log.Printf("Couldn't delete temporary file %s: %s", tmpFileName, derr)
|
||||
}
|
||||
err = errors.WithDeferred(err, finalizeUpdate(tmpFile, flt, ok, name, rnum, cs))
|
||||
ok = ok && err == nil
|
||||
if ok {
|
||||
log.Printf("updated filter %d: %d bytes, %d rules", flt.ID, n, rnum)
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -562,72 +622,42 @@ func (f *Filtering) updateIntl(filter *filter) (updated bool, err error) {
|
||||
// end users.
|
||||
//
|
||||
// See https://github.com/AdguardTeam/AdGuardHome/issues/3198.
|
||||
err = tmpFile.Chmod(0o644)
|
||||
if err != nil {
|
||||
return updated, fmt.Errorf("changing file mode: %w", err)
|
||||
if err = tmpFile.Chmod(0o644); err != nil {
|
||||
return false, fmt.Errorf("changing file mode: %w", err)
|
||||
}
|
||||
|
||||
var reader io.Reader
|
||||
if filepath.IsAbs(filter.URL) {
|
||||
var f io.ReadCloser
|
||||
f, err = os.Open(filter.URL)
|
||||
var r io.Reader
|
||||
if filepath.IsAbs(flt.URL) {
|
||||
var file io.ReadCloser
|
||||
file, err = os.Open(flt.URL)
|
||||
if err != nil {
|
||||
return updated, fmt.Errorf("open file: %w", err)
|
||||
return false, fmt.Errorf("open file: %w", err)
|
||||
}
|
||||
defer func() { err = errors.WithDeferred(err, f.Close()) }()
|
||||
defer func() { err = errors.WithDeferred(err, file.Close()) }()
|
||||
|
||||
reader = f
|
||||
r = file
|
||||
} else {
|
||||
var resp *http.Response
|
||||
resp, err = Context.client.Get(filter.URL)
|
||||
resp, err = Context.client.Get(flt.URL)
|
||||
if err != nil {
|
||||
log.Printf("Couldn't request filter from URL %s, skipping: %s", filter.URL, err)
|
||||
log.Printf("requesting filter from %s, skip: %s", flt.URL, err)
|
||||
|
||||
return updated, err
|
||||
return false, err
|
||||
}
|
||||
defer func() { err = errors.WithDeferred(err, resp.Body.Close()) }()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
log.Printf("Got status code %d from URL %s, skipping", resp.StatusCode, filter.URL)
|
||||
return updated, fmt.Errorf("got status code != 200: %d", resp.StatusCode)
|
||||
log.Printf("got status code %d from %s, skip", resp.StatusCode, flt.URL)
|
||||
|
||||
return false, fmt.Errorf("got status code != 200: %d", resp.StatusCode)
|
||||
}
|
||||
reader = resp.Body
|
||||
|
||||
r = resp.Body
|
||||
}
|
||||
|
||||
total, err := f.read(reader, tmpFile, filter)
|
||||
if err != nil {
|
||||
return updated, err
|
||||
}
|
||||
name, rnum, cs, n, err = f.processUpdate(r, tmpFile, flt)
|
||||
|
||||
// Extract filter name and count number of rules
|
||||
_, _ = tmpFile.Seek(0, io.SeekStart)
|
||||
rulesCount, checksum, filterName := f.parseFilterContents(tmpFile)
|
||||
// Check if the filter has been really changed
|
||||
if filter.checksum == checksum {
|
||||
log.Tracef("Filter #%d at URL %s hasn't changed, not updating it", filter.ID, filter.URL)
|
||||
return updated, nil
|
||||
}
|
||||
|
||||
log.Printf("Filter %d has been updated: %d bytes, %d rules",
|
||||
filter.ID, total, rulesCount)
|
||||
if len(filter.Name) == 0 {
|
||||
filter.Name = filterName
|
||||
}
|
||||
filter.RulesCount = rulesCount
|
||||
filter.checksum = checksum
|
||||
filterFilePath := filter.Path()
|
||||
log.Printf("Saving filter %d contents to: %s", filter.ID, filterFilePath)
|
||||
|
||||
// Closing the file before renaming it is necessary on Windows
|
||||
_ = tmpFile.Close()
|
||||
err = os.Rename(tmpFile.Name(), filterFilePath)
|
||||
if err != nil {
|
||||
return updated, err
|
||||
}
|
||||
tmpFile = nil
|
||||
updated = true
|
||||
|
||||
return updated, nil
|
||||
return cs != flt.checksum, err
|
||||
}
|
||||
|
||||
// loads filter contents from the file in dataDir
|
||||
|
||||
@@ -1,76 +1,118 @@
|
||||
package home
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/golibs/netutil"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func testStartFilterListener(t *testing.T) net.Listener {
|
||||
const testFltsFileName = "1.txt"
|
||||
|
||||
func testStartFilterListener(t *testing.T, fltContent *[]byte) (l net.Listener) {
|
||||
t.Helper()
|
||||
|
||||
h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
n, werr := w.Write(*fltContent)
|
||||
require.NoError(t, werr)
|
||||
require.Equal(t, len(*fltContent), n)
|
||||
})
|
||||
|
||||
var err error
|
||||
l, err = net.Listen("tcp", ":0")
|
||||
require.NoError(t, err)
|
||||
|
||||
go func() {
|
||||
_ = http.Serve(l, h)
|
||||
}()
|
||||
t.Cleanup(func() {
|
||||
require.NoError(t, l.Close())
|
||||
})
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
func TestFilters(t *testing.T) {
|
||||
const content = `||example.org^$third-party
|
||||
# Inline comment example
|
||||
||example.com^$third-party
|
||||
0.0.0.0 example.com
|
||||
`
|
||||
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/filters/1.txt", func(w http.ResponseWriter, r *http.Request) {
|
||||
_, werr := w.Write([]byte(content))
|
||||
assert.Nil(t, werr)
|
||||
})
|
||||
fltContent := []byte(content)
|
||||
|
||||
listener, err := net.Listen("tcp", ":0")
|
||||
require.Nil(t, err)
|
||||
|
||||
go func() {
|
||||
_ = http.Serve(listener, mux)
|
||||
}()
|
||||
|
||||
t.Cleanup(func() {
|
||||
assert.Nil(t, listener.Close())
|
||||
})
|
||||
|
||||
return listener
|
||||
}
|
||||
|
||||
func TestFilters(t *testing.T) {
|
||||
l := testStartFilterListener(t)
|
||||
dir := t.TempDir()
|
||||
l := testStartFilterListener(t, &fltContent)
|
||||
|
||||
Context = homeContext{
|
||||
workDir: dir,
|
||||
workDir: t.TempDir(),
|
||||
client: &http.Client{
|
||||
Timeout: 5 * time.Second,
|
||||
},
|
||||
}
|
||||
Context.filters.Init()
|
||||
|
||||
f := filter{
|
||||
URL: fmt.Sprintf("http://127.0.0.1:%d/filters/1.txt", l.Addr().(*net.TCPAddr).Port),
|
||||
f := &filter{
|
||||
URL: (&url.URL{
|
||||
Scheme: "http",
|
||||
Host: (&netutil.IPPort{
|
||||
IP: net.IP{127, 0, 0, 1},
|
||||
Port: l.Addr().(*net.TCPAddr).Port,
|
||||
}).String(),
|
||||
Path: path.Join(filterDir, testFltsFileName),
|
||||
}).String(),
|
||||
}
|
||||
|
||||
// Download.
|
||||
ok, err := Context.filters.update(&f)
|
||||
require.Nil(t, err)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, 3, f.RulesCount)
|
||||
updateAndAssert := func(t *testing.T, want require.BoolAssertionFunc, wantRulesCount int) {
|
||||
ok, err := Context.filters.update(f)
|
||||
require.NoError(t, err)
|
||||
want(t, ok)
|
||||
|
||||
// Refresh.
|
||||
ok, err = Context.filters.update(&f)
|
||||
require.Nil(t, err)
|
||||
require.False(t, ok)
|
||||
assert.Equal(t, wantRulesCount, f.RulesCount)
|
||||
|
||||
err = Context.filters.load(&f)
|
||||
require.Nil(t, err)
|
||||
var dir []fs.DirEntry
|
||||
dir, err = os.ReadDir(filepath.Join(Context.getDataDir(), filterDir))
|
||||
require.NoError(t, err)
|
||||
|
||||
f.unload()
|
||||
require.Nil(t, os.Remove(f.Path()))
|
||||
assert.Len(t, dir, 1)
|
||||
|
||||
require.FileExists(t, f.Path())
|
||||
|
||||
err = Context.filters.load(f)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
t.Run("download", func(t *testing.T) {
|
||||
updateAndAssert(t, require.True, 3)
|
||||
})
|
||||
|
||||
t.Run("refresh_idle", func(t *testing.T) {
|
||||
updateAndAssert(t, require.False, 3)
|
||||
})
|
||||
|
||||
t.Run("refresh_actually", func(t *testing.T) {
|
||||
fltContent = []byte(`||example.com^`)
|
||||
t.Cleanup(func() {
|
||||
fltContent = []byte(content)
|
||||
})
|
||||
|
||||
updateAndAssert(t, require.True, 1)
|
||||
})
|
||||
|
||||
t.Run("load_unload", func(t *testing.T) {
|
||||
err := Context.filters.load(f)
|
||||
require.NoError(t, err)
|
||||
|
||||
f.unload()
|
||||
})
|
||||
|
||||
require.NoError(t, os.Remove(f.Path()))
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ type homeContext struct {
|
||||
|
||||
configFilename string // Config filename (can be overridden via the command line arguments)
|
||||
workDir string // Location of our directory, used to protect against CWD being somewhere else
|
||||
firstRun bool // if set to true, don't run any services except HTTP web inteface, and serve only first-run html
|
||||
firstRun bool // if set to true, don't run any services except HTTP web interface, and serve only first-run html
|
||||
pidFileName string // PID file name. Empty if no PID file was created.
|
||||
disableUpdate bool // If set, don't check for updates
|
||||
controlLock sync.Mutex
|
||||
|
||||
@@ -3,6 +3,7 @@ package home
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
@@ -10,7 +11,6 @@ import (
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/dnsforward"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/AdguardTeam/golibs/stringutil"
|
||||
uuid "github.com/satori/go.uuid"
|
||||
"howett.net/plist"
|
||||
)
|
||||
@@ -31,9 +31,8 @@ type dnsSettings struct {
|
||||
// DNSProtocol is not "TLS".
|
||||
ServerName string `plist:",omitempty"`
|
||||
|
||||
// ServerAddresses is a list of plain DNS server IP addresses used to
|
||||
// resolve the hostname in ServerURL or ServerName.
|
||||
ServerAddresses []string `plist:",omitempty"`
|
||||
// ServerAddresses is a list IP addresses of the server.
|
||||
ServerAddresses []net.IP `plist:",omitempty"`
|
||||
}
|
||||
|
||||
// payloadContent is a Device Management Profile payload.
|
||||
@@ -158,10 +157,19 @@ func handleMobileConfig(w http.ResponseWriter, r *http.Request, dnsp string) {
|
||||
}
|
||||
}
|
||||
|
||||
dnsIPs, err := collectDNSIPs()
|
||||
if err != nil {
|
||||
// Don't add a lot of formatting, since the error is already
|
||||
// wrapped by collectDNSIPs.
|
||||
respondJSONError(w, http.StatusInternalServerError, err.Error())
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
d := &dnsSettings{
|
||||
DNSProtocol: dnsp,
|
||||
ServerName: host,
|
||||
ServerAddresses: cloneBootstrap(),
|
||||
ServerAddresses: dnsIPs,
|
||||
}
|
||||
|
||||
mobileconfig, err := encodeMobileConfig(d, clientID)
|
||||
@@ -188,14 +196,6 @@ func handleMobileConfig(w http.ResponseWriter, r *http.Request, dnsp string) {
|
||||
_, _ = w.Write(mobileconfig)
|
||||
}
|
||||
|
||||
// cloneBootstrap returns a clone of the current bootstrap DNS servers.
|
||||
func cloneBootstrap() (bootstrap []string) {
|
||||
config.RLock()
|
||||
defer config.RUnlock()
|
||||
|
||||
return stringutil.CloneSlice(config.DNS.BootstrapDNS)
|
||||
}
|
||||
|
||||
func handleMobileConfigDoH(w http.ResponseWriter, r *http.Request) {
|
||||
handleMobileConfig(w, r, dnsProtoHTTPS)
|
||||
}
|
||||
@@ -203,3 +203,25 @@ func handleMobileConfigDoH(w http.ResponseWriter, r *http.Request) {
|
||||
func handleMobileConfigDoT(w http.ResponseWriter, r *http.Request) {
|
||||
handleMobileConfig(w, r, dnsProtoTLS)
|
||||
}
|
||||
|
||||
// collectDNSIPs returns a slice of IP addresses the server is listening
|
||||
// on, including the addresses on all interfaces in cases of unspecified IPs but
|
||||
// excluding loopback addresses.
|
||||
func collectDNSIPs() (ips []net.IP, err error) {
|
||||
// TODO(a.garipov): This really shouldn't be a function that parses
|
||||
// a list of strings. Instead, we need a function that returns this
|
||||
// data as []net.IP or []*netutil.IPPort. Maybe someday.
|
||||
addrs, err := collectDNSAddresses()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, addr := range addrs {
|
||||
ip := net.ParseIP(addr)
|
||||
if ip != nil && !ip.IsLoopback() {
|
||||
ips = append(ips, ip)
|
||||
}
|
||||
}
|
||||
|
||||
return ips, nil
|
||||
}
|
||||
|
||||
@@ -3,42 +3,41 @@ package home
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/dnsforward"
|
||||
"github.com/AdguardTeam/golibs/netutil"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"howett.net/plist"
|
||||
)
|
||||
|
||||
// testBootstrapDNS are the bootstrap plain DNS server addresses for tests.
|
||||
var testBootstrapDNS = []string{
|
||||
"94.140.14.14",
|
||||
"94.140.15.15",
|
||||
}
|
||||
|
||||
// setupBootstraps is a helper that sets up the bootstrap plain DNS server
|
||||
// configuration for tests and also tears it down in a cleanup function.
|
||||
func setupBootstraps(t testing.TB) {
|
||||
// setupDNSIPs is a helper that sets up the server IP address configuration for
|
||||
// tests and also tears it down in a cleanup function.
|
||||
func setupDNSIPs(t testing.TB) {
|
||||
t.Helper()
|
||||
|
||||
prevConfig := config
|
||||
prevTLS := Context.tls
|
||||
t.Cleanup(func() {
|
||||
config = prevConfig
|
||||
Context.tls = prevTLS
|
||||
})
|
||||
|
||||
config = &configuration{
|
||||
DNS: dnsConfig{
|
||||
FilteringConfig: dnsforward.FilteringConfig{
|
||||
BootstrapDNS: testBootstrapDNS,
|
||||
},
|
||||
BindHosts: []net.IP{netutil.IPv4Zero()},
|
||||
Port: defaultPortDNS,
|
||||
},
|
||||
}
|
||||
|
||||
Context.tls = &TLSMod{}
|
||||
}
|
||||
|
||||
func TestHandleMobileConfigDoH(t *testing.T) {
|
||||
setupBootstraps(t)
|
||||
setupDNSIPs(t)
|
||||
|
||||
t.Run("success", func(t *testing.T) {
|
||||
r, err := http.NewRequest(http.MethodGet, "https://example.com:12345/apple/doh.mobileconfig?host=example.org", nil)
|
||||
@@ -59,7 +58,7 @@ func TestHandleMobileConfigDoH(t *testing.T) {
|
||||
s := mc.PayloadContent[0].DNSSettings
|
||||
require.NotNil(t, s)
|
||||
|
||||
assert.Equal(t, testBootstrapDNS, s.ServerAddresses)
|
||||
assert.NotEmpty(t, s.ServerAddresses)
|
||||
assert.Empty(t, s.ServerName)
|
||||
assert.Equal(t, "https://example.org/dns-query", s.ServerURL)
|
||||
})
|
||||
@@ -105,14 +104,14 @@ func TestHandleMobileConfigDoH(t *testing.T) {
|
||||
s := mc.PayloadContent[0].DNSSettings
|
||||
require.NotNil(t, s)
|
||||
|
||||
assert.Equal(t, testBootstrapDNS, s.ServerAddresses)
|
||||
assert.NotEmpty(t, s.ServerAddresses)
|
||||
assert.Empty(t, s.ServerName)
|
||||
assert.Equal(t, "https://example.org/dns-query/cli42", s.ServerURL)
|
||||
})
|
||||
}
|
||||
|
||||
func TestHandleMobileConfigDoT(t *testing.T) {
|
||||
setupBootstraps(t)
|
||||
setupDNSIPs(t)
|
||||
|
||||
t.Run("success", func(t *testing.T) {
|
||||
r, err := http.NewRequest(http.MethodGet, "https://example.com:12345/apple/dot.mobileconfig?host=example.org", nil)
|
||||
@@ -133,7 +132,7 @@ func TestHandleMobileConfigDoT(t *testing.T) {
|
||||
s := mc.PayloadContent[0].DNSSettings
|
||||
require.NotNil(t, s)
|
||||
|
||||
assert.Equal(t, testBootstrapDNS, s.ServerAddresses)
|
||||
assert.NotEmpty(t, s.ServerAddresses)
|
||||
assert.Equal(t, "example.org", s.ServerName)
|
||||
assert.Empty(t, s.ServerURL)
|
||||
})
|
||||
@@ -180,7 +179,7 @@ func TestHandleMobileConfigDoT(t *testing.T) {
|
||||
s := mc.PayloadContent[0].DNSSettings
|
||||
require.NotNil(t, s)
|
||||
|
||||
assert.Equal(t, testBootstrapDNS, s.ServerAddresses)
|
||||
assert.NotEmpty(t, s.ServerAddresses)
|
||||
assert.Equal(t, "cli42.example.org", s.ServerName)
|
||||
assert.Empty(t, s.ServerURL)
|
||||
})
|
||||
|
||||
@@ -17,8 +17,8 @@ import (
|
||||
"github.com/kardianos/service"
|
||||
)
|
||||
|
||||
// TODO(a.garipov): Move shell templates into actual files. Either during the
|
||||
// v0.106.0 cycle using packr or during the following cycle using go:embed.
|
||||
// TODO(a.garipov): Consider moving the shell templates into actual files and
|
||||
// using go:embed instead of using large string constants.
|
||||
|
||||
const (
|
||||
launchdStdoutPath = "/var/log/AdGuardHome.stdout.log"
|
||||
@@ -313,18 +313,21 @@ func configureService(c *service.Config) {
|
||||
// POSIX / systemd
|
||||
|
||||
// Redirect stderr and stdout to files. Make sure we always restart.
|
||||
// Start only once network is up on Linux/systemd.
|
||||
c.Option["LogOutput"] = true
|
||||
c.Option["Restart"] = "always"
|
||||
c.Dependencies = []string{
|
||||
"After=syslog.target network-online.target",
|
||||
|
||||
// Start only once network is up on Linux/systemd.
|
||||
if runtime.GOOS == "linux" {
|
||||
c.Dependencies = []string{
|
||||
"After=syslog.target network-online.target",
|
||||
}
|
||||
}
|
||||
|
||||
// Use modified service file templates.
|
||||
// Use the modified service file templates.
|
||||
c.Option["SystemdScript"] = systemdScript
|
||||
c.Option["SysvScript"] = sysvScript
|
||||
|
||||
// On OpenWrt we're using a different type of sysvScript.
|
||||
// Use different scripts on OpenWrt and FreeBSD.
|
||||
if aghos.IsOpenWrt() {
|
||||
c.Option["SysvScript"] = openWrtScript
|
||||
} else if runtime.GOOS == "freebsd" {
|
||||
@@ -392,7 +395,7 @@ ConditionFileIsExecutable={{.Path|cmdEscape}}
|
||||
[Service]
|
||||
StartLimitInterval=5
|
||||
StartLimitBurst=10
|
||||
ExecStartPre=mkdir -p /var/log/
|
||||
ExecStartPre=/bin/mkdir -p /var/log/
|
||||
ExecStart={{.Path|cmdEscape}}{{range .Arguments}} {{.|cmd}}{{end}}
|
||||
{{if .ChRoot}}RootDirectory={{.ChRoot|cmd}}{{end}}
|
||||
{{if .WorkingDirectory}}WorkingDirectory={{.WorkingDirectory|cmdEscape}}{{end}}
|
||||
|
||||
@@ -47,7 +47,7 @@ is_little_endian() {
|
||||
# machine. The required software:
|
||||
#
|
||||
# curl
|
||||
# unzip (macOS) / tar (other unices)
|
||||
# unzip (macOS) / tar (other unixes)
|
||||
#
|
||||
check_required() {
|
||||
required_darwin="unzip"
|
||||
@@ -426,6 +426,8 @@ download() {
|
||||
then
|
||||
error_exit "cannot download the package from $url into $pkg_name"
|
||||
fi
|
||||
|
||||
log "successfully downloaded $pkg_name"
|
||||
}
|
||||
|
||||
# Function unpack unpacks the passed archive depending on it's extension.
|
||||
@@ -433,7 +435,7 @@ unpack() {
|
||||
log "unpacking package from $pkg_name into $out_dir"
|
||||
if ! mkdir -p "$out_dir"
|
||||
then
|
||||
error_exit "cannot create directory at the $out_dir"
|
||||
error_exit "cannot create directory $out_dir"
|
||||
fi
|
||||
|
||||
case "$pkg_ext"
|
||||
@@ -449,6 +451,8 @@ unpack() {
|
||||
;;
|
||||
esac
|
||||
|
||||
log "successfully unpacked, contents: $( echo; ls -l -A "$agh_dir" )"
|
||||
|
||||
rm "$pkg_name"
|
||||
}
|
||||
|
||||
@@ -507,6 +511,8 @@ install_service() {
|
||||
return 0
|
||||
fi
|
||||
|
||||
log "installation failed, removing $agh_dir"
|
||||
|
||||
rm -r "$agh_dir"
|
||||
|
||||
# Some devices detected to have armv7 CPU face the compatibility
|
||||
|
||||
Reference in New Issue
Block a user