all: sync with master

This commit is contained in:
Ainar Garipov 2024-01-30 18:43:51 +03:00
parent f6ad64bf69
commit b01c10b73e
196 changed files with 3190 additions and 1790 deletions

View file

@ -23,6 +23,83 @@ See also the [v0.107.44 GitHub milestone][ms-v0.107.44].
NOTE: Add new changes BELOW THIS COMMENT.
-->
### Added
- Etc timezones to the timezone list ([#6568]).
- The schema version of the configuration file to the output of running
`AdGuardHome` (or `AdGuardHome.exe`) with `-v --version` command-line options
([#6545]).
- Ability to disable plain-DNS serving via UI if an encrypted protocol is
already used ([#1660]).
### Changed
- The bootstrapped upstream addresses now updated according to the TTL of the
bootstrap DNS response ([#6321]).
- Logging level of timeout errors is now `error` instead of `debug` ([#6574]).
- The field `"upstream_mode"` in `POST /control/dns_config` and
`GET /control/dns_info` HTTP APIs now accepts `load_balance` value. Check
`openapi/CHANGELOG.md` for more details.
#### Configuration changes
In this release, the schema version has changed from 27 to 28.
- The new property `clients.persistent.*.uid`, which is unique identifier of the
persistent client.
- The properties `dns.all_servers` and `dns.fastest_addr` were removed, their
values migrated to newly added field `dns.upstream_mode` that describes the
logic through which upstreams will be used. See also a [Wiki
page][wiki-config].
```yaml
# BEFORE:
'dns':
# …
'all_servers': true
'fastest_addr': true
# AFTER:
'dns':
# …
'upstream_mode': 'parallel'
```
To rollback this change, remove the new field `upstream_mode`, set back
`dns.all_servers` and `dns.fastest_addr` properties in `dns` section, and
change the `schema_version` back to `27`.
### Fixed
- Panic on using `--no-etc-hosts` flag ([#6644]).
- Schedule display in the client settings after creating or updating.
- Zero value in `querylog.size_memory` disables logging ([#6570]).
- Non-anonymized IP addresses on the dashboard ([#6584]).
- Maximum cache TTL requirement when editing minimum cache TTL in the Web UI
([#6409]).
- Load balancing algorithm stuck on a single server ([#6480]).
- Statistics for 7 days displayed as 168 hours on the dashboard.
- Pre-filling the Edit static lease window with data ([#6534]).
- Names defined in the `/etc/hosts` for a single address family wrongly
considered undefined for another family ([#6541]).
- Omitted CNAME records in safe search results, which can cause YouTube to not
work on iOS ([#6352]).
[#6321]: https://github.com/AdguardTeam/AdGuardHome/issues/6321
[#6352]: https://github.com/AdguardTeam/AdGuardHome/issues/6352
[#6409]: https://github.com/AdguardTeam/AdGuardHome/issues/6409
[#6480]: https://github.com/AdguardTeam/AdGuardHome/issues/6480
[#6534]: https://github.com/AdguardTeam/AdGuardHome/issues/6534
[#6541]: https://github.com/AdguardTeam/AdGuardHome/issues/6541
[#6545]: https://github.com/AdguardTeam/AdGuardHome/issues/6545
[#6568]: https://github.com/AdguardTeam/AdGuardHome/issues/6568
[#6570]: https://github.com/AdguardTeam/AdGuardHome/issues/6570
[#6574]: https://github.com/AdguardTeam/AdGuardHome/issues/6574
[#6584]: https://github.com/AdguardTeam/AdGuardHome/issues/6584
[#6644]: https://github.com/AdguardTeam/AdGuardHome/issues/6644
[wiki-config]: https://github.com/AdguardTeam/AdGuardHome/wiki/Configuration
<!--
NOTE: Add new changes ABOVE THIS COMMENT.
-->

View file

@ -276,6 +276,15 @@ Open your terminal and execute these commands:
git clone https://github.com/AdguardTeam/AdGuardHome
cd AdGuardHome
make
```
#### <a href="#building-node" id="building-node" name="building-node">Building with Node.js 17 and later</a>
In order to build AdGuard Home with Node.js 17 and later, specify
`--openssl-legacy-provider` option.
```sh
export NODE_OPTIONS=--openssl-legacy-provider
```
**NOTE:** The non-standard `-j` flag is currently not supported, so building
@ -409,6 +418,7 @@ There are three options how you can install an unstable version:
```sh
curl -s -S -L https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/master/scripts/install.sh | sh -s -- -c edge
```
[wiki-platf]: https://github.com/AdguardTeam/AdGuardHome/wiki/Platforms
@ -463,10 +473,6 @@ bug or implementing the feature.
[@kongfl888](https://github.com/kongfl888) (originally by
[@rufengsuixing](https://github.com/rufengsuixing)).
* [Prometheus exporter for AdGuard
Home](https://github.com/ebrianne/adguard-exporter) by
[@ebrianne](https://github.com/ebrianne).
* [Terminal-based, real-time traffic monitoring and statistics for your AdGuard Home
instance](https://github.com/Lissy93/AdGuardian-Term) by
[@Lissy93](https://github.com/Lissy93)
@ -485,7 +491,8 @@ bug or implementing the feature.
* [Node.js library](https://github.com/Andrea055/AdguardHomeAPI) by
[@Andrea055](https://github.com/Andrea055/).
* [Browser Extension](https://github.com/satheshshiva/Adguard-Home-Browser-Ext) by
[@satheshshiva](https://github.com/satheshshiva/).
## <a href="#acknowledgments" id="acknowledgments" name="acknowledgments">Acknowledgments</a>

26
client/package-lock.json generated vendored
View file

@ -11,6 +11,7 @@
"@nivo/line": "^0.64.0",
"axios": "^0.19.2",
"classnames": "^2.2.6",
"countries-and-timezones": "^3.6.0",
"date-fns": "^1.29.0",
"i18next": "^19.6.2",
"i18next-browser-languagedetector": "^4.2.0",
@ -37,7 +38,6 @@
"redux-actions": "^2.6.5",
"redux-form": "^8.3.5",
"redux-thunk": "^2.3.0",
"timezones-list": "^3.0.2",
"url-polyfill": "^1.1.9"
},
"devDependencies": {
@ -5596,6 +5596,15 @@
"node": ">=8"
}
},
"node_modules/countries-and-timezones": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/countries-and-timezones/-/countries-and-timezones-3.6.0.tgz",
"integrity": "sha512-8/nHBCs1eKeQ1jnsZVGdqrLYxS8nPcfJn8PnmxdJXWRLZdXsGFR8gnVhRjatGDBjqmPm7H+FtYpBYTPWd0Eiqg==",
"engines": {
"node": ">=8.x",
"npm": ">=5.x"
}
},
"node_modules/create-ecdh": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz",
@ -18881,11 +18890,6 @@
"node": ">=0.6.0"
}
},
"node_modules/timezones-list": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/timezones-list/-/timezones-list-3.0.2.tgz",
"integrity": "sha512-I698hm6Jp/xxkwyTSOr39pZkYKETL8LDJeSIhjxXBfPUAHM5oZNuQ4o9UK3PSkDBOkjATecSOBb3pR1IkIBUsg=="
},
"node_modules/tiny-invariant": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.1.0.tgz",
@ -25268,6 +25272,11 @@
"yaml": "^1.7.2"
}
},
"countries-and-timezones": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/countries-and-timezones/-/countries-and-timezones-3.6.0.tgz",
"integrity": "sha512-8/nHBCs1eKeQ1jnsZVGdqrLYxS8nPcfJn8PnmxdJXWRLZdXsGFR8gnVhRjatGDBjqmPm7H+FtYpBYTPWd0Eiqg=="
},
"create-ecdh": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz",
@ -35674,11 +35683,6 @@
"setimmediate": "^1.0.4"
}
},
"timezones-list": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/timezones-list/-/timezones-list-3.0.2.tgz",
"integrity": "sha512-I698hm6Jp/xxkwyTSOr39pZkYKETL8LDJeSIhjxXBfPUAHM5oZNuQ4o9UK3PSkDBOkjATecSOBb3pR1IkIBUsg=="
},
"tiny-invariant": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.1.0.tgz",

2
client/package.json vendored
View file

@ -16,6 +16,7 @@
"@nivo/line": "^0.64.0",
"axios": "^0.19.2",
"classnames": "^2.2.6",
"countries-and-timezones": "^3.6.0",
"date-fns": "^1.29.0",
"i18next": "^19.6.2",
"i18next-browser-languagedetector": "^4.2.0",
@ -42,7 +43,6 @@
"redux-actions": "^2.6.5",
"redux-form": "^8.3.5",
"redux-thunk": "^2.3.0",
"timezones-list": "^3.0.2",
"url-polyfill": "^1.1.9"
},
"devDependencies": {

View file

@ -423,6 +423,9 @@
"encryption_hostnames": "Názvy hostitelů",
"encryption_reset": "Opravdu chcete obnovit nastavení šifrování?",
"encryption_warning": "Varování",
"encryption_plain_dns_enable": "Povolit běžný DNS",
"encryption_plain_dns_desc": "Ve výchozím nastavení je povolen běžný DNS. Můžete ho zakázat, aby všechna zařízení používala šifrovaný DNS. Chcete-li to provést, musíte povolit alespoň jeden šifrovaný protokol DNS",
"encryption_plain_dns_error": "Chcete-li zakázat běžný DNS, povolte alespoň jeden šifrovaný protokol DNS",
"topline_expiring_certificate": "Váš SSL certifikát brzy vyprší. Aktualizujte <0>Nastavení šifrování</0>.",
"topline_expired_certificate": "Váš SSL certifikát vypršel. Aktualizujte <0>Nastavení šifrování</0>.",
"form_error_port_range": "Zadejte číslo portu v rozmezí 80-65535",

View file

@ -423,6 +423,9 @@
"encryption_hostnames": "Værtsnavne",
"encryption_reset": "Sikker på, at du vil nulstille krypteringsindstillingerne?",
"encryption_warning": "Advarsel",
"encryption_plain_dns_enable": "Aktivér almindelig DNS",
"encryption_plain_dns_desc": "Almindelig DNS er aktiveret som standard. Den kan deaktiveres for at tvinge alle enheder til at bruge krypteret DNS. For at gøre dette, aktivér mindst én krypteret DNS-protokol",
"encryption_plain_dns_error": "Aktivér mindst én krypteret DNS-protokol for at deaktivere almindelig DNS",
"topline_expiring_certificate": "Dit SSL-certifikat er ved at udløbe. Opdatér <0>Krypteringsindstillinger</0>.",
"topline_expired_certificate": "Dit SSL-certifikat er udløbet. Opdatér <0>Krypteringsindstillinger</0>.",
"form_error_port_range": "Angiv portnummer i intervallet 80-65535",

View file

@ -89,7 +89,7 @@
"form_enter_hostname": "Gerätenamen eingeben",
"error_details": "Fehlerdetails",
"response_details": "Einzelheiten der Antwort",
"request_details": "Einzelheiten der Anfrage",
"request_details": "Informationen zur Anfrage",
"client_details": "Einzelheiten des Clients",
"details": "Details",
"back": "Zurück",
@ -121,7 +121,7 @@
"stats_adult": "Gesperrte jugendgefährdende Websites",
"stats_query_domain": "Am häufigsten angefragte Domains",
"for_last_hours": "in die letzte {{count}} Stunde",
"for_last_hours_plural": "in die letzten {{count}} Stunden",
"for_last_hours_plural": "in den letzten {{count}} Stunden",
"for_last_days": "am letzten {{count}} Tag",
"for_last_days_plural": "in den letzten {{count}} Tage",
"stats_disabled": "Die Statistik wurde deaktiviert. Sie können diese in den <0>Einstellungen</0> erneut aktivieren.",
@ -260,7 +260,7 @@
"next_btn": "Nächste",
"loading_table_status": "Wird geladen …",
"page_table_footer_text": "Seite",
"rows_table_footer_text": "Reihen",
"rows_table_footer_text": "Zeilen",
"updated_custom_filtering_toast": "Benutzerdefinierten Filterregeln erfolgreich gespeichert",
"rule_removed_from_custom_filtering_toast": "Regel wurde aus den benutzerdefinierten Filterregeln entfernt: {{rule}}",
"rule_added_to_custom_filtering_toast": "Regel wurde zu den benutzerdefinierten Filterregeln hinzugefügt: {{rule}}",
@ -423,6 +423,9 @@
"encryption_hostnames": "Hostnamen",
"encryption_reset": "Möchten Sie die Verschlüsselungseinstellungen wirklich zurücksetzen?",
"encryption_warning": "Warnhinweis",
"encryption_plain_dns_enable": "Einfaches DNS aktivieren",
"encryption_plain_dns_desc": "Einfaches DNS ist standardmäßig aktiviert. Sie können es deaktivieren, um alle Geräte zu zwingen, verschlüsseltes DNS zu verwenden. Dazu müssen Sie mindestens ein verschlüsseltes DNS-Protokoll aktivieren",
"encryption_plain_dns_error": "Um einfaches DNS zu deaktivieren, aktivieren Sie mindestens ein verschlüsseltes DNS-Protokoll",
"topline_expiring_certificate": "Ihr SSL-Zertifikat läuft demnächst ab. Aktualisieren Sie Ihre <0>Verschlüsselungseinstellungen</0>.",
"topline_expired_certificate": "Ihr SSL-Zertifikat ist abgelaufen. Aktualisieren Sie Ihre <0>Verschlüsselungseinstellungen</0>.",
"form_error_port_range": "Geben Sie die Portnummer zwischen 80 und 65535 ein",
@ -680,8 +683,8 @@
"clear_cache": "Cache leeren",
"make_static": "Statisch machen",
"theme_auto_desc": "Automatisch (basierend auf dem Farbschema Ihres Geräts)",
"theme_dark_desc": "Dunkles Farbschema",
"theme_light_desc": "Helles Farbschema",
"theme_dark_desc": "Dunkles Design",
"theme_light_desc": "Helles Design",
"disable_for_seconds": "Für {{count}} Sekunde",
"disable_for_seconds_plural": "Für {{count}} Sekunden",
"disable_for_minutes": "Für {{count}} Minute",

View file

@ -423,6 +423,9 @@
"encryption_hostnames": "Hostnames",
"encryption_reset": "Are you sure you want to reset encryption settings?",
"encryption_warning": "Warning",
"encryption_plain_dns_enable": "Enable plain DNS",
"encryption_plain_dns_desc": "Plain DNS is enabled by default. You can disable it to force all devices to use encrypted DNS. To do this, you must enable at least one encrypted DNS protocol",
"encryption_plain_dns_error": "To disable plain DNS, enable at least one encrypted DNS protocol",
"topline_expiring_certificate": "Your SSL certificate is about to expire. Update <0>Encryption settings</0>.",
"topline_expired_certificate": "Your SSL certificate is expired. Update <0>Encryption settings</0>.",
"form_error_port_range": "Enter port number in the range of 80-65535",

View file

@ -423,6 +423,9 @@
"encryption_hostnames": "Nombres de hosts",
"encryption_reset": "¿Estás seguro de que deseas restablecer la configuración de cifrado?",
"encryption_warning": "Advertencia",
"encryption_plain_dns_enable": "Activar DNS simple (sin cifrado)",
"encryption_plain_dns_desc": "El DNS simple (sin cifrado) está activado de forma predeterminada. Puedes desactivarlo para obligar a todos los dispositivos a utilizar DNS cifrado. Para ello, debes habilitar al menos un protocolo DNS cifrado",
"encryption_plain_dns_error": "Para desactivar el DNS simple, activa al menos un protocolo DNS cifrado",
"topline_expiring_certificate": "Tu certificado SSL está a punto de expirar. Actualiza la <0>configuración de cifrado</0>.",
"topline_expired_certificate": "Tu certificado SSL ha expirado. Actualiza la <0>configuración de cifrado</0>.",
"form_error_port_range": "Ingresa el número del puerto en el rango de 80 a 65535",

View file

@ -270,12 +270,12 @@
"query_log_cleared": "Pyyntöhistorian tyhjennys onnistui",
"query_log_updated": "Pyyntöhistorian päivitys onnistui",
"query_log_clear": "Tyhjennä pyyntöhistoria",
"query_log_retention": "Kyselylokien kierto",
"query_log_retention": "Pyyntöhistorian kierto",
"query_log_enable": "Käytä historiaa",
"query_log_configuration": "Historian määritys",
"query_log_disabled": "Pyyntöhistoria ei ole käytössä. Voit ottaa sen käyttöön <0>asetuksissa</0>",
"query_log_disabled": "Pyyntöhistoria ei ole käytössä. Voit ottaa sen käyttöön <0>asetuksista</0>.",
"query_log_strict_search": "Käytä tarkalle haulle lainausmerkkejä",
"query_log_retention_confirm": "Haluatko varmasti muuttaa kyselylokin kiertoa? Jos pienennät intervalliarvoa, osa tiedoista menetetään",
"query_log_retention_confirm": "Haluatko varmasti muuttaa pyyntöhistorian kiertoa? Jos pienennät aikaväliä, osa tiedoista menetetään.",
"anonymize_client_ip": "Piilota päätelaitteen IP-osoite",
"anonymize_client_ip_desc": "Älä tallenna päätelaitteen täydellistä IP-osoitetta historiaan ja tilastoihin.",
"dns_config": "DNS-palvelimen määritys",
@ -303,22 +303,22 @@
"download_mobileconfig_dot": "Lataa .mobileconfig-tiedosto DNS-over-TLS -käytölle",
"download_mobileconfig": "Lataa asetustiedosto",
"plain_dns": "Tavallinen DNS",
"form_enter_rate_limit": "Syötä rajoitus",
"rate_limit": "Pyyntöjen ajoitus",
"form_enter_rate_limit": "Syötä pyyntörajoitus",
"rate_limit": "Pyyntöajoitus",
"edns_enable": "Käytä EDNS-päätelaitealivekkoa",
"edns_cs_desc": "Lähetä päätelaitteiden aliverkot DNS-palvelimille.",
"edns_cs_desc": "Lisää EDNS Client Subnet (ECS) -valinta ylävirran pyyntöihin ja kirjaa päätelaitteiden lähettämät arvot pyyntöhistoriaan.",
"edns_use_custom_ip": "Käytä omaa IP-osoitetta EDNS:lle",
"edns_use_custom_ip_desc": "Salli oman IP-osoitteen käyttö EDNS-mekanismille.",
"rate_limit_desc": "Päätelaitteelle sallittu pyyntöjen enimmäismäärä sekunnissa. Arvo 0 tarkoittaa rajatonta.",
"rate_limit_subnet_len_ipv4": "IPv4-osoitteiden aliverkon etuliitteen pituus",
"rate_limit_subnet_len_ipv4_desc": "Aliverkon etuliitteen pituus IPv4-osoitteille, joita käytetään nopeuden rajoittamiseen. Oletusarvo on 24",
"rate_limit_subnet_len_ipv4_error": "IPv4-aliverkon etuliitteen pituuden tulee olla 032",
"rate_limit_subnet_len_ipv4_desc": "Pyyntörajoitukseen käytettävien IPv4-osoitteiden aliverkon etuliitteen pituus. Oletusarvo on 24.",
"rate_limit_subnet_len_ipv4_error": "IPv4-aliverkon etuliitteen pituuden tulee olla väliltä 032.",
"rate_limit_subnet_len_ipv6": "IPv6-osoitteiden aliverkon etuliitteen pituus",
"rate_limit_subnet_len_ipv6_desc": "Aliverkon etuliitteen pituus IPv6-osoitteille, joita käytetään nopeuden rajoittamiseen. Oletusarvo on 56",
"rate_limit_subnet_len_ipv6_error": "IPv6-aliverkon etuliitteen pituuden tulee olla 0128",
"form_enter_rate_limit_subnet_len": "Anna aliverkon etuliitteen pituus nopeuden rajoittamista varten",
"rate_limit_whitelist": "Nopeutta rajoittava sallittu luettelo",
"rate_limit_whitelist_desc": "IP-osoitteet, jotka eivät kuulu nopeusrajoituksen piiriin",
"rate_limit_subnet_len_ipv6_desc": "Pyyntörajoitukseen käytettävien IPv6-osoitteiden aliverkon etuliitteen pituus. Oletusarvo on 56.",
"rate_limit_subnet_len_ipv6_error": "IPv6-aliverkon etuliitteen pituuden tulee olla väliltä 0128.",
"form_enter_rate_limit_subnet_len": "Syötä pyyntörajoitukseen käytettävä aliverkon etuliitteen pituus",
"rate_limit_whitelist": "Pyyntörajoituksen ohituslista",
"rate_limit_whitelist_desc": "IP-osoitteet, jotka eivät kuulu pyyntörajoituksen piiriin.",
"rate_limit_whitelist_placeholder": "Syötä yksi IP-osoite per rivi",
"blocking_ipv4_desc": "Estettyyn A-pyyntöön palautettava IP-osoite",
"blocking_ipv6_desc": "Estettyyn AAAA-pyyntöön palautettava IP-osoite",
@ -423,6 +423,9 @@
"encryption_hostnames": "Isäntänimet",
"encryption_reset": "Haluatko varmasti palauttaa salausasetukset?",
"encryption_warning": "Varoitus",
"encryption_plain_dns_enable": "Käytä tavallista DNS:ää",
"encryption_plain_dns_desc": "Tavallinen DNS on oletusarvoisesti käytössä. Voit poistaa sen käytöstä pakottaaksesi kaikki laitteet käyttämään salattua DNS:ää. Tätä varten sinun on otettava käyttöön ainakin yksi salattu DNS-protokolla.",
"encryption_plain_dns_error": "Voit poistaa tavallisen DNS:n käytöstä ottamalla käyttöön ainakin yhden salatun DNS-protokollan.",
"topline_expiring_certificate": "SSL-varmenteesi on erääntymässä. Päivitä <0>Salausasetukset</0>.",
"topline_expired_certificate": "SSL-varmenteesi on erääntynyt. Päivitä <0>Salausasetukset</0>.",
"form_error_port_range": "Syötä portti väliltä 80-65535",
@ -475,10 +478,10 @@
"access_desc": "Tässä voidaan määrittää AdGuard Homen DNS-palvelimen käyttöoikeussääntöjä.",
"access_allowed_title": "Sallitut päätelaitteet",
"access_allowed_desc": "Lista CIDR-merkinnöistä, IP-osoitteista tai <a>ClientID</a>-tunnisteista. Jos listalla on kohteita, hyväksyy AdGuard Home pyyntöjä vain näiltä päätelaitteilta.",
"access_disallowed_title": "Kielletyt päätelaitteet",
"access_disallowed_title": "Estetyt päätelaitteet",
"access_disallowed_desc": "Lista CIDR-merkinnöistä, IP-osoitteista tai <a>ClientID</a>-tunnisteista. Jos listalla on kohteita, hylkää AdGuard Home näiden päätelaitteiden pyynnöt. Tätä kenttää ei huomioida, jos sallittuja päätelaitteita on määritetty.",
"access_blocked_title": "Kielletyt verkkotunnukset",
"access_blocked_desc": "Ei pidä sekoittaa suodattimiin. AdGuard Home hylkää näiden verkkotunnusten DNS-pyynnöt, eivätkä nämä pyynnöt näy edes pyyntöhistoriassa. Tähän voidaan syöttää tarkkoja verkkotunnuksia, jokerimerkkejä tai URL-suodatussääntöjä, kuten \"example.org\", \"*.example.org\" tai \"||example.org^\".",
"access_blocked_title": "Estetyt verkkotunnukset",
"access_blocked_desc": "Ei pidä sekoittaa suodattimiin. AdGuard Home hylkää näiden verkkotunnusten DNS-pyynnöt, eivätkä nämä pyynnöt myöskään näy pyyntöhistoriassa. Tähän voidaan syöttää tarkkoja verkkotunnuksia, jokerimerkkejä tai URL-suodatussääntöjä, kuten \"example.org\", \"*.example.org\" tai \"||example.org^\".",
"access_settings_saved": "Käytön asetukset tallennettiin",
"updates_checked": "Uusi versio AdGuard Home -ohjelmasta on saatavana\n",
"updates_version_equal": "AdGuard Home on ajan tasalla",
@ -557,7 +560,7 @@
"ignore_domains": "Ohitettavat verkkotunnukset (erotettu rivinvaihdolla)",
"ignore_domains_title": "Ohitettavat verkkotunnukset",
"ignore_domains_desc_stats": "Sääntöihin sopivat kyselyt eivät kirjoitu tilastoihin",
"ignore_domains_desc_query": "Sääntöihin sopivat kyselyt eivät tallennu kyselylokiin",
"ignore_domains_desc_query": "Näitä sääntöjä vastaavia pyyntöjä ei tallenneta pyyntöhistoriaan.",
"interval_hours": "{{count}} tunti",
"interval_hours_plural": "{{count}} tuntia",
"filters_configuration": "Suodatinten määritys",
@ -700,8 +703,8 @@
"custom_retention_input": "Syötä säilytysaika tunteina",
"custom_rotation_input": "Syötä uudistusaika tunteina",
"protection_section_label": "Suojaus",
"log_and_stats_section_label": "Kyselyhistoria ja tilastot",
"ignore_query_log": "Älä huomioi tätä päätettä kyselyhistoriassa",
"log_and_stats_section_label": "Pyyntöhistoria ja tilastot",
"ignore_query_log": "Älä huomioi tätä päätelaitetta pyyntöhistoriassa",
"ignore_statistics": "Älä huomioi tätä päätettä tilastoissa",
"schedule_services": "Keskeytä palveluesto",
"schedule_services_desc": "Määritä palvelunestosuodattimen keskeytysajoitus.",

View file

@ -10,7 +10,7 @@
"bootstrap_dns": "Server DNS bootstrap",
"bootstrap_dns_desc": "Alamat IP server DNS yang digunakan untuk menyelesaikan alamat IP resolver DoH/DoT yang Anda tentukan sebagai upstream. Komentar tidak diizinkan.",
"fallback_dns_title": "Server DNS cadangan",
"fallback_dns_desc": "Daftar server DNS cadangan yang digunakan ketika server DNS hulu tidak merespons. Sintaksnya sama dengan bidang hulu utama di atas.",
"fallback_dns_desc": "Daftar server DNS cadangan yang digunakan ketika server hulu DNS tidak merespons. Sintaksnya sama dengan kolom hulu utama di atas.",
"fallback_dns_placeholder": "Masukkan satu server DNS cadangan per baris",
"local_ptr_title": "Server pembalik DNS pribadi",
"local_ptr_desc": "Server DNS yang digunakan AdGuard Home untuk kueri PTR lokal. Server ini digunakan untuk menyelesaikan nama host klien dengan alamat IP pribadi, misalnya \"192.168.12.34\", menggunakan DNS terbalik. Jika tidak disetel, AdGuard Home menggunakan alamat resolver DNS default OS Anda kecuali untuk alamat AdGuard Home itu sendiri.",
@ -20,7 +20,7 @@
"resolve_clients_title": "Aktifkan resolusi hostname klien",
"resolve_clients_desc": "Menyelesaikan alamat IP klien secara terbalik ke nama host mereka dengan mengirimkan kueri PTR ke resolver yang sesuai (server DNS pribadi untuk klien lokal, server upstream untuk klien dengan alamat IP publik).",
"use_private_ptr_resolvers_title": "Gunakan server pembalik DNS pribadi",
"use_private_ptr_resolvers_desc": "Lakukan pencarian DNS terbalik untuk alamat yang disajikan secara lokal menggunakan server upstream ini. Jika dinonaktifkan, Adguard Home merespon dengan NXDOMAIN untuk semua permintaan PTR tersebut kecuali untuk klien yang diketahui dari DHCP, /etc/hosts, dan seterusnya.",
"use_private_ptr_resolvers_desc": "Lakukan pencarian DNS terbalik untuk alamat yang disajikan secara lokal menggunakan server hulu ini. Jika dinonaktifkan, Adguard Home merespons dengan NXDOMAIN untuk semua permintaan PTR tersebut kecuali untuk klien yang diketahui dari DHCP, /etc/hosts, dan seterusnya.",
"check_dhcp_servers": "Cek untuk server DHCP",
"save_config": "Simpan pengaturan",
"enabled_dhcp": "Server DHCP diaktifkan",
@ -68,7 +68,7 @@
"ip": "IP",
"dhcp_table_hostname": "Nama host",
"dhcp_table_expires": "Kadaluwarsa",
"dhcp_warning": "Jika anda ingin mengaktifkan server DHCP bawaan, pastikan tidak ada server DHCP lain yang aktif. Jika tidak, akan memutus koneksi internet pada perangkat yang telah terhubung!",
"dhcp_warning": "Jika Anda tetap ingin mengaktifkan server DHCP, pastikan tidak ada server DHCP lain yang aktif di jaringan Anda, karena hal ini dapat memutus konektivitas Internet untuk perangkat di jaringan!",
"dhcp_error": "AdGuard Home tidak dapat menentukan apakah ada server DHCP aktif lain pada jaringan",
"dhcp_static_ip_error": "Jika ingin menggunakan server DHCP, alamat IP statis harus diatur. AdGuard Home gagal menentukan jika antarmuka jaringan ini dikonfigurasi menggunakan alamat IP statis. Silakan atur alamat IP statis secara manual.",
"dhcp_dynamic_ip_found": "Sistem Anda menggunakan konfigurasi alamat IP dinamis untuk antarmuka <0>{{interfaceName}}</0>. Untuk menggunakan server DHCP, alamat IP statis harus ditetapkan. Alamat IP Anda saat ini adalah <0>{{ipAddress}}</0>. AdGuard Home akan secara otomatis menetapkan alamat IP ini sebagai statis jika Anda menekan tombol Aktifkan DHCP.",
@ -144,7 +144,7 @@
"enforced_save_search": "Paksa pencarian aman",
"number_of_dns_query_to_safe_search": "Jumlah perminataan DNS ke mesin pencari yang dipaksa Pencarian Aman",
"average_processing_time": "Rata-rata waktu pemrosesan",
"average_upstream_response_time": "Waktu respons server upstream rata-rata",
"average_upstream_response_time": "Rata-rata waktu respons hulu",
"response_time": "Waktu respons",
"average_processing_time_hint": "Rata-rata waktu dalam milidetik untuk pemrosesan sebuah permintaan DNS",
"block_domain_use_filters_and_hosts": "Blokir domain menggunakan filter dan file hosts",
@ -166,7 +166,7 @@
"encryption_settings": "Pengaturan enkripsi",
"dhcp_settings": "Pengaturan DHCP",
"upstream_dns": "Server DNS hulu",
"upstream_dns_help": "Masukkan alamat server per baris. <a>Pelajari lebih</a> mengenai konfigurasi upstream server DNS.",
"upstream_dns_help": "Masukkan satu alamat server per baris. <a>Pelajari lebih lanjut</a> mengenai cara mengonfigurasi server DNS hulu.",
"upstream_dns_configured_in_file": "Diatur dalam {{path}}",
"test_upstream_btn": "Uji hulu",
"upstreams": "Upstream",
@ -192,10 +192,10 @@
"delete_table_action": "Hapus",
"elapsed": "Berlalu",
"filters_and_hosts_hint": "AdGuard Home memahami aturan dasar adblock dan sintak file hosts.",
"no_blocklist_added": "Tiada daftar hitam ditambahkan",
"no_whitelist_added": "Tiada daftar putih ditambahkan",
"add_blocklist": "Tambah daftar hitam",
"add_allowlist": "Tambah daftar putih",
"no_blocklist_added": "Tidak ada daftar hitam yang ditambahkan",
"no_whitelist_added": "Tidak ada daftar putih yang ditambahkan",
"add_blocklist": "Tambahkan daftar hitam",
"add_allowlist": "Tambahkan daftar putih",
"cancel_btn": "Batal",
"enter_name_hint": "Masukkan nama",
"enter_url_or_path_hint": "Masukan sebuah URL atau jalur absolut dari daftar",
@ -289,7 +289,7 @@
"custom_ip": "Custom IP",
"blocking_ipv4": "Blokiran IPv4",
"blocking_ipv6": "Blokiran IPv6",
"blocked_response_ttl": "Respon TLL diblokir",
"blocked_response_ttl": "TTL Respons yang diblokir",
"blocked_response_ttl_desc": "Menentukan berapa detik klien harus menyimpan respons yang difilter dalam cache",
"form_enter_blocked_response_ttl": "Masukkan TTL respons yang diblokir (detik)",
"dnscrypt": "DNSCrypt",
@ -330,7 +330,7 @@
"theme_auto": "Auto",
"theme_light": "Terang",
"theme_dark": "Gelap",
"upstream_dns_client_desc": "Jika Anda biarkan bidang ini kosong, AdGuard Home akan memakai server yang dikonfigurasi di<0>Pengaturan DNS</0>.",
"upstream_dns_client_desc": "Jika Anda biarkan kolom ini kosong, AdGuard Home akan menggunakan server yang dikonfigurasi di <0>pengaturan DNS</0>.",
"tracker_source": "Sumber pelacak",
"source_label": "Sumber",
"found_in_known_domain_db": "Ditemukan di database domain dikenal",
@ -340,7 +340,7 @@
"unknown_filter": "Penyaringan {{filterId}} tidak dikenal",
"known_tracker": "Pelacak yang dikenal",
"install_welcome_title": "Selamat datang di AdGuard Home!",
"install_welcome_desc": "AdGuard Home adalah sebuah server DNS pemblokiran iklan dan pelacak di jaringan. Tujuannya adalah memungkinkan anda mengkontrol seluruh jaringan dan semua perangkat anda, dan ini tidak membutuhkan aplikasi tambahan di klien",
"install_welcome_desc": "AdGuard Home adalah server DNS pemblokir iklan dan pelacak di seluruh jaringan. Tujuannya untuk memungkinkan Anda mengendalikan seluruh jaringan dan semua perangkat Anda, dan tidak perlu menggunakan program sisi klien.",
"install_settings_title": "Antarmuka Halaman Admin",
"install_settings_listen": "Antarmuka pengoperasian",
"install_settings_port": "Port",
@ -362,10 +362,10 @@
"install_submit_title": "Selamat!",
"install_submit_desc": "Prosedur pengaturan telah selesai, dan anda siap untuk mulai menggunakan AdGuard Home.",
"install_devices_router": "Router",
"install_devices_router_desc": "Setelan ini akan secara otomatis mencakup semua perangkat yang terhubung ke router rumah anda dan anda tak perlu mengkonfigurasikan secara manual.",
"install_devices_router_desc": "Penyiapan ini secara otomatis mencakup semua perangkat yang terhubung ke router rumah Anda, tidak perlu mengkonfigurasi masing-masing perangkat secara manual.",
"install_devices_address": "Server DNS AdGuard Home akan menggunakan alamat berikut",
"install_devices_router_list_1": "Buka preferensi untuk router Anda. Biasanya, Anda dapat mengaksesnya dari browser Anda melalui URL, seperti http://192.168.0.1/ atau http://192.168.1.1/. Anda mungkin diminta untuk memasukkan kata sandi. Jika Anda tidak mengingatnya, Anda sering kali dapat mengatur ulang kata sandi dengan menekan tombol pada perute itu sendiri, tetapi perlu diketahui bahwa jika prosedur ini dipilih, Anda mungkin akan kehilangan seluruh konfigurasi perute. Jika router Anda memerlukan aplikasi untuk menyiapkannya, instal aplikasi tersebut di ponsel atau PC Anda dan gunakan untuk mengakses pengaturan router.",
"install_devices_router_list_2": "Temukan pengaturan DHCP / DNS. Cari huruf DNS di sebelah bidang yang memungkinkan dua atau tiga set angka, masing-masing dipecah menjadi empat grup dengan satu hingga tiga digit.",
"install_devices_router_list_2": "Temukan pengaturan DHCP / DNS. Cari huruf DNS di sebelah kolom yang memungkinkan dua atau tiga set angka, masing-masing dipecah menjadi empat kelompok dengan satu hingga tiga digit.",
"install_devices_router_list_3": "Masukkan alamat server AdGuard Home disana",
"install_devices_router_list_4": "Anda tidak dapat menyetel server DNS kustom pada beberapa tipe router. Dalam hal ini mungkin membantu jika Anda mengatur AdGuard Home sebagai <0>server DHCP</0>. Jika tidak, Anda harus mencari petunjuk tentang cara mengkustomisasi server DNS untuk model router khusus Anda.",
"install_devices_windows_list_1": "Buka Panel Kontrol melalui menu Start atau pencarian Windows.",
@ -386,7 +386,7 @@
"install_devices_ios_list_1": "Dari layar beranda, ketuk Pengaturan.",
"install_devices_ios_list_2": "Pilih Wi-Fi di menu sebelah kiri (tidak mungkin untuk mengkonfigurasi DNS untuk jaringan seluler).",
"install_devices_ios_list_3": "Ketuk nama jaringan yang saat ini aktif.",
"install_devices_ios_list_4": "Di bidang DNS, masukkan alamat server AdGuard Home anda.",
"install_devices_ios_list_4": "Di kolom DNS, masukkan alamat server AdGuard Home Anda.",
"get_started": "Mari mulai",
"next": "Selanjutnya",
"open_dashboard": "Buka Beranda",
@ -423,6 +423,9 @@
"encryption_hostnames": "Nama host",
"encryption_reset": "Anda yakin ingin mengatur ulang pengaturan enkripsi?",
"encryption_warning": "Peringatan",
"encryption_plain_dns_enable": "Aktifkan DNS biasa",
"encryption_plain_dns_desc": "DNS Biasa diaktifkan secara standar. Anda dapat menonaktifkannya untuk memaksa semua perangkat menggunakan DNS terenkripsi. Untuk melakukan ini, Anda harus mengaktifkan setidaknya satu protokol DNS terenkripsi",
"encryption_plain_dns_error": "Untuk menonaktifkan DNS biasa, aktifkan setidaknya satu protokol DNS terenkripsi",
"topline_expiring_certificate": "Sertifikat SSL Anda hampir kedaluwarsa. Perbarui <0>Pengaturan enkripsi</0>.",
"topline_expired_certificate": "Sertifikat SSL Anda kedaluwarsa. Perbarui <0>Pengaturan enkripsi</0>.",
"form_error_port_range": "Masukkan nomor port di kisaran 80-65535",
@ -449,7 +452,7 @@
"table_client": "Klien",
"table_name": "Nama",
"save_btn": "Simpan",
"client_add": "Tambah Klien",
"client_add": "Tambahkan Klien",
"client_new": "Klien Baru",
"client_edit": "Ubah Klien",
"client_identifier": "Identifikasi",
@ -459,7 +462,7 @@
"form_enter_subnet_ip": "Masukkan alamat IP di subnet \"{{cidr}}\"",
"form_enter_mac": "Masukkan MAC",
"form_enter_id": "Masukkan pengenal",
"form_add_id": "Tambah pengenal",
"form_add_id": "Tambahkan pengenal",
"form_client_name": "Masukkan nama klien",
"name": "Nama",
"client_global_settings": "Gunakan pengaturan global",
@ -476,7 +479,7 @@
"access_allowed_title": "Klien yang diizinkan",
"access_allowed_desc": "Daftar CIDR, alamat IP, atau <a>ClientID</a>. Jika daftar ini memiliki entri, AdGuard Home hanya akan menerima permintaan dari klien ini.",
"access_disallowed_title": "Klien yang tidak diizinkan",
"access_disallowed_desc": "Daftar CIDR, alamat IP, atau <a>ClientID</a>. Jika daftar ini memiliki entri, AdGuard Home akan membatalkan permintaan dari klien ini. Bidang ini diabaikan jika ada entri di klien yang diizinkan.",
"access_disallowed_desc": "Daftar CIDR, alamat IP, atau <a>ClientID</a>. Jika daftar ini memiliki entri, AdGuard Home akan membatalkan permintaan dari klien ini. Kolom ini diabaikan jika ada entri di daftar putih klien.",
"access_blocked_title": "Domain yang diblokir",
"access_blocked_desc": "Jangan bingung dengan filter. AdGuard Home menghapus kueri DNS yang cocok dengan domain ini, dan kueri ini bahkan tidak muncul di log kueri. Anda dapat menentukan nama domain, karakter pengganti, atau aturan filter URL yang tepat, mis. \"example.org\", \"*.example.org\", atau \"||example.org^\" yang sesuai.",
"access_settings_saved": "Pengaturan akses berhasil disimpan",
@ -485,7 +488,7 @@
"check_updates_now": "Periksa pembaruan sekarang",
"version_request_error": "Pemeriksaan pembaruan gagal. Harap periksa koneksi internet anda.",
"dns_privacy": "DNS Privasi",
"setup_dns_privacy_1": "<0>DNS-over-TLS:</0> Memakai <1>{{address}}</1> string.",
"setup_dns_privacy_1": "<0>DNS melalui TLS:</0> Gunakan <1>{{address}}</1> string.",
"setup_dns_privacy_2": "<0>DNS-over-TLS:</0> Memakai <1>{{address}}</1> string.",
"setup_dns_privacy_3": "<0>Berikut daftar perangkat lunak yang dapat Anda gunakan.</0>",
"setup_dns_privacy_4": "Di perangkat iOS 14 atau macOS Big Sur, Anda dapat mengunduh file '.mobileconfig' khusus yang menambahkan server <highlight>DNS-over-HTTPS</highlight> atau <highlight>DNS-over-TLS</highlight> ke pengaturan DNS.",
@ -505,7 +508,7 @@
"rewrite_added": "DNS rewrite untuk \"{{key}}\" berhasil ditambahkan",
"rewrite_deleted": "DNS rewrite untuk \"{{key}}\" berhasil dihapus",
"rewrite_updated": "Penulisan ulang DNS berhasil diperbarui",
"rewrite_add": "Tambah DNS rewrite",
"rewrite_add": "Tambahkan penulisan ulang DNS",
"rewrite_edit": "Edit penulisan ulang DNS",
"rewrite_not_found": "Tidak ada DNS rewrite ditemukan",
"rewrite_confirm_delete": "Apakah anda yakin ingin menghapus DNS rewrite untuk \"{{key}}\"?",
@ -594,10 +597,10 @@
"fastest_addr": "Alamat IP tercepat",
"fastest_addr_desc": "Kuiri semua server DNS dan kembalikan alamat IP tercepat diantara semua tanggapan. Ini memperlambat pencarian DNS Sebagai Rumah AdGuard harus menunggu tanggapan dari semua server DNS, tapi meningkatkan konektivitas keseluruhan.",
"autofix_warning_text": "Apabila anda menekan \"Perbaiki\", AdGuardHome akan mengatur sistem anda untuk menggunakan server DNS AdGuardHome.",
"autofix_warning_list": "Ini akan melakukan tugas berikut: <0>Nonaktifkan sistem DNSStubListener</0> <0> Atur alamat server DNS ke 127.0.0.1</0> <0>Ganti target tautan simbolis /etc/resolv.conf pakai /run/systemd/resolve/resolv.conf</0> <0>Hentikan DNSStubListener (muat ulang layanan sistemd-resolve service)</0>",
"autofix_warning_list": "Ini akan melakukan tugas berikut: <0>Nonaktifkan sistem DNSStubListener</0> <0>Atur alamat server DNS ke 127.0.0.1</0> <0>Ganti target tautan simbolis /etc/resolv.conf dengan /run/systemd/resolve/resolv.conf</0> <0>Hentikan DNSStubListener (muat ulang layanan sistemd-resolved)</0>",
"autofix_warning_result": "Hasilnya, semua permintaan DNS dari sistem anda akan diproses oleh AdGuardHome secara standar.",
"tags_title": "Tag",
"tags_desc": "Anda dapat memilih tag sesuai dengan klien. Tag dapat dimasukkan dalam aturan pemfilteran dan memungkinkan Anda untuk menerapkannya lebih akurat. <0>Pelajari lebih</0>.",
"tags_desc": "Anda dapat memilih tag yang sesuai dengan klien. Sertakan tag dalam aturan pemfilteran untuk menerapkannya dengan lebih akurat. <0>Pelajari lebih lanjut</0>.",
"form_select_tags": "Pilih tag klien",
"check_title": "Periksa penyaringan",
"check_desc": "Periksa apakah nama host telah tersaring.",
@ -605,7 +608,7 @@
"form_enter_host": "Masukkan nama host",
"filtered_custom_rules": "Tersaring oleh aturan penyaring Buatan",
"choose_from_list": "Pilih dari daftar",
"add_custom_list": "Tambah daftar buatan",
"add_custom_list": "Tambahkan daftar kustom",
"host_whitelisted": "Host didaftar putihkan",
"check_ip": "Alamat IP: {{ip}}",
"check_cname": "CNAME: {{cname}}",
@ -649,7 +652,7 @@
"enter_cache_size": "Masukkan ukuran cache (bytes)",
"enter_cache_ttl_min_override": "Masukkan TTL minimum (detik)",
"enter_cache_ttl_max_override": "Masukkan TTL maksimum (detik)",
"cache_ttl_min_override_desc": "Perpanjang nilai waktu-online singkat (detik) yang diterima dari server upstream saat menyimpan respons DNS.",
"cache_ttl_min_override_desc": "Perpanjang nilai waktu untuk hidup (detik) yang diterima dari server hulu saat menyimpan respons DNS.",
"cache_ttl_max_override_desc": "Tetapkan nilai waktu-online maksimum (detik) untuk entri di cache DNS.",
"ttl_cache_validation": "Nilai TTL cache minimum harus kurang dari atau sama dengan nilai maksimum",
"cache_optimistic": "Caching yang optimis",
@ -736,6 +739,6 @@
"friday_short": "Jum",
"saturday_short": "Sab",
"upstream_dns_cache_configuration": "Konfigurasi cache DNS upstream",
"enable_upstream_dns_cache": "Aktifkan cache DNS untuk konfigurasi upstream kustom klien ini",
"enable_upstream_dns_cache": "Aktifkan cache DNS untuk konfigurasi hulu kustom pada klien ini",
"dns_cache_size": "Ukuran cache DNS, dalam byte"
}

View file

@ -423,6 +423,9 @@
"encryption_hostnames": "Hostnamen",
"encryption_reset": "Ben je zeker dat je de encryptie instellingen wil resetten?",
"encryption_warning": "Waarschuwing",
"encryption_plain_dns_enable": "Gewone DNS inschakelen",
"encryption_plain_dns_desc": "Gewone DNS is standaard ingeschakeld. Je kunt het uitschakelen om alle apparaten te dwingen versleutelde DNS te gebruiken. Om dit te doen, moet je ten minste één versleuteld DNS-protocol inschakelen",
"encryption_plain_dns_error": "Als je gewone DNS wilt uitschakelen, schakel je ten minste één versleuteld DNS-protocol in",
"topline_expiring_certificate": "Jouw SSL-certificaat vervalt binnenkort. Werk de <0>encryptie-instellingen</0> bij.",
"topline_expired_certificate": "Jouw SSL-certificaat is vervallen. Werk de <0>encryptie-instellingen</0> bij.",
"form_error_port_range": "Poortnummer invoeren tussen 80 en 65535",

View file

@ -423,6 +423,9 @@
"encryption_hostnames": "Nomes dos servidores",
"encryption_reset": "Você tem certeza de que deseja redefinir a configuração de criptografia?",
"encryption_warning": "Aviso",
"encryption_plain_dns_enable": "Ativar DNS simples (sem criptografia)",
"encryption_plain_dns_desc": "O DNS simples (sem criptografia) está ativado por padrão. Você pode desativá-lo para forçar todos os dispositivos a usar DNS criptografado. Para fazer isso, você deve ativar pelo menos um protocolo DNS criptografado",
"encryption_plain_dns_error": "Para desativar o DNS simples, ative pelo menos um protocolo DNS criptografado",
"topline_expiring_certificate": "Seu certificado SSL está prestes a expirar. Atualize suas <0>configurações de criptografia</]0>",
"topline_expired_certificate": "Seu certificado SSL está expirado. Atualize suas <0>configurações de criptografia</0>",
"form_error_port_range": "Digite um número de porta entre 80 e 65535",

View file

@ -423,6 +423,9 @@
"encryption_hostnames": "Nomes dos servidores",
"encryption_reset": "Tem a certeza de que deseja repor a definição de criptografia?",
"encryption_warning": "Cuidado",
"encryption_plain_dns_enable": "Habilitar DNS simples (sem criptografia)",
"encryption_plain_dns_desc": "O DNS simples (sem criptografia) está ativado por padrão. Pode desativá-lo para forçar todos os dispositivos a usar DNS criptografado. Para isso, deve ativar pelo menos um protocolo DNS criptografado",
"encryption_plain_dns_error": "Para desabilitar o DNS simples, habilite pelo menos um protocolo DNS criptografado",
"topline_expiring_certificate": "O seu certificado SSL está prestes a expirar. Atualize as suas <0>definições de criptografia</0>.",
"topline_expired_certificate": "O seu certificado SSL está expirado. Atualize as suas <0>definições de criptografia</0>.",
"form_error_port_range": "Digite um numero de porta entre 80 e 65535",

View file

@ -423,6 +423,9 @@
"encryption_hostnames": "Имена хостов",
"encryption_reset": "Вы уверены, что хотите сбросить настройки шифрования?",
"encryption_warning": "Предупреждение",
"encryption_plain_dns_enable": "Включить незашифрованный DNS",
"encryption_plain_dns_desc": "Незашифрованный DNS включён по умолчанию. Вы можете отключить его, чтобы заставить все устройства использовать зашифрованный DNS. Для этого необходимо включить хотя бы один зашифрованный протокол DNS",
"encryption_plain_dns_error": "Чтобы отключить незашифрованный DNS, включите хотя бы один зашифрованный протокол DNS",
"topline_expiring_certificate": "Ваш SSL-сертификат скоро истекает. Обновите <0>Настройки шифрования</0>.",
"topline_expired_certificate": "Ваш SSL-сертификат истёк. Обновите <0>Настройки шифрования</0>.",
"form_error_port_range": "Введите номер порта из интервала 80-65535",

View file

@ -208,9 +208,9 @@
"dns_test_not_ok_toast": "\"{{key}}\" සේවාදායක(ය): භාවිතා කිරීමට නොහැකි විය, ඔබ එය නිවැරදිව ලියා ඇතිදැයි පරීක්‍ෂා කරන්න",
"unblock": "අනවහිර",
"block": "අවහිර",
"disallow_this_client": "මෙම අනුග්‍රාහකයට නොඉඩ දෙන්න",
"disallow_this_client": "මෙම අනුග්‍රාහකයට ඉඩ නොදෙන්න",
"allow_this_client": "මෙම අනුග්‍රාහකයට ඉඩ දෙන්න",
"block_for_this_client_only": "මෙම අනුග්‍රාහකයට පමණක් අවහිර කරන්න",
"block_for_this_client_only": "මෙම අනුග්‍රාහකයට අවහිර කරන්න",
"unblock_for_this_client_only": "මෙම අනුග්‍රාහකයට පමණක් අනවහිර කරන්න",
"time_table_header": "වේලාව",
"date": "දිනය",

View file

@ -423,6 +423,9 @@
"encryption_hostnames": "Názvy hostiteľov",
"encryption_reset": "Naozaj chcete obnoviť nastavenia šifrovania?",
"encryption_warning": "Varovanie",
"encryption_plain_dns_enable": "Zapnúť jednoduchý DNS",
"encryption_plain_dns_desc": "Jednoduchý DNS je predvolene zapnutý. Môžete ho vypnúť, aby ste prinútili všetky zariadenia používať šifrovaný DNS. Ak to chcete urobiť, musíte zapnúť aspoň jeden šifrovaný DNS protokol",
"encryption_plain_dns_error": "Ak chcete vypnúť jednoduchý DNS protokol, zapnite aspoň jeden šifrovaný DNS protokol",
"topline_expiring_certificate": "Váš SSL certifikát čoskoro vyprší. Aktualizujte <0>Nastavenia šifrovania</0>.",
"topline_expired_certificate": "Váš SSL certifikát vypršal. Aktualizujte <0>Nastavenia šifrovania</0>.",
"form_error_port_range": "Zadajte číslo portu v rozsahu 80-65535",

View file

@ -423,6 +423,9 @@
"encryption_hostnames": "Imena gostiteljev",
"encryption_reset": "Ali ste prepričani, da želite ponastaviti nastavitve šifriranja?",
"encryption_warning": "Opozorilo",
"encryption_plain_dns_enable": "Omogoči navaden DNS",
"encryption_plain_dns_desc": "Navaden DNS je privzeto omogočen. Lahko ga onemogočite, da vse naprave prisilite k uporabi šifriranega DNS-ja. Če želite to narediti, morate omogočiti vsaj en šifriran protokol DNS",
"encryption_plain_dns_error": "Da onemogočite navaden DNS, omogočite vsaj en šifriran protokol DNS",
"topline_expiring_certificate": "Vaš e digitalno potrdilo SSL bo kmalu poteklol. Posodobite <0>Nastavitve šifriranja</0>.",
"topline_expired_certificate": "Vaše digitalno potrdilo SSL je poteklo. Posodobi <0>Nastavitve šifriranja</0>.",
"form_error_port_range": "Vnesite številko vrat v razponu med 80-65535",

View file

@ -423,6 +423,9 @@
"encryption_hostnames": "Ana makine adları",
"encryption_reset": "Şifreleme ayarlarını sıfırlamak istediğinizden emin misiniz?",
"encryption_warning": "Uyarı",
"encryption_plain_dns_enable": "Düz DNS'i etkinleştir",
"encryption_plain_dns_desc": "Düz DNS varsayılan olarak etkindir. Tüm aygıtları şifrelenmiş DNS kullanmaya zorlamak için bunu devre dışı bırakabilirsiniz. Bunu yapmak için en az bir şifrelenmiş DNS protokolünü etkinleştirmeniz gerekir",
"encryption_plain_dns_error": "Düz DNS'i devre dışı bırakmak için en az bir şifrelenmiş DNS protokolünü etkinleştirin",
"topline_expiring_certificate": "SSL sertifikanızın süresi sona üzere. <0>Şifreleme ayarlarını</0> güncelleyin.",
"topline_expired_certificate": "SSL sertifikanızın süresi sona erdi. <0>Şifreleme ayarlarını</0> güncelleyin.",
"form_error_port_range": "80-65535 aralığında geçerli bir bağlantı noktası değeri girin",

View file

@ -423,6 +423,9 @@
"encryption_hostnames": "Назви вузлів",
"encryption_reset": "Ви впевнені, що хочете скинути налаштування шифрування?",
"encryption_warning": "Попередження",
"encryption_plain_dns_enable": "Увімкнути звичайний DNS",
"encryption_plain_dns_desc": "Звичайний DNS усталено увімкнений. Ви можете вимкнути його, щоб змусити всі пристрої використовувати зашифрований DNS. Для цього необхідно увімкнути хоча б один зашифрований протокол DNS",
"encryption_plain_dns_error": "Щоб вимкнути звичайний DNS, увімкніть принаймні один зашифрований протокол DNS",
"topline_expiring_certificate": "Ваш сертифікат SSL скоро закінчиться. Оновіть <0>Налаштування шифрування</0>.",
"topline_expired_certificate": "Термін дії вашого сертифіката SSL закінчився. Оновіть <0>Налаштування шифрування</0>.",
"form_error_port_range": "Введіть значення порту в діапазоні 8065535",

View file

@ -310,15 +310,15 @@
"edns_use_custom_ip": "為 EDNS 使用自訂的 IP",
"edns_use_custom_ip_desc": "允許為 EDNS 使用自訂的 IP",
"rate_limit_desc": "每個用戶端被允許的每秒請求之數量。設定它為 0 表示無限制。",
"rate_limit_subnet_len_ipv4": "IPv4 位址的子網路前綴長度",
"rate_limit_subnet_len_ipv4": "用於 IPv4 位址的子網路前綴長度",
"rate_limit_subnet_len_ipv4_desc": "用於速率限制的 IPv4 位址的子網路前綴長度。預設值為 24",
"rate_limit_subnet_len_ipv4_error": "IPv4 子網路前綴長度應在 0 至 32 之間",
"rate_limit_subnet_len_ipv6": "IPv6 位址的子網路前綴長度",
"rate_limit_subnet_len_ipv6": "用於 IPv6 位址的子網路前綴長度",
"rate_limit_subnet_len_ipv6_desc": "用於速率限制的 IPv6 位址的子網路前綴長度。預設值為 56",
"rate_limit_subnet_len_ipv6_error": "IPv6 子網路前綴長度應在 0 至 128 之間",
"form_enter_rate_limit_subnet_len": "輸入用於速率限制的子網路前綴長度",
"rate_limit_whitelist": "速率限制允許清單",
"rate_limit_whitelist_desc": "從速率限制排除的 IP 位址",
"rate_limit_whitelist_desc": "從速率限制排除的 IP 位址",
"rate_limit_whitelist_placeholder": "每行輸入一個 IP 位址",
"blocking_ipv4_desc": "要被返回給已封鎖的 A 請求之 IP 位址",
"blocking_ipv6_desc": "要被返回給已封鎖的 AAAA 請求之 IP 位址",

View file

@ -36,7 +36,7 @@ const Examples = () => (
<Trans
components={[
<a
href="https://github.com/AdguardTeam/AdGuardHome/wiki/Hosts-Blocklists"
href="https://link.adtidy.org/forward.html?action=dns_kb_filtering_syntax&from=ui&app=home"
target="_blank"
rel="noopener noreferrer"
key="0"

View file

@ -1,5 +1,5 @@
import React from 'react';
import timezones from 'timezones-list';
import ct from 'countries-and-timezones';
import { useTranslation } from 'react-i18next';
import PropTypes from 'prop-types';
@ -15,6 +15,8 @@ export const Timezone = ({
setTimezone(event.target.value);
};
const timezones = ct.getAllTimezones();
return (
<div className="schedule__timezone">
<label className="form__label form__label--with-desc mb-2">
@ -30,9 +32,9 @@ export const Timezone = ({
{t('schedule_timezone')}
</option>
{/* TODO: get timezones from backend method when the method is ready */}
{timezones.map((zone) => (
<option key={zone.name} value={zone.tzCode}>
{zone.label}
{Object.keys(timezones).map((zone) => (
<option key={zone} value={zone}>
{zone} (GMT{timezones[zone].utcOffsetStr})
</option>
))}
</select>

View file

@ -1,4 +1,4 @@
import React, { useState, useMemo } from 'react';
import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';
import PropTypes from 'prop-types';
import cn from 'classnames';
@ -21,10 +21,7 @@ export const ScheduleForm = ({
const onModalOpen = () => setModalOpen(true);
const onModalClose = () => setModalOpen(false);
const filteredScheduleKeys = useMemo(() => (
schedule ? Object.keys(schedule).filter((v) => v !== 'time_zone') : []
), [schedule]);
const filteredScheduleKeys = schedule ? Object.keys(schedule).filter((v) => v !== 'time_zone') : [];
const scheduleMap = new Map();
filteredScheduleKeys.forEach((day) => scheduleMap.set(day, schedule[day]));

View file

@ -162,7 +162,7 @@ let Form = (props) => {
const [activeTabLabel, setActiveTabLabel] = useState('settings');
const handleScheduleSubmit = (values) => {
change('blocked_services_schedule', values);
change('blocked_services_schedule', { ...values });
};
const tabs = {
@ -371,7 +371,7 @@ let Form = (props) => {
</div>
<div className="form__desc mt-0 mb-2">
<Trans components={[
<a target="_blank" rel="noopener noreferrer" href="https://github.com/AdguardTeam/AdGuardHome/wiki/Hosts-Blocklists#ctag"
<a target="_blank" rel="noopener noreferrer" href="https://link.adtidy.org/forward.html?action=dns_kb_filtering_syntax_ctag&from=ui&app=home"
key="0">link</a>,
]}>
tags_desc

View file

@ -3,7 +3,7 @@ import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import ReactTable from 'react-table';
import { Trans, withTranslation } from 'react-i18next';
import { LEASES_TABLE_DEFAULT_PAGE_SIZE } from '../../../helpers/constants';
import { LEASES_TABLE_DEFAULT_PAGE_SIZE, MODAL_TYPE } from '../../../helpers/constants';
import { sortIp } from '../../../helpers/helpers';
import { toggleLeaseModal } from '../../../actions';
@ -18,7 +18,10 @@ class Leases extends Component {
convertToStatic = (data) => () => {
const { dispatch } = this.props;
dispatch(toggleLeaseModal(data));
dispatch(toggleLeaseModal({
type: MODAL_TYPE.ADD_LEASE,
config: data,
}));
}
makeStatic = ({ row }) => {

View file

@ -41,7 +41,7 @@ const Form = ({
cache_ttl_max, cache_ttl_min,
} = useSelector((state) => state.form[FORM_NAME.CACHE].values, shallowEqual);
const minExceedsMax = cache_ttl_min > cache_ttl_max;
const minExceedsMax = cache_ttl_min > 0 && cache_ttl_max > 0 && cache_ttl_min > cache_ttl_max;
const handleClearCache = () => {
if (window.confirm(t('confirm_dns_cache_clear'))) {

View file

@ -12,7 +12,7 @@ import {
toNumber,
} from '../../../helpers/form';
import {
validateServerName, validateIsSafePort, validatePort, validatePortQuic, validatePortTLS,
validateServerName, validateIsSafePort, validatePort, validatePortQuic, validatePortTLS, validatePlainDns,
} from '../../../helpers/validators';
import i18n from '../../../i18n';
import KeyStatus from './KeyStatus';
@ -47,6 +47,7 @@ const clearFields = (change, setTlsConfig, validateTlsConfig, t) => {
force_https: false,
enabled: false,
private_key_saved: false,
serve_plain_dns: true,
};
// eslint-disable-next-line no-alert
if (window.confirm(t('encryption_reset'))) {
@ -83,6 +84,7 @@ let Form = (props) => {
handleSubmit,
handleChange,
isEnabled,
servePlainDns,
certificateChain,
privateKey,
certificatePath,
@ -109,21 +111,24 @@ let Form = (props) => {
privateKeySaved,
} = props;
const isSavingDisabled = invalid
|| submitting
|| processingConfig
|| processingValidate
|| !valid_key
|| !valid_cert
|| !valid_pair;
const isSavingDisabled = () => {
const processing = submitting || processingConfig || processingValidate;
if (servePlainDns && !isEnabled) {
return invalid || processing;
}
return invalid || processing || !valid_key || !valid_cert || !valid_pair;
};
const isDisabled = isSavingDisabled();
const isWarning = valid_key && valid_cert && valid_pair;
return (
<form onSubmit={handleSubmit}>
<div className="row">
<div className="col-12">
<div className="form__group form__group--settings">
<div className="form__group form__group--settings mb-3">
<Field
name="enabled"
type="checkbox"
@ -135,6 +140,19 @@ let Form = (props) => {
<div className="form__desc">
<Trans>encryption_enable_desc</Trans>
</div>
<div className="form__group mb-3 mt-5">
<Field
name="serve_plain_dns"
type="checkbox"
component={CheckboxField}
placeholder={t('encryption_plain_dns_enable')}
onChange={handleChange}
validate={validatePlainDns}
/>
</div>
<div className="form__desc">
<Trans>encryption_plain_dns_desc</Trans>
</div>
<hr />
</div>
<div className="col-12">
@ -412,8 +430,8 @@ let Form = (props) => {
<div className="btn-list mt-2">
<button
type="submit"
disabled={isDisabled}
className="btn btn-success btn-standart"
disabled={isSavingDisabled}
>
<Trans>save_config</Trans>
</button>
@ -434,6 +452,7 @@ Form.propTypes = {
handleSubmit: PropTypes.func.isRequired,
handleChange: PropTypes.func,
isEnabled: PropTypes.bool.isRequired,
servePlainDns: PropTypes.bool.isRequired,
certificateChain: PropTypes.string.isRequired,
privateKey: PropTypes.string.isRequired,
certificatePath: PropTypes.string.isRequired,
@ -467,6 +486,7 @@ const selector = formValueSelector(FORM_NAME.ENCRYPTION);
Form = connect((state) => {
const isEnabled = selector(state, 'enabled');
const servePlainDns = selector(state, 'serve_plain_dns');
const certificateChain = selector(state, 'certificate_chain');
const privateKey = selector(state, 'private_key');
const certificatePath = selector(state, 'certificate_path');
@ -476,6 +496,7 @@ Form = connect((state) => {
const privateKeySaved = selector(state, 'private_key_saved');
return {
isEnabled,
servePlainDns,
certificateChain,
privateKey,
certificatePath,

View file

@ -25,7 +25,8 @@ class Encryption extends Component {
handleFormChange = debounce((values) => {
const submitValues = this.getSubmitValues(values);
if (submitValues.enabled) {
if (submitValues.enabled || submitValues.serve_plain_dns) {
this.props.validateTlsConfig(submitValues);
}
}, DEBOUNCE_TIMEOUT);
@ -85,6 +86,7 @@ class Encryption extends Component {
certificate_path,
private_key_path,
private_key_saved,
serve_plain_dns,
} = encryption;
const initialValues = this.getInitialValues({
@ -99,6 +101,7 @@ class Encryption extends Component {
certificate_path,
private_key_path,
private_key_saved,
serve_plain_dns,
});
return (

View file

@ -181,6 +181,7 @@ export const MODAL_TYPE = {
ADD_REWRITE: 'ADD_REWRITE',
EDIT_REWRITE: 'EDIT_REWRITE',
EDIT_LEASE: 'EDIT_LEASE',
ADD_LEASE: 'ADD_LEASE',
};
export const CLIENT_ID = {
@ -435,7 +436,7 @@ export const SCHEME_TO_PROTOCOL_MAP = {
export const DNS_REQUEST_OPTIONS = {
PARALLEL: 'parallel',
FASTEST_ADDR: 'fastest_addr',
LOAD_BALANCING: '',
LOAD_BALANCING: 'load_balance',
};
export const DHCP_FORM_NAMES = {

View file

@ -142,11 +142,11 @@ export default {
"homepage": "https://github.com/AdguardTeam/AdGuardSDNSFilter",
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_1.txt"
},
"adway_default_blocklist": {
"name": "AdAway Default Blocklist",
"awavenue_ads_rule": {
"name": "AWAvenue Ads Rule",
"categoryId": "general",
"homepage": "https://github.com/AdAway/adaway.github.io/",
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_2.txt"
"homepage": "https://awavenue.top/",
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_53.txt"
},
"curben_phishing_filter": {
"name": "Phishing URL Blocklist (PhishTank and OpenPhish)",
@ -190,6 +190,12 @@ export default {
"homepage": "https://github.com/hagezi/dns-blocklists#piracy",
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_46.txt"
},
"hagezi_dyndns_blocklist": {
"name": "HaGeZi's DynDNS Blocklist",
"categoryId": "security",
"homepage": "https://github.com/hagezi/dns-blocklists",
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_54.txt"
},
"hagezi_encrypted_dns_vpn_tor_proxy_bypass": {
"name": "HaGeZi's Encrypted DNS/VPN/TOR/Proxy Bypass",
"categoryId": "security",

View file

@ -180,7 +180,7 @@ export const CheckboxField = ({
{!disabled
&& touched
&& error
&& <span className="form__message form__message--error"><Trans>{error}</Trans></span>}
&& <div className="form__message form__message--error mt-1"><Trans>{error}</Trans></div>}
</>;
CheckboxField.propTypes = {

View file

@ -1,5 +1,5 @@
{
"timeUpdated": "2023-12-01T15:24:07.522Z",
"timeUpdated": "2024-01-22T00:10:10.554Z",
"categories": {
"0": "audio_video_player",
"1": "comments",
@ -10758,9 +10758,10 @@
},
"localytics": {
"name": "Localytics",
"categoryId": 6,
"url": "http://www.localytics.com/",
"companyId": "localytics"
"categoryId": 101,
"url": "https://uplandsoftware.com/localytics/",
"companyId": "upland",
"source": "AdGuard"
},
"lockerdome": {
"name": "LockerDome",
@ -15613,10 +15614,11 @@
"companyId": "sharecompany"
},
"sharepoint": {
"name": "Microsoft SharePoint",
"categoryId": 2,
"url": "https://products.office.com/en-us/sharepoint/sharepoint-online-collaboration-software",
"companyId": "microsoft"
"name": "SharePoint",
"categoryId": 8,
"url": "https://www.microsoft.com/microsoft-365/sharepoint/collaboration",
"companyId": "microsoft",
"source": "AdGuard"
},
"sharethis": {
"name": "ShareThis",
@ -22958,6 +22960,7 @@
"loadercdn.com": "loadercdn.com",
"loadsource.org": "loadsource.org",
"web.localytics.com": "localytics",
"localytics.com": "localytics",
"cdn2.lockerdome.com": "lockerdome",
"addtoany.com": "lockerz_share",
"pixel.loganmedia.mobi": "logan_media",
@ -23254,6 +23257,7 @@
"k-msedge.net": "msedge",
"l-msedge.net": "msedge",
"s-msedge.net": "msedge",
"spo-msedge.net": "msedge",
"t-msedge.net": "msedge",
"wac-msedge.net": "msedge",
"msn.com": "msn",
@ -23521,8 +23525,10 @@
"outbrain.com": "outbrain",
"outbrainimg.com": "outbrain",
"live.com": "outlook",
"cloud.microsoft": "outlook",
"hotmail.com": "outlook",
"outlook.com": "outlook",
"svc.ms": "outlook",
"overheat.it": "overheat.it",
"oewabox.at": "owa",
"owneriq.net": "owneriq",
@ -23790,6 +23796,7 @@
"rcsmediagroup.it": "rcs.it",
"d335luupugsy2.cloudfront.net": "rd_station",
"rea-group.com": "rea_group",
"reagroupdata.com.au": "rea_group",
"reastatic.net": "rea_group",
"d12ulf131zb0yj.cloudfront.net": "reachforce",
"reachforce.com": "reachforce",
@ -24109,6 +24116,8 @@
"quintrics.nl": "sharecompany",
"sharecompany.nl": "sharecompany",
"sharepointonline.com": "sharepoint",
"onmicrosoft.com": "sharepoint",
"sharepoint.com": "sharepoint",
"sharethis.com": "sharethis",
"shareth.ru": "sharethrough",
"sharethrough.com": "sharethrough",

View file

@ -389,3 +389,18 @@ export const validateIPv6Subnet = (value) => {
}
return undefined;
};
/**
* @returns {undefined|string}
* @param value
* @param allValues
*/
export const validatePlainDns = (value, allValues) => {
const { enabled } = allValues;
if (!enabled && !value) {
return 'encryption_plain_dns_error';
}
return undefined;
};

View file

@ -128,7 +128,8 @@ const dhcp = handleActions(
const newState = {
...state,
isModalOpen: !state.isModalOpen,
leaseModalConfig: payload,
modalType: payload?.type || '',
leaseModalConfig: payload?.config,
};
return newState;
},

View file

@ -1,7 +1,7 @@
import { handleActions } from 'redux-actions';
import * as actions from '../actions/dnsConfig';
import { ALL_INTERFACES_IP, BLOCKING_MODES } from '../helpers/constants';
import { ALL_INTERFACES_IP, BLOCKING_MODES, DNS_REQUEST_OPTIONS } from '../helpers/constants';
const DEFAULT_BLOCKING_IPV4 = ALL_INTERFACES_IP;
const DEFAULT_BLOCKING_IPV6 = '::';
@ -15,6 +15,7 @@ const dnsConfig = handleActions(
blocking_ipv4,
blocking_ipv6,
upstream_dns,
upstream_mode,
fallback_dns,
bootstrap_dns,
local_ptr_upstreams,
@ -33,6 +34,7 @@ const dnsConfig = handleActions(
local_ptr_upstreams: (local_ptr_upstreams && local_ptr_upstreams.join('\n')) || '',
ratelimit_whitelist: (ratelimit_whitelist && ratelimit_whitelist.join('\n')) || '',
processingGetConfig: false,
upstream_mode: upstream_mode === '' ? DNS_REQUEST_OPTIONS.LOAD_BALANCING : upstream_mode,
};
},

View file

@ -62,6 +62,7 @@ const encryption = handleActions({
processingConfig: false,
processingValidate: false,
enabled: false,
serve_plain_dns: false,
dns_names: null,
force_https: false,
issuer: '',

36
go.mod
View file

@ -3,12 +3,13 @@ module github.com/AdguardTeam/AdGuardHome
go 1.20
require (
github.com/AdguardTeam/dnsproxy v0.60.1
github.com/AdguardTeam/golibs v0.18.0
github.com/AdguardTeam/dnsproxy v0.63.1
github.com/AdguardTeam/golibs v0.19.0
github.com/AdguardTeam/urlfilter v0.17.3
github.com/NYTimes/gziphandler v1.1.1
github.com/ameshkov/dnscrypt/v2 v2.2.7
github.com/bluele/gcache v0.0.2
github.com/c2h5oh/datasize v0.0.0-20231215233829-aa82cc1e6500
github.com/digineo/go-ipset/v2 v2.2.1
github.com/dimfeld/httptreemux/v5 v5.5.0
github.com/fsnotify/fsnotify v1.7.0
@ -16,7 +17,7 @@ require (
github.com/google/go-cmp v0.6.0
github.com/google/gopacket v1.1.19
github.com/google/renameio/v2 v2.0.0
github.com/google/uuid v1.4.0
github.com/google/uuid v1.5.0
github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2
github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86
github.com/kardianos/service v1.2.2
@ -26,15 +27,15 @@ require (
// TODO(a.garipov): This package is deprecated; find a new one or use our
// own code for that. Perhaps, use gopacket.
github.com/mdlayher/raw v0.1.0
github.com/miekg/dns v1.1.57
github.com/quic-go/quic-go v0.40.0
github.com/miekg/dns v1.1.58
github.com/quic-go/quic-go v0.40.1
github.com/stretchr/testify v1.8.4
github.com/ti-mo/netfilter v0.5.1
go.etcd.io/bbolt v1.3.8
golang.org/x/crypto v0.16.0
golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb
golang.org/x/net v0.19.0
golang.org/x/sys v0.15.0
golang.org/x/crypto v0.18.0
golang.org/x/exp v0.0.0-20240119083558-1b970713d09a
golang.org/x/net v0.20.0
golang.org/x/sys v0.16.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1
gopkg.in/yaml.v3 v3.0.1
howett.net/plist v1.0.1
@ -47,20 +48,21 @@ require (
github.com/beefsack/go-rate v0.0.0-20220214233405-116f4ca011a0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/google/pprof v0.0.0-20231205033806-a5a03c77bf08 // indirect
github.com/google/pprof v0.0.0-20240117000934-35fc243c5815 // indirect
// TODO(a.garipov): Upgrade to v0.5.0 once we switch to Go 1.21+.
github.com/mdlayher/socket v0.4.1 // indirect
github.com/onsi/ginkgo/v2 v2.13.2 // indirect
github.com/mdlayher/socket v0.5.0 // indirect
github.com/onsi/ginkgo/v2 v2.15.0 // indirect
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
github.com/pierrec/lz4/v4 v4.1.19 // indirect
github.com/pierrec/lz4/v4 v4.1.21 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/quic-go/qpack v0.4.0 // indirect
github.com/quic-go/qtls-go1-20 v0.4.1 // indirect
github.com/u-root/uio v0.0.0-20230305220412-3e8cd9d6bf63 // indirect
go.uber.org/mock v0.3.0 // indirect
github.com/u-root/uio v0.0.0-20240118234441-a3c409a6018e // indirect
go.uber.org/mock v0.4.0 // indirect
golang.org/x/mod v0.14.0 // indirect
golang.org/x/sync v0.5.0 // indirect
golang.org/x/sync v0.6.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/tools v0.16.0 // indirect
golang.org/x/tools v0.17.0 // indirect
gonum.org/v1/gonum v0.14.0 // indirect
)

74
go.sum
View file

@ -1,7 +1,7 @@
github.com/AdguardTeam/dnsproxy v0.60.1 h1:YveGe7UZLaAiePkaV3orkc0IIfPX9vi/qQDIFdeO//A=
github.com/AdguardTeam/dnsproxy v0.60.1/go.mod h1:B7FvvTFQZBfey1cJXQo732EyCLX6xj4JqrciCawATzg=
github.com/AdguardTeam/golibs v0.18.0 h1:ckS2YK7t2Ub6UkXl0fnreVaM15Zb07Hh1gmFqttjpWg=
github.com/AdguardTeam/golibs v0.18.0/go.mod h1:DKhCIXHcUYtBhU8ibTLKh1paUL96n5zhQBlx763sj+U=
github.com/AdguardTeam/dnsproxy v0.63.1 h1:CilxSuLYcuYpbPCGB7w41UUqWRMu3dvj4c9TvkIrpBg=
github.com/AdguardTeam/dnsproxy v0.63.1/go.mod h1:dRRAFOjrq4QYM92jGs4lt4BoY0Dm3EY3HkaleoM2Feo=
github.com/AdguardTeam/golibs v0.19.0 h1:y/x+Xn3pDg1ZfQ+QEZapPJqaeVYUIMp/EODMtVhn7PM=
github.com/AdguardTeam/golibs v0.19.0/go.mod h1:3WunclLLfrVAq7fYQRhd6f168FHOEMssnipVXCxDL/w=
github.com/AdguardTeam/urlfilter v0.17.3 h1:fg/ObbnO0Cv6aw0tW6N/ETDMhhNvmcUUOZ7HlmKC3rw=
github.com/AdguardTeam/urlfilter v0.17.3/go.mod h1:Jru7jFfeH2CoDf150uDs+rRYcZBzHHBz05r9REyDKyE=
github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I=
@ -18,6 +18,8 @@ github.com/beefsack/go-rate v0.0.0-20220214233405-116f4ca011a0 h1:0b2vaepXIfMsG+
github.com/beefsack/go-rate v0.0.0-20220214233405-116f4ca011a0/go.mod h1:6YNgTHLutezwnBvyneBbwvB8C82y3dcoOj5EQJIdGXA=
github.com/bluele/gcache v0.0.2 h1:WcbfdXICg7G/DGBh1PFfcirkWOQV+v077yF1pSy3DGw=
github.com/bluele/gcache v0.0.2/go.mod h1:m15KV+ECjptwSPxKhOhQoAFQVtUFjTVkc3H8o0t/fp0=
github.com/c2h5oh/datasize v0.0.0-20231215233829-aa82cc1e6500 h1:6lhrsTEnloDPXyeZBvSYvQf8u86jbKehZPVDDlkgDl4=
github.com/c2h5oh/datasize v0.0.0-20231215233829-aa82cc1e6500/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -41,13 +43,13 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
github.com/google/pprof v0.0.0-20231205033806-a5a03c77bf08 h1:PxlBVtIFHR/mtWk2i0gTEdCz+jBnqiuHNSki0epDbVs=
github.com/google/pprof v0.0.0-20231205033806-a5a03c77bf08/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
github.com/google/pprof v0.0.0-20240117000934-35fc243c5815 h1:WzfWbQz/Ze8v6l++GGbGNFZnUShVpP/0xffCPLL+ax8=
github.com/google/pprof v0.0.0-20240117000934-35fc243c5815/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
github.com/google/renameio/v2 v2.0.0 h1:UifI23ZTGY8Tt29JbYFiuyIU3eX+RNFtUwefq9qAhxg=
github.com/google/renameio/v2 v2.0.0/go.mod h1:BtmJXm5YlszgC+TD4HOEEUFgkJP3nLxehU6hfe7jRt4=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714 h1:/jC7qQFrv8CrSJVmaolDVOxTfS9kc36uB6H40kdbQq8=
github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2 h1:9K06NfxkBh25x56yVhWWlKFE8YpicaSfHwoV8SFbueA=
github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2/go.mod h1:3A9PQ1cunSDF/1rbTq99Ts4pVnycWg+vlPkfeD2NLFI=
@ -71,19 +73,19 @@ github.com/mdlayher/packet v1.1.2/go.mod h1:GEu1+n9sG5VtiRE4SydOmX5GTwyyYlteZiFU
github.com/mdlayher/raw v0.1.0 h1:K4PFMVy+AFsp0Zdlrts7yNhxc/uXoPVHi9RzRvtZF2Y=
github.com/mdlayher/raw v0.1.0/go.mod h1:yXnxvs6c0XoF/aK52/H5PjsVHmWBCFfZUfoh/Y5s9Sg=
github.com/mdlayher/socket v0.2.1/go.mod h1:QLlNPkFR88mRUNQIzRBMfXxwKal8H7u1h3bL1CV+f0E=
github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U=
github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA=
github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM=
github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk=
github.com/mdlayher/socket v0.5.0 h1:ilICZmJcQz70vrWVes1MFera4jGiWNocSkykwwoy3XI=
github.com/mdlayher/socket v0.5.0/go.mod h1:WkcBFfvyG8QENs5+hfQPl1X6Jpd2yeLIYgrGFmJiJxI=
github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4=
github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/onsi/ginkgo/v2 v2.13.2 h1:Bi2gGVkfn6gQcjNjZJVO8Gf0FHzMPf2phUei9tejVMs=
github.com/onsi/ginkgo/v2 v2.13.2/go.mod h1:XStQ8QcGwLyF4HdfcZB8SFOS/MWCgDuXMSBe6zrvLgM=
github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg=
github.com/onsi/ginkgo/v2 v2.15.0 h1:79HwNRBAZHOEwrczrgSOPy+eFTTlIGELKy5as+ClttY=
github.com/onsi/ginkgo/v2 v2.15.0/go.mod h1:HlxMHtYF57y6Dpf+mc5529KKmSq9h2FpCF+/ZkwUxKM=
github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8=
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pierrec/lz4/v4 v4.1.19 h1:tYLzDnjDXh9qIxSTKHwXwOYmm9d887Y7Y1ZkyXYHAN4=
github.com/pierrec/lz4/v4 v4.1.19/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@ -94,8 +96,8 @@ github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
github.com/quic-go/qtls-go1-20 v0.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs=
github.com/quic-go/qtls-go1-20 v0.4.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
github.com/quic-go/quic-go v0.40.0 h1:GYd1iznlKm7dpHD7pOVpUvItgMPo/jrMgDWZhMCecqw=
github.com/quic-go/quic-go v0.40.0/go.mod h1:PeN7kuVJ4xZbxSv/4OX6S1USOX8MJvydwpTx31vx60c=
github.com/quic-go/quic-go v0.40.1 h1:X3AGzUNFs0jVuO3esAGnTfvdgvL4fq655WaOi1snv1Q=
github.com/quic-go/quic-go v0.40.1/go.mod h1:PeN7kuVJ4xZbxSv/4OX6S1USOX8MJvydwpTx31vx60c=
github.com/shirou/gopsutil/v3 v3.23.7 h1:C+fHO8hfIppoJ1WdsVm1RoI0RwXoNdfTK7yWXV0wVj4=
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@ -109,19 +111,19 @@ github.com/ti-mo/netfilter v0.5.1 h1:cqamEd1c1zmpfpqvInLOro0Znq/RAfw2QL5wL2rAR/8
github.com/ti-mo/netfilter v0.5.1/go.mod h1:h9UPQ3ZrTZGBitay+LETMxZvNgWGK/efTUcqES2YiLw=
github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM=
github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms=
github.com/u-root/uio v0.0.0-20230305220412-3e8cd9d6bf63 h1:YcojQL98T/OO+rybuzn2+5KrD5dBwXIvYBvQ2cD3Avg=
github.com/u-root/uio v0.0.0-20230305220412-3e8cd9d6bf63/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264=
github.com/u-root/uio v0.0.0-20240118234441-a3c409a6018e h1:BA9O3BmlTmpjbvajAwzWx4Wo2TRVdpPXZEeemGQcajw=
github.com/u-root/uio v0.0.0-20240118234441-a3c409a6018e/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264=
github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw=
go.etcd.io/bbolt v1.3.8 h1:xs88BrvEv273UsB79e0hcVrlUWmS0a8upikMFhSyAtA=
go.etcd.io/bbolt v1.3.8/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
go.uber.org/mock v0.3.0 h1:3mUxI1No2/60yUYax92Pt8eNOEecx2D3lcXZh2NEZJo=
go.uber.org/mock v0.3.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY=
golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb h1:c0vyKkb6yr3KR7jEfJaOSv4lG7xPkbN6r52aJz1d8a8=
golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI=
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
golang.org/x/exp v0.0.0-20240119083558-1b970713d09a h1:Q8/wZp0KX97QFTc2ywcOE0YRjZPVIx+MXInMzdvQqcA=
golang.org/x/exp v0.0.0-20240119083558-1b970713d09a/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
@ -132,12 +134,12 @@ golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190322080309-f49334f85ddc/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -149,8 +151,8 @@ golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.4.1-0.20230131160137-e7d7f63158de/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
@ -158,10 +160,12 @@ golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.16.0 h1:GO788SKMRunPIBCXiQyo2AaexLstOrVhuAL5YwsckQM=
golang.org/x/tools v0.16.0/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0=
golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc=
golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.14.0 h1:2NiG67LD1tEH0D7kM+ps2V+fXmsAnpUeec7n8tcr4S0=
gonum.org/v1/gonum v0.14.0/go.mod h1:AoWeoz0becf9QMWtE8iWXNXc27fK4fNeHNf/oMejGfU=
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=

View file

@ -1,23 +1,15 @@
package aghalg
import (
"github.com/AdguardTeam/golibs/errors"
)
// RingBuffer is the implementation of ring buffer data structure.
type RingBuffer[T any] struct {
buf []T
cur int
cur uint
full bool
}
// NewRingBuffer initializes the new instance of ring buffer. size must be
// greater or equal to zero.
func NewRingBuffer[T any](size int) (rb *RingBuffer[T]) {
if size < 0 {
panic(errors.Error("ring buffer: size must be greater or equal to zero"))
}
func NewRingBuffer[T any](size uint) (rb *RingBuffer[T]) {
return &RingBuffer[T]{
buf: make([]T, size),
}
@ -30,7 +22,7 @@ func (rb *RingBuffer[T]) Append(e T) {
}
rb.buf[rb.cur] = e
rb.cur = (rb.cur + 1) % cap(rb.buf)
rb.cur = (rb.cur + 1) % uint(cap(rb.buf))
if rb.cur == 0 {
rb.full = true
}
@ -87,12 +79,12 @@ func (rb *RingBuffer[T]) splitCur() (before, after []T) {
}
// Len returns a length of the buffer.
func (rb *RingBuffer[T]) Len() (l int) {
func (rb *RingBuffer[T]) Len() (l uint) {
if !rb.full {
return rb.cur
}
return cap(rb.buf)
return uint(cap(rb.buf))
}
// Clear clears the buffer.

View file

@ -9,13 +9,13 @@ import (
)
// elements is a helper function that returns n elements of the buffer.
func elements(b *aghalg.RingBuffer[int], n int, reverse bool) (es []int) {
func elements(b *aghalg.RingBuffer[int], n uint, reverse bool) (es []int) {
fn := b.Range
if reverse {
fn = b.ReverseRange
}
i := 0
var i uint
fn(func(e int) (cont bool) {
if i >= n {
return false
@ -42,19 +42,14 @@ func TestNewRingBuffer(t *testing.T) {
assert.Zero(t, b.Len())
})
t.Run("negative_size", func(t *testing.T) {
assert.PanicsWithError(t, "ring buffer: size must be greater or equal to zero", func() {
aghalg.NewRingBuffer[int](-5)
})
})
t.Run("zero", func(t *testing.T) {
b := aghalg.NewRingBuffer[int](0)
for i := 0; i < 10; i++ {
b.Append(i)
assert.Equal(t, 0, b.Len())
assert.Empty(t, elements(b, b.Len(), false))
assert.Empty(t, elements(b, b.Len(), true))
bufLen := b.Len()
assert.EqualValues(t, 0, bufLen)
assert.Empty(t, elements(b, bufLen, false))
assert.Empty(t, elements(b, bufLen, true))
}
})
@ -62,9 +57,10 @@ func TestNewRingBuffer(t *testing.T) {
b := aghalg.NewRingBuffer[int](1)
for i := 0; i < 10; i++ {
b.Append(i)
assert.Equal(t, 1, b.Len())
assert.Equal(t, []int{i}, elements(b, b.Len(), false))
assert.Equal(t, []int{i}, elements(b, b.Len(), true))
bufLen := b.Len()
assert.EqualValues(t, 1, bufLen)
assert.Equal(t, []int{i}, elements(b, bufLen, false))
assert.Equal(t, []int{i}, elements(b, bufLen, true))
}
})
}
@ -78,7 +74,7 @@ func TestRingBuffer_Range(t *testing.T) {
name string
want []int
count int
length int
length uint
}{{
name: "three",
count: 3,
@ -163,11 +159,11 @@ func TestRingBuffer_Range_increment(t *testing.T) {
for i, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
b.Append(i)
assert.Equal(t, tc.want, elements(b, b.Len(), false))
bufLen := b.Len()
assert.Equal(t, tc.want, elements(b, bufLen, false))
slices.Reverse(tc.want)
assert.Equal(t, tc.want, elements(b, b.Len(), true))
assert.Equal(t, tc.want, elements(b, bufLen, true))
})
}
}

View file

@ -1,33 +0,0 @@
// Package aghchan contains channel utilities.
package aghchan
import (
"fmt"
"time"
)
// Receive returns an error if it cannot receive a value form c before timeout
// runs out.
func Receive[T any](c <-chan T, timeout time.Duration) (v T, ok bool, err error) {
var zero T
timeoutCh := time.After(timeout)
select {
case <-timeoutCh:
// TODO(a.garipov): Consider implementing [errors.Aser] for
// os.ErrTimeout.
return zero, false, fmt.Errorf("did not receive after %s", timeout)
case v, ok = <-c:
return v, ok, nil
}
}
// MustReceive panics if it cannot receive a value form c before timeout runs
// out.
func MustReceive[T any](c <-chan T, timeout time.Duration) (v T, ok bool) {
v, ok, err := Receive(c, timeout)
if err != nil {
panic(err)
}
return v, ok
}

View file

@ -1,65 +1,23 @@
package aghnet
import (
"context"
"fmt"
"io"
"io/fs"
"net/netip"
"path"
"strings"
"sync/atomic"
"github.com/AdguardTeam/AdGuardHome/internal/aghos"
"github.com/AdguardTeam/dnsproxy/upstream"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/hostsfile"
"github.com/AdguardTeam/golibs/log"
"golang.org/x/exp/maps"
"golang.org/x/exp/slices"
)
// DefaultHostsPaths returns the slice of paths default for the operating system
// to files and directories which are containing the hosts database. The result
// is intended to be used within fs.FS so the initial slash is omitted.
func DefaultHostsPaths() (paths []string) {
return defaultHostsPaths()
}
// MatchAddr returns the records for the IP address.
func (hc *HostsContainer) MatchAddr(ip netip.Addr) (recs []*hostsfile.Record) {
cur := hc.current.Load()
if cur == nil {
return nil
}
return cur.addrs[ip]
}
// MatchName returns the records for the hostname.
func (hc *HostsContainer) MatchName(name string) (recs []*hostsfile.Record) {
cur := hc.current.Load()
if cur != nil {
recs = cur.names[name]
}
return recs
}
// hostsContainerPrefix is a prefix for logging and wrapping errors in
// HostsContainer's methods.
const hostsContainerPrefix = "hosts container"
// Hosts is a map of IP addresses to the records, as it primarily stored in the
// [HostsContainer]. It should not be accessed for writing since it may be read
// concurrently, users should clone it before modifying.
//
// The order of records for each address is preserved from original files, but
// the order of the addresses, being a map key, is not.
//
// TODO(e.burkov): Probably, this should be a sorted slice of records.
type Hosts map[netip.Addr][]*hostsfile.Record
// HostsContainer stores the relevant hosts database provided by the OS and
// processes both A/AAAA and PTR DNS requests for those.
type HostsContainer struct {
@ -67,10 +25,10 @@ type HostsContainer struct {
done chan struct{}
// updates is the channel for receiving updated hosts.
updates chan Hosts
updates chan *hostsfile.DefaultStorage
// current is the last set of hosts parsed.
current atomic.Pointer[hostsIndex]
current atomic.Pointer[hostsfile.DefaultStorage]
// fsys is the working file system to read hosts files from.
fsys fs.FS
@ -111,7 +69,7 @@ func NewHostsContainer(
hc = &HostsContainer{
done: make(chan struct{}, 1),
updates: make(chan Hosts, 1),
updates: make(chan *hostsfile.DefaultStorage, 1),
fsys: fsys,
watcher: w,
patterns: patterns,
@ -152,11 +110,25 @@ func (hc *HostsContainer) Close() (err error) {
return err
}
// Upd returns the channel into which the updates are sent.
func (hc *HostsContainer) Upd() (updates <-chan Hosts) {
// Upd returns the channel into which the updates are sent. The updates
// themselves must not be modified.
func (hc *HostsContainer) Upd() (updates <-chan *hostsfile.DefaultStorage) {
return hc.updates
}
// type check
var _ hostsfile.Storage = (*HostsContainer)(nil)
// ByAddr implements the [hostsfile.Storage] interface for *HostsContainer.
func (hc *HostsContainer) ByAddr(addr netip.Addr) (names []string) {
return hc.current.Load().ByAddr(addr)
}
// ByName implements the [hostsfile.Storage] interface for *HostsContainer.
func (hc *HostsContainer) ByName(name string) (addrs []netip.Addr) {
return hc.current.Load().ByName(name)
}
// pathsToPatterns converts paths into patterns compatible with fs.Glob.
func pathsToPatterns(fsys fs.FS, paths []string) (patterns []string, err error) {
for i, p := range paths {
@ -167,7 +139,7 @@ func pathsToPatterns(fsys fs.FS, paths []string) (patterns []string, err error)
continue
}
// Don't put a filename here since it's already added by fs.Stat.
// Don't put a filename here since it's already added by [fs.Stat].
return nil, fmt.Errorf("path at index %d: %w", i, err)
}
@ -209,7 +181,7 @@ func (hc *HostsContainer) handleEvents() {
}
// sendUpd tries to send the parsed data to the ch.
func (hc *HostsContainer) sendUpd(recs Hosts) {
func (hc *HostsContainer) sendUpd(recs *hostsfile.DefaultStorage) {
log.Debug("%s: sending upd", hostsContainerPrefix)
ch := hc.updates
@ -226,67 +198,6 @@ func (hc *HostsContainer) sendUpd(recs Hosts) {
}
}
// hostsIndex is a [hostsfile.Set] to enumerate all the records.
type hostsIndex struct {
// addrs maps IP addresses to the records.
addrs Hosts
// names maps hostnames to the records.
names map[string][]*hostsfile.Record
}
// walk is a file walking function for hostsIndex.
func (idx *hostsIndex) walk(r io.Reader) (patterns []string, cont bool, err error) {
return nil, true, hostsfile.Parse(idx, r, nil)
}
// type check
var _ hostsfile.Set = (*hostsIndex)(nil)
// Add implements the [hostsfile.Set] interface for *hostsIndex.
func (idx *hostsIndex) Add(rec *hostsfile.Record) {
idx.addrs[rec.Addr] = append(idx.addrs[rec.Addr], rec)
for _, name := range rec.Names {
idx.names[name] = append(idx.names[name], rec)
}
}
// type check
var _ hostsfile.HandleSet = (*hostsIndex)(nil)
// HandleInvalid implements the [hostsfile.HandleSet] interface for *hostsIndex.
func (idx *hostsIndex) HandleInvalid(src string, _ []byte, err error) {
lineErr := &hostsfile.LineError{}
if !errors.As(err, &lineErr) {
// Must not happen if idx passed to [hostsfile.Parse].
return
} else if errors.Is(lineErr, hostsfile.ErrEmptyLine) {
// Ignore empty lines.
return
}
log.Info("%s: warning: parsing %q: %s", hostsContainerPrefix, src, lineErr)
}
// equalRecs is an equality function for [*hostsfile.Record].
func equalRecs(a, b *hostsfile.Record) (ok bool) {
return a.Addr == b.Addr && a.Source == b.Source && slices.Equal(a.Names, b.Names)
}
// equalRecSlices is an equality function for slices of [*hostsfile.Record].
func equalRecSlices(a, b []*hostsfile.Record) (ok bool) { return slices.EqualFunc(a, b, equalRecs) }
// Equal returns true if indexes are equal.
func (idx *hostsIndex) Equal(other *hostsIndex) (ok bool) {
if idx == nil {
return other == nil
} else if other == nil {
return false
}
return maps.EqualFunc(idx.addrs, other.addrs, equalRecSlices)
}
// refresh gets the data from specified files and propagates the updates if
// needed.
//
@ -294,63 +205,22 @@ func (idx *hostsIndex) Equal(other *hostsIndex) (ok bool) {
func (hc *HostsContainer) refresh() (err error) {
log.Debug("%s: refreshing", hostsContainerPrefix)
var addrLen, nameLen int
last := hc.current.Load()
if last != nil {
addrLen, nameLen = len(last.addrs), len(last.names)
}
idx := &hostsIndex{
addrs: make(Hosts, addrLen),
names: make(map[string][]*hostsfile.Record, nameLen),
}
_, err = aghos.FileWalker(idx.walk).Walk(hc.fsys, hc.patterns...)
// The error is always nil here since no readers passed.
strg, _ := hostsfile.NewDefaultStorage()
_, err = aghos.FileWalker(func(r io.Reader) (patterns []string, cont bool, err error) {
// Don't wrap the error since it's already informative enough as is.
return nil, true, hostsfile.Parse(strg, r, nil)
}).Walk(hc.fsys, hc.patterns...)
if err != nil {
// Don't wrap the error since it's informative enough as is.
return err
}
// TODO(e.burkov): Serialize updates using time.
if !last.Equal(idx) {
hc.current.Store(idx)
hc.sendUpd(idx.addrs)
// TODO(e.burkov): Serialize updates using [time.Time].
if !hc.current.Load().Equal(strg) {
hc.current.Store(strg)
hc.sendUpd(strg)
}
return nil
}
// type check
var _ upstream.Resolver = (*HostsContainer)(nil)
// LookupNetIP implements the [upstream.Resolver] interface for *HostsContainer.
func (hc *HostsContainer) LookupNetIP(
ctx context.Context,
network string,
hostname string,
) (addrs []netip.Addr, err error) {
// TODO(e.burkov): Think of extracting this logic to a golibs function if
// needed anywhere else.
var isDesiredProto func(ip netip.Addr) (ok bool)
switch network {
case "ip4":
isDesiredProto = (netip.Addr).Is4
case "ip6":
isDesiredProto = (netip.Addr).Is6
case "ip":
isDesiredProto = func(ip netip.Addr) (ok bool) { return true }
default:
return nil, fmt.Errorf("unsupported network: %q", network)
}
idx := hc.current.Load()
recs := idx.names[strings.ToLower(hostname)]
addrs = make([]netip.Addr, 0, len(recs))
for _, rec := range recs {
if isDesiredProto(rec.Addr) {
addrs = append(addrs, rec.Addr)
}
}
return slices.Clip(addrs), nil
}

View file

@ -1,17 +0,0 @@
//go:build linux
package aghnet
import (
"github.com/AdguardTeam/AdGuardHome/internal/aghos"
)
func defaultHostsPaths() (paths []string) {
paths = []string{"etc/hosts"}
if aghos.IsOpenWrt() {
paths = append(paths, "tmp/hosts")
}
return paths
}

View file

@ -1,7 +0,0 @@
//go:build !(windows || linux)
package aghnet
func defaultHostsPaths() (paths []string) {
return []string{"etc/hosts"}
}

View file

@ -3,13 +3,11 @@ package aghnet_test
import (
"net/netip"
"path"
"path/filepath"
"sync/atomic"
"testing"
"testing/fstest"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/aghchan"
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
"github.com/AdguardTeam/AdGuardHome/internal/aghtest"
"github.com/AdguardTeam/golibs/errors"
@ -20,139 +18,6 @@ import (
"github.com/stretchr/testify/require"
)
// nl is a newline character.
const nl = "\n"
// Variables mirroring the etc_hosts file from testdata.
var (
addr1000 = netip.MustParseAddr("1.0.0.0")
addr1001 = netip.MustParseAddr("1.0.0.1")
addr1002 = netip.MustParseAddr("1.0.0.2")
addr1003 = netip.MustParseAddr("1.0.0.3")
addr1004 = netip.MustParseAddr("1.0.0.4")
addr1357 = netip.MustParseAddr("1.3.5.7")
addr4216 = netip.MustParseAddr("4.2.1.6")
addr7531 = netip.MustParseAddr("7.5.3.1")
addr0 = netip.MustParseAddr("::")
addr1 = netip.MustParseAddr("::1")
addr2 = netip.MustParseAddr("::2")
addr3 = netip.MustParseAddr("::3")
addr4 = netip.MustParseAddr("::4")
addr42 = netip.MustParseAddr("::42")
addr13 = netip.MustParseAddr("::13")
addr31 = netip.MustParseAddr("::31")
hostsSrc = "./" + filepath.Join("./testdata", "etc_hosts")
testHosts = map[netip.Addr][]*hostsfile.Record{
addr1000: {{
Addr: addr1000,
Source: hostsSrc,
Names: []string{"hello", "hello.world"},
}, {
Addr: addr1000,
Source: hostsSrc,
Names: []string{"hello.world.again"},
}, {
Addr: addr1000,
Source: hostsSrc,
Names: []string{"hello.world"},
}},
addr1001: {{
Addr: addr1001,
Source: hostsSrc,
Names: []string{"simplehost"},
}, {
Addr: addr1001,
Source: hostsSrc,
Names: []string{"simplehost"},
}},
addr1002: {{
Addr: addr1002,
Source: hostsSrc,
Names: []string{"a.whole", "lot.of", "aliases", "for.testing"},
}},
addr1003: {{
Addr: addr1003,
Source: hostsSrc,
Names: []string{"*"},
}},
addr1004: {{
Addr: addr1004,
Source: hostsSrc,
Names: []string{"*.com"},
}},
addr1357: {{
Addr: addr1357,
Source: hostsSrc,
Names: []string{"domain4", "domain4.alias"},
}},
addr7531: {{
Addr: addr7531,
Source: hostsSrc,
Names: []string{"domain4.alias", "domain4"},
}},
addr4216: {{
Addr: addr4216,
Source: hostsSrc,
Names: []string{"domain", "domain.alias"},
}},
addr0: {{
Addr: addr0,
Source: hostsSrc,
Names: []string{"hello", "hello.world"},
}, {
Addr: addr0,
Source: hostsSrc,
Names: []string{"hello.world.again"},
}, {
Addr: addr0,
Source: hostsSrc,
Names: []string{"hello.world"},
}},
addr1: {{
Addr: addr1,
Source: hostsSrc,
Names: []string{"simplehost"},
}, {
Addr: addr1,
Source: hostsSrc,
Names: []string{"simplehost"},
}},
addr2: {{
Addr: addr2,
Source: hostsSrc,
Names: []string{"a.whole", "lot.of", "aliases", "for.testing"},
}},
addr3: {{
Addr: addr3,
Source: hostsSrc,
Names: []string{"*"},
}},
addr4: {{
Addr: addr4,
Source: hostsSrc,
Names: []string{"*.com"},
}},
addr42: {{
Addr: addr42,
Source: hostsSrc,
Names: []string{"domain.alias", "domain"},
}},
addr13: {{
Addr: addr13,
Source: hostsSrc,
Names: []string{"domain6", "domain6.alias"},
}},
addr31: {{
Addr: addr31,
Source: hostsSrc,
Names: []string{"domain6.alias", "domain6"},
}},
}
)
func TestNewHostsContainer(t *testing.T) {
const dirname = "dir"
const filename = "file1"
@ -267,7 +132,21 @@ func TestHostsContainer_refresh(t *testing.T) {
anotherIPStr := "1.2.3.4"
anotherIP := netip.MustParseAddr(anotherIPStr)
testFS := fstest.MapFS{"dir/file1": &fstest.MapFile{Data: []byte(ipStr + ` hostname` + nl)}}
r1 := &hostsfile.Record{
Addr: ip,
Source: "file1",
Names: []string{"hostname"},
}
r2 := &hostsfile.Record{
Addr: anotherIP,
Source: "file2",
Names: []string{"alias"},
}
r1Data, _ := r1.MarshalText()
r2Data, _ := r2.MarshalText()
testFS := fstest.MapFS{"dir/file1": &fstest.MapFile{Data: r1Data}}
// event is a convenient alias for an empty struct{} to emit test events.
type event = struct{}
@ -289,172 +168,47 @@ func TestHostsContainer_refresh(t *testing.T) {
require.NoError(t, err)
testutil.CleanupAndRequireSuccess(t, hc.Close)
checkRefresh := func(t *testing.T, want aghnet.Hosts) {
t.Helper()
upd, ok := aghchan.MustReceive(hc.Upd(), 1*time.Second)
require.True(t, ok)
assert.Equal(t, want, upd)
}
strg, _ := hostsfile.NewDefaultStorage()
strg.Add(r1)
t.Run("initial_refresh", func(t *testing.T) {
checkRefresh(t, aghnet.Hosts{
ip: {{
Addr: ip,
Source: "file1",
Names: []string{"hostname"},
}},
})
upd, ok := testutil.RequireReceive(t, hc.Upd(), 1*time.Second)
require.True(t, ok)
assert.True(t, strg.Equal(upd))
})
strg.Add(r2)
t.Run("second_refresh", func(t *testing.T) {
testFS["dir/file2"] = &fstest.MapFile{Data: []byte(anotherIPStr + ` alias` + nl)}
testFS["dir/file2"] = &fstest.MapFile{Data: r2Data}
eventsCh <- event{}
checkRefresh(t, aghnet.Hosts{
ip: {{
Addr: ip,
Source: "file1",
Names: []string{"hostname"},
}},
anotherIP: {{
Addr: anotherIP,
Source: "file2",
Names: []string{"alias"},
}},
})
upd, ok := testutil.RequireReceive(t, hc.Upd(), 1*time.Second)
require.True(t, ok)
assert.True(t, strg.Equal(upd))
})
t.Run("double_refresh", func(t *testing.T) {
// Make a change once.
testFS["dir/file1"] = &fstest.MapFile{Data: []byte(ipStr + ` alias` + nl)}
testFS["dir/file1"] = &fstest.MapFile{Data: []byte(ipStr + " alias\n")}
eventsCh <- event{}
// Require the changes are written.
require.Eventually(t, func() bool {
ips := hc.MatchName("hostname")
current, ok := testutil.RequireReceive(t, hc.Upd(), 1*time.Second)
require.True(t, ok)
return len(ips) == 0
}, 5*time.Second, time.Second/2)
require.Empty(t, current.ByName("hostname"))
// Make a change again.
testFS["dir/file2"] = &fstest.MapFile{Data: []byte(ipStr + ` hostname` + nl)}
testFS["dir/file2"] = &fstest.MapFile{Data: []byte(ipStr + " hostname\n")}
eventsCh <- event{}
// Require the changes are written.
require.Eventually(t, func() bool {
ips := hc.MatchName("hostname")
current, ok = testutil.RequireReceive(t, hc.Upd(), 1*time.Second)
require.True(t, ok)
return len(ips) > 0
}, 5*time.Second, time.Second/2)
assert.Len(t, hc.Upd(), 1)
require.NotEmpty(t, current.ByName("hostname"))
})
}
func TestHostsContainer_MatchName(t *testing.T) {
require.NoError(t, fstest.TestFS(testdata, "etc_hosts"))
stubWatcher := aghtest.FSWatcher{
OnEvents: func() (e <-chan struct{}) { return nil },
OnAdd: func(name string) (err error) { return nil },
OnClose: func() (err error) { return nil },
}
testCases := []struct {
req string
name string
want []*hostsfile.Record
}{{
req: "simplehost",
name: "simple",
want: append(testHosts[addr1001], testHosts[addr1]...),
}, {
req: "hello.world",
name: "hello_alias",
want: []*hostsfile.Record{
testHosts[addr1000][0],
testHosts[addr1000][2],
testHosts[addr0][0],
testHosts[addr0][2],
},
}, {
req: "hello.world.again",
name: "other_line_alias",
want: []*hostsfile.Record{
testHosts[addr1000][1],
testHosts[addr0][1],
},
}, {
req: "say.hello",
name: "hello_subdomain",
want: nil,
}, {
req: "say.hello.world",
name: "hello_alias_subdomain",
want: nil,
}, {
req: "for.testing",
name: "lots_of_aliases",
want: append(testHosts[addr1002], testHosts[addr2]...),
}, {
req: "nonexistent.example",
name: "non-existing",
want: nil,
}, {
req: "domain",
name: "issue_4216_4_6",
want: append(testHosts[addr4216], testHosts[addr42]...),
}, {
req: "domain4",
name: "issue_4216_4",
want: append(testHosts[addr1357], testHosts[addr7531]...),
}, {
req: "domain6",
name: "issue_4216_6",
want: append(testHosts[addr13], testHosts[addr31]...),
}}
hc, err := aghnet.NewHostsContainer(testdata, &stubWatcher, "etc_hosts")
require.NoError(t, err)
testutil.CleanupAndRequireSuccess(t, hc.Close)
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
recs := hc.MatchName(tc.req)
assert.Equal(t, tc.want, recs)
})
}
}
func TestHostsContainer_MatchAddr(t *testing.T) {
require.NoError(t, fstest.TestFS(testdata, "etc_hosts"))
stubWatcher := aghtest.FSWatcher{
OnEvents: func() (e <-chan struct{}) { return nil },
OnAdd: func(name string) (err error) { return nil },
OnClose: func() (err error) { return nil },
}
hc, err := aghnet.NewHostsContainer(testdata, &stubWatcher, "etc_hosts")
require.NoError(t, err)
testutil.CleanupAndRequireSuccess(t, hc.Close)
testCases := []struct {
req netip.Addr
name string
want []*hostsfile.Record
}{{
req: netip.AddrFrom4([4]byte{1, 0, 0, 1}),
name: "reverse",
want: testHosts[addr1001],
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
recs := hc.MatchAddr(tc.req)
assert.Equal(t, tc.want, recs)
})
}
}

View file

@ -1,32 +0,0 @@
//go:build windows
package aghnet
import (
"os"
"path"
"path/filepath"
"strings"
"github.com/AdguardTeam/golibs/log"
"golang.org/x/sys/windows"
)
func defaultHostsPaths() (paths []string) {
sysDir, err := windows.GetSystemDirectory()
if err != nil {
log.Error("aghnet: getting system directory: %s", err)
return []string{}
}
// Split all the elements of the path to join them afterwards. This is
// needed to make the Windows-specific path string returned by
// windows.GetSystemDirectory to be compatible with fs.FS.
pathElems := strings.Split(sysDir, string(os.PathSeparator))
if len(pathElems) > 0 && pathElems[0] == filepath.VolumeName(sysDir) {
pathElems = pathElems[1:]
}
return []string{path.Join(append(pathElems, "drivers/etc/hosts")...)}
}

View file

@ -1,11 +1,9 @@
package aghnet_test
import (
"io/fs"
"net"
"net/netip"
"net/url"
"os"
"testing"
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
@ -18,9 +16,6 @@ func TestMain(m *testing.M) {
testutil.DiscardLogOutput(m)
}
// testdata is the filesystem containing data for testing the package.
var testdata fs.FS = os.DirFS("./testdata")
func TestParseAddrPort(t *testing.T) {
const defaultPort = 1

View file

@ -1,38 +0,0 @@
#
# Test /etc/hosts file
#
1.0.0.1 simplehost
1.0.0.0 hello hello.world
# See https://github.com/AdguardTeam/AdGuardHome/issues/3846.
1.0.0.2 a.whole lot.of aliases for.testing
# See https://github.com/AdguardTeam/AdGuardHome/issues/3946.
1.0.0.3 *
1.0.0.4 *.com
# See https://github.com/AdguardTeam/AdGuardHome/issues/4079.
1.0.0.0 hello.world.again
# Duplicates of a main host and an alias.
1.0.0.1 simplehost
1.0.0.0 hello.world
# Same for IPv6.
::1 simplehost
:: hello hello.world
::2 a.whole lot.of aliases for.testing
::3 *
::4 *.com
:: hello.world.again
::1 simplehost
:: hello.world
# See https://github.com/AdguardTeam/AdGuardHome/issues/4216.
4.2.1.6 domain domain.alias
::42 domain.alias domain
1.3.5.7 domain4 domain4.alias
7.5.3.1 domain4.alias domain4
::13 domain6 domain6.alias
::31 domain6.alias domain6

View file

@ -1 +0,0 @@
iface sample_name inet static

View file

@ -1,5 +0,0 @@
# The "testdata" part is added here because the test is actually run from the
# parent directory. Real interface files usually contain only absolute paths.
source ./testdata/ifaces
source ./testdata/*

View file

@ -262,8 +262,7 @@ func (p *DefaultAddrProc) processRDNS(ip netip.Addr) (host string) {
// 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()))
return !ip.IsLoopback() && (p.usePrivateRDNS || !p.privateSubnets.Contains(ip))
}
// processWHOIS looks up the information about clients' IP addresses in the

View file

@ -7,6 +7,8 @@ package client
import (
"encoding"
"fmt"
"github.com/AdguardTeam/AdGuardHome/internal/whois"
)
// Source represents the source from which the information about the client has
@ -15,8 +17,7 @@ type Source uint8
// Clients information sources. The order determines the priority.
const (
SourceNone Source = iota
SourceWHOIS
SourceWHOIS Source = iota + 1
SourceARP
SourceRDNS
SourceDHCP
@ -52,3 +53,107 @@ var _ encoding.TextMarshaler = Source(0)
func (cs Source) MarshalText() (text []byte, err error) {
return []byte(cs.String()), nil
}
// Runtime is a client information from different sources.
type Runtime struct {
// whois is the filtered WHOIS information of a client.
whois *whois.Info
// arp is the ARP information of a client. nil indicates that there is no
// information from the source. Empty non-nil slice indicates that the data
// from the source is present, but empty.
arp []string
// rdns is the RDNS information of a client. nil indicates that there is no
// information from the source. Empty non-nil slice indicates that the data
// from the source is present, but empty.
rdns []string
// dhcp is the DHCP information of a client. nil indicates that there is no
// information from the source. Empty non-nil slice indicates that the data
// from the source is present, but empty.
dhcp []string
// hostsFile is the information from the hosts file. nil indicates that
// there is no information from the source. Empty non-nil slice indicates
// that the data from the source is present, but empty.
hostsFile []string
}
// Info returns a client information from the highest-priority source.
func (r *Runtime) Info() (cs Source, host string) {
info := []string{}
switch {
case r.hostsFile != nil:
cs, info = SourceHostsFile, r.hostsFile
case r.dhcp != nil:
cs, info = SourceDHCP, r.dhcp
case r.rdns != nil:
cs, info = SourceRDNS, r.rdns
case r.arp != nil:
cs, info = SourceARP, r.arp
case r.whois != nil:
cs = SourceWHOIS
}
if len(info) == 0 {
return cs, ""
}
// TODO(s.chzhen): Return the full information.
return cs, info[0]
}
// SetInfo sets a host as a client information from the cs.
func (r *Runtime) SetInfo(cs Source, hosts []string) {
if len(hosts) == 1 && hosts[0] == "" {
hosts = []string{}
}
switch cs {
case SourceARP:
r.arp = hosts
case SourceRDNS:
r.rdns = hosts
case SourceDHCP:
r.dhcp = hosts
case SourceHostsFile:
r.hostsFile = hosts
}
}
// WHOIS returns a WHOIS client information.
func (r *Runtime) WHOIS() (info *whois.Info) {
return r.whois
}
// SetWHOIS sets a WHOIS client information. info must be non-nil.
func (r *Runtime) SetWHOIS(info *whois.Info) {
r.whois = info
}
// Unset clears a cs information.
func (r *Runtime) Unset(cs Source) {
switch cs {
case SourceWHOIS:
r.whois = nil
case SourceARP:
r.arp = nil
case SourceRDNS:
r.rdns = nil
case SourceDHCP:
r.dhcp = nil
case SourceHostsFile:
r.hostsFile = nil
}
}
// IsEmpty returns true if there is no information from any source.
func (r *Runtime) IsEmpty() (ok bool) {
return r.whois == nil &&
r.arp == nil &&
r.rdns == nil &&
r.dhcp == nil &&
r.hostsFile == nil
}

View file

@ -0,0 +1,5 @@
// Package configmigrate provides a way to upgrade the YAML configuration file.
package configmigrate
// LastSchemaVersion is the most recent schema version.
const LastSchemaVersion uint = 28

View file

@ -1,9 +1,10 @@
package confmigrate
package configmigrate
import (
"testing"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/dnsforward"
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/golibs/testutil"
"github.com/AdguardTeam/golibs/timeutil"
@ -1646,3 +1647,84 @@ func TestUpgradeSchema26to27(t *testing.T) {
})
}
}
func TestUpgradeSchema27to28(t *testing.T) {
const newSchemaVer = 28
testCases := []struct {
in yobj
want yobj
name string
}{{
name: "empty",
in: yobj{},
want: yobj{
"schema_version": newSchemaVer,
},
}, {
name: "load_balance",
in: yobj{
"dns": yobj{
"all_servers": false,
"fastest_addr": false,
},
},
want: yobj{
"dns": yobj{
"upstream_mode": dnsforward.UpstreamModeLoadBalance,
},
"schema_version": newSchemaVer,
},
}, {
name: "parallel",
in: yobj{
"dns": yobj{
"all_servers": true,
"fastest_addr": false,
},
},
want: yobj{
"dns": yobj{
"upstream_mode": dnsforward.UpstreamModeParallel,
},
"schema_version": newSchemaVer,
},
}, {
name: "parallel_fastest",
in: yobj{
"dns": yobj{
"all_servers": true,
"fastest_addr": true,
},
},
want: yobj{
"dns": yobj{
"upstream_mode": dnsforward.UpstreamModeParallel,
},
"schema_version": newSchemaVer,
},
}, {
name: "load_balance",
in: yobj{
"dns": yobj{
"all_servers": false,
"fastest_addr": true,
},
},
want: yobj{
"dns": yobj{
"upstream_mode": dnsforward.UpstreamModeFastestAddr,
},
"schema_version": newSchemaVer,
},
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
err := migrateTo28(tc.in)
require.NoError(t, err)
assert.Equal(t, tc.want, tc.in)
})
}
}

View file

@ -1,5 +1,4 @@
// Package confmigrate provides a way to upgrade the YAML configuration file.
package confmigrate
package configmigrate
import (
"bytes"
@ -9,9 +8,6 @@ import (
yaml "gopkg.in/yaml.v3"
)
// LastSchemaVersion is the most recent schema version.
const LastSchemaVersion uint = 27
// Config is a the configuration for initializing a [Migrator].
type Config struct {
// WorkingDir is an absolute path to the working directory of AdGuardHome.
@ -123,6 +119,7 @@ func (m *Migrator) upgradeConfigSchema(current, target uint, diskConf yobj) (err
24: migrateTo25,
25: migrateTo26,
26: migrateTo27,
27: migrateTo28,
}
for i, migrate := range upgrades[current:target] {

View file

@ -1,4 +1,4 @@
package confmigrate_test
package configmigrate_test
import (
"io/fs"
@ -6,7 +6,7 @@ import (
"path"
"testing"
"github.com/AdguardTeam/AdGuardHome/internal/confmigrate"
"github.com/AdguardTeam/AdGuardHome/internal/configmigrate"
"github.com/AdguardTeam/golibs/testutil"
"github.com/stretchr/testify/require"
"golang.org/x/crypto/bcrypt"
@ -200,7 +200,7 @@ func TestMigrateConfig_Migrate(t *testing.T) {
wantBody, err := fs.ReadFile(testdata, path.Join(t.Name(), outputFileName))
require.NoError(t, err)
migrator := confmigrate.New(&confmigrate.Config{
migrator := configmigrate.New(&configmigrate.Config{
WorkingDir: t.Name(),
})
newBody, upgraded, err := migrator.Migrate(body, tc.targetVersion)

Some files were not shown because too many files have changed in this diff Show more