all: sync with master

This commit is contained in:
Eugene Burkov
2025-07-10 17:55:28 +03:00
parent 7ceb213c56
commit ed12040673
71 changed files with 1351 additions and 905 deletions

View File

@@ -1,7 +1,7 @@
'name': 'build'
'env':
'GO_VERSION': '1.24.4'
'GO_VERSION': '1.24.5'
'NODE_VERSION': '20'
'on':

View File

@@ -1,7 +1,7 @@
'name': 'lint'
'env':
'GO_VERSION': '1.24.4'
'GO_VERSION': '1.24.5'
'on':
'push':

View File

@@ -9,11 +9,11 @@ The format is based on [*Keep a Changelog*](https://keepachangelog.com/en/1.0.0/
<!--
## [v0.108.0] TBA
## [v0.107.64] - 2025-06-15 (APPROX.)
## [v0.107.65] - 2025-07-30 (APPROX.)
See also the [v0.107.64 GitHub milestone][ms-v0.107.64].
See also the [v0.107.65 GitHub milestone][ms-v0.107.65].
[ms-v0.107.64]: https://github.com/AdguardTeam/AdGuardHome/milestone/99?closed=1
[ms-v0.107.65]: https://github.com/AdguardTeam/AdGuardHome/milestone/100?closed=1
NOTE: Add new changes BELOW THIS COMMENT.
-->
@@ -21,6 +21,25 @@ NOTE: Add new changes BELOW THIS COMMENT.
NOTE: Add new changes ABOVE THIS COMMENT.
-->
## [v0.107.64] - 2025-07-14
See also the [v0.107.64 GitHub milestone][ms-v0.107.64].
### Security
- Go version has been updated to prevent the possibility of exploiting the Go vulnerabilities fixed in [1.24.5][go-1.24.5].
### Fixed
- TTL override calculation ([#7903]).
- Validation process for DNSCrypt settings ([#7856]).
[#7856]: https://github.com/AdguardTeam/AdGuardHome/issues/7856
[#7903]: https://github.com/AdguardTeam/AdGuardHome/issues/7903
[go-1.24.5]: https://groups.google.com/g/golang-announce/c/gTNJnDXmn34
[ms-v0.107.64]: https://github.com/AdguardTeam/AdGuardHome/milestone/99?closed=1
## [v0.107.63] - 2025-06-26
See also the [v0.107.63 GitHub milestone][ms-v0.107.63].
@@ -3158,11 +3177,12 @@ See also the [v0.104.2 GitHub milestone][ms-v0.104.2].
[ms-v0.104.2]: https://github.com/AdguardTeam/AdGuardHome/milestone/28?closed=1
<!--
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.64...HEAD
[v0.107.64]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.63...v0.107.64
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.65...HEAD
[v0.107.65]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.64...v0.107.65
-->
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.63...HEAD
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.64...HEAD
[v0.107.64]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.63...v0.107.64
[v0.107.63]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.62...v0.107.63
[v0.107.62]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.61...v0.107.62
[v0.107.61]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.60...v0.107.61

View File

@@ -27,7 +27,7 @@ DIST_DIR = dist
GOAMD64 = v1
GOPROXY = https://proxy.golang.org|direct
GOTELEMETRY = off
GOTOOLCHAIN = go1.24.4
GOTOOLCHAIN = go1.24.5
GPG_KEY = devteam@adguard.com
GPG_KEY_PASSPHRASE = not-a-real-password
NPM = npm

View File

@@ -8,7 +8,7 @@
'variables':
'channel': 'edge'
'dockerFrontend': 'adguard/home-js-builder:3.1'
'dockerGo': 'adguard/go-builder:1.24.4--1'
'dockerGo': 'adguard/go-builder:1.24.5--1'
'stages':
- 'Build frontend':
@@ -279,7 +279,7 @@
'variables':
'channel': 'beta'
'dockerFrontend': 'adguard/home-js-builder:3.1'
'dockerGo': 'adguard/go-builder:1.24.4--1'
'dockerGo': 'adguard/go-builder:1.24.5--1'
# release-vX.Y.Z branches are the branches from which the actual final
# release is built.
- '^release-v[0-9]+\.[0-9]+\.[0-9]+':
@@ -295,4 +295,4 @@
'variables':
'channel': 'release'
'dockerFrontend': 'adguard/home-js-builder:3.1'
'dockerGo': 'adguard/go-builder:1.24.4--1'
'dockerGo': 'adguard/go-builder:1.24.5--1'

View File

@@ -6,7 +6,7 @@
'name': 'AdGuard Home - Build and run tests'
'variables':
'dockerFrontend': 'adguard/home-js-builder:3.1'
'dockerGo': 'adguard/go-builder:1.24.4--1'
'dockerGo': 'adguard/go-builder:1.24.5--1'
'channel': 'development'
'stages':
@@ -234,5 +234,5 @@
# may need to build a few of these.
'variables':
'dockerFrontend': 'adguard/home-js-builder:3.1'
'dockerGo': 'adguard/go-builder:1.24.4--1'
'dockerGo': 'adguard/go-builder:1.24.5--1'
'channel': 'candidate'

View File

@@ -291,7 +291,7 @@
"custom_ip": "Свой IP",
"blocking_ipv4": "Блакаванне IPv4",
"blocking_ipv6": "Блакаванне IPv6",
"blocked_response_ttl": "TTL заблакаванага адказу",
"blocked_response_ttl": "Заблакіраваны адказ TTL",
"blocked_response_ttl_desc": "Паказвае, на працягу колькіх секунд кліенты павінны кэшаваць адфільтраваць адказ",
"form_enter_blocked_response_ttl": "Увядзіце TTL заблакіраванага адказу (у секундах)",
"upstream_timeout": "Час чакання для upstream.",

View File

@@ -224,11 +224,11 @@
"example_upstream_regular": "obvyklý DNS (přes UDP);",
"example_upstream_regular_port": "obvyklý DNS (skrze UDP, s portem);",
"example_upstream_udp": "obvyklý DNS (skrze UDP, název hostitele);",
"example_upstream_dot": "šifrovaný <0>DNS skrze TLS</0>;",
"example_upstream_doh": "šifrovaný <0>DNS skrze HTTPS</0>;",
"example_upstream_doh3": "šifrovaný DNS skrze HTTPS s vynuceným <0>HTTP/3</0> a bez možnosti zpětného přechodu na HTTP/2 nebo nižší;",
"example_upstream_doq": "šifrovaný <0>DNS skrze QUIC</0>;",
"example_upstream_sdns": "<0>DNS razítka</0> pro <1>DNSCrypt</1> nebo <2>DNS skrze HTTPS</2> řešitele;",
"example_upstream_dot": "šifrovaný <0>DNS-over-TLS</0>;",
"example_upstream_doh": "šifrovaný <0>DNS-over-HTTPS</0>;",
"example_upstream_doh3": "šifrovaný DNS-over-HTTPS s vynuceným <0>HTTP/3</0> a bez možnosti zpětného přechodu na HTTP/2 nebo nižší;",
"example_upstream_doq": "šifrovaný <0>DNS-over-QUIC</0>;",
"example_upstream_sdns": "<0>DNS razítka</0> pro <1>DNSCrypt</1> nebo <2>DNS-over-HTTPS</2> řešitele;",
"example_upstream_tcp": "obvyklý DNS (přes TCP);",
"example_upstream_tcp_port": "obvyklý DNS (skrze TCP, s portem);",
"example_upstream_tcp_hostname": "obvyklý DNS (skrze TCP, název hostitele);",
@@ -298,14 +298,14 @@
"upstream_timeout_desc": "Určuje počet sekund čekání na odpověď od odchozího serveru",
"form_enter_upstream_timeout": "Zadejte dobu časového limitu odchozího serveru v sekundách",
"dnscrypt": "DNSCrypt",
"dns_over_https": "DNS skrze HTTPS",
"dns_over_tls": "DNS skrze TLS",
"dns_over_quic": "DNS skrze QUIC",
"dns_over_https": "DNS-over-HTTPS",
"dns_over_tls": "DNS-over-TLS",
"dns_over_quic": "DNS-over-QUIC",
"client_id": "ID klienta",
"client_id_placeholder": "Zadejte ID klienta",
"client_id_desc": "Klienty lze identifikovat pomocí ID klienta. <a>Zde</a> se můžete dozvědět více o tom, jak klienty identifikovat.",
"download_mobileconfig_doh": "Stáhnout .mobileconfig pro DNS skrze HTTPS",
"download_mobileconfig_dot": "Stáhnout .mobileconfig pro DNS skrze TLS",
"download_mobileconfig_doh": "Stáhnout .mobileconfig pro DNS-over-HTTPS",
"download_mobileconfig_dot": "Stáhnout .mobileconfig pro DNS-over-TLS",
"download_mobileconfig": "Stáhnout konfigurační soubor",
"plain_dns": "Běžný DNS",
"form_enter_rate_limit": "Zadejte rychlostní limit",
@@ -405,11 +405,11 @@
"encryption_redirect": "Automaticky přesměrovat na HTTPS",
"encryption_redirect_desc": "Pokud je zaškrtnuto, AdGuard Home vás automaticky přesměruje z adres HTTP na HTTPS.",
"encryption_https": "HTTPS port",
"encryption_https_desc": "Pokud je nakonfigurován port HTTPS, AdGuard Home administrátorské rozhraní bude přístupné přes HTTPS a bude také poskytovat DNS skrze HTTPS na '/dns-query'.",
"encryption_dot": "DNS skrze TLS port",
"encryption_dot_desc": "Pokud je tento port nakonfigurován, AdGuard Home bude na tomto portu spouštět DNS skrze TLS server.",
"encryption_doq": "Port DNS skrze QUIC",
"encryption_doq_desc": "Pokud je tento port nakonfigurován, AdGuard Home bude na tomto portu spouštět DNS skrze QUIC server.",
"encryption_https_desc": "Pokud je nakonfigurován port HTTPS, AdGuard Home administrátorské rozhraní bude přístupné přes HTTPS a bude také poskytovat DNS-over-HTTPS na '/dns-query'.",
"encryption_dot": "DNS-over-TLS port",
"encryption_dot_desc": "Pokud je tento port nakonfigurován, AdGuard Home bude na tomto portu spouštět DNS-over-TLS server.",
"encryption_doq": "DNS-over-QUIC port",
"encryption_doq_desc": "Pokud je tento port nakonfigurován, AdGuard Home bude na tomto portu spouštět DNS-over-QUIC server.",
"encryption_certificates": "Certifikáty",
"encryption_certificates_desc": "Chcete-li používat šifrování, musíte pro svou doménu poskytnout platný řetězec certifikátů SSL. Certifikát můžete získat bezplatně na adrese <0>{{link}}</ 0>, nebo jej můžete zakoupit od jednoho z důvěryhodných certifikačních úřadů.",
"encryption_certificates_input": "Zde můžete nakopírovat/vložit certifikáty PEM.",
@@ -417,8 +417,8 @@
"encryption_expire": "Vyprší",
"encryption_key": "Osobní kód",
"encryption_key_input": "Zde můžete nakopírovat/vložit soukromý klíč k certifikátu PEM.",
"encryption_enable": "Povolit šifrování (HTTPS, DNS skrze HTTPS a DNS skrze TLS)",
"encryption_enable_desc": "Pokud je šifrování zapnuto, administrátorské rozhraní AdGuard Home bude pracovat skrze HTTPS a DNS server bude naslouchat požadavky přes DNS skrze HTTPS a DNS skrze TLS.",
"encryption_enable": "Povolit šifrování (HTTPS, DNS-over-HTTPS a DNS-over-TLS)",
"encryption_enable_desc": "Pokud je šifrování zapnuto, administrátorské rozhraní AdGuard Home bude pracovat skrze HTTPS a DNS server bude naslouchat požadavky přes DNS-over-HTTPS a DNS-over-TLS.",
"encryption_chain_valid": "Certifikační řetězec je platný",
"encryption_chain_invalid": "Certifikační řetězec je neplatný",
"encryption_key_valid": "Toto je platný {{type}} osobní klíč",
@@ -494,23 +494,23 @@
"check_updates_now": "Zkontrolovat aktualizace nyní",
"version_request_error": "Kontrola aktualizace se nezdařila. Zkontrolujte prosím připojení k Internetu.",
"dns_privacy": "Soukromí DNS",
"setup_dns_privacy_1": "<0>DNS skrze TLS:</0> Použít <1>{{address}}</1> řetězec.",
"setup_dns_privacy_2": "<0>DNS skrze HTTPS:</0> Použít <1>{{address}}</1> řetězec.",
"setup_dns_privacy_1": "<0>DNS-over-TLS:</0> Použít <1>{{address}}</1> řetězec.",
"setup_dns_privacy_2": "<0>DNS-over-HTTPS:</0> Použít <1>{{address}}</1> řetězec.",
"setup_dns_privacy_3": "<0>Zde je seznam softwaru, který můžete použít.</0>",
"setup_dns_privacy_4": "Na zařízení se systémem iOS 14 nebo macOS Big Sur si můžete stáhnout speciální soubor '.mobileconfig', který do nastavení DNS přidává servery <highlight>DNS skrze HTTPS</highlight> nebo <highlight> DNS skrze TLS</highlight>.",
"setup_dns_privacy_android_1": "Android 9 podporuje DNS skrze TLS nativně. Pokud ho chcete konfigurovat, přejděte na Nastavení → Síť & internet → Pokročilé → Soukromé DNS a tam zadejte název vaší domény.",
"setup_dns_privacy_android_2": "<0>AdGuard pro Android</0> podporuje <1>DNS skrze HTTPS</1> a <1>DNS skrze LS</1>.",
"setup_dns_privacy_android_3": "<0>Intra</0> přidává podporu <1>DNS skrze HTTPS</1> pro Android.",
"setup_dns_privacy_ios_1": "<0>DNSCloak</0> podporuje funkci <1>DNS skrze HTTPS</1>, ale abyste ji mohli nakonfigurovat pro používání vlastního serveru, musíte vygenerovat značku <2>DNS Stamp</2>.",
"setup_dns_privacy_ios_2": "<0>AdGuard pro iOS</0> podporuje nastavení <1>DNS skrze HTTPS</1> a <1>DNS skrze TLS</1>.",
"setup_dns_privacy_4": "Na zařízení se systémem iOS 14 nebo macOS Big Sur si můžete stáhnout speciální soubor '.mobileconfig', který do nastavení DNS přidává servery <highlight>DNS-over-HTTPS</highlight> nebo <highlight> DNS-over-TLS</highlight>.",
"setup_dns_privacy_android_1": "Android 9 podporuje DNS-over-TLS nativně. Pokud ho chcete konfigurovat, přejděte na Nastavení → Síť a internet → Pokročilé → Soukromé DNS a tam zadejte název vaší domény.",
"setup_dns_privacy_android_2": "<0>AdGuard pro Android</0> podporuje <1>DNS-over-HTTPS</1> a <1>DNS-over-TLS</1>.",
"setup_dns_privacy_android_3": "<0>Intra</0> přidává podporu <1>DNS-over-HTTPS</1> pro Android.",
"setup_dns_privacy_ios_1": "<0>DNSCloak</0> podporuje funkci <1>DNS-over-HTTPS</1>, ale abyste ji mohli nakonfigurovat pro používání vlastního serveru, musíte vygenerovat značku <2>DNS Stamp</2>.",
"setup_dns_privacy_ios_2": "<0>AdGuard pro iOS</0> podporuje nastavení <1>DNS-over-HTTPS</1> a <1>DNS-over-TLS</1>.",
"setup_dns_privacy_other_title": "Další implementace",
"setup_dns_privacy_other_1": "Samotný AdGuard Home může být bezpečným klientem DNS na jakékoli platformě.",
"setup_dns_privacy_other_2": "<0>dnsproxy</0> podporuje všechny známé bezpečné DNS protokoly.",
"setup_dns_privacy_other_3": "<0>dnscrypt-proxy</0> podporuje <1>DNS skrze HTTPS</1>.",
"setup_dns_privacy_other_4": "<0>Mozilla Firefox</0> podporuje <1>DNS skrze HTTPS</1>.",
"setup_dns_privacy_other_3": "<0>dnscrypt-proxy</0> podporuje <1>DNS-over-HTTPS</1>.",
"setup_dns_privacy_other_4": "<0>Mozilla Firefox</0> podporuje <1>DNS-over-HTTPS</1>.",
"setup_dns_privacy_other_5": "Další implementace naleznete <0>zde</0> a <1>zde</1>.",
"setup_dns_privacy_ioc_mac": "Konfigurace pro iOS a macOS",
"setup_dns_notice": "Pro použití <1>DNS skrze HTTPS</1> nebo <1>DNS skrze TLS</1> potřebujete v nastaveních AdGuard Home <0>nakonfigurovat šifrování</0>.",
"setup_dns_notice": "Pro použití <1>DNS-over-HTTPS</1> nebo <1>DNS-over-TLS</1> potřebujete v nastaveních AdGuard Home <0>nakonfigurovat šifrování</0>.",
"rewrite_added": "Přesměrování DNS pro „{{key}}“ úspěšně přidáno",
"rewrite_deleted": "Přesměrování DNS pro „{{key}}“ úspěšně smazáno",
"rewrite_updated": "Přesměrování DNS bylo úspěšně aktualizováno",

View File

@@ -1,24 +1,24 @@
{
"client_settings": "İstemci ayarları",
"example_upstream_reserved": "<0>belirli alan adları</0> için bir üst sunucusu;",
"example_multiple_upstreams_reserved": "<0>belirli alanlar için</0> birden fazla üst kaynaklar;",
"example_upstream_reserved": "<0>belirli alan adları için</0> bir üst kaynak;",
"example_multiple_upstreams_reserved": "<0>belirli alan adları için</0> birden fazla üst kaynak;",
"example_upstream_comment": "bir yorum.",
"upstream_parallel": "Tüm üst sunucuları eş zamanlı sorgulayarak çözümlemeyi hızlandırmak için paralel sorgular kullanın.",
"parallel_requests": "Paralel istekler",
"upstream_parallel": "Tüm üst kaynak sunucuları aynı anda sorgulayarak çözümlemeyi hızlandırır.",
"parallel_requests": "Eş zamanlı sorgu",
"load_balancing": "Yük dengeleme",
"load_balancing_desc": "Üst kaynak sunucuları aynı anda sorgulanır.<br/>AdGuard Home, en düşük başarısız sorgu sayısına ve en düşük ortalama sorgu süresine sahip sunucuları seçmek için ağırlıklı rastgele algoritma kullanır.",
"bootstrap_dns": "DNS Önyükleme sunucuları",
"bootstrap_dns": "Ön yükleme DNS sunucuları",
"bootstrap_dns_desc": "Üst kaynak olarak belirttiğiniz DoH/DoT çözümleyicilerin IP adreslerini çözümlemek için kullanılan DNS sunucularının IP adresleri. Yorumlara izin verilmez.",
"fallback_dns_title": "Yedek DNS sunucuları",
"fallback_dns_desc": "Yukarı akış DNS sunucuları yanıt vermediğinde kullanılan yedek DNS sunucularının listesi. Söz dizimi yukarıdaki ana üst kaynak alanıyla aynıdır.",
"fallback_dns_placeholder": "Her satıra bir yedek DNS sunucusu girin",
"local_ptr_title": "Özel ters DNS sunucuları",
"local_ptr_desc": "AdGuard Home tarafından özel PTR, SOA ve NS istekleri için kullanılan DNS sunucuları. Bir istek, özel IP aralıkları (\"192.168.12.34\" gibi) içinde bir alt ağ içeren bir ARPA alan adı ister ve özel IP adresine sahip bir istemciden gelirse özel olarak kabul edilir. Ayarlanmadığı durumda AdGuard Home, IP adresleri dışında işletim sisteminizin varsayılan DNS çözümleyicileri kullanılır.",
"local_ptr_desc": "AdGuard Home tarafından özel PTR, SOA ve NS istekleri için kullanılan DNS sunucuları. Bir istek, özel IP aralıklarında (örneğin \"192.168.12.34\" gibi) bir alt ağ içeren bir ARPA alanı soruyorsa ve özel bir IP adresine sahip bir istemciden geliyorsa özel kabul edilir. Ayarlanmadığı durumda AdGuard Home IP adresleri hariç, işletim sisteminizin varsayılan DNS çözümleyicileri kullanılır.",
"local_ptr_default_resolver": "AdGuard Home, varsayılan olarak aşağıdaki ters DNS çözümleyicilerini kullanır: {{ip}}.",
"local_ptr_no_default_resolver": "AdGuard Home, bu sistem için uygun olan özel ters DNS çözümleyicilerini belirleyemedi.",
"local_ptr_placeholder": "Her satıra bir IP adresi girin",
"resolve_clients_title": "İstemcilerin IP adreslerinin ters çözümlenmesini etkinleştir",
"resolve_clients_desc": "Karşılık gelen çözümleyicilere (yerel istemciler için özel DNS sunucuları, genel IP adresleri olan istemciler için üst sunucuları) PTR sorguları göndererek istemcilerin IP adreslerini ana makine adlarının tersine çözün.",
"resolve_clients_desc": "Belirtilen çözümleyicilere (yerel istemciler için özel DNS sunucuları, genel IP adresi olan istemciler için üst kaynak sunucuları) PTR sorguları göndererek istemcilerin IP adreslerinin ana makine adlarına tersine çözülmesini sağlar.",
"use_private_ptr_resolvers_title": "Özel ters DNS çözümleyicileri kullan",
"use_private_ptr_resolvers_desc": "Özel üst kaynak sunucuları, DHCP, /etc/hosts, vb. aracılığıyla özel IP adresleri içeren ARPA alan adları için PTR, SOA ve NS isteklerini çözümleyin. Devre dışı bırakılırsa, AdGuard Home bu tür tüm isteklere NXDOMAIN ile yanıt verir.",
"check_dhcp_servers": "DHCP sunucularını denetle",
@@ -71,7 +71,7 @@
"dhcp_warning": "DHCP sunucusunu yine de etkinleştirmek istiyorsanız, ağınızda başka bir aktif DHCP sunucusu olmadığından emin olun, aksi takdirde ağa bağlı cihazların internet bağlantısı kesilebilir!",
"dhcp_error": "AdGuard Home, ağda başka bir etkin DHCP sunucusu olup olmadığını belirleyemedi",
"dhcp_static_ip_error": "DHCP sunucusunu kullanmak için sabit bir IP adresi ayarlanmalıdır. AdGuard Home, bu ağ arayüzünün sabit bir IP adresi kullanılarak yapılandırılıp yapılandırılmadığını belirleyemedi. Lütfen sabit IP adresini elle ayarlayın.",
"dhcp_dynamic_ip_found": "Sisteminiz, <0>{{interfaceName}}</0> arayüzü için dinamik IP adresi yapılandırması kullanıyor. DHCP sunucusunu kullanmak için sabit bir IP adresi ayarlanmalıdır. Geçerli olan IP adresiniz <0>{{ipAddress}}</0>. \"DHCP sunucusunu etkinleştir\" düğmesine basarsanız, AdGuard Home bu IP adresini otomatik bir şekilde sabit olarak ayarlayacaktır.",
"dhcp_dynamic_ip_found": "Sisteminiz, <0>{{interfaceName}}</0> arayüzü için değişebilen IP adresi yapılandırması kullanıyor. DHCP sunucusunu kullanmak için sabit bir IP adresi ayarlanmalıdır. Geçerli olan IP adresiniz <0>{{ipAddress}}</0>. \"DHCP sunucusunu etkinleştir\" düğmesine basarsanız, AdGuard Home bu IP adresini otomatik bir şekilde sabit olarak ayarlar.",
"dhcp_lease_added": "Sabit kiralama \"{{key}}\" başarıyla eklendi",
"dhcp_lease_deleted": "Sabit kiralama \"{{key}}\" başarıyla silindi",
"dhcp_lease_updated": "Statik kiralama \"{{key}}\" başarıyla güncellendi",
@@ -122,8 +122,8 @@
"stats_query_domain": "Başlıca sorgulanan alan adları",
"for_last_hours": "son {{count}} saat için",
"for_last_hours_plural": "son {{count}} saat için",
"for_last_days": "son {{count}} gün boyunca",
"for_last_days_plural": "son {{count}} gün boyunca",
"for_last_days": "son {{count}} gün için",
"for_last_days_plural": "son {{count}} gün için",
"stats_disabled": "İstatistikler devre dışı bırakıldı. Bunu, <0>ayarlar sayfasından</0> etkinleştirebilirsiniz.",
"stats_disabled_short": "İstatistikler devre dışı bırakıldı",
"no_domains_found": "Alan adı bulunamadı",
@@ -134,10 +134,10 @@
"general_statistics": "Genel istatistikler",
"top_upstreams": "Başlıca üst kaynaklar",
"no_upstreams_data_found": "Üst kaynak verisi bulunamadı",
"number_of_dns_query_days": "Son {{count}} gün boyunca işlenen DNS sorgularının sayısı",
"number_of_dns_query_days_plural": "Son {{count}} gün boyunca işlenen DNS sorgularının sayısı",
"number_of_dns_query_hours": "Son {{count}} saat için işlenen DNS sorgularının sayısı",
"number_of_dns_query_hours_plural": "Son {{count}} saatiçin işlenen DNS sorgularının sayısı",
"number_of_dns_query_days": "Son {{count}} gün içinde işlenen DNS sorgularının sayısı",
"number_of_dns_query_days_plural": "Son {{count}} gün içinde işlenen DNS sorgularının sayısı",
"number_of_dns_query_hours": "Son {{count}} saat içinde işlenen DNS sorgularının sayısı",
"number_of_dns_query_hours_plural": "Son {{count}} saat içinde işlenen DNS sorgularının sayısı",
"number_of_dns_query_blocked_24_hours": "Reklam engelleme filtreleri ve hosts engel listeleri tarafından engellenen DNS isteklerinin sayısı",
"number_of_dns_query_blocked_24_hours_by_sec": "AdGuard gezinti koruması modülü tarafından engellenen DNS isteklerinin sayısı",
"number_of_dns_query_blocked_24_hours_adult": "Engellenen yetişkin içerikli sitelerin sayısı",
@@ -165,10 +165,10 @@
"custom_filtering_rules": "Özel filtreleme kuralları",
"encryption_settings": "Şifreleme ayarları",
"dhcp_settings": "DHCP ayarları",
"upstream_dns": "Üst DNS sunucusu",
"upstream_dns": "Üst kaynak DNS sunucusu",
"upstream_dns_help": "Her satıra bir sunucu adresi girin. Üst DNS sunucularını yapılandırma hakkında <a>daha fazla bilgi edinin</a>.",
"upstream_dns_configured_in_file": "{{path}} dosyasında yapılandırıldı",
"test_upstream_btn": "Üst sunucuyu test et",
"test_upstream_btn": "Üst kaynakları test et",
"upstreams": "Üst kaynak",
"upstream": "Üst kaynak",
"apply_btn": "Uygula",
@@ -198,7 +198,7 @@
"add_allowlist": "İzin listesi ekle",
"cancel_btn": "İptal",
"enter_name_hint": "Ad girin",
"enter_url_or_path_hint": "Listenin URL adresini veya dosya yolunu girin",
"enter_url_or_path_hint": "Listenin URL'sini veya dosya yolunu girin",
"check_updates_btn": "Güncellemeleri denetle",
"new_blocklist": "Yeni engel listesi",
"new_allowlist": "Yeni izin listesi",
@@ -208,8 +208,8 @@
"choose_allowlist": "İzin listelerini seçin",
"enter_valid_blocklist": "Engel listesine geçerli bir URL girin.",
"enter_valid_allowlist": "İzin listesine geçerli bir URL girin.",
"form_error_url_format": "Geçersiz URL biçimi",
"form_error_url_or_path_format": "Geçersiz URL adresi veya dosya yolu",
"form_error_url_format": "URL biçimi geçersiz",
"form_error_url_or_path_format": "Listenin URL'si veya dosya konumu geçersiz",
"custom_filter_rules": "Özel filtreleme kuralları",
"custom_filter_rules_hint": "Her satıra bir kural girin. Reklam engelleme kuralı veya hosts dosyası söz dizimi kullanabilirsiniz.",
"system_host_files": "Sistem hosts dosyaları",
@@ -232,7 +232,7 @@
"example_upstream_tcp": "normal DNS (TCP üzerinden);",
"example_upstream_tcp_port": "normal DNS (TCP üzerinden, bağlantı noktası ile);",
"example_upstream_tcp_hostname": "normal DNS (TCP üzerinden, ana makine adı);",
"all_lists_up_to_date_toast": "Tüm listeler güncel durumda",
"all_lists_up_to_date_toast": "Tüm listeler güncel",
"updated_upstream_dns_toast": "Üst sunucular başarıyla kaydedildi",
"dns_test_ok_toast": "Belirtilen DNS sunucuları düzgün çalışıyor",
"dns_test_not_ok_toast": "Sunucu \"{{key}}\": kullanılamıyor, lütfen doğru yazdığınızdan emin olun",
@@ -272,12 +272,12 @@
"query_log_cleared": "Sorgu günlüğü başarıyla temizlendi",
"query_log_updated": "Sorgu günlüğü başarıyla güncellendi",
"query_log_clear": "Sorgu günlüklerini temizle",
"query_log_retention": "Sorgu günlükleri rotasyonu",
"query_log_retention": "Sorgu günlüğü döngüsü",
"query_log_enable": "Günlüğü etkinleştir",
"query_log_configuration": "Günlük yapılandırması",
"query_log_disabled": "Sorgu günlüğü devre dışı bırakıldı, bunu <0>ayarlar</0> kısmından yapılandırılabilirsiniz",
"query_log_strict_search": "Tam arama için çift tırnak işareti kullanın",
"query_log_retention_confirm": "Sorgu günlüğü rotasyonunu değiştirmek istediğinizden emin misiniz? Aralık değerini düşürürseniz, bazı veriler kaybolacaktır.",
"query_log_retention_confirm": "Sorgu günlüğü döngüsünü değiştirmek istediğinizden emin misiniz? Aralık değerini düşürürseniz, bazı veriler kaybolacaktır",
"anonymize_client_ip": "İstemcinin IP adresini gizle",
"anonymize_client_ip_desc": "İstemcinin tam IP adresini günlüklere veya istatistiklere kaydetmez",
"dns_config": "DNS sunucu yapılandırması",
@@ -292,7 +292,7 @@
"blocking_ipv4": "IPv4 engelleme",
"blocking_ipv6": "IPv6 engelleme",
"blocked_response_ttl": "Engellenen yanıtın geçerlilik süresi",
"blocked_response_ttl_desc": "İstemcilerin filtrelenmiş bir yanıtı kaç saniye süreyle önbelleğe alması gerektiğini belirtir",
"blocked_response_ttl_desc": "İstemcilerin filtrelenmiş bir yanıtı kaç saniye boyunca önbellekte tutması gerektiğini belirtir",
"form_enter_blocked_response_ttl": "Engellenen yanıt kullanım süresini girin (saniye)",
"upstream_timeout": "Üst kaynak zaman aşımı",
"upstream_timeout_desc": "Üst kaynak sunucusundan yanıt almak için kaç saniye bekleneceğini belirtir",
@@ -303,7 +303,7 @@
"dns_over_quic": "DNS-over-QUIC",
"client_id": "İstemci Kimliği",
"client_id_placeholder": "İstemci kimliği girin",
"client_id_desc": "İstemciler, İstemci Kimliği ile tanımlanabilir. İstemcileri nasıl tanımlayacağınız hakkında daha fazla bilgiyi <a>buradan</a> öğrenin.",
"client_id_desc": "İstemciler, İstemci Kimliği ile tanımlanabilir. İstemcileri nasıl tanımlayacağınız hakkında daha fazla bilgiye <a>buradan</a> ulaşabilirsiniz.",
"download_mobileconfig_doh": "DNS-over-HTTPS için .mobileconfig dosyasını indir",
"download_mobileconfig_dot": "DNS-over-TLS için .mobileconfig dosyasını indir",
"download_mobileconfig": "Yapılandırma dosyasını indir",
@@ -327,11 +327,11 @@
"rate_limit_whitelist_placeholder": "Her satıra bir IP adresi girin",
"blocking_ipv4_desc": "Engellenen bir A isteği için geri döndürülecek IP adresi",
"blocking_ipv6_desc": "Engellenen bir AAAA isteği için geri döndürülecek IP adresi",
"blocking_mode_default": "Varsayılan: Reklam engelleme stili kuralı tarafından engellendiğinde sıfır IP adresiyle (A için 0.0.0.0; :: AAAA için) yanıt verin; /etc/hosts-tarzı kural tarafından engellendiğinde, kuralda belirtilen IP adresiyle yanıt verin",
"blocking_mode_default": "Varsayılan: Reklam engelleyici tarzı kural tarafından engellendiğinde sıfır IP adresiyle (A için 0.0.0.0; AAAA için ::) yanıt verir; /etc/hosts tarzı kural tarafından engellendiğinde, kuralda belirtilen IP adresiyle yanıt verir",
"blocking_mode_refused": "REFUSED: REFUSED koduyla yanıt verin",
"blocking_mode_nxdomain": "NXDOMAIN: NXDOMAIN koduyla yanıt verin",
"blocking_mode_null_ip": "Boş IP: Sıfır IP adresiyle yanıt verin (A için 0.0.0.0; :: AAAA için)",
"blocking_mode_custom_ip": "Özel IP: El ile ayarlanmış bir IP adresiyle yanıt verin",
"blocking_mode_null_ip": "Boş IP: Sıfır IP adresiyle yanıt verin (A için 0.0.0.0; AAAA için ::)",
"blocking_mode_custom_ip": "Özel IP: Elle ayarlanmış IP adresiyle yanıt verin",
"theme_auto": "Otomatik",
"theme_light": "Açık",
"theme_dark": "Koyu",
@@ -365,7 +365,7 @@
"install_devices_title": "Cihazlarınızı yapılandırın",
"install_devices_desc": "AdGuard Home'u kullanmaya başlamak için, cihazlarınızı onu kullanacak şekilde yapılandırmanız gerekir.",
"install_submit_title": "Tebrikler!",
"install_submit_desc": "Yükleme işlemi tamamlandı ve artık AdGuard Home'u kullanmaya hazırsınız.",
"install_submit_desc": "Kurulum işlemi tamamlandı ve artık AdGuard Home'u kullanmaya hazırsınız.",
"install_devices_router": "Yönlendirici",
"install_devices_router_desc": "Bu kurulum, ev yönlendiricinize bağlı tüm cihazları otomatik olarak kapsar ve her birini elle yapılandırmanıza gerek yoktur.",
"install_devices_address": "AdGuard Home DNS sunucusu aşağıdaki adresleri dinliyor",
@@ -378,7 +378,7 @@
"install_devices_windows_list_3": "Panelin solunda \"Bağdaştırıcı ayarlarını değiştirin\" öğesine tıklayın.",
"install_devices_windows_list_4": "Kullandığınız aktif bağlantının üzerine sağ tıklayın ve Özellikler öğesine tıklayın.",
"install_devices_windows_list_5": "Listede \"İnternet Protokolü Sürüm 4 (TCP/IPv4)\" (veya IPv6 için \"İnternet Protokolü Sürüm 6 (TCP/IPv6)\") öğesini bulun, seçin ve ardından tekrar Özellikler öğesine tıklayın.",
"install_devices_windows_list_6": "\"Aşağıdaki DNS sunucu adreslerini kullan\"ı seçin ve AdGuard Home sunucu adreslerinizi girin.",
"install_devices_windows_list_6": "\"Aşağıdaki DNS sunucu adreslerini kullan\" seçeneğini seçin ve ardından AdGuard Home sunucunuzun adreslerini girin.",
"install_devices_macos_list_1": "Apple simgesine tıklayın ve Sistem Tercihleri öğesine gidin.",
"install_devices_macos_list_2": "Ağ öğesine tıklayın.",
"install_devices_macos_list_3": "Listedeki ilk bağlantıyı seçin ve Gelişmiş öğesine tıklayın.",
@@ -402,7 +402,7 @@
"encryption_server": "Sunucu adı",
"encryption_server_enter": "Alan adınızı girin",
"encryption_server_desc": "Ayarlanırsa, AdGuard Home ClientID'leri algılar, DDR sorgularına yanıt verir ve ek bağlantı doğrulamaları gerçekleştirir. Ayarlanmazsa, bu özellikler devre dışı bırakılır. Sertifikadaki DNS Adlarından biriyle eşleşmelidir.",
"encryption_redirect": "Otomatik olarak HTTPS'e yönlendir",
"encryption_redirect": "HTTPS'e otomatik olarak yönlendir",
"encryption_redirect_desc": "İşaretlenirse, AdGuard Home sizi otomatik olarak HTTP adresinden HTTPS adreslerine yönlendirir.",
"encryption_https": "HTTPS bağlantı noktası",
"encryption_https_desc": "HTTPS bağlantı noktası yapılandırılırsa, AdGuard Home yönetici arayüzüne HTTPS aracılığıyla erişilebilir olacak ve ayrıca '/dns-query' üzerinden DNS-over-HTTPS bağlantısı sağlar.",
@@ -420,7 +420,7 @@
"encryption_enable": "Şifrelemeyi etkinleştir (HTTPS, DNS-over-HTTPS ve DNS-over-TLS)",
"encryption_enable_desc": "Şifrelemeyi etkinleştirirseniz, AdGuard Home yönetici arayüzü HTTPS üzerinden çalışır ve DNS sunucusu, DNS-over-HTTPS ve DNS-over-TLS üzerinden gelen istekleri dinler.",
"encryption_chain_valid": "Sertifika zinciri geçerli",
"encryption_chain_invalid": "Sertifika zinciri geçersiz.",
"encryption_chain_invalid": "Sertifika zinciri geçersiz",
"encryption_key_valid": "Bu geçerli bir {{type}} özel anahtarıdır",
"encryption_key_invalid": "Bu geçersiz bir {{type}} özel anahtarıdır",
"encryption_subject": "Konu",
@@ -429,7 +429,7 @@
"encryption_reset": "Şifreleme ayarlarını sıfırlamak istediğinizden emin misiniz?",
"encryption_warning": "Uyarı",
"encryption_plain_dns_enable": "Düz DNS'i etkinleştir",
"encryption_plain_dns_desc": "Düz DNS varsayılan olarak etkindir. Tüm aygıtları şifrelenmiş DNS kullanmaya zorlamak için bunu devre dışı bırakabilirsiniz. Bunu yapmak için en az bir şifrelenmiş DNS protokolünü etkinleştirmeniz gerekir",
"encryption_plain_dns_desc": "Düz DNS varsayılan olarak etkindir. Tüm cihazları şifrelenmiş DNS kullanmaya zorlamak için bunu devre dışı bırakabilirsiniz. Bunu yapmak için en az bir şifrelenmiş DNS protokolünü etkinleştirmeniz gerekir",
"encryption_plain_dns_error": "Düz DNS'i devre dışı bırakmak için en az bir şifrelenmiş DNS protokolünü etkinleştirin",
"topline_expiring_certificate": "SSL sertifikanızın süresi sona üzere. <0>Şifreleme ayarlarını</0> güncelleyin.",
"topline_expired_certificate": "SSL sertifikanızın süresi sona erdi. <0>Şifreleme ayarlarını</0> güncelleyin.",
@@ -438,11 +438,11 @@
"form_error_equal": "Aynı olmamalıdır",
"form_error_password": "Parolalar uyuşmuyor",
"reset_settings": "Ayarları sıfırla",
"update_announcement": "AdGuard Home {{version}} sürümü artık mevcut! Daha fazla bilgi için <0>buraya tıklayın</0>.",
"update_announcement": "AdGuard Home'un {{version}} sürümü mevcut! Daha fazla bilgi için <0>buraya tıklayın</0>.",
"setup_guide": "Kurulum Rehberi",
"dns_addresses": "DNS adresleri",
"dns_start": "DNS sunucusu başlatılıyor",
"dns_status_error": "DNS sunucusunun durumu denetlenirken bir hata oluştu",
"dns_status_error": "DNS sunucusunun durumu denetlenirken hata oluştu",
"down": "Kapalı",
"fix": "Düzelt",
"dns_providers": "Aralarından seçim yapabileceğiniz, bilinen <0>DNS sağlayıcıların listesi</0>.",
@@ -451,7 +451,7 @@
"manual_update": "Elle güncellemek için lütfen <a>bu adımları uygulayın</a>.",
"processing_update": "Lütfen bekleyin, AdGuard Home güncelleniyor",
"clients_title": "Kalıcı istemciler",
"clients_desc": "AdGuard Home'a bağlı cihazlar için kalıcı istemci kayıtlarını yapılandırın",
"clients_desc": "AdGuard Home'a bağlı cihazlar için kalıcı istemci kayıtlarını yapılandırır",
"settings_global": "Genel",
"settings_custom": "Özel",
"table_client": "İstemci",
@@ -468,7 +468,7 @@
"form_enter_mac": "MAC adresi girin",
"form_enter_id": "Tanımlayıcı girin",
"form_add_id": "Tanımlayıcı ekle",
"form_client_name": "İstemci ismi girin",
"form_client_name": "İstemci adını girin",
"name": "Adı",
"client_name": "İstemci {{id}}",
"client_global_settings": "Genel ayarları kullan",
@@ -481,18 +481,18 @@
"auto_clients_title": "Çalışma zamanı istemcileri",
"auto_clients_desc": "AdGuard Home'u kullanan veya kullanabilecek cihazların IP adresleri hakkında bilgiler. Bu bilgiler, ana bilgisayar dosyaları, ters DNS sorguları ve çeşitli diğer kaynaklardan toplanmaktadır.",
"access_title": "Erişim ayarları",
"access_desc": "AdGuard Home DNS sunucusu için erişim kurallarını buradan yapılandırabilirsiniz",
"access_desc": "AdGuard Home DNS sunucusu için erişim kuralları buradan yapılandırılabilir",
"access_allowed_title": "İzin verilen istemciler",
"access_allowed_desc": "CIDR'lerin, IP adreslerinin veya <a>İstemci Kimliklerin</a> listesi. Bu listede girişler varsa, AdGuard Home yalnızca bu istemcilerden gelen istekleri kabul eder.",
"access_allowed_desc": "CIDR'lerin, IP adreslerinin veya <a>İstemci Kimliklerinin</a> listesi. Bu listede girişler varsa, AdGuard Home yalnızca bu istemcilerden gelen istekleri kabul eder.",
"access_disallowed_title": "İzin verilmeyen istemciler",
"access_disallowed_desc": "CIDR'lerin, IP adreslerinin veya <a>İstemci Kimliklerin</a> listesi. Bu listede girişler varsa, AdGuard Home bu istemcilerden gelen istekleri keser. İzin verilen istemcilerde girişler varsa, bu alan yok sayılır.",
"access_disallowed_desc": "CIDR'lerin, IP adreslerinin veya <a>İstemci Kimliklerinin</a> listesi. Bu listede girişler varsa, AdGuard Home bu istemcilerden gelen istekleri kabul etmez. İzin verilen istemcilerde girişler varsa, bu alan yok sayılır.",
"access_blocked_title": "İzin verilmeyen alan adları",
"access_blocked_desc": "Bu işlem filtrelerle ilgili değildir. AdGuard Home, bu alan adlarından gelen DNS sorgularını yanıtsız bırakır ve bu sorgular sorgu günlüğünde görünmez. Tam alan adlarını, joker karakterleri veya URL filtre kurallarını belirtebilirsiniz, örn. \"example.org\", \"*.example.org\" veya \"||example.org^\".",
"access_settings_saved": "Erişim ayarları başarıyla kaydedildi!",
"updates_checked": "AdGuard Home'un yeni bir sürümü mevcut",
"updates_version_equal": "AdGuard Home yazılımı güncel durumda",
"updates_version_equal": "AdGuard Home güncel",
"check_updates_now": "Güncellemeleri şimdi denetle",
"version_request_error": "Güncelleme denetimi başarısız. Lütfen internet bağlantınızı kontrol edin.",
"version_request_error": "Güncelleme denetlenemedi. Lütfen internet bağlantınızı kontrol edin.",
"dns_privacy": "DNS Gizliliği",
"setup_dns_privacy_1": "<0>DNS-over-TLS:</0> <1>{{address}}</1> dizesini kullan.",
"setup_dns_privacy_2": "<0>DNS-over-HTTPS:</0> <1>{{address}}</1> dizesini kullan.",
@@ -533,14 +533,14 @@
"blocked_services_desc": "Popüler siteleri ve hizmetleri hızlı bir şekilde engellemenizi sağlar.",
"blocked_services_saved": "Engellenen hizmetler başarıyla kaydedildi",
"blocked_services_global": "Genel olarak engellenen hizmetleri kullan",
"blocked_service": "Engellenen hizmet",
"blocked_service": "Hizmet engellendi",
"block_all": "Tümünü engelle",
"unblock_all": "Tüm engellemeyi kaldır",
"encryption_certificate_path": "Sertifika dosya yolu",
"encryption_private_key_path": "Özel anahtar yolu",
"encryption_private_key_path": "Özel anahtar dosya yolu",
"encryption_certificates_source_path": "Bir sertifika dosyası yolu ayarlayın",
"encryption_certificates_source_content": "Sertifika içeriğini yapıştır",
"encryption_key_source_path": "Özel bir anahtar dosyası belirleyin",
"encryption_key_source_path": "Özel bir anahtar dosyası belirle",
"encryption_key_source_content": "Özel anahtar içeriğini yapıştır",
"stats_params": "İstatistik yapılandırması",
"config_successfully_saved": "Yapılandırma başarıyla kaydedildi",
@@ -592,27 +592,27 @@
"blocked_by_cname_or_ip": "CNAME veya IP tarafından engellendi",
"try_again": "Tekrar dene",
"domain_desc": "Yeniden yazılmasını istediğiniz alan adını veya joker karakteri girin.",
"example_rewrite_domain": "yanıtları yalnızca bu alan adı için yeniden yaz.",
"example_rewrite_wildcard": "tüm <0>example.org</0> alt alanları için yanıtları yeniden yaz.",
"rewrite_ip_address": "IP adresi: bu IP'yi A veya AAAA yanıtında kullanın",
"rewrite_domain_name": "Alan adı: bir CNAME kaydı ekleyin",
"rewrite_A": "<0>A</0>: özel değer, üst sunucudan gelen <0>A</0> kayıtlarını tutun",
"rewrite_AAAA": "<0>AAA</0>: özel değer, üst sunucudan gelen <0>AAA</0> kayıtlarını tutun",
"example_rewrite_domain": "yanıtları yalnızca bu alan adı için yeniden yazar.",
"example_rewrite_wildcard": "tüm <0>example.org</0> yanıtları alt alan adları için yeniden yazar.",
"rewrite_ip_address": "IP adresi: bu IP'yi A veya AAAA yanıtında kullanır",
"rewrite_domain_name": "Alan adı: bir CNAME kaydı ekler",
"rewrite_A": "<0>A</0>: özel değer, üst kaynak sunucudan gelen <0>A</0> kayıtlarını tutar",
"rewrite_AAAA": "<0>AAA</0>: özel değer, üst sunucudan gelen <0>AAA</0> kayıtlarını tutar",
"disable_ipv6": "IPv6 adreslerinin çözümlenmesini devre dışı bırak",
"disable_ipv6_desc": "IPv6 adresleri için tüm DNS sorgularını bırakın (AAAA yazın) ve HTTPS yanıtlarından IPv6 ipuçlarını kaldırın.",
"disable_ipv6_desc": "IPv6 adresleri için tüm DNS sorgularını yanıtsız bırakır (AAAA yazar) ve HTTPS yanıtlarından IPv6 ipuçlarını kaldırır.",
"fastest_addr": "En hızlı IP adresi",
"fastest_addr_desc": "<b>Tüm</b> DNS sunucularından yanıt bekler, her sunucu için TCP bağlantı hızını ölçer ve en hızlı bağlantı hızına sahip sunucunun IP adresini döndürür.<br/>Bu yapılandırma, bir veya daha fazla üst kaynak sunucusu yanıt vermediğinde, DNS sorgularını önemli ölçüde yavaşlatabilir. Üst kaynak sunucularınızın kararlı olduğundan ve üst kaynak zaman aşım sürenizin düşük olduğundan emin olun.",
"autofix_warning_text": "\"Düzelt\" seçeneğine tıklarsanız, AdGuard Home, sisteminizi AdGuard Home DNS sunucusunu kullanacak şekilde yapılandırır.",
"autofix_warning_list": "Bu görevleri gerçekleştirir: <0>Sistem DNSStubListener'ı devre dışı bırakın</0> <0>DNS sunucusu adresini 127.0.0.1 olarak ayarlayın</0> <0>/etc/resolv.conf'un sembolik bağlantı hedefini /run/systemd/resolve/resolv.conf ile değiştirin<0> <0>DNSStubListener'ı durdurun (systemd çözümlenmiş hizmeti yeniden yükleyin)</0>",
"autofix_warning_result": "Sonuç olarak, sisteminizden gelen tüm DNS istekleri varsayılan olarak AdGuard Home tarafından işlenecektir.",
"tags_title": "Etiketler",
"tags_desc": "İstemciyi tanımlayan etiketleri seçebilirsiniz. Filtreleme kurallarına etiketleri dahil ederek daha hassas bir şekilde uygulayabilirsiniz. <0>Daha fazla bilgi edinin</0>.",
"tags_desc": "İstemciyi tanımlayan etiketleri seçebilirsiniz. Etiketleri filtreleme kurallarına ekleyerek filtrelemeyi daha etkin bir şekilde uygulayabilirsiniz. <0>Daha fazla bilgi edinin</0>.",
"form_select_tags": "İstemci etiketlerini seçin",
"check_title": "Filtrelemeyi denetleyin",
"check_desc": "Ana makine adının filtreleme durumunu kontrol edin.",
"check_desc": "Ana makine adının filtreleme durumunu denetler.",
"check": "Denetle",
"form_enter_host": "Ana makine adı girin",
"filtered_custom_rules": "Özel filtreleme kurallarına göre filtrelendi",
"filtered_custom_rules": "Özel filtreleme kuralları tarafından filtrelendi",
"choose_from_list": "Listeden seç",
"add_custom_list": "Özel liste ekle",
"host_whitelisted": "Ana makineye izin verildi",
@@ -629,44 +629,44 @@
"client_confirm_block": "\"{{ip}}\" istemcisini engellemek istediğinizden emin misiniz?",
"client_confirm_unblock": "\"{{ip}}\" istemcisinin engellemesini kaldırmak istediğinizden emin misiniz?",
"client_blocked": "\"{{ip}}\" istemcisi başarıyla engellendi",
"client_unblocked": "\"{{ip}}\" istemcinin engellemesi başarıyla kaldırıldı",
"client_unblocked": "\"{{ip}}\" istemcisinin engeli başarıyla kaldırıldı",
"static_ip": "Sabit IP adresi",
"static_ip_desc": "AdGuard Home bir sunucudur, bu nedenle düzgün çalışabilmesi için sabit bir IP adresine ihtiyaç duyar. Aksi takdirde, yönlendiriciniz bu cihaza farklı bir IP adresi atayabilir.",
"set_static_ip": "Sabit IP adresi ayarla",
"install_static_ok": "Güzel haber! Sabit IP adresi zaten yapılandırılmış",
"install_static_error": "AdGuard Home, bu ağ arayüzü için otomatik olarak yapılandıramıyor. Lütfen bunu elle nasıl yapacağınızla ilgili talimatlara bakın.",
"set_static_ip": "Sabit IP adresi olarak ayarla",
"install_static_ok": "İyi haber! Sabit IP adresi zaten yapılandırılmış",
"install_static_error": "AdGuard Home, bu ağ arayüzü için otomatik olarak yapılandırılamıyor. Lütfen bunu elle nasıl yapacağınızla ilgili talimatlara bakın.",
"install_static_configure": "AdGuard Home, <0>{{ip}}</0> sabit IP adresinin kullanıldığını tespit etti. Sabit adresiniz olarak ayarlanmasını istiyor musunuz?",
"confirm_static_ip": "AdGuard Home, {{ip}} adresini sabit IP adresiniz olacak şekilde yapılandırır. Devam etmek istiyor musunuz?",
"list_updated": "{{count}} liste güncellendi",
"list_updated_plural": "{{count}} liste güncellendi",
"dnssec_enable": "DNSSEC'i etkinleştir",
"dnssec_enable_desc": "Giden DNS sorguları için DNSSEC özelliğini etkinleştir ve sonucu kontrol et (DNSSEC özellikli çözümleyici gerekli).",
"dnssec_enable_desc": "Giden DNS sorguları için DNSSEC işaretini etkinleştirir ve sonucu denetler (DNSSEC özellikli çözümleyici gereklidir).",
"validated_with_dnssec": "DNSSEC ile doğrulandı",
"all_queries": "Tüm sorgular",
"show_blocked_responses": "Engellenen",
"show_blocked_responses": "Engellendi",
"show_whitelisted_responses": "İzin verilen",
"show_processed_responses": "İşlenen",
"show_processed_responses": "İşlendi",
"blocked_safebrowsing": "Güvenli Gezinti tarafından engellendi",
"blocked_adult_websites": "Ebeveyn Denetimi tarafından engellendi",
"blocked_threats": "Engellenen tehditler",
"blocked_threats": "Tehdit engellendi",
"allowed": "İzin verilen",
"filtered": "Filtrelenen",
"rewritten": "Yeniden yazılan",
"filtered": "Filtrelendi",
"rewritten": "Yeniden yazıldı",
"safe_search": "Güvenli Arama",
"blocklist": "Engel listesi",
"milliseconds_abbreviation": "ms",
"cache_size": "Önbellek boyutu",
"cache_size_desc": "DNS önbellek boyutu (bayt cinsinden). Önbelleği devre dışı bırakmak için 0 olarak ayarlayın.",
"cache_ttl_min_override": "Minimum kullanım süresini geçersiz kıl",
"cache_ttl_max_override": "Maksimum kullanım süresini geçersiz kıl",
"enter_cache_size": "Önbellek boyutunu girin (bayt)",
"enter_cache_ttl_min_override": "Minimum kullanım süresi girin (saniye olarak)",
"enter_cache_ttl_max_override": "Maksimum kullanım süresi girin (saniye olarak)",
"cache_ttl_min_override_desc": "DNS yanıtlarını önbelleğe alırken üst sunucudan alınan kullanım süresi değerini uzatın (saniye olarak).",
"cache_ttl_max_override_desc": "DNS önbelleğindeki girişler için maksimum kullanım süresi değerini ayarlayın (saniye olarak).",
"cache_ttl_min_override": "En az kullanım süresini geçersiz kıl",
"cache_ttl_max_override": "En fazla kullanım süresini geçersiz kıl",
"enter_cache_size": "Önbellek boyutunu bayt türünden girin",
"enter_cache_ttl_min_override": "En az kullanım süresini saniye olarak girin",
"enter_cache_ttl_max_override": "En fazla kullanım süresini saniye olarak girin",
"cache_ttl_min_override_desc": "DNS yanıtlarını önbelleğe alırken üst sunucudan alınan kullanım süresi değerini saniye türünden uzatır.",
"cache_ttl_max_override_desc": "DNS önbelleğindeki girişler için en fazla kullanım süresi değerini saniye türünden belirler.",
"ttl_cache_validation": "Minimum önbellek kullanım süresi geçersiz kılma, maksimum değerden küçük veya ona eşit olmalıdır",
"cache_optimistic": "İyimser önbelleğe alma",
"cache_optimistic_desc": "Girişlerin süresi dolduğunda bile AdGuard Home'un önbellekten yanıt vermesini sağlayın ve bunları yenilemeye çalışın.",
"cache_optimistic_desc": "AdGuard Home, yanıtların süresi dolduğunda bile önbellekten yanıt vermesini sağlar ve bu yanıtları yenilemeyi dener.",
"filter_category_general": "Genel",
"filter_category_security": "Güvenlik",
"filter_category_regional": "Bölgesel",
@@ -678,8 +678,8 @@
"setup_config_to_enable_dhcp_server": "DHCP sunucusunu etkinleştirmek için kurulum yapılandırması",
"original_response": "Gerçek yanıt",
"click_to_view_queries": "Sorguları görmek için tıklayın",
"port_53_faq_link": "53 numaralı bağlantı noktası genellikle \"DNSStubListener\" veya \"systemd-resolved\" hizmetleri tarafından kullanılır. Lütfen bu sorunun nasıl çözüleceğine ilişkin <0>bu talimatı</0> okuyun.",
"adg_will_drop_dns_queries": "AdGuard Home, bu istemciden gelen tüm DNS sorgularını yok sayacaktır.",
"port_53_faq_link": "53 numaralı bağlantı noktası genellikle \"DNSStubListener\" veya \"systemd-resolved\" hizmetleri tarafından kullanılır. Bu sorunun nasıl çözüleceğine ilişkin lütfen <0>bu talimatı</0> okuyun.",
"adg_will_drop_dns_queries": "AdGuard Home, bu istemciden gelen tüm DNS sorgularını yok sayar.",
"filter_allowlist": "UYARI: Bu işlem ayrıca \"{{disallowed_rule}}\" kuralını izin verilen istemciler listesinden hariç tutar.",
"last_rule_in_allowlist": "\"{{disallowed_rule}}\" kuralı hariç tutulduğunda \"İzin verilen istemciler\" listesi DEVRE DIŞI bırakılacağı için bu istemciye izin verilemez.",
"use_saved_key": "Önceden kaydedilmiş anahtarı kullan",
@@ -687,7 +687,7 @@
"safe_browsing": "Güvenli Gezinti",
"served_from_cache_label": "Önbellekten kullanıldı",
"form_error_password_length": "Parola {{min}} ila {{max}} karakter uzunluğunda olmalıdır",
"anonymizer_notification": "<0>Not:</0> IP anonimleştirme etkinleştirildi. Bunu <1>Genel ayarlardan</1> devre dışı bırakabilirsiniz.",
"anonymizer_notification": "<0>Not:</0> IP gizleme etkinleştirildi. Bunu <1>Genel ayarlardan</1> devre dışı bırakabilirsiniz.",
"confirm_dns_cache_clear": "DNS önbelleğini temizlemek istediğinizden emin misiniz?",
"cache_cleared": "DNS önbelleği başarıyla temizlendi",
"clear_cache": "Önbelleği temizle",
@@ -704,14 +704,14 @@
"disable_until_tomorrow": "Yarına kadar",
"disable_notify_for_seconds": "Korumayı {{count}} saniyeliğine devre dışı bırak",
"disable_notify_for_seconds_plural": "Korumayı {{count}} saniyeliğine devre dışı bırak",
"disable_notify_for_minutes": "Korumayı {{count}} dakiklığına devre dışı bırak",
"disable_notify_for_minutes_plural": "Korumayı {{count}} dakiklığına devre dışı bırak",
"disable_notify_for_minutes": "Korumayı {{count}} dakikalığına devre dışı bırak",
"disable_notify_for_minutes_plural": "Korumayı {{count}} dakikalığına devre dışı bırak",
"disable_notify_for_hours": "Korumayı {{count}} saatliğine devre dışı bırak",
"disable_notify_for_hours_plural": "Korumayı {{count}} saatliğine devre dışı bırak",
"disable_notify_until_tomorrow": "Korumayı yarına kadar devre dışı bırak",
"enable_protection_timer": "Koruma {{time}} içinde etkinleştirilecektir",
"custom_retention_input": "Saklama süresini saat olarak girin",
"custom_rotation_input": "Rotasyonu saat cinsinden girin",
"custom_rotation_input": "Döngüyü saat cinsinden girin",
"protection_section_label": "Koruma",
"log_and_stats_section_label": "Sorgu günlüğü ve istatistikler",
"ignore_query_log": "Sorgu günlüğünde bu istemciyi gösterme",
@@ -719,13 +719,13 @@
"schedule_services": "Hizmet engellemeyi duraklat",
"schedule_services_desc": "Hizmet engelleme filtresinin duraklatma planını yapılandırın",
"schedule_services_desc_client": "Bu istemci için hizmet engelleme filtresinin duraklatma planını yapılandırın",
"schedule_desc": "Engellenen hizmetler için hareketsizlik sürelerini ayarlayın",
"schedule_desc": "Engellenen hizmetler için duraklatma zamanı ayarlayın",
"schedule_invalid_select": "Başlangıç zamanı, bitiş zamanından önce olmalıdır",
"schedule_select_days": "Günleri seçin",
"schedule_timezone": "Saat dilimi seçin",
"schedule_current_timezone": "Şu anki saat dilimi: {{value}}",
"schedule_time_all_day": "Tüm gün",
"schedule_modal_description": "Bu plan, haftanın aynı günü için mevcut planların yerini alır. Haftanın her gününde yalnızca bir hareketsizlik süresine sahip olabilir.",
"schedule_modal_description": "Bu plan, haftanın aynı günü için mevcut planların yerini alır. Haftanın her gününde yalnızca bir duraklatma zamanı olabilir.",
"schedule_modal_time_off": "Hizmet engelleme yok:",
"schedule_new": "Yeni plan",
"schedule_edit": "Planı düzenle",

42
go.mod
View File

@@ -1,10 +1,10 @@
module github.com/AdguardTeam/AdGuardHome
go 1.24.4
go 1.24.5
require (
github.com/AdguardTeam/dnsproxy v0.75.6
github.com/AdguardTeam/golibs v0.32.11
github.com/AdguardTeam/dnsproxy v0.76.1
github.com/AdguardTeam/golibs v0.32.15
github.com/AdguardTeam/urlfilter v0.20.0
github.com/NYTimes/gziphandler v1.1.1
github.com/ameshkov/dnscrypt/v2 v2.4.0
@@ -28,12 +28,12 @@ require (
// own code for that. Perhaps, use gopacket.
github.com/mdlayher/raw v0.1.0
github.com/miekg/dns v1.1.66
github.com/quic-go/quic-go v0.52.0
github.com/quic-go/quic-go v0.53.0
github.com/stretchr/testify v1.10.0
github.com/ti-mo/netfilter v0.5.3
go.etcd.io/bbolt v1.4.0
go.etcd.io/bbolt v1.4.1
golang.org/x/crypto v0.39.0
golang.org/x/exp v0.0.0-20250531010427-b6e5de432a8b
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b
golang.org/x/net v0.41.0
golang.org/x/sys v0.33.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1
@@ -42,7 +42,7 @@ require (
)
require (
cloud.google.com/go v0.121.2 // indirect
cloud.google.com/go v0.121.3 // indirect
cloud.google.com/go/ai v0.12.1 // indirect
cloud.google.com/go/auth v0.16.2 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
@@ -57,10 +57,8 @@ require (
github.com/fzipp/gocyclo v0.6.0 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
github.com/golangci/misspell v0.7.0 // indirect
github.com/google/generative-ai-go v0.20.1 // indirect
github.com/google/pprof v0.0.0-20250602020802-c6617b811d0e // indirect
github.com/google/s2a-go v0.1.9 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect
github.com/googleapis/gax-go/v2 v2.14.2 // indirect
@@ -70,7 +68,6 @@ require (
github.com/jstemmer/go-junit-report/v2 v2.1.0 // indirect
github.com/kisielk/errcheck v1.9.0 // indirect
github.com/mdlayher/socket v0.5.1 // indirect
github.com/onsi/ginkgo/v2 v2.23.4 // indirect
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
github.com/pierrec/lz4/v4 v4.1.22 // indirect
github.com/pkg/errors v0.9.1 // indirect
@@ -78,38 +75,37 @@ require (
github.com/quic-go/qpack v0.5.1 // indirect
github.com/robfig/cron/v3 v3.0.1 // indirect
github.com/rogpeppe/go-internal v1.14.1 // indirect
github.com/securego/gosec/v2 v2.22.4 // indirect
github.com/securego/gosec/v2 v2.22.5 // indirect
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 // indirect
github.com/uudashr/gocognit v1.2.0 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
go.opentelemetry.io/otel v1.36.0 // indirect
go.opentelemetry.io/otel/metric v1.36.0 // indirect
go.opentelemetry.io/otel/trace v1.36.0 // indirect
go.uber.org/automaxprocs v1.6.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.62.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0 // indirect
go.opentelemetry.io/otel v1.37.0 // indirect
go.opentelemetry.io/otel/metric v1.37.0 // indirect
go.opentelemetry.io/otel/trace v1.37.0 // indirect
go.uber.org/mock v0.5.2 // indirect
golang.org/x/exp/typeparams v0.0.0-20250606033433-dcc06ee1d476 // indirect
golang.org/x/exp/typeparams v0.0.0-20250620022241-b7579e27df2b // indirect
golang.org/x/mod v0.25.0 // indirect
golang.org/x/oauth2 v0.30.0 // indirect
golang.org/x/sync v0.15.0 // indirect
golang.org/x/telemetry v0.0.0-20250609191608-4884ade64e8b // indirect
golang.org/x/telemetry v0.0.0-20250708141652-5a6bbb13955f // indirect
golang.org/x/term v0.32.0 // indirect
golang.org/x/text v0.26.0 // indirect
golang.org/x/time v0.12.0 // indirect
golang.org/x/tools v0.34.0 // indirect
golang.org/x/vuln v1.1.4 // indirect
gonum.org/v1/gonum v0.16.0 // indirect
google.golang.org/api v0.236.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect
google.golang.org/api v0.240.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7 // indirect
google.golang.org/grpc v1.73.0 // indirect
google.golang.org/protobuf v1.36.6 // indirect
honnef.co/go/tools v0.6.1 // indirect
mvdan.cc/editorconfig v0.3.0 // indirect
mvdan.cc/gofumpt v0.8.0 // indirect
mvdan.cc/sh/v3 v3.11.0 // indirect
mvdan.cc/sh/v3 v3.12.0 // indirect
mvdan.cc/unparam v0.0.0-20250301125049-0df0534333a4 // indirect
)

88
go.sum
View File

@@ -1,5 +1,5 @@
cloud.google.com/go v0.121.2 h1:v2qQpN6Dx9x2NmwrqlesOt3Ys4ol5/lFZ6Mg1B7OJCg=
cloud.google.com/go v0.121.2/go.mod h1:nRFlrHq39MNVWu+zESP2PosMWA0ryJw8KUBZ2iZpxbw=
cloud.google.com/go v0.121.3 h1:84RD+hQXNdY5Sw/MWVAx5O9Aui/rd5VQ9HEcdN19afo=
cloud.google.com/go v0.121.3/go.mod h1:6vWF3nJWRrEUv26mMB3FEIU/o1MQNVPG1iHdisa2SJc=
cloud.google.com/go/ai v0.12.1 h1:m1n/VjUuHS+pEO/2R4/VbuuEIkgk0w67fDQvFaMngM0=
cloud.google.com/go/ai v0.12.1/go.mod h1:5vIPNe1ZQsVZqCliXIPL4QnhObQQY4d9hAGHdVc4iw4=
cloud.google.com/go/auth v0.16.2 h1:QvBAGFPLrDeoiNjyfVunhQ10HKNYuOwZ5noee0M5df4=
@@ -10,10 +10,10 @@ cloud.google.com/go/compute/metadata v0.7.0 h1:PBWF+iiAerVNe8UCHxdOt6eHLVc3ydFeO
cloud.google.com/go/compute/metadata v0.7.0/go.mod h1:j5MvL9PprKL39t166CoB1uVHfQMs4tFQZZcKwksXUjo=
cloud.google.com/go/longrunning v0.6.7 h1:IGtfDWHhQCgCjwQjV9iiLnUta9LBCo8R9QmAFsS/PrE=
cloud.google.com/go/longrunning v0.6.7/go.mod h1:EAFV3IZAKmM56TyiE6VAP3VoTzhZzySwI/YI1s/nRsY=
github.com/AdguardTeam/dnsproxy v0.75.6 h1:Xz5dciFQeMCtaqBHAyUYGI5PO1Lu7HwIi5ejhSVGsLY=
github.com/AdguardTeam/dnsproxy v0.75.6/go.mod h1:WHwb2fHfyujIZLrBymBQtqDUTbzDlNebWqykd1mCNuM=
github.com/AdguardTeam/golibs v0.32.11 h1:75EquS8SWvzsM3JFJY0359ZBw66jDjAegteHzh9nSw8=
github.com/AdguardTeam/golibs v0.32.11/go.mod h1:LXr0gqqZuVpt+L+bP3Nnr0/CecLmm3rxkdgyyW5JXXM=
github.com/AdguardTeam/dnsproxy v0.76.1 h1:ms5vgdbYYXrKGPEpMFqUeql2j3aSfK1tGbCKju9rUgM=
github.com/AdguardTeam/dnsproxy v0.76.1/go.mod h1:9Mw3wQMTYwM/HR9FdtatQAd+m0S8mbwq2J+UZiy/gXc=
github.com/AdguardTeam/golibs v0.32.15 h1:arDRDWiZCH3g5Onr8AqMnOHhaOppNoBpgC3DNhmeDeA=
github.com/AdguardTeam/golibs v0.32.15/go.mod h1:G9CzUOzx87J+2u+eClJrrwWD7lMbROvuUnT8uvDUzIA=
github.com/AdguardTeam/urlfilter v0.20.0 h1:X32qiuVCVd8WDYCEsbdZKfXMzwdVqrdulamtUi4rmzs=
github.com/AdguardTeam/urlfilter v0.20.0/go.mod h1:gjrywLTxfJh6JOkwi9SU+frhP7kVVEZ5exFGkR99qpk=
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
@@ -72,8 +72,8 @@ github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
github.com/google/pprof v0.0.0-20250602020802-c6617b811d0e h1:FJta/0WsADCe1r9vQjdHbd3KuiLPu7Y9WlyLGwMUNyE=
github.com/google/pprof v0.0.0-20250602020802-c6617b811d0e/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA=
github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a h1://KbezygeMJZCSHH+HgUZiTeSoiuFspbMg1ge+eFj18=
github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA=
github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/renameio/v2 v2.0.0 h1:UifI23ZTGY8Tt29JbYFiuyIU3eX+RNFtUwefq9qAhxg=
@@ -141,18 +141,16 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
github.com/quic-go/quic-go v0.52.0 h1:/SlHrCRElyaU6MaEPKqKr9z83sBg2v4FLLvWM+Z47pA=
github.com/quic-go/quic-go v0.52.0/go.mod h1:MFlGGpcpJqRAfmYi6NC2cptDPSxRWTOGNuP4wqrWmzQ=
github.com/quic-go/quic-go v0.53.0 h1:QHX46sISpG2S03dPeZBgVIZp8dGagIaiu2FiVYvpCZI=
github.com/quic-go/quic-go v0.53.0/go.mod h1:e68ZEaCdyviluZmy44P6Iey98v/Wfz6HCjQEm+l8zTY=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/securego/gosec/v2 v2.22.4 h1:21VdNGcKicFSv6rUDBc0cEtEl7lWyCKZxKIm0iwvrIM=
github.com/securego/gosec/v2 v2.22.4/go.mod h1:ww5Yie7KJ3AH8XZQTletkW5zOmIse6FACs/Ys8VR3qE=
github.com/securego/gosec/v2 v2.22.5 h1:ySws9uwOeE42DsG54v2moaJfh7r08Ev7SAYJuoMDfRA=
github.com/securego/gosec/v2 v2.22.5/go.mod h1:AWfgrFsVewk5LKobsPWlygCHt8K91boVPyL6GUZG5NY=
github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=
github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk=
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
@@ -179,36 +177,38 @@ github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJu
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
go.etcd.io/bbolt v1.4.0 h1:TU77id3TnN/zKr7CO/uk+fBCwF2jGcMuw2B/FMAzYIk=
go.etcd.io/bbolt v1.4.0/go.mod h1:AsD+OCi/qPN1giOX1aiLAha3o1U8rAz65bvN4j0sRuk=
go.etcd.io/bbolt v1.4.1 h1:5mOV+HWjIPLEAlUGMsveaUvK2+byZMFOzojoi7bh7uI=
go.etcd.io/bbolt v1.4.1/go.mod h1:c8zu2BnXWTu2XM4XcICtbGSl9cFwsXtcf9zLt2OncM8=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 h1:q4XOmH/0opmeuJtPsbFNivyl7bCt7yRBbeEm2sC/XtQ=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0/go.mod h1:snMWehoOh2wsEwnvvwtDyFCxVeDAODenXHtn5vzrKjo=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg=
go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E=
go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE=
go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs=
go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs=
go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY=
go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis=
go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4=
go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w=
go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.62.0 h1:rbRJ8BBoVMsQShESYZ0FkvcITu8X8QNwJogcLUmDNNw=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.62.0/go.mod h1:ru6KHrNtNHxM4nD/vd6QrLVWgKhxPYgblq4VAtNawTQ=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0 h1:Hf9xI/XLML9ElpiHVDNwvqI0hIFlzV8dgIr35kV1kRU=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0/go.mod h1:NfchwuyNoMcZ5MLHwPrODwUF1HWCXWrL31s8gSAdIKY=
go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ=
go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I=
go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE=
go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E=
go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI=
go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg=
go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc=
go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps=
go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=
go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko=
go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
golang.org/x/exp v0.0.0-20250531010427-b6e5de432a8b h1:QoALfVG9rhQ/M7vYDScfPdWjGL9dlsVVM5VGh7aKoAA=
golang.org/x/exp v0.0.0-20250531010427-b6e5de432a8b/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ=
golang.org/x/exp/typeparams v0.0.0-20250606033433-dcc06ee1d476 h1:tceuojoK22x7b3gj32PCOPkkFeVtewHm1imqrGyFp1I=
golang.org/x/exp/typeparams v0.0.0-20250606033433-dcc06ee1d476/go.mod h1:LKZHyeOpPuZcMgxeHjJp4p5yvxrCX1xDvH10zYHhjjQ=
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o=
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8=
golang.org/x/exp/typeparams v0.0.0-20250620022241-b7579e27df2b h1:KdrhdYPDUvJTvrDK9gdjfFd6JTk8vA1WJoldYSi0kHo=
golang.org/x/exp/typeparams v0.0.0-20250620022241-b7579e27df2b/go.mod h1:LKZHyeOpPuZcMgxeHjJp4p5yvxrCX1xDvH10zYHhjjQ=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
@@ -242,8 +242,8 @@ golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/telemetry v0.0.0-20250609191608-4884ade64e8b h1:Y/IUTO67KgVkBoAByyCNRMMLAXUzEiblXXJqxR3OF8Q=
golang.org/x/telemetry v0.0.0-20250609191608-4884ade64e8b/go.mod h1:mUcjA5g0luJpMYCLjhH91f4t4RAUNp+zq9ZmUoqPD7M=
golang.org/x/telemetry v0.0.0-20250708141652-5a6bbb13955f h1:GnwFSf1cKD9qa+VRJWGjDBK0OHWJgTMaj49bSkN3agw=
golang.org/x/telemetry v0.0.0-20250708141652-5a6bbb13955f/go.mod h1:mUcjA5g0luJpMYCLjhH91f4t4RAUNp+zq9ZmUoqPD7M=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
@@ -267,14 +267,14 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/api v0.236.0 h1:CAiEiDVtO4D/Qja2IA9VzlFrgPnK3XVMmRoJZlSWbc0=
google.golang.org/api v0.236.0/go.mod h1:X1WF9CU2oTc+Jml1tiIxGmWFK/UZezdqEu09gcxZAj4=
google.golang.org/api v0.240.0 h1:PxG3AA2UIqT1ofIzWV2COM3j3JagKTKSwy7L6RHNXNU=
google.golang.org/api v0.240.0/go.mod h1:cOVEm2TpdAGHL2z+UwyS+kmlGr3bVWQQ6sYEqkKje50=
google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2 h1:1tXaIXCracvtsRxSBsYDiSBN0cuJvM7QYW+MrpIRY78=
google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:49MsLSx0oWMOZqcpB3uL8ZOkAh1+TndpJ8ONoCBWiZk=
google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 h1:oWVWY3NzT7KJppx2UKhKmzPq4SRe0LdCijVRwvGeikY=
google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822/go.mod h1:h3c4v36UTKzUiuaOKQ6gr3S+0hovBtUrXzTG/i3+XEc=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 h1:fc6jSaCT0vBduLYZHYrBBNY4dsWuvgyff9noRNDdBeE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7 h1:FiusG7LWj+4byqhbvmB+Q93B/mOxJLN2DTozDuZm4EU=
google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7/go.mod h1:kXqgZtrWaf6qS3jZOCnCH7WYfrvFjkC51bM8fz3RsCA=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7 h1:pFyd6EwwL2TqFf8emdthzeX+gZE1ElRq3iM8pui4KBY=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
@@ -295,7 +295,7 @@ mvdan.cc/editorconfig v0.3.0 h1:D1D2wLYEYGpawWT5SpM5pRivgEgXjtEXwC9MWhEY0gQ=
mvdan.cc/editorconfig v0.3.0/go.mod h1:NcJHuDtNOTEJ6251indKiWuzK6+VcrMuLzGMLKBFupQ=
mvdan.cc/gofumpt v0.8.0 h1:nZUCeC2ViFaerTcYKstMmfysj6uhQrA2vJe+2vwGU6k=
mvdan.cc/gofumpt v0.8.0/go.mod h1:vEYnSzyGPmjvFkqJWtXkh79UwPWP9/HMxQdGEXZHjpg=
mvdan.cc/sh/v3 v3.11.0 h1:q5h+XMDRfUGUedCqFFsjoFjrhwf2Mvtt1rkMvVz0blw=
mvdan.cc/sh/v3 v3.11.0/go.mod h1:LRM+1NjoYCzuq/WZ6y44x14YNAI0NK7FLPeQSaFagGg=
mvdan.cc/sh/v3 v3.12.0 h1:ejKUR7ONP5bb+UGHGEG/k9V5+pRVIyD+LsZz7o8KHrI=
mvdan.cc/sh/v3 v3.12.0/go.mod h1:Se6Cj17eYSn+sNooLZiEUnNNmNxg0imoYlTu4CyaGyg=
mvdan.cc/unparam v0.0.0-20250301125049-0df0534333a4 h1:WjUu4yQoT5BHT1w8Zu56SP8367OuBV5jvo+4Ulppyf8=
mvdan.cc/unparam v0.0.0-20250301125049-0df0534333a4/go.mod h1:rthT7OuvRbaGcd5ginj6dA2oLE7YNlta9qhBNNdCaLE=

View File

@@ -0,0 +1,52 @@
// Package aghslog contains logging constants and helpers.
package aghslog
import (
"log/slog"
"github.com/AdguardTeam/golibs/logutil/slogutil"
)
// PrefixDNSProxy is the prefix for DNS proxy logs.
const PrefixDNSProxy = "dnsproxy"
const (
// KeyClientName is the log attribute for the client name.
KeyClientName = "client_name"
// KeyUpstreamType is the log attribute for the upstream types. See the
// UpstreamType* constants below.
KeyUpstreamType = "upstream_type"
)
const (
// UpstreamTypeBootstrap is the log attribute value for bootstrap upstreams.
UpstreamTypeBootstrap = "bootstrap"
// UpstreamTypeCustom is the log attribute value for custom upstreams.
UpstreamTypeCustom = "custom"
// UpstreamTypeFallback is the log attribute value for fallback upstreams.
UpstreamTypeFallback = "fallback"
// UpstreamTypeMain is the log attribute value for main upstreams.
UpstreamTypeMain = "main"
// UpstreamTypeLocal is the log attribute value for upstreams used for
// resolving PTR records for local addresses.
UpstreamTypeLocal = "local"
// UpstreamTypeService is the log attribute value for upstreams used for
// safe browsing and parental services.
UpstreamTypeService = "service"
// UpstreamTypeTest is the log attribute value for upstreams used for
// testing and validation.
UpstreamTypeTest = "test"
)
// NewForUpstream returns a new logger with a prefix for logs related to a
// specific upstream type.
func NewForUpstream(baseLogger *slog.Logger, typ string) (l *slog.Logger) {
return baseLogger.With(slogutil.KeyPrefix, PrefixDNSProxy, KeyUpstreamType, typ)
}

View File

@@ -3,7 +3,6 @@ package aghtest
import (
"crypto/sha256"
"io"
"net/http"
"net/http/httptest"
"net/netip"
@@ -12,7 +11,6 @@ import (
"time"
"github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/testutil"
"github.com/miekg/dns"
@@ -27,33 +25,6 @@ const (
ReqFQDN = ReqHost + "."
)
// ReplaceLogWriter moves logger output to w and uses Cleanup method of t to
// revert changes.
func ReplaceLogWriter(t testing.TB, w io.Writer) {
t.Helper()
prev := log.Writer()
t.Cleanup(func() { log.SetOutput(prev) })
log.SetOutput(w)
}
// ReplaceLogLevel sets logging level to l and uses Cleanup method of t to
// revert changes.
func ReplaceLogLevel(t testing.TB, l log.Level) {
t.Helper()
switch l {
case log.INFO, log.DEBUG, log.ERROR:
// Go on.
default:
t.Fatalf("wrong l value (must be one of %v, %v, %v)", log.INFO, log.DEBUG, log.ERROR)
}
prev := log.GetLevel()
t.Cleanup(func() { log.SetLevel(prev) })
log.SetLevel(l)
}
// HostToIPs is a helper that generates one IPv4 and one IPv6 address from host.
func HostToIPs(host string) (ipv4, ipv6 netip.Addr) {
hash := sha256.Sum256([]byte(host))

View File

@@ -10,6 +10,7 @@ import (
"slices"
"strings"
"github.com/AdguardTeam/AdGuardHome/internal/aghslog"
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/dnsproxy/upstream"
@@ -142,7 +143,9 @@ func (c *Persistent) validate(ctx context.Context, l *slog.Logger, allTags []str
return errors.Error("uid required")
}
conf, err := proxy.ParseUpstreamsConfig(c.Upstreams, &upstream.Options{})
conf, err := proxy.ParseUpstreamsConfig(c.Upstreams, &upstream.Options{
Logger: l.With(aghslog.KeyUpstreamType, aghslog.UpstreamTypeTest),
})
if err != nil {
return fmt.Errorf("invalid upstream servers: %w", err)
}

View File

@@ -88,6 +88,10 @@ type HostsContainer interface {
// StorageConfig is the client storage configuration structure.
type StorageConfig struct {
// BaseLogger is used to create loggers for other entities. It should not
// have a prefix and must not be nil.
BaseLogger *slog.Logger
// Logger is used for logging the operation of the client storage. It must
// not be nil.
Logger *slog.Logger
@@ -174,7 +178,7 @@ func NewStorage(ctx context.Context, conf *StorageConfig) (s *Storage, err error
mu: &sync.Mutex{},
index: newIndex(),
runtimeIndex: newRuntimeIndex(),
upstreamManager: newUpstreamManager(conf.Logger, conf.Clock),
upstreamManager: newUpstreamManager(conf.BaseLogger, conf.Clock),
dhcp: conf.DHCP,
etcHosts: conf.EtcHosts,
arpDB: conf.ARPDB,
@@ -739,7 +743,7 @@ func (s *Storage) CustomUpstreamConfig(
return nil
}
return s.upstreamManager.customUpstreamConfig(c.UID)
return s.upstreamManager.customUpstreamConfig(c.UID, c.Name)
}
// UpdateCommonUpstreamConfig implements the [dnsforward.ClientsContainer]

View File

@@ -25,14 +25,18 @@ import (
"github.com/stretchr/testify/require"
)
// testLogger is a logger used in tests.
var testLogger = slogutil.NewDiscardLogger()
// newTestStorage is a helper function that returns initialized storage.
func newTestStorage(tb testing.TB, clock timeutil.Clock) (s *client.Storage) {
tb.Helper()
ctx := testutil.ContextWithTimeout(tb, testTimeout)
s, err := client.NewStorage(ctx, &client.StorageConfig{
Logger: slogutil.NewDiscardLogger(),
Clock: clock,
BaseLogger: testLogger,
Logger: testLogger,
Clock: clock,
})
require.NoError(tb, err)
@@ -134,7 +138,8 @@ func TestStorage_Add_hostsfile(t *testing.T) {
ctx := testutil.ContextWithTimeout(t, testTimeout)
storage, err := client.NewStorage(ctx, &client.StorageConfig{
Logger: slogutil.NewDiscardLogger(),
BaseLogger: testLogger,
Logger: testLogger,
DHCP: client.EmptyDHCP{},
EtcHosts: h,
ARPClientsUpdatePeriod: testTimeout / 10,
@@ -224,7 +229,8 @@ func TestStorage_Add_arp(t *testing.T) {
ctx := testutil.ContextWithTimeout(t, testTimeout)
storage, err := client.NewStorage(ctx, &client.StorageConfig{
Logger: slogutil.NewDiscardLogger(),
BaseLogger: testLogger,
Logger: testLogger,
DHCP: client.EmptyDHCP{},
ARPDB: a,
ARPClientsUpdatePeriod: testTimeout / 10,
@@ -301,8 +307,9 @@ func TestStorage_Add_whois(t *testing.T) {
ctx := testutil.ContextWithTimeout(t, testTimeout)
storage, err := client.NewStorage(ctx, &client.StorageConfig{
Logger: slogutil.NewDiscardLogger(),
DHCP: client.EmptyDHCP{},
BaseLogger: testLogger,
Logger: testLogger,
DHCP: client.EmptyDHCP{},
})
require.NoError(t, err)
@@ -417,7 +424,8 @@ func TestClientsDHCP(t *testing.T) {
ctx := testutil.ContextWithTimeout(t, testTimeout)
storage, err := client.NewStorage(ctx, &client.StorageConfig{
Logger: slogutil.NewDiscardLogger(),
BaseLogger: testLogger,
Logger: testLogger,
ARPDB: arpDB,
DHCP: dhcp,
EtcHosts: etcHosts,
@@ -566,8 +574,9 @@ func TestClientsAddExisting(t *testing.T) {
t.Run("simple", func(t *testing.T) {
storage, err := client.NewStorage(ctx, &client.StorageConfig{
Logger: slogutil.NewDiscardLogger(),
DHCP: client.EmptyDHCP{},
BaseLogger: testLogger,
Logger: testLogger,
DHCP: client.EmptyDHCP{},
})
require.NoError(t, err)
@@ -613,8 +622,9 @@ func TestClientsAddExisting(t *testing.T) {
require.NoError(t, err)
storage, err := client.NewStorage(ctx, &client.StorageConfig{
Logger: slogutil.NewDiscardLogger(),
DHCP: dhcpServer,
BaseLogger: testLogger,
Logger: testLogger,
DHCP: dhcpServer,
})
require.NoError(t, err)
@@ -653,8 +663,9 @@ func newStorage(tb testing.TB, m []*client.Persistent) (s *client.Storage) {
ctx := testutil.ContextWithTimeout(tb, testTimeout)
s, err := client.NewStorage(ctx, &client.StorageConfig{
Logger: slogutil.NewDiscardLogger(),
DHCP: client.EmptyDHCP{},
BaseLogger: testLogger,
Logger: testLogger,
DHCP: client.EmptyDHCP{},
})
require.NoError(tb, err)
@@ -1210,9 +1221,10 @@ func TestStorage_CustomUpstreamConfig(t *testing.T) {
ctx := testutil.ContextWithTimeout(t, testTimeout)
s, err := client.NewStorage(ctx, &client.StorageConfig{
Logger: slogutil.NewDiscardLogger(),
Clock: clock,
DHCP: dhcp,
BaseLogger: testLogger,
Logger: testLogger,
Clock: clock,
DHCP: dhcp,
})
require.NoError(t, err)

View File

@@ -7,6 +7,7 @@ import (
"time"
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
"github.com/AdguardTeam/AdGuardHome/internal/aghslog"
"github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/dnsproxy/upstream"
"github.com/AdguardTeam/golibs/errors"
@@ -56,10 +57,12 @@ type customUpstreamConfig struct {
// upstreamManager stores and updates custom client upstream configurations.
type upstreamManager struct {
// baseLogger is used to create loggers for client upstream configurations.
// It should not have a prefix and must not be nil.
baseLogger *slog.Logger
// logger is used for logging the operation of the upstream manager. It
// must not be nil.
//
// TODO(s.chzhen): Consider using a logger with its own prefix.
logger *slog.Logger
// uidToCustomConf maps persistent client UID to the custom client upstream
@@ -78,9 +81,10 @@ type upstreamManager struct {
}
// newUpstreamManager returns the new properly initialized upstream manager.
func newUpstreamManager(logger *slog.Logger, clock timeutil.Clock) (m *upstreamManager) {
func newUpstreamManager(baseLogger *slog.Logger, clock timeutil.Clock) (m *upstreamManager) {
return &upstreamManager{
logger: logger,
baseLogger: baseLogger,
logger: baseLogger.With(slogutil.KeyPrefix, "upstream_manager"),
uidToCustomConf: make(map[UID]*customUpstreamConfig),
clock: clock,
}
@@ -115,7 +119,10 @@ func (m *upstreamManager) updateCustomUpstreamConfig(c *Persistent) {
}
// customUpstreamConfig returns the custom client upstream configuration.
func (m *upstreamManager) customUpstreamConfig(uid UID) (proxyConf *proxy.CustomUpstreamConfig) {
func (m *upstreamManager) customUpstreamConfig(
uid UID,
clientName string,
) (proxyConf *proxy.CustomUpstreamConfig) {
cliConf, ok := m.uidToCustomConf[uid]
if !ok {
// TODO(s.chzhen): Consider panic.
@@ -136,7 +143,11 @@ func (m *upstreamManager) customUpstreamConfig(uid UID) (proxyConf *proxy.Custom
}
}
proxyConf = newCustomUpstreamConfig(cliConf, m.commonConf)
cliLogger := aghslog.NewForUpstream(m.baseLogger, aghslog.UpstreamTypeCustom).With(
aghslog.KeyClientName,
clientName,
)
proxyConf = newCustomUpstreamConfig(cliConf, m.commonConf, cliLogger)
cliConf.proxyConf = proxyConf
cliConf.commonConfUpdate = m.confUpdate
cliConf.isChanged = false
@@ -193,10 +204,12 @@ func (m *upstreamManager) close() (err error) {
}
// newCustomUpstreamConfig returns the new properly initialized custom proxy
// upstream configuration for the client.
// upstream configuration for the client. cliConf, conf, and cliLogger must not
// be nil.
func newCustomUpstreamConfig(
cliConf *customUpstreamConfig,
conf *CommonUpstreamConfig,
cliLogger *slog.Logger,
) (proxyConf *proxy.CustomUpstreamConfig) {
upstreams := stringutil.FilterOut(cliConf.upstreams, aghnet.IsCommentOrEmpty)
if len(upstreams) == 0 {
@@ -206,8 +219,9 @@ func newCustomUpstreamConfig(
upsConf, err := proxy.ParseUpstreamsConfig(
upstreams,
&upstream.Options{
Logger: cliLogger,
Bootstrap: conf.Bootstrap,
Timeout: time.Duration(conf.UpstreamTimeout),
Timeout: conf.UpstreamTimeout,
HTTPVersions: aghnet.UpstreamHTTPVersions(conf.UseHTTP3Upstreams),
PreferIPv6: conf.BootstrapPreferIPv6,
},

View File

@@ -11,7 +11,6 @@ import (
"github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/netutil"
"github.com/quic-go/quic-go"
)
// clientIDFromClientServerName extracts and validates a ClientID. hostSrvName
@@ -93,11 +92,6 @@ type tlsConn interface {
ConnectionState() (cs tls.ConnectionState)
}
// quicConnection is a narrow interface for quic.Connection to simplify testing.
type quicConnection interface {
ConnectionState() (cs quic.ConnectionState)
}
// clientServerName returns the TLS server name based on the protocol. For
// DNS-over-HTTPS requests, it will return the hostname part of the Host header
// if there is one.
@@ -116,13 +110,7 @@ func clientServerName(pctx *proxy.DNSContext, proto proxy.Proto) (srvName string
from = "host header"
}
case proxy.ProtoQUIC:
qConn := pctx.QUICConnection
conn, ok := qConn.(quicConnection)
if !ok {
return "", fmt.Errorf("pctx conn of proto %s is %T, want quic.Connection", proto, qConn)
}
srvName = conn.ConnectionState().TLS.ServerName
srvName = pctx.QUICConnection.ConnectionState().TLS.ServerName
case proxy.ProtoTLS:
conn := pctx.Conn
tc, ok := conn.(tlsConn)

View File

@@ -8,9 +8,7 @@ import (
"testing"
"github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/testutil"
"github.com/quic-go/quic-go"
"github.com/stretchr/testify/assert"
)
@@ -30,23 +28,6 @@ func (c testTLSConn) ConnectionState() (cs tls.ConnectionState) {
return cs
}
// testQUICConnection is a quicConnection for tests.
type testQUICConnection struct {
// Connection is embedded here simply to make testQUICConnection a
// quic.Connection without actually implementing all methods.
quic.Connection
serverName string
}
// ConnectionState implements the quicConnection interface for
// testQUICConnection.
func (c testQUICConnection) ConnectionState() (cs quic.ConnectionState) {
cs.TLS.ServerName = c.serverName
return cs
}
func TestServer_clientIDFromDNSContext(t *testing.T) {
testCases := []struct {
name string
@@ -219,12 +200,11 @@ func TestServer_clientIDFromDNSContext(t *testing.T) {
srv := &Server{
conf: ServerConfig{TLSConf: tlsConf},
baseLogger: slogutil.NewDiscardLogger(),
baseLogger: testLogger,
}
var (
conn net.Conn
qconn quic.Connection
httpReq *http.Request
)
@@ -232,9 +212,9 @@ func TestServer_clientIDFromDNSContext(t *testing.T) {
case proxy.ProtoHTTPS:
httpReq = newHTTPReq(tc.cliSrvName, tc.inclHTTPTLS)
case proxy.ProtoQUIC:
qconn = testQUICConnection{
serverName: tc.cliSrvName,
}
// TODO(a.garipov): Find ways of testing this with the new
// quic-go API.
t.Skipf("skipped during the quic-go api update")
case proxy.ProtoTLS:
conn = testTLSConn{
serverName: tc.cliSrvName,
@@ -242,10 +222,9 @@ func TestServer_clientIDFromDNSContext(t *testing.T) {
}
pctx := &proxy.DNSContext{
Proto: tc.proto,
Conn: conn,
HTTPRequest: httpReq,
QUICConnection: qconn,
Proto: tc.proto,
Conn: conn,
HTTPRequest: httpReq,
}
clientID, err := srv.clientIDFromDNSContext(pctx)

View File

@@ -13,6 +13,7 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
"github.com/AdguardTeam/AdGuardHome/internal/aghslog"
"github.com/AdguardTeam/AdGuardHome/internal/aghtls"
"github.com/AdguardTeam/AdGuardHome/internal/client"
"github.com/AdguardTeam/dnsproxy/proxy"
@@ -167,10 +168,14 @@ type EDNSClientSubnet struct {
UseCustom bool `yaml:"use_custom"`
}
// TLSConfig contains the TLS configuration settings for DNS-over-HTTPS (DoH),
// DNS-over-TLS (DoT), DNS-over-QUIC (DoQ), and Discovery of Designated
// Resolvers (DDR).
// TLSConfig contains the TLS configuration settings for DNSCrypt,
// DNS-over-HTTPS (DoH), DNS-over-TLS (DoT), DNS-over-QUIC (DoQ), and Discovery
// of Designated Resolvers (DDR).
type TLSConfig struct {
// DNSCryptConf contains the configuration settings for a DNSCrypt server.
// It is nil if the DNSCrypt server is disabled.
DNSCryptConf *DNSCryptConfig
// Cert is the TLS certificate used for TLS connections. It is nil if
// encryption is disabled.
Cert *tls.Certificate
@@ -197,13 +202,23 @@ type TLSConfig struct {
StrictSNICheck bool
}
// DNSCryptConfig is the DNSCrypt server configuration struct.
// DNSCryptConfig contains the configuration settings for a DNSCrypt server.
type DNSCryptConfig struct {
ResolverCert *dnscrypt.Cert
ProviderName string
// ResolverCert is the certificate used for DNSCrypt connections. It is not
// nil if there is at least one UDP or TCP address present.
ResolverCert *dnscrypt.Cert
// UDPListenAddrs are the addresses to listen on for DNSCrypt UDP
// connections.
UDPListenAddrs []*net.UDPAddr
// TCPListenAddrs are the addresses to listen on for DNSCrypt TCP
// connections.
TCPListenAddrs []*net.TCPAddr
Enabled bool
// ProviderName is the name of the DNSCrypt provider. It is not empty if
// there is at least one UDP or TCP address present.
ProviderName string
}
// ServerConfig represents server configuration.
@@ -234,7 +249,6 @@ type ServerConfig struct {
TLSConf *TLSConfig
Config
DNSCryptConfig
TLSAllowUnencryptedDoH bool
// UpstreamTimeout is the timeout for querying upstream servers.
@@ -298,7 +312,7 @@ func (s *Server) newProxyConfig() (conf *proxy.Config, err error) {
trustedPrefixes := netutil.UnembedPrefixes(srvConf.TrustedProxies)
conf = &proxy.Config{
Logger: s.baseLogger.With(slogutil.KeyPrefix, "dnsproxy"),
Logger: s.baseLogger.With(slogutil.KeyPrefix, aghslog.PrefixDNSProxy),
HTTP3: srvConf.ServeHTTP3,
Ratelimit: int(srvConf.Ratelimit),
RatelimitSubnetLenIPv4: srvConf.RatelimitSubnetLenIPv4,
@@ -351,13 +365,6 @@ func (s *Server) newProxyConfig() (conf *proxy.Config, err error) {
return nil, fmt.Errorf("validating plain: %w", err)
}
if c := srvConf.DNSCryptConfig; c.Enabled {
conf.DNSCryptUDPListenAddr = c.UDPListenAddrs
conf.DNSCryptTCPListenAddr = c.TCPListenAddrs
conf.DNSCryptProviderName = c.ProviderName
conf.DNSCryptResolverCert = c.ResolverCert
}
conf, err = prepareCacheConfig(conf,
srvConf.CacheSize,
srvConf.CacheMinTTL,
@@ -608,8 +615,23 @@ func (conf *ServerConfig) ourAddrsSet() (m addrPortSet, err error) {
}
}
// prepareDNSCrypt sets up the DNSCrypt configuration for the DNS proxy.
func (s *Server) prepareDNSCrypt(proxyConf *proxy.Config) {
dnsCryptConf := s.conf.TLSConf.DNSCryptConf
if dnsCryptConf == nil {
return
}
proxyConf.DNSCryptUDPListenAddr = dnsCryptConf.UDPListenAddrs
proxyConf.DNSCryptTCPListenAddr = dnsCryptConf.TCPListenAddrs
proxyConf.DNSCryptProviderName = dnsCryptConf.ProviderName
proxyConf.DNSCryptResolverCert = dnsCryptConf.ResolverCert
}
// prepareTLS sets up the TLS configuration for the DNS proxy.
func (s *Server) prepareTLS(proxyConfig *proxy.Config) (err error) {
func (s *Server) prepareTLS(proxyConf *proxy.Config) (err error) {
s.prepareDNSCrypt(proxyConf)
if s.conf.TLSConf.Cert == nil {
return
}
@@ -618,8 +640,8 @@ func (s *Server) prepareTLS(proxyConfig *proxy.Config) (err error) {
return nil
}
proxyConfig.TLSListenAddr = s.conf.TLSConf.TLSListenAddrs
proxyConfig.QUICListenAddr = s.conf.TLSConf.QUICListenAddrs
proxyConf.TLSListenAddr = s.conf.TLSConf.TLSListenAddrs
proxyConf.QUICListenAddr = s.conf.TLSConf.QUICListenAddrs
cert, err := x509.ParseCertificate(s.conf.TLSConf.Cert.Certificate[0])
if err != nil {
@@ -639,7 +661,7 @@ func (s *Server) prepareTLS(proxyConfig *proxy.Config) (err error) {
}
}
proxyConfig.TLSConfig = &tls.Config{
proxyConf.TLSConfig = &tls.Config{
GetCertificate: s.onGetCertificate,
CipherSuites: s.conf.TLSCiphers,
MinVersion: tls.VersionTLS12,

View File

@@ -18,6 +18,7 @@ import (
"time"
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
"github.com/AdguardTeam/AdGuardHome/internal/aghslog"
"github.com/AdguardTeam/AdGuardHome/internal/client"
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/AdGuardHome/internal/querylog"
@@ -546,6 +547,7 @@ func (s *Server) prepareUpstreamSettings(boot upstream.Resolver) (err error) {
}
uc, err := newUpstreamConfig(upstreams, defaultDNS, &upstream.Options{
Logger: aghslog.NewForUpstream(s.baseLogger, aghslog.UpstreamTypeMain),
Bootstrap: boot,
Timeout: s.conf.UpstreamTimeout,
HTTPVersions: aghnet.UpstreamHTTPVersions(s.conf.UseHTTP3Upstreams),
@@ -612,6 +614,7 @@ func (s *Server) prepareLocalResolvers() (uc *proxy.UpstreamConfig, err error) {
}
opts := &upstream.Options{
Logger: aghslog.NewForUpstream(s.baseLogger, aghslog.UpstreamTypeLocal),
Bootstrap: s.bootstrap,
Timeout: defaultLocalTimeout,
// TODO(e.burkov): Should we verify server's certificates?
@@ -644,6 +647,7 @@ func (s *Server) prepareInternalDNS() (err error) {
}
bootOpts := &upstream.Options{
Logger: aghslog.NewForUpstream(s.baseLogger, aghslog.UpstreamTypeBootstrap),
Timeout: DefaultTimeout,
HTTPVersions: aghnet.UpstreamHTTPVersions(s.conf.UseHTTP3Upstreams),
}
@@ -682,6 +686,7 @@ func (s *Server) setupFallbackDNS() (uc *proxy.UpstreamConfig, err error) {
}
uc, err = proxy.ParseUpstreamsConfig(fallbacks, &upstream.Options{
Logger: aghslog.NewForUpstream(s.baseLogger, aghslog.UpstreamTypeFallback),
// TODO(s.chzhen): Investigate if other options are needed.
Timeout: s.conf.UpstreamTimeout,
PreferIPv6: s.conf.BootstrapPreferIPv6,
@@ -750,7 +755,7 @@ func validateBlockingMode(
func (s *Server) prepareInternalProxy() (err error) {
srvConf := s.conf
conf := &proxy.Config{
Logger: s.baseLogger.With(slogutil.KeyPrefix, "dnsproxy"),
Logger: s.baseLogger.With(slogutil.KeyPrefix, aghslog.PrefixDNSProxy),
CacheEnabled: true,
CacheSizeBytes: 4096,
PrivateRDNSUpstreamConfig: srvConf.PrivateRDNSUpstreamConfig,

View File

@@ -39,6 +39,9 @@ import (
"github.com/stretchr/testify/require"
)
// testLogger is a logger used in tests.
var testLogger = slogutil.NewDiscardLogger()
func TestMain(m *testing.M) {
testutil.DiscardLogOutput(m)
}
@@ -129,6 +132,8 @@ func createTestServer(
) (s *Server) {
t.Helper()
filterConf.Logger = cmp.Or(filterConf.Logger, testLogger)
rules := `||nxdomain.example.org
||NULL.example.org^
127.0.0.1 host.example.org
@@ -159,7 +164,7 @@ func createTestServer(
DHCPServer: dhcp,
DNSFilter: f,
PrivateNets: netutil.SubnetSetFunc(netutil.IsLocallyServed),
Logger: slogutil.NewDiscardLogger(),
Logger: testLogger,
})
require.NoError(t, err)
@@ -410,7 +415,7 @@ func TestServer_timeout(t *testing.T) {
s, err := NewServer(DNSCreateParams{
DNSFilter: createTestDNSFilter(t),
Logger: slogutil.NewDiscardLogger(),
Logger: testLogger,
})
require.NoError(t, err)
@@ -423,7 +428,7 @@ func TestServer_timeout(t *testing.T) {
t.Run("default", func(t *testing.T) {
s, err := NewServer(DNSCreateParams{
DNSFilter: createTestDNSFilter(t),
Logger: slogutil.NewDiscardLogger(),
Logger: testLogger,
})
require.NoError(t, err)
@@ -456,7 +461,7 @@ func TestServer_Prepare_fallbacks(t *testing.T) {
}
s, err := NewServer(DNSCreateParams{
Logger: slogutil.NewDiscardLogger(),
Logger: testLogger,
})
require.NoError(t, err)
@@ -527,7 +532,10 @@ func TestDoQServer(t *testing.T) {
// Create a DNS-over-QUIC upstream.
addr := s.dnsProxy.Addr(proxy.ProtoQUIC)
opts := &upstream.Options{InsecureSkipVerify: true}
opts := &upstream.Options{
Logger: testLogger,
InsecureSkipVerify: true,
}
u, err := upstream.AddressToUpstream(fmt.Sprintf("%s://%s", proxy.ProtoQUIC, addr), opts)
require.NoError(t, err)
@@ -584,6 +592,7 @@ func TestSafeSearch(t *testing.T) {
}
filterConf := &filtering.Config{
Logger: testLogger,
BlockingMode: filtering.BlockingModeDefault,
ProtectionEnabled: true,
SafeSearchConf: safeSearchConf,
@@ -593,7 +602,7 @@ func TestSafeSearch(t *testing.T) {
ctx := testutil.ContextWithTimeout(t, testTimeout)
safeSearch, err := safesearch.NewDefault(ctx, &safesearch.DefaultConfig{
Logger: slogutil.NewDiscardLogger(),
Logger: testLogger,
ServicesConfig: safeSearchConf,
CacheSize: filterConf.SafeSearchCacheSize,
CacheTTL: time.Minute * time.Duration(filterConf.CacheTime),
@@ -1055,6 +1064,7 @@ func TestBlockedCustomIP(t *testing.T) {
}}
f, err := filtering.New(&filtering.Config{
Logger: testLogger,
ProtectionEnabled: true,
ApplyClientFiltering: applyEmptyClientFiltering,
BlockedServices: emptyFilteringBlockedServices(),
@@ -1073,7 +1083,7 @@ func TestBlockedCustomIP(t *testing.T) {
DHCPServer: dhcp,
DNSFilter: f,
PrivateNets: netutil.SubnetSetFunc(netutil.IsLocallyServed),
Logger: slogutil.NewDiscardLogger(),
Logger: testLogger,
})
require.NoError(t, err)
@@ -1173,6 +1183,7 @@ func TestBlockedBySafeBrowsing(t *testing.T) {
)
sbChecker := hashprefix.New(&hashprefix.Config{
Logger: testLogger,
CacheTime: cacheTime,
CacheSize: cacheSize,
Upstream: aghtest.NewBlockUpstream(hostname, true),
@@ -1216,6 +1227,7 @@ func TestBlockedBySafeBrowsing(t *testing.T) {
func TestRewrite(t *testing.T) {
c := &filtering.Config{
Logger: testLogger,
ApplyClientFiltering: applyEmptyClientFiltering,
BlockedServices: emptyFilteringBlockedServices(),
BlockingMode: filtering.BlockingModeDefault,
@@ -1247,7 +1259,7 @@ func TestRewrite(t *testing.T) {
DHCPServer: dhcp,
DNSFilter: f,
PrivateNets: netutil.SubnetSetFunc(netutil.IsLocallyServed),
Logger: slogutil.NewDiscardLogger(),
Logger: testLogger,
})
require.NoError(t, err)
@@ -1365,6 +1377,7 @@ func TestPTRResponseFromDHCPLeases(t *testing.T) {
const localDomain = "lan"
flt, err := filtering.New(&filtering.Config{
Logger: testLogger,
ApplyClientFiltering: applyEmptyClientFiltering,
BlockedServices: emptyFilteringBlockedServices(),
BlockingMode: filtering.BlockingModeDefault,
@@ -1381,7 +1394,7 @@ func TestPTRResponseFromDHCPLeases(t *testing.T) {
},
},
PrivateNets: netutil.SubnetSetFunc(netutil.IsLocallyServed),
Logger: slogutil.NewDiscardLogger(),
Logger: testLogger,
LocalDomain: localDomain,
})
require.NoError(t, err)
@@ -1457,6 +1470,7 @@ func TestPTRResponseFromHosts(t *testing.T) {
})
flt, err := filtering.New(&filtering.Config{
Logger: testLogger,
ApplyClientFiltering: applyEmptyClientFiltering,
BlockedServices: emptyFilteringBlockedServices(),
BlockingMode: filtering.BlockingModeDefault,
@@ -1471,7 +1485,7 @@ func TestPTRResponseFromHosts(t *testing.T) {
DHCPServer: dhcp,
DNSFilter: flt,
PrivateNets: netutil.SubnetSetFunc(netutil.IsLocallyServed),
Logger: slogutil.NewDiscardLogger(),
Logger: testLogger,
})
require.NoError(t, err)
@@ -1527,27 +1541,27 @@ func TestNewServer(t *testing.T) {
}{{
name: "success",
in: DNSCreateParams{
Logger: slogutil.NewDiscardLogger(),
Logger: testLogger,
},
wantErrMsg: "",
}, {
name: "success_local_tld",
in: DNSCreateParams{
Logger: slogutil.NewDiscardLogger(),
Logger: testLogger,
LocalDomain: "mynet",
},
wantErrMsg: "",
}, {
name: "success_local_domain",
in: DNSCreateParams{
Logger: slogutil.NewDiscardLogger(),
Logger: testLogger,
LocalDomain: "my.local.net",
},
wantErrMsg: "",
}, {
name: "bad_local_domain",
in: DNSCreateParams{
Logger: slogutil.NewDiscardLogger(),
Logger: testLogger,
LocalDomain: "!!!",
},
wantErrMsg: `local domain: bad domain name "!!!": ` +

View File

@@ -9,7 +9,6 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/dnsproxy/upstream"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/netutil"
"github.com/miekg/dns"
"github.com/stretchr/testify/assert"
@@ -46,6 +45,7 @@ func TestHandleDNSRequest_handleDNSRequest(t *testing.T) {
}}
f, err := filtering.New(&filtering.Config{
Logger: testLogger,
ProtectionEnabled: true,
ApplyClientFiltering: applyEmptyClientFiltering,
BlockedServices: emptyFilteringBlockedServices(),
@@ -62,7 +62,7 @@ func TestHandleDNSRequest_handleDNSRequest(t *testing.T) {
},
DNSFilter: f,
PrivateNets: netutil.SubnetSetFunc(netutil.IsLocallyServed),
Logger: slogutil.NewDiscardLogger(),
Logger: testLogger,
})
require.NoError(t, err)
@@ -226,7 +226,9 @@ func TestHandleDNSRequest_filterDNSResponse(t *testing.T) {
ID: 0, Data: []byte(blockRules),
}}
f, err := filtering.New(&filtering.Config{}, filters)
f, err := filtering.New(&filtering.Config{
Logger: testLogger,
}, filters)
require.NoError(t, err)
f.SetEnabled(true)
@@ -235,7 +237,7 @@ func TestHandleDNSRequest_filterDNSResponse(t *testing.T) {
DHCPServer: &testDHCP{},
DNSFilter: f,
PrivateNets: netutil.SubnetSetFunc(netutil.IsLocallyServed),
Logger: slogutil.NewDiscardLogger(),
Logger: testLogger,
})
require.NoError(t, err)

View File

@@ -12,11 +12,13 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
"github.com/AdguardTeam/AdGuardHome/internal/aghslog"
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/dnsproxy/upstream"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/stringutil"
"github.com/AdguardTeam/golibs/validate"
@@ -366,7 +368,9 @@ func (req *jsonDNSConfig) checkPrivateRDNS(
addrs := cmp.Or(req.LocalPTRUpstreams, &[]string{})
uc, err := newPrivateConfig(*addrs, ownAddrs, sysResolvers, privateNets, &upstream.Options{})
uc, err := newPrivateConfig(*addrs, ownAddrs, sysResolvers, privateNets, &upstream.Options{
Logger: slogutil.NewDiscardLogger(),
})
err = errors.WithDeferred(err, uc.Close())
if err != nil {
return fmt.Errorf("private upstream servers: %w", err)
@@ -382,7 +386,9 @@ func (req *jsonDNSConfig) validateUpstreamDNSServers(
privateNets netutil.SubnetSet,
) (err error) {
var uc *proxy.UpstreamConfig
opts := &upstream.Options{}
opts := &upstream.Options{
Logger: slogutil.NewDiscardLogger(),
}
if req.Upstreams != nil {
uc, err = proxy.ParseUpstreamsConfig(*req.Upstreams, opts)
@@ -651,6 +657,7 @@ func (s *Server) handleTestUpstreamDNS(w http.ResponseWriter, r *http.Request) {
req.BootstrapDNS = stringutil.FilterOut(req.BootstrapDNS, aghnet.IsCommentOrEmpty)
opts := &upstream.Options{
Logger: aghslog.NewForUpstream(s.baseLogger, aghslog.UpstreamTypeTest),
Timeout: s.conf.UpstreamTimeout,
PreferIPv6: s.conf.BootstrapPreferIPv6,
}

View File

@@ -6,7 +6,6 @@ import (
"testing"
"github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/miekg/dns"
"github.com/stretchr/testify/assert"
)
@@ -61,7 +60,7 @@ func TestIpsetCtx_process(t *testing.T) {
}
ictx := &ipsetHandler{
logger: slogutil.NewDiscardLogger(),
logger: testLogger,
}
rc := ictx.process(dctx)
assert.Equal(t, resultCodeSuccess, rc)
@@ -83,7 +82,7 @@ func TestIpsetCtx_process(t *testing.T) {
m := &fakeIpsetMgr{}
ictx := &ipsetHandler{
ipsetMgr: m,
logger: slogutil.NewDiscardLogger(),
logger: testLogger,
}
rc := ictx.process(dctx)
@@ -108,7 +107,7 @@ func TestIpsetCtx_process(t *testing.T) {
m := &fakeIpsetMgr{}
ictx := &ipsetHandler{
ipsetMgr: m,
logger: slogutil.NewDiscardLogger(),
logger: testLogger,
}
rc := ictx.process(dctx)
@@ -132,7 +131,7 @@ func TestIpsetCtx_SkipIpsetProcessing(t *testing.T) {
m := &fakeIpsetMgr{}
ictx := &ipsetHandler{
ipsetMgr: m,
logger: slogutil.NewDiscardLogger(),
logger: testLogger,
}
testCases := []struct {

View File

@@ -12,7 +12,6 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/dnsproxy/upstream"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/testutil"
"github.com/AdguardTeam/urlfilter/rules"
@@ -378,6 +377,7 @@ func createTestDNSFilter(t *testing.T) (f *filtering.DNSFilter) {
t.Helper()
f, err := filtering.New(&filtering.Config{
Logger: testLogger,
BlockingMode: filtering.BlockingModeDefault,
}, []filtering.Filter{})
require.NoError(t, err)
@@ -439,7 +439,7 @@ func TestServer_ProcessDHCPHosts_localRestriction(t *testing.T) {
dnsFilter: createTestDNSFilter(t),
dhcpServer: dhcp,
localDomainSuffix: localDomainSuffix,
baseLogger: slogutil.NewDiscardLogger(),
baseLogger: testLogger,
}
req := &dns.Msg{
@@ -591,7 +591,7 @@ func TestServer_ProcessDHCPHosts(t *testing.T) {
dnsFilter: createTestDNSFilter(t),
dhcpServer: testDHCP,
localDomainSuffix: tc.suffix,
baseLogger: slogutil.NewDiscardLogger(),
baseLogger: testLogger,
}
req := (&dns.Msg{}).SetQuestion(dns.Fqdn(tc.host), tc.qtyp)

View File

@@ -11,7 +11,6 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/stats"
"github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/dnsproxy/upstream"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/miekg/dns"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -203,7 +202,7 @@ func TestServer_ProcessQueryLogsAndStats(t *testing.T) {
ql := &testQueryLog{}
st := &testStats{}
srv := &Server{
baseLogger: slogutil.NewDiscardLogger(),
baseLogger: testLogger,
queryLog: ql,
stats: st,
anonymizer: aghnet.NewIPMut(nil),

View File

@@ -144,6 +144,7 @@ func TestUpstreamConfigValidator(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
cv := newUpstreamConfigValidator(tc.general, tc.fallback, tc.private, &upstream.Options{
Logger: testLogger,
Timeout: upsTimeout,
Bootstrap: net.DefaultResolver,
})
@@ -195,6 +196,7 @@ func TestUpstreamConfigValidator_Check_once(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
cv := newUpstreamConfigValidator(tc.ups, nil, nil, &upstream.Options{
Logger: testLogger,
Timeout: testTimeout,
})

View File

@@ -1,8 +1,10 @@
package filtering
import (
"context"
"encoding/json"
"fmt"
"log/slog"
"net/http"
"slices"
"time"
@@ -10,7 +12,7 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
"github.com/AdguardTeam/AdGuardHome/internal/filtering/rulelist"
"github.com/AdguardTeam/AdGuardHome/internal/schedule"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/urlfilter/rules"
)
@@ -20,23 +22,30 @@ var serviceRules map[string][]*rules.NetworkRule
// serviceIDs contains service IDs sorted alphabetically.
var serviceIDs []string
// initBlockedServices initializes package-level blocked service data.
func initBlockedServices() {
l := len(blockedServices)
serviceIDs = make([]string, l)
serviceRules = make(map[string][]*rules.NetworkRule, l)
// initBlockedServices initializes package-level blocked service data. l must
// not be nil.
func initBlockedServices(ctx context.Context, l *slog.Logger) {
svcLen := len(blockedServices)
serviceIDs = make([]string, svcLen)
serviceRules = make(map[string][]*rules.NetworkRule, svcLen)
for i, s := range blockedServices {
netRules := make([]*rules.NetworkRule, 0, len(s.Rules))
for _, text := range s.Rules {
rule, err := rules.NewNetworkRule(text, rulelist.URLFilterIDBlockedService)
if err != nil {
log.Error("parsing blocked service %q rule %q: %s", s.ID, text, err)
if err == nil {
netRules = append(netRules, rule)
continue
}
netRules = append(netRules, rule)
l.ErrorContext(
ctx,
"parsing blocked service rule",
"svc", s.ID,
"rule", text,
slogutil.KeyError, err,
)
}
serviceIDs[i] = s.ID
@@ -45,7 +54,7 @@ func initBlockedServices() {
slices.Sort(serviceIDs)
log.Debug("filtering: initialized %d services", l)
l.DebugContext(ctx, "initialized services", "svc_len", svcLen)
}
// BlockedServices is the configuration of blocked services.
@@ -105,7 +114,7 @@ func (d *DNSFilter) ApplyBlockedServicesList(setts *Settings, list []string) {
for _, name := range list {
rules, ok := serviceRules[name]
if !ok {
log.Error("unknown service name: %s", name)
d.logger.ErrorContext(context.TODO(), "unknown service name", "name", name)
continue
}
@@ -163,7 +172,7 @@ func (d *DNSFilter) handleBlockedServicesSet(w http.ResponseWriter, r *http.Requ
defer d.confMu.Unlock()
d.conf.BlockedServices.IDs = list
log.Debug("Updated blocked services list: %d", len(list))
d.logger.DebugContext(r.Context(), "updated blocked services list", "len", len(list))
}()
d.conf.ConfigModified()
@@ -212,7 +221,7 @@ func (d *DNSFilter) handleBlockedServicesUpdate(w http.ResponseWriter, r *http.R
d.conf.BlockedServices = bsvc
}()
log.Debug("updated blocked services schedule: %d", len(bsvc.IDs))
d.logger.DebugContext(r.Context(), "updated blocked services schedule", "len", len(bsvc.IDs))
d.conf.ConfigModified()
}

View File

@@ -6,6 +6,7 @@ import (
"testing"
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/netutil"
"github.com/miekg/dns"
"github.com/stretchr/testify/assert"
@@ -52,6 +53,7 @@ func TestDNSFilter_CheckHostRules_dnsrewrite(t *testing.T) {
`
conf := &filtering.Config{
Logger: slogutil.NewDiscardLogger(),
SafeBrowsingCacheSize: 10000,
ParentalCacheSize: 10000,
SafeSearchCacheSize: 1000,

View File

@@ -1,6 +1,7 @@
package filtering
import (
"context"
"fmt"
"io"
"net/http"
@@ -17,7 +18,7 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/filtering/rulelist"
"github.com/AdguardTeam/golibs/container"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/logutil/slogutil"
)
// filterDir is the subdirectory of a data directory to store downloaded
@@ -105,12 +106,13 @@ func (d *DNSFilter) filterSetProperties(
}
flt := &filters[i]
log.Debug(
"filtering: set name to %q, url to %s, enabled to %t for filter %s",
newList.Name,
newList.URL,
newList.Enabled,
flt.URL,
d.logger.DebugContext(
context.TODO(),
"updating filter",
"name", newList.Name,
"url", newList.URL,
"enabled", newList.Enabled,
"filter_url", flt.URL,
)
defer func(oldURL, oldName string, oldEnabled bool, oldUpdated time.Time, oldRulesCount int) {
@@ -213,12 +215,12 @@ func (d *DNSFilter) filterAdd(flt FilterYAML) (err error) {
// Load filters from the disk
// And if any filter has zero ID, assign a new one
func (d *DNSFilter) loadFilters(array []FilterYAML) {
func (d *DNSFilter) loadFilters(ctx context.Context, array []FilterYAML) {
for i := range array {
filter := &array[i] // otherwise we're operating on a copy
if filter.ID == 0 {
newID := d.idGen.next()
log.Info("filtering: warning: filter at index %d has no id; assigning to %d", i, newID)
d.logger.WarnContext(ctx, "filter has no id", "idx", i, "new_id", newID)
filter.ID = newID
}
@@ -228,9 +230,9 @@ func (d *DNSFilter) loadFilters(array []FilterYAML) {
continue
}
err := d.load(filter)
err := d.load(ctx, filter)
if err != nil {
log.Error("filtering: loading filter %d: %s", filter.ID, err)
d.logger.ErrorContext(ctx, "loading filter", "id", filter.ID, slogutil.KeyError, err)
}
}
}
@@ -300,10 +302,15 @@ func (d *DNSFilter) listsToUpdate(filters *[]FilterYAML, force bool) (toUpd []Fi
return toUpd
}
func (d *DNSFilter) refreshFiltersArray(filters *[]FilterYAML, force bool) (int, []FilterYAML, []bool, bool) {
var updateFlags []bool // 'true' if filter data has changed
updateFilters := d.listsToUpdate(filters, force)
// refreshFiltersArray updates the filters array and returns the number of
// filters that have been refreshed. updateFlags is true if filter data has
// changed.
func (d *DNSFilter) refreshFiltersArray(
ctx context.Context,
filters *[]FilterYAML,
force bool,
) (updateCount int, updateFilters []FilterYAML, updateFlags []bool, isNetErr bool) {
updateFilters = d.listsToUpdate(filters, force)
if len(updateFilters) == 0 {
return 0, nil, nil, false
}
@@ -315,7 +322,7 @@ func (d *DNSFilter) refreshFiltersArray(filters *[]FilterYAML, force bool) (int,
updateFlags = append(updateFlags, updated)
if err != nil {
failNum++
log.Error("filtering: updating filter from url %q: %s\n", uf.URL, err)
d.logger.ErrorContext(ctx, "updating filter", "url", uf.URL, slogutil.KeyError, err)
continue
}
@@ -325,8 +332,6 @@ func (d *DNSFilter) refreshFiltersArray(filters *[]FilterYAML, force bool) (int,
return 0, nil, nil, true
}
updateCount := 0
d.conf.filtersMu.Lock()
defer d.conf.filtersMu.Unlock()
@@ -345,11 +350,12 @@ func (d *DNSFilter) refreshFiltersArray(filters *[]FilterYAML, force bool) (int,
continue
}
log.Info(
"filtering: updated filter %d; rule count: %d (was %d)",
f.ID,
uf.RulesCount,
f.RulesCount,
d.logger.InfoContext(
ctx,
"updated filter",
"id", f.ID,
"rules_count", uf.RulesCount,
"prev_rules_count", f.RulesCount,
)
f.Name = uf.Name
@@ -381,19 +387,27 @@ func (d *DNSFilter) refreshFiltersArray(filters *[]FilterYAML, force bool) (int,
//
// TODO(a.garipov, e.burkov): What the hell?
func (d *DNSFilter) refreshFiltersIntl(block, allow, force bool) (int, bool) {
ctx := context.TODO()
updNum := 0
log.Debug("filtering: starting updating")
defer func() { log.Debug("filtering: finished updating, %d updated", updNum) }()
d.logger.DebugContext(ctx, "starting update")
defer func() {
d.logger.DebugContext(ctx, "finished update", "updated", updNum)
}()
var lists []FilterYAML
var toUpd []bool
isNetErr := false
if block {
updNum, lists, toUpd, isNetErr = d.refreshFiltersArray(&d.conf.Filters, force)
updNum, lists, toUpd, isNetErr = d.refreshFiltersArray(ctx, &d.conf.Filters, force)
}
if allow {
updNumAl, listsAl, toUpdAl, isNetErrAl := d.refreshFiltersArray(&d.conf.WhitelistFilters, force)
updNumAl, listsAl, toUpdAl, isNetErrAl := d.refreshFiltersArray(
ctx,
&d.conf.WhitelistFilters,
force,
)
updNum += updNumAl
lists = append(lists, listsAl...)
@@ -417,7 +431,7 @@ func (d *DNSFilter) refreshFiltersIntl(block, allow, force bool) (int, bool) {
p := uf.Path(d.conf.DataDir)
err := os.Remove(p + ".old")
if err != nil {
log.Debug("filtering: removing old filter file %q: %s", p, err)
d.logger.ErrorContext(ctx, "removing old filter", "path", p, slogutil.KeyError, err)
}
}
}
@@ -427,7 +441,9 @@ func (d *DNSFilter) refreshFiltersIntl(block, allow, force bool) (int, bool) {
// update refreshes filter's content and a/mtimes of it's file.
func (d *DNSFilter) update(filter *FilterYAML) (b bool, err error) {
b, err = d.updateIntl(filter)
ctx := context.TODO()
b, err = d.updateIntl(ctx, filter)
filter.LastUpdated = time.Now()
if !b {
chErr := os.Chtimes(
@@ -436,7 +452,7 @@ func (d *DNSFilter) update(filter *FilterYAML) (b bool, err error) {
filter.LastUpdated,
)
if chErr != nil {
log.Error("filtering: os.Chtimes(): %s", chErr)
d.logger.ErrorContext(ctx, "changing last modified time", slogutil.KeyError, chErr)
}
}
@@ -445,8 +461,8 @@ func (d *DNSFilter) update(filter *FilterYAML) (b bool, err error) {
// updateIntl updates the flt rewriting it's actual file. It returns true if
// the actual update has been performed.
func (d *DNSFilter) updateIntl(flt *FilterYAML) (ok bool, err error) {
log.Debug("filtering: downloading update for filter %d from %q", flt.ID, flt.URL)
func (d *DNSFilter) updateIntl(ctx context.Context, flt *FilterYAML) (ok bool, err error) {
d.logger.DebugContext(ctx, "downloading update for filter", "id", flt.ID, "url", flt.URL)
var res *rulelist.ParseResult
@@ -454,7 +470,7 @@ func (d *DNSFilter) updateIntl(flt *FilterYAML) (ok bool, err error) {
if err != nil {
return false, err
}
defer func() { err = d.finalizeUpdate(tmpFile, flt, res, err, ok) }()
defer func() { err = d.finalizeUpdate(ctx, tmpFile, flt, res, err, ok) }()
r, err := d.reader(flt.URL)
if err != nil {
@@ -476,6 +492,7 @@ func (d *DNSFilter) updateIntl(flt *FilterYAML) (ok bool, err error) {
// according to updated. It also saves new values of flt's name, rules number
// and checksum if succeeded.
func (d *DNSFilter) finalizeUpdate(
ctx context.Context,
file aghrenameio.PendingFile,
flt *FilterYAML,
res *rulelist.ParseResult,
@@ -485,13 +502,13 @@ func (d *DNSFilter) finalizeUpdate(
id := flt.ID
if !updated {
if returned == nil {
log.Debug("filtering: filter %d from url %q has no changes, skipping", id, flt.URL)
d.logger.DebugContext(ctx, "skipping filter with no changes", "id", id, "url", flt.URL)
}
return errors.WithDeferred(returned, file.Cleanup())
}
log.Info("filtering: saving contents of filter %d into %q", id, flt.Path(d.conf.DataDir))
d.logger.InfoContext(ctx, "saving contents", "id", id, "path", flt.Path(d.conf.DataDir))
err = file.CloseReplace()
if err != nil {
@@ -499,7 +516,13 @@ func (d *DNSFilter) finalizeUpdate(
}
rulesCount := res.RulesCount
log.Info("filtering: updated filter %d: %d bytes, %d rules", id, res.BytesWritten, rulesCount)
d.logger.InfoContext(
ctx,
"filter updated",
"id", id,
"bytes_written", res.BytesWritten,
"rules_count", rulesCount,
)
flt.ensureName(res.Title)
flt.checksum = res.Checksum
@@ -550,10 +573,10 @@ func (d *DNSFilter) readerFromURL(fltURL string) (r io.ReadCloser, err error) {
}
// loads filter contents from the file in dataDir
func (d *DNSFilter) load(flt *FilterYAML) (err error) {
func (d *DNSFilter) load(ctx context.Context, flt *FilterYAML) (err error) {
fileName := flt.Path(d.conf.DataDir)
log.Debug("filtering: loading filter %d from %q", flt.ID, fileName)
d.logger.DebugContext(ctx, "loading filter", "id", flt.ID, "path", fileName)
file, err := os.Open(fileName)
if errors.Is(err, os.ErrNotExist) {
@@ -569,7 +592,7 @@ func (d *DNSFilter) load(flt *FilterYAML) (err error) {
return fmt.Errorf("getting filter file stat: %w", err)
}
log.Debug("filtering: file %q, id %d, length %d", fileName, flt.ID, st.Size())
d.logger.DebugContext(ctx, "filter file", "id", flt.ID, "path", fileName, "len", st.Size())
bufPtr := d.bufPool.Get()
defer d.bufPool.Put(bufPtr)
@@ -586,14 +609,16 @@ func (d *DNSFilter) load(flt *FilterYAML) (err error) {
return nil
}
// EnableFilters enables filters.
func (d *DNSFilter) EnableFilters(async bool) {
d.conf.filtersMu.RLock()
defer d.conf.filtersMu.RUnlock()
d.enableFiltersLocked(async)
d.enableFiltersLocked(context.TODO(), async)
}
func (d *DNSFilter) enableFiltersLocked(async bool) {
// enableFiltersLocked enables filters under the conf.filtersMu lock.
func (d *DNSFilter) enableFiltersLocked(ctx context.Context, async bool) {
filters := make([]Filter, 1, len(d.conf.Filters)+len(d.conf.WhitelistFilters)+1)
filters[0] = Filter{
ID: rulelist.URLFilterIDCustom,
@@ -623,9 +648,9 @@ func (d *DNSFilter) enableFiltersLocked(async bool) {
})
}
err := d.setFilters(filters, allowFilters, async)
err := d.setFilters(ctx, filters, allowFilters, async)
if err != nil {
log.Error("filtering: enabling filters: %s", err)
d.logger.ErrorContext(ctx, "enabling filters", slogutil.KeyError, err)
}
d.SetEnabled(d.conf.FilteringEnabled)

View File

@@ -1,6 +1,7 @@
package filtering
import (
"context"
"net"
"net/http"
"net/url"
@@ -9,6 +10,7 @@ import (
"testing"
"time"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/netutil/urlutil"
"github.com/AdguardTeam/golibs/testutil"
"github.com/stretchr/testify/assert"
@@ -56,6 +58,7 @@ func serveFiltersLocally(t *testing.T, fltContent []byte) (urlStr string) {
// count.
func updateAndAssert(
t *testing.T,
ctx context.Context,
dnsFilter *DNSFilter,
f *FilterYAML,
wantUpd require.BoolAssertionFunc,
@@ -75,7 +78,7 @@ func updateAndAssert(
assert.Len(t, dir, 1)
err = dnsFilter.load(f)
err = dnsFilter.load(ctx, f)
require.NoError(t, err)
}
@@ -84,6 +87,7 @@ func newDNSFilter(t *testing.T) (d *DNSFilter) {
t.Helper()
dnsFilter, err := New(&Config{
Logger: slogutil.NewDiscardLogger(),
DataDir: t.TempDir(),
HTTPClient: &http.Client{
Timeout: testTimeout,
@@ -95,6 +99,8 @@ func newDNSFilter(t *testing.T) (d *DNSFilter) {
}
func TestDNSFilter_Update(t *testing.T) {
ctx := testutil.ContextWithTimeout(t, testTimeout)
const content = `||example.org^$third-party
# Inline comment example
||example.com^$third-party
@@ -111,11 +117,11 @@ func TestDNSFilter_Update(t *testing.T) {
dnsFilter := newDNSFilter(t)
t.Run("download", func(t *testing.T) {
updateAndAssert(t, dnsFilter, f, require.True, 3)
updateAndAssert(t, ctx, dnsFilter, f, require.True, 3)
})
t.Run("refresh_idle", func(t *testing.T) {
updateAndAssert(t, dnsFilter, f, require.False, 3)
updateAndAssert(t, ctx, dnsFilter, f, require.False, 3)
})
t.Run("refresh_actually", func(t *testing.T) {
@@ -125,11 +131,11 @@ func TestDNSFilter_Update(t *testing.T) {
f.URL = serveFiltersLocally(t, anotherContent)
t.Cleanup(func() { f.URL = oldURL })
updateAndAssert(t, dnsFilter, f, require.True, 1)
updateAndAssert(t, ctx, dnsFilter, f, require.True, 1)
})
t.Run("load_unload", func(t *testing.T) {
err := dnsFilter.load(f)
err := dnsFilter.load(ctx, f)
require.NoError(t, err)
f.unload()
@@ -137,6 +143,8 @@ func TestDNSFilter_Update(t *testing.T) {
}
func TestFilterYAML_EnsureName(t *testing.T) {
ctx := testutil.ContextWithTimeout(t, testTimeout)
dnsFilter := newDNSFilter(t)
t.Run("title_custom", func(t *testing.T) {
@@ -147,7 +155,7 @@ func TestFilterYAML_EnsureName(t *testing.T) {
Name: "user-custom",
}
updateAndAssert(t, dnsFilter, f, require.True, 1)
updateAndAssert(t, ctx, dnsFilter, f, require.True, 1)
assert.Equal(t, "user-custom", f.Name)
})
@@ -158,7 +166,7 @@ func TestFilterYAML_EnsureName(t *testing.T) {
URL: serveFiltersLocally(t, content),
}
updateAndAssert(t, dnsFilter, f, require.True, 1)
updateAndAssert(t, ctx, dnsFilter, f, require.True, 1)
assert.Equal(t, "src-title", f.Name)
})
@@ -169,7 +177,7 @@ func TestFilterYAML_EnsureName(t *testing.T) {
URL: serveFiltersLocally(t, content),
}
updateAndAssert(t, dnsFilter, f, require.True, 1)
updateAndAssert(t, ctx, dnsFilter, f, require.True, 1)
assert.Equal(t, "List 0", f.Name)
})
}

View File

@@ -5,6 +5,7 @@ import (
"context"
"fmt"
"io/fs"
"log/slog"
"net"
"net/http"
"net/netip"
@@ -24,7 +25,7 @@ import (
"github.com/AdguardTeam/golibs/container"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/hostsfile"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/mathutil"
"github.com/AdguardTeam/golibs/syncutil"
"github.com/AdguardTeam/urlfilter"
@@ -70,6 +71,10 @@ type Resolver interface {
// Config allows you to configure DNS filtering with New() or just change variables directly.
type Config struct {
// logger is used to log the operations of DNS filtering. It must not be
// nil.
Logger *slog.Logger `yaml:"-"`
// BlockingIPv4 is the IP address to be returned for a blocked A request.
BlockingIPv4 netip.Addr `yaml:"blocking_ipv4"`
@@ -235,6 +240,9 @@ type Checker interface {
// DNSFilter matches hostnames and DNS requests against filtering rules.
type DNSFilter struct {
// logger is used for logging the filtering process.
logger *slog.Logger
// idGen is used to generate IDs for package urlfilter.
idGen *idGenerator
@@ -413,7 +421,12 @@ func (d *DNSFilter) WriteDiskConfig(c *Config) {
// filters are ready.
//
// In this case the caller must ensure that the old filter files are intact.
func (d *DNSFilter) setFilters(blockFilters, allowFilters []Filter, async bool) error {
func (d *DNSFilter) setFilters(
ctx context.Context,
blockFilters []Filter,
allowFilters []Filter,
async bool,
) (err error) {
if async {
params := filtersInitializerParams{
allowFilters: allowFilters,
@@ -439,7 +452,7 @@ func (d *DNSFilter) setFilters(blockFilters, allowFilters []Filter, async bool)
return nil
}
return d.initFiltering(allowFilters, blockFilters)
return d.initFiltering(ctx, allowFilters, blockFilters)
}
// Close - close the object
@@ -451,19 +464,19 @@ func (d *DNSFilter) Close() {
d.done <- struct{}{}
}
d.reset()
d.reset(context.TODO())
}
func (d *DNSFilter) reset() {
func (d *DNSFilter) reset(ctx context.Context) {
if d.rulesStorage != nil {
if err := d.rulesStorage.Close(); err != nil {
log.Error("filtering: rulesStorage.Close: %s", err)
d.logger.ErrorContext(ctx, "closing rules storage", slogutil.KeyError, err)
}
}
if d.rulesStorageAllow != nil {
if err := d.rulesStorageAllow.Close(); err != nil {
log.Error("filtering: rulesStorageAllow.Close: %s", err)
d.logger.ErrorContext(ctx, "closing allow rules storage", slogutil.KeyError, err)
}
}
}
@@ -649,6 +662,8 @@ func (d *DNSFilter) processRewrites(host string, qtype uint16) (res Result) {
d.confMu.RLock()
defer d.confMu.RUnlock()
ctx := context.TODO()
rewrites, matched := findRewrites(d.conf.Rewrites, host, qtype)
if !matched {
return Result{}
@@ -663,7 +678,7 @@ func (d *DNSFilter) processRewrites(host string, qtype uint16) (res Result) {
rwPat := rw.Domain
rwAns := rw.Answer
log.Debug("rewrite: cname for %s is %s", host, rwAns)
d.logger.DebugContext(ctx, "found rewrite", "host", host, "cname", rwAns)
if origHost == rwAns || rwPat == rwAns {
// Either a request for the hostname itself or a rewrite of
@@ -682,7 +697,7 @@ func (d *DNSFilter) processRewrites(host string, qtype uint16) (res Result) {
host = rwAns
if cnames.Has(host) {
log.Info("rewrite: cname loop for %q on %q", origHost, host)
d.logger.InfoContext(ctx, "cname loop", "host", host, "original", origHost)
return res
}
@@ -692,15 +707,15 @@ func (d *DNSFilter) processRewrites(host string, qtype uint16) (res Result) {
rewrites, matched = findRewrites(d.conf.Rewrites, host, qtype)
}
setRewriteResult(&res, host, rewrites, qtype)
d.setRewriteResult(ctx, &res, host, rewrites, qtype)
return res
}
// matchBlockedServicesRules checks the host against the blocked services rules
// in settings, if any. The err is always nil, it is only there to make this
// a valid hostChecker function.
func matchBlockedServicesRules(
// in settings, if any. err is always nil, it is only there to make this a
// valid hostChecker function.
func (d *DNSFilter) matchBlockedServicesRules(
host string,
_ uint16,
setts *Settings,
@@ -728,8 +743,13 @@ func matchBlockedServicesRules(
Text: ruleText,
}}
log.Debug("blocked services: matched rule: %s host: %s service: %s",
ruleText, host, s.Name)
d.logger.DebugContext(
context.TODO(),
"blocked services matched rule",
"rule", ruleText,
"host", host,
"service", s.Name,
)
return res, nil
}
@@ -793,7 +813,7 @@ func newRuleStorage(filters []Filter) (rs *filterlist.RuleStorage, err error) {
}
// Initialize urlfilter objects.
func (d *DNSFilter) initFiltering(allowFilters, blockFilters []Filter) (err error) {
func (d *DNSFilter) initFiltering(ctx context.Context, allowFilters, blockFilters []Filter) (err error) {
rulesStorage, err := newRuleStorage(blockFilters)
if err != nil {
return err
@@ -811,7 +831,7 @@ func (d *DNSFilter) initFiltering(allowFilters, blockFilters []Filter) (err erro
d.engineLock.Lock()
defer d.engineLock.Unlock()
d.reset()
d.reset(ctx)
d.rulesStorage = rulesStorage
d.filteringEngine = filteringEngine
d.rulesStorageAllow = rulesStorageAllow
@@ -821,7 +841,7 @@ func (d *DNSFilter) initFiltering(allowFilters, blockFilters []Filter) (err erro
// Make sure that the OS reclaims memory as soon as possible.
debug.FreeOSMemory()
log.Debug("filtering: initialized filtering engine")
d.logger.DebugContext(ctx, "initialized filtering engine")
return nil
}
@@ -843,6 +863,7 @@ func hostRulesToRules(netRules []*rules.HostRule) (res []rules.Rule) {
// matchHostProcessAllowList processes the allowlist logic of host matching.
func (d *DNSFilter) matchHostProcessAllowList(
ctx context.Context,
host string,
dnsres *urlfilter.DNSResult,
) (res Result, err error) {
@@ -859,7 +880,12 @@ func (d *DNSFilter) matchHostProcessAllowList(
return Result{}, fmt.Errorf("invalid dns result: rules are empty")
}
log.Debug("filtering: allowlist rules for host %q: %+v", host, matchedRules)
d.logger.DebugContext(
ctx,
"allowlist rules for host",
"host", host,
"rules", matchedRules,
)
return makeResult(matchedRules, NotFilteredAllowList), nil
}
@@ -929,6 +955,8 @@ func (d *DNSFilter) matchHost(
return Result{}, nil
}
ctx := context.TODO()
ufReq := &urlfilter.DNSRequest{
Hostname: host,
SortedClientTags: setts.ClientTags,
@@ -947,7 +975,7 @@ func (d *DNSFilter) matchHost(
if setts.ProtectionEnabled && d.filteringEngineAllow != nil {
dnsres, ok := d.filteringEngineAllow.MatchRequest(ufReq)
if ok {
return d.matchHostProcessAllowList(host, dnsres)
return d.matchHostProcessAllowList(ctx, host, dnsres)
}
}
@@ -972,11 +1000,12 @@ func (d *DNSFilter) matchHost(
res = d.matchHostProcessDNSResult(rrtype, dnsres)
for _, r := range res.Rules {
log.Debug(
"filtering: found rule %q for host %q, filter list id: %d",
r.Text,
host,
r.FilterListID,
d.logger.DebugContext(
ctx,
"found rule for host",
"host", host,
"rule", r.Text,
"filter_list_id", r.FilterListID,
)
}
@@ -1000,16 +1029,19 @@ func makeResult(matchedRules []rules.Rule, reason Reason) (res Result) {
}
}
// InitModule manually initializes blocked services map.
func InitModule() {
initBlockedServices()
// InitModule manually initializes blocked services map. l must not be nil.
func InitModule(ctx context.Context, l *slog.Logger) {
initBlockedServices(ctx, l)
}
// New creates properly initialized DNS Filter that is ready to be used. c must
// be non-nil.
func New(c *Config, blockFilters []Filter) (d *DNSFilter, err error) {
ctx := context.TODO()
d = &DNSFilter{
idGen: newIDGenerator(int32(time.Now().Unix())),
logger: c.Logger,
idGen: newIDGenerator(int32(time.Now().Unix()), c.Logger),
bufPool: syncutil.NewSlicePool[byte](rulelist.DefaultRuleBufSize),
safeSearch: c.SafeSearch,
refreshLock: &sync.Mutex{},
@@ -1036,7 +1068,7 @@ func New(c *Config, blockFilters []Filter) (d *DNSFilter, err error) {
check: d.matchHost,
name: "filtering",
}, {
check: matchBlockedServicesRules,
check: d.matchBlockedServicesRules,
name: "blocked services",
}, {
check: d.checkSafeBrowsing,
@@ -1054,7 +1086,7 @@ func New(c *Config, blockFilters []Filter) (d *DNSFilter, err error) {
d.conf = c
d.conf.filtersMu = &sync.RWMutex{}
err = d.prepareRewrites()
err = d.prepareRewrites(ctx)
if err != nil {
return nil, fmt.Errorf("rewrites: preparing: %w", err)
}
@@ -1067,7 +1099,7 @@ func New(c *Config, blockFilters []Filter) (d *DNSFilter, err error) {
}
if blockFilters != nil {
err = d.initFiltering(nil, blockFilters)
err = d.initFiltering(ctx, nil, blockFilters)
if err != nil {
d.Close()
@@ -1082,8 +1114,8 @@ func New(c *Config, blockFilters []Filter) (d *DNSFilter, err error) {
return nil, fmt.Errorf("making filtering directory: %w", err)
}
d.loadFilters(d.conf.Filters)
d.loadFilters(d.conf.WhitelistFilters)
d.loadFilters(ctx, d.conf.Filters)
d.loadFilters(ctx, d.conf.WhitelistFilters)
d.conf.Filters = deduplicateFilters(d.conf.Filters)
d.conf.WhitelistFilters = deduplicateFilters(d.conf.WhitelistFilters)
@@ -1101,12 +1133,12 @@ func (d *DNSFilter) Start() {
d.RegisterFilteringHandlers()
go d.updatesLoop()
go d.updatesLoop(context.TODO())
}
// updatesLoop initializes new filters and checks for filters updates in a loop.
func (d *DNSFilter) updatesLoop() {
defer log.OnPanic("filtering: updates loop")
func (d *DNSFilter) updatesLoop(ctx context.Context) {
defer slogutil.RecoverAndLog(ctx, d.logger)
ivl := time.Second * 5
t := time.NewTimer(ivl)
@@ -1114,9 +1146,9 @@ func (d *DNSFilter) updatesLoop() {
for {
select {
case params := <-d.filtersInitializerChan:
err := d.initFiltering(params.allowFilters, params.blockFilters)
err := d.initFiltering(ctx, params.allowFilters, params.blockFilters)
if err != nil {
log.Error("filtering: initializing: %s", err)
d.logger.ErrorContext(ctx, "initializing", slogutil.KeyError, err)
continue
}
@@ -1165,9 +1197,13 @@ func (d *DNSFilter) checkSafeBrowsing(
return Result{}, nil
}
if log.GetLevel() >= log.DEBUG {
timer := log.StartTimer()
defer timer.LogElapsed("filtering: safebrowsing lookup for %q", host)
ctx := context.TODO()
if d.logger.Enabled(ctx, slogutil.LevelDebug) {
startTime := time.Now()
defer func() {
elapsed := time.Since(startTime)
d.logger.DebugContext(ctx, "safebrowsing lookup", "host", host, "elapsed", elapsed)
}()
}
res = Result{
@@ -1197,9 +1233,13 @@ func (d *DNSFilter) checkParental(
return Result{}, nil
}
if log.GetLevel() >= log.DEBUG {
timer := log.StartTimer()
defer timer.LogElapsed("filtering: parental lookup for %q", host)
ctx := context.TODO()
if d.logger.Enabled(ctx, slogutil.LevelDebug) {
startTime := time.Now()
defer func() {
elapsed := time.Since(startTime)
d.logger.DebugContext(ctx, "parental lookup", "host", host, "elapsed", elapsed)
}()
}
res = Result{

View File

@@ -2,13 +2,14 @@ package filtering
import (
"bytes"
"cmp"
"fmt"
"net/netip"
"testing"
"github.com/AdguardTeam/AdGuardHome/internal/aghtest"
"github.com/AdguardTeam/AdGuardHome/internal/filtering/hashprefix"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/testutil"
"github.com/AdguardTeam/urlfilter/rules"
@@ -17,15 +18,14 @@ import (
"github.com/stretchr/testify/require"
)
func TestMain(m *testing.M) {
testutil.DiscardLogOutput(m)
}
const (
sbBlocked = "wmconvirus.narod.ru"
pcBlocked = "pornhub.com"
)
// testLogger is the common logger for tests.
var testLogger = slogutil.NewDiscardLogger()
// Helpers.
func newForTest(t testing.TB, c *Config, filters []Filter) (f *DNSFilter, setts *Settings) {
@@ -34,6 +34,7 @@ func newForTest(t testing.TB, c *Config, filters []Filter) (f *DNSFilter, setts
FilteringEnabled: true,
}
if c != nil {
c.Logger = cmp.Or(c.Logger, testLogger)
c.SafeBrowsingCacheSize = 10000
c.ParentalCacheSize = 10000
c.SafeSearchCacheSize = 1000
@@ -43,7 +44,9 @@ func newForTest(t testing.TB, c *Config, filters []Filter) (f *DNSFilter, setts
setts.ParentalEnabled = c.ParentalEnabled
} else {
// It must not be nil.
c = &Config{}
c = &Config{
Logger: testLogger,
}
}
f, err := New(c, filters)
require.NoError(t, err)
@@ -53,6 +56,7 @@ func newForTest(t testing.TB, c *Config, filters []Filter) (f *DNSFilter, setts
func newChecker(host string) Checker {
return hashprefix.New(&hashprefix.Config{
Logger: testLogger,
CacheTime: 10,
CacheSize: 100000,
Upstream: aghtest.NewBlockUpstream(host, true),
@@ -168,12 +172,15 @@ func TestDNSFilter_CheckHost_hostRules(t *testing.T) {
func TestSafeBrowsing(t *testing.T) {
logOutput := &bytes.Buffer{}
aghtest.ReplaceLogWriter(t, logOutput)
aghtest.ReplaceLogLevel(t, log.DEBUG)
sbChecker := newChecker(sbBlocked)
d, setts := newForTest(t, &Config{
Logger: slogutil.New(&slogutil.Config{
Level: slogutil.LevelDebug,
Output: logOutput,
Format: slogutil.FormatDefault,
AddTimestamp: false,
}),
SafeBrowsingEnabled: true,
SafeBrowsingChecker: sbChecker,
}, nil)
@@ -181,7 +188,7 @@ func TestSafeBrowsing(t *testing.T) {
d.checkMatch(t, sbBlocked, setts)
require.Contains(t, logOutput.String(), fmt.Sprintf("safebrowsing lookup for %q", sbBlocked))
require.Contains(t, logOutput.String(), fmt.Sprintf("safebrowsing lookup host=%s", sbBlocked))
d.checkMatch(t, "test."+sbBlocked, setts)
d.checkMatchEmpty(t, "yandex.ru", setts)
@@ -216,17 +223,21 @@ func TestParallelSB(t *testing.T) {
func TestParentalControl(t *testing.T) {
logOutput := &bytes.Buffer{}
aghtest.ReplaceLogWriter(t, logOutput)
aghtest.ReplaceLogLevel(t, log.DEBUG)
d, setts := newForTest(t, &Config{
Logger: slogutil.New(&slogutil.Config{
Level: slogutil.LevelDebug,
Output: logOutput,
Format: slogutil.FormatDefault,
AddTimestamp: false,
}),
ParentalEnabled: true,
ParentalControlChecker: newChecker(pcBlocked),
}, nil)
t.Cleanup(d.Close)
d.checkMatch(t, pcBlocked, setts)
require.Contains(t, logOutput.String(), fmt.Sprintf("parental lookup for %q", pcBlocked))
require.Contains(t, logOutput.String(), fmt.Sprintf("parental lookup host=%s", pcBlocked))
d.checkMatch(t, "www."+pcBlocked, setts)
d.checkMatchEmpty(t, "www.yandex.ru", setts)
@@ -548,7 +559,8 @@ func TestWhitelist(t *testing.T) {
}}
d, setts := newForTest(t, nil, filters)
err := d.setFilters(filters, whiteFilters, false)
ctx := testutil.ContextWithTimeout(t, testTimeout)
err := d.setFilters(ctx, filters, whiteFilters, false)
require.NoError(t, err)
t.Cleanup(d.Close)
@@ -663,6 +675,7 @@ func TestClientSettings(t *testing.T) {
func BenchmarkSafeBrowsing(b *testing.B) {
d, setts := newForTest(b, &Config{
Logger: testLogger,
SafeBrowsingEnabled: true,
SafeBrowsingChecker: newChecker(sbBlocked),
}, nil)
@@ -689,6 +702,7 @@ func BenchmarkSafeBrowsing(b *testing.B) {
func BenchmarkSafeBrowsing_parallel(b *testing.B) {
d, setts := newForTest(b, &Config{
Logger: testLogger,
SafeBrowsingEnabled: true,
SafeBrowsingChecker: newChecker(sbBlocked),
}, nil)

View File

@@ -1,10 +1,9 @@
package hashprefix
import (
"context"
"encoding/binary"
"time"
"github.com/AdguardTeam/golibs/log"
)
// expirySize is the size of expiry in cacheItem.
@@ -91,7 +90,7 @@ func (c *Checker) findInCache(
}
// storeInCache caches hashes.
func (c *Checker) storeInCache(hashesToRequest, respHashes []hostnameHash) {
func (c *Checker) storeInCache(ctx context.Context, hashesToRequest, respHashes []hostnameHash) {
hashToStore := make(map[prefix][]hostnameHash)
for _, hash := range respHashes {
@@ -102,7 +101,7 @@ func (c *Checker) storeInCache(hashesToRequest, respHashes []hostnameHash) {
}
for pref, hash := range hashToStore {
c.setCache(pref, hash)
c.setCache(ctx, pref, hash)
}
for _, hash := range hashesToRequest {
@@ -111,18 +110,18 @@ func (c *Checker) storeInCache(hashesToRequest, respHashes []hostnameHash) {
var pref prefix
copy(pref[:], hash[:])
c.setCache(pref, nil)
c.setCache(ctx, pref, nil)
}
}
}
// setCache stores hash in cache.
func (c *Checker) setCache(pref prefix, hashes []hostnameHash) {
func (c *Checker) setCache(ctx context.Context, pref prefix, hashes []hostnameHash) {
item := &cacheItem{
expiry: time.Now().Add(c.cacheTime),
hashes: hashes,
}
c.cache.Set(pref[:], fromCacheItem(item))
log.Debug("%s: stored in cache: %v", c.svc, pref)
c.logger.DebugContext(ctx, "stored in cache", "pref", pref)
}

View File

@@ -2,16 +2,18 @@
package hashprefix
import (
"context"
"crypto/sha256"
"encoding/hex"
"fmt"
"log/slog"
"slices"
"strings"
"time"
"github.com/AdguardTeam/dnsproxy/upstream"
"github.com/AdguardTeam/golibs/cache"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/stringutil"
"github.com/miekg/dns"
@@ -52,12 +54,12 @@ func findMatch(a, b []hostnameHash) (matched bool) {
// Config is the configuration structure for safe browsing and parental
// control.
type Config struct {
// Logger is used for logging the check process. It must not be nil.
Logger *slog.Logger
// Upstream is the upstream DNS server.
Upstream upstream.Upstream
// ServiceName is the name of the service.
ServiceName string
// TXTSuffix is the TXT suffix for DNS request.
TXTSuffix string
@@ -70,15 +72,15 @@ type Config struct {
}
type Checker struct {
// logger is used for logging the check process.
logger *slog.Logger
// upstream is the upstream DNS server.
upstream upstream.Upstream
// cache stores hostname hashes.
cache cache.Cache
// svc is the name of the service.
svc string
// txtSuffix is the TXT suffix for DNS request.
txtSuffix string
@@ -89,12 +91,12 @@ type Checker struct {
// New returns Checker.
func New(conf *Config) (c *Checker) {
return &Checker{
logger: conf.Logger,
upstream: conf.Upstream,
cache: cache.New(cache.Config{
EnableLRU: true,
MaxSize: conf.CacheSize,
}),
svc: conf.ServiceName,
txtSuffix: conf.TXTSuffix,
cacheTime: conf.CacheTime,
}
@@ -102,18 +104,22 @@ func New(conf *Config) (c *Checker) {
// Check returns true if request for the host should be blocked.
func (c *Checker) Check(host string) (ok bool, err error) {
ctx := context.TODO()
hashes := hostnameToHashes(host)
l := c.logger.With("host", host)
found, blocked, hashesToRequest := c.findInCache(hashes)
if found {
log.Debug("%s: found %q in cache, blocked: %t", c.svc, host, blocked)
l.DebugContext(ctx, "found in cache", "blocked", blocked)
return blocked, nil
}
question := c.getQuestion(hashesToRequest)
log.Debug("%s: checking %s: %s", c.svc, host, question)
l.DebugContext(ctx, "checking", "question", question)
req := (&dns.Msg{}).SetQuestion(question, dns.TypeTXT)
resp, err := c.upstream.Exchange(req)
@@ -121,9 +127,9 @@ func (c *Checker) Check(host string) (ok bool, err error) {
return false, fmt.Errorf("getting hashes: %w", err)
}
matched, receivedHashes := c.processAnswer(hashesToRequest, resp, host)
matched, receivedHashes := c.processAnswer(ctx, l, hashesToRequest, resp)
c.storeInCache(hashesToRequest, receivedHashes)
c.storeInCache(ctx, hashesToRequest, receivedHashes)
return matched, nil
}
@@ -182,11 +188,12 @@ func (c *Checker) getQuestion(hashes []hostnameHash) (q string) {
}
// processAnswer returns true if DNS response matches the hash, and received
// hashed hostnames from the upstream.
// hashed hostnames from the upstream. l must not be nil.
func (c *Checker) processAnswer(
ctx context.Context,
l *slog.Logger,
hashesToRequest []hostnameHash,
resp *dns.Msg,
host string,
) (matched bool, receivedHashes []hostnameHash) {
txtCount := 0
@@ -198,14 +205,14 @@ func (c *Checker) processAnswer(
txtCount++
receivedHashes = c.appendHashesFromTXT(receivedHashes, txt, host)
receivedHashes = c.appendHashesFromTXT(ctx, l, receivedHashes, txt)
}
log.Debug("%s: received answer for %s with %d TXT count", c.svc, host, txtCount)
l.DebugContext(ctx, "processing answer with TXT", "txt_count", txtCount)
matched = findMatch(hashesToRequest, receivedHashes)
if matched {
log.Debug("%s: matched %s", c.svc, host)
l.DebugContext(ctx, "matched")
return true, receivedHashes
}
@@ -213,24 +220,25 @@ func (c *Checker) processAnswer(
return false, receivedHashes
}
// appendHashesFromTXT appends received hashed hostnames.
// appendHashesFromTXT appends received hashed hostnames. l must not be nil.
func (c *Checker) appendHashesFromTXT(
ctx context.Context,
l *slog.Logger,
hashes []hostnameHash,
txt *dns.TXT,
host string,
) (receivedHashes []hostnameHash) {
log.Debug("%s: received hashes for %s: %v", c.svc, host, txt.Txt)
l.DebugContext(ctx, "received hashes", "txt", txt.Txt)
for _, t := range txt.Txt {
if len(t) != hexSize {
log.Debug("%s: wrong hex size %d for %s %s", c.svc, len(t), host, t)
l.DebugContext(ctx, "wrong hex size", "len", len(t), "txt", t)
continue
}
buf, err := hex.DecodeString(t)
if err != nil {
log.Debug("%s: decoding hex string %s: %s", c.svc, t, err)
l.DebugContext(ctx, "decoding hex string", "txt", t, slogutil.KeyError, err)
continue
}

View File

@@ -10,6 +10,8 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/aghtest"
"github.com/AdguardTeam/golibs/cache"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/testutil"
"github.com/miekg/dns"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -42,10 +44,10 @@ func TestChcker_getQuestion(t *testing.T) {
hash = sha256.Sum256([]byte("com"))
assert.False(t, slices.Contains(hashes, hash))
c := &Checker{
svc: "SafeBrowsing",
txtSuffix: suf,
}
c := New(&Config{
Logger: slogutil.NewDiscardLogger(),
TXTSuffix: suf,
})
q := c.getQuestion(hashes)
@@ -95,10 +97,13 @@ func TestHostnameToHashes(t *testing.T) {
}
func TestChecker_storeInCache(t *testing.T) {
c := &Checker{
svc: "SafeBrowsing",
cacheTime: cacheTime,
}
const testTimeout = 1 * time.Second
c := New(&Config{
Logger: slogutil.NewDiscardLogger(),
CacheTime: cacheTime,
})
conf := cache.Config{}
c.cache = cache.New(conf)
@@ -112,7 +117,7 @@ func TestChecker_storeInCache(t *testing.T) {
hashesArray = append(hashesArray, hash4)
hash2 := sha256.Sum256([]byte("host.com"))
hashesArray = append(hashesArray, hash2)
c.storeInCache(hashes, hashesArray)
c.storeInCache(testutil.ContextWithTimeout(t, testTimeout), hashes, hashesArray)
// match "3.sub.host.com" or "host.com" from cache
hashes = []hostnameHash{}
@@ -152,10 +157,11 @@ func TestChecker_storeInCache(t *testing.T) {
ok = slices.Contains(hashesToRequest, hash)
assert.True(t, ok)
c = &Checker{
svc: "SafeBrowsing",
cacheTime: cacheTime,
}
c = New(&Config{
Logger: slogutil.NewDiscardLogger(),
CacheTime: cacheTime,
})
c.cache = cache.New(cache.Config{})
hashes = []hostnameHash{}
@@ -189,6 +195,7 @@ func TestChecker_Check(t *testing.T) {
for _, tc := range testCases {
c := New(&Config{
Logger: slogutil.NewDiscardLogger(),
CacheTime: cacheTime,
CacheSize: cacheSize,
})

View File

@@ -1,12 +1,13 @@
package filtering
import (
"context"
"fmt"
"net/netip"
"github.com/AdguardTeam/AdGuardHome/internal/filtering/rulelist"
"github.com/AdguardTeam/golibs/hostsfile"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/urlfilter/rules"
"github.com/miekg/dns"
@@ -24,7 +25,7 @@ func (d *DNSFilter) matchSysHosts(
return Result{}, nil
}
vals, rs, matched := hostsRewrites(qtype, host, d.conf.EtcHosts)
vals, rs, matched := d.hostsRewrites(qtype, host, d.conf.EtcHosts)
if !matched {
return Result{}, nil
}
@@ -42,11 +43,13 @@ func (d *DNSFilter) matchSysHosts(
}
// hostsRewrites returns values and rules matched by qt and host within hs.
func hostsRewrites(
func (d *DNSFilter) hostsRewrites(
qtype uint16,
host string,
hs hostsfile.Storage,
) (vals []rules.RRValue, rls []*ResultRule, matched bool) {
ctx := context.TODO()
var isValidProto func(netip.Addr) (ok bool)
switch qtype {
case dns.TypeA:
@@ -56,7 +59,12 @@ func hostsRewrites(
case dns.TypePTR:
addr, err := netutil.IPFromReversedAddr(host)
if err != nil {
log.Debug("filtering: failed to parse PTR record %q: %s", host, err)
d.logger.DebugContext(
ctx,
"failed to parse PTR record",
"host", host,
slogutil.KeyError, err,
)
return nil, nil, false
}
@@ -73,7 +81,11 @@ func hostsRewrites(
return vals, rls, len(names) > 0
default:
log.Debug("filtering: unsupported qtype %d", qtype)
d.logger.DebugContext(
ctx,
"unsupported qtype",
"qtype", qtype,
)
return nil, nil, false
}

View File

@@ -10,6 +10,7 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/aghtest"
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/AdGuardHome/internal/filtering/rulelist"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/testutil"
"github.com/AdguardTeam/urlfilter/rules"
"github.com/miekg/dns"
@@ -52,6 +53,7 @@ func TestDNSFilter_CheckHost_hostsContainer(t *testing.T) {
testutil.CleanupAndRequireSuccess(t, hc.Close)
conf := &filtering.Config{
Logger: slogutil.NewDiscardLogger(),
EtcHosts: hc,
}
f, err := filtering.New(conf, nil)

View File

@@ -17,7 +17,7 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
"github.com/AdguardTeam/AdGuardHome/internal/filtering/rulelist"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/netutil/urlutil"
"github.com/miekg/dns"
)
@@ -148,6 +148,8 @@ func (d *DNSFilter) handleFilteringRemoveURL(w http.ResponseWriter, r *http.Requ
Whitelist bool `json:"whitelist"`
}
ctx := r.Context()
req := request{}
err := json.NewDecoder(r.Body).Decode(&req)
if err != nil {
@@ -170,7 +172,12 @@ func (d *DNSFilter) handleFilteringRemoveURL(w http.ResponseWriter, r *http.Requ
return flt.URL == req.URL
})
if delIdx == -1 {
log.Error("deleting filter with url %q: %s", req.URL, errFilterNotExist)
d.logger.ErrorContext(
ctx,
"deleting filter",
"url", req.URL,
slogutil.KeyError, errFilterNotExist,
)
return
}
@@ -179,14 +186,20 @@ func (d *DNSFilter) handleFilteringRemoveURL(w http.ResponseWriter, r *http.Requ
p := deleted.Path(d.conf.DataDir)
err = os.Rename(p, p+".old")
if err != nil && !errors.Is(err, os.ErrNotExist) {
log.Error("deleting filter %d: renaming file %q: %s", deleted.ID, p, err)
d.logger.ErrorContext(
ctx,
"renaming filter file",
"id", deleted.ID,
"path", p,
slogutil.KeyError, err,
)
return
}
*filters = slices.Delete(*filters, delIdx, delIdx+1)
log.Info("deleted filter %d", deleted.ID)
d.logger.InfoContext(ctx, "deleted filter", "id", deleted.ID)
}()
d.conf.ConfigModified()

View File

@@ -12,6 +12,7 @@ import (
"time"
"github.com/AdguardTeam/AdGuardHome/internal/schedule"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/testutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -103,6 +104,7 @@ func TestDNSFilter_handleFilteringSetURL(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
confModifiedCalled := false
d, err := New(&Config{
Logger: slogutil.NewDiscardLogger(),
FilteringEnabled: true,
Filters: tc.initial,
HTTPClient: &http.Client{
@@ -183,6 +185,7 @@ func TestDNSFilter_handleSafeBrowsingStatus(t *testing.T) {
handlers := make(map[string]http.Handler)
d, err := New(&Config{
Logger: slogutil.NewDiscardLogger(),
ConfigModified: func() {
testutil.RequireSend(testutil.PanicT{}, confModCh, struct{}{}, testTimeout)
},
@@ -267,6 +270,7 @@ func TestDNSFilter_handleParentalStatus(t *testing.T) {
handlers := make(map[string]http.Handler)
d, err := New(&Config{
Logger: slogutil.NewDiscardLogger(),
ConfigModified: func() {
testutil.RequireSend(testutil.PanicT{}, confModCh, struct{}{}, testTimeout)
},
@@ -370,6 +374,7 @@ func TestDNSFilter_HandleCheckHost(t *testing.T) {
}
dnsFilter, err := New(&Config{
Logger: slogutil.NewDiscardLogger(),
BlockedServices: &BlockedServices{
Schedule: schedule.EmptyWeekly(),
},

View File

@@ -1,12 +1,13 @@
package filtering
import (
"context"
"fmt"
"log/slog"
"sync/atomic"
"github.com/AdguardTeam/AdGuardHome/internal/filtering/rulelist"
"github.com/AdguardTeam/golibs/container"
"github.com/AdguardTeam/golibs/log"
)
// idGenerator generates filtering-list IDs in a way broadly compatible with the
@@ -16,13 +17,15 @@ import (
// rule-list architecture.
type idGenerator struct {
current *atomic.Int32
logger *slog.Logger
}
// newIDGenerator returns a new ID generator initialized with the given seed
// value.
func newIDGenerator(seed int32) (g *idGenerator) {
func newIDGenerator(seed int32, l *slog.Logger) (g *idGenerator) {
g = &idGenerator{
current: &atomic.Int32{},
logger: l,
}
g.current.Store(seed)
@@ -61,11 +64,12 @@ func (g *idGenerator) fix(flts []FilterYAML) {
newID = g.next()
}
log.Info(
"filtering: warning: filter at index %d has duplicate id %d; reassigning to %d",
i,
id,
newID,
g.logger.WarnContext(
context.TODO(),
"filter has duplicate id; reassigning",
"idx", i,
"id", id,
"new_id", newID,
)
flts[i].ID = newID

View File

@@ -5,6 +5,7 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/aghalg"
"github.com/AdguardTeam/AdGuardHome/internal/filtering/rulelist"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/stretchr/testify/assert"
)
@@ -64,7 +65,7 @@ func TestIDGenerator_Fix(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
g := newIDGenerator(1)
g := newIDGenerator(1, slogutil.NewDiscardLogger())
g.fix(tc.in)
assertUniqueIDs(t, tc.in)

View File

@@ -2,13 +2,14 @@
package rewrite
import (
"context"
"fmt"
"log/slog"
"slices"
"strings"
"sync"
"github.com/AdguardTeam/golibs/container"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/urlfilter"
"github.com/AdguardTeam/urlfilter/filterlist"
"github.com/AdguardTeam/urlfilter/rules"
@@ -30,8 +31,23 @@ type Storage interface {
List() (items []*Item)
}
// Config is the configuration for DefaultStorage.
type Config struct {
// logger is used for logging storage processes. It must not be nil.
Logger *slog.Logger
// Rewrites stores the rewrite entries. It must not be nil.
Rewrites []*Item
// ListID is used as an identifier of the underlying rules list.
ListID int
}
// DefaultStorage is the default storage for rewrite rules.
type DefaultStorage struct {
// logger is used for logging storage processes. It must not be nil.
logger *slog.Logger
// mu protects items.
mu *sync.RWMutex
@@ -51,13 +67,13 @@ type DefaultStorage struct {
urlFilterID int
}
// NewDefaultStorage returns new rewrites storage. listID is used as an
// identifier of the underlying rules list. rewrites must not be nil.
func NewDefaultStorage(listID int, rewrites []*Item) (s *DefaultStorage, err error) {
// NewDefaultStorage returns new rewrites storage. conf must not be nil.
func NewDefaultStorage(conf *Config) (s *DefaultStorage, err error) {
s = &DefaultStorage{
logger: conf.Logger,
mu: &sync.RWMutex{},
urlFilterID: listID,
rewrites: rewrites,
urlFilterID: conf.ListID,
rewrites: conf.Rewrites,
}
s.mu.Lock()
@@ -79,6 +95,8 @@ func (s *DefaultStorage) MatchRequest(dReq *urlfilter.DNSRequest) (rws []*rules.
s.mu.RLock()
defer s.mu.RUnlock()
ctx := context.TODO()
rrules := s.rewriteRulesForReq(dReq)
if len(rrules) == 0 {
return nil
@@ -91,7 +109,7 @@ func (s *DefaultStorage) MatchRequest(dReq *urlfilter.DNSRequest) (rws []*rules.
rule := rrules[0]
rwAns := rule.DNSRewrite.NewCNAME
log.Debug("rewrite: cname for %s is %s", host, rwAns)
s.logger.DebugContext(ctx, "cname found", "host", host, "cname", rwAns)
if dReq.Hostname == rwAns {
// A request for the hostname itself is an exception rule.
@@ -109,7 +127,7 @@ func (s *DefaultStorage) MatchRequest(dReq *urlfilter.DNSRequest) (rws []*rules.
}
if cnames.Has(rwAns) {
log.Info("rewrite: cname loop for %q on %q", dReq.Hostname, rwAns)
s.logger.InfoContext(ctx, "rewrite cname loop", "host", dReq.Hostname, "rewrite", rwAns)
return nil
}
@@ -168,12 +186,14 @@ func (s *DefaultStorage) Remove(item *Item) (err error) {
s.mu.Lock()
defer s.mu.Unlock()
ctx := context.TODO()
arr := []*Item{}
// TODO(d.kolyshev): Use slices.IndexFunc + slices.Delete?
for _, ent := range s.rewrites {
if ent.equal(item) {
log.Debug("rewrite: removed element: %s -> %s", ent.Domain, ent.Answer)
s.logger.DebugContext(ctx, "removed element", "domain", ent.Domain, "ans", ent.Answer)
continue
}
@@ -215,7 +235,12 @@ func (s *DefaultStorage) resetRules() (err error) {
s.ruleList = strList
s.engine = urlfilter.NewDNSEngine(rs)
log.Info("rewrite: filter %d: reset %d rules", s.urlFilterID, s.engine.RulesCount)
s.logger.InfoContext(
context.TODO(),
"reset rules",
"filter", s.urlFilterID,
"count", s.engine.RulesCount,
)
return nil
}

View File

@@ -4,6 +4,7 @@ import (
"net/netip"
"testing"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/urlfilter"
"github.com/AdguardTeam/urlfilter/rules"
@@ -18,7 +19,11 @@ func TestNewDefaultStorage(t *testing.T) {
Answer: "answer.com",
}}
s, err := NewDefaultStorage(-1, items)
s, err := NewDefaultStorage(&Config{
Logger: slogutil.NewDiscardLogger(),
Rewrites: items,
ListID: -1,
})
require.NoError(t, err)
require.Len(t, s.List(), 1)
@@ -27,7 +32,11 @@ func TestNewDefaultStorage(t *testing.T) {
func TestDefaultStorage_CRUD(t *testing.T) {
var items []*Item
s, err := NewDefaultStorage(-1, items)
s, err := NewDefaultStorage(&Config{
Logger: slogutil.NewDiscardLogger(),
Rewrites: items,
ListID: -1,
})
require.NoError(t, err)
require.Len(t, s.List(), 0)
@@ -112,7 +121,11 @@ func TestDefaultStorage_MatchRequest(t *testing.T) {
Answer: "sub.issue4016.com",
}}
s, err := NewDefaultStorage(-1, items)
s, err := NewDefaultStorage(&Config{
Logger: slogutil.NewDiscardLogger(),
Rewrites: items,
ListID: -1,
})
require.NoError(t, err)
testCases := []struct {
@@ -284,7 +297,11 @@ func TestDefaultStorage_MatchRequest_Levels(t *testing.T) {
Answer: addr3.String(),
}}
s, err := NewDefaultStorage(-1, items)
s, err := NewDefaultStorage(&Config{
Logger: slogutil.NewDiscardLogger(),
Rewrites: items,
ListID: -1,
})
require.NoError(t, err)
testCases := []struct {
@@ -352,7 +369,11 @@ func TestDefaultStorage_MatchRequest_ExceptionCNAME(t *testing.T) {
Answer: "*.sub.host.com",
}}
s, err := NewDefaultStorage(-1, items)
s, err := NewDefaultStorage(&Config{
Logger: slogutil.NewDiscardLogger(),
Rewrites: items,
ListID: -1,
})
require.NoError(t, err)
testCases := []struct {
@@ -416,7 +437,11 @@ func TestDefaultStorage_MatchRequest_ExceptionIP(t *testing.T) {
Answer: "A",
}}
s, err := NewDefaultStorage(-1, items)
s, err := NewDefaultStorage(&Config{
Logger: slogutil.NewDiscardLogger(),
Rewrites: items,
ListID: -1,
})
require.NoError(t, err)
testCases := []struct {

View File

@@ -6,7 +6,6 @@ import (
"slices"
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
"github.com/AdguardTeam/golibs/log"
)
// TODO(d.kolyshev): Use [rewrite.Item] instead.
@@ -50,7 +49,7 @@ func (d *DNSFilter) handleRewriteAdd(w http.ResponseWriter, r *http.Request) {
Answer: rwJSON.Answer,
}
err = rw.normalize()
err = rw.normalize(r.Context(), d.logger)
if err != nil {
// Shouldn't happen currently, since normalize only returns a non-nil
// error when a rewrite is nil, but be change-proof.
@@ -64,11 +63,12 @@ func (d *DNSFilter) handleRewriteAdd(w http.ResponseWriter, r *http.Request) {
defer d.confMu.Unlock()
d.conf.Rewrites = append(d.conf.Rewrites, rw)
log.Debug(
"rewrite: added element: %s -> %s [%d]",
rw.Domain,
rw.Answer,
len(d.conf.Rewrites),
d.logger.DebugContext(
r.Context(),
"added rewrite element",
"domain", rw.Domain,
"answer", rw.Answer,
"rewrites_len", len(d.conf.Rewrites),
)
}()
@@ -98,7 +98,12 @@ func (d *DNSFilter) handleRewriteDelete(w http.ResponseWriter, r *http.Request)
for _, ent := range d.conf.Rewrites {
if ent.equal(entDel) {
log.Debug("rewrite: removed element: %s -> %s", ent.Domain, ent.Answer)
d.logger.DebugContext(
r.Context(),
"removed rewrite element",
"domain", ent.Domain,
"answer", ent.Answer,
)
continue
}
@@ -138,7 +143,7 @@ func (d *DNSFilter) handleRewriteUpdate(w http.ResponseWriter, r *http.Request)
Answer: updateJSON.Update.Answer,
}
err = rwAdd.normalize()
err = rwAdd.normalize(r.Context(), d.logger)
if err != nil {
// Shouldn't happen currently, since normalize only returns a non-nil
// error when a rewrite is nil, but be change-proof.
@@ -166,6 +171,17 @@ func (d *DNSFilter) handleRewriteUpdate(w http.ResponseWriter, r *http.Request)
d.conf.Rewrites = slices.Replace(d.conf.Rewrites, index, index+1, rwAdd)
log.Debug("rewrite: removed element: %s -> %s", rwDel.Domain, rwDel.Answer)
log.Debug("rewrite: added element: %s -> %s", rwAdd.Domain, rwAdd.Answer)
ctx := r.Context()
d.logger.DebugContext(
ctx,
"removed rewrite element",
"domain", rwDel.Domain,
"answer", rwDel.Answer,
)
d.logger.DebugContext(
ctx,
"added rewrite element",
"domain", rwAdd.Domain,
"answer", rwAdd.Answer,
)
}

View File

@@ -10,6 +10,7 @@ import (
"time"
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/testutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -159,6 +160,7 @@ func TestDNSFilter_handleRewriteHTTP(t *testing.T) {
handlers := make(map[string]http.Handler)
d, err := filtering.New(&filtering.Config{
Logger: slogutil.NewDiscardLogger(),
ConfigModified: onConfModified,
HTTPRegister: func(_, url string, handler http.HandlerFunc) {
handlers[url] = handler

View File

@@ -1,13 +1,15 @@
package filtering
import (
"context"
"fmt"
"log/slog"
"net/netip"
"slices"
"strings"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/miekg/dns"
)
@@ -58,7 +60,7 @@ func (rw *LegacyRewrite) matchesQType(qt uint16) (ok bool) {
// to domain name case, IP length, and so on.
//
// If rw is nil, it returns an errors.
func (rw *LegacyRewrite) normalize() (err error) {
func (rw *LegacyRewrite) normalize(ctx context.Context, l *slog.Logger) (err error) {
if rw == nil {
return errors.Error("nil rewrite entry")
}
@@ -85,7 +87,7 @@ func (rw *LegacyRewrite) normalize() (err error) {
ip, err := netip.ParseAddr(rw.Answer)
if err != nil {
log.Debug("normalizing legacy rewrite: %s", err)
l.DebugContext(ctx, "normalizing legacy rewrite", slogutil.KeyError, err)
rw.Type = dns.TypeCNAME
return nil
@@ -136,9 +138,9 @@ func (rw *LegacyRewrite) Compare(b *LegacyRewrite) (res int) {
}
// prepareRewrites normalizes and validates all legacy DNS rewrites.
func (d *DNSFilter) prepareRewrites() (err error) {
func (d *DNSFilter) prepareRewrites(ctx context.Context) (err error) {
for i, r := range d.conf.Rewrites {
err = r.normalize()
err = r.normalize(ctx, d.logger)
if err != nil {
return fmt.Errorf("at index %d: %w", i, err)
}
@@ -191,7 +193,13 @@ func findRewrites(
// setRewriteResult sets the Reason or IPList of res if necessary. res must not
// be nil.
func setRewriteResult(res *Result, host string, rewrites []*LegacyRewrite, qtype uint16) {
func (d *DNSFilter) setRewriteResult(
ctx context.Context,
res *Result,
host string,
rewrites []*LegacyRewrite,
qtype uint16,
) {
for _, rw := range rewrites {
if rw.Type == qtype && (qtype == dns.TypeA || qtype == dns.TypeAAAA) {
if rw.IP == (netip.Addr{}) {
@@ -203,7 +211,7 @@ func setRewriteResult(res *Result, host string, rewrites []*LegacyRewrite, qtype
res.IPList = append(res.IPList, rw.IP)
log.Debug("rewrite: a/aaaa for %s is %s", host, rw.IP)
d.logger.DebugContext(ctx, "set a/aaaa rewrite", "host", host, "ans", rw.IP)
}
}
}

View File

@@ -5,6 +5,7 @@ import (
"net/netip"
"testing"
"github.com/AdguardTeam/golibs/testutil"
"github.com/miekg/dns"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -88,7 +89,8 @@ func TestRewrites(t *testing.T) {
Answer: addr1v4.String(),
}}
require.NoError(t, d.prepareRewrites())
ctx := testutil.ContextWithTimeout(t, testTimeout)
require.NoError(t, d.prepareRewrites(ctx))
testCases := []struct {
name string
@@ -236,7 +238,8 @@ func TestRewritesLevels(t *testing.T) {
Type: dns.TypeA,
}}
require.NoError(t, d.prepareRewrites())
ctx := testutil.ContextWithTimeout(t, testTimeout)
require.NoError(t, d.prepareRewrites(ctx))
testCases := []struct {
name string
@@ -280,7 +283,8 @@ func TestRewritesExceptionCNAME(t *testing.T) {
Answer: "*.sub.host.com",
}}
require.NoError(t, d.prepareRewrites())
ctx := testutil.ContextWithTimeout(t, testTimeout)
require.NoError(t, d.prepareRewrites(ctx))
testCases := []struct {
name string
@@ -342,7 +346,8 @@ func TestRewritesExceptionIP(t *testing.T) {
Type: dns.TypeA,
}}
require.NoError(t, d.prepareRewrites())
ctx := testutil.ContextWithTimeout(t, testTimeout)
require.NoError(t, d.prepareRewrites(ctx))
testCases := []struct {
name string

View File

@@ -2535,6 +2535,7 @@ var blockedServices = []blockedService{{
"||tiktok.com^",
"||tiktokcdn-us.com^",
"||tiktokcdn.com^",
"||tiktokrow-cdn.com^",
"||tiktokv.com^",
"||ttlivecdn.com.c.bytefcdn-oversea.com^",
"||ttlivecdn.com^",

View File

@@ -112,6 +112,7 @@ func (clients *clientsContainer) Init(
}
clients.storage, err = client.NewStorage(ctx, &client.StorageConfig{
BaseLogger: baseLogger,
Logger: baseLogger.With(slogutil.KeyPrefix, "client_storage"),
Clock: timeutil.SystemClock{},
InitialClients: confClients,

View File

@@ -26,7 +26,9 @@ func newClientsContainer(t *testing.T) (c *clientsContainer) {
client.EmptyDHCP{},
nil,
nil,
&filtering.Config{},
&filtering.Config{
Logger: testLogger,
},
newSignalHandler(nil, nil),
)

View File

@@ -431,6 +431,7 @@ func (web *webAPI) handleInstallConfigure(w http.ResponseWriter, r *http.Request
globalContext.firstRun = false
config.DNS.BindHosts = []netip.Addr{req.DNS.IP}
config.DNS.Port = req.DNS.Port
config.Filtering.Logger = web.baseLogger.With(slogutil.KeyPrefix, "filtering")
config.Filtering.SafeFSPatterns = []string{
filepath.Join(globalContext.workDir, userFilterDataDir, "*"),
}

View File

@@ -295,13 +295,6 @@ func newServerConfig(
UseWHOIS: clientSrcConf.WHOIS,
}
newConf.DNSCryptConfig, err = newDNSCryptConfig(tlsConf, hosts)
if err != nil {
// Don't wrap the error, because it's already wrapped by
// newDNSCryptConfig.
return nil, err
}
return newConf, nil
}
@@ -315,7 +308,14 @@ func newDNSTLSConfig(
return &dnsforward.TLSConfig{}, nil
}
dnsCryptConf, err := newDNSCryptConfig(conf, addrs)
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return nil, err
}
dnsConf = &dnsforward.TLSConfig{
DNSCryptConf: dnsCryptConf,
ServerName: conf.ServerName,
StrictSNICheck: conf.StrictSNICheck,
}
@@ -334,15 +334,16 @@ func newDNSTLSConfig(
cert, err := tls.X509KeyPair(conf.CertificateChainData, conf.PrivateKeyData)
if err != nil {
const format = "parsing tls key pair: %w"
if conf.AllowUnencryptedDoH {
err = fmt.Errorf("parsing tls key pair: %w", err)
if conf.AllowUnencryptedDoH || dnsCryptConf != nil {
// TODO(s.chzhen): Use [slog.Logger].
log.Info("warning: %s: %s", format, err)
log.Info("warning: %s", err)
return dnsConf, nil
}
return nil, fmt.Errorf(format, err)
// Don't wrap the error, because it's already annotated.
return nil, err
}
dnsConf.Cert = &cert
@@ -355,38 +356,37 @@ func newDNSTLSConfig(
func newDNSCryptConfig(
conf *tlsConfigSettings,
addrs []netip.Addr,
) (dnsCryptConf dnsforward.DNSCryptConfig, err error) {
if !conf.Enabled || conf.PortDNSCrypt == 0 {
return dnsforward.DNSCryptConfig{}, nil
) (dnsCryptConf *dnsforward.DNSCryptConfig, err error) {
if conf.PortDNSCrypt == 0 {
return nil, nil
}
if conf.DNSCryptConfigFile == "" {
return dnsforward.DNSCryptConfig{}, errors.Error("no dnscrypt_config_file")
return nil, fmt.Errorf("dnscrypt_config_file: %w", errors.ErrEmptyValue)
}
f, err := os.Open(conf.DNSCryptConfigFile)
if err != nil {
return dnsforward.DNSCryptConfig{}, fmt.Errorf("opening dnscrypt config: %w", err)
return nil, fmt.Errorf("opening dnscrypt config: %w", err)
}
defer func() { err = errors.WithDeferred(err, f.Close()) }()
rc := &dnscrypt.ResolverConfig{}
err = yaml.NewDecoder(f).Decode(rc)
if err != nil {
return dnsforward.DNSCryptConfig{}, fmt.Errorf("decoding dnscrypt config: %w", err)
return nil, fmt.Errorf("decoding dnscrypt config: %w", err)
}
cert, err := rc.CreateCert()
if err != nil {
return dnsforward.DNSCryptConfig{}, fmt.Errorf("creating dnscrypt cert: %w", err)
return nil, fmt.Errorf("creating dnscrypt cert: %w", err)
}
return dnsforward.DNSCryptConfig{
return &dnsforward.DNSCryptConfig{
ResolverCert: cert,
ProviderName: rc.ProviderName,
UDPListenAddrs: ipsToUDPAddrs(addrs, conf.PortDNSCrypt),
TCPListenAddrs: ipsToTCPAddrs(addrs, conf.PortDNSCrypt),
Enabled: true,
ProviderName: rc.ProviderName,
}, nil
}

View File

@@ -21,6 +21,7 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/aghalg"
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
"github.com/AdguardTeam/AdGuardHome/internal/aghos"
"github.com/AdguardTeam/AdGuardHome/internal/aghslog"
"github.com/AdguardTeam/AdGuardHome/internal/arpdb"
"github.com/AdguardTeam/AdGuardHome/internal/dhcpd"
"github.com/AdguardTeam/AdGuardHome/internal/dnsforward"
@@ -357,15 +358,17 @@ func setupDNSFilteringConf(
const (
dnsTimeout = 3 * time.Second
sbService = "safe browsing"
sbService = "safe_browsing"
defaultSafeBrowsingServer = `https://family.adguard-dns.com/dns-query`
sbTXTSuffix = `sb.dns.adguard.com.`
pcService = "parental control"
pcService = "parental_control"
defaultParentalServer = `https://family.adguard-dns.com/dns-query`
pcTXTSuffix = `pc.dns.adguard.com.`
)
conf.Logger = baseLogger.With(slogutil.KeyPrefix, "filtering")
conf.EtcHosts = globalContext.etcHosts
// TODO(s.chzhen): Use empty interface.
if globalContext.etcHosts == nil || !config.DNS.HostsFileEnabled {
@@ -383,6 +386,7 @@ func setupDNSFilteringConf(
cacheTime := time.Duration(conf.CacheTime) * time.Minute
upsOpts := &upstream.Options{
Logger: aghslog.NewForUpstream(baseLogger, aghslog.UpstreamTypeService),
Timeout: dnsTimeout,
Bootstrap: upstream.StaticResolver{
// 94.140.14.15.
@@ -402,11 +406,11 @@ func setupDNSFilteringConf(
}
conf.SafeBrowsingChecker = hashprefix.New(&hashprefix.Config{
Upstream: sbUps,
ServiceName: sbService,
TXTSuffix: sbTXTSuffix,
CacheTime: cacheTime,
CacheSize: conf.SafeBrowsingCacheSize,
Logger: baseLogger.With(slogutil.KeyPrefix, sbService),
Upstream: sbUps,
TXTSuffix: sbTXTSuffix,
CacheTime: cacheTime,
CacheSize: conf.SafeBrowsingCacheSize,
})
// Protect against invalid configuration, see #6181.
@@ -415,7 +419,11 @@ func setupDNSFilteringConf(
// default.
if conf.SafeBrowsingBlockHost == "" {
host := defaultSafeBrowsingBlockHost
log.Info("%s: warning: empty blocking host; using default: %q", sbService, host)
baseLogger.WarnContext(ctx,
"empty blocking host; set default",
"service", sbService,
"host", host,
)
conf.SafeBrowsingBlockHost = host
}
@@ -426,11 +434,11 @@ func setupDNSFilteringConf(
}
conf.ParentalControlChecker = hashprefix.New(&hashprefix.Config{
Upstream: parUps,
ServiceName: pcService,
TXTSuffix: pcTXTSuffix,
CacheTime: cacheTime,
CacheSize: conf.ParentalCacheSize,
Logger: baseLogger.With(slogutil.KeyPrefix, pcService),
Upstream: parUps,
TXTSuffix: pcTXTSuffix,
CacheTime: cacheTime,
CacheSize: conf.ParentalCacheSize,
})
// Protect against invalid configuration, see #6181.
@@ -439,7 +447,11 @@ func setupDNSFilteringConf(
// default.
if conf.ParentalBlockHost == "" {
host := defaultParentalBlockHost
log.Info("%s: warning: empty blocking host; using default: %q", pcService, host)
baseLogger.WarnContext(ctx,
"empty blocking host; set default",
"service", pcService,
"host", host,
)
conf.ParentalBlockHost = host
}
@@ -614,13 +626,13 @@ func run(opts options, clientBuildFS fs.FS, done chan struct{}, sigHdlr *signalH
err = configureOS(config)
fatalOnError(err)
// TODO(s.chzhen): Use it for the entire initialization process.
ctx := context.Background()
// Clients package uses filtering package's static data
// (filtering.BlockedSvcKnown()), so we have to initialize filtering static
// data first, but also to avoid relying on automatic Go init() function.
filtering.InitModule()
// TODO(s.chzhen): Use it for the entire initialization process.
ctx := context.Background()
filtering.InitModule(ctx, slogLogger)
err = initContextClients(ctx, slogLogger, sigHdlr)
fatalOnError(err)

View File

@@ -230,8 +230,9 @@ func TestTLSManager_Reload(t *testing.T) {
require.NoError(t, err)
globalContext.clients.storage, err = client.NewStorage(ctx, &client.StorageConfig{
Logger: testLogger,
Clock: timeutil.SystemClock{},
BaseLogger: testLogger,
Logger: testLogger,
Clock: timeutil.SystemClock{},
})
require.NoError(t, err)
@@ -492,8 +493,9 @@ func TestTLSManager_HandleTLSConfigure(t *testing.T) {
require.NoError(t, err)
globalContext.clients.storage, err = client.NewStorage(ctx, &client.StorageConfig{
Logger: testLogger,
Clock: timeutil.SystemClock{},
BaseLogger: testLogger,
Logger: testLogger,
Clock: timeutil.SystemClock{},
})
require.NoError(t, err)

View File

@@ -3,28 +3,33 @@
# TODO(a.garipov): Move to the top level once the rewrite is over.
dns:
addresses:
- '0.0.0.0:53'
bootstrap_dns:
- '9.9.9.10'
- '149.112.112.10'
- '2620:fe::10'
- '2620:fe::fe:10'
upstream_dns:
- '8.8.8.8'
dns64_prefixes:
- '1234::/64'
upstream_timeout: 1s
bootstrap_prefer_ipv6: true
use_dns64: true
upstream_mode: parallel
addresses:
- '0.0.0.0:53'
bootstrap_dns:
- '9.9.9.10'
- '149.112.112.10'
- '2620:fe::10'
- '2620:fe::fe:10'
upstream_dns:
- '8.8.8.8'
dns64_prefixes:
- '1234::/64'
upstream_timeout: 1s
cache_size: 1048576
ratelimit: 100
bootstrap_prefer_ipv6: true
refuse_any: true
use_dns64: true
http:
pprof:
enabled: true
port: 6060
addresses:
- '0.0.0.0:3000'
secure_addresses: []
timeout: 5s
force_https: true
pprof:
port: 6060
enabled: true
addresses:
- '0.0.0.0:3000'
secure_addresses: []
timeout: 5s
force_https: true
log:
verbose: true
verbose: true
schema_version: 100

View File

@@ -61,38 +61,28 @@ func Main(embeddedFrontend fs.FS) {
FileName: opts.confFile,
}
confMgr, err := configmgr.New(startCtx, confMgrConf)
svc, err := newServiceMgr(ctx, &serviceMgrConfig{
confMgrConf: confMgrConf,
logger: baseLogger.With(slogutil.KeyPrefix, "svc"),
pidFilePath: opts.pidFile,
})
errors.Check(err)
web := confMgr.Web()
err = web.Start(startCtx)
errors.Check(err)
errors.Check(svc.Start(startCtx))
dns := confMgr.DNS()
err = dns.Start(startCtx)
errors.Check(err)
sigHdlr := service.NewSignalHandler(&service.SignalHandlerConfig{
Logger: baseLogger.With(slogutil.KeyPrefix, service.SignalHandlerPrefix),
})
sigHdlr := newSignalHandler(
baseLogger.With(slogutil.KeyPrefix, service.SignalHandlerPrefix),
confMgrConf,
opts.pidFile,
web,
dns,
)
sigHdlr.AddService(svc)
sigHdlr.AddRefresher(svc)
os.Exit(sigHdlr.handle(ctx))
os.Exit(sigHdlr.Handle(ctx))
}
// Default timeouts.
//
// TODO(a.garipov): Make configurable.
const (
defaultTimeoutStart = 1 * time.Minute
defaultTimeoutShutdown = 5 * time.Second
defaultTimeoutStart = 1 * time.Minute
)
// newConfigMgr returns a new configuration manager using defaultTimeout as the
// context timeout.
func newConfigMgr(ctx context.Context, c *configmgr.Config) (m *configmgr.Manager, err error) {
return configmgr.New(ctx, c)
}

View File

@@ -0,0 +1,187 @@
package cmd
import (
"context"
"fmt"
"log/slog"
"os"
"strconv"
"sync"
"github.com/AdguardTeam/AdGuardHome/internal/next/configmgr"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/service"
"github.com/google/renameio/v2/maybe"
)
// serviceMgr manages AdGuard Home services.
type serviceMgr struct {
// confMgrMu protects confMgr.
confMgrMu *sync.RWMutex
confMgr *configmgr.Manager
confMgrConf *configmgr.Config
logger *slog.Logger
pidFilePath string
}
// serviceMgrConfig contains service manager configuration parameters.
type serviceMgrConfig struct {
// confMgrConf is the configuration manager config, it must not be nil.
confMgrConf *configmgr.Config
// logger is the logger used to log services activity, it must not be nil.
logger *slog.Logger
// pidFilePath is the path to the file where to store the PID, if any.
pidFilePath string
}
// newServiceMgr creates a new *serviceMgr.
func newServiceMgr(ctx context.Context, conf *serviceMgrConfig) (s *serviceMgr, err error) {
confMgr, err := configmgr.New(ctx, conf.confMgrConf)
if err != nil {
return nil, fmt.Errorf("creating config manager: %w", err)
}
return &serviceMgr{
confMgr: confMgr,
confMgrMu: &sync.RWMutex{},
confMgrConf: conf.confMgrConf,
logger: conf.logger,
pidFilePath: conf.pidFilePath,
}, nil
}
// type check
var _ service.Interface = (*serviceMgr)(nil)
// Start implements the [service.Interface] interface for *serviceMgr.
func (s *serviceMgr) Start(ctx context.Context) (err error) {
s.writePID(ctx)
s.confMgrMu.RLock()
defer s.confMgrMu.RUnlock()
var errs []error
err = s.confMgr.Web().Start(ctx)
if err != nil {
errs = append(errs, fmt.Errorf("starting web: %w", err))
}
err = s.confMgr.DNS().Start(ctx)
if err != nil {
errs = append(errs, fmt.Errorf("starting dnssvc: %w", err))
}
return errors.Join(errs...)
}
// writePID writes the PID to the file. Any errors are reported to log.
func (s *serviceMgr) writePID(ctx context.Context) {
if s.pidFilePath == "" {
return
}
pid := os.Getpid()
data := strconv.AppendInt(nil, int64(pid), 10)
data = append(data, '\n')
err := maybe.WriteFile(s.pidFilePath, data, 0o644)
if err != nil {
s.logger.ErrorContext(ctx, "writing pidfile", slogutil.KeyError, err)
return
}
s.logger.DebugContext(ctx, "wrote pid", "file", s.pidFilePath, "pid", pid)
}
// Shutdown implements the [service.Interface] interface for *serviceMgr.
func (s *serviceMgr) Shutdown(ctx context.Context) (err error) {
s.confMgrMu.RLock()
defer s.confMgrMu.RUnlock()
var errs []error
err = s.confMgr.Web().Shutdown(ctx)
if err != nil {
errs = append(errs, fmt.Errorf("shutting down web: %w", err))
}
err = s.confMgr.DNS().Shutdown(ctx)
if err != nil {
errs = append(errs, fmt.Errorf("shutting down dnssvc: %w", err))
}
s.removePID(ctx)
return errors.Join(errs...)
}
// removePID removes the PID file. Any errors are reported to log.
func (s *serviceMgr) removePID(ctx context.Context) {
if s.pidFilePath == "" {
return
}
err := os.Remove(s.pidFilePath)
if err != nil {
s.logger.ErrorContext(ctx, "removing pidfile", slogutil.KeyError, err)
return
}
s.logger.DebugContext(ctx, "removed pidfile", "file", s.pidFilePath)
}
// type check
var _ service.Refresher = (*serviceMgr)(nil)
// Refresh implements the [service.Refresher] interface for *serviceMgr.
func (s *serviceMgr) Refresh(ctx context.Context) (err error) {
s.logger.InfoContext(ctx, "reconfiguring started")
err = s.Shutdown(ctx)
if err != nil {
return fmt.Errorf("shutdown failed: %w", err)
}
// TODO(a.garipov): This is a very rough way to do it. Some services can
// be reconfigured without the full shutdown, and the error handling is
// currently not the best.
ctx, cancel := context.WithTimeout(ctx, defaultTimeoutStart)
defer cancel()
err = s.updConfMgr(ctx)
if err != nil {
return fmt.Errorf("updating configuration manager: %w", err)
}
err = s.Start(ctx)
if err != nil {
return fmt.Errorf("restarting services: %w", err)
}
s.logger.InfoContext(ctx, "reconfiguring finished")
return nil
}
// updConfMgr updates the configuration manager.
func (s *serviceMgr) updConfMgr(ctx context.Context) (err error) {
confMgr, err := configmgr.New(ctx, s.confMgrConf)
if err != nil {
return fmt.Errorf("creating config manager: %w", err)
}
s.confMgrMu.Lock()
defer s.confMgrMu.Unlock()
s.confMgr = confMgr
return nil
}

View File

@@ -1,203 +0,0 @@
package cmd
import (
"context"
"fmt"
"log/slog"
"os"
"strconv"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/next/configmgr"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/osutil"
"github.com/AdguardTeam/golibs/service"
"github.com/google/renameio/v2/maybe"
)
// signalHandler processes incoming signals and shuts services down.
type signalHandler struct {
// logger is used for logging the operation of the signal handler.
logger *slog.Logger
// confMgrConf contains the configuration parameters for the configuration
// manager.
confMgrConf *configmgr.Config
// signal is the channel to which OS signals are sent.
signal chan os.Signal
// pidFile is the path to the file where to store the PID, if any.
pidFile string
// services are the services that are shut down before application exiting.
services []service.Interface
// shutdownTimeout is the timeout for the shutdown operation.
shutdownTimeout time.Duration
}
// handle processes OS signals. It blocks until a termination or a
// reconfiguration signal is received, after which it either shuts down all
// services or reconfigures them. ctx is used for logging and serves as the
// base for the shutdown timeout. status is [osutil.ExitCodeSuccess] on success
// and [osutil.ExitCodeFailure] on error.
//
// TODO(a.garipov): Add reconfiguration logic to golibs.
func (h *signalHandler) handle(ctx context.Context) (status osutil.ExitCode) {
defer slogutil.RecoverAndLog(ctx, h.logger)
h.writePID(ctx)
for sig := range h.signal {
h.logger.InfoContext(ctx, "received", "signal", sig)
if osutil.IsReconfigureSignal(sig) {
err := h.reconfigure(ctx)
if err != nil {
h.logger.ErrorContext(ctx, "reconfiguration error", slogutil.KeyError, err)
return osutil.ExitCodeFailure
}
} else if osutil.IsShutdownSignal(sig) {
status = h.shutdown(ctx)
h.removePID(ctx)
return status
}
}
// Shouldn't happen, since h.signal is currently never closed.
panic("unexpected close of h.signal")
}
// writePID writes the PID to the file, if needed. Any errors are reported to
// log.
func (h *signalHandler) writePID(ctx context.Context) {
if h.pidFile == "" {
return
}
pid := os.Getpid()
data := strconv.AppendInt(nil, int64(pid), 10)
data = append(data, '\n')
err := maybe.WriteFile(h.pidFile, data, 0o644)
if err != nil {
h.logger.ErrorContext(ctx, "writing pidfile", slogutil.KeyError, err)
return
}
h.logger.DebugContext(ctx, "wrote pid", "file", h.pidFile, "pid", pid)
}
// reconfigure rereads the configuration file and updates and restarts services.
func (h *signalHandler) reconfigure(ctx context.Context) (err error) {
h.logger.InfoContext(ctx, "reconfiguring started")
status := h.shutdown(ctx)
if status != osutil.ExitCodeSuccess {
return errors.Error("shutdown failed")
}
// TODO(a.garipov): This is a very rough way to do it. Some services can
// be reconfigured without the full shutdown, and the error handling is
// currently not the best.
var errs []error
ctx, cancel := context.WithTimeout(ctx, defaultTimeoutStart)
defer cancel()
confMgr, err := newConfigMgr(ctx, h.confMgrConf)
if err != nil {
errs = append(errs, fmt.Errorf("configuration manager: %w", err))
}
web := confMgr.Web()
err = web.Start(ctx)
if err != nil {
errs = append(errs, fmt.Errorf("starting web: %w", err))
}
dns := confMgr.DNS()
err = dns.Start(ctx)
if err != nil {
errs = append(errs, fmt.Errorf("starting dns: %w", err))
}
if len(errs) > 0 {
return errors.Join(errs...)
}
h.services = []service.Interface{
dns,
web,
}
h.logger.InfoContext(ctx, "reconfiguring finished")
return nil
}
// shutdown gracefully shuts down all services.
func (h *signalHandler) shutdown(ctx context.Context) (status int) {
ctx, cancel := context.WithTimeout(ctx, h.shutdownTimeout)
defer cancel()
status = osutil.ExitCodeSuccess
h.logger.InfoContext(ctx, "shutting down")
for i, svc := range h.services {
err := svc.Shutdown(ctx)
if err != nil {
h.logger.ErrorContext(ctx, "shutting down service", "idx", i, slogutil.KeyError, err)
status = osutil.ExitCodeFailure
}
}
return status
}
// newSignalHandler returns a new signalHandler that shuts down svcs. logger
// and confMgrConf must not be nil.
func newSignalHandler(
logger *slog.Logger,
confMgrConf *configmgr.Config,
pidFile string,
svcs ...service.Interface,
) (h *signalHandler) {
h = &signalHandler{
logger: logger,
confMgrConf: confMgrConf,
signal: make(chan os.Signal, 1),
pidFile: pidFile,
services: svcs,
shutdownTimeout: defaultTimeoutShutdown,
}
notifier := osutil.DefaultSignalNotifier{}
osutil.NotifyShutdownSignal(notifier, h.signal)
osutil.NotifyReconfigureSignal(notifier, h.signal)
return h
}
// removePID removes the PID file, if any.
func (h *signalHandler) removePID(ctx context.Context) {
if h.pidFile == "" {
return
}
err := os.Remove(h.pidFile)
if err != nil {
h.logger.ErrorContext(ctx, "removing pidfile", slogutil.KeyError, err)
return
}
h.logger.DebugContext(ctx, "removed pidfile", "file", h.pidFile)
}

View File

@@ -3,6 +3,7 @@ package configmgr
import (
"net/netip"
"github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/golibs/container"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/timeutil"
@@ -51,13 +52,17 @@ func (c *config) Validate() (err error) {
// dnsConfig is the on-disk DNS configuration.
type dnsConfig struct {
Addresses []netip.AddrPort `yaml:"addresses"`
BootstrapDNS []string `yaml:"bootstrap_dns"`
UpstreamDNS []string `yaml:"upstream_dns"`
DNS64Prefixes []netip.Prefix `yaml:"dns64_prefixes"`
UpstreamTimeout timeutil.Duration `yaml:"upstream_timeout"`
BootstrapPreferIPv6 bool `yaml:"bootstrap_prefer_ipv6"`
UseDNS64 bool `yaml:"use_dns64"`
UpstreamMode proxy.UpstreamMode `yaml:"upstream_mode"`
Addresses []netip.AddrPort `yaml:"addresses"`
BootstrapDNS []string `yaml:"bootstrap_dns"`
UpstreamDNS []string `yaml:"upstream_dns"`
DNS64Prefixes []netip.Prefix `yaml:"dns64_prefixes"`
UpstreamTimeout timeutil.Duration `yaml:"upstream_timeout"`
Ratelimit int `yaml:"ratelimit"`
CacheSize int `yaml:"cache_size"`
BootstrapPreferIPv6 bool `yaml:"bootstrap_prefer_ipv6"`
RefuseAny bool `yaml:"refuse_any"`
UseDNS64 bool `yaml:"use_dns64"`
}
// type check

View File

@@ -158,12 +158,17 @@ func (m *Manager) assemble(
) (err error) {
dnsConf := &dnssvc.Config{
Logger: m.baseLogger.With(slogutil.KeyPrefix, "dnssvc"),
UpstreamMode: conf.DNS.UpstreamMode,
Addresses: conf.DNS.Addresses,
BootstrapServers: conf.DNS.BootstrapDNS,
UpstreamServers: conf.DNS.UpstreamDNS,
DNS64Prefixes: conf.DNS.DNS64Prefixes,
UpstreamTimeout: time.Duration(conf.DNS.UpstreamTimeout),
CacheSize: conf.DNS.CacheSize,
Ratelimit: conf.DNS.Ratelimit,
BootstrapPreferIPv6: conf.DNS.BootstrapPreferIPv6,
CacheEnabled: conf.DNS.CacheSize > 0,
RefuseAny: conf.DNS.RefuseAny,
UseDNS64: conf.DNS.UseDNS64,
}
err = m.updateDNS(ctx, dnsConf)
@@ -263,11 +268,15 @@ func (m *Manager) updateDNS(ctx context.Context, c *dnssvc.Config) (err error) {
// updateCurrentDNS updates the DNS configuration in the current config.
func (m *Manager) updateCurrentDNS(c *dnssvc.Config) {
m.current.DNS.Addresses = slices.Clone(c.Addresses)
m.current.DNS.UpstreamMode = c.UpstreamMode
m.current.DNS.BootstrapDNS = slices.Clone(c.BootstrapServers)
m.current.DNS.UpstreamDNS = slices.Clone(c.UpstreamServers)
m.current.DNS.DNS64Prefixes = slices.Clone(c.DNS64Prefixes)
m.current.DNS.UpstreamTimeout = timeutil.Duration(c.UpstreamTimeout)
m.current.DNS.Ratelimit = c.Ratelimit
m.current.DNS.CacheSize = c.CacheSize
m.current.DNS.BootstrapPreferIPv6 = c.BootstrapPreferIPv6
m.current.DNS.RefuseAny = c.RefuseAny
m.current.DNS.UseDNS64 = c.UseDNS64
}

View File

@@ -4,16 +4,21 @@ import (
"log/slog"
"net/netip"
"time"
"github.com/AdguardTeam/dnsproxy/proxy"
)
// Config is the AdGuard Home DNS service configuration structure.
//
// TODO(a.garipov): Add timeout for incoming requests.
type Config struct {
// Logger is used for logging the operation of the web API service. It must
// not be nil.
// Logger is used for logging the operation of the DNS service. It must not
// be nil.
Logger *slog.Logger
// UpstreamMode defines how upstreams are used.
UpstreamMode proxy.UpstreamMode
// Addresses are the addresses on which to serve plain DNS queries.
Addresses []netip.AddrPort
@@ -31,10 +36,25 @@ type Config struct {
// UpstreamTimeout is the timeout for upstream requests.
UpstreamTimeout time.Duration
// CacheSize is the maximum cache size in bytes.
//
// TODO(a.garipov): Use bytesize.Bytes everywhere.
CacheSize int
// Ratelimit is the maximum number of requests per second from a given IP or
// subnet. If it is zero, rate limiting is disabled.
Ratelimit int
// BootstrapPreferIPv6, if true, instructs the bootstrapper to prefer IPv6
// addresses to IPv4 ones when bootstrapping.
BootstrapPreferIPv6 bool
// CacheEnabled defines if the response cache should be used.
CacheEnabled bool
// RefuseAny, if true, refuses DNS queries with the type ANY.
RefuseAny bool
// UseDNS64, if true, enables DNS64 protection for incoming requests.
UseDNS64 bool
}

View File

@@ -14,13 +14,12 @@ import (
"time"
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
"github.com/AdguardTeam/AdGuardHome/internal/aghslog"
"github.com/AdguardTeam/AdGuardHome/internal/next/agh"
// TODO(a.garipov): Add a “dnsproxy proxy” package to shield us from changes
// and replacement of module dnsproxy.
"github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/dnsproxy/upstream"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/logutil/slogutil"
)
// Service is the AdGuard Home DNS service. A nil *Service is a valid
@@ -29,16 +28,30 @@ import (
// TODO(a.garipov): Consider saving a [*proxy.Config] instance for those
// fields that are only used in [New] and [Service.Config].
type Service struct {
logger *slog.Logger
proxy *proxy.Proxy
// logger is used for logging the operation of the DNS service.
logger *slog.Logger
// proxy is the current DNS proxy.
proxy *proxy.Proxy
// proxyConf contains the fields that have been used to create proxy to
// return them in [Service.Config].
proxyConf *proxy.Config
// The fields below have been used to create proxy and are saved to return
// them in [Service.Config].
bootstraps []string
bootstrapResolvers []*upstream.UpstreamResolver
upstreams []string
dns64Prefixes []netip.Prefix
upsTimeout time.Duration
running atomic.Bool
upstreamTimeout time.Duration
bootstrapPreferIPv6 bool
useDNS64 bool
// The fields above have been used to create proxy and are saved to return
// them in [Service.Config].
// running is true when the service has started.
running atomic.Bool
}
// New returns a new properly initialized *Service. If c is nil, svc is a nil
@@ -50,16 +63,24 @@ func New(c *Config) (svc *Service, err error) {
}
svc = &Service{
logger: c.Logger,
logger: c.Logger,
proxyConf: &proxy.Config{
UpstreamMode: c.UpstreamMode,
Ratelimit: c.Ratelimit,
DNS64Prefs: c.DNS64Prefixes,
CacheSizeBytes: c.CacheSize,
CacheEnabled: c.CacheEnabled,
RefuseAny: c.RefuseAny,
UseDNS64: c.UseDNS64,
},
bootstraps: c.BootstrapServers,
upstreams: c.UpstreamServers,
dns64Prefixes: c.DNS64Prefixes,
upsTimeout: c.UpstreamTimeout,
upstreamTimeout: c.UpstreamTimeout,
bootstrapPreferIPv6: c.BootstrapPreferIPv6,
useDNS64: c.UseDNS64,
}
upstreams, resolvers, err := addressesToUpstreams(
svc.logger.With(slogutil.KeyPrefix, aghslog.PrefixDNSProxy),
c.UpstreamServers,
c.BootstrapServers,
c.UpstreamTimeout,
@@ -70,15 +91,20 @@ func New(c *Config) (svc *Service, err error) {
}
svc.bootstrapResolvers = resolvers
svc.proxy, err = proxy.New(&proxy.Config{
Logger: svc.logger,
UDPListenAddr: udpAddrs(c.Addresses),
TCPListenAddr: tcpAddrs(c.Addresses),
Logger: svc.logger,
UpstreamConfig: &proxy.UpstreamConfig{
Upstreams: upstreams,
},
UseDNS64: c.UseDNS64,
DNS64Prefs: c.DNS64Prefixes,
UDPListenAddr: udpAddrs(c.Addresses),
TCPListenAddr: tcpAddrs(c.Addresses),
UpstreamMode: svc.proxyConf.UpstreamMode,
Ratelimit: svc.proxyConf.Ratelimit,
DNS64Prefs: svc.proxyConf.DNS64Prefs,
CacheEnabled: svc.proxyConf.CacheEnabled,
RefuseAny: svc.proxyConf.RefuseAny,
UseDNS64: svc.proxyConf.UseDNS64,
})
if err != nil {
return nil, fmt.Errorf("proxy: %w", err)
@@ -89,19 +115,19 @@ func New(c *Config) (svc *Service, err error) {
// addressesToUpstreams is a wrapper around [upstream.AddressToUpstream]. It
// accepts a slice of addresses and other upstream parameters, and returns a
// slice of upstreams.
// slice of upstreams. logger must not be nil.
func addressesToUpstreams(
logger *slog.Logger,
upsStrs []string,
bootstraps []string,
timeout time.Duration,
preferIPv6 bool,
) (upstreams []upstream.Upstream, boots []*upstream.UpstreamResolver, err error) {
opts := &upstream.Options{
boots, err = aghnet.ParseBootstraps(bootstraps, &upstream.Options{
Logger: logger.With(aghslog.KeyUpstreamType, aghslog.UpstreamTypeBootstrap),
Timeout: timeout,
PreferIPv6: preferIPv6,
}
boots, err = aghnet.ParseBootstraps(bootstraps, opts)
})
if err != nil {
// Don't wrap the error, since it's informative enough as is.
return nil, nil, err
@@ -116,6 +142,7 @@ func addressesToUpstreams(
upstreams = make([]upstream.Upstream, len(upsStrs))
for i, upsStr := range upsStrs {
upstreams[i], err = upstream.AddressToUpstream(upsStr, &upstream.Options{
Logger: logger.With(aghslog.KeyUpstreamType, aghslog.UpstreamTypeMain),
Bootstrap: bootstrap,
Timeout: timeout,
PreferIPv6: preferIPv6,
@@ -220,13 +247,18 @@ func (svc *Service) Config() (c *Config) {
c = &Config{
Logger: svc.logger,
UpstreamMode: svc.proxyConf.UpstreamMode,
Addresses: addrs,
BootstrapServers: svc.bootstraps,
UpstreamServers: svc.upstreams,
DNS64Prefixes: svc.dns64Prefixes,
UpstreamTimeout: svc.upsTimeout,
DNS64Prefixes: svc.proxyConf.DNS64Prefs,
UpstreamTimeout: svc.upstreamTimeout,
CacheSize: svc.proxyConf.CacheSizeBytes,
Ratelimit: svc.proxyConf.Ratelimit,
BootstrapPreferIPv6: svc.bootstrapPreferIPv6,
UseDNS64: svc.useDNS64,
CacheEnabled: svc.proxyConf.CacheEnabled,
RefuseAny: svc.proxyConf.RefuseAny,
UseDNS64: svc.proxyConf.UseDNS64,
}
return c

View File

@@ -6,6 +6,7 @@ import (
"time"
"github.com/AdguardTeam/AdGuardHome/internal/next/dnssvc"
"github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/testutil"
"github.com/miekg/dns"
@@ -56,14 +57,12 @@ func TestService(t *testing.T) {
_, _ = testutil.RequireReceive(t, upstreamStartedCh, testTimeout)
c := &dnssvc.Config{
Logger: slogutil.NewDiscardLogger(),
Addresses: []netip.AddrPort{netip.MustParseAddrPort(listenAddr)},
BootstrapServers: []string{upstreamSrv.PacketConn.LocalAddr().String()},
UpstreamServers: []string{upstreamAddr},
DNS64Prefixes: nil,
UpstreamTimeout: testTimeout,
BootstrapPreferIPv6: false,
UseDNS64: false,
Logger: slogutil.NewDiscardLogger(),
UpstreamMode: proxy.UpstreamModeParallel,
Addresses: []netip.AddrPort{netip.MustParseAddrPort(listenAddr)},
BootstrapServers: []string{upstreamSrv.PacketConn.LocalAddr().String()},
UpstreamServers: []string{upstreamAddr},
UpstreamTimeout: testTimeout,
}
svc, err := dnssvc.New(c)

View File

@@ -8,6 +8,7 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
"github.com/AdguardTeam/AdGuardHome/internal/next/jsonpatch"
"github.com/AdguardTeam/dnsproxy/proxy"
)
// ReqPatchSettingsDNS describes the request to the PATCH /api/v1/settings/dns
@@ -15,6 +16,8 @@ import (
type ReqPatchSettingsDNS struct {
// TODO(a.garipov): Add more as we go.
UpstreamMode jsonpatch.NonRemovable[proxy.UpstreamMode] `json:"upstream_mode"`
Addresses jsonpatch.NonRemovable[[]netip.AddrPort] `json:"addresses"`
BootstrapServers jsonpatch.NonRemovable[[]string] `json:"bootstrap_servers"`
UpstreamServers jsonpatch.NonRemovable[[]string] `json:"upstream_servers"`
@@ -22,7 +25,11 @@ type ReqPatchSettingsDNS struct {
UpstreamTimeout jsonpatch.NonRemovable[aghhttp.JSONDuration] `json:"upstream_timeout"`
CacheSize jsonpatch.NonRemovable[int] `json:"cache_size"`
Ratelimit jsonpatch.NonRemovable[int] `json:"ratelimit"`
BootstrapPreferIPv6 jsonpatch.NonRemovable[bool] `json:"bootstrap_prefer_ipv6"`
RefuseAny jsonpatch.NonRemovable[bool] `json:"refuse_any"`
UseDNS64 jsonpatch.NonRemovable[bool] `json:"use_dns64"`
}
@@ -31,13 +38,23 @@ type ReqPatchSettingsDNS struct {
type HTTPAPIDNSSettings struct {
// TODO(a.garipov): Add more as we go.
Addresses []netip.AddrPort `json:"addresses"`
BootstrapServers []string `json:"bootstrap_servers"`
UpstreamServers []string `json:"upstream_servers"`
DNS64Prefixes []netip.Prefix `json:"dns64_prefixes"`
UpstreamTimeout aghhttp.JSONDuration `json:"upstream_timeout"`
BootstrapPreferIPv6 bool `json:"bootstrap_prefer_ipv6"`
UseDNS64 bool `json:"use_dns64"`
UpstreamMode proxy.UpstreamMode `json:"upstream_mode"`
Addresses []netip.AddrPort `json:"addresses"`
BootstrapServers []string `json:"bootstrap_servers"`
UpstreamServers []string `json:"upstream_servers"`
DNS64Prefixes []netip.Prefix `json:"dns64_prefixes"`
UpstreamTimeout aghhttp.JSONDuration `json:"upstream_timeout"`
Ratelimit int `json:"ratelimit"`
CacheSize int `json:"cache_size"`
BootstrapPreferIPv6 bool `json:"bootstrap_prefer_ipv6"`
RefuseAny bool `json:"refuse_any"`
UseDNS64 bool `json:"use_dns64"`
}
// handlePatchSettingsDNS is the handler for the PATCH /api/v1/settings/dns HTTP
@@ -57,6 +74,8 @@ func (svc *Service) handlePatchSettingsDNS(w http.ResponseWriter, r *http.Reques
// TODO(a.garipov): Add more as we go.
req.UpstreamMode.Set(&newConf.UpstreamMode)
req.Addresses.Set(&newConf.Addresses)
req.BootstrapServers.Set(&newConf.BootstrapServers)
req.UpstreamServers.Set(&newConf.UpstreamServers)
@@ -64,7 +83,14 @@ func (svc *Service) handlePatchSettingsDNS(w http.ResponseWriter, r *http.Reques
req.UpstreamTimeout.Set((*aghhttp.JSONDuration)(&newConf.UpstreamTimeout))
if req.CacheSize.IsSet {
newConf.CacheSize = req.CacheSize.Value
newConf.CacheEnabled = req.CacheSize.Value > 0
}
req.Ratelimit.Set(&newConf.Ratelimit)
req.BootstrapPreferIPv6.Set(&newConf.BootstrapPreferIPv6)
req.RefuseAny.Set(&newConf.RefuseAny)
req.UseDNS64.Set(&newConf.UseDNS64)
ctx := r.Context()
@@ -84,12 +110,16 @@ func (svc *Service) handlePatchSettingsDNS(w http.ResponseWriter, r *http.Reques
}
aghhttp.WriteJSONResponseOK(w, r, &HTTPAPIDNSSettings{
UpstreamMode: newConf.UpstreamMode,
Addresses: newConf.Addresses,
BootstrapServers: newConf.BootstrapServers,
UpstreamServers: newConf.UpstreamServers,
DNS64Prefixes: newConf.DNS64Prefixes,
UpstreamTimeout: aghhttp.JSONDuration(newConf.UpstreamTimeout),
Ratelimit: newConf.Ratelimit,
BootstrapPreferIPv6: newConf.BootstrapPreferIPv6,
CacheSize: newConf.CacheSize,
RefuseAny: newConf.RefuseAny,
UseDNS64: newConf.UseDNS64,
})
}

View File

@@ -15,6 +15,7 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/next/agh"
"github.com/AdguardTeam/AdGuardHome/internal/next/dnssvc"
"github.com/AdguardTeam/AdGuardHome/internal/next/websvc"
"github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/golibs/netutil/urlutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -22,12 +23,16 @@ import (
func TestService_HandlePatchSettingsDNS(t *testing.T) {
wantDNS := &websvc.HTTPAPIDNSSettings{
UpstreamMode: proxy.UpstreamModeParallel,
Addresses: []netip.AddrPort{netip.MustParseAddrPort("127.0.1.1:53")},
BootstrapServers: []string{"1.0.0.1"},
UpstreamServers: []string{"1.1.1.1"},
DNS64Prefixes: []netip.Prefix{netip.MustParsePrefix("1234::/64")},
UpstreamTimeout: aghhttp.JSONDuration(2 * time.Second),
Ratelimit: 100,
CacheSize: 1048576,
BootstrapPreferIPv6: true,
RefuseAny: true,
UseDNS64: true,
}
@@ -56,12 +61,16 @@ func TestService_HandlePatchSettingsDNS(t *testing.T) {
}
req := jobj{
"upstream_mode": wantDNS.UpstreamMode,
"addresses": wantDNS.Addresses,
"bootstrap_servers": wantDNS.BootstrapServers,
"upstream_servers": wantDNS.UpstreamServers,
"dns64_prefixes": wantDNS.DNS64Prefixes,
"upstream_timeout": wantDNS.UpstreamTimeout,
"cache_size": wantDNS.CacheSize,
"ratelimit": wantDNS.Ratelimit,
"bootstrap_prefer_ipv6": wantDNS.BootstrapPreferIPv6,
"refuse_any": wantDNS.RefuseAny,
"use_dns64": wantDNS.UseDNS64,
}

View File

@@ -29,12 +29,16 @@ func (svc *Service) handleGetSettingsAll(w http.ResponseWriter, r *http.Request)
// TODO(a.garipov): Add all currently supported parameters.
aghhttp.WriteJSONResponseOK(w, r, &RespGetV1SettingsAll{
DNS: &HTTPAPIDNSSettings{
UpstreamMode: dnsConf.UpstreamMode,
Addresses: dnsConf.Addresses,
BootstrapServers: dnsConf.BootstrapServers,
UpstreamServers: dnsConf.UpstreamServers,
DNS64Prefixes: dnsConf.DNS64Prefixes,
UpstreamTimeout: aghhttp.JSONDuration(dnsConf.UpstreamTimeout),
Ratelimit: dnsConf.Ratelimit,
BootstrapPreferIPv6: dnsConf.BootstrapPreferIPv6,
CacheSize: dnsConf.CacheSize,
RefuseAny: dnsConf.RefuseAny,
UseDNS64: dnsConf.UseDNS64,
},
HTTP: &HTTPAPIHTTPSettings{

View File

@@ -12,6 +12,7 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/next/agh"
"github.com/AdguardTeam/AdGuardHome/internal/next/dnssvc"
"github.com/AdguardTeam/AdGuardHome/internal/next/websvc"
"github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/netutil/urlutil"
"github.com/stretchr/testify/assert"
@@ -22,22 +23,32 @@ func TestService_HandleGetSettingsAll(t *testing.T) {
// TODO(a.garipov): Add all currently supported parameters.
wantDNS := &websvc.HTTPAPIDNSSettings{
UpstreamMode: proxy.UpstreamModeParallel,
Addresses: []netip.AddrPort{netip.MustParseAddrPort("127.0.0.1:53")},
BootstrapServers: []string{"94.140.14.140", "94.140.14.141"},
UpstreamServers: []string{"94.140.14.14", "1.1.1.1"},
UpstreamTimeout: aghhttp.JSONDuration(1 * time.Second),
Ratelimit: 100,
CacheSize: 1048576,
BootstrapPreferIPv6: true,
RefuseAny: true,
UseDNS64: true,
}
confMgr := newConfigManager()
confMgr.onDNS = func() (s agh.ServiceWithConfig[*dnssvc.Config]) {
c, err := dnssvc.New(&dnssvc.Config{
Logger: slogutil.NewDiscardLogger(),
UpstreamMode: proxy.UpstreamModeParallel,
Addresses: wantDNS.Addresses,
UpstreamServers: wantDNS.UpstreamServers,
BootstrapServers: wantDNS.BootstrapServers,
UpstreamTimeout: time.Duration(wantDNS.UpstreamTimeout),
CacheSize: 1048576,
Ratelimit: 100,
BootstrapPreferIPv6: true,
RefuseAny: true,
UseDNS64: true,
})
require.NoError(t, err)

View File

@@ -3,7 +3,9 @@
// NOTE: Packages other than cmd must not import this package, as it imports
// most other packages.
//
// TODO(a.garipov): Add tests.
// TODO(a.garipov): Add tests.
//
// TODO(a.garipov): Split into subpackages for groups of handlers?
package websvc
import (
@@ -25,6 +27,8 @@ import (
)
// ConfigManager is the configuration manager interface.
//
// TODO(a.garipov): Add docs.
type ConfigManager interface {
DNS() (svc agh.ServiceWithConfig[*dnssvc.Config])
Web() (svc agh.ServiceWithConfig[*Config])

View File

@@ -2284,7 +2284,8 @@
'dnssec': false
'edns_client_subnet': false
'ipv6': true
'rate_limit': 20
'ratelimit': 20
'refuse_any': true
'upstream_mode': 'load_balancing'
'upstream_servers':
- '1.1.1.1'
@@ -2300,7 +2301,8 @@
- 'dnssec'
- 'edns_client_subnet'
- 'ipv6'
- 'rate_limit'
- 'ratelimit'
- 'refuse_any'
- 'upstream_mode'
- 'upstream_servers'
- 'upstream_timeout'
@@ -2380,13 +2382,17 @@
If `true`, accept `AAAA` DNS queries. If `false`, respond to them
with an empty answer.
'type': 'boolean'
'rate_limit':
'ratelimit':
'description': >
The number of requests per second that a single client is allowed to
make. `0` means no limit.
'format': 'int64'
'minimum': 0
'type': 'integer'
'refuse_any':
'description': >
If `true`, reject `ANY` DNS queries.
'type': 'boolean'
'upstream_mode':
'$ref': '#/components/schemas/DnsUpstreamMode'
'upstream_servers':