From ed12040673f4120c22a5454b508ce9d87d677fb2 Mon Sep 17 00:00:00 2001 From: Eugene Burkov Date: Thu, 10 Jul 2025 17:55:28 +0300 Subject: [PATCH] all: sync with master --- .github/workflows/build.yml | 2 +- .github/workflows/lint.yml | 2 +- CHANGELOG.md | 32 ++- Makefile | 2 +- bamboo-specs/release.yaml | 6 +- bamboo-specs/test.yaml | 4 +- client/src/__locales/be.json | 2 +- client/src/__locales/cs.json | 56 ++--- client/src/__locales/tr.json | 160 +++++++------- go.mod | 42 ++-- go.sum | 88 ++++---- internal/aghslog/aghslog.go | 52 +++++ internal/aghtest/aghtest.go | 29 --- internal/client/persistent.go | 5 +- internal/client/storage.go | 8 +- internal/client/storage_test.go | 44 ++-- internal/client/upstreammanager.go | 30 ++- internal/dnsforward/clientid.go | 14 +- internal/dnsforward/clientid_internal_test.go | 35 +-- internal/dnsforward/config.go | 62 ++++-- internal/dnsforward/dnsforward.go | 7 +- .../dnsforward/dnsforward_internal_test.go | 42 ++-- internal/dnsforward/filter_internal_test.go | 10 +- internal/dnsforward/http.go | 11 +- internal/dnsforward/ipset_internal_test.go | 9 +- internal/dnsforward/process_internal_test.go | 6 +- internal/dnsforward/stats_internal_test.go | 3 +- .../dnsforward/upstreams_internal_test.go | 2 + internal/filtering/blocked.go | 35 +-- internal/filtering/dnsrewrite_test.go | 2 + internal/filtering/filter.go | 111 ++++++---- internal/filtering/filter_internal_test.go | 24 ++- internal/filtering/filtering.go | 130 +++++++---- internal/filtering/filtering_internal_test.go | 42 ++-- internal/filtering/hashprefix/cache.go | 13 +- internal/filtering/hashprefix/hashprefix.go | 52 +++-- .../hashprefix/hashprefix_internal_test.go | 33 +-- internal/filtering/hosts.go | 22 +- internal/filtering/hosts_test.go | 2 + internal/filtering/http.go | 21 +- internal/filtering/http_internal_test.go | 5 + internal/filtering/idgenerator.go | 18 +- .../filtering/idgenerator_internal_test.go | 3 +- internal/filtering/rewrite/storage.go | 45 +++- .../rewrite/storage_internal_test.go | 37 +++- internal/filtering/rewritehttp.go | 38 +++- internal/filtering/rewritehttp_test.go | 2 + internal/filtering/rewrites.go | 22 +- internal/filtering/rewrites_internal_test.go | 13 +- internal/filtering/servicelist.go | 1 + internal/home/clients.go | 1 + internal/home/clients_internal_test.go | 4 +- internal/home/controlinstall.go | 1 + internal/home/dns.go | 42 ++-- internal/home/home.go | 48 +++-- internal/home/tls_internal_test.go | 10 +- internal/next/AdGuardHome.example.yaml | 51 +++-- internal/next/cmd/cmd.go | 36 ++-- internal/next/cmd/service.go | 187 ++++++++++++++++ internal/next/cmd/signal.go | 203 ------------------ internal/next/configmgr/config.go | 19 +- internal/next/configmgr/configmgr.go | 9 + internal/next/dnssvc/config.go | 24 ++- internal/next/dnssvc/dnssvc.go | 84 +++++--- internal/next/dnssvc/dnssvc_test.go | 15 +- internal/next/websvc/dns.go | 44 +++- internal/next/websvc/dns_test.go | 9 + internal/next/websvc/settings.go | 4 + internal/next/websvc/settings_test.go | 11 + internal/next/websvc/websvc.go | 6 +- openapi/next.yaml | 12 +- 71 files changed, 1351 insertions(+), 905 deletions(-) create mode 100644 internal/aghslog/aghslog.go create mode 100644 internal/next/cmd/service.go delete mode 100644 internal/next/cmd/signal.go diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 42f2fab1..617f3119 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,7 +1,7 @@ 'name': 'build' 'env': - 'GO_VERSION': '1.24.4' + 'GO_VERSION': '1.24.5' 'NODE_VERSION': '20' 'on': diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 7cf17e1e..2c0b13e8 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -1,7 +1,7 @@ 'name': 'lint' 'env': - 'GO_VERSION': '1.24.4' + 'GO_VERSION': '1.24.5' 'on': 'push': diff --git a/CHANGELOG.md b/CHANGELOG.md index e0475e0c..1a1cb609 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,11 +9,11 @@ The format is based on [*Keep a Changelog*](https://keepachangelog.com/en/1.0.0/ @@ -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.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 diff --git a/Makefile b/Makefile index 0d6552b1..a3bd3cfe 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/bamboo-specs/release.yaml b/bamboo-specs/release.yaml index 4319a820..27b7217e 100644 --- a/bamboo-specs/release.yaml +++ b/bamboo-specs/release.yaml @@ -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' diff --git a/bamboo-specs/test.yaml b/bamboo-specs/test.yaml index 737677a5..ef116eaf 100644 --- a/bamboo-specs/test.yaml +++ b/bamboo-specs/test.yaml @@ -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' diff --git a/client/src/__locales/be.json b/client/src/__locales/be.json index 2b99f58d..db963b2e 100644 --- a/client/src/__locales/be.json +++ b/client/src/__locales/be.json @@ -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.", diff --git a/client/src/__locales/cs.json b/client/src/__locales/cs.json index 2075cb04..4851d0ba 100644 --- a/client/src/__locales/cs.json +++ b/client/src/__locales/cs.json @@ -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;", - "example_upstream_doh": "šifrovaný <0>DNS skrze HTTPS;", - "example_upstream_doh3": "šifrovaný DNS skrze HTTPS s vynuceným <0>HTTP/3 a bez možnosti zpětného přechodu na HTTP/2 nebo nižší;", - "example_upstream_doq": "šifrovaný <0>DNS skrze QUIC;", - "example_upstream_sdns": "<0>DNS razítka pro <1>DNSCrypt nebo <2>DNS skrze HTTPS řešitele;", + "example_upstream_dot": "šifrovaný <0>DNS-over-TLS;", + "example_upstream_doh": "šifrovaný <0>DNS-over-HTTPS;", + "example_upstream_doh3": "šifrovaný DNS-over-HTTPS s vynuceným <0>HTTP/3 a bez možnosti zpětného přechodu na HTTP/2 nebo nižší;", + "example_upstream_doq": "šifrovaný <0>DNS-over-QUIC;", + "example_upstream_sdns": "<0>DNS razítka pro <1>DNSCrypt nebo <2>DNS-over-HTTPS ř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. Zde 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}}, 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: Použít <1>{{address}} řetězec.", - "setup_dns_privacy_2": "<0>DNS skrze HTTPS: Použít <1>{{address}} řetězec.", + "setup_dns_privacy_1": "<0>DNS-over-TLS: Použít <1>{{address}} řetězec.", + "setup_dns_privacy_2": "<0>DNS-over-HTTPS: Použít <1>{{address}} řetězec.", "setup_dns_privacy_3": "<0>Zde je seznam softwaru, který můžete použít.", - "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 DNS skrze HTTPS nebo DNS skrze TLS.", - "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 podporuje <1>DNS skrze HTTPS a <1>DNS skrze LS.", - "setup_dns_privacy_android_3": "<0>Intra přidává podporu <1>DNS skrze HTTPS pro Android.", - "setup_dns_privacy_ios_1": "<0>DNSCloak podporuje funkci <1>DNS skrze HTTPS, ale abyste ji mohli nakonfigurovat pro používání vlastního serveru, musíte vygenerovat značku <2>DNS Stamp.", - "setup_dns_privacy_ios_2": "<0>AdGuard pro iOS podporuje nastavení <1>DNS skrze HTTPS a <1>DNS skrze TLS.", + "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 DNS-over-HTTPS nebo DNS-over-TLS.", + "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 podporuje <1>DNS-over-HTTPS a <1>DNS-over-TLS.", + "setup_dns_privacy_android_3": "<0>Intra přidává podporu <1>DNS-over-HTTPS pro Android.", + "setup_dns_privacy_ios_1": "<0>DNSCloak podporuje funkci <1>DNS-over-HTTPS, ale abyste ji mohli nakonfigurovat pro používání vlastního serveru, musíte vygenerovat značku <2>DNS Stamp.", + "setup_dns_privacy_ios_2": "<0>AdGuard pro iOS podporuje nastavení <1>DNS-over-HTTPS a <1>DNS-over-TLS.", "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 podporuje všechny známé bezpečné DNS protokoly.", - "setup_dns_privacy_other_3": "<0>dnscrypt-proxy podporuje <1>DNS skrze HTTPS.", - "setup_dns_privacy_other_4": "<0>Mozilla Firefox podporuje <1>DNS skrze HTTPS.", + "setup_dns_privacy_other_3": "<0>dnscrypt-proxy podporuje <1>DNS-over-HTTPS.", + "setup_dns_privacy_other_4": "<0>Mozilla Firefox podporuje <1>DNS-over-HTTPS.", "setup_dns_privacy_other_5": "Další implementace naleznete <0>zde a <1>zde.", "setup_dns_privacy_ioc_mac": "Konfigurace pro iOS a macOS", - "setup_dns_notice": "Pro použití <1>DNS skrze HTTPS nebo <1>DNS skrze TLS potřebujete v nastaveních AdGuard Home <0>nakonfigurovat šifrování.", + "setup_dns_notice": "Pro použití <1>DNS-over-HTTPS nebo <1>DNS-over-TLS potřebujete v nastaveních AdGuard Home <0>nakonfigurovat šifrování.", "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", diff --git a/client/src/__locales/tr.json b/client/src/__locales/tr.json index f7db7231..aab240e1 100644 --- a/client/src/__locales/tr.json +++ b/client/src/__locales/tr.json @@ -1,24 +1,24 @@ { "client_settings": "İstemci ayarları", - "example_upstream_reserved": "<0>belirli alan adları için bir üst sunucusu;", - "example_multiple_upstreams_reserved": "<0>belirli alanlar için birden fazla üst kaynaklar;", + "example_upstream_reserved": "<0>belirli alan adları için bir üst kaynak;", + "example_multiple_upstreams_reserved": "<0>belirli alan adları için 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.
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}} 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}}. \"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}} 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}}. \"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 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 daha fazla bilgi edinin.", "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 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 buradan öğrenin.", + "client_id_desc": "İstemciler, İstemci Kimliği ile tanımlanabilir. İstemcileri nasıl tanımlayacağınız hakkında daha fazla bilgiye buradan 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ı güncelleyin.", "topline_expired_certificate": "SSL sertifikanızın süresi sona erdi. <0>Şifreleme ayarlarını 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.", + "update_announcement": "AdGuard Home'un {{version}} sürümü mevcut! Daha fazla bilgi için <0>buraya tıklayın.", "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.", @@ -451,7 +451,7 @@ "manual_update": "Elle güncellemek için lütfen bu adımları uygulayın.", "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 İstemci Kimliklerin listesi. Bu listede girişler varsa, AdGuard Home yalnızca bu istemcilerden gelen istekleri kabul eder.", + "access_allowed_desc": "CIDR'lerin, IP adreslerinin veya İstemci Kimliklerinin 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 İstemci Kimliklerin 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 İstemci Kimliklerinin 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: <1>{{address}} dizesini kullan.", "setup_dns_privacy_2": "<0>DNS-over-HTTPS: <1>{{address}} 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 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: özel değer, üst sunucudan gelen <0>A kayıtlarını tutun", - "rewrite_AAAA": "<0>AAA: özel değer, üst sunucudan gelen <0>AAA 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 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: özel değer, üst kaynak sunucudan gelen <0>A kayıtlarını tutar", + "rewrite_AAAA": "<0>AAA: özel değer, üst sunucudan gelen <0>AAA 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": "Tüm 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.
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>DNS sunucusu adresini 127.0.0.1 olarak ayarlayın <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)", "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.", + "tags_desc": "İstemciyi tanımlayan etiketleri seçebilirsiniz. Etiketleri filtreleme kurallarına ekleyerek filtrelemeyi daha etkin bir şekilde uygulayabilirsiniz. <0>Daha fazla bilgi edinin.", "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}} 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ı 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ı 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: IP anonimleştirme etkinleştirildi. Bunu <1>Genel ayarlardan devre dışı bırakabilirsiniz.", + "anonymizer_notification": "<0>Not: IP gizleme etkinleştirildi. Bunu <1>Genel ayarlardan devre dışı bırakabilirsiniz.", "confirm_dns_cache_clear": "DNS önbelleğini temizlemek istediğinizden emin misiniz?", "cache_cleared": "DNS önbelleği başarıyla temizlendi", "clear_cache": "Önbelleği temizle", @@ -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", diff --git a/go.mod b/go.mod index 190f3259..a0c6ff7f 100644 --- a/go.mod +++ b/go.mod @@ -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 ) diff --git a/go.sum b/go.sum index 7b9586f1..942b62ec 100644 --- a/go.sum +++ b/go.sum @@ -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= diff --git a/internal/aghslog/aghslog.go b/internal/aghslog/aghslog.go new file mode 100644 index 00000000..c307f344 --- /dev/null +++ b/internal/aghslog/aghslog.go @@ -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) +} diff --git a/internal/aghtest/aghtest.go b/internal/aghtest/aghtest.go index 1d9067c5..fcf62be4 100644 --- a/internal/aghtest/aghtest.go +++ b/internal/aghtest/aghtest.go @@ -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)) diff --git a/internal/client/persistent.go b/internal/client/persistent.go index 145dcc4b..1d1967a2 100644 --- a/internal/client/persistent.go +++ b/internal/client/persistent.go @@ -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) } diff --git a/internal/client/storage.go b/internal/client/storage.go index 734394d7..4ec327ad 100644 --- a/internal/client/storage.go +++ b/internal/client/storage.go @@ -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] diff --git a/internal/client/storage_test.go b/internal/client/storage_test.go index b1f68e41..f7d95715 100644 --- a/internal/client/storage_test.go +++ b/internal/client/storage_test.go @@ -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) diff --git a/internal/client/upstreammanager.go b/internal/client/upstreammanager.go index 38ad4231..3c571741 100644 --- a/internal/client/upstreammanager.go +++ b/internal/client/upstreammanager.go @@ -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, }, diff --git a/internal/dnsforward/clientid.go b/internal/dnsforward/clientid.go index 2a2d3825..b648b8ef 100644 --- a/internal/dnsforward/clientid.go +++ b/internal/dnsforward/clientid.go @@ -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) diff --git a/internal/dnsforward/clientid_internal_test.go b/internal/dnsforward/clientid_internal_test.go index ce325c4b..ec110f60 100644 --- a/internal/dnsforward/clientid_internal_test.go +++ b/internal/dnsforward/clientid_internal_test.go @@ -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) diff --git a/internal/dnsforward/config.go b/internal/dnsforward/config.go index 3217d2fd..a5d83c6c 100644 --- a/internal/dnsforward/config.go +++ b/internal/dnsforward/config.go @@ -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, diff --git a/internal/dnsforward/dnsforward.go b/internal/dnsforward/dnsforward.go index 2f695d7a..05814288 100644 --- a/internal/dnsforward/dnsforward.go +++ b/internal/dnsforward/dnsforward.go @@ -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, diff --git a/internal/dnsforward/dnsforward_internal_test.go b/internal/dnsforward/dnsforward_internal_test.go index 3889fb4c..53be7bbc 100644 --- a/internal/dnsforward/dnsforward_internal_test.go +++ b/internal/dnsforward/dnsforward_internal_test.go @@ -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 "!!!": ` + diff --git a/internal/dnsforward/filter_internal_test.go b/internal/dnsforward/filter_internal_test.go index b14df3f2..8a07b4f1 100644 --- a/internal/dnsforward/filter_internal_test.go +++ b/internal/dnsforward/filter_internal_test.go @@ -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) diff --git a/internal/dnsforward/http.go b/internal/dnsforward/http.go index cfa428cb..59f3fde8 100644 --- a/internal/dnsforward/http.go +++ b/internal/dnsforward/http.go @@ -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, } diff --git a/internal/dnsforward/ipset_internal_test.go b/internal/dnsforward/ipset_internal_test.go index 09601ac6..90c200d0 100644 --- a/internal/dnsforward/ipset_internal_test.go +++ b/internal/dnsforward/ipset_internal_test.go @@ -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 { diff --git a/internal/dnsforward/process_internal_test.go b/internal/dnsforward/process_internal_test.go index 71a91fdd..8b335832 100644 --- a/internal/dnsforward/process_internal_test.go +++ b/internal/dnsforward/process_internal_test.go @@ -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) diff --git a/internal/dnsforward/stats_internal_test.go b/internal/dnsforward/stats_internal_test.go index 6e4d5d86..301f8c8b 100644 --- a/internal/dnsforward/stats_internal_test.go +++ b/internal/dnsforward/stats_internal_test.go @@ -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), diff --git a/internal/dnsforward/upstreams_internal_test.go b/internal/dnsforward/upstreams_internal_test.go index 128c3eeb..4dbd289a 100644 --- a/internal/dnsforward/upstreams_internal_test.go +++ b/internal/dnsforward/upstreams_internal_test.go @@ -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, }) diff --git a/internal/filtering/blocked.go b/internal/filtering/blocked.go index ca59a1b8..8150f309 100644 --- a/internal/filtering/blocked.go +++ b/internal/filtering/blocked.go @@ -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() } diff --git a/internal/filtering/dnsrewrite_test.go b/internal/filtering/dnsrewrite_test.go index 89b6b30d..58353a43 100644 --- a/internal/filtering/dnsrewrite_test.go +++ b/internal/filtering/dnsrewrite_test.go @@ -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, diff --git a/internal/filtering/filter.go b/internal/filtering/filter.go index 14572d01..240b623d 100644 --- a/internal/filtering/filter.go +++ b/internal/filtering/filter.go @@ -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) diff --git a/internal/filtering/filter_internal_test.go b/internal/filtering/filter_internal_test.go index 8cfcdef9..c2d0de71 100644 --- a/internal/filtering/filter_internal_test.go +++ b/internal/filtering/filter_internal_test.go @@ -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) }) } diff --git a/internal/filtering/filtering.go b/internal/filtering/filtering.go index df65308f..41d437c3 100644 --- a/internal/filtering/filtering.go +++ b/internal/filtering/filtering.go @@ -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{ diff --git a/internal/filtering/filtering_internal_test.go b/internal/filtering/filtering_internal_test.go index 27260bf0..5097755c 100644 --- a/internal/filtering/filtering_internal_test.go +++ b/internal/filtering/filtering_internal_test.go @@ -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) diff --git a/internal/filtering/hashprefix/cache.go b/internal/filtering/hashprefix/cache.go index 7db2ae22..99d6cc73 100644 --- a/internal/filtering/hashprefix/cache.go +++ b/internal/filtering/hashprefix/cache.go @@ -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) } diff --git a/internal/filtering/hashprefix/hashprefix.go b/internal/filtering/hashprefix/hashprefix.go index 55795f9a..0a85c417 100644 --- a/internal/filtering/hashprefix/hashprefix.go +++ b/internal/filtering/hashprefix/hashprefix.go @@ -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 } diff --git a/internal/filtering/hashprefix/hashprefix_internal_test.go b/internal/filtering/hashprefix/hashprefix_internal_test.go index a575d0dd..f1ab3b86 100644 --- a/internal/filtering/hashprefix/hashprefix_internal_test.go +++ b/internal/filtering/hashprefix/hashprefix_internal_test.go @@ -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, }) diff --git a/internal/filtering/hosts.go b/internal/filtering/hosts.go index 4943b1af..ba5b3899 100644 --- a/internal/filtering/hosts.go +++ b/internal/filtering/hosts.go @@ -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 } diff --git a/internal/filtering/hosts_test.go b/internal/filtering/hosts_test.go index 14e20adc..5c692814 100644 --- a/internal/filtering/hosts_test.go +++ b/internal/filtering/hosts_test.go @@ -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) diff --git a/internal/filtering/http.go b/internal/filtering/http.go index 99acdb16..dca039a0 100644 --- a/internal/filtering/http.go +++ b/internal/filtering/http.go @@ -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() diff --git a/internal/filtering/http_internal_test.go b/internal/filtering/http_internal_test.go index a46d5d7b..4d45e254 100644 --- a/internal/filtering/http_internal_test.go +++ b/internal/filtering/http_internal_test.go @@ -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(), }, diff --git a/internal/filtering/idgenerator.go b/internal/filtering/idgenerator.go index e50f86ee..7d07cf23 100644 --- a/internal/filtering/idgenerator.go +++ b/internal/filtering/idgenerator.go @@ -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 diff --git a/internal/filtering/idgenerator_internal_test.go b/internal/filtering/idgenerator_internal_test.go index 57af4ad1..e9c0db2f 100644 --- a/internal/filtering/idgenerator_internal_test.go +++ b/internal/filtering/idgenerator_internal_test.go @@ -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) diff --git a/internal/filtering/rewrite/storage.go b/internal/filtering/rewrite/storage.go index 42b36273..bab84089 100644 --- a/internal/filtering/rewrite/storage.go +++ b/internal/filtering/rewrite/storage.go @@ -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 } diff --git a/internal/filtering/rewrite/storage_internal_test.go b/internal/filtering/rewrite/storage_internal_test.go index 502c20b9..10df670c 100644 --- a/internal/filtering/rewrite/storage_internal_test.go +++ b/internal/filtering/rewrite/storage_internal_test.go @@ -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 { diff --git a/internal/filtering/rewritehttp.go b/internal/filtering/rewritehttp.go index af2ddf1f..d6415a05 100644 --- a/internal/filtering/rewritehttp.go +++ b/internal/filtering/rewritehttp.go @@ -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, + ) } diff --git a/internal/filtering/rewritehttp_test.go b/internal/filtering/rewritehttp_test.go index 93eef85a..b95435b8 100644 --- a/internal/filtering/rewritehttp_test.go +++ b/internal/filtering/rewritehttp_test.go @@ -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 diff --git a/internal/filtering/rewrites.go b/internal/filtering/rewrites.go index 5ac2ffcc..809859b8 100644 --- a/internal/filtering/rewrites.go +++ b/internal/filtering/rewrites.go @@ -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) } } } diff --git a/internal/filtering/rewrites_internal_test.go b/internal/filtering/rewrites_internal_test.go index cdec8529..baa17a31 100644 --- a/internal/filtering/rewrites_internal_test.go +++ b/internal/filtering/rewrites_internal_test.go @@ -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 diff --git a/internal/filtering/servicelist.go b/internal/filtering/servicelist.go index 21162f3f..0e52ee48 100644 --- a/internal/filtering/servicelist.go +++ b/internal/filtering/servicelist.go @@ -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^", diff --git a/internal/home/clients.go b/internal/home/clients.go index 208b326b..e6459342 100644 --- a/internal/home/clients.go +++ b/internal/home/clients.go @@ -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, diff --git a/internal/home/clients_internal_test.go b/internal/home/clients_internal_test.go index 899ead65..8bbfd185 100644 --- a/internal/home/clients_internal_test.go +++ b/internal/home/clients_internal_test.go @@ -26,7 +26,9 @@ func newClientsContainer(t *testing.T) (c *clientsContainer) { client.EmptyDHCP{}, nil, nil, - &filtering.Config{}, + &filtering.Config{ + Logger: testLogger, + }, newSignalHandler(nil, nil), ) diff --git a/internal/home/controlinstall.go b/internal/home/controlinstall.go index 6e52f80a..602b0f64 100644 --- a/internal/home/controlinstall.go +++ b/internal/home/controlinstall.go @@ -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, "*"), } diff --git a/internal/home/dns.go b/internal/home/dns.go index 9afc6263..dea65bf7 100644 --- a/internal/home/dns.go +++ b/internal/home/dns.go @@ -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 } diff --git a/internal/home/home.go b/internal/home/home.go index 052892f8..f38a350f 100644 --- a/internal/home/home.go +++ b/internal/home/home.go @@ -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) diff --git a/internal/home/tls_internal_test.go b/internal/home/tls_internal_test.go index 6ce1782f..b65d0a97 100644 --- a/internal/home/tls_internal_test.go +++ b/internal/home/tls_internal_test.go @@ -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) diff --git a/internal/next/AdGuardHome.example.yaml b/internal/next/AdGuardHome.example.yaml index 1bdd8313..dd75a302 100644 --- a/internal/next/AdGuardHome.example.yaml +++ b/internal/next/AdGuardHome.example.yaml @@ -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 diff --git a/internal/next/cmd/cmd.go b/internal/next/cmd/cmd.go index 3bab1396..db6c5200 100644 --- a/internal/next/cmd/cmd.go +++ b/internal/next/cmd/cmd.go @@ -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) -} diff --git a/internal/next/cmd/service.go b/internal/next/cmd/service.go new file mode 100644 index 00000000..2aec9d73 --- /dev/null +++ b/internal/next/cmd/service.go @@ -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 +} diff --git a/internal/next/cmd/signal.go b/internal/next/cmd/signal.go deleted file mode 100644 index cdff1b44..00000000 --- a/internal/next/cmd/signal.go +++ /dev/null @@ -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) -} diff --git a/internal/next/configmgr/config.go b/internal/next/configmgr/config.go index 0a828f13..e4fdc00e 100644 --- a/internal/next/configmgr/config.go +++ b/internal/next/configmgr/config.go @@ -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 diff --git a/internal/next/configmgr/configmgr.go b/internal/next/configmgr/configmgr.go index 0e271355..bd8e73c4 100644 --- a/internal/next/configmgr/configmgr.go +++ b/internal/next/configmgr/configmgr.go @@ -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 } diff --git a/internal/next/dnssvc/config.go b/internal/next/dnssvc/config.go index e4e882be..f70f4057 100644 --- a/internal/next/dnssvc/config.go +++ b/internal/next/dnssvc/config.go @@ -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 } diff --git a/internal/next/dnssvc/dnssvc.go b/internal/next/dnssvc/dnssvc.go index 9e3b5b35..1d74a8f6 100644 --- a/internal/next/dnssvc/dnssvc.go +++ b/internal/next/dnssvc/dnssvc.go @@ -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 diff --git a/internal/next/dnssvc/dnssvc_test.go b/internal/next/dnssvc/dnssvc_test.go index c8a438eb..be81fc51 100644 --- a/internal/next/dnssvc/dnssvc_test.go +++ b/internal/next/dnssvc/dnssvc_test.go @@ -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) diff --git a/internal/next/websvc/dns.go b/internal/next/websvc/dns.go index cf185e2c..d6a79a49 100644 --- a/internal/next/websvc/dns.go +++ b/internal/next/websvc/dns.go @@ -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, }) } diff --git a/internal/next/websvc/dns_test.go b/internal/next/websvc/dns_test.go index 1965b3e6..960bf024 100644 --- a/internal/next/websvc/dns_test.go +++ b/internal/next/websvc/dns_test.go @@ -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, } diff --git a/internal/next/websvc/settings.go b/internal/next/websvc/settings.go index 44364ca3..f89d935a 100644 --- a/internal/next/websvc/settings.go +++ b/internal/next/websvc/settings.go @@ -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{ diff --git a/internal/next/websvc/settings_test.go b/internal/next/websvc/settings_test.go index b2003556..754cf796 100644 --- a/internal/next/websvc/settings_test.go +++ b/internal/next/websvc/settings_test.go @@ -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) diff --git a/internal/next/websvc/websvc.go b/internal/next/websvc/websvc.go index 189d231e..bf1a2578 100644 --- a/internal/next/websvc/websvc.go +++ b/internal/next/websvc/websvc.go @@ -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]) diff --git a/openapi/next.yaml b/openapi/next.yaml index adab6d4d..c9fdee12 100644 --- a/openapi/next.yaml +++ b/openapi/next.yaml @@ -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':