Compare commits

..

114 Commits

Author SHA1 Message Date
Ildar Kamalov
76bf99499a Merge branch 'master' into ADG-9415 2025-02-26 18:31:41 +03:00
Ainar Garipov
29529970a3 Merge branch 'master' into ADG-9415 2025-02-24 15:44:38 +03:00
Ildar Kamalov
b49790daf8 fix default lease duration value 2025-02-24 15:30:18 +03:00
Ildar Kamalov
cb307472ec fix default response status 2025-02-24 10:35:26 +03:00
Ildar Kamalov
115e743e1a fix upstream description 2025-02-24 10:32:46 +03:00
Ildar Kamalov
26b0eddaca use const for test config file 2025-02-18 17:40:41 +03:00
Ildar Kamalov
58faa7c537 fix install config 2025-02-18 17:31:04 +03:00
Ildar Kamalov
0a3346d911 fix install check config 2025-02-17 15:25:23 +03:00
Ildar Kamalov
17c4c26ea8 fix query log 2025-02-14 17:18:20 +03:00
Ildar Kamalov
14a2685ae3 fix dhcp initial values 2025-02-14 15:52:36 +03:00
Ildar Kamalov
e7a8db7afd fix encryption form values 2025-02-14 14:37:24 +03:00
Ildar Kamalov
1c8917f7ac fix blocked services submit 2025-02-14 14:07:29 +03:00
Ildar Kamalov
4dfa536cea dns config ip validation 2025-02-14 13:50:47 +03:00
Ildar Kamalov
4fee83fe13 add playwright warning 2025-02-12 17:49:54 +03:00
Ildar Kamalov
8c2f36e7a6 fix config file name 2025-02-11 18:36:18 +03:00
Ildar Kamalov
83db5f33dc temp config file 2025-02-11 16:16:43 +03:00
Ildar Kamalov
9080c1620f update readme 2025-02-11 15:01:46 +03:00
Ildar Kamalov
ee1520307f Merge branch 'master' into ADG-9415 2025-02-11 14:44:06 +03:00
Igor Lobanov
fd12e33c06 added typecheck on build, fixed eslint 2025-02-10 10:29:43 +01:00
Igor Lobanov
b3849eebc4 Merge branch 'ADG-9415' of https://bit.int.agrd.dev/scm/dns/adguard-home into ADG-9415 2025-02-10 09:43:32 +01:00
Ildar Kamalov
9bf3ee128b fix playwright version 2025-02-07 15:58:54 +03:00
Ildar Kamalov
a411421712 install deps 2025-02-07 15:47:25 +03:00
Ildar Kamalov
f8fbf96547 Merge branch 'master' into ADG-9415 2025-02-07 15:00:58 +03:00
Ildar Kamalov
391d577aca update docker frontend version 2025-02-07 12:41:10 +03:00
Ildar Kamalov
0b52ef1d0a Merge branch 'master' into ADG-9415 2025-02-06 12:22:37 +03:00
Ildar Kamalov
8b6d785c72 fix encryption validation 2025-02-05 17:52:45 +03:00
Igor Lobanov
225167a8bb cleanup 2025-02-05 11:35:42 +01:00
Igor Lobanov
6fef2df6b9 do not remove config on local e2e run 2025-02-05 11:18:29 +01:00
Ildar Kamalov
42846cce08 review fix 2025-02-05 11:51:34 +03:00
Igor Lobanov
c3f52d1f84 removed debug code 2025-02-04 09:25:05 +01:00
Igor Lobanov
9c692da2b7 refactor network helper for e2e tests 2025-02-04 09:24:51 +01:00
Igor Lobanov
84130a6419 refactor network helper for e2e tests 2025-02-04 09:18:26 +01:00
Igor Lobanov
55567a33bc adjust test data 2025-02-03 23:38:45 +01:00
Igor Lobanov
3cfc3d2327 debug playwright 2025-02-03 20:14:02 +01:00
Igor Lobanov
15cb5ed90a github node version bump 2025-02-03 19:27:28 +01:00
Igor Lobanov
126f1e2127 removed the rest of ignore flags 2025-02-03 19:23:00 +01:00
Igor Lobanov
4188c4c64e removed ignore platform flag for npm install 2025-02-03 19:11:54 +01:00
Igor Lobanov
14adad7055 removed ignore optional flag for npm install 2025-02-03 19:08:30 +01:00
Igor Lobanov
d58e823c1c fix command 2025-02-03 18:57:10 +01:00
Igor Lobanov
8b49a82ac4 debug 2025-02-03 18:00:22 +01:00
Ildar Kamalov
280c1ab830 docker frontend 2025-02-03 19:47:05 +03:00
Igor Lobanov
b3c05abbdb adjusting playwright install 2025-02-03 13:34:05 +01:00
Igor Lobanov
4c66e04d28 Merge branch 'ADG-9415' of https://bit.int.agrd.dev/scm/dns/adguard-home into ADG-9415 2025-02-03 13:23:40 +01:00
Igor Lobanov
d076ada956 paywright install move to bamboo spec (temp) 2025-02-03 13:22:35 +01:00
Ildar Kamalov
f1008ab7a5 remove unknown command 2025-02-03 15:18:11 +03:00
Ildar Kamalov
e34fc6ac5e remove with deps flag 2025-02-03 15:04:29 +03:00
Ildar Kamalov
f218cbccfd add playwright install checks 2025-02-03 14:52:25 +03:00
Ildar Kamalov
f645ff30bc e2e deps 2025-02-03 14:30:05 +03:00
Ildar Kamalov
5b73b53ac2 add playwright install 2025-02-03 14:18:22 +03:00
Ildar Kamalov
56180d4e40 js-deps 2025-02-03 14:09:25 +03:00
Ildar Kamalov
8d22b72834 add build output 2025-02-03 14:04:47 +03:00
Ildar Kamalov
80fb6013f7 frontend artifact 2025-02-03 14:02:20 +03:00
Ainar Garipov
1fe5fd2ed6 home: imp logs 2025-02-03 13:44:44 +03:00
Ildar Kamalov
5105d35e72 revert process in the background 2025-02-03 13:35:57 +03:00
Ildar Kamalov
d08945811e rm sudo 2025-02-03 13:23:40 +03:00
Ildar Kamalov
f89a24d6e9 run adguard home in the background 2025-02-03 13:16:15 +03:00
Igor Lobanov
2aff577658 debug 2025-01-31 17:45:53 +01:00
Igor Lobanov
5461fa9281 increase timeout 2025-01-31 17:32:19 +01:00
Ildar Kamalov
952c2f4f30 increase timeout 2025-01-31 19:24:29 +03:00
Ildar Kamalov
df20c590d3 increase timeout 2025-01-31 19:13:23 +03:00
Ildar Kamalov
66ac10fa2e fix directory 2025-01-31 19:05:45 +03:00
Ildar Kamalov
c4c58c0eed mv binary to the project root 2025-01-31 18:46:20 +03:00
Ildar Kamalov
215036b4bc mv binary to root 2025-01-31 18:37:02 +03:00
Ildar Kamalov
216ba0fdde stdout pipe 2025-01-31 18:33:39 +03:00
Ildar Kamalov
f5d6c50874 revert artifact name 2025-01-31 18:14:15 +03:00
Ildar Kamalov
dff6ac859a fix file name 2025-01-31 18:01:28 +03:00
Ildar Kamalov
8657483022 try darwin artifact 2025-01-31 17:47:03 +03:00
Ildar Kamalov
275677f494 web server command with sudo 2025-01-31 17:36:39 +03:00
Ildar Kamalov
8a13ca8ab8 list file 2025-01-31 17:06:50 +03:00
Ildar Kamalov
7f9c33db32 fix property name 2025-01-31 16:51:12 +03:00
Ildar Kamalov
58174d0364 use artifact-subscriptions 2025-01-31 16:49:41 +03:00
Ildar Kamalov
bac9991a95 add e2e stage 2025-01-31 16:44:20 +03:00
Ildar Kamalov
86ac5e5567 add ci to process env 2025-01-31 16:16:09 +03:00
Ildar Kamalov
3d6cca333c try to install deps 2025-01-31 16:00:39 +03:00
Ildar Kamalov
236708f2e4 try to add e2e job 2025-01-31 15:37:49 +03:00
Ildar Kamalov
7f85a04257 try to run e2e tests 2025-01-31 12:42:57 +03:00
Ildar Kamalov
e42c051ab7 replace jest with vitest 2025-01-31 11:56:38 +03:00
Ildar Kamalov
77d05e550e fix tests 2025-01-29 18:28:34 +03:00
Ildar Kamalov
885e24496c Merge branch 'master' into ADG-9415 2025-01-29 15:30:03 +03:00
Ildar Kamalov
47216d6c05 encryption 2025-01-28 16:50:30 +03:00
Ildar Kamalov
6cb3d85d01 fix forms 2025-01-27 13:29:07 +03:00
Ildar Kamalov
09b15210ed fix filters form 2025-01-27 10:51:49 +03:00
Ildar Kamalov
5279124130 fix static leases form 2025-01-27 10:47:43 +03:00
Ildar Kamalov
eb9b9b6c2b fix dhcp form 2025-01-24 17:09:54 +03:00
Ildar Kamalov
f78dc10c2a fix forms 2025-01-24 16:42:30 +03:00
Ildar Kamalov
f3f38e1a57 fix login 2025-01-24 15:26:38 +03:00
Ildar Kamalov
9d5042b1f0 fix install types and test ids 2025-01-24 15:15:07 +03:00
Ildar Kamalov
681cdb023e fix form validation mode 2025-01-24 14:49:12 +03:00
Ildar Kamalov
254b25a026 cleanup forms 2025-01-21 16:52:12 +03:00
Ildar Kamalov
70aeee3037 cleanup forms 2025-01-21 16:14:20 +03:00
Ildar Kamalov
edd9bf7b59 cleanup forms 2025-01-21 15:31:08 +03:00
Ildar Kamalov
290987d020 fix client form 2025-01-21 14:27:05 +03:00
Ildar Kamalov
28cd8a41d2 add clients forms 2025-01-21 13:47:40 +03:00
Ildar Kamalov
efd907216f add DHCP form 2025-01-20 10:54:13 +03:00
Ildar Kamalov
93890c2c6f add encryption form and common components 2025-01-18 19:21:20 +03:00
Ildar Kamalov
b4aa411826 fix checkbox field 2025-01-17 16:54:55 +03:00
Ildar Kamalov
bcf5fb2521 use common checkbox component 2025-01-17 16:36:39 +03:00
Ildar Kamalov
92c004d15d filters form 2025-01-15 16:42:26 +03:00
Ildar Kamalov
c389d88aae add filters config form 2025-01-15 15:20:16 +03:00
Ildar Kamalov
657c047784 fix install and devices 2025-01-15 12:19:25 +03:00
Ildar Kamalov
87fe4dfb7f static lease form 2025-01-15 11:53:26 +03:00
Ildar Kamalov
ddc1f73554 logs filter form 2025-01-14 18:45:29 +03:00
Ildar Kamalov
aeab662335 blocked services form 2025-01-14 16:20:51 +03:00
Ildar Kamalov
8b40fe97a6 stats config form 2025-01-14 15:17:00 +03:00
Ildar Kamalov
d0dc0c600d logs config form 2025-01-14 14:11:38 +03:00
Ildar Kamalov
61cba0e212 add mobile config form 2025-01-13 16:57:12 +03:00
dmitrii
b538096ab0 update components to use react-hook-form 2024-12-24 14:37:52 +03:00
dmitrii
914affe0c0 update components to use react-hook-form 2024-12-24 13:55:54 +03:00
dmitrii
da808bfcbc move login to react-hook-form 2024-12-19 12:43:35 +03:00
Ildar Kamalov
8e2dea267c rewrite form 2024-12-12 17:01:30 +03:00
Ildar Kamalov
77420d8c96 filter check form 2024-12-12 15:21:57 +03:00
Ildar Kamalov
81f66c5b9f fix import 2024-12-12 15:09:28 +03:00
Ildar Kamalov
0a1739df3b install forms 2024-12-12 15:08:42 +03:00
Igor Lobanov
8e43af21d9 first playwrite test 2024-12-11 13:10:47 +01:00
169 changed files with 4090 additions and 4936 deletions

View File

@@ -1,8 +1,8 @@
'name': 'build' 'name': 'build'
'env': 'env':
'GO_VERSION': '1.24.2' 'GO_VERSION': '1.23.6'
'NODE_VERSION': '20' 'NODE_VERSION': '18'
'on': 'on':
'push': 'push':

View File

@@ -1,7 +1,7 @@
'name': 'lint' 'name': 'lint'
'env': 'env':
'GO_VERSION': '1.24.2' 'GO_VERSION': '1.23.6'
'on': 'on':
'push': 'push':

View File

@@ -9,116 +9,25 @@ The format is based on [*Keep a Changelog*](https://keepachangelog.com/en/1.0.0/
<!-- <!--
## [v0.108.0] TBA ## [v0.108.0] TBA
## [v0.107.61] - 2025-04-22 (APPROX.) ## [v0.107.58] - 2025-03-11 (APPROX.)
See also the [v0.107.61 GitHub milestone][ms-v0.107.61]. See also the [v0.107.58 GitHub milestone][ms-v0.107.58].
[ms-v0.107.61]: https://github.com/AdguardTeam/AdGuardHome/milestone/96?closed=1 [ms-v0.107.58]: https://github.com/AdguardTeam/AdGuardHome/milestone/93?closed=1
NOTE: Add new changes BELOW THIS COMMENT. NOTE: Add new changes BELOW THIS COMMENT.
--> -->
### Security ### Fixed
- Any simultaneous requests that are considered duplicates will now only result in a single request to upstreams, reducing the chance of a cache poisoning attack succeeding. This is controlled by the new configuration object `pending_requests`, which has a single `enabled` property, set to `true` by default. - The formatting of large numbers in the clients tables on the *Client settings* page ([#7583]).
**NOTE:** We thank [Xiang Li][mr-xiang-li] for reporting this security issue. It's strongly recommended to leave it enabled, otherwise AdGuard Home will be vulnerable to untrusted clients. [#7583]: https://github.com/AdguardTeam/AdGuardHome/issues/7583
[mr-xiang-li]: https://lixiang521.com/
<!-- <!--
NOTE: Add new changes ABOVE THIS COMMENT. NOTE: Add new changes ABOVE THIS COMMENT.
--> -->
## [v0.107.60] - 2025-04-14
See also the [v0.107.60 GitHub milestone][ms-v0.107.60].
### Security
- Go version has been updated to prevent the possibility of exploiting the Go vulnerabilities fixed in [1.24.2][go-1.24.2].
### Changed
- Alpine Linux version in `Dockerfile` has been updated to 3.21 ([#7588]).
### Deprecated
- Node 20 support, Node 22 will be required in future releases.
**NOTE:** `npm` may be replaced with a different tool, such as `pnpm` or `yarn`, in a future release.
### Fixed
- Filtering for DHCP clients ([#7734]).
- Incorrect label on login page ([#7729]).
- Validation process for the HTTPS port on the *Encryption Settings* page.
### Removed
- Node 18 support.
[#7588]: https://github.com/AdguardTeam/AdGuardHome/issues/7588
[#7729]: https://github.com/AdguardTeam/AdGuardHome/issues/7729
[#7734]: https://github.com/AdguardTeam/AdGuardHome/issues/7734
[go-1.24.2]: https://groups.google.com/g/golang-announce/c/Y2uBTVKjBQk
[ms-v0.107.60]: https://github.com/AdguardTeam/AdGuardHome/milestone/95?closed=1
## [v0.107.59] - 2025-03-21
See also the [v0.107.59 GitHub milestone][ms-v0.107.59].
### Fixed
- Validation process for the DNS-over-TLS, DNS-over-QUIC, and HTTPS ports on the *Encryption Settings* page.
- Rules with the `client` modifier not working ([#7708]).
- The search form not working in the query log ([#7704]).
[#7704]: https://github.com/AdguardTeam/AdGuardHome/issues/7704
[#7708]: https://github.com/AdguardTeam/AdGuardHome/issues/7708
[ms-v0.107.59]: https://github.com/AdguardTeam/AdGuardHome/milestone/94?closed=1
## [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 ## [v0.107.57] - 2025-02-20
See also the [v0.107.57 GitHub milestone][ms-v0.107.57]. See also the [v0.107.57 GitHub milestone][ms-v0.107.57].
@@ -138,7 +47,6 @@ See also the [v0.107.57 GitHub milestone][ms-v0.107.57].
### Fixed ### Fixed
- The hostnames of DHCP clients not being shown in the *Top clients* table on the dashboard ([#7627]). - 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]). - The formatting of large numbers in the upstream table and query log ([#7590]).
[#7590]: https://github.com/AdguardTeam/AdGuardHome/issues/7590 [#7590]: https://github.com/AdguardTeam/AdGuardHome/issues/7590
@@ -3115,14 +3023,11 @@ 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 [ms-v0.104.2]: https://github.com/AdguardTeam/AdGuardHome/milestone/28?closed=1
<!-- <!--
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.61...HEAD [Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.58...HEAD
[v0.107.61]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.60...v0.107.61 [v0.107.58]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.57...v0.107.58
--> -->
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.60...HEAD [Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.57...HEAD
[v0.107.60]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.59...v0.107.60
[v0.107.59]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.58...v0.107.59
[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.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.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 [v0.107.55]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.54...v0.107.55

View File

@@ -27,7 +27,7 @@ DIST_DIR = dist
GOAMD64 = v1 GOAMD64 = v1
GOPROXY = https://proxy.golang.org|direct GOPROXY = https://proxy.golang.org|direct
GOTELEMETRY = off GOTELEMETRY = off
GOTOOLCHAIN = go1.24.2 GOTOOLCHAIN = go1.23.6
GPG_KEY = devteam@adguard.com GPG_KEY = devteam@adguard.com
GPG_KEY_PASSPHRASE = not-a-real-password GPG_KEY_PASSPHRASE = not-a-real-password
NPM = npm NPM = npm
@@ -38,6 +38,7 @@ REVISION = $${REVISION:-$$(git rev-parse --short HEAD)}
SIGN = 1 SIGN = 1
SIGNER_API_KEY = not-a-real-key SIGNER_API_KEY = not-a-real-key
VERSION = v0.0.0 VERSION = v0.0.0
YARN = yarn
NEXTAPI = 0 NEXTAPI = 0
@@ -138,4 +139,5 @@ txt-lint: ; $(ENV) "$(SHELL)" ./scripts/make/txt-lint.sh
md-lint: ; $(ENV_MISC) "$(SHELL)" ./scripts/make/md-lint.sh md-lint: ; $(ENV_MISC) "$(SHELL)" ./scripts/make/md-lint.sh
sh-lint: ; $(ENV_MISC) "$(SHELL)" ./scripts/make/sh-lint.sh sh-lint: ; $(ENV_MISC) "$(SHELL)" ./scripts/make/sh-lint.sh
# TODO(a.garipov): Re-add openapi-lint. openapi-lint: ; cd ./openapi/ && $(YARN) test
openapi-show: ; cd ./openapi/ && $(YARN) start

View File

@@ -205,9 +205,9 @@ Run `make init` to prepare the development environment.
You will need this to build AdGuard Home: You will need this to build AdGuard Home:
- [Go](https://golang.org/dl/) v1.24 or later; - [Go](https://golang.org/dl/) v1.23 or later;
- [Node.js](https://nodejs.org/en/download/) v20.19 or later; - [Node.js](https://nodejs.org/en/download/) v18.18 or later;
- [npm](https://www.npmjs.com/) v10.8 or later; - [npm](https://www.npmjs.com/) v8 or later;
### <a href="#building" id="building" name="building">Building</a> ### <a href="#building" id="building" name="building">Building</a>

View File

@@ -7,8 +7,8 @@
# Make sure to sync any changes with the branch overrides below. # Make sure to sync any changes with the branch overrides below.
'variables': 'variables':
'channel': 'edge' 'channel': 'edge'
'dockerFrontend': 'adguard/home-js-builder:3.1' 'dockerFrontend': 'adguard/home-js-builder:2.1-bullseye'
'dockerGo': 'adguard/go-builder:1.24.2--1' 'dockerGo': 'adguard/go-builder:1.23.6--1'
'stages': 'stages':
- 'Build frontend': - 'Build frontend':
@@ -50,7 +50,7 @@
'docker': 'docker':
'image': '${bamboo.dockerFrontend}' 'image': '${bamboo.dockerFrontend}'
'volumes': 'volumes':
'${system.NPM_DIR}': '${bamboo.cacheNpm}' '${system.YARN_DIR}': '${bamboo.cacheYarn}'
'key': 'BF' 'key': 'BF'
'other': 'other':
'clean-working-dir': true 'clean-working-dir': true
@@ -157,7 +157,6 @@
# Print Docker info. # Print Docker info.
docker info docker info
docker buildx version
# Prepare and push the build. # Prepare and push the build.
env \ env \
@@ -278,8 +277,8 @@
# need to build a few of these. # need to build a few of these.
'variables': 'variables':
'channel': 'beta' 'channel': 'beta'
'dockerFrontend': 'adguard/home-js-builder:3.1' 'dockerFrontend': 'adguard/home-js-builder:2.1-bullseye'
'dockerGo': 'adguard/go-builder:1.24.2--1' 'dockerGo': 'adguard/go-builder:1.23.6--1'
# release-vX.Y.Z branches are the branches from which the actual final # release-vX.Y.Z branches are the branches from which the actual final
# release is built. # release is built.
- '^release-v[0-9]+\.[0-9]+\.[0-9]+': - '^release-v[0-9]+\.[0-9]+\.[0-9]+':
@@ -294,5 +293,5 @@
# are the ones that actually get released. # are the ones that actually get released.
'variables': 'variables':
'channel': 'release' 'channel': 'release'
'dockerFrontend': 'adguard/home-js-builder:3.1' 'dockerFrontend': 'adguard/home-js-builder:2.1-bullseye'
'dockerGo': 'adguard/go-builder:1.24.2--1' 'dockerGo': 'adguard/go-builder:1.23.6--1'

View File

@@ -5,8 +5,8 @@
'key': 'AHBRTSPECS' 'key': 'AHBRTSPECS'
'name': 'AdGuard Home - Build and run tests' 'name': 'AdGuard Home - Build and run tests'
'variables': 'variables':
'dockerFrontend': 'adguard/home-js-builder:3.1' 'dockerFrontend': 'adguard/home-js-builder:2.1-bullseye'
'dockerGo': 'adguard/go-builder:1.24.2--1' 'dockerGo': 'adguard/go-builder:1.23.6--1'
'channel': 'development' 'channel': 'development'
'stages': 'stages':
@@ -39,7 +39,7 @@
'docker': 'docker':
'image': '${bamboo.dockerFrontend}' 'image': '${bamboo.dockerFrontend}'
'volumes': 'volumes':
'${system.NPM_DIR}': '${bamboo.cacheNpm}' '${system.YARN_DIR}': '${bamboo.cacheYarn}'
'key': 'JSTEST' 'key': 'JSTEST'
'other': 'other':
'clean-working-dir': true 'clean-working-dir': true
@@ -103,7 +103,7 @@
'docker': 'docker':
'image': '${bamboo.dockerFrontend}' 'image': '${bamboo.dockerFrontend}'
'volumes': 'volumes':
'${system.NPM_DIR}': '${bamboo.cacheNpm}' '${system.YARN_DIR}': '${bamboo.cacheYarn}'
'key': 'BF' 'key': 'BF'
'other': 'other':
'clean-working-dir': true 'clean-working-dir': true
@@ -178,7 +178,7 @@
'docker': 'docker':
'image': '${bamboo.dockerFrontend}' 'image': '${bamboo.dockerFrontend}'
'volumes': 'volumes':
'${system.NPM_DIR}': '${bamboo.cacheNpm}' '${system.YARN_DIR}': '${bamboo.cacheYarn}'
'key': 'E2ETEST' 'key': 'E2ETEST'
'other': 'other':
'clean-working-dir': true 'clean-working-dir': true
@@ -233,6 +233,6 @@
# Set the default release channel on the release branch to beta, as we # Set the default release channel on the release branch to beta, as we
# may need to build a few of these. # may need to build a few of these.
'variables': 'variables':
'dockerFrontend': 'adguard/home-js-builder:3.1' 'dockerFrontend': 'adguard/home-js-builder:2.1-bullseye'
'dockerGo': 'adguard/go-builder:1.24.2--1' 'dockerGo': 'adguard/go-builder:1.23.6--1'
'channel': 'candidate' 'channel': 'candidate'

1207
client/package-lock.json generated vendored

File diff suppressed because it is too large Load Diff

4
client/package.json vendored
View File

@@ -66,7 +66,7 @@
"@babel/preset-react": "^7.24.1", "@babel/preset-react": "^7.24.1",
"@playwright/test": "1.50.1", "@playwright/test": "1.50.1",
"@types/lodash": "^4.17.4", "@types/lodash": "^4.17.4",
"@types/node": "^22.13.10", "@types/node": "^22.10.2",
"@types/react": "^17.0.80", "@types/react": "^17.0.80",
"@types/react-dom": "^18.3.0", "@types/react-dom": "^18.3.0",
"@types/react-redux": "^7.1.33", "@types/react-redux": "^7.1.33",
@@ -99,7 +99,7 @@
"stylelint": "^16.5.0", "stylelint": "^16.5.0",
"ts-loader": "^9.5.1", "ts-loader": "^9.5.1",
"url-loader": "^4.1.1", "url-loader": "^4.1.1",
"vitest": "^3.1.1", "vitest": "^3.0.4",
"webpack": "^5.91.0", "webpack": "^5.91.0",
"webpack-cli": "^5.1.4", "webpack-cli": "^5.1.4",
"webpack-dev-server": "^5.0.4", "webpack-dev-server": "^5.0.4",

View File

@@ -45,7 +45,6 @@
"filter": "Филтър", "filter": "Филтър",
"query_log": "История на заявките", "query_log": "История на заявките",
"compact": "Compact", "compact": "Compact",
"nothing_found": "Нищо не е намерено",
"faq": "ЧЗВ", "faq": "ЧЗВ",
"version": "версия", "version": "версия",
"address": "Адрес", "address": "Адрес",
@@ -66,12 +65,14 @@
"stats_malware_phishing": "вируси/атаки", "stats_malware_phishing": "вируси/атаки",
"stats_adult": "сайтове за възрастни", "stats_adult": "сайтове за възрастни",
"stats_query_domain": "Най-отваряни страници", "stats_query_domain": "Най-отваряни страници",
"for_last_24_hours": "за последните 24 часа",
"no_domains_found": "Няма намерени резултати", "no_domains_found": "Няма намерени резултати",
"requests_count": "Сума на заявките", "requests_count": "Сума на заявките",
"top_blocked_domains": "Най-блокирани страници", "top_blocked_domains": "Най-блокирани страници",
"top_clients": "Най-активни IP адреси", "top_clients": "Най-активни IP адреси",
"no_clients_found": "Нямa намерени адреси", "no_clients_found": "Нямa намерени адреси",
"general_statistics": "Обща статисика", "general_statistics": "Обща статисика",
"number_of_dns_query_24_hours": "Сума на DNS заявки за последните 24 часа",
"number_of_dns_query_blocked_24_hours": "Сума на блокирани DNS заявки от филтрите за реклама и местни", "number_of_dns_query_blocked_24_hours": "Сума на блокирани DNS заявки от филтрите за реклама и местни",
"number_of_dns_query_blocked_24_hours_by_sec": "Сума на блокирани DNS заявки от AdGuard свързани със сигурността", "number_of_dns_query_blocked_24_hours_by_sec": "Сума на блокирани DNS заявки от AdGuard свързани със сигурността",
"number_of_dns_query_blocked_24_hours_adult": "Сума на блокирани сайтове за възрастни", "number_of_dns_query_blocked_24_hours_adult": "Сума на блокирани сайтове за възрастни",
@@ -155,7 +156,6 @@
"rule_added_to_custom_filtering_toast": "Добавено до местни правила за филтриране: {{rule}}", "rule_added_to_custom_filtering_toast": "Добавено до местни правила за филтриране: {{rule}}",
"default": "По подразбиране", "default": "По подразбиране",
"custom_ip": "Персонализиран IP", "custom_ip": "Персонализиран IP",
"dnscrypt": "DNSCrypt",
"dns_over_https": "DNS-пред-HTTPS", "dns_over_https": "DNS-пред-HTTPS",
"dns_over_quic": "DNS-over-QUIC", "dns_over_quic": "DNS-over-QUIC",
"plain_dns": "Обикновен DNS", "plain_dns": "Обикновен DNS",

View File

@@ -620,10 +620,6 @@
"check_cname": "CNAME: {{cname}}", "check_cname": "CNAME: {{cname}}",
"check_reason": "Důvod: {{reason}}", "check_reason": "Důvod: {{reason}}",
"check_service": "Název služby: {{service}}", "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", "service_name": "Název služby",
"check_not_found": "Nenalezeno ve Vašich seznamech filtrů", "check_not_found": "Nenalezeno ve Vašich seznamech filtrů",
"client_confirm_block": "Opravdu chcete zablokovat klienta „{{ip}}“?", "client_confirm_block": "Opravdu chcete zablokovat klienta „{{ip}}“?",

View File

@@ -620,10 +620,6 @@
"check_cname": "CNAME: {{cname}}", "check_cname": "CNAME: {{cname}}",
"check_reason": "Årsag: {{reason}}", "check_reason": "Årsag: {{reason}}",
"check_service": "Tjenestenavn: {{service}}", "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", "service_name": "Tjenestenavn",
"check_not_found": "Ikke fundet i dine filterlister", "check_not_found": "Ikke fundet i dine filterlister",
"client_confirm_block": "Sikker på, at du vil blokere klienten \"{{ip}}\"?", "client_confirm_block": "Sikker på, at du vil blokere klienten \"{{ip}}\"?",

View File

@@ -97,9 +97,9 @@
"settings": "Einstellungen", "settings": "Einstellungen",
"filters": "Filter", "filters": "Filter",
"filter": "Filter", "filter": "Filter",
"query_log": "Abfrageprotokoll", "query_log": "Anfragenprotokoll",
"compact": "Kompakt", "compact": "Kompakt",
"nothing_found": "Nichts gefunden", "nothing_found": "Nichts gefunden\n",
"faq": "FAQ", "faq": "FAQ",
"version": "Version", "version": "Version",
"address": "Adresse", "address": "Adresse",
@@ -199,7 +199,7 @@
"cancel_btn": "Abbrechen", "cancel_btn": "Abbrechen",
"enter_name_hint": "Name eingeben", "enter_name_hint": "Name eingeben",
"enter_url_or_path_hint": "URL oder absoluten Pfad der Liste eingeben", "enter_url_or_path_hint": "URL oder absoluten Pfad der Liste eingeben",
"check_updates_btn": "Nach Updates suchen", "check_updates_btn": "Nach Aktualisierungen suchen",
"new_blocklist": "Neue Sperrliste", "new_blocklist": "Neue Sperrliste",
"new_allowlist": "Neue Positivliste", "new_allowlist": "Neue Positivliste",
"edit_blocklist": "Sperrliste bearbeiten", "edit_blocklist": "Sperrliste bearbeiten",
@@ -566,7 +566,7 @@
"ignore_domains": "Ignorierte Domains (durch Zeilenumbruch getrennt)", "ignore_domains": "Ignorierte Domains (durch Zeilenumbruch getrennt)",
"ignore_domains_title": "Ignorierte Domains", "ignore_domains_title": "Ignorierte Domains",
"ignore_domains_desc_stats": "Abfragen, die diesen Regeln entsprechen, werden nicht in die Statistik aufgenommen", "ignore_domains_desc_stats": "Abfragen, die diesen Regeln entsprechen, werden nicht in die Statistik aufgenommen",
"ignore_domains_desc_query": "Abfragen, die diesen Regeln entsprechen, werden nicht in das Abfrageprotokoll aufgenommen", "ignore_domains_desc_query": "Abfragen, die diesen Regeln entsprechen, werden nicht in das Anfragenprotokoll aufgenommen",
"interval_hours": "{{count}} Stunde", "interval_hours": "{{count}} Stunde",
"interval_hours_plural": "{{count}} Stunden", "interval_hours_plural": "{{count}} Stunden",
"filters_configuration": "Filterkonfiguration", "filters_configuration": "Filterkonfiguration",
@@ -620,10 +620,6 @@
"check_cname": "CNAME: {{cname}}", "check_cname": "CNAME: {{cname}}",
"check_reason": "Grund: {{reason}}", "check_reason": "Grund: {{reason}}",
"check_service": "Dienstname: {{service}}", "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", "service_name": "Name des Dienstes",
"check_not_found": "Nicht in Ihren Filterlisten enthalten", "check_not_found": "Nicht in Ihren Filterlisten enthalten",
"client_confirm_block": "Möchten Sie den Client „{{ip}}“ wirklich sperren?", "client_confirm_block": "Möchten Sie den Client „{{ip}}“ wirklich sperren?",

View File

@@ -620,10 +620,6 @@
"check_cname": "CNAME: {{cname}}", "check_cname": "CNAME: {{cname}}",
"check_reason": "Reason: {{reason}}", "check_reason": "Reason: {{reason}}",
"check_service": "Service name: {{service}}", "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", "service_name": "Service name",
"check_not_found": "Not found in your filter lists", "check_not_found": "Not found in your filter lists",
"client_confirm_block": "Are you sure you want to block the client \"{{ip}}\"?", "client_confirm_block": "Are you sure you want to block the client \"{{ip}}\"?",

View File

@@ -1,16 +1,16 @@
{ {
"client_settings": "Configuración de clientes", "client_settings": "Configuración de clientes",
"example_upstream_reserved": "un proveedor DNS <0>para un dominio específico</0>.", "example_upstream_reserved": "un DNS de subida <0>para un dominio específico</0>.",
"example_multiple_upstreams_reserved": "múltiples proveedores DNS <0>para dominios específicos</0>.", "example_multiple_upstreams_reserved": "múltiples upstreams <0>para dominios específicos</0>;",
"example_upstream_comment": "un comentario.", "example_upstream_comment": "un comentario.",
"upstream_parallel": "Usar consultas paralelas para acelerar la resolución al consultar simultáneamente a todos los proveedores DNS.", "upstream_parallel": "Usar consultas paralelas para acelerar la resolución al consultar simultáneamente a todos los servidores DNS de subida.",
"parallel_requests": "Consultas paralelas", "parallel_requests": "Consultas paralelas",
"load_balancing": "Balanceo de carga", "load_balancing": "Balanceo de carga",
"load_balancing_desc": "Consulta un proveedor DNS a la vez.<br/>AdGuard Home utiliza un algoritmo aleatorio ponderado para seleccionar los servidores con el menor número de fallos y el menor tiempo promedio de búsqueda.", "load_balancing_desc": "Consulta un servidor Dns upstream a la vez.<br/>AdGuard Home utiliza un algoritmo aleatorio ponderado para seleccionar los servidores con el menor número de fallos y el menor tiempo medio de búsqueda.",
"bootstrap_dns": "Servidores DNS de arranque", "bootstrap_dns": "Servidores DNS de arranque",
"bootstrap_dns_desc": "Direcciones IP de los servidores DNS utilizados para resolver las direcciones IP de los resolutores DoH/DoT que especifiques como proveedores DNS. No se permiten comentarios.", "bootstrap_dns_desc": "Direcciones IP de servidores DNS utilizadas para resolver direcciones IP de los solucionadores DoH/DoT que especifiques como ascendentes. No se permiten comentarios.",
"fallback_dns_title": "Servidores DNS alternativos", "fallback_dns_title": "Servidores DNS alternativos",
"fallback_dns_desc": "Lista de servidores DNS alternativos utilizados cuando los proveedores DNS no responden. La sintaxis es la misma que en el campo de los principales proveedores DNS anterior.", "fallback_dns_desc": "Lista de servidores DNS alternativos utilizados cuando los servidores DNS de subida no responden. La sintaxis es la misma que en el campo de los principales DNS de subida anterior.",
"fallback_dns_placeholder": "Ingresa un servidor DNS alternativo por línea", "fallback_dns_placeholder": "Ingresa un servidor DNS alternativo por línea",
"local_ptr_title": "Servidores DNS inversos y privados", "local_ptr_title": "Servidores DNS inversos y privados",
"local_ptr_desc": "Los servidores DNS que AdGuard Home utiliza para consultas PTR, SOA y NS privadas. La petición se considera privada si solicita un dominio ARPA que contiene una subred dentro de rangos IP privados, por ejemplo \"192.168.12.34\", y procede de un cliente con dirección privada. Si no se configura, AdGuard Home utiliza las direcciones de los resolvedores DNS predeterminados de tu sistema operativo, excepto las direcciones del propio AdGuard Home.", "local_ptr_desc": "Los servidores DNS que AdGuard Home utiliza para consultas PTR, SOA y NS privadas. La petición se considera privada si solicita un dominio ARPA que contiene una subred dentro de rangos IP privados, por ejemplo \"192.168.12.34\", y procede de un cliente con dirección privada. Si no se configura, AdGuard Home utiliza las direcciones de los resolvedores DNS predeterminados de tu sistema operativo, excepto las direcciones del propio AdGuard Home.",
@@ -18,9 +18,9 @@
"local_ptr_no_default_resolver": "AdGuard Home no pudo determinar los resolutores DNS inversos y privados adecuados para este sistema.", "local_ptr_no_default_resolver": "AdGuard Home no pudo determinar los resolutores DNS inversos y privados adecuados para este sistema.",
"local_ptr_placeholder": "Ingresa una dirección IP por línea", "local_ptr_placeholder": "Ingresa una dirección IP por línea",
"resolve_clients_title": "Habilitar la resolución inversa de las direcciones IP de clientes", "resolve_clients_title": "Habilitar la resolución inversa de las direcciones IP de clientes",
"resolve_clients_desc": "Resuelve de manera inversa las direcciones IP de los clientes a sus nombres de hosts enviando consultas PTR a los resolutores correspondientes (servidores DNS privados para clientes locales, proveedores DNS para clientes con direcciones IP públicas).", "resolve_clients_desc": "Resolve de manera inversa las direcciones IP de los clientes a sus nombres de hosts enviando consultas PTR a los resolutores correspondientes (servidores DNS privados para clientes locales, servidores DNS de subida para clientes con direcciones IP públicas).",
"use_private_ptr_resolvers_title": "Usar resolutores DNS inversos y privados", "use_private_ptr_resolvers_title": "Usar resolutores DNS inversos y privados",
"use_private_ptr_resolvers_desc": "Resuelve peticiones PTR, SOA y NS para dominios ARPA que contienen direcciones IP privadas a través de proveedores DNS privados, DHCP, /etc/hosts, etc. Si se deshabilita, AdGuard Home responde a todas estas peticiones con NXDOMAIN.", "use_private_ptr_resolvers_desc": "Resolver las peticiones PTR, SOA y NS para dominios ARPA que contienen direcciones privadas utilizando servidores upstream privados, DHCP, /etc/hosts, etc. Si se desactiva, AdGuard Home responde a todas estas consultas con NXDOMAIN.",
"check_dhcp_servers": "Comprobar si hay servidores DHCP", "check_dhcp_servers": "Comprobar si hay servidores DHCP",
"save_config": "Guardar configuración", "save_config": "Guardar configuración",
"enabled_dhcp": "Servidor DHCP habilitado", "enabled_dhcp": "Servidor DHCP habilitado",
@@ -132,8 +132,8 @@
"top_clients": "Clientes más frecuentes", "top_clients": "Clientes más frecuentes",
"no_clients_found": "No se han encontrado clientes", "no_clients_found": "No se han encontrado clientes",
"general_statistics": "Estadísticas generales", "general_statistics": "Estadísticas generales",
"top_upstreams": "Proveedores DNS más frecuentes", "top_upstreams": "DNS de subida más frecuentes",
"no_upstreams_data_found": "No se han encontrado datos de proveedores DNS", "no_upstreams_data_found": "No se han encontrado datos de DNS de subida",
"number_of_dns_query_days": "Número de consultas DNS procesadas durante el último {{count}} día", "number_of_dns_query_days": "Número de consultas DNS procesadas durante el último {{count}} día",
"number_of_dns_query_days_plural": "Número de consultas DNS procesadas durante los últimos {{count}} días", "number_of_dns_query_days_plural": "Número de consultas DNS procesadas durante los últimos {{count}} días",
"number_of_dns_query_hours": "Número de consultas DNS procesadas durante la última {{count}} hora", "number_of_dns_query_hours": "Número de consultas DNS procesadas durante la última {{count}} hora",
@@ -144,7 +144,7 @@
"enforced_save_search": "Búsquedas seguras forzadas", "enforced_save_search": "Búsquedas seguras forzadas",
"number_of_dns_query_to_safe_search": "Número de peticiones DNS a los motores de búsqueda para los que se aplicó la búsqueda segura forzada", "number_of_dns_query_to_safe_search": "Número de peticiones DNS a los motores de búsqueda para los que se aplicó la búsqueda segura forzada",
"average_processing_time": "Tiempo promedio de procesamiento", "average_processing_time": "Tiempo promedio de procesamiento",
"average_upstream_response_time": "Tiempo promedio de respuesta del proveedor DNS", "average_upstream_response_time": "Tiempo promedio de respuesta upstream",
"response_time": "Tiempo de respuesta", "response_time": "Tiempo de respuesta",
"average_processing_time_hint": "Tiempo promedio en milisegundos al procesar una petición DNS", "average_processing_time_hint": "Tiempo promedio en milisegundos al procesar una petición DNS",
"block_domain_use_filters_and_hosts": "Bloquear dominios usando filtros y archivos hosts", "block_domain_use_filters_and_hosts": "Bloquear dominios usando filtros y archivos hosts",
@@ -157,7 +157,7 @@
"enforce_save_search_hint": "AdGuard Home reforzará la búsqueda segura en los siguientes motores de búsqueda: Google, YouTube, Bing, DuckDuckGo, Ecosia, Yandex y Pixabay.", "enforce_save_search_hint": "AdGuard Home reforzará la búsqueda segura en los siguientes motores de búsqueda: Google, YouTube, Bing, DuckDuckGo, Ecosia, Yandex y Pixabay.",
"no_servers_specified": "No hay servidores especificados", "no_servers_specified": "No hay servidores especificados",
"general_settings": "Configuración general", "general_settings": "Configuración general",
"dns_settings": "Configuración DNS", "dns_settings": "Configuración del DNS",
"dns_blocklists": "Listas de bloqueo DNS", "dns_blocklists": "Listas de bloqueo DNS",
"dns_allowlists": "Listas de permitido DNS", "dns_allowlists": "Listas de permitido DNS",
"dns_blocklists_desc": "AdGuard Home bloqueará los dominios que coincidan con las listas de bloqueo.", "dns_blocklists_desc": "AdGuard Home bloqueará los dominios que coincidan con las listas de bloqueo.",
@@ -165,12 +165,12 @@
"custom_filtering_rules": "Reglas de filtrado personalizado", "custom_filtering_rules": "Reglas de filtrado personalizado",
"encryption_settings": "Configuración de cifrado", "encryption_settings": "Configuración de cifrado",
"dhcp_settings": "Configuración DHCP", "dhcp_settings": "Configuración DHCP",
"upstream_dns": "Proveedores DNS", "upstream_dns": "Servidores DNS de subida",
"upstream_dns_help": "Ingresa una dirección de servidor por línea. <a>Más información</a> sobre la configuración de los proveedores DNS.", "upstream_dns_help": "Ingresa una dirección de servidor por línea. <a>Más información</a> sobre la configuración de los servidores DNS de subida.",
"upstream_dns_configured_in_file": "Configurado en {{path}}", "upstream_dns_configured_in_file": "Configurado en {{path}}",
"test_upstream_btn": "Probar proveedores DNS", "test_upstream_btn": "Probar DNS de subida",
"upstreams": "Proveedores DNS", "upstreams": "DNS de subida",
"upstream": "Proveedor DNS", "upstream": "DNS de subida",
"apply_btn": "Aplicar", "apply_btn": "Aplicar",
"disabled_filtering_toast": "Filtrado deshabilitado", "disabled_filtering_toast": "Filtrado deshabilitado",
"enabled_filtering_toast": "Filtrado habilitado", "enabled_filtering_toast": "Filtrado habilitado",
@@ -233,11 +233,11 @@
"example_upstream_tcp_port": "DNS regular (mediante TCP, con puerto).", "example_upstream_tcp_port": "DNS regular (mediante TCP, con puerto).",
"example_upstream_tcp_hostname": "DNS regular (mediante TCP, nombre del host).", "example_upstream_tcp_hostname": "DNS regular (mediante TCP, nombre del host).",
"all_lists_up_to_date_toast": "Todas las listas ya están actualizadas", "all_lists_up_to_date_toast": "Todas las listas ya están actualizadas",
"updated_upstream_dns_toast": "Proveedores DNS guardados correctamente", "updated_upstream_dns_toast": "Servidores DNS de subida guardados correctamente",
"dns_test_ok_toast": "Los servidores DNS especificados funcionan correctamente", "dns_test_ok_toast": "Los servidores DNS especificados funcionan correctamente",
"dns_test_not_ok_toast": "Servidor \"{{key}}\": no se puede utilizar, por favor revisa si lo has escrito correctamente", "dns_test_not_ok_toast": "Servidor \"{{key}}\": no se puede utilizar, por favor revisa si lo has escrito correctamente",
"dns_test_parsing_error_toast": "No se pudo utilizar la sección {{section}}: línea {{line}}:, verifica si la escribiste correctamente", "dns_test_parsing_error_toast": "No se pudo utilizar la sección {{section}}: línea {{line}}:, verifica si la escribiste correctamente",
"dns_test_warning_toast": "Proveedor DNS \"{{key}}\" no responde a las peticiones de prueba y es posible que no funcione correctamente", "dns_test_warning_toast": "DNS de subida \"{{key}}\" no responde a las peticiones de prueba y es posible que no funcione correctamente",
"unblock": "Desbloquear", "unblock": "Desbloquear",
"block": "Bloquear", "block": "Bloquear",
"disallow_this_client": "No permitir a este cliente", "disallow_this_client": "No permitir a este cliente",
@@ -294,9 +294,9 @@
"blocked_response_ttl": "Respuesta TTL bloqueada", "blocked_response_ttl": "Respuesta TTL bloqueada",
"blocked_response_ttl_desc": "Especifica durante cuántos segundos los clientes deben almacenar en cache una respuesta filtrada", "blocked_response_ttl_desc": "Especifica durante cuántos segundos los clientes deben almacenar en cache una respuesta filtrada",
"form_enter_blocked_response_ttl": "Ingresa el TTL de respuesta bloqueada (segundos)", "form_enter_blocked_response_ttl": "Ingresa el TTL de respuesta bloqueada (segundos)",
"upstream_timeout": "Tiempo de espera del proveedor DNS", "upstream_timeout": "Tiempo de espera del upstream",
"upstream_timeout_desc": "Especifica el número de segundos que se debe esperar para recibir una respuesta del proveedor DNS", "upstream_timeout_desc": "Especifica el número de segundos que se debe esperar para recibir una respuesta del servidor upstream",
"form_enter_upstream_timeout": "Ingresa la duración de tiempo de espera del proveedor DNS en segundos", "form_enter_upstream_timeout": "Ingresa la duración del tiempo de espera del servidor DNS upstream en segundos",
"dnscrypt": "DNSCrypt", "dnscrypt": "DNSCrypt",
"dns_over_https": "DNS mediante HTTPS", "dns_over_https": "DNS mediante HTTPS",
"dns_over_tls": "DNS mediante TLS", "dns_over_tls": "DNS mediante TLS",
@@ -311,7 +311,7 @@
"form_enter_rate_limit": "Ingresa el límite de cantidad", "form_enter_rate_limit": "Ingresa el límite de cantidad",
"rate_limit": "Límite de cantidad", "rate_limit": "Límite de cantidad",
"edns_enable": "Habilitar subred de cliente EDNS", "edns_enable": "Habilitar subred de cliente EDNS",
"edns_cs_desc": "Añade la opción subred de cliente EDNS (ECS) a las peticiones del proveedor DNS y registra los valores enviados por los clientes en el registro de consultas.", "edns_cs_desc": "Añade la opción subred de cliente EDNS (ECS) a las peticiones del DNS de subida y registra los valores enviados por los clientes en el registro de consultas.",
"edns_use_custom_ip": "Usar IP personalizada para EDNS", "edns_use_custom_ip": "Usar IP personalizada para EDNS",
"edns_use_custom_ip_desc": "Permitir el uso de IP personalizadas para EDNS", "edns_use_custom_ip_desc": "Permitir el uso de IP personalizadas para EDNS",
"rate_limit_desc": "Número de peticiones por segundo permitidas por cliente. Establecerlo en 0 significa que no hay límite.", "rate_limit_desc": "Número de peticiones por segundo permitidas por cliente. Establecerlo en 0 significa que no hay límite.",
@@ -335,7 +335,7 @@
"theme_auto": "Auto", "theme_auto": "Auto",
"theme_light": "Claro", "theme_light": "Claro",
"theme_dark": "Oscuro", "theme_dark": "Oscuro",
"upstream_dns_client_desc": "Si se mantiene este campo vacío, AdGuard Home utilizará los servidores configurados en la <0>configuración DNS</0>.", "upstream_dns_client_desc": "Si se mantiene este campo vacío, AdGuard Home utilizará los servidores configurados en la <0>configuración del DNS</0>.",
"tracker_source": "Fuente del rastreador", "tracker_source": "Fuente del rastreador",
"source_label": "Fuente", "source_label": "Fuente",
"found_in_known_domain_db": "Encontrado en la base de datos de dominios conocidos.", "found_in_known_domain_db": "Encontrado en la base de datos de dominios conocidos.",
@@ -596,12 +596,12 @@
"example_rewrite_wildcard": "reescribe las respuestas para todos los subdominios de <0>ejemplo.org</0>.", "example_rewrite_wildcard": "reescribe las respuestas para todos los subdominios de <0>ejemplo.org</0>.",
"rewrite_ip_address": "Dirección IP: utiliza esta IP en una respuesta A o AAAA", "rewrite_ip_address": "Dirección IP: utiliza esta IP en una respuesta A o AAAA",
"rewrite_domain_name": "Nombre de dominio: añade un registro CNAME", "rewrite_domain_name": "Nombre de dominio: añade un registro CNAME",
"rewrite_A": "<0>A</0>: valor especial, mantiene registros <0>A</0> del proveedor DNS", "rewrite_A": "<0>A</0>: valor especial, mantiene registros <0>A</0> del DNS de subida",
"rewrite_AAAA": "<0>AAAA</0>: valor especial, mantiene registros <0>AAAA</0> del proveedor DNS", "rewrite_AAAA": "<0>AAAA</0>: valor especial, mantiene registros <0>AAAA</0> del DNS de subida",
"disable_ipv6": "Deshabilitar resolución de direcciones IPv6", "disable_ipv6": "Deshabilitar resolución de direcciones IPv6",
"disable_ipv6_desc": "Descarta todas las consultas de DNS para direcciones IPv6 (tipo AAAA) y elimina las sugerencias de IPv6 de las respuestas HTTPS.", "disable_ipv6_desc": "Descarta todas las consultas de DNS para direcciones IPv6 (tipo AAAA) y elimina las sugerencias de IPv6 de las respuestas HTTPS.",
"fastest_addr": "Dirección IP más rápida", "fastest_addr": "Dirección IP más rápida",
"fastest_addr_desc": "Espera respuestas de <b>todos</b> los servidores DNS, mide la velocidad de conexión TCP de cada servidor y devuelve la dirección IP del servidor con la velocidad de conexión más rápida.<br/>Este modo puede ralentizar significativamente las consultas DNS, si uno o más proveedores DNS no responden. Asegúrate de que tus proveedores DNS sean estables y de que el tiempo de espera tu proveedor DNS sea bajo.", "fastest_addr_desc": "Espera a que respondan <b>todos</b> los servidores DNS, mide la velocidad de conexión TCP de cada servidor y devuelve la Dirección IP del servidor con la velocidad de conexión más rápida.<br/>Este modo puede ralentizar significativamente las consultas DNS, si uno o más servidores DNS de upstream no están respondiendo. Asegúrate de que tus servidores DNS upstream sean estables y tu tiempo de espera de upstream sea bajo.",
"autofix_warning_text": "Si haces clic en \"Corregir\", AdGuard Home configurará tu sistema para utilizar el servidor DNS de AdGuard Home.", "autofix_warning_text": "Si haces clic en \"Corregir\", AdGuard Home configurará tu sistema para utilizar el servidor DNS de AdGuard Home.",
"autofix_warning_list": "Realizará estas tareas: <0>Deshabilitar el sistema DNSStubListener</0> <0>Establecer la dirección del servidor DNS en 127.0.0.1</0> <0>Reemplazar el destino del enlace simbólico de /etc/resolv.conf por /run/systemd/resolve/resolv.conf</0> <0>Detener DNSStubListener (recargar el servicio systemd-resolved)</0>", "autofix_warning_list": "Realizará estas tareas: <0>Deshabilitar el sistema DNSStubListener</0> <0>Establecer la dirección del servidor DNS en 127.0.0.1</0> <0>Reemplazar el destino del enlace simbólico de /etc/resolv.conf por /run/systemd/resolve/resolv.conf</0> <0>Detener DNSStubListener (recargar el servicio systemd-resolved)</0>",
"autofix_warning_result": "Como resultado, todas las peticiones DNS de tu sistema serán procesadas por AdGuard Home de manera predeterminada.", "autofix_warning_result": "Como resultado, todas las peticiones DNS de tu sistema serán procesadas por AdGuard Home de manera predeterminada.",
@@ -620,10 +620,6 @@
"check_cname": "CNAME: {{cname}}", "check_cname": "CNAME: {{cname}}",
"check_reason": "Razón: {{reason}}", "check_reason": "Razón: {{reason}}",
"check_service": "Nombre del servicio: {{service}}", "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", "service_name": "Nombre del servicio",
"check_not_found": "No se ha encontrado en tus listas de filtros", "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}}\"?", "client_confirm_block": "¿Estás seguro de que deseas bloquear al cliente \"{{ip}}\"?",
@@ -662,7 +658,7 @@
"enter_cache_size": "Ingresa el tamaño de la caché (bytes)", "enter_cache_size": "Ingresa el tamaño de la caché (bytes)",
"enter_cache_ttl_min_override": "Ingresa el TTL mínimo (en segundos)", "enter_cache_ttl_min_override": "Ingresa el TTL mínimo (en segundos)",
"enter_cache_ttl_max_override": "Ingresa el TTL máximo (en segundos)", "enter_cache_ttl_max_override": "Ingresa el TTL máximo (en segundos)",
"cache_ttl_min_override_desc": "Amplía el corto tiempo de vida (segundos) de los valores recibidos del proveedor DNS al almacenar en caché las respuestas DNS.", "cache_ttl_min_override_desc": "Amplía el corto tiempo de vida (segundos) de los valores recibidos del servidor DNS de subida al almacenar en caché las respuestas DNS.",
"cache_ttl_max_override_desc": "Establece un valor de tiempo de vida (segundos) máximo para las entradas en la caché DNS.", "cache_ttl_max_override_desc": "Establece un valor de tiempo de vida (segundos) máximo para las entradas en la caché DNS.",
"ttl_cache_validation": "La anulación TTL mínimo de la caché debe ser menor o igual al máximo", "ttl_cache_validation": "La anulación TTL mínimo de la caché debe ser menor o igual al máximo",
"cache_optimistic": "Caché optimista", "cache_optimistic": "Caché optimista",
@@ -748,7 +744,7 @@
"thursday_short": "Jue.", "thursday_short": "Jue.",
"friday_short": "Vie.", "friday_short": "Vie.",
"saturday_short": "Sáb.", "saturday_short": "Sáb.",
"upstream_dns_cache_configuration": "Configuración de la caché del proveedor DNS", "upstream_dns_cache_configuration": "Configuración de la caché DNS upstream",
"enable_upstream_dns_cache": "Habilitar el almacenamiento en caché del DNS para la configuración personalizada de este cliente", "enable_upstream_dns_cache": "Habilitar el almacenamiento en caché de DNS para la configuración personalizada de este cliente",
"dns_cache_size": "Tamaño de la caché DNS, en bytes" "dns_cache_size": "Tamaño de la caché DNS, en bytes"
} }

View File

@@ -620,10 +620,6 @@
"check_cname": "CNAME : {{cname}}", "check_cname": "CNAME : {{cname}}",
"check_reason": "Raison : {{reason}}", "check_reason": "Raison : {{reason}}",
"check_service": "Nom du service : {{service}}", "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", "service_name": "Nom du service",
"check_not_found": "Introuvable dans vos listes de filtres", "check_not_found": "Introuvable dans vos listes de filtres",
"client_confirm_block": "Voulez-vous vraiment bloquer le client « {{ip}} » ?", "client_confirm_block": "Voulez-vous vraiment bloquer le client « {{ip}} » ?",

View File

@@ -620,10 +620,6 @@
"check_cname": "CNAME: {{cname}}", "check_cname": "CNAME: {{cname}}",
"check_reason": "Motivo: {{reason}}", "check_reason": "Motivo: {{reason}}",
"check_service": "Nome servizio: {{service}}", "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", "service_name": "Nome servizio",
"check_not_found": "Non trovato negli elenchi dei filtri", "check_not_found": "Non trovato negli elenchi dei filtri",
"client_confirm_block": "Sei sicuro di voler bloccare il client \"{{ip}}\"?", "client_confirm_block": "Sei sicuro di voler bloccare il client \"{{ip}}\"?",

View File

@@ -620,10 +620,6 @@
"check_cname": "CNAME: {{cname}}", "check_cname": "CNAME: {{cname}}",
"check_reason": "理由: {{reason}}", "check_reason": "理由: {{reason}}",
"check_service": "サービス名: {{service}}", "check_service": "サービス名: {{service}}",
"check_hostname": "ホスト名またはドメイン名",
"check_client_id": "クライアント識別子 (ClientID または IP アドレス)",
"check_enter_client_id": "クライアント識別子を入力してください",
"check_dns_record": "DNSレコードタイプDNS record typeを選択",
"service_name": "サービス名", "service_name": "サービス名",
"check_not_found": "フィルタ一覧には見つかりません", "check_not_found": "フィルタ一覧には見つかりません",
"client_confirm_block": "クライアント\"{{ip}}\"をブロックしてもよろしいですか?", "client_confirm_block": "クライアント\"{{ip}}\"をブロックしてもよろしいですか?",

View File

@@ -620,10 +620,6 @@
"check_cname": "CNAME: {{cname}}", "check_cname": "CNAME: {{cname}}",
"check_reason": "이유: {{reason}}", "check_reason": "이유: {{reason}}",
"check_service": "서비스 이름: {{service}}", "check_service": "서비스 이름: {{service}}",
"check_hostname": "호스트 이름 또는 도메인 이름",
"check_client_id": "클라이언트 식별자(클라이언트 ID 또는 IP 주소)",
"check_enter_client_id": "클라이언트 식별자 입력",
"check_dns_record": "DNS 레코드 유형 선택",
"service_name": "서비스 이름", "service_name": "서비스 이름",
"check_not_found": "필터 목록에서 찾을 수 없음", "check_not_found": "필터 목록에서 찾을 수 없음",
"client_confirm_block": "정말로 클라이언트 '{{ip}}'을(를) 차단하시겠습니까?", "client_confirm_block": "정말로 클라이언트 '{{ip}}'을(를) 차단하시겠습니까?",

View File

@@ -110,9 +110,9 @@
"homepage": "Startpagina", "homepage": "Startpagina",
"report_an_issue": "Rapporteer een probleem", "report_an_issue": "Rapporteer een probleem",
"privacy_policy": "Privacybeleid", "privacy_policy": "Privacybeleid",
"enable_protection": "Bescherming inschakelen", "enable_protection": "Schakel bescherming in",
"enabled_protection": "Bescherming ingeschakeld", "enabled_protection": "Bescherming ingeschakeld",
"disable_protection": "Bescherming uitschakelen", "disable_protection": "Schakel bescherming uit",
"disabled_protection": "Bescherming uitgeschakeld", "disabled_protection": "Bescherming uitgeschakeld",
"refresh_statics": "Ververs statistieken", "refresh_statics": "Ververs statistieken",
"dns_query": "DNS-queries", "dns_query": "DNS-queries",
@@ -327,10 +327,10 @@
"rate_limit_whitelist_placeholder": "Voer één IP-adres per regel in", "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_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_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_refused": "REFUSED: Antwoorden met REFUSED code",
"blocking_mode_nxdomain": "NXDOMAIN: Reageer met NXDOMAIN code", "blocking_mode_nxdomain": "NXDOMAIN: Reageer met NXDOMAIN code",
"blocking_mode_null_ip": "Nul IP: Reageer met een nul IP-adres (0.0.0.0 voor A; :: voor AAAA)", "blocking_mode_null_ip": "Nul IP: Reageer met een nul IP address (0.0.0.0 voor A; :: voor AAAA)",
"blocking_mode_custom_ip": "Aangepast IP: Reageer met een handmatige ingesteld IP adres", "blocking_mode_custom_ip": "Aangepast IP: Reageer met een handmatige ingesteld IP adres",
"theme_auto": "Automatisch", "theme_auto": "Automatisch",
"theme_light": "Licht", "theme_light": "Licht",
@@ -620,10 +620,6 @@
"check_cname": "CNAME: {{cname}}", "check_cname": "CNAME: {{cname}}",
"check_reason": "Reden: {{reason}}", "check_reason": "Reden: {{reason}}",
"check_service": "Servicenaam: {{service}}", "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", "service_name": "Naam service",
"check_not_found": "Niet in je lijst met filters gevonden", "check_not_found": "Niet in je lijst met filters gevonden",
"client_confirm_block": "Weet je zeker dat je client \"{{ip}}\" wil blokkeren?", "client_confirm_block": "Weet je zeker dat je client \"{{ip}}\" wil blokkeren?",
@@ -702,13 +698,13 @@
"disable_for_hours": "Voor {{count}} uur", "disable_for_hours": "Voor {{count}} uur",
"disable_for_hours_plural": "Voor {{count}} uren", "disable_for_hours_plural": "Voor {{count}} uren",
"disable_until_tomorrow": "Tot morgen", "disable_until_tomorrow": "Tot morgen",
"disable_notify_for_seconds": "Bescherming uitschakelen voor {{count}} seconde", "disable_notify_for_seconds": "Beveiliging uitschakelen voor {{count}} seconde",
"disable_notify_for_seconds_plural": "Bescherming uitschakelen voor {{count}} seconden", "disable_notify_for_seconds_plural": "Beveiliging uitschakelen voor {{count}} seconden",
"disable_notify_for_minutes": "Bescherming uitschakelen voor {{count}} minuut", "disable_notify_for_minutes": "Beveiliging uitschakelen voor {{count}} minuut",
"disable_notify_for_minutes_plural": "Bescherming uitschakelen voor {{count}} minuten", "disable_notify_for_minutes_plural": "Beveiliging uitschakelen voor {{count}} minuten",
"disable_notify_for_hours": "Bescherming uitschakelen voor {{count}} uur", "disable_notify_for_hours": "Beveiliging uitschakelen voor {{count}} uur",
"disable_notify_for_hours_plural": "Bescherming uitschakelen voor {{count}} uren", "disable_notify_for_hours_plural": "Beveiliging uitschakelen voor {{count}} uren",
"disable_notify_until_tomorrow": "Bescherming uitschakelen tot morgen", "disable_notify_until_tomorrow": "Beveiliging uitschakelen tot morgen",
"enable_protection_timer": "Bescherming wordt ingeschakeld over {{time}}", "enable_protection_timer": "Bescherming wordt ingeschakeld over {{time}}",
"custom_retention_input": "Voer retentie in uren in", "custom_retention_input": "Voer retentie in uren in",
"custom_rotation_input": "Voer rotatie in uren in", "custom_rotation_input": "Voer rotatie in uren in",

View File

@@ -106,6 +106,7 @@
"stats_malware_phishing": "Blokkert skadevare/phishing", "stats_malware_phishing": "Blokkert skadevare/phishing",
"stats_adult": "Blokkerte voksennettsteder", "stats_adult": "Blokkerte voksennettsteder",
"stats_query_domain": "Mest forespurte domener", "stats_query_domain": "Mest forespurte domener",
"for_last_24_hours": "de siste 24 timene",
"for_last_days": "for den siste {{count}} dagen", "for_last_days": "for den siste {{count}} dagen",
"for_last_days_plural": "de siste {{count}} dagene", "for_last_days_plural": "de siste {{count}} dagene",
"stats_disabled": "Statistikkene har blitt skrudd av. Du kan skru den på fra <0>innstillingssiden</0>.", "stats_disabled": "Statistikkene har blitt skrudd av. Du kan skru den på fra <0>innstillingssiden</0>.",
@@ -120,6 +121,7 @@
"no_upstreams_data_found": "Ingen oppstrøms servere data funnet", "no_upstreams_data_found": "Ingen oppstrøms servere data funnet",
"number_of_dns_query_days": "Antall DNS-spørringer behandlet for de siste {{count}} dagene", "number_of_dns_query_days": "Antall DNS-spørringer behandlet for de siste {{count}} dagene",
"number_of_dns_query_days_plural": "Antall DNS-forespørsler som ble behandlet de siste {{count}} dagene", "number_of_dns_query_days_plural": "Antall DNS-forespørsler som ble behandlet de siste {{count}} dagene",
"number_of_dns_query_24_hours": "Antall DNS-forespørsler som ble behandlet de siste 24 timene",
"number_of_dns_query_blocked_24_hours": "Antall DNS-forespørsler som ble blokkert av adblock-filtre, hosts-lister, og domene-lister", "number_of_dns_query_blocked_24_hours": "Antall DNS-forespørsler som ble blokkert av adblock-filtre, hosts-lister, og domene-lister",
"number_of_dns_query_blocked_24_hours_by_sec": "Antall DNS-forespørsler som ble blokkert av AdGuard sin nettlesersikkerhetsmodul", "number_of_dns_query_blocked_24_hours_by_sec": "Antall DNS-forespørsler som ble blokkert av AdGuard sin nettlesersikkerhetsmodul",
"number_of_dns_query_blocked_24_hours_adult": "Antall voksennettsteder som ble blokkert", "number_of_dns_query_blocked_24_hours_adult": "Antall voksennettsteder som ble blokkert",
@@ -264,7 +266,6 @@
"custom_ip": "Tilpasset IP", "custom_ip": "Tilpasset IP",
"blocking_ipv4": "IPv4-blokkering", "blocking_ipv4": "IPv4-blokkering",
"blocking_ipv6": "IPv6-blokkering", "blocking_ipv6": "IPv6-blokkering",
"blocked_response_ttl": "Blokkerte svars TTL",
"dnscrypt": "DNSCrypt", "dnscrypt": "DNSCrypt",
"dns_over_https": "DNS-over-HTTPS", "dns_over_https": "DNS-over-HTTPS",
"dns_over_tls": "DNS-over-TLS", "dns_over_tls": "DNS-over-TLS",
@@ -626,6 +627,7 @@
"use_saved_key": "Bruk den tidligere lagrede nøkkelen", "use_saved_key": "Bruk den tidligere lagrede nøkkelen",
"parental_control": "Foreldrekontroll", "parental_control": "Foreldrekontroll",
"safe_browsing": "Sikker surfing", "safe_browsing": "Sikker surfing",
"served_from_cache": "{{value}} <i>(formidlet fra mellomlageret)</i>",
"theme_dark_desc": "Mørkt tema", "theme_dark_desc": "Mørkt tema",
"theme_light_desc": "Lyst tema", "theme_light_desc": "Lyst tema",
"disable_notify_until_tomorrow": "Deaktiver beskyttelsen til i morgen", "disable_notify_until_tomorrow": "Deaktiver beskyttelsen til i morgen",

View File

@@ -620,10 +620,6 @@
"check_cname": "CNAME: {{cname}}", "check_cname": "CNAME: {{cname}}",
"check_reason": "Motivo: {{reason}}", "check_reason": "Motivo: {{reason}}",
"check_service": "Nome do serviço: {{service}}", "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", "service_name": "Nome do serviço",
"check_not_found": "Não encontrado em suas listas de filtros", "check_not_found": "Não encontrado em suas listas de filtros",
"client_confirm_block": "Você tem certeza de que deseja bloquear o cliente \"{{ip}}\"?", "client_confirm_block": "Você tem certeza de que deseja bloquear o cliente \"{{ip}}\"?",

View File

@@ -620,10 +620,6 @@
"check_cname": "CNAME: {{cname}}", "check_cname": "CNAME: {{cname}}",
"check_reason": "Motivo: {{reason}}", "check_reason": "Motivo: {{reason}}",
"check_service": "Nome do serviço: {{service}}", "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", "service_name": "Nome do serviço",
"check_not_found": "Não encontrado nas tuas listas de filtros", "check_not_found": "Não encontrado nas tuas listas de filtros",
"client_confirm_block": "Você tem certeza de que deseja bloquear o cliente \"{{ip}}\"?", "client_confirm_block": "Você tem certeza de que deseja bloquear o cliente \"{{ip}}\"?",

View File

@@ -620,10 +620,6 @@
"check_cname": "CNAME: {{cname}}", "check_cname": "CNAME: {{cname}}",
"check_reason": "Причина: {{reason}}", "check_reason": "Причина: {{reason}}",
"check_service": "Название сервиса: {{service}}", "check_service": "Название сервиса: {{service}}",
"check_hostname": "Имя хоста или домена",
"check_client_id": "Идентификатор клиента (ClientID или IP-адрес)",
"check_enter_client_id": "Введите идентификатор клиента",
"check_dns_record": "Выберите тип DNS-записи",
"service_name": "Имя сервиса", "service_name": "Имя сервиса",
"check_not_found": "Не найдено в вашем списке фильтров", "check_not_found": "Не найдено в вашем списке фильтров",
"client_confirm_block": "Вы уверены, что хотите заблокировать клиента «{{ip}}»?", "client_confirm_block": "Вы уверены, что хотите заблокировать клиента «{{ip}}»?",

View File

@@ -620,10 +620,6 @@
"check_cname": "CNAME: {{cname}}", "check_cname": "CNAME: {{cname}}",
"check_reason": "Dôvod: {{reason}}", "check_reason": "Dôvod: {{reason}}",
"check_service": "Meno služby: {{service}}", "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", "service_name": "Názov služby",
"check_not_found": "Nenašlo sa vo Vašom zozname filtrov", "check_not_found": "Nenašlo sa vo Vašom zozname filtrov",
"client_confirm_block": "Naozaj chcete zablokovať klienta \"{{ip}}\"?", "client_confirm_block": "Naozaj chcete zablokovať klienta \"{{ip}}\"?",

View File

@@ -40,11 +40,11 @@
"dhcp_ipv4_settings": "DHCP IPv4 Ayarları", "dhcp_ipv4_settings": "DHCP IPv4 Ayarları",
"dhcp_ipv6_settings": "DHCP IPv6 Ayarları", "dhcp_ipv6_settings": "DHCP IPv6 Ayarları",
"form_error_required": "Gerekli alan", "form_error_required": "Gerekli alan",
"form_error_ip4_format": "IPv4 adresi geçersiz", "form_error_ip4_format": "Geçersiz IPv4 adresi",
"form_error_ip4_gateway_format": "Ağ geçidi IPv4 adresi geçersiz", "form_error_ip4_gateway_format": "Geçersiz ağ geçidi IPv4 adresi",
"form_error_ip6_format": "IPv6 adresi geçersiz", "form_error_ip6_format": "Geçersiz IPv6 adresi",
"form_error_ip_format": "IP adresi geçersiz", "form_error_ip_format": "Geçersiz IP adresi",
"form_error_mac_format": "MAC adresi geçersiz", "form_error_mac_format": "Geçersiz MAC adresi",
"form_error_client_id_format": "İstemci Kimliği yalnızca sayılar, küçük harfler ve kısa çizgiler içermelidir", "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_server_name": "Sunucu adı geçersiz",
"form_error_subnet": "\"{{cidr}}\" alt ağı, \"{{ip}}\" IP adresini içermiyor", "form_error_subnet": "\"{{cidr}}\" alt ağı, \"{{ip}}\" IP adresini içermiyor",
@@ -68,7 +68,7 @@
"ip": "IP", "ip": "IP",
"dhcp_table_hostname": "Ana makine Adı", "dhcp_table_hostname": "Ana makine Adı",
"dhcp_table_expires": "Bitiş tarihi", "dhcp_table_expires": "Bitiş tarihi",
"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_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_error": "AdGuard Home, ağda başka bir etkin DHCP sunucusu olup olmadığını belirleyemedi", "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_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.", "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", "average_upstream_response_time": "Ortalama üst kaynak yanıt süresi",
"response_time": "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", "average_processing_time_hint": "Bir DNS isteğinin milisaniye cinsinden ortalama işlem süresi",
"block_domain_use_filters_and_hosts": "Filtre ve ana bilgisayar dosyalarını kullanarak alan adlarını engelle", "block_domain_use_filters_and_hosts": "Filtre ve hosts dosyalarını kullanarak alan adlarını engelle",
"filters_block_toggle_hint": "<a>Filtreler</a> ayarlarında engelleme kuralları oluşturabilirsiniz.", "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": "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_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": "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.", "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.", "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", "no_servers_specified": "Sunucu belirtilmedi",
"general_settings": "Genel ayarlar", "general_settings": "Genel ayarlar",
@@ -294,9 +294,6 @@
"blocked_response_ttl": "Engellenen yanıt kullanım süresi", "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", "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)", "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", "dnscrypt": "DNSCrypt",
"dns_over_https": "DNS-over-HTTPS", "dns_over_https": "DNS-over-HTTPS",
"dns_over_tls": "DNS-over-TLS", "dns_over_tls": "DNS-over-TLS",
@@ -311,7 +308,7 @@
"form_enter_rate_limit": "Sıklık limitini girin", "form_enter_rate_limit": "Sıklık limitini girin",
"rate_limit": "Sıklık limiti", "rate_limit": "Sıklık limiti",
"edns_enable": "EDNS istemci alt ağını etkinleştir", "edns_enable": "EDNS istemci alt ağını etkinleştir",
"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_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_use_custom_ip": "EDNS için özel IP kullan", "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", "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.", "rate_limit_desc": "İstemci başına izin verilen saniyedeki istek sayısı. 0 olarak ayarlamak, sınır olmadığı anlamına gelir.",
@@ -345,17 +342,17 @@
"unknown_filter": "Bilinmeyen filtre {{filterId}}", "unknown_filter": "Bilinmeyen filtre {{filterId}}",
"known_tracker": "Bilinen izleyici", "known_tracker": "Bilinen izleyici",
"install_welcome_title": "AdGuard Home'a hoş geldiniz!", "install_welcome_title": "AdGuard Home'a hoş geldiniz!",
"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_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_settings_title": "Yönetici Web Arayüzü", "install_settings_title": "Yönetici Web Arayüzü",
"install_settings_listen": "Dinleme arayüzü", "install_settings_listen": "Dinleme arayüzü",
"install_settings_port": "Bağlantı noktası", "install_settings_port": "Bağlantı noktası",
"install_settings_interface_link": "AdGuard Home yönetici web arayüzüne aşağıdaki adreslerden erişebilirsiniz:", "install_settings_interface_link": "AdGuard Home yönetici web arayüzünüz aşağıdaki adreslerde bulunacaktır:",
"form_error_port": "Geçerli bir bağlantı noktası değeri girin", "form_error_port": "Geçerli bir bağlantı noktası değeri girin",
"install_settings_dns": "DNS sunucusu", "install_settings_dns": "DNS sunucusu",
"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_dns_desc": "Aşağıdaki adreslerde DNS sunucusunu kullanmak için cihazlarınızı veya yönlendiricinizi yapılandırmanız gerekir:",
"install_settings_all_interfaces": "Tüm arayüzler", "install_settings_all_interfaces": "Tüm arayüzler",
"install_auth_title": "Kimlik Doğrulama", "install_auth_title": "Kimlik Doğrulama",
"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_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_username": "Kullanıcı adı", "install_auth_username": "Kullanıcı adı",
"install_auth_password": "Parola", "install_auth_password": "Parola",
"install_auth_confirm": "Parolayı onayla", "install_auth_confirm": "Parolayı onayla",
@@ -369,10 +366,10 @@
"install_devices_router": "Yönlendirici", "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_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_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 ü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_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_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_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_3": "AdGuard Home sunucu adreslerinizi oraya girin.",
"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_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_windows_list_1": "Başlat menüsünden veya Windows araması aracılığıyla Denetim Masası'nıın.", "install_devices_windows_list_1": "Başlat menüsünden veya Windows araması aracılığıyla Denetim Masası'nıı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_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.", "install_devices_windows_list_3": "Sol panelde \"Bağdaştırıcı ayarlarını değiştirin\" öğesine tıklayın.",
@@ -392,7 +389,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_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_3": "O anda aktif olan ağın adına dokunun.",
"install_devices_ios_list_4": "DNS alanına AdGuard Home sunucunuzun adreslerini girin.", "install_devices_ios_list_4": "DNS alanına AdGuard Home sunucunuzun adreslerini girin.",
"get_started": "Başla", "get_started": "Başlayın",
"next": "Sonraki", "next": "Sonraki",
"open_dashboard": "Panoyu Aç", "open_dashboard": "Panoyu Aç",
"install_saved": "Başarıyla kaydedildi", "install_saved": "Başarıyla kaydedildi",
@@ -455,14 +452,14 @@
"settings_global": "Genel", "settings_global": "Genel",
"settings_custom": "Özel", "settings_custom": "Özel",
"table_client": "İstemci", "table_client": "İstemci",
"table_name": "Ad", "table_name": "AdAdı",
"save_btn": "Kaydet", "save_btn": "Kaydet",
"client_add": "İstemci Ekle", "client_add": "İstemci Ekle",
"client_new": "Yeni İstemci", "client_new": "Yeni İstemci",
"client_edit": "İstemciyi Düzenle", "client_edit": "İstemciyi Düzenle",
"client_identifier": "Tanımlayıcı", "client_identifier": "Tanımlayıcı",
"ip_address": "IP adresi", "ip_address": "IP adresi",
"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.", "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.",
"form_enter_ip": "IP girin", "form_enter_ip": "IP girin",
"form_enter_subnet_ip": "\"{{cidr}}\" alt ağına bir IP adresi girin", "form_enter_subnet_ip": "\"{{cidr}}\" alt ağına bir IP adresi girin",
"form_enter_mac": "MAC adresi girin", "form_enter_mac": "MAC adresi girin",
@@ -479,7 +476,7 @@
"client_confirm_delete": "\"{{key}}\" istemcisini silmek istediğinizden emin misiniz?", "client_confirm_delete": "\"{{key}}\" istemcisini silmek istediğinizden emin misiniz?",
"list_confirm_delete": "Bu listeyi silmek istediğinizden emin misiniz?", "list_confirm_delete": "Bu listeyi silmek istediğinizden emin misiniz?",
"auto_clients_title": "Çalışma zamanı istemcileri", "auto_clients_title": "Çalışma zamanı istemcileri",
"auto_clients_desc": "AdGuard Home'u kullanan veya kullanabilecek cihazların IP adresleri hakkında bilgiler. Bu bilgiler, ana bilgisayar dosyaları, ters DNS sorguları ve çeşitli diğer kaynaklardan toplanmaktadır.", "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.",
"access_title": "Erişim ayarları", "access_title": "Erişim ayarları",
"access_desc": "AdGuard Home DNS sunucusu için erişim kurallarını buradan yapılandırabilirsiniz", "access_desc": "AdGuard Home DNS sunucusu için erişim kurallarını buradan yapılandırabilirsiniz",
"access_allowed_title": "İzin verilen istemciler", "access_allowed_title": "İzin verilen istemciler",
@@ -601,12 +598,12 @@
"disable_ipv6": "IPv6 adreslerinin çözümlenmesini devre dışı bırak", "disable_ipv6": "IPv6 adreslerinin çözümlenmesini devre dışı bırak",
"disable_ipv6_desc": "IPv6 adresleri için tüm DNS sorgularını bırakın (AAAA yazın) ve HTTPS yanıtlarından IPv6 ipuçlarını kaldırın.", "disable_ipv6_desc": "IPv6 adresleri için tüm DNS sorgularını 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": "En hızlı IP adresi",
"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.", "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.",
"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_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_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.", "autofix_warning_result": "Sonuç olarak, sisteminizden gelen tüm DNS istekleri varsayılan olarak AdGuard Home tarafından işlenecektir.",
"tags_title": "Etiketler", "tags_title": "Etiketler",
"tags_desc": "İstemciyi tanımlayan etiketleri seçebilirsiniz. Filtreleme kurallarına etiketleri dahil ederek daha hassas bir şekilde uygulayabilirsiniz. <0>Daha fazla bilgi edinin</0>.", "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>.",
"form_select_tags": "İstemci etiketlerini seçin", "form_select_tags": "İstemci etiketlerini seçin",
"check_title": "Filtrelemeyi denetleyin", "check_title": "Filtrelemeyi denetleyin",
"check_desc": "Ana makine adının filtreleme durumunu kontrol edin.", "check_desc": "Ana makine adının filtreleme durumunu kontrol edin.",
@@ -620,10 +617,6 @@
"check_cname": "CNAME: {{cname}}", "check_cname": "CNAME: {{cname}}",
"check_reason": "Sebep: {{reason}}", "check_reason": "Sebep: {{reason}}",
"check_service": "Hizmet adı: {{service}}", "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ı", "service_name": "Hizmet adı",
"check_not_found": "Filtre listelerinizde bulunamadı", "check_not_found": "Filtre listelerinizde bulunamadı",
"client_confirm_block": "\"{{ip}}\" istemcisini engellemek istediğinizden emin misiniz?", "client_confirm_block": "\"{{ip}}\" istemcisini engellemek istediğinizden emin misiniz?",
@@ -631,11 +624,11 @@
"client_blocked": "\"{{ip}}\" istemcisi başarıyla engellendi", "client_blocked": "\"{{ip}}\" istemcisi başarıyla engellendi",
"client_unblocked": "\"{{ip}}\" istemcinin engellemesi başarıyla kaldırıldı", "client_unblocked": "\"{{ip}}\" istemcinin engellemesi başarıyla kaldırıldı",
"static_ip": "Sabit IP adresi", "static_ip": "Sabit IP adresi",
"static_ip_desc": "AdGuard Home bir sunucudur, bu nedenle düzgün çalışabilmesi için sabit bir IP adresine ihtiyaç duyar. Aksi takdirde, yönlendiriciniz bu cihaza farklı bir IP adresi atayabilir.", "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.",
"set_static_ip": "Sabit IP adresi ayarla", "set_static_ip": "Sabit IP adresi ayarla",
"install_static_ok": "Güzel haber! Sabit IP adresi zaten yapılandırılmış", "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_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> sabit IP adresinin kullanıldığını tespit etti. Sabit adresiniz olarak ayarlanmasını ister misiniz?", "install_static_configure": "AdGuard Home, <0>{{ip}}</0> dinamik 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?", "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": "{{count}} liste güncellendi",
"list_updated_plural": "{{count}} liste güncellendi", "list_updated_plural": "{{count}} liste güncellendi",
@@ -714,8 +707,8 @@
"custom_rotation_input": "Rotasyonu saat cinsinden girin", "custom_rotation_input": "Rotasyonu saat cinsinden girin",
"protection_section_label": "Koruma", "protection_section_label": "Koruma",
"log_and_stats_section_label": "Sorgu günlüğü ve istatistikler", "log_and_stats_section_label": "Sorgu günlüğü ve istatistikler",
"ignore_query_log": "Sorgu günlüğünde bu istemciyi gösterme", "ignore_query_log": "Sorgu günlüğünde bu istemciyi yoksay",
"ignore_statistics": "İstatistiklerde bu istemciyi gösterme", "ignore_statistics": "İstatistiklerde bu istemciyi yoksay",
"schedule_services": "Hizmet engellemeyi duraklat", "schedule_services": "Hizmet engellemeyi duraklat",
"schedule_services_desc": "Hizmet engelleme filtresinin duraklatma planını yapılandırın", "schedule_services_desc": "Hizmet engelleme filtresinin duraklatma planını yapılandırın",
"schedule_services_desc_client": "Bu istemci için hizmet engelleme filtresinin duraklatma planını yapılandırın", "schedule_services_desc_client": "Bu istemci için hizmet engelleme filtresinin duraklatma planını yapılandırın",
@@ -749,6 +742,6 @@
"friday_short": "Cum", "friday_short": "Cum",
"saturday_short": "Cmt", "saturday_short": "Cmt",
"upstream_dns_cache_configuration": "Üst kaynak DNS önbellek yapılandırması", "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ğini etkinleştir", "enable_upstream_dns_cache": "Bu istemcinin özel üst kaynak yapılandırması için DNS önbelleğe almayı etkinleştir",
"dns_cache_size": "DNS önbellek boyutu, bayt cinsinden" "dns_cache_size": "DNS önbellek boyutu, bayt cinsinden"
} }

View File

@@ -620,10 +620,6 @@
"check_cname": "CNAME: {{cname}}", "check_cname": "CNAME: {{cname}}",
"check_reason": "原因:{{reason}}", "check_reason": "原因:{{reason}}",
"check_service": "服务名称:{{service}}", "check_service": "服务名称:{{service}}",
"check_hostname": "主机名或域名",
"check_client_id": "客户端标识符ClientID 或 IP 地址)",
"check_enter_client_id": "输入客户端标识符",
"check_dns_record": "选择 DNS 记录类型",
"service_name": "服务名称", "service_name": "服务名称",
"check_not_found": "未在您的筛选列表中找到", "check_not_found": "未在您的筛选列表中找到",
"client_confirm_block": "确定要阻止客户端 \"{{ip}}\"?", "client_confirm_block": "确定要阻止客户端 \"{{ip}}\"?",

View File

@@ -1,6 +1,6 @@
{ {
"client_settings": "用戶端設定", "client_settings": "用戶端設定",
"example_upstream_reserved": "<0>特定網域</0>上游;", "example_upstream_reserved": "<0>特定網域</0>上游;",
"example_multiple_upstreams_reserved": "<0>特定網域</0>的多個上游伺服器;", "example_multiple_upstreams_reserved": "<0>特定網域</0>的多個上游伺服器;",
"example_upstream_comment": "註解。", "example_upstream_comment": "註解。",
"upstream_parallel": "透過同時地查詢所有上游的伺服器,使用並行的查詢以加速解析。", "upstream_parallel": "透過同時地查詢所有上游的伺服器,使用並行的查詢以加速解析。",
@@ -20,7 +20,7 @@
"resolve_clients_title": "啟用用戶端的 IP 位址之反向的解析", "resolve_clients_title": "啟用用戶端的 IP 位址之反向的解析",
"resolve_clients_desc": "透過傳送指標PTR查詢到對應的解析器私人 DNS 伺服器供區域的用戶端,上游的伺服器供有公共 IP 位址的用戶端),反向地解析用戶端的 IP 位址變為它們的主機名稱。", "resolve_clients_desc": "透過傳送指標PTR查詢到對應的解析器私人 DNS 伺服器供區域的用戶端,上游的伺服器供有公共 IP 位址的用戶端),反向地解析用戶端的 IP 位址變為它們的主機名稱。",
"use_private_ptr_resolvers_title": "使用私人反向的 DNS 解析器", "use_private_ptr_resolvers_title": "使用私人反向的 DNS 解析器",
"use_private_ptr_resolvers_desc": "透過私有上游伺服器、DHCP/etc/hosts 等管道,解析含有私有 IP 位址的 ARPA 網域的 PTR、SOA NS 請求。若停用此功能AdGuard Home 將以 NXDOMAIN 回應所有相關請求。", "use_private_ptr_resolvers_desc": "使用私人上游伺服器、DHCP/etc/hosts 等方式解析包含私人 IP 位址的 ARPA 網域的 PTR、SOA NS 請求。如果停用AdGuard Home 將對所有此類請求以 NXDOMAIN 回應。",
"check_dhcp_servers": "檢查動態主機設定協定DHCP伺服器", "check_dhcp_servers": "檢查動態主機設定協定DHCP伺服器",
"save_config": "儲存配置", "save_config": "儲存配置",
"enabled_dhcp": "動態主機設定協定DHCP伺服器被啟用", "enabled_dhcp": "動態主機設定協定DHCP伺服器被啟用",
@@ -112,8 +112,8 @@
"privacy_policy": "隱私政策", "privacy_policy": "隱私政策",
"enable_protection": "啟用防護", "enable_protection": "啟用防護",
"enabled_protection": "已啟用防護", "enabled_protection": "已啟用防護",
"disable_protection": "用防護", "disable_protection": "用防護",
"disabled_protection": "已用防護", "disabled_protection": "已用防護",
"refresh_statics": "重新整理統計資料", "refresh_statics": "重新整理統計資料",
"dns_query": "DNS 查詢", "dns_query": "DNS 查詢",
"blocked_by": "<0>被過濾器封鎖</0>", "blocked_by": "<0>被過濾器封鎖</0>",
@@ -124,8 +124,8 @@
"for_last_hours_plural": "在過去的 {{count}} 小時內", "for_last_hours_plural": "在過去的 {{count}} 小時內",
"for_last_days": "在最近的 {{count}} 日內", "for_last_days": "在最近的 {{count}} 日內",
"for_last_days_plural": "在最近的 {{count}} 日內", "for_last_days_plural": "在最近的 {{count}} 日內",
"stats_disabled": "統計功能目前停用中,請至<0>設定頁面</0>重新開啟。", "stats_disabled": "統計資料已被禁用。您可從<0>設定頁面</0>中打開它。",
"stats_disabled_short": "該統計資料已用", "stats_disabled_short": "該統計資料已被禁用",
"no_domains_found": "無已發現之網域", "no_domains_found": "無已發現之網域",
"requests_count": "請求總數", "requests_count": "請求總數",
"top_blocked_domains": "熱門已封鎖的網域", "top_blocked_domains": "熱門已封鎖的網域",
@@ -172,13 +172,13 @@
"upstreams": "上游", "upstreams": "上游",
"upstream": "上游伺服器", "upstream": "上游伺服器",
"apply_btn": "套用", "apply_btn": "套用",
"disabled_filtering_toast": "已用過濾", "disabled_filtering_toast": "已用過濾",
"enabled_filtering_toast": "已啟用過濾", "enabled_filtering_toast": "已啟用過濾",
"disabled_safe_browsing_toast": "已用安全瀏覽", "disabled_safe_browsing_toast": "已用安全瀏覽",
"enabled_safe_browsing_toast": "已啟用安全瀏覽", "enabled_safe_browsing_toast": "已啟用安全瀏覽",
"disabled_parental_toast": "已用家長控制", "disabled_parental_toast": "已用家長控制",
"enabled_parental_toast": "已啟用家長控制", "enabled_parental_toast": "已啟用家長控制",
"disabled_safe_search_toast": "已用安全搜尋", "disabled_safe_search_toast": "已用安全搜尋",
"enabled_save_search_toast": "已啟用安全搜尋", "enabled_save_search_toast": "已啟用安全搜尋",
"updated_save_search_toast": "安全搜尋設定更新成功", "updated_save_search_toast": "安全搜尋設定更新成功",
"enabled_table_header": "已啟用", "enabled_table_header": "已啟用",
@@ -275,7 +275,7 @@
"query_log_retention": "查詢記錄保留時間", "query_log_retention": "查詢記錄保留時間",
"query_log_enable": "啟用記錄", "query_log_enable": "啟用記錄",
"query_log_configuration": "記錄配置", "query_log_configuration": "記錄配置",
"query_log_disabled": "查詢記錄功能已停用,請至「<0>設定</0>」調整", "query_log_disabled": "查詢記錄被禁用並可在<0>設定</0>中被配置",
"query_log_strict_search": "使用雙引號於嚴謹的搜尋", "query_log_strict_search": "使用雙引號於嚴謹的搜尋",
"query_log_retention_confirm": "您確定要更改記錄檔保存期限嗎?如果您縮短期限部分資料可能將會遺失", "query_log_retention_confirm": "您確定要更改記錄檔保存期限嗎?如果您縮短期限部分資料可能將會遺失",
"anonymize_client_ip": "將用戶端 IP 匿名", "anonymize_client_ip": "將用戶端 IP 匿名",
@@ -401,7 +401,7 @@
"encryption_config_saved": "加密配置被儲存", "encryption_config_saved": "加密配置被儲存",
"encryption_server": "伺服器名稱", "encryption_server": "伺服器名稱",
"encryption_server_enter": "輸入您的域名", "encryption_server_enter": "輸入您的域名",
"encryption_server_desc": "如果設定AdGuard Home 會偵測 ClientID、回應 DDR 查詢,並執行其他連線驗證。如果未設定,則會停用這些功能。必須符合憑證中的一個 DNS 名稱。", "encryption_server_desc": "如果設定AdGuard Home 檢測用戶端 IDs回覆 DDR 查詢,並執行額外的連線驗證。如果未設定,這些功能被禁用。必須與在該憑證裡的 DNS 名稱其中之一相符。",
"encryption_redirect": "自動地重新導向到 HTTPS", "encryption_redirect": "自動地重新導向到 HTTPS",
"encryption_redirect_desc": "如果被勾選AdGuard Home 將自動地重新導向您從 HTTP 到 HTTPS 位址。", "encryption_redirect_desc": "如果被勾選AdGuard Home 將自動地重新導向您從 HTTP 到 HTTPS 位址。",
"encryption_https": "HTTPS 連接埠", "encryption_https": "HTTPS 連接埠",
@@ -429,8 +429,8 @@
"encryption_reset": "您確定您想要重置加密設定嗎?", "encryption_reset": "您確定您想要重置加密設定嗎?",
"encryption_warning": "警告", "encryption_warning": "警告",
"encryption_plain_dns_enable": "啟用一般的 DNS", "encryption_plain_dns_enable": "啟用一般的 DNS",
"encryption_plain_dns_desc": "預設啟用一般 DNS。可以用它強制所有裝置使用加密 DNS。若要這樣做,您必須啟用至少一個加密 DNS 通訊協定", "encryption_plain_dns_desc": "預設情況下啟用一般 DNS。使用者可以用它強制所有裝置使用一般的 DNS。為此,必須至少啟用一個一般的 DNS 協定",
"encryption_plain_dns_error": "若要停用一般 DNS啟用至少一個加密 DNS 通訊協定", "encryption_plain_dns_error": "要禁用一般 DNS至少啟用一個一般的 DNS 協定",
"topline_expiring_certificate": "您的安全通訊端層SSL憑證即將到期。更新<0>加密設定</0>。", "topline_expiring_certificate": "您的安全通訊端層SSL憑證即將到期。更新<0>加密設定</0>。",
"topline_expired_certificate": "您的安全通訊端層SSL憑證為已到期的。更新<0>加密設定</0>。", "topline_expired_certificate": "您的安全通訊端層SSL憑證為已到期的。更新<0>加密設定</0>。",
"form_error_port_range": "輸入在 80-65535 之範圍內的連接埠號碼", "form_error_port_range": "輸入在 80-65535 之範圍內的連接埠號碼",
@@ -572,7 +572,7 @@
"filters_configuration": "過濾器配置", "filters_configuration": "過濾器配置",
"filters_enable": "啟用過濾器", "filters_enable": "啟用過濾器",
"filters_interval": "過濾器更新間隔", "filters_interval": "過濾器更新間隔",
"disabled": "已用", "disabled": "已用",
"username_label": "使用者名稱", "username_label": "使用者名稱",
"username_placeholder": "輸入使用者名稱", "username_placeholder": "輸入使用者名稱",
"password_label": "密碼", "password_label": "密碼",
@@ -598,7 +598,7 @@
"rewrite_domain_name": "域名新增一筆正規名稱CNAME記錄", "rewrite_domain_name": "域名新增一筆正規名稱CNAME記錄",
"rewrite_A": "<0>A</0>:特殊的數值,阻止 <0>A</0> 記錄免於該上游", "rewrite_A": "<0>A</0>:特殊的數值,阻止 <0>A</0> 記錄免於該上游",
"rewrite_AAAA": "<0>AAAA</0>:特殊的數值,阻止 <0>AAAA</0> 記錄免於該上游", "rewrite_AAAA": "<0>AAAA</0>:特殊的數值,阻止 <0>AAAA</0> 記錄免於該上游",
"disable_ipv6": "用 IPv6 位址解析", "disable_ipv6": "用 IPv6 位址解析",
"disable_ipv6_desc": "停止所有對於 IPv6 位址(類型 AAAA的 DNS 查詢,並從 HTTPS 回應中移除 IPv6 的提示。", "disable_ipv6_desc": "停止所有對於 IPv6 位址(類型 AAAA的 DNS 查詢,並從 HTTPS 回應中移除 IPv6 的提示。",
"fastest_addr": "最快的 IP 位址", "fastest_addr": "最快的 IP 位址",
"fastest_addr_desc": "等待<b>所有</b> DNS 伺服器的回應,測量每個伺服器的 TCP 連線速度,並返回連線速度最快的伺服器的 IP 位址。<br/>如果一個或多個上游伺服器沒有回應,此模式會顯著減慢 DNS 查詢速度。確保您的上游伺服器穩定且上游超時時間短。", "fastest_addr_desc": "等待<b>所有</b> DNS 伺服器的回應,測量每個伺服器的 TCP 連線速度,並返回連線速度最快的伺服器的 IP 位址。<br/>如果一個或多個上游伺服器沒有回應,此模式會顯著減慢 DNS 查詢速度。確保您的上游伺服器穩定且上游超時時間短。",
@@ -620,10 +620,6 @@
"check_cname": "正規名稱CNAME{{cname}}", "check_cname": "正規名稱CNAME{{cname}}",
"check_reason": "原因:{{reason}}", "check_reason": "原因:{{reason}}",
"check_service": "服務名稱:{{service}}", "check_service": "服務名稱:{{service}}",
"check_hostname": "主機名稱或域名",
"check_client_id": "用戶端識別碼ClientID 或 IP 位址)",
"check_enter_client_id": "輸入用戶識別碼",
"check_dns_record": "選擇 DNS 記錄類型",
"service_name": "服務名稱", "service_name": "服務名稱",
"check_not_found": "未在您的過濾器清單中被找到", "check_not_found": "未在您的過濾器清單中被找到",
"client_confirm_block": "您確定您想要封鎖該用戶端 \"{{ip}}\" 嗎?", "client_confirm_block": "您確定您想要封鎖該用戶端 \"{{ip}}\" 嗎?",
@@ -656,7 +652,7 @@
"blocklist": "封鎖清單", "blocklist": "封鎖清單",
"milliseconds_abbreviation": "ms", "milliseconds_abbreviation": "ms",
"cache_size": "快取大小", "cache_size": "快取大小",
"cache_size_desc": "DNS 快取大小 (位元組)。若要停用快取,留空。", "cache_size_desc": "DNS 快取大小(以位元組)。要禁用快取,留空。",
"cache_ttl_min_override": "覆寫最小的存活時間TTL", "cache_ttl_min_override": "覆寫最小的存活時間TTL",
"cache_ttl_max_override": "覆寫最大的存活時間TTL", "cache_ttl_max_override": "覆寫最大的存活時間TTL",
"enter_cache_size": "輸入快取大小(位元組)", "enter_cache_size": "輸入快取大小(位元組)",
@@ -681,13 +677,13 @@
"port_53_faq_link": "連接埠 53 常被 \"DNSStubListener\" 或 \"systemd-resolved\" 服務佔用。請閱讀有關如何解決這個的<0>用法說明</0>。", "port_53_faq_link": "連接埠 53 常被 \"DNSStubListener\" 或 \"systemd-resolved\" 服務佔用。請閱讀有關如何解決這個的<0>用法說明</0>。",
"adg_will_drop_dns_queries": "AdGuard Home 將持續排除來自此用戶端之所有的 DNS 查詢。", "adg_will_drop_dns_queries": "AdGuard Home 將持續排除來自此用戶端之所有的 DNS 查詢。",
"filter_allowlist": "警告:此動作也將把 \"{{disallowed_rule}}\" 規則排除在被允許的用戶端的清單之外。", "filter_allowlist": "警告:此動作也將把 \"{{disallowed_rule}}\" 規則排除在被允許的用戶端的清單之外。",
"last_rule_in_allowlist": "無法禁止此用戶端,因為排除規則 \"{{disallowed_rule}}\" 會停用「允許的用戶端」清單。", "last_rule_in_allowlist": "因為排除 \"{{disallowed_rule}}\" 規則將禁用\"被允許的用戶端\"清單,無法不允許此用戶端。",
"use_saved_key": "使用該先前已儲存的金鑰", "use_saved_key": "使用該先前已儲存的金鑰",
"parental_control": "家長控制", "parental_control": "家長控制",
"safe_browsing": "安全瀏覽", "safe_browsing": "安全瀏覽",
"served_from_cache_label": "從快取中", "served_from_cache_label": "從快取中",
"form_error_password_length": "密碼長度必須為 {{min}} 到 {{max}} 個字符", "form_error_password_length": "密碼長度必須為 {{min}} 到 {{max}} 個字符",
"anonymizer_notification": "<0>注意:</0>IP 匿名功能已開啟。您可在<1>一般設定</1>中關閉。", "anonymizer_notification": "<0>注意:</0>IP 匿名化被啟用。您可在<1>一般設定</1>中禁用它。",
"confirm_dns_cache_clear": "您確定您想要清除 DNS 快取嗎?", "confirm_dns_cache_clear": "您確定您想要清除 DNS 快取嗎?",
"cache_cleared": "DNS 快取被成功地清除", "cache_cleared": "DNS 快取被成功地清除",
"clear_cache": "清除快取", "clear_cache": "清除快取",
@@ -702,14 +698,14 @@
"disable_for_hours": "{{count}} 小時", "disable_for_hours": "{{count}} 小時",
"disable_for_hours_plural": "{{count}} 小時", "disable_for_hours_plural": "{{count}} 小時",
"disable_until_tomorrow": "直到明天", "disable_until_tomorrow": "直到明天",
"disable_notify_for_seconds": "計 {{count}} 秒用防護", "disable_notify_for_seconds": "計 {{count}} 秒用防護",
"disable_notify_for_seconds_plural": "計 {{count}} 秒用防護", "disable_notify_for_seconds_plural": "計 {{count}} 秒用防護",
"disable_notify_for_minutes": "計 {{count}} 分鐘用防護", "disable_notify_for_minutes": "計 {{count}} 分鐘用防護",
"disable_notify_for_minutes_plural": "計 {{count}} 分鐘用防護", "disable_notify_for_minutes_plural": "計 {{count}} 分鐘用防護",
"disable_notify_for_hours": "計 {{count}} 小時用防護", "disable_notify_for_hours": "計 {{count}} 小時用防護",
"disable_notify_for_hours_plural": "計 {{count}} 小時用防護", "disable_notify_for_hours_plural": "計 {{count}} 小時用防護",
"disable_notify_until_tomorrow": "用防護直到明天", "disable_notify_until_tomorrow": "用防護直到明天",
"enable_protection_timer": "防護將於 {{time}} 啟用", "enable_protection_timer": "防護將於 {{time}} 啟用",
"custom_retention_input": "輸入保留時間(小時)", "custom_retention_input": "輸入保留時間(小時)",
"custom_rotation_input": "輸入旋轉時間(小時)", "custom_rotation_input": "輸入旋轉時間(小時)",
"protection_section_label": "防護", "protection_section_label": "防護",

View File

@@ -9,17 +9,13 @@ import Info from './Info';
import { RootState } from '../../../initialState'; import { RootState } from '../../../initialState';
import { validateRequiredValue } from '../../../helpers/validators'; import { validateRequiredValue } from '../../../helpers/validators';
import { Input } from '../../ui/Controls/Input'; import { Input } from '../../ui/Controls/Input';
import { DNS_RECORD_TYPES } from '../../../helpers/constants';
import { Select } from '../../ui/Controls/Select';
export type FilteringCheckFormValues = { interface FormValues {
name: string; name: string;
client?: string;
qtype?: string;
} }
type Props = { type Props = {
onSubmit?: (data: FilteringCheckFormValues) => void; onSubmit?: (data: FormValues) => void;
}; };
const Check = ({ onSubmit }: Props) => { const Check = ({ onSubmit }: Props) => {
@@ -31,13 +27,11 @@ const Check = ({ onSubmit }: Props) => {
const { const {
control, control,
handleSubmit, handleSubmit,
formState: { isValid }, formState: { isDirty, isValid },
} = useForm<FilteringCheckFormValues>({ } = useForm<FormValues>({
mode: 'onBlur', mode: 'onBlur',
defaultValues: { defaultValues: {
name: '', name: '',
client: '',
qtype: DNS_RECORD_TYPES[0],
}, },
}); });
@@ -54,56 +48,24 @@ const Check = ({ onSubmit }: Props) => {
<Input <Input
{...field} {...field}
type="text" type="text"
label={t('check_hostname')}
data-testid="check_domain_name" data-testid="check_domain_name"
placeholder="example.com" placeholder={t('form_enter_host')}
error={fieldState.error?.message} 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 && ( {hostname && (
<> <>
<hr /> <hr />

View File

@@ -7,7 +7,7 @@ import PageTitle from '../ui/PageTitle';
import Examples from './Examples'; import Examples from './Examples';
import Check, { FilteringCheckFormValues } from './Check'; import Check from './Check';
import { getTextareaCommentsHighlight, syncScroll } from '../../helpers/highlightTextareaComments'; import { getTextareaCommentsHighlight, syncScroll } from '../../helpers/highlightTextareaComments';
import { COMMENT_LINE_DEFAULT_TOKEN } from '../../helpers/constants'; import { COMMENT_LINE_DEFAULT_TOKEN } from '../../helpers/constants';
@@ -48,18 +48,8 @@ class CustomRules extends Component<CustomRulesProps> {
this.props.setRules(this.props.filtering.userRules); this.props.setRules(this.props.filtering.userRules);
}; };
handleCheck = (values: FilteringCheckFormValues) => { handleCheck = (values: any) => {
const params: FilteringCheckFormValues = { name: values.name }; this.props.checkHost(values);
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); onScroll = (e: any) => syncScroll(e, this.ref);
@@ -78,7 +68,6 @@ class CustomRules extends Component<CustomRulesProps> {
<form onSubmit={this.handleSubmit}> <form onSubmit={this.handleSubmit}>
<div className="text-edit-container mb-4"> <div className="text-edit-container mb-4">
<textarea <textarea
data-testid="custom_rule_textarea"
className="form-control font-monospace text-input" className="form-control font-monospace text-input"
value={userRules} value={userRules}
onChange={this.handleChange} onChange={this.handleChange}
@@ -92,7 +81,6 @@ class CustomRules extends Component<CustomRulesProps> {
<div className="card-actions"> <div className="card-actions">
<button <button
data-testid="apply_custom_rule"
className="btn btn-success btn-standard btn-large" className="btn btn-success btn-standard btn-large"
type="submit" type="submit"
onClick={this.handleSubmit}> onClick={this.handleSubmit}>

View File

@@ -59,7 +59,7 @@ const Header = () => {
<div className="header__column"> <div className="header__column">
<div className="header__right"> <div className="header__right">
{!processingProfile && name && ( {!processingProfile && name && (
<a href="control/logout" className="btn btn-sm btn-outline-secondary" data-testid="sign_out"> <a href="control/logout" className="btn btn-sm btn-outline-secondary">
{t('sign_out')} {t('sign_out')}
</a> </a>
)} )}

View File

@@ -288,7 +288,7 @@ const Row = memo(
); );
return ( return (
<div style={style} className={className} onClick={onClick} role="row" data-testid="querylog_cell"> <div style={style} className={className} onClick={onClick} role="row">
<DateCell {...rowProps} /> <DateCell {...rowProps} />
<DomainCell {...rowProps} /> <DomainCell {...rowProps} />

View File

@@ -6,8 +6,6 @@ import { useDispatch } from 'react-redux';
import { useHistory } from 'react-router-dom'; import { useHistory } from 'react-router-dom';
import classNames from 'classnames'; import classNames from 'classnames';
import { useFormContext } from 'react-hook-form'; import { useFormContext } from 'react-hook-form';
import queryString from 'query-string';
import { import {
DEBOUNCE_FILTER_TIMEOUT, DEBOUNCE_FILTER_TIMEOUT,
DEFAULT_LOGS_FILTER, DEFAULT_LOGS_FILTER,
@@ -56,17 +54,9 @@ export const Form = ({ className, setIsLoading }: Props) => {
} }
}, [responseStatusValue, setValue]); }, [responseStatusValue, setValue]);
useEffect(() => {
const { search: searchUrlParam } = queryString.parse(history.location.search);
if (searchUrlParam !== searchValue) {
setValue('search', searchUrlParam ? searchUrlParam.toString() : '');
}
}, [history.location.search]);
const onInputClear = async () => { const onInputClear = async () => {
setIsLoading(true); setIsLoading(true);
history.push(getLogsUrlParams(DEFAULT_LOGS_FILTER.search, responseStatusValue)); setValue('search', DEFAULT_LOGS_FILTER.search);
setIsLoading(false); setIsLoading(false);
}; };
@@ -84,7 +74,6 @@ export const Form = ({ className, setIsLoading }: Props) => {
}}> }}>
<div className="field__search"> <div className="field__search">
<SearchField <SearchField
data-testid="querylog_search"
value={searchValue} value={searchValue}
handleChange={(val) => setValue('search', val)} handleChange={(val) => setValue('search', val)}
onKeyDown={onEnterPress} onKeyDown={onEnterPress}

View File

@@ -314,7 +314,7 @@ const ClientsTable = ({
} }
if (!content) { if (!content) {
return content; return content;
} }
return <LogsSearchLink search={row.original.name}>{content}</LogsSearchLink>; return <LogsSearchLink search={row.original.name}>{content}</LogsSearchLink>;
}, },
}, },

View File

@@ -27,14 +27,12 @@ const SETTINGS = {
enabled: false, enabled: false,
title: i18next.t('use_adguard_browsing_sec'), title: i18next.t('use_adguard_browsing_sec'),
subtitle: i18next.t('use_adguard_browsing_sec_hint'), subtitle: i18next.t('use_adguard_browsing_sec_hint'),
testId: 'safebrowsing',
[ORDER_KEY]: 0, [ORDER_KEY]: 0,
}, },
parental: { parental: {
enabled: false, enabled: false,
title: i18next.t('use_adguard_parental'), title: i18next.t('use_adguard_parental'),
subtitle: i18next.t('use_adguard_parental_hint'), subtitle: i18next.t('use_adguard_parental_hint'),
testId: 'parental',
[ORDER_KEY]: 1, [ORDER_KEY]: 1,
}, },
}; };
@@ -92,12 +90,11 @@ class Settings extends Component<SettingsProps> {
renderSettings = (settings: any) => renderSettings = (settings: any) =>
getObjectKeysSorted(SETTINGS, ORDER_KEY).map((key: any) => { getObjectKeysSorted(SETTINGS, ORDER_KEY).map((key: any) => {
const setting = settings[key]; const setting = settings[key];
const { enabled, title, subtitle, testId } = setting; const { enabled, title, subtitle } = setting;
return ( return (
<div key={key} className="form__group form__group--checkbox"> <div key={key} className="form__group form__group--checkbox">
<Checkbox <Checkbox
data-testid={testId}
value={enabled} value={enabled}
title={title} title={title}
subtitle={subtitle} subtitle={subtitle}
@@ -121,7 +118,6 @@ class Settings extends Component<SettingsProps> {
<> <>
<div className="form__group form__group--checkbox"> <div className="form__group form__group--checkbox">
<Checkbox <Checkbox
data-testid="safesearch"
value={enabled} value={enabled}
title={i18next.t('enforce_safe_search')} title={i18next.t('enforce_safe_search')}
subtitle={i18next.t('enforce_save_search_hint')} subtitle={i18next.t('enforce_save_search_hint')}

View File

@@ -94,17 +94,14 @@ const Footer = () => {
auto: { auto: {
desc: t('theme_auto_desc'), desc: t('theme_auto_desc'),
icon: '#auto', icon: '#auto',
testId: 'theme_auto',
}, },
dark: { dark: {
desc: t('theme_dark_desc'), desc: t('theme_dark_desc'),
icon: '#dark', icon: '#dark',
testId: 'theme_dark',
}, },
light: { light: {
desc: t('theme_light_desc'), desc: t('theme_light_desc'),
icon: '#light', icon: '#light',
testId: 'theme_light',
}, },
}; };
@@ -116,9 +113,7 @@ const Footer = () => {
type="button" type="button"
className="btn btn-sm btn-secondary footer__theme-button" className="btn btn-sm btn-secondary footer__theme-button"
onClick={() => onThemeChange(theme)} onClick={() => onThemeChange(theme)}
title={content[theme].desc} title={content[theme].desc}>
data-testid={content[theme].testId}
>
<svg className={cn('footer__theme-icon', { 'footer__theme-icon--active': currentValue === theme })}> <svg className={cn('footer__theme-icon', { 'footer__theme-icon--active': currentValue === theme })}>
<use xlinkHref={content[theme].icon} /> <use xlinkHref={content[theme].icon} />
</svg> </svg>

View File

@@ -523,12 +523,3 @@ export const TIME_UNITS = {
HOURS: 'hours', HOURS: 'hours',
DAYS: 'days', 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"
];

View File

@@ -28,6 +28,12 @@ export default {
"homepage": "https://badmojr.github.io/1Hosts/", "homepage": "https://badmojr.github.io/1Hosts/",
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_24.txt" "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": { "CHN_adrules": {
"name": "CHN: AdRules DNS List", "name": "CHN: AdRules DNS List",
"categoryId": "regional", "categoryId": "regional",

View File

@@ -669,17 +669,15 @@ export const countClientsStatistics = (ids: any, autoClients: any) => {
* @returns {string} * @returns {string}
*/ */
export const formatElapsedMs = (elapsedMs: string, t: (key: string) => string) => { export const formatElapsedMs = (elapsedMs: string, t: (key: string) => string) => {
const parsedElapsedMs = parseFloat(elapsedMs); const parsedElapsedMs = parseInt(elapsedMs, 10);
if (Number.isNaN(parsedElapsedMs)) { if (Number.isNaN(parsedElapsedMs)) {
return elapsedMs; return elapsedMs;
} }
const formattedValue = parsedElapsedMs < 1 const formattedMs = formatNumber(parsedElapsedMs);
? parsedElapsedMs.toFixed(2)
: Math.floor(parsedElapsedMs).toString();
return `${formattedValue} ${t('milliseconds_abbreviation')}`; return `${formattedMs} ${t('milliseconds_abbreviation')}`;
}; };
/** /**

View File

@@ -1,5 +1,5 @@
{ {
"timeUpdated": "2025-03-17T10:05:02.622Z", "timeUpdated": "2025-01-13T10:04:54.031Z",
"categories": { "categories": {
"0": "audio_video_player", "0": "audio_video_player",
"1": "comments", "1": "comments",
@@ -5940,8 +5940,7 @@
"name": "Digioh", "name": "Digioh",
"categoryId": 4, "categoryId": 4,
"url": "https://digioh.com/", "url": "https://digioh.com/",
"companyId": "digioh", "companyId": null
"source": "AdGuard"
}, },
"digital.gov": { "digital.gov": {
"name": "Digital.gov", "name": "Digital.gov",
@@ -8262,8 +8261,8 @@
}, },
"google_marketing": { "google_marketing": {
"name": "Google Marketing", "name": "Google Marketing",
"categoryId": 4, "categoryId": 6,
"url": "https://marketingplatform.google.com/about/enterprise", "url": "https://marketingplatform.google.com/",
"companyId": "google", "companyId": "google",
"source": "AdGuard" "source": "AdGuard"
}, },
@@ -9059,13 +9058,6 @@
"url": "https://www.ippen-digital.de/", "url": "https://www.ippen-digital.de/",
"companyId": null "companyId": null
}, },
"id5-sync": {
"name": "ID5 Sync",
"categoryId": 4,
"url": "https://id5.io/",
"companyId": "id5-sync",
"source": "AdGuard"
},
"id_services": { "id_services": {
"name": "ID Services", "name": "ID Services",
"categoryId": 6, "categoryId": 6,
@@ -20956,9 +20948,6 @@
"wunderloop.net": "audience_science", "wunderloop.net": "audience_science",
"12mlbe.com": "audiencerate", "12mlbe.com": "audiencerate",
"audiencesquare.com": "audiencesquare.com", "audiencesquare.com": "audiencesquare.com",
"ad.gt": "audiencesquare.com",
"audigent.com": "audiencesquare.com",
"hadronid.net": "audiencesquare.com",
"auditude.com": "auditude", "auditude.com": "auditude",
"audtd.com": "audtd.com", "audtd.com": "audtd.com",
"cdn.augur.io": "augur", "cdn.augur.io": "augur",
@@ -21414,7 +21403,6 @@
"static.clmbtech.com": "columbia_online", "static.clmbtech.com": "columbia_online",
"combotag.com": "combotag", "combotag.com": "combotag",
"pdk.theplatform.com": "comcast_technology_solutions", "pdk.theplatform.com": "comcast_technology_solutions",
"theplatform.com": "comcast_technology_solutions",
"comm100.cn": "comm100", "comm100.cn": "comm100",
"comm100.com": "comm100", "comm100.com": "comm100",
"cdn-cs.com": "commerce_sciences", "cdn-cs.com": "commerce_sciences",
@@ -21694,6 +21682,9 @@
"dtmpub.com": "dotomi", "dtmpub.com": "dotomi",
"double.net": "double.net", "double.net": "double.net",
"2mdn.net": "doubleclick", "2mdn.net": "doubleclick",
"doubleclick.net": "doubleclick",
"invitemedia.com": "doubleclick",
"doubleclick.com": "doubleclick",
"doublepimp.com": "doublepimp", "doublepimp.com": "doublepimp",
"doublepimpssl.com": "doublepimp", "doublepimpssl.com": "doublepimp",
"redcourtside.com": "doublepimp", "redcourtside.com": "doublepimp",
@@ -21997,7 +21988,6 @@
"cdn.foxpush.net": "foxpush", "cdn.foxpush.net": "foxpush",
"foxpush.com": "foxpush", "foxpush.com": "foxpush",
"foxtel.com.au": "foxtel", "foxtel.com.au": "foxtel",
"foxtelgroupcdn.net.au": "foxtel",
"foxydeal.com": "foxydeal_com", "foxydeal.com": "foxydeal_com",
"yabidos.com": "fraudlogix", "yabidos.com": "fraudlogix",
"besucherstatistiken.com": "free_counter", "besucherstatistiken.com": "free_counter",
@@ -22399,8 +22389,6 @@
"maps.google.es": "google_maps", "maps.google.es": "google_maps",
"maps.google.se": "google_maps", "maps.google.se": "google_maps",
"maps.gstatic.com": "google_maps", "maps.gstatic.com": "google_maps",
"doubleclick.net": "google_marketing",
"invitemedia.com": "google_marketing",
"adsense.google.com": "google_marketing", "adsense.google.com": "google_marketing",
"adservice.google.ca": "google_marketing", "adservice.google.ca": "google_marketing",
"adservice.google.co.in": "google_marketing", "adservice.google.co.in": "google_marketing",
@@ -22431,7 +22419,6 @@
"adservice.google.vg": "google_marketing", "adservice.google.vg": "google_marketing",
"adtrafficquality.google": "google_marketing", "adtrafficquality.google": "google_marketing",
"dai.google.com": "google_marketing", "dai.google.com": "google_marketing",
"doubleclick.com": "google_marketing",
"doubleclickbygoogle.com": "google_marketing", "doubleclickbygoogle.com": "google_marketing",
"googlesyndication-cn.com": "google_marketing", "googlesyndication-cn.com": "google_marketing",
"duo.google.com": "google_meet", "duo.google.com": "google_meet",
@@ -22617,9 +22604,6 @@
"icuazeczpeoohx.com": "icuazeczpeoohx.com", "icuazeczpeoohx.com": "icuazeczpeoohx.com",
"id-news.net": "id-news.net", "id-news.net": "id-news.net",
"idcdn.de": "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", "cdn.id.services": "id_services",
"e-generator.com": "ideal_media", "e-generator.com": "ideal_media",
"idealo.com": "idealo_com", "idealo.com": "idealo_com",
@@ -23672,7 +23656,6 @@
"blockmetrics.com": "pagefair", "blockmetrics.com": "pagefair",
"pagefair.com": "pagefair", "pagefair.com": "pagefair",
"pagefair.net": "pagefair", "pagefair.net": "pagefair",
"btloader.com": "pagefair",
"ghmedia.com": "pagescience", "ghmedia.com": "pagescience",
"777seo.com": "paid-to-promote", "777seo.com": "paid-to-promote",
"paid-to-promote.net": "paid-to-promote", "paid-to-promote.net": "paid-to-promote",

View File

@@ -21,7 +21,7 @@ const Form = ({ onSubmit, processing }: LoginFormProps) => {
control, control,
formState: { isValid }, formState: { isValid },
} = useForm<LoginFormValues>({ } = useForm<LoginFormValues>({
mode: 'onChange', mode: 'onBlur',
defaultValues: { defaultValues: {
username: '', username: '',
password: '', password: '',
@@ -62,7 +62,7 @@ const Form = ({ onSubmit, processing }: LoginFormProps) => {
{...field} {...field}
data-testid="password" data-testid="password"
type="password" type="password"
label={t('password_label')} label={t('username_label')}
placeholder={t('password_placeholder')} placeholder={t('password_placeholder')}
error={fieldState.error?.message} error={fieldState.error?.message}
autoComplete="current-password" autoComplete="current-password"

View File

@@ -1,34 +0,0 @@
import { test, expect } from '@playwright/test';
import { ADMIN_USERNAME, ADMIN_PASSWORD } from '../constants';
test.describe('Control Panel', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/login.html');
await page.getByTestId('username').click();
await page.getByTestId('username').fill(ADMIN_USERNAME);
await page.getByTestId('password').click();
await page.getByTestId('password').fill(ADMIN_PASSWORD);
await page.keyboard.press('Tab');
await page.getByTestId('sign_in').click();
await page.waitForURL((url) => !url.href.endsWith('/login.html'));
});
test('should sign out successfully', async ({ page }) => {
await page.getByTestId('sign_out').click();
await page.waitForURL((url) => url.href.endsWith('/login.html'));
await expect(page.getByTestId('sign_in')).toBeVisible();
});
test('should change theme to dark and then light', async ({ page }) => {
await page.getByTestId('theme_dark').click();
await expect(page.locator('body[data-theme="dark"]')).toBeVisible();
await page.getByTestId('theme_light').click();
await expect(page.locator('body:not([data-theme="dark"])')).toBeVisible();
});
});

View File

@@ -1,52 +0,0 @@
import { test, expect, type Page } from '@playwright/test';
import { ADMIN_USERNAME, ADMIN_PASSWORD } from '../constants';
test.describe('DNS Settings', () => {
test.beforeEach(async ({ page }) => {
// Login before each test
await page.goto('/login.html');
await page.getByTestId('username').click();
await page.getByTestId('username').fill(ADMIN_USERNAME);
await page.getByTestId('password').click();
await page.getByTestId('password').fill(ADMIN_PASSWORD);
await page.keyboard.press('Tab');
await page.getByTestId('sign_in').click();
await page.waitForURL((url) => !url.href.endsWith('/login.html'));
});
const runDNSSettingsTest = async (page: Page, address: string) => {
await page.goto('/#dns');
const currentDns = await page.getByTestId('upstream_dns').inputValue();
await page.getByTestId('upstream_dns').fill(address);
await page.getByTestId('dns_upstream_test').click();
await page.waitForTimeout(2000);
await expect(page.getByTestId('upstream_dns')).toHaveValue(address);
await page.getByTestId('upstream_dns').fill(currentDns);
await page.getByTestId('dns_upstream_save').click({ force: true });
};
test('test for Default DNS', async ({ page }) => {
await runDNSSettingsTest(page, 'https://dns10.quad9.net/dns-query');
});
test('test for Plain DNS', async ({ page }) => {
await runDNSSettingsTest(page, '94.140.14.140');
});
test('test for DNS-over-HTTPS', async ({ page }) => {
await runDNSSettingsTest(page, 'https://unfiltered.adguard-dns.com/dns-query');
});
test('test for DNS-over-TLS', async ({ page }) => {
await runDNSSettingsTest(page, 'tls://unfiltered.adguard-dns.com');
});
test('test for DNS-over-QUIC', async ({ page }) => {
await runDNSSettingsTest(page, 'quic://unfiltered.adguard-dns.com');
});
});

View File

@@ -1,73 +0,0 @@
import { test, expect, type Page } from '@playwright/test';
import { execSync } from 'child_process';
import { ADMIN_USERNAME, ADMIN_PASSWORD } from '../constants';
test.describe('Filtering', () => {
test.beforeEach(async ({ page }) => {
// Login before each test
await page.goto('/login.html');
await page.getByTestId('username').click();
await page.getByTestId('username').fill(ADMIN_USERNAME);
await page.getByTestId('password').click();
await page.getByTestId('password').fill(ADMIN_PASSWORD);
await page.keyboard.press('Tab');
await page.getByTestId('sign_in').click();
await page.waitForURL((url) => !url.href.endsWith('/login.html'));
});
const runTerminalCommand = (command: string) => {
try {
console.info(`Executing command: ${command}`);
const output = execSync(command, { encoding: 'utf-8', stdio: 'pipe' }).trim();
console.info('Command executed successfully.');
console.debug(`Command output:\n${output}`);
return output;
} catch (error: any) {
console.error(`Command execution failed with error:\n${error.message}`);
throw new Error(`Failed to execute command: ${command}\nError: ${error.message}`);
}
}
const runCustomRuleTest = async (page: Page, domain_to_block: string) => {
await page.goto('/#custom_rules');
await page.getByTestId('custom_rule_textarea').fill(domain_to_block);
await page.getByTestId('apply_custom_rule').click();
const nslookupBlockedResult = await runTerminalCommand(`nslookup ${domain_to_block} 127.0.0.1`).toString();
console.info(`nslookup blocked CNAME result: '${nslookupBlockedResult}'`);
const currentRules = await page.getByTestId('custom_rule_textarea').inputValue();
console.debug(`Current rules before removal:\n${currentRules}`);
if (currentRules.includes(domain_to_block)) {
const updatedRules = currentRules
.split('\n')
.filter((line) => line.trim() !== domain_to_block.trim())
.join('\n');
await page.getByTestId('custom_rule_textarea').fill(updatedRules);
console.info(`Rule '${domain_to_block}' removed successfully.`);
console.info('Applying the updated filtering rules after removal.');
await page.getByTestId('apply_custom_rule').click();
await page.waitForLoadState('domcontentloaded');
console.info(`Filtering rules successfully updated after removing '${domain_to_block}'.`);
} else {
console.warn(`Rule '${domain_to_block}' not found. No changes were made.`);
}
const nslookupUnblockedResult = await runTerminalCommand(`nslookup ${domain_to_block} 127.0.0.1`).toString();
console.info(`nslookup unblocked CNAME result: '${nslookupUnblockedResult}'`);
};
test('Test blocking rule for apple.com', async ({ page }) => {
await runCustomRuleTest(page, 'apple.com');
});
});

View File

@@ -1,89 +0,0 @@
import { test, expect } from '@playwright/test';
import { execSync } from 'child_process';
import { ADMIN_USERNAME, ADMIN_PASSWORD } from '../constants';
test.describe('General Settings', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/login.html');
await page.getByTestId('username').click();
await page.getByTestId('username').fill(ADMIN_USERNAME);
await page.getByTestId('password').click();
await page.getByTestId('password').fill(ADMIN_PASSWORD);
await page.keyboard.press('Tab');
await page.getByTestId('sign_in').click();
await page.waitForURL((url) => !url.href.endsWith('/login.html'));
});
test('should toggle browsing security feature and verify DNS changes', async ({ page }) => {
await page.goto('/#settings');
const browsingSecurity = await page.getByTestId('safebrowsing');
const browsingSecurityLabel = await browsingSecurity.locator('xpath=following-sibling::*[1]');
const initialState = await browsingSecurity.isChecked();
if (!initialState) {
await browsingSecurityLabel.click();
await expect(browsingSecurity).toBeChecked();
}
const resultEnabled = execSync('nslookup totalvirus.com 127.0.0.1').toString();
await browsingSecurityLabel.click();
await expect(browsingSecurity).not.toBeChecked();
const resultDisabled = execSync('nslookup totalvirus.com 127.0.0.1').toString();
expect(resultEnabled).not.toEqual(resultDisabled);
if (initialState) {
await browsingSecurityLabel.click();
await expect(browsingSecurity).toBeChecked();
}
});
test('should toggle parental control feature and verify DNS changes', async ({ page }) => {
await page.goto('/#settings');
const parentalControl = page.getByTestId('parental');
const parentalControlLabel = await parentalControl.locator('xpath=following-sibling::*[1]');
const initialState = await parentalControl.isChecked();
if (!initialState) {
await parentalControlLabel.click();
await expect(parentalControl).toBeChecked();
}
const resultEnabled = execSync('nslookup pornhub.com 127.0.0.1').toString();
await parentalControlLabel.click();
await expect(parentalControl).not.toBeChecked();
const resultDisabled = execSync('nslookup pornhub.com 127.0.0.1').toString();
expect(resultEnabled).not.toEqual(resultDisabled);
if (initialState) {
await parentalControlLabel.click();
await expect(parentalControl).toBeChecked();
}
});
test('should toggle safe search feature', async ({ page }) => {
await page.goto('/#settings');
const safeSearch = page.getByTestId('safesearch');
const safeSearchLabel = await safeSearch.locator('xpath=following-sibling::*[1]');
const initialState = await safeSearch.isChecked();
await safeSearchLabel.click();
await expect(safeSearch).not.toBeChecked({ checked: initialState });
await safeSearchLabel.click();
await expect(safeSearch).toBeChecked({ checked: initialState });
});
});

View File

@@ -1,124 +0,0 @@
import { test, expect } from '@playwright/test';
import { ADMIN_USERNAME, ADMIN_PASSWORD } from '../constants';
test.describe('QueryLog', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/login.html');
await page.getByTestId('username').click();
await page.getByTestId('username').fill(ADMIN_USERNAME);
await page.getByTestId('password').click();
await page.getByTestId('password').fill(ADMIN_PASSWORD);
await page.keyboard.press('Tab');
await page.getByTestId('sign_in').click();
await page.waitForURL((url) => !url.href.endsWith('/login.html'));
});
test('Search of queryLog should work correctly', async ({ page }) => {
await page.route('/control/querylog', async (route) => {
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify(
{
"data": [
{
"answer": [
{
"type": "A",
"value": "77.88.44.242",
"ttl": 294
},
{
"type": "A",
"value": "5.255.255.242",
"ttl": 294
},
{
"type": "A",
"value": "77.88.55.242",
"ttl": 294
}
],
"answer_dnssec": false,
"cached": false,
"client": "127.0.0.1",
"client_info": {
"whois": {},
"name": "localhost",
"disallowed_rule": "127.0.0.1",
"disallowed": false
},
"client_proto": "",
"elapsedMs": "78.163167",
"question": {
"class": "IN",
"name": "ya.ru",
"type": "A"
},
"reason": "NotFilteredNotFound",
"rules": [],
"status": "NOERROR",
"time": "2024-07-17T16:02:37.500662+02:00",
"upstream": "https://dns10.quad9.net:443/dns-query"
},
{
"answer": [
{
"type": "A",
"value": "77.88.55.242",
"ttl": 351
},
{
"type": "A",
"value": "77.88.44.242",
"ttl": 351
},
{
"type": "A",
"value": "5.255.255.242",
"ttl": 351
}
],
"answer_dnssec": false,
"cached": false,
"client": "127.0.0.1",
"client_info": {
"whois": {},
"name": "localhost",
"disallowed_rule": "127.0.0.1",
"disallowed": false
},
"client_proto": "",
"elapsedMs": "5051.070708",
"question": {
"class": "IN",
"name": "ya.ru",
"type": "A"
},
"reason": "NotFilteredNotFound",
"rules": [],
"status": "NOERROR",
"time": "2024-07-17T16:02:37.4983+02:00",
"upstream": "https://dns10.quad9.net:443/dns-query"
}
],
"oldest": "2024-07-17T16:02:37.4983+02:00"
}
),
});
});
await page.goto('/#logs');
await page.getByTestId('querylog_search').fill('127.0.0.1');
const [request] = await Promise.all([
page.waitForRequest((req) => req.url().includes('/control/querylog')),
]);
if (request) {
expect(request.url()).toContain('search=127.0.0.1');
expect(await page.getByTestId('querylog_cell').first().isVisible()).toBe(true);
}
});
});

View File

@@ -1,12 +1,12 @@
# A docker file for scripts/make/build-docker.sh. # A docker file for scripts/make/build-docker.sh.
FROM alpine:3.21 FROM alpine:3.18
ARG BUILD_DATE ARG BUILD_DATE
ARG VERSION ARG VERSION
ARG VCS_REF ARG VCS_REF
LABEL \ LABEL\
maintainer="AdGuard Team <devteam@adguard.com>" \ maintainer="AdGuard Team <devteam@adguard.com>" \
org.opencontainers.image.authors="AdGuard Team <devteam@adguard.com>" \ org.opencontainers.image.authors="AdGuard Team <devteam@adguard.com>" \
org.opencontainers.image.created=$BUILD_DATE \ org.opencontainers.image.created=$BUILD_DATE \
@@ -30,8 +30,8 @@ ARG TARGETARCH
ARG TARGETOS ARG TARGETOS
ARG TARGETVARIANT ARG TARGETVARIANT
COPY --chown=nobody:nogroup \ COPY --chown=nobody:nogroup\
./${DIST_DIR}/docker/AdGuardHome_${TARGETOS}_${TARGETARCH}_${TARGETVARIANT} \ ./${DIST_DIR}/docker/AdGuardHome_${TARGETOS}_${TARGETARCH}_${TARGETVARIANT}\
/opt/adguardhome/AdGuardHome /opt/adguardhome/AdGuardHome
RUN setcap 'cap_net_bind_service=+eip' /opt/adguardhome/AdGuardHome RUN setcap 'cap_net_bind_service=+eip' /opt/adguardhome/AdGuardHome
@@ -45,15 +45,8 @@ RUN setcap 'cap_net_bind_service=+eip' /opt/adguardhome/AdGuardHome
# 3000 : TCP, UDP : HTTP(S) (alt, incl. HTTP/3) # 3000 : TCP, UDP : HTTP(S) (alt, incl. HTTP/3)
# 5443 : TCP, UDP : DNSCrypt (alt) # 5443 : TCP, UDP : DNSCrypt (alt)
# 6060 : TCP : HTTP (pprof) # 6060 : TCP : HTTP (pprof)
EXPOSE 53/tcp 53/udp \ EXPOSE 53/tcp 53/udp 67/udp 68/udp 80/tcp 443/tcp 443/udp 853/tcp\
67/udp \ 853/udp 3000/tcp 3000/udp 5443/tcp 5443/udp 6060/tcp
68/udp \
80/tcp \
443/tcp 443/udp \
853/tcp 853/udp \
3000/tcp 3000/udp \
5443/tcp 5443/udp \
6060/tcp
WORKDIR /opt/adguardhome/work WORKDIR /opt/adguardhome/work

108
go.mod
View File

@@ -1,21 +1,21 @@
module github.com/AdguardTeam/AdGuardHome module github.com/AdguardTeam/AdGuardHome
go 1.24.2 go 1.23.6
require ( require (
github.com/AdguardTeam/dnsproxy v0.75.3 github.com/AdguardTeam/dnsproxy v0.75.0
github.com/AdguardTeam/golibs v0.32.8 github.com/AdguardTeam/golibs v0.32.1
github.com/AdguardTeam/urlfilter v0.20.0 github.com/AdguardTeam/urlfilter v0.20.0
github.com/NYTimes/gziphandler v1.1.1 github.com/NYTimes/gziphandler v1.1.1
github.com/ameshkov/dnscrypt/v2 v2.4.0 github.com/ameshkov/dnscrypt/v2 v2.3.0
github.com/bluele/gcache v0.0.2 github.com/bluele/gcache v0.0.2
github.com/c2h5oh/datasize v0.0.0-20231215233829-aa82cc1e6500 github.com/c2h5oh/datasize v0.0.0-20231215233829-aa82cc1e6500
github.com/digineo/go-ipset/v2 v2.2.1 github.com/digineo/go-ipset/v2 v2.2.1
github.com/fsnotify/fsnotify v1.9.0 github.com/fsnotify/fsnotify v1.8.0
// TODO(e.burkov): This package is deprecated; find a new one or use our // TODO(e.burkov): This package is deprecated; find a new one or use our
// own code for that. Perhaps, use gopacket. // own code for that. Perhaps, use gopacket.
github.com/go-ping/ping v1.2.0 github.com/go-ping/ping v1.2.0
github.com/google/go-cmp v0.7.0 github.com/google/go-cmp v0.6.0
github.com/google/gopacket v1.1.19 github.com/google/gopacket v1.1.19
github.com/google/renameio/v2 v2.0.0 github.com/google/renameio/v2 v2.0.0
github.com/google/uuid v1.6.0 github.com/google/uuid v1.6.0
@@ -28,105 +28,41 @@ require (
// TODO(a.garipov): This package is deprecated; find a new one or use our // TODO(a.garipov): This package is deprecated; find a new one or use our
// own code for that. Perhaps, use gopacket. // own code for that. Perhaps, use gopacket.
github.com/mdlayher/raw v0.1.0 github.com/mdlayher/raw v0.1.0
github.com/miekg/dns v1.1.65 github.com/miekg/dns v1.1.63
github.com/quic-go/quic-go v0.50.1 github.com/quic-go/quic-go v0.49.0
github.com/stretchr/testify v1.10.0 github.com/stretchr/testify v1.10.0
github.com/ti-mo/netfilter v0.5.2 github.com/ti-mo/netfilter v0.5.2
go.etcd.io/bbolt v1.4.0 go.etcd.io/bbolt v1.4.0
golang.org/x/crypto v0.37.0 golang.org/x/crypto v0.32.0
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 golang.org/x/exp v0.0.0-20250207012021-f9890c6ad9f3
golang.org/x/net v0.39.0 golang.org/x/net v0.34.0
golang.org/x/sys v0.32.0 golang.org/x/sys v0.30.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1 gopkg.in/natefinch/lumberjack.v2 v2.2.1
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
howett.net/plist v1.0.1 howett.net/plist v1.0.1
) )
require ( require (
cloud.google.com/go v0.120.1 // indirect github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect
cloud.google.com/go/ai v0.10.2 // indirect github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 // indirect
cloud.google.com/go/auth v0.16.0 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
cloud.google.com/go/compute/metadata v0.6.0 // indirect
cloud.google.com/go/longrunning v0.6.7 // indirect
github.com/BurntSushi/toml v1.5.0 // indirect
github.com/ameshkov/dnsstamps v1.0.3 // indirect github.com/ameshkov/dnsstamps v1.0.3 // indirect
github.com/beefsack/go-rate v0.0.0-20220214233405-116f4ca011a0 // indirect github.com/beefsack/go-rate v0.0.0-20220214233405-116f4ca011a0 // indirect
github.com/ccojocar/zxcvbn-go v1.0.4 // indirect
github.com/davecgh/go-spew v1.1.1 // 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/go-task/slim-sprig/v3 v3.0.0 // indirect
github.com/golangci/misspell v0.6.0 // indirect github.com/google/pprof v0.0.0-20250202011525-fc3143867406 // indirect
github.com/google/generative-ai-go v0.19.0 // indirect
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect
github.com/google/s2a-go v0.1.9 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect
github.com/googleapis/gax-go/v2 v2.14.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/mdlayher/socket v0.5.1 // indirect
github.com/onsi/ginkgo/v2 v2.23.4 // indirect github.com/onsi/ginkgo/v2 v2.22.2 // indirect
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
github.com/pierrec/lz4/v4 v4.1.22 // indirect github.com/pierrec/lz4/v4 v4.1.22 // indirect
github.com/pkg/errors v0.9.1 // indirect github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/quic-go/qpack v0.5.1 // indirect github.com/quic-go/qpack v0.5.1 // indirect
github.com/robfig/cron/v3 v3.0.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.3 // indirect
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 // indirect github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 // indirect
github.com/uudashr/gocognit v1.2.0 // indirect go.uber.org/mock v0.5.0 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect golang.org/x/mod v0.23.0 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect golang.org/x/sync v0.11.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect golang.org/x/text v0.22.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect golang.org/x/tools v0.29.0 // indirect
go.opentelemetry.io/otel v1.35.0 // indirect gonum.org/v1/gonum v0.15.1 // indirect
go.opentelemetry.io/otel/metric v1.35.0 // indirect
go.opentelemetry.io/otel/trace v1.35.0 // indirect
go.uber.org/automaxprocs v1.6.0 // indirect
go.uber.org/mock v0.5.1 // indirect
golang.org/x/exp/typeparams v0.0.0-20250408133849-7e4ce0ab07d0 // indirect
golang.org/x/mod v0.24.0 // indirect
golang.org/x/oauth2 v0.29.0 // indirect
golang.org/x/sync v0.13.0 // indirect
golang.org/x/telemetry v0.0.0-20250417124945-06ef541f3fa3 // indirect
golang.org/x/term v0.31.0 // indirect
golang.org/x/text v0.24.0 // indirect
golang.org/x/time v0.11.0 // indirect
golang.org/x/tools v0.32.0 // indirect
golang.org/x/vuln v1.1.4 // indirect
gonum.org/v1/gonum v0.16.0 // indirect
google.golang.org/api v0.229.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250414145226-207652e42e2e // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250414145226-207652e42e2e // indirect
google.golang.org/grpc v1.71.1 // indirect
google.golang.org/protobuf v1.36.6 // indirect
honnef.co/go/tools v0.6.1 // indirect
mvdan.cc/editorconfig v0.3.0 // indirect
mvdan.cc/gofumpt v0.8.0 // indirect
mvdan.cc/sh/v3 v3.11.0 // indirect
mvdan.cc/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
) )

215
go.sum
View File

@@ -1,27 +1,17 @@
cloud.google.com/go v0.120.1 h1:Z+5V7yd383+9617XDCyszmK5E4wJRJL+tquMfDj9hLM= github.com/AdguardTeam/dnsproxy v0.75.0 h1:v8/Oq/xPYzNoALR7SEUZEIbKmjnPcXLVhJLFVbrozEc=
cloud.google.com/go v0.120.1/go.mod h1:56Vs7sf/i2jYM6ZL9NYlC82r04PThNcPS5YgFmb0rp8= github.com/AdguardTeam/dnsproxy v0.75.0/go.mod h1:O2qoXwF4BUBFui7OMUiWSYwapEDcYxKWeur4+jfy9nM=
cloud.google.com/go/ai v0.10.2 h1:5NHzmZlRs+3kvlsVdjT0cTnLrjQdROJ/8VOljVfs+8o= github.com/AdguardTeam/golibs v0.32.1 h1:Ajf6Q0k+A9zjFbj8HOzNAbHImrV4JtbT0vwy02D6VeI=
cloud.google.com/go/ai v0.10.2/go.mod h1:xZuZuE9d3RgsR132meCnPadiU9XV0qXjpLr+P4J46eE= github.com/AdguardTeam/golibs v0.32.1/go.mod h1:dXRLSsnJQDxOfQVl0ochy1bfk4NgnJQGQdR1YPJdwcw=
cloud.google.com/go/auth v0.16.0 h1:Pd8P1s9WkcrBE2n/PhAwKsdrR35V3Sg2II9B+ndM3CU=
cloud.google.com/go/auth v0.16.0/go.mod h1:1howDHJ5IETh/LwYs3ZxvlkXF48aSqqJUM+5o02dNOI=
cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc=
cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=
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.7 h1:IGtfDWHhQCgCjwQjV9iiLnUta9LBCo8R9QmAFsS/PrE=
cloud.google.com/go/longrunning v0.6.7/go.mod h1:EAFV3IZAKmM56TyiE6VAP3VoTzhZzySwI/YI1s/nRsY=
github.com/AdguardTeam/dnsproxy v0.75.3 h1:pxlMNO+cP1A3px40PY/old6SAE82pkdLPUA2P3KY8u0=
github.com/AdguardTeam/dnsproxy v0.75.3/go.mod h1:50OyTHao+uQzUJiXay08hgfvWQ3o2Q2WV99W8u8ypDE=
github.com/AdguardTeam/golibs v0.32.8 h1:O3mc3kYcPkW3kbmd+gqzFNgUka13a+iBgFLThwOYSQE=
github.com/AdguardTeam/golibs v0.32.8/go.mod h1:McV1QFFlKLElKa306V4OL/T2kr7564PhsayfvTWYBVs=
github.com/AdguardTeam/urlfilter v0.20.0 h1:X32qiuVCVd8WDYCEsbdZKfXMzwdVqrdulamtUi4rmzs= github.com/AdguardTeam/urlfilter v0.20.0 h1:X32qiuVCVd8WDYCEsbdZKfXMzwdVqrdulamtUi4rmzs=
github.com/AdguardTeam/urlfilter v0.20.0/go.mod h1:gjrywLTxfJh6JOkwi9SU+frhP7kVVEZ5exFGkR99qpk= github.com/AdguardTeam/urlfilter v0.20.0/go.mod h1:gjrywLTxfJh6JOkwi9SU+frhP7kVVEZ5exFGkR99qpk=
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I= github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I=
github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
github.com/ameshkov/dnscrypt/v2 v2.4.0 h1:if6ZG2cuQmcP2TwSY+D0+8+xbPfoatufGlOQTMNkI9o= github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY=
github.com/ameshkov/dnscrypt/v2 v2.4.0/go.mod h1:WpEFV2uhebXb8Jhes/5/fSdpmhGV8TL22RDaeWwV6hI= github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA=
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 h1:52m0LGchQBBVqJRyYYufQuIbVqRawmubW3OFGqK1ekw=
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635/go.mod h1:lmLxL+FV291OopO93Bwf9fQLQeLyt33VJRUg5VJ30us=
github.com/ameshkov/dnscrypt/v2 v2.3.0 h1:pDXDF7eFa6Lw+04C0hoMh8kCAQM8NwUdFEllSP2zNLs=
github.com/ameshkov/dnscrypt/v2 v2.3.0/go.mod h1:N5hDwgx2cNb4Ay7AhvOSKst+eUiOZ/vbKRO9qMpQttE=
github.com/ameshkov/dnsstamps v1.0.3 h1:Srzik+J9mivH1alRACTbys2xOxs0lRH9qnTA7Y1OYVo= github.com/ameshkov/dnsstamps v1.0.3 h1:Srzik+J9mivH1alRACTbys2xOxs0lRH9qnTA7Y1OYVo=
github.com/ameshkov/dnsstamps v1.0.3/go.mod h1:Ii3eUu73dx4Vw5O4wjzmT5+lkCwovjzaEZZ4gKyIH5A= github.com/ameshkov/dnsstamps v1.0.3/go.mod h1:Ii3eUu73dx4Vw5O4wjzmT5+lkCwovjzaEZZ4gKyIH5A=
github.com/beefsack/go-rate v0.0.0-20220214233405-116f4ca011a0 h1:0b2vaepXIfMsG++IsjHiI2p4bxALD1Y2nQKGMR5zDQM= github.com/beefsack/go-rate v0.0.0-20220214233405-116f4ca011a0 h1:0b2vaepXIfMsG++IsjHiI2p4bxALD1Y2nQKGMR5zDQM=
@@ -30,67 +20,35 @@ 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/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 h1:6lhrsTEnloDPXyeZBvSYvQf8u86jbKehZPVDDlkgDl4=
github.com/c2h5oh/datasize v0.0.0-20231215233829-aa82cc1e6500/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M= github.com/c2h5oh/datasize v0.0.0-20231215233829-aa82cc1e6500/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M=
github.com/ccojocar/zxcvbn-go v1.0.4 h1:FWnCIRMXPj43ukfX000kvBZvV6raSxakYr1nzyNrUcc=
github.com/ccojocar/zxcvbn-go v1.0.4/go.mod h1:3GxGX+rHmueTUMvm5ium7irpyjmm7ikxYFOSJB21Das=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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 h1:k6skY+0fMqeUjjeWO/m5OuWPSZUAn7AucHMnQ1MX77g=
github.com/digineo/go-ipset/v2 v2.2.1/go.mod h1:wBsNzJlZlABHUITkesrggFnZQtgW5wkqw1uo8Qxe0VU= 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/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.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 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= 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 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= 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 h1:vsJ8slZBZAXNCK4dPcI2PEE9eM9n9RbXbGouVQ/Y4yQ=
github.com/go-ping/ping v1.2.0/go.mod h1:xIFjORFzTxqIV/tDVGO4eDy/bLuSyawEeojSm3GfRGk= 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 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= 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.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.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.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
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 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8= github.com/google/pprof v0.0.0-20250202011525-fc3143867406 h1:wlQI2cYY0BsWmmPPAnxfQ8SDW0S3Jasn+4B8kXFxprg=
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= 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 h1:UifI23ZTGY8Tt29JbYFiuyIU3eX+RNFtUwefq9qAhxg=
github.com/google/renameio/v2 v2.0.0/go.mod h1:BtmJXm5YlszgC+TD4HOEEUFgkJP3nLxehU6hfe7jRt4= 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.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 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4=
github.com/googleapis/enterprise-certificate-proxy v0.3.6/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 h1:/jC7qQFrv8CrSJVmaolDVOxTfS9kc36uB6H40kdbQq8=
github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis= 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= github.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905 h1:q3OEI9RaN/wwcx+qgGo6ZaoJkCiDYe/gjDLfq7lQQF4=
@@ -99,14 +57,8 @@ 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.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 h1:elKwZS1OcdQ0WwEDBeqxKwb7WB62QX8bvZ/FJnVXIfk=
github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86/go.mod h1:aFAMtuldEgx/4q7iSGazk22+IcgvtiC+HIimFO9XlS8= 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 h1:ZvePhAHfvo0A7Mftk/tEzqEZ7Q4lgnR8sGz4xu1YX60=
github.com/kardianos/service v1.2.2/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM= 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 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
@@ -124,12 +76,14 @@ github.com/mdlayher/raw v0.1.0/go.mod h1:yXnxvs6c0XoF/aK52/H5PjsVHmWBCFfZUfoh/Y5
github.com/mdlayher/socket v0.2.1/go.mod h1:QLlNPkFR88mRUNQIzRBMfXxwKal8H7u1h3bL1CV+f0E= github.com/mdlayher/socket v0.2.1/go.mod h1:QLlNPkFR88mRUNQIzRBMfXxwKal8H7u1h3bL1CV+f0E=
github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos= github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos=
github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ= github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ=
github.com/miekg/dns v1.1.65 h1:0+tIPHzUW0GCge7IiK3guGP57VAw7hoPDfApjkMD1Fc= github.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY=
github.com/miekg/dns v1.1.65/go.mod h1:Dzw9769uoKVaLuODMDZz9M6ynFU6Em65csPuoi8G0ck= github.com/miekg/dns v1.1.63/go.mod h1:6NGHfjhpmr5lt3XPLuyfDJi5AXbNIPM9PY6H6sF1Nfs=
github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/onsi/gomega v1.36.3 h1:hID7cr8t3Wp26+cYnfcjR6HpJ00fdogN6dqZ1t6IylU= github.com/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU=
github.com/onsi/gomega v1.36.3/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0= 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=
github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY=
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU= github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU=
@@ -141,18 +95,12 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
github.com/quic-go/quic-go v0.50.1 h1:unsgjFIUqW8a2oopkY7YNONpV1gYND6Nt9hnt1PN94Q= github.com/quic-go/quic-go v0.49.0 h1:w5iJHXwHxs1QxyBv1EHKuC50GX5to8mJAxvtnttJp94=
github.com/quic-go/quic-go v0.50.1/go.mod h1:Vim6OmUvlYdwBhXP9ZVrtGmCMWa3wEqhq3NgYrI8b4E= 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 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= 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.3 h1:mRrCNmRF2NgZp4RJ8oJ6yPJ7G4x6OCiAXHd8x4trLRc=
github.com/securego/gosec/v2 v2.22.3/go.mod h1:42M9Xs0v1WseinaB/BmNGO8AVqG8vRfhC2686ACY48k=
github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI= 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/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk=
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
@@ -172,63 +120,34 @@ github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+F
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= 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 h1:pyC9PaHYZFgEKFdlp3G8RaCKgVpHZnecvArXvPXcFkM=
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701/go.mod h1:P3a5rG4X7tI17Nn3aOIAYr5HbIMukwXG0urG0WuL8OA= 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 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= 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 h1:TU77id3TnN/zKr7CO/uk+fBCwF2jGcMuw2B/FMAzYIk=
go.etcd.io/bbolt v1.4.0/go.mod h1:AsD+OCi/qPN1giOX1aiLAha3o1U8rAz65bvN4j0sRuk= go.etcd.io/bbolt v1.4.0/go.mod h1:AsD+OCi/qPN1giOX1aiLAha3o1U8rAz65bvN4j0sRuk=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
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/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
go.uber.org/mock v0.5.1 h1:ASgazW/qBmR+A32MYFDB6E2POoTgOwT509VP0CT/fjs=
go.uber.org/mock v0.5.1/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-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.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE= golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM= golang.org/x/exp v0.0.0-20250207012021-f9890c6ad9f3 h1:qNgPs5exUA+G0C96DrPwNrvLSj7GT/9D+3WMWUcUg34=
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8= golang.org/x/exp v0.0.0-20250207012021-f9890c6ad9f3/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU=
golang.org/x/exp/typeparams v0.0.0-20250408133849-7e4ce0ab07d0 h1:oMe07YcizemJ09rs2kRkFYAp0pt4e1lYLwPWiEGMpXE=
golang.org/x/exp/typeparams v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:LKZHyeOpPuZcMgxeHjJp4p5yvxrCX1xDvH10zYHhjjQ=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 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.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM=
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
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-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-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-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-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-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.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY= golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98=
golang.org/x/oauth2 v0.29.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-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.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610= golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-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-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190322080309-f49334f85ddc/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190322080309-f49334f85ddc/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -236,65 +155,35 @@ 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-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-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-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-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.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.4.1-0.20230131160137-e7d7f63158de/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/telemetry v0.0.0-20250417124945-06ef541f3fa3 h1:RXY2+rSHXvxO2Y+gKrPjYVaEoGOqh3VEXFhnWAt1Irg=
golang.org/x/telemetry v0.0.0-20250417124945-06ef541f3fa3/go.mod h1:RoaXAWDwS90j6FxVKwJdBV+0HCU+llrKUGgJaxiKl6M=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o=
golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 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.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 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.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE=
golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU= golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588=
golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s=
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-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-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.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.15.1/go.mod h1:eZTZuRFrzu5pcyjN5wJhcIhnUdNijYxX1T2IcrOGY0o=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= google.golang.org/protobuf v1.36.3 h1:82DV7MYdb8anAVi3qge1wSnMDrnKK7ebr+I0hHRN1BU=
google.golang.org/api v0.229.0 h1:p98ymMtqeJ5i3lIBMj5MpR9kzIIgzpHHh8vQ+vgAzx8= google.golang.org/protobuf v1.36.3/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
google.golang.org/api v0.229.0/go.mod h1:wyDfmq5g1wYJWn29O22FDWN48P7Xcz0xz+LBpptYvB0=
google.golang.org/genproto/googleapis/api v0.0.0-20250414145226-207652e42e2e h1:UdXH7Kzbj+Vzastr5nVfccbmFsmYNygVLSPk1pEfDoY=
google.golang.org/genproto/googleapis/api v0.0.0-20250414145226-207652e42e2e/go.mod h1:085qFyf2+XaZlRdCgKNCIZ3afY2p4HHZdoIRpId8F4A=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250414145226-207652e42e2e h1:ztQaXfzEXTmCBvbtWYRhJxW+0iJcz2qXfd38/e9l7bA=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250414145226-207652e42e2e/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
google.golang.org/grpc v1.71.1 h1:ffsFWr7ygTUscGPI0KKK6TLrGz0476KUvvsbqWK0rPI=
google.golang.org/grpc v1.71.1/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec=
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= 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/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.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 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 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 h1:37GdZ8tP09Q35o9ych3ehygcsL+HqKSwzctveSlarvM=
howett.net/plist v1.0.1/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g= 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.8.0 h1:nZUCeC2ViFaerTcYKstMmfysj6uhQrA2vJe+2vwGU6k=
mvdan.cc/gofumpt v0.8.0/go.mod h1:vEYnSzyGPmjvFkqJWtXkh79UwPWP9/HMxQdGEXZHjpg=
mvdan.cc/sh/v3 v3.11.0 h1:q5h+XMDRfUGUedCqFFsjoFjrhwf2Mvtt1rkMvVz0blw=
mvdan.cc/sh/v3 v3.11.0/go.mod h1:LRM+1NjoYCzuq/WZ6y44x14YNAI0NK7FLPeQSaFagGg=
mvdan.cc/unparam v0.0.0-20250301125049-0df0534333a4 h1:WjUu4yQoT5BHT1w8Zu56SP8367OuBV5jvo+4Ulppyf8=
mvdan.cc/unparam v0.0.0-20250301125049-0df0534333a4/go.mod h1:rthT7OuvRbaGcd5ginj6dA2oLE7YNlta9qhBNNdCaLE=

View File

@@ -1,15 +1,14 @@
package aghalg_test package aghalg
import ( import (
"strings" "strings"
"testing" "testing"
"github.com/AdguardTeam/AdGuardHome/internal/aghalg"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func TestNewSortedMap(t *testing.T) { func TestNewSortedMap(t *testing.T) {
var m aghalg.SortedMap[string, int] var m SortedMap[string, int]
letters := []string{} letters := []string{}
for i := range 10 { for i := range 10 {
@@ -18,7 +17,7 @@ func TestNewSortedMap(t *testing.T) {
} }
t.Run("create_and_fill", func(t *testing.T) { t.Run("create_and_fill", func(t *testing.T) {
m = aghalg.NewSortedMap[string, int](strings.Compare) m = NewSortedMap[string, int](strings.Compare)
nums := []int{} nums := []int{}
for i, r := range letters { for i, r := range letters {
@@ -69,7 +68,7 @@ func TestNewSortedMap_nil(t *testing.T) {
val = "val" val = "val"
) )
var m aghalg.SortedMap[string, string] var m SortedMap[string, string]
assert.Panics(t, func() { assert.Panics(t, func() {
m.Set(key, val) m.Set(key, val)

View File

@@ -1,10 +1,9 @@
package aghnet_test package aghnet
import ( import (
"net/netip" "net/netip"
"testing" "testing"
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@@ -30,13 +29,13 @@ func TestGenerateHostName(t *testing.T) {
for _, tc := range testCases { for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
hostname := aghnet.GenerateHostname(tc.ip) hostname := GenerateHostname(tc.ip)
assert.Equal(t, tc.want, hostname) assert.Equal(t, tc.want, hostname)
}) })
} }
}) })
t.Run("invalid", func(t *testing.T) { t.Run("invalid", func(t *testing.T) {
assert.Panics(t, func() { aghnet.GenerateHostname(netip.Addr{}) }) assert.Panics(t, func() { GenerateHostname(netip.Addr{}) })
}) })
} }

View File

@@ -1,24 +1,22 @@
package aghnet_test package aghnet
import ( import (
"net" "net"
"testing" "testing"
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
"github.com/AdguardTeam/golibs/errors" "github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/testutil" "github.com/AdguardTeam/golibs/testutil"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
// fakeIface is a stub implementation of [aghnet.NetIface] interface to simplify // fakeIface is a stub implementation of aghnet.NetIface to simplify testing.
// testing.
type fakeIface struct { type fakeIface struct {
err error err error
addrs []net.Addr addrs []net.Addr
} }
// Addrs implements the [aghnet.NetIface] interface for *fakeIface. // Addrs implements the NetIface interface for *fakeIface.
func (iface *fakeIface) Addrs() (addrs []net.Addr, err error) { func (iface *fakeIface) Addrs() (addrs []net.Addr, err error) {
if iface.err != nil { if iface.err != nil {
return nil, iface.err return nil, iface.err
@@ -27,9 +25,6 @@ func (iface *fakeIface) Addrs() (addrs []net.Addr, err error) {
return iface.addrs, nil return iface.addrs, nil
} }
// type check
var _ aghnet.NetIface = (*fakeIface)(nil)
func TestIfaceIPAddrs(t *testing.T) { func TestIfaceIPAddrs(t *testing.T) {
const errTest errors.Error = "test error" const errTest errors.Error = "test error"
@@ -40,76 +35,76 @@ func TestIfaceIPAddrs(t *testing.T) {
addr6 := &net.IPNet{IP: ip6} addr6 := &net.IPNet{IP: ip6}
testCases := []struct { testCases := []struct {
iface aghnet.NetIface iface NetIface
name string name string
wantErrMsg string wantErrMsg string
want []net.IP want []net.IP
ipv aghnet.IPVersion ipv IPVersion
}{{ }{{
iface: &fakeIface{addrs: []net.Addr{addr4}, err: nil}, iface: &fakeIface{addrs: []net.Addr{addr4}, err: nil},
name: "ipv4_success", name: "ipv4_success",
wantErrMsg: "", wantErrMsg: "",
want: []net.IP{ip4}, want: []net.IP{ip4},
ipv: aghnet.IPVersion4, ipv: IPVersion4,
}, { }, {
iface: &fakeIface{addrs: []net.Addr{addr6, addr4}, err: nil}, iface: &fakeIface{addrs: []net.Addr{addr6, addr4}, err: nil},
name: "ipv4_success_with_ipv6", name: "ipv4_success_with_ipv6",
wantErrMsg: "", wantErrMsg: "",
want: []net.IP{ip4}, want: []net.IP{ip4},
ipv: aghnet.IPVersion4, ipv: IPVersion4,
}, { }, {
iface: &fakeIface{addrs: []net.Addr{addr4}, err: errTest}, iface: &fakeIface{addrs: []net.Addr{addr4}, err: errTest},
name: "ipv4_error", name: "ipv4_error",
wantErrMsg: errTest.Error(), wantErrMsg: errTest.Error(),
want: nil, want: nil,
ipv: aghnet.IPVersion4, ipv: IPVersion4,
}, { }, {
iface: &fakeIface{addrs: []net.Addr{addr6}, err: nil}, iface: &fakeIface{addrs: []net.Addr{addr6}, err: nil},
name: "ipv6_success", name: "ipv6_success",
wantErrMsg: "", wantErrMsg: "",
want: []net.IP{ip6}, want: []net.IP{ip6},
ipv: aghnet.IPVersion6, ipv: IPVersion6,
}, { }, {
iface: &fakeIface{addrs: []net.Addr{addr6, addr4}, err: nil}, iface: &fakeIface{addrs: []net.Addr{addr6, addr4}, err: nil},
name: "ipv6_success_with_ipv4", name: "ipv6_success_with_ipv4",
wantErrMsg: "", wantErrMsg: "",
want: []net.IP{ip6}, want: []net.IP{ip6},
ipv: aghnet.IPVersion6, ipv: IPVersion6,
}, { }, {
iface: &fakeIface{addrs: []net.Addr{addr6}, err: errTest}, iface: &fakeIface{addrs: []net.Addr{addr6}, err: errTest},
name: "ipv6_error", name: "ipv6_error",
wantErrMsg: errTest.Error(), wantErrMsg: errTest.Error(),
want: nil, want: nil,
ipv: aghnet.IPVersion6, ipv: IPVersion6,
}, { }, {
iface: &fakeIface{addrs: nil, err: nil}, iface: &fakeIface{addrs: nil, err: nil},
name: "bad_proto", name: "bad_proto",
wantErrMsg: "invalid ip version 10", wantErrMsg: "invalid ip version 10",
want: nil, want: nil,
ipv: aghnet.IPVersion6 + aghnet.IPVersion4, ipv: IPVersion6 + IPVersion4,
}, { }, {
iface: &fakeIface{addrs: []net.Addr{&net.IPAddr{IP: ip4}}, err: nil}, iface: &fakeIface{addrs: []net.Addr{&net.IPAddr{IP: ip4}}, err: nil},
name: "ipaddr_v4", name: "ipaddr_v4",
wantErrMsg: "", wantErrMsg: "",
want: []net.IP{ip4}, want: []net.IP{ip4},
ipv: aghnet.IPVersion4, ipv: IPVersion4,
}, { }, {
iface: &fakeIface{addrs: []net.Addr{&net.IPAddr{IP: ip6, Zone: ""}}, err: nil}, iface: &fakeIface{addrs: []net.Addr{&net.IPAddr{IP: ip6, Zone: ""}}, err: nil},
name: "ipaddr_v6", name: "ipaddr_v6",
wantErrMsg: "", wantErrMsg: "",
want: []net.IP{ip6}, want: []net.IP{ip6},
ipv: aghnet.IPVersion6, ipv: IPVersion6,
}, { }, {
iface: &fakeIface{addrs: []net.Addr{&net.UnixAddr{}}, err: nil}, iface: &fakeIface{addrs: []net.Addr{&net.UnixAddr{}}, err: nil},
name: "non-ipv4", name: "non-ipv4",
wantErrMsg: "", wantErrMsg: "",
want: nil, want: nil,
ipv: aghnet.IPVersion4, ipv: IPVersion4,
}} }}
for _, tc := range testCases { for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
got, err := aghnet.IfaceIPAddrs(tc.iface, tc.ipv) got, err := IfaceIPAddrs(tc.iface, tc.ipv)
testutil.AssertErrorMsg(t, tc.wantErrMsg, err) testutil.AssertErrorMsg(t, tc.wantErrMsg, err)
assert.Equal(t, tc.want, got) assert.Equal(t, tc.want, got)
@@ -123,10 +118,7 @@ type waitingFakeIface struct {
n int n int
} }
// type check // Addrs implements the NetIface interface for *waitingFakeIface.
var _ aghnet.NetIface = (*waitingFakeIface)(nil)
// Addrs implements the [aghnet.NetIface] interface for *waitingFakeIface.
func (iface *waitingFakeIface) Addrs() (addrs []net.Addr, err error) { func (iface *waitingFakeIface) Addrs() (addrs []net.Addr, err error) {
if iface.err != nil { if iface.err != nil {
return nil, iface.err return nil, iface.err
@@ -151,76 +143,76 @@ func TestIfaceDNSIPAddrs(t *testing.T) {
addr6 := &net.IPNet{IP: ip6} addr6 := &net.IPNet{IP: ip6}
testCases := []struct { testCases := []struct {
iface aghnet.NetIface iface NetIface
wantErr error wantErr error
name string name string
want []net.IP want []net.IP
ipv aghnet.IPVersion ipv IPVersion
}{{ }{{
name: "ipv4_success", name: "ipv4_success",
iface: &fakeIface{addrs: []net.Addr{addr4}, err: nil}, iface: &fakeIface{addrs: []net.Addr{addr4}, err: nil},
ipv: aghnet.IPVersion4, ipv: IPVersion4,
want: []net.IP{ip4, ip4}, want: []net.IP{ip4, ip4},
wantErr: nil, wantErr: nil,
}, { }, {
name: "ipv4_success_with_ipv6", name: "ipv4_success_with_ipv6",
iface: &fakeIface{addrs: []net.Addr{addr6, addr4}, err: nil}, iface: &fakeIface{addrs: []net.Addr{addr6, addr4}, err: nil},
ipv: aghnet.IPVersion4, ipv: IPVersion4,
want: []net.IP{ip4, ip4}, want: []net.IP{ip4, ip4},
wantErr: nil, wantErr: nil,
}, { }, {
name: "ipv4_error", name: "ipv4_error",
iface: &fakeIface{addrs: []net.Addr{addr4}, err: errTest}, iface: &fakeIface{addrs: []net.Addr{addr4}, err: errTest},
ipv: aghnet.IPVersion4, ipv: IPVersion4,
want: nil, want: nil,
wantErr: errTest, wantErr: errTest,
}, { }, {
name: "ipv4_wait", name: "ipv4_wait",
iface: &waitingFakeIface{addrs: []net.Addr{addr4}, err: nil, n: 1}, iface: &waitingFakeIface{addrs: []net.Addr{addr4}, err: nil, n: 1},
ipv: aghnet.IPVersion4, ipv: IPVersion4,
want: []net.IP{ip4, ip4}, want: []net.IP{ip4, ip4},
wantErr: nil, wantErr: nil,
}, { }, {
name: "ipv6_success", name: "ipv6_success",
iface: &fakeIface{addrs: []net.Addr{addr6}, err: nil}, iface: &fakeIface{addrs: []net.Addr{addr6}, err: nil},
ipv: aghnet.IPVersion6, ipv: IPVersion6,
want: []net.IP{ip6, ip6}, want: []net.IP{ip6, ip6},
wantErr: nil, wantErr: nil,
}, { }, {
name: "ipv6_success_with_ipv4", name: "ipv6_success_with_ipv4",
iface: &fakeIface{addrs: []net.Addr{addr6, addr4}, err: nil}, iface: &fakeIface{addrs: []net.Addr{addr6, addr4}, err: nil},
ipv: aghnet.IPVersion6, ipv: IPVersion6,
want: []net.IP{ip6, ip6}, want: []net.IP{ip6, ip6},
wantErr: nil, wantErr: nil,
}, { }, {
name: "ipv6_error", name: "ipv6_error",
iface: &fakeIface{addrs: []net.Addr{addr6}, err: errTest}, iface: &fakeIface{addrs: []net.Addr{addr6}, err: errTest},
ipv: aghnet.IPVersion6, ipv: IPVersion6,
want: nil, want: nil,
wantErr: errTest, wantErr: errTest,
}, { }, {
name: "ipv6_wait", name: "ipv6_wait",
iface: &waitingFakeIface{addrs: []net.Addr{addr6}, err: nil, n: 1}, iface: &waitingFakeIface{addrs: []net.Addr{addr6}, err: nil, n: 1},
ipv: aghnet.IPVersion6, ipv: IPVersion6,
want: []net.IP{ip6, ip6}, want: []net.IP{ip6, ip6},
wantErr: nil, wantErr: nil,
}, { }, {
name: "empty", name: "empty",
iface: &fakeIface{addrs: nil, err: nil}, iface: &fakeIface{addrs: nil, err: nil},
ipv: aghnet.IPVersion4, ipv: IPVersion4,
want: nil, want: nil,
wantErr: nil, wantErr: nil,
}, { }, {
name: "many", name: "many",
iface: &fakeIface{addrs: []net.Addr{addr4, addr4}}, iface: &fakeIface{addrs: []net.Addr{addr4, addr4}},
ipv: aghnet.IPVersion4, ipv: IPVersion4,
want: []net.IP{ip4, ip4}, want: []net.IP{ip4, ip4},
wantErr: nil, wantErr: nil,
}} }}
for _, tc := range testCases { for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
got, err := aghnet.IfaceDNSIPAddrs(tc.iface, tc.ipv, 2, 0) got, err := IfaceDNSIPAddrs(tc.iface, tc.ipv, 2, 0)
require.ErrorIs(t, err, tc.wantErr) require.ErrorIs(t, err, tc.wantErr)
assert.Equal(t, tc.want, got) assert.Equal(t, tc.want, got)

View File

@@ -1,10 +1,9 @@
package aghnet_test package aghnet
import ( import (
"net" "net"
"testing" "testing"
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
"github.com/AdguardTeam/golibs/netutil" "github.com/AdguardTeam/golibs/netutil"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@@ -19,7 +18,7 @@ func TestIPMut(t *testing.T) {
}} }}
t.Run("nil_no_mut", func(t *testing.T) { t.Run("nil_no_mut", func(t *testing.T) {
ipmut := aghnet.NewIPMut(nil) ipmut := NewIPMut(nil)
ips := netutil.CloneIPs(testIPs) ips := netutil.CloneIPs(testIPs)
for i := range ips { for i := range ips {
@@ -29,7 +28,7 @@ func TestIPMut(t *testing.T) {
}) })
t.Run("not_nil_mut", func(t *testing.T) { t.Run("not_nil_mut", func(t *testing.T) {
ipmut := aghnet.NewIPMut(func(ip net.IP) { ipmut := NewIPMut(func(ip net.IP) {
for i := range ip { for i := range ip {
ip[i] = 0 ip[i] = 0
} }

View File

@@ -1,5 +1,3 @@
//go:build darwin
package aghnet package aghnet
import ( import (

View File

@@ -1,24 +0,0 @@
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] == '#'
}

View File

@@ -1,26 +0,0 @@
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))
}
}

View File

@@ -10,6 +10,7 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/next/agh" "github.com/AdguardTeam/AdGuardHome/internal/next/agh"
"github.com/AdguardTeam/AdGuardHome/internal/rdns" "github.com/AdguardTeam/AdGuardHome/internal/rdns"
"github.com/AdguardTeam/AdGuardHome/internal/whois" "github.com/AdguardTeam/AdGuardHome/internal/whois"
"github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/dnsproxy/upstream" "github.com/AdguardTeam/dnsproxy/upstream"
"github.com/miekg/dns" "github.com/miekg/dns"
) )
@@ -120,6 +121,26 @@ func (p *AddressUpdater) UpdateAddress(
p.OnUpdateAddress(ctx, ip, host, info) 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 // Package filtering
// Resolver is a fake [filtering.Resolver] implementation for tests. // Resolver is a fake [filtering.Resolver] implementation for tests.

View File

@@ -3,6 +3,7 @@ package aghtest_test
import ( import (
"github.com/AdguardTeam/AdGuardHome/internal/aghtest" "github.com/AdguardTeam/AdGuardHome/internal/aghtest"
"github.com/AdguardTeam/AdGuardHome/internal/client" "github.com/AdguardTeam/AdGuardHome/internal/client"
"github.com/AdguardTeam/AdGuardHome/internal/dnsforward"
"github.com/AdguardTeam/AdGuardHome/internal/filtering" "github.com/AdguardTeam/AdGuardHome/internal/filtering"
) )
@@ -11,6 +12,9 @@ import (
// type check // type check
var _ filtering.Resolver = (*aghtest.Resolver)(nil) var _ filtering.Resolver = (*aghtest.Resolver)(nil)
// type check
var _ dnsforward.ClientsContainer = (*aghtest.ClientsContainer)(nil)
// type check // type check
// //
// TODO(s.chzhen): It's here to avoid the import cycle. Remove it. // TODO(s.chzhen): It's here to avoid the import cycle. Remove it.

View File

@@ -1,58 +0,0 @@
package aghuser
import (
"context"
"github.com/AdguardTeam/golibs/errors"
"golang.org/x/crypto/bcrypt"
)
// Login is the type for web user logins.
type Login string
// NewLogin returns a web user login.
//
// TODO(s.chzhen): Add more constraints as needed.
func NewLogin(s string) (l Login, err error) {
if s == "" {
return "", errors.ErrEmptyValue
}
return Login(s), nil
}
// Password is an interface that defines methods for handling web user
// passwords.
type Password interface {
// Authenticate returns true if the provided password is allowed.
Authenticate(ctx context.Context, password string) (ok bool)
// Hash returns a hashed representation of the web user password.
Hash() (b []byte)
}
// DefaultPassword is the default bcrypt implementation of the [Password]
// interface.
type DefaultPassword struct {
hash []byte
}
// NewDefaultPassword returns the new properly initialized *DefaultPassword.
func NewDefaultPassword(hash string) (p *DefaultPassword) {
return &DefaultPassword{
hash: []byte(hash),
}
}
// type check
var _ Password = (*DefaultPassword)(nil)
// Authenticate implements the [Password] interface for *DefaultPassword.
func (p *DefaultPassword) Authenticate(ctx context.Context, passwd string) (ok bool) {
return bcrypt.CompareHashAndPassword([]byte(p.hash), []byte(passwd)) == nil
}
// Hash implements the [Password] interface for *DefaultPassword.
func (p *DefaultPassword) Hash() (b []byte) {
return p.hash
}

View File

@@ -1,6 +0,0 @@
package aghuser_test
import "time"
// testTimeout is the common timeout for tests.
const testTimeout = 1 * time.Second

View File

@@ -1,149 +0,0 @@
package aghuser
import (
"cmp"
"context"
"fmt"
"maps"
"slices"
"sync"
"github.com/AdguardTeam/golibs/errors"
)
// DB is an interface that defines methods for interacting with user
// information. All methods must be safe for concurrent use.
//
// TODO(s.chzhen): Use this.
//
// TODO(s.chzhen): Consider updating methods to return a clone.
type DB interface {
// All retrieves all users from the database, sorted by login.
//
// TODO(s.chzhen): Consider function signature change to reflect the
// in-memory implementation, as it currently always returns nil for error.
All(ctx context.Context) (users []*User, err error)
// ByLogin retrieves a user by their login. u must not be modified.
//
// TODO(s.chzhen): Remove this once user sessions support [UserID].
ByLogin(ctx context.Context, login Login) (u *User, err error)
// ByUUID retrieves a user by their unique identifier. u must not be
// modified.
//
// TODO(s.chzhen): Use this.
ByUUID(ctx context.Context, id UserID) (u *User, err error)
// Create adds a new user to the database. If the credentials already
// exist, it returns the [errors.ErrDuplicated] error. It also can return
// an error from the cryptographic randomness reader. u must not be
// modified.
Create(ctx context.Context, u *User) (err error)
}
// DefaultDB is the default in-memory implementation of the [DB] interface.
type DefaultDB struct {
// mu protects all properties below.
mu *sync.Mutex
// loginToUserID maps a web user login to their UserID. The values must not
// be empty.
//
// TODO(s.chzhen): Remove this once user sessions support [UserID].
loginToUserID map[Login]UserID
// userIDToUser maps a UserID to a web user. The values must not be nil.
// It must be synchronized with loginToUserID, meaning all UserIDs stored in
// loginToUserID must also be stored in this map.
userIDToUser map[UserID]*User
}
// NewDefaultDB returns the new properly initialized *DefaultDB.
func NewDefaultDB() (db *DefaultDB) {
return &DefaultDB{
mu: &sync.Mutex{},
loginToUserID: map[Login]UserID{},
userIDToUser: map[UserID]*User{},
}
}
// type check
var _ DB = (*DefaultDB)(nil)
// All implements the [DB] interface for *DefaultDB.
func (db *DefaultDB) All(ctx context.Context) (users []*User, err error) {
db.mu.Lock()
defer db.mu.Unlock()
if len(db.userIDToUser) == 0 {
return nil, nil
}
users = slices.SortedStableFunc(
maps.Values(db.userIDToUser),
func(a, b *User) (res int) {
// TODO(s.chzhen): Consider adding a custom comparer.
return cmp.Compare(a.Login, b.Login)
},
)
return users, nil
}
// ByLogin implements the [DB] interface for *DefaultDB.
func (db *DefaultDB) ByLogin(ctx context.Context, login Login) (u *User, err error) {
db.mu.Lock()
defer db.mu.Unlock()
id, ok := db.loginToUserID[login]
if !ok {
return nil, nil
}
u, ok = db.userIDToUser[id]
if !ok {
// Should not happen.
panic(fmt.Errorf("no web user present with login %q", login))
}
return u, nil
}
// ByUUID implements the [DB] interface for *DefaultDB.
func (db *DefaultDB) ByUUID(ctx context.Context, id UserID) (u *User, err error) {
db.mu.Lock()
defer db.mu.Unlock()
u, ok := db.userIDToUser[id]
if !ok {
return nil, nil
}
return u, nil
}
// Create implements the [DB] interface for *DefaultDB.
func (db *DefaultDB) Create(ctx context.Context, u *User) (err error) {
db.mu.Lock()
defer db.mu.Unlock()
if u.ID == (UserID{}) {
return fmt.Errorf("userid: %w", errors.ErrEmptyValue)
}
_, ok := db.userIDToUser[u.ID]
if ok {
return fmt.Errorf("userid: %w", errors.ErrDuplicated)
}
_, ok = db.loginToUserID[u.Login]
if ok {
return fmt.Errorf("login: %w", errors.ErrDuplicated)
}
db.userIDToUser[u.ID] = u
db.loginToUserID[u.Login] = u.ID
return nil
}

View File

@@ -1,83 +0,0 @@
package aghuser_test
import (
"testing"
"github.com/AdguardTeam/AdGuardHome/internal/aghuser"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/testutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/crypto/bcrypt"
)
func TestDB(t *testing.T) {
db := aghuser.NewDefaultDB()
const (
userWithIDPassRaw = "user_with_id_password"
userSecondPassRaw = "user_second_password"
)
userWithIDPassHash, err := bcrypt.GenerateFromPassword(
[]byte(userWithIDPassRaw),
bcrypt.DefaultCost,
)
require.NoError(t, err)
userSecondPassHash, err := bcrypt.GenerateFromPassword(
[]byte(userSecondPassRaw),
bcrypt.DefaultCost,
)
require.NoError(t, err)
userWithIDPass := aghuser.NewDefaultPassword(string(userWithIDPassHash))
userSecondPass := aghuser.NewDefaultPassword(string(userSecondPassHash))
var (
userWithID = &aghuser.User{
ID: aghuser.MustNewUserID(),
Login: "user_with_id",
Password: userWithIDPass,
}
userSecond = &aghuser.User{
ID: aghuser.MustNewUserID(),
Login: "user_second",
Password: userSecondPass,
}
userDuplicateLogin = &aghuser.User{
ID: aghuser.MustNewUserID(),
Login: userWithID.Login,
Password: userWithIDPass,
}
)
ctx := testutil.ContextWithTimeout(t, testTimeout)
err = db.Create(ctx, userWithID)
require.NoError(t, err)
err = db.Create(ctx, userSecond)
require.NoError(t, err)
err = db.Create(ctx, userDuplicateLogin)
assert.ErrorIs(t, err, errors.ErrDuplicated)
got, err := db.ByUUID(ctx, userWithID.ID)
require.NoError(t, err)
assert.Equal(t, userWithID, got)
assert.True(t, got.Password.Authenticate(ctx, userWithIDPassRaw))
got, err = db.ByLogin(ctx, userSecond.Login)
require.NoError(t, err)
assert.Equal(t, userSecond, got)
assert.True(t, got.Password.Authenticate(ctx, userSecondPassRaw))
users, err := db.All(ctx)
require.NoError(t, err)
assert.Len(t, users, 2)
assert.Equal(t, []*aghuser.User{userSecond, userWithID}, users)
}

View File

@@ -1,44 +0,0 @@
// Package aghuser contains types and logic for dealing with AdGuard Home's web
// users.
package aghuser
import (
"fmt"
"github.com/google/uuid"
)
// UserID is the type for the unique IDs of web users.
type UserID uuid.UUID
// NewUserID returns a new web user unique identifier. Any error returned is an
// error from the cryptographic randomness reader.
func NewUserID() (uid UserID, err error) {
uuidv7, err := uuid.NewV7()
return UserID(uuidv7), err
}
// MustNewUserID is a wrapper around [NewUserID] that panics if there is an
// error. It is currently only used in tests.
func MustNewUserID() (uid UserID) {
uid, err := NewUserID()
if err != nil {
panic(fmt.Errorf("unexpected uuidv7 error: %w", err))
}
return uid
}
// User represents a web user.
type User struct {
// ID is the unique identifier for the web user. It must not be empty.
ID UserID
// Login is the login name of the web user. It must not be empty.
Login Login
// Password stores the password information for the web user. It must not
// be nil.
Password Password
}

View File

@@ -9,6 +9,7 @@ import (
"strings" "strings"
"github.com/AdguardTeam/AdGuardHome/internal/aghalg" "github.com/AdguardTeam/AdGuardHome/internal/aghalg"
"github.com/AdguardTeam/golibs/errors"
) )
// macKey contains MAC as byte array of 6, 8, or 20 bytes. // macKey contains MAC as byte array of 6, 8, or 20 bytes.
@@ -34,7 +35,7 @@ type index struct {
// nameToUID maps client name to UID. // nameToUID maps client name to UID.
nameToUID map[string]UID nameToUID map[string]UID
// clientIDToUID maps ClientID to UID. // clientIDToUID maps client ID to UID.
clientIDToUID map[string]UID clientIDToUID map[string]UID
// ipToUID maps IP address to UID. // ipToUID maps IP address to UID.
@@ -204,19 +205,19 @@ func (ci *index) clashesMAC(c *Persistent) (p *Persistent, mac net.HardwareAddr)
return nil, nil return nil, nil
} }
// find finds persistent client by string representation of the ClientID, IP // find finds persistent client by string representation of the client ID, IP
// address, or MAC. // address, or MAC.
func (ci *index) find(id string) (c *Persistent, ok bool) { func (ci *index) find(id string) (c *Persistent, ok bool) {
c, ok = ci.findByClientID(id) uid, found := ci.clientIDToUID[id]
if ok { if found {
return c, true return ci.uidToClient[uid], true
} }
ip, err := netip.ParseAddr(id) ip, err := netip.ParseAddr(id)
if err == nil { if err == nil {
// MAC addresses can be successfully parsed as IP addresses. // MAC addresses can be successfully parsed as IP addresses.
c, ok = ci.findByIP(ip) c, found = ci.findByIP(ip)
if ok { if found {
return c, true return c, true
} }
} }
@@ -229,16 +230,6 @@ func (ci *index) find(id string) (c *Persistent, ok bool) {
return nil, false 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. // findByName finds persistent client by name.
func (ci *index) findByName(name string) (c *Persistent, found bool) { func (ci *index) findByName(name string) (c *Persistent, found bool) {
uid, found := ci.nameToUID[name] uid, found := ci.nameToUID[name]
@@ -352,3 +343,18 @@ 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...)
}

View File

@@ -58,6 +58,12 @@ func (uid *UID) UnmarshalText(data []byte) error {
// Persistent contains information about persistent clients. // Persistent contains information about persistent clients.
type Persistent struct { 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 handles search engine hosts rewrites.
SafeSearch filtering.SafeSearch SafeSearch filtering.SafeSearch
@@ -256,7 +262,7 @@ func ValidateClientID(id string) (err error) {
return nil return nil
} }
// IDs returns a list of ClientIDs containing at least one element. // IDs returns a list of client IDs containing at least one element.
func (c *Persistent) IDs() (ids []string) { func (c *Persistent) IDs() (ids []string) {
ids = make([]string, 0, c.IDsLen()) ids = make([]string, 0, c.IDsLen())
@@ -275,7 +281,7 @@ func (c *Persistent) IDs() (ids []string) {
return append(ids, c.ClientIDs...) return append(ids, c.ClientIDs...)
} }
// IDsLen returns a length of ClientIDs. // IDsLen returns a length of client ids.
func (c *Persistent) IDsLen() (n int) { func (c *Persistent) IDsLen() (n int) {
return len(c.IPs) + len(c.Subnets) + len(c.MACs) + len(c.ClientIDs) return len(c.IPs) + len(c.Subnets) + len(c.MACs) + len(c.ClientIDs)
} }
@@ -306,3 +312,14 @@ func (c *Persistent) ShallowClone() (clone *Persistent) {
return clone 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
}

View File

@@ -12,13 +12,10 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/arpdb" "github.com/AdguardTeam/AdGuardHome/internal/arpdb"
"github.com/AdguardTeam/AdGuardHome/internal/dhcpsvc" "github.com/AdguardTeam/AdGuardHome/internal/dhcpsvc"
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/AdGuardHome/internal/whois" "github.com/AdguardTeam/AdGuardHome/internal/whois"
"github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/golibs/errors" "github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/hostsfile" "github.com/AdguardTeam/golibs/hostsfile"
"github.com/AdguardTeam/golibs/logutil/slogutil" "github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/timeutil"
) )
// allowedTags is the list of available client tags. // allowedTags is the list of available client tags.
@@ -91,10 +88,6 @@ type StorageConfig struct {
// not be nil. // not be nil.
Logger *slog.Logger 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 // DHCP is used to match IPs against MACs of persistent clients and update
// [SourceDHCP] runtime client information. It must not be nil. // [SourceDHCP] runtime client information. It must not be nil.
DHCP DHCP DHCP DHCP
@@ -133,9 +126,6 @@ type Storage struct {
// runtimeIndex contains information about runtime clients. // runtimeIndex contains information about runtime clients.
runtimeIndex *runtimeIndex runtimeIndex *runtimeIndex
// upstreamManager stores and updates custom client upstream configurations.
upstreamManager *upstreamManager
// dhcp is used to update [SourceDHCP] runtime client information. // dhcp is used to update [SourceDHCP] runtime client information.
dhcp DHCP dhcp DHCP
@@ -173,7 +163,6 @@ func NewStorage(ctx context.Context, conf *StorageConfig) (s *Storage, err error
mu: &sync.Mutex{}, mu: &sync.Mutex{},
index: newIndex(), index: newIndex(),
runtimeIndex: newRuntimeIndex(), runtimeIndex: newRuntimeIndex(),
upstreamManager: newUpstreamManager(conf.Logger, conf.Clock),
dhcp: conf.DHCP, dhcp: conf.DHCP,
etcHosts: conf.EtcHosts, etcHosts: conf.EtcHosts,
arpDB: conf.ARPDB, arpDB: conf.ARPDB,
@@ -211,7 +200,7 @@ func (s *Storage) Start(ctx context.Context) (err error) {
func (s *Storage) Shutdown(_ context.Context) (err error) { func (s *Storage) Shutdown(_ context.Context) (err error) {
close(s.done) close(s.done)
return s.upstreamManager.close() return s.closeUpstreams()
} }
// periodicARPUpdate periodically reloads runtime clients from ARP. It is // periodicARPUpdate periodically reloads runtime clients from ARP. It is
@@ -427,7 +416,6 @@ func (s *Storage) Add(ctx context.Context, p *Persistent) (err error) {
} }
s.index.add(p) s.index.add(p)
s.upstreamManager.updateCustomUpstreamConfig(p)
s.logger.DebugContext( s.logger.DebugContext(
ctx, ctx,
@@ -453,7 +441,7 @@ func (s *Storage) FindByName(name string) (p *Persistent, ok bool) {
return nil, false return nil, false
} }
// Find finds persistent client by string representation of the ClientID, IP // Find finds persistent client by string representation of the client ID, IP
// address, or MAC. And returns its shallow copy. // address, or MAC. And returns its shallow copy.
// //
// TODO(s.chzhen): Accept ClientIDData structure instead, which will contain // TODO(s.chzhen): Accept ClientIDData structure instead, which will contain
@@ -496,11 +484,6 @@ func (s *Storage) FindLoose(ip netip.Addr, id string) (p *Persistent, ok bool) {
return p.ShallowClone(), ok return p.ShallowClone(), ok
} }
foundMAC := s.dhcp.MACByIP(ip)
if foundMAC != nil {
return s.FindByMAC(foundMAC)
}
p = s.index.findByIPWithoutZone(ip) p = s.index.findByIPWithoutZone(ip)
if p != nil { if p != nil {
return p.ShallowClone(), true return p.ShallowClone(), true
@@ -531,13 +514,12 @@ func (s *Storage) RemoveByName(ctx context.Context, name string) (ok bool) {
return false return false
} }
s.index.remove(p) if err := p.CloseUpstreams(); err != nil {
s.logger.ErrorContext(ctx, "removing client", "name", p.Name, slogutil.KeyError, err)
err := s.upstreamManager.remove(p.UID)
if err != nil {
s.logger.DebugContext(ctx, "closing client upstreams", "name", name, slogutil.KeyError, err)
} }
s.index.remove(p)
return true return true
} }
@@ -574,8 +556,6 @@ func (s *Storage) Update(ctx context.Context, name string, p *Persistent) (err e
s.index.remove(stored) s.index.remove(stored)
s.index.add(p) s.index.add(p)
s.upstreamManager.updateCustomUpstreamConfig(p)
return nil return nil
} }
@@ -596,6 +576,14 @@ func (s *Storage) Size() (n int) {
return s.index.size() 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 // ClientRuntime returns a copy of the saved runtime client by ip. If no such
// client exists, returns nil. // client exists, returns nil.
func (s *Storage) ClientRuntime(ip netip.Addr) (rc *Runtime) { func (s *Storage) ClientRuntime(ip netip.Addr) (rc *Runtime) {
@@ -638,83 +626,3 @@ func (s *Storage) RangeRuntime(f func(rc *Runtime) (cont bool)) {
func (s *Storage) AllowedTags() (tags []string) { func (s *Storage) AllowedTags() (tags []string) {
return s.allowedTags 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.
// setts must not be nil.
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 {
foundMAC := s.dhcp.MACByIP(addr)
if foundMAC != nil {
c, ok = s.FindByMAC(foundMAC)
}
}
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)
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
}

View File

@@ -13,34 +13,27 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/client" "github.com/AdguardTeam/AdGuardHome/internal/client"
"github.com/AdguardTeam/AdGuardHome/internal/dhcpd" "github.com/AdguardTeam/AdGuardHome/internal/dhcpd"
"github.com/AdguardTeam/AdGuardHome/internal/dhcpsvc" "github.com/AdguardTeam/AdGuardHome/internal/dhcpsvc"
"github.com/AdguardTeam/AdGuardHome/internal/dnsforward"
"github.com/AdguardTeam/AdGuardHome/internal/whois" "github.com/AdguardTeam/AdGuardHome/internal/whois"
"github.com/AdguardTeam/golibs/hostsfile" "github.com/AdguardTeam/golibs/hostsfile"
"github.com/AdguardTeam/golibs/logutil/slogutil" "github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/testutil" "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/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
// newTestStorage is a helper function that returns initialized storage. // newTestStorage is a helper function that returns initialized storage.
func newTestStorage(tb testing.TB, clock timeutil.Clock) (s *client.Storage) { func newTestStorage(tb testing.TB) (s *client.Storage) {
tb.Helper() tb.Helper()
ctx := testutil.ContextWithTimeout(tb, testTimeout) ctx := testutil.ContextWithTimeout(tb, testTimeout)
s, err := client.NewStorage(ctx, &client.StorageConfig{ s, err := client.NewStorage(ctx, &client.StorageConfig{
Logger: slogutil.NewDiscardLogger(), Logger: slogutil.NewDiscardLogger(),
Clock: clock,
}) })
require.NoError(tb, err) require.NoError(tb, err)
return s return s
} }
// type check
var _ dnsforward.ClientsContainer = (*client.Storage)(nil)
// testHostsContainer is a mock implementation of the [client.HostsContainer] // testHostsContainer is a mock implementation of the [client.HostsContainer]
// interface. // interface.
type testHostsContainer struct { type testHostsContainer struct {
@@ -698,7 +691,7 @@ func TestStorage_Add(t *testing.T) {
} }
ctx := testutil.ContextWithTimeout(t, testTimeout) ctx := testutil.ContextWithTimeout(t, testTimeout)
s := newTestStorage(t, timeutil.SystemClock{}) s := newTestStorage(t)
tags := s.AllowedTags() tags := s.AllowedTags()
require.NotZero(t, len(tags)) require.NotZero(t, len(tags))
require.True(t, slices.IsSorted(tags)) require.True(t, slices.IsSorted(tags))
@@ -829,7 +822,7 @@ func TestStorage_RemoveByName(t *testing.T) {
} }
ctx := testutil.ContextWithTimeout(t, testTimeout) ctx := testutil.ContextWithTimeout(t, testTimeout)
s := newTestStorage(t, timeutil.SystemClock{}) s := newTestStorage(t)
err := s.Add(ctx, existingClient) err := s.Add(ctx, existingClient)
require.NoError(t, err) require.NoError(t, err)
@@ -854,7 +847,7 @@ func TestStorage_RemoveByName(t *testing.T) {
} }
t.Run("duplicate_remove", func(t *testing.T) { t.Run("duplicate_remove", func(t *testing.T) {
s = newTestStorage(t, timeutil.SystemClock{}) s = newTestStorage(t)
err = s.Add(ctx, existingClient) err = s.Add(ctx, existingClient)
require.NoError(t, err) require.NoError(t, err)
@@ -1285,99 +1278,3 @@ 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)
})
}

View File

@@ -1,226 +0,0 @@
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,
)
}

View File

@@ -4,7 +4,6 @@ import (
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"net" "net"
"slices"
"sync/atomic" "sync/atomic"
"time" "time"
@@ -15,48 +14,18 @@ import (
"golang.org/x/net/ipv6" "golang.org/x/net/ipv6"
) )
// raCtx is a context for the Router Advertisement logic.
type raCtx struct { type raCtx struct {
// raAllowSLAAC is used to determine if the ICMP Router Advertisement raAllowSLAAC bool // send RA packets without MO flags
// messages should be sent. raSLAACOnly bool // send RA packets with MO flags
// ipAddr net.IP // source IP address (link-local-unicast)
// If both raAllowSLAAC and raSLAACOnly are false, the Router Advertisement dnsIPAddr net.IP // IP address for DNS Server option
// messages aren't sent. prefixIPAddr net.IP // IP address for Prefix option
raAllowSLAAC bool ifaceName string
iface *net.Interface
packetSendPeriod time.Duration // how often RA packets are sent
// raSLAACOnly is used to determine if the ICMP Router Advertisement conn *icmp.PacketConn // ICMPv6 socket
// messages should set M and O flags, see RFC 4861, section 4.2. stop atomic.Value // stop the packet sending loop
//
// 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 { type icmpv6RA struct {
@@ -69,11 +38,10 @@ type icmpv6RA struct {
mtu uint32 mtu uint32
} }
// hwAddrToLinkLayerAddr clones the hardware address and returns it as a byte // hwAddrToLinkLayerAddr converts a hardware address into a form required by
// slice suitable for the Source Link-Layer Address option in the ICMPv6 // RFC4861. That is, a byte slice of length divisible by 8.
// Router Advertisement packet.
// //
// TODO(e.burkov): Check if it's safe to use the original slice. // See https://tools.ietf.org/html/rfc4861#section-4.6.1.
func hwAddrToLinkLayerAddr(hwa net.HardwareAddr) (lla []byte, err error) { func hwAddrToLinkLayerAddr(hwa net.HardwareAddr) (lla []byte, err error) {
err = netutil.ValidateMAC(hwa) err = netutil.ValidateMAC(hwa)
if err != nil { if err != nil {
@@ -82,7 +50,19 @@ func hwAddrToLinkLayerAddr(hwa net.HardwareAddr) (lla []byte, err error) {
return nil, err return nil, err
} }
return slices.Clone(hwa), nil 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
} }
// Create an ICMPv6.RouterAdvertisement packet with all necessary options. // Create an ICMPv6.RouterAdvertisement packet with all necessary options.
@@ -123,24 +103,15 @@ func hwAddrToLinkLayerAddr(hwa net.HardwareAddr) (lla []byte, err error) {
// //
// TODO(a.garipov): Replace with an existing implementation from a dependency. // TODO(a.garipov): Replace with an existing implementation from a dependency.
func createICMPv6RAPacket(params icmpv6RA) (data []byte, err error) { func createICMPv6RAPacket(params icmpv6RA) (data []byte, err error) {
lla, err := hwAddrToLinkLayerAddr(params.sourceLinkLayerAddress) var lla []byte
lla, err = hwAddrToLinkLayerAddr(params.sourceLinkLayerAddress)
if err != nil { 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 // TODO(a.garipov): Don't use a magic constant here. Refactor the code
// and make all constants named instead of all those comments. // and make all constants named instead of all those comments..
data = make([]byte, 80+srcLLAOptLen+srcLLAPadLen) data = make([]byte, 82+len(lla))
i := 0 i := 0
// ICMPv6: // ICMPv6:
@@ -204,11 +175,12 @@ func createICMPv6RAPacket(params icmpv6RA) (data []byte, err error) {
// Option=Source link-layer address: // Option=Source link-layer address:
data[i] = 1 // Type data[i] = 1 // Type
data[i+1] = byte(srcLLAOptLenValue) // Length data[i+1] = 1 // Length
i += 2 i += 2
copy(data[i:], lla) // Link-Layer Address[8/24] copy(data[i:], lla) // Link-Layer Address[8/24]
i += len(lla) + srcLLAPadLen i += len(lla)
// Option=Recursive DNS Server: // Option=Recursive DNS Server:

View File

@@ -1,63 +0,0 @@
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)
}

View File

@@ -0,0 +1,38 @@
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)
}

View File

@@ -239,7 +239,7 @@ func (s *Server) handleAccessSet(w http.ResponseWriter, r *http.Request) {
err = validateAccessSet(list) err = validateAccessSet(list)
if err != nil { if err != nil {
aghhttp.Error(r, w, http.StatusBadRequest, "%s", err) aghhttp.Error(r, w, http.StatusBadRequest, err.Error())
return return
} }

View File

@@ -15,7 +15,7 @@ import (
var _ proxy.BeforeRequestHandler = (*Server)(nil) var _ proxy.BeforeRequestHandler = (*Server)(nil)
// HandleBefore is the handler that is called before any other processing, // HandleBefore is the handler that is called before any other processing,
// including logs. It performs access checks and puts the ClientID, if there // including logs. It performs access checks and puts the client ID, if there
// is one, into the server's cache. // is one, into the server's cache.
// //
// TODO(d.kolyshev): Extract to separate package. // TODO(d.kolyshev): Extract to separate package.
@@ -74,7 +74,7 @@ func (s *Server) clientIDFromDNSContext(pctx *proxy.DNSContext) (clientID string
return "", nil return "", nil
} }
hostSrvName := s.conf.TLSConf.ServerName hostSrvName := s.conf.ServerName
if hostSrvName == "" { if hostSrvName == "" {
return "", nil return "", nil
} }
@@ -87,7 +87,7 @@ func (s *Server) clientIDFromDNSContext(pctx *proxy.DNSContext) (clientID string
clientID, err = clientIDFromClientServerName( clientID, err = clientIDFromClientServerName(
hostSrvName, hostSrvName,
cliSrvName, cliSrvName,
s.conf.TLSConf.StrictSNICheck, s.conf.StrictSNICheck,
) )
if err != nil { if err != nil {
return "", fmt.Errorf("clientid check: %w", err) return "", fmt.Errorf("clientid check: %w", err)

View File

@@ -121,7 +121,7 @@ func TestServer_HandleBefore_tls(t *testing.T) {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
t.Parallel() t.Parallel()
s, _ := createTestTLS(t, &TLSConfig{ s, _ := createTestTLS(t, TLSConfig{
TLSListenAddrs: []*net.TCPAddr{{}}, TLSListenAddrs: []*net.TCPAddr{{}},
ServerName: tlsServerName, ServerName: tlsServerName,
}) })
@@ -259,7 +259,6 @@ func TestServer_HandleBefore_udp(t *testing.T) {
}, ServerConfig{ }, ServerConfig{
UDPListenAddrs: []*net.UDPAddr{{}}, UDPListenAddrs: []*net.UDPAddr{{}},
TCPListenAddrs: []*net.TCPAddr{{}}, TCPListenAddrs: []*net.TCPAddr{{}},
TLSConf: &TLSConfig{},
Config: Config{ Config: Config{
AllowedClients: tc.allowedClients, AllowedClients: tc.allowedClients,
DisallowedClients: tc.disallowedClients, DisallowedClients: tc.disallowedClients,
@@ -267,7 +266,6 @@ func TestServer_HandleBefore_udp(t *testing.T) {
UpstreamDNS: []string{localUpsAddr}, UpstreamDNS: []string{localUpsAddr},
UpstreamMode: UpstreamModeLoadBalance, UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false}, EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
ClientsContainer: EmptyClientsContainer{},
}, },
ServePlainDNS: true, ServePlainDNS: true,
}) })

View File

@@ -62,7 +62,7 @@ func clientIDFromClientServerName(
return strings.ToLower(clientID), nil return strings.ToLower(clientID), nil
} }
// clientIDFromDNSContextHTTPS extracts the ClientID from the path of the // clientIDFromDNSContextHTTPS extracts the client's ID from the path of the
// client's DNS-over-HTTPS request. // client's DNS-over-HTTPS request.
func clientIDFromDNSContextHTTPS(pctx *proxy.DNSContext) (clientID string, err error) { func clientIDFromDNSContextHTTPS(pctx *proxy.DNSContext) (clientID string, err error) {
r := pctx.HTTPRequest r := pctx.HTTPRequest

View File

@@ -212,13 +212,13 @@ func TestServer_clientIDFromDNSContext(t *testing.T) {
for _, tc := range testCases { for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
tlsConf := &TLSConfig{ tlsConf := TLSConfig{
ServerName: tc.confSrvName, ServerName: tc.confSrvName,
StrictSNICheck: tc.strictSNI, StrictSNICheck: tc.strictSNI,
} }
srv := &Server{ srv := &Server{
conf: ServerConfig{TLSConf: tlsConf}, conf: ServerConfig{TLSConfig: tlsConf},
baseLogger: slogutil.NewDiscardLogger(), baseLogger: slogutil.NewDiscardLogger(),
} }

View File

@@ -1,46 +0,0 @@
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() {}

View File

@@ -11,10 +11,12 @@ import (
"strings" "strings"
"time" "time"
"github.com/AdguardTeam/AdGuardHome/internal/aghalg"
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp" "github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
"github.com/AdguardTeam/AdGuardHome/internal/aghnet" "github.com/AdguardTeam/AdGuardHome/internal/aghnet"
"github.com/AdguardTeam/AdGuardHome/internal/aghtls" "github.com/AdguardTeam/AdGuardHome/internal/aghtls"
"github.com/AdguardTeam/AdGuardHome/internal/client" "github.com/AdguardTeam/AdGuardHome/internal/client"
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/dnsproxy/proxy" "github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/dnsproxy/upstream" "github.com/AdguardTeam/dnsproxy/upstream"
"github.com/AdguardTeam/golibs/container" "github.com/AdguardTeam/golibs/container"
@@ -27,11 +29,27 @@ import (
"github.com/ameshkov/dnscrypt/v2" "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 represents the DNS filtering configuration of AdGuard Home. The zero
// Config is empty and ready for use. // Config is empty and ready for use.
type Config struct { type Config struct {
// Callbacks for other modules // 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 // ClientsContainer stores the information about special handling of some
// DNS clients. // DNS clients.
ClientsContainer ClientsContainer `yaml:"-"` ClientsContainer ClientsContainer `yaml:"-"`
@@ -167,34 +185,43 @@ type EDNSClientSubnet struct {
UseCustom bool `yaml:"use_custom"` UseCustom bool `yaml:"use_custom"`
} }
// TLSConfig contains the TLS configuration settings for DNS-over-HTTPS (DoH), // TLSConfig is the TLS configuration for HTTPS, DNS-over-HTTPS, and DNS-over-TLS
// DNS-over-TLS (DoT), DNS-over-QUIC (DoQ), and Discovery of Designated
// Resolvers (DDR).
type TLSConfig struct { type TLSConfig struct {
// Cert is the TLS certificate used for TLS connections. It is nil if cert tls.Certificate
// encryption is disabled.
Cert *tls.Certificate
// TLSListenAddrs are the addresses to listen on for DoT connections. Each TLSListenAddrs []*net.TCPAddr `yaml:"-" json:"-"`
// item in the list must be non-nil if Cert is not nil. QUICListenAddrs []*net.UDPAddr `yaml:"-" json:"-"`
TLSListenAddrs []*net.TCPAddr HTTPSListenAddrs []*net.TCPAddr `yaml:"-" json:"-"`
// QUICListenAddrs are the addresses to listen on for DoQ connections. Each // PEM-encoded certificates chain
// item in the list must be non-nil if Cert is not nil. CertificateChain string `yaml:"certificate_chain" json:"certificate_chain"`
QUICListenAddrs []*net.UDPAddr // PEM-encoded private key
PrivateKey string `yaml:"private_key" json:"private_key"`
// HTTPSListenAddrs should be the addresses AdGuard Home is listening on for CertificatePath string `yaml:"certificate_path" json:"certificate_path"`
// DoH connections. These addresses are announced with DDR. Each item in PrivateKeyPath string `yaml:"private_key_path" json:"private_key_path"`
// the list must be non-nil.
HTTPSListenAddrs []*net.TCPAddr CertificateChainData []byte `yaml:"-" json:"-"`
PrivateKeyData []byte `yaml:"-" json:"-"`
// ServerName is the hostname of the server. Currently, it is only being // ServerName is the hostname of the server. Currently, it is only being
// used for ClientID checking and Discovery of Designated Resolvers (DDR). // used for ClientID checking and Discovery of Designated Resolvers (DDR).
ServerName string ServerName string `yaml:"-" json:"-"`
// DNS names from certificate (SAN) or CN value from Subject
dnsNames []string
// OverrideTLSCiphers, when set, contains the names of the cipher suites to
// use. If the slice is empty, the default safe suites are used.
OverrideTLSCiphers []string `yaml:"override_tls_ciphers,omitempty" json:"-"`
// StrictSNICheck controls if the connections with SNI mismatching the // StrictSNICheck controls if the connections with SNI mismatching the
// certificate's ones should be rejected. // certificate's ones should be rejected.
StrictSNICheck bool StrictSNICheck bool `yaml:"strict_sni_check" json:"-"`
// hasIPAddrs is set during the certificate parsing and is true if the
// configured certificate contains at least a single IP address.
hasIPAddrs bool
} }
// DNSCryptConfig is the DNSCrypt server configuration struct. // DNSCryptConfig is the DNSCrypt server configuration struct.
@@ -229,11 +256,8 @@ type ServerConfig struct {
// Remove that. // Remove that.
AddrProcConf *client.DefaultAddrProcConfig AddrProcConf *client.DefaultAddrProcConfig
// TLSConf is the TLS configuration for DNS-over-TLS, DNS-over-QUIC, and
// HTTPS. It must not be nil.
TLSConf *TLSConfig
Config Config
TLSConfig
DNSCryptConfig DNSCryptConfig
TLSAllowUnencryptedDoH bool TLSAllowUnencryptedDoH bool
@@ -274,10 +298,6 @@ type ServerConfig struct {
// ServePlainDNS defines if plain DNS is allowed for incoming requests. // ServePlainDNS defines if plain DNS is allowed for incoming requests.
ServePlainDNS bool ServePlainDNS bool
// PendingRequestsEnabled defines if duplicate requests should be forwarded
// to upstreams along with the original one.
PendingRequestsEnabled bool
} }
// UpstreamMode is a enumeration of upstream mode representations. See // UpstreamMode is a enumeration of upstream mode representations. See
@@ -321,9 +341,6 @@ func (s *Server) newProxyConfig() (conf *proxy.Config, err error) {
UsePrivateRDNS: srvConf.UsePrivateRDNS, UsePrivateRDNS: srvConf.UsePrivateRDNS,
PrivateSubnets: s.privateNets, PrivateSubnets: s.privateNets,
MessageConstructor: s, MessageConstructor: s,
PendingRequests: &proxy.PendingRequestsConfig{
Enabled: srvConf.PendingRequestsEnabled,
},
} }
if srvConf.EDNSClientSubnet.UseCustom { if srvConf.EDNSClientSubnet.UseCustom {
@@ -450,7 +467,7 @@ func (s *Server) prepareIpsetListSettings() (ipsets []string, err error) {
} }
ipsets = stringutil.SplitTrimmed(string(data), "\n") ipsets = stringutil.SplitTrimmed(string(data), "\n")
ipsets = slices.DeleteFunc(ipsets, aghnet.IsCommentOrEmpty) ipsets = slices.DeleteFunc(ipsets, IsCommentOrEmpty)
log.Debug("dns: using %d ipset rules from file %q", len(ipsets), fn) log.Debug("dns: using %d ipset rules from file %q", len(ipsets), fn)
@@ -461,7 +478,7 @@ func (s *Server) prepareIpsetListSettings() (ipsets []string, err error) {
// the configuration itself. // the configuration itself.
func (conf *ServerConfig) loadUpstreams() (upstreams []string, err error) { func (conf *ServerConfig) loadUpstreams() (upstreams []string, err error) {
if conf.UpstreamDNSFileName == "" { if conf.UpstreamDNSFileName == "" {
return stringutil.FilterOut(conf.UpstreamDNS, aghnet.IsCommentOrEmpty), nil return stringutil.FilterOut(conf.UpstreamDNS, IsCommentOrEmpty), nil
} }
var data []byte var data []byte
@@ -474,7 +491,7 @@ func (conf *ServerConfig) loadUpstreams() (upstreams []string, err error) {
log.Debug("dnsforward: got %d upstreams in %q", len(upstreams), conf.UpstreamDNSFileName) log.Debug("dnsforward: got %d upstreams in %q", len(upstreams), conf.UpstreamDNSFileName)
return stringutil.FilterOut(upstreams, aghnet.IsCommentOrEmpty), nil return stringutil.FilterOut(upstreams, IsCommentOrEmpty), nil
} }
// collectListenAddr adds addrPort to addrs. It also adds its port to // collectListenAddr adds addrPort to addrs. It also adds its port to
@@ -608,33 +625,45 @@ func (conf *ServerConfig) ourAddrsSet() (m addrPortSet, err error) {
} }
} }
// prepareTLS sets up the TLS configuration for the DNS proxy. // prepareTLS - prepares TLS configuration for the DNS proxy
func (s *Server) prepareTLS(proxyConfig *proxy.Config) (err error) { func (s *Server) prepareTLS(proxyConfig *proxy.Config) (err error) {
if s.conf.TLSConf.Cert == nil { if len(s.conf.CertificateChainData) == 0 || len(s.conf.PrivateKeyData) == 0 {
return
}
if s.conf.TLSConf.TLSListenAddrs == nil && s.conf.TLSConf.QUICListenAddrs == nil {
return nil return nil
} }
proxyConfig.TLSListenAddr = s.conf.TLSConf.TLSListenAddrs if s.conf.TLSListenAddrs == nil && s.conf.QUICListenAddrs == nil {
proxyConfig.QUICListenAddr = s.conf.TLSConf.QUICListenAddrs return nil
}
cert, err := x509.ParseCertificate(s.conf.TLSConf.Cert.Certificate[0]) proxyConfig.TLSListenAddr = aghalg.CoalesceSlice(
s.conf.TLSListenAddrs,
proxyConfig.TLSListenAddr,
)
proxyConfig.QUICListenAddr = aghalg.CoalesceSlice(
s.conf.QUICListenAddrs,
proxyConfig.QUICListenAddr,
)
s.conf.cert, err = tls.X509KeyPair(s.conf.CertificateChainData, s.conf.PrivateKeyData)
if err != nil {
return fmt.Errorf("failed to parse TLS keypair: %w", err)
}
cert, err := x509.ParseCertificate(s.conf.cert.Certificate[0])
if err != nil { if err != nil {
return fmt.Errorf("x509.ParseCertificate(): %w", err) return fmt.Errorf("x509.ParseCertificate(): %w", err)
} }
s.hasIPAddrs = aghtls.CertificateHasIP(cert) s.conf.hasIPAddrs = aghtls.CertificateHasIP(cert)
if s.conf.TLSConf.StrictSNICheck { if s.conf.StrictSNICheck {
if len(cert.DNSNames) != 0 { if len(cert.DNSNames) != 0 {
s.dnsNames = cert.DNSNames s.conf.dnsNames = cert.DNSNames
log.Debug("dns: using certificate's SAN as DNS names: %v", cert.DNSNames) log.Debug("dns: using certificate's SAN as DNS names: %v", cert.DNSNames)
slices.Sort(s.dnsNames) slices.Sort(s.conf.dnsNames)
} else { } else {
s.dnsNames = []string{cert.Subject.CommonName} s.conf.dnsNames = append(s.conf.dnsNames, cert.Subject.CommonName)
log.Debug("dns: using certificate's CN as DNS name: %s", cert.Subject.CommonName) log.Debug("dns: using certificate's CN as DNS name: %s", cert.Subject.CommonName)
} }
} }
@@ -683,11 +712,11 @@ func anyNameMatches(dnsNames []string, sni string) (ok bool) {
// Called by 'tls' package when Client Hello is received // Called by 'tls' package when Client Hello is received
// If the server name (from SNI) supplied by client is incorrect - we terminate the ongoing TLS handshake. // If the server name (from SNI) supplied by client is incorrect - we terminate the ongoing TLS handshake.
func (s *Server) onGetCertificate(ch *tls.ClientHelloInfo) (*tls.Certificate, error) { func (s *Server) onGetCertificate(ch *tls.ClientHelloInfo) (*tls.Certificate, error) {
if s.conf.TLSConf.StrictSNICheck && !anyNameMatches(s.dnsNames, ch.ServerName) { if s.conf.StrictSNICheck && !anyNameMatches(s.conf.dnsNames, ch.ServerName) {
log.Info("dns: tls: unknown SNI in Client Hello: %s", ch.ServerName) log.Info("dns: tls: unknown SNI in Client Hello: %s", ch.ServerName)
return nil, fmt.Errorf("invalid SNI") return nil, fmt.Errorf("invalid SNI")
} }
return s.conf.TLSConf.Cert, nil return &s.conf.cert, nil
} }
// preparePlain prepares the plain-DNS configuration for the DNS proxy. // preparePlain prepares the plain-DNS configuration for the DNS proxy.

View File

@@ -296,11 +296,9 @@ func TestServer_HandleDNSRequest_dns64(t *testing.T) {
UDPListenAddrs: []*net.UDPAddr{{}}, UDPListenAddrs: []*net.UDPAddr{{}},
TCPListenAddrs: []*net.TCPAddr{{}}, TCPListenAddrs: []*net.TCPAddr{{}},
UseDNS64: true, UseDNS64: true,
TLSConf: &TLSConfig{},
Config: Config{ Config: Config{
UpstreamMode: UpstreamModeLoadBalance, UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false}, EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
ClientsContainer: EmptyClientsContainer{},
UpstreamDNS: []string{upsAddr}, UpstreamDNS: []string{upsAddr},
}, },
UsePrivateRDNS: true, UsePrivateRDNS: true,
@@ -336,11 +334,9 @@ func TestServer_dns64WithDisabledRDNS(t *testing.T) {
UDPListenAddrs: []*net.UDPAddr{{}}, UDPListenAddrs: []*net.UDPAddr{{}},
TCPListenAddrs: []*net.TCPAddr{{}}, TCPListenAddrs: []*net.TCPAddr{{}},
UseDNS64: true, UseDNS64: true,
TLSConf: &TLSConfig{},
Config: Config{ Config: Config{
UpstreamMode: UpstreamModeLoadBalance, UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false}, EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
ClientsContainer: EmptyClientsContainer{},
UpstreamDNS: []string{upsAddr}, UpstreamDNS: []string{upsAddr},
}, },
UsePrivateRDNS: false, UsePrivateRDNS: false,

View File

@@ -103,26 +103,16 @@ type SystemResolvers interface {
// //
// The zero Server is empty and ready for use. // The zero Server is empty and ready for use.
type Server struct { type Server struct {
// addrProc, if not nil, is used to process clients' IP addresses with rDNS, // dnsProxy is the DNS proxy for forwarding client's DNS requests.
// WHOIS, etc. dnsProxy *proxy.Proxy
addrProc client.AddressProcessor
// bootstrap is the resolver for upstreams' hostnames. // dnsFilter is the DNS filter for filtering client's DNS requests and
bootstrap upstream.Resolver // responses.
dnsFilter *filtering.DNSFilter
// clientIDCache is a temporary storage for ClientIDs that were extracted
// during the BeforeRequestHandler stage.
clientIDCache cache.Cache
// dhcpServer is the DHCP server for accessing lease data. // dhcpServer is the DHCP server for accessing lease data.
dhcpServer DHCP dhcpServer DHCP
// etcHosts contains the current data from the system's hosts files.
etcHosts upstream.Resolver
// privateNets is the configured set of IP networks considered private.
privateNets netutil.SubnetSet
// queryLog is the query log for client's DNS requests, responses and // queryLog is the query log for client's DNS requests, responses and
// filtering results. // filtering results.
queryLog querylog.QueryLog queryLog querylog.QueryLog
@@ -130,43 +120,37 @@ type Server struct {
// stats is the statistics collector for client's DNS usage data. // stats is the statistics collector for client's DNS usage data.
stats stats.Interface stats stats.Interface
// sysResolvers used to fetch system resolvers to use by default for private
// PTR resolving.
sysResolvers SystemResolvers
// access drops disallowed clients. // access drops disallowed clients.
access *accessManager access *accessManager
// anonymizer masks the client's IP addresses if needed.
anonymizer *aghnet.IPMut
// baseLogger is used to create loggers for other entities. It should not // baseLogger is used to create loggers for other entities. It should not
// have a prefix and must not be nil. // have a prefix and must not be nil.
baseLogger *slog.Logger baseLogger *slog.Logger
// dnsFilter is the DNS filter for filtering client's DNS requests and // localDomainSuffix is the suffix used to detect internal hosts. It
// responses. // must be a valid domain name plus dots on each side.
dnsFilter *filtering.DNSFilter localDomainSuffix string
// dnsProxy is the DNS proxy for forwarding client's DNS requests.
dnsProxy *proxy.Proxy
// internalProxy resolves internal requests from the application itself. It
// isn't started and so no listen ports are required.
internalProxy *proxy.Proxy
// ipset processes DNS requests using ipset data. It must not be nil after // ipset processes DNS requests using ipset data. It must not be nil after
// initialization. See [newIpsetHandler]. // initialization. See [newIpsetHandler].
ipset *ipsetHandler ipset *ipsetHandler
// dns64Pref is the NAT64 prefix used for DNS64 response mapping. The major // privateNets is the configured set of IP networks considered private.
// part of DNS64 happens inside the [proxy] package, but there still are privateNets netutil.SubnetSet
// some places where response mapping is needed (e.g. DHCP).
dns64Pref netip.Prefix
// localDomainSuffix is the suffix used to detect internal hosts. It // addrProc, if not nil, is used to process clients' IP addresses with rDNS,
// must be a valid domain name plus dots on each side. // WHOIS, etc.
localDomainSuffix string addrProc client.AddressProcessor
// sysResolvers used to fetch system resolvers to use by default for private
// PTR resolving.
sysResolvers SystemResolvers
// etcHosts contains the current data from the system's hosts files.
etcHosts upstream.Resolver
// bootstrap is the resolver for upstreams' hostnames.
bootstrap upstream.Resolver
// bootResolvers are the resolvers that should be used for // bootResolvers are the resolvers that should be used for
// bootstrapping along with [etcHosts]. // bootstrapping along with [etcHosts].
@@ -175,26 +159,34 @@ type Server struct {
// [upstream.Resolver] interface. // [upstream.Resolver] interface.
bootResolvers []*upstream.UpstreamResolver bootResolvers []*upstream.UpstreamResolver
// dnsNames are the DNS names from certificate (SAN) or CN value from // dns64Pref is the NAT64 prefix used for DNS64 response mapping. The major
// Subject. // part of DNS64 happens inside the [proxy] package, but there still are
dnsNames []string // some places where response mapping is needed (e.g. DHCP).
dns64Pref netip.Prefix
// anonymizer masks the client's IP addresses if needed.
anonymizer *aghnet.IPMut
// clientIDCache is a temporary storage for ClientIDs that were extracted
// during the BeforeRequestHandler stage.
clientIDCache cache.Cache
// internalProxy resolves internal requests from the application itself. It
// isn't started and so no listen ports are required.
internalProxy *proxy.Proxy
// isRunning is true if the DNS server is running.
isRunning bool
// protectionUpdateInProgress is used to make sure that only one goroutine
// updating the protection configuration after a pause is running at a time.
protectionUpdateInProgress atomic.Bool
// conf is the current configuration of the server. // conf is the current configuration of the server.
conf ServerConfig conf ServerConfig
// serverLock protects Server. // serverLock protects Server.
serverLock sync.RWMutex serverLock sync.RWMutex
// protectionUpdateInProgress is used to make sure that only one goroutine
// updating the protection configuration after a pause is running at a time.
protectionUpdateInProgress atomic.Bool
// isRunning is true if the DNS server is running.
isRunning bool
// hasIPAddrs is set during the certificate parsing and is true if the
// configured certificate contains at least a single IP address.
hasIPAddrs bool
} }
// defaultLocalDomainSuffix is the default suffix used to detect internal hosts // defaultLocalDomainSuffix is the default suffix used to detect internal hosts
@@ -548,7 +540,7 @@ func (s *Server) prepareUpstreamSettings(boot upstream.Resolver) (err error) {
uc, err := newUpstreamConfig(upstreams, defaultDNS, &upstream.Options{ uc, err := newUpstreamConfig(upstreams, defaultDNS, &upstream.Options{
Bootstrap: boot, Bootstrap: boot,
Timeout: s.conf.UpstreamTimeout, Timeout: s.conf.UpstreamTimeout,
HTTPVersions: aghnet.UpstreamHTTPVersions(s.conf.UseHTTP3Upstreams), HTTPVersions: UpstreamHTTPVersions(s.conf.UseHTTP3Upstreams),
PreferIPv6: s.conf.BootstrapPreferIPv6, PreferIPv6: s.conf.BootstrapPreferIPv6,
// Use a customized set of RootCAs, because Go's default mechanism of // 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 // loading TLS roots does not always work properly on some routers so we're
@@ -565,13 +557,6 @@ func (s *Server) prepareUpstreamSettings(boot upstream.Resolver) (err error) {
} }
s.conf.UpstreamConfig = uc 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 return nil
} }
@@ -645,7 +630,7 @@ func (s *Server) prepareInternalDNS() (err error) {
bootOpts := &upstream.Options{ bootOpts := &upstream.Options{
Timeout: DefaultTimeout, Timeout: DefaultTimeout,
HTTPVersions: aghnet.UpstreamHTTPVersions(s.conf.UseHTTP3Upstreams), HTTPVersions: UpstreamHTTPVersions(s.conf.UseHTTP3Upstreams),
} }
s.bootstrap, s.bootResolvers, err = newBootstrap(s.conf.BootstrapDNS, s.etcHosts, bootOpts) s.bootstrap, s.bootResolvers, err = newBootstrap(s.conf.BootstrapDNS, s.etcHosts, bootOpts)
@@ -676,7 +661,7 @@ func (s *Server) prepareInternalDNS() (err error) {
// setupFallbackDNS initializes the fallback DNS servers. // setupFallbackDNS initializes the fallback DNS servers.
func (s *Server) setupFallbackDNS() (uc *proxy.UpstreamConfig, err error) { func (s *Server) setupFallbackDNS() (uc *proxy.UpstreamConfig, err error) {
fallbacks := s.conf.FallbackDNS fallbacks := s.conf.FallbackDNS
fallbacks = stringutil.FilterOut(fallbacks, aghnet.IsCommentOrEmpty) fallbacks = stringutil.FilterOut(fallbacks, IsCommentOrEmpty)
if len(fallbacks) == 0 { if len(fallbacks) == 0 {
return nil, nil return nil, nil
} }

View File

@@ -23,11 +23,9 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/aghnet" "github.com/AdguardTeam/AdGuardHome/internal/aghnet"
"github.com/AdguardTeam/AdGuardHome/internal/aghtest" "github.com/AdguardTeam/AdGuardHome/internal/aghtest"
"github.com/AdguardTeam/AdGuardHome/internal/client"
"github.com/AdguardTeam/AdGuardHome/internal/filtering" "github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/AdGuardHome/internal/filtering/hashprefix" "github.com/AdguardTeam/AdGuardHome/internal/filtering/hashprefix"
"github.com/AdguardTeam/AdGuardHome/internal/filtering/safesearch" "github.com/AdguardTeam/AdGuardHome/internal/filtering/safesearch"
"github.com/AdguardTeam/AdGuardHome/internal/schedule"
"github.com/AdguardTeam/dnsproxy/proxy" "github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/dnsproxy/upstream" "github.com/AdguardTeam/dnsproxy/upstream"
"github.com/AdguardTeam/golibs/logutil/slogutil" "github.com/AdguardTeam/golibs/logutil/slogutil"
@@ -63,42 +61,6 @@ const (
// TODO(a.garipov): Use more. // TODO(a.garipov): Use more.
var testClientAddrPort = netip.MustParseAddrPort("1.2.3.4:12345") 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) { func startDeferStop(t *testing.T, s *Server) {
t.Helper() t.Helper()
@@ -107,21 +69,6 @@ func startDeferStop(t *testing.T, s *Server) {
testutil.CleanupAndRequireSuccess(t, s.Stop) 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( func createTestServer(
t *testing.T, t *testing.T,
filterConf *filtering.Config, filterConf *filtering.Config,
@@ -139,12 +86,6 @@ func createTestServer(
Data: []byte(rules), Data: []byte(rules),
}} }}
filterConf.BlockedServices = cmp.Or(filterConf.BlockedServices, emptyFilteringBlockedServices())
if filterConf.ApplyClientFiltering == nil {
filterConf.ApplyClientFiltering = applyEmptyClientFiltering
}
f, err := filtering.New(filterConf, filters) f, err := filtering.New(filterConf, filters)
require.NoError(t, err) require.NoError(t, err)
@@ -213,32 +154,28 @@ func createServerTLSConfig(t *testing.T) (*tls.Config, []byte, []byte) {
}, certPem, keyPem }, certPem, keyPem
} }
func createTestTLS(t *testing.T, tlsConf *TLSConfig) (s *Server, certPem []byte) { func createTestTLS(t *testing.T, tlsConf TLSConfig) (s *Server, certPem []byte) {
t.Helper() t.Helper()
var keyPem []byte var keyPem []byte
_, certPem, keyPem = createServerTLSConfig(t) _, certPem, keyPem = createServerTLSConfig(t)
cert, err := tls.X509KeyPair(certPem, keyPem)
require.NoError(t, err)
tlsConf.Cert = &cert
s = createTestServer(t, &filtering.Config{ s = createTestServer(t, &filtering.Config{
BlockingMode: filtering.BlockingModeDefault, BlockingMode: filtering.BlockingModeDefault,
}, ServerConfig{ }, ServerConfig{
UDPListenAddrs: []*net.UDPAddr{{}}, UDPListenAddrs: []*net.UDPAddr{{}},
TCPListenAddrs: []*net.TCPAddr{{}}, TCPListenAddrs: []*net.TCPAddr{{}},
TLSConf: tlsConf,
Config: Config{ Config: Config{
UpstreamMode: UpstreamModeLoadBalance, UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false}, EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
ClientsContainer: EmptyClientsContainer{},
}, },
ServePlainDNS: true, ServePlainDNS: true,
}) })
err = s.Prepare(&s.conf) tlsConf.CertificateChainData, tlsConf.PrivateKeyData = certPem, keyPem
s.conf.TLSConfig = tlsConf
err := s.Prepare(&s.conf)
require.NoErrorf(t, err, "failed to prepare server: %s", err) require.NoErrorf(t, err, "failed to prepare server: %s", err)
return s, certPem return s, certPem
@@ -357,11 +294,9 @@ func TestServer(t *testing.T) {
}, ServerConfig{ }, ServerConfig{
UDPListenAddrs: []*net.UDPAddr{{}}, UDPListenAddrs: []*net.UDPAddr{{}},
TCPListenAddrs: []*net.TCPAddr{{}}, TCPListenAddrs: []*net.TCPAddr{{}},
TLSConf: &TLSConfig{},
Config: Config{ Config: Config{
UpstreamMode: UpstreamModeLoadBalance, UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false}, EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
ClientsContainer: EmptyClientsContainer{},
}, },
ServePlainDNS: true, ServePlainDNS: true,
}) })
@@ -399,11 +334,9 @@ func TestServer_timeout(t *testing.T) {
t.Run("custom", func(t *testing.T) { t.Run("custom", func(t *testing.T) {
srvConf := &ServerConfig{ srvConf := &ServerConfig{
UpstreamTimeout: testTimeout, UpstreamTimeout: testTimeout,
TLSConf: &TLSConfig{},
Config: Config{ Config: Config{
UpstreamMode: UpstreamModeLoadBalance, UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false}, EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
ClientsContainer: EmptyClientsContainer{},
}, },
ServePlainDNS: true, ServePlainDNS: true,
} }
@@ -427,12 +360,10 @@ func TestServer_timeout(t *testing.T) {
}) })
require.NoError(t, err) require.NoError(t, err)
s.conf.TLSConf = &TLSConfig{}
s.conf.Config.UpstreamMode = UpstreamModeLoadBalance s.conf.Config.UpstreamMode = UpstreamModeLoadBalance
s.conf.Config.EDNSClientSubnet = &EDNSClientSubnet{ s.conf.Config.EDNSClientSubnet = &EDNSClientSubnet{
Enabled: false, Enabled: false,
} }
s.conf.Config.ClientsContainer = EmptyClientsContainer{}
err = s.Prepare(&s.conf) err = s.Prepare(&s.conf)
require.NoError(t, err) require.NoError(t, err)
@@ -442,7 +373,6 @@ func TestServer_timeout(t *testing.T) {
func TestServer_Prepare_fallbacks(t *testing.T) { func TestServer_Prepare_fallbacks(t *testing.T) {
srvConf := &ServerConfig{ srvConf := &ServerConfig{
TLSConf: &TLSConfig{},
Config: Config{ Config: Config{
FallbackDNS: []string{ FallbackDNS: []string{
"#tls://1.1.1.1", "#tls://1.1.1.1",
@@ -450,7 +380,6 @@ func TestServer_Prepare_fallbacks(t *testing.T) {
}, },
UpstreamMode: UpstreamModeLoadBalance, UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false}, EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
ClientsContainer: EmptyClientsContainer{},
}, },
ServePlainDNS: true, ServePlainDNS: true,
} }
@@ -473,11 +402,9 @@ func TestServerWithProtectionDisabled(t *testing.T) {
}, ServerConfig{ }, ServerConfig{
UDPListenAddrs: []*net.UDPAddr{{}}, UDPListenAddrs: []*net.UDPAddr{{}},
TCPListenAddrs: []*net.TCPAddr{{}}, TCPListenAddrs: []*net.TCPAddr{{}},
TLSConf: &TLSConfig{},
Config: Config{ Config: Config{
UpstreamMode: UpstreamModeLoadBalance, UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false}, EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
ClientsContainer: EmptyClientsContainer{},
}, },
ServePlainDNS: true, ServePlainDNS: true,
}) })
@@ -495,7 +422,7 @@ func TestServerWithProtectionDisabled(t *testing.T) {
} }
func TestDoTServer(t *testing.T) { func TestDoTServer(t *testing.T) {
s, certPem := createTestTLS(t, &TLSConfig{ s, certPem := createTestTLS(t, TLSConfig{
TLSListenAddrs: []*net.TCPAddr{{}}, TLSListenAddrs: []*net.TCPAddr{{}},
}) })
s.conf.UpstreamConfig.Upstreams = []upstream.Upstream{newGoogleUpstream()} s.conf.UpstreamConfig.Upstreams = []upstream.Upstream{newGoogleUpstream()}
@@ -519,7 +446,7 @@ func TestDoTServer(t *testing.T) {
} }
func TestDoQServer(t *testing.T) { func TestDoQServer(t *testing.T) {
s, _ := createTestTLS(t, &TLSConfig{ s, _ := createTestTLS(t, TLSConfig{
QUICListenAddrs: []*net.UDPAddr{{IP: net.IP{127, 0, 0, 1}}}, QUICListenAddrs: []*net.UDPAddr{{IP: net.IP{127, 0, 0, 1}}},
}) })
s.conf.UpstreamConfig.Upstreams = []upstream.Upstream{newGoogleUpstream()} s.conf.UpstreamConfig.Upstreams = []upstream.Upstream{newGoogleUpstream()}
@@ -604,13 +531,11 @@ func TestSafeSearch(t *testing.T) {
forwardConf := ServerConfig{ forwardConf := ServerConfig{
UDPListenAddrs: []*net.UDPAddr{{}}, UDPListenAddrs: []*net.UDPAddr{{}},
TCPListenAddrs: []*net.TCPAddr{{}}, TCPListenAddrs: []*net.TCPAddr{{}},
TLSConf: &TLSConfig{},
Config: Config{ Config: Config{
UpstreamMode: UpstreamModeLoadBalance, UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{ EDNSClientSubnet: &EDNSClientSubnet{
Enabled: false, Enabled: false,
}, },
ClientsContainer: EmptyClientsContainer{},
}, },
ServePlainDNS: true, ServePlainDNS: true,
} }
@@ -699,13 +624,11 @@ func TestInvalidRequest(t *testing.T) {
}, ServerConfig{ }, ServerConfig{
UDPListenAddrs: []*net.UDPAddr{{}}, UDPListenAddrs: []*net.UDPAddr{{}},
TCPListenAddrs: []*net.TCPAddr{{}}, TCPListenAddrs: []*net.TCPAddr{{}},
TLSConf: &TLSConfig{},
Config: Config{ Config: Config{
UpstreamMode: UpstreamModeLoadBalance, UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{ EDNSClientSubnet: &EDNSClientSubnet{
Enabled: false, Enabled: false,
}, },
ClientsContainer: EmptyClientsContainer{},
}, },
ServePlainDNS: true, ServePlainDNS: true,
}) })
@@ -731,13 +654,11 @@ func TestBlockedRequest(t *testing.T) {
forwardConf := ServerConfig{ forwardConf := ServerConfig{
UDPListenAddrs: []*net.UDPAddr{{}}, UDPListenAddrs: []*net.UDPAddr{{}},
TCPListenAddrs: []*net.TCPAddr{{}}, TCPListenAddrs: []*net.TCPAddr{{}},
TLSConf: &TLSConfig{},
Config: Config{ Config: Config{
UpstreamMode: UpstreamModeLoadBalance, UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{ EDNSClientSubnet: &EDNSClientSubnet{
Enabled: false, Enabled: false,
}, },
ClientsContainer: EmptyClientsContainer{},
}, },
ServePlainDNS: true, ServePlainDNS: true,
} }
@@ -769,14 +690,12 @@ func TestServerCustomClientUpstream(t *testing.T) {
forwardConf := ServerConfig{ forwardConf := ServerConfig{
UDPListenAddrs: []*net.UDPAddr{{}}, UDPListenAddrs: []*net.UDPAddr{{}},
TCPListenAddrs: []*net.TCPAddr{{}}, TCPListenAddrs: []*net.TCPAddr{{}},
TLSConf: &TLSConfig{},
Config: Config{ Config: Config{
CacheSize: defaultCacheSize, CacheSize: defaultCacheSize,
UpstreamMode: UpstreamModeLoadBalance, UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{ EDNSClientSubnet: &EDNSClientSubnet{
Enabled: false, Enabled: false,
}, },
ClientsContainer: EmptyClientsContainer{},
}, },
ServePlainDNS: true, ServePlainDNS: true,
} }
@@ -802,12 +721,12 @@ func TestServerCustomClientUpstream(t *testing.T) {
forwardConf.EDNSClientSubnet.Enabled, forwardConf.EDNSClientSubnet.Enabled,
) )
s.conf.ClientsContainer = &clientsContainer{ s.conf.ClientsContainer = &aghtest.ClientsContainer{
OnCustomUpstreamConfig: func( OnUpstreamConfigByID: func(
_ string, _ string,
_ netip.Addr, _ upstream.Resolver,
) (conf *proxy.CustomUpstreamConfig) { ) (conf *proxy.CustomUpstreamConfig, err error) {
return customUpsConf return customUpsConf, nil
}, },
} }
@@ -850,13 +769,11 @@ func TestBlockCNAMEProtectionEnabled(t *testing.T) {
}, ServerConfig{ }, ServerConfig{
UDPListenAddrs: []*net.UDPAddr{{}}, UDPListenAddrs: []*net.UDPAddr{{}},
TCPListenAddrs: []*net.TCPAddr{{}}, TCPListenAddrs: []*net.TCPAddr{{}},
TLSConf: &TLSConfig{},
Config: Config{ Config: Config{
UpstreamMode: UpstreamModeLoadBalance, UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{ EDNSClientSubnet: &EDNSClientSubnet{
Enabled: false, Enabled: false,
}, },
ClientsContainer: EmptyClientsContainer{},
}, },
ServePlainDNS: true, ServePlainDNS: true,
}) })
@@ -886,13 +803,11 @@ func TestBlockCNAME(t *testing.T) {
forwardConf := ServerConfig{ forwardConf := ServerConfig{
UDPListenAddrs: []*net.UDPAddr{{}}, UDPListenAddrs: []*net.UDPAddr{{}},
TCPListenAddrs: []*net.TCPAddr{{}}, TCPListenAddrs: []*net.TCPAddr{{}},
TLSConf: &TLSConfig{},
Config: Config{ Config: Config{
UpstreamMode: UpstreamModeLoadBalance, UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{ EDNSClientSubnet: &EDNSClientSubnet{
Enabled: false, Enabled: false,
}, },
ClientsContainer: EmptyClientsContainer{},
}, },
ServePlainDNS: true, ServePlainDNS: true,
} }
@@ -961,13 +876,14 @@ func TestClientRulesForCNAMEMatching(t *testing.T) {
forwardConf := ServerConfig{ forwardConf := ServerConfig{
UDPListenAddrs: []*net.UDPAddr{{}}, UDPListenAddrs: []*net.UDPAddr{{}},
TCPListenAddrs: []*net.TCPAddr{{}}, TCPListenAddrs: []*net.TCPAddr{{}},
TLSConf: &TLSConfig{},
Config: Config{ Config: Config{
FilterHandler: func(_ netip.Addr, _ string, settings *filtering.Settings) {
settings.FilteringEnabled = false
},
UpstreamMode: UpstreamModeLoadBalance, UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{ EDNSClientSubnet: &EDNSClientSubnet{
Enabled: false, Enabled: false,
}, },
ClientsContainer: EmptyClientsContainer{},
}, },
ServePlainDNS: true, ServePlainDNS: true,
} }
@@ -1009,13 +925,11 @@ func TestNullBlockedRequest(t *testing.T) {
forwardConf := ServerConfig{ forwardConf := ServerConfig{
UDPListenAddrs: []*net.UDPAddr{{}}, UDPListenAddrs: []*net.UDPAddr{{}},
TCPListenAddrs: []*net.TCPAddr{{}}, TCPListenAddrs: []*net.TCPAddr{{}},
TLSConf: &TLSConfig{},
Config: Config{ Config: Config{
UpstreamMode: UpstreamModeLoadBalance, UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{ EDNSClientSubnet: &EDNSClientSubnet{
Enabled: false, Enabled: false,
}, },
ClientsContainer: EmptyClientsContainer{},
}, },
ServePlainDNS: true, ServePlainDNS: true,
} }
@@ -1055,12 +969,10 @@ func TestBlockedCustomIP(t *testing.T) {
}} }}
f, err := filtering.New(&filtering.Config{ f, err := filtering.New(&filtering.Config{
ProtectionEnabled: true, ProtectionEnabled: true,
ApplyClientFiltering: applyEmptyClientFiltering, BlockingMode: filtering.BlockingModeCustomIP,
BlockedServices: emptyFilteringBlockedServices(), BlockingIPv4: netip.Addr{},
BlockingMode: filtering.BlockingModeCustomIP, BlockingIPv6: netip.Addr{},
BlockingIPv4: netip.Addr{},
BlockingIPv6: netip.Addr{},
}, filters) }, filters)
require.NoError(t, err) require.NoError(t, err)
@@ -1080,14 +992,12 @@ func TestBlockedCustomIP(t *testing.T) {
conf := &ServerConfig{ conf := &ServerConfig{
UDPListenAddrs: []*net.UDPAddr{{}}, UDPListenAddrs: []*net.UDPAddr{{}},
TCPListenAddrs: []*net.TCPAddr{{}}, TCPListenAddrs: []*net.TCPAddr{{}},
TLSConf: &TLSConfig{},
Config: Config{ Config: Config{
UpstreamDNS: []string{"8.8.8.8:53", "8.8.4.4:53"}, UpstreamDNS: []string{"8.8.8.8:53", "8.8.4.4:53"},
UpstreamMode: UpstreamModeLoadBalance, UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{ EDNSClientSubnet: &EDNSClientSubnet{
Enabled: false, Enabled: false,
}, },
ClientsContainer: EmptyClientsContainer{},
}, },
ServePlainDNS: true, ServePlainDNS: true,
} }
@@ -1136,13 +1046,11 @@ func TestBlockedByHosts(t *testing.T) {
forwardConf := ServerConfig{ forwardConf := ServerConfig{
UDPListenAddrs: []*net.UDPAddr{{}}, UDPListenAddrs: []*net.UDPAddr{{}},
TCPListenAddrs: []*net.TCPAddr{{}}, TCPListenAddrs: []*net.TCPAddr{{}},
TLSConf: &TLSConfig{},
Config: Config{ Config: Config{
UpstreamMode: UpstreamModeLoadBalance, UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{ EDNSClientSubnet: &EDNSClientSubnet{
Enabled: false, Enabled: false,
}, },
ClientsContainer: EmptyClientsContainer{},
}, },
ServePlainDNS: true, ServePlainDNS: true,
} }
@@ -1190,13 +1098,11 @@ func TestBlockedBySafeBrowsing(t *testing.T) {
forwardConf := ServerConfig{ forwardConf := ServerConfig{
UDPListenAddrs: []*net.UDPAddr{{}}, UDPListenAddrs: []*net.UDPAddr{{}},
TCPListenAddrs: []*net.TCPAddr{{}}, TCPListenAddrs: []*net.TCPAddr{{}},
TLSConf: &TLSConfig{},
Config: Config{ Config: Config{
UpstreamMode: UpstreamModeLoadBalance, UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{ EDNSClientSubnet: &EDNSClientSubnet{
Enabled: false, Enabled: false,
}, },
ClientsContainer: EmptyClientsContainer{},
}, },
ServePlainDNS: true, ServePlainDNS: true,
} }
@@ -1216,9 +1122,7 @@ func TestBlockedBySafeBrowsing(t *testing.T) {
func TestRewrite(t *testing.T) { func TestRewrite(t *testing.T) {
c := &filtering.Config{ c := &filtering.Config{
ApplyClientFiltering: applyEmptyClientFiltering, BlockingMode: filtering.BlockingModeDefault,
BlockedServices: emptyFilteringBlockedServices(),
BlockingMode: filtering.BlockingModeDefault,
Rewrites: []*filtering.LegacyRewrite{{ Rewrites: []*filtering.LegacyRewrite{{
Domain: "test.com", Domain: "test.com",
Answer: "1.2.3.4", Answer: "1.2.3.4",
@@ -1254,14 +1158,12 @@ func TestRewrite(t *testing.T) {
assert.NoError(t, s.Prepare(&ServerConfig{ assert.NoError(t, s.Prepare(&ServerConfig{
UDPListenAddrs: []*net.UDPAddr{{}}, UDPListenAddrs: []*net.UDPAddr{{}},
TCPListenAddrs: []*net.TCPAddr{{}}, TCPListenAddrs: []*net.TCPAddr{{}},
TLSConf: &TLSConfig{},
Config: Config{ Config: Config{
UpstreamDNS: []string{"8.8.8.8:53"}, UpstreamDNS: []string{"8.8.8.8:53"},
UpstreamMode: UpstreamModeLoadBalance, UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{ EDNSClientSubnet: &EDNSClientSubnet{
Enabled: false, Enabled: false,
}, },
ClientsContainer: EmptyClientsContainer{},
}, },
ServePlainDNS: true, ServePlainDNS: true,
})) }))
@@ -1365,9 +1267,7 @@ func TestPTRResponseFromDHCPLeases(t *testing.T) {
const localDomain = "lan" const localDomain = "lan"
flt, err := filtering.New(&filtering.Config{ flt, err := filtering.New(&filtering.Config{
ApplyClientFiltering: applyEmptyClientFiltering, BlockingMode: filtering.BlockingModeDefault,
BlockedServices: emptyFilteringBlockedServices(),
BlockingMode: filtering.BlockingModeDefault,
}, nil) }, nil)
require.NoError(t, err) require.NoError(t, err)
@@ -1389,9 +1289,7 @@ func TestPTRResponseFromDHCPLeases(t *testing.T) {
s.conf.UDPListenAddrs = []*net.UDPAddr{{}} s.conf.UDPListenAddrs = []*net.UDPAddr{{}}
s.conf.TCPListenAddrs = []*net.TCPAddr{{}} s.conf.TCPListenAddrs = []*net.TCPAddr{{}}
s.conf.UpstreamDNS = []string{"127.0.0.1:53"} s.conf.UpstreamDNS = []string{"127.0.0.1:53"}
s.conf.TLSConf = &TLSConfig{}
s.conf.Config.EDNSClientSubnet = &EDNSClientSubnet{Enabled: false} s.conf.Config.EDNSClientSubnet = &EDNSClientSubnet{Enabled: false}
s.conf.Config.ClientsContainer = EmptyClientsContainer{}
s.conf.Config.UpstreamMode = UpstreamModeLoadBalance s.conf.Config.UpstreamMode = UpstreamModeLoadBalance
err = s.Prepare(&s.conf) err = s.Prepare(&s.conf)
@@ -1457,10 +1355,8 @@ func TestPTRResponseFromHosts(t *testing.T) {
}) })
flt, err := filtering.New(&filtering.Config{ flt, err := filtering.New(&filtering.Config{
ApplyClientFiltering: applyEmptyClientFiltering, BlockingMode: filtering.BlockingModeDefault,
BlockedServices: emptyFilteringBlockedServices(), EtcHosts: hc,
BlockingMode: filtering.BlockingModeDefault,
EtcHosts: hc,
}, nil) }, nil)
require.NoError(t, err) require.NoError(t, err)
@@ -1478,9 +1374,7 @@ func TestPTRResponseFromHosts(t *testing.T) {
s.conf.UDPListenAddrs = []*net.UDPAddr{{}} s.conf.UDPListenAddrs = []*net.UDPAddr{{}}
s.conf.TCPListenAddrs = []*net.TCPAddr{{}} s.conf.TCPListenAddrs = []*net.TCPAddr{{}}
s.conf.UpstreamDNS = []string{"127.0.0.1:53"} s.conf.UpstreamDNS = []string{"127.0.0.1:53"}
s.conf.TLSConf = &TLSConfig{}
s.conf.Config.EDNSClientSubnet = &EDNSClientSubnet{Enabled: false} s.conf.Config.EDNSClientSubnet = &EDNSClientSubnet{Enabled: false}
s.conf.Config.ClientsContainer = EmptyClientsContainer{}
s.conf.Config.UpstreamMode = UpstreamModeLoadBalance s.conf.Config.UpstreamMode = UpstreamModeLoadBalance
err = s.Prepare(&s.conf) err = s.Prepare(&s.conf)
@@ -1745,12 +1639,10 @@ func TestServer_Exchange(t *testing.T) {
srv := createTestServer(t, &filtering.Config{ srv := createTestServer(t, &filtering.Config{
BlockingMode: filtering.BlockingModeDefault, BlockingMode: filtering.BlockingModeDefault,
}, ServerConfig{ }, ServerConfig{
TLSConf: &TLSConfig{},
Config: Config{ Config: Config{
UpstreamDNS: []string{upsAddr}, UpstreamDNS: []string{upsAddr},
UpstreamMode: UpstreamModeLoadBalance, UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false}, EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
ClientsContainer: EmptyClientsContainer{},
}, },
LocalPTRResolvers: []string{localUpsAddr}, LocalPTRResolvers: []string{localUpsAddr},
UsePrivateRDNS: true, UsePrivateRDNS: true,
@@ -1769,12 +1661,10 @@ func TestServer_Exchange(t *testing.T) {
srv := createTestServer(t, &filtering.Config{ srv := createTestServer(t, &filtering.Config{
BlockingMode: filtering.BlockingModeDefault, BlockingMode: filtering.BlockingModeDefault,
}, ServerConfig{ }, ServerConfig{
TLSConf: &TLSConfig{},
Config: Config{ Config: Config{
UpstreamDNS: []string{upsAddr}, UpstreamDNS: []string{upsAddr},
UpstreamMode: UpstreamModeLoadBalance, UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false}, EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
ClientsContainer: EmptyClientsContainer{},
}, },
LocalPTRResolvers: []string{}, LocalPTRResolvers: []string{},
ServePlainDNS: true, ServePlainDNS: true,

View File

@@ -37,11 +37,9 @@ func TestServer_FilterDNSRewrite(t *testing.T) {
srv := createTestServer(t, &filtering.Config{ srv := createTestServer(t, &filtering.Config{
BlockingMode: filtering.BlockingModeDefault, BlockingMode: filtering.BlockingModeDefault,
}, ServerConfig{ }, ServerConfig{
TLSConf: &TLSConfig{},
Config: Config{ Config: Config{
UpstreamMode: UpstreamModeLoadBalance, UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false}, EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
ClientsContainer: EmptyClientsContainer{},
}, },
ServePlainDNS: true, ServePlainDNS: true,
}) })

View File

@@ -17,7 +17,9 @@ import (
func (s *Server) clientRequestFilteringSettings(dctx *dnsContext) (setts *filtering.Settings) { func (s *Server) clientRequestFilteringSettings(dctx *dnsContext) (setts *filtering.Settings) {
setts = s.dnsFilter.Settings() setts = s.dnsFilter.Settings()
setts.ProtectionEnabled = dctx.protectionEnabled setts.ProtectionEnabled = dctx.protectionEnabled
s.dnsFilter.ApplyAdditionalFiltering(dctx.proxyCtx.Addr.Addr(), dctx.clientID, setts) if s.conf.FilterHandler != nil {
s.conf.FilterHandler(dctx.proxyCtx.Addr.Addr(), dctx.clientID, setts)
}
return setts return setts
} }

View File

@@ -31,13 +31,11 @@ func TestHandleDNSRequest_handleDNSRequest(t *testing.T) {
forwardConf := ServerConfig{ forwardConf := ServerConfig{
UDPListenAddrs: []*net.UDPAddr{{}}, UDPListenAddrs: []*net.UDPAddr{{}},
TCPListenAddrs: []*net.TCPAddr{{}}, TCPListenAddrs: []*net.TCPAddr{{}},
TLSConf: &TLSConfig{},
Config: Config{ Config: Config{
UpstreamMode: UpstreamModeLoadBalance, UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{ EDNSClientSubnet: &EDNSClientSubnet{
Enabled: false, Enabled: false,
}, },
ClientsContainer: EmptyClientsContainer{},
}, },
ServePlainDNS: true, ServePlainDNS: true,
} }
@@ -46,10 +44,8 @@ func TestHandleDNSRequest_handleDNSRequest(t *testing.T) {
}} }}
f, err := filtering.New(&filtering.Config{ f, err := filtering.New(&filtering.Config{
ProtectionEnabled: true, ProtectionEnabled: true,
ApplyClientFiltering: applyEmptyClientFiltering, BlockingMode: filtering.BlockingModeDefault,
BlockedServices: emptyFilteringBlockedServices(),
BlockingMode: filtering.BlockingModeDefault,
}, filters) }, filters)
require.NoError(t, err) require.NoError(t, err)
f.SetEnabled(true) f.SetEnabled(true)

View File

@@ -11,7 +11,6 @@ import (
"time" "time"
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp" "github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
"github.com/AdguardTeam/AdGuardHome/internal/filtering" "github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/dnsproxy/proxy" "github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/dnsproxy/upstream" "github.com/AdguardTeam/dnsproxy/upstream"
@@ -648,7 +647,7 @@ func (s *Server) handleTestUpstreamDNS(w http.ResponseWriter, r *http.Request) {
return return
} }
req.BootstrapDNS = stringutil.FilterOut(req.BootstrapDNS, aghnet.IsCommentOrEmpty) req.BootstrapDNS = stringutil.FilterOut(req.BootstrapDNS, IsCommentOrEmpty)
opts := &upstream.Options{ opts := &upstream.Options{
Timeout: s.conf.UpstreamTimeout, Timeout: s.conf.UpstreamTimeout,
@@ -674,8 +673,6 @@ func (s *Server) handleTestUpstreamDNS(w http.ResponseWriter, r *http.Request) {
// handleCacheClear is the handler for the POST /control/cache_clear HTTP API. // handleCacheClear is the handler for the POST /control/cache_clear HTTP API.
func (s *Server) handleCacheClear(w http.ResponseWriter, _ *http.Request) { func (s *Server) handleCacheClear(w http.ResponseWriter, _ *http.Request) {
s.dnsProxy.ClearCache() s.dnsProxy.ClearCache()
s.conf.ClientsContainer.ClearUpstreamCache()
_, _ = io.WriteString(w, "OK") _, _ = io.WriteString(w, "OK")
} }

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