Compare commits
2 Commits
v0.108.0-b
...
6006-refac
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6d3b5c364b | ||
|
|
9f93a21bf6 |
60
.github/ISSUE_TEMPLATE/bug.yml
vendored
60
.github/ISSUE_TEMPLATE/bug.yml
vendored
@@ -32,33 +32,31 @@
|
||||
- 'attributes':
|
||||
'description': 'On which Platform does the issue occur?'
|
||||
'label': 'Platform (OS and CPU architecture)'
|
||||
# NOTE: Keep the 386 at the bottom for each OS, because a lot of people
|
||||
# Seem to confuse them with AMD64, which is what they actually need.
|
||||
'options':
|
||||
- 'Darwin (aka macOS), AMD64 (aka x86_64)'
|
||||
- 'Darwin (aka macOS), ARM64'
|
||||
- 'FreeBSD, AMD64 (aka x86_64)'
|
||||
- 'FreeBSD, ARM64'
|
||||
- 'FreeBSD, ARMv5'
|
||||
- 'FreeBSD, ARMv6'
|
||||
- 'FreeBSD, ARMv7'
|
||||
- 'FreeBSD, 32-bit Intel (aka 386)'
|
||||
- 'Linux, AMD64 (aka x86_64)'
|
||||
- 'Linux, ARM64'
|
||||
- 'Linux, ARMv5'
|
||||
- 'Linux, ARMv6'
|
||||
- 'Linux, ARMv7'
|
||||
- 'Linux, MIPS LE'
|
||||
- 'Linux, MIPS'
|
||||
- 'Linux, MIPS64 LE'
|
||||
- 'Linux, MIPS64'
|
||||
- 'Linux, PPC64 LE'
|
||||
- 'Linux, 32-bit Intel (aka 386)'
|
||||
- 'OpenBSD, AMD64 (aka x86_64)'
|
||||
- 'OpenBSD, ARM64'
|
||||
- 'Windows, AMD64 (aka x86_64)'
|
||||
- 'Windows, ARM64'
|
||||
- 'Windows, 32-bit Intel (aka 386)'
|
||||
- 'Darwin (aka macOS)/AMD64 (aka x86_64)'
|
||||
- 'Darwin (aka macOS)/ARM64'
|
||||
- 'FreeBSD/386'
|
||||
- 'FreeBSD/AMD64 (aka x86_64)'
|
||||
- 'FreeBSD/ARM64'
|
||||
- 'FreeBSD/ARMv5'
|
||||
- 'FreeBSD/ARMv6'
|
||||
- 'FreeBSD/ARMv7'
|
||||
- 'Linux/386'
|
||||
- 'Linux/AMD64 (aka x86_64)'
|
||||
- 'Linux/ARM64'
|
||||
- 'Linux/ARMv5'
|
||||
- 'Linux/ARMv6'
|
||||
- 'Linux/ARMv7'
|
||||
- 'Linux/MIPS LE'
|
||||
- 'Linux/MIPS'
|
||||
- 'Linux/MIPS64 LE'
|
||||
- 'Linux/MIPS64'
|
||||
- 'Linux/PPC64 LE'
|
||||
- 'OpenBSD/AMD64 (aka x86_64)'
|
||||
- 'OpenBSD/ARM64'
|
||||
- 'Windows/386'
|
||||
- 'Windows/AMD64 (aka x86_64)'
|
||||
- 'Windows/ARM64'
|
||||
- 'Custom (please mention in the description)'
|
||||
'id': 'os'
|
||||
'type': 'dropdown'
|
||||
@@ -144,10 +142,8 @@
|
||||
'type': 'textarea'
|
||||
'validations':
|
||||
'required': false
|
||||
# NOTE: GitHub limits the description length to 200 characters. Also, Markdown
|
||||
# doesn't work here.
|
||||
'description': |
|
||||
For help, use the Discussions section instead. Write the title in English
|
||||
to make it easier for other people to search for duplicates. (Any language
|
||||
is fine in the body.)
|
||||
'description': >
|
||||
Open a bug report. Please do not open bug reports for questions or help
|
||||
with configuring clients. If you want to ask for help, use the Discussions
|
||||
section.
|
||||
'name': 'Bug'
|
||||
|
||||
6
.github/ISSUE_TEMPLATE/feature.yml
vendored
6
.github/ISSUE_TEMPLATE/feature.yml
vendored
@@ -48,11 +48,7 @@
|
||||
'type': 'textarea'
|
||||
'validations':
|
||||
'required': false
|
||||
# NOTE: GitHub limits the description length to 200 characters. Also, Markdown
|
||||
# doesn't work here.
|
||||
'description': |
|
||||
Write the title in English to make it easier for other people to search for
|
||||
duplicates. (Any language is fine in the body.)
|
||||
'description': 'Suggest a feature or an enhancement for AdGuard Home'
|
||||
'labels':
|
||||
- 'feature request'
|
||||
'name': 'Feature request or enhancement'
|
||||
|
||||
@@ -27,9 +27,8 @@ NOTE: Add new changes BELOW THIS COMMENT.
|
||||
|
||||
- Occasional client information lookup failures that could lead to the DNS
|
||||
server getting stuck ([#6006]).
|
||||
- `bufio.Scanner: token too long` and other errors when trying to add
|
||||
filtering-rule lists with lines over 1024 bytes long or containing cosmetic
|
||||
rules ([#6003]).
|
||||
- `bufio.Scanner: token too long` errors when trying to add filtering-rule lists
|
||||
with lines over 1024 bytes long ([#6003]).
|
||||
|
||||
### Removed
|
||||
|
||||
|
||||
7
Makefile
7
Makefile
@@ -130,10 +130,3 @@ openapi-lint: ; cd ./openapi/ && $(YARN) test
|
||||
openapi-show: ; cd ./openapi/ && $(YARN) start
|
||||
|
||||
txt-lint: ; $(ENV) "$(SHELL)" ./scripts/make/txt-lint.sh
|
||||
|
||||
# TODO(a.garipov): Consider adding to scripts/ and the common project
|
||||
# structure.
|
||||
go-upd-tools:
|
||||
cd ./internal/tools/ &&\
|
||||
"$(GO.MACRO)" get -u &&\
|
||||
"$(GO.MACRO)" mod tidy
|
||||
|
||||
@@ -416,8 +416,7 @@ There are three options how you can install an unstable version:
|
||||
### <a href="#reporting-issues" id="reporting-issues" name="reporting-issues">Report issues</a>
|
||||
|
||||
If you run into any problem or have a suggestion, head to [this page][iss] and
|
||||
click on the “New issue” button. Please follow the instructions in the issue
|
||||
form carefully and don't forget to start by searching for duplicates.
|
||||
click on the “New issue” button.
|
||||
|
||||
[iss]: https://github.com/AdguardTeam/AdGuardHome/issues
|
||||
|
||||
|
||||
@@ -444,7 +444,7 @@
|
||||
"client_confirm_delete": "Opravdu chcete odstranit klienta \"{{key}}\"?",
|
||||
"list_confirm_delete": "Opravdu chcete smazat tento seznam?",
|
||||
"auto_clients_title": "Spuštění klienti",
|
||||
"auto_clients_desc": "Informace o IP adresách zařízení, která používají nebo mohou používat AdGuard Home. Tyto informace se získávají z několika zdrojů, včetně souborů hosts, reverzního DNS atd.",
|
||||
"auto_clients_desc": "Zařízení, která nejsou na seznamu stálých klientů, a mohou nadále používat AdGuard Home",
|
||||
"access_title": "Nastavení přístupu",
|
||||
"access_desc": "Zde můžete konfigurovat pravidla přístupu pro server DNS AdGuard Home",
|
||||
"access_allowed_title": "Povolení klienti",
|
||||
|
||||
@@ -444,7 +444,7 @@
|
||||
"client_confirm_delete": "Sikker på, at du vil slette klient \"{{key}}\"?",
|
||||
"list_confirm_delete": "Sikker på, at du vil slette denne liste?",
|
||||
"auto_clients_title": "Klienter (runtime)",
|
||||
"auto_clients_desc": "Oplysninger om IP-adresser på enheder, som (måske) bruger AdGuard Home. Disse oplysninger indsamles fra flere kilder, herunder hosts-filer, reverse DNS mv.",
|
||||
"auto_clients_desc": "Enheder, som ikke er på listen over Permanente klienter, kan stadig bruge AdGuard Home",
|
||||
"access_title": "Adgangsindstillinger",
|
||||
"access_desc": "Her kan adgangsregler for AdGuard Home DNS-serveren opsættes",
|
||||
"access_allowed_title": "Tilladte klienter",
|
||||
|
||||
@@ -444,7 +444,7 @@
|
||||
"client_confirm_delete": "Möchten Sie den Client „{{key}}“ wirklich löschen?",
|
||||
"list_confirm_delete": "Möchten Sie diese Liste wirklich löschen?",
|
||||
"auto_clients_title": "Laufzeit-Clients",
|
||||
"auto_clients_desc": "Informationen über IP-Adressen der Geräten, die AdGuard Home nutzen oder nutzen könnten. Diese Informationen werden aus verschiedenen Quellen gesammelt, darunter Hosts-Dateien, Reverse-DNS usw.",
|
||||
"auto_clients_desc": "Geräte, die nicht auf der Liste der persistenten Clients stehen und trotzdem AdGuard Home verwenden dürfen",
|
||||
"access_title": "Zugriffsrechte",
|
||||
"access_desc": "Hier können Sie die Zugriffsregeln für den DNS-Server von AdGuard Home konfigurieren",
|
||||
"access_allowed_title": "Zugelassene Clients",
|
||||
|
||||
@@ -444,7 +444,7 @@
|
||||
"client_confirm_delete": "¿Estás seguro de que deseas eliminar el cliente \"{{key}}\"?",
|
||||
"list_confirm_delete": "¿Estás seguro de que deseas eliminar esta lista?",
|
||||
"auto_clients_title": "Clientes activos",
|
||||
"auto_clients_desc": "Información sobre las direcciones IP de los dispositivos que usan o pueden usar AdGuard Home. Esta información se recopila de varias fuentes, incluidos ficheros de host, DNS inverso, etc.",
|
||||
"auto_clients_desc": "Dispositivos que no están en la lista de clientes persistentes que aún pueden utilizar AdGuard Home",
|
||||
"access_title": "Configuración de acceso",
|
||||
"access_desc": "Aquí puedes configurar las reglas de acceso para el servidor DNS de AdGuard Home",
|
||||
"access_allowed_title": "Clientes permitidos",
|
||||
|
||||
@@ -2,21 +2,21 @@
|
||||
"client_settings": "Päätelaiteasetukset",
|
||||
"example_upstream_reserved": "ylävirta <0>tietyille verkkotunnuksille</0>;",
|
||||
"example_upstream_comment": "kommentti.",
|
||||
"upstream_parallel": "Käytä rinnakkaisia pyyntöjä ja nopeuta selvitystä käyttämällä kaikkia ylävirtapalvelimia samanaikaisesti.",
|
||||
"upstream_parallel": "Käytä rinnakkaisia pyyntöjä ja nopeuta selvitystä käyttämällä kaikkia ylävirran palvelimia samanaikaisesti.",
|
||||
"parallel_requests": "Rinnakkaiset pyynnöt",
|
||||
"load_balancing": "Kuormantasaus",
|
||||
"load_balancing_desc": "Lähetä pyyntö yhdelle ylävirtapalvelimelle kerrallaan. AdGuard Home pyrkii valitsemaan nopeimman palvelimen painotetun satunnaisalgoritminsa avulla.",
|
||||
"load_balancing_desc": "Lähetä pyyntö yhdelle ylävirran palvelimelle kerrallaan. AdGuard Home pyrkii valitsemaan nopeimman palvelimen painotetun satunnaisalgoritminsa avulla.",
|
||||
"bootstrap_dns": "Bootstrap DNS-palvelimet",
|
||||
"bootstrap_dns_desc": "Bootstrap DNS-palvelimia käytetään ylävirroiksi määritettyjen DoH/DoT-resolvereiden IP-osoitteiden selvitykseen.",
|
||||
"local_ptr_title": "Yksityiset käänteis-DNS-palvelimet",
|
||||
"local_ptr_desc": "DNS-palvelimet, joita AdGuard Home käyttää paikallisille PTR-pyynnöille. Näitä palvelimia käytetään yksityistä IP-osoitetta käyttävien PTR-pyyntöjen osoitteiden, kuten \"192.168.12.34\", selvitykseen käänteis-DNS:n avulla. Jos ei käytössä, AdGuard Home käyttää käyttöjärjestelmän oletusarvoisia DNS-resolvereita, poislukien AdGuard Homen omat osoitteet.",
|
||||
"local_ptr_default_resolver": "Oletusarvoisesti AdGuard Home käyttää seuraavia käänteis-DNS-resolvereita: {{ip}}.",
|
||||
"local_ptr_no_default_resolver": "AdGuard Home ei voinut määrittää tälle järjestelmälle sopivaa yksityistä käänteis-DNS-resolveria.",
|
||||
"local_ptr_title": "Yksityiset käänteiset DNS-palvelimet",
|
||||
"local_ptr_desc": "DNS-palvelimet, joita AdGuard Home käyttää paikallisille PTR-pyynnöille. Näitä palvelimia käytetään yksityistä IP-osoitetta käyttävien PTR-pyyntöjen osoitteiden, kuten \"192.168.12.34\", selvitykseen käänteisen DNS:n avulla. Jos ei käytössä, AdGuard Home käyttää käyttöjärjestelmän oletusarvoisia DNS-resolvereita, poislukien AdGuard Homen omat osoitteet.",
|
||||
"local_ptr_default_resolver": "Oletusarvoisesti AdGuard Home käyttää seuraavia käänteisiä DNS-resolvereita: {{ip}}.",
|
||||
"local_ptr_no_default_resolver": "AdGuard Home ei voinut määrittää tälle järjestelmälle sopivaa yksityistä käänteistä DNS-resolveria.",
|
||||
"local_ptr_placeholder": "Syötä yksi palvelimen osoite per rivi",
|
||||
"resolve_clients_title": "Käytä päätelaitteiden IP-osoitteille käänteistä selvitystä",
|
||||
"resolve_clients_desc": "Selvitä päätelaitteiden IP-osoitteiden isäntänimet käänteisesti lähettämällä PTR-pyynnöt sopiville resolvereille (yksityiset DNS-palvelimet paikallisille päätelaitteille, ylävirtapalvelimet päätelaitteille, joilla on julkiset IP-osoitteet).",
|
||||
"use_private_ptr_resolvers_title": "Käytä yksityisiä käänteis-DNS-resolvereita",
|
||||
"use_private_ptr_resolvers_desc": "Suorita käänteis-DNS-selvitykset paikallisesti tarjotuille osoitteille käyttäen näitä ylävirtapalvelimia. Jos ei käytössä, vastaa AdGuard Home kaikkiin sen tyyppisiin PTR-pyyntöihin NXDOMAIN-arvolla, pois lukien DHCP, /etc/hosts, yms. -tiedoista tunnistettut päätelaitteet.",
|
||||
"resolve_clients_desc": "Selvitä päätelaitteiden IP-osoitteiden isäntänimet käänteisesti lähettämällä PTR-pyynnöt sopiville resolvereille (yksityiset DNS-palvelimet paikallisille päätelaitteille, lähtevät palvelimet päätelaitteille, joilla on julkiset IP-osoitteet).",
|
||||
"use_private_ptr_resolvers_title": "Käytä yksityisiä käänteisiä DNS-resolvereita",
|
||||
"use_private_ptr_resolvers_desc": "Suorita käänteiset DNS-selvitykset paikallisesti tarjotuille osoitteille käyttäen näitä ylävirran palvelimia. Jos ei käytössä, vastaa AdGuard Home kaikkiin sen tyyppisiin PTR-pyyntöihin NXDOMAIN-arvolla, pois lukien DHCP, /etc/hosts, yms. -tiedoista tunnistettut päätelaitteet.",
|
||||
"check_dhcp_servers": "Etsi DHCP-palvelimia",
|
||||
"save_config": "Tallenna asetukset",
|
||||
"enabled_dhcp": "DHCP-palvelin otettiin käyttöön",
|
||||
@@ -220,7 +220,7 @@
|
||||
"example_upstream_tcp_port": "tavallinen DNS (TCP, portti);",
|
||||
"example_upstream_tcp_hostname": "tavallinen DNS (TCP, isäntänimi);",
|
||||
"all_lists_up_to_date_toast": "Kaikki listat ovat ajan tasalla",
|
||||
"updated_upstream_dns_toast": "Ylävirtapalvelimet tallennettiin",
|
||||
"updated_upstream_dns_toast": "Ylävirtojen palvelimet tallennettiin",
|
||||
"dns_test_ok_toast": "Määritetyt DNS-palvelimet toimivat oikein",
|
||||
"dns_test_not_ok_toast": "Palvelin \"{{key}}\": Ei voitu käyttää, tarkista oikeinkirjoitus",
|
||||
"dns_test_warning_toast": "Datavuon \"{{key}}\" ei vastaa testipyyntöihin eikä välttämättä toimi kunnolla",
|
||||
@@ -444,7 +444,7 @@
|
||||
"client_confirm_delete": "Haluatko varmasti poistaa päätelaitteen \"{{key}}\"?",
|
||||
"list_confirm_delete": "Haluatko varmasti poistaa tämän listan?",
|
||||
"auto_clients_title": "Määrittämättömät päätelaitteet",
|
||||
"auto_clients_desc": "Päätelaitteet, joita ei ole määritetty pysyviksi ja jotka voivat silti käyttää AdGuard Homea. Näitä tietoja kertään useista lähteistä, mm. hosts-tiedostoista ja kääteis-DNS:llä.",
|
||||
"auto_clients_desc": "Päätelaitteet, joita ei ole määritetty pysyviksi ja jotka voivat silti käyttää AdGuard Homea.",
|
||||
"access_title": "Käytön asetukset",
|
||||
"access_desc": "Tässä voidaan määrittää AdGuard Homen DNS-palvelimen käyttöoikeussääntöjä.",
|
||||
"access_allowed_title": "Sallitut päätelaitteet",
|
||||
@@ -623,7 +623,7 @@
|
||||
"enter_cache_size": "Syötä välimuistin koko (tavuina)",
|
||||
"enter_cache_ttl_min_override": "Syötä vähimmäis-TTL (sekunteina)",
|
||||
"enter_cache_ttl_max_override": "Syötä enimmäis-TTL (sekunteina)",
|
||||
"cache_ttl_min_override_desc": "Pidennä ylävirtapalvelimelta vastaanotettuja, lyhyitä elinaika-arvoja (sekunteina) tallennettaessa DNS-vastauksia välimuistiin.",
|
||||
"cache_ttl_min_override_desc": "Pidennä ylävirran palvelimelta vastaanotettuja, lyhyitä elinaika-arvoja (sekunteina) tallennettaessa DNS-vastauksia välimuistiin.",
|
||||
"cache_ttl_max_override_desc": "Määritä DNS-välimuistin kohteiden enimmäiselinaika (sekunteina).",
|
||||
"ttl_cache_validation": "Välimuistin vähimmäiselinajan on oltava pienempi tai sama kuin enimmäiselinajan",
|
||||
"cache_optimistic": "Optimistinen välimuisti",
|
||||
|
||||
@@ -444,7 +444,7 @@
|
||||
"client_confirm_delete": "Voulez-vous vraiment supprimer le client « {{key}} » ?",
|
||||
"list_confirm_delete": "Voulez-vous vraiment supprimer cette liste ?",
|
||||
"auto_clients_title": "Clients d'exécution",
|
||||
"auto_clients_desc": "Informations sur les adresses IP des appareils qui utilisent ou pourraient utiliser AdGuard Home. Ces informations sont recueillies à partir de plusieurs sources, notamment les fichiers hosts, le DNS inverse, etc.",
|
||||
"auto_clients_desc": "Appareils ne figurant pas sur la liste des clients persistants qui peuvent encore utiliser AdGuard Home.",
|
||||
"access_title": "Paramètres d'accès",
|
||||
"access_desc": "Ici vous pouvez configurer les règles d'accès au serveur DNS AdGuard Home",
|
||||
"access_allowed_title": "Clients autorisés",
|
||||
|
||||
@@ -444,7 +444,7 @@
|
||||
"client_confirm_delete": "Sei sicuro di voler eliminare il client \"{{key}}\"?",
|
||||
"list_confirm_delete": "Sei sicuro di voler eliminare questo elenco?",
|
||||
"auto_clients_title": "Client in tempo reale",
|
||||
"auto_clients_desc": "Informazioni sugli indirizzi IP dei dispositivi che utilizzano o potrebbero utilizzare AdGuard Home. Queste informazioni vengono raccolte da diverse fonti, inclusi file host, DNS inverso, ecc.",
|
||||
"auto_clients_desc": "Dispositivi non presenti nell'elenco dei client Persistenti che possono ancora utilizzare AdGuard Home",
|
||||
"access_title": "Impostazioni di accesso",
|
||||
"access_desc": "Qui puoi configurare le regole d'accesso per il server DNS di AdGuard Home",
|
||||
"access_allowed_title": "Client permessi",
|
||||
|
||||
@@ -444,7 +444,7 @@
|
||||
"client_confirm_delete": "クライアント \"{{key}}\" を削除してもよろしいですか?",
|
||||
"list_confirm_delete": "このリストを削除してもよろしいですか?",
|
||||
"auto_clients_title": "ランタイムクライアント",
|
||||
"auto_clients_desc": "AdGuard Home を使用している、または使用する可能性のあるデバイスの IP アドレスに関する情報です。この情報は、hosts ファイル、リバース DNS など、複数の情報源から収集されます。",
|
||||
"auto_clients_desc": "永続的クライアントのリストに未登録で、AdGuard Homeを使用する場合があるデバイスのリスト。",
|
||||
"access_title": "アクセス設定",
|
||||
"access_desc": "こちらでは、AdGuard Home DNSサーバーのアクセスルールを設定できます。",
|
||||
"access_allowed_title": "許可されたクライアント",
|
||||
|
||||
@@ -444,7 +444,7 @@
|
||||
"client_confirm_delete": "Ben je zeker dat je deze gebruiker \"{{key}}\" wilt verwijderen?",
|
||||
"list_confirm_delete": "Ben je zeker om deze lijst te verwijderen?",
|
||||
"auto_clients_title": "Runtime-clients",
|
||||
"auto_clients_desc": "Informatie over IP-adressen van apparaten die AdGuard Home gebruiken of kunnen gebruiken. Deze informatie wordt verzameld uit verschillende bronnen, waaronder hosts-bestanden, reverse DNS, enz.",
|
||||
"auto_clients_desc": "Apparaten die niet op de lijst van permanente clients staan die mogelijk nog steeds AdGuard Home gebruiken",
|
||||
"access_title": "Toegangs instellingen",
|
||||
"access_desc": "Hier kan je toegangsregels voor de AdGuard Home DNS-server instellen",
|
||||
"access_allowed_title": "Toegestane gebruikers",
|
||||
|
||||
@@ -444,7 +444,7 @@
|
||||
"client_confirm_delete": "Czy na pewno chcesz usunąć klienta \"{{key}}\"?",
|
||||
"list_confirm_delete": "Czy na pewno chcesz usunąć tę listę?",
|
||||
"auto_clients_title": "Uruchomieni klienci",
|
||||
"auto_clients_desc": "Informacje o adresach IP urządzeń korzystających lub mogących korzystać z AdGuard Home. Te informacje są gromadzone z wielu źródeł takich jak pliki hosta, odwrotna translacja DNS, itp.",
|
||||
"auto_clients_desc": "Urządzenia, których nie ma na liście stałych klientów, które mogą nadal korzystać z AdGuard Home",
|
||||
"access_title": "Ustawienia dostępu",
|
||||
"access_desc": "Tutaj możesz skonfigurować reguły dostępu dla serwera DNS AdGuard Home",
|
||||
"access_allowed_title": "Dozwoleni klienci",
|
||||
@@ -470,7 +470,7 @@
|
||||
"setup_dns_privacy_ios_2": "Aplikacja <0>AdGuard dla iOS</0> obsługuje <1>DNS-over-HTTPS</1> i <1>DNS-over-TLS</1>.",
|
||||
"setup_dns_privacy_other_title": "Inne implementacje",
|
||||
"setup_dns_privacy_other_1": "Sam AdGuard Home może być bezpiecznym klientem DNS na dowolnej platformie.",
|
||||
"setup_dns_privacy_other_2": "<0>dnsproxy</0> obsługuje wszystkie znane bezpieczne protokoły DNS.",
|
||||
"setup_dns_privacy_other_2": "<0>dnsproxy</0> obsługuje wszystkie znane bezpieczne protokoły DNS.\n\n",
|
||||
"setup_dns_privacy_other_3": "<0>dnscrypt-proxy</0> obsługuje <1>DNS-over-HTTPS</1>.",
|
||||
"setup_dns_privacy_other_4": "<0>Mozilla Firefox</0> obsługuje <1>DNS-over-HTTPS</1>.",
|
||||
"setup_dns_privacy_other_5": "Znajdziesz więcej implementacji <0>tutaj</0> i <1>tutaj</1>.",
|
||||
|
||||
@@ -444,7 +444,7 @@
|
||||
"client_confirm_delete": "Você tem certeza de que deseja excluir o cliente \"{{key}}\"?",
|
||||
"list_confirm_delete": "Você tem certeza de que deseja excluir essa lista?",
|
||||
"auto_clients_title": "Clientes ativos",
|
||||
"auto_clients_desc": "Informações sobre endereços IP de dispositivos que usam ou podem usar o AdGuard Home. Essas informações são coletadas de várias fontes, incluindo arquivos de hosts, DNS reverso, etc.",
|
||||
"auto_clients_desc": "Dispositivo não está na lista de dispositivos persistentes que podem ser utilizados no AdGuard Home",
|
||||
"access_title": "Configurações de acessos",
|
||||
"access_desc": "Aqui você pode configurar as regras de acesso para o servidores de DNS do AdGuard Home",
|
||||
"access_allowed_title": "Clientes permitidos",
|
||||
|
||||
@@ -444,7 +444,7 @@
|
||||
"client_confirm_delete": "Tem a certeza de que deseja excluir o cliente \"{{key}}\"?",
|
||||
"list_confirm_delete": "Você tem certeza de que deseja excluir essa lista?",
|
||||
"auto_clients_title": "Clientes ativos",
|
||||
"auto_clients_desc": "Informações sobre endereços IP de dispositivos que estão a utilizar ou podem utilizar o AdGuard Home. Estas informações são recolhidas a partir de várias fontes, incluindo ficheiros hosts, DNS reverso etc.",
|
||||
"auto_clients_desc": "Dispositivo não está na lista de dispositivos persistentes que podem ser utilizados no AdGuard Home",
|
||||
"access_title": "Definições de acesso",
|
||||
"access_desc": "Aqui pode configurar as regras de acesso para o servidores de DNS do AdGuard Home",
|
||||
"access_allowed_title": "Clientes permitidos",
|
||||
|
||||
@@ -135,7 +135,7 @@
|
||||
"number_of_dns_query_to_safe_search": "Количество запросов DNS для поисковых систем, для которых был применён Безопасный поиск",
|
||||
"average_processing_time": "Среднее время обработки запроса",
|
||||
"average_processing_time_hint": "Среднее время для обработки запроса DNS в миллисекундах",
|
||||
"block_domain_use_filters_and_hosts": "Блокировать домены с использованием фильтров и файлов hosts",
|
||||
"block_domain_use_filters_and_hosts": "Блокировать домены с использованием фильтров и файлов хостов",
|
||||
"filters_block_toggle_hint": "Вы можете настроить правила блокировки в <a>«Фильтрах»</a>.",
|
||||
"use_adguard_browsing_sec": "Включить Безопасную навигацию AdGuard",
|
||||
"use_adguard_browsing_sec_hint": "AdGuard Home проверит, включён ли домен в веб-службу безопасности браузера. Он будет использовать API, чтобы выполнить проверку: на сервер отправляется только короткий префикс имени домена SHA256.",
|
||||
@@ -296,7 +296,7 @@
|
||||
"rate_limit_desc": "Ограничение на количество запросов в секунду для каждого клиента (0 — неограниченно).",
|
||||
"blocking_ipv4_desc": "IP-адрес, возвращаемый при блокировке A-запроса",
|
||||
"blocking_ipv6_desc": "IP-адрес, возвращаемый при блокировке AAAA-запроса",
|
||||
"blocking_mode_default": "Стандартный: Отвечает с нулевым IP-адресом, (0.0.0.0 для A; :: для AAAA) когда заблокировано правилом в стиле Adblock; отвечает с IP-адресом, указанным в правиле, когда заблокировано правилом в стиле файлов hosts",
|
||||
"blocking_mode_default": "Стандартный: Отвечает с нулевым IP-адресом, (0.0.0.0 для A; :: для AAAA) когда заблокировано правилом в стиле Adblock; отвечает с IP-адресом, указанным в правиле, когда заблокировано правилом в стиле /etc/hosts-style",
|
||||
"blocking_mode_refused": "REFUSED: Отвечает с кодом REFUSED",
|
||||
"blocking_mode_nxdomain": "NXDOMAIN: Отвечает с кодом NXDOMAIN\n",
|
||||
"blocking_mode_null_ip": "Нулевой IP: Отвечает с нулевым IP-адресом (0.0.0.0 для A; :: для AAAA)",
|
||||
@@ -444,7 +444,7 @@
|
||||
"client_confirm_delete": "Вы уверены, что хотите удалить клиента «{{key}}»?",
|
||||
"list_confirm_delete": "Вы уверены, что хотите удалить этот список?",
|
||||
"auto_clients_title": "Клиенты (runtime)",
|
||||
"auto_clients_desc": "Информация об IP-адресах устройств, которые используют или могут использовать AdGuard Home. Эта информация собирается из нескольких источников, включая файлы hosts, обратный DNS и так далее.",
|
||||
"auto_clients_desc": "Несохранённые клиенты, которые могут пользоваться AdGuard Home",
|
||||
"access_title": "Настройки доступа",
|
||||
"access_desc": "Здесь вы можете настроить правила доступа к DNS-серверу AdGuard Home",
|
||||
"access_allowed_title": "Разрешённые клиенты",
|
||||
|
||||
@@ -387,7 +387,7 @@
|
||||
"encryption_key": "Súkromný kľúč",
|
||||
"encryption_key_input": "Skopírujte a prilepte sem svoj súkromný kľúč vo formáte PEM pre Váš certifikát.",
|
||||
"encryption_enable": "Zapnite šifrovanie (HTTPS, DNS-cez-HTTPS a DNS-cez-TLS)",
|
||||
"encryption_enable_desc": "Ak je šifrovanie zapnuté, AdGuard Home administrátorské rozhranie bude pracovať cez HTTPS a DNS server bude počúvať dopyty cez DNS-cez-HTTPS a DNS-cez-TLS.",
|
||||
"encryption_enable_desc": "Ak je šifrovanie zapnuté, AdGuard Home administrátorské rozhranie bude pracovať cez HTTPS a DNS server bude počúvať požiadavky cez DNS-cez-HTTPS a DNS-cez-TLS.",
|
||||
"encryption_chain_valid": "Certifikačný reťazec je platný",
|
||||
"encryption_chain_invalid": "Certifikačný reťazec je neplatný",
|
||||
"encryption_key_valid": "Toto je platný {{type}} súkromný kľúč",
|
||||
@@ -497,7 +497,7 @@
|
||||
"blocked_services": "Blokované služby",
|
||||
"blocked_services_desc": "Umožňuje rýchlo blokovať populárne stránky a služby.",
|
||||
"blocked_services_saved": "Blokované služby boli úspešne uložené",
|
||||
"blocked_services_global": "Použiť globálne blokované služby",
|
||||
"blocked_services_global": "Použite globálne blokované služby",
|
||||
"blocked_service": "Blokované služby",
|
||||
"block_all": "Blokovať všetko",
|
||||
"unblock_all": "Odblokovať všetko",
|
||||
@@ -554,7 +554,7 @@
|
||||
"whois": "WHOIS",
|
||||
"filtering_rules_learn_more": "<0>Dozvedieť sa viac</0> o tvorbe vlastných zoznamov hostiteľov.",
|
||||
"blocked_by_response": "Blokované pomocou CNAME alebo IP v odpovedi",
|
||||
"blocked_by_cname_or_ip": "Blokované pomocou CNAME alebo IP",
|
||||
"blocked_by_cname_or_ip": "Zablokované na základe CNAME alebo IP",
|
||||
"try_again": "Skúste znova",
|
||||
"domain_desc": "Zadajte meno domény alebo zástupný znak, ktorý chcete prepísať.",
|
||||
"example_rewrite_domain": "prepísať odpovede iba pre toto meno domény.",
|
||||
@@ -571,7 +571,7 @@
|
||||
"autofix_warning_list": "Bude vykonávať tieto úlohy: <0>Deaktivovať systém DNSStubListener</0> <0>Nastaviť adresu servera DNS na 127.0.0.1</0> <0>Nahradiť cieľový symbolický odkaz /etc/resolv.conf na /run/systemd/resolve/resolv.conf</0> <0>Zastaviť službu DNSStubListener (znova načítať službu systemd-resolved)</0>",
|
||||
"autofix_warning_result": "Výsledkom bude, že všetky DNS dopyty z Vášho systému budú štandardne spracované službou AdGuard Home.",
|
||||
"tags_title": "Tagy",
|
||||
"tags_desc": "Môžete vybrať značky, ktoré zodpovedajú klientovi. Zahrňte značky do pravidiel filtrácie, aby ste ich použili presnejšie. <0>Viac informácií</0>.",
|
||||
"tags_desc": "Môžete vybrať značky, ktoré zodpovedajú klientovi. Zahrňte značky do pravidiel filtrovania, aby ste ich použili presnejšie. <0>Viac informácií</0>.",
|
||||
"form_select_tags": "Zvoľte tagy klienta",
|
||||
"check_title": "Skontrolujte filtráciu",
|
||||
"check_desc": "Skontrolujte, či je názov hostiteľa filtrovaný.",
|
||||
@@ -608,7 +608,7 @@
|
||||
"show_whitelisted_responses": "Obsiahnuté v bielej listine",
|
||||
"show_processed_responses": "Spracované",
|
||||
"blocked_safebrowsing": "Zablokované modulom Bezpečné prehliadanie",
|
||||
"blocked_adult_websites": "Zablokované Rodičovskou kontrolou",
|
||||
"blocked_adult_websites": "Zablokovaná stránka pre dospelých",
|
||||
"blocked_threats": "Zablokované hrozby",
|
||||
"allowed": "Povolené",
|
||||
"filtered": "Filtrované",
|
||||
|
||||
@@ -444,7 +444,7 @@
|
||||
"client_confirm_delete": "Ali ste prepričani, da želite izbrisati odjemalca \"{{key}}\"?",
|
||||
"list_confirm_delete": "Ali ste prepričani, da želite izbrisati ta seznam?",
|
||||
"auto_clients_title": "Odjemalci izvajanja",
|
||||
"auto_clients_desc": "Informacije o naslovih IP naprav, ki uporabljajo ali bi lahko uporabljale AdGuard Home. Te informacije so zbrane iz več virov, vključno z datotekami gostiteljev, povratnim DNS-jem itd.",
|
||||
"auto_clients_desc": "Naprave, ki niso na seznamu trajnih odjemalcev, ki morda še vedno uporabljajo AdGuard Home",
|
||||
"access_title": "Nastavitve dostopa",
|
||||
"access_desc": "Tukaj lahko nastavite pravila dostopa strežnika DNS AdGuard Home",
|
||||
"access_allowed_title": "Dovoljeni odjemalci",
|
||||
|
||||
@@ -444,7 +444,7 @@
|
||||
"client_confirm_delete": "\"{{key}}\" istemcisini silmek istediğinizden emin misiniz?",
|
||||
"list_confirm_delete": "Bu listeyi silmek istediğinizden emin misiniz?",
|
||||
"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, hosts dosyaları, ters DNS, vb. dahil olmak üzere çeşitli kaynaklardan toplanır.",
|
||||
"auto_clients_desc": "Henüz AdGuard Home'u kullanabilecek Kalıcı istemciler listesinde olmayan cihazlar",
|
||||
"access_title": "Erişim ayarları",
|
||||
"access_desc": "AdGuard Home DNS sunucusu için erişim kurallarını buradan yapılandırabilirsiniz",
|
||||
"access_allowed_title": "İzin verilen istemciler",
|
||||
|
||||
@@ -444,7 +444,7 @@
|
||||
"client_confirm_delete": "您确定要删除客户端 \"{{key}}\"?",
|
||||
"list_confirm_delete": "您确定要删除此列表吗?",
|
||||
"auto_clients_title": "客户端(运行时间)",
|
||||
"auto_clients_desc": "有关正在使用或可能使用 AdGuard Home 的设备的 IP 地址的信息。此信息是从多个来源收集的,包括 hosts 文件、反向 DNS 等。",
|
||||
"auto_clients_desc": "不在可继续使用 AdGuard Home 的持久客户端列表中的设备。",
|
||||
"access_title": "访问设置",
|
||||
"access_desc": "您可以在此处配置 AdGuard Home 的 DNS 服务器的访问规则",
|
||||
"access_allowed_title": "允许的客户端",
|
||||
|
||||
@@ -164,7 +164,7 @@
|
||||
"disabled_parental_toast": "已停用家長監護",
|
||||
"enabled_parental_toast": "已啟用家長監護",
|
||||
"disabled_safe_search_toast": "已停用安全搜尋",
|
||||
"updated_save_search_toast": "已更新安全搜尋設定",
|
||||
"enabled_save_search_toast": "已啟用安全搜尋",
|
||||
"enabled_table_header": "啟用",
|
||||
"name_table_header": "名稱",
|
||||
"list_url_table_header": "清單 URL 網址",
|
||||
@@ -468,7 +468,6 @@
|
||||
"rewrite_added": "「{{key}}」的 DNS 覆寫新增成功",
|
||||
"rewrite_deleted": "「{{key}}」的 DNS 覆寫刪除成功",
|
||||
"rewrite_add": "新增 DNS 覆寫",
|
||||
"rewrite_edit": "編輯 DNS 覆寫",
|
||||
"rewrite_not_found": "找不到 DNS 覆寫",
|
||||
"rewrite_confirm_delete": "您確定要刪除 \"{{key}}\" 的 DNS 覆寫?",
|
||||
"rewrite_desc": "提供簡單的方式對特定網域自訂 DNS 回應。",
|
||||
@@ -502,7 +501,6 @@
|
||||
"interval_days": "{{count}} 天",
|
||||
"interval_days_plural": "{{count}} 天",
|
||||
"domain": "網域",
|
||||
"ecs": "EDNS 子網",
|
||||
"punycode": "Punycode",
|
||||
"answer": "回應",
|
||||
"filter_added_successfully": "已成功新增清單",
|
||||
@@ -516,8 +514,6 @@
|
||||
"statistics_retention_confirm": "您確定要更改統計資料保存時間嗎?如果您縮短期限部分資料可能將會遺失",
|
||||
"statistics_cleared": "已清除統計資料",
|
||||
"statistics_enable": "啟用統計數據",
|
||||
"ignore_domains": "已忽略網域(每行一個)",
|
||||
"ignore_domains_title": "已忽略網域",
|
||||
"interval_hours": "{{count}} 小時",
|
||||
"interval_hours_plural": "{{count}} 小時",
|
||||
"filters_configuration": "過濾器設定",
|
||||
@@ -630,7 +626,6 @@
|
||||
"safe_browsing": "安全瀏覽",
|
||||
"served_from_cache": "{{value}} <i>(由快取回應)</i>",
|
||||
"form_error_password_length": "密碼必須至少 {{value}} 個字元長度",
|
||||
"make_static": "新增為靜態",
|
||||
"theme_dark_desc": "深色主題",
|
||||
"theme_light_desc": "淺色主題",
|
||||
"disable_for_seconds": "{{count}} 秒",
|
||||
|
||||
@@ -444,7 +444,7 @@
|
||||
"client_confirm_delete": "您確定您想要刪除用戶端 \"{{key}}\" 嗎?",
|
||||
"list_confirm_delete": "您確定您想要刪除該清單嗎?",
|
||||
"auto_clients_title": "執行時期用戶端",
|
||||
"auto_clients_desc": "AdGuard Home 使用或可能使用的裝置的 IP 地址資訊。這些資訊來自多個來源,包括主機檔案、反向 DNS 等。",
|
||||
"auto_clients_desc": "未於可能仍然使用 AdGuard Home 的持續性用戶端之清單上的裝置",
|
||||
"access_title": "存取設定",
|
||||
"access_desc": "於此您可配置用於 AdGuard Home DNS 伺服器之存取規則",
|
||||
"access_allowed_title": "已允許的用戶端",
|
||||
|
||||
@@ -64,6 +64,12 @@ export default {
|
||||
"homepage": "https://github.com/MasterKia/PersianBlocker",
|
||||
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_19.txt"
|
||||
},
|
||||
"ITA_filtri_dns": {
|
||||
"name": "ITA: Filtri-DNS",
|
||||
"categoryId": "regional",
|
||||
"homepage": "https://filtri-dns.ga/",
|
||||
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_18.txt"
|
||||
},
|
||||
"KOR_list_kr": {
|
||||
"name": "KOR: List-KR DNS",
|
||||
"categoryId": "regional",
|
||||
@@ -160,20 +166,14 @@ export default {
|
||||
"homepage": "https://github.com/DandelionSprout/adfilt",
|
||||
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_12.txt"
|
||||
},
|
||||
"dandelion_sprouts_anti_push_notifications": {
|
||||
"name": "Dandelion Sprout's Anti Push Notifications",
|
||||
"categoryId": "other",
|
||||
"homepage": "https://github.com/DandelionSprout/adfilt",
|
||||
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_39.txt"
|
||||
},
|
||||
"dandelion_sprouts_game_console_adblock_list": {
|
||||
"name": "Dandelion Sprout's Game Console Adblock List",
|
||||
"categoryId": "other",
|
||||
"homepage": "https://github.com/DandelionSprout/adfilt",
|
||||
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_6.txt"
|
||||
},
|
||||
"hagezi_multinormal": {
|
||||
"name": "HaGeZi Multi NORMAL",
|
||||
"hagezi_personal": {
|
||||
"name": "HaGeZi Personal Black \u0026 White",
|
||||
"categoryId": "general",
|
||||
"homepage": "https://github.com/hagezi/dns-blocklists",
|
||||
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_34.txt"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"timeUpdated": "2023-07-15T00:10:47.501Z",
|
||||
"timeUpdated": "2023-07-01T00:11:37.465Z",
|
||||
"categories": {
|
||||
"0": "audio_video_player",
|
||||
"1": "comments",
|
||||
@@ -14088,11 +14088,10 @@
|
||||
"companyId": "qihoo_360_technology"
|
||||
},
|
||||
"qq.com": {
|
||||
"name": "QQ International",
|
||||
"categoryId": 2,
|
||||
"url": "https://www.qq.com/",
|
||||
"companyId": "tencent",
|
||||
"source": "AdGuard"
|
||||
"name": "qq.com",
|
||||
"categoryId": 8,
|
||||
"url": "http://www.qq.com/",
|
||||
"companyId": "qq.com"
|
||||
},
|
||||
"qrius": {
|
||||
"name": "Qrius",
|
||||
@@ -20509,7 +20508,6 @@
|
||||
"amazon.com.au": "amazon",
|
||||
"amazon-corp.com": "amazon",
|
||||
"a2z.com": "amazon",
|
||||
"firetvcaptiveportal.com": "amazon",
|
||||
"amazon-adsystem.com": "amazon_adsystem",
|
||||
"serving-sys.com": "amazon_adsystem",
|
||||
"sizmek.com": "amazon_adsystem",
|
||||
@@ -22999,7 +22997,6 @@
|
||||
"mrskincash.com": "mrskincash",
|
||||
"e-msedge.net": "msedge",
|
||||
"l-msedge.net": "msedge",
|
||||
"s-msedge.net": "msedge",
|
||||
"msn.com": "msn",
|
||||
"s-msn.com": "msn",
|
||||
"musculahq.appspot.com": "muscula",
|
||||
|
||||
5
go.mod
5
go.mod
@@ -3,7 +3,8 @@ module github.com/AdguardTeam/AdGuardHome
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
github.com/AdguardTeam/dnsproxy v0.52.0
|
||||
// TODO(a.garipov): Update to a tagged version when it's released.
|
||||
github.com/AdguardTeam/dnsproxy v0.50.3-0.20230628054307-31e374065768
|
||||
github.com/AdguardTeam/golibs v0.13.4
|
||||
github.com/AdguardTeam/urlfilter v0.16.1
|
||||
github.com/NYTimes/gziphandler v1.1.1
|
||||
@@ -27,7 +28,7 @@ require (
|
||||
// own code for that. Perhaps, use gopacket.
|
||||
github.com/mdlayher/raw v0.1.0
|
||||
github.com/miekg/dns v1.1.55
|
||||
github.com/quic-go/quic-go v0.36.1
|
||||
github.com/quic-go/quic-go v0.35.1
|
||||
github.com/stretchr/testify v1.8.4
|
||||
github.com/ti-mo/netfilter v0.5.0
|
||||
go.etcd.io/bbolt v1.3.7
|
||||
|
||||
8
go.sum
8
go.sum
@@ -1,5 +1,5 @@
|
||||
github.com/AdguardTeam/dnsproxy v0.52.0 h1:uZxCXflHSAwtJ7uTYXP6qgWcxaBsH0pJvldpwTqIDJk=
|
||||
github.com/AdguardTeam/dnsproxy v0.52.0/go.mod h1:Jo2zeRe97Rxt3yikXc+fn0LdLtqCj0Xlyh1PNBj6bpM=
|
||||
github.com/AdguardTeam/dnsproxy v0.50.3-0.20230628054307-31e374065768 h1:5Ia6wA+tqAlTyzuaOVGSlHmb0osLWXeJUs3NxCuC4gA=
|
||||
github.com/AdguardTeam/dnsproxy v0.50.3-0.20230628054307-31e374065768/go.mod h1:CQhZTkqC8X0ID6glrtyaxgqRRdiYfn1gJulC1cZ5Dn8=
|
||||
github.com/AdguardTeam/golibs v0.4.0/go.mod h1:skKsDKIBB7kkFflLJBpfGX+G8QFTx0WKUzB6TIgtUj4=
|
||||
github.com/AdguardTeam/golibs v0.10.4/go.mod h1:rSfQRGHIdgfxriDDNgNJ7HmE5zRoURq8R+VdR81Zuzw=
|
||||
github.com/AdguardTeam/golibs v0.13.4 h1:ACTwIR1pEENBijHcEWtiMbSh4wWQOlIHRxmUB8oBHf8=
|
||||
@@ -108,8 +108,8 @@ github.com/quic-go/qtls-go1-19 v0.3.2 h1:tFxjCFcTQzK+oMxG6Zcvp4Dq8dx4yD3dDiIiyc8
|
||||
github.com/quic-go/qtls-go1-19 v0.3.2/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI=
|
||||
github.com/quic-go/qtls-go1-20 v0.2.2 h1:WLOPx6OY/hxtTxKV1Zrq20FtXtDEkeY00CGQm8GEa3E=
|
||||
github.com/quic-go/qtls-go1-20 v0.2.2/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM=
|
||||
github.com/quic-go/quic-go v0.36.1 h1:WsG73nVtnDy1TiACxFxhQ3TqaW+DipmqzLEtNlAwZyY=
|
||||
github.com/quic-go/quic-go v0.36.1/go.mod h1:zPetvwDlILVxt15n3hr3Gf/I3mDf7LpLKPhR4Ez0AZQ=
|
||||
github.com/quic-go/quic-go v0.35.1 h1:b0kzj6b/cQAf05cT0CkQubHM31wiA+xH3IBkxP62poo=
|
||||
github.com/quic-go/quic-go v0.35.1/go.mod h1:+4CVgVppm0FNjpG3UcX8Joi/frKOH7/ciD5yGcwOO1g=
|
||||
github.com/shirou/gopsutil/v3 v3.21.8 h1:nKct+uP0TV8DjjNiHanKf8SAuub+GNsbrOtM9Nl9biA=
|
||||
github.com/shirou/gopsutil/v3 v3.21.8/go.mod h1:YWp/H8Qs5fVmf17v7JNZzA0mPJ+mS2e9JdiUF9LlKzQ=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
|
||||
@@ -4,13 +4,9 @@ import (
|
||||
"context"
|
||||
"io"
|
||||
"io/fs"
|
||||
"net/netip"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghos"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/client"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/next/agh"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/rdns"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/whois"
|
||||
"github.com/AdguardTeam/dnsproxy/upstream"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
@@ -139,59 +135,6 @@ func (s *ServiceWithConfig[ConfigType]) Config() (c ConfigType) {
|
||||
return s.OnConfig()
|
||||
}
|
||||
|
||||
// Package client
|
||||
|
||||
// AddressProcessor is a fake [client.AddressProcessor] implementation for
|
||||
// tests.
|
||||
type AddressProcessor struct {
|
||||
OnProcess func(ip netip.Addr)
|
||||
OnClose func() (err error)
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ client.AddressProcessor = (*AddressProcessor)(nil)
|
||||
|
||||
// Process implements the [client.AddressProcessor] interface for
|
||||
// *AddressProcessor.
|
||||
func (p *AddressProcessor) Process(ip netip.Addr) {
|
||||
p.OnProcess(ip)
|
||||
}
|
||||
|
||||
// Close implements the [client.AddressProcessor] interface for
|
||||
// *AddressProcessor.
|
||||
func (p *AddressProcessor) Close() (err error) {
|
||||
return p.OnClose()
|
||||
}
|
||||
|
||||
// AddressUpdater is a fake [client.AddressUpdater] implementation for tests.
|
||||
type AddressUpdater struct {
|
||||
OnUpdateAddress func(ip netip.Addr, host string, info *whois.Info)
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ client.AddressUpdater = (*AddressUpdater)(nil)
|
||||
|
||||
// UpdateAddress implements the [client.AddressUpdater] interface for
|
||||
// *AddressUpdater.
|
||||
func (p *AddressUpdater) UpdateAddress(ip netip.Addr, host string, info *whois.Info) {
|
||||
p.OnUpdateAddress(ip, host, info)
|
||||
}
|
||||
|
||||
// Package rdns
|
||||
|
||||
// Exchanger is a fake [rdns.Exchanger] implementation for tests.
|
||||
type Exchanger struct {
|
||||
OnExchange func(ip netip.Addr) (host string, err error)
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ rdns.Exchanger = (*Exchanger)(nil)
|
||||
|
||||
// Exchange implements [rdns.Exchanger] interface for *Exchanger.
|
||||
func (e *Exchanger) Exchange(ip netip.Addr) (host string, err error) {
|
||||
return e.OnExchange(ip)
|
||||
}
|
||||
|
||||
// Module dnsproxy
|
||||
|
||||
// Package upstream
|
||||
|
||||
@@ -1,293 +0,0 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/netip"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/rdns"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/whois"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/AdguardTeam/golibs/netutil"
|
||||
)
|
||||
|
||||
// ErrClosed is returned from [AddressProcessor.Close] if it's closed more than
|
||||
// once.
|
||||
const ErrClosed errors.Error = "use of closed address processor"
|
||||
|
||||
// AddressProcessor is the interface for types that can process clients.
|
||||
type AddressProcessor interface {
|
||||
Process(ip netip.Addr)
|
||||
Close() (err error)
|
||||
}
|
||||
|
||||
// EmptyAddrProc is an [AddressProcessor] that does nothing.
|
||||
type EmptyAddrProc struct{}
|
||||
|
||||
// type check
|
||||
var _ AddressProcessor = EmptyAddrProc{}
|
||||
|
||||
// Process implements the [AddressProcessor] interface for EmptyAddrProc.
|
||||
func (EmptyAddrProc) Process(_ netip.Addr) {}
|
||||
|
||||
// Close implements the [AddressProcessor] interface for EmptyAddrProc.
|
||||
func (EmptyAddrProc) Close() (_ error) { return nil }
|
||||
|
||||
// DefaultAddrProcConfig is the configuration structure for address processors.
|
||||
type DefaultAddrProcConfig struct {
|
||||
// DialContext is used to create TCP connections to WHOIS servers.
|
||||
// DialContext must not be nil if [DefaultAddrProcConfig.UseWHOIS] is true.
|
||||
DialContext whois.DialContextFunc
|
||||
|
||||
// Exchanger is used to perform rDNS queries. Exchanger must not be nil if
|
||||
// [DefaultAddrProcConfig.UseRDNS] is true.
|
||||
Exchanger rdns.Exchanger
|
||||
|
||||
// PrivateSubnets are used to determine if an incoming IP address is
|
||||
// private. It must not be nil.
|
||||
PrivateSubnets netutil.SubnetSet
|
||||
|
||||
// AddressUpdater is used to update the information about a client's IP
|
||||
// address. It must not be nil.
|
||||
AddressUpdater AddressUpdater
|
||||
|
||||
// InitialAddresses are the addresses that are queued for processing
|
||||
// immediately by [NewDefaultAddrProc].
|
||||
InitialAddresses []netip.Addr
|
||||
|
||||
// UseRDNS, if true, enables resolving of client IP addresses using reverse
|
||||
// DNS.
|
||||
UseRDNS bool
|
||||
|
||||
// UsePrivateRDNS, if true, enables resolving of private client IP addresses
|
||||
// using reverse DNS. See [DefaultAddrProcConfig.PrivateSubnets].
|
||||
UsePrivateRDNS bool
|
||||
|
||||
// UseWHOIS, if true, enables resolving of client IP addresses using WHOIS.
|
||||
UseWHOIS bool
|
||||
}
|
||||
|
||||
// AddressUpdater is the interface for storages of DNS clients that can update
|
||||
// information about them.
|
||||
//
|
||||
// TODO(a.garipov): Consider using the actual client storage once it is moved
|
||||
// into this package.
|
||||
type AddressUpdater interface {
|
||||
// UpdateAddress updates information about an IP address, setting host (if
|
||||
// not empty) and WHOIS information (if not nil).
|
||||
UpdateAddress(ip netip.Addr, host string, info *whois.Info)
|
||||
}
|
||||
|
||||
// DefaultAddrProc processes incoming client addresses with rDNS and WHOIS, if
|
||||
// configured, and updates that information in a client storage.
|
||||
type DefaultAddrProc struct {
|
||||
// clientIPsMu serializes closure of clientIPs and access to isClosed.
|
||||
clientIPsMu *sync.Mutex
|
||||
|
||||
// clientIPs is the channel queueing client processing tasks.
|
||||
clientIPs chan netip.Addr
|
||||
|
||||
// rdns is used to perform rDNS lookups of clients' IP addresses.
|
||||
rdns rdns.Interface
|
||||
|
||||
// whois is used to perform WHOIS lookups of clients' IP addresses.
|
||||
whois whois.Interface
|
||||
|
||||
// addrUpdater is used to update the information about a client's IP
|
||||
// address.
|
||||
addrUpdater AddressUpdater
|
||||
|
||||
// privateSubnets are used to determine if an incoming IP address is
|
||||
// private.
|
||||
privateSubnets netutil.SubnetSet
|
||||
|
||||
// isClosed is set to true once the address processor is closed.
|
||||
isClosed bool
|
||||
|
||||
// usePrivateRDNS, if true, enables resolving of private client IP addresses
|
||||
// using reverse DNS.
|
||||
usePrivateRDNS bool
|
||||
}
|
||||
|
||||
const (
|
||||
// defaultQueueSize is the size of queue of IPs for rDNS and WHOIS
|
||||
// processing.
|
||||
defaultQueueSize = 255
|
||||
|
||||
// defaultCacheSize is the maximum size of the cache for rDNS and WHOIS
|
||||
// processing. It must be greater than zero.
|
||||
defaultCacheSize = 10_000
|
||||
|
||||
// defaultIPTTL is the Time to Live duration for IP addresses cached by
|
||||
// rDNS and WHOIS.
|
||||
defaultIPTTL = 1 * time.Hour
|
||||
)
|
||||
|
||||
// NewDefaultAddrProc returns a new running client address processor. c must
|
||||
// not be nil.
|
||||
func NewDefaultAddrProc(c *DefaultAddrProcConfig) (p *DefaultAddrProc) {
|
||||
p = &DefaultAddrProc{
|
||||
clientIPsMu: &sync.Mutex{},
|
||||
clientIPs: make(chan netip.Addr, defaultQueueSize),
|
||||
rdns: &rdns.Empty{},
|
||||
addrUpdater: c.AddressUpdater,
|
||||
whois: &whois.Empty{},
|
||||
privateSubnets: c.PrivateSubnets,
|
||||
usePrivateRDNS: c.UsePrivateRDNS,
|
||||
}
|
||||
|
||||
if c.UseRDNS {
|
||||
p.rdns = rdns.New(&rdns.Config{
|
||||
Exchanger: c.Exchanger,
|
||||
CacheSize: defaultCacheSize,
|
||||
CacheTTL: defaultIPTTL,
|
||||
})
|
||||
}
|
||||
|
||||
if c.UseWHOIS {
|
||||
p.whois = newWHOIS(c.DialContext)
|
||||
}
|
||||
|
||||
go p.process()
|
||||
|
||||
for _, ip := range c.InitialAddresses {
|
||||
p.Process(ip)
|
||||
}
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
// newWHOIS returns a whois.Interface instance using the given function for
|
||||
// dialing.
|
||||
func newWHOIS(dialFunc whois.DialContextFunc) (w whois.Interface) {
|
||||
// TODO(s.chzhen): Consider making configurable.
|
||||
const (
|
||||
// defaultTimeout is the timeout for WHOIS requests.
|
||||
defaultTimeout = 5 * time.Second
|
||||
|
||||
// defaultMaxConnReadSize is an upper limit in bytes for reading from a
|
||||
// net.Conn.
|
||||
defaultMaxConnReadSize = 64 * 1024
|
||||
|
||||
// defaultMaxRedirects is the maximum redirects count.
|
||||
defaultMaxRedirects = 5
|
||||
|
||||
// defaultMaxInfoLen is the maximum length of whois.Info fields.
|
||||
defaultMaxInfoLen = 250
|
||||
)
|
||||
|
||||
return whois.New(&whois.Config{
|
||||
DialContext: dialFunc,
|
||||
ServerAddr: whois.DefaultServer,
|
||||
Port: whois.DefaultPort,
|
||||
Timeout: defaultTimeout,
|
||||
CacheSize: defaultCacheSize,
|
||||
MaxConnReadSize: defaultMaxConnReadSize,
|
||||
MaxRedirects: defaultMaxRedirects,
|
||||
MaxInfoLen: defaultMaxInfoLen,
|
||||
CacheTTL: defaultIPTTL,
|
||||
})
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ AddressProcessor = (*DefaultAddrProc)(nil)
|
||||
|
||||
// Process implements the [AddressProcessor] interface for *DefaultAddrProc.
|
||||
func (p *DefaultAddrProc) Process(ip netip.Addr) {
|
||||
p.clientIPsMu.Lock()
|
||||
defer p.clientIPsMu.Unlock()
|
||||
|
||||
if p.isClosed {
|
||||
return
|
||||
}
|
||||
|
||||
select {
|
||||
case p.clientIPs <- ip:
|
||||
// Go on.
|
||||
default:
|
||||
log.Debug("clients: ip channel is full; len: %d", len(p.clientIPs))
|
||||
}
|
||||
}
|
||||
|
||||
// process processes the incoming client IP-address information. It is intended
|
||||
// to be used as a goroutine. Once clientIPs is closed, process exits.
|
||||
func (p *DefaultAddrProc) process() {
|
||||
defer log.OnPanic("addrProcessor.process")
|
||||
|
||||
log.Info("clients: processing addresses")
|
||||
|
||||
for ip := range p.clientIPs {
|
||||
host := p.processRDNS(ip)
|
||||
info := p.processWHOIS(ip)
|
||||
|
||||
p.addrUpdater.UpdateAddress(ip, host, info)
|
||||
}
|
||||
|
||||
log.Info("clients: finished processing addresses")
|
||||
}
|
||||
|
||||
// processRDNS resolves the clients' IP addresses using reverse DNS. host is
|
||||
// empty if there were errors or if the information hasn't changed.
|
||||
func (p *DefaultAddrProc) processRDNS(ip netip.Addr) (host string) {
|
||||
start := time.Now()
|
||||
log.Debug("clients: processing %s with rdns", ip)
|
||||
defer func() {
|
||||
log.Debug("clients: finished processing %s with rdns in %s", ip, time.Since(start))
|
||||
}()
|
||||
|
||||
ok := p.shouldResolve(ip)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
host, changed := p.rdns.Process(ip)
|
||||
if !changed {
|
||||
host = ""
|
||||
}
|
||||
|
||||
return host
|
||||
}
|
||||
|
||||
// shouldResolve returns false if ip is a loopback address, or ip is private and
|
||||
// resolving of private addresses is disabled.
|
||||
func (p *DefaultAddrProc) shouldResolve(ip netip.Addr) (ok bool) {
|
||||
return !ip.IsLoopback() &&
|
||||
(p.usePrivateRDNS || !p.privateSubnets.Contains(ip.AsSlice()))
|
||||
}
|
||||
|
||||
// processWHOIS looks up the information about clients' IP addresses in the
|
||||
// WHOIS databases. info is nil if there were errors or if the information
|
||||
// hasn't changed.
|
||||
func (p *DefaultAddrProc) processWHOIS(ip netip.Addr) (info *whois.Info) {
|
||||
start := time.Now()
|
||||
log.Debug("clients: processing %s with whois", ip)
|
||||
defer func() {
|
||||
log.Debug("clients: finished processing %s with whois in %s", ip, time.Since(start))
|
||||
}()
|
||||
|
||||
// TODO(s.chzhen): Move the timeout logic from WHOIS configuration to the
|
||||
// context.
|
||||
info, changed := p.whois.Process(context.Background(), ip)
|
||||
if !changed {
|
||||
info = nil
|
||||
}
|
||||
|
||||
return info
|
||||
}
|
||||
|
||||
// Close implements the [AddressProcessor] interface for *DefaultAddrProc.
|
||||
func (p *DefaultAddrProc) Close() (err error) {
|
||||
p.clientIPsMu.Lock()
|
||||
defer p.clientIPsMu.Unlock()
|
||||
|
||||
if p.isClosed {
|
||||
return ErrClosed
|
||||
}
|
||||
|
||||
close(p.clientIPs)
|
||||
p.isClosed = true
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,259 +0,0 @@
|
||||
package client_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net"
|
||||
"net/netip"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghtest"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/client"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/whois"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/netutil"
|
||||
"github.com/AdguardTeam/golibs/testutil"
|
||||
"github.com/AdguardTeam/golibs/testutil/fakenet"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestEmptyAddrProc(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
p := client.EmptyAddrProc{}
|
||||
|
||||
assert.NotPanics(t, func() {
|
||||
p.Process(testIP)
|
||||
})
|
||||
|
||||
assert.NotPanics(t, func() {
|
||||
err := p.Close()
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestDefaultAddrProc_Process_rDNS(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
privateIP := netip.MustParseAddr("192.168.0.1")
|
||||
|
||||
testCases := []struct {
|
||||
rdnsErr error
|
||||
ip netip.Addr
|
||||
name string
|
||||
host string
|
||||
usePrivate bool
|
||||
wantUpd bool
|
||||
}{{
|
||||
rdnsErr: nil,
|
||||
ip: testIP,
|
||||
name: "success",
|
||||
host: testHost,
|
||||
usePrivate: false,
|
||||
wantUpd: true,
|
||||
}, {
|
||||
rdnsErr: nil,
|
||||
ip: testIP,
|
||||
name: "no_host",
|
||||
host: "",
|
||||
usePrivate: false,
|
||||
wantUpd: false,
|
||||
}, {
|
||||
rdnsErr: nil,
|
||||
ip: netip.MustParseAddr("127.0.0.1"),
|
||||
name: "localhost",
|
||||
host: "",
|
||||
usePrivate: false,
|
||||
wantUpd: false,
|
||||
}, {
|
||||
rdnsErr: nil,
|
||||
ip: privateIP,
|
||||
name: "private_ignored",
|
||||
host: "",
|
||||
usePrivate: false,
|
||||
wantUpd: false,
|
||||
}, {
|
||||
rdnsErr: nil,
|
||||
ip: privateIP,
|
||||
name: "private_processed",
|
||||
host: "private.example",
|
||||
usePrivate: true,
|
||||
wantUpd: true,
|
||||
}, {
|
||||
rdnsErr: errors.Error("rdns error"),
|
||||
ip: testIP,
|
||||
name: "rdns_error",
|
||||
host: "",
|
||||
usePrivate: false,
|
||||
wantUpd: false,
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
updIPCh := make(chan netip.Addr, 1)
|
||||
updHostCh := make(chan string, 1)
|
||||
updInfoCh := make(chan *whois.Info, 1)
|
||||
|
||||
p := client.NewDefaultAddrProc(&client.DefaultAddrProcConfig{
|
||||
DialContext: func(_ context.Context, _, _ string) (conn net.Conn, err error) {
|
||||
panic("not implemented")
|
||||
},
|
||||
Exchanger: &aghtest.Exchanger{
|
||||
OnExchange: func(ip netip.Addr) (host string, err error) {
|
||||
return tc.host, tc.rdnsErr
|
||||
},
|
||||
},
|
||||
PrivateSubnets: netutil.SubnetSetFunc(netutil.IsLocallyServed),
|
||||
AddressUpdater: &aghtest.AddressUpdater{
|
||||
OnUpdateAddress: newOnUpdateAddress(tc.wantUpd, updIPCh, updHostCh, updInfoCh),
|
||||
},
|
||||
UseRDNS: true,
|
||||
UsePrivateRDNS: tc.usePrivate,
|
||||
UseWHOIS: false,
|
||||
})
|
||||
testutil.CleanupAndRequireSuccess(t, p.Close)
|
||||
|
||||
p.Process(tc.ip)
|
||||
|
||||
if !tc.wantUpd {
|
||||
return
|
||||
}
|
||||
|
||||
gotIP, _ := testutil.RequireReceive(t, updIPCh, testTimeout)
|
||||
assert.Equal(t, tc.ip, gotIP)
|
||||
|
||||
gotHost, _ := testutil.RequireReceive(t, updHostCh, testTimeout)
|
||||
assert.Equal(t, tc.host, gotHost)
|
||||
|
||||
gotInfo, _ := testutil.RequireReceive(t, updInfoCh, testTimeout)
|
||||
assert.Nil(t, gotInfo)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// newOnUpdateAddress is a test helper that returns a new OnUpdateAddress
|
||||
// callback using the provided channels if an update is expected and panicking
|
||||
// otherwise.
|
||||
func newOnUpdateAddress(
|
||||
want bool,
|
||||
ips chan<- netip.Addr,
|
||||
hosts chan<- string,
|
||||
infos chan<- *whois.Info,
|
||||
) (f func(ip netip.Addr, host string, info *whois.Info)) {
|
||||
return func(ip netip.Addr, host string, info *whois.Info) {
|
||||
if !want {
|
||||
panic("got unexpected update")
|
||||
}
|
||||
|
||||
ips <- ip
|
||||
hosts <- host
|
||||
infos <- info
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefaultAddrProc_Process_WHOIS(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
testCases := []struct {
|
||||
wantInfo *whois.Info
|
||||
exchErr error
|
||||
name string
|
||||
wantUpd bool
|
||||
}{{
|
||||
wantInfo: &whois.Info{
|
||||
City: testWHOISCity,
|
||||
},
|
||||
exchErr: nil,
|
||||
name: "success",
|
||||
wantUpd: true,
|
||||
}, {
|
||||
wantInfo: nil,
|
||||
exchErr: nil,
|
||||
name: "no_info",
|
||||
wantUpd: false,
|
||||
}, {
|
||||
wantInfo: nil,
|
||||
exchErr: errors.Error("whois error"),
|
||||
name: "whois_error",
|
||||
wantUpd: false,
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
whoisConn := &fakenet.Conn{
|
||||
OnClose: func() (err error) { return nil },
|
||||
OnRead: func(b []byte) (n int, err error) {
|
||||
if tc.wantInfo == nil {
|
||||
return 0, tc.exchErr
|
||||
}
|
||||
|
||||
data := "city: " + tc.wantInfo.City + "\n"
|
||||
copy(b, data)
|
||||
|
||||
return len(data), io.EOF
|
||||
},
|
||||
OnSetDeadline: func(_ time.Time) (err error) { return nil },
|
||||
OnWrite: func(b []byte) (n int, err error) { return len(b), nil },
|
||||
}
|
||||
|
||||
updIPCh := make(chan netip.Addr, 1)
|
||||
updHostCh := make(chan string, 1)
|
||||
updInfoCh := make(chan *whois.Info, 1)
|
||||
|
||||
p := client.NewDefaultAddrProc(&client.DefaultAddrProcConfig{
|
||||
DialContext: func(_ context.Context, _, _ string) (conn net.Conn, err error) {
|
||||
return whoisConn, nil
|
||||
},
|
||||
Exchanger: &aghtest.Exchanger{
|
||||
OnExchange: func(_ netip.Addr) (host string, err error) {
|
||||
panic("not implemented")
|
||||
},
|
||||
},
|
||||
PrivateSubnets: netutil.SubnetSetFunc(netutil.IsLocallyServed),
|
||||
AddressUpdater: &aghtest.AddressUpdater{
|
||||
OnUpdateAddress: newOnUpdateAddress(tc.wantUpd, updIPCh, updHostCh, updInfoCh),
|
||||
},
|
||||
UseRDNS: false,
|
||||
UsePrivateRDNS: false,
|
||||
UseWHOIS: true,
|
||||
})
|
||||
testutil.CleanupAndRequireSuccess(t, p.Close)
|
||||
|
||||
p.Process(testIP)
|
||||
|
||||
if !tc.wantUpd {
|
||||
return
|
||||
}
|
||||
|
||||
gotIP, _ := testutil.RequireReceive(t, updIPCh, testTimeout)
|
||||
assert.Equal(t, testIP, gotIP)
|
||||
|
||||
gotHost, _ := testutil.RequireReceive(t, updHostCh, testTimeout)
|
||||
assert.Empty(t, gotHost)
|
||||
|
||||
gotInfo, _ := testutil.RequireReceive(t, updInfoCh, testTimeout)
|
||||
assert.Equal(t, tc.wantInfo, gotInfo)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefaultAddrProc_Close(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
p := client.NewDefaultAddrProc(&client.DefaultAddrProcConfig{})
|
||||
|
||||
err := p.Close()
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = p.Close()
|
||||
assert.ErrorIs(t, err, client.ErrClosed)
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
// Package client contains types and logic dealing with AdGuard Home's DNS
|
||||
// clients.
|
||||
//
|
||||
// TODO(a.garipov): Expand.
|
||||
package client
|
||||
@@ -1,25 +0,0 @@
|
||||
package client_test
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/golibs/testutil"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
testutil.DiscardLogOutput(m)
|
||||
}
|
||||
|
||||
// testHost is the common hostname for tests.
|
||||
const testHost = "client.example"
|
||||
|
||||
// testTimeout is the common timeout for tests.
|
||||
const testTimeout = 1 * time.Second
|
||||
|
||||
// testWHOISCity is the common city for tests.
|
||||
const testWHOISCity = "Brussels"
|
||||
|
||||
// testIP is the common IP address for tests.
|
||||
var testIP = netip.MustParseAddr("1.2.3.4")
|
||||
@@ -13,7 +13,6 @@ import (
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghalg"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghtls"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/client"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
||||
"github.com/AdguardTeam/dnsproxy/proxy"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
@@ -272,12 +271,9 @@ type ServerConfig struct {
|
||||
TCPListenAddrs []*net.TCPAddr // TCP listen address
|
||||
UpstreamConfig *proxy.UpstreamConfig // Upstream DNS servers config
|
||||
|
||||
// AddrProcConf defines the configuration for the client IP processor.
|
||||
// If nil, [client.EmptyAddrProc] is used.
|
||||
//
|
||||
// TODO(a.garipov): The use of [client.EmptyAddrProc] is a crutch for tests.
|
||||
// Remove that.
|
||||
AddrProcConf *client.DefaultAddrProcConfig
|
||||
// ClientIPs, if not nil, is used to send clients' IP addresses to other
|
||||
// parts of AdGuard Home that may use it for resolving rDNS, WHOIS, etc.
|
||||
ClientIPs chan netip.Addr
|
||||
|
||||
FilteringConfig
|
||||
TLSConfig
|
||||
@@ -305,6 +301,9 @@ type ServerConfig struct {
|
||||
// DNS64Prefixes is a slice of NAT64 prefixes to be used for DNS64.
|
||||
DNS64Prefixes []netip.Prefix
|
||||
|
||||
// ResolveClients signals if the RDNS should resolve clients' addresses.
|
||||
ResolveClients bool
|
||||
|
||||
// UsePrivateRDNS defines if the PTR requests for unknown addresses from
|
||||
// locally-served networks should be resolved via private PTR resolvers.
|
||||
UsePrivateRDNS bool
|
||||
@@ -344,7 +343,6 @@ func (s *Server) createProxyConfig() (conf proxy.Config, err error) {
|
||||
UpstreamConfig: srvConf.UpstreamConfig,
|
||||
BeforeRequestHandler: s.beforeRequestHandler,
|
||||
RequestHandler: s.handleDNSRequest,
|
||||
HTTPSServerName: aghhttp.UserAgent(),
|
||||
EnableEDNSClientSubnet: srvConf.EDNSClientSubnet.Enabled,
|
||||
MaxGoroutines: int(srvConf.MaxGoroutines),
|
||||
UseDNS64: srvConf.UseDNS64,
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
package dnsforward
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
)
|
||||
|
||||
// DialContext is a [whois.DialContextFunc] that uses s to resolve hostnames.
|
||||
func (s *Server) DialContext(ctx context.Context, network, addr string) (conn net.Conn, err error) {
|
||||
log.Debug("dnsforward: dialing %q for network %q", addr, network)
|
||||
|
||||
host, port, err := net.SplitHostPort(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dialer := &net.Dialer{
|
||||
// TODO(a.garipov): Consider making configurable.
|
||||
Timeout: time.Minute * 5,
|
||||
}
|
||||
|
||||
if net.ParseIP(host) != nil {
|
||||
return dialer.DialContext(ctx, network, addr)
|
||||
}
|
||||
|
||||
addrs, err := s.Resolve(host)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("resolving %q: %w", host, err)
|
||||
}
|
||||
|
||||
log.Debug("dnsforward: resolving %q: %v", host, addrs)
|
||||
|
||||
if len(addrs) == 0 {
|
||||
return nil, fmt.Errorf("no addresses for host %q", host)
|
||||
}
|
||||
|
||||
var dialErrs []error
|
||||
for _, a := range addrs {
|
||||
addr = net.JoinHostPort(a.String(), port)
|
||||
conn, err = dialer.DialContext(ctx, network, addr)
|
||||
if err != nil {
|
||||
dialErrs = append(dialErrs, err)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
return conn, err
|
||||
}
|
||||
|
||||
// TODO(a.garipov): Use errors.Join in Go 1.20.
|
||||
return nil, errors.List(fmt.Sprintf("dialing %q", addr), dialErrs...)
|
||||
}
|
||||
@@ -14,7 +14,6 @@ import (
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghalg"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/client"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/dhcpd"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/querylog"
|
||||
@@ -100,17 +99,12 @@ type Server struct {
|
||||
// must be a valid domain name plus dots on each side.
|
||||
localDomainSuffix string
|
||||
|
||||
ipset ipsetCtx
|
||||
privateNets netutil.SubnetSet
|
||||
// ClientIPs, if not nil, is used to send clients' IP addresses to other
|
||||
// parts of AdGuard Home that may use it for resolving rDNS, WHOIS, etc.
|
||||
clientIPs chan<- netip.Addr
|
||||
|
||||
// addrProc, if not nil, is used to process clients' IP addresses with rDNS,
|
||||
// WHOIS, etc.
|
||||
addrProc client.AddressProcessor
|
||||
|
||||
// localResolvers is a DNS proxy instance used to resolve PTR records for
|
||||
// addresses considered private as per the [privateNets].
|
||||
//
|
||||
// TODO(e.burkov): Remove once the local resolvers logic moved to dnsproxy.
|
||||
ipset ipsetCtx
|
||||
privateNets netutil.SubnetSet
|
||||
localResolvers *proxy.Proxy
|
||||
sysResolvers aghnet.SystemResolvers
|
||||
|
||||
@@ -180,9 +174,6 @@ const (
|
||||
|
||||
// NewServer creates a new instance of the dnsforward.Server
|
||||
// Note: this function must be called only once
|
||||
//
|
||||
// TODO(a.garipov): How many constructors and initializers does this thing have?
|
||||
// Refactor!
|
||||
func NewServer(p DNSCreateParams) (s *Server, err error) {
|
||||
var localDomainSuffix string
|
||||
if p.LocalDomain == "" {
|
||||
@@ -270,25 +261,14 @@ func (s *Server) WriteDiskConfig(c *FilteringConfig) {
|
||||
c.UpstreamDNS = stringutil.CloneSlice(sc.UpstreamDNS)
|
||||
}
|
||||
|
||||
// LocalPTRResolvers returns the current local PTR resolver configuration.
|
||||
func (s *Server) LocalPTRResolvers() (localPTRResolvers []string) {
|
||||
// RDNSSettings returns the copy of actual RDNS configuration.
|
||||
func (s *Server) RDNSSettings() (localPTRResolvers []string, resolveClients, resolvePTR bool) {
|
||||
s.serverLock.RLock()
|
||||
defer s.serverLock.RUnlock()
|
||||
|
||||
return stringutil.CloneSlice(s.conf.LocalPTRResolvers)
|
||||
}
|
||||
|
||||
// AddrProcConfig returns the current address processing configuration. Only
|
||||
// fields c.UsePrivateRDNS, c.UseRDNS, and c.UseWHOIS are filled.
|
||||
func (s *Server) AddrProcConfig() (c *client.DefaultAddrProcConfig) {
|
||||
s.serverLock.RLock()
|
||||
defer s.serverLock.RUnlock()
|
||||
|
||||
return &client.DefaultAddrProcConfig{
|
||||
UsePrivateRDNS: s.conf.UsePrivateRDNS,
|
||||
UseRDNS: s.conf.AddrProcConf.UseRDNS,
|
||||
UseWHOIS: s.conf.AddrProcConf.UseWHOIS,
|
||||
}
|
||||
return stringutil.CloneSlice(s.conf.LocalPTRResolvers),
|
||||
s.conf.ResolveClients,
|
||||
s.conf.UsePrivateRDNS
|
||||
}
|
||||
|
||||
// Resolve - get IP addresses by host name from an upstream server.
|
||||
@@ -320,6 +300,10 @@ func (s *Server) Exchange(ip netip.Addr) (host string, err error) {
|
||||
s.serverLock.RLock()
|
||||
defer s.serverLock.RUnlock()
|
||||
|
||||
if !s.conf.ResolveClients {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
arpa, err := netutil.IPToReversedAddr(ip.AsSlice())
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("reversing ip: %w", err)
|
||||
@@ -346,7 +330,7 @@ func (s *Server) Exchange(ip netip.Addr) (host string, err error) {
|
||||
}
|
||||
|
||||
var resolver *proxy.Proxy
|
||||
if s.privateNets.Contains(ip.AsSlice()) {
|
||||
if s.isPrivateIP(ip) {
|
||||
if !s.conf.UsePrivateRDNS {
|
||||
return "", nil
|
||||
}
|
||||
@@ -385,6 +369,27 @@ func hostFromPTR(resp *dns.Msg) (host string, err error) {
|
||||
return "", ErrRDNSNoData
|
||||
}
|
||||
|
||||
// isPrivateIP returns true if the ip is private.
|
||||
func (s *Server) isPrivateIP(ip netip.Addr) (ok bool) {
|
||||
return s.privateNets.Contains(ip.AsSlice())
|
||||
}
|
||||
|
||||
// ShouldResolveClient returns false if ip is a loopback address, or ip is
|
||||
// private and resolving of private addresses is disabled.
|
||||
func (s *Server) ShouldResolveClient(ip netip.Addr) (ok bool) {
|
||||
if ip.IsLoopback() {
|
||||
return false
|
||||
}
|
||||
|
||||
isPrivate := s.isPrivateIP(ip)
|
||||
|
||||
s.serverLock.RLock()
|
||||
defer s.serverLock.RUnlock()
|
||||
|
||||
return s.conf.ResolveClients &&
|
||||
(s.conf.UsePrivateRDNS || !isPrivate)
|
||||
}
|
||||
|
||||
// Start starts the DNS server.
|
||||
func (s *Server) Start() error {
|
||||
s.serverLock.Lock()
|
||||
@@ -457,27 +462,23 @@ func (s *Server) filterOurDNSAddrs(addrs []string) (filtered []string, err error
|
||||
return stringutil.FilterOut(addrs, ourAddrsSet.Has), nil
|
||||
}
|
||||
|
||||
// setupLocalResolvers initializes the resolvers for local addresses. For
|
||||
// internal use only.
|
||||
func (s *Server) setupLocalResolvers() (err error) {
|
||||
// setupResolvers initializes the resolvers for local addresses. For internal
|
||||
// use only.
|
||||
func (s *Server) setupResolvers(localAddrs []string) (err error) {
|
||||
bootstraps := s.conf.BootstrapDNS
|
||||
resolvers := s.conf.LocalPTRResolvers
|
||||
|
||||
if len(resolvers) == 0 {
|
||||
resolvers = s.sysResolvers.Get()
|
||||
if len(localAddrs) == 0 {
|
||||
localAddrs = s.sysResolvers.Get()
|
||||
bootstraps = nil
|
||||
} else {
|
||||
resolvers = stringutil.FilterOut(resolvers, IsCommentOrEmpty)
|
||||
}
|
||||
|
||||
resolvers, err = s.filterOurDNSAddrs(resolvers)
|
||||
localAddrs, err = s.filterOurDNSAddrs(localAddrs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Debug("dnsforward: upstreams to resolve ptr for local addresses: %v", resolvers)
|
||||
log.Debug("dnsforward: upstreams to resolve ptr for local addresses: %v", localAddrs)
|
||||
|
||||
uc, err := s.prepareUpstreamConfig(resolvers, nil, &upstream.Options{
|
||||
upsConfig, err := s.prepareUpstreamConfig(localAddrs, nil, &upstream.Options{
|
||||
Bootstrap: bootstraps,
|
||||
Timeout: defaultLocalTimeout,
|
||||
// TODO(e.burkov): Should we verify server's certificates?
|
||||
@@ -490,17 +491,10 @@ func (s *Server) setupLocalResolvers() (err error) {
|
||||
|
||||
s.localResolvers = &proxy.Proxy{
|
||||
Config: proxy.Config{
|
||||
UpstreamConfig: uc,
|
||||
UpstreamConfig: upsConfig,
|
||||
},
|
||||
}
|
||||
|
||||
if s.conf.UsePrivateRDNS &&
|
||||
// Only set the upstream config if there are any upstreams. It's safe
|
||||
// to put nil into [proxy.Config.PrivateRDNSUpstreamConfig].
|
||||
len(uc.Upstreams)+len(uc.DomainReservedUpstreams)+len(uc.SpecifiedDomainUpstreams) > 0 {
|
||||
s.dnsProxy.PrivateRDNSUpstreamConfig = uc
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -550,37 +544,23 @@ func (s *Server) Prepare(conf *ServerConfig) (err error) {
|
||||
return fmt.Errorf("preparing access: %w", err)
|
||||
}
|
||||
|
||||
// Set the proxy here because [setupLocalResolvers] sets its values.
|
||||
//
|
||||
// TODO(e.burkov): Remove once the local resolvers logic moved to dnsproxy.
|
||||
s.dnsProxy = &proxy.Proxy{Config: proxyConfig}
|
||||
s.registerHandlers()
|
||||
|
||||
err = s.setupLocalResolvers()
|
||||
// TODO(e.burkov): Remove once the local resolvers logic moved to dnsproxy.
|
||||
err = s.setupResolvers(s.conf.LocalPTRResolvers)
|
||||
if err != nil {
|
||||
return fmt.Errorf("setting up resolvers: %w", err)
|
||||
}
|
||||
|
||||
s.recDetector.clear()
|
||||
|
||||
if s.conf.AddrProcConf == nil {
|
||||
// TODO(a.garipov): This is a crutch for tests; remove.
|
||||
s.conf.AddrProcConf = &client.DefaultAddrProcConfig{}
|
||||
s.addrProc = client.EmptyAddrProc{}
|
||||
} else {
|
||||
c := s.conf.AddrProcConf
|
||||
c.DialContext = s.DialContext
|
||||
c.PrivateSubnets = s.privateNets
|
||||
c.UsePrivateRDNS = s.conf.UsePrivateRDNS
|
||||
s.addrProc = client.NewDefaultAddrProc(s.conf.AddrProcConf)
|
||||
|
||||
// Clear the initial addresses to not resolve them again.
|
||||
//
|
||||
// TODO(a.garipov): Consider ways of removing this once more client
|
||||
// logic is moved to package client.
|
||||
c.InitialAddresses = nil
|
||||
if s.conf.UsePrivateRDNS {
|
||||
proxyConfig.PrivateRDNSUpstreamConfig = s.localResolvers.UpstreamConfig
|
||||
}
|
||||
|
||||
s.registerHandlers()
|
||||
s.dnsProxy = &proxy.Proxy{Config: proxyConfig}
|
||||
|
||||
s.recDetector.clear()
|
||||
|
||||
s.clientIPs = s.conf.ClientIPs
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -723,11 +703,9 @@ func (s *Server) Reconfigure(conf *ServerConfig) error {
|
||||
// TODO(a.garipov): This whole piece of API is weird and needs to be remade.
|
||||
if conf == nil {
|
||||
conf = &s.conf
|
||||
} else {
|
||||
closeErr := s.addrProc.Close()
|
||||
if closeErr != nil {
|
||||
log.Error("dnsforward: closing address processor: %s", closeErr)
|
||||
}
|
||||
} else if s.clientIPs != nil {
|
||||
close(s.clientIPs)
|
||||
s.clientIPs = nil
|
||||
}
|
||||
|
||||
err = s.Prepare(conf)
|
||||
|
||||
@@ -1346,7 +1346,9 @@ func TestServer_Exchange(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
srv.conf.ResolveClients = true
|
||||
srv.conf.UsePrivateRDNS = true
|
||||
|
||||
srv.privateNets = netutil.SubnetSetFunc(netutil.IsLocallyServed)
|
||||
|
||||
testCases := []struct {
|
||||
@@ -1420,3 +1422,57 @@ func TestServer_Exchange(t *testing.T) {
|
||||
assert.Empty(t, host)
|
||||
})
|
||||
}
|
||||
|
||||
func TestServer_ShouldResolveClient(t *testing.T) {
|
||||
srv := &Server{
|
||||
privateNets: netutil.SubnetSetFunc(netutil.IsLocallyServed),
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
ip netip.Addr
|
||||
want require.BoolAssertionFunc
|
||||
name string
|
||||
resolve bool
|
||||
usePrivate bool
|
||||
}{{
|
||||
name: "default",
|
||||
ip: netip.MustParseAddr("1.1.1.1"),
|
||||
want: require.True,
|
||||
resolve: true,
|
||||
usePrivate: true,
|
||||
}, {
|
||||
name: "no_rdns",
|
||||
ip: netip.MustParseAddr("1.1.1.1"),
|
||||
want: require.False,
|
||||
resolve: false,
|
||||
usePrivate: true,
|
||||
}, {
|
||||
name: "loopback",
|
||||
ip: netip.MustParseAddr("127.0.0.1"),
|
||||
want: require.False,
|
||||
resolve: true,
|
||||
usePrivate: true,
|
||||
}, {
|
||||
name: "private_resolve",
|
||||
ip: netip.MustParseAddr("192.168.0.1"),
|
||||
want: require.True,
|
||||
resolve: true,
|
||||
usePrivate: true,
|
||||
}, {
|
||||
name: "private_no_resolve",
|
||||
ip: netip.MustParseAddr("192.168.0.1"),
|
||||
want: require.False,
|
||||
resolve: true,
|
||||
usePrivate: false,
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
srv.conf.ResolveClients = tc.resolve
|
||||
srv.conf.UsePrivateRDNS = tc.usePrivate
|
||||
|
||||
ok := srv.ShouldResolveClient(tc.ip)
|
||||
tc.want(t, ok)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ func (s *Server) getDNSConfig() (c *jsonDNSConfig) {
|
||||
cacheMinTTL := s.conf.CacheMinTTL
|
||||
cacheMaxTTL := s.conf.CacheMaxTTL
|
||||
cacheOptimistic := s.conf.CacheOptimistic
|
||||
resolveClients := s.conf.AddrProcConf.UseRDNS
|
||||
resolveClients := s.conf.ResolveClients
|
||||
usePrivateRDNS := s.conf.UsePrivateRDNS
|
||||
localPTRUpstreams := stringutil.CloneSliceOrEmpty(s.conf.LocalPTRResolvers)
|
||||
|
||||
@@ -314,6 +314,8 @@ func (s *Server) setConfig(dc *jsonDNSConfig) (shouldRestart bool) {
|
||||
setIfNotNil(&s.conf.ProtectionEnabled, dc.ProtectionEnabled)
|
||||
setIfNotNil(&s.conf.EnableDNSSEC, dc.DNSSECEnabled)
|
||||
setIfNotNil(&s.conf.AAAADisabled, dc.DisableIPv6)
|
||||
setIfNotNil(&s.conf.ResolveClients, dc.ResolveClients)
|
||||
setIfNotNil(&s.conf.UsePrivateRDNS, dc.UsePrivateRDNS)
|
||||
|
||||
return s.setConfigRestartable(dc)
|
||||
}
|
||||
@@ -333,9 +335,6 @@ func setIfNotNil[T any](currentPtr, newPtr *T) (hasSet bool) {
|
||||
// setConfigRestartable sets the parameters which trigger a restart.
|
||||
// shouldRestart is true if the server should be restarted to apply changes.
|
||||
// s.serverLock is expected to be locked.
|
||||
//
|
||||
// TODO(a.garipov): Some of these could probably be updated without a restart.
|
||||
// Inspect and consider refactoring.
|
||||
func (s *Server) setConfigRestartable(dc *jsonDNSConfig) (shouldRestart bool) {
|
||||
for _, hasSet := range []bool{
|
||||
setIfNotNil(&s.conf.UpstreamDNS, dc.Upstreams),
|
||||
@@ -348,8 +347,6 @@ func (s *Server) setConfigRestartable(dc *jsonDNSConfig) (shouldRestart bool) {
|
||||
setIfNotNil(&s.conf.CacheMinTTL, dc.CacheMinTTL),
|
||||
setIfNotNil(&s.conf.CacheMaxTTL, dc.CacheMaxTTL),
|
||||
setIfNotNil(&s.conf.CacheOptimistic, dc.CacheOptimistic),
|
||||
setIfNotNil(&s.conf.AddrProcConf.UseRDNS, dc.ResolveClients),
|
||||
setIfNotNil(&s.conf.UsePrivateRDNS, dc.UsePrivateRDNS),
|
||||
} {
|
||||
shouldRestart = shouldRestart || hasSet
|
||||
if shouldRestart {
|
||||
|
||||
@@ -171,7 +171,7 @@ const mozillaFQDN = "use-application-dns.net."
|
||||
//
|
||||
// [Section 6.2 of RFC 6761] states that DNS Registries/Registrars must not
|
||||
// grant requests to register test names in the normal way to any person or
|
||||
// entity, making domain names under the .test TLD free to use in internal
|
||||
// entity, making domain names under the test. TLD free to use in internal
|
||||
// purposes.
|
||||
//
|
||||
// [Section 6.2 of RFC 6761]: https://www.rfc-editor.org/rfc/rfc6761.html#section-6.2
|
||||
@@ -222,7 +222,7 @@ func (s *Server) processInitial(dctx *dnsContext) (rc resultCode) {
|
||||
return resultCodeSuccess
|
||||
}
|
||||
|
||||
// processClientIP sends the client IP address to s.addrProc, if needed.
|
||||
// processClientIP sends the client IP address to s.clientIPs, if needed.
|
||||
func (s *Server) processClientIP(addr net.Addr) {
|
||||
clientIP := netutil.NetAddrToAddrPort(addr).Addr()
|
||||
if clientIP == (netip.Addr{}) {
|
||||
@@ -231,12 +231,17 @@ func (s *Server) processClientIP(addr net.Addr) {
|
||||
return
|
||||
}
|
||||
|
||||
// Do not assign s.addrProc to a local variable to then use, since this lock
|
||||
// also serializes the closure of s.addrProc.
|
||||
// Do not assign s.clientIPs to a local variable to then use, since this
|
||||
// lock also serializes the closure of s.clientIPs.
|
||||
s.serverLock.RLock()
|
||||
defer s.serverLock.RUnlock()
|
||||
|
||||
s.addrProc.Process(clientIP)
|
||||
select {
|
||||
case s.clientIPs <- clientIP:
|
||||
// Go on.
|
||||
default:
|
||||
log.Debug("dnsforward: client ip channel is nil or full; len: %d", len(s.clientIPs))
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) setTableHostToIP(t hostToIPTable) {
|
||||
@@ -719,18 +724,6 @@ func (s *Server) processLocalPTR(dctx *dnsContext) (rc resultCode) {
|
||||
if s.conf.UsePrivateRDNS {
|
||||
s.recDetector.add(*pctx.Req)
|
||||
if err := s.localResolvers.Resolve(pctx); err != nil {
|
||||
// Generate the server failure if the private upstream configuration
|
||||
// is empty.
|
||||
//
|
||||
// TODO(e.burkov): Get rid of this crutch once the local resolvers
|
||||
// logic is moved to the dnsproxy completely.
|
||||
if errors.Is(err, upstream.ErrNoUpstreams) {
|
||||
pctx.Res = s.genServerFailure(pctx.Req)
|
||||
|
||||
// Do not even put into query log.
|
||||
return resultCodeFinish
|
||||
}
|
||||
|
||||
dctx.err = err
|
||||
|
||||
return resultCodeError
|
||||
|
||||
@@ -76,21 +76,18 @@ func TestServer_ProcessInitial(t *testing.T) {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
clientIPs := make(chan netip.Addr, 1)
|
||||
|
||||
c := ServerConfig{
|
||||
FilteringConfig: FilteringConfig{
|
||||
AAAADisabled: tc.aaaaDisabled,
|
||||
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
|
||||
},
|
||||
ClientIPs: clientIPs,
|
||||
}
|
||||
|
||||
s := createTestServer(t, &filtering.Config{}, c, nil)
|
||||
|
||||
var gotAddr netip.Addr
|
||||
s.addrProc = &aghtest.AddressProcessor{
|
||||
OnProcess: func(ip netip.Addr) { gotAddr = ip },
|
||||
OnClose: func() (err error) { panic("not implemented") },
|
||||
}
|
||||
|
||||
dctx := &dnsContext{
|
||||
proxyCtx: &proxy.DNSContext{
|
||||
Req: createTestMessageWithType(tc.target, tc.qType),
|
||||
@@ -101,6 +98,8 @@ func TestServer_ProcessInitial(t *testing.T) {
|
||||
|
||||
gotRC := s.processInitial(dctx)
|
||||
assert.Equal(t, tc.wantRC, gotRC)
|
||||
|
||||
gotAddr, _ := testutil.RequireReceive(t, clientIPs, testTimeout)
|
||||
assert.Equal(t, netutil.NetAddrToAddrPort(testClientAddr).Addr(), gotAddr)
|
||||
|
||||
if tc.wantRCode > 0 {
|
||||
|
||||
@@ -6,9 +6,10 @@ import (
|
||||
"fmt"
|
||||
"hash/crc32"
|
||||
"io"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
// Parser is a filtering-rule parser that collects data, such as the checksum
|
||||
@@ -104,11 +105,13 @@ func (p *Parser) processLine(dst io.Writer, line []byte, lineNum int) (n int, er
|
||||
badIdx, isRule = p.parseLineTitle(trimmed)
|
||||
}
|
||||
if badIdx != -1 {
|
||||
badRune, _ := utf8.DecodeRune(trimmed[badIdx:])
|
||||
|
||||
return 0, fmt.Errorf(
|
||||
"line %d: character %d: likely binary character %q",
|
||||
"line %d: character %d: non-printable character %q",
|
||||
lineNum,
|
||||
badIdx+bytes.Index(line, trimmed)+1,
|
||||
trimmed[badIdx],
|
||||
badRune,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -141,37 +144,41 @@ func hasPrefixFold(b, prefix []byte) (ok bool) {
|
||||
}
|
||||
|
||||
// parseLine returns true if the parsed line is a filtering rule. line is
|
||||
// assumed to be trimmed of whitespace characters. badIdx is the index of the
|
||||
// first character that may indicate that this is a binary file, or -1 if none.
|
||||
// assumed to be trimmed of whitespace characters. nonPrintIdx is the index of
|
||||
// the first non-printable character, if any; if there are none, nonPrintIdx is
|
||||
// -1.
|
||||
//
|
||||
// A line is considered a rule if it's not empty, not a comment, and contains
|
||||
// only printable characters.
|
||||
func parseLine(line []byte) (badIdx int, isRule bool) {
|
||||
func parseLine(line []byte) (nonPrintIdx int, isRule bool) {
|
||||
if len(line) == 0 || line[0] == '#' || line[0] == '!' {
|
||||
return -1, false
|
||||
}
|
||||
|
||||
badIdx = slices.IndexFunc(line, likelyBinary)
|
||||
nonPrintIdx = bytes.IndexFunc(line, isNotPrintable)
|
||||
|
||||
return badIdx, badIdx == -1
|
||||
return nonPrintIdx, nonPrintIdx == -1
|
||||
}
|
||||
|
||||
// likelyBinary returns true if b is likely to be a byte from a binary file.
|
||||
func likelyBinary(b byte) (ok bool) {
|
||||
return (b < ' ' || b == 0x7f) && b != '\n' && b != '\r' && b != '\t'
|
||||
// isNotPrintable returns true if r is not a printable character that can be
|
||||
// contained in a filtering rule.
|
||||
func isNotPrintable(r rune) (ok bool) {
|
||||
// Tab isn't included into Unicode's graphic symbols, so include it here
|
||||
// explicitly.
|
||||
return r != '\t' && !unicode.IsGraphic(r)
|
||||
}
|
||||
|
||||
// parseLineTitle is like [parseLine] but additionally looks for a title. line
|
||||
// is assumed to be trimmed of whitespace characters.
|
||||
func (p *Parser) parseLineTitle(line []byte) (badIdx int, isRule bool) {
|
||||
func (p *Parser) parseLineTitle(line []byte) (nonPrintIdx int, isRule bool) {
|
||||
if len(line) == 0 || line[0] == '#' {
|
||||
return -1, false
|
||||
}
|
||||
|
||||
if line[0] != '!' {
|
||||
badIdx = slices.IndexFunc(line, likelyBinary)
|
||||
nonPrintIdx = bytes.IndexFunc(line, isNotPrintable)
|
||||
|
||||
return badIdx, badIdx == -1
|
||||
return nonPrintIdx, nonPrintIdx == -1
|
||||
}
|
||||
|
||||
const titlePattern = "! Title: "
|
||||
|
||||
@@ -77,14 +77,6 @@ func TestParser_Parse(t *testing.T) {
|
||||
wantTitle: "Test Title",
|
||||
wantRulesNum: 1,
|
||||
wantWritten: len(testRuleTextBlocked),
|
||||
}, {
|
||||
name: "cosmetic_with_zwnj",
|
||||
in: testRuleTextCosmetic,
|
||||
wantDst: testRuleTextCosmetic,
|
||||
wantErrMsg: "",
|
||||
wantTitle: "",
|
||||
wantRulesNum: 1,
|
||||
wantWritten: len(testRuleTextCosmetic),
|
||||
}, {
|
||||
name: "bad_char",
|
||||
in: "! Title: Test Title \n" +
|
||||
@@ -93,7 +85,7 @@ func TestParser_Parse(t *testing.T) {
|
||||
wantDst: testRuleTextBlocked,
|
||||
wantErrMsg: "line 3: " +
|
||||
"character 4: " +
|
||||
"likely binary character '\\x7f'",
|
||||
"non-printable character '\\x7f'",
|
||||
wantTitle: "Test Title",
|
||||
wantRulesNum: 1,
|
||||
wantWritten: len(testRuleTextBlocked),
|
||||
@@ -223,14 +215,6 @@ func BenchmarkParser_Parse(b *testing.B) {
|
||||
|
||||
require.NoError(b, errSink)
|
||||
require.NotNil(b, resSink)
|
||||
|
||||
// Most recent result, on a ThinkPad X13 with a Ryzen Pro 7 CPU:
|
||||
//
|
||||
// goos: linux
|
||||
// goarch: amd64
|
||||
// pkg: github.com/AdguardTeam/AdGuardHome/internal/filtering/rulelist
|
||||
// cpu: AMD Ryzen 7 PRO 4750U with Radeon Graphics
|
||||
// BenchmarkParser_Parse-16 100000000 128.0 ns/op 48 B/op 1 allocs/op
|
||||
}
|
||||
|
||||
func FuzzParser_Parse(f *testing.F) {
|
||||
@@ -242,17 +226,15 @@ func FuzzParser_Parse(f *testing.F) {
|
||||
"! Comment",
|
||||
"! Title ",
|
||||
"! Title XXX",
|
||||
testRuleTextBadTab,
|
||||
testRuleTextBlocked,
|
||||
testRuleTextCosmetic,
|
||||
testRuleTextEtcHostsTab,
|
||||
testRuleTextHTML,
|
||||
testRuleTextBlocked,
|
||||
testRuleTextBadTab,
|
||||
"1.2.3.4",
|
||||
"1.2.3.4 etc-hosts.example",
|
||||
">>>\x00<<<",
|
||||
">>>\x7F<<<",
|
||||
strings.Repeat("a", rulelist.DefaultRuleBufSize+1),
|
||||
strings.Repeat("a", bufio.MaxScanTokenSize+1),
|
||||
strings.Repeat("a", n+1),
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
|
||||
@@ -7,13 +7,8 @@ const testTimeout = 1 * time.Second
|
||||
|
||||
// Common texts for tests.
|
||||
const (
|
||||
testRuleTextBadTab = "||bad-tab-and-comment.example^\t# A comment.\n"
|
||||
testRuleTextBlocked = "||blocked.example^\n"
|
||||
testRuleTextEtcHostsTab = "0.0.0.0 tab..example^\t# A comment.\n"
|
||||
testRuleTextHTML = "<!DOCTYPE html>\n"
|
||||
|
||||
// testRuleTextCosmetic is a cosmetic rule with a zero-width non-joiner.
|
||||
//
|
||||
// See https://github.com/AdguardTeam/AdGuardHome/issues/6003.
|
||||
testRuleTextCosmetic = "||cosmetic.example## :has-text(/\u200c/i)\n"
|
||||
testRuleTextBlocked = "||blocked.example^\n"
|
||||
testRuleTextBadTab = "||bad-tab-and-comment.example^\t# A comment.\n"
|
||||
testRuleTextEtcHostsTab = "0.0.0.0 tab..example^\t# A comment.\n"
|
||||
)
|
||||
|
||||
@@ -1505,6 +1505,7 @@ var blockedServices = []blockedService{{
|
||||
"||aus.social^",
|
||||
"||awscommunity.social^",
|
||||
"||climatejustice.social^",
|
||||
"||cupoftea.social^",
|
||||
"||cyberplace.social^",
|
||||
"||defcon.social^",
|
||||
"||det.social^",
|
||||
@@ -1546,12 +1547,12 @@ var blockedServices = []blockedService{{
|
||||
"||mastodon.social^",
|
||||
"||mastodon.uno^",
|
||||
"||mastodon.world^",
|
||||
"||mastodon.zaclys.com^",
|
||||
"||mastodonapp.uk^",
|
||||
"||mastodonners.nl^",
|
||||
"||mastodont.cat^",
|
||||
"||mastodontech.de^",
|
||||
"||mastodontti.fi^",
|
||||
"||mastouille.fr^",
|
||||
"||mathstodon.xyz^",
|
||||
"||metalhead.club^",
|
||||
"||mindly.social^",
|
||||
@@ -1588,7 +1589,6 @@ var blockedServices = []blockedService{{
|
||||
"||techhub.social^",
|
||||
"||theblower.au^",
|
||||
"||tkz.one^",
|
||||
"||todon.eu^",
|
||||
"||toot.aquilenet.fr^",
|
||||
"||toot.community^",
|
||||
"||toot.funami.tech^",
|
||||
@@ -1661,7 +1661,6 @@ var blockedServices = []blockedService{{
|
||||
"||nintendo.jp^",
|
||||
"||nintendo.net^",
|
||||
"||nintendo.nl^",
|
||||
"||nintendo.pt^",
|
||||
"||nintendoswitch.cn^",
|
||||
"||nintendowifi.net^",
|
||||
},
|
||||
@@ -2161,20 +2160,6 @@ var blockedServices = []blockedService{{
|
||||
Rules: []string{
|
||||
"||voot.com^",
|
||||
},
|
||||
}, {
|
||||
ID: "wargaming",
|
||||
Name: "Wargaming",
|
||||
IconSVG: []byte("<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" viewBox=\"0 0 24 24\"><path d=\"M12 1.998c-5.52 0-10 4.481-10 9.988 0 5.52 4.48 9.996 10 9.996s10-4.476 10-9.996c0-5.507-4.48-9.988-10-9.988zm0 2c4.413 0 8 3.588 8 7.988 0 3.246-1.944 6.04-4.727 7.293.54-1.861.831-3.988.807-6.226l1.414.414a23.648 23.648 0 0 0-2-4.041c-.627 1.347-1.48 2.56-2.52 3.68l1.68-.133c-1.507 2.92-3.134 3.906-5.547 4.013-.386-4.213.12-7.014 2.827-9.04l.386 1.493c.653-.974 1.36-2.12 2.373-2.947-1.506-.6-2.999-.627-4.492-.334.386.16.76.588 1.014.828-3.485 1.662-5.643 4.202-6.744 7.68A7.95 7.95 0 0 1 4 11.986c0-4.4 3.587-7.988 8-7.988z\"/></svg>"),
|
||||
Rules: []string{
|
||||
"||wargaming.com^",
|
||||
"||wargaming.net^",
|
||||
"||wgcdn.co^",
|
||||
"||wgcrowd.io^",
|
||||
"||worldoftanks.com^",
|
||||
"||worldofwarplanes.com^",
|
||||
"||worldofwarships.eu^",
|
||||
"||wotblitz.com^",
|
||||
},
|
||||
}, {
|
||||
ID: "wechat",
|
||||
Name: "WeChat",
|
||||
|
||||
145
internal/home/clientaddr.go
Normal file
145
internal/home/clientaddr.go
Normal file
@@ -0,0 +1,145 @@
|
||||
package home
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/netip"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/rdns"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/whois"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
)
|
||||
|
||||
// TODO(a.garipov): It is currently hard to add tests for this structure due to
|
||||
// strong coupling between it and Context.dnsServer with Context.clients.
|
||||
// Resolve this coupling and add proper testing.
|
||||
|
||||
// clientAddrProcessor processes incoming client addresses with rDNS and WHOIS,
|
||||
// if configured.
|
||||
type clientAddrProcessor struct {
|
||||
rdns rdns.Interface
|
||||
whois whois.Interface
|
||||
}
|
||||
|
||||
const (
|
||||
// defaultQueueSize is the size of queue of IPs for rDNS and WHOIS
|
||||
// processing.
|
||||
defaultQueueSize = 255
|
||||
|
||||
// defaultCacheSize is the maximum size of the cache for rDNS and WHOIS
|
||||
// processing. It must be greater than zero.
|
||||
defaultCacheSize = 10_000
|
||||
|
||||
// defaultIPTTL is the Time to Live duration for IP addresses cached by
|
||||
// rDNS and WHOIS.
|
||||
defaultIPTTL = 1 * time.Hour
|
||||
)
|
||||
|
||||
// newClientAddrProcessor returns a new client address processor. c must not be
|
||||
// nil.
|
||||
func newClientAddrProcessor(c *clientSourcesConfig) (p *clientAddrProcessor) {
|
||||
p = &clientAddrProcessor{
|
||||
rdns: &rdns.Empty{},
|
||||
whois: &whois.Empty{},
|
||||
}
|
||||
|
||||
if c.RDNS {
|
||||
p.rdns = rdns.New(&rdns.Config{
|
||||
Exchanger: Context.dnsServer,
|
||||
CacheSize: defaultCacheSize,
|
||||
CacheTTL: defaultIPTTL,
|
||||
})
|
||||
}
|
||||
|
||||
if c.WHOIS {
|
||||
// TODO(s.chzhen): Consider making configurable.
|
||||
const (
|
||||
// defaultTimeout is the timeout for WHOIS requests.
|
||||
defaultTimeout = 5 * time.Second
|
||||
|
||||
// defaultMaxConnReadSize is an upper limit in bytes for reading from a
|
||||
// net.Conn.
|
||||
defaultMaxConnReadSize = 64 * 1024
|
||||
|
||||
// defaultMaxRedirects is the maximum redirects count.
|
||||
defaultMaxRedirects = 5
|
||||
|
||||
// defaultMaxInfoLen is the maximum length of whois.Info fields.
|
||||
defaultMaxInfoLen = 250
|
||||
)
|
||||
|
||||
p.whois = whois.New(&whois.Config{
|
||||
DialContext: customDialContext,
|
||||
ServerAddr: whois.DefaultServer,
|
||||
Port: whois.DefaultPort,
|
||||
Timeout: defaultTimeout,
|
||||
CacheSize: defaultCacheSize,
|
||||
MaxConnReadSize: defaultMaxConnReadSize,
|
||||
MaxRedirects: defaultMaxRedirects,
|
||||
MaxInfoLen: defaultMaxInfoLen,
|
||||
CacheTTL: defaultIPTTL,
|
||||
})
|
||||
}
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
// process processes the incoming client IP-address information. It is intended
|
||||
// to be used as a goroutine. Once clientIPs is closed, process exits.
|
||||
func (p *clientAddrProcessor) process(clientIPs <-chan netip.Addr) {
|
||||
defer log.OnPanic("clientAddrProcessor.process")
|
||||
|
||||
log.Info("home: processing client addresses")
|
||||
|
||||
for ip := range clientIPs {
|
||||
p.processRDNS(ip)
|
||||
p.processWHOIS(ip)
|
||||
}
|
||||
|
||||
log.Info("home: finished processing client addresses")
|
||||
}
|
||||
|
||||
// processRDNS resolves the clients' IP addresses using reverse DNS.
|
||||
func (p *clientAddrProcessor) processRDNS(ip netip.Addr) {
|
||||
start := time.Now()
|
||||
log.Debug("home: processing client %s with rdns", ip)
|
||||
defer func() {
|
||||
log.Debug("home: finished processing client %s with rdns in %s", ip, time.Since(start))
|
||||
}()
|
||||
|
||||
ok := Context.dnsServer.ShouldResolveClient(ip)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
host, changed := p.rdns.Process(ip)
|
||||
if host == "" || !changed {
|
||||
return
|
||||
}
|
||||
|
||||
ok = Context.clients.AddHost(ip, host, ClientSourceRDNS)
|
||||
if ok {
|
||||
return
|
||||
}
|
||||
|
||||
log.Debug("dns: setting rdns info for client %q: already set with higher priority source", ip)
|
||||
}
|
||||
|
||||
// processWHOIS looks up the information aobut clients' IP addresses in the
|
||||
// WHOIS databases.
|
||||
func (p *clientAddrProcessor) processWHOIS(ip netip.Addr) {
|
||||
start := time.Now()
|
||||
log.Debug("home: processing client %s with whois", ip)
|
||||
defer func() {
|
||||
log.Debug("home: finished processing client %s with whois in %s", ip, time.Since(start))
|
||||
}()
|
||||
|
||||
// TODO(s.chzhen): Move the timeout logic from WHOIS configuration to the
|
||||
// context.
|
||||
info, changed := p.whois.Process(context.Background(), ip)
|
||||
if info == nil || !changed {
|
||||
return
|
||||
}
|
||||
|
||||
Context.clients.setWHOISInfo(ip, info)
|
||||
}
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/client"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/dhcpd"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/dhcpsvc"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/dnsforward"
|
||||
@@ -744,9 +743,11 @@ func (clients *clientsContainer) Update(prev, c *Client) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// setWHOISInfo sets the WHOIS information for a client. clients.lock is
|
||||
// expected to be locked.
|
||||
// setWHOISInfo sets the WHOIS information for a client.
|
||||
func (clients *clientsContainer) setWHOISInfo(ip netip.Addr, wi *whois.Info) {
|
||||
clients.lock.Lock()
|
||||
defer clients.lock.Unlock()
|
||||
|
||||
_, ok := clients.findLocked(ip.String())
|
||||
if ok {
|
||||
log.Debug("clients: client for %s is already created, ignore whois info", ip)
|
||||
@@ -773,11 +774,9 @@ func (clients *clientsContainer) setWHOISInfo(ip netip.Addr, wi *whois.Info) {
|
||||
rc.WHOIS = wi
|
||||
}
|
||||
|
||||
// addHost adds a new IP-hostname pairing. The priorities of the sources are
|
||||
// AddHost adds a new IP-hostname pairing. The priorities of the sources are
|
||||
// taken into account. ok is true if the pairing was added.
|
||||
//
|
||||
// TODO(a.garipov): Only used in internal tests. Consider removing.
|
||||
func (clients *clientsContainer) addHost(
|
||||
func (clients *clientsContainer) AddHost(
|
||||
ip netip.Addr,
|
||||
host string,
|
||||
src clientSource,
|
||||
@@ -788,32 +787,6 @@ func (clients *clientsContainer) addHost(
|
||||
return clients.addHostLocked(ip, host, src)
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ client.AddressUpdater = (*clientsContainer)(nil)
|
||||
|
||||
// UpdateAddress implements the [client.AddressUpdater] interface for
|
||||
// *clientsContainer
|
||||
func (clients *clientsContainer) UpdateAddress(ip netip.Addr, host string, info *whois.Info) {
|
||||
// Common fast path optimization.
|
||||
if host == "" && info == nil {
|
||||
return
|
||||
}
|
||||
|
||||
clients.lock.Lock()
|
||||
defer clients.lock.Unlock()
|
||||
|
||||
if host != "" {
|
||||
ok := clients.addHostLocked(ip, host, ClientSourceRDNS)
|
||||
if !ok {
|
||||
log.Debug("clients: host for client %q already set with higher priority source", ip)
|
||||
}
|
||||
}
|
||||
|
||||
if info != nil {
|
||||
clients.setWHOISInfo(ip, info)
|
||||
}
|
||||
}
|
||||
|
||||
// addHostLocked adds a new IP-hostname pairing. clients.lock is expected to be
|
||||
// locked.
|
||||
func (clients *clientsContainer) addHostLocked(
|
||||
|
||||
@@ -168,13 +168,13 @@ func TestClients(t *testing.T) {
|
||||
|
||||
t.Run("addhost_success", func(t *testing.T) {
|
||||
ip := netip.MustParseAddr("1.1.1.1")
|
||||
ok := clients.addHost(ip, "host", ClientSourceARP)
|
||||
ok := clients.AddHost(ip, "host", ClientSourceARP)
|
||||
assert.True(t, ok)
|
||||
|
||||
ok = clients.addHost(ip, "host2", ClientSourceARP)
|
||||
ok = clients.AddHost(ip, "host2", ClientSourceARP)
|
||||
assert.True(t, ok)
|
||||
|
||||
ok = clients.addHost(ip, "host3", ClientSourceHostsFile)
|
||||
ok = clients.AddHost(ip, "host3", ClientSourceHostsFile)
|
||||
assert.True(t, ok)
|
||||
|
||||
assert.Equal(t, clients.clientSource(ip), ClientSourceHostsFile)
|
||||
@@ -182,18 +182,18 @@ func TestClients(t *testing.T) {
|
||||
|
||||
t.Run("dhcp_replaces_arp", func(t *testing.T) {
|
||||
ip := netip.MustParseAddr("1.2.3.4")
|
||||
ok := clients.addHost(ip, "from_arp", ClientSourceARP)
|
||||
ok := clients.AddHost(ip, "from_arp", ClientSourceARP)
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, clients.clientSource(ip), ClientSourceARP)
|
||||
|
||||
ok = clients.addHost(ip, "from_dhcp", ClientSourceDHCP)
|
||||
ok = clients.AddHost(ip, "from_dhcp", ClientSourceDHCP)
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, clients.clientSource(ip), ClientSourceDHCP)
|
||||
})
|
||||
|
||||
t.Run("addhost_fail", func(t *testing.T) {
|
||||
ip := netip.MustParseAddr("1.1.1.1")
|
||||
ok := clients.addHost(ip, "host1", ClientSourceRDNS)
|
||||
ok := clients.AddHost(ip, "host1", ClientSourceRDNS)
|
||||
assert.False(t, ok)
|
||||
})
|
||||
}
|
||||
@@ -216,7 +216,7 @@ func TestClientsWHOIS(t *testing.T) {
|
||||
|
||||
t.Run("existing_auto-client", func(t *testing.T) {
|
||||
ip := netip.MustParseAddr("1.1.1.1")
|
||||
ok := clients.addHost(ip, "host", ClientSourceRDNS)
|
||||
ok := clients.AddHost(ip, "host", ClientSourceRDNS)
|
||||
assert.True(t, ok)
|
||||
|
||||
clients.setWHOISInfo(ip, whois)
|
||||
@@ -259,7 +259,7 @@ func TestClientsAddExisting(t *testing.T) {
|
||||
assert.True(t, ok)
|
||||
|
||||
// Now add an auto-client with the same IP.
|
||||
ok = clients.addHost(ip, "test", ClientSourceRDNS)
|
||||
ok = clients.AddHost(ip, "test", ClientSourceRDNS)
|
||||
assert.True(t, ok)
|
||||
})
|
||||
|
||||
@@ -590,13 +590,7 @@ func (c *configuration) write() (err error) {
|
||||
s.WriteDiskConfig(&c)
|
||||
dns := &config.DNS
|
||||
dns.FilteringConfig = c
|
||||
|
||||
dns.LocalPTRResolvers = s.LocalPTRResolvers()
|
||||
|
||||
addrProcConf := s.AddrProcConfig()
|
||||
config.Clients.Sources.RDNS = addrProcConf.UseRDNS
|
||||
config.Clients.Sources.WHOIS = addrProcConf.UseWHOIS
|
||||
dns.UsePrivateRDNS = addrProcConf.UsePrivateRDNS
|
||||
dns.LocalPTRResolvers, config.Clients.Sources.RDNS, dns.UsePrivateRDNS = s.RDNSSettings()
|
||||
}
|
||||
|
||||
if Context.dhcpServer != nil {
|
||||
|
||||
@@ -176,16 +176,12 @@ func handleStatus(w http.ResponseWriter, r *http.Request) {
|
||||
// ------------------------
|
||||
// registration of handlers
|
||||
// ------------------------
|
||||
func registerControlHandlers(web *webAPI) {
|
||||
Context.mux.HandleFunc(
|
||||
"/control/version.json",
|
||||
postInstall(optionalAuth(web.handleVersionJSON)),
|
||||
)
|
||||
httpRegister(http.MethodPost, "/control/update", web.handleUpdate)
|
||||
|
||||
func registerControlHandlers() {
|
||||
httpRegister(http.MethodGet, "/control/status", handleStatus)
|
||||
httpRegister(http.MethodPost, "/control/i18n/change_language", handleI18nChangeLanguage)
|
||||
httpRegister(http.MethodGet, "/control/i18n/current_language", handleI18nCurrentLanguage)
|
||||
Context.mux.HandleFunc("/control/version.json", postInstall(optionalAuth(handleVersionJSON)))
|
||||
httpRegister(http.MethodPost, "/control/update", handleUpdate)
|
||||
httpRegister(http.MethodGet, "/control/profile", handleGetProfile)
|
||||
httpRegister(http.MethodPut, "/control/profile/update", handlePutProfile)
|
||||
|
||||
|
||||
@@ -448,7 +448,7 @@ func (web *webAPI) handleInstallConfigure(w http.ResponseWriter, r *http.Request
|
||||
web.conf.BindHost = req.Web.IP
|
||||
web.conf.BindPort = req.Web.Port
|
||||
|
||||
registerControlHandlers(web)
|
||||
registerControlHandlers()
|
||||
|
||||
aghhttp.OK(w)
|
||||
if f, ok := w.(http.Flusher); ok {
|
||||
|
||||
@@ -29,9 +29,9 @@ type temporaryError interface {
|
||||
// handleVersionJSON is the handler for the POST /control/version.json HTTP API.
|
||||
//
|
||||
// TODO(a.garipov): Find out if this API used with a GET method by anyone.
|
||||
func (web *webAPI) handleVersionJSON(w http.ResponseWriter, r *http.Request) {
|
||||
func handleVersionJSON(w http.ResponseWriter, r *http.Request) {
|
||||
resp := &versionResponse{}
|
||||
if web.conf.disableUpdate {
|
||||
if Context.disableUpdate {
|
||||
resp.Disabled = true
|
||||
_ = aghhttp.WriteJSONResponse(w, r, resp)
|
||||
|
||||
@@ -52,7 +52,7 @@ func (web *webAPI) handleVersionJSON(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
err = web.requestVersionInfo(resp, req.Recheck)
|
||||
err = requestVersionInfo(resp, req.Recheck)
|
||||
if err != nil {
|
||||
// Don't wrap the error, because it's informative enough as is.
|
||||
aghhttp.Error(r, w, http.StatusBadGateway, "%s", err)
|
||||
@@ -73,10 +73,9 @@ func (web *webAPI) handleVersionJSON(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// requestVersionInfo sets the VersionInfo field of resp if it can reach the
|
||||
// update server.
|
||||
func (web *webAPI) requestVersionInfo(resp *versionResponse, recheck bool) (err error) {
|
||||
updater := web.conf.updater
|
||||
func requestVersionInfo(resp *versionResponse, recheck bool) (err error) {
|
||||
for i := 0; i != 3; i++ {
|
||||
resp.VersionInfo, err = updater.VersionInfo(recheck)
|
||||
resp.VersionInfo, err = Context.updater.VersionInfo(recheck)
|
||||
if err != nil {
|
||||
var terr temporaryError
|
||||
if errors.As(err, &terr) && terr.Temporary() {
|
||||
@@ -96,7 +95,7 @@ func (web *webAPI) requestVersionInfo(resp *versionResponse, recheck bool) (err
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
vcu := updater.VersionCheckURL()
|
||||
vcu := Context.updater.VersionCheckURL()
|
||||
|
||||
return fmt.Errorf("getting version info from %s: %w", vcu, err)
|
||||
}
|
||||
@@ -105,9 +104,8 @@ func (web *webAPI) requestVersionInfo(resp *versionResponse, recheck bool) (err
|
||||
}
|
||||
|
||||
// handleUpdate performs an update to the latest available version procedure.
|
||||
func (web *webAPI) handleUpdate(w http.ResponseWriter, r *http.Request) {
|
||||
updater := web.conf.updater
|
||||
if updater.NewVersion() == "" {
|
||||
func handleUpdate(w http.ResponseWriter, r *http.Request) {
|
||||
if Context.updater.NewVersion() == "" {
|
||||
aghhttp.Error(r, w, http.StatusBadRequest, "/update request isn't allowed now")
|
||||
|
||||
return
|
||||
@@ -124,7 +122,7 @@ func (web *webAPI) handleUpdate(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
err = updater.Update(false)
|
||||
err = Context.updater.Update(false)
|
||||
if err != nil {
|
||||
aghhttp.Error(r, w, http.StatusInternalServerError, "%s", err)
|
||||
|
||||
@@ -139,7 +137,7 @@ func (web *webAPI) handleUpdate(w http.ResponseWriter, r *http.Request) {
|
||||
// The background context is used because the underlying functions wrap it
|
||||
// with timeout and shut down the server, which handles current request. It
|
||||
// also should be done in a separate goroutine for the same reason.
|
||||
go finishUpdate(context.Background(), execPath, web.conf.runningAsService)
|
||||
go finishUpdate(context.Background(), execPath)
|
||||
}
|
||||
|
||||
// versionResponse is the response for /control/version.json endpoint.
|
||||
@@ -180,7 +178,7 @@ func tlsConfUsesPrivilegedPorts(c *tlsConfigSettings) (ok bool) {
|
||||
}
|
||||
|
||||
// finishUpdate completes an update procedure.
|
||||
func finishUpdate(ctx context.Context, execPath string, runningAsService bool) {
|
||||
func finishUpdate(ctx context.Context, execPath string) {
|
||||
var err error
|
||||
|
||||
log.Info("stopping all tasks")
|
||||
@@ -189,7 +187,7 @@ func finishUpdate(ctx context.Context, execPath string, runningAsService bool) {
|
||||
cleanupAlways()
|
||||
|
||||
if runtime.GOOS == "windows" {
|
||||
if runningAsService {
|
||||
if Context.runningAsService {
|
||||
// NOTE: We can't restart the service via "kardianos/service"
|
||||
// package, because it kills the process first we can't start a new
|
||||
// instance, because Windows doesn't allow it.
|
||||
|
||||
@@ -13,7 +13,6 @@ import (
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghalg"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/client"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/dhcpd"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/dnsforward"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
||||
@@ -133,7 +132,7 @@ func initDNSServer(
|
||||
return fmt.Errorf("preparing set of private subnets: %w", err)
|
||||
}
|
||||
|
||||
Context.dnsServer, err = dnsforward.NewServer(dnsforward.DNSCreateParams{
|
||||
p := dnsforward.DNSCreateParams{
|
||||
DNSFilter: filters,
|
||||
Stats: sts,
|
||||
QueryLog: qlog,
|
||||
@@ -141,7 +140,9 @@ func initDNSServer(
|
||||
Anonymizer: anonymizer,
|
||||
LocalDomain: config.DHCP.LocalDomainName,
|
||||
DHCPServer: dhcpSrv,
|
||||
})
|
||||
}
|
||||
|
||||
Context.dnsServer, err = dnsforward.NewServer(p)
|
||||
if err != nil {
|
||||
closeDNSServer()
|
||||
|
||||
@@ -164,6 +165,15 @@ func initDNSServer(
|
||||
return fmt.Errorf("dnsServer.Prepare: %w", err)
|
||||
}
|
||||
|
||||
clientIPs := dnsConf.ClientIPs
|
||||
addrProc := newClientAddrProcessor(config.Clients.Sources)
|
||||
go addrProc.process(clientIPs)
|
||||
|
||||
const topClientsNumber = 100
|
||||
for _, ip := range Context.stats.TopClientsIP(topClientsNumber) {
|
||||
clientIPs <- ip
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -229,35 +239,18 @@ func newServerConfig(
|
||||
) (newConf *dnsforward.ServerConfig, err error) {
|
||||
dnsConf := config.DNS
|
||||
hosts := aghalg.CoalesceSlice(dnsConf.BindHosts, []netip.Addr{netutil.IPv4Localhost()})
|
||||
|
||||
clientIPs := make(chan netip.Addr, defaultQueueSize)
|
||||
newConf = &dnsforward.ServerConfig{
|
||||
UDPListenAddrs: ipsToUDPAddrs(hosts, dnsConf.Port),
|
||||
TCPListenAddrs: ipsToTCPAddrs(hosts, dnsConf.Port),
|
||||
FilteringConfig: dnsConf.FilteringConfig,
|
||||
ConfigModified: onConfigModified,
|
||||
HTTPRegister: httpReg,
|
||||
ClientIPs: clientIPs,
|
||||
UseDNS64: config.DNS.UseDNS64,
|
||||
DNS64Prefixes: config.DNS.DNS64Prefixes,
|
||||
}
|
||||
|
||||
var initialAddresses []netip.Addr
|
||||
// Context.stats may be nil here if initDNSServer is called from
|
||||
// [cmdlineUpdate].
|
||||
if sts := Context.stats; sts != nil {
|
||||
const initialClientsNum = 100
|
||||
initialAddresses = Context.stats.TopClientsIP(initialClientsNum)
|
||||
}
|
||||
|
||||
// Do not set DialContext, PrivateSubnets, and UsePrivateRDNS, because they
|
||||
// are set by [dnsforward.Server.Prepare].
|
||||
newConf.AddrProcConf = &client.DefaultAddrProcConfig{
|
||||
Exchanger: Context.dnsServer,
|
||||
AddressUpdater: &Context.clients,
|
||||
InitialAddresses: initialAddresses,
|
||||
UseRDNS: config.Clients.Sources.RDNS,
|
||||
UseWHOIS: config.Clients.Sources.WHOIS,
|
||||
}
|
||||
|
||||
if tlsConf.Enabled {
|
||||
newConf.TLSConfig = tlsConf.TLSConfig
|
||||
newConf.TLSConfig.ServerName = tlsConf.ServerName
|
||||
@@ -293,6 +286,7 @@ func newServerConfig(
|
||||
newConf.LocalPTRResolvers = dnsConf.LocalPTRResolvers
|
||||
newConf.UpstreamTimeout = dnsConf.UpstreamTimeout.Duration
|
||||
|
||||
newConf.ResolveClients = config.Clients.Sources.RDNS
|
||||
newConf.UsePrivateRDNS = dnsConf.UsePrivateRDNS
|
||||
newConf.ServeHTTP3 = dnsConf.ServeHTTP3
|
||||
newConf.UseHTTP3Upstreams = dnsConf.UseHTTP3Upstreams
|
||||
@@ -464,6 +458,9 @@ func reconfigureDNSServer() (err error) {
|
||||
return fmt.Errorf("starting forwarding dns server: %w", err)
|
||||
}
|
||||
|
||||
addrProc := newClientAddrProcessor(config.Clients.Sources)
|
||||
go addrProc.process(newConf.ClientIPs)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -3,12 +3,14 @@ package home
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/netip"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
@@ -64,24 +66,34 @@ type homeContext struct {
|
||||
// configuration files, for example /etc/hosts.
|
||||
etcHosts *aghnet.HostsContainer
|
||||
|
||||
updater *updater.Updater
|
||||
|
||||
// mux is our custom http.ServeMux.
|
||||
mux *http.ServeMux
|
||||
|
||||
// Runtime properties
|
||||
// --
|
||||
|
||||
configFilename string // Config filename (can be overridden via the command line arguments)
|
||||
workDir string // Location of our directory, used to protect against CWD being somewhere else
|
||||
pidFileName string // PID file name. Empty if no PID file was created.
|
||||
controlLock sync.Mutex
|
||||
tlsRoots *x509.CertPool // list of root CAs for TLSv1.2
|
||||
configFilename string // Config filename (can be overridden via the command line arguments)
|
||||
workDir string // Location of our directory, used to protect against CWD being somewhere else
|
||||
pidFileName string // PID file name. Empty if no PID file was created.
|
||||
controlLock sync.Mutex
|
||||
tlsRoots *x509.CertPool // list of root CAs for TLSv1.2
|
||||
client *http.Client
|
||||
appSignalChannel chan os.Signal // Channel for receiving OS signals by the console app
|
||||
|
||||
// tlsCipherIDs are the ID of the cipher suites that AdGuard Home must use.
|
||||
tlsCipherIDs []uint16
|
||||
|
||||
// disableUpdate, if true, tells AdGuard Home to not check for updates.
|
||||
disableUpdate bool
|
||||
|
||||
// firstRun, if true, tells AdGuard Home to only start the web interface
|
||||
// service, and only serve the first-run APIs.
|
||||
firstRun bool
|
||||
|
||||
// runningAsService flag is set to true when options are passed from the service runner
|
||||
runningAsService bool
|
||||
}
|
||||
|
||||
// getDataDir returns path to the directory where we store databases and filters
|
||||
@@ -104,11 +116,11 @@ func Main(clientBuildFS fs.FS) {
|
||||
// package flag.
|
||||
opts := loadCmdLineOpts()
|
||||
|
||||
signals := make(chan os.Signal, 1)
|
||||
signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP, syscall.SIGQUIT)
|
||||
Context.appSignalChannel = make(chan os.Signal)
|
||||
signal.Notify(Context.appSignalChannel, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP, syscall.SIGQUIT)
|
||||
go func() {
|
||||
for {
|
||||
sig := <-signals
|
||||
sig := <-Context.appSignalChannel
|
||||
log.Info("Received signal %q", sig)
|
||||
switch sig {
|
||||
case syscall.SIGHUP:
|
||||
@@ -123,7 +135,7 @@ func Main(clientBuildFS fs.FS) {
|
||||
}()
|
||||
|
||||
if opts.serviceControlAction != "" {
|
||||
handleServiceControlAction(opts, clientBuildFS, signals)
|
||||
handleServiceControlAction(opts, clientBuildFS)
|
||||
|
||||
return
|
||||
}
|
||||
@@ -135,48 +147,74 @@ func Main(clientBuildFS fs.FS) {
|
||||
// setupContext initializes [Context] fields. It also reads and upgrades
|
||||
// config file if necessary.
|
||||
func setupContext(opts options) (err error) {
|
||||
Context.firstRun = detectFirstRun()
|
||||
setupContextFlags(opts)
|
||||
|
||||
Context.tlsRoots = aghtls.SystemRootCAs()
|
||||
Context.client = &http.Client{
|
||||
Timeout: time.Minute * 5,
|
||||
Transport: &http.Transport{
|
||||
DialContext: customDialContext,
|
||||
Proxy: getHTTPProxy,
|
||||
TLSClientConfig: &tls.Config{
|
||||
RootCAs: Context.tlsRoots,
|
||||
CipherSuites: Context.tlsCipherIDs,
|
||||
MinVersion: tls.VersionTLS12,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
Context.mux = http.NewServeMux()
|
||||
|
||||
if Context.firstRun {
|
||||
log.Info("This is the first time AdGuard Home is launched")
|
||||
checkPermissions()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do the upgrade if necessary.
|
||||
err = upgradeConfig()
|
||||
if err != nil {
|
||||
// Don't wrap the error, because it's informative enough as is.
|
||||
return err
|
||||
}
|
||||
|
||||
if err = parseConfig(); err != nil {
|
||||
log.Error("parsing configuration file: %s", err)
|
||||
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if opts.checkConfig {
|
||||
log.Info("configuration file is ok")
|
||||
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if !opts.noEtcHosts && config.Clients.Sources.HostsFile {
|
||||
err = setupHostsContainer()
|
||||
if !Context.firstRun {
|
||||
// Do the upgrade if necessary.
|
||||
err = upgradeConfig()
|
||||
if err != nil {
|
||||
// Don't wrap the error, because it's informative enough as is.
|
||||
return err
|
||||
}
|
||||
|
||||
if err = parseConfig(); err != nil {
|
||||
log.Error("parsing configuration file: %s", err)
|
||||
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if opts.checkConfig {
|
||||
log.Info("configuration file is ok")
|
||||
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if !opts.noEtcHosts && config.Clients.Sources.HostsFile {
|
||||
err = setupHostsContainer()
|
||||
if err != nil {
|
||||
// Don't wrap the error, because it's informative enough as is.
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// setupContextFlags sets global flags and prints their status to the log.
|
||||
func setupContextFlags(opts options) {
|
||||
Context.firstRun = detectFirstRun()
|
||||
if Context.firstRun {
|
||||
log.Info("This is the first time AdGuard Home is launched")
|
||||
checkPermissions()
|
||||
}
|
||||
|
||||
Context.runningAsService = opts.runningAsService
|
||||
// Don't print the runningAsService flag, since that has already been done
|
||||
// in [run].
|
||||
|
||||
Context.disableUpdate = opts.disableUpdate || version.Channel() == version.ChannelDevelopment
|
||||
if Context.disableUpdate {
|
||||
log.Info("AdGuard Home updates are disabled")
|
||||
}
|
||||
}
|
||||
|
||||
// logIfUnsupported logs a formatted warning if the error is one of the
|
||||
// unsupported errors and returns nil. If err is nil, logIfUnsupported returns
|
||||
// nil. Otherwise, it returns err.
|
||||
@@ -281,7 +319,7 @@ func initContextClients() (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
//lint:ignore SA1019 Migration is not over.
|
||||
//lint:ignore SA1019 Migration is not over.
|
||||
config.DHCP.WorkDir = Context.workDir
|
||||
config.DHCP.DataDir = Context.getDataDir()
|
||||
config.DHCP.HTTPRegister = httpRegister
|
||||
@@ -296,6 +334,18 @@ func initContextClients() (err error) {
|
||||
return fmt.Errorf("initing dhcp: %w", err)
|
||||
}
|
||||
|
||||
Context.updater = updater.NewUpdater(&updater.Config{
|
||||
Client: Context.client,
|
||||
Version: version.Version(),
|
||||
Channel: version.Channel(),
|
||||
GOARCH: runtime.GOARCH,
|
||||
GOOS: runtime.GOOS,
|
||||
GOARM: version.GOARM(),
|
||||
GOMIPS: version.GOMIPS(),
|
||||
WorkDir: Context.workDir,
|
||||
ConfName: config.getConfigFilename(),
|
||||
})
|
||||
|
||||
var arpdb aghnet.ARPDB
|
||||
if config.Clients.Sources.ARP {
|
||||
arpdb = aghnet.NewARPDB()
|
||||
@@ -377,7 +427,7 @@ func setupDNSFilteringConf(conf *filtering.Config) (err error) {
|
||||
conf.Filters = slices.Clone(config.Filters)
|
||||
conf.WhitelistFilters = slices.Clone(config.WhitelistFilters)
|
||||
conf.UserRules = slices.Clone(config.UserRules)
|
||||
conf.HTTPClient = httpClient()
|
||||
conf.HTTPClient = Context.client
|
||||
|
||||
cacheTime := time.Duration(conf.CacheTime) * time.Minute
|
||||
|
||||
@@ -459,7 +509,7 @@ func checkPorts() (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func initWeb(opts options, clientBuildFS fs.FS, upd *updater.Updater) (web *webAPI, err error) {
|
||||
func initWeb(opts options, clientBuildFS fs.FS) (web *webAPI, err error) {
|
||||
var clientFS fs.FS
|
||||
if opts.localFrontend {
|
||||
log.Info("warning: using local frontend files")
|
||||
@@ -472,16 +522,8 @@ func initWeb(opts options, clientBuildFS fs.FS, upd *updater.Updater) (web *webA
|
||||
}
|
||||
}
|
||||
|
||||
disableUpdate := opts.disableUpdate || version.Channel() == version.ChannelDevelopment
|
||||
if disableUpdate {
|
||||
log.Info("AdGuard Home updates are disabled")
|
||||
}
|
||||
|
||||
webConf := &webConfig{
|
||||
updater: upd,
|
||||
|
||||
clientFS: clientFS,
|
||||
|
||||
webConf := webConfig{
|
||||
firstRun: Context.firstRun,
|
||||
BindHost: config.HTTPConfig.Address.Addr(),
|
||||
BindPort: int(config.HTTPConfig.Address.Port()),
|
||||
|
||||
@@ -489,13 +531,12 @@ func initWeb(opts options, clientBuildFS fs.FS, upd *updater.Updater) (web *webA
|
||||
ReadHeaderTimeout: readHdrTimeout,
|
||||
WriteTimeout: writeTimeout,
|
||||
|
||||
firstRun: Context.firstRun,
|
||||
disableUpdate: disableUpdate,
|
||||
runningAsService: opts.runningAsService,
|
||||
serveHTTP3: config.DNS.ServeHTTP3,
|
||||
clientFS: clientFS,
|
||||
|
||||
serveHTTP3: config.DNS.ServeHTTP3,
|
||||
}
|
||||
|
||||
web = newWebAPI(webConf)
|
||||
web = newWebAPI(&webConf)
|
||||
if web == nil {
|
||||
return nil, fmt.Errorf("initializing web: %w", err)
|
||||
}
|
||||
@@ -546,21 +587,9 @@ func run(opts options, clientBuildFS fs.FS) {
|
||||
err = setupOpts(opts)
|
||||
fatalOnError(err)
|
||||
|
||||
upd := updater.NewUpdater(&updater.Config{
|
||||
Client: config.DNS.DnsfilterConf.HTTPClient,
|
||||
Version: version.Version(),
|
||||
Channel: version.Channel(),
|
||||
GOARCH: runtime.GOARCH,
|
||||
GOOS: runtime.GOOS,
|
||||
GOARM: version.GOARM(),
|
||||
GOMIPS: version.GOMIPS(),
|
||||
WorkDir: Context.workDir,
|
||||
ConfName: config.getConfigFilename(),
|
||||
})
|
||||
|
||||
// TODO(e.burkov): This could be made earlier, probably as the option's
|
||||
// effect.
|
||||
cmdlineUpdate(opts, upd)
|
||||
cmdlineUpdate(opts)
|
||||
|
||||
if !Context.firstRun {
|
||||
// Save the updated config.
|
||||
@@ -589,7 +618,7 @@ func run(opts options, clientBuildFS fs.FS) {
|
||||
onConfigModified()
|
||||
}
|
||||
|
||||
Context.web, err = initWeb(opts, clientBuildFS, upd)
|
||||
Context.web, err = initWeb(opts, clientBuildFS)
|
||||
fatalOnError(err)
|
||||
|
||||
if !Context.firstRun {
|
||||
@@ -961,6 +990,62 @@ func detectFirstRun() bool {
|
||||
return errors.Is(err, os.ErrNotExist)
|
||||
}
|
||||
|
||||
// Connect to a remote server resolving hostname using our own DNS server.
|
||||
//
|
||||
// TODO(e.burkov): This messy logic should be decomposed and clarified.
|
||||
//
|
||||
// TODO(a.garipov): Support network.
|
||||
func customDialContext(ctx context.Context, network, addr string) (conn net.Conn, err error) {
|
||||
log.Debug("home: customdial: dialing addr %q for network %s", addr, network)
|
||||
|
||||
host, port, err := net.SplitHostPort(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dialer := &net.Dialer{
|
||||
Timeout: time.Minute * 5,
|
||||
}
|
||||
|
||||
if net.ParseIP(host) != nil || config.DNS.Port == 0 {
|
||||
return dialer.DialContext(ctx, network, addr)
|
||||
}
|
||||
|
||||
addrs, err := Context.dnsServer.Resolve(host)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("resolving %q: %w", host, err)
|
||||
}
|
||||
|
||||
log.Debug("dnsServer.Resolve: %q: %v", host, addrs)
|
||||
|
||||
if len(addrs) == 0 {
|
||||
return nil, fmt.Errorf("couldn't lookup host: %q", host)
|
||||
}
|
||||
|
||||
var dialErrs []error
|
||||
for _, a := range addrs {
|
||||
addr = net.JoinHostPort(a.String(), port)
|
||||
conn, err = dialer.DialContext(ctx, network, addr)
|
||||
if err != nil {
|
||||
dialErrs = append(dialErrs, err)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
return conn, err
|
||||
}
|
||||
|
||||
return nil, errors.List(fmt.Sprintf("couldn't dial to %s", addr), dialErrs...)
|
||||
}
|
||||
|
||||
func getHTTPProxy(_ *http.Request) (*url.URL, error) {
|
||||
if config.ProxyURL == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return url.Parse(config.ProxyURL)
|
||||
}
|
||||
|
||||
// jsonError is a generic JSON error response.
|
||||
//
|
||||
// TODO(a.garipov): Merge together with the implementations in [dhcpd] and other
|
||||
@@ -971,7 +1056,7 @@ type jsonError struct {
|
||||
}
|
||||
|
||||
// cmdlineUpdate updates current application and exits.
|
||||
func cmdlineUpdate(opts options, upd *updater.Updater) {
|
||||
func cmdlineUpdate(opts options) {
|
||||
if !opts.performUpdate {
|
||||
return
|
||||
}
|
||||
@@ -986,9 +1071,10 @@ func cmdlineUpdate(opts options, upd *updater.Updater) {
|
||||
|
||||
log.Info("cmdline update: performing update")
|
||||
|
||||
info, err := upd.VersionInfo(true)
|
||||
updater := Context.updater
|
||||
info, err := updater.VersionInfo(true)
|
||||
if err != nil {
|
||||
vcu := upd.VersionCheckURL()
|
||||
vcu := updater.VersionCheckURL()
|
||||
log.Error("getting version info from %s: %s", vcu, err)
|
||||
|
||||
os.Exit(1)
|
||||
@@ -1000,7 +1086,7 @@ func cmdlineUpdate(opts options, upd *updater.Updater) {
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
err = upd.Update(Context.firstRun)
|
||||
err = updater.Update(Context.firstRun)
|
||||
fatalOnError(err)
|
||||
|
||||
err = restartService()
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
package home
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
// httpClient returns a new HTTP client that uses the AdGuard Home's own DNS
|
||||
// server for resolving hostnames. The resulting client should not be used
|
||||
// until [Context.dnsServer] is initialized.
|
||||
//
|
||||
// TODO(a.garipov, e.burkov): This is rather messy. Refactor.
|
||||
func httpClient() (c *http.Client) {
|
||||
// Do not use Context.dnsServer.DialContext directly in the struct literal
|
||||
// below, since Context.dnsServer may be nil when this function is called.
|
||||
dialContext := func(ctx context.Context, network, addr string) (conn net.Conn, err error) {
|
||||
return Context.dnsServer.DialContext(ctx, network, addr)
|
||||
}
|
||||
|
||||
return &http.Client{
|
||||
// TODO(a.garipov): Make configurable.
|
||||
Timeout: time.Minute * 5,
|
||||
Transport: &http.Transport{
|
||||
DialContext: dialContext,
|
||||
Proxy: httpProxy,
|
||||
TLSClientConfig: &tls.Config{
|
||||
RootCAs: Context.tlsRoots,
|
||||
CipherSuites: Context.tlsCipherIDs,
|
||||
MinVersion: tls.VersionTLS12,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// httpProxy returns parses and returns an HTTP proxy URL from the config, if
|
||||
// any.
|
||||
func httpProxy(_ *http.Request) (u *url.URL, err error) {
|
||||
if config.ProxyURL == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return url.Parse(config.ProxyURL)
|
||||
}
|
||||
@@ -33,13 +33,9 @@ const (
|
||||
// daemon.
|
||||
type program struct {
|
||||
clientBuildFS fs.FS
|
||||
signals chan os.Signal
|
||||
opts options
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ service.Interface = (*program)(nil)
|
||||
|
||||
// Start implements service.Interface interface for *program.
|
||||
func (p *program) Start(_ service.Service) (err error) {
|
||||
// Start should not block. Do the actual work async.
|
||||
@@ -52,14 +48,14 @@ func (p *program) Start(_ service.Service) (err error) {
|
||||
}
|
||||
|
||||
// Stop implements service.Interface interface for *program.
|
||||
func (p *program) Stop(_ service.Service) (err error) {
|
||||
select {
|
||||
case p.signals <- syscall.SIGINT:
|
||||
// Go on.
|
||||
default:
|
||||
// Stop should not block.
|
||||
func (p *program) Stop(_ service.Service) error {
|
||||
// Stop should not block. Return with a few seconds.
|
||||
if Context.appSignalChannel == nil {
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
Context.appSignalChannel <- syscall.SIGINT
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -198,7 +194,7 @@ func restartService() (err error) {
|
||||
// - run: This is a special command that is not supposed to be used directly
|
||||
// it is specified when we register a service, and it indicates to the app
|
||||
// that it is being run as a service/daemon.
|
||||
func handleServiceControlAction(opts options, clientBuildFS fs.FS, signals chan os.Signal) {
|
||||
func handleServiceControlAction(opts options, clientBuildFS fs.FS) {
|
||||
// Call chooseSystem explicitly to introduce OpenBSD support for service
|
||||
// package. It's a noop for other GOOS values.
|
||||
chooseSystem()
|
||||
@@ -230,11 +226,7 @@ func handleServiceControlAction(opts options, clientBuildFS fs.FS, signals chan
|
||||
}
|
||||
configureService(svcConfig)
|
||||
|
||||
s, err := service.New(&program{
|
||||
clientBuildFS: clientBuildFS,
|
||||
signals: signals,
|
||||
opts: runOpts,
|
||||
}, svcConfig)
|
||||
s, err := service.New(&program{clientBuildFS: clientBuildFS, opts: runOpts}, svcConfig)
|
||||
if err != nil {
|
||||
log.Fatalf("service: initializing service: %s", err)
|
||||
}
|
||||
|
||||
@@ -11,11 +11,11 @@ import (
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/updater"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/AdguardTeam/golibs/netutil"
|
||||
"github.com/NYTimes/gziphandler"
|
||||
"github.com/quic-go/quic-go"
|
||||
"github.com/quic-go/quic-go/http3"
|
||||
"golang.org/x/net/http2"
|
||||
"golang.org/x/net/http2/h2c"
|
||||
@@ -33,8 +33,6 @@ const (
|
||||
)
|
||||
|
||||
type webConfig struct {
|
||||
updater *updater.Updater
|
||||
|
||||
clientFS fs.FS
|
||||
|
||||
BindHost netip.Addr
|
||||
@@ -54,13 +52,6 @@ type webConfig struct {
|
||||
|
||||
firstRun bool
|
||||
|
||||
// disableUpdate, if true, tells AdGuard Home to not check for updates.
|
||||
disableUpdate bool
|
||||
|
||||
// runningAsService flag is set to true when options are passed from the
|
||||
// service runner.
|
||||
runningAsService bool
|
||||
|
||||
serveHTTP3 bool
|
||||
}
|
||||
|
||||
@@ -111,7 +102,7 @@ func newWebAPI(conf *webConfig) (w *webAPI) {
|
||||
Context.mux.Handle("/install.html", preInstallHandler(clientFS))
|
||||
w.registerInstallHandlers()
|
||||
} else {
|
||||
registerControlHandlers(w)
|
||||
registerControlHandlers()
|
||||
}
|
||||
|
||||
w.httpsServer.cond = sync.NewCond(&w.httpsServer.condLock)
|
||||
@@ -304,7 +295,7 @@ func (web *webAPI) mustStartHTTP3(address string) {
|
||||
|
||||
log.Debug("web: starting http/3 server")
|
||||
err := web.httpsServer.server3.ListenAndServe()
|
||||
if !errors.Is(err, http.ErrServerClosed) {
|
||||
if !errors.Is(err, quic.ErrServerClosed) {
|
||||
cleanupAlways()
|
||||
log.Fatalf("web: http3: %s", err)
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ type Interface interface {
|
||||
Process(ip netip.Addr) (host string, changed bool)
|
||||
}
|
||||
|
||||
// Empty is an empty [Interface] implementation which does nothing.
|
||||
// Empty is an empty [Inteface] implementation which does nothing.
|
||||
type Empty struct{}
|
||||
|
||||
// type check
|
||||
|
||||
@@ -5,13 +5,25 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghtest"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/rdns"
|
||||
"github.com/AdguardTeam/golibs/netutil"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// fakeRDNSExchanger is a mock [rdns.Exchanger] implementation for tests.
|
||||
type fakeRDNSExchanger struct {
|
||||
OnExchange func(ip netip.Addr) (host string, err error)
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ rdns.Exchanger = (*fakeRDNSExchanger)(nil)
|
||||
|
||||
// Exchange implements [rdns.Exchanger] interface for *fakeRDNSExchanger.
|
||||
func (e *fakeRDNSExchanger) Exchange(ip netip.Addr) (host string, err error) {
|
||||
return e.OnExchange(ip)
|
||||
}
|
||||
|
||||
func TestDefault_Process(t *testing.T) {
|
||||
ip1 := netip.MustParseAddr("1.2.3.4")
|
||||
revAddr1, err := netutil.IPToReversedAddr(ip1.AsSlice())
|
||||
@@ -69,7 +81,7 @@ func TestDefault_Process(t *testing.T) {
|
||||
return "", nil
|
||||
}
|
||||
}
|
||||
exchanger := &aghtest.Exchanger{
|
||||
exchanger := &fakeRDNSExchanger{
|
||||
OnExchange: onExchange,
|
||||
}
|
||||
|
||||
|
||||
@@ -9,9 +9,9 @@ require (
|
||||
github.com/kisielk/errcheck v1.6.3
|
||||
github.com/kyoh86/looppointer v0.2.1
|
||||
github.com/securego/gosec/v2 v2.16.0
|
||||
github.com/uudashr/gocognit v1.0.7
|
||||
github.com/uudashr/gocognit v1.0.6
|
||||
golang.org/x/tools v0.11.0
|
||||
golang.org/x/vuln v1.0.0
|
||||
golang.org/x/vuln v0.2.0
|
||||
// TODO(a.garipov): Return to tagged releases once a new one appears.
|
||||
honnef.co/go/tools v0.5.0-0.dev.0.20230709092525-bc759185c5ee
|
||||
mvdan.cc/gofumpt v0.5.0
|
||||
@@ -27,7 +27,7 @@ require (
|
||||
github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354 // indirect
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // indirect
|
||||
golang.org/x/exp/typeparams v0.0.0-20230713183714-613f0c0eb8a1 // indirect
|
||||
golang.org/x/exp/typeparams v0.0.0-20230711023510-fffb14384f22 // indirect
|
||||
golang.org/x/mod v0.12.0 // indirect
|
||||
golang.org/x/sync v0.3.0 // indirect
|
||||
golang.org/x/sys v0.10.0 // indirect
|
||||
|
||||
@@ -39,8 +39,8 @@ github.com/securego/gosec/v2 v2.16.0 h1:Pi0JKoasQQ3NnoRao/ww/N/XdynIB9NRYYZT5CyO
|
||||
github.com/securego/gosec/v2 v2.16.0/go.mod h1:xvLcVZqUfo4aAQu56TNv7/Ltz6emAOQAEsrZrt7uGlI=
|
||||
github.com/stretchr/testify v1.1.4/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
||||
github.com/uudashr/gocognit v1.0.7 h1:e9aFXgKgUJrQ5+bs61zBigmj7bFJ/5cC6HmMahVzuDo=
|
||||
github.com/uudashr/gocognit v1.0.7/go.mod h1:nAIUuVBnYU7pcninia3BHOvQkpQCeO76Uscky5BOwcY=
|
||||
github.com/uudashr/gocognit v1.0.6 h1:2Cgi6MweCsdB6kpcVQp7EW4U23iBFQWfTXiWlyp842Y=
|
||||
github.com/uudashr/gocognit v1.0.6/go.mod h1:nAIUuVBnYU7pcninia3BHOvQkpQCeO76Uscky5BOwcY=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
@@ -52,8 +52,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug=
|
||||
golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||
golang.org/x/exp/typeparams v0.0.0-20230713183714-613f0c0eb8a1 h1:VXDua8UTGWl3e7L5kCk5Vyt0LA3QpsyRu6XXL7K3v1w=
|
||||
golang.org/x/exp/typeparams v0.0.0-20230713183714-613f0c0eb8a1/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
|
||||
golang.org/x/exp/typeparams v0.0.0-20230711023510-fffb14384f22 h1:e8iSCQYXZ4EB6q3kIfy2fgPFTvDbozqzRe4OuIOyrL4=
|
||||
golang.org/x/exp/typeparams v0.0.0-20230711023510-fffb14384f22/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
|
||||
@@ -98,8 +98,8 @@ golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E
|
||||
golang.org/x/tools v0.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4=
|
||||
golang.org/x/tools v0.11.0 h1:EMCa6U9S2LtZXLAMoWiR/R8dAQFRqbAitmbJ2UKhoi8=
|
||||
golang.org/x/tools v0.11.0/go.mod h1:anzJrxPjNtfgiYQYirP2CPGzGLxrH2u2QBhn6Bf3qY8=
|
||||
golang.org/x/vuln v1.0.0 h1:tYLAU3jD9LQr98Y+3el06lWyGMCnvzw06PIWP3LIy7g=
|
||||
golang.org/x/vuln v1.0.0/go.mod h1:V0eyhHwaAaHrt42J9bgrN6rd12f6GU4T0Lu0ex2wDg4=
|
||||
golang.org/x/vuln v0.2.0 h1:Dlz47lW0pvPHU7tnb10S8vbMn9GnV2B6eyT7Tem5XBI=
|
||||
golang.org/x/vuln v0.2.0/go.mod h1:V0eyhHwaAaHrt42J9bgrN6rd12f6GU4T0Lu0ex2wDg4=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
||||
@@ -48,8 +48,9 @@ func (Empty) Process(_ context.Context, _ netip.Addr) (info *Info, changed bool)
|
||||
|
||||
// Config is the configuration structure for Default.
|
||||
type Config struct {
|
||||
// DialContext is used to create TCP connections to WHOIS servers.
|
||||
DialContext DialContextFunc
|
||||
// DialContext specifies the dial function for creating unencrypted TCP
|
||||
// connections.
|
||||
DialContext func(ctx context.Context, network, addr string) (conn net.Conn, err error)
|
||||
|
||||
// ServerAddr is the address of the WHOIS server.
|
||||
ServerAddr string
|
||||
@@ -77,13 +78,6 @@ type Config struct {
|
||||
Port uint16
|
||||
}
|
||||
|
||||
// DialContextFunc is the semantic alias for dialing functions, such as
|
||||
// [http.Transport.DialContext].
|
||||
//
|
||||
// TODO(a.garipov): Move to aghnet once it stops importing aghtest, because
|
||||
// otherwise there is an import cycle.
|
||||
type DialContextFunc = func(ctx context.Context, network, addr string) (conn net.Conn, err error)
|
||||
|
||||
// Default is the default WHOIS information processor.
|
||||
type Default struct {
|
||||
// cache is the cache containing IP addresses of clients. An active IP
|
||||
@@ -92,8 +86,9 @@ type Default struct {
|
||||
// resolve the same IP.
|
||||
cache gcache.Cache
|
||||
|
||||
// dialContext is used to create TCP connections to WHOIS servers.
|
||||
dialContext DialContextFunc
|
||||
// dialContext connects to a remote server resolving hostname using our own
|
||||
// DNS server and unecrypted TCP connection.
|
||||
dialContext func(ctx context.Context, network, addr string) (conn net.Conn, err error)
|
||||
|
||||
// serverAddr is the address of the WHOIS server.
|
||||
serverAddr string
|
||||
@@ -220,7 +215,7 @@ func (w *Default) query(ctx context.Context, target, serverAddr string) (data []
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_ = conn.SetDeadline(time.Now().Add(w.timeout))
|
||||
_ = conn.SetReadDeadline(time.Now().Add(w.timeout))
|
||||
_, err = io.WriteString(conn, target+"\r\n")
|
||||
if err != nil {
|
||||
// Don't wrap the error since it's informative enough as is.
|
||||
@@ -315,7 +310,7 @@ func (w *Default) requestInfo(
|
||||
|
||||
kv, err := w.queryAll(ctx, ip.String())
|
||||
if err != nil {
|
||||
log.Debug("whois: querying %q: %s", ip, err)
|
||||
log.Debug("whois: quering about %q: %s", ip, err)
|
||||
|
||||
return nil, true
|
||||
}
|
||||
|
||||
@@ -113,14 +113,20 @@ func TestDefault_Process(t *testing.T) {
|
||||
|
||||
return copy(b, tc.data), io.EOF
|
||||
},
|
||||
OnWrite: func(b []byte) (n int, err error) { return len(b), nil },
|
||||
OnClose: func() (err error) { return nil },
|
||||
OnSetDeadline: func(t time.Time) (err error) { return nil },
|
||||
OnWrite: func(b []byte) (n int, err error) {
|
||||
return len(b), nil
|
||||
},
|
||||
OnClose: func() (err error) {
|
||||
return nil
|
||||
},
|
||||
OnSetReadDeadline: func(t time.Time) (err error) {
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
w := whois.New(&whois.Config{
|
||||
Timeout: 5 * time.Second,
|
||||
DialContext: func(_ context.Context, _, _ string) (_ net.Conn, _ error) {
|
||||
DialContext: func(_ context.Context, _, addr string) (_ net.Conn, _ error) {
|
||||
hit = 0
|
||||
|
||||
return fakeConn, nil
|
||||
|
||||
@@ -176,29 +176,16 @@ run_linter gocognit --over 10\
|
||||
./internal/aghchan/\
|
||||
./internal/aghhttp/\
|
||||
./internal/aghio/\
|
||||
./internal/client/\
|
||||
./internal/dhcpsvc\
|
||||
./internal/filtering/hashprefix/\
|
||||
./internal/filtering/rulelist/\
|
||||
./internal/next/\
|
||||
./internal/rdns/\
|
||||
./internal/schedule/\
|
||||
./internal/tools/\
|
||||
./internal/version/\
|
||||
./internal/whois/\
|
||||
./scripts/\
|
||||
;
|
||||
|
||||
# TODO(a.garipov): move these to the group above.
|
||||
run_linter gocognit --over 20 ./internal/aghnet/ ./internal/querylog/
|
||||
run_linter gocognit --over 19 ./internal/dnsforward/ ./internal/home/
|
||||
run_linter gocognit --over 18 ./internal/aghtls/
|
||||
run_linter gocognit --over 17 ./internal/filtering ./internal/filtering/rewrite/
|
||||
run_linter gocognit --over 15 ./internal/aghos/ ./internal/dhcpd/
|
||||
run_linter gocognit --over 14 ./internal/stats/
|
||||
run_linter gocognit --over 12 ./internal/updater/
|
||||
run_linter gocognit --over 11 ./internal/aghtest/
|
||||
|
||||
run_linter ineffassign ./...
|
||||
|
||||
run_linter unparam ./...
|
||||
@@ -224,14 +211,12 @@ run_linter gosec --quiet\
|
||||
./internal/aghnet\
|
||||
./internal/aghos\
|
||||
./internal/aghtest\
|
||||
./internal/client\
|
||||
./internal/dhcpd\
|
||||
./internal/dhcpsvc\
|
||||
./internal/dnsforward\
|
||||
./internal/filtering/hashprefix/\
|
||||
./internal/filtering/rulelist/\
|
||||
./internal/next\
|
||||
./internal/rdns\
|
||||
./internal/schedule\
|
||||
./internal/stats\
|
||||
./internal/tools\
|
||||
|
||||
Reference in New Issue
Block a user