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':
|
- 'attributes':
|
||||||
'description': 'On which Platform does the issue occur?'
|
'description': 'On which Platform does the issue occur?'
|
||||||
'label': 'Platform (OS and CPU architecture)'
|
'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':
|
'options':
|
||||||
- 'Darwin (aka macOS), AMD64 (aka x86_64)'
|
- 'Darwin (aka macOS)/AMD64 (aka x86_64)'
|
||||||
- 'Darwin (aka macOS), ARM64'
|
- 'Darwin (aka macOS)/ARM64'
|
||||||
- 'FreeBSD, AMD64 (aka x86_64)'
|
- 'FreeBSD/386'
|
||||||
- 'FreeBSD, ARM64'
|
- 'FreeBSD/AMD64 (aka x86_64)'
|
||||||
- 'FreeBSD, ARMv5'
|
- 'FreeBSD/ARM64'
|
||||||
- 'FreeBSD, ARMv6'
|
- 'FreeBSD/ARMv5'
|
||||||
- 'FreeBSD, ARMv7'
|
- 'FreeBSD/ARMv6'
|
||||||
- 'FreeBSD, 32-bit Intel (aka 386)'
|
- 'FreeBSD/ARMv7'
|
||||||
- 'Linux, AMD64 (aka x86_64)'
|
- 'Linux/386'
|
||||||
- 'Linux, ARM64'
|
- 'Linux/AMD64 (aka x86_64)'
|
||||||
- 'Linux, ARMv5'
|
- 'Linux/ARM64'
|
||||||
- 'Linux, ARMv6'
|
- 'Linux/ARMv5'
|
||||||
- 'Linux, ARMv7'
|
- 'Linux/ARMv6'
|
||||||
- 'Linux, MIPS LE'
|
- 'Linux/ARMv7'
|
||||||
- 'Linux, MIPS'
|
- 'Linux/MIPS LE'
|
||||||
- 'Linux, MIPS64 LE'
|
- 'Linux/MIPS'
|
||||||
- 'Linux, MIPS64'
|
- 'Linux/MIPS64 LE'
|
||||||
- 'Linux, PPC64 LE'
|
- 'Linux/MIPS64'
|
||||||
- 'Linux, 32-bit Intel (aka 386)'
|
- 'Linux/PPC64 LE'
|
||||||
- 'OpenBSD, AMD64 (aka x86_64)'
|
- 'OpenBSD/AMD64 (aka x86_64)'
|
||||||
- 'OpenBSD, ARM64'
|
- 'OpenBSD/ARM64'
|
||||||
- 'Windows, AMD64 (aka x86_64)'
|
- 'Windows/386'
|
||||||
- 'Windows, ARM64'
|
- 'Windows/AMD64 (aka x86_64)'
|
||||||
- 'Windows, 32-bit Intel (aka 386)'
|
- 'Windows/ARM64'
|
||||||
- 'Custom (please mention in the description)'
|
- 'Custom (please mention in the description)'
|
||||||
'id': 'os'
|
'id': 'os'
|
||||||
'type': 'dropdown'
|
'type': 'dropdown'
|
||||||
@@ -144,10 +142,8 @@
|
|||||||
'type': 'textarea'
|
'type': 'textarea'
|
||||||
'validations':
|
'validations':
|
||||||
'required': false
|
'required': false
|
||||||
# NOTE: GitHub limits the description length to 200 characters. Also, Markdown
|
'description': >
|
||||||
# doesn't work here.
|
Open a bug report. Please do not open bug reports for questions or help
|
||||||
'description': |
|
with configuring clients. If you want to ask for help, use the Discussions
|
||||||
For help, use the Discussions section instead. Write the title in English
|
section.
|
||||||
to make it easier for other people to search for duplicates. (Any language
|
|
||||||
is fine in the body.)
|
|
||||||
'name': 'Bug'
|
'name': 'Bug'
|
||||||
|
|||||||
6
.github/ISSUE_TEMPLATE/feature.yml
vendored
6
.github/ISSUE_TEMPLATE/feature.yml
vendored
@@ -48,11 +48,7 @@
|
|||||||
'type': 'textarea'
|
'type': 'textarea'
|
||||||
'validations':
|
'validations':
|
||||||
'required': false
|
'required': false
|
||||||
# NOTE: GitHub limits the description length to 200 characters. Also, Markdown
|
'description': 'Suggest a feature or an enhancement for AdGuard Home'
|
||||||
# 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.)
|
|
||||||
'labels':
|
'labels':
|
||||||
- 'feature request'
|
- 'feature request'
|
||||||
'name': 'Feature request or enhancement'
|
'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
|
- Occasional client information lookup failures that could lead to the DNS
|
||||||
server getting stuck ([#6006]).
|
server getting stuck ([#6006]).
|
||||||
- `bufio.Scanner: token too long` and other errors when trying to add
|
- `bufio.Scanner: token too long` errors when trying to add filtering-rule lists
|
||||||
filtering-rule lists with lines over 1024 bytes long or containing cosmetic
|
with lines over 1024 bytes long ([#6003]).
|
||||||
rules ([#6003]).
|
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
|
|||||||
7
Makefile
7
Makefile
@@ -130,10 +130,3 @@ openapi-lint: ; cd ./openapi/ && $(YARN) test
|
|||||||
openapi-show: ; cd ./openapi/ && $(YARN) start
|
openapi-show: ; cd ./openapi/ && $(YARN) start
|
||||||
|
|
||||||
txt-lint: ; $(ENV) "$(SHELL)" ./scripts/make/txt-lint.sh
|
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>
|
### <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
|
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
|
click on the “New issue” button.
|
||||||
form carefully and don't forget to start by searching for duplicates.
|
|
||||||
|
|
||||||
[iss]: https://github.com/AdguardTeam/AdGuardHome/issues
|
[iss]: https://github.com/AdguardTeam/AdGuardHome/issues
|
||||||
|
|
||||||
|
|||||||
@@ -444,7 +444,7 @@
|
|||||||
"client_confirm_delete": "Opravdu chcete odstranit klienta \"{{key}}\"?",
|
"client_confirm_delete": "Opravdu chcete odstranit klienta \"{{key}}\"?",
|
||||||
"list_confirm_delete": "Opravdu chcete smazat tento seznam?",
|
"list_confirm_delete": "Opravdu chcete smazat tento seznam?",
|
||||||
"auto_clients_title": "Spuštění klienti",
|
"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_title": "Nastavení přístupu",
|
||||||
"access_desc": "Zde můžete konfigurovat pravidla přístupu pro server DNS AdGuard Home",
|
"access_desc": "Zde můžete konfigurovat pravidla přístupu pro server DNS AdGuard Home",
|
||||||
"access_allowed_title": "Povolení klienti",
|
"access_allowed_title": "Povolení klienti",
|
||||||
|
|||||||
@@ -444,7 +444,7 @@
|
|||||||
"client_confirm_delete": "Sikker på, at du vil slette klient \"{{key}}\"?",
|
"client_confirm_delete": "Sikker på, at du vil slette klient \"{{key}}\"?",
|
||||||
"list_confirm_delete": "Sikker på, at du vil slette denne liste?",
|
"list_confirm_delete": "Sikker på, at du vil slette denne liste?",
|
||||||
"auto_clients_title": "Klienter (runtime)",
|
"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_title": "Adgangsindstillinger",
|
||||||
"access_desc": "Her kan adgangsregler for AdGuard Home DNS-serveren opsættes",
|
"access_desc": "Her kan adgangsregler for AdGuard Home DNS-serveren opsættes",
|
||||||
"access_allowed_title": "Tilladte klienter",
|
"access_allowed_title": "Tilladte klienter",
|
||||||
|
|||||||
@@ -444,7 +444,7 @@
|
|||||||
"client_confirm_delete": "Möchten Sie den Client „{{key}}“ wirklich löschen?",
|
"client_confirm_delete": "Möchten Sie den Client „{{key}}“ wirklich löschen?",
|
||||||
"list_confirm_delete": "Möchten Sie diese Liste wirklich löschen?",
|
"list_confirm_delete": "Möchten Sie diese Liste wirklich löschen?",
|
||||||
"auto_clients_title": "Laufzeit-Clients",
|
"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_title": "Zugriffsrechte",
|
||||||
"access_desc": "Hier können Sie die Zugriffsregeln für den DNS-Server von AdGuard Home konfigurieren",
|
"access_desc": "Hier können Sie die Zugriffsregeln für den DNS-Server von AdGuard Home konfigurieren",
|
||||||
"access_allowed_title": "Zugelassene Clients",
|
"access_allowed_title": "Zugelassene Clients",
|
||||||
|
|||||||
@@ -444,7 +444,7 @@
|
|||||||
"client_confirm_delete": "¿Estás seguro de que deseas eliminar el cliente \"{{key}}\"?",
|
"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?",
|
"list_confirm_delete": "¿Estás seguro de que deseas eliminar esta lista?",
|
||||||
"auto_clients_title": "Clientes activos",
|
"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_title": "Configuración de acceso",
|
||||||
"access_desc": "Aquí puedes configurar las reglas de acceso para el servidor DNS de AdGuard Home",
|
"access_desc": "Aquí puedes configurar las reglas de acceso para el servidor DNS de AdGuard Home",
|
||||||
"access_allowed_title": "Clientes permitidos",
|
"access_allowed_title": "Clientes permitidos",
|
||||||
|
|||||||
@@ -2,21 +2,21 @@
|
|||||||
"client_settings": "Päätelaiteasetukset",
|
"client_settings": "Päätelaiteasetukset",
|
||||||
"example_upstream_reserved": "ylävirta <0>tietyille verkkotunnuksille</0>;",
|
"example_upstream_reserved": "ylävirta <0>tietyille verkkotunnuksille</0>;",
|
||||||
"example_upstream_comment": "kommentti.",
|
"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",
|
"parallel_requests": "Rinnakkaiset pyynnöt",
|
||||||
"load_balancing": "Kuormantasaus",
|
"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": "Bootstrap DNS-palvelimet",
|
||||||
"bootstrap_dns_desc": "Bootstrap DNS-palvelimia käytetään ylävirroiksi määritettyjen DoH/DoT-resolvereiden IP-osoitteiden selvitykseen.",
|
"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_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ää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_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äänteis-DNS-resolvereita: {{ip}}.",
|
"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äänteis-DNS-resolveria.",
|
"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",
|
"local_ptr_placeholder": "Syötä yksi palvelimen osoite per rivi",
|
||||||
"resolve_clients_title": "Käytä päätelaitteiden IP-osoitteille käänteistä selvitystä",
|
"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).",
|
"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äänteis-DNS-resolvereita",
|
"use_private_ptr_resolvers_title": "Käytä yksityisiä käänteisiä 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.",
|
"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",
|
"check_dhcp_servers": "Etsi DHCP-palvelimia",
|
||||||
"save_config": "Tallenna asetukset",
|
"save_config": "Tallenna asetukset",
|
||||||
"enabled_dhcp": "DHCP-palvelin otettiin käyttöön",
|
"enabled_dhcp": "DHCP-palvelin otettiin käyttöön",
|
||||||
@@ -220,7 +220,7 @@
|
|||||||
"example_upstream_tcp_port": "tavallinen DNS (TCP, portti);",
|
"example_upstream_tcp_port": "tavallinen DNS (TCP, portti);",
|
||||||
"example_upstream_tcp_hostname": "tavallinen DNS (TCP, isäntänimi);",
|
"example_upstream_tcp_hostname": "tavallinen DNS (TCP, isäntänimi);",
|
||||||
"all_lists_up_to_date_toast": "Kaikki listat ovat ajan tasalla",
|
"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_ok_toast": "Määritetyt DNS-palvelimet toimivat oikein",
|
||||||
"dns_test_not_ok_toast": "Palvelin \"{{key}}\": Ei voitu käyttää, tarkista oikeinkirjoitus",
|
"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",
|
"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}}\"?",
|
"client_confirm_delete": "Haluatko varmasti poistaa päätelaitteen \"{{key}}\"?",
|
||||||
"list_confirm_delete": "Haluatko varmasti poistaa tämän listan?",
|
"list_confirm_delete": "Haluatko varmasti poistaa tämän listan?",
|
||||||
"auto_clients_title": "Määrittämättömät päätelaitteet",
|
"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_title": "Käytön asetukset",
|
||||||
"access_desc": "Tässä voidaan määrittää AdGuard Homen DNS-palvelimen käyttöoikeussääntöjä.",
|
"access_desc": "Tässä voidaan määrittää AdGuard Homen DNS-palvelimen käyttöoikeussääntöjä.",
|
||||||
"access_allowed_title": "Sallitut päätelaitteet",
|
"access_allowed_title": "Sallitut päätelaitteet",
|
||||||
@@ -623,7 +623,7 @@
|
|||||||
"enter_cache_size": "Syötä välimuistin koko (tavuina)",
|
"enter_cache_size": "Syötä välimuistin koko (tavuina)",
|
||||||
"enter_cache_ttl_min_override": "Syötä vähimmäis-TTL (sekunteina)",
|
"enter_cache_ttl_min_override": "Syötä vähimmäis-TTL (sekunteina)",
|
||||||
"enter_cache_ttl_max_override": "Syötä enimmä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).",
|
"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",
|
"ttl_cache_validation": "Välimuistin vähimmäiselinajan on oltava pienempi tai sama kuin enimmäiselinajan",
|
||||||
"cache_optimistic": "Optimistinen välimuisti",
|
"cache_optimistic": "Optimistinen välimuisti",
|
||||||
|
|||||||
@@ -444,7 +444,7 @@
|
|||||||
"client_confirm_delete": "Voulez-vous vraiment supprimer le client « {{key}} » ?",
|
"client_confirm_delete": "Voulez-vous vraiment supprimer le client « {{key}} » ?",
|
||||||
"list_confirm_delete": "Voulez-vous vraiment supprimer cette liste ?",
|
"list_confirm_delete": "Voulez-vous vraiment supprimer cette liste ?",
|
||||||
"auto_clients_title": "Clients d'exécution",
|
"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_title": "Paramètres d'accès",
|
||||||
"access_desc": "Ici vous pouvez configurer les règles d'accès au serveur DNS AdGuard Home",
|
"access_desc": "Ici vous pouvez configurer les règles d'accès au serveur DNS AdGuard Home",
|
||||||
"access_allowed_title": "Clients autorisés",
|
"access_allowed_title": "Clients autorisés",
|
||||||
|
|||||||
@@ -444,7 +444,7 @@
|
|||||||
"client_confirm_delete": "Sei sicuro di voler eliminare il client \"{{key}}\"?",
|
"client_confirm_delete": "Sei sicuro di voler eliminare il client \"{{key}}\"?",
|
||||||
"list_confirm_delete": "Sei sicuro di voler eliminare questo elenco?",
|
"list_confirm_delete": "Sei sicuro di voler eliminare questo elenco?",
|
||||||
"auto_clients_title": "Client in tempo reale",
|
"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_title": "Impostazioni di accesso",
|
||||||
"access_desc": "Qui puoi configurare le regole d'accesso per il server DNS di AdGuard Home",
|
"access_desc": "Qui puoi configurare le regole d'accesso per il server DNS di AdGuard Home",
|
||||||
"access_allowed_title": "Client permessi",
|
"access_allowed_title": "Client permessi",
|
||||||
|
|||||||
@@ -444,7 +444,7 @@
|
|||||||
"client_confirm_delete": "クライアント \"{{key}}\" を削除してもよろしいですか?",
|
"client_confirm_delete": "クライアント \"{{key}}\" を削除してもよろしいですか?",
|
||||||
"list_confirm_delete": "このリストを削除してもよろしいですか?",
|
"list_confirm_delete": "このリストを削除してもよろしいですか?",
|
||||||
"auto_clients_title": "ランタイムクライアント",
|
"auto_clients_title": "ランタイムクライアント",
|
||||||
"auto_clients_desc": "AdGuard Home を使用している、または使用する可能性のあるデバイスの IP アドレスに関する情報です。この情報は、hosts ファイル、リバース DNS など、複数の情報源から収集されます。",
|
"auto_clients_desc": "永続的クライアントのリストに未登録で、AdGuard Homeを使用する場合があるデバイスのリスト。",
|
||||||
"access_title": "アクセス設定",
|
"access_title": "アクセス設定",
|
||||||
"access_desc": "こちらでは、AdGuard Home DNSサーバーのアクセスルールを設定できます。",
|
"access_desc": "こちらでは、AdGuard Home DNSサーバーのアクセスルールを設定できます。",
|
||||||
"access_allowed_title": "許可されたクライアント",
|
"access_allowed_title": "許可されたクライアント",
|
||||||
|
|||||||
@@ -444,7 +444,7 @@
|
|||||||
"client_confirm_delete": "Ben je zeker dat je deze gebruiker \"{{key}}\" wilt verwijderen?",
|
"client_confirm_delete": "Ben je zeker dat je deze gebruiker \"{{key}}\" wilt verwijderen?",
|
||||||
"list_confirm_delete": "Ben je zeker om deze lijst te verwijderen?",
|
"list_confirm_delete": "Ben je zeker om deze lijst te verwijderen?",
|
||||||
"auto_clients_title": "Runtime-clients",
|
"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_title": "Toegangs instellingen",
|
||||||
"access_desc": "Hier kan je toegangsregels voor de AdGuard Home DNS-server instellen",
|
"access_desc": "Hier kan je toegangsregels voor de AdGuard Home DNS-server instellen",
|
||||||
"access_allowed_title": "Toegestane gebruikers",
|
"access_allowed_title": "Toegestane gebruikers",
|
||||||
|
|||||||
@@ -444,7 +444,7 @@
|
|||||||
"client_confirm_delete": "Czy na pewno chcesz usunąć klienta \"{{key}}\"?",
|
"client_confirm_delete": "Czy na pewno chcesz usunąć klienta \"{{key}}\"?",
|
||||||
"list_confirm_delete": "Czy na pewno chcesz usunąć tę listę?",
|
"list_confirm_delete": "Czy na pewno chcesz usunąć tę listę?",
|
||||||
"auto_clients_title": "Uruchomieni klienci",
|
"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_title": "Ustawienia dostępu",
|
||||||
"access_desc": "Tutaj możesz skonfigurować reguły dostępu dla serwera DNS AdGuard Home",
|
"access_desc": "Tutaj możesz skonfigurować reguły dostępu dla serwera DNS AdGuard Home",
|
||||||
"access_allowed_title": "Dozwoleni klienci",
|
"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_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_title": "Inne implementacje",
|
||||||
"setup_dns_privacy_other_1": "Sam AdGuard Home może być bezpiecznym klientem DNS na dowolnej platformie.",
|
"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_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_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>.",
|
"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}}\"?",
|
"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?",
|
"list_confirm_delete": "Você tem certeza de que deseja excluir essa lista?",
|
||||||
"auto_clients_title": "Clientes ativos",
|
"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_title": "Configurações de acessos",
|
||||||
"access_desc": "Aqui você pode configurar as regras de acesso para o servidores de DNS do AdGuard Home",
|
"access_desc": "Aqui você pode configurar as regras de acesso para o servidores de DNS do AdGuard Home",
|
||||||
"access_allowed_title": "Clientes permitidos",
|
"access_allowed_title": "Clientes permitidos",
|
||||||
|
|||||||
@@ -444,7 +444,7 @@
|
|||||||
"client_confirm_delete": "Tem a certeza de que deseja excluir o cliente \"{{key}}\"?",
|
"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?",
|
"list_confirm_delete": "Você tem certeza de que deseja excluir essa lista?",
|
||||||
"auto_clients_title": "Clientes ativos",
|
"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_title": "Definições de acesso",
|
||||||
"access_desc": "Aqui pode configurar as regras de acesso para o servidores de DNS do AdGuard Home",
|
"access_desc": "Aqui pode configurar as regras de acesso para o servidores de DNS do AdGuard Home",
|
||||||
"access_allowed_title": "Clientes permitidos",
|
"access_allowed_title": "Clientes permitidos",
|
||||||
|
|||||||
@@ -135,7 +135,7 @@
|
|||||||
"number_of_dns_query_to_safe_search": "Количество запросов DNS для поисковых систем, для которых был применён Безопасный поиск",
|
"number_of_dns_query_to_safe_search": "Количество запросов DNS для поисковых систем, для которых был применён Безопасный поиск",
|
||||||
"average_processing_time": "Среднее время обработки запроса",
|
"average_processing_time": "Среднее время обработки запроса",
|
||||||
"average_processing_time_hint": "Среднее время для обработки запроса DNS в миллисекундах",
|
"average_processing_time_hint": "Среднее время для обработки запроса DNS в миллисекундах",
|
||||||
"block_domain_use_filters_and_hosts": "Блокировать домены с использованием фильтров и файлов hosts",
|
"block_domain_use_filters_and_hosts": "Блокировать домены с использованием фильтров и файлов хостов",
|
||||||
"filters_block_toggle_hint": "Вы можете настроить правила блокировки в <a>«Фильтрах»</a>.",
|
"filters_block_toggle_hint": "Вы можете настроить правила блокировки в <a>«Фильтрах»</a>.",
|
||||||
"use_adguard_browsing_sec": "Включить Безопасную навигацию AdGuard",
|
"use_adguard_browsing_sec": "Включить Безопасную навигацию AdGuard",
|
||||||
"use_adguard_browsing_sec_hint": "AdGuard Home проверит, включён ли домен в веб-службу безопасности браузера. Он будет использовать API, чтобы выполнить проверку: на сервер отправляется только короткий префикс имени домена SHA256.",
|
"use_adguard_browsing_sec_hint": "AdGuard Home проверит, включён ли домен в веб-службу безопасности браузера. Он будет использовать API, чтобы выполнить проверку: на сервер отправляется только короткий префикс имени домена SHA256.",
|
||||||
@@ -296,7 +296,7 @@
|
|||||||
"rate_limit_desc": "Ограничение на количество запросов в секунду для каждого клиента (0 — неограниченно).",
|
"rate_limit_desc": "Ограничение на количество запросов в секунду для каждого клиента (0 — неограниченно).",
|
||||||
"blocking_ipv4_desc": "IP-адрес, возвращаемый при блокировке A-запроса",
|
"blocking_ipv4_desc": "IP-адрес, возвращаемый при блокировке A-запроса",
|
||||||
"blocking_ipv6_desc": "IP-адрес, возвращаемый при блокировке AAAA-запроса",
|
"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_refused": "REFUSED: Отвечает с кодом REFUSED",
|
||||||
"blocking_mode_nxdomain": "NXDOMAIN: Отвечает с кодом NXDOMAIN\n",
|
"blocking_mode_nxdomain": "NXDOMAIN: Отвечает с кодом NXDOMAIN\n",
|
||||||
"blocking_mode_null_ip": "Нулевой IP: Отвечает с нулевым IP-адресом (0.0.0.0 для A; :: для AAAA)",
|
"blocking_mode_null_ip": "Нулевой IP: Отвечает с нулевым IP-адресом (0.0.0.0 для A; :: для AAAA)",
|
||||||
@@ -444,7 +444,7 @@
|
|||||||
"client_confirm_delete": "Вы уверены, что хотите удалить клиента «{{key}}»?",
|
"client_confirm_delete": "Вы уверены, что хотите удалить клиента «{{key}}»?",
|
||||||
"list_confirm_delete": "Вы уверены, что хотите удалить этот список?",
|
"list_confirm_delete": "Вы уверены, что хотите удалить этот список?",
|
||||||
"auto_clients_title": "Клиенты (runtime)",
|
"auto_clients_title": "Клиенты (runtime)",
|
||||||
"auto_clients_desc": "Информация об IP-адресах устройств, которые используют или могут использовать AdGuard Home. Эта информация собирается из нескольких источников, включая файлы hosts, обратный DNS и так далее.",
|
"auto_clients_desc": "Несохранённые клиенты, которые могут пользоваться AdGuard Home",
|
||||||
"access_title": "Настройки доступа",
|
"access_title": "Настройки доступа",
|
||||||
"access_desc": "Здесь вы можете настроить правила доступа к DNS-серверу AdGuard Home",
|
"access_desc": "Здесь вы можете настроить правила доступа к DNS-серверу AdGuard Home",
|
||||||
"access_allowed_title": "Разрешённые клиенты",
|
"access_allowed_title": "Разрешённые клиенты",
|
||||||
|
|||||||
@@ -387,7 +387,7 @@
|
|||||||
"encryption_key": "Súkromný kľúč",
|
"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_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": "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_valid": "Certifikačný reťazec je platný",
|
||||||
"encryption_chain_invalid": "Certifikačný reťazec je neplatný",
|
"encryption_chain_invalid": "Certifikačný reťazec je neplatný",
|
||||||
"encryption_key_valid": "Toto je platný {{type}} súkromný kľúč",
|
"encryption_key_valid": "Toto je platný {{type}} súkromný kľúč",
|
||||||
@@ -497,7 +497,7 @@
|
|||||||
"blocked_services": "Blokované služby",
|
"blocked_services": "Blokované služby",
|
||||||
"blocked_services_desc": "Umožňuje rýchlo blokovať populárne stránky a 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_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",
|
"blocked_service": "Blokované služby",
|
||||||
"block_all": "Blokovať všetko",
|
"block_all": "Blokovať všetko",
|
||||||
"unblock_all": "Odblokovať všetko",
|
"unblock_all": "Odblokovať všetko",
|
||||||
@@ -554,7 +554,7 @@
|
|||||||
"whois": "WHOIS",
|
"whois": "WHOIS",
|
||||||
"filtering_rules_learn_more": "<0>Dozvedieť sa viac</0> o tvorbe vlastných zoznamov hostiteľov.",
|
"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_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",
|
"try_again": "Skúste znova",
|
||||||
"domain_desc": "Zadajte meno domény alebo zástupný znak, ktorý chcete prepísať.",
|
"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.",
|
"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_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.",
|
"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_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",
|
"form_select_tags": "Zvoľte tagy klienta",
|
||||||
"check_title": "Skontrolujte filtráciu",
|
"check_title": "Skontrolujte filtráciu",
|
||||||
"check_desc": "Skontrolujte, či je názov hostiteľa filtrovaný.",
|
"check_desc": "Skontrolujte, či je názov hostiteľa filtrovaný.",
|
||||||
@@ -608,7 +608,7 @@
|
|||||||
"show_whitelisted_responses": "Obsiahnuté v bielej listine",
|
"show_whitelisted_responses": "Obsiahnuté v bielej listine",
|
||||||
"show_processed_responses": "Spracované",
|
"show_processed_responses": "Spracované",
|
||||||
"blocked_safebrowsing": "Zablokované modulom Bezpečné prehliadanie",
|
"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",
|
"blocked_threats": "Zablokované hrozby",
|
||||||
"allowed": "Povolené",
|
"allowed": "Povolené",
|
||||||
"filtered": "Filtrované",
|
"filtered": "Filtrované",
|
||||||
|
|||||||
@@ -444,7 +444,7 @@
|
|||||||
"client_confirm_delete": "Ali ste prepričani, da želite izbrisati odjemalca \"{{key}}\"?",
|
"client_confirm_delete": "Ali ste prepričani, da želite izbrisati odjemalca \"{{key}}\"?",
|
||||||
"list_confirm_delete": "Ali ste prepričani, da želite izbrisati ta seznam?",
|
"list_confirm_delete": "Ali ste prepričani, da želite izbrisati ta seznam?",
|
||||||
"auto_clients_title": "Odjemalci izvajanja",
|
"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_title": "Nastavitve dostopa",
|
||||||
"access_desc": "Tukaj lahko nastavite pravila dostopa strežnika DNS AdGuard Home",
|
"access_desc": "Tukaj lahko nastavite pravila dostopa strežnika DNS AdGuard Home",
|
||||||
"access_allowed_title": "Dovoljeni odjemalci",
|
"access_allowed_title": "Dovoljeni odjemalci",
|
||||||
|
|||||||
@@ -444,7 +444,7 @@
|
|||||||
"client_confirm_delete": "\"{{key}}\" istemcisini silmek istediğinizden emin misiniz?",
|
"client_confirm_delete": "\"{{key}}\" istemcisini silmek istediğinizden emin misiniz?",
|
||||||
"list_confirm_delete": "Bu listeyi silmek istediğinizden emin misiniz?",
|
"list_confirm_delete": "Bu listeyi silmek istediğinizden emin misiniz?",
|
||||||
"auto_clients_title": "Çalışma zamanı istemcileri",
|
"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_title": "Erişim ayarları",
|
||||||
"access_desc": "AdGuard Home DNS sunucusu için erişim kurallarını buradan yapılandırabilirsiniz",
|
"access_desc": "AdGuard Home DNS sunucusu için erişim kurallarını buradan yapılandırabilirsiniz",
|
||||||
"access_allowed_title": "İzin verilen istemciler",
|
"access_allowed_title": "İzin verilen istemciler",
|
||||||
|
|||||||
@@ -444,7 +444,7 @@
|
|||||||
"client_confirm_delete": "您确定要删除客户端 \"{{key}}\"?",
|
"client_confirm_delete": "您确定要删除客户端 \"{{key}}\"?",
|
||||||
"list_confirm_delete": "您确定要删除此列表吗?",
|
"list_confirm_delete": "您确定要删除此列表吗?",
|
||||||
"auto_clients_title": "客户端(运行时间)",
|
"auto_clients_title": "客户端(运行时间)",
|
||||||
"auto_clients_desc": "有关正在使用或可能使用 AdGuard Home 的设备的 IP 地址的信息。此信息是从多个来源收集的,包括 hosts 文件、反向 DNS 等。",
|
"auto_clients_desc": "不在可继续使用 AdGuard Home 的持久客户端列表中的设备。",
|
||||||
"access_title": "访问设置",
|
"access_title": "访问设置",
|
||||||
"access_desc": "您可以在此处配置 AdGuard Home 的 DNS 服务器的访问规则",
|
"access_desc": "您可以在此处配置 AdGuard Home 的 DNS 服务器的访问规则",
|
||||||
"access_allowed_title": "允许的客户端",
|
"access_allowed_title": "允许的客户端",
|
||||||
|
|||||||
@@ -164,7 +164,7 @@
|
|||||||
"disabled_parental_toast": "已停用家長監護",
|
"disabled_parental_toast": "已停用家長監護",
|
||||||
"enabled_parental_toast": "已啟用家長監護",
|
"enabled_parental_toast": "已啟用家長監護",
|
||||||
"disabled_safe_search_toast": "已停用安全搜尋",
|
"disabled_safe_search_toast": "已停用安全搜尋",
|
||||||
"updated_save_search_toast": "已更新安全搜尋設定",
|
"enabled_save_search_toast": "已啟用安全搜尋",
|
||||||
"enabled_table_header": "啟用",
|
"enabled_table_header": "啟用",
|
||||||
"name_table_header": "名稱",
|
"name_table_header": "名稱",
|
||||||
"list_url_table_header": "清單 URL 網址",
|
"list_url_table_header": "清單 URL 網址",
|
||||||
@@ -468,7 +468,6 @@
|
|||||||
"rewrite_added": "「{{key}}」的 DNS 覆寫新增成功",
|
"rewrite_added": "「{{key}}」的 DNS 覆寫新增成功",
|
||||||
"rewrite_deleted": "「{{key}}」的 DNS 覆寫刪除成功",
|
"rewrite_deleted": "「{{key}}」的 DNS 覆寫刪除成功",
|
||||||
"rewrite_add": "新增 DNS 覆寫",
|
"rewrite_add": "新增 DNS 覆寫",
|
||||||
"rewrite_edit": "編輯 DNS 覆寫",
|
|
||||||
"rewrite_not_found": "找不到 DNS 覆寫",
|
"rewrite_not_found": "找不到 DNS 覆寫",
|
||||||
"rewrite_confirm_delete": "您確定要刪除 \"{{key}}\" 的 DNS 覆寫?",
|
"rewrite_confirm_delete": "您確定要刪除 \"{{key}}\" 的 DNS 覆寫?",
|
||||||
"rewrite_desc": "提供簡單的方式對特定網域自訂 DNS 回應。",
|
"rewrite_desc": "提供簡單的方式對特定網域自訂 DNS 回應。",
|
||||||
@@ -502,7 +501,6 @@
|
|||||||
"interval_days": "{{count}} 天",
|
"interval_days": "{{count}} 天",
|
||||||
"interval_days_plural": "{{count}} 天",
|
"interval_days_plural": "{{count}} 天",
|
||||||
"domain": "網域",
|
"domain": "網域",
|
||||||
"ecs": "EDNS 子網",
|
|
||||||
"punycode": "Punycode",
|
"punycode": "Punycode",
|
||||||
"answer": "回應",
|
"answer": "回應",
|
||||||
"filter_added_successfully": "已成功新增清單",
|
"filter_added_successfully": "已成功新增清單",
|
||||||
@@ -516,8 +514,6 @@
|
|||||||
"statistics_retention_confirm": "您確定要更改統計資料保存時間嗎?如果您縮短期限部分資料可能將會遺失",
|
"statistics_retention_confirm": "您確定要更改統計資料保存時間嗎?如果您縮短期限部分資料可能將會遺失",
|
||||||
"statistics_cleared": "已清除統計資料",
|
"statistics_cleared": "已清除統計資料",
|
||||||
"statistics_enable": "啟用統計數據",
|
"statistics_enable": "啟用統計數據",
|
||||||
"ignore_domains": "已忽略網域(每行一個)",
|
|
||||||
"ignore_domains_title": "已忽略網域",
|
|
||||||
"interval_hours": "{{count}} 小時",
|
"interval_hours": "{{count}} 小時",
|
||||||
"interval_hours_plural": "{{count}} 小時",
|
"interval_hours_plural": "{{count}} 小時",
|
||||||
"filters_configuration": "過濾器設定",
|
"filters_configuration": "過濾器設定",
|
||||||
@@ -630,7 +626,6 @@
|
|||||||
"safe_browsing": "安全瀏覽",
|
"safe_browsing": "安全瀏覽",
|
||||||
"served_from_cache": "{{value}} <i>(由快取回應)</i>",
|
"served_from_cache": "{{value}} <i>(由快取回應)</i>",
|
||||||
"form_error_password_length": "密碼必須至少 {{value}} 個字元長度",
|
"form_error_password_length": "密碼必須至少 {{value}} 個字元長度",
|
||||||
"make_static": "新增為靜態",
|
|
||||||
"theme_dark_desc": "深色主題",
|
"theme_dark_desc": "深色主題",
|
||||||
"theme_light_desc": "淺色主題",
|
"theme_light_desc": "淺色主題",
|
||||||
"disable_for_seconds": "{{count}} 秒",
|
"disable_for_seconds": "{{count}} 秒",
|
||||||
|
|||||||
@@ -444,7 +444,7 @@
|
|||||||
"client_confirm_delete": "您確定您想要刪除用戶端 \"{{key}}\" 嗎?",
|
"client_confirm_delete": "您確定您想要刪除用戶端 \"{{key}}\" 嗎?",
|
||||||
"list_confirm_delete": "您確定您想要刪除該清單嗎?",
|
"list_confirm_delete": "您確定您想要刪除該清單嗎?",
|
||||||
"auto_clients_title": "執行時期用戶端",
|
"auto_clients_title": "執行時期用戶端",
|
||||||
"auto_clients_desc": "AdGuard Home 使用或可能使用的裝置的 IP 地址資訊。這些資訊來自多個來源,包括主機檔案、反向 DNS 等。",
|
"auto_clients_desc": "未於可能仍然使用 AdGuard Home 的持續性用戶端之清單上的裝置",
|
||||||
"access_title": "存取設定",
|
"access_title": "存取設定",
|
||||||
"access_desc": "於此您可配置用於 AdGuard Home DNS 伺服器之存取規則",
|
"access_desc": "於此您可配置用於 AdGuard Home DNS 伺服器之存取規則",
|
||||||
"access_allowed_title": "已允許的用戶端",
|
"access_allowed_title": "已允許的用戶端",
|
||||||
|
|||||||
@@ -64,6 +64,12 @@ export default {
|
|||||||
"homepage": "https://github.com/MasterKia/PersianBlocker",
|
"homepage": "https://github.com/MasterKia/PersianBlocker",
|
||||||
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_19.txt"
|
"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": {
|
"KOR_list_kr": {
|
||||||
"name": "KOR: List-KR DNS",
|
"name": "KOR: List-KR DNS",
|
||||||
"categoryId": "regional",
|
"categoryId": "regional",
|
||||||
@@ -160,20 +166,14 @@ export default {
|
|||||||
"homepage": "https://github.com/DandelionSprout/adfilt",
|
"homepage": "https://github.com/DandelionSprout/adfilt",
|
||||||
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_12.txt"
|
"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": {
|
"dandelion_sprouts_game_console_adblock_list": {
|
||||||
"name": "Dandelion Sprout's Game Console Adblock List",
|
"name": "Dandelion Sprout's Game Console Adblock List",
|
||||||
"categoryId": "other",
|
"categoryId": "other",
|
||||||
"homepage": "https://github.com/DandelionSprout/adfilt",
|
"homepage": "https://github.com/DandelionSprout/adfilt",
|
||||||
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_6.txt"
|
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_6.txt"
|
||||||
},
|
},
|
||||||
"hagezi_multinormal": {
|
"hagezi_personal": {
|
||||||
"name": "HaGeZi Multi NORMAL",
|
"name": "HaGeZi Personal Black \u0026 White",
|
||||||
"categoryId": "general",
|
"categoryId": "general",
|
||||||
"homepage": "https://github.com/hagezi/dns-blocklists",
|
"homepage": "https://github.com/hagezi/dns-blocklists",
|
||||||
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_34.txt"
|
"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": {
|
"categories": {
|
||||||
"0": "audio_video_player",
|
"0": "audio_video_player",
|
||||||
"1": "comments",
|
"1": "comments",
|
||||||
@@ -14088,11 +14088,10 @@
|
|||||||
"companyId": "qihoo_360_technology"
|
"companyId": "qihoo_360_technology"
|
||||||
},
|
},
|
||||||
"qq.com": {
|
"qq.com": {
|
||||||
"name": "QQ International",
|
"name": "qq.com",
|
||||||
"categoryId": 2,
|
"categoryId": 8,
|
||||||
"url": "https://www.qq.com/",
|
"url": "http://www.qq.com/",
|
||||||
"companyId": "tencent",
|
"companyId": "qq.com"
|
||||||
"source": "AdGuard"
|
|
||||||
},
|
},
|
||||||
"qrius": {
|
"qrius": {
|
||||||
"name": "Qrius",
|
"name": "Qrius",
|
||||||
@@ -20509,7 +20508,6 @@
|
|||||||
"amazon.com.au": "amazon",
|
"amazon.com.au": "amazon",
|
||||||
"amazon-corp.com": "amazon",
|
"amazon-corp.com": "amazon",
|
||||||
"a2z.com": "amazon",
|
"a2z.com": "amazon",
|
||||||
"firetvcaptiveportal.com": "amazon",
|
|
||||||
"amazon-adsystem.com": "amazon_adsystem",
|
"amazon-adsystem.com": "amazon_adsystem",
|
||||||
"serving-sys.com": "amazon_adsystem",
|
"serving-sys.com": "amazon_adsystem",
|
||||||
"sizmek.com": "amazon_adsystem",
|
"sizmek.com": "amazon_adsystem",
|
||||||
@@ -22999,7 +22997,6 @@
|
|||||||
"mrskincash.com": "mrskincash",
|
"mrskincash.com": "mrskincash",
|
||||||
"e-msedge.net": "msedge",
|
"e-msedge.net": "msedge",
|
||||||
"l-msedge.net": "msedge",
|
"l-msedge.net": "msedge",
|
||||||
"s-msedge.net": "msedge",
|
|
||||||
"msn.com": "msn",
|
"msn.com": "msn",
|
||||||
"s-msn.com": "msn",
|
"s-msn.com": "msn",
|
||||||
"musculahq.appspot.com": "muscula",
|
"musculahq.appspot.com": "muscula",
|
||||||
|
|||||||
5
go.mod
5
go.mod
@@ -3,7 +3,8 @@ module github.com/AdguardTeam/AdGuardHome
|
|||||||
go 1.19
|
go 1.19
|
||||||
|
|
||||||
require (
|
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/golibs v0.13.4
|
||||||
github.com/AdguardTeam/urlfilter v0.16.1
|
github.com/AdguardTeam/urlfilter v0.16.1
|
||||||
github.com/NYTimes/gziphandler v1.1.1
|
github.com/NYTimes/gziphandler v1.1.1
|
||||||
@@ -27,7 +28,7 @@ require (
|
|||||||
// own code for that. Perhaps, use gopacket.
|
// own code for that. Perhaps, use gopacket.
|
||||||
github.com/mdlayher/raw v0.1.0
|
github.com/mdlayher/raw v0.1.0
|
||||||
github.com/miekg/dns v1.1.55
|
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/stretchr/testify v1.8.4
|
||||||
github.com/ti-mo/netfilter v0.5.0
|
github.com/ti-mo/netfilter v0.5.0
|
||||||
go.etcd.io/bbolt v1.3.7
|
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.50.3-0.20230628054307-31e374065768 h1:5Ia6wA+tqAlTyzuaOVGSlHmb0osLWXeJUs3NxCuC4gA=
|
||||||
github.com/AdguardTeam/dnsproxy v0.52.0/go.mod h1:Jo2zeRe97Rxt3yikXc+fn0LdLtqCj0Xlyh1PNBj6bpM=
|
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.4.0/go.mod h1:skKsDKIBB7kkFflLJBpfGX+G8QFTx0WKUzB6TIgtUj4=
|
||||||
github.com/AdguardTeam/golibs v0.10.4/go.mod h1:rSfQRGHIdgfxriDDNgNJ7HmE5zRoURq8R+VdR81Zuzw=
|
github.com/AdguardTeam/golibs v0.10.4/go.mod h1:rSfQRGHIdgfxriDDNgNJ7HmE5zRoURq8R+VdR81Zuzw=
|
||||||
github.com/AdguardTeam/golibs v0.13.4 h1:ACTwIR1pEENBijHcEWtiMbSh4wWQOlIHRxmUB8oBHf8=
|
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-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 h1:WLOPx6OY/hxtTxKV1Zrq20FtXtDEkeY00CGQm8GEa3E=
|
||||||
github.com/quic-go/qtls-go1-20 v0.2.2/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM=
|
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.35.1 h1:b0kzj6b/cQAf05cT0CkQubHM31wiA+xH3IBkxP62poo=
|
||||||
github.com/quic-go/quic-go v0.36.1/go.mod h1:zPetvwDlILVxt15n3hr3Gf/I3mDf7LpLKPhR4Ez0AZQ=
|
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 h1:nKct+uP0TV8DjjNiHanKf8SAuub+GNsbrOtM9Nl9biA=
|
||||||
github.com/shirou/gopsutil/v3 v3.21.8/go.mod h1:YWp/H8Qs5fVmf17v7JNZzA0mPJ+mS2e9JdiUF9LlKzQ=
|
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=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
|||||||
@@ -4,13 +4,9 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"io"
|
"io"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"net/netip"
|
|
||||||
|
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/aghos"
|
"github.com/AdguardTeam/AdGuardHome/internal/aghos"
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/client"
|
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/next/agh"
|
"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/AdguardTeam/dnsproxy/upstream"
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
)
|
)
|
||||||
@@ -139,59 +135,6 @@ func (s *ServiceWithConfig[ConfigType]) Config() (c ConfigType) {
|
|||||||
return s.OnConfig()
|
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
|
// Module dnsproxy
|
||||||
|
|
||||||
// Package upstream
|
// 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/aghalg"
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
|
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/aghtls"
|
"github.com/AdguardTeam/AdGuardHome/internal/aghtls"
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/client"
|
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
||||||
"github.com/AdguardTeam/dnsproxy/proxy"
|
"github.com/AdguardTeam/dnsproxy/proxy"
|
||||||
"github.com/AdguardTeam/golibs/errors"
|
"github.com/AdguardTeam/golibs/errors"
|
||||||
@@ -272,12 +271,9 @@ type ServerConfig struct {
|
|||||||
TCPListenAddrs []*net.TCPAddr // TCP listen address
|
TCPListenAddrs []*net.TCPAddr // TCP listen address
|
||||||
UpstreamConfig *proxy.UpstreamConfig // Upstream DNS servers config
|
UpstreamConfig *proxy.UpstreamConfig // Upstream DNS servers config
|
||||||
|
|
||||||
// AddrProcConf defines the configuration for the client IP processor.
|
// ClientIPs, if not nil, is used to send clients' IP addresses to other
|
||||||
// If nil, [client.EmptyAddrProc] is used.
|
// parts of AdGuard Home that may use it for resolving rDNS, WHOIS, etc.
|
||||||
//
|
ClientIPs chan netip.Addr
|
||||||
// TODO(a.garipov): The use of [client.EmptyAddrProc] is a crutch for tests.
|
|
||||||
// Remove that.
|
|
||||||
AddrProcConf *client.DefaultAddrProcConfig
|
|
||||||
|
|
||||||
FilteringConfig
|
FilteringConfig
|
||||||
TLSConfig
|
TLSConfig
|
||||||
@@ -305,6 +301,9 @@ type ServerConfig struct {
|
|||||||
// DNS64Prefixes is a slice of NAT64 prefixes to be used for DNS64.
|
// DNS64Prefixes is a slice of NAT64 prefixes to be used for DNS64.
|
||||||
DNS64Prefixes []netip.Prefix
|
DNS64Prefixes []netip.Prefix
|
||||||
|
|
||||||
|
// ResolveClients signals if the RDNS should resolve clients' addresses.
|
||||||
|
ResolveClients bool
|
||||||
|
|
||||||
// UsePrivateRDNS defines if the PTR requests for unknown addresses from
|
// UsePrivateRDNS defines if the PTR requests for unknown addresses from
|
||||||
// locally-served networks should be resolved via private PTR resolvers.
|
// locally-served networks should be resolved via private PTR resolvers.
|
||||||
UsePrivateRDNS bool
|
UsePrivateRDNS bool
|
||||||
@@ -344,7 +343,6 @@ func (s *Server) createProxyConfig() (conf proxy.Config, err error) {
|
|||||||
UpstreamConfig: srvConf.UpstreamConfig,
|
UpstreamConfig: srvConf.UpstreamConfig,
|
||||||
BeforeRequestHandler: s.beforeRequestHandler,
|
BeforeRequestHandler: s.beforeRequestHandler,
|
||||||
RequestHandler: s.handleDNSRequest,
|
RequestHandler: s.handleDNSRequest,
|
||||||
HTTPSServerName: aghhttp.UserAgent(),
|
|
||||||
EnableEDNSClientSubnet: srvConf.EDNSClientSubnet.Enabled,
|
EnableEDNSClientSubnet: srvConf.EDNSClientSubnet.Enabled,
|
||||||
MaxGoroutines: int(srvConf.MaxGoroutines),
|
MaxGoroutines: int(srvConf.MaxGoroutines),
|
||||||
UseDNS64: srvConf.UseDNS64,
|
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/aghalg"
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/client"
|
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/dhcpd"
|
"github.com/AdguardTeam/AdGuardHome/internal/dhcpd"
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/querylog"
|
"github.com/AdguardTeam/AdGuardHome/internal/querylog"
|
||||||
@@ -100,17 +99,12 @@ type Server struct {
|
|||||||
// must be a valid domain name plus dots on each side.
|
// must be a valid domain name plus dots on each side.
|
||||||
localDomainSuffix string
|
localDomainSuffix string
|
||||||
|
|
||||||
ipset ipsetCtx
|
// ClientIPs, if not nil, is used to send clients' IP addresses to other
|
||||||
privateNets netutil.SubnetSet
|
// 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,
|
ipset ipsetCtx
|
||||||
// WHOIS, etc.
|
privateNets netutil.SubnetSet
|
||||||
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.
|
|
||||||
localResolvers *proxy.Proxy
|
localResolvers *proxy.Proxy
|
||||||
sysResolvers aghnet.SystemResolvers
|
sysResolvers aghnet.SystemResolvers
|
||||||
|
|
||||||
@@ -180,9 +174,6 @@ const (
|
|||||||
|
|
||||||
// NewServer creates a new instance of the dnsforward.Server
|
// NewServer creates a new instance of the dnsforward.Server
|
||||||
// Note: this function must be called only once
|
// 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) {
|
func NewServer(p DNSCreateParams) (s *Server, err error) {
|
||||||
var localDomainSuffix string
|
var localDomainSuffix string
|
||||||
if p.LocalDomain == "" {
|
if p.LocalDomain == "" {
|
||||||
@@ -270,25 +261,14 @@ func (s *Server) WriteDiskConfig(c *FilteringConfig) {
|
|||||||
c.UpstreamDNS = stringutil.CloneSlice(sc.UpstreamDNS)
|
c.UpstreamDNS = stringutil.CloneSlice(sc.UpstreamDNS)
|
||||||
}
|
}
|
||||||
|
|
||||||
// LocalPTRResolvers returns the current local PTR resolver configuration.
|
// RDNSSettings returns the copy of actual RDNS configuration.
|
||||||
func (s *Server) LocalPTRResolvers() (localPTRResolvers []string) {
|
func (s *Server) RDNSSettings() (localPTRResolvers []string, resolveClients, resolvePTR bool) {
|
||||||
s.serverLock.RLock()
|
s.serverLock.RLock()
|
||||||
defer s.serverLock.RUnlock()
|
defer s.serverLock.RUnlock()
|
||||||
|
|
||||||
return stringutil.CloneSlice(s.conf.LocalPTRResolvers)
|
return stringutil.CloneSlice(s.conf.LocalPTRResolvers),
|
||||||
}
|
s.conf.ResolveClients,
|
||||||
|
s.conf.UsePrivateRDNS
|
||||||
// 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,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolve - get IP addresses by host name from an upstream server.
|
// 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()
|
s.serverLock.RLock()
|
||||||
defer s.serverLock.RUnlock()
|
defer s.serverLock.RUnlock()
|
||||||
|
|
||||||
|
if !s.conf.ResolveClients {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
arpa, err := netutil.IPToReversedAddr(ip.AsSlice())
|
arpa, err := netutil.IPToReversedAddr(ip.AsSlice())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("reversing ip: %w", err)
|
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
|
var resolver *proxy.Proxy
|
||||||
if s.privateNets.Contains(ip.AsSlice()) {
|
if s.isPrivateIP(ip) {
|
||||||
if !s.conf.UsePrivateRDNS {
|
if !s.conf.UsePrivateRDNS {
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
@@ -385,6 +369,27 @@ func hostFromPTR(resp *dns.Msg) (host string, err error) {
|
|||||||
return "", ErrRDNSNoData
|
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.
|
// Start starts the DNS server.
|
||||||
func (s *Server) Start() error {
|
func (s *Server) Start() error {
|
||||||
s.serverLock.Lock()
|
s.serverLock.Lock()
|
||||||
@@ -457,27 +462,23 @@ func (s *Server) filterOurDNSAddrs(addrs []string) (filtered []string, err error
|
|||||||
return stringutil.FilterOut(addrs, ourAddrsSet.Has), nil
|
return stringutil.FilterOut(addrs, ourAddrsSet.Has), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// setupLocalResolvers initializes the resolvers for local addresses. For
|
// setupResolvers initializes the resolvers for local addresses. For internal
|
||||||
// internal use only.
|
// use only.
|
||||||
func (s *Server) setupLocalResolvers() (err error) {
|
func (s *Server) setupResolvers(localAddrs []string) (err error) {
|
||||||
bootstraps := s.conf.BootstrapDNS
|
bootstraps := s.conf.BootstrapDNS
|
||||||
resolvers := s.conf.LocalPTRResolvers
|
if len(localAddrs) == 0 {
|
||||||
|
localAddrs = s.sysResolvers.Get()
|
||||||
if len(resolvers) == 0 {
|
|
||||||
resolvers = s.sysResolvers.Get()
|
|
||||||
bootstraps = nil
|
bootstraps = nil
|
||||||
} else {
|
|
||||||
resolvers = stringutil.FilterOut(resolvers, IsCommentOrEmpty)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resolvers, err = s.filterOurDNSAddrs(resolvers)
|
localAddrs, err = s.filterOurDNSAddrs(localAddrs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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,
|
Bootstrap: bootstraps,
|
||||||
Timeout: defaultLocalTimeout,
|
Timeout: defaultLocalTimeout,
|
||||||
// TODO(e.burkov): Should we verify server's certificates?
|
// TODO(e.burkov): Should we verify server's certificates?
|
||||||
@@ -490,17 +491,10 @@ func (s *Server) setupLocalResolvers() (err error) {
|
|||||||
|
|
||||||
s.localResolvers = &proxy.Proxy{
|
s.localResolvers = &proxy.Proxy{
|
||||||
Config: proxy.Config{
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -550,37 +544,23 @@ func (s *Server) Prepare(conf *ServerConfig) (err error) {
|
|||||||
return fmt.Errorf("preparing access: %w", err)
|
return fmt.Errorf("preparing access: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the proxy here because [setupLocalResolvers] sets its values.
|
s.registerHandlers()
|
||||||
//
|
|
||||||
// TODO(e.burkov): Remove once the local resolvers logic moved to dnsproxy.
|
|
||||||
s.dnsProxy = &proxy.Proxy{Config: proxyConfig}
|
|
||||||
|
|
||||||
err = s.setupLocalResolvers()
|
// TODO(e.burkov): Remove once the local resolvers logic moved to dnsproxy.
|
||||||
|
err = s.setupResolvers(s.conf.LocalPTRResolvers)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("setting up resolvers: %w", err)
|
return fmt.Errorf("setting up resolvers: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
s.recDetector.clear()
|
if s.conf.UsePrivateRDNS {
|
||||||
|
proxyConfig.PrivateRDNSUpstreamConfig = s.localResolvers.UpstreamConfig
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
s.registerHandlers()
|
s.dnsProxy = &proxy.Proxy{Config: proxyConfig}
|
||||||
|
|
||||||
|
s.recDetector.clear()
|
||||||
|
|
||||||
|
s.clientIPs = s.conf.ClientIPs
|
||||||
|
|
||||||
return nil
|
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.
|
// TODO(a.garipov): This whole piece of API is weird and needs to be remade.
|
||||||
if conf == nil {
|
if conf == nil {
|
||||||
conf = &s.conf
|
conf = &s.conf
|
||||||
} else {
|
} else if s.clientIPs != nil {
|
||||||
closeErr := s.addrProc.Close()
|
close(s.clientIPs)
|
||||||
if closeErr != nil {
|
s.clientIPs = nil
|
||||||
log.Error("dnsforward: closing address processor: %s", closeErr)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err = s.Prepare(conf)
|
err = s.Prepare(conf)
|
||||||
|
|||||||
@@ -1346,7 +1346,9 @@ func TestServer_Exchange(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
srv.conf.ResolveClients = true
|
||||||
srv.conf.UsePrivateRDNS = true
|
srv.conf.UsePrivateRDNS = true
|
||||||
|
|
||||||
srv.privateNets = netutil.SubnetSetFunc(netutil.IsLocallyServed)
|
srv.privateNets = netutil.SubnetSetFunc(netutil.IsLocallyServed)
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
@@ -1420,3 +1422,57 @@ func TestServer_Exchange(t *testing.T) {
|
|||||||
assert.Empty(t, host)
|
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
|
cacheMinTTL := s.conf.CacheMinTTL
|
||||||
cacheMaxTTL := s.conf.CacheMaxTTL
|
cacheMaxTTL := s.conf.CacheMaxTTL
|
||||||
cacheOptimistic := s.conf.CacheOptimistic
|
cacheOptimistic := s.conf.CacheOptimistic
|
||||||
resolveClients := s.conf.AddrProcConf.UseRDNS
|
resolveClients := s.conf.ResolveClients
|
||||||
usePrivateRDNS := s.conf.UsePrivateRDNS
|
usePrivateRDNS := s.conf.UsePrivateRDNS
|
||||||
localPTRUpstreams := stringutil.CloneSliceOrEmpty(s.conf.LocalPTRResolvers)
|
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.ProtectionEnabled, dc.ProtectionEnabled)
|
||||||
setIfNotNil(&s.conf.EnableDNSSEC, dc.DNSSECEnabled)
|
setIfNotNil(&s.conf.EnableDNSSEC, dc.DNSSECEnabled)
|
||||||
setIfNotNil(&s.conf.AAAADisabled, dc.DisableIPv6)
|
setIfNotNil(&s.conf.AAAADisabled, dc.DisableIPv6)
|
||||||
|
setIfNotNil(&s.conf.ResolveClients, dc.ResolveClients)
|
||||||
|
setIfNotNil(&s.conf.UsePrivateRDNS, dc.UsePrivateRDNS)
|
||||||
|
|
||||||
return s.setConfigRestartable(dc)
|
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.
|
// setConfigRestartable sets the parameters which trigger a restart.
|
||||||
// shouldRestart is true if the server should be restarted to apply changes.
|
// shouldRestart is true if the server should be restarted to apply changes.
|
||||||
// s.serverLock is expected to be locked.
|
// 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) {
|
func (s *Server) setConfigRestartable(dc *jsonDNSConfig) (shouldRestart bool) {
|
||||||
for _, hasSet := range []bool{
|
for _, hasSet := range []bool{
|
||||||
setIfNotNil(&s.conf.UpstreamDNS, dc.Upstreams),
|
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.CacheMinTTL, dc.CacheMinTTL),
|
||||||
setIfNotNil(&s.conf.CacheMaxTTL, dc.CacheMaxTTL),
|
setIfNotNil(&s.conf.CacheMaxTTL, dc.CacheMaxTTL),
|
||||||
setIfNotNil(&s.conf.CacheOptimistic, dc.CacheOptimistic),
|
setIfNotNil(&s.conf.CacheOptimistic, dc.CacheOptimistic),
|
||||||
setIfNotNil(&s.conf.AddrProcConf.UseRDNS, dc.ResolveClients),
|
|
||||||
setIfNotNil(&s.conf.UsePrivateRDNS, dc.UsePrivateRDNS),
|
|
||||||
} {
|
} {
|
||||||
shouldRestart = shouldRestart || hasSet
|
shouldRestart = shouldRestart || hasSet
|
||||||
if shouldRestart {
|
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
|
// [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
|
// 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.
|
// purposes.
|
||||||
//
|
//
|
||||||
// [Section 6.2 of RFC 6761]: https://www.rfc-editor.org/rfc/rfc6761.html#section-6.2
|
// [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
|
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) {
|
func (s *Server) processClientIP(addr net.Addr) {
|
||||||
clientIP := netutil.NetAddrToAddrPort(addr).Addr()
|
clientIP := netutil.NetAddrToAddrPort(addr).Addr()
|
||||||
if clientIP == (netip.Addr{}) {
|
if clientIP == (netip.Addr{}) {
|
||||||
@@ -231,12 +231,17 @@ func (s *Server) processClientIP(addr net.Addr) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do not assign s.addrProc to a local variable to then use, since this lock
|
// Do not assign s.clientIPs to a local variable to then use, since this
|
||||||
// also serializes the closure of s.addrProc.
|
// lock also serializes the closure of s.clientIPs.
|
||||||
s.serverLock.RLock()
|
s.serverLock.RLock()
|
||||||
defer s.serverLock.RUnlock()
|
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) {
|
func (s *Server) setTableHostToIP(t hostToIPTable) {
|
||||||
@@ -719,18 +724,6 @@ func (s *Server) processLocalPTR(dctx *dnsContext) (rc resultCode) {
|
|||||||
if s.conf.UsePrivateRDNS {
|
if s.conf.UsePrivateRDNS {
|
||||||
s.recDetector.add(*pctx.Req)
|
s.recDetector.add(*pctx.Req)
|
||||||
if err := s.localResolvers.Resolve(pctx); err != nil {
|
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
|
dctx.err = err
|
||||||
|
|
||||||
return resultCodeError
|
return resultCodeError
|
||||||
|
|||||||
@@ -76,21 +76,18 @@ func TestServer_ProcessInitial(t *testing.T) {
|
|||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
clientIPs := make(chan netip.Addr, 1)
|
||||||
|
|
||||||
c := ServerConfig{
|
c := ServerConfig{
|
||||||
FilteringConfig: FilteringConfig{
|
FilteringConfig: FilteringConfig{
|
||||||
AAAADisabled: tc.aaaaDisabled,
|
AAAADisabled: tc.aaaaDisabled,
|
||||||
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
|
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
|
||||||
},
|
},
|
||||||
|
ClientIPs: clientIPs,
|
||||||
}
|
}
|
||||||
|
|
||||||
s := createTestServer(t, &filtering.Config{}, c, nil)
|
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{
|
dctx := &dnsContext{
|
||||||
proxyCtx: &proxy.DNSContext{
|
proxyCtx: &proxy.DNSContext{
|
||||||
Req: createTestMessageWithType(tc.target, tc.qType),
|
Req: createTestMessageWithType(tc.target, tc.qType),
|
||||||
@@ -101,6 +98,8 @@ func TestServer_ProcessInitial(t *testing.T) {
|
|||||||
|
|
||||||
gotRC := s.processInitial(dctx)
|
gotRC := s.processInitial(dctx)
|
||||||
assert.Equal(t, tc.wantRC, gotRC)
|
assert.Equal(t, tc.wantRC, gotRC)
|
||||||
|
|
||||||
|
gotAddr, _ := testutil.RequireReceive(t, clientIPs, testTimeout)
|
||||||
assert.Equal(t, netutil.NetAddrToAddrPort(testClientAddr).Addr(), gotAddr)
|
assert.Equal(t, netutil.NetAddrToAddrPort(testClientAddr).Addr(), gotAddr)
|
||||||
|
|
||||||
if tc.wantRCode > 0 {
|
if tc.wantRCode > 0 {
|
||||||
|
|||||||
@@ -6,9 +6,10 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"hash/crc32"
|
"hash/crc32"
|
||||||
"io"
|
"io"
|
||||||
|
"unicode"
|
||||||
|
"unicode/utf8"
|
||||||
|
|
||||||
"github.com/AdguardTeam/golibs/errors"
|
"github.com/AdguardTeam/golibs/errors"
|
||||||
"golang.org/x/exp/slices"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Parser is a filtering-rule parser that collects data, such as the checksum
|
// 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)
|
badIdx, isRule = p.parseLineTitle(trimmed)
|
||||||
}
|
}
|
||||||
if badIdx != -1 {
|
if badIdx != -1 {
|
||||||
|
badRune, _ := utf8.DecodeRune(trimmed[badIdx:])
|
||||||
|
|
||||||
return 0, fmt.Errorf(
|
return 0, fmt.Errorf(
|
||||||
"line %d: character %d: likely binary character %q",
|
"line %d: character %d: non-printable character %q",
|
||||||
lineNum,
|
lineNum,
|
||||||
badIdx+bytes.Index(line, trimmed)+1,
|
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
|
// 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
|
// assumed to be trimmed of whitespace characters. nonPrintIdx is the index of
|
||||||
// first character that may indicate that this is a binary file, or -1 if none.
|
// 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
|
// A line is considered a rule if it's not empty, not a comment, and contains
|
||||||
// only printable characters.
|
// 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] == '!' {
|
if len(line) == 0 || line[0] == '#' || line[0] == '!' {
|
||||||
return -1, false
|
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.
|
// isNotPrintable returns true if r is not a printable character that can be
|
||||||
func likelyBinary(b byte) (ok bool) {
|
// contained in a filtering rule.
|
||||||
return (b < ' ' || b == 0x7f) && b != '\n' && b != '\r' && b != '\t'
|
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
|
// parseLineTitle is like [parseLine] but additionally looks for a title. line
|
||||||
// is assumed to be trimmed of whitespace characters.
|
// 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] == '#' {
|
if len(line) == 0 || line[0] == '#' {
|
||||||
return -1, false
|
return -1, false
|
||||||
}
|
}
|
||||||
|
|
||||||
if line[0] != '!' {
|
if line[0] != '!' {
|
||||||
badIdx = slices.IndexFunc(line, likelyBinary)
|
nonPrintIdx = bytes.IndexFunc(line, isNotPrintable)
|
||||||
|
|
||||||
return badIdx, badIdx == -1
|
return nonPrintIdx, nonPrintIdx == -1
|
||||||
}
|
}
|
||||||
|
|
||||||
const titlePattern = "! Title: "
|
const titlePattern = "! Title: "
|
||||||
|
|||||||
@@ -77,14 +77,6 @@ func TestParser_Parse(t *testing.T) {
|
|||||||
wantTitle: "Test Title",
|
wantTitle: "Test Title",
|
||||||
wantRulesNum: 1,
|
wantRulesNum: 1,
|
||||||
wantWritten: len(testRuleTextBlocked),
|
wantWritten: len(testRuleTextBlocked),
|
||||||
}, {
|
|
||||||
name: "cosmetic_with_zwnj",
|
|
||||||
in: testRuleTextCosmetic,
|
|
||||||
wantDst: testRuleTextCosmetic,
|
|
||||||
wantErrMsg: "",
|
|
||||||
wantTitle: "",
|
|
||||||
wantRulesNum: 1,
|
|
||||||
wantWritten: len(testRuleTextCosmetic),
|
|
||||||
}, {
|
}, {
|
||||||
name: "bad_char",
|
name: "bad_char",
|
||||||
in: "! Title: Test Title \n" +
|
in: "! Title: Test Title \n" +
|
||||||
@@ -93,7 +85,7 @@ func TestParser_Parse(t *testing.T) {
|
|||||||
wantDst: testRuleTextBlocked,
|
wantDst: testRuleTextBlocked,
|
||||||
wantErrMsg: "line 3: " +
|
wantErrMsg: "line 3: " +
|
||||||
"character 4: " +
|
"character 4: " +
|
||||||
"likely binary character '\\x7f'",
|
"non-printable character '\\x7f'",
|
||||||
wantTitle: "Test Title",
|
wantTitle: "Test Title",
|
||||||
wantRulesNum: 1,
|
wantRulesNum: 1,
|
||||||
wantWritten: len(testRuleTextBlocked),
|
wantWritten: len(testRuleTextBlocked),
|
||||||
@@ -223,14 +215,6 @@ func BenchmarkParser_Parse(b *testing.B) {
|
|||||||
|
|
||||||
require.NoError(b, errSink)
|
require.NoError(b, errSink)
|
||||||
require.NotNil(b, resSink)
|
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) {
|
func FuzzParser_Parse(f *testing.F) {
|
||||||
@@ -242,17 +226,15 @@ func FuzzParser_Parse(f *testing.F) {
|
|||||||
"! Comment",
|
"! Comment",
|
||||||
"! Title ",
|
"! Title ",
|
||||||
"! Title XXX",
|
"! Title XXX",
|
||||||
testRuleTextBadTab,
|
|
||||||
testRuleTextBlocked,
|
|
||||||
testRuleTextCosmetic,
|
|
||||||
testRuleTextEtcHostsTab,
|
testRuleTextEtcHostsTab,
|
||||||
testRuleTextHTML,
|
testRuleTextHTML,
|
||||||
|
testRuleTextBlocked,
|
||||||
|
testRuleTextBadTab,
|
||||||
"1.2.3.4",
|
"1.2.3.4",
|
||||||
"1.2.3.4 etc-hosts.example",
|
"1.2.3.4 etc-hosts.example",
|
||||||
">>>\x00<<<",
|
">>>\x00<<<",
|
||||||
">>>\x7F<<<",
|
">>>\x7F<<<",
|
||||||
strings.Repeat("a", rulelist.DefaultRuleBufSize+1),
|
strings.Repeat("a", n+1),
|
||||||
strings.Repeat("a", bufio.MaxScanTokenSize+1),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
|
|||||||
@@ -7,13 +7,8 @@ const testTimeout = 1 * time.Second
|
|||||||
|
|
||||||
// Common texts for tests.
|
// Common texts for tests.
|
||||||
const (
|
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"
|
testRuleTextHTML = "<!DOCTYPE html>\n"
|
||||||
|
testRuleTextBlocked = "||blocked.example^\n"
|
||||||
// testRuleTextCosmetic is a cosmetic rule with a zero-width non-joiner.
|
testRuleTextBadTab = "||bad-tab-and-comment.example^\t# A comment.\n"
|
||||||
//
|
testRuleTextEtcHostsTab = "0.0.0.0 tab..example^\t# A comment.\n"
|
||||||
// See https://github.com/AdguardTeam/AdGuardHome/issues/6003.
|
|
||||||
testRuleTextCosmetic = "||cosmetic.example## :has-text(/\u200c/i)\n"
|
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1505,6 +1505,7 @@ var blockedServices = []blockedService{{
|
|||||||
"||aus.social^",
|
"||aus.social^",
|
||||||
"||awscommunity.social^",
|
"||awscommunity.social^",
|
||||||
"||climatejustice.social^",
|
"||climatejustice.social^",
|
||||||
|
"||cupoftea.social^",
|
||||||
"||cyberplace.social^",
|
"||cyberplace.social^",
|
||||||
"||defcon.social^",
|
"||defcon.social^",
|
||||||
"||det.social^",
|
"||det.social^",
|
||||||
@@ -1546,12 +1547,12 @@ var blockedServices = []blockedService{{
|
|||||||
"||mastodon.social^",
|
"||mastodon.social^",
|
||||||
"||mastodon.uno^",
|
"||mastodon.uno^",
|
||||||
"||mastodon.world^",
|
"||mastodon.world^",
|
||||||
|
"||mastodon.zaclys.com^",
|
||||||
"||mastodonapp.uk^",
|
"||mastodonapp.uk^",
|
||||||
"||mastodonners.nl^",
|
"||mastodonners.nl^",
|
||||||
"||mastodont.cat^",
|
"||mastodont.cat^",
|
||||||
"||mastodontech.de^",
|
"||mastodontech.de^",
|
||||||
"||mastodontti.fi^",
|
"||mastodontti.fi^",
|
||||||
"||mastouille.fr^",
|
|
||||||
"||mathstodon.xyz^",
|
"||mathstodon.xyz^",
|
||||||
"||metalhead.club^",
|
"||metalhead.club^",
|
||||||
"||mindly.social^",
|
"||mindly.social^",
|
||||||
@@ -1588,7 +1589,6 @@ var blockedServices = []blockedService{{
|
|||||||
"||techhub.social^",
|
"||techhub.social^",
|
||||||
"||theblower.au^",
|
"||theblower.au^",
|
||||||
"||tkz.one^",
|
"||tkz.one^",
|
||||||
"||todon.eu^",
|
|
||||||
"||toot.aquilenet.fr^",
|
"||toot.aquilenet.fr^",
|
||||||
"||toot.community^",
|
"||toot.community^",
|
||||||
"||toot.funami.tech^",
|
"||toot.funami.tech^",
|
||||||
@@ -1661,7 +1661,6 @@ var blockedServices = []blockedService{{
|
|||||||
"||nintendo.jp^",
|
"||nintendo.jp^",
|
||||||
"||nintendo.net^",
|
"||nintendo.net^",
|
||||||
"||nintendo.nl^",
|
"||nintendo.nl^",
|
||||||
"||nintendo.pt^",
|
|
||||||
"||nintendoswitch.cn^",
|
"||nintendoswitch.cn^",
|
||||||
"||nintendowifi.net^",
|
"||nintendowifi.net^",
|
||||||
},
|
},
|
||||||
@@ -2161,20 +2160,6 @@ var blockedServices = []blockedService{{
|
|||||||
Rules: []string{
|
Rules: []string{
|
||||||
"||voot.com^",
|
"||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",
|
ID: "wechat",
|
||||||
Name: "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"
|
"time"
|
||||||
|
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/client"
|
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/dhcpd"
|
"github.com/AdguardTeam/AdGuardHome/internal/dhcpd"
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/dhcpsvc"
|
"github.com/AdguardTeam/AdGuardHome/internal/dhcpsvc"
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/dnsforward"
|
"github.com/AdguardTeam/AdGuardHome/internal/dnsforward"
|
||||||
@@ -744,9 +743,11 @@ func (clients *clientsContainer) Update(prev, c *Client) (err error) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// setWHOISInfo sets the WHOIS information for a client. clients.lock is
|
// setWHOISInfo sets the WHOIS information for a client.
|
||||||
// expected to be locked.
|
|
||||||
func (clients *clientsContainer) setWHOISInfo(ip netip.Addr, wi *whois.Info) {
|
func (clients *clientsContainer) setWHOISInfo(ip netip.Addr, wi *whois.Info) {
|
||||||
|
clients.lock.Lock()
|
||||||
|
defer clients.lock.Unlock()
|
||||||
|
|
||||||
_, ok := clients.findLocked(ip.String())
|
_, ok := clients.findLocked(ip.String())
|
||||||
if ok {
|
if ok {
|
||||||
log.Debug("clients: client for %s is already created, ignore whois info", ip)
|
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
|
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.
|
// taken into account. ok is true if the pairing was added.
|
||||||
//
|
func (clients *clientsContainer) AddHost(
|
||||||
// TODO(a.garipov): Only used in internal tests. Consider removing.
|
|
||||||
func (clients *clientsContainer) addHost(
|
|
||||||
ip netip.Addr,
|
ip netip.Addr,
|
||||||
host string,
|
host string,
|
||||||
src clientSource,
|
src clientSource,
|
||||||
@@ -788,32 +787,6 @@ func (clients *clientsContainer) addHost(
|
|||||||
return clients.addHostLocked(ip, host, src)
|
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
|
// addHostLocked adds a new IP-hostname pairing. clients.lock is expected to be
|
||||||
// locked.
|
// locked.
|
||||||
func (clients *clientsContainer) addHostLocked(
|
func (clients *clientsContainer) addHostLocked(
|
||||||
|
|||||||
@@ -168,13 +168,13 @@ func TestClients(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("addhost_success", func(t *testing.T) {
|
t.Run("addhost_success", func(t *testing.T) {
|
||||||
ip := netip.MustParseAddr("1.1.1.1")
|
ip := netip.MustParseAddr("1.1.1.1")
|
||||||
ok := clients.addHost(ip, "host", ClientSourceARP)
|
ok := clients.AddHost(ip, "host", ClientSourceARP)
|
||||||
assert.True(t, ok)
|
assert.True(t, ok)
|
||||||
|
|
||||||
ok = clients.addHost(ip, "host2", ClientSourceARP)
|
ok = clients.AddHost(ip, "host2", ClientSourceARP)
|
||||||
assert.True(t, ok)
|
assert.True(t, ok)
|
||||||
|
|
||||||
ok = clients.addHost(ip, "host3", ClientSourceHostsFile)
|
ok = clients.AddHost(ip, "host3", ClientSourceHostsFile)
|
||||||
assert.True(t, ok)
|
assert.True(t, ok)
|
||||||
|
|
||||||
assert.Equal(t, clients.clientSource(ip), ClientSourceHostsFile)
|
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) {
|
t.Run("dhcp_replaces_arp", func(t *testing.T) {
|
||||||
ip := netip.MustParseAddr("1.2.3.4")
|
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.True(t, ok)
|
||||||
assert.Equal(t, clients.clientSource(ip), ClientSourceARP)
|
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.True(t, ok)
|
||||||
assert.Equal(t, clients.clientSource(ip), ClientSourceDHCP)
|
assert.Equal(t, clients.clientSource(ip), ClientSourceDHCP)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("addhost_fail", func(t *testing.T) {
|
t.Run("addhost_fail", func(t *testing.T) {
|
||||||
ip := netip.MustParseAddr("1.1.1.1")
|
ip := netip.MustParseAddr("1.1.1.1")
|
||||||
ok := clients.addHost(ip, "host1", ClientSourceRDNS)
|
ok := clients.AddHost(ip, "host1", ClientSourceRDNS)
|
||||||
assert.False(t, ok)
|
assert.False(t, ok)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -216,7 +216,7 @@ func TestClientsWHOIS(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("existing_auto-client", func(t *testing.T) {
|
t.Run("existing_auto-client", func(t *testing.T) {
|
||||||
ip := netip.MustParseAddr("1.1.1.1")
|
ip := netip.MustParseAddr("1.1.1.1")
|
||||||
ok := clients.addHost(ip, "host", ClientSourceRDNS)
|
ok := clients.AddHost(ip, "host", ClientSourceRDNS)
|
||||||
assert.True(t, ok)
|
assert.True(t, ok)
|
||||||
|
|
||||||
clients.setWHOISInfo(ip, whois)
|
clients.setWHOISInfo(ip, whois)
|
||||||
@@ -259,7 +259,7 @@ func TestClientsAddExisting(t *testing.T) {
|
|||||||
assert.True(t, ok)
|
assert.True(t, ok)
|
||||||
|
|
||||||
// Now add an auto-client with the same IP.
|
// 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)
|
assert.True(t, ok)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -590,13 +590,7 @@ func (c *configuration) write() (err error) {
|
|||||||
s.WriteDiskConfig(&c)
|
s.WriteDiskConfig(&c)
|
||||||
dns := &config.DNS
|
dns := &config.DNS
|
||||||
dns.FilteringConfig = c
|
dns.FilteringConfig = c
|
||||||
|
dns.LocalPTRResolvers, config.Clients.Sources.RDNS, dns.UsePrivateRDNS = s.RDNSSettings()
|
||||||
dns.LocalPTRResolvers = s.LocalPTRResolvers()
|
|
||||||
|
|
||||||
addrProcConf := s.AddrProcConfig()
|
|
||||||
config.Clients.Sources.RDNS = addrProcConf.UseRDNS
|
|
||||||
config.Clients.Sources.WHOIS = addrProcConf.UseWHOIS
|
|
||||||
dns.UsePrivateRDNS = addrProcConf.UsePrivateRDNS
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if Context.dhcpServer != nil {
|
if Context.dhcpServer != nil {
|
||||||
|
|||||||
@@ -176,16 +176,12 @@ func handleStatus(w http.ResponseWriter, r *http.Request) {
|
|||||||
// ------------------------
|
// ------------------------
|
||||||
// registration of handlers
|
// registration of handlers
|
||||||
// ------------------------
|
// ------------------------
|
||||||
func registerControlHandlers(web *webAPI) {
|
func registerControlHandlers() {
|
||||||
Context.mux.HandleFunc(
|
|
||||||
"/control/version.json",
|
|
||||||
postInstall(optionalAuth(web.handleVersionJSON)),
|
|
||||||
)
|
|
||||||
httpRegister(http.MethodPost, "/control/update", web.handleUpdate)
|
|
||||||
|
|
||||||
httpRegister(http.MethodGet, "/control/status", handleStatus)
|
httpRegister(http.MethodGet, "/control/status", handleStatus)
|
||||||
httpRegister(http.MethodPost, "/control/i18n/change_language", handleI18nChangeLanguage)
|
httpRegister(http.MethodPost, "/control/i18n/change_language", handleI18nChangeLanguage)
|
||||||
httpRegister(http.MethodGet, "/control/i18n/current_language", handleI18nCurrentLanguage)
|
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.MethodGet, "/control/profile", handleGetProfile)
|
||||||
httpRegister(http.MethodPut, "/control/profile/update", handlePutProfile)
|
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.BindHost = req.Web.IP
|
||||||
web.conf.BindPort = req.Web.Port
|
web.conf.BindPort = req.Web.Port
|
||||||
|
|
||||||
registerControlHandlers(web)
|
registerControlHandlers()
|
||||||
|
|
||||||
aghhttp.OK(w)
|
aghhttp.OK(w)
|
||||||
if f, ok := w.(http.Flusher); ok {
|
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.
|
// 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.
|
// 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{}
|
resp := &versionResponse{}
|
||||||
if web.conf.disableUpdate {
|
if Context.disableUpdate {
|
||||||
resp.Disabled = true
|
resp.Disabled = true
|
||||||
_ = aghhttp.WriteJSONResponse(w, r, resp)
|
_ = 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 {
|
if err != nil {
|
||||||
// Don't wrap the error, because it's informative enough as is.
|
// Don't wrap the error, because it's informative enough as is.
|
||||||
aghhttp.Error(r, w, http.StatusBadGateway, "%s", err)
|
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
|
// requestVersionInfo sets the VersionInfo field of resp if it can reach the
|
||||||
// update server.
|
// update server.
|
||||||
func (web *webAPI) requestVersionInfo(resp *versionResponse, recheck bool) (err error) {
|
func requestVersionInfo(resp *versionResponse, recheck bool) (err error) {
|
||||||
updater := web.conf.updater
|
|
||||||
for i := 0; i != 3; i++ {
|
for i := 0; i != 3; i++ {
|
||||||
resp.VersionInfo, err = updater.VersionInfo(recheck)
|
resp.VersionInfo, err = Context.updater.VersionInfo(recheck)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
var terr temporaryError
|
var terr temporaryError
|
||||||
if errors.As(err, &terr) && terr.Temporary() {
|
if errors.As(err, &terr) && terr.Temporary() {
|
||||||
@@ -96,7 +95,7 @@ func (web *webAPI) requestVersionInfo(resp *versionResponse, recheck bool) (err
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
vcu := updater.VersionCheckURL()
|
vcu := Context.updater.VersionCheckURL()
|
||||||
|
|
||||||
return fmt.Errorf("getting version info from %s: %w", vcu, err)
|
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.
|
// handleUpdate performs an update to the latest available version procedure.
|
||||||
func (web *webAPI) handleUpdate(w http.ResponseWriter, r *http.Request) {
|
func handleUpdate(w http.ResponseWriter, r *http.Request) {
|
||||||
updater := web.conf.updater
|
if Context.updater.NewVersion() == "" {
|
||||||
if updater.NewVersion() == "" {
|
|
||||||
aghhttp.Error(r, w, http.StatusBadRequest, "/update request isn't allowed now")
|
aghhttp.Error(r, w, http.StatusBadRequest, "/update request isn't allowed now")
|
||||||
|
|
||||||
return
|
return
|
||||||
@@ -124,7 +122,7 @@ func (web *webAPI) handleUpdate(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = updater.Update(false)
|
err = Context.updater.Update(false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
aghhttp.Error(r, w, http.StatusInternalServerError, "%s", err)
|
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
|
// The background context is used because the underlying functions wrap it
|
||||||
// with timeout and shut down the server, which handles current request. 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.
|
// 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.
|
// versionResponse is the response for /control/version.json endpoint.
|
||||||
@@ -180,7 +178,7 @@ func tlsConfUsesPrivilegedPorts(c *tlsConfigSettings) (ok bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// finishUpdate completes an update procedure.
|
// finishUpdate completes an update procedure.
|
||||||
func finishUpdate(ctx context.Context, execPath string, runningAsService bool) {
|
func finishUpdate(ctx context.Context, execPath string) {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
log.Info("stopping all tasks")
|
log.Info("stopping all tasks")
|
||||||
@@ -189,7 +187,7 @@ func finishUpdate(ctx context.Context, execPath string, runningAsService bool) {
|
|||||||
cleanupAlways()
|
cleanupAlways()
|
||||||
|
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
if runningAsService {
|
if Context.runningAsService {
|
||||||
// NOTE: We can't restart the service via "kardianos/service"
|
// NOTE: We can't restart the service via "kardianos/service"
|
||||||
// package, because it kills the process first we can't start a new
|
// package, because it kills the process first we can't start a new
|
||||||
// instance, because Windows doesn't allow it.
|
// instance, because Windows doesn't allow it.
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import (
|
|||||||
"github.com/AdguardTeam/AdGuardHome/internal/aghalg"
|
"github.com/AdguardTeam/AdGuardHome/internal/aghalg"
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
|
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/client"
|
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/dhcpd"
|
"github.com/AdguardTeam/AdGuardHome/internal/dhcpd"
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/dnsforward"
|
"github.com/AdguardTeam/AdGuardHome/internal/dnsforward"
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
||||||
@@ -133,7 +132,7 @@ func initDNSServer(
|
|||||||
return fmt.Errorf("preparing set of private subnets: %w", err)
|
return fmt.Errorf("preparing set of private subnets: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
Context.dnsServer, err = dnsforward.NewServer(dnsforward.DNSCreateParams{
|
p := dnsforward.DNSCreateParams{
|
||||||
DNSFilter: filters,
|
DNSFilter: filters,
|
||||||
Stats: sts,
|
Stats: sts,
|
||||||
QueryLog: qlog,
|
QueryLog: qlog,
|
||||||
@@ -141,7 +140,9 @@ func initDNSServer(
|
|||||||
Anonymizer: anonymizer,
|
Anonymizer: anonymizer,
|
||||||
LocalDomain: config.DHCP.LocalDomainName,
|
LocalDomain: config.DHCP.LocalDomainName,
|
||||||
DHCPServer: dhcpSrv,
|
DHCPServer: dhcpSrv,
|
||||||
})
|
}
|
||||||
|
|
||||||
|
Context.dnsServer, err = dnsforward.NewServer(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
closeDNSServer()
|
closeDNSServer()
|
||||||
|
|
||||||
@@ -164,6 +165,15 @@ func initDNSServer(
|
|||||||
return fmt.Errorf("dnsServer.Prepare: %w", err)
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -229,35 +239,18 @@ func newServerConfig(
|
|||||||
) (newConf *dnsforward.ServerConfig, err error) {
|
) (newConf *dnsforward.ServerConfig, err error) {
|
||||||
dnsConf := config.DNS
|
dnsConf := config.DNS
|
||||||
hosts := aghalg.CoalesceSlice(dnsConf.BindHosts, []netip.Addr{netutil.IPv4Localhost()})
|
hosts := aghalg.CoalesceSlice(dnsConf.BindHosts, []netip.Addr{netutil.IPv4Localhost()})
|
||||||
|
clientIPs := make(chan netip.Addr, defaultQueueSize)
|
||||||
newConf = &dnsforward.ServerConfig{
|
newConf = &dnsforward.ServerConfig{
|
||||||
UDPListenAddrs: ipsToUDPAddrs(hosts, dnsConf.Port),
|
UDPListenAddrs: ipsToUDPAddrs(hosts, dnsConf.Port),
|
||||||
TCPListenAddrs: ipsToTCPAddrs(hosts, dnsConf.Port),
|
TCPListenAddrs: ipsToTCPAddrs(hosts, dnsConf.Port),
|
||||||
FilteringConfig: dnsConf.FilteringConfig,
|
FilteringConfig: dnsConf.FilteringConfig,
|
||||||
ConfigModified: onConfigModified,
|
ConfigModified: onConfigModified,
|
||||||
HTTPRegister: httpReg,
|
HTTPRegister: httpReg,
|
||||||
|
ClientIPs: clientIPs,
|
||||||
UseDNS64: config.DNS.UseDNS64,
|
UseDNS64: config.DNS.UseDNS64,
|
||||||
DNS64Prefixes: config.DNS.DNS64Prefixes,
|
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 {
|
if tlsConf.Enabled {
|
||||||
newConf.TLSConfig = tlsConf.TLSConfig
|
newConf.TLSConfig = tlsConf.TLSConfig
|
||||||
newConf.TLSConfig.ServerName = tlsConf.ServerName
|
newConf.TLSConfig.ServerName = tlsConf.ServerName
|
||||||
@@ -293,6 +286,7 @@ func newServerConfig(
|
|||||||
newConf.LocalPTRResolvers = dnsConf.LocalPTRResolvers
|
newConf.LocalPTRResolvers = dnsConf.LocalPTRResolvers
|
||||||
newConf.UpstreamTimeout = dnsConf.UpstreamTimeout.Duration
|
newConf.UpstreamTimeout = dnsConf.UpstreamTimeout.Duration
|
||||||
|
|
||||||
|
newConf.ResolveClients = config.Clients.Sources.RDNS
|
||||||
newConf.UsePrivateRDNS = dnsConf.UsePrivateRDNS
|
newConf.UsePrivateRDNS = dnsConf.UsePrivateRDNS
|
||||||
newConf.ServeHTTP3 = dnsConf.ServeHTTP3
|
newConf.ServeHTTP3 = dnsConf.ServeHTTP3
|
||||||
newConf.UseHTTP3Upstreams = dnsConf.UseHTTP3Upstreams
|
newConf.UseHTTP3Upstreams = dnsConf.UseHTTP3Upstreams
|
||||||
@@ -464,6 +458,9 @@ func reconfigureDNSServer() (err error) {
|
|||||||
return fmt.Errorf("starting forwarding dns server: %w", err)
|
return fmt.Errorf("starting forwarding dns server: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addrProc := newClientAddrProcessor(config.Clients.Sources)
|
||||||
|
go addrProc.process(newConf.ClientIPs)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,12 +3,14 @@ package home
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@@ -64,24 +66,34 @@ type homeContext struct {
|
|||||||
// configuration files, for example /etc/hosts.
|
// configuration files, for example /etc/hosts.
|
||||||
etcHosts *aghnet.HostsContainer
|
etcHosts *aghnet.HostsContainer
|
||||||
|
|
||||||
|
updater *updater.Updater
|
||||||
|
|
||||||
// mux is our custom http.ServeMux.
|
// mux is our custom http.ServeMux.
|
||||||
mux *http.ServeMux
|
mux *http.ServeMux
|
||||||
|
|
||||||
// Runtime properties
|
// Runtime properties
|
||||||
// --
|
// --
|
||||||
|
|
||||||
configFilename string // Config filename (can be overridden via the command line arguments)
|
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
|
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.
|
pidFileName string // PID file name. Empty if no PID file was created.
|
||||||
controlLock sync.Mutex
|
controlLock sync.Mutex
|
||||||
tlsRoots *x509.CertPool // list of root CAs for TLSv1.2
|
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 are the ID of the cipher suites that AdGuard Home must use.
|
||||||
tlsCipherIDs []uint16
|
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
|
// firstRun, if true, tells AdGuard Home to only start the web interface
|
||||||
// service, and only serve the first-run APIs.
|
// service, and only serve the first-run APIs.
|
||||||
firstRun bool
|
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
|
// getDataDir returns path to the directory where we store databases and filters
|
||||||
@@ -104,11 +116,11 @@ func Main(clientBuildFS fs.FS) {
|
|||||||
// package flag.
|
// package flag.
|
||||||
opts := loadCmdLineOpts()
|
opts := loadCmdLineOpts()
|
||||||
|
|
||||||
signals := make(chan os.Signal, 1)
|
Context.appSignalChannel = make(chan os.Signal)
|
||||||
signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP, syscall.SIGQUIT)
|
signal.Notify(Context.appSignalChannel, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP, syscall.SIGQUIT)
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
sig := <-signals
|
sig := <-Context.appSignalChannel
|
||||||
log.Info("Received signal %q", sig)
|
log.Info("Received signal %q", sig)
|
||||||
switch sig {
|
switch sig {
|
||||||
case syscall.SIGHUP:
|
case syscall.SIGHUP:
|
||||||
@@ -123,7 +135,7 @@ func Main(clientBuildFS fs.FS) {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
if opts.serviceControlAction != "" {
|
if opts.serviceControlAction != "" {
|
||||||
handleServiceControlAction(opts, clientBuildFS, signals)
|
handleServiceControlAction(opts, clientBuildFS)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -135,48 +147,74 @@ func Main(clientBuildFS fs.FS) {
|
|||||||
// setupContext initializes [Context] fields. It also reads and upgrades
|
// setupContext initializes [Context] fields. It also reads and upgrades
|
||||||
// config file if necessary.
|
// config file if necessary.
|
||||||
func setupContext(opts options) (err error) {
|
func setupContext(opts options) (err error) {
|
||||||
Context.firstRun = detectFirstRun()
|
setupContextFlags(opts)
|
||||||
|
|
||||||
Context.tlsRoots = aghtls.SystemRootCAs()
|
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()
|
Context.mux = http.NewServeMux()
|
||||||
|
|
||||||
if Context.firstRun {
|
if !Context.firstRun {
|
||||||
log.Info("This is the first time AdGuard Home is launched")
|
// Do the upgrade if necessary.
|
||||||
checkPermissions()
|
err = upgradeConfig()
|
||||||
|
|
||||||
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 err != nil {
|
if err != nil {
|
||||||
// Don't wrap the error, because it's informative enough as is.
|
// Don't wrap the error, because it's informative enough as is.
|
||||||
return err
|
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
|
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
|
// logIfUnsupported logs a formatted warning if the error is one of the
|
||||||
// unsupported errors and returns nil. If err is nil, logIfUnsupported returns
|
// unsupported errors and returns nil. If err is nil, logIfUnsupported returns
|
||||||
// nil. Otherwise, it returns err.
|
// nil. Otherwise, it returns err.
|
||||||
@@ -281,7 +319,7 @@ func initContextClients() (err error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
//lint:ignore SA1019 Migration is not over.
|
//lint:ignore SA1019 Migration is not over.
|
||||||
config.DHCP.WorkDir = Context.workDir
|
config.DHCP.WorkDir = Context.workDir
|
||||||
config.DHCP.DataDir = Context.getDataDir()
|
config.DHCP.DataDir = Context.getDataDir()
|
||||||
config.DHCP.HTTPRegister = httpRegister
|
config.DHCP.HTTPRegister = httpRegister
|
||||||
@@ -296,6 +334,18 @@ func initContextClients() (err error) {
|
|||||||
return fmt.Errorf("initing dhcp: %w", err)
|
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
|
var arpdb aghnet.ARPDB
|
||||||
if config.Clients.Sources.ARP {
|
if config.Clients.Sources.ARP {
|
||||||
arpdb = aghnet.NewARPDB()
|
arpdb = aghnet.NewARPDB()
|
||||||
@@ -377,7 +427,7 @@ func setupDNSFilteringConf(conf *filtering.Config) (err error) {
|
|||||||
conf.Filters = slices.Clone(config.Filters)
|
conf.Filters = slices.Clone(config.Filters)
|
||||||
conf.WhitelistFilters = slices.Clone(config.WhitelistFilters)
|
conf.WhitelistFilters = slices.Clone(config.WhitelistFilters)
|
||||||
conf.UserRules = slices.Clone(config.UserRules)
|
conf.UserRules = slices.Clone(config.UserRules)
|
||||||
conf.HTTPClient = httpClient()
|
conf.HTTPClient = Context.client
|
||||||
|
|
||||||
cacheTime := time.Duration(conf.CacheTime) * time.Minute
|
cacheTime := time.Duration(conf.CacheTime) * time.Minute
|
||||||
|
|
||||||
@@ -459,7 +509,7 @@ func checkPorts() (err error) {
|
|||||||
return nil
|
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
|
var clientFS fs.FS
|
||||||
if opts.localFrontend {
|
if opts.localFrontend {
|
||||||
log.Info("warning: using local frontend files")
|
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
|
webConf := webConfig{
|
||||||
if disableUpdate {
|
firstRun: Context.firstRun,
|
||||||
log.Info("AdGuard Home updates are disabled")
|
|
||||||
}
|
|
||||||
|
|
||||||
webConf := &webConfig{
|
|
||||||
updater: upd,
|
|
||||||
|
|
||||||
clientFS: clientFS,
|
|
||||||
|
|
||||||
BindHost: config.HTTPConfig.Address.Addr(),
|
BindHost: config.HTTPConfig.Address.Addr(),
|
||||||
BindPort: int(config.HTTPConfig.Address.Port()),
|
BindPort: int(config.HTTPConfig.Address.Port()),
|
||||||
|
|
||||||
@@ -489,13 +531,12 @@ func initWeb(opts options, clientBuildFS fs.FS, upd *updater.Updater) (web *webA
|
|||||||
ReadHeaderTimeout: readHdrTimeout,
|
ReadHeaderTimeout: readHdrTimeout,
|
||||||
WriteTimeout: writeTimeout,
|
WriteTimeout: writeTimeout,
|
||||||
|
|
||||||
firstRun: Context.firstRun,
|
clientFS: clientFS,
|
||||||
disableUpdate: disableUpdate,
|
|
||||||
runningAsService: opts.runningAsService,
|
serveHTTP3: config.DNS.ServeHTTP3,
|
||||||
serveHTTP3: config.DNS.ServeHTTP3,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
web = newWebAPI(webConf)
|
web = newWebAPI(&webConf)
|
||||||
if web == nil {
|
if web == nil {
|
||||||
return nil, fmt.Errorf("initializing web: %w", err)
|
return nil, fmt.Errorf("initializing web: %w", err)
|
||||||
}
|
}
|
||||||
@@ -546,21 +587,9 @@ func run(opts options, clientBuildFS fs.FS) {
|
|||||||
err = setupOpts(opts)
|
err = setupOpts(opts)
|
||||||
fatalOnError(err)
|
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
|
// TODO(e.burkov): This could be made earlier, probably as the option's
|
||||||
// effect.
|
// effect.
|
||||||
cmdlineUpdate(opts, upd)
|
cmdlineUpdate(opts)
|
||||||
|
|
||||||
if !Context.firstRun {
|
if !Context.firstRun {
|
||||||
// Save the updated config.
|
// Save the updated config.
|
||||||
@@ -589,7 +618,7 @@ func run(opts options, clientBuildFS fs.FS) {
|
|||||||
onConfigModified()
|
onConfigModified()
|
||||||
}
|
}
|
||||||
|
|
||||||
Context.web, err = initWeb(opts, clientBuildFS, upd)
|
Context.web, err = initWeb(opts, clientBuildFS)
|
||||||
fatalOnError(err)
|
fatalOnError(err)
|
||||||
|
|
||||||
if !Context.firstRun {
|
if !Context.firstRun {
|
||||||
@@ -961,6 +990,62 @@ func detectFirstRun() bool {
|
|||||||
return errors.Is(err, os.ErrNotExist)
|
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.
|
// jsonError is a generic JSON error response.
|
||||||
//
|
//
|
||||||
// TODO(a.garipov): Merge together with the implementations in [dhcpd] and other
|
// 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.
|
// cmdlineUpdate updates current application and exits.
|
||||||
func cmdlineUpdate(opts options, upd *updater.Updater) {
|
func cmdlineUpdate(opts options) {
|
||||||
if !opts.performUpdate {
|
if !opts.performUpdate {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -986,9 +1071,10 @@ func cmdlineUpdate(opts options, upd *updater.Updater) {
|
|||||||
|
|
||||||
log.Info("cmdline update: performing update")
|
log.Info("cmdline update: performing update")
|
||||||
|
|
||||||
info, err := upd.VersionInfo(true)
|
updater := Context.updater
|
||||||
|
info, err := updater.VersionInfo(true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
vcu := upd.VersionCheckURL()
|
vcu := updater.VersionCheckURL()
|
||||||
log.Error("getting version info from %s: %s", vcu, err)
|
log.Error("getting version info from %s: %s", vcu, err)
|
||||||
|
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
@@ -1000,7 +1086,7 @@ func cmdlineUpdate(opts options, upd *updater.Updater) {
|
|||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = upd.Update(Context.firstRun)
|
err = updater.Update(Context.firstRun)
|
||||||
fatalOnError(err)
|
fatalOnError(err)
|
||||||
|
|
||||||
err = restartService()
|
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.
|
// daemon.
|
||||||
type program struct {
|
type program struct {
|
||||||
clientBuildFS fs.FS
|
clientBuildFS fs.FS
|
||||||
signals chan os.Signal
|
|
||||||
opts options
|
opts options
|
||||||
}
|
}
|
||||||
|
|
||||||
// type check
|
|
||||||
var _ service.Interface = (*program)(nil)
|
|
||||||
|
|
||||||
// Start implements service.Interface interface for *program.
|
// Start implements service.Interface interface for *program.
|
||||||
func (p *program) Start(_ service.Service) (err error) {
|
func (p *program) Start(_ service.Service) (err error) {
|
||||||
// Start should not block. Do the actual work async.
|
// 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.
|
// Stop implements service.Interface interface for *program.
|
||||||
func (p *program) Stop(_ service.Service) (err error) {
|
func (p *program) Stop(_ service.Service) error {
|
||||||
select {
|
// Stop should not block. Return with a few seconds.
|
||||||
case p.signals <- syscall.SIGINT:
|
if Context.appSignalChannel == nil {
|
||||||
// Go on.
|
os.Exit(0)
|
||||||
default:
|
|
||||||
// Stop should not block.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Context.appSignalChannel <- syscall.SIGINT
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -198,7 +194,7 @@ func restartService() (err error) {
|
|||||||
// - run: This is a special command that is not supposed to be used directly
|
// - 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
|
// it is specified when we register a service, and it indicates to the app
|
||||||
// that it is being run as a service/daemon.
|
// 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
|
// Call chooseSystem explicitly to introduce OpenBSD support for service
|
||||||
// package. It's a noop for other GOOS values.
|
// package. It's a noop for other GOOS values.
|
||||||
chooseSystem()
|
chooseSystem()
|
||||||
@@ -230,11 +226,7 @@ func handleServiceControlAction(opts options, clientBuildFS fs.FS, signals chan
|
|||||||
}
|
}
|
||||||
configureService(svcConfig)
|
configureService(svcConfig)
|
||||||
|
|
||||||
s, err := service.New(&program{
|
s, err := service.New(&program{clientBuildFS: clientBuildFS, opts: runOpts}, svcConfig)
|
||||||
clientBuildFS: clientBuildFS,
|
|
||||||
signals: signals,
|
|
||||||
opts: runOpts,
|
|
||||||
}, svcConfig)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("service: initializing service: %s", err)
|
log.Fatalf("service: initializing service: %s", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,11 +11,11 @@ import (
|
|||||||
|
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
|
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/updater"
|
|
||||||
"github.com/AdguardTeam/golibs/errors"
|
"github.com/AdguardTeam/golibs/errors"
|
||||||
"github.com/AdguardTeam/golibs/log"
|
"github.com/AdguardTeam/golibs/log"
|
||||||
"github.com/AdguardTeam/golibs/netutil"
|
"github.com/AdguardTeam/golibs/netutil"
|
||||||
"github.com/NYTimes/gziphandler"
|
"github.com/NYTimes/gziphandler"
|
||||||
|
"github.com/quic-go/quic-go"
|
||||||
"github.com/quic-go/quic-go/http3"
|
"github.com/quic-go/quic-go/http3"
|
||||||
"golang.org/x/net/http2"
|
"golang.org/x/net/http2"
|
||||||
"golang.org/x/net/http2/h2c"
|
"golang.org/x/net/http2/h2c"
|
||||||
@@ -33,8 +33,6 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type webConfig struct {
|
type webConfig struct {
|
||||||
updater *updater.Updater
|
|
||||||
|
|
||||||
clientFS fs.FS
|
clientFS fs.FS
|
||||||
|
|
||||||
BindHost netip.Addr
|
BindHost netip.Addr
|
||||||
@@ -54,13 +52,6 @@ type webConfig struct {
|
|||||||
|
|
||||||
firstRun bool
|
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
|
serveHTTP3 bool
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,7 +102,7 @@ func newWebAPI(conf *webConfig) (w *webAPI) {
|
|||||||
Context.mux.Handle("/install.html", preInstallHandler(clientFS))
|
Context.mux.Handle("/install.html", preInstallHandler(clientFS))
|
||||||
w.registerInstallHandlers()
|
w.registerInstallHandlers()
|
||||||
} else {
|
} else {
|
||||||
registerControlHandlers(w)
|
registerControlHandlers()
|
||||||
}
|
}
|
||||||
|
|
||||||
w.httpsServer.cond = sync.NewCond(&w.httpsServer.condLock)
|
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")
|
log.Debug("web: starting http/3 server")
|
||||||
err := web.httpsServer.server3.ListenAndServe()
|
err := web.httpsServer.server3.ListenAndServe()
|
||||||
if !errors.Is(err, http.ErrServerClosed) {
|
if !errors.Is(err, quic.ErrServerClosed) {
|
||||||
cleanupAlways()
|
cleanupAlways()
|
||||||
log.Fatalf("web: http3: %s", err)
|
log.Fatalf("web: http3: %s", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ type Interface interface {
|
|||||||
Process(ip netip.Addr) (host string, changed bool)
|
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 Empty struct{}
|
||||||
|
|
||||||
// type check
|
// type check
|
||||||
|
|||||||
@@ -5,13 +5,25 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/aghtest"
|
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/rdns"
|
"github.com/AdguardTeam/AdGuardHome/internal/rdns"
|
||||||
"github.com/AdguardTeam/golibs/netutil"
|
"github.com/AdguardTeam/golibs/netutil"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"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) {
|
func TestDefault_Process(t *testing.T) {
|
||||||
ip1 := netip.MustParseAddr("1.2.3.4")
|
ip1 := netip.MustParseAddr("1.2.3.4")
|
||||||
revAddr1, err := netutil.IPToReversedAddr(ip1.AsSlice())
|
revAddr1, err := netutil.IPToReversedAddr(ip1.AsSlice())
|
||||||
@@ -69,7 +81,7 @@ func TestDefault_Process(t *testing.T) {
|
|||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
exchanger := &aghtest.Exchanger{
|
exchanger := &fakeRDNSExchanger{
|
||||||
OnExchange: onExchange,
|
OnExchange: onExchange,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ require (
|
|||||||
github.com/kisielk/errcheck v1.6.3
|
github.com/kisielk/errcheck v1.6.3
|
||||||
github.com/kyoh86/looppointer v0.2.1
|
github.com/kyoh86/looppointer v0.2.1
|
||||||
github.com/securego/gosec/v2 v2.16.0
|
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/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.
|
// TODO(a.garipov): Return to tagged releases once a new one appears.
|
||||||
honnef.co/go/tools v0.5.0-0.dev.0.20230709092525-bc759185c5ee
|
honnef.co/go/tools v0.5.0-0.dev.0.20230709092525-bc759185c5ee
|
||||||
mvdan.cc/gofumpt v0.5.0
|
mvdan.cc/gofumpt v0.5.0
|
||||||
@@ -27,7 +27,7 @@ require (
|
|||||||
github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354 // indirect
|
github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354 // indirect
|
||||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // 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 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/mod v0.12.0 // indirect
|
||||||
golang.org/x/sync v0.3.0 // indirect
|
golang.org/x/sync v0.3.0 // indirect
|
||||||
golang.org/x/sys v0.10.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/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.1.4/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
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.6 h1:2Cgi6MweCsdB6kpcVQp7EW4U23iBFQWfTXiWlyp842Y=
|
||||||
github.com/uudashr/gocognit v1.0.7/go.mod h1:nAIUuVBnYU7pcninia3BHOvQkpQCeO76Uscky5BOwcY=
|
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 h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
||||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
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=
|
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/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 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug=
|
||||||
golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
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-20230711023510-fffb14384f22 h1:e8iSCQYXZ4EB6q3kIfy2fgPFTvDbozqzRe4OuIOyrL4=
|
||||||
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/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
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.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
|
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.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4=
|
||||||
golang.org/x/tools v0.11.0 h1:EMCa6U9S2LtZXLAMoWiR/R8dAQFRqbAitmbJ2UKhoi8=
|
golang.org/x/tools v0.11.0 h1:EMCa6U9S2LtZXLAMoWiR/R8dAQFRqbAitmbJ2UKhoi8=
|
||||||
golang.org/x/tools v0.11.0/go.mod h1:anzJrxPjNtfgiYQYirP2CPGzGLxrH2u2QBhn6Bf3qY8=
|
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 v0.2.0 h1:Dlz47lW0pvPHU7tnb10S8vbMn9GnV2B6eyT7Tem5XBI=
|
||||||
golang.org/x/vuln v1.0.0/go.mod h1:V0eyhHwaAaHrt42J9bgrN6rd12f6GU4T0Lu0ex2wDg4=
|
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-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-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/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.
|
// Config is the configuration structure for Default.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
// DialContext is used to create TCP connections to WHOIS servers.
|
// DialContext specifies the dial function for creating unencrypted TCP
|
||||||
DialContext DialContextFunc
|
// connections.
|
||||||
|
DialContext func(ctx context.Context, network, addr string) (conn net.Conn, err error)
|
||||||
|
|
||||||
// ServerAddr is the address of the WHOIS server.
|
// ServerAddr is the address of the WHOIS server.
|
||||||
ServerAddr string
|
ServerAddr string
|
||||||
@@ -77,13 +78,6 @@ type Config struct {
|
|||||||
Port uint16
|
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.
|
// Default is the default WHOIS information processor.
|
||||||
type Default struct {
|
type Default struct {
|
||||||
// cache is the cache containing IP addresses of clients. An active IP
|
// cache is the cache containing IP addresses of clients. An active IP
|
||||||
@@ -92,8 +86,9 @@ type Default struct {
|
|||||||
// resolve the same IP.
|
// resolve the same IP.
|
||||||
cache gcache.Cache
|
cache gcache.Cache
|
||||||
|
|
||||||
// dialContext is used to create TCP connections to WHOIS servers.
|
// dialContext connects to a remote server resolving hostname using our own
|
||||||
dialContext DialContextFunc
|
// 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 is the address of the WHOIS server.
|
||||||
serverAddr string
|
serverAddr string
|
||||||
@@ -220,7 +215,7 @@ func (w *Default) query(ctx context.Context, target, serverAddr string) (data []
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = conn.SetDeadline(time.Now().Add(w.timeout))
|
_ = conn.SetReadDeadline(time.Now().Add(w.timeout))
|
||||||
_, err = io.WriteString(conn, target+"\r\n")
|
_, err = io.WriteString(conn, target+"\r\n")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Don't wrap the error since it's informative enough as is.
|
// 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())
|
kv, err := w.queryAll(ctx, ip.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("whois: querying %q: %s", ip, err)
|
log.Debug("whois: quering about %q: %s", ip, err)
|
||||||
|
|
||||||
return nil, true
|
return nil, true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -113,14 +113,20 @@ func TestDefault_Process(t *testing.T) {
|
|||||||
|
|
||||||
return copy(b, tc.data), io.EOF
|
return copy(b, tc.data), io.EOF
|
||||||
},
|
},
|
||||||
OnWrite: func(b []byte) (n int, err error) { return len(b), nil },
|
OnWrite: func(b []byte) (n int, err error) {
|
||||||
OnClose: func() (err error) { return nil },
|
return len(b), nil
|
||||||
OnSetDeadline: func(t time.Time) (err error) { return nil },
|
},
|
||||||
|
OnClose: func() (err error) {
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
OnSetReadDeadline: func(t time.Time) (err error) {
|
||||||
|
return nil
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
w := whois.New(&whois.Config{
|
w := whois.New(&whois.Config{
|
||||||
Timeout: 5 * time.Second,
|
Timeout: 5 * time.Second,
|
||||||
DialContext: func(_ context.Context, _, _ string) (_ net.Conn, _ error) {
|
DialContext: func(_ context.Context, _, addr string) (_ net.Conn, _ error) {
|
||||||
hit = 0
|
hit = 0
|
||||||
|
|
||||||
return fakeConn, nil
|
return fakeConn, nil
|
||||||
|
|||||||
@@ -176,29 +176,16 @@ run_linter gocognit --over 10\
|
|||||||
./internal/aghchan/\
|
./internal/aghchan/\
|
||||||
./internal/aghhttp/\
|
./internal/aghhttp/\
|
||||||
./internal/aghio/\
|
./internal/aghio/\
|
||||||
./internal/client/\
|
|
||||||
./internal/dhcpsvc\
|
|
||||||
./internal/filtering/hashprefix/\
|
./internal/filtering/hashprefix/\
|
||||||
./internal/filtering/rulelist/\
|
./internal/filtering/rulelist/\
|
||||||
./internal/next/\
|
./internal/next/\
|
||||||
./internal/rdns/\
|
./internal/rdns/\
|
||||||
./internal/schedule/\
|
|
||||||
./internal/tools/\
|
./internal/tools/\
|
||||||
./internal/version/\
|
./internal/version/\
|
||||||
./internal/whois/\
|
./internal/whois/\
|
||||||
./scripts/\
|
./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 ineffassign ./...
|
||||||
|
|
||||||
run_linter unparam ./...
|
run_linter unparam ./...
|
||||||
@@ -224,14 +211,12 @@ run_linter gosec --quiet\
|
|||||||
./internal/aghnet\
|
./internal/aghnet\
|
||||||
./internal/aghos\
|
./internal/aghos\
|
||||||
./internal/aghtest\
|
./internal/aghtest\
|
||||||
./internal/client\
|
|
||||||
./internal/dhcpd\
|
./internal/dhcpd\
|
||||||
./internal/dhcpsvc\
|
./internal/dhcpsvc\
|
||||||
./internal/dnsforward\
|
./internal/dnsforward\
|
||||||
./internal/filtering/hashprefix/\
|
./internal/filtering/hashprefix/\
|
||||||
./internal/filtering/rulelist/\
|
./internal/filtering/rulelist/\
|
||||||
./internal/next\
|
./internal/next\
|
||||||
./internal/rdns\
|
|
||||||
./internal/schedule\
|
./internal/schedule\
|
||||||
./internal/stats\
|
./internal/stats\
|
||||||
./internal/tools\
|
./internal/tools\
|
||||||
|
|||||||
Reference in New Issue
Block a user