Compare commits
272 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bd16eee509 | ||
|
|
729c56de20 | ||
|
|
e6b31bab77 | ||
|
|
d8f855ebfc | ||
|
|
a829adad10 | ||
|
|
2fc1e258ed | ||
|
|
bbf1ceb870 | ||
|
|
8de3734e2c | ||
|
|
474cba52f0 | ||
|
|
805de59805 | ||
|
|
effc822b85 | ||
|
|
aef00413d5 | ||
|
|
49bd72c27f | ||
|
|
2e52a2c8a0 | ||
|
|
ceb178fcd5 | ||
|
|
0d202cb544 | ||
|
|
6ce6c2c04d | ||
|
|
3f95db98d3 | ||
|
|
54f3a5f990 | ||
|
|
44cebc06ec | ||
|
|
6affa96490 | ||
|
|
2aaf8ab3c1 | ||
|
|
ee1eb80786 | ||
|
|
e8fd4b1872 | ||
|
|
8cb5781770 | ||
|
|
c7d8b9ede1 | ||
|
|
5c6bb33e3a | ||
|
|
158d4f0249 | ||
|
|
f73717ec08 | ||
|
|
1807198a9b | ||
|
|
1ccf8fe116 | ||
|
|
d22f0eefe2 | ||
|
|
344c66f7ab | ||
|
|
83be002b41 | ||
|
|
9945cd3991 | ||
|
|
667263a3a8 | ||
|
|
6318fc424b | ||
|
|
e32a37a747 | ||
|
|
7805a71332 | ||
|
|
6fb2aee210 | ||
|
|
ce9bb588ed | ||
|
|
55fb914537 | ||
|
|
6f7bfd6c9c | ||
|
|
fbc0d981ba | ||
|
|
48d1c673a9 | ||
|
|
889a0eb8b3 | ||
|
|
b01c10b73e | ||
|
|
f6ad64bf69 | ||
|
|
a5e8443735 | ||
|
|
2860929a47 | ||
|
|
ecdac56616 | ||
|
|
25918e56fa | ||
|
|
df91f016f2 | ||
|
|
f7d259f653 | ||
|
|
82ab4328d4 | ||
|
|
b21e19a223 | ||
|
|
c6aed4eb57 | ||
|
|
760d466b38 | ||
|
|
258eecc55b | ||
|
|
7b93f5d7cf | ||
|
|
3be7676970 | ||
|
|
48ee2f8a42 | ||
|
|
ec83d0eb86 | ||
|
|
19347d263a | ||
|
|
b22b16d98c | ||
|
|
cadb765b7d | ||
|
|
1116da8b83 | ||
|
|
c65700923a | ||
|
|
7030c7c24c | ||
|
|
09718a2170 | ||
|
|
77cda2c2c5 | ||
|
|
d9c57cdd9a | ||
|
|
0dad53b5f7 | ||
|
|
9a7315dbea | ||
|
|
a21558f418 | ||
|
|
4f928be393 | ||
|
|
f543b47261 | ||
|
|
66b831072c | ||
|
|
80eb339896 | ||
|
|
c69639c013 | ||
|
|
5f6fbe8e08 | ||
|
|
b40bbf0260 | ||
|
|
a11c8e91ab | ||
|
|
618d0e596c | ||
|
|
fde9ea5cb1 | ||
|
|
03d9803238 | ||
|
|
bd64b8b014 | ||
|
|
67fe064fcf | ||
|
|
471668d19a | ||
|
|
42762dfe54 | ||
|
|
c9314610d4 | ||
|
|
16755c37d8 | ||
|
|
73fcbd6ea2 | ||
|
|
30244f361f | ||
|
|
083991fb21 | ||
|
|
e3200d5046 | ||
|
|
21f6ed36fe | ||
|
|
77d04d44eb | ||
|
|
b34d119255 | ||
|
|
63bd71a10c | ||
|
|
faf2b32389 | ||
|
|
d23da1b757 | ||
|
|
beb8e36eee | ||
|
|
fe70161c01 | ||
|
|
39fa4b1f8e | ||
|
|
c7a8883201 | ||
|
|
3fd467413c | ||
|
|
9728dd856f | ||
|
|
ecadf78d60 | ||
|
|
eba4612d72 | ||
|
|
9200163f85 | ||
|
|
3c17853344 | ||
|
|
993a3fc42c | ||
|
|
7bb9b2416b | ||
|
|
2de321ce24 | ||
|
|
30b2b85ff1 | ||
|
|
6ea4788f56 | ||
|
|
3c52a021b9 | ||
|
|
0ceea9af5f | ||
|
|
39b404be19 | ||
|
|
56dc3eab02 | ||
|
|
554a38eeb1 | ||
|
|
c8d3afe869 | ||
|
|
44222c604c | ||
|
|
cbf221585e | ||
|
|
48322f6d0d | ||
|
|
d5a213c639 | ||
|
|
8166c4bc33 | ||
|
|
133cd9ef6b | ||
|
|
11146f73ed | ||
|
|
1beb18db47 | ||
|
|
f7bc2273a7 | ||
|
|
d1e735a003 | ||
|
|
af4ff5c748 | ||
|
|
fc951c1226 | ||
|
|
f81fd42472 | ||
|
|
1029ea5966 | ||
|
|
c0abdb4bc7 | ||
|
|
6681178ad3 | ||
|
|
e73605c4c5 | ||
|
|
c7017d49aa | ||
|
|
191d3bde49 | ||
|
|
18876a8e5c | ||
|
|
aa4a0d9880 | ||
|
|
d03d731d65 | ||
|
|
33b58a42fe | ||
|
|
2e9e708647 | ||
|
|
8ad22841ab | ||
|
|
32cf02264c | ||
|
|
0e8445b38f | ||
|
|
cb27ecd6c0 | ||
|
|
535220b3df | ||
|
|
7b9cfa94f8 | ||
|
|
b3f2e88e9c | ||
|
|
aa7a8d45e4 | ||
|
|
49cdef3d6a | ||
|
|
fecd146552 | ||
|
|
b01efd8c98 | ||
|
|
bd4dfb261c | ||
|
|
e754e4d2f6 | ||
|
|
b220e35c99 | ||
|
|
4f5131f423 | ||
|
|
dcb043df5f | ||
|
|
86e5756262 | ||
|
|
ba0cf5739b | ||
|
|
c4a13b92d2 | ||
|
|
723279121a | ||
|
|
3ad7649f7d | ||
|
|
2898a49d86 | ||
|
|
1547f9d35e | ||
|
|
adadd55c42 | ||
|
|
33b0225aa4 | ||
|
|
97d4058d80 | ||
|
|
86207e719d | ||
|
|
113f94ff46 | ||
|
|
5673deb391 | ||
|
|
3548a393ed | ||
|
|
254515f274 | ||
|
|
bccbecc6ea | ||
|
|
66f53803af | ||
|
|
faef005ce7 | ||
|
|
941cd2a562 | ||
|
|
6a4a9a0239 | ||
|
|
b9dbe6f1b6 | ||
|
|
7fec111ef8 | ||
|
|
5e1bd99718 | ||
|
|
9d75f72ceb | ||
|
|
d98d96db1a | ||
|
|
6a0ef2df15 | ||
|
|
75c2eb4c8a | ||
|
|
d021a67d66 | ||
|
|
4ed97cab12 | ||
|
|
a38742eed7 | ||
|
|
5efa95ed26 | ||
|
|
04db7db607 | ||
|
|
d17c6c6bb3 | ||
|
|
b2052f2ef1 | ||
|
|
cddcf852c2 | ||
|
|
1def426b45 | ||
|
|
b114fd5279 | ||
|
|
d27c3284f6 | ||
|
|
ba24a26b53 | ||
|
|
3e6678b6b4 | ||
|
|
83fd6f9782 | ||
|
|
52bc1b3f10 | ||
|
|
dd2153b7ac | ||
|
|
dd96a34861 | ||
|
|
daf26ee25a | ||
|
|
7e140eaaac | ||
|
|
d07a712988 | ||
|
|
95863288bf | ||
|
|
ea12be658b | ||
|
|
faa7c9aae5 | ||
|
|
e3653e8c25 | ||
|
|
b40cb24822 | ||
|
|
74004c1aa0 | ||
|
|
3e240741f1 | ||
|
|
6cfdbef1a5 | ||
|
|
d9bde6425b | ||
|
|
e2ae9e1591 | ||
|
|
5ebcbfa9ad | ||
|
|
e276bd7a31 | ||
|
|
659b2529bf | ||
|
|
97b3ed43ab | ||
|
|
767d6d3f28 | ||
|
|
31fc9bfc52 | ||
|
|
3f06b02409 | ||
|
|
5bf958ec6b | ||
|
|
959d9ff9a0 | ||
|
|
4813b4de25 | ||
|
|
119100924c | ||
|
|
bd584de4ee | ||
|
|
ede85ab2f2 | ||
|
|
12c20288e4 | ||
|
|
5bbbf89c10 | ||
|
|
d55393ecd5 | ||
|
|
2b5927306f | ||
|
|
4f016b6ed7 | ||
|
|
3a2a6d10ec | ||
|
|
2491426b09 | ||
|
|
5ebdd1390e | ||
|
|
b7f0247575 | ||
|
|
e28186a28a | ||
|
|
de1a7ce48f | ||
|
|
48480fb33b | ||
|
|
f41332fe6b | ||
|
|
1f8b340b8f | ||
|
|
fdaf1d09d3 | ||
|
|
b9682c4f10 | ||
|
|
69dcb4effd | ||
|
|
d50fd0ba91 | ||
|
|
c2c7b4c731 | ||
|
|
952d5f3a3d | ||
|
|
3f126c9ec9 | ||
|
|
0be58ef918 | ||
|
|
8f9053e2fc | ||
|
|
68452e5330 | ||
|
|
2eacc46eaa | ||
|
|
74dcc91ea7 | ||
|
|
dd7bf61323 | ||
|
|
2819d6cace | ||
|
|
75355a6883 | ||
|
|
e9c007d56b | ||
|
|
84c9085516 | ||
|
|
9f36e57c1e | ||
|
|
7528699fc2 | ||
|
|
d280151c18 | ||
|
|
b44c755d25 | ||
|
|
e4078e87a1 | ||
|
|
be36204756 | ||
|
|
b5409d6d00 | ||
|
|
f3d6bce03e |
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@@ -1,7 +1,7 @@
|
||||
'name': 'build'
|
||||
|
||||
'env':
|
||||
'GO_VERSION': '1.23.6'
|
||||
'GO_VERSION': '1.24.1'
|
||||
'NODE_VERSION': '18'
|
||||
|
||||
'on':
|
||||
|
||||
2
.github/workflows/lint.yml
vendored
2
.github/workflows/lint.yml
vendored
@@ -1,7 +1,7 @@
|
||||
'name': 'lint'
|
||||
|
||||
'env':
|
||||
'GO_VERSION': '1.23.6'
|
||||
'GO_VERSION': '1.24.1'
|
||||
|
||||
'on':
|
||||
'push':
|
||||
|
||||
55
CHANGELOG.md
55
CHANGELOG.md
@@ -9,25 +9,54 @@ The format is based on [*Keep a Changelog*](https://keepachangelog.com/en/1.0.0/
|
||||
<!--
|
||||
## [v0.108.0] – TBA
|
||||
|
||||
## [v0.107.58] - 2025-03-11 (APPROX.)
|
||||
## [v0.107.59] - 2025-04-01 (APPROX.)
|
||||
|
||||
See also the [v0.107.58 GitHub milestone][ms-v0.107.58].
|
||||
See also the [v0.107.59 GitHub milestone][ms-v0.107.59].
|
||||
|
||||
[ms-v0.107.58]: https://github.com/AdguardTeam/AdGuardHome/milestone/93?closed=1
|
||||
[ms-v0.107.59]: https://github.com/AdguardTeam/AdGuardHome/milestone/94?closed=1
|
||||
|
||||
NOTE: Add new changes BELOW THIS COMMENT.
|
||||
-->
|
||||
|
||||
### Fixed
|
||||
|
||||
- The formatting of large numbers in the clients tables on the *Client settings* page ([#7583]).
|
||||
|
||||
[#7583]: https://github.com/AdguardTeam/AdGuardHome/issues/7583
|
||||
|
||||
<!--
|
||||
NOTE: Add new changes ABOVE THIS COMMENT.
|
||||
-->
|
||||
|
||||
## [v0.107.58] - 2025-03-19
|
||||
|
||||
See also the [v0.107.58 GitHub milestone][ms-v0.107.58].
|
||||
|
||||
### Security
|
||||
|
||||
- Go version has been updated to prevent the possibility of exploiting the Go vulnerabilities fixed in [1.24.1][go-1.24.1].
|
||||
|
||||
### Added
|
||||
|
||||
- The ability to check filtering rules for host names using an optional query type and optional ClientID or client IP address ([#4036]).
|
||||
|
||||
- Optional `client` and `qtype` URL query parameters to the `GET /control/check_host` HTTP API.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Clearing the DNS cache on the *DNS settings* page now includes both global cache and custom client cache.
|
||||
|
||||
- Invalid ICMPv6 Router Advertisement messages ([#7547]).
|
||||
|
||||
- Disabled button for autofilled login form.
|
||||
|
||||
- Formatting of elapsed times less than one millisecond.
|
||||
|
||||
- Changes to global upstream DNS settings not applying to custom client upstream configurations.
|
||||
|
||||
- The formatting of large numbers in the clients tables on the *Client settings* page ([#7583]).
|
||||
|
||||
[#4036]: https://github.com/AdguardTeam/AdGuardHome/issues/4036
|
||||
[#7547]: https://github.com/AdguardTeam/AdGuardHome/issues/7547
|
||||
[#7583]: https://github.com/AdguardTeam/AdGuardHome/issues/7583
|
||||
|
||||
[go-1.24.1]: https://groups.google.com/g/golang-announce/c/4t3lzH3I0eI
|
||||
[ms-v0.107.58]: https://github.com/AdguardTeam/AdGuardHome/milestone/93?closed=1
|
||||
|
||||
## [v0.107.57] - 2025-02-20
|
||||
|
||||
See also the [v0.107.57 GitHub milestone][ms-v0.107.57].
|
||||
@@ -47,6 +76,7 @@ See also the [v0.107.57 GitHub milestone][ms-v0.107.57].
|
||||
### Fixed
|
||||
|
||||
- The hostnames of DHCP clients not being shown in the *Top clients* table on the dashboard ([#7627]).
|
||||
|
||||
- The formatting of large numbers in the upstream table and query log ([#7590]).
|
||||
|
||||
[#7590]: https://github.com/AdguardTeam/AdGuardHome/issues/7590
|
||||
@@ -3023,11 +3053,12 @@ See also the [v0.104.2 GitHub milestone][ms-v0.104.2].
|
||||
[ms-v0.104.2]: https://github.com/AdguardTeam/AdGuardHome/milestone/28?closed=1
|
||||
|
||||
<!--
|
||||
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.58...HEAD
|
||||
[v0.107.58]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.57...v0.107.58
|
||||
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.59...HEAD
|
||||
[v0.107.59]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.58...v0.107.59
|
||||
-->
|
||||
|
||||
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.57...HEAD
|
||||
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.58...HEAD
|
||||
[v0.107.58]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.57...v0.107.58
|
||||
[v0.107.57]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.56...v0.107.57
|
||||
[v0.107.56]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.55...v0.107.56
|
||||
[v0.107.55]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.54...v0.107.55
|
||||
|
||||
2
Makefile
2
Makefile
@@ -27,7 +27,7 @@ DIST_DIR = dist
|
||||
GOAMD64 = v1
|
||||
GOPROXY = https://proxy.golang.org|direct
|
||||
GOTELEMETRY = off
|
||||
GOTOOLCHAIN = go1.23.6
|
||||
GOTOOLCHAIN = go1.24.1
|
||||
GPG_KEY = devteam@adguard.com
|
||||
GPG_KEY_PASSPHRASE = not-a-real-password
|
||||
NPM = npm
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
'variables':
|
||||
'channel': 'edge'
|
||||
'dockerFrontend': 'adguard/home-js-builder:2.1-bullseye'
|
||||
'dockerGo': 'adguard/go-builder:1.23.6--1'
|
||||
'dockerGo': 'adguard/go-builder:1.24.1--1'
|
||||
|
||||
'stages':
|
||||
- 'Build frontend':
|
||||
@@ -278,7 +278,7 @@
|
||||
'variables':
|
||||
'channel': 'beta'
|
||||
'dockerFrontend': 'adguard/home-js-builder:2.1-bullseye'
|
||||
'dockerGo': 'adguard/go-builder:1.23.6--1'
|
||||
'dockerGo': 'adguard/go-builder:1.24.1--1'
|
||||
# release-vX.Y.Z branches are the branches from which the actual final
|
||||
# release is built.
|
||||
- '^release-v[0-9]+\.[0-9]+\.[0-9]+':
|
||||
@@ -294,4 +294,4 @@
|
||||
'variables':
|
||||
'channel': 'release'
|
||||
'dockerFrontend': 'adguard/home-js-builder:2.1-bullseye'
|
||||
'dockerGo': 'adguard/go-builder:1.23.6--1'
|
||||
'dockerGo': 'adguard/go-builder:1.24.1--1'
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
'name': 'AdGuard Home - Build and run tests'
|
||||
'variables':
|
||||
'dockerFrontend': 'adguard/home-js-builder:2.1-bullseye'
|
||||
'dockerGo': 'adguard/go-builder:1.23.6--1'
|
||||
'dockerGo': 'adguard/go-builder:1.24.1--1'
|
||||
'channel': 'development'
|
||||
|
||||
'stages':
|
||||
@@ -234,5 +234,5 @@
|
||||
# may need to build a few of these.
|
||||
'variables':
|
||||
'dockerFrontend': 'adguard/home-js-builder:2.1-bullseye'
|
||||
'dockerGo': 'adguard/go-builder:1.23.6--1'
|
||||
'dockerGo': 'adguard/go-builder:1.24.1--1'
|
||||
'channel': 'candidate'
|
||||
|
||||
@@ -620,6 +620,10 @@
|
||||
"check_cname": "CNAME: {{cname}}",
|
||||
"check_reason": "Důvod: {{reason}}",
|
||||
"check_service": "Název služby: {{service}}",
|
||||
"check_hostname": "Název hostitele nebo domény",
|
||||
"check_client_id": "Identifikátor klienta (ClientID nebo IP adresa)",
|
||||
"check_enter_client_id": "Zadejte identifikátor klienta",
|
||||
"check_dns_record": "Vyberte typ DNS záznamu",
|
||||
"service_name": "Název služby",
|
||||
"check_not_found": "Nenalezeno ve Vašich seznamech filtrů",
|
||||
"client_confirm_block": "Opravdu chcete zablokovat klienta „{{ip}}“?",
|
||||
|
||||
@@ -620,6 +620,10 @@
|
||||
"check_cname": "CNAME: {{cname}}",
|
||||
"check_reason": "Årsag: {{reason}}",
|
||||
"check_service": "Tjenestenavn: {{service}}",
|
||||
"check_hostname": "Værts- eller domænenavn",
|
||||
"check_client_id": "Klientidentifikator (ClientID eller IP-adresse)",
|
||||
"check_enter_client_id": "Angiv klientidentifikator",
|
||||
"check_dns_record": "Vælg DNS-posttype",
|
||||
"service_name": "Tjenestenavn",
|
||||
"check_not_found": "Ikke fundet i dine filterlister",
|
||||
"client_confirm_block": "Sikker på, at du vil blokere klienten \"{{ip}}\"?",
|
||||
|
||||
@@ -620,6 +620,10 @@
|
||||
"check_cname": "CNAME: {{cname}}",
|
||||
"check_reason": "Grund: {{reason}}",
|
||||
"check_service": "Dienstname: {{service}}",
|
||||
"check_hostname": "Hostname oder Domainname",
|
||||
"check_client_id": "Client-Kennung (ClientID oder IP-Adresse)",
|
||||
"check_enter_client_id": "Client-Kennung eingeben",
|
||||
"check_dns_record": "DNS-Datensatztyp auswählen",
|
||||
"service_name": "Name des Dienstes",
|
||||
"check_not_found": "Nicht in Ihren Filterlisten enthalten",
|
||||
"client_confirm_block": "Möchten Sie den Client „{{ip}}“ wirklich sperren?",
|
||||
|
||||
@@ -620,6 +620,10 @@
|
||||
"check_cname": "CNAME: {{cname}}",
|
||||
"check_reason": "Reason: {{reason}}",
|
||||
"check_service": "Service name: {{service}}",
|
||||
"check_hostname": "Hostname or domain name",
|
||||
"check_client_id": "Client identifier (ClientID or IP address)",
|
||||
"check_enter_client_id": "Enter client identifier",
|
||||
"check_dns_record": "Select DNS record type",
|
||||
"service_name": "Service name",
|
||||
"check_not_found": "Not found in your filter lists",
|
||||
"client_confirm_block": "Are you sure you want to block the client \"{{ip}}\"?",
|
||||
|
||||
@@ -620,6 +620,10 @@
|
||||
"check_cname": "CNAME: {{cname}}",
|
||||
"check_reason": "Razón: {{reason}}",
|
||||
"check_service": "Nombre del servicio: {{service}}",
|
||||
"check_hostname": "Nombre de host o nombre de dominio",
|
||||
"check_client_id": "Identificador del cliente (ClientID o dirección IP)",
|
||||
"check_enter_client_id": "Ingresa el identificador del cliente",
|
||||
"check_dns_record": "Selecciona el tipo de registro DNS",
|
||||
"service_name": "Nombre del servicio",
|
||||
"check_not_found": "No se ha encontrado en tus listas de filtros",
|
||||
"client_confirm_block": "¿Estás seguro de que deseas bloquear al cliente \"{{ip}}\"?",
|
||||
|
||||
@@ -620,6 +620,10 @@
|
||||
"check_cname": "CNAME : {{cname}}",
|
||||
"check_reason": "Raison : {{reason}}",
|
||||
"check_service": "Nom du service : {{service}}",
|
||||
"check_hostname": "Nom d'hôte ou nom de domaine",
|
||||
"check_client_id": "Identifiant du client (ClientID ou adresse IP)",
|
||||
"check_enter_client_id": "Saisissez l'identifiant du client",
|
||||
"check_dns_record": "Sélectionnez le type d'enregistrement DNS",
|
||||
"service_name": "Nom du service",
|
||||
"check_not_found": "Introuvable dans vos listes de filtres",
|
||||
"client_confirm_block": "Voulez-vous vraiment bloquer le client « {{ip}} » ?",
|
||||
|
||||
@@ -620,6 +620,10 @@
|
||||
"check_cname": "CNAME: {{cname}}",
|
||||
"check_reason": "Motivo: {{reason}}",
|
||||
"check_service": "Nome servizio: {{service}}",
|
||||
"check_hostname": "Nome host o nome di dominio",
|
||||
"check_client_id": "Identificatore client (ClientID o indirizzo IP)",
|
||||
"check_enter_client_id": "Inserisci identificatore client",
|
||||
"check_dns_record": "Seleziona il tipo di registrazione DNS",
|
||||
"service_name": "Nome servizio",
|
||||
"check_not_found": "Non trovato negli elenchi dei filtri",
|
||||
"client_confirm_block": "Sei sicuro di voler bloccare il client \"{{ip}}\"?",
|
||||
|
||||
@@ -620,6 +620,10 @@
|
||||
"check_cname": "CNAME: {{cname}}",
|
||||
"check_reason": "理由: {{reason}}",
|
||||
"check_service": "サービス名: {{service}}",
|
||||
"check_hostname": "ホスト名またはドメイン名",
|
||||
"check_client_id": "クライアント識別子 (ClientID または IP アドレス)",
|
||||
"check_enter_client_id": "クライアント識別子を入力してください",
|
||||
"check_dns_record": "DNSレコードタイプ(DNS record type)を選択",
|
||||
"service_name": "サービス名",
|
||||
"check_not_found": "フィルタ一覧には見つかりません",
|
||||
"client_confirm_block": "クライアント\"{{ip}}\"をブロックしてもよろしいですか?",
|
||||
|
||||
@@ -620,6 +620,10 @@
|
||||
"check_cname": "CNAME: {{cname}}",
|
||||
"check_reason": "이유: {{reason}}",
|
||||
"check_service": "서비스 이름: {{service}}",
|
||||
"check_hostname": "호스트 이름 또는 도메인 이름",
|
||||
"check_client_id": "클라이언트 식별자(클라이언트 ID 또는 IP 주소)",
|
||||
"check_enter_client_id": "클라이언트 식별자 입력",
|
||||
"check_dns_record": "DNS 레코드 유형 선택",
|
||||
"service_name": "서비스 이름",
|
||||
"check_not_found": "필터 목록에서 찾을 수 없음",
|
||||
"client_confirm_block": "정말로 클라이언트 '{{ip}}'을(를) 차단하시겠습니까?",
|
||||
|
||||
@@ -327,10 +327,10 @@
|
||||
"rate_limit_whitelist_placeholder": "Voer één IP-adres per regel in",
|
||||
"blocking_ipv4_desc": "IP-adres dat moet worden teruggegeven voor een geblokkeerd A-verzoek",
|
||||
"blocking_ipv6_desc": "IP-adres dat moet worden teruggegeven voor een geblokkeerd A-verzoek",
|
||||
"blocking_mode_default": "Standaard: Reageer met een nul IP adres (0.0.0.0 for A; :: voor AAAA) wanneer geblokkeerd door een Adblock-type regel; reageer met het IP-adres dat is opgegeven in de regel wanneer geblokkeerd door een /etc/hosts type regel",
|
||||
"blocking_mode_default": "Standaard: Reageer met een nul IP-adres (0.0.0.0 for A; :: voor AAAA) wanneer geblokkeerd door een Adblock-type regel; reageer met het IP-adres dat is opgegeven in de regel wanneer geblokkeerd door een /etc/hosts type regel",
|
||||
"blocking_mode_refused": "REFUSED: Antwoorden met REFUSED code",
|
||||
"blocking_mode_nxdomain": "NXDOMAIN: Reageer met NXDOMAIN code",
|
||||
"blocking_mode_null_ip": "Nul IP: Reageer met een nul IP address (0.0.0.0 voor A; :: voor AAAA)",
|
||||
"blocking_mode_null_ip": "Nul IP: Reageer met een nul IP-adres (0.0.0.0 voor A; :: voor AAAA)",
|
||||
"blocking_mode_custom_ip": "Aangepast IP: Reageer met een handmatige ingesteld IP adres",
|
||||
"theme_auto": "Automatisch",
|
||||
"theme_light": "Licht",
|
||||
@@ -620,6 +620,10 @@
|
||||
"check_cname": "CNAME: {{cname}}",
|
||||
"check_reason": "Reden: {{reason}}",
|
||||
"check_service": "Servicenaam: {{service}}",
|
||||
"check_hostname": "Hostnaam of domeinnaam",
|
||||
"check_client_id": "Client identificator (ClientID of IP-adres)",
|
||||
"check_enter_client_id": "Voer Client identificator in",
|
||||
"check_dns_record": "Selecteer type DNS-record",
|
||||
"service_name": "Naam service",
|
||||
"check_not_found": "Niet in je lijst met filters gevonden",
|
||||
"client_confirm_block": "Weet je zeker dat je client \"{{ip}}\" wil blokkeren?",
|
||||
|
||||
@@ -620,6 +620,10 @@
|
||||
"check_cname": "CNAME: {{cname}}",
|
||||
"check_reason": "Motivo: {{reason}}",
|
||||
"check_service": "Nome do serviço: {{service}}",
|
||||
"check_hostname": "Nome do anfitrião ou nome de domínio",
|
||||
"check_client_id": "Identificador do cliente (ClienteID ou endereço de IP)",
|
||||
"check_enter_client_id": "Insira o identificador do cliente",
|
||||
"check_dns_record": "Selecione o tipo de registro DNS",
|
||||
"service_name": "Nome do serviço",
|
||||
"check_not_found": "Não encontrado em suas listas de filtros",
|
||||
"client_confirm_block": "Você tem certeza de que deseja bloquear o cliente \"{{ip}}\"?",
|
||||
|
||||
@@ -620,6 +620,10 @@
|
||||
"check_cname": "CNAME: {{cname}}",
|
||||
"check_reason": "Motivo: {{reason}}",
|
||||
"check_service": "Nome do serviço: {{service}}",
|
||||
"check_hostname": "Nome do hospedeiro ou nome de domínio",
|
||||
"check_client_id": "Identificador do cliente (ClientID ou endereço IP)",
|
||||
"check_enter_client_id": "Insira o identificador do cliente",
|
||||
"check_dns_record": "Selecione o tipo de registro DNS",
|
||||
"service_name": "Nome do serviço",
|
||||
"check_not_found": "Não encontrado nas tuas listas de filtros",
|
||||
"client_confirm_block": "Você tem certeza de que deseja bloquear o cliente \"{{ip}}\"?",
|
||||
|
||||
@@ -620,6 +620,10 @@
|
||||
"check_cname": "CNAME: {{cname}}",
|
||||
"check_reason": "Причина: {{reason}}",
|
||||
"check_service": "Название сервиса: {{service}}",
|
||||
"check_hostname": "Имя хоста или домена",
|
||||
"check_client_id": "Идентификатор клиента (ClientID или IP-адрес)",
|
||||
"check_enter_client_id": "Введите идентификатор клиента",
|
||||
"check_dns_record": "Выберите тип DNS-записи",
|
||||
"service_name": "Имя сервиса",
|
||||
"check_not_found": "Не найдено в вашем списке фильтров",
|
||||
"client_confirm_block": "Вы уверены, что хотите заблокировать клиента «{{ip}}»?",
|
||||
|
||||
@@ -620,6 +620,10 @@
|
||||
"check_cname": "CNAME: {{cname}}",
|
||||
"check_reason": "Dôvod: {{reason}}",
|
||||
"check_service": "Meno služby: {{service}}",
|
||||
"check_hostname": "Názov hostiteľa alebo názov domény",
|
||||
"check_client_id": "Identifikátor klienta (ClientID alebo IP adresa)",
|
||||
"check_enter_client_id": "Zadajte identifikátor klienta",
|
||||
"check_dns_record": "Vyberte typ DNS záznamu",
|
||||
"service_name": "Názov služby",
|
||||
"check_not_found": "Nenašlo sa vo Vašom zozname filtrov",
|
||||
"client_confirm_block": "Naozaj chcete zablokovať klienta \"{{ip}}\"?",
|
||||
|
||||
@@ -40,11 +40,11 @@
|
||||
"dhcp_ipv4_settings": "DHCP IPv4 Ayarları",
|
||||
"dhcp_ipv6_settings": "DHCP IPv6 Ayarları",
|
||||
"form_error_required": "Gerekli alan",
|
||||
"form_error_ip4_format": "Geçersiz IPv4 adresi",
|
||||
"form_error_ip4_gateway_format": "Geçersiz ağ geçidi IPv4 adresi",
|
||||
"form_error_ip6_format": "Geçersiz IPv6 adresi",
|
||||
"form_error_ip_format": "Geçersiz IP adresi",
|
||||
"form_error_mac_format": "Geçersiz MAC adresi",
|
||||
"form_error_ip4_format": "IPv4 adresi geçersiz",
|
||||
"form_error_ip4_gateway_format": "Ağ geçidi IPv4 adresi geçersiz",
|
||||
"form_error_ip6_format": "IPv6 adresi geçersiz",
|
||||
"form_error_ip_format": "IP adresi geçersiz",
|
||||
"form_error_mac_format": "MAC adresi geçersiz",
|
||||
"form_error_client_id_format": "İstemci Kimliği yalnızca sayılar, küçük harfler ve kısa çizgiler içermelidir",
|
||||
"form_error_server_name": "Sunucu adı geçersiz",
|
||||
"form_error_subnet": "\"{{cidr}}\" alt ağı, \"{{ip}}\" IP adresini içermiyor",
|
||||
@@ -68,7 +68,7 @@
|
||||
"ip": "IP",
|
||||
"dhcp_table_hostname": "Ana makine Adı",
|
||||
"dhcp_table_expires": "Bitiş tarihi",
|
||||
"dhcp_warning": "DHCP sunucusunu yine de etkinleştirmek istiyorsanız, ağınızda başka aktif DHCP sunucusu olmadığından emin olun, aksi takdirde ağa bağlı cihazların internet bağlantısı kesilebilir!",
|
||||
"dhcp_warning": "DHCP sunucusunu yine de etkinleştirmek istiyorsanız, ağınızda başka bir aktif DHCP sunucusu olmadığından emin olun, aksi takdirde ağa bağlı cihazların internet bağlantısı kesilebilir!",
|
||||
"dhcp_error": "AdGuard Home, ağda başka bir etkin DHCP sunucusu olup olmadığını belirleyemedi",
|
||||
"dhcp_static_ip_error": "DHCP sunucusunu kullanmak için sabit bir IP adresi ayarlanmalıdır. AdGuard Home, bu ağ arayüzünün sabit bir IP adresi kullanılarak yapılandırılıp yapılandırılmadığını belirleyemedi. Lütfen sabit IP adresini elle ayarlayın.",
|
||||
"dhcp_dynamic_ip_found": "Sisteminiz, <0>{{interfaceName}}</0> arayüzü için dinamik IP adresi yapılandırması kullanıyor. DHCP sunucusunu kullanmak için sabit bir IP adresi ayarlanmalıdır. Geçerli olan IP adresiniz <0>{{ipAddress}}</0>. \"DHCP sunucusunu etkinleştir\" düğmesine basarsanız, AdGuard Home bu IP adresini otomatik bir şekilde sabit olarak ayarlayacaktır.",
|
||||
@@ -147,13 +147,13 @@
|
||||
"average_upstream_response_time": "Ortalama üst kaynak yanıt süresi",
|
||||
"response_time": "Yanıt süresi",
|
||||
"average_processing_time_hint": "Bir DNS isteğinin milisaniye cinsinden ortalama işlem süresi",
|
||||
"block_domain_use_filters_and_hosts": "Filtre ve hosts dosyalarını kullanarak alan adlarını engelle",
|
||||
"block_domain_use_filters_and_hosts": "Filtre ve ana bilgisayar dosyalarını kullanarak alan adlarını engelle",
|
||||
"filters_block_toggle_hint": "<a>Filtreler</a> ayarlarında engelleme kuralları oluşturabilirsiniz.",
|
||||
"use_adguard_browsing_sec": "AdGuard gezinti koruması web hizmetini kullan",
|
||||
"use_adguard_browsing_sec_hint": "AdGuard Home, alan adının gezinti koruması web hizmeti tarafından engellenip engellenmediğini kontrol eder. Kontrolü gerçekleştirmek için gizlilik dostu arama API'sini kullanır: sunucuya yalnızca SHA256 karma alan adının kısa bir ön eki gönderilir.",
|
||||
"use_adguard_parental": "AdGuard ebeveyn denetimi web hizmetini kullan",
|
||||
"use_adguard_parental_hint": "AdGuard Home, alan adının yetişkin içerik bulundurup bulundurmadığını kontrol eder. Gezinti koruması web hizmeti ile kullandığımız aynı gizlilik dostu API'yi kullanır.",
|
||||
"enforce_safe_search": "Güvenli Aramayı kullan",
|
||||
"enforce_safe_search": "Güvenli aramayı kullan",
|
||||
"enforce_save_search_hint": "AdGuard Home, şu arama motorlarında güvenli aramayı uygular: Google, YouTube, Bing, DuckDuckGo, Ecosia, Yandex, Pixabay.",
|
||||
"no_servers_specified": "Sunucu belirtilmedi",
|
||||
"general_settings": "Genel ayarlar",
|
||||
@@ -294,6 +294,9 @@
|
||||
"blocked_response_ttl": "Engellenen yanıt kullanım süresi",
|
||||
"blocked_response_ttl_desc": "İstemcilerin filtrelenmiş bir yanıtı kaç saniye süreyle önbelleğe alması gerektiğini belirtir",
|
||||
"form_enter_blocked_response_ttl": "Engellenen yanıt kullanım süresini girin (saniye)",
|
||||
"upstream_timeout": "Üst kaynak zaman aşımı",
|
||||
"upstream_timeout_desc": "Üst kaynak sunucusundan yanıt almak için kaç saniye bekleneceğini belirtir",
|
||||
"form_enter_upstream_timeout": "Üst kaynak sunucusu zaman aşımı süresini saniye cinsinden girin",
|
||||
"dnscrypt": "DNSCrypt",
|
||||
"dns_over_https": "DNS-over-HTTPS",
|
||||
"dns_over_tls": "DNS-over-TLS",
|
||||
@@ -308,7 +311,7 @@
|
||||
"form_enter_rate_limit": "Sıklık limitini girin",
|
||||
"rate_limit": "Sıklık limiti",
|
||||
"edns_enable": "EDNS istemci alt ağını etkinleştir",
|
||||
"edns_cs_desc": "Kaynak yönü isteklerine EDNS İstemci Alt Ağı seçeneğini (ECS) ekleyin ve istemciler tarafından gönderilen değerleri sorgu günlüğüne kaydedin.",
|
||||
"edns_cs_desc": "Üst sunucu isteklerine ECS (EDNS İstemci Alt Ağı) seçeneğini ekler ve istemciler tarafından gönderilen değerleri sorgu günlüğünde kaydeder.",
|
||||
"edns_use_custom_ip": "EDNS için özel IP kullan",
|
||||
"edns_use_custom_ip_desc": "EDNS için özel IP kullanımına izin ver",
|
||||
"rate_limit_desc": "İstemci başına izin verilen saniyedeki istek sayısı. 0 olarak ayarlamak, sınır olmadığı anlamına gelir.",
|
||||
@@ -342,17 +345,17 @@
|
||||
"unknown_filter": "Bilinmeyen filtre {{filterId}}",
|
||||
"known_tracker": "Bilinen izleyici",
|
||||
"install_welcome_title": "AdGuard Home'a hoş geldiniz!",
|
||||
"install_welcome_desc": "AdGuard Home, ağ genelinde reklamları ve izleyicileri engelleyen bir DNS sunucusudur. Tüm ağınızı ve tüm cihazlarınızı kontrol etmenizi sağlar, istemci tarafında herhangi bir program kullanmanıza gerek duymaz.",
|
||||
"install_welcome_desc": "AdGuard Home, ağ genelinde reklam ve izleyici engelleyen bir DNS sunucusudur. Tüm ağınızı ve cihazlarınızı kontrol etmenizi sağlar ve istemci tarafında ek bir yazılım kullanmanıza gerek duymaz.",
|
||||
"install_settings_title": "Yönetici Web Arayüzü",
|
||||
"install_settings_listen": "Dinleme arayüzü",
|
||||
"install_settings_port": "Bağlantı noktası",
|
||||
"install_settings_interface_link": "AdGuard Home yönetici web arayüzünüz aşağıdaki adreslerde bulunacaktır:",
|
||||
"install_settings_interface_link": "AdGuard Home yönetici web arayüzüne aşağıdaki adreslerden erişebilirsiniz:",
|
||||
"form_error_port": "Geçerli bir bağlantı noktası değeri girin",
|
||||
"install_settings_dns": "DNS sunucusu",
|
||||
"install_settings_dns_desc": "Aşağıdaki adreslerde DNS sunucusunu kullanmak için cihazlarınızı veya yönlendiricinizi yapılandırmanız gerekir:",
|
||||
"install_settings_dns_desc": "Cihazlarınızı veya yönlendiricinizi aşağıdaki adreslerdeki DNS sunucusunu kullanacak şekilde yapılandırmanız gerekir:",
|
||||
"install_settings_all_interfaces": "Tüm arayüzler",
|
||||
"install_auth_title": "Kimlik Doğrulama",
|
||||
"install_auth_desc": "AdGuard Home yönetim web arayüzü için şifre doğrulaması yapılandırılmalıdır. AdGuard Home'a yalnızca yerel ağınızdan erişilebilir olsa bile, onu sınırsız erişimden korumak yine de önemlidir.",
|
||||
"install_auth_desc": "AdGuard Home yönetici web arayüzüne parola ile kimlik doğrulama yapılandırılmalıdır. AdGuard Home yalnızca yerel ağınızdan erişilebilir olsa bile, yine de yetkisiz erişime karşı korunması önemlidir.",
|
||||
"install_auth_username": "Kullanıcı adı",
|
||||
"install_auth_password": "Parola",
|
||||
"install_auth_confirm": "Parolayı onayla",
|
||||
@@ -366,10 +369,10 @@
|
||||
"install_devices_router": "Yönlendirici",
|
||||
"install_devices_router_desc": "Bu kurulum, ev yönlendiricinize bağlı tüm cihazları otomatik olarak kapsar ve her birini elle yapılandırmanıza gerek yoktur.",
|
||||
"install_devices_address": "AdGuard Home DNS sunucusu aşağıdaki adresleri dinliyor",
|
||||
"install_devices_router_list_1": "Yönlendiricinizin ayarlarına gidin. Genellikle tarayıcınızdan http://192.168.0.1/ veya http://192.168.1.1/ gibi bir URL aracılığıyla erişebilirsiniz. Bir parola girmeniz istenebilir. Hatırlamıyorsanız, genellikle yönlendiricinin üzerindeki bir düğmeye basarak parolayı sıfırlayabilirsiniz, ancak bu işlemin seçilmesi durumunda yüksek ihtimalle tüm yönlendirici yapılandırmasını kaybedeceğinizi unutmayın. Yönlendiricinizin kurulumu için bir uygulama gerekiyorsa, lütfen uygulamayı telefonunuza veya PC'nize yükleyin ve yönlendiricinin ayarlarına erişmek için kullanın.",
|
||||
"install_devices_router_list_1": "Yönlendiricinizin ayarlarına gidin. Genellikle, tarayıcınızdan http://192.168.0.1/ veya http://192.168.1.1/ gibi bir URL üzerinden erişebilirsiniz. Giriş yaparken bir parola girmeniz istenebilir. Parolanızı hatırlamıyorsanız, genellikle yönlendiricinin üzerindeki bir düğmeye basarak parolayı sıfırlayabilirsiniz, ancak bu işlemi seçerseniz yönlendiricinin tüm yapılandırmasını kaybedebileceğinizi unutmayın. Yönlendiricinizin kurulumu için bir uygulama gerekiyorsa, lütfen uygulamayı telefonunuza veya bilgisayarınıza yükleyin ve yönlendiricinin ayarlarına erişmek için bu uygulamayı kullanın.",
|
||||
"install_devices_router_list_2": "DHCP/DNS ayarlarını bulun. DNS satırlarını arayın, genelde iki veya üç tanedir, üç rakam girilebilen dört ayrı grup içeren satırdır.",
|
||||
"install_devices_router_list_3": "AdGuard Home sunucu adreslerinizi oraya girin.",
|
||||
"install_devices_router_list_4": "Bazı yönlendirici türlerinde özel bir DNS sunucusu ayarlanamaz. Bu durumda, AdGuard Home'u <0>DHCP sunucusu</0> olarak ayarlamak yardımcı olabilir. Aksi takdirde, yönlendirici modeliniz için DNS sunucularını nasıl ayarlayacağınız konusunda yönlendirici kılavuzuna bakmalısınız.",
|
||||
"install_devices_router_list_4": "Bazı yönlendirici türlerinde özel bir DNS sunucusu yapılandırılamaz. Bu durumda, AdGuard Home'u bir <0>DHCP sunucusu</0> olarak yapılandırmak yardımcı olabilir. Aksi takdirde, yönlendirici modelinizde DNS sunucularını nasıl özelleştireceğinizi öğrenmek için yönlendirici kılavuzunu kontrol etmelisiniz.",
|
||||
"install_devices_windows_list_1": "Başlat menüsünden veya Windows araması aracılığıyla Denetim Masası'nı açın.",
|
||||
"install_devices_windows_list_2": "Ağ ve İnternet kategorisine girin ve ardından Ağ ve Paylaşım Merkezi'ne girin.",
|
||||
"install_devices_windows_list_3": "Sol panelde \"Bağdaştırıcı ayarlarını değiştirin\" öğesine tıklayın.",
|
||||
@@ -389,7 +392,7 @@
|
||||
"install_devices_ios_list_2": "Sol menüde bulunan Wi-Fi bölümüne girin (telefon ağlar için özel DNS sunucusu ayarlanamaz).",
|
||||
"install_devices_ios_list_3": "O anda aktif olan ağın adına dokunun.",
|
||||
"install_devices_ios_list_4": "DNS alanına AdGuard Home sunucunuzun adreslerini girin.",
|
||||
"get_started": "Başlayın",
|
||||
"get_started": "Başla",
|
||||
"next": "Sonraki",
|
||||
"open_dashboard": "Panoyu Aç",
|
||||
"install_saved": "Başarıyla kaydedildi",
|
||||
@@ -452,14 +455,14 @@
|
||||
"settings_global": "Genel",
|
||||
"settings_custom": "Özel",
|
||||
"table_client": "İstemci",
|
||||
"table_name": "AdAdı",
|
||||
"table_name": "Ad",
|
||||
"save_btn": "Kaydet",
|
||||
"client_add": "İstemci Ekle",
|
||||
"client_new": "Yeni İstemci",
|
||||
"client_edit": "İstemciyi Düzenle",
|
||||
"client_identifier": "Tanımlayıcı",
|
||||
"ip_address": "IP adresi",
|
||||
"client_identifier_desc": "İstemciler IP adresleri, CIDR, MAC adresleri veya ClientID (DoT/DoH/DoQ için kullanılabilir) ile tanımlanabilir. İstemcileri nasıl tanımlayacağınız hakkında daha fazla bilgiyi <0>buradan</0> edinebilirsiniz.",
|
||||
"client_identifier_desc": "İstemciler, IP adresi, CIDR, MAC adresi veya ClientID (DoT/DoH/DoQ için kullanılabilir) ile tanımlanabilir. İstemcileri nasıl tanımlayacağınız hakkında daha fazla bilgiye <0>buradan</0> ulaşabilirsiniz.",
|
||||
"form_enter_ip": "IP girin",
|
||||
"form_enter_subnet_ip": "\"{{cidr}}\" alt ağına bir IP adresi girin",
|
||||
"form_enter_mac": "MAC adresi girin",
|
||||
@@ -476,7 +479,7 @@
|
||||
"client_confirm_delete": "\"{{key}}\" istemcisini silmek istediğinizden emin misiniz?",
|
||||
"list_confirm_delete": "Bu listeyi silmek istediğinizden emin misiniz?",
|
||||
"auto_clients_title": "Çalışma zamanı istemcileri",
|
||||
"auto_clients_desc": "AdGuard Home'u kullanan veya kullanabilecek cihazların IP adresleri hakkında bilgiler. Bu bilgiler, hosts dosyaları, ters DNS, vb. dâhil olmak üzere çeşitli kaynaklardan toplanır.",
|
||||
"auto_clients_desc": "AdGuard Home'u kullanan veya kullanabilecek cihazların IP adresleri hakkında bilgiler. Bu bilgiler, ana bilgisayar dosyaları, ters DNS sorguları ve çeşitli diğer kaynaklardan toplanmaktadır.",
|
||||
"access_title": "Erişim ayarları",
|
||||
"access_desc": "AdGuard Home DNS sunucusu için erişim kurallarını buradan yapılandırabilirsiniz",
|
||||
"access_allowed_title": "İzin verilen istemciler",
|
||||
@@ -598,12 +601,12 @@
|
||||
"disable_ipv6": "IPv6 adreslerinin çözümlenmesini devre dışı bırak",
|
||||
"disable_ipv6_desc": "IPv6 adresleri için tüm DNS sorgularını bırakın (AAAA yazın) ve HTTPS yanıtlarından IPv6 ipuçlarını kaldırın.",
|
||||
"fastest_addr": "En hızlı IP adresi",
|
||||
"fastest_addr_desc": "Tüm DNS sunucularını sorgulayın ve tüm yanıtlar arasından en hızlı olan IP adresini döndürün. AdGuard Home'un tüm DNS sunucularından yanıt beklemesi gerektiği için DNS sorgularını yavaşlatır, ancak genel bağlantıyı iyileştirir.",
|
||||
"fastest_addr_desc": "<b>Tüm</b> DNS sunucularından yanıt bekler, her sunucu için TCP bağlantı hızını ölçer ve en hızlı bağlantı hızına sahip sunucunun IP adresini döndürür.<br/>Bu yapılandırma, bir veya daha fazla üst kaynak sunucusu yanıt vermediğinde, DNS sorgularını önemli ölçüde yavaşlatabilir. Üst kaynak sunucularınızın kararlı olduğundan ve üst kaynak zaman aşım sürenizin düşük olduğundan emin olun.",
|
||||
"autofix_warning_text": "\"Düzelt\" seçeneğine tıklarsanız, AdGuard Home, sisteminizi AdGuard Home DNS sunucusunu kullanacak şekilde yapılandırır.",
|
||||
"autofix_warning_list": "Bu görevleri gerçekleştirir: <0>Sistem DNSStubListener'ı devre dışı bırakın</0> <0>DNS sunucusu adresini 127.0.0.1 olarak ayarlayın</0> <0>/etc/resolv.conf'un sembolik bağlantı hedefini /run/systemd/resolve/resolv.conf ile değiştirin<0> <0>DNSStubListener'ı durdurun (systemd çözümlenmiş hizmeti yeniden yükleyin)</0>",
|
||||
"autofix_warning_result": "Sonuç olarak, sisteminizden gelen tüm DNS istekleri varsayılan olarak AdGuard Home tarafından işlenecektir.",
|
||||
"tags_title": "Etiketler",
|
||||
"tags_desc": "İstemciye karşılık gelen etiketleri seçebilirsiniz. Etiketleri daha kesin olarak uygulamak için filtreleme kurallarına dâhil edin. <0>Daha fazla bilgi edinin</0>.",
|
||||
"tags_desc": "İstemciyi tanımlayan etiketleri seçebilirsiniz. Filtreleme kurallarına etiketleri dahil ederek daha hassas bir şekilde uygulayabilirsiniz. <0>Daha fazla bilgi edinin</0>.",
|
||||
"form_select_tags": "İstemci etiketlerini seçin",
|
||||
"check_title": "Filtrelemeyi denetleyin",
|
||||
"check_desc": "Ana makine adının filtreleme durumunu kontrol edin.",
|
||||
@@ -617,6 +620,10 @@
|
||||
"check_cname": "CNAME: {{cname}}",
|
||||
"check_reason": "Sebep: {{reason}}",
|
||||
"check_service": "Hizmet adı: {{service}}",
|
||||
"check_hostname": "Ana makine adı veya alan adı",
|
||||
"check_client_id": "İstemci tanımlayıcısı (ClientID veya IP adresi)",
|
||||
"check_enter_client_id": "İstemci tanımlayıcısı girin",
|
||||
"check_dns_record": "DNS kayıt türünü seçin",
|
||||
"service_name": "Hizmet adı",
|
||||
"check_not_found": "Filtre listelerinizde bulunamadı",
|
||||
"client_confirm_block": "\"{{ip}}\" istemcisini engellemek istediğinizden emin misiniz?",
|
||||
@@ -624,11 +631,11 @@
|
||||
"client_blocked": "\"{{ip}}\" istemcisi başarıyla engellendi",
|
||||
"client_unblocked": "\"{{ip}}\" istemcinin engellemesi başarıyla kaldırıldı",
|
||||
"static_ip": "Sabit IP adresi",
|
||||
"static_ip_desc": "AdGuard Home bir sunucudur, bu nedenle düzgün çalışması için sabit bir IP adresine ihtiyacı vardır. Aksi takdirde, yönlendiriciniz bir zaman sonra bu cihaza farklı bir IP adresi atayabilir.",
|
||||
"static_ip_desc": "AdGuard Home bir sunucudur, bu nedenle düzgün çalışabilmesi için sabit bir IP adresine ihtiyaç duyar. Aksi takdirde, yönlendiriciniz bu cihaza farklı bir IP adresi atayabilir.",
|
||||
"set_static_ip": "Sabit IP adresi ayarla",
|
||||
"install_static_ok": "Güzel haber! Sabit IP adresi zaten yapılandırılmış",
|
||||
"install_static_error": "AdGuard Home, bu ağ arayüzü için otomatik olarak yapılandıramıyor. Lütfen bunu elle nasıl yapacağınızla ilgili talimatlara bakın.",
|
||||
"install_static_configure": "AdGuard Home, <0>{{ip}}</0> dinamik IP adresinin kullanıldığını tespit etti. Sabit adresiniz olarak ayarlanmasını ister misiniz?",
|
||||
"install_static_configure": "AdGuard Home, <0>{{ip}}</0> sabit IP adresinin kullanıldığını tespit etti. Sabit adresiniz olarak ayarlanmasını ister misiniz?",
|
||||
"confirm_static_ip": "AdGuard Home, {{ip}} adresini sabit IP adresiniz olacak şekilde yapılandırır. Devam etmek istiyor musunuz?",
|
||||
"list_updated": "{{count}} liste güncellendi",
|
||||
"list_updated_plural": "{{count}} liste güncellendi",
|
||||
@@ -707,8 +714,8 @@
|
||||
"custom_rotation_input": "Rotasyonu saat cinsinden girin",
|
||||
"protection_section_label": "Koruma",
|
||||
"log_and_stats_section_label": "Sorgu günlüğü ve istatistikler",
|
||||
"ignore_query_log": "Sorgu günlüğünde bu istemciyi yoksay",
|
||||
"ignore_statistics": "İstatistiklerde bu istemciyi yoksay",
|
||||
"ignore_query_log": "Sorgu günlüğünde bu istemciyi gösterme",
|
||||
"ignore_statistics": "İstatistiklerde bu istemciyi gösterme",
|
||||
"schedule_services": "Hizmet engellemeyi duraklat",
|
||||
"schedule_services_desc": "Hizmet engelleme filtresinin duraklatma planını yapılandırın",
|
||||
"schedule_services_desc_client": "Bu istemci için hizmet engelleme filtresinin duraklatma planını yapılandırın",
|
||||
@@ -742,6 +749,6 @@
|
||||
"friday_short": "Cum",
|
||||
"saturday_short": "Cmt",
|
||||
"upstream_dns_cache_configuration": "Üst kaynak DNS önbellek yapılandırması",
|
||||
"enable_upstream_dns_cache": "Bu istemcinin özel üst kaynak yapılandırması için DNS önbelleğe almayı etkinleştir",
|
||||
"enable_upstream_dns_cache": "Bu istemcinin özel üst kaynak yapılandırması için DNS önbelleğini etkinleştir",
|
||||
"dns_cache_size": "DNS önbellek boyutu, bayt cinsinden"
|
||||
}
|
||||
|
||||
@@ -620,6 +620,10 @@
|
||||
"check_cname": "CNAME: {{cname}}",
|
||||
"check_reason": "原因:{{reason}}",
|
||||
"check_service": "服务名称:{{service}}",
|
||||
"check_hostname": "主机名或域名",
|
||||
"check_client_id": "客户端标识符(ClientID 或 IP 地址)",
|
||||
"check_enter_client_id": "输入客户端标识符",
|
||||
"check_dns_record": "选择 DNS 记录类型",
|
||||
"service_name": "服务名称",
|
||||
"check_not_found": "未在您的筛选列表中找到",
|
||||
"client_confirm_block": "确定要阻止客户端 \"{{ip}}\"?",
|
||||
|
||||
@@ -620,6 +620,10 @@
|
||||
"check_cname": "正規名稱(CNAME):{{cname}}",
|
||||
"check_reason": "原因:{{reason}}",
|
||||
"check_service": "服務名稱:{{service}}",
|
||||
"check_hostname": "主機名稱或域名",
|
||||
"check_client_id": "用戶端識別碼(ClientID 或 IP 位址)",
|
||||
"check_enter_client_id": "輸入用戶識別碼",
|
||||
"check_dns_record": "選擇 DNS 記錄類型",
|
||||
"service_name": "服務名稱",
|
||||
"check_not_found": "未在您的過濾器清單中被找到",
|
||||
"client_confirm_block": "您確定您想要封鎖該用戶端 \"{{ip}}\" 嗎?",
|
||||
|
||||
@@ -9,13 +9,17 @@ import Info from './Info';
|
||||
import { RootState } from '../../../initialState';
|
||||
import { validateRequiredValue } from '../../../helpers/validators';
|
||||
import { Input } from '../../ui/Controls/Input';
|
||||
import { DNS_RECORD_TYPES } from '../../../helpers/constants';
|
||||
import { Select } from '../../ui/Controls/Select';
|
||||
|
||||
interface FormValues {
|
||||
export type FilteringCheckFormValues = {
|
||||
name: string;
|
||||
client?: string;
|
||||
qtype?: string;
|
||||
}
|
||||
|
||||
type Props = {
|
||||
onSubmit?: (data: FormValues) => void;
|
||||
onSubmit?: (data: FilteringCheckFormValues) => void;
|
||||
};
|
||||
|
||||
const Check = ({ onSubmit }: Props) => {
|
||||
@@ -27,11 +31,13 @@ const Check = ({ onSubmit }: Props) => {
|
||||
const {
|
||||
control,
|
||||
handleSubmit,
|
||||
formState: { isDirty, isValid },
|
||||
} = useForm<FormValues>({
|
||||
formState: { isValid },
|
||||
} = useForm<FilteringCheckFormValues>({
|
||||
mode: 'onBlur',
|
||||
defaultValues: {
|
||||
name: '',
|
||||
client: '',
|
||||
qtype: DNS_RECORD_TYPES[0],
|
||||
},
|
||||
});
|
||||
|
||||
@@ -48,24 +54,56 @@ const Check = ({ onSubmit }: Props) => {
|
||||
<Input
|
||||
{...field}
|
||||
type="text"
|
||||
label={t('check_hostname')}
|
||||
data-testid="check_domain_name"
|
||||
placeholder={t('form_enter_host')}
|
||||
placeholder="example.com"
|
||||
error={fieldState.error?.message}
|
||||
rightAddon={
|
||||
<span className="input-group-append">
|
||||
<button
|
||||
className="btn btn-success btn-standard btn-large"
|
||||
type="submit"
|
||||
data-testid="check_domain_submit"
|
||||
disabled={!isDirty || !isValid || processingCheck}>
|
||||
{t('check')}
|
||||
</button>
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
||||
<Controller
|
||||
name="client"
|
||||
control={control}
|
||||
render={({ field, fieldState }) => (
|
||||
<Input
|
||||
{...field}
|
||||
type="text"
|
||||
data-testid="check_client_id"
|
||||
label={t('check_client_id')}
|
||||
placeholder={t('check_enter_client_id')}
|
||||
error={fieldState.error?.message}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
||||
<Controller
|
||||
name="qtype"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Select
|
||||
{...field}
|
||||
label={t('check_dns_record')}
|
||||
data-testid="check_dns_record_type"
|
||||
>
|
||||
{DNS_RECORD_TYPES.map((type) => (
|
||||
<option key={type} value={type}>
|
||||
{type}
|
||||
</option>
|
||||
))}
|
||||
</Select>
|
||||
)}
|
||||
/>
|
||||
|
||||
<button
|
||||
className="btn btn-success btn-standard btn-large"
|
||||
type="submit"
|
||||
data-testid="check_domain_submit"
|
||||
disabled={!isValid || processingCheck}
|
||||
>
|
||||
{t('check')}
|
||||
</button>
|
||||
|
||||
{hostname && (
|
||||
<>
|
||||
<hr />
|
||||
|
||||
@@ -7,7 +7,7 @@ import PageTitle from '../ui/PageTitle';
|
||||
|
||||
import Examples from './Examples';
|
||||
|
||||
import Check from './Check';
|
||||
import Check, { FilteringCheckFormValues } from './Check';
|
||||
|
||||
import { getTextareaCommentsHighlight, syncScroll } from '../../helpers/highlightTextareaComments';
|
||||
import { COMMENT_LINE_DEFAULT_TOKEN } from '../../helpers/constants';
|
||||
@@ -48,8 +48,18 @@ class CustomRules extends Component<CustomRulesProps> {
|
||||
this.props.setRules(this.props.filtering.userRules);
|
||||
};
|
||||
|
||||
handleCheck = (values: any) => {
|
||||
this.props.checkHost(values);
|
||||
handleCheck = (values: FilteringCheckFormValues) => {
|
||||
const params: FilteringCheckFormValues = { name: values.name };
|
||||
|
||||
if (values.client) {
|
||||
params.client = values.client;
|
||||
}
|
||||
|
||||
if (values.qtype) {
|
||||
params.qtype = values.qtype;
|
||||
}
|
||||
|
||||
this.props.checkHost(params);
|
||||
};
|
||||
|
||||
onScroll = (e: any) => syncScroll(e, this.ref);
|
||||
|
||||
@@ -6,6 +6,8 @@ import { useDispatch } from 'react-redux';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import classNames from 'classnames';
|
||||
import { useFormContext } from 'react-hook-form';
|
||||
import queryString from 'query-string';
|
||||
|
||||
import {
|
||||
DEBOUNCE_FILTER_TIMEOUT,
|
||||
DEFAULT_LOGS_FILTER,
|
||||
@@ -54,9 +56,17 @@ export const Form = ({ className, setIsLoading }: Props) => {
|
||||
}
|
||||
}, [responseStatusValue, setValue]);
|
||||
|
||||
useEffect(() => {
|
||||
const { search: searchUrlParam } = queryString.parse(history.location.search);
|
||||
|
||||
if (searchUrlParam !== searchValue) {
|
||||
setValue('search', searchUrlParam ? searchUrlParam.toString() : '');
|
||||
}
|
||||
}, [history.location.search, setValue, searchValue]);
|
||||
|
||||
const onInputClear = async () => {
|
||||
setIsLoading(true);
|
||||
setValue('search', DEFAULT_LOGS_FILTER.search);
|
||||
history.push(getLogsUrlParams(DEFAULT_LOGS_FILTER.search, responseStatusValue));
|
||||
setIsLoading(false);
|
||||
};
|
||||
|
||||
|
||||
@@ -314,7 +314,7 @@ const ClientsTable = ({
|
||||
}
|
||||
if (!content) {
|
||||
return content;
|
||||
}
|
||||
}
|
||||
return <LogsSearchLink search={row.original.name}>{content}</LogsSearchLink>;
|
||||
},
|
||||
},
|
||||
|
||||
@@ -523,3 +523,12 @@ export const TIME_UNITS = {
|
||||
HOURS: 'hours',
|
||||
DAYS: 'days',
|
||||
};
|
||||
|
||||
export const DNS_RECORD_TYPES = [
|
||||
"A", "AAAA", "AFSDB", "APL", "CAA", "CDNSKEY", "CDS", "CERT", "CNAME",
|
||||
"CSYNC", "DHCID", "DLV", "DNAME", "DNSKEY", "DS", "EUI48", "EUI64",
|
||||
"HINFO", "HIP", "HTTPS", "IPSECKEY", "KEY", "KX", "LOC", "MX", "NAPTR",
|
||||
"NS", "NSEC", "NSEC3", "NSEC3PARAM", "OPENPGPKEY", "PTR", "RP", "RRSIG",
|
||||
"SIG", "SMIMEA", "SOA", "SRV", "SSHFP", "SVCB", "TA", "TKEY",
|
||||
"TLSA", "TSIG", "TXT", "URI", "ZONEMD"
|
||||
];
|
||||
|
||||
@@ -28,12 +28,6 @@ export default {
|
||||
"homepage": "https://badmojr.github.io/1Hosts/",
|
||||
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_24.txt"
|
||||
},
|
||||
"1hosts_mini": {
|
||||
"name": "1Hosts (mini)",
|
||||
"categoryId": "general",
|
||||
"homepage": "https://badmojr.github.io/1Hosts/",
|
||||
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_38.txt"
|
||||
},
|
||||
"CHN_adrules": {
|
||||
"name": "CHN: AdRules DNS List",
|
||||
"categoryId": "regional",
|
||||
|
||||
@@ -669,15 +669,17 @@ export const countClientsStatistics = (ids: any, autoClients: any) => {
|
||||
* @returns {string}
|
||||
*/
|
||||
export const formatElapsedMs = (elapsedMs: string, t: (key: string) => string) => {
|
||||
const parsedElapsedMs = parseInt(elapsedMs, 10);
|
||||
const parsedElapsedMs = parseFloat(elapsedMs);
|
||||
|
||||
if (Number.isNaN(parsedElapsedMs)) {
|
||||
return elapsedMs;
|
||||
}
|
||||
|
||||
const formattedMs = formatNumber(parsedElapsedMs);
|
||||
const formattedValue = parsedElapsedMs < 1
|
||||
? parsedElapsedMs.toFixed(2)
|
||||
: Math.floor(parsedElapsedMs).toString();
|
||||
|
||||
return `${formattedMs} ${t('milliseconds_abbreviation')}`;
|
||||
return `${formattedValue} ${t('milliseconds_abbreviation')}`;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"timeUpdated": "2025-01-13T10:04:54.031Z",
|
||||
"timeUpdated": "2025-03-17T10:05:02.622Z",
|
||||
"categories": {
|
||||
"0": "audio_video_player",
|
||||
"1": "comments",
|
||||
@@ -5940,7 +5940,8 @@
|
||||
"name": "Digioh",
|
||||
"categoryId": 4,
|
||||
"url": "https://digioh.com/",
|
||||
"companyId": null
|
||||
"companyId": "digioh",
|
||||
"source": "AdGuard"
|
||||
},
|
||||
"digital.gov": {
|
||||
"name": "Digital.gov",
|
||||
@@ -8261,8 +8262,8 @@
|
||||
},
|
||||
"google_marketing": {
|
||||
"name": "Google Marketing",
|
||||
"categoryId": 6,
|
||||
"url": "https://marketingplatform.google.com/",
|
||||
"categoryId": 4,
|
||||
"url": "https://marketingplatform.google.com/about/enterprise",
|
||||
"companyId": "google",
|
||||
"source": "AdGuard"
|
||||
},
|
||||
@@ -9058,6 +9059,13 @@
|
||||
"url": "https://www.ippen-digital.de/",
|
||||
"companyId": null
|
||||
},
|
||||
"id5-sync": {
|
||||
"name": "ID5 Sync",
|
||||
"categoryId": 4,
|
||||
"url": "https://id5.io/",
|
||||
"companyId": "id5-sync",
|
||||
"source": "AdGuard"
|
||||
},
|
||||
"id_services": {
|
||||
"name": "ID Services",
|
||||
"categoryId": 6,
|
||||
@@ -20948,6 +20956,9 @@
|
||||
"wunderloop.net": "audience_science",
|
||||
"12mlbe.com": "audiencerate",
|
||||
"audiencesquare.com": "audiencesquare.com",
|
||||
"ad.gt": "audiencesquare.com",
|
||||
"audigent.com": "audiencesquare.com",
|
||||
"hadronid.net": "audiencesquare.com",
|
||||
"auditude.com": "auditude",
|
||||
"audtd.com": "audtd.com",
|
||||
"cdn.augur.io": "augur",
|
||||
@@ -21403,6 +21414,7 @@
|
||||
"static.clmbtech.com": "columbia_online",
|
||||
"combotag.com": "combotag",
|
||||
"pdk.theplatform.com": "comcast_technology_solutions",
|
||||
"theplatform.com": "comcast_technology_solutions",
|
||||
"comm100.cn": "comm100",
|
||||
"comm100.com": "comm100",
|
||||
"cdn-cs.com": "commerce_sciences",
|
||||
@@ -21682,9 +21694,6 @@
|
||||
"dtmpub.com": "dotomi",
|
||||
"double.net": "double.net",
|
||||
"2mdn.net": "doubleclick",
|
||||
"doubleclick.net": "doubleclick",
|
||||
"invitemedia.com": "doubleclick",
|
||||
"doubleclick.com": "doubleclick",
|
||||
"doublepimp.com": "doublepimp",
|
||||
"doublepimpssl.com": "doublepimp",
|
||||
"redcourtside.com": "doublepimp",
|
||||
@@ -21988,6 +21997,7 @@
|
||||
"cdn.foxpush.net": "foxpush",
|
||||
"foxpush.com": "foxpush",
|
||||
"foxtel.com.au": "foxtel",
|
||||
"foxtelgroupcdn.net.au": "foxtel",
|
||||
"foxydeal.com": "foxydeal_com",
|
||||
"yabidos.com": "fraudlogix",
|
||||
"besucherstatistiken.com": "free_counter",
|
||||
@@ -22389,6 +22399,8 @@
|
||||
"maps.google.es": "google_maps",
|
||||
"maps.google.se": "google_maps",
|
||||
"maps.gstatic.com": "google_maps",
|
||||
"doubleclick.net": "google_marketing",
|
||||
"invitemedia.com": "google_marketing",
|
||||
"adsense.google.com": "google_marketing",
|
||||
"adservice.google.ca": "google_marketing",
|
||||
"adservice.google.co.in": "google_marketing",
|
||||
@@ -22419,6 +22431,7 @@
|
||||
"adservice.google.vg": "google_marketing",
|
||||
"adtrafficquality.google": "google_marketing",
|
||||
"dai.google.com": "google_marketing",
|
||||
"doubleclick.com": "google_marketing",
|
||||
"doubleclickbygoogle.com": "google_marketing",
|
||||
"googlesyndication-cn.com": "google_marketing",
|
||||
"duo.google.com": "google_meet",
|
||||
@@ -22604,6 +22617,9 @@
|
||||
"icuazeczpeoohx.com": "icuazeczpeoohx.com",
|
||||
"id-news.net": "id-news.net",
|
||||
"idcdn.de": "id-news.net",
|
||||
"eu-1-id5-sync.com": "id5-sync",
|
||||
"id5-sync.com": "id5-sync",
|
||||
"id5.io": "id5-sync",
|
||||
"cdn.id.services": "id_services",
|
||||
"e-generator.com": "ideal_media",
|
||||
"idealo.com": "idealo_com",
|
||||
@@ -23656,6 +23672,7 @@
|
||||
"blockmetrics.com": "pagefair",
|
||||
"pagefair.com": "pagefair",
|
||||
"pagefair.net": "pagefair",
|
||||
"btloader.com": "pagefair",
|
||||
"ghmedia.com": "pagescience",
|
||||
"777seo.com": "paid-to-promote",
|
||||
"paid-to-promote.net": "paid-to-promote",
|
||||
|
||||
@@ -21,7 +21,7 @@ const Form = ({ onSubmit, processing }: LoginFormProps) => {
|
||||
control,
|
||||
formState: { isValid },
|
||||
} = useForm<LoginFormValues>({
|
||||
mode: 'onBlur',
|
||||
mode: 'onChange',
|
||||
defaultValues: {
|
||||
username: '',
|
||||
password: '',
|
||||
|
||||
89
go.mod
89
go.mod
@@ -1,10 +1,10 @@
|
||||
module github.com/AdguardTeam/AdGuardHome
|
||||
|
||||
go 1.23.6
|
||||
go 1.24.1
|
||||
|
||||
require (
|
||||
github.com/AdguardTeam/dnsproxy v0.75.0
|
||||
github.com/AdguardTeam/golibs v0.32.1
|
||||
github.com/AdguardTeam/dnsproxy v0.75.1
|
||||
github.com/AdguardTeam/golibs v0.32.5
|
||||
github.com/AdguardTeam/urlfilter v0.20.0
|
||||
github.com/NYTimes/gziphandler v1.1.1
|
||||
github.com/ameshkov/dnscrypt/v2 v2.3.0
|
||||
@@ -15,7 +15,7 @@ require (
|
||||
// TODO(e.burkov): This package is deprecated; find a new one or use our
|
||||
// own code for that. Perhaps, use gopacket.
|
||||
github.com/go-ping/ping v1.2.0
|
||||
github.com/google/go-cmp v0.6.0
|
||||
github.com/google/go-cmp v0.7.0
|
||||
github.com/google/gopacket v1.1.19
|
||||
github.com/google/renameio/v2 v2.0.0
|
||||
github.com/google/uuid v1.6.0
|
||||
@@ -33,23 +33,44 @@ require (
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/ti-mo/netfilter v0.5.2
|
||||
go.etcd.io/bbolt v1.4.0
|
||||
golang.org/x/crypto v0.32.0
|
||||
golang.org/x/exp v0.0.0-20250207012021-f9890c6ad9f3
|
||||
golang.org/x/net v0.34.0
|
||||
golang.org/x/sys v0.30.0
|
||||
golang.org/x/crypto v0.36.0
|
||||
golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa
|
||||
golang.org/x/net v0.37.0
|
||||
golang.org/x/sys v0.31.0
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
howett.net/plist v1.0.1
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.118.3 // indirect
|
||||
cloud.google.com/go/ai v0.10.0 // indirect
|
||||
cloud.google.com/go/auth v0.15.0 // indirect
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.7 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.6.0 // indirect
|
||||
cloud.google.com/go/longrunning v0.6.5 // indirect
|
||||
github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c // indirect
|
||||
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect
|
||||
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 // indirect
|
||||
github.com/ameshkov/dnsstamps v1.0.3 // indirect
|
||||
github.com/beefsack/go-rate v0.0.0-20220214233405-116f4ca011a0 // indirect
|
||||
github.com/ccojocar/zxcvbn-go v1.0.2 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/fzipp/gocyclo v0.6.0 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
||||
github.com/golangci/misspell v0.6.0 // indirect
|
||||
github.com/google/generative-ai-go v0.19.0 // indirect
|
||||
github.com/google/pprof v0.0.0-20250202011525-fc3143867406 // indirect
|
||||
github.com/google/s2a-go v0.1.9 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.5 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.14.1 // indirect
|
||||
github.com/gookit/color v1.5.4 // indirect
|
||||
github.com/gordonklaus/ineffassign v0.1.0 // indirect
|
||||
github.com/jstemmer/go-junit-report/v2 v2.1.0 // indirect
|
||||
github.com/kisielk/errcheck v1.9.0 // indirect
|
||||
github.com/mdlayher/socket v0.5.1 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.22.2 // indirect
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
|
||||
@@ -58,11 +79,55 @@ require (
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/quic-go/qpack v0.5.1 // indirect
|
||||
github.com/robfig/cron/v3 v3.0.1 // indirect
|
||||
github.com/rogpeppe/go-internal v1.14.1 // indirect
|
||||
github.com/securego/gosec/v2 v2.22.2 // indirect
|
||||
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 // indirect
|
||||
github.com/uudashr/gocognit v1.2.0 // indirect
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect
|
||||
go.opentelemetry.io/otel v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.35.0 // indirect
|
||||
go.uber.org/mock v0.5.0 // indirect
|
||||
golang.org/x/mod v0.23.0 // indirect
|
||||
golang.org/x/sync v0.11.0 // indirect
|
||||
golang.org/x/text v0.22.0 // indirect
|
||||
golang.org/x/tools v0.29.0 // indirect
|
||||
golang.org/x/exp/typeparams v0.0.0-20250305212735-054e65f0b394 // indirect
|
||||
golang.org/x/mod v0.24.0 // indirect
|
||||
golang.org/x/oauth2 v0.28.0 // indirect
|
||||
golang.org/x/sync v0.12.0 // indirect
|
||||
golang.org/x/telemetry v0.0.0-20250305155315-2a181eac97a3 // indirect
|
||||
golang.org/x/term v0.30.0 // indirect
|
||||
golang.org/x/text v0.23.0 // indirect
|
||||
golang.org/x/time v0.11.0 // indirect
|
||||
golang.org/x/tools v0.31.0 // indirect
|
||||
golang.org/x/vuln v1.1.4 // indirect
|
||||
gonum.org/v1/gonum v0.15.1 // indirect
|
||||
google.golang.org/api v0.224.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb // indirect
|
||||
google.golang.org/grpc v1.71.0 // indirect
|
||||
google.golang.org/protobuf v1.36.5 // indirect
|
||||
honnef.co/go/tools v0.6.1 // indirect
|
||||
mvdan.cc/editorconfig v0.3.0 // indirect
|
||||
mvdan.cc/gofumpt v0.7.0 // indirect
|
||||
mvdan.cc/sh/v3 v3.11.0 // indirect
|
||||
mvdan.cc/unparam v0.0.0-20250301125049-0df0534333a4 // indirect
|
||||
)
|
||||
|
||||
tool (
|
||||
github.com/fzipp/gocyclo/cmd/gocyclo
|
||||
github.com/golangci/misspell/cmd/misspell
|
||||
github.com/gordonklaus/ineffassign
|
||||
github.com/jstemmer/go-junit-report/v2
|
||||
github.com/kisielk/errcheck
|
||||
github.com/securego/gosec/v2/cmd/gosec
|
||||
github.com/uudashr/gocognit/cmd/gocognit
|
||||
golang.org/x/tools/go/analysis/passes/fieldalignment/cmd/fieldalignment
|
||||
golang.org/x/tools/go/analysis/passes/nilness/cmd/nilness
|
||||
golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow
|
||||
golang.org/x/vuln/cmd/govulncheck
|
||||
honnef.co/go/tools/cmd/staticcheck
|
||||
mvdan.cc/gofumpt
|
||||
mvdan.cc/sh/v3/cmd/shfmt
|
||||
mvdan.cc/unparam
|
||||
)
|
||||
|
||||
171
go.sum
171
go.sum
@@ -1,9 +1,23 @@
|
||||
github.com/AdguardTeam/dnsproxy v0.75.0 h1:v8/Oq/xPYzNoALR7SEUZEIbKmjnPcXLVhJLFVbrozEc=
|
||||
github.com/AdguardTeam/dnsproxy v0.75.0/go.mod h1:O2qoXwF4BUBFui7OMUiWSYwapEDcYxKWeur4+jfy9nM=
|
||||
github.com/AdguardTeam/golibs v0.32.1 h1:Ajf6Q0k+A9zjFbj8HOzNAbHImrV4JtbT0vwy02D6VeI=
|
||||
github.com/AdguardTeam/golibs v0.32.1/go.mod h1:dXRLSsnJQDxOfQVl0ochy1bfk4NgnJQGQdR1YPJdwcw=
|
||||
cloud.google.com/go v0.118.3 h1:jsypSnrE/w4mJysioGdMBg4MiW/hHx/sArFpaBWHdME=
|
||||
cloud.google.com/go v0.118.3/go.mod h1:Lhs3YLnBlwJ4KA6nuObNMZ/fCbOQBPuWKPoE0Wa/9Vc=
|
||||
cloud.google.com/go/ai v0.10.0 h1:hwj6CI6sMKubXodoJJGTy/c2T1RbbLGM6TL3QoAvzU8=
|
||||
cloud.google.com/go/ai v0.10.0/go.mod h1:kvnt2KeHqX8+41PVeMRBETDyQAp/RFvBWGdx/aGjNMo=
|
||||
cloud.google.com/go/auth v0.15.0 h1:Ly0u4aA5vG/fsSsxu98qCQBemXtAtJf+95z9HK+cxps=
|
||||
cloud.google.com/go/auth v0.15.0/go.mod h1:WJDGqZ1o9E9wKIL+IwStfyn/+s59zl4Bi+1KQNVXLZ8=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.7 h1:/Lc7xODdqcEw8IrZ9SvwnlLX6j9FHQM74z6cBk9Rw6M=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.7/go.mod h1:NTbTTzfvPl1Y3V1nPpOgl2w6d/FjO7NNUQaWSox6ZMc=
|
||||
cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I=
|
||||
cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg=
|
||||
cloud.google.com/go/longrunning v0.6.5 h1:sD+t8DO8j4HKW4QfouCklg7ZC1qC4uzVZt8iz3uTW+Q=
|
||||
cloud.google.com/go/longrunning v0.6.5/go.mod h1:Et04XK+0TTLKa5IPYryKf5DkpwImy6TluQ1QTLwlKmY=
|
||||
github.com/AdguardTeam/dnsproxy v0.75.1 h1:ux2sQfF/9+WRo6a32g9NtfaAPU19gJhqkEu2OZflxJg=
|
||||
github.com/AdguardTeam/dnsproxy v0.75.1/go.mod h1:HKBI/IO2/ACOjfTV6qIzB5ZDDxfjgHHvQ3hIbGg9wvc=
|
||||
github.com/AdguardTeam/golibs v0.32.5 h1:4Rkv2xBnyJe6l/EM2MFgoY1S4pweYwDgLTYg2MDArEA=
|
||||
github.com/AdguardTeam/golibs v0.32.5/go.mod h1:agsvz8Iyv0uV9NU56hpCoFLAtSPkiBf9nPVhDvdUIb0=
|
||||
github.com/AdguardTeam/urlfilter v0.20.0 h1:X32qiuVCVd8WDYCEsbdZKfXMzwdVqrdulamtUi4rmzs=
|
||||
github.com/AdguardTeam/urlfilter v0.20.0/go.mod h1:gjrywLTxfJh6JOkwi9SU+frhP7kVVEZ5exFGkR99qpk=
|
||||
github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c h1:pxW6RcqyfI9/kWtOwnv/G+AzdKuy2ZrqINhenH4HyNs=
|
||||
github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I=
|
||||
github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
|
||||
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY=
|
||||
@@ -20,35 +34,67 @@ 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/ccojocar/zxcvbn-go v1.0.2 h1:na/czXU8RrhXO4EZme6eQJLR4PzcGsahsBOAwU6I3Vg=
|
||||
github.com/ccojocar/zxcvbn-go v1.0.2/go.mod h1:g1qkXtUSvHP8lhHp5GrSmTz6uWALGRMQdw6Qnz/hi60=
|
||||
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=
|
||||
github.com/digineo/go-ipset/v2 v2.2.1 h1:k6skY+0fMqeUjjeWO/m5OuWPSZUAn7AucHMnQ1MX77g=
|
||||
github.com/digineo/go-ipset/v2 v2.2.1/go.mod h1:wBsNzJlZlABHUITkesrggFnZQtgW5wkqw1uo8Qxe0VU=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
|
||||
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/fzipp/gocyclo v0.6.0 h1:lsblElZG7d3ALtGMx9fmxeTKZaLLpU8mET09yN4BBLo=
|
||||
github.com/fzipp/gocyclo v0.6.0/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlyaLkLoA=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/go-ping/ping v1.2.0 h1:vsJ8slZBZAXNCK4dPcI2PEE9eM9n9RbXbGouVQ/Y4yQ=
|
||||
github.com/go-ping/ping v1.2.0/go.mod h1:xIFjORFzTxqIV/tDVGO4eDy/bLuSyawEeojSm3GfRGk=
|
||||
github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI=
|
||||
github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/golangci/misspell v0.6.0 h1:JCle2HUTNWirNlDIAUO44hUsKhOFqGPoC4LZxlaSXDs=
|
||||
github.com/golangci/misspell v0.6.0/go.mod h1:keMNyY6R9isGaSAu+4Q8NMBwMPkh15Gtc8UCVoDtAWo=
|
||||
github.com/google/generative-ai-go v0.19.0 h1:R71szggh8wHMCUlEMsW2A/3T+5LdEIkiaHSYgSpUgdg=
|
||||
github.com/google/generative-ai-go v0.19.0/go.mod h1:JYolL13VG7j79kM5BtHz4qwONHkeJQzOCkKXnpqtS/E=
|
||||
github.com/google/go-cmdtest v0.4.1-0.20220921163831-55ab3332a786 h1:rcv+Ippz6RAtvaGgKxc+8FQIpxHgsF+HBzPyYL2cyVU=
|
||||
github.com/google/go-cmdtest v0.4.1-0.20220921163831-55ab3332a786/go.mod h1:apVn/GCasLZUVpAJ6oWAuyP7Ne7CEsQbTnc0plM3m+o=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
|
||||
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/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
|
||||
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
|
||||
github.com/google/pprof v0.0.0-20250202011525-fc3143867406 h1:wlQI2cYY0BsWmmPPAnxfQ8SDW0S3Jasn+4B8kXFxprg=
|
||||
github.com/google/pprof v0.0.0-20250202011525-fc3143867406/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
||||
github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/renameio/v2 v2.0.0 h1:UifI23ZTGY8Tt29JbYFiuyIU3eX+RNFtUwefq9qAhxg=
|
||||
github.com/google/renameio/v2 v2.0.0/go.mod h1:BtmJXm5YlszgC+TD4HOEEUFgkJP3nLxehU6hfe7jRt4=
|
||||
github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
|
||||
github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
|
||||
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.5 h1:VgzTY2jogw3xt39CusEnFJWm7rlsq5yL5q9XdLOuP5g=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.5/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA=
|
||||
github.com/googleapis/gax-go/v2 v2.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrkurSS/Q=
|
||||
github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA=
|
||||
github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0=
|
||||
github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w=
|
||||
github.com/gordonklaus/ineffassign v0.1.0 h1:y2Gd/9I7MdY1oEIt+n+rowjBNDcLQq3RsH5hwJd0f9s=
|
||||
github.com/gordonklaus/ineffassign v0.1.0/go.mod h1:Qcp2HIAYhR7mNUVSIxZww3Guk4it82ghYcEXIAk+QT0=
|
||||
github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714 h1:/jC7qQFrv8CrSJVmaolDVOxTfS9kc36uB6H40kdbQq8=
|
||||
github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905 h1:q3OEI9RaN/wwcx+qgGo6ZaoJkCiDYe/gjDLfq7lQQF4=
|
||||
@@ -57,8 +103,14 @@ github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJS
|
||||
github.com/josharian/native v1.0.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||
github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86 h1:elKwZS1OcdQ0WwEDBeqxKwb7WB62QX8bvZ/FJnVXIfk=
|
||||
github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86/go.mod h1:aFAMtuldEgx/4q7iSGazk22+IcgvtiC+HIimFO9XlS8=
|
||||
github.com/jstemmer/go-junit-report/v2 v2.1.0 h1:X3+hPYlSczH9IMIpSC9CQSZA0L+BipYafciZUWHEmsc=
|
||||
github.com/jstemmer/go-junit-report/v2 v2.1.0/go.mod h1:mgHVr7VUo5Tn8OLVr1cKnLuEy0M92wdRntM99h7RkgQ=
|
||||
github.com/kardianos/service v1.2.2 h1:ZvePhAHfvo0A7Mftk/tEzqEZ7Q4lgnR8sGz4xu1YX60=
|
||||
github.com/kardianos/service v1.2.2/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM=
|
||||
github.com/kisielk/errcheck v1.9.0 h1:9xt1zI9EBfcYBvdU1nVrzMzzUPUtPKs9bVSIM3TAb3M=
|
||||
github.com/kisielk/errcheck v1.9.0/go.mod h1:kQxWMMVZgIkDq7U8xtG/n2juOjbLgZtedi0D+/VL/i8=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
|
||||
@@ -78,8 +130,6 @@ github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos
|
||||
github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ=
|
||||
github.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY=
|
||||
github.com/miekg/dns v1.1.63/go.mod h1:6NGHfjhpmr5lt3XPLuyfDJi5AXbNIPM9PY6H6sF1Nfs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU=
|
||||
github.com/onsi/ginkgo/v2 v2.22.2/go.mod h1:oeMosUL+8LtarXBHu/c0bx2D/K9zyQ6uX3cTyztHwsk=
|
||||
github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8=
|
||||
@@ -101,6 +151,10 @@ github.com/quic-go/quic-go v0.49.0 h1:w5iJHXwHxs1QxyBv1EHKuC50GX5to8mJAxvtnttJp9
|
||||
github.com/quic-go/quic-go v0.49.0/go.mod h1:s2wDnmCdooUQBmQfpUSTCYBl1/D4FcqbULMMkASvR6s=
|
||||
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/securego/gosec/v2 v2.22.2 h1:IXbuI7cJninj0nRpZSLCUlotsj8jGusohfONMrHoF6g=
|
||||
github.com/securego/gosec/v2 v2.22.2/go.mod h1:UEBGA+dSKb+VqM6TdehR7lnQtIIMorYJ4/9CW1KVQBE=
|
||||
github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=
|
||||
github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk=
|
||||
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
|
||||
@@ -120,34 +174,61 @@ github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+F
|
||||
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
|
||||
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 h1:pyC9PaHYZFgEKFdlp3G8RaCKgVpHZnecvArXvPXcFkM=
|
||||
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701/go.mod h1:P3a5rG4X7tI17Nn3aOIAYr5HbIMukwXG0urG0WuL8OA=
|
||||
github.com/uudashr/gocognit v1.2.0 h1:3BU9aMr1xbhPlvJLSydKwdLN3tEUUrzPSSM8S4hDYRA=
|
||||
github.com/uudashr/gocognit v1.2.0/go.mod h1:k/DdKPI6XBZO1q7HgoV2juESI2/Ofj9AcHPZhBBdrTU=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
go.etcd.io/bbolt v1.4.0 h1:TU77id3TnN/zKr7CO/uk+fBCwF2jGcMuw2B/FMAzYIk=
|
||||
go.etcd.io/bbolt v1.4.0/go.mod h1:AsD+OCi/qPN1giOX1aiLAha3o1U8rAz65bvN4j0sRuk=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 h1:x7wzEgXfnzJcHDwStJT+mxOz4etr2EcexjqhBvmoakw=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ=
|
||||
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
|
||||
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
|
||||
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
|
||||
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
|
||||
go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
|
||||
go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
|
||||
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
|
||||
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
|
||||
go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
|
||||
go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
|
||||
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.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
|
||||
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
|
||||
golang.org/x/exp v0.0.0-20250207012021-f9890c6ad9f3 h1:qNgPs5exUA+G0C96DrPwNrvLSj7GT/9D+3WMWUcUg34=
|
||||
golang.org/x/exp v0.0.0-20250207012021-f9890c6ad9f3/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU=
|
||||
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
|
||||
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
|
||||
golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa h1:t2QcU6V556bFjYgu4L6C+6VrCPyJZ+eyRsABUPs1mz4=
|
||||
golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa/go.mod h1:BHOTPb3L19zxehTsLoJXVaTktb06DFgmdW6Wb9s8jqk=
|
||||
golang.org/x/exp/typeparams v0.0.0-20250305212735-054e65f0b394 h1:VI4qDpTkfFaCXEPrbojidLgVQhj2x4nzTccG0hjaLlU=
|
||||
golang.org/x/exp/typeparams v0.0.0-20250305212735-054e65f0b394/go.mod h1:LKZHyeOpPuZcMgxeHjJp4p5yvxrCX1xDvH10zYHhjjQ=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM=
|
||||
golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
|
||||
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
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.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
|
||||
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c=
|
||||
golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
||||
golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc=
|
||||
golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
|
||||
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.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
|
||||
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
|
||||
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
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=
|
||||
@@ -155,35 +236,65 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/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.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/telemetry v0.0.0-20250305155315-2a181eac97a3 h1:k+pofz4/0MRETtVtItAwfDgPUvNlWrUrFw+8dtUVUa8=
|
||||
golang.org/x/telemetry v0.0.0-20250305155315-2a181eac97a3/go.mod h1:16eI1RtbPZAEm3u7hpIh7JM/w5AbmlDtnrdKYaREic8=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
|
||||
golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
|
||||
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=
|
||||
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
||||
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
||||
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
||||
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
|
||||
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE=
|
||||
golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU=
|
||||
golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ=
|
||||
golang.org/x/vuln v1.1.4 h1:Ju8QsuyhX3Hk8ma3CesTbO8vfJD9EvUBgHvkxHBzj0I=
|
||||
golang.org/x/vuln v1.1.4/go.mod h1:F+45wmU18ym/ca5PLTPLsSzr2KppzswxPP603ldA67s=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gonum.org/v1/gonum v0.15.1 h1:FNy7N6OUZVUaWG9pTiD+jlhdQ3lMP+/LcTpJ6+a8sQ0=
|
||||
gonum.org/v1/gonum v0.15.1/go.mod h1:eZTZuRFrzu5pcyjN5wJhcIhnUdNijYxX1T2IcrOGY0o=
|
||||
google.golang.org/protobuf v1.36.3 h1:82DV7MYdb8anAVi3qge1wSnMDrnKK7ebr+I0hHRN1BU=
|
||||
google.golang.org/protobuf v1.36.3/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
google.golang.org/api v0.224.0 h1:Ir4UPtDsNiwIOHdExr3fAj4xZ42QjK7uQte3lORLJwU=
|
||||
google.golang.org/api v0.224.0/go.mod h1:3V39my2xAGkodXy0vEqcEtkqgw2GtrFL5WuBZlCTCOQ=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb h1:p31xT4yrYrSM/G4Sn2+TNUkVhFCbG9y8itM2S6Th950=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:jbe3Bkdp+Dh2IrslsFCklNhweNTBgSYanP1UXhJDhKg=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb h1:TLPQVbx1GJ8VKZxz52VAxl1EBgKXXbTiU9Fc5fZeLn4=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I=
|
||||
google.golang.org/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg=
|
||||
google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec=
|
||||
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
|
||||
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
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=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
|
||||
gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.6.1 h1:R094WgE8K4JirYjBaOpz/AvTyUu/3wbmAoskKN/pxTI=
|
||||
honnef.co/go/tools v0.6.1/go.mod h1:3puzxxljPCe8RGJX7BIy1plGbxEOZni5mR2aXe3/uk4=
|
||||
howett.net/plist v1.0.1 h1:37GdZ8tP09Q35o9ych3ehygcsL+HqKSwzctveSlarvM=
|
||||
howett.net/plist v1.0.1/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g=
|
||||
mvdan.cc/editorconfig v0.3.0 h1:D1D2wLYEYGpawWT5SpM5pRivgEgXjtEXwC9MWhEY0gQ=
|
||||
mvdan.cc/editorconfig v0.3.0/go.mod h1:NcJHuDtNOTEJ6251indKiWuzK6+VcrMuLzGMLKBFupQ=
|
||||
mvdan.cc/gofumpt v0.7.0 h1:bg91ttqXmi9y2xawvkuMXyvAA/1ZGJqYAEGjXuP0JXU=
|
||||
mvdan.cc/gofumpt v0.7.0/go.mod h1:txVFJy/Sc/mvaycET54pV8SW8gWxTlUuGHVEcncmNUo=
|
||||
mvdan.cc/sh/v3 v3.11.0 h1:q5h+XMDRfUGUedCqFFsjoFjrhwf2Mvtt1rkMvVz0blw=
|
||||
mvdan.cc/sh/v3 v3.11.0/go.mod h1:LRM+1NjoYCzuq/WZ6y44x14YNAI0NK7FLPeQSaFagGg=
|
||||
mvdan.cc/unparam v0.0.0-20250301125049-0df0534333a4 h1:WjUu4yQoT5BHT1w8Zu56SP8367OuBV5jvo+4Ulppyf8=
|
||||
mvdan.cc/unparam v0.0.0-20250301125049-0df0534333a4/go.mod h1:rthT7OuvRbaGcd5ginj6dA2oLE7YNlta9qhBNNdCaLE=
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
package aghalg
|
||||
package aghalg_test
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghalg"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNewSortedMap(t *testing.T) {
|
||||
var m SortedMap[string, int]
|
||||
var m aghalg.SortedMap[string, int]
|
||||
|
||||
letters := []string{}
|
||||
for i := range 10 {
|
||||
@@ -17,7 +18,7 @@ func TestNewSortedMap(t *testing.T) {
|
||||
}
|
||||
|
||||
t.Run("create_and_fill", func(t *testing.T) {
|
||||
m = NewSortedMap[string, int](strings.Compare)
|
||||
m = aghalg.NewSortedMap[string, int](strings.Compare)
|
||||
|
||||
nums := []int{}
|
||||
for i, r := range letters {
|
||||
@@ -68,7 +69,7 @@ func TestNewSortedMap_nil(t *testing.T) {
|
||||
val = "val"
|
||||
)
|
||||
|
||||
var m SortedMap[string, string]
|
||||
var m aghalg.SortedMap[string, string]
|
||||
|
||||
assert.Panics(t, func() {
|
||||
m.Set(key, val)
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package aghnet
|
||||
package aghnet_test
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
"testing"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -29,13 +30,13 @@ func TestGenerateHostName(t *testing.T) {
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
hostname := GenerateHostname(tc.ip)
|
||||
hostname := aghnet.GenerateHostname(tc.ip)
|
||||
assert.Equal(t, tc.want, hostname)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("invalid", func(t *testing.T) {
|
||||
assert.Panics(t, func() { GenerateHostname(netip.Addr{}) })
|
||||
assert.Panics(t, func() { aghnet.GenerateHostname(netip.Addr{}) })
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,22 +1,24 @@
|
||||
package aghnet
|
||||
package aghnet_test
|
||||
|
||||
import (
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/testutil"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// fakeIface is a stub implementation of aghnet.NetIface to simplify testing.
|
||||
// fakeIface is a stub implementation of [aghnet.NetIface] interface to simplify
|
||||
// testing.
|
||||
type fakeIface struct {
|
||||
err error
|
||||
addrs []net.Addr
|
||||
}
|
||||
|
||||
// Addrs implements the NetIface interface for *fakeIface.
|
||||
// Addrs implements the [aghnet.NetIface] interface for *fakeIface.
|
||||
func (iface *fakeIface) Addrs() (addrs []net.Addr, err error) {
|
||||
if iface.err != nil {
|
||||
return nil, iface.err
|
||||
@@ -25,6 +27,9 @@ func (iface *fakeIface) Addrs() (addrs []net.Addr, err error) {
|
||||
return iface.addrs, nil
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ aghnet.NetIface = (*fakeIface)(nil)
|
||||
|
||||
func TestIfaceIPAddrs(t *testing.T) {
|
||||
const errTest errors.Error = "test error"
|
||||
|
||||
@@ -35,76 +40,76 @@ func TestIfaceIPAddrs(t *testing.T) {
|
||||
addr6 := &net.IPNet{IP: ip6}
|
||||
|
||||
testCases := []struct {
|
||||
iface NetIface
|
||||
iface aghnet.NetIface
|
||||
name string
|
||||
wantErrMsg string
|
||||
want []net.IP
|
||||
ipv IPVersion
|
||||
ipv aghnet.IPVersion
|
||||
}{{
|
||||
iface: &fakeIface{addrs: []net.Addr{addr4}, err: nil},
|
||||
name: "ipv4_success",
|
||||
wantErrMsg: "",
|
||||
want: []net.IP{ip4},
|
||||
ipv: IPVersion4,
|
||||
ipv: aghnet.IPVersion4,
|
||||
}, {
|
||||
iface: &fakeIface{addrs: []net.Addr{addr6, addr4}, err: nil},
|
||||
name: "ipv4_success_with_ipv6",
|
||||
wantErrMsg: "",
|
||||
want: []net.IP{ip4},
|
||||
ipv: IPVersion4,
|
||||
ipv: aghnet.IPVersion4,
|
||||
}, {
|
||||
iface: &fakeIface{addrs: []net.Addr{addr4}, err: errTest},
|
||||
name: "ipv4_error",
|
||||
wantErrMsg: errTest.Error(),
|
||||
want: nil,
|
||||
ipv: IPVersion4,
|
||||
ipv: aghnet.IPVersion4,
|
||||
}, {
|
||||
iface: &fakeIface{addrs: []net.Addr{addr6}, err: nil},
|
||||
name: "ipv6_success",
|
||||
wantErrMsg: "",
|
||||
want: []net.IP{ip6},
|
||||
ipv: IPVersion6,
|
||||
ipv: aghnet.IPVersion6,
|
||||
}, {
|
||||
iface: &fakeIface{addrs: []net.Addr{addr6, addr4}, err: nil},
|
||||
name: "ipv6_success_with_ipv4",
|
||||
wantErrMsg: "",
|
||||
want: []net.IP{ip6},
|
||||
ipv: IPVersion6,
|
||||
ipv: aghnet.IPVersion6,
|
||||
}, {
|
||||
iface: &fakeIface{addrs: []net.Addr{addr6}, err: errTest},
|
||||
name: "ipv6_error",
|
||||
wantErrMsg: errTest.Error(),
|
||||
want: nil,
|
||||
ipv: IPVersion6,
|
||||
ipv: aghnet.IPVersion6,
|
||||
}, {
|
||||
iface: &fakeIface{addrs: nil, err: nil},
|
||||
name: "bad_proto",
|
||||
wantErrMsg: "invalid ip version 10",
|
||||
want: nil,
|
||||
ipv: IPVersion6 + IPVersion4,
|
||||
ipv: aghnet.IPVersion6 + aghnet.IPVersion4,
|
||||
}, {
|
||||
iface: &fakeIface{addrs: []net.Addr{&net.IPAddr{IP: ip4}}, err: nil},
|
||||
name: "ipaddr_v4",
|
||||
wantErrMsg: "",
|
||||
want: []net.IP{ip4},
|
||||
ipv: IPVersion4,
|
||||
ipv: aghnet.IPVersion4,
|
||||
}, {
|
||||
iface: &fakeIface{addrs: []net.Addr{&net.IPAddr{IP: ip6, Zone: ""}}, err: nil},
|
||||
name: "ipaddr_v6",
|
||||
wantErrMsg: "",
|
||||
want: []net.IP{ip6},
|
||||
ipv: IPVersion6,
|
||||
ipv: aghnet.IPVersion6,
|
||||
}, {
|
||||
iface: &fakeIface{addrs: []net.Addr{&net.UnixAddr{}}, err: nil},
|
||||
name: "non-ipv4",
|
||||
wantErrMsg: "",
|
||||
want: nil,
|
||||
ipv: IPVersion4,
|
||||
ipv: aghnet.IPVersion4,
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
got, err := IfaceIPAddrs(tc.iface, tc.ipv)
|
||||
got, err := aghnet.IfaceIPAddrs(tc.iface, tc.ipv)
|
||||
testutil.AssertErrorMsg(t, tc.wantErrMsg, err)
|
||||
|
||||
assert.Equal(t, tc.want, got)
|
||||
@@ -118,7 +123,10 @@ type waitingFakeIface struct {
|
||||
n int
|
||||
}
|
||||
|
||||
// Addrs implements the NetIface interface for *waitingFakeIface.
|
||||
// type check
|
||||
var _ aghnet.NetIface = (*waitingFakeIface)(nil)
|
||||
|
||||
// Addrs implements the [aghnet.NetIface] interface for *waitingFakeIface.
|
||||
func (iface *waitingFakeIface) Addrs() (addrs []net.Addr, err error) {
|
||||
if iface.err != nil {
|
||||
return nil, iface.err
|
||||
@@ -143,76 +151,76 @@ func TestIfaceDNSIPAddrs(t *testing.T) {
|
||||
addr6 := &net.IPNet{IP: ip6}
|
||||
|
||||
testCases := []struct {
|
||||
iface NetIface
|
||||
iface aghnet.NetIface
|
||||
wantErr error
|
||||
name string
|
||||
want []net.IP
|
||||
ipv IPVersion
|
||||
ipv aghnet.IPVersion
|
||||
}{{
|
||||
name: "ipv4_success",
|
||||
iface: &fakeIface{addrs: []net.Addr{addr4}, err: nil},
|
||||
ipv: IPVersion4,
|
||||
ipv: aghnet.IPVersion4,
|
||||
want: []net.IP{ip4, ip4},
|
||||
wantErr: nil,
|
||||
}, {
|
||||
name: "ipv4_success_with_ipv6",
|
||||
iface: &fakeIface{addrs: []net.Addr{addr6, addr4}, err: nil},
|
||||
ipv: IPVersion4,
|
||||
ipv: aghnet.IPVersion4,
|
||||
want: []net.IP{ip4, ip4},
|
||||
wantErr: nil,
|
||||
}, {
|
||||
name: "ipv4_error",
|
||||
iface: &fakeIface{addrs: []net.Addr{addr4}, err: errTest},
|
||||
ipv: IPVersion4,
|
||||
ipv: aghnet.IPVersion4,
|
||||
want: nil,
|
||||
wantErr: errTest,
|
||||
}, {
|
||||
name: "ipv4_wait",
|
||||
iface: &waitingFakeIface{addrs: []net.Addr{addr4}, err: nil, n: 1},
|
||||
ipv: IPVersion4,
|
||||
ipv: aghnet.IPVersion4,
|
||||
want: []net.IP{ip4, ip4},
|
||||
wantErr: nil,
|
||||
}, {
|
||||
name: "ipv6_success",
|
||||
iface: &fakeIface{addrs: []net.Addr{addr6}, err: nil},
|
||||
ipv: IPVersion6,
|
||||
ipv: aghnet.IPVersion6,
|
||||
want: []net.IP{ip6, ip6},
|
||||
wantErr: nil,
|
||||
}, {
|
||||
name: "ipv6_success_with_ipv4",
|
||||
iface: &fakeIface{addrs: []net.Addr{addr6, addr4}, err: nil},
|
||||
ipv: IPVersion6,
|
||||
ipv: aghnet.IPVersion6,
|
||||
want: []net.IP{ip6, ip6},
|
||||
wantErr: nil,
|
||||
}, {
|
||||
name: "ipv6_error",
|
||||
iface: &fakeIface{addrs: []net.Addr{addr6}, err: errTest},
|
||||
ipv: IPVersion6,
|
||||
ipv: aghnet.IPVersion6,
|
||||
want: nil,
|
||||
wantErr: errTest,
|
||||
}, {
|
||||
name: "ipv6_wait",
|
||||
iface: &waitingFakeIface{addrs: []net.Addr{addr6}, err: nil, n: 1},
|
||||
ipv: IPVersion6,
|
||||
ipv: aghnet.IPVersion6,
|
||||
want: []net.IP{ip6, ip6},
|
||||
wantErr: nil,
|
||||
}, {
|
||||
name: "empty",
|
||||
iface: &fakeIface{addrs: nil, err: nil},
|
||||
ipv: IPVersion4,
|
||||
ipv: aghnet.IPVersion4,
|
||||
want: nil,
|
||||
wantErr: nil,
|
||||
}, {
|
||||
name: "many",
|
||||
iface: &fakeIface{addrs: []net.Addr{addr4, addr4}},
|
||||
ipv: IPVersion4,
|
||||
ipv: aghnet.IPVersion4,
|
||||
want: []net.IP{ip4, ip4},
|
||||
wantErr: nil,
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
got, err := IfaceDNSIPAddrs(tc.iface, tc.ipv, 2, 0)
|
||||
got, err := aghnet.IfaceDNSIPAddrs(tc.iface, tc.ipv, 2, 0)
|
||||
require.ErrorIs(t, err, tc.wantErr)
|
||||
|
||||
assert.Equal(t, tc.want, got)
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package aghnet
|
||||
package aghnet_test
|
||||
|
||||
import (
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
||||
"github.com/AdguardTeam/golibs/netutil"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
@@ -18,7 +19,7 @@ func TestIPMut(t *testing.T) {
|
||||
}}
|
||||
|
||||
t.Run("nil_no_mut", func(t *testing.T) {
|
||||
ipmut := NewIPMut(nil)
|
||||
ipmut := aghnet.NewIPMut(nil)
|
||||
|
||||
ips := netutil.CloneIPs(testIPs)
|
||||
for i := range ips {
|
||||
@@ -28,7 +29,7 @@ func TestIPMut(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("not_nil_mut", func(t *testing.T) {
|
||||
ipmut := NewIPMut(func(ip net.IP) {
|
||||
ipmut := aghnet.NewIPMut(func(ip net.IP) {
|
||||
for i := range ip {
|
||||
ip[i] = 0
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//go:build darwin
|
||||
|
||||
package aghnet
|
||||
|
||||
import (
|
||||
24
internal/aghnet/upstream.go
Normal file
24
internal/aghnet/upstream.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package aghnet
|
||||
|
||||
import "github.com/AdguardTeam/dnsproxy/upstream"
|
||||
|
||||
// UpstreamHTTPVersions returns the HTTP versions for upstream configuration
|
||||
// depending on configuration.
|
||||
func UpstreamHTTPVersions(http3 bool) (v []upstream.HTTPVersion) {
|
||||
if !http3 {
|
||||
return upstream.DefaultHTTPVersions
|
||||
}
|
||||
|
||||
return []upstream.HTTPVersion{
|
||||
upstream.HTTPVersion3,
|
||||
upstream.HTTPVersion2,
|
||||
upstream.HTTPVersion11,
|
||||
}
|
||||
}
|
||||
|
||||
// IsCommentOrEmpty returns true if s starts with a "#" character or is empty.
|
||||
// This function is useful for filtering out non-upstream lines from upstream
|
||||
// configs.
|
||||
func IsCommentOrEmpty(s string) (ok bool) {
|
||||
return len(s) == 0 || s[0] == '#'
|
||||
}
|
||||
26
internal/aghnet/upstream_test.go
Normal file
26
internal/aghnet/upstream_test.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package aghnet_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestIsCommentOrEmpty(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
want assert.BoolAssertionFunc
|
||||
str string
|
||||
}{{
|
||||
want: assert.True,
|
||||
str: "",
|
||||
}, {
|
||||
want: assert.True,
|
||||
str: "# comment",
|
||||
}, {
|
||||
want: assert.False,
|
||||
str: "1.2.3.4",
|
||||
}} {
|
||||
tc.want(t, aghnet.IsCommentOrEmpty(tc.str))
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/next/agh"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/rdns"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/whois"
|
||||
"github.com/AdguardTeam/dnsproxy/proxy"
|
||||
"github.com/AdguardTeam/dnsproxy/upstream"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
@@ -121,26 +120,6 @@ func (p *AddressUpdater) UpdateAddress(
|
||||
p.OnUpdateAddress(ctx, ip, host, info)
|
||||
}
|
||||
|
||||
// Package dnsforward
|
||||
|
||||
// ClientsContainer is a fake [dnsforward.ClientsContainer] implementation for
|
||||
// tests.
|
||||
type ClientsContainer struct {
|
||||
OnUpstreamConfigByID func(
|
||||
id string,
|
||||
boot upstream.Resolver,
|
||||
) (conf *proxy.CustomUpstreamConfig, err error)
|
||||
}
|
||||
|
||||
// UpstreamConfigByID implements the [dnsforward.ClientsContainer] interface
|
||||
// for *ClientsContainer.
|
||||
func (c *ClientsContainer) UpstreamConfigByID(
|
||||
id string,
|
||||
boot upstream.Resolver,
|
||||
) (conf *proxy.CustomUpstreamConfig, err error) {
|
||||
return c.OnUpstreamConfigByID(id, boot)
|
||||
}
|
||||
|
||||
// Package filtering
|
||||
|
||||
// Resolver is a fake [filtering.Resolver] implementation for tests.
|
||||
|
||||
@@ -3,7 +3,6 @@ package aghtest_test
|
||||
import (
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghtest"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/client"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/dnsforward"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
||||
)
|
||||
|
||||
@@ -12,9 +11,6 @@ import (
|
||||
// type check
|
||||
var _ filtering.Resolver = (*aghtest.Resolver)(nil)
|
||||
|
||||
// type check
|
||||
var _ dnsforward.ClientsContainer = (*aghtest.ClientsContainer)(nil)
|
||||
|
||||
// type check
|
||||
//
|
||||
// TODO(s.chzhen): It's here to avoid the import cycle. Remove it.
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghalg"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
)
|
||||
|
||||
// macKey contains MAC as byte array of 6, 8, or 20 bytes.
|
||||
@@ -35,7 +34,7 @@ type index struct {
|
||||
// nameToUID maps client name to UID.
|
||||
nameToUID map[string]UID
|
||||
|
||||
// clientIDToUID maps client ID to UID.
|
||||
// clientIDToUID maps ClientID to UID.
|
||||
clientIDToUID map[string]UID
|
||||
|
||||
// ipToUID maps IP address to UID.
|
||||
@@ -205,19 +204,19 @@ func (ci *index) clashesMAC(c *Persistent) (p *Persistent, mac net.HardwareAddr)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// find finds persistent client by string representation of the client ID, IP
|
||||
// find finds persistent client by string representation of the ClientID, IP
|
||||
// address, or MAC.
|
||||
func (ci *index) find(id string) (c *Persistent, ok bool) {
|
||||
uid, found := ci.clientIDToUID[id]
|
||||
if found {
|
||||
return ci.uidToClient[uid], true
|
||||
c, ok = ci.findByClientID(id)
|
||||
if ok {
|
||||
return c, true
|
||||
}
|
||||
|
||||
ip, err := netip.ParseAddr(id)
|
||||
if err == nil {
|
||||
// MAC addresses can be successfully parsed as IP addresses.
|
||||
c, found = ci.findByIP(ip)
|
||||
if found {
|
||||
c, ok = ci.findByIP(ip)
|
||||
if ok {
|
||||
return c, true
|
||||
}
|
||||
}
|
||||
@@ -230,6 +229,16 @@ func (ci *index) find(id string) (c *Persistent, ok bool) {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// findByClientID finds persistent client by ClientID.
|
||||
func (ci *index) findByClientID(clientID string) (c *Persistent, ok bool) {
|
||||
uid, ok := ci.clientIDToUID[clientID]
|
||||
if ok {
|
||||
return ci.uidToClient[uid], true
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// findByName finds persistent client by name.
|
||||
func (ci *index) findByName(name string) (c *Persistent, found bool) {
|
||||
uid, found := ci.nameToUID[name]
|
||||
@@ -343,18 +352,3 @@ func (ci *index) rangeByName(f func(c *Persistent) (cont bool)) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// closeUpstreams closes upstream configurations of persistent clients.
|
||||
func (ci *index) closeUpstreams() (err error) {
|
||||
var errs []error
|
||||
ci.rangeByName(func(c *Persistent) (cont bool) {
|
||||
err = c.CloseUpstreams()
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
|
||||
@@ -58,12 +58,6 @@ func (uid *UID) UnmarshalText(data []byte) error {
|
||||
|
||||
// Persistent contains information about persistent clients.
|
||||
type Persistent struct {
|
||||
// UpstreamConfig is the custom upstream configuration for this client. If
|
||||
// it's nil, it has not been initialized yet. If it's non-nil and empty,
|
||||
// there are no valid upstreams. If it's non-nil and non-empty, these
|
||||
// upstream must be used.
|
||||
UpstreamConfig *proxy.CustomUpstreamConfig
|
||||
|
||||
// SafeSearch handles search engine hosts rewrites.
|
||||
SafeSearch filtering.SafeSearch
|
||||
|
||||
@@ -262,7 +256,7 @@ func ValidateClientID(id string) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// IDs returns a list of client IDs containing at least one element.
|
||||
// IDs returns a list of ClientIDs containing at least one element.
|
||||
func (c *Persistent) IDs() (ids []string) {
|
||||
ids = make([]string, 0, c.IDsLen())
|
||||
|
||||
@@ -281,7 +275,7 @@ func (c *Persistent) IDs() (ids []string) {
|
||||
return append(ids, c.ClientIDs...)
|
||||
}
|
||||
|
||||
// IDsLen returns a length of client ids.
|
||||
// IDsLen returns a length of ClientIDs.
|
||||
func (c *Persistent) IDsLen() (n int) {
|
||||
return len(c.IPs) + len(c.Subnets) + len(c.MACs) + len(c.ClientIDs)
|
||||
}
|
||||
@@ -312,14 +306,3 @@ func (c *Persistent) ShallowClone() (clone *Persistent) {
|
||||
|
||||
return clone
|
||||
}
|
||||
|
||||
// CloseUpstreams closes the client-specific upstream config of c if any.
|
||||
func (c *Persistent) CloseUpstreams() (err error) {
|
||||
if c.UpstreamConfig != nil {
|
||||
if err = c.UpstreamConfig.Close(); err != nil {
|
||||
return fmt.Errorf("closing upstreams of client %q: %w", c.Name, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -12,10 +12,13 @@ import (
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/arpdb"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/dhcpsvc"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/whois"
|
||||
"github.com/AdguardTeam/dnsproxy/proxy"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/hostsfile"
|
||||
"github.com/AdguardTeam/golibs/logutil/slogutil"
|
||||
"github.com/AdguardTeam/golibs/timeutil"
|
||||
)
|
||||
|
||||
// allowedTags is the list of available client tags.
|
||||
@@ -88,6 +91,10 @@ type StorageConfig struct {
|
||||
// not be nil.
|
||||
Logger *slog.Logger
|
||||
|
||||
// Clock is used by [upstreamManager] to retrieve the current time. It must
|
||||
// not be nil.
|
||||
Clock timeutil.Clock
|
||||
|
||||
// DHCP is used to match IPs against MACs of persistent clients and update
|
||||
// [SourceDHCP] runtime client information. It must not be nil.
|
||||
DHCP DHCP
|
||||
@@ -126,6 +133,9 @@ type Storage struct {
|
||||
// runtimeIndex contains information about runtime clients.
|
||||
runtimeIndex *runtimeIndex
|
||||
|
||||
// upstreamManager stores and updates custom client upstream configurations.
|
||||
upstreamManager *upstreamManager
|
||||
|
||||
// dhcp is used to update [SourceDHCP] runtime client information.
|
||||
dhcp DHCP
|
||||
|
||||
@@ -163,6 +173,7 @@ func NewStorage(ctx context.Context, conf *StorageConfig) (s *Storage, err error
|
||||
mu: &sync.Mutex{},
|
||||
index: newIndex(),
|
||||
runtimeIndex: newRuntimeIndex(),
|
||||
upstreamManager: newUpstreamManager(conf.Logger, conf.Clock),
|
||||
dhcp: conf.DHCP,
|
||||
etcHosts: conf.EtcHosts,
|
||||
arpDB: conf.ARPDB,
|
||||
@@ -200,7 +211,7 @@ func (s *Storage) Start(ctx context.Context) (err error) {
|
||||
func (s *Storage) Shutdown(_ context.Context) (err error) {
|
||||
close(s.done)
|
||||
|
||||
return s.closeUpstreams()
|
||||
return s.upstreamManager.close()
|
||||
}
|
||||
|
||||
// periodicARPUpdate periodically reloads runtime clients from ARP. It is
|
||||
@@ -416,6 +427,7 @@ func (s *Storage) Add(ctx context.Context, p *Persistent) (err error) {
|
||||
}
|
||||
|
||||
s.index.add(p)
|
||||
s.upstreamManager.updateCustomUpstreamConfig(p)
|
||||
|
||||
s.logger.DebugContext(
|
||||
ctx,
|
||||
@@ -441,7 +453,7 @@ func (s *Storage) FindByName(name string) (p *Persistent, ok bool) {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// Find finds persistent client by string representation of the client ID, IP
|
||||
// Find finds persistent client by string representation of the ClientID, IP
|
||||
// address, or MAC. And returns its shallow copy.
|
||||
//
|
||||
// TODO(s.chzhen): Accept ClientIDData structure instead, which will contain
|
||||
@@ -514,12 +526,13 @@ func (s *Storage) RemoveByName(ctx context.Context, name string) (ok bool) {
|
||||
return false
|
||||
}
|
||||
|
||||
if err := p.CloseUpstreams(); err != nil {
|
||||
s.logger.ErrorContext(ctx, "removing client", "name", p.Name, slogutil.KeyError, err)
|
||||
}
|
||||
|
||||
s.index.remove(p)
|
||||
|
||||
err := s.upstreamManager.remove(p.UID)
|
||||
if err != nil {
|
||||
s.logger.DebugContext(ctx, "closing client upstreams", "name", name, slogutil.KeyError, err)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -556,6 +569,8 @@ func (s *Storage) Update(ctx context.Context, name string, p *Persistent) (err e
|
||||
s.index.remove(stored)
|
||||
s.index.add(p)
|
||||
|
||||
s.upstreamManager.updateCustomUpstreamConfig(p)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -576,14 +591,6 @@ func (s *Storage) Size() (n int) {
|
||||
return s.index.size()
|
||||
}
|
||||
|
||||
// closeUpstreams closes upstream configurations of persistent clients.
|
||||
func (s *Storage) closeUpstreams() (err error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
return s.index.closeUpstreams()
|
||||
}
|
||||
|
||||
// ClientRuntime returns a copy of the saved runtime client by ip. If no such
|
||||
// client exists, returns nil.
|
||||
func (s *Storage) ClientRuntime(ip netip.Addr) (rc *Runtime) {
|
||||
@@ -626,3 +633,77 @@ func (s *Storage) RangeRuntime(f func(rc *Runtime) (cont bool)) {
|
||||
func (s *Storage) AllowedTags() (tags []string) {
|
||||
return s.allowedTags
|
||||
}
|
||||
|
||||
// CustomUpstreamConfig implements the [dnsforward.ClientsContainer] interface
|
||||
// for *Storage
|
||||
func (s *Storage) CustomUpstreamConfig(
|
||||
id string,
|
||||
addr netip.Addr,
|
||||
) (prxConf *proxy.CustomUpstreamConfig) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
c, ok := s.index.findByClientID(id)
|
||||
if !ok {
|
||||
c, ok = s.index.findByIP(addr)
|
||||
}
|
||||
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
return s.upstreamManager.customUpstreamConfig(c.UID)
|
||||
}
|
||||
|
||||
// UpdateCommonUpstreamConfig implements the [dnsforward.ClientsContainer]
|
||||
// interface for *Storage
|
||||
func (s *Storage) UpdateCommonUpstreamConfig(conf *CommonUpstreamConfig) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
s.upstreamManager.updateCommonUpstreamConfig(conf)
|
||||
}
|
||||
|
||||
// ClearUpstreamCache implements the [dnsforward.ClientsContainer] interface for
|
||||
// *Storage
|
||||
func (s *Storage) ClearUpstreamCache() {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
s.upstreamManager.clearUpstreamCache()
|
||||
}
|
||||
|
||||
// ApplyClientFiltering retrieves persistent client information using the
|
||||
// ClientID or client IP address, and applies it to the filtering settings.
|
||||
func (s *Storage) ApplyClientFiltering(id string, addr netip.Addr, setts *filtering.Settings) {
|
||||
c, ok := s.index.findByClientID(id)
|
||||
if !ok {
|
||||
c, ok = s.index.findByIP(addr)
|
||||
}
|
||||
|
||||
if !ok {
|
||||
s.logger.Debug("no client filtering settings found", "clientid", id, "addr", addr)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
s.logger.Debug("applying custom client filtering settings", "client_name", c.Name)
|
||||
|
||||
setts.ClientIP = addr
|
||||
|
||||
if c.UseOwnBlockedServices {
|
||||
setts.BlockedServices = c.BlockedServices.Clone()
|
||||
}
|
||||
|
||||
setts.ClientName = c.Name
|
||||
setts.ClientTags = slices.Clone(c.Tags)
|
||||
if !c.UseOwnSettings {
|
||||
return
|
||||
}
|
||||
|
||||
setts.FilteringEnabled = c.FilteringEnabled
|
||||
setts.SafeSearchEnabled = c.SafeSearchConf.Enabled
|
||||
setts.ClientSafeSearch = c.SafeSearch
|
||||
setts.SafeBrowsingEnabled = c.SafeBrowsingEnabled
|
||||
setts.ParentalEnabled = c.ParentalEnabled
|
||||
}
|
||||
|
||||
@@ -13,27 +13,34 @@ import (
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/client"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/dhcpd"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/dhcpsvc"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/dnsforward"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/whois"
|
||||
"github.com/AdguardTeam/golibs/hostsfile"
|
||||
"github.com/AdguardTeam/golibs/logutil/slogutil"
|
||||
"github.com/AdguardTeam/golibs/testutil"
|
||||
"github.com/AdguardTeam/golibs/testutil/faketime"
|
||||
"github.com/AdguardTeam/golibs/timeutil"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// newTestStorage is a helper function that returns initialized storage.
|
||||
func newTestStorage(tb testing.TB) (s *client.Storage) {
|
||||
func newTestStorage(tb testing.TB, clock timeutil.Clock) (s *client.Storage) {
|
||||
tb.Helper()
|
||||
|
||||
ctx := testutil.ContextWithTimeout(tb, testTimeout)
|
||||
s, err := client.NewStorage(ctx, &client.StorageConfig{
|
||||
Logger: slogutil.NewDiscardLogger(),
|
||||
Clock: clock,
|
||||
})
|
||||
require.NoError(tb, err)
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ dnsforward.ClientsContainer = (*client.Storage)(nil)
|
||||
|
||||
// testHostsContainer is a mock implementation of the [client.HostsContainer]
|
||||
// interface.
|
||||
type testHostsContainer struct {
|
||||
@@ -691,7 +698,7 @@ func TestStorage_Add(t *testing.T) {
|
||||
}
|
||||
|
||||
ctx := testutil.ContextWithTimeout(t, testTimeout)
|
||||
s := newTestStorage(t)
|
||||
s := newTestStorage(t, timeutil.SystemClock{})
|
||||
tags := s.AllowedTags()
|
||||
require.NotZero(t, len(tags))
|
||||
require.True(t, slices.IsSorted(tags))
|
||||
@@ -822,7 +829,7 @@ func TestStorage_RemoveByName(t *testing.T) {
|
||||
}
|
||||
|
||||
ctx := testutil.ContextWithTimeout(t, testTimeout)
|
||||
s := newTestStorage(t)
|
||||
s := newTestStorage(t, timeutil.SystemClock{})
|
||||
err := s.Add(ctx, existingClient)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -847,7 +854,7 @@ func TestStorage_RemoveByName(t *testing.T) {
|
||||
}
|
||||
|
||||
t.Run("duplicate_remove", func(t *testing.T) {
|
||||
s = newTestStorage(t)
|
||||
s = newTestStorage(t, timeutil.SystemClock{})
|
||||
err = s.Add(ctx, existingClient)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -1278,3 +1285,99 @@ func TestStorage_RangeByName(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestStorage_CustomUpstreamConfig(t *testing.T) {
|
||||
const (
|
||||
existingName = "existing_name"
|
||||
existingClientID = "existing_client_id"
|
||||
|
||||
nonExistingClientID = "non_existing_client_id"
|
||||
)
|
||||
|
||||
var (
|
||||
existingClientUID = client.MustNewUID()
|
||||
existingIP = netip.MustParseAddr("192.0.2.1")
|
||||
|
||||
nonExistingIP = netip.MustParseAddr("192.0.2.255")
|
||||
|
||||
testUpstreamTimeout = time.Second
|
||||
)
|
||||
|
||||
existingClient := &client.Persistent{
|
||||
Name: existingName,
|
||||
IPs: []netip.Addr{existingIP},
|
||||
ClientIDs: []string{existingClientID},
|
||||
UID: existingClientUID,
|
||||
Upstreams: []string{"192.0.2.0"},
|
||||
}
|
||||
|
||||
date := time.Now()
|
||||
clock := &faketime.Clock{
|
||||
OnNow: func() (now time.Time) {
|
||||
date = date.Add(time.Second)
|
||||
|
||||
return date
|
||||
},
|
||||
}
|
||||
|
||||
s := newTestStorage(t, clock)
|
||||
s.UpdateCommonUpstreamConfig(&client.CommonUpstreamConfig{
|
||||
UpstreamTimeout: testUpstreamTimeout,
|
||||
})
|
||||
|
||||
testutil.CleanupAndRequireSuccess(t, func() (err error) {
|
||||
return s.Shutdown(testutil.ContextWithTimeout(t, testTimeout))
|
||||
})
|
||||
|
||||
ctx := testutil.ContextWithTimeout(t, testTimeout)
|
||||
err := s.Add(ctx, existingClient)
|
||||
require.NoError(t, err)
|
||||
|
||||
testCases := []struct {
|
||||
cliAddr netip.Addr
|
||||
wantNilConf assert.ValueAssertionFunc
|
||||
name string
|
||||
cliID string
|
||||
}{{
|
||||
name: "client_id",
|
||||
cliID: existingClientID,
|
||||
cliAddr: netip.Addr{},
|
||||
wantNilConf: assert.NotNil,
|
||||
}, {
|
||||
name: "client_addr",
|
||||
cliID: "",
|
||||
cliAddr: existingIP,
|
||||
wantNilConf: assert.NotNil,
|
||||
}, {
|
||||
name: "non_existing_client_id",
|
||||
cliID: nonExistingClientID,
|
||||
cliAddr: netip.Addr{},
|
||||
wantNilConf: assert.Nil,
|
||||
}, {
|
||||
name: "non_existing_client_addr",
|
||||
cliID: "",
|
||||
cliAddr: nonExistingIP,
|
||||
wantNilConf: assert.Nil,
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
conf := s.CustomUpstreamConfig(tc.cliID, tc.cliAddr)
|
||||
tc.wantNilConf(t, conf)
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("update_common_config", func(t *testing.T) {
|
||||
conf := s.CustomUpstreamConfig(existingClientID, existingIP)
|
||||
require.NotNil(t, conf)
|
||||
|
||||
s.UpdateCommonUpstreamConfig(&client.CommonUpstreamConfig{
|
||||
UpstreamTimeout: testUpstreamTimeout * 2,
|
||||
})
|
||||
|
||||
updConf := s.CustomUpstreamConfig(existingClientID, existingIP)
|
||||
require.NotNil(t, updConf)
|
||||
|
||||
assert.NotEqual(t, conf, updConf)
|
||||
})
|
||||
}
|
||||
|
||||
226
internal/client/upstreammanager.go
Normal file
226
internal/client/upstreammanager.go
Normal file
@@ -0,0 +1,226 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
||||
"github.com/AdguardTeam/dnsproxy/proxy"
|
||||
"github.com/AdguardTeam/dnsproxy/upstream"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/logutil/slogutil"
|
||||
"github.com/AdguardTeam/golibs/stringutil"
|
||||
"github.com/AdguardTeam/golibs/timeutil"
|
||||
)
|
||||
|
||||
// CommonUpstreamConfig contains common settings for custom client upstream
|
||||
// configurations.
|
||||
type CommonUpstreamConfig struct {
|
||||
Bootstrap upstream.Resolver
|
||||
UpstreamTimeout time.Duration
|
||||
BootstrapPreferIPv6 bool
|
||||
EDNSClientSubnetEnabled bool
|
||||
UseHTTP3Upstreams bool
|
||||
}
|
||||
|
||||
// customUpstreamConfig contains custom client upstream configuration and the
|
||||
// timestamp of the latest configuration update.
|
||||
type customUpstreamConfig struct {
|
||||
// proxyConf is the constructed upstream configuration for the [proxy],
|
||||
// derived from the fields below. It is initialized on demand with
|
||||
// [newCustomUpstreamConfig].
|
||||
proxyConf *proxy.CustomUpstreamConfig
|
||||
|
||||
// commonConfUpdate is the timestamp of the latest configuration update,
|
||||
// used to check against [upstreamManager.confUpdate] to determine if the
|
||||
// configuration is up to date.
|
||||
commonConfUpdate time.Time
|
||||
|
||||
// upstreams is the cached list of custom upstream DNS servers used for the
|
||||
// configuration of proxyConf.
|
||||
upstreams []string
|
||||
|
||||
// upstreamsCacheSize is the cached value of the cache size of the
|
||||
// upstreams, used for the configuration of proxyConf.
|
||||
upstreamsCacheSize uint32
|
||||
|
||||
// upstreamsCacheEnabled is the cached value indicating whether the cache of
|
||||
// the upstreams is enabled for the configuration of proxyConf.
|
||||
upstreamsCacheEnabled bool
|
||||
|
||||
// isChanged indicates whether the proxyConf needs to be updated.
|
||||
isChanged bool
|
||||
}
|
||||
|
||||
// upstreamManager stores and updates custom client upstream configurations.
|
||||
type upstreamManager struct {
|
||||
// logger is used for logging the operation of the upstream manager. It
|
||||
// must not be nil.
|
||||
//
|
||||
// TODO(s.chzhen): Consider using a logger with its own prefix.
|
||||
logger *slog.Logger
|
||||
|
||||
// uidToCustomConf maps persistent client UID to the custom client upstream
|
||||
// configuration. Stored UIDs must be in sync with the [index.uidToClient].
|
||||
uidToCustomConf map[UID]*customUpstreamConfig
|
||||
|
||||
// commonConf is the common upstream configuration.
|
||||
commonConf *CommonUpstreamConfig
|
||||
|
||||
// clock is used to get the current time. It must not be nil.
|
||||
clock timeutil.Clock
|
||||
|
||||
// confUpdate is the timestamp of the latest common upstream configuration
|
||||
// update.
|
||||
confUpdate time.Time
|
||||
}
|
||||
|
||||
// newUpstreamManager returns the new properly initialized upstream manager.
|
||||
func newUpstreamManager(logger *slog.Logger, clock timeutil.Clock) (m *upstreamManager) {
|
||||
return &upstreamManager{
|
||||
logger: logger,
|
||||
uidToCustomConf: make(map[UID]*customUpstreamConfig),
|
||||
clock: clock,
|
||||
}
|
||||
}
|
||||
|
||||
// updateCommonUpstreamConfig updates the common upstream configuration and the
|
||||
// timestamp of the latest configuration update.
|
||||
func (m *upstreamManager) updateCommonUpstreamConfig(conf *CommonUpstreamConfig) {
|
||||
m.commonConf = conf
|
||||
m.confUpdate = m.clock.Now()
|
||||
}
|
||||
|
||||
// updateCustomUpstreamConfig updates the stored custom client upstream
|
||||
// configuration associated with the persistent client. It also sets
|
||||
// [customUpstreamConfig.isChanged] to true so [customUpstreamConfig.proxyConf]
|
||||
// can be updated later in [upstreamManager.customUpstreamConfig].
|
||||
func (m *upstreamManager) updateCustomUpstreamConfig(c *Persistent) {
|
||||
cliConf, ok := m.uidToCustomConf[c.UID]
|
||||
if !ok {
|
||||
cliConf = &customUpstreamConfig{
|
||||
commonConfUpdate: m.confUpdate,
|
||||
}
|
||||
|
||||
m.uidToCustomConf[c.UID] = cliConf
|
||||
}
|
||||
|
||||
// TODO(s.chzhen): Compare before cloning.
|
||||
cliConf.upstreams = slices.Clone(c.Upstreams)
|
||||
cliConf.upstreamsCacheSize = c.UpstreamsCacheSize
|
||||
cliConf.upstreamsCacheEnabled = c.UpstreamsCacheEnabled
|
||||
cliConf.isChanged = true
|
||||
}
|
||||
|
||||
// customUpstreamConfig returns the custom client upstream configuration.
|
||||
func (m *upstreamManager) customUpstreamConfig(uid UID) (proxyConf *proxy.CustomUpstreamConfig) {
|
||||
cliConf, ok := m.uidToCustomConf[uid]
|
||||
if !ok {
|
||||
// TODO(s.chzhen): Consider panic.
|
||||
m.logger.Error("no associated custom client upstream config")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
if !m.isConfigChanged(cliConf) {
|
||||
return cliConf.proxyConf
|
||||
}
|
||||
|
||||
if cliConf.proxyConf != nil {
|
||||
err := cliConf.proxyConf.Close()
|
||||
if err != nil {
|
||||
// TODO(s.chzhen): Pass context.
|
||||
m.logger.Debug("closing custom upstream config", slogutil.KeyError, err)
|
||||
}
|
||||
}
|
||||
|
||||
proxyConf = newCustomUpstreamConfig(cliConf, m.commonConf)
|
||||
cliConf.proxyConf = proxyConf
|
||||
cliConf.isChanged = false
|
||||
|
||||
return proxyConf
|
||||
}
|
||||
|
||||
// isConfigChanged returns true if the update is necessary for the custom client
|
||||
// upstream configuration.
|
||||
func (m *upstreamManager) isConfigChanged(cliConf *customUpstreamConfig) (ok bool) {
|
||||
return !m.confUpdate.Equal(cliConf.commonConfUpdate) || cliConf.isChanged
|
||||
}
|
||||
|
||||
// clearUpstreamCache clears the upstream cache for each stored custom client
|
||||
// upstream configuration.
|
||||
func (m *upstreamManager) clearUpstreamCache() {
|
||||
for _, c := range m.uidToCustomConf {
|
||||
if c.proxyConf != nil {
|
||||
c.proxyConf.ClearCache()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// remove deletes the custom client upstream configuration and closes
|
||||
// [customUpstreamConfig.proxyConf] if necessary.
|
||||
func (m *upstreamManager) remove(uid UID) (err error) {
|
||||
cliConf, ok := m.uidToCustomConf[uid]
|
||||
if !ok {
|
||||
// TODO(s.chzhen): Consider panic.
|
||||
return errors.Error("no associated custom client upstream config")
|
||||
}
|
||||
|
||||
delete(m.uidToCustomConf, uid)
|
||||
|
||||
if cliConf.proxyConf != nil {
|
||||
return cliConf.proxyConf.Close()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// close shuts down each stored custom client upstream configuration.
|
||||
func (m *upstreamManager) close() (err error) {
|
||||
var errs []error
|
||||
for _, c := range m.uidToCustomConf {
|
||||
if c.proxyConf == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
errs = append(errs, c.proxyConf.Close())
|
||||
}
|
||||
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
|
||||
// newCustomUpstreamConfig returns the new properly initialized custom proxy
|
||||
// upstream configuration for the client.
|
||||
func newCustomUpstreamConfig(
|
||||
cliConf *customUpstreamConfig,
|
||||
conf *CommonUpstreamConfig,
|
||||
) (proxyConf *proxy.CustomUpstreamConfig) {
|
||||
upstreams := stringutil.FilterOut(cliConf.upstreams, aghnet.IsCommentOrEmpty)
|
||||
if len(upstreams) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
upsConf, err := proxy.ParseUpstreamsConfig(
|
||||
upstreams,
|
||||
&upstream.Options{
|
||||
Bootstrap: conf.Bootstrap,
|
||||
Timeout: time.Duration(conf.UpstreamTimeout),
|
||||
HTTPVersions: aghnet.UpstreamHTTPVersions(conf.UseHTTP3Upstreams),
|
||||
PreferIPv6: conf.BootstrapPreferIPv6,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
// Should not happen because upstreams are already validated. See
|
||||
// [Persistent.validate].
|
||||
panic(fmt.Errorf("creating custom upstream config: %w", err))
|
||||
}
|
||||
|
||||
return proxy.NewCustomUpstreamConfig(
|
||||
upsConf,
|
||||
cliConf.upstreamsCacheEnabled,
|
||||
int(cliConf.upstreamsCacheSize),
|
||||
conf.EDNSClientSubnetEnabled,
|
||||
)
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"net"
|
||||
"slices"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
@@ -14,18 +15,48 @@ import (
|
||||
"golang.org/x/net/ipv6"
|
||||
)
|
||||
|
||||
// raCtx is a context for the Router Advertisement logic.
|
||||
type raCtx struct {
|
||||
raAllowSLAAC bool // send RA packets without MO flags
|
||||
raSLAACOnly bool // send RA packets with MO flags
|
||||
ipAddr net.IP // source IP address (link-local-unicast)
|
||||
dnsIPAddr net.IP // IP address for DNS Server option
|
||||
prefixIPAddr net.IP // IP address for Prefix option
|
||||
ifaceName string
|
||||
iface *net.Interface
|
||||
packetSendPeriod time.Duration // how often RA packets are sent
|
||||
// raAllowSLAAC is used to determine if the ICMP Router Advertisement
|
||||
// messages should be sent.
|
||||
//
|
||||
// If both raAllowSLAAC and raSLAACOnly are false, the Router Advertisement
|
||||
// messages aren't sent.
|
||||
raAllowSLAAC bool
|
||||
|
||||
conn *icmp.PacketConn // ICMPv6 socket
|
||||
stop atomic.Value // stop the packet sending loop
|
||||
// raSLAACOnly is used to determine if the ICMP Router Advertisement
|
||||
// messages should set M and O flags, see RFC 4861, section 4.2.
|
||||
//
|
||||
// If both raAllowSLAAC and raSLAACOnly are false, the Router Advertisement
|
||||
// messages aren't sent.
|
||||
raSLAACOnly bool
|
||||
|
||||
// ipAddr is an IP address used within the Source Link-Layer Address option.
|
||||
// See RFC 4861, section 4.6.1.
|
||||
ipAddr net.IP
|
||||
|
||||
// dnsIPAddr is an IP address used within the DNS Server option.
|
||||
dnsIPAddr net.IP
|
||||
|
||||
// prefixIPAddr is an IP address used within the Prefix Information option.
|
||||
// See RFC 4861, section 4.6.2.
|
||||
prefixIPAddr net.IP
|
||||
|
||||
// ifaceName is the name of the interface used as a scope of the IP
|
||||
// addresses.
|
||||
ifaceName string
|
||||
|
||||
// iface is the network interface used to send the ICMPv6 packets.
|
||||
iface *net.Interface
|
||||
|
||||
// packetSendPeriod is the interval between sending the ICMPv6 packets.
|
||||
packetSendPeriod time.Duration
|
||||
|
||||
// conn is the ICMPv6 socket.
|
||||
conn *icmp.PacketConn
|
||||
|
||||
// stop is used to stop the packet sending loop.
|
||||
stop atomic.Value
|
||||
}
|
||||
|
||||
type icmpv6RA struct {
|
||||
@@ -38,10 +69,11 @@ type icmpv6RA struct {
|
||||
mtu uint32
|
||||
}
|
||||
|
||||
// hwAddrToLinkLayerAddr converts a hardware address into a form required by
|
||||
// RFC4861. That is, a byte slice of length divisible by 8.
|
||||
// hwAddrToLinkLayerAddr clones the hardware address and returns it as a byte
|
||||
// slice suitable for the Source Link-Layer Address option in the ICMPv6
|
||||
// Router Advertisement packet.
|
||||
//
|
||||
// See https://tools.ietf.org/html/rfc4861#section-4.6.1.
|
||||
// TODO(e.burkov): Check if it's safe to use the original slice.
|
||||
func hwAddrToLinkLayerAddr(hwa net.HardwareAddr) (lla []byte, err error) {
|
||||
err = netutil.ValidateMAC(hwa)
|
||||
if err != nil {
|
||||
@@ -50,19 +82,7 @@ func hwAddrToLinkLayerAddr(hwa net.HardwareAddr) (lla []byte, err error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(hwa) == 6 || len(hwa) == 8 {
|
||||
lla = make([]byte, 8)
|
||||
copy(lla, hwa)
|
||||
|
||||
return lla, nil
|
||||
}
|
||||
|
||||
// Assume that netutil.ValidateMAC prevents lengths other than 20 by
|
||||
// now.
|
||||
lla = make([]byte, 24)
|
||||
copy(lla, hwa)
|
||||
|
||||
return lla, nil
|
||||
return slices.Clone(hwa), nil
|
||||
}
|
||||
|
||||
// Create an ICMPv6.RouterAdvertisement packet with all necessary options.
|
||||
@@ -103,15 +123,24 @@ func hwAddrToLinkLayerAddr(hwa net.HardwareAddr) (lla []byte, err error) {
|
||||
//
|
||||
// TODO(a.garipov): Replace with an existing implementation from a dependency.
|
||||
func createICMPv6RAPacket(params icmpv6RA) (data []byte, err error) {
|
||||
var lla []byte
|
||||
lla, err = hwAddrToLinkLayerAddr(params.sourceLinkLayerAddress)
|
||||
lla, err := hwAddrToLinkLayerAddr(params.sourceLinkLayerAddress)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("converting source link layer address: %w", err)
|
||||
return nil, fmt.Errorf("converting source link-layer address: %w", err)
|
||||
}
|
||||
|
||||
// Calculate length of the source link-layer address option. As per RFC
|
||||
// 4861, section 4.6.1, the length should be in units of 8 octets, including
|
||||
// the type and length fields.
|
||||
//
|
||||
// See https://datatracker.ietf.org/doc/html/rfc4861#section-4.6.1.
|
||||
srcLLAOptLen := len(lla) + 2
|
||||
// Make sure the value is rounded up to the nearest multiple of 8.
|
||||
srcLLAOptLenValue := (srcLLAOptLen + 7) / 8
|
||||
srcLLAPadLen := srcLLAOptLenValue*8 - srcLLAOptLen
|
||||
|
||||
// TODO(a.garipov): Don't use a magic constant here. Refactor the code
|
||||
// and make all constants named instead of all those comments..
|
||||
data = make([]byte, 82+len(lla))
|
||||
// and make all constants named instead of all those comments.
|
||||
data = make([]byte, 80+srcLLAOptLen+srcLLAPadLen)
|
||||
i := 0
|
||||
|
||||
// ICMPv6:
|
||||
@@ -175,12 +204,11 @@ func createICMPv6RAPacket(params icmpv6RA) (data []byte, err error) {
|
||||
|
||||
// Option=Source link-layer address:
|
||||
|
||||
data[i] = 1 // Type
|
||||
data[i+1] = 1 // Length
|
||||
data[i] = 1 // Type
|
||||
data[i+1] = byte(srcLLAOptLenValue) // Length
|
||||
i += 2
|
||||
|
||||
copy(data[i:], lla) // Link-Layer Address[8/24]
|
||||
i += len(lla)
|
||||
i += len(lla) + srcLLAPadLen
|
||||
|
||||
// Option=Recursive DNS Server:
|
||||
|
||||
|
||||
63
internal/dhcpd/routeradv_internal_test.go
Normal file
63
internal/dhcpd/routeradv_internal_test.go
Normal file
@@ -0,0 +1,63 @@
|
||||
package dhcpd
|
||||
|
||||
import (
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
"github.com/google/gopacket/layers"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestCreateICMPv6RAPacket(t *testing.T) {
|
||||
raConf := icmpv6RA{
|
||||
managedAddressConfiguration: false,
|
||||
otherConfiguration: true,
|
||||
mtu: 1500,
|
||||
prefix: net.ParseIP("1234::"),
|
||||
prefixLen: 64,
|
||||
recursiveDNSServer: net.ParseIP("fe80::800:27ff:fe00:0"),
|
||||
sourceLinkLayerAddress: []byte{0x0A, 0x00, 0x27, 0x00, 0x00, 0x00},
|
||||
}
|
||||
|
||||
pkt, err := createICMPv6RAPacket(raConf)
|
||||
require.NoError(t, err)
|
||||
|
||||
icmpPkt := &layers.ICMPv6{}
|
||||
err = icmpPkt.DecodeFromBytes(pkt, gopacket.NilDecodeFeedback)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, layers.LayerTypeICMPv6RouterAdvertisement, icmpPkt.NextLayerType())
|
||||
raPkt := &layers.ICMPv6RouterAdvertisement{}
|
||||
err = raPkt.DecodeFromBytes(icmpPkt.LayerPayload(), gopacket.NilDecodeFeedback)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, raConf.managedAddressConfiguration, raPkt.ManagedAddressConfig())
|
||||
assert.Equal(t, raConf.otherConfiguration, raPkt.OtherConfig())
|
||||
|
||||
wantOpts := layers.ICMPv6Options{{
|
||||
Type: layers.ICMPv6OptPrefixInfo,
|
||||
Data: []uint8{
|
||||
0x40, 0xC0, 0x00, 0x00, 0x0E, 0x10, 0x00, 0x00,
|
||||
0x0E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x12, 0x34,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
},
|
||||
}, {
|
||||
Type: layers.ICMPv6OptMTU,
|
||||
Data: []uint8{0x00, 0x00, 0x00, 0x00, 0x05, 0xDC},
|
||||
}, {
|
||||
Type: layers.ICMPv6OptSourceAddress,
|
||||
Data: []uint8{0x0A, 0x00, 0x27, 0x00, 0x00, 0x0},
|
||||
}, {
|
||||
// Package layers declares no constant for Recursive DNS Server option.
|
||||
Type: layers.ICMPv6Opt(25),
|
||||
Data: []uint8{
|
||||
0x00, 0x00, 0x00, 0x00, 0x0E, 0x10, 0xFE, 0x80,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00,
|
||||
0x27, 0xFF, 0xFE, 0x00, 0x00, 0x00,
|
||||
},
|
||||
}}
|
||||
assert.Equal(t, wantOpts, raPkt.Options)
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
package dhcpd
|
||||
|
||||
import (
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCreateICMPv6RAPacket(t *testing.T) {
|
||||
wantData := []byte{
|
||||
0x86, 0x00, 0x00, 0x00, 0x40, 0x40, 0x07, 0x08,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x03, 0x04, 0x40, 0xc0, 0x00, 0x00, 0x0e, 0x10,
|
||||
0x00, 0x00, 0x0e, 0x10, 0x00, 0x00, 0x00, 0x00,
|
||||
0x12, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x05, 0xdc,
|
||||
0x01, 0x01, 0x0a, 0x00, 0x27, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x19, 0x03, 0x00, 0x00, 0x00, 0x00,
|
||||
0x0e, 0x10, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x08, 0x00, 0x27, 0xff, 0xfe, 0x00,
|
||||
0x00, 0x00,
|
||||
}
|
||||
|
||||
gotData, err := createICMPv6RAPacket(icmpv6RA{
|
||||
managedAddressConfiguration: false,
|
||||
otherConfiguration: true,
|
||||
mtu: 1500,
|
||||
prefix: net.ParseIP("1234::"),
|
||||
prefixLen: 64,
|
||||
recursiveDNSServer: net.ParseIP("fe80::800:27ff:fe00:0"),
|
||||
sourceLinkLayerAddress: []byte{0x0a, 0x00, 0x27, 0x00, 0x00, 0x00},
|
||||
})
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, wantData, gotData)
|
||||
}
|
||||
@@ -239,7 +239,7 @@ func (s *Server) handleAccessSet(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
err = validateAccessSet(list)
|
||||
if err != nil {
|
||||
aghhttp.Error(r, w, http.StatusBadRequest, err.Error())
|
||||
aghhttp.Error(r, w, http.StatusBadRequest, "%s", err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
var _ proxy.BeforeRequestHandler = (*Server)(nil)
|
||||
|
||||
// HandleBefore is the handler that is called before any other processing,
|
||||
// including logs. It performs access checks and puts the client ID, if there
|
||||
// including logs. It performs access checks and puts the ClientID, if there
|
||||
// is one, into the server's cache.
|
||||
//
|
||||
// TODO(d.kolyshev): Extract to separate package.
|
||||
|
||||
@@ -266,6 +266,7 @@ func TestServer_HandleBefore_udp(t *testing.T) {
|
||||
UpstreamDNS: []string{localUpsAddr},
|
||||
UpstreamMode: UpstreamModeLoadBalance,
|
||||
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
|
||||
ClientsContainer: EmptyClientsContainer{},
|
||||
},
|
||||
ServePlainDNS: true,
|
||||
})
|
||||
|
||||
@@ -62,7 +62,7 @@ func clientIDFromClientServerName(
|
||||
return strings.ToLower(clientID), nil
|
||||
}
|
||||
|
||||
// clientIDFromDNSContextHTTPS extracts the client's ID from the path of the
|
||||
// clientIDFromDNSContextHTTPS extracts the ClientID from the path of the
|
||||
// client's DNS-over-HTTPS request.
|
||||
func clientIDFromDNSContextHTTPS(pctx *proxy.DNSContext) (clientID string, err error) {
|
||||
r := pctx.HTTPRequest
|
||||
|
||||
46
internal/dnsforward/clientscontainer.go
Normal file
46
internal/dnsforward/clientscontainer.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package dnsforward
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/client"
|
||||
"github.com/AdguardTeam/dnsproxy/proxy"
|
||||
)
|
||||
|
||||
// ClientsContainer provides information about preconfigured DNS clients.
|
||||
type ClientsContainer interface {
|
||||
// CustomUpstreamConfig returns the custom client upstream configuration, if
|
||||
// any. It prioritizes ClientID over client IP address to identify the
|
||||
// client.
|
||||
CustomUpstreamConfig(clientID string, cliAddr netip.Addr) (conf *proxy.CustomUpstreamConfig)
|
||||
|
||||
// UpdateCommonUpstreamConfig updates the common upstream configuration.
|
||||
UpdateCommonUpstreamConfig(conf *client.CommonUpstreamConfig)
|
||||
|
||||
// ClearUpstreamCache clears the upstream cache for each stored custom
|
||||
// client upstream configuration.
|
||||
ClearUpstreamCache()
|
||||
}
|
||||
|
||||
// EmptyClientsContainer is an [ClientsContainer] implementation that does nothing.
|
||||
type EmptyClientsContainer struct{}
|
||||
|
||||
// type check
|
||||
var _ ClientsContainer = EmptyClientsContainer{}
|
||||
|
||||
// CustomUpstreamConfig implements the [ClientsContainer] interface for
|
||||
// EmptyClientsContainer.
|
||||
func (EmptyClientsContainer) CustomUpstreamConfig(
|
||||
clientID string,
|
||||
cliAddr netip.Addr,
|
||||
) (conf *proxy.CustomUpstreamConfig) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateCommonUpstreamConfig implements the [ClientsContainer] interface for
|
||||
// EmptyClientsContainer.
|
||||
func (EmptyClientsContainer) UpdateCommonUpstreamConfig(conf *client.CommonUpstreamConfig) {}
|
||||
|
||||
// ClearUpstreamCache implements the [ClientsContainer] interface for
|
||||
// EmptyClientsContainer.
|
||||
func (EmptyClientsContainer) ClearUpstreamCache() {}
|
||||
@@ -16,7 +16,6 @@ import (
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghtls"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/client"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
||||
"github.com/AdguardTeam/dnsproxy/proxy"
|
||||
"github.com/AdguardTeam/dnsproxy/upstream"
|
||||
"github.com/AdguardTeam/golibs/container"
|
||||
@@ -29,27 +28,11 @@ import (
|
||||
"github.com/ameshkov/dnscrypt/v2"
|
||||
)
|
||||
|
||||
// ClientsContainer provides information about preconfigured DNS clients.
|
||||
type ClientsContainer interface {
|
||||
// UpstreamConfigByID returns the custom upstream configuration for the
|
||||
// client having id, using boot to initialize the one if necessary. It
|
||||
// returns nil if there is no custom upstream configuration for the client.
|
||||
// The id is expected to be either a string representation of an IP address
|
||||
// or the ClientID.
|
||||
UpstreamConfigByID(
|
||||
id string,
|
||||
boot upstream.Resolver,
|
||||
) (conf *proxy.CustomUpstreamConfig, err error)
|
||||
}
|
||||
|
||||
// Config represents the DNS filtering configuration of AdGuard Home. The zero
|
||||
// Config is empty and ready for use.
|
||||
type Config struct {
|
||||
// Callbacks for other modules
|
||||
|
||||
// FilterHandler is an optional additional filtering callback.
|
||||
FilterHandler func(cliAddr netip.Addr, clientID string, settings *filtering.Settings) `yaml:"-"`
|
||||
|
||||
// ClientsContainer stores the information about special handling of some
|
||||
// DNS clients.
|
||||
ClientsContainer ClientsContainer `yaml:"-"`
|
||||
@@ -467,7 +450,7 @@ func (s *Server) prepareIpsetListSettings() (ipsets []string, err error) {
|
||||
}
|
||||
|
||||
ipsets = stringutil.SplitTrimmed(string(data), "\n")
|
||||
ipsets = slices.DeleteFunc(ipsets, IsCommentOrEmpty)
|
||||
ipsets = slices.DeleteFunc(ipsets, aghnet.IsCommentOrEmpty)
|
||||
|
||||
log.Debug("dns: using %d ipset rules from file %q", len(ipsets), fn)
|
||||
|
||||
@@ -478,7 +461,7 @@ func (s *Server) prepareIpsetListSettings() (ipsets []string, err error) {
|
||||
// the configuration itself.
|
||||
func (conf *ServerConfig) loadUpstreams() (upstreams []string, err error) {
|
||||
if conf.UpstreamDNSFileName == "" {
|
||||
return stringutil.FilterOut(conf.UpstreamDNS, IsCommentOrEmpty), nil
|
||||
return stringutil.FilterOut(conf.UpstreamDNS, aghnet.IsCommentOrEmpty), nil
|
||||
}
|
||||
|
||||
var data []byte
|
||||
@@ -491,7 +474,7 @@ func (conf *ServerConfig) loadUpstreams() (upstreams []string, err error) {
|
||||
|
||||
log.Debug("dnsforward: got %d upstreams in %q", len(upstreams), conf.UpstreamDNSFileName)
|
||||
|
||||
return stringutil.FilterOut(upstreams, IsCommentOrEmpty), nil
|
||||
return stringutil.FilterOut(upstreams, aghnet.IsCommentOrEmpty), nil
|
||||
}
|
||||
|
||||
// collectListenAddr adds addrPort to addrs. It also adds its port to
|
||||
|
||||
@@ -299,6 +299,7 @@ func TestServer_HandleDNSRequest_dns64(t *testing.T) {
|
||||
Config: Config{
|
||||
UpstreamMode: UpstreamModeLoadBalance,
|
||||
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
|
||||
ClientsContainer: EmptyClientsContainer{},
|
||||
UpstreamDNS: []string{upsAddr},
|
||||
},
|
||||
UsePrivateRDNS: true,
|
||||
@@ -337,6 +338,7 @@ func TestServer_dns64WithDisabledRDNS(t *testing.T) {
|
||||
Config: Config{
|
||||
UpstreamMode: UpstreamModeLoadBalance,
|
||||
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
|
||||
ClientsContainer: EmptyClientsContainer{},
|
||||
UpstreamDNS: []string{upsAddr},
|
||||
},
|
||||
UsePrivateRDNS: false,
|
||||
@@ -540,7 +540,7 @@ func (s *Server) prepareUpstreamSettings(boot upstream.Resolver) (err error) {
|
||||
uc, err := newUpstreamConfig(upstreams, defaultDNS, &upstream.Options{
|
||||
Bootstrap: boot,
|
||||
Timeout: s.conf.UpstreamTimeout,
|
||||
HTTPVersions: UpstreamHTTPVersions(s.conf.UseHTTP3Upstreams),
|
||||
HTTPVersions: aghnet.UpstreamHTTPVersions(s.conf.UseHTTP3Upstreams),
|
||||
PreferIPv6: s.conf.BootstrapPreferIPv6,
|
||||
// Use a customized set of RootCAs, because Go's default mechanism of
|
||||
// loading TLS roots does not always work properly on some routers so we're
|
||||
@@ -557,6 +557,13 @@ func (s *Server) prepareUpstreamSettings(boot upstream.Resolver) (err error) {
|
||||
}
|
||||
|
||||
s.conf.UpstreamConfig = uc
|
||||
s.conf.ClientsContainer.UpdateCommonUpstreamConfig(&client.CommonUpstreamConfig{
|
||||
Bootstrap: boot,
|
||||
UpstreamTimeout: s.conf.UpstreamTimeout,
|
||||
BootstrapPreferIPv6: s.conf.BootstrapPreferIPv6,
|
||||
EDNSClientSubnetEnabled: s.conf.EDNSClientSubnet.Enabled,
|
||||
UseHTTP3Upstreams: s.conf.UseHTTP3Upstreams,
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -630,7 +637,7 @@ func (s *Server) prepareInternalDNS() (err error) {
|
||||
|
||||
bootOpts := &upstream.Options{
|
||||
Timeout: DefaultTimeout,
|
||||
HTTPVersions: UpstreamHTTPVersions(s.conf.UseHTTP3Upstreams),
|
||||
HTTPVersions: aghnet.UpstreamHTTPVersions(s.conf.UseHTTP3Upstreams),
|
||||
}
|
||||
|
||||
s.bootstrap, s.bootResolvers, err = newBootstrap(s.conf.BootstrapDNS, s.etcHosts, bootOpts)
|
||||
@@ -661,7 +668,7 @@ func (s *Server) prepareInternalDNS() (err error) {
|
||||
// setupFallbackDNS initializes the fallback DNS servers.
|
||||
func (s *Server) setupFallbackDNS() (uc *proxy.UpstreamConfig, err error) {
|
||||
fallbacks := s.conf.FallbackDNS
|
||||
fallbacks = stringutil.FilterOut(fallbacks, IsCommentOrEmpty)
|
||||
fallbacks = stringutil.FilterOut(fallbacks, aghnet.IsCommentOrEmpty)
|
||||
if len(fallbacks) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -23,9 +23,11 @@ import (
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghtest"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/client"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/filtering/hashprefix"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/filtering/safesearch"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/schedule"
|
||||
"github.com/AdguardTeam/dnsproxy/proxy"
|
||||
"github.com/AdguardTeam/dnsproxy/upstream"
|
||||
"github.com/AdguardTeam/golibs/logutil/slogutil"
|
||||
@@ -61,6 +63,42 @@ const (
|
||||
// TODO(a.garipov): Use more.
|
||||
var testClientAddrPort = netip.MustParseAddrPort("1.2.3.4:12345")
|
||||
|
||||
// type check
|
||||
var _ ClientsContainer = (*clientsContainer)(nil)
|
||||
|
||||
// clientsContainer is a mock [ClientsContainer] implementation for tests.
|
||||
type clientsContainer struct {
|
||||
OnCustomUpstreamConfig func(
|
||||
clientID string,
|
||||
cliAddr netip.Addr,
|
||||
) (conf *proxy.CustomUpstreamConfig)
|
||||
|
||||
OnUpdateCommonUpstreamConfig func(conf *client.CommonUpstreamConfig)
|
||||
|
||||
OnClearUpstreamCache func()
|
||||
}
|
||||
|
||||
// CustomUpstreamConfig implements the [ClientsContainer] interface for
|
||||
// *clientsContainer.
|
||||
func (c *clientsContainer) CustomUpstreamConfig(
|
||||
clientID string,
|
||||
cliAddr netip.Addr,
|
||||
) (conf *proxy.CustomUpstreamConfig) {
|
||||
return c.OnCustomUpstreamConfig(clientID, cliAddr)
|
||||
}
|
||||
|
||||
// UpdateCommonUpstreamConfig implements the [ClientsContainer] interface for
|
||||
// *clientsContainer.
|
||||
func (c *clientsContainer) UpdateCommonUpstreamConfig(conf *client.CommonUpstreamConfig) {
|
||||
c.OnUpdateCommonUpstreamConfig(conf)
|
||||
}
|
||||
|
||||
// ClearUpstreamCache implements the [ClientsContainer] interface for
|
||||
// *clientsContainer.
|
||||
func (c *clientsContainer) ClearUpstreamCache() {
|
||||
c.OnClearUpstreamCache()
|
||||
}
|
||||
|
||||
func startDeferStop(t *testing.T, s *Server) {
|
||||
t.Helper()
|
||||
|
||||
@@ -69,6 +107,21 @@ func startDeferStop(t *testing.T, s *Server) {
|
||||
testutil.CleanupAndRequireSuccess(t, s.Stop)
|
||||
}
|
||||
|
||||
// applyEmptyClientFiltering is a helper function for tests with
|
||||
// [filtering.Config] that does nothing.
|
||||
func applyEmptyClientFiltering(_ string, _ netip.Addr, _ *filtering.Settings) {}
|
||||
|
||||
// emptyFilteringBlockedServices is a helper function that returns an empty
|
||||
// filtering blocked services for tests.
|
||||
func emptyFilteringBlockedServices() (bsvc *filtering.BlockedServices) {
|
||||
return &filtering.BlockedServices{
|
||||
Schedule: schedule.EmptyWeekly(),
|
||||
}
|
||||
}
|
||||
|
||||
// createTestServer is a helper function that returns a properly initialized
|
||||
// *Server for use in tests, given the provided parameters. It also populates
|
||||
// the filtering configuration with default parameters.
|
||||
func createTestServer(
|
||||
t *testing.T,
|
||||
filterConf *filtering.Config,
|
||||
@@ -86,6 +139,12 @@ func createTestServer(
|
||||
Data: []byte(rules),
|
||||
}}
|
||||
|
||||
filterConf.BlockedServices = cmp.Or(filterConf.BlockedServices, emptyFilteringBlockedServices())
|
||||
|
||||
if filterConf.ApplyClientFiltering == nil {
|
||||
filterConf.ApplyClientFiltering = applyEmptyClientFiltering
|
||||
}
|
||||
|
||||
f, err := filtering.New(filterConf, filters)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -168,6 +227,7 @@ func createTestTLS(t *testing.T, tlsConf TLSConfig) (s *Server, certPem []byte)
|
||||
Config: Config{
|
||||
UpstreamMode: UpstreamModeLoadBalance,
|
||||
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
|
||||
ClientsContainer: EmptyClientsContainer{},
|
||||
},
|
||||
ServePlainDNS: true,
|
||||
})
|
||||
@@ -297,6 +357,7 @@ func TestServer(t *testing.T) {
|
||||
Config: Config{
|
||||
UpstreamMode: UpstreamModeLoadBalance,
|
||||
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
|
||||
ClientsContainer: EmptyClientsContainer{},
|
||||
},
|
||||
ServePlainDNS: true,
|
||||
})
|
||||
@@ -337,6 +398,7 @@ func TestServer_timeout(t *testing.T) {
|
||||
Config: Config{
|
||||
UpstreamMode: UpstreamModeLoadBalance,
|
||||
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
|
||||
ClientsContainer: EmptyClientsContainer{},
|
||||
},
|
||||
ServePlainDNS: true,
|
||||
}
|
||||
@@ -364,6 +426,7 @@ func TestServer_timeout(t *testing.T) {
|
||||
s.conf.Config.EDNSClientSubnet = &EDNSClientSubnet{
|
||||
Enabled: false,
|
||||
}
|
||||
s.conf.Config.ClientsContainer = EmptyClientsContainer{}
|
||||
err = s.Prepare(&s.conf)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -380,6 +443,7 @@ func TestServer_Prepare_fallbacks(t *testing.T) {
|
||||
},
|
||||
UpstreamMode: UpstreamModeLoadBalance,
|
||||
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
|
||||
ClientsContainer: EmptyClientsContainer{},
|
||||
},
|
||||
ServePlainDNS: true,
|
||||
}
|
||||
@@ -405,6 +469,7 @@ func TestServerWithProtectionDisabled(t *testing.T) {
|
||||
Config: Config{
|
||||
UpstreamMode: UpstreamModeLoadBalance,
|
||||
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
|
||||
ClientsContainer: EmptyClientsContainer{},
|
||||
},
|
||||
ServePlainDNS: true,
|
||||
})
|
||||
@@ -536,6 +601,7 @@ func TestSafeSearch(t *testing.T) {
|
||||
EDNSClientSubnet: &EDNSClientSubnet{
|
||||
Enabled: false,
|
||||
},
|
||||
ClientsContainer: EmptyClientsContainer{},
|
||||
},
|
||||
ServePlainDNS: true,
|
||||
}
|
||||
@@ -629,6 +695,7 @@ func TestInvalidRequest(t *testing.T) {
|
||||
EDNSClientSubnet: &EDNSClientSubnet{
|
||||
Enabled: false,
|
||||
},
|
||||
ClientsContainer: EmptyClientsContainer{},
|
||||
},
|
||||
ServePlainDNS: true,
|
||||
})
|
||||
@@ -659,6 +726,7 @@ func TestBlockedRequest(t *testing.T) {
|
||||
EDNSClientSubnet: &EDNSClientSubnet{
|
||||
Enabled: false,
|
||||
},
|
||||
ClientsContainer: EmptyClientsContainer{},
|
||||
},
|
||||
ServePlainDNS: true,
|
||||
}
|
||||
@@ -696,6 +764,7 @@ func TestServerCustomClientUpstream(t *testing.T) {
|
||||
EDNSClientSubnet: &EDNSClientSubnet{
|
||||
Enabled: false,
|
||||
},
|
||||
ClientsContainer: EmptyClientsContainer{},
|
||||
},
|
||||
ServePlainDNS: true,
|
||||
}
|
||||
@@ -721,12 +790,12 @@ func TestServerCustomClientUpstream(t *testing.T) {
|
||||
forwardConf.EDNSClientSubnet.Enabled,
|
||||
)
|
||||
|
||||
s.conf.ClientsContainer = &aghtest.ClientsContainer{
|
||||
OnUpstreamConfigByID: func(
|
||||
s.conf.ClientsContainer = &clientsContainer{
|
||||
OnCustomUpstreamConfig: func(
|
||||
_ string,
|
||||
_ upstream.Resolver,
|
||||
) (conf *proxy.CustomUpstreamConfig, err error) {
|
||||
return customUpsConf, nil
|
||||
_ netip.Addr,
|
||||
) (conf *proxy.CustomUpstreamConfig) {
|
||||
return customUpsConf
|
||||
},
|
||||
}
|
||||
|
||||
@@ -774,6 +843,7 @@ func TestBlockCNAMEProtectionEnabled(t *testing.T) {
|
||||
EDNSClientSubnet: &EDNSClientSubnet{
|
||||
Enabled: false,
|
||||
},
|
||||
ClientsContainer: EmptyClientsContainer{},
|
||||
},
|
||||
ServePlainDNS: true,
|
||||
})
|
||||
@@ -808,6 +878,7 @@ func TestBlockCNAME(t *testing.T) {
|
||||
EDNSClientSubnet: &EDNSClientSubnet{
|
||||
Enabled: false,
|
||||
},
|
||||
ClientsContainer: EmptyClientsContainer{},
|
||||
},
|
||||
ServePlainDNS: true,
|
||||
}
|
||||
@@ -877,13 +948,11 @@ func TestClientRulesForCNAMEMatching(t *testing.T) {
|
||||
UDPListenAddrs: []*net.UDPAddr{{}},
|
||||
TCPListenAddrs: []*net.TCPAddr{{}},
|
||||
Config: Config{
|
||||
FilterHandler: func(_ netip.Addr, _ string, settings *filtering.Settings) {
|
||||
settings.FilteringEnabled = false
|
||||
},
|
||||
UpstreamMode: UpstreamModeLoadBalance,
|
||||
EDNSClientSubnet: &EDNSClientSubnet{
|
||||
Enabled: false,
|
||||
},
|
||||
ClientsContainer: EmptyClientsContainer{},
|
||||
},
|
||||
ServePlainDNS: true,
|
||||
}
|
||||
@@ -930,6 +999,7 @@ func TestNullBlockedRequest(t *testing.T) {
|
||||
EDNSClientSubnet: &EDNSClientSubnet{
|
||||
Enabled: false,
|
||||
},
|
||||
ClientsContainer: EmptyClientsContainer{},
|
||||
},
|
||||
ServePlainDNS: true,
|
||||
}
|
||||
@@ -969,10 +1039,12 @@ func TestBlockedCustomIP(t *testing.T) {
|
||||
}}
|
||||
|
||||
f, err := filtering.New(&filtering.Config{
|
||||
ProtectionEnabled: true,
|
||||
BlockingMode: filtering.BlockingModeCustomIP,
|
||||
BlockingIPv4: netip.Addr{},
|
||||
BlockingIPv6: netip.Addr{},
|
||||
ProtectionEnabled: true,
|
||||
ApplyClientFiltering: applyEmptyClientFiltering,
|
||||
BlockedServices: emptyFilteringBlockedServices(),
|
||||
BlockingMode: filtering.BlockingModeCustomIP,
|
||||
BlockingIPv4: netip.Addr{},
|
||||
BlockingIPv6: netip.Addr{},
|
||||
}, filters)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -998,6 +1070,7 @@ func TestBlockedCustomIP(t *testing.T) {
|
||||
EDNSClientSubnet: &EDNSClientSubnet{
|
||||
Enabled: false,
|
||||
},
|
||||
ClientsContainer: EmptyClientsContainer{},
|
||||
},
|
||||
ServePlainDNS: true,
|
||||
}
|
||||
@@ -1051,6 +1124,7 @@ func TestBlockedByHosts(t *testing.T) {
|
||||
EDNSClientSubnet: &EDNSClientSubnet{
|
||||
Enabled: false,
|
||||
},
|
||||
ClientsContainer: EmptyClientsContainer{},
|
||||
},
|
||||
ServePlainDNS: true,
|
||||
}
|
||||
@@ -1103,6 +1177,7 @@ func TestBlockedBySafeBrowsing(t *testing.T) {
|
||||
EDNSClientSubnet: &EDNSClientSubnet{
|
||||
Enabled: false,
|
||||
},
|
||||
ClientsContainer: EmptyClientsContainer{},
|
||||
},
|
||||
ServePlainDNS: true,
|
||||
}
|
||||
@@ -1122,7 +1197,9 @@ func TestBlockedBySafeBrowsing(t *testing.T) {
|
||||
|
||||
func TestRewrite(t *testing.T) {
|
||||
c := &filtering.Config{
|
||||
BlockingMode: filtering.BlockingModeDefault,
|
||||
ApplyClientFiltering: applyEmptyClientFiltering,
|
||||
BlockedServices: emptyFilteringBlockedServices(),
|
||||
BlockingMode: filtering.BlockingModeDefault,
|
||||
Rewrites: []*filtering.LegacyRewrite{{
|
||||
Domain: "test.com",
|
||||
Answer: "1.2.3.4",
|
||||
@@ -1164,6 +1241,7 @@ func TestRewrite(t *testing.T) {
|
||||
EDNSClientSubnet: &EDNSClientSubnet{
|
||||
Enabled: false,
|
||||
},
|
||||
ClientsContainer: EmptyClientsContainer{},
|
||||
},
|
||||
ServePlainDNS: true,
|
||||
}))
|
||||
@@ -1267,7 +1345,9 @@ func TestPTRResponseFromDHCPLeases(t *testing.T) {
|
||||
const localDomain = "lan"
|
||||
|
||||
flt, err := filtering.New(&filtering.Config{
|
||||
BlockingMode: filtering.BlockingModeDefault,
|
||||
ApplyClientFiltering: applyEmptyClientFiltering,
|
||||
BlockedServices: emptyFilteringBlockedServices(),
|
||||
BlockingMode: filtering.BlockingModeDefault,
|
||||
}, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -1290,6 +1370,7 @@ func TestPTRResponseFromDHCPLeases(t *testing.T) {
|
||||
s.conf.TCPListenAddrs = []*net.TCPAddr{{}}
|
||||
s.conf.UpstreamDNS = []string{"127.0.0.1:53"}
|
||||
s.conf.Config.EDNSClientSubnet = &EDNSClientSubnet{Enabled: false}
|
||||
s.conf.Config.ClientsContainer = EmptyClientsContainer{}
|
||||
s.conf.Config.UpstreamMode = UpstreamModeLoadBalance
|
||||
|
||||
err = s.Prepare(&s.conf)
|
||||
@@ -1355,8 +1436,10 @@ func TestPTRResponseFromHosts(t *testing.T) {
|
||||
})
|
||||
|
||||
flt, err := filtering.New(&filtering.Config{
|
||||
BlockingMode: filtering.BlockingModeDefault,
|
||||
EtcHosts: hc,
|
||||
ApplyClientFiltering: applyEmptyClientFiltering,
|
||||
BlockedServices: emptyFilteringBlockedServices(),
|
||||
BlockingMode: filtering.BlockingModeDefault,
|
||||
EtcHosts: hc,
|
||||
}, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -1375,6 +1458,7 @@ func TestPTRResponseFromHosts(t *testing.T) {
|
||||
s.conf.TCPListenAddrs = []*net.TCPAddr{{}}
|
||||
s.conf.UpstreamDNS = []string{"127.0.0.1:53"}
|
||||
s.conf.Config.EDNSClientSubnet = &EDNSClientSubnet{Enabled: false}
|
||||
s.conf.Config.ClientsContainer = EmptyClientsContainer{}
|
||||
s.conf.Config.UpstreamMode = UpstreamModeLoadBalance
|
||||
|
||||
err = s.Prepare(&s.conf)
|
||||
@@ -1643,6 +1727,7 @@ func TestServer_Exchange(t *testing.T) {
|
||||
UpstreamDNS: []string{upsAddr},
|
||||
UpstreamMode: UpstreamModeLoadBalance,
|
||||
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
|
||||
ClientsContainer: EmptyClientsContainer{},
|
||||
},
|
||||
LocalPTRResolvers: []string{localUpsAddr},
|
||||
UsePrivateRDNS: true,
|
||||
@@ -1665,6 +1750,7 @@ func TestServer_Exchange(t *testing.T) {
|
||||
UpstreamDNS: []string{upsAddr},
|
||||
UpstreamMode: UpstreamModeLoadBalance,
|
||||
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
|
||||
ClientsContainer: EmptyClientsContainer{},
|
||||
},
|
||||
LocalPTRResolvers: []string{},
|
||||
ServePlainDNS: true,
|
||||
@@ -40,6 +40,7 @@ func TestServer_FilterDNSRewrite(t *testing.T) {
|
||||
Config: Config{
|
||||
UpstreamMode: UpstreamModeLoadBalance,
|
||||
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
|
||||
ClientsContainer: EmptyClientsContainer{},
|
||||
},
|
||||
ServePlainDNS: true,
|
||||
})
|
||||
@@ -17,9 +17,7 @@ import (
|
||||
func (s *Server) clientRequestFilteringSettings(dctx *dnsContext) (setts *filtering.Settings) {
|
||||
setts = s.dnsFilter.Settings()
|
||||
setts.ProtectionEnabled = dctx.protectionEnabled
|
||||
if s.conf.FilterHandler != nil {
|
||||
s.conf.FilterHandler(dctx.proxyCtx.Addr.Addr(), dctx.clientID, setts)
|
||||
}
|
||||
s.dnsFilter.ApplyAdditionalFiltering(dctx.proxyCtx.Addr.Addr(), dctx.clientID, setts)
|
||||
|
||||
return setts
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ func TestHandleDNSRequest_handleDNSRequest(t *testing.T) {
|
||||
EDNSClientSubnet: &EDNSClientSubnet{
|
||||
Enabled: false,
|
||||
},
|
||||
ClientsContainer: EmptyClientsContainer{},
|
||||
},
|
||||
ServePlainDNS: true,
|
||||
}
|
||||
@@ -44,8 +45,10 @@ func TestHandleDNSRequest_handleDNSRequest(t *testing.T) {
|
||||
}}
|
||||
|
||||
f, err := filtering.New(&filtering.Config{
|
||||
ProtectionEnabled: true,
|
||||
BlockingMode: filtering.BlockingModeDefault,
|
||||
ProtectionEnabled: true,
|
||||
ApplyClientFiltering: applyEmptyClientFiltering,
|
||||
BlockedServices: emptyFilteringBlockedServices(),
|
||||
BlockingMode: filtering.BlockingModeDefault,
|
||||
}, filters)
|
||||
require.NoError(t, err)
|
||||
f.SetEnabled(true)
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
||||
"github.com/AdguardTeam/dnsproxy/proxy"
|
||||
"github.com/AdguardTeam/dnsproxy/upstream"
|
||||
@@ -647,7 +648,7 @@ func (s *Server) handleTestUpstreamDNS(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
req.BootstrapDNS = stringutil.FilterOut(req.BootstrapDNS, IsCommentOrEmpty)
|
||||
req.BootstrapDNS = stringutil.FilterOut(req.BootstrapDNS, aghnet.IsCommentOrEmpty)
|
||||
|
||||
opts := &upstream.Options{
|
||||
Timeout: s.conf.UpstreamTimeout,
|
||||
@@ -673,6 +674,8 @@ func (s *Server) handleTestUpstreamDNS(w http.ResponseWriter, r *http.Request) {
|
||||
// handleCacheClear is the handler for the POST /control/cache_clear HTTP API.
|
||||
func (s *Server) handleCacheClear(w http.ResponseWriter, _ *http.Request) {
|
||||
s.dnsProxy.ClearCache()
|
||||
s.conf.ClientsContainer.ClearUpstreamCache()
|
||||
|
||||
_, _ = io.WriteString(w, "OK")
|
||||
}
|
||||
|
||||
|
||||
@@ -83,6 +83,7 @@ func TestDNSForwardHTTP_handleGetConfig(t *testing.T) {
|
||||
RatelimitSubnetLenIPv6: 56,
|
||||
UpstreamMode: UpstreamModeLoadBalance,
|
||||
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
|
||||
ClientsContainer: EmptyClientsContainer{},
|
||||
},
|
||||
ConfigModified: func() {},
|
||||
ServePlainDNS: true,
|
||||
@@ -164,6 +165,7 @@ func TestDNSForwardHTTP_handleSetConfig(t *testing.T) {
|
||||
RatelimitSubnetLenIPv6: 56,
|
||||
UpstreamMode: UpstreamModeLoadBalance,
|
||||
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
|
||||
ClientsContainer: EmptyClientsContainer{},
|
||||
},
|
||||
ConfigModified: func() {},
|
||||
ServePlainDNS: true,
|
||||
@@ -299,24 +301,6 @@ func TestDNSForwardHTTP_handleSetConfig(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsCommentOrEmpty(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
want assert.BoolAssertionFunc
|
||||
str string
|
||||
}{{
|
||||
want: assert.True,
|
||||
str: "",
|
||||
}, {
|
||||
want: assert.True,
|
||||
str: "# comment",
|
||||
}, {
|
||||
want: assert.False,
|
||||
str: "1.2.3.4",
|
||||
}} {
|
||||
tc.want(t, IsCommentOrEmpty(tc.str))
|
||||
}
|
||||
}
|
||||
|
||||
func newLocalUpstreamListener(t *testing.T, port uint16, handler dns.Handler) (real netip.AddrPort) {
|
||||
t.Helper()
|
||||
|
||||
@@ -388,6 +372,7 @@ func TestServer_HandleTestUpstreamDNS(t *testing.T) {
|
||||
Config: Config{
|
||||
UpstreamMode: UpstreamModeLoadBalance,
|
||||
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
|
||||
ClientsContainer: EmptyClientsContainer{},
|
||||
},
|
||||
ServePlainDNS: true,
|
||||
})
|
||||
@@ -1,7 +1,6 @@
|
||||
package dnsforward
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"net"
|
||||
@@ -577,17 +576,14 @@ func (s *Server) setCustomUpstream(pctx *proxy.DNSContext, clientID string) {
|
||||
return
|
||||
}
|
||||
|
||||
// Use the ClientID first, since it has a higher priority.
|
||||
id := cmp.Or(clientID, pctx.Addr.Addr().String())
|
||||
upsConf, err := s.conf.ClientsContainer.UpstreamConfigByID(id, s.bootstrap)
|
||||
if err != nil {
|
||||
log.Error("dnsforward: getting custom upstreams for client %s: %s", id, err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
cliAddr := pctx.Addr.Addr()
|
||||
upsConf := s.conf.ClientsContainer.CustomUpstreamConfig(clientID, cliAddr)
|
||||
if upsConf != nil {
|
||||
log.Debug("dnsforward: using custom upstreams for client %s", id)
|
||||
log.Debug(
|
||||
"dnsforward: using custom upstreams for client with ip %s and clientid %q",
|
||||
cliAddr,
|
||||
clientID,
|
||||
)
|
||||
|
||||
pctx.CustomUpstreamConfig = upsConf
|
||||
}
|
||||
|
||||
@@ -81,6 +81,7 @@ func TestServer_ProcessInitial(t *testing.T) {
|
||||
AAAADisabled: tc.aaaaDisabled,
|
||||
UpstreamMode: UpstreamModeLoadBalance,
|
||||
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
|
||||
ClientsContainer: EmptyClientsContainer{},
|
||||
},
|
||||
ServePlainDNS: true,
|
||||
}
|
||||
@@ -180,6 +181,7 @@ func TestServer_ProcessFilteringAfterResponse(t *testing.T) {
|
||||
AAAADisabled: tc.aaaaDisabled,
|
||||
UpstreamMode: UpstreamModeLoadBalance,
|
||||
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
|
||||
ClientsContainer: EmptyClientsContainer{},
|
||||
},
|
||||
ServePlainDNS: true,
|
||||
}
|
||||
@@ -324,6 +326,7 @@ func TestServer_ProcessDDRQuery(t *testing.T) {
|
||||
HandleDDR: tc.ddrEnabled,
|
||||
UpstreamMode: UpstreamModeLoadBalance,
|
||||
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
|
||||
ClientsContainer: EmptyClientsContainer{},
|
||||
},
|
||||
TLSConfig: TLSConfig{
|
||||
ServerName: ddrTestDomainName,
|
||||
@@ -660,6 +663,7 @@ func TestServer_HandleDNSRequest_restrictLocal(t *testing.T) {
|
||||
UpstreamDNS: []string{localUpsAddr},
|
||||
UpstreamMode: UpstreamModeLoadBalance,
|
||||
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
|
||||
ClientsContainer: EmptyClientsContainer{},
|
||||
},
|
||||
UsePrivateRDNS: true,
|
||||
LocalPTRResolvers: []string{localUpsAddr},
|
||||
@@ -788,6 +792,7 @@ func TestServer_ProcessUpstream_localPTR(t *testing.T) {
|
||||
Config: Config{
|
||||
UpstreamMode: UpstreamModeLoadBalance,
|
||||
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
|
||||
ClientsContainer: EmptyClientsContainer{},
|
||||
},
|
||||
UsePrivateRDNS: true,
|
||||
LocalPTRResolvers: []string{localUpsAddr},
|
||||
@@ -816,6 +821,7 @@ func TestServer_ProcessUpstream_localPTR(t *testing.T) {
|
||||
Config: Config{
|
||||
UpstreamMode: UpstreamModeLoadBalance,
|
||||
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
|
||||
ClientsContainer: EmptyClientsContainer{},
|
||||
},
|
||||
UsePrivateRDNS: false,
|
||||
LocalPTRResolvers: []string{localUpsAddr},
|
||||
|
||||
@@ -19,6 +19,7 @@ func TestGenAnswerHTTPS_andSVCB(t *testing.T) {
|
||||
Config: Config{
|
||||
UpstreamMode: UpstreamModeLoadBalance,
|
||||
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
|
||||
ClientsContainer: EmptyClientsContainer{},
|
||||
},
|
||||
ServePlainDNS: true,
|
||||
})
|
||||
@@ -94,7 +94,7 @@ func newPrivateConfig(
|
||||
) (uc *proxy.UpstreamConfig, err error) {
|
||||
confNeedsFiltering := len(addrs) > 0
|
||||
if confNeedsFiltering {
|
||||
addrs = stringutil.FilterOut(addrs, IsCommentOrEmpty)
|
||||
addrs = stringutil.FilterOut(addrs, aghnet.IsCommentOrEmpty)
|
||||
} else {
|
||||
sysResolvers := slices.DeleteFunc(slices.Clone(sysResolvers.Addrs()), unwanted.Has)
|
||||
addrs = make([]string, 0, len(sysResolvers))
|
||||
@@ -127,20 +127,6 @@ func newPrivateConfig(
|
||||
return uc, nil
|
||||
}
|
||||
|
||||
// UpstreamHTTPVersions returns the HTTP versions for upstream configuration
|
||||
// depending on configuration.
|
||||
func UpstreamHTTPVersions(http3 bool) (v []upstream.HTTPVersion) {
|
||||
if !http3 {
|
||||
return upstream.DefaultHTTPVersions
|
||||
}
|
||||
|
||||
return []upstream.HTTPVersion{
|
||||
upstream.HTTPVersion3,
|
||||
upstream.HTTPVersion2,
|
||||
upstream.HTTPVersion11,
|
||||
}
|
||||
}
|
||||
|
||||
// setProxyUpstreamMode sets the upstream mode and related settings in conf
|
||||
// based on provided parameters.
|
||||
func setProxyUpstreamMode(
|
||||
@@ -162,10 +148,3 @@ func setProxyUpstreamMode(
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsCommentOrEmpty returns true if s starts with a "#" character or is empty.
|
||||
// This function is useful for filtering out non-upstream lines from upstream
|
||||
// configs.
|
||||
func IsCommentOrEmpty(s string) (ok bool) {
|
||||
return len(s) == 0 || s[0] == '#'
|
||||
}
|
||||
|
||||
@@ -49,6 +49,9 @@ func initBlockedServices() {
|
||||
}
|
||||
|
||||
// BlockedServices is the configuration of blocked services.
|
||||
//
|
||||
// TODO(s.chzhen): Move to a higher-level package to allow importing the client
|
||||
// package into the filtering package.
|
||||
type BlockedServices struct {
|
||||
// Schedule is blocked services schedule for every day of the week.
|
||||
Schedule *schedule.Weekly `json:"schedule" yaml:"schedule"`
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
package filtering
|
||||
package filtering_test
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
||||
"github.com/AdguardTeam/golibs/netutil"
|
||||
"github.com/miekg/dns"
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -50,8 +51,17 @@ func TestDNSFilter_CheckHostRules_dnsrewrite(t *testing.T) {
|
||||
|1.2.3.5.in-addr.arpa^$dnsrewrite=NOERROR;PTR;new-ptr-with-dot.
|
||||
`
|
||||
|
||||
f, _ := newForTest(t, nil, []Filter{{ID: 0, Data: []byte(text)}})
|
||||
setts := &Settings{
|
||||
conf := &filtering.Config{
|
||||
SafeBrowsingCacheSize: 10000,
|
||||
ParentalCacheSize: 10000,
|
||||
SafeSearchCacheSize: 1000,
|
||||
CacheTime: 30,
|
||||
}
|
||||
|
||||
f, err := filtering.New(conf, []filtering.Filter{{ID: 0, Data: []byte(text)}})
|
||||
require.NoError(t, err)
|
||||
|
||||
setts := &filtering.Settings{
|
||||
FilteringEnabled: true,
|
||||
}
|
||||
|
||||
@@ -117,7 +127,8 @@ func TestDNSFilter_CheckHostRules_dnsrewrite(t *testing.T) {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
host := path.Base(tc.name)
|
||||
|
||||
res, err := f.CheckHostRules(host, tc.dtyp, setts)
|
||||
var res filtering.Result
|
||||
res, err = f.CheckHostRules(host, tc.dtyp, setts)
|
||||
require.NoError(t, err)
|
||||
|
||||
dnsrr := res.DNSRewriteResult
|
||||
@@ -141,7 +152,8 @@ func TestDNSFilter_CheckHostRules_dnsrewrite(t *testing.T) {
|
||||
dtyp := dns.TypeA
|
||||
host := path.Base(t.Name())
|
||||
|
||||
res, err := f.CheckHostRules(host, dtyp, setts)
|
||||
var res filtering.Result
|
||||
res, err = f.CheckHostRules(host, dtyp, setts)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, "new-cname", res.CanonName)
|
||||
@@ -151,7 +163,8 @@ func TestDNSFilter_CheckHostRules_dnsrewrite(t *testing.T) {
|
||||
dtyp := dns.TypeA
|
||||
host := path.Base(t.Name())
|
||||
|
||||
res, err := f.CheckHostRules(host, dtyp, setts)
|
||||
var res filtering.Result
|
||||
res, err = f.CheckHostRules(host, dtyp, setts)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, "new-cname-2", res.CanonName)
|
||||
@@ -162,7 +175,8 @@ func TestDNSFilter_CheckHostRules_dnsrewrite(t *testing.T) {
|
||||
dtyp := dns.TypeA
|
||||
host := path.Base(t.Name())
|
||||
|
||||
res, err := f.CheckHostRules(host, dtyp, setts)
|
||||
var res filtering.Result
|
||||
res, err = f.CheckHostRules(host, dtyp, setts)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Empty(t, res.CanonName)
|
||||
@@ -173,7 +187,8 @@ func TestDNSFilter_CheckHostRules_dnsrewrite(t *testing.T) {
|
||||
dtyp := dns.TypePTR
|
||||
host := path.Base(t.Name())
|
||||
|
||||
res, err := f.CheckHostRules(host, dtyp, setts)
|
||||
var res filtering.Result
|
||||
res, err = f.CheckHostRules(host, dtyp, setts)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, res.DNSRewriteResult)
|
||||
|
||||
@@ -193,7 +208,8 @@ func TestDNSFilter_CheckHostRules_dnsrewrite(t *testing.T) {
|
||||
dtyp := dns.TypePTR
|
||||
host := path.Base(t.Name())
|
||||
|
||||
res, err := f.CheckHostRules(host, dtyp, setts)
|
||||
var res filtering.Result
|
||||
res, err = f.CheckHostRules(host, dtyp, setts)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, res.DNSRewriteResult)
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/netip"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
@@ -629,3 +630,19 @@ func (d *DNSFilter) enableFiltersLocked(async bool) {
|
||||
|
||||
d.SetEnabled(d.conf.FilteringEnabled)
|
||||
}
|
||||
|
||||
// ApplyAdditionalFiltering enhances the provided filtering settings with
|
||||
// blocked services and client-specific configurations.
|
||||
func (d *DNSFilter) ApplyAdditionalFiltering(cliAddr netip.Addr, clientID string, setts *Settings) {
|
||||
d.ApplyBlockedServices(setts)
|
||||
|
||||
d.applyClientFiltering(clientID, cliAddr, setts)
|
||||
if setts.BlockedServices != nil {
|
||||
// TODO(e.burkov): Get rid of this crutch.
|
||||
setts.ServicesRules = nil
|
||||
svcs := setts.BlockedServices.IDs
|
||||
if !setts.BlockedServices.Schedule.Contains(time.Now()) {
|
||||
d.ApplyBlockedServicesList(setts, svcs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,6 +40,8 @@ type ServiceEntry struct {
|
||||
}
|
||||
|
||||
// Settings are custom filtering settings for a client.
|
||||
//
|
||||
// TODO(s.chzhen): Move to the client package.
|
||||
type Settings struct {
|
||||
ClientName string
|
||||
ClientIP netip.Addr
|
||||
@@ -47,6 +49,10 @@ type Settings struct {
|
||||
|
||||
ServicesRules []ServiceEntry
|
||||
|
||||
// BlockedServices is the configuration of blocked services of a client. It
|
||||
// is nil if the client does not have any blocked services.
|
||||
BlockedServices *BlockedServices
|
||||
|
||||
ProtectionEnabled bool
|
||||
FilteringEnabled bool
|
||||
SafeSearchEnabled bool
|
||||
@@ -78,6 +84,11 @@ type Config struct {
|
||||
|
||||
SafeSearch SafeSearch `yaml:"-"`
|
||||
|
||||
// ApplyClientFiltering retrieves persistent client information using the
|
||||
// ClientID or client IP address, and applies it to the filtering settings.
|
||||
// It must not be nil.
|
||||
ApplyClientFiltering func(clientID string, cliAddr netip.Addr, setts *Settings) `yaml:"-"`
|
||||
|
||||
// BlockedServices is the configuration of blocked services.
|
||||
// Per-client settings can override this configuration.
|
||||
BlockedServices *BlockedServices `yaml:"blocked_services"`
|
||||
@@ -244,6 +255,13 @@ type DNSFilter struct {
|
||||
// parentalControl is the parental control hash-prefix checker.
|
||||
parentalControlChecker Checker
|
||||
|
||||
// applyClientFiltering retrieves persistent client information using the
|
||||
// ClientID or client IP address, and applies it to the filtering settings.
|
||||
//
|
||||
// TODO(s.chzhen): Consider finding a better approach while taking an
|
||||
// import cycle into account.
|
||||
applyClientFiltering func(clientID string, cliAddr netip.Addr, setts *Settings)
|
||||
|
||||
engineLock sync.RWMutex
|
||||
|
||||
// confMu protects conf.
|
||||
@@ -998,6 +1016,7 @@ func New(c *Config, blockFilters []Filter) (d *DNSFilter, err error) {
|
||||
refreshLock: &sync.Mutex{},
|
||||
safeBrowsingChecker: c.SafeBrowsingChecker,
|
||||
parentalControlChecker: c.ParentalControlChecker,
|
||||
applyClientFiltering: c.ApplyClientFiltering,
|
||||
confMu: &sync.RWMutex{},
|
||||
}
|
||||
|
||||
|
||||
@@ -661,8 +661,6 @@ func TestClientSettings(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// Benchmarks.
|
||||
|
||||
func BenchmarkSafeBrowsing(b *testing.B) {
|
||||
d, setts := newForTest(b, &Config{
|
||||
SafeBrowsingEnabled: true,
|
||||
@@ -670,15 +668,26 @@ func BenchmarkSafeBrowsing(b *testing.B) {
|
||||
}, nil)
|
||||
b.Cleanup(d.Close)
|
||||
|
||||
for range b.N {
|
||||
res, err := d.CheckHost(sbBlocked, dns.TypeA, setts)
|
||||
require.NoError(b, err)
|
||||
|
||||
assert.Truef(b, res.IsFiltered, "expected hostname %q to match", sbBlocked)
|
||||
var res Result
|
||||
var err error
|
||||
b.ReportAllocs()
|
||||
for b.Loop() {
|
||||
res, err = d.CheckHost(sbBlocked, dns.TypeA, setts)
|
||||
}
|
||||
|
||||
require.NoError(b, err)
|
||||
assert.Truef(b, res.IsFiltered, "expected hostname %q to match", sbBlocked)
|
||||
|
||||
// Most recent results:
|
||||
//
|
||||
// goos: darwin
|
||||
// goarch: amd64
|
||||
// pkg: github.com/AdguardTeam/AdGuardHome/internal/filtering
|
||||
// cpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
|
||||
// BenchmarkSafeBrowsing-12 358934 2994 ns/op 1304 B/op 40 allocs/op
|
||||
}
|
||||
|
||||
func BenchmarkSafeBrowsingParallel(b *testing.B) {
|
||||
func BenchmarkSafeBrowsing_parallel(b *testing.B) {
|
||||
d, setts := newForTest(b, &Config{
|
||||
SafeBrowsingEnabled: true,
|
||||
SafeBrowsingChecker: newChecker(sbBlocked),
|
||||
@@ -693,4 +702,12 @@ func BenchmarkSafeBrowsingParallel(b *testing.B) {
|
||||
assert.Truef(b, res.IsFiltered, "expected hostname %q to match", sbBlocked)
|
||||
}
|
||||
})
|
||||
|
||||
// Most recent results:
|
||||
//
|
||||
// goos: darwin
|
||||
// goarch: amd64
|
||||
// pkg: github.com/AdguardTeam/AdGuardHome/internal/filtering
|
||||
// cpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
|
||||
// BenchmarkSafeBrowsing_parallel-12 507327 2382 ns/op 1352 B/op 42 allocs/op
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package filtering
|
||||
package filtering_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghtest"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/filtering/rulelist"
|
||||
"github.com/AdguardTeam/golibs/testutil"
|
||||
"github.com/AdguardTeam/urlfilter/rules"
|
||||
@@ -50,27 +51,27 @@ func TestDNSFilter_CheckHost_hostsContainer(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
testutil.CleanupAndRequireSuccess(t, hc.Close)
|
||||
|
||||
conf := &Config{
|
||||
conf := &filtering.Config{
|
||||
EtcHosts: hc,
|
||||
}
|
||||
f, err := New(conf, nil)
|
||||
f, err := filtering.New(conf, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
setts := &Settings{
|
||||
setts := &filtering.Settings{
|
||||
FilteringEnabled: true,
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
host string
|
||||
wantRules []*ResultRule
|
||||
wantRules []*filtering.ResultRule
|
||||
wantResps []rules.RRValue
|
||||
dtyp uint16
|
||||
}{{
|
||||
name: "v4",
|
||||
host: "v4.host.example",
|
||||
dtyp: dns.TypeA,
|
||||
wantRules: []*ResultRule{{
|
||||
wantRules: []*filtering.ResultRule{{
|
||||
Text: "1.2.3.4 v4.host.example",
|
||||
FilterListID: rulelist.URLFilterIDEtcHosts,
|
||||
}},
|
||||
@@ -79,7 +80,7 @@ func TestDNSFilter_CheckHost_hostsContainer(t *testing.T) {
|
||||
name: "v6",
|
||||
host: "v6.host.example",
|
||||
dtyp: dns.TypeAAAA,
|
||||
wantRules: []*ResultRule{{
|
||||
wantRules: []*filtering.ResultRule{{
|
||||
Text: "::1 v6.host.example",
|
||||
FilterListID: rulelist.URLFilterIDEtcHosts,
|
||||
}},
|
||||
@@ -88,7 +89,7 @@ func TestDNSFilter_CheckHost_hostsContainer(t *testing.T) {
|
||||
name: "mapped",
|
||||
host: "mapped.host.example",
|
||||
dtyp: dns.TypeAAAA,
|
||||
wantRules: []*ResultRule{{
|
||||
wantRules: []*filtering.ResultRule{{
|
||||
Text: "::ffff:1.2.3.4 mapped.host.example",
|
||||
FilterListID: rulelist.URLFilterIDEtcHosts,
|
||||
}},
|
||||
@@ -97,7 +98,7 @@ func TestDNSFilter_CheckHost_hostsContainer(t *testing.T) {
|
||||
name: "ptr",
|
||||
host: "4.3.2.1.in-addr.arpa",
|
||||
dtyp: dns.TypePTR,
|
||||
wantRules: []*ResultRule{{
|
||||
wantRules: []*filtering.ResultRule{{
|
||||
Text: "1.2.3.4 v4.host.example",
|
||||
FilterListID: rulelist.URLFilterIDEtcHosts,
|
||||
}},
|
||||
@@ -106,7 +107,7 @@ func TestDNSFilter_CheckHost_hostsContainer(t *testing.T) {
|
||||
name: "ptr-mapped",
|
||||
host: "4.0.3.0.2.0.1.0.f.f.f.f.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa",
|
||||
dtyp: dns.TypePTR,
|
||||
wantRules: []*ResultRule{{
|
||||
wantRules: []*filtering.ResultRule{{
|
||||
Text: "::ffff:1.2.3.4 mapped.host.example",
|
||||
FilterListID: rulelist.URLFilterIDEtcHosts,
|
||||
}},
|
||||
@@ -133,7 +134,7 @@ func TestDNSFilter_CheckHost_hostsContainer(t *testing.T) {
|
||||
name: "v4_mismatch",
|
||||
host: "v4.host.example",
|
||||
dtyp: dns.TypeAAAA,
|
||||
wantRules: []*ResultRule{{
|
||||
wantRules: []*filtering.ResultRule{{
|
||||
Text: fmt.Sprintf("%s v4.host.example", addrv4),
|
||||
FilterListID: rulelist.URLFilterIDEtcHosts,
|
||||
}},
|
||||
@@ -142,7 +143,7 @@ func TestDNSFilter_CheckHost_hostsContainer(t *testing.T) {
|
||||
name: "v6_mismatch",
|
||||
host: "v6.host.example",
|
||||
dtyp: dns.TypeA,
|
||||
wantRules: []*ResultRule{{
|
||||
wantRules: []*filtering.ResultRule{{
|
||||
Text: fmt.Sprintf("%s v6.host.example", addrv6),
|
||||
FilterListID: rulelist.URLFilterIDEtcHosts,
|
||||
}},
|
||||
@@ -163,7 +164,7 @@ func TestDNSFilter_CheckHost_hostsContainer(t *testing.T) {
|
||||
name: "v4_dup",
|
||||
host: "v4.host.with-dup",
|
||||
dtyp: dns.TypeA,
|
||||
wantRules: []*ResultRule{{
|
||||
wantRules: []*filtering.ResultRule{{
|
||||
Text: "4.3.2.1 v4.host.with-dup",
|
||||
FilterListID: rulelist.URLFilterIDEtcHosts,
|
||||
}},
|
||||
@@ -172,7 +173,7 @@ func TestDNSFilter_CheckHost_hostsContainer(t *testing.T) {
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
var res Result
|
||||
var res filtering.Result
|
||||
res, err = f.CheckHost(tc.host, tc.dtyp, setts)
|
||||
require.NoError(t, err)
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -244,7 +246,7 @@ func (d *DNSFilter) handleFilteringSetURL(w http.ResponseWriter, r *http.Request
|
||||
|
||||
restart, err := d.filterSetProperties(fj.URL, filt, fj.Whitelist)
|
||||
if err != nil {
|
||||
aghhttp.Error(r, w, http.StatusBadRequest, err.Error())
|
||||
aghhttp.Error(r, w, http.StatusBadRequest, "%s", err)
|
||||
|
||||
return
|
||||
}
|
||||
@@ -420,15 +422,53 @@ type checkHostResp struct {
|
||||
FilterID rulelist.URLFilterID `json:"filter_id"`
|
||||
}
|
||||
|
||||
// handleCheckHost is the handler for the GET /control/filtering/check_host HTTP
|
||||
// API.
|
||||
func (d *DNSFilter) handleCheckHost(w http.ResponseWriter, r *http.Request) {
|
||||
host := r.URL.Query().Get("name")
|
||||
query := r.URL.Query()
|
||||
host := query.Get("name")
|
||||
if host == "" {
|
||||
aghhttp.Error(
|
||||
r,
|
||||
w,
|
||||
http.StatusBadRequest,
|
||||
`query parameter "name" is required`,
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
cli := query.Get("client")
|
||||
qTypeStr := query.Get("qtype")
|
||||
qType, err := stringToDNSType(qTypeStr)
|
||||
if err != nil {
|
||||
aghhttp.Error(
|
||||
r,
|
||||
w,
|
||||
http.StatusUnprocessableEntity,
|
||||
"bad qtype query parameter: %q",
|
||||
qTypeStr,
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
setts := d.Settings()
|
||||
setts.FilteringEnabled = true
|
||||
setts.ProtectionEnabled = true
|
||||
|
||||
d.ApplyBlockedServices(setts)
|
||||
result, err := d.CheckHost(host, dns.TypeA, setts)
|
||||
addr, err := netip.ParseAddr(cli)
|
||||
if err == nil {
|
||||
setts.ClientIP = addr
|
||||
d.ApplyAdditionalFiltering(addr, "", setts)
|
||||
} else if cli != "" {
|
||||
// TODO(s.chzhen): Set [Settings.ClientName] once urlfilter supports
|
||||
// multiple client names. This will handle the case when a rule exists
|
||||
// but the persistent client does not.
|
||||
d.ApplyAdditionalFiltering(netip.Addr{}, cli, setts)
|
||||
}
|
||||
|
||||
result, err := d.CheckHost(host, qType, setts)
|
||||
if err != nil {
|
||||
aghhttp.Error(
|
||||
r,
|
||||
@@ -466,6 +506,33 @@ func (d *DNSFilter) handleCheckHost(w http.ResponseWriter, r *http.Request) {
|
||||
aghhttp.WriteJSONResponseOK(w, r, resp)
|
||||
}
|
||||
|
||||
// stringToDNSType is a helper function that converts a string to DNS type. If
|
||||
// the string is empty, it returns the default value [dns.TypeA].
|
||||
func stringToDNSType(str string) (qtype uint16, err error) {
|
||||
if str == "" {
|
||||
return dns.TypeA, nil
|
||||
}
|
||||
|
||||
qtype, ok := dns.StringToType[str]
|
||||
if ok {
|
||||
return qtype, nil
|
||||
}
|
||||
|
||||
// typePref is a prefix for DNS types from experimental RFCs.
|
||||
const typePref = "TYPE"
|
||||
|
||||
if !strings.HasPrefix(str, typePref) {
|
||||
return 0, errors.ErrBadEnumValue
|
||||
}
|
||||
|
||||
val, err := strconv.ParseUint(str[len(typePref):], 10, 16)
|
||||
if err != nil {
|
||||
return 0, errors.ErrBadEnumValue
|
||||
}
|
||||
|
||||
return uint16(val), nil
|
||||
}
|
||||
|
||||
// setProtectedBool sets the value of a boolean pointer under a lock. l must
|
||||
// protect the value under ptr.
|
||||
//
|
||||
|
||||
@@ -3,11 +3,15 @@ package filtering
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/netip"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/schedule"
|
||||
"github.com/AdguardTeam/golibs/testutil"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -305,3 +309,168 @@ func TestDNSFilter_handleParentalStatus(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDNSFilter_HandleCheckHost(t *testing.T) {
|
||||
const (
|
||||
cliName = "client_name"
|
||||
cliID = "client_id"
|
||||
|
||||
notFilteredHost = "not.filterd.example"
|
||||
allowedHost = "allowed.example"
|
||||
blockedHost = "blocked.example"
|
||||
cliHost = "client.example"
|
||||
qTypeHost = "qtype.example"
|
||||
cliQTypeHost = "cli.qtype.example"
|
||||
|
||||
target = "/control/check_host"
|
||||
hostFmt = target + "?name=%s"
|
||||
hostCliFmt = hostFmt + "&client=%s"
|
||||
hostQTypeFmt = hostFmt + "&qtype=%s"
|
||||
hostCliQTypeFmt = hostCliFmt + "&qtype=%s"
|
||||
|
||||
allowedRuleFmt = "@@||%s^"
|
||||
blockedRuleFmt = "||%s^"
|
||||
blockedRuleCliFmt = blockedRuleFmt + "$client=%s"
|
||||
blockedRuleQTypeFmt = blockedRuleFmt + "$dnstype=%s"
|
||||
blockedRuleCliQTypeFmt = blockedRuleCliFmt + ",dnstype=%s"
|
||||
)
|
||||
|
||||
var (
|
||||
allowedRule = fmt.Sprintf(allowedRuleFmt, allowedHost)
|
||||
blockedRule = fmt.Sprintf(blockedRuleFmt, blockedHost)
|
||||
blockedClientRule = fmt.Sprintf(blockedRuleCliFmt, cliHost, cliName)
|
||||
blockedQTypeRule = fmt.Sprintf(blockedRuleQTypeFmt, qTypeHost, "CNAME")
|
||||
blockedClientQTypeRule = fmt.Sprintf(blockedRuleCliQTypeFmt, cliQTypeHost, cliName, "CNAME")
|
||||
|
||||
notFilteredURL = fmt.Sprintf(hostFmt, notFilteredHost)
|
||||
allowedURL = fmt.Sprintf(hostFmt, allowedHost)
|
||||
blockedURL = fmt.Sprintf(hostFmt, blockedHost)
|
||||
blockedClientURL = fmt.Sprintf(hostCliFmt, cliHost, cliID)
|
||||
allowedQTypeURL = fmt.Sprintf(hostQTypeFmt, qTypeHost, "AAAA")
|
||||
blockedQTypeURL = fmt.Sprintf(hostQTypeFmt, qTypeHost, "CNAME")
|
||||
allowedClientQTypeURL = fmt.Sprintf(hostCliQTypeFmt, cliQTypeHost, cliID, "AAAA")
|
||||
blockedClientQTypeURL = fmt.Sprintf(hostCliQTypeFmt, cliQTypeHost, cliID, "CNAME")
|
||||
)
|
||||
|
||||
rules := []string{
|
||||
allowedRule,
|
||||
blockedRule,
|
||||
blockedClientRule,
|
||||
blockedQTypeRule,
|
||||
blockedClientQTypeRule,
|
||||
}
|
||||
rulesData := strings.Join(rules, "\n")
|
||||
|
||||
filters := []Filter{{
|
||||
ID: 0, Data: []byte(rulesData),
|
||||
}}
|
||||
|
||||
clientNames := map[string]string{
|
||||
cliID: cliName,
|
||||
}
|
||||
|
||||
dnsFilter, err := New(&Config{
|
||||
BlockedServices: &BlockedServices{
|
||||
Schedule: schedule.EmptyWeekly(),
|
||||
},
|
||||
ApplyClientFiltering: func(clientID string, cliAddr netip.Addr, setts *Settings) {
|
||||
setts.ClientName = clientNames[clientID]
|
||||
},
|
||||
}, filters)
|
||||
require.NoError(t, err)
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
url string
|
||||
want *checkHostResp
|
||||
}{{
|
||||
name: "not_filtered",
|
||||
url: notFilteredURL,
|
||||
want: &checkHostResp{
|
||||
Reason: reasonNames[NotFilteredNotFound],
|
||||
Rule: "",
|
||||
Rules: []*checkHostRespRule{},
|
||||
},
|
||||
}, {
|
||||
name: "allowed",
|
||||
url: allowedURL,
|
||||
want: &checkHostResp{
|
||||
Reason: reasonNames[NotFilteredAllowList],
|
||||
Rule: allowedRule,
|
||||
Rules: []*checkHostRespRule{{
|
||||
Text: allowedRule,
|
||||
}},
|
||||
},
|
||||
}, {
|
||||
name: "blocked",
|
||||
url: blockedURL,
|
||||
want: &checkHostResp{
|
||||
Reason: reasonNames[FilteredBlockList],
|
||||
Rule: blockedRule,
|
||||
Rules: []*checkHostRespRule{{
|
||||
Text: blockedRule,
|
||||
}},
|
||||
},
|
||||
}, {
|
||||
name: "blocked_client",
|
||||
url: blockedClientURL,
|
||||
want: &checkHostResp{
|
||||
Reason: reasonNames[FilteredBlockList],
|
||||
Rule: blockedClientRule,
|
||||
Rules: []*checkHostRespRule{{
|
||||
Text: blockedClientRule,
|
||||
}},
|
||||
},
|
||||
}, {
|
||||
name: "allowed_qtype",
|
||||
url: allowedQTypeURL,
|
||||
want: &checkHostResp{
|
||||
Reason: reasonNames[NotFilteredNotFound],
|
||||
Rule: "",
|
||||
Rules: []*checkHostRespRule{},
|
||||
},
|
||||
}, {
|
||||
name: "blocked_qtype",
|
||||
url: blockedQTypeURL,
|
||||
want: &checkHostResp{
|
||||
Reason: reasonNames[FilteredBlockList],
|
||||
Rule: blockedQTypeRule,
|
||||
Rules: []*checkHostRespRule{{
|
||||
Text: blockedQTypeRule,
|
||||
}},
|
||||
},
|
||||
}, {
|
||||
name: "blocked_client_qtype",
|
||||
url: blockedClientQTypeURL,
|
||||
want: &checkHostResp{
|
||||
Reason: reasonNames[FilteredBlockList],
|
||||
Rule: blockedClientQTypeRule,
|
||||
Rules: []*checkHostRespRule{{
|
||||
Text: blockedClientQTypeRule,
|
||||
}},
|
||||
},
|
||||
}, {
|
||||
name: "allowed_client_qtype",
|
||||
url: allowedClientQTypeURL,
|
||||
want: &checkHostResp{
|
||||
Reason: reasonNames[NotFilteredNotFound],
|
||||
Rule: "",
|
||||
Rules: []*checkHostRespRule{},
|
||||
},
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
r := httptest.NewRequest(http.MethodGet, tc.url, nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
dnsFilter.handleCheckHost(w, r)
|
||||
|
||||
res := &checkHostResp{}
|
||||
err = json.NewDecoder(w.Body).Decode(res)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, tc.want, res)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -202,34 +202,30 @@ func TestParser_Parse_checksums(t *testing.T) {
|
||||
assert.Equal(t, gotWithoutComments, gotWithComments)
|
||||
}
|
||||
|
||||
var (
|
||||
resSink *rulelist.ParseResult
|
||||
errSink error
|
||||
)
|
||||
|
||||
func BenchmarkParser_Parse(b *testing.B) {
|
||||
dst := &bytes.Buffer{}
|
||||
src := strings.NewReader(strings.Repeat(testRuleTextBlocked, 1000))
|
||||
buf := make([]byte, rulelist.DefaultRuleBufSize)
|
||||
p := rulelist.NewParser()
|
||||
|
||||
var res *rulelist.ParseResult
|
||||
var err error
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for range b.N {
|
||||
resSink, errSink = p.Parse(dst, src, buf)
|
||||
for b.Loop() {
|
||||
res, err = p.Parse(dst, src, buf)
|
||||
dst.Reset()
|
||||
}
|
||||
|
||||
require.NoError(b, errSink)
|
||||
require.NotNil(b, resSink)
|
||||
require.NoError(b, err)
|
||||
require.NotNil(b, res)
|
||||
|
||||
// Most recent result, on a ThinkPad X13 with a Ryzen Pro 7 CPU:
|
||||
// Most recent results:
|
||||
//
|
||||
// goos: linux
|
||||
// goos: darwin
|
||||
// goarch: amd64
|
||||
// pkg: github.com/AdguardTeam/AdGuardHome/internal/filtering/rulelist
|
||||
// cpu: AMD Ryzen 7 PRO 4750U with Radeon Graphics
|
||||
// BenchmarkParser_Parse-16 100000000 128.0 ns/op 48 B/op 1 allocs/op
|
||||
// cpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
|
||||
// BenchmarkParser_Parse-12 19635926 53.70 ns/op 48 B/op 1 allocs/op
|
||||
}
|
||||
|
||||
func FuzzParser_Parse(f *testing.F) {
|
||||
|
||||
@@ -3,6 +3,9 @@ package filtering
|
||||
import "context"
|
||||
|
||||
// SafeSearch interface describes a service for search engines hosts rewrites.
|
||||
//
|
||||
// TODO(s.chzhen): Move to a higher-level package to allow importing the client
|
||||
// package into the filtering package.
|
||||
type SafeSearch interface {
|
||||
// CheckHost checks host with safe search filter. CheckHost must be safe
|
||||
// for concurrent use. qtype must be either [dns.TypeA] or [dns.TypeAAAA].
|
||||
|
||||
@@ -88,16 +88,25 @@ func TestSafeSearchCacheYandex(t *testing.T) {
|
||||
assert.Equal(t, cachedValue.Rules[0].IP, yandexIP)
|
||||
}
|
||||
|
||||
const googleHost = "www.google.com"
|
||||
func BenchmarkDefault_SearchHost(b *testing.B) {
|
||||
const googleHost = "www.google.com"
|
||||
|
||||
var dnsRewriteSink *rules.DNSRewrite
|
||||
|
||||
func BenchmarkSafeSearch(b *testing.B) {
|
||||
ss := newForTest(b, defaultSafeSearchConf)
|
||||
|
||||
for range b.N {
|
||||
dnsRewriteSink = ss.searchHost(googleHost, testQType)
|
||||
var rewrite *rules.DNSRewrite
|
||||
b.ReportAllocs()
|
||||
for b.Loop() {
|
||||
rewrite = ss.searchHost(googleHost, testQType)
|
||||
}
|
||||
|
||||
assert.Equal(b, "forcesafesearch.google.com", dnsRewriteSink.NewCNAME)
|
||||
require.NotNil(b, rewrite)
|
||||
assert.Equal(b, "forcesafesearch.google.com", rewrite.NewCNAME)
|
||||
|
||||
// Most recent results:
|
||||
//
|
||||
// goos: darwin
|
||||
// goarch: amd64
|
||||
// pkg: github.com/AdguardTeam/AdGuardHome/internal/filtering/safesearch
|
||||
// cpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
|
||||
// BenchmarkDefault_SearchHost-12 751882 1604 ns/op 129 B/op 5 allocs/op
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user