Compare commits

...

67 Commits

Author SHA1 Message Date
Dimitry Kolyshev
261cf35e52 all: upd dnsproxy 2024-05-24 13:12:24 +03:00
Eugene Burkov
7c002e1a99 Pull request 2225: Upd all
Squashed commit of the following:

commit eb3e0850ae5df1daf47c63f22ce2dba67caae1a4
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu May 23 16:09:08 2024 +0300

    all: upd chlog
2024-05-23 16:26:23 +03:00
Eugene Burkov
a030dd45d8 Pull request 2223: 7013 Initial RDNS
Updates #7013.

Squashed commit of the following:

commit 68a53ec702ea4ba6c1e077eeea43a14cb93e76ff
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed May 22 15:55:31 2024 +0300

    all: imp chlog

commit a02b8e1165e05fbe96aea73dd238760e2b2fcce2
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed May 22 14:21:27 2024 +0300

    all: log changes, imp docs

commit f9ec0efe6dc8a257da8177b2e9bc41ed44b18bb7
Merge: ee7202a7b 1be34ab96
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed May 22 14:16:30 2024 +0300

    Merge branch 'master' into 7013-initial-rdns

commit ee7202a7b4a16eb8936ecaa81a27b3b81b982008
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed May 22 13:11:58 2024 +0300

    dnsforward: fix http rdns check

commit 5eaa024b1148dabd92064a7ec8bc9e7d544af522
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed May 22 12:40:30 2024 +0300

    all: fix initial rdns check
2024-05-22 16:40:28 +03:00
Ainar Garipov
1be34ab963 Pull request 2222: upd-all
Squashed commit of the following:

commit 40a2b4fd5a12d1ff9f468c018f84953d44f80c3c
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Tue May 21 20:09:02 2024 +0300

    all: upd chlog, deps
2024-05-22 13:54:02 +03:00
Ainar Garipov
bcda80bee7 Pull request 2220: upd-all
Squashed commit of the following:

commit 565d3251e4d2f6e162b43522538a98ecc39ca0d9
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed May 15 13:56:16 2024 +0300

    all: upd flts, i18n, tools
2024-05-15 14:12:30 +03:00
Ainar Garipov
ec059dd236 Pull request 2218: upd-bamboo
Squashed commit of the following:

commit 69fa33141d5613ca62c37cbf6cac2e2742d9a4ff
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Tue May 14 19:42:09 2024 +0300

    bamboo-specs: upd chan
2024-05-14 19:50:05 +03:00
Stanislav Chzhen
71c44fa40c Pull request 2208: AG-27492-client-persistent-list
Squashed commit of the following:

commit 1b1a21b07baa15499e5e4963d35bfd2e542533ed
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed May 8 17:32:38 2024 +0300

    client: imp tests

commit 7e6d17158a254aa29bf4033fb68171d4209bb954
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed May 8 17:27:00 2024 +0300

    client: imp tests

commit 5e4cd2b3ca9557929b9b79a0610151ce09c792f9
Merge: 7faddd8aa 1a62ce471
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed May 8 15:57:33 2024 +0300

    Merge branch 'master' into AG-27492-client-persistent-list

commit 7faddd8aade2b1b791beec694b88513b0a2a520e
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon May 6 20:55:43 2024 +0300

    client: imp code

commit 54212e975b700f792a53fc3bfe1c2970778e05ea
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon May 6 20:24:18 2024 +0300

    all: imp code

commit 3f23c9af470036c2166e20c8d0b5d84810b35b6e
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon May 6 17:07:40 2024 +0300

    home: imp tests

commit 39b99fc050047cebadc51ae64e220ec1cb873d83
Merge: 76469ac59 17c4eeb64
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon May 6 16:39:56 2024 +0300

    Merge branch 'master' into AG-27492-client-persistent-list

commit 76469ac59400aae2f7563750a981138b8cbf3aa1
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon May 6 14:36:22 2024 +0300

    home: imp naming

commit 4e4aa5802c9aafc67c52b8a290d8046531f8a1c8
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu May 2 19:50:45 2024 +0300

    client: imp docs

commit bf5c23a72c93e58c8bc7e0ca896b2ea28519cf54
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu May 2 19:40:53 2024 +0300

    home: add tests

commit c6cdba7a8d0dfce22634f88258f61abb09ecca5a
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Apr 24 14:21:44 2024 +0300

    all: add tests

commit 1fc43cb45efbd428abaae9eba030f9bea818dfe3
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Fri Apr 19 19:19:48 2024 +0300

    all: add tests

commit ccc423b296d9037f0aa23a125a5ad3af95b8c9f3
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Fri Apr 19 15:37:15 2024 +0300

    all: client persistent list
2024-05-13 20:09:18 +03:00
Eugene Burkov
1a62ce471e Pull request 2217: Upd Go
Squashed commit of the following:

commit a5be7f94ba2c0ca80a275d9c3861e1450bab9c85
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed May 8 15:01:52 2024 +0300

    all: upd go
2024-05-08 15:07:36 +03:00
Eugene Burkov
17c4eeb646 Pull request 2214: 6744 Fix TLD subdomain
Updates #6744.

Squashed commit of the following:

commit 04894ca2684b9b871b16b43ce572cc8828c04c07
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu May 2 16:48:37 2024 +0300

    all: upd proxy finally

commit 0cb063ed572903b5c5869a5d8e217441c81ca9ed
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu May 2 15:26:17 2024 +0300

    all: upd again

commit 3deb71c08d8db84dc21817ddc955a6c483795795
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu May 2 14:46:38 2024 +0300

    all: upd proxy
2024-05-02 17:47:24 +03:00
Stanislav Chzhen
c05bce7a3f Pull request 2210: AG-32341-client-duplicate-uids
Squashed commit of the following:

commit 6f83ec8a4f0dfb2360912116a48d35d1f7df7f66
Merge: 2fea9c06a 2383ab57c
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Fri Apr 26 19:23:15 2024 +0300

    Merge branch 'master' into AG-32341-client-duplicate-uids

commit 2fea9c06af9a0b349e612620292bc629f514fead
Merge: 672a30ce4 b9d5e5ba0
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Fri Apr 26 14:00:49 2024 +0300

    Merge branch 'master' into AG-32341-client-duplicate-uids

commit 672a30ce402f51819d741ea92ec5283eea4e6813
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Fri Apr 26 13:59:59 2024 +0300

    all: upd chlog

commit 7c2b26e3ece123d67d0377f816003600408b5e94
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Apr 24 21:08:47 2024 +0300

    all: imp chlog

commit fcca9afe0cdb3d23e6fc653dd334161cbc6d832a
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Apr 24 18:56:34 2024 +0300

    all: fix client duplicate uids
2024-04-26 19:30:24 +03:00
Ainar Garipov
2383ab57c6 Pull request 2213: 6902-doc-fix
Updates #6902.

* commit 'b18fe84c5c87a87246afc1fe06aeeeca472ffc67':
  dnsforward, home: imp more
  chore: fix function names in comment
2024-04-26 18:53:35 +03:00
Ainar Garipov
b18fe84c5c dnsforward, home: imp more 2024-04-26 18:45:10 +03:00
Ainar Garipov
10f11b9725 Merge branch 'master' into 6902-doc-fix 2024-04-26 18:44:52 +03:00
Eugene Burkov
6dabfb463f Pull request 2212: 6744 Upd proxy
Updates #6744.

Squashed commit of the following:

commit 160694a13a02d575dc363003ec9f3fab6141fb51
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Apr 26 14:46:04 2024 +0300

    all: upd to release

commit b7dd961459cd7dd72bb1d60bdec67a83a79a2f9c
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Apr 25 20:45:04 2024 +0300

    all: fix tags

commit 2374845fbf90e14bd73cbe820f9b8f28ddb5e623
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Apr 25 19:54:39 2024 +0300

    all: upd proxy
2024-04-26 15:50:36 +03:00
Ainar Garipov
b9d5e5ba0f Pull request 2211: fix-i18n
Squashed commit of the following:

commit 7f15bcb2a679dabb217ebfd46d280e6235070606
Merge: 0f2efaab9 c1ee2c7e5
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Apr 24 20:01:42 2024 +0300

    Merge branch 'master' into fix-i18n

commit 0f2efaab94fd79b49ea8fc72312392c833072f62
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Apr 24 19:54:35 2024 +0300

    client: imp i18n
2024-04-24 20:15:43 +03:00
Stanislav Chzhen
c1ee2c7e5e Pull request 2200: 6312-client-ipv6-zone
Updates #6312.

Squashed commit of the following:

commit bd9146ee161a67fa41763070f985e1e73b85823b
Merge: 58d2fd98d 856cc40cf
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Apr 24 18:09:19 2024 +0300

    Merge branch 'master' into 6312-client-ipv6-zone

commit 58d2fd98d3e82c84638d58dd4d74d13a9a8fbca6
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Apr 24 18:00:56 2024 +0300

    client: imp naming

commit 922a14b036d829c2775feb7bb3e6beb6aa49692e
Merge: 6f4d58fe1 60f48e2d0
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Apr 24 14:29:00 2024 +0300

    Merge branch 'master' into 6312-client-ipv6-zone

commit 6f4d58fe1c42504e8345bff24dbb3f523e8c5f85
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Apr 24 14:27:55 2024 +0300

    client: imp docs

commit fa292eee828cd6f27f62b782675aa1f998e44518
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Apr 22 19:20:28 2024 +0300

    client: fix typo

commit 599414be0ccd3f9deb044e022a8ac0006c96b467
Merge: 502571756 762ef4a6d
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Apr 22 18:42:06 2024 +0300

    Merge branch 'master' into 6312-client-ipv6-zone

commit 502571756400a00445086b5ba412e03fca65e39f
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Apr 22 18:39:22 2024 +0300

    all: imp code; add tests

commit 155b2fef500a0d835f49957d9f30b0870712f6f2
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Apr 16 19:56:00 2024 +0300

    all: upd chlog; imp code

commit 7a4426c5d0a511cd3865884c00328b8c130746bf
Merge: e9c1cbb85 48c6242a7
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Apr 16 19:52:00 2024 +0300

    Merge branch 'master' into 6312-client-ipv6-zone

commit e9c1cbb85e4afa173969d5bedfaaaf92716b7fad
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Apr 10 16:23:07 2024 +0300

    client: client ipv6 zone
2024-04-24 19:08:54 +03:00
Ainar Garipov
856cc40cf3 Pull request 2209: 6422-upd-quic-go
Updates #6422.

Squashed commit of the following:

commit 6baf47e571539461abc15b01c06df653147050fe
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Apr 24 16:37:15 2024 +0300

    all: upd again

commit 2ad480a5665b4744f34596f7bf29fe4e61063fc5
Merge: 5b260d05a 0cff3dbcd
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Apr 24 16:37:02 2024 +0300

    Merge branch 'master' into 6422-upd-quic-go

commit 5b260d05a8221857ebd2b8f49a765d33fd02abeb
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Apr 24 14:39:35 2024 +0300

    all: upd quic-go
2024-04-24 16:57:45 +03:00
Dimitry Kolyshev
0cff3dbcda Pull request: AG-31863-dnsforward-tests
Merge in DNS/adguard-home from AG-31863-dnsforward-tests to master

Squashed commit of the following:

commit cbdad627c00b30a17af843d98236fe9758b7ddee
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Wed Apr 24 15:00:15 2024 +0200

    dnsforward: imp tests

commit b71304a925c0ea49440e493dc65accadc6e47c5f
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Wed Apr 24 12:53:51 2024 +0200

    dnsforward: imp tests

commit 3c42fcaa4906fce48b179a1c523e33839b2a8eee
Merge: 50888df66 60f48e2d0
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Wed Apr 24 08:41:19 2024 +0200

    Merge remote-tracking branch 'origin/master' into AG-31863-dnsforward-tests

commit 50888df6616085872d956c42898ab0eff6c5f5d4
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Wed Apr 24 08:39:37 2024 +0200

    dnsforward: imp code

commit dcd5e41f13d698ce7a6beb0a601910ae03ff4407
Merge: af2507b2a f85d04831
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Tue Apr 23 10:02:45 2024 +0200

    Merge remote-tracking branch 'origin/master' into AG-31863-dnsforward-tests

commit af2507b2ace94d0c39803c2d21273cf11a00843e
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Tue Apr 23 10:01:30 2024 +0200

    dnsforward: imp tests

commit 67fc9d3d9c6210e600ca3b26abf7ebfffca5e770
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Mon Apr 22 10:37:45 2024 +0200

    dnsforward: imp tests

commit e7f7df2b688be525ee7582a4e161a4837124e74b
Merge: c610a6c88 762ef4a6d
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Mon Apr 22 09:51:04 2024 +0200

    Merge remote-tracking branch 'origin/master' into AG-31863-dnsforward-tests

commit c610a6c886554f6a2677c993c1c7aae4ca99228e
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Fri Apr 19 12:28:49 2024 +0200

    dnsforward: imp tests

commit ca252e8fa28b70f303068775cc682682dd41e77c
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Fri Apr 19 11:58:49 2024 +0200

    dnsforward: imp tests

commit 9d4de18934b1c5b0ae2edbec98bc84d5e0b23126
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Fri Apr 19 11:38:15 2024 +0200

    dnsforward: imp tests

commit a349374d90f48724ddca637e3d547f90026ff72a
Merge: 2243770b3 48c6242a7
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Wed Apr 17 11:02:56 2024 +0200

    Merge remote-tracking branch 'origin/master' into AG-31863-dnsforward-tests

commit 2243770b3a54a55eaf4fd48328a8e40a7b8a8349
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Tue Apr 16 10:56:40 2024 +0200

    dnsforward: imp tests

commit 4c4b565eec0ce8839b94cbaa0b29be6355e3c2e4
Merge: f1e4b72a8 201ac73cf
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Tue Apr 16 10:53:48 2024 +0200

    Merge remote-tracking branch 'origin/master' into AG-31863-dnsforward-tests

commit f1e4b72a8aa4fd0bbc738d959ca9be2f19fbc338
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Mon Apr 15 12:36:21 2024 +0200

    dnsforward: imp tests

commit 6ee6cc9519ddd31f0e78c7521ec612404f85e4b5
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Sun Apr 14 13:55:09 2024 +0200

    dnsforward: add test
2024-04-24 16:22:50 +03:00
Dimitry Kolyshev
60f48e2d00 Pull request: 6717-freebsd-daemon
Updates #6717.

Squashed commit of the following:

commit ae80a774e6d1863e2bc635cede30427d27457560
Merge: 73bd06124 f85d04831
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Tue Apr 23 13:26:05 2024 +0200

    Merge remote-tracking branch 'origin/master' into 6717-freebsd-daemon

commit 73bd06124bd4ec1f8c304e1e3ff3564a52d4500c
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Sun Apr 14 10:23:14 2024 +0200

    home: imp freebsd daemon
2024-04-23 15:51:48 +03:00
Eugene Burkov
f85d048315 Pull request 2207: 6882 Extend private rDNS
Updates #6882.

Squashed commit of the following:

commit 80fa6d62c67bdea6c4be6d8bcd066a0fb027a42a
Merge: c0fdf1a3c 762ef4a6d
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Apr 22 18:53:18 2024 +0300

    Merge branch 'master' into 6882-extend-private-rdns

commit c0fdf1a3c56990a1d86850c1f723769361b6133d
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Apr 18 18:19:36 2024 +0300

    client: imp ui text

commit f07a509d3d5b58f3fd83de304f6bfcb5c8c278e5
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Apr 18 16:22:11 2024 +0300

    all: imp docs, upd proxy

commit 0d33079a96b70d10d363a8c32be789963e75438c
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Apr 18 12:48:50 2024 +0300

    all: upd proxy
2024-04-22 19:17:30 +03:00
Dimitry Kolyshev
762ef4a6db Pull request: AG-31778-fix-safesearch-https
Squashed commit of the following:

commit 85ea3d985e83209e3b49119959aedd330df24d23
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Thu Apr 18 15:19:38 2024 +0200

    all: imp docs

commit b0695daddbcf191454c5e829ca4d19def8ddacbf
Merge: a79f98f2f 48c6242a7
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Wed Apr 17 11:06:49 2024 +0200

    Merge remote-tracking branch 'origin/master' into AG-31778-fix-safesearch-https

    # Conflicts:
    #	CHANGELOG.md

commit a79f98f2f215a4a79ca4d186c0da33db936429dc
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Wed Apr 17 11:05:34 2024 +0200

    dnsforward: imp code

commit b901a1169cc78313298d70cce770cd1523ccbf9f
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Tue Apr 16 11:03:52 2024 +0200

    dnsforward: imp code

commit fb6e66971b1b984147ec400ceaff856e7b5710c7
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Tue Apr 16 10:08:51 2024 +0200

    all: safesearch rewrites

commit 88add21831fff7e04539f5dd299832883a6f3995
Merge: b78ad8f74 201ac73cf
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Tue Apr 16 09:43:20 2024 +0200

    Merge remote-tracking branch 'origin/master' into AG-31778-fix-safesearch-https

    # Conflicts:
    #	CHANGELOG.md

commit b78ad8f748c7fa52533e0541cae16bd51c201370
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Fri Apr 12 13:34:39 2024 +0200

    all: safesearch rewrites

commit fb3efbb053242c537ca872542006917b8e8ac1ff
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Thu Apr 11 13:15:37 2024 +0200

    safesearch: imp code

commit 1193c704f4d30be4a2cc66e84a31c9a6020ab269
Merge: 14e823d7c ff7c715c5
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Thu Apr 11 13:13:44 2024 +0200

    Merge remote-tracking branch 'origin/master' into AG-31778-fix-safesearch-https

    # Conflicts:
    #	CHANGELOG.md

commit 14e823d7cc13c275c2ed04704883a94b95e29963
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Thu Apr 11 13:11:43 2024 +0200

    all: safesearch https

commit cd403a2897ae56a9059a78f24b104af5805d84ab
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Thu Apr 11 12:09:27 2024 +0200

    Revert "all: safesearch https"

    This reverts commit 1c9564b9b4db70f85b2f827cc06b65d2b67b08b1.

commit 1c9564b9b4db70f85b2f827cc06b65d2b67b08b1
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Wed Apr 10 12:41:47 2024 +0200

    all: safesearch https

commit 5f42688fbab566973acc8dc414a992819492a9ac
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Wed Apr 10 09:22:30 2024 +0200

    filtering: imp code

commit eb9bd9f47cd71cafe8eee4698a8a0d5d25dea3d3
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Wed Apr 10 09:19:22 2024 +0200

    all: changelog

commit 0c77c705a942fe83d3809a7efbc8a6baf5886762
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Wed Apr 10 08:55:22 2024 +0200

    safesearch: imp tests

commit 492a93fbb5ff54678e22a15577f509b2327c2ebe
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Tue Apr 9 14:45:16 2024 +0200

    all: changelog

commit a665e7246d11503c47d48ccc714e6862f764e930
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Tue Apr 9 14:41:24 2024 +0200

    safesearch: https req
2024-04-22 10:48:26 +03:00
Stanislav Chzhen
48c6242a7b Pull request 2201: 6192-access-ipv6-zone
Updates #6192.

Squashed commit of the following:

commit e98c2f0fff0d617bff36f1bb583b1a95fe3a1af9
Merge: 4dd9218d4 6f7d5cc4b
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Apr 16 17:24:38 2024 +0300

    Merge branch 'master' into 6192-access-ipv6-zone

commit 4dd9218d4eee5918c152ca768f5a0a5ed19bfa6f
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Apr 16 16:12:24 2024 +0300

    all: upd chlog

commit e126e12f7024aaf9bb4d9abe0acbc5c1ccf00977
Merge: d57c34c51 201ac73cf
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Apr 16 14:34:45 2024 +0300

    Merge branch 'master' into 6192-access-ipv6-zone

commit d57c34c51d2d5f6324c96e26a5a7c4134cae3a7f
Merge: decb768d3 df7f19eb8
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Apr 15 16:26:57 2024 +0300

    Merge branch 'master' into 6192-access-ipv6-zone

commit decb768d3b9a9352f8012ae1f3e112d5774e5428
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Apr 11 17:06:54 2024 +0300

    all: upd chlog

commit c8184bef8f3ec579049b4ac6b8451b611ae66e17
Merge: 5e0059b51 ff7c715c5
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Apr 11 16:52:10 2024 +0300

    Merge branch 'master' into 6192-access-ipv6-zone

commit 5e0059b5199466ea88b246d1fc27563fc71af6d3
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Apr 10 16:59:37 2024 +0300

    dnsforward: access ipv6 zone
2024-04-16 17:33:34 +03:00
Eugene Burkov
6f7d5cc4be Pull request 2202: Fix access error
Squashed commit of the following:

commit 55074010c38c0824c1df4a7c682a3baef4755015
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Apr 16 17:16:41 2024 +0300

    all: rm replace

commit 983f8d133199225f495e25efa4afae3ef6d2eee4
Merge: d9fc69d69 201ac73cf
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Apr 16 16:06:28 2024 +0300

    Merge branch 'master' into fix-access-error

commit d9fc69d69a315de83ef50a64bf9f5b4b2e50c8d3
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Apr 11 19:18:24 2024 +0300

    all: fix before request, upd golibs
2024-04-16 17:23:46 +03:00
Eugene Burkov
201ac73cf0 Pull request 2205: AGDNS-1982 Fix RDNS HTTP
Squashed commit of the following:

commit a7d5023390ce607d7f356edd79fe023348f84740
Merge: 0be18b91a df7f19eb8
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Apr 15 15:11:51 2024 +0300

    Merge branch 'master' into AGDNS-1982-fix-rdns-http

commit 0be18b91ac410cf43aa72b6eaed5c86dfdb93863
Merge: 54c1017a8 36986a8be
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Apr 15 15:04:42 2024 +0300

    Merge branch 'master' into AGDNS-1982-fix-rdns-http

commit 54c1017a8ee8986123596de89708a13a2a2f992d
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Apr 15 14:00:14 2024 +0300

    all: log changes

commit 851a1a3ac18debc46c8c3ff60576e2c067657564
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Apr 15 13:44:13 2024 +0300

    dnsforward: fix http private rdns
2024-04-15 19:31:04 +03:00
Stanislav Chzhen
df7f19eb8c Pull request 2199: 5812-query-log-client-id
Updates #5812.

Squashed commit of the following:

commit 43aa147fe125f85af4767f413c3c73cea5a4db6c
Merge: 97ab71284 36986a8be
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Apr 15 15:00:36 2024 +0300

    Merge branch 'master' into 5812-query-log-client-id

commit 97ab71284e6e215d0e8e9c4dd9d2ba97ef791ad3
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Apr 11 14:22:58 2024 +0300

    all: upd chlog

commit 525ac91977cfb36dc8f862fb36df11071f77c29d
Merge: 5d4db3f2e ff7c715c5
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Apr 11 14:15:28 2024 +0300

    Merge branch 'master' into 5812-query-log-client-id

commit 5d4db3f2ec42a8533e41dc9547bf061cd96451f2
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Apr 10 16:19:11 2024 +0300

    dnsforward: client id priority
2024-04-15 15:07:58 +03:00
Ainar Garipov
36986a8beb Pull request 2206: upd-golibs
Squashed commit of the following:

commit bb94329168215dffb12073bd8748453b17c52b02
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Mon Apr 15 13:53:23 2024 +0300

    all: upd golibs
2024-04-15 14:23:48 +03:00
Eugene Burkov
ff7c715c5f Pull request 2193: AGDNS-1982 Upd proxy
Closes #6854.Updates #6875.

Squashed commit of the following:

commit b98adbc0cc6eeaffb262d57775c487e03b1d5ba5
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Apr 10 19:21:44 2024 +0300

    dnsforward: upd proxy, imp code, docs

commit 4de1eb2bca1047426e02ba680c212f46782e5616
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Apr 10 16:09:58 2024 +0300

    WIP

commit afa9d61e8dc129f907dc681cd2f831cb5c3b054a
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Apr 9 19:24:09 2024 +0300

    all: log changes

commit c8340676a448687a39acd26bc8ce5f94473e441f
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Apr 9 19:06:10 2024 +0300

    dnsforward: move code

commit 08bb7d43d2a3f689ef2ef2409935dc3946752e94
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Apr 9 18:09:46 2024 +0300

    dnsforward: imp code

commit b27547ec806dd9bce502d3c6a7c28f33693ed575
Merge: b7efca788 6f36ebc06
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Apr 9 17:33:19 2024 +0300

    Merge branch 'master' into AGDNS-1982-upd-proxy

commit b7efca788b66aa672598b088040d4534ce2e55b0
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Apr 9 17:27:14 2024 +0300

    all: upd proxy finally

commit 3e16fa87befe4c0ef3a3e7a638d7add28627f9b6
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Apr 5 18:20:13 2024 +0300

    dnsforward: upd proxy

commit f3cdfc86334a182effcd0de22fac5e678fa53ea7
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Apr 4 20:37:32 2024 +0300

    all: upd proxy, golibs

commit a79298d6d0504521893ee11fdc3a23c098aea911
Merge: 9feeba5c7 fd25dcacb
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Apr 4 20:34:01 2024 +0300

    Merge branch 'master' into AGDNS-1982-upd-proxy

commit 9feeba5c7f24ff1d308a216608d985cb2a7b7588
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Apr 4 20:25:57 2024 +0300

    all: imp code, docs

commit 6c68d463db64293eb9c5e29ff91879fd68920a77
Merge: d8108e651 ee619b2db
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Apr 4 18:46:11 2024 +0300

    Merge branch 'master' into AGDNS-1982-upd-proxy

commit d8108e65164df8d67aa4e95154a8768a06255b78
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Apr 3 19:25:27 2024 +0300

    all: imp code

commit 20461565801c9fcd06a652c6066b524b06c80433
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Apr 3 17:10:33 2024 +0300

    all: remove private rdns logic
2024-04-11 14:03:37 +03:00
looklose
9f319d5c51 chore: fix function names in comment
Signed-off-by: looklose <shishuaiqun@yeah.net>
2024-04-10 19:40:42 +08:00
Stanislav Chzhen
6f36ebc06c Pull request 2189: 5345-ipset-file-comments
Updates #5345.

Squashed commit of the following:

commit 66ceac9ac2646bd9a8d6be545b283008342f3153
Merge: 8be4dce3c 82247d764
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Fri Apr 5 19:05:48 2024 +0300

    Merge branch 'master' into 5345-ipset-file-comments

commit 8be4dce3ca7420464084ce062aac81de9ddb53c8
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Apr 2 14:56:21 2024 +0300

    all: upd chlog

commit 53c72130157b4bb8c55208f2faee24a80456b85f
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Apr 2 14:48:56 2024 +0300

    dnsforward: ipset file comments
2024-04-05 19:32:38 +03:00
Ainar Garipov
82247d764a Pull request 2197: upd-chlog
Squashed commit of the following:

commit 85deb72cd2293e7467e225c0a5eaef36f7a0f5b4
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Fri Apr 5 18:30:40 2024 +0300

    all: upd chlog
2024-04-05 18:50:50 +03:00
Stanislav Chzhen
fd25dcacbd Pull request 2183: AG-27492-client-runtime-index
Squashed commit of the following:

commit d0b37e3de1552ea42d776461045a76ff0ae18128
Merge: 025c29bcd ee619b2db
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Apr 4 18:58:08 2024 +0300

    Merge branch 'master' into AG-27492-client-runtime-index

commit 025c29bcd279b5448908df7c3a1a997a64095641
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Apr 1 17:20:15 2024 +0300

    client: imp code

commit 548a15c000db3989b1398e68fa4c05a450e93ea0
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Mar 28 13:43:17 2024 +0300

    all: add tests

commit c9015e732f1e0475ec8cf95487c9ec56cd69a395
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Mar 25 16:33:30 2024 +0300

    all: imp docs

commit 81e8b944928176733b2971b2b6400b55496a7843
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Mar 25 15:33:17 2024 +0300

    all: imp code

commit 1428d60bf72d7a0ffd9dc854403391646f82c6cc
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Mar 25 14:45:01 2024 +0300

    all: client runtime index
2024-04-04 19:17:23 +03:00
Ainar Garipov
ee619b2dbd Pull request 2195: upd-go-code
Squashed commit of the following:

commit a1bd3c249be043108c84a902d2e88bf80946d444
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Apr 4 14:59:37 2024 +0300

    all: upd more

commit 9e55bbb02c2af2064aa2a2ca7b49fd28b544a02c
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Apr 4 14:12:45 2024 +0300

    all: upd go code
2024-04-04 15:52:39 +03:00
Ainar Garipov
0e1e568899 Pull request 2194: upd-all
Squashed commit of the following:

commit 483b77abb95949089542997b980188c453a519f6
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Apr 3 20:41:12 2024 +0300

    all: upd docker

commit 6fff1f8da88d60621382508a735961413289b54d
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Apr 3 20:25:07 2024 +0300

    all: upd go, i18n, flts, svcs
2024-04-03 21:17:01 +03:00
Ainar Garipov
5cc05e2c4b Pull request 2187: upd-golibs
Squashed commit of the following:

commit 63c14cf0eb395f58149f5a82ff1389353f7f8127
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Tue Apr 2 20:10:10 2024 +0300

    all: imp code, docs

commit 185ccdd1d9f5acc8376fabeac647f6fddcf108b5
Merge: b6ca80a9f d4fff41b3
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Tue Apr 2 20:04:23 2024 +0300

    Merge branch 'master' into upd-golibs

commit b6ca80a9f639394758cc9000345c132a713c183c
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Tue Apr 2 20:01:10 2024 +0300

    all: upd to tags

commit 474f62319befbe22cf1bccd2320cd0d3da1629b1
Author: Ainar Garipov <a.garipov@adguard.com>
Date:   Tue Mar 26 16:33:45 2024 +0300

    all: upd golibs
2024-04-03 13:44:51 +03:00
Stanislav Chzhen
d4fff41b3a Pull request 2190: 6758-embed-tzdata
Closes #6758.

Squashed commit of the following:

commit d62b4271d9861e328a1d5cd9f57123ddb5fadb40
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Apr 2 18:25:34 2024 +0300

    all: add docs

commit bb129d749fa94926150e301a58f13ee3be78eca0
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Apr 2 17:04:42 2024 +0300

    all: upd chlog

commit 59573d9869668ff9ddb1ef7d646a034799882bf8
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Apr 2 15:38:42 2024 +0300

    all: embed tzdata
2024-04-02 18:43:24 +03:00
Ainar Garipov
d951692aca Pull request 2188: imp-readme
Squashed commit of the following:

commit 1abd78ab1a59122d276029f9717273da56c199b7
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Mon Apr 1 17:53:50 2024 +0300

    all: imp readme; add projects
2024-04-01 18:12:33 +03:00
Dimitry Kolyshev
7d9f33b2f0 Pull request: 6717-fix-conf-symlink
Updates #6717.

Squashed commit of the following:

commit d17a6de1485e68887a1732dfaa6844f433519b9f
Merge: 806ff9bad 9305c4581
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Mon Apr 1 09:22:33 2024 +0800

    Merge remote-tracking branch 'origin/master' into 6717-fix-conf-symlink

    # Conflicts:
    #	CHANGELOG.md

commit 806ff9bad09e8bf253a1bdc379fbed2b1a56c5a7
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Thu Mar 28 09:00:00 2024 +0800

    home: imp code

commit 06dbcfead7c43fac146e92e4a83145dcd5c07b40
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Wed Mar 27 13:55:30 2024 +0800

    all: changelog

commit 5476625eacfda06efec768865e0885c502d37607
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Wed Mar 27 13:43:17 2024 +0800

    home: fix conf symlink usage
2024-04-01 11:31:51 +03:00
Eugene Burkov
9305c45813 Pull request 2184: 6851 upstream mode reset
Updates #6851.

Squashed commit of the following:

commit ffc50daff8e9a2b0fd48f10c5e66cdc0ab350488
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Mar 25 17:55:39 2024 +0300

    all: fix changelog

commit cdc2193875e72f6504cf283c84e1e6c6768d4f6e
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Mar 25 17:28:06 2024 +0300

    dnsforward: fix upstream mode set
2024-03-29 10:45:20 +03:00
Ainar Garipov
2611534de0 Pull request 2182: AG-20945-rule-list-id
Squashed commit of the following:

commit 87bad8c1c202902f344ad75c7b767f03d5123a4a
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Mar 21 16:39:12 2024 +0300

    all: imp lint, names, tests

commit 284f8c7cc0c26d443342ad3d39007152714c0799
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Mar 21 15:37:54 2024 +0300

    filtering: imp id handling
2024-03-21 18:45:34 +03:00
Ainar Garipov
70c88f2ba2 Pull request 2181: imp-docs
Squashed commit of the following:

commit 1f7be03896a6eaf0a292e4c7e9a7ef7c712ed734
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Mar 21 13:35:37 2024 +0300

    all: use standard md style in some docs
2024-03-21 13:51:08 +03:00
Ainar Garipov
2e5595defd Pull request 2180: fix-docker-build
Squashed commit of the following:

commit 8713a0851ab54360179d0525b43a23852a369a83
Merge: 7eed5e88c 3b12ff2cc
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Mar 20 20:26:11 2024 +0300

    Merge branch 'master' into fix-docker-build

commit 7eed5e88cc6361d0b40016f3c7f01eac30175cce
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Mar 20 20:12:16 2024 +0300

    bamboo-specs: fix docker build
2024-03-20 20:34:58 +03:00
Stanislav Chzhen
3b12ff2cc2 Pull request 2166: 5829-trusted-ip
Updates #5829.

Squashed commit of the following:

commit 8a93b30d5bd1c40c30bd10cd3fc77c3a3a64cb71
Merge: 8e4429c48 54f77c010
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Mar 20 19:15:07 2024 +0300

    Merge branch 'master' into 5829-trusted-ip

commit 8e4429c483c0fd6fffdc93fa808adcca6678bc3e
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Mar 20 18:37:26 2024 +0300

    all: upd chlog

commit b598a8d1ea239cc574bfdfdd6a2da47792582589
Merge: 1f58bf8fd 054233962
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Mar 20 18:34:13 2024 +0300

    Merge branch 'master' into 5829-trusted-ip

commit 1f58bf8fd1bc3b3790475651cb87494885cadf66
Merge: ffb4b9a65 c64a36c94
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Mar 20 17:09:09 2024 +0300

    Merge branch 'master' into 5829-trusted-ip

commit ffb4b9a65fea5555d0d401194d3fc3820b2e6766
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Mar 14 17:40:07 2024 +0300

    home: fix alignment

commit 7f11807ff13eff286be1d3bd4b796273454bdbda
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Mar 14 17:35:13 2024 +0300

    all: imp code

commit 2aee9a66c70af929e28653245eb73c0f29a46e97
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Mar 11 18:17:58 2024 +0300

    home: real ip in logs
2024-03-20 19:25:59 +03:00
Ainar Garipov
54f77c0101 Pull request 2179: upd-docker
Squashed commit of the following:

commit 17e145c87b1fabde75d6dbe96570a1720a2e1306
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Mar 20 18:49:18 2024 +0300

    bamboo-specs: upd builder img

commit d6399a084b0e6b26cf27acc617838fdf7ea94e00
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Mar 20 18:35:01 2024 +0300

    bamboo-specs: fix frontend build

commit 9f4cbf5fd8f085e634ff021ab5320454856b3425
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Mar 20 18:21:50 2024 +0300

    bamboo-specs: fix yaml

commit a26d2ab063c23873e132e659ae4ba1035325ad3a
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Mar 20 18:18:57 2024 +0300

    all: use new docker imgs
2024-03-20 19:03:46 +03:00
Ainar Garipov
0542339623 Pull request 2178: upd-chlog
Squashed commit of the following:

commit 026be50666057f46d9b2454f31b618c34e82e4a2
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Mar 20 17:57:17 2024 +0300

    all: upd chlog
2024-03-20 18:09:25 +03:00
Ainar Garipov
c64a36c94d Pull request 2176: AG-20945-rule-list-engine
Squashed commit of the following:

commit 56756b2299dd01d79dc9bdbe873ffdab30e78c58
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Fri Mar 15 15:40:39 2024 +0300

    all: imp code, docs, tests

commit 45849e651ced92ebb889ff3902c7710e2161b5a3
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Mar 14 20:18:07 2024 +0300

    rulelist: add engine, textengine
2024-03-15 17:23:36 +03:00
Ainar Garipov
ee0144185f Pull request 2175: split-snap-docker
Squashed commit of the following:

commit 895f50c3c811a3f4d5b9d514f4b24129db104b1f
Merge: 5bd2edd06 4e3b53f1b
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Mar 14 19:11:32 2024 +0300

    Merge branch 'master' into split-snap-docker

commit 5bd2edd06bc359b4eb9cdde6309a256945830248
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Mar 14 19:00:06 2024 +0300

    bamboo-specs: split snap docker, use go-builder
2024-03-14 19:21:48 +03:00
Eugene Burkov
4e3b53f1b7 Pull request 2174: 6820 Warn local ptrs
Squashed commit of the following:

commit c2319658a49eb750c9c362632697c481ff560c71
Merge: c6162a211 bcd143068
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Mar 14 18:10:20 2024 +0300

    Merge branch 'master' into 6820-warn-local-ptrs

commit c6162a211b96e220271383bd8c84e87ad44ba7f8
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Mar 13 19:35:20 2024 +0300

    dnsforward: fix doc

commit c6cce9644e629a085f3b66cac503e9de0bc9b753
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Mar 13 19:19:49 2024 +0300

    all: fix private conf fail on start

commit c11fc3e7abcd4f592d44a3c163b554017eb305a0
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Mar 13 18:43:31 2024 +0300

    WIP
2024-03-14 18:19:27 +03:00
Ainar Garipov
bcd1430680 Pull request 2173: upd-all
Squashed commit of the following:

commit 19b10b5834cc9f7b0121620270603419bc994e8f
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Mar 13 17:20:52 2024 +0300

    client: upd i18n more

commit 9f44193c4d27b800bf0d4c6af2e6bac5a545dbb0
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Mar 13 17:04:55 2024 +0300

    all: upd deps, i18n, svcs
2024-03-13 17:59:21 +03:00
Eugene Burkov
3608a2def2 Pull request 2171: AG-28455 remove checkout
Squashed commit of the following:

commit 3b90850dae360878925a82a48eeb360ecf6c6969
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Mar 13 15:34:11 2024 +0300

    version: sort version strings

commit 7a9cb73a2f6c3579e104745b5c31abdd9edc8e33
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Mar 13 13:02:36 2024 +0300

    all: account candidate channel

commit 76d237412f17ddfdd09def97f13489a4fc98d641
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Mar 12 20:02:55 2024 +0300

    bamboo-specs: add todo

commit f99c726386a4a3ef322a32677a6c0a0e2dd6bbcc
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Mar 12 20:01:38 2024 +0300

    bamboo-specs: revert release

commit 70a3b3e93647ed9474e0679e6e2d91b4b06691d4
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Mar 12 19:58:20 2024 +0300

    bamboo-specs: rm explicit checkout
2024-03-13 15:51:22 +03:00
Ainar Garipov
14c6be3b1f Pull request 2169: fix-rc
Squashed commit of the following:

commit 153271c34258624e016e8e8a4602d1095aa71159
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Tue Mar 12 18:08:26 2024 +0300

    all: rm trigger for rc
2024-03-12 18:15:22 +03:00
Stanislav Chzhen
6409011510 Pull request 2164: 6712-hourly-graphs
Updates #6712.

Squashed commit of the following:

commit dd4c822dfea04cdedf2f3fd5ea7d492ef1c5f7d1
Merge: 73207860f 28a6b9f30
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Mar 12 12:38:19 2024 +0300

    Merge branch 'master' into 6712-hourly-graphs

commit 73207860f6428ad2c7d3afe7e2d654cfeefcffdc
Merge: ca29ee8e4 5388ad55a
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Mar 7 16:25:49 2024 +0300

    Merge branch 'master' into 6712-hourly-graphs

commit ca29ee8e41bf34b7fa4d261a2c9d2a809b97f874
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Mar 7 14:41:45 2024 +0300

    all: upd chlog

commit 9d6154aef8f0c4db24c50bee6aaedc4b98e26b3c
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Mar 7 14:10:32 2024 +0300

    all: hourly graphs
2024-03-12 13:13:59 +03:00
Eugene Burkov
28a6b9f303 Pull request 2159: Upd proxy
Squashed commit of the following:

commit 4468e826bcf8e856014059cac7cd83aa6d754ab0
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Mar 11 13:50:36 2024 +0300

    all: upd dnsproxy

commit 7887f521799b75ea43aac8eca0586d26a1287c94
Merge: 9120da6ab 36f9fecae
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Mar 11 13:50:09 2024 +0300

    Merge branch 'master' into upd-proxy

commit 9120da6ab841fc8f9090c16f0cfe6a103073c772
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Mar 5 11:50:16 2024 +0300

    all: upd proxy
2024-03-11 18:17:04 +03:00
Ildar Kamalov
36f9fecaed Pull request: fix served from cache label
Updates #6740.

Squashed commit of the following:

commit a3cb491099060c6bb1f543196c6d179edddb9a3a
Author: Ildar Kamalov <ik@adguard.com>
Date:   Thu Mar 7 17:15:55 2024 +0300

    fix tooltip label

commit 4ab301e34ca2983e3bbcaeb914b3707bcec99a25
Merge: 6bc325d35 5388ad55a
Author: Ildar Kamalov <ik@adguard.com>
Date:   Thu Mar 7 17:08:45 2024 +0300

    Merge branch 'master' into ADG-8259

commit 6bc325d35db9cdbf1199a309fbef50e8aefa0834
Author: Ildar Kamalov <ik@adguard.com>
Date:   Thu Mar 7 09:58:35 2024 +0300

    fix icon

commit 8274faaf39c62d1777c21ab5d9b6fdfd88674527
Author: Ildar Kamalov <ik@adguard.com>
Date:   Thu Mar 7 09:56:50 2024 +0300

    ADG-8259 fix server from cache label
2024-03-11 11:12:45 +03:00
Ainar Garipov
5388ad55ab Pull request 2165: upd-chlog
Squashed commit of the following:

commit 74c903d1a3d3c299399915c6c7b6224f3ddcaffc
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Mar 7 15:44:52 2024 +0300

    all: upd chlog
2024-03-07 15:52:13 +03:00
Ainar Garipov
5df1d32fba Pull request 2162: upd-all
Squashed commit of the following:

commit 206c01e3ff58f474c6f0cfa95d0435bda9b5c98b
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Mar 6 17:54:21 2024 +0300

    all: upd i18n, svcs, tools, trackers
2024-03-06 18:05:01 +03:00
Stanislav Chzhen
98bcadd00c Pull request 2160: AG-30904-fix-ip-in-logs
Squashed commit of the following:

commit 0a54ca2746dac7a21d05265f26c161df763af7d8
Merge: 1d30c0995 5565b9e1c
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Mar 6 16:19:36 2024 +0300

    Merge branch 'master' into AG-30904-fix-ip-in-logs

commit 1d30c099577bda192609049bab4510cf6f69a972
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Mar 5 17:14:02 2024 +0300

    all: missing ip in logs
2024-03-06 17:09:06 +03:00
Eugene Burkov
5565b9e1c1 Pull request 2161: Upd Go
Squashed commit of the following:

commit 304703195e66e935a5900b07d1cefea262a4689d
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Mar 6 15:42:57 2024 +0300

    all: log changes better

commit c47f31ac7f6be6d5bada670065f20c32b0bd29e9
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Mar 6 15:33:19 2024 +0300

    bamboo-specs: upd img

commit 5286ca1afe2c04e9b1d0b55bcbcd02d462947e8c
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Mar 6 14:59:18 2024 +0300

    tools: upd go

commit fd5e94f0d4afadbf48deab1cee4ee23f82f1bd0f
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Mar 6 14:11:05 2024 +0300

    all: upd go
2024-03-06 16:12:40 +03:00
Dimitry Kolyshev
7f24fc7ccc Pull request: 6770 Fix blank settings page when access clients first
* commit 'da2cf75480369e304b3f3e06001b9e146f589a90':
  Fix blank settings page when access clients first
2024-03-01 13:44:52 +03:00
Hoàng Rio
da2cf75480 Merge branch 'AdguardTeam:master' into fix/blank-settings-when-access-clients-first 2024-02-28 20:55:00 +07:00
Eugene Burkov
499dcaa115 Pull request 2154: 6723 caching bootstrap
Updates #6723.

Squashed commit of the following:

commit 594286c7b9cddd73e4360fb731d04172ba1edef0
Merge: 17411a7f9 31c3d7d30
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Feb 28 14:01:06 2024 +0300

    Merge branch 'master' into 6723-caching-bootstrap

commit 17411a7f9163857ad16f60c961d13d1383407cbb
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Feb 26 20:51:01 2024 +0300

    all: up[d finally

commit f47491c872af1f731132558ac655a34387880a9d
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Feb 21 14:25:49 2024 +0300

    all: log changes

commit a04fc78f10f3b7790a620ce139377338f7ac92ea
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Feb 20 16:30:08 2024 +0300

    all: upd proxy
2024-02-28 14:10:03 +03:00
Stanislav Chzhen
31c3d7d302 Pull request 2155: AG-27492-client-persistent-index
Squashed commit of the following:

commit 1f99640f9f0a24ade7d2325737edf83ad0da3895
Merge: 5a9211e8c 9276afd79
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Feb 27 13:13:03 2024 +0300

    Merge branch 'master' into AG-27492-client-persistent-index

commit 5a9211e8c7832700ae4f58cea25ad38ccba98efa
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Feb 22 19:08:35 2024 +0300

    all: add todo

commit a4fc94904b0b05ed5ca5ba270125a7d7fb1e6817
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Feb 21 14:48:33 2024 +0300

    all: client persistent index
2024-02-27 13:48:11 +03:00
Hoàng Rio
560758b812 Fix blank settings page when access clients first 2024-02-26 09:08:27 +07:00
Ainar Garipov
9276afd79d Pull request 2156: 6717-conf-path-logs
Updates #6717.

Squashed commit of the following:

commit 05a7e18923e987ea9bd6294fe675a22cf86f6dce
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Feb 21 16:44:00 2024 +0300

    home: imp docs, logs
2024-02-21 17:01:15 +03:00
Stanislav Chzhen
4605e7c90e Pull request 2153: 6610-hostsfile-enabled
Updates #6610.

Squashed commit of the following:

commit 13522f0768fe0a4f6abe3fc82ffb7ddf3f4df9bf
Merge: befa3bd1d 6fd0a624c
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Feb 21 12:53:08 2024 +0300

    Merge branch 'master' into 6610-hostsfile-enabled

commit befa3bd1dedcd2853ef299c06d5c14c9ed7f2c84
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Feb 19 15:56:20 2024 +0300

    all: upd chlog

commit c1954306fe7c4d7af164f599b298374ab983216f
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Feb 15 20:52:52 2024 +0300

    home: hostsfile enabled
2024-02-21 13:04:58 +03:00
Eugene Burkov
6fd0a624ca Pull request 2152: 4923 gopacket DHCP vol.7
Updates #4923.

Squashed commit of the following:

commit 0f90eb3596fcbca0d87cb4eb857d45aea26f3854
Merge: 38b3165b6 bd99e3e09
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Feb 19 20:11:38 2024 +0300

    Merge branch 'master' into 4923-gopacket-dhcp-vol.7

commit 38b3165b696c9a3f69484d8e4c2c847340ed9363
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Feb 19 14:52:01 2024 +0300

    dhcpsvc: imp docs

commit 0a078920a20de9fb2d864c90d2311800c6f3bc3f
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Feb 19 14:48:19 2024 +0300

    dhcpsvc: imp code

commit 30691f0d989c48b2f0dff8079952615dbfbdaea1
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Feb 15 19:57:41 2024 +0300

    dhcpsvc: imp code, dry

commit 20f5ef80fb2d1cad869883da3684a01e5b8b3315
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Feb 15 15:57:09 2024 +0300

    dhcpsvc: finish leases methods
2024-02-20 14:52:38 +03:00
Eugene Burkov
bd99e3e09d Pull request 2150: AG-28455 rc versions
Squashed commit of the following:

commit 9b80bf2da8676c7a80982b88b547b35760afd4dd
Merge: 2c184158c fede29794
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Feb 16 15:49:23 2024 +0300

    Merge branch 'master' into AG-28455-rc-versions

commit 2c184158c052dc1ddc57f4bdf53ad31a50410659
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Feb 14 13:01:30 2024 +0300

    scripts: imp code

commit f0965058ad2231b342cf406a8434d76cc3f546c2
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Feb 13 14:52:10 2024 +0300

    scripts: imp code

commit a5bed23c7077ea3655ae88528c1dfc5ea9d061a7
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Feb 12 18:35:52 2024 +0300

    scripts: fix typo

commit 36e9ea1ac3403a53452fcc16d35d32ec7663fd1e
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Feb 12 15:41:57 2024 +0300

    scripts: revert changes, imp docs

commit d74c85d4ec77b12ceaae28db5a8e7961896d688a
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Feb 5 18:12:38 2024 +0300

    all: mark rc versions separately
2024-02-16 19:19:00 +03:00
Stanislav Chzhen
fede297942 Pull request 2138: AG-27492-client-persistent-storage
Squashed commit of the following:

commit 37e33ec761cfa30164125af2c5bb40789412355e
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Feb 14 15:25:25 2024 +0300

    aghalg: imp code

commit 6b2f09a44298b474ec1bdf3d027fb4941d2f7bea
Merge: b8ea924aa 37736289e
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Feb 14 15:04:59 2024 +0300

    Merge branch 'master' into AG-27492-client-persistent-storage

commit b8ea924aa7ed4c052760a6068f945d83d184e7e3
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Feb 13 19:07:52 2024 +0300

    home: imp tests

commit aa6fec03b1a1ead96bc76919b7ad51ae19626633
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Feb 13 14:54:28 2024 +0300

    home: imp docs

commit 10637fdec47d0b035cf5c7949ddcd9ec564851a3
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Feb 8 20:16:11 2024 +0300

    all: imp code

commit b45c7d868ddb1be73e119b3260e2a866d57baa91
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Feb 7 19:15:11 2024 +0300

    aghalg: add tests

commit 7abe33dbaa7221ddbc8b7d802dbfa7f951d90cf8
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Feb 6 20:50:22 2024 +0300

    all: imp code, tests

commit 4a44e993c9bd393d2cb9853108eae1ad91e64402
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Feb 1 14:59:11 2024 +0300

    all: persistent client index

commit 66b16e216e03e9f3d5e69496a89b18a9d732b564
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Jan 31 15:06:05 2024 +0300

    aghalg: ordered map
2024-02-15 14:08:05 +03:00
167 changed files with 6245 additions and 3653 deletions

View File

@@ -1,7 +1,7 @@
'name': 'build'
'env':
'GO_VERSION': '1.21.7'
'GO_VERSION': '1.22.3'
'NODE_VERSION': '16'
'on':
@@ -53,9 +53,9 @@
'path': '${{ steps.npm-cache.outputs.dir }}'
'key': "${{ runner.os }}-node-${{ hashFiles('client/package-lock.json') }}"
'restore-keys': '${{ runner.os }}-node-'
- 'name': 'Run make ci'
- 'name': 'Run tests'
'shell': 'bash'
'run': 'make VERBOSE=1 ci'
'run': 'make VERBOSE=1 deps test go-bench go-fuzz'
- 'name': 'Upload coverage'
'uses': 'codecov/codecov-action@v1'
'if': "success() && matrix.os == 'ubuntu-latest'"

View File

@@ -1,7 +1,7 @@
'name': 'lint'
'env':
'GO_VERSION': '1.21.7'
'GO_VERSION': '1.22.3'
'on':
'push':

View File

@@ -14,24 +14,202 @@ and this project adheres to
<!--
## [v0.108.0] - TBA
## [v0.107.45] - 2024-03-05 (APPROX.)
## [v0.107.51] - 2024-06-22 (APPROX.)
See also the [v0.107.45 GitHub milestone][ms-v0.107.45].
See also the [v0.107.51 GitHub milestone][ms-v0.107.51].
[ms-v0.107.45]: https://github.com/AdguardTeam/AdGuardHome/milestone/80?closed=1
[ms-v0.107.51]: https://github.com/AdguardTeam/AdGuardHome/milestone/86?closed=1
NOTE: Add new changes BELOW THIS COMMENT.
-->
<!--
NOTE: Add new changes ABOVE THIS COMMENT.
-->
## [v0.107.50] - 2024-05-23
See also the [v0.107.50 GitHub milestone][ms-v0.107.50].
### Fixed
- Broken private reverse DNS upstream servers validation causing update failures
([#7013]).
[#7013]: https://github.com/AdguardTeam/AdGuardHome/issues/7013
[ms-v0.107.50]: https://github.com/AdguardTeam/AdGuardHome/milestone/85?closed=1
## [v0.107.49] - 2024-05-21
See also the [v0.107.49 GitHub milestone][ms-v0.107.49].
### Security
- Go version has been updated to prevent the possibility of exploiting the Go
vulnerabilities fixed in Go 1.21.6 and Go 1.21.7.
vulnerabilities fixed in [Go 1.22.3][go-1.22.3].
### Added
- Support for comments in the ipset file ([#5345]).
### Changed
- Private rDNS resolution now also affects `SOA` and `NS` requests ([#6882]).
- Rewrite rules mechanics were changed due to improved resolving in safe search.
### Deprecated
- Currently, AdGuard Home skips persistent clients that have duplicate fields
when reading them from the configuration file. This behaviour is deprecated
and will cause errors on startup in a future release.
### Fixed
- Acceptance of duplicate UIDs for persistent clients at startup. See also the
section on client settings on the [Wiki page][wiki-config].
- Domain specifications for top-level domains not considered for requests to
unqualified domains ([#6744]).
- Support for link-local subnets, i.e. `fe80::/16`, as client identifiers
([#6312]).
- Issues with QUIC and HTTP/3 upstreams on older Linux kernel versions
([#6422]).
- YouTube restricted mode is not enforced by HTTPS queries on Firefox.
- Support for link-local subnets, i.e. `fe80::/16`, in the access settings
([#6192]).
- The ability to apply an invalid configuration for private rDNS, which led to
server not starting.
- Ignoring query log for clients with ClientID set ([#5812]).
- Subdomains of `in-addr.arpa` and `ip6.arpa` containing zero-length prefix
incorrectly considered invalid when specified for private rDNS upstream
servers ([#6854]).
- Unspecified IP addresses aren't checked when using "Fastest IP address" mode
([#6875]).
[#5345]: https://github.com/AdguardTeam/AdGuardHome/issues/5345
[#5812]: https://github.com/AdguardTeam/AdGuardHome/issues/5812
[#6192]: https://github.com/AdguardTeam/AdGuardHome/issues/6192
[#6312]: https://github.com/AdguardTeam/AdGuardHome/issues/6312
[#6422]: https://github.com/AdguardTeam/AdGuardHome/issues/6422
[#6744]: https://github.com/AdguardTeam/AdGuardHome/issues/6744
[#6854]: https://github.com/AdguardTeam/AdGuardHome/issues/6854
[#6875]: https://github.com/AdguardTeam/AdGuardHome/issues/6875
[#6882]: https://github.com/AdguardTeam/AdGuardHome/issues/6882
[go-1.22.3]: https://groups.google.com/g/golang-announce/c/wkkO4P9stm0
[ms-v0.107.49]: https://github.com/AdguardTeam/AdGuardHome/milestone/84?closed=1
## [v0.107.48] - 2024-04-05
See also the [v0.107.48 GitHub milestone][ms-v0.107.48].
### Fixed
- Access settings not being applied to encrypted protocols ([#6890]).
[#6890]: https://github.com/AdguardTeam/AdGuardHome/issues/6890
[ms-v0.107.48]: https://github.com/AdguardTeam/AdGuardHome/milestone/83?closed=1
## [v0.107.47] - 2024-04-04
See also the [v0.107.47 GitHub milestone][ms-v0.107.47].
### Security
- Go version has been updated to prevent the possibility of exploiting the Go
vulnerabilities fixed in [Go 1.22.2][go-1.22.2].
### Changed
- Time Zone Database is now embedded in the binary ([#6758]).
- Failed authentication attempts show the originating IP address in the logs, if
the request came from a trusted proxy ([#5829]).
### Deprecated
- Go 1.22 support. Future versions will require at least Go 1.23 to build.
- Currently, AdGuard Home uses a best-effort algorithm to fix invalid IDs of
filtering-rule lists on startup. This feature is deprecated, and invalid IDs
will cause errors on startup in a future version.
- Node.JS 16. Future versions will require at least Node.JS 18 to build.
### Fixed
- Resetting DNS upstream mode when applying unrelated settings ([#6851]).
- Symbolic links to the configuration file begin replaced by a copy of the real
file upon startup on FreeBSD ([#6717]).
### Removed
- Go 1.21 support.
[#5829]: https://github.com/AdguardTeam/AdGuardHome/issues/5829
[#6717]: https://github.com/AdguardTeam/AdGuardHome/issues/6717
[#6758]: https://github.com/AdguardTeam/AdGuardHome/issues/6758
[#6851]: https://github.com/AdguardTeam/AdGuardHome/issues/6851
[go-1.22.2]: https://groups.google.com/g/golang-announce/c/YgW0sx8mN3M/
[ms-v0.107.47]: https://github.com/AdguardTeam/AdGuardHome/milestone/82?closed=1
## [v0.107.46] - 2024-03-20
See also the [v0.107.46 GitHub milestone][ms-v0.107.46].
### Added
- Ability to disable the use of system hosts file information for query
resolution ([#6610]).
- Ability to define custom directories for storage of query log files and
statistics ([#5992]).
### Changed
- Private rDNS resolution (`dns.use_private_ptr_resolvers` in YAML
configuration) now requires a valid "Private reverse DNS servers", when
enabled ([#6820]).
**NOTE:** Disabling private rDNS resolution behaves effectively the same as if
no private reverse DNS servers provided by user and by the OS.
### Fixed
- Statistics for 7 days displayed by day on the dashboard graph ([#6712]).
- Missing "served from cache" label on long DNS server strings ([#6740]).
- Incorrect tracking of the system hosts file's changes ([#6711]).
[#5992]: https://github.com/AdguardTeam/AdGuardHome/issues/5992
[#6610]: https://github.com/AdguardTeam/AdGuardHome/issues/6610
[#6711]: https://github.com/AdguardTeam/AdGuardHome/issues/6711
[#6712]: https://github.com/AdguardTeam/AdGuardHome/issues/6712
[#6740]: https://github.com/AdguardTeam/AdGuardHome/issues/6740
[#6820]: https://github.com/AdguardTeam/AdGuardHome/issues/6820
[ms-v0.107.46]: https://github.com/AdguardTeam/AdGuardHome/milestone/81?closed=1
## [v0.107.45] - 2024-03-06
See also the [v0.107.45 GitHub milestone][ms-v0.107.45].
### Security
- Go version has been updated to prevent the possibility of exploiting the Go
vulnerabilities fixed in [Go 1.21.8][go-1.21.8].
### Added
- Context menu item in the Query Log to add a Client to the Persistent client
list ([#6679]).
@@ -59,21 +237,22 @@ NOTE: Add new changes BELOW THIS COMMENT.
### Fixed
- Incorrect tracking of the system hosts file's changes ([#6711]).
- Missing IP addresses in logs when querying for domain names from the ignore
lists.
- Blank page after resetting access clients ([#6634]).
- Wrong algorithm for caching bootstrapped upstream addresses ([#6723]).
### Removed
- Go 1.20 support, as it has reached end of life.
[#5992]: https://github.com/AdguardTeam/AdGuardHome/issues/5992
[#6634]: https://github.com/AdguardTeam/AdGuardHome/issues/6634
[#6679]: https://github.com/AdguardTeam/AdGuardHome/issues/6679
[#6711]: https://github.com/AdguardTeam/AdGuardHome/issues/6711
[#6723]: https://github.com/AdguardTeam/AdGuardHome/issues/6723
[go-1.21.8]: https://groups.google.com/g/golang-announce/c/5pwGVUPoMbg
[go-toolchain]: https://go.dev/blog/toolchain
<!--
NOTE: Add new changes ABOVE THIS COMMENT.
-->
[ms-v0.107.45]: https://github.com/AdguardTeam/AdGuardHome/milestone/80?closed=1
@@ -2807,11 +2986,17 @@ See also the [v0.104.2 GitHub milestone][ms-v0.104.2].
<!--
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.45...HEAD
[v0.107.45]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.44...v0.107.45
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.51...HEAD
[v0.107.51]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.50...v0.107.51
-->
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.44...HEAD
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.50...HEAD
[v0.107.50]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.49...v0.107.50
[v0.107.49]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.48...v0.107.49
[v0.107.48]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.47...v0.107.48
[v0.107.47]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.46...v0.107.47
[v0.107.46]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.45...v0.107.46
[v0.107.45]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.44...v0.107.45
[v0.107.44]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.43...v0.107.44
[v0.107.43]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.42...v0.107.43
[v0.107.42]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.41...v0.107.42

View File

@@ -1,89 +1,57 @@
# Contributing to AdGuard Home
# Contributing to AdGuard Home
If you want to contribute to AdGuard Home by filing or commenting on an issue or
opening a pull request, please follow the instructions below.
If you want to contribute to AdGuard Home by filing or commenting on an issue or opening a pull request, please follow the instructions below.
## General recommendations
Please dont:
## General recommendations
- post comments like “+1” or “this”. Use the :+1: reaction on the issue instead, as this allows us to actually see the level of support for issues.
Please don't:
- file issues about localization errors or send localization updates as PRs. Were using [CrowdIn] to manage our translations and we generally update them before each Beta and Release build. You can learn more about translating AdGuard products [in our Knowledge Base][kb-trans].
* post comments like “+1” or “this”. Use the :+1: reaction on the issue
instead, as this allows us to actually see the level of support for issues.
- file issues about a particular filtering-rule list misbehaving. These are tracked through the [separate form for filtering issues][form].
* file issues about localization errors or send localization updates as PRs.
We're using [CrowdIn] to manage our translations and we generally update
them before each Beta and Release build. You can learn more about
translating AdGuard products [in our Knowledge Base][kb-trans].
* file issues about a particular filtering-rule list misbehaving. These are
tracked through the [separate form for filtering issues][form].
* send updates to filtering-rule lists, such as the ones for the Blocked
Services feature or the list of approved filtering-rule lists. We update
them once before each Beta and Release build.
- send or request updates to filtering-rule lists, such as the ones for the Blocked Services feature or the list of approved filtering-rule lists. We update them from the [separate repository][hostlist] once before each Beta and Release build.
Please do:
* follow the template instructions and provide data for reproducing issues.
- follow the template instructions and provide data for reproducing issues.
* write the title of your issue or pull request in English. Any language is
fine in the body, but it is important to keep the title in English to make
it easier for people and bots to look up duplicated issues.
- write the title of your issue or pull request in English. Any language is fine in the body, but it is important to keep the title in English to make it easier for people and bots to look up duplicated issues.
[CrowdIn]: https://crowdin.com/project/adguard-applications/en#/adguard-home
[form]: https://link.adtidy.org/forward.html?action=report&app=home&from=github
[hostlist]: https://github.com/AdguardTeam/HostlistsRegistry
[kb-trans]: https://kb.adguard.com/en/general/adguard-translations
## Issues
### Search first
## Issues
### Search first
Please make sure that the issue is not a duplicate or a question. If it's a
duplicate, please react to the original issue with a thumbs up. If it's a
question, please look through our [Wiki] and, if you haven't found the answer,
post it to the GitHub [Discussions] page.
Please make sure that the issue is not a duplicate or a question. If its a duplicate, please react to the original issue with a thumbs up. If its a question, please look through our [Wiki] and, if you havent found the answer, post it to the GitHub [Discussions] page.
[Discussions]: https://github.com/AdguardTeam/AdGuardHome/discussions/categories/q-a
[Wiki]: https://github.com/AdguardTeam/AdGuardHome/wiki
### Follow the issue template
Developers need to be able to reproduce the faulty behavior in order to fix an issue, so please make sure that you follow the instructions in the issue template carefully.
### Follow the issue template
## Pull requests
Developers need to be able to reproduce the faulty behavior in order to fix an
issue, so please make sure that you follow the instructions in the issue
template carefully.
### Discuss your changes first
Please discuss your changes by opening an issue. The maintainers should evaluate your proposal, and its generally better if thats done before any code is written.
### Review your changes for style
## Pull requests
### Discuss your changes first
Please discuss your changes by opening an issue. The maintainers should
evaluate your proposal, and it's generally better if that's done before any code
is written.
### Review your changes for style
We have a set of [code guidelines][hacking] that we expect the code to follow.
Please make sure you follow it.
We have a set of [code guidelines][hacking] that we expect the code to follow. Please make sure you follow it.
[hacking]: https://github.com/AdguardTeam/CodeGuidelines/blob/master/Go/Go.md
### Test your changes
Make sure that it passes linters and tests by running the corresponding Make targets. For backend changes, its `make go-check`. For frontend, run `make js-lint`.
### Test your changes
Make sure that it passes linters and tests by running the corresponding Make
targets. For backend changes, it's `make go-check`. For frontend, run
`make js-lint`.
Additionally, a manual test is often required. While we're constantly working
on improving our test suites, they're still not as good as we'd like them to be.
Additionally, a manual test is often required. While were constantly working on improving our test suites, theyre still not as good as wed like them to be.

View File

@@ -1,65 +1,56 @@
# AdGuard Home Developer Guidelines
# AdGuard Home developer guidelines
This document was moved to the [AdGuard Code Guidelines repository][repo]. All
sections with IDs now only have links to the corresponding files and sections in
that repository.
This document was moved to the [AdGuard Code Guidelines repository][repo]. All sections with IDs now only have links to the corresponding files and sections in that repository.
## <a href="#git" id="git" name="git">Git</a>
## <a href="#git" id="git" name="git">Git</a>
This section was moved to [its own document][git].
## <a href="#go" id="go" name="go">Go</a>
## <a href="#go" id="go" name="go">Go</a>
This section was moved to [its own document][go].
### <a href="#code" id="code" name="code">Code</a>
### <a href="#code" id="code" name="code">Code</a>
This subsection was moved to the [corresponding section][code] of the Go
guidelines document.
This subsection was moved to the [corresponding section][code] of the Go guidelines document.
### <a href="#commenting" id="commenting" name="commenting">Commenting</a>
### <a href="#commenting" id="commenting" name="commenting">Commenting</a>
This subsection was moved to the [corresponding section][cmnt] of the Go
guidelines document.
This subsection was moved to the [corresponding section][cmnt] of the Go guidelines document.
### <a href="#formatting" id="formatting" name="formatting">Formatting</a>
### <a href="#formatting" id="formatting" name="formatting">Formatting</a>
This subsection was moved to the [corresponding section][fmt] of the Go
guidelines document.
This subsection was moved to the [corresponding section][fmt] of the Go guidelines document.
### <a href="#naming" id="naming" name="naming">Naming</a>
### <a href="#naming" id="naming" name="naming">Naming</a>
This subsection was moved to the [corresponding section][name] of the Go
guidelines document.
This subsection was moved to the [corresponding section][name] of the Go guidelines document.
### <a href="#testing" id="testing" name="testing">Testing</a>
### <a href="#testing" id="testing" name="testing">Testing</a>
This subsection was moved to the [corresponding section][test] of the Go
guidelines document.
This subsection was moved to the [corresponding section][test] of the Go guidelines document.
### <a href="#recommended-reading" id="recommended-reading" name="recommended-reading">Recommended Reading</a>
### <a href="#recommended-reading" id="recommended-reading" name="recommended-reading">Recommended Reading</a>
This subsection was moved to the [corresponding section][read] of the Go
guidelines document.
This subsection was moved to the [corresponding section][read] of the Go guidelines document.
## <a href="#markdown" id="markdown" name="markdown">Markdown</a>
## <a href="#markdown" id="markdown" name="markdown">Markdown</a>
This section was moved to [its own document][md].
## <a href="#shell-scripting" id="shell-scripting" name="shell-scripting">Shell Scripting</a>
## <a href="#shell-scripting" id="shell-scripting" name="shell-scripting">Shell Scripting</a>
This section was moved to [its own document][sh].
### <a href="#shell-conditionals" id="shell-conditionals" name="shell-conditionals">Shell Conditionals</a>
### <a href="#shell-conditionals" id="shell-conditionals" name="shell-conditionals">Shell Conditionals</a>
This subsection was moved to the [corresponding section][cond] of the Shell
guidelines document.
This subsection was moved to the [corresponding section][cond] of the Shell guidelines document.
## <a href="#text-including-comments" id="text-including-comments" name="text-including-comments">Text, Including Comments</a>
## <a href="#text-including-comments" id="text-including-comments" name="text-including-comments">Text, Including Comments</a>
This section was moved to [its own document][txt].
## <a href="#yaml" id="yaml" name="yaml">YAML</a>
## <a href="#yaml" id="yaml" name="yaml">YAML</a>
This section was moved to [its own document][yaml].

View File

@@ -27,7 +27,7 @@ DIST_DIR = dist
GOAMD64 = v1
GOPROXY = https://goproxy.cn|https://proxy.golang.org|direct
GOSUMDB = sum.golang.google.cn
GOTOOLCHAIN = go1.21.7
GOTOOLCHAIN = go1.22.3
GPG_KEY = devteam@adguard.com
GPG_KEY_PASSPHRASE = not-a-real-password
NPM = npm
@@ -82,8 +82,6 @@ build: deps quick-build
quick-build: js-build go-build
ci: deps test go-bench go-fuzz
deps: js-deps go-deps
lint: js-lint go-lint
test: js-test go-test
@@ -98,15 +96,10 @@ build-release: $(BUILD_RELEASE_DEPS_$(FRONTEND_PREBUILT))
clean: ; $(ENV) "$(SHELL)" ./scripts/make/clean.sh
init: ; git config core.hooksPath ./scripts/hooks
js-build:
$(NPM) $(NPM_FLAGS) run build-prod
js-deps:
$(NPM) $(NPM_INSTALL_FLAGS) ci
# TODO(a.garipov): Remove the legacy client tasks support once the new
# client is done and the old one is removed.
js-lint: ; $(NPM) $(NPM_FLAGS) run lint
js-test: ; $(NPM) $(NPM_FLAGS) run test
js-build: ; $(NPM) $(NPM_FLAGS) run build-prod
js-deps: ; $(NPM) $(NPM_INSTALL_FLAGS) ci
js-lint: ; $(NPM) $(NPM_FLAGS) run lint
js-test: ; $(NPM) $(NPM_FLAGS) run test
go-bench: ; $(ENV) "$(SHELL)" ./scripts/make/go-bench.sh
go-build: ; $(ENV) "$(SHELL)" ./scripts/make/go-build.sh

474
README.md
View File

@@ -1,85 +1,75 @@
&nbsp;
<p align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="doc/adguard_home_darkmode.svg">
<img alt="AdGuard Home" src="doc/adguard_home_lightmode.svg" width="300px">
</picture>
<picture>
<source media="(prefers-color-scheme: dark)" srcset="doc/adguard_home_darkmode.svg">
<img alt="AdGuard Home" src="doc/adguard_home_lightmode.svg" width="300px">
</picture>
</p>
<h3 align="center">Privacy protection center for you and your devices</h3>
<p align="center">
Free and open source, powerful network-wide ads & trackers blocking DNS
server.
Free and open source, powerful network-wide ads & trackers blocking DNS server.
</p>
<p align="center">
<a href="https://adguard.com/">AdGuard.com</a> |
<a href="https://github.com/AdguardTeam/AdGuardHome/wiki">Wiki</a> |
<a href="https://reddit.com/r/Adguard">Reddit</a> |
<a href="https://twitter.com/AdGuard">Twitter</a> |
<a href="https://t.me/adguard_en">Telegram</a>
<br/><br/>
<a href="https://codecov.io/github/AdguardTeam/AdGuardHome?branch=master">
<img src="https://img.shields.io/codecov/c/github/AdguardTeam/AdGuardHome/master.svg" alt="Code Coverage"/>
</a>
<a href="https://goreportcard.com/report/AdguardTeam/AdGuardHome">
<img src="https://goreportcard.com/badge/github.com/AdguardTeam/AdGuardHome" alt="Go Report Card"/>
</a>
<a href="https://hub.docker.com/r/adguard/adguardhome">
<img alt="Docker Pulls" src="https://img.shields.io/docker/pulls/adguard/adguardhome.svg?maxAge=604800"/>
</a>
<br/>
<a href="https://github.com/AdguardTeam/AdGuardHome/releases">
<img src="https://img.shields.io/github/release/AdguardTeam/AdGuardHome/all.svg" alt="Latest release"/>
</a>
<a href="https://snapcraft.io/adguard-home">
<img alt="adguard-home" src="https://snapcraft.io/adguard-home/badge.svg"/>
</a>
<a href="https://adguard.com/">AdGuard.com</a> |
<a href="https://github.com/AdguardTeam/AdGuardHome/wiki">Wiki</a> |
<a href="https://reddit.com/r/Adguard">Reddit</a> |
<a href="https://twitter.com/AdGuard">Twitter</a> |
<a href="https://t.me/adguard_en">Telegram</a>
<br/><br/>
<a href="https://codecov.io/github/AdguardTeam/AdGuardHome?branch=master">
<img src="https://img.shields.io/codecov/c/github/AdguardTeam/AdGuardHome/master.svg" alt="Code Coverage"/>
</a>
<a href="https://goreportcard.com/report/AdguardTeam/AdGuardHome">
<img src="https://goreportcard.com/badge/github.com/AdguardTeam/AdGuardHome" alt="Go Report Card"/>
</a>
<a href="https://hub.docker.com/r/adguard/adguardhome">
<img alt="Docker Pulls" src="https://img.shields.io/docker/pulls/adguard/adguardhome.svg?maxAge=604800"/>
</a>
<br/>
<a href="https://github.com/AdguardTeam/AdGuardHome/releases">
<img src="https://img.shields.io/github/release/AdguardTeam/AdGuardHome/all.svg" alt="Latest release"/>
</a>
<a href="https://snapcraft.io/adguard-home">
<img alt="adguard-home" src="https://snapcraft.io/adguard-home/badge.svg"/>
</a>
</p>
<br/>
<p align="center">
<img src="https://cdn.adtidy.org/public/Adguard/Common/adguard_home.gif" width="800"/>
<img src="https://cdn.adtidy.org/public/Adguard/Common/adguard_home.gif" width="800"/>
</p>
<hr/>
AdGuard Home is a network-wide software for blocking ads and tracking. After you
set it up, it'll cover ALL your home devices, and you don't need any client-side
software for that.
AdGuard Home is a network-wide software for blocking ads and tracking. After you set it up, it'll cover ALL your home devices, and you don't need any client-side software for that.
It operates as a DNS server that re-routes tracking domains to a “black hole”,
thus preventing your devices from connecting to those servers. It's based on
software we use for our public [AdGuard DNS] servers, and both share a lot of
code.
It operates as a DNS server that re-routes tracking domains to a “black hole”, thus preventing your devices from connecting to those servers. It's based on software we use for our public [AdGuard DNS] servers, and both share a lot of code.
[AdGuard DNS]: https://adguard-dns.io/
- [Getting Started](#getting-started)
- [Automated install (Linux/Unix/MacOS/FreeBSD/OpenBSD)](#automated-install-linux-and-mac)
- [Alternative methods](#alternative-methods)
- [Guides](#guides)
- [API](#api)
- [Comparing AdGuard Home to other solutions](#comparison)
- [How is this different from public AdGuard DNS servers?](#comparison-adguard-dns)
- [How does AdGuard Home compare to Pi-Hole](#comparison-pi-hole)
- [How does AdGuard Home compare to traditional ad blockers](#comparison-adblock)
- [Known limitations](#comparison-limitations)
- [How to build from source](#how-to-build)
- [Prerequisites](#prerequisites)
- [Building](#building)
- [Contributing](#contributing)
- [Test unstable versions](#test-unstable-versions)
- [Reporting issues](#reporting-issues)
- [Help with translations](#translate)
- [Other](#help-other)
- [Projects that use AdGuard Home](#uses)
- [Acknowledgments](#acknowledgments)
- [Privacy](#privacy)
## <a href="#getting-started" id="getting-started" name="getting-started">Getting Started</a>
* [Getting Started](#getting-started)
* [Automated install (Linux/Unix/MacOS/FreeBSD/OpenBSD)](#automated-install-linux-and-mac)
* [Alternative methods](#alternative-methods)
* [Guides](#guides)
* [API](#api)
* [Comparing AdGuard Home to other solutions](#comparison)
* [How is this different from public AdGuard DNS servers?](#comparison-adguard-dns)
* [How does AdGuard Home compare to Pi-Hole](#comparison-pi-hole)
* [How does AdGuard Home compare to traditional ad blockers](#comparison-adblock)
* [Known limitations](#comparison-limitations)
* [How to build from source](#how-to-build)
* [Prerequisites](#prerequisites)
* [Building](#building)
* [Contributing](#contributing)
* [Test unstable versions](#test-unstable-versions)
* [Reporting issues](#reporting-issues)
* [Help with translations](#translate)
* [Other](#help-other)
* [Projects that use AdGuard Home](#uses)
* [Acknowledgments](#acknowledgments)
* [Privacy](#privacy)
## <a href="#getting-started" id="getting-started" name="getting-started">Getting Started</a>
### <a href="#automated-install-linux-and-mac" id="automated-install-linux-and-mac" name="automated-install-linux-and-mac">Automated install (Linux/Unix/MacOS/FreeBSD/OpenBSD)</a>
### <a href="#automated-install-linux-and-mac" id="automated-install-linux-and-mac" name="automated-install-linux-and-mac">Automated install (Linux/Unix/MacOS/FreeBSD/OpenBSD)</a>
To install with `curl` run the following command:
@@ -101,95 +91,70 @@ fetch -o - https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/master/scri
The script also accepts some options:
* `-c <channel>` to use specified channel;
* `-r` to reinstall AdGuard Home;
* `-u` to uninstall AdGuard Home;
* `-v` for verbose output.
- `-c <channel>` to use specified channel;
- `-r` to reinstall AdGuard Home;
- `-u` to uninstall AdGuard Home;
- `-v` for verbose output.
Note that options `-r` and `-u` are mutually exclusive.
### <a href="#alternative-methods" id="alternative-methods" name="alternative-methods">Alternative methods</a>
#### <a href="#manual-installation" id="manual-installation" name="manual-installation">Manual installation</a>
### <a href="#alternative-methods" id="alternative-methods" name="alternative-methods">Alternative methods</a>
Please read the **[Getting Started][wiki-start]** article on our Wiki to learn how to install AdGuard Home manually, and how to configure your devices to use it.
#### <a href="#manual-installation" id="manual-installation" name="manual-installation">Manual installation</a>
Please read the **[Getting Started][wiki-start]** article on our Wiki to learn
how to install AdGuard Home manually, and how to configure your devices to use
it.
#### <a href="#docker" id="docker" name="docker">Docker</a>
#### <a href="#docker" id="docker" name="docker">Docker</a>
You can use our official Docker image on [Docker Hub].
#### <a href="#snap-store" id="snap-store" name="snap-store">Snap Store</a>
#### <a href="#snap-store" id="snap-store" name="snap-store">Snap Store</a>
If you're running **Linux,** there's a secure and easy way to install AdGuard
Home: get it from the [Snap Store].
If you're running **Linux,** there's a secure and easy way to install AdGuard Home: get it from the [Snap Store].
[Docker Hub]: https://hub.docker.com/r/adguard/adguardhome
[Snap Store]: https://snapcraft.io/adguard-home
[wiki-start]: https://github.com/AdguardTeam/AdGuardHome/wiki/Getting-Started
### <a href="#guides" id="guides" name="guides">Guides</a>
### <a href="#guides" id="guides" name="guides">Guides</a>
See our [Wiki][wiki].
[wiki]: https://github.com/AdguardTeam/AdGuardHome/wiki
### <a href="#api" id="api" name="api">API</a>
### <a href="#api" id="api" name="api">API</a>
If you want to integrate with AdGuard Home, you can use our [REST API][openapi].
Alternatively, you can use this [python client][pyclient], which is used to
build the [AdGuard Home Hass.io Add-on][hassio].
If you want to integrate with AdGuard Home, you can use our [REST API][openapi]. Alternatively, you can use this [python client][pyclient], which is used to build the [AdGuard Home Hass.io Add-on][hassio].
[hassio]: https://www.home-assistant.io/integrations/adguard/
[openapi]: https://github.com/AdguardTeam/AdGuardHome/tree/master/openapi
[pyclient]: https://pypi.org/project/adguardhome/
## <a href="#comparison" id="comparison" name="comparison">Comparing AdGuard Home to other solutions</a>
### <a href="#comparison-adguard-dns" id="comparison-adguard-dns" name="comparison-adguard-dns">How is this different from public AdGuard DNS servers?</a>
## <a href="#comparison" id="comparison" name="comparison">Comparing AdGuard Home to other solutions</a>
Running your own AdGuard Home server allows you to do much more than using a public DNS server. It's a completely different level. See for yourself:
### <a href="#comparison-adguard-dns" id="comparison-adguard-dns" name="comparison-adguard-dns">How is this different from public AdGuard DNS servers?</a>
- Choose what exactly the server blocks and permits.
Running your own AdGuard Home server allows you to do much more than using a
public DNS server. It's a completely different level. See for yourself:
- Monitor your network activity.
* Choose what exactly the server blocks and permits.
- Add your own custom filtering rules.
* Monitor your network activity.
- **Most importantly, it's your own server, and you are the only one who's in control.**
* Add your own custom filtering rules.
### <a href="#comparison-pi-hole" id="comparison-pi-hole" name="comparison-pi-hole">How does AdGuard Home compare to Pi-Hole</a>
* **Most importantly, it's your own server, and you are the only one who's in
control.**
At this point, AdGuard Home has a lot in common with Pi-Hole. Both block ads and trackers using the so-called “DNS sinkholing” method and both allow customizing what's blocked.
> [!NOTE]
> We're not going to stop here. DNS sinkholing is not a bad starting point, but this is just the beginning.
AdGuard Home provides a lot of features out-of-the-box with no need to install and configure additional software. We want it to be simple to the point when even casual users can set it up with minimal effort.
### <a href="#comparison-pi-hole" id="comparison-pi-hole" name="comparison-pi-hole">How does AdGuard Home compare to Pi-Hole</a>
At this point, AdGuard Home has a lot in common with Pi-Hole. Both block ads
and trackers using the so-called “DNS sinkholing” method and both allow
customizing what's blocked.
<aside>
We're not going to stop here. DNS sinkholing is not a bad starting point, but
this is just the beginning.
</aside>
AdGuard Home provides a lot of features out-of-the-box with no need to install
and configure additional software. We want it to be simple to the point when
even casual users can set it up with minimal effort.
**Disclaimer:** some of the listed features can be added to Pi-Hole by
installing additional software or by manually using SSH terminal and
reconfiguring one of the utilities Pi-Hole consists of. However, in our
opinion, this cannot be legitimately counted as a Pi-Hole's feature.
> [!NOTE]
> Some of the listed features can be added to Pi-Hole by installing additional software or by manually using SSH terminal and reconfiguring one of the utilities Pi-Hole consists of. However, in our opinion, this cannot be legitimately counted as a Pi-Hole's feature.
| Feature | AdGuard&nbsp;Home | Pi-Hole |
|-------------------------------------------------------------------------|-------------------|-----------------------------------------------------------|
@@ -207,68 +172,45 @@ opinion, this cannot be legitimately counted as a Pi-Hole's feature.
| Access settings (choose who can use AGH DNS) | ✅ | ❌ |
| Running [without root privileges][wiki-noroot] | ✅ | ❌ |
[wiki-noroot]: https://github.com/AdguardTeam/AdGuardHome/wiki/Getting-Started#running-without-superuser
[wiki-noroot]: https://adguard-dns.io/kb/adguard-home/getting-started/#running-without-superuser
### <a href="#comparison-adblock" id="comparison-adblock" name="comparison-adblock">How does AdGuard Home compare to traditional ad blockers</a>
### <a href="#comparison-adblock" id="comparison-adblock" name="comparison-adblock">How does AdGuard Home compare to traditional ad blockers</a>
It depends.
DNS sinkholing is capable of blocking a big percentage of ads, but it lacks
the flexibility and the power of traditional ad blockers. You can get a good
impression about the difference between these methods by reading [this
article][blog-adaway], which compares AdGuard for Android (a traditional ad
blocker) to hosts-level ad blockers (which are almost identical to DNS-based
blockers in their capabilities). This level of protection is enough for some
users.
DNS sinkholing is capable of blocking a big percentage of ads, but it lacks the flexibility and the power of traditional ad blockers. You can get a good impression about the difference between these methods by reading [this article][blog-adaway], which compares AdGuard for Android (a traditional ad blocker) to hosts-level ad blockers (which are almost identical to DNS-based blockers in their capabilities). This level of protection is enough for some users.
Additionally, using a DNS-based blocker can help to block ads, tracking and
analytics requests on other types of devices, such as SmartTVs, smart speakers
or other kinds of IoT devices (on which you can't install traditional ad
blockers).
Additionally, using a DNS-based blocker can help to block ads, tracking and analytics requests on other types of devices, such as SmartTVs, smart speakers or other kinds of IoT devices (on which you can't install traditional ad blockers).
### <a href="#comparison-limitations" id="comparison-limitations" name="comparison-limitations">Known limitations</a>
### <a href="#comparison-limitations" id="comparison-limitations" name="comparison-limitations">Known limitations</a>
Here are some examples of what cannot be blocked by a DNS-level blocker:
* YouTube, Twitch ads;
- YouTube, Twitch ads;
* Facebook, Twitter, Instagram sponsored posts.
- Facebook, Twitter, Instagram sponsored posts.
Essentially, any advertising that shares a domain with content cannot be blocked
by a DNS-level blocker.
Essentially, any advertising that shares a domain with content cannot be blocked by a DNS-level blocker.
Is there a chance to handle this in the future? DNS will never be enough to do
this. Our only option is to use a content blocking proxy like what we do in the
standalone AdGuard applications. We're [going to bring][issue-1228] this
feature support to AdGuard Home in the future. Unfortunately, even in this
case, there still will be cases when this won't be enough or would require quite
a complicated configuration.
Is there a chance to handle this in the future? DNS will never be enough to do this. Our only option is to use a content blocking proxy like what we do in the standalone AdGuard applications. We're [going to bring][issue-1228] this feature support to AdGuard Home in the future. Unfortunately, even in this case, there still will be cases when this won't be enough or would require quite a complicated configuration.
[blog-adaway]: https://adguard.com/blog/adguard-vs-adaway-dns66.html
[issue-1228]: https://github.com/AdguardTeam/AdGuardHome/issues/1228
## <a href="#how-to-build" id="how-to-build" name="how-to-build">How to build from source</a>
## <a href="#how-to-build" id="how-to-build" name="how-to-build">How to build from source</a>
### <a href="#prerequisites" id="prerequisites" name="prerequisites">Prerequisites</a>
### <a href="#prerequisites" id="prerequisites" name="prerequisites">Prerequisites</a>
Run `make init` to prepare the development environment.
You will need this to build AdGuard Home:
* [Go](https://golang.org/dl/) v1.20 or later;
* [Node.js](https://nodejs.org/en/download/) v16 or later;
* [npm](https://www.npmjs.com/) v8 or later;
* [yarn](https://yarnpkg.com/) v1.22.5 or later.
- [Go](https://golang.org/dl/) v1.22 or later;
- [Node.js](https://nodejs.org/en/download/) v16 or later;
- [npm](https://www.npmjs.com/) v8 or later;
- [yarn](https://yarnpkg.com/) v1.22.5 or later.
### <a href="#building" id="building" name="building">Building</a>
### <a href="#building" id="building" name="building">Building</a>
Open your terminal and execute these commands:
@@ -278,27 +220,22 @@ cd AdGuardHome
make
```
#### <a href="#building-node" id="building-node" name="building-node">Building with Node.js 17 and later</a>
#### <a href="#building-node" id="building-node" name="building-node">Building with Node.js 17 and later</a>
In order to build AdGuard Home with Node.js 17 and later, specify
`--openssl-legacy-provider` option.
In order to build AdGuard Home with Node.js 17 and later, specify `--openssl-legacy-provider` option.
```sh
export NODE_OPTIONS=--openssl-legacy-provider
```
**NOTE:** The non-standard `-j` flag is currently not supported, so building
with `make -j 4` or setting your `MAKEFLAGS` to include, for example, `-j 4` is
likely to break the build. If you do have your `MAKEFLAGS` set to that, and you
don't want to change it, you can override it by running `make -j 1`.
> [!WARNING]
> The non-standard `-j` flag is currently not supported, so building with `make -j 4` or setting your `MAKEFLAGS` to include, for example, `-j 4` is likely to break the build. If you do have your `MAKEFLAGS` set to that, and you don't want to change it, you can override it by running `make -j 1`.
Check the [`Makefile`][src-makefile] to learn about other commands.
#### <a href="#building-cross" id="building-cross" name="building-cross">Building for a different platform</a>
#### <a href="#building-cross" id="building-cross" name="building-cross">Building for a different platform</a>
You can build AdGuard Home for any OS/ARCH that Go supports. In order to do
this, specify `GOOS` and `GOARCH` environment variables as macros when running
`make`.
You can build AdGuard Home for any OS/ARCH that Go supports. In order to do this, specify `GOOS` and `GOARCH` environment variables as macros when running `make`.
For example:
@@ -312,10 +249,9 @@ or:
make GOOS='linux' GOARCH='arm64'
```
#### <a href="#preparing-releases" id="preparing-releases" name="preparing-releases">Preparing releases</a>
#### <a href="#preparing-releases" id="preparing-releases" name="preparing-releases">Preparing releases</a>
You'll need [`snapcraft`] to prepare a release build. Once installed, run the
following command:
You'll need [`snapcraft`] to prepare a release build. Once installed, run the following command:
```sh
make build-release CHANNEL='...' VERSION='...'
@@ -323,47 +259,39 @@ make build-release CHANNEL='...' VERSION='...'
See the [`build-release` target documentation][targ-release].
#### <a href="#docker-image" id="docker-image" name="docker-image">Docker image</a>
#### <a href="#docker-image" id="docker-image" name="docker-image">Docker image</a>
Run `make build-docker` to build the Docker image locally (the one that we
publish to DockerHub). Please note, that we're using [Docker Buildx][buildx] to
build our official image.
Run `make build-docker` to build the Docker image locally (the one that we publish to DockerHub). Please note, that we're using [Docker Buildx][buildx] to build our official image.
You may need to prepare before using these builds:
* (Linux-only) Install Qemu:
- (Linux-only) Install Qemu:
```sh
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes --credential yes
```
```sh
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes --credential yes
```
* Prepare the builder:
- Prepare the builder:
```sh
docker buildx create --name buildx-builder --driver docker-container --use
```
```sh
docker buildx create --name buildx-builder --driver docker-container --use
```
See the [`build-docker` target documentation][targ-docker].
#### <a href="#debugging-the-frontend" id="debugging-the-frontend" name="debugging-the-frontend">Debugging the frontend</a>
#### <a href="#debugging-the-frontend" id="debugging-the-frontend" name="debugging-the-frontend">Debugging the frontend</a>
When you need to debug the frontend without recompiling the production version
every time, for example to check how your labels would look on a form, you can
run the frontend build a development environment.
When you need to debug the frontend without recompiling the production version every time, for example to check how your labels would look on a form, you can run the frontend build a development environment.
1. In a separate terminal, run:
1. In a separate terminal, run:
```sh
( cd ./client/ && env NODE_ENV='development' npm run watch )
```
```sh
( cd ./client/ && env NODE_ENV='development' npm run watch )
```
2. Run your `AdGuardHome` binary with the `--local-frontend` flag, which
instructs AdGuard Home to ignore the built-in frontend files and use those
from the `./build/` directory.
2. Run your `AdGuardHome` binary with the `--local-frontend` flag, which instructs AdGuard Home to ignore the built-in frontend files and use those from the `./build/` directory.
3. Now any changes you make in the `./client/` directory should be recompiled
and become available on the web UI. Make sure that you disable the browser
cache to make sure that you actually get the recompiled version.
3. Now any changes you make in the `./client/` directory should be recompiled and become available on the web UI. Make sure that you disable the browser cache to make sure that you actually get the recompiled version.
[`snapcraft`]: https://snapcraft.io/
[buildx]: https://docs.docker.com/buildx/working-with-buildx/
@@ -371,170 +299,120 @@ run the frontend build a development environment.
[targ-docker]: https://github.com/AdguardTeam/AdGuardHome/tree/master/scripts#build-dockersh-build-a-multi-architecture-docker-image
[targ-release]: https://github.com/AdguardTeam/AdGuardHome/tree/master/scripts#build-releasesh-build-a-release-for-all-platforms
## <a href="#contributing" id="contributing" name="contributing">Contributing</a>
You are welcome to fork this repository, make your changes and [submit a pull
request][pr]. Please make sure you follow our [code guidelines][guide] though.
You are welcome to fork this repository, make your changes and [submit a pull request][pr]. Please make sure you follow our [code guidelines][guide] though.
Please note that we don't expect people to contribute to both UI and backend
parts of the program simultaneously. Ideally, the backend part is implemented
first, i.e. configuration, API, and the functionality itself. The UI part can
be implemented later in a different pull request by a different person.
Please note that we don't expect people to contribute to both UI and backend parts of the program simultaneously. Ideally, the backend part is implemented first, i.e. configuration, API, and the functionality itself. The UI part can be implemented later in a different pull request by a different person.
[guide]: https://github.com/AdguardTeam/CodeGuidelines/
[pr]: https://github.com/AdguardTeam/AdGuardHome/pulls
### <a href="#test-unstable-versions" id="test-unstable-versions" name="test-unstable-versions">Test unstable versions</a>
### <a href="#test-unstable-versions" id="test-unstable-versions" name="test-unstable-versions">Test unstable versions</a>
There are two update channels that you can use:
* `beta`: beta versions of AdGuard Home. More or less stable versions,
usually released every two weeks or more often.
- `beta`: beta versions of AdGuard Home. More or less stable versions, usually released every two weeks or more often.
* `edge`: the newest version of AdGuard Home from the development branch. New
updates are pushed to this channel daily.
- `edge`: the newest version of AdGuard Home from the development branch. New updates are pushed to this channel daily.
There are three options how you can install an unstable version:
1. [Snap Store]: look for the `beta` and `edge` channels.
1. [Snap Store]: look for the `beta` and `edge` channels.
2. [Docker Hub]: look for the `beta` and `edge` tags.
2. [Docker Hub]: look for the `beta` and `edge` tags.
3. Standalone builds. Use the automated installation script or look for the
available builds [on the Wiki][wiki-platf].
3. Standalone builds. Use the automated installation script or look for the available builds [on the Wiki][wiki-platf].
Script to install a beta version:
Script to install a beta version:
```sh
curl -s -S -L https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/master/scripts/install.sh | sh -s -- -c beta
```
```sh
curl -s -S -L https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/master/scripts/install.sh | sh -s -- -c beta
```
Script to install an edge version:
Script to install an edge version:
```sh
curl -s -S -L https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/master/scripts/install.sh | sh -s -- -c edge
```
```sh
curl -s -S -L https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/master/scripts/install.sh | sh -s -- -c edge
```
[wiki-platf]: https://github.com/AdguardTeam/AdGuardHome/wiki/Platforms
### <a href="#reporting-issues" id="reporting-issues" name="reporting-issues">Report issues</a>
### <a href="#reporting-issues" id="reporting-issues" name="reporting-issues">Report issues</a>
If you run into any problem or have a suggestion, head to [this page][iss] and
click on the “New issue” button. Please follow the instructions in the issue
form carefully and don't forget to start by searching for duplicates.
If you run into any problem or have a suggestion, head to [this page][iss] and click on the “New issue” button. Please follow the instructions in the issue form carefully and don't forget to start by searching for duplicates.
[iss]: https://github.com/AdguardTeam/AdGuardHome/issues
### <a href="#translate" id="translate" name="translate">Help with translations</a>
### <a href="#translate" id="translate" name="translate">Help with translations</a>
If you want to help with AdGuard Home translations, please learn more about
translating AdGuard products [in our Knowledge Base][kb-trans]. You can
contribute to the [AdGuardHome project on CrowdIn][crowdin].
If you want to help with AdGuard Home translations, please learn more about translating AdGuard products [in our Knowledge Base][kb-trans]. You can contribute to the [AdGuardHome project on CrowdIn][crowdin].
[crowdin]: https://crowdin.com/project/adguard-applications/en#/adguard-home
[kb-trans]: https://kb.adguard.com/en/general/adguard-translations
### <a href="#help-other" id="help-other" name="help-other">Other</a>
### <a href="#help-other" id="help-other" name="help-other">Other</a>
Another way you can contribute is by [looking for issues][iss-help] marked as
`help wanted`, asking if the issue is up for grabs, and sending a PR fixing the
bug or implementing the feature.
Another way you can contribute is by [looking for issues][iss-help] marked as `help wanted`, asking if the issue is up for grabs, and sending a PR fixing the bug or implementing the feature.
[iss-help]: https://github.com/AdguardTeam/AdGuardHome/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22
## <a href="#uses" id="uses" name="uses">Projects that use AdGuard Home</a>
<!--
TODO(a.garipov): Use reference links.
-->
Please note that these projects are not affiliated with AdGuard, but are made by third-party developers and fans.
* [AdGuard Home Remote](https://apps.apple.com/app/apple-store/id1543143740):
iOS app by [Joost](https://rocketscience-it.nl/).
- [AdGuard Home Remote](https://apps.apple.com/app/apple-store/id1543143740): iOS app by [Joost](https://rocketscience-it.nl/).
* [Python library](https://github.com/frenck/python-adguardhome) by
[@frenck](https://github.com/frenck).
- [Python library](https://github.com/frenck/python-adguardhome) by [@frenck](https://github.com/frenck).
* [Home Assistant add-on](https://github.com/hassio-addons/addon-adguard-home)
by [@frenck](https://github.com/frenck).
- [Home Assistant add-on](https://github.com/hassio-addons/addon-adguard-home) by [@frenck](https://github.com/frenck).
* [OpenWrt LUCI app](https://github.com/kongfl888/luci-app-adguardhome) by
[@kongfl888](https://github.com/kongfl888) (originally by
[@rufengsuixing](https://github.com/rufengsuixing)).
- [OpenWrt LUCI app](https://github.com/kongfl888/luci-app-adguardhome) by [@kongfl888](https://github.com/kongfl888) (originally by [@rufengsuixing](https://github.com/rufengsuixing)).
* [AdGuardHome sync](https://github.com/bakito/adguardhome-sync) by
[@bakito](https://github.com/bakito).
- [AdGuardHome sync](https://github.com/bakito/adguardhome-sync) by [@bakito](https://github.com/bakito).
* [Terminal-based, real-time traffic monitoring and statistics for your AdGuard Home
instance](https://github.com/Lissy93/AdGuardian-Term) by
[@Lissy93](https://github.com/Lissy93)
- [Terminal-based, real-time traffic monitoring and statistics for your AdGuard Home instance](https://github.com/Lissy93/AdGuardian-Term) by [@Lissy93](https://github.com/Lissy93)
* [AdGuard Home on GLInet
routers](https://forum.gl-inet.com/t/adguardhome-on-gl-routers/10664) by
[Gl-Inet](https://gl-inet.com/).
- [AdGuard Home on GLInet routers](https://forum.gl-inet.com/t/adguardhome-on-gl-routers/10664) by [Gl-Inet](https://gl-inet.com/).
* [Cloudron app](https://git.cloudron.io/cloudron/adguard-home-app) by
[@gramakri](https://github.com/gramakri).
- [Cloudron app](https://git.cloudron.io/cloudron/adguard-home-app) by [@gramakri](https://github.com/gramakri).
* [Asuswrt-Merlin-AdGuardHome-Installer](https://github.com/jumpsmm7/Asuswrt-Merlin-AdGuardHome-Installer)
by [@jumpsmm7](https://github.com/jumpsmm7) aka
[@SomeWhereOverTheRainBow](https://www.snbforums.com/members/somewhereovertherainbow.64179/).
- [Asuswrt-Merlin-AdGuardHome-Installer](https://github.com/jumpsmm7/Asuswrt-Merlin-AdGuardHome-Installer) by [@jumpsmm7](https://github.com/jumpsmm7) aka [@SomeWhereOverTheRainBow](https://www.snbforums.com/members/somewhereovertherainbow.64179/).
* [Node.js library](https://github.com/Andrea055/AdguardHomeAPI) by
[@Andrea055](https://github.com/Andrea055/).
- [Node.js library](https://github.com/Andrea055/AdguardHomeAPI) by [@Andrea055](https://github.com/Andrea055/).
* [Browser Extension](https://github.com/satheshshiva/Adguard-Home-Browser-Ext) by
[@satheshshiva](https://github.com/satheshshiva/).
- [Browser Extension](https://github.com/satheshshiva/Adguard-Home-Browser-Ext) by [@satheshshiva](https://github.com/satheshshiva/).
- [Zabbix Template for AdGuard Home](https://github.com/diasdmhub/AdGuard_Home_Zabbix_Template) by [@diasdmhub](https://github.com/diasdmhub).
- [Chocolatey package](https://community.chocolatey.org/packages/adguardhome/) by [niks255](https://community.chocolatey.org/profiles/niks255).
## <a href="#acknowledgments" id="acknowledgments" name="acknowledgments">Acknowledgments</a>
<!--
TODO(a.garipov): Use reference links.
-->
This software wouldn't have been possible without:
* [Go](https://golang.org/dl/) and its libraries:
* [gcache](https://github.com/bluele/gcache)
* [miekg's dns](https://github.com/miekg/dns)
* [go-yaml](https://github.com/go-yaml/yaml)
* [service](https://godoc.org/github.com/kardianos/service)
* [dnsproxy](https://github.com/AdguardTeam/dnsproxy)
* [urlfilter](https://github.com/AdguardTeam/urlfilter)
* [Node.js](https://nodejs.org/) and its libraries:
* And many more Node.js packages.
* [React.js](https://reactjs.org)
* [Tabler](https://github.com/tabler/tabler)
* [whotracks.me data](https://github.com/cliqz-oss/whotracks.me)
- [Go](https://golang.org/dl/) and its libraries:
- [gcache](https://github.com/bluele/gcache)
- [miekg's dns](https://github.com/miekg/dns)
- [go-yaml](https://github.com/go-yaml/yaml)
- [service](https://godoc.org/github.com/kardianos/service)
- [dnsproxy](https://github.com/AdguardTeam/dnsproxy)
- [urlfilter](https://github.com/AdguardTeam/urlfilter)
- [Node.js](https://nodejs.org/) and its libraries:
- [React.js](https://reactjs.org)
- [Tabler](https://github.com/tabler/tabler)
- And many more Node.js packages.
- [whotracks.me data](https://github.com/cliqz-oss/whotracks.me)
You might have seen that [CoreDNS] was mentioned here before, but we've stopped
using it in AdGuard Home.
You might have seen that [CoreDNS] was mentioned here before, but we've stopped using it in AdGuard Home.
For the full list of all Node.js packages in use, please take a look at
[`client/package.json`][src-packagejson] file.
For the full list of all Node.js packages in use, please take a look at [`client/package.json`][src-packagejson] file.
[CoreDNS]: https://coredns.io
[src-packagejson]: https://github.com/AdguardTeam/AdGuardHome/blob/master/client/package.json
## <a href="#privacy" id="privacy" name="privacy">Privacy</a>
## <a href="#privacy" id="privacy" name="privacy">Privacy</a>
Our main idea is that you are the one, who should be in control of your data.
So it is only natural, that AdGuard Home does not collect any usage statistics,
and does not use any web services unless you configure it to do so. See also
the [full privacy policy][privacy] with every bit that *could in theory be sent*
by AdGuard Home is available.
Our main idea is that you are the one, who should be in control of your data. So it is only natural, that AdGuard Home does not collect any usage statistics, and does not use any web services unless you configure it to do so. See also the [full privacy policy][privacy] with every bit that *could in theory be sent* by AdGuard Home is available.
[privacy]: https://adguard.com/en/privacy/home.html

View File

@@ -1,18 +1,13 @@
# Security Policy
# Security Policy
## Reporting a Vulnerability
## Reporting vulnerabilities
Please send your vulnerability reports to <security@adguard.com>. To make sure
that your report reaches us, please:
Please send your vulnerability reports to <security@adguard.com>. To make sure that your report reaches us, please:
1. Include the words “AdGuard Home” and “vulnerability” to the subject line as
well as a short description of the vulnerability. For example:
1. Include the words “AdGuard Home” and “vulnerability” to the subject line as well as a short description of the vulnerability. For example:
> AdGuard Home API vulnerability: possible XSS attack
> AdGuard Home API vulnerability: possible XSS attack
2. Make sure that the message body contains a clear description of the
vulnerability.
1. Make sure that the message body contains a clear description of the vulnerability.
If you have not received a reply to your email within 7 days, please make sure
to follow up with us again at <security@adguard.com>. Once again, make sure
that the word “vulnerability” is in the subject line.
If you have not received a reply to your email within 7 days, please make sure to follow up with us again at <security@adguard.com>. Once again, make sure that the word “vulnerability” is in the subject line.

View File

@@ -7,7 +7,8 @@
# Make sure to sync any changes with the branch overrides below.
'variables':
'channel': 'edge'
'dockerGo': 'adguard/golang-ubuntu:8.0'
'dockerFrontend': 'adguard/home-js-builder:1.1'
'dockerGo': 'adguard/go-builder:1.22.3--1'
'stages':
- 'Build frontend':
@@ -41,8 +42,13 @@
- 'Publish to GitHub Releases'
'Build frontend':
'artifacts':
- 'name': 'AdGuardHome frontend'
'pattern': 'build/**'
'shared': true
'required': true
'docker':
'image': '${bamboo.dockerGo}'
'image': '${bamboo.dockerFrontend}'
'volumes':
'${system.YARN_DIR}': '${bamboo.cacheYarn}'
'key': 'BF'
@@ -59,19 +65,21 @@
set -e -f -u -x
# Explicitly checkout the revision that we need.
git checkout "${bamboo.repository.revision.number}"
make js-deps js-build
'artifacts':
- 'name': 'AdGuardHome frontend'
'pattern': 'build/**'
'shared': true
'required': true
make\
VERBOSE=1\
js-deps js-build
'requirements':
- 'adg-docker': 'true'
'Make release':
'artifact-subscriptions':
- 'artifact': 'AdGuardHome frontend'
# TODO(a.garipov): Use more fine-grained artifact rules.
'artifacts':
- 'name': 'AdGuardHome dists'
'pattern': 'dist/**'
'shared': true
'required': true
'docker':
'image': '${bamboo.dockerGo}'
'volumes':
@@ -91,9 +99,6 @@
set -e -f -u -x
# Explicitly checkout the revision that we need.
git checkout "${bamboo.repository.revision.number}"
# Run the build with the specified channel.
echo "${bamboo.gpgSecretKeyPart1}${bamboo.gpgSecretKeyPart2}"\
| awk '{ gsub(/\\n/, "\n"); print; }'\
@@ -106,12 +111,6 @@
PARALLELISM=1\
VERBOSE=2\
build-release
# TODO(a.garipov): Use more fine-grained artifact rules.
'artifacts':
- 'name': 'AdGuardHome dists'
'pattern': 'dist/**'
'shared': true
'required': true
'requirements':
- 'adg-docker': 'true'
@@ -130,13 +129,6 @@
set -e -f -u -x
COMMIT="${bamboo.repository.revision.number}"
export COMMIT
readonly COMMIT
# Explicitly checkout the revision that we need.
git checkout "$COMMIT"
# Install Qemu, create builder.
docker version -f '{{ .Server.Experimental }}'
docker buildx rm buildx-builder || :
@@ -157,6 +149,7 @@
# Prepare and push the build.
env\
CHANNEL="${bamboo.channel}"\
COMMIT="${bamboo.repository.revision.number}"\
DIST_DIR='dist'\
DOCKER_IMAGE_NAME='adguard/adguardhome'\
DOCKER_OUTPUT="type=image,name=adguard/adguardhome,push=true"\
@@ -256,7 +249,7 @@
'recipients':
- 'webhook':
'name': 'Build webhook'
'url': 'http://prod.jirahub.service.eu.consul/v1/webhook/bamboo?channel=adguard-qa'
'url': 'http://prod.jirahub.service.eu.consul/v1/webhook/bamboo?channel=adguard-qa-dns-builds'
'labels': []
'other':
@@ -272,7 +265,8 @@
# need to build a few of these.
'variables':
'channel': 'beta'
'dockerGo': 'adguard/golang-ubuntu:8.0'
'dockerFrontend': 'adguard/home-js-builder:1.1'
'dockerGo': 'adguard/go-builder:1.22.3--1'
# release-vX.Y.Z branches are the branches from which the actual final
# release is built.
- '^release-v[0-9]+\.[0-9]+\.[0-9]+':
@@ -287,4 +281,5 @@
# are the ones that actually get released.
'variables':
'channel': 'release'
'dockerGo': 'adguard/golang-ubuntu:8.0'
'dockerFrontend': 'adguard/home-js-builder:1.1'
'dockerGo': 'adguard/go-builder:1.22.3--1'

View File

@@ -10,7 +10,7 @@
# Make sure to sync any changes with the branch overrides below.
'variables':
'channel': 'edge'
'dockerGo': 'adguard/golang-ubuntu:8.0'
'dockerSnap': 'adguard/snap-builder:1.1'
'snapcraftChannel': 'edge'
'stages':
@@ -53,7 +53,7 @@
'shared': true
'required': true
'docker':
'image': '${bamboo.dockerGo}'
'image': '${bamboo.dockerSnap}'
'key': 'DR'
'other':
'clean-working-dir': true
@@ -99,7 +99,7 @@
'shared': true
'required': true
'docker':
'image': '${bamboo.dockerGo}'
'image': '${bamboo.dockerSnap}'
'key': 'BP'
'other':
'clean-working-dir': true
@@ -127,7 +127,7 @@
- 'artifact': 'armhf_snap'
- 'artifact': 'arm64_snap'
'docker':
'image': '${bamboo.dockerGo}'
'image': '${bamboo.dockerSnap}'
'key': 'PTS'
'other':
'clean-working-dir': true
@@ -175,7 +175,7 @@
'recipients':
- 'webhook':
'name': 'Build webhook'
'url': 'http://prod.jirahub.service.eu.consul/v1/webhook/bamboo?channel=adguard-qa'
'url': 'http://prod.jirahub.service.eu.consul/v1/webhook/bamboo?channel=adguard-qa-dns-builds'
'labels': []
'other':
@@ -191,7 +191,7 @@
# need to build a few of these.
'variables':
'channel': 'beta'
'dockerGo': 'adguard/golang-ubuntu:8.0'
'dockerSnap': 'adguard/snap-builder:1.1'
'snapcraftChannel': 'beta'
# release-vX.Y.Z branches are the branches from which the actual final
# release is built.
@@ -207,5 +207,5 @@
# are the ones that actually get released.
'variables':
'channel': 'release'
'dockerGo': 'adguard/golang-ubuntu:8.0'
'dockerSnap': 'adguard/snap-builder:1.1'
'snapcraftChannel': 'candidate'

View File

@@ -5,14 +5,23 @@
'key': 'AHBRTSPECS'
'name': 'AdGuard Home - Build and run tests'
'variables':
'dockerGo': 'adguard/golang-ubuntu:8.0'
'dockerFrontend': 'adguard/home-js-builder:1.1'
'dockerGo': 'adguard/go-builder:1.22.3--1'
'channel': 'development'
'stages':
- 'Tests':
'manual': false
'final': false
'jobs':
- 'Test'
- 'Test frontend'
- 'Test backend'
- 'Frontend':
manual: false
final: false
jobs:
- 'Build frontend'
- 'Artifact':
manual: false
@@ -20,14 +29,12 @@
jobs:
- 'Artifact'
'Test':
'Test frontend':
'docker':
'image': '${bamboo.dockerGo}'
'image': '${bamboo.dockerFrontend}'
'volumes':
'${system.YARN_DIR}': '${bamboo.cacheYarn}'
'${system.GO_CACHE_DIR}': '${bamboo.cacheGo}'
'${system.GO_PKG_CACHE_DIR}': '${bamboo.cacheGoPkg}'
'key': 'TEST'
'key': 'JSTEST'
'other':
'clean-working-dir': true
'tasks':
@@ -41,13 +48,91 @@
set -e -f -u -x
make VERBOSE=1 ci go-tools lint
make VERBOSE=1 js-deps js-lint js-test
'final-tasks':
- 'clean'
'requirements':
- 'adg-docker': 'true'
'Test backend':
'docker':
'image': '${bamboo.dockerGo}'
'volumes':
'${system.GO_CACHE_DIR}': '${bamboo.cacheGo}'
'${system.GO_PKG_CACHE_DIR}': '${bamboo.cacheGoPkg}'
'key': 'GOTEST'
'other':
'clean-working-dir': true
'tasks':
- 'checkout':
'force-clean-build': true
- 'script':
'interpreter': 'SHELL'
'scripts':
- |
#!/bin/sh
set -e -f -u -x
make\
GOMAXPROCS=1\
VERBOSE=1\
go-deps go-tools go-lint
make\
VERBOSE=1\
go-test
'final-tasks':
- 'clean'
'requirements':
- 'adg-docker': 'true'
'Build frontend':
'artifacts':
- 'name': 'AdGuardHome frontend'
'pattern': 'build/**'
'shared': true
'required': true
'docker':
'image': '${bamboo.dockerFrontend}'
'volumes':
'${system.YARN_DIR}': '${bamboo.cacheYarn}'
'key': 'BF'
'other':
'clean-working-dir': true
'tasks':
- 'checkout':
'force-clean-build': true
- 'script':
'interpreter': 'SHELL'
'scripts':
- |-
#!/bin/sh
set -e -f -u -x
make\
VERBOSE=1\
js-deps js-build
'requirements':
- 'adg-docker': 'true'
'Artifact':
'artifact-subscriptions':
- 'artifact': 'AdGuardHome frontend'
'artifacts':
- 'name': 'AdGuardHome_windows_amd64'
'pattern': 'dist/AdGuardHome_windows_amd64.zip'
'shared': true
'required': true
- 'name': 'AdGuardHome_darwin_amd64'
'pattern': 'dist/AdGuardHome_darwin_amd64.zip'
'shared': true
'required': true
- 'name': 'AdGuardHome_linux_amd64'
'pattern': 'dist/AdGuardHome_linux_amd64.tar.gz'
'shared': true
'required': true
'docker':
'image': '${bamboo.dockerGo}'
'volumes':
@@ -67,30 +152,15 @@
set -e -f -u -x
# Explicitly checkout the revision that we need.
git checkout "${bamboo.repository.revision.number}"
make\
ARCH="amd64"\
CHANNEL=${bamboo.channel}\
FRONTEND_PREBUILT=1\
OS="windows darwin linux"\
CHANNEL="development"\
SIGN=0\
PARALLELISM=1\
SIGN=0\
VERBOSE=2\
build-release
'artifacts':
- 'name': 'AdGuardHome_windows_amd64'
'pattern': 'dist/AdGuardHome_windows_amd64.zip'
'shared': true
'required': true
- 'name': 'AdGuardHome_darwin_amd64'
'pattern': 'dist/AdGuardHome_darwin_amd64.zip'
'shared': true
'required': true
- 'name': 'AdGuardHome_linux_amd64'
'pattern': 'dist/AdGuardHome_linux_amd64.tar.gz'
'shared': true
'required': true
'requirements':
- 'adg-docker': 'true'
@@ -115,3 +185,15 @@
'labels': []
'other':
'concurrent-build-plugin': 'system-default'
'branch-overrides':
# rc-vX.Y.Z branches are the release candidate branches. They are created
# from the release branch and are used to build the release candidate
# images.
- '^rc-v[0-9]+\.[0-9]+\.[0-9]+':
# Set the default release channel on the release branch to beta, as we
# may need to build a few of these.
'variables':
'dockerFrontend': 'adguard/home-js-builder:1.1'
'dockerGo': 'adguard/go-builder:1.22.3--1'
'channel': 'candidate'

View File

@@ -1,18 +1,22 @@
{
"client_settings": "إعدادات العميل",
"example_upstream_reserved": "يمكنك تحديد <0> DNS upstream لنطاق معين (نطاقات) </0>",
"example_upstream_comment": "يمكنك تحديد تعليق",
"upstream_parallel": "استخدام الاستعلامات المتوازية لتسريع الحل عن طريق الاستعلام في وقت واحد عن جميع خوادم المنبع",
"parallel_requests": "طلبات موازية",
"load_balancing": "توزيع الحمل",
"example_upstream_reserved": "من المنبع <0>لمجالات محددة</0>;",
"example_multiple_upstreams_reserved": "منابع متعددة <0>لمجالات محددة</0>;",
"example_upstream_comment": "تعليق.",
"upstream_parallel": "استخدم الاستعلامات المتوازية لتسريع عملية الحل عن طريق الاستعلام عن جميع الخوادم المنبع في وقت واحد.",
"parallel_requests": "الطلبات الموازية",
"load_balancing": "موازنة الأحمال",
"load_balancing_desc": "الاستعلام عن خادم واحد في كل مرة سيستخدم AdGuard الرئيسية الخوارزمية العشوائية الموزونة لاختيار الخادم بحيث يتم استخدام أسرع خادم في كثير من الأحيان",
"bootstrap_dns": "خوادم Bootstrap DNS",
"bootstrap_dns_desc": "عناوين IP لخوادم DNS المستخدمة لحل عناوين IP الخاصة بمحللات DoH/DoT التي تحددها كمصدرين رئيسيين. التعليقات غير مسموح بها.",
"fallback_dns_title": "خوادم DNS الاحتياطية",
"fallback_dns_desc": "قائمة الخوادم الاحتياطية المستخدمة في حالة عدم الاستجابة من خوادم DNS الرئيسية. تمتلك تلك الخوادم والخوادم الرئيسية نفس الأوامر.",
"fallback_dns_placeholder": "أدخل خادم DNS احتياطي واحد لكل سطر",
"local_ptr_title": "خوادم DNS العكسية الخاصة",
"local_ptr_desc": "خوادم DNS التي يستخدمها AdGuard Home لاستعلامات PTR المحلية. تُستخدم هذه الخوادم لحل أسماء المضيفين للعملاء بعناوين IP خاصة ، على سبيل المثال \"192.168.12.34\" ، باستخدام DNS العكسي. في حالة عدم التعيين ، يستخدم AdGuard Home عناوين محللات DNS الافتراضية لنظام التشغيل الخاص بك باستثناء عناوين AdGuard Home نفسها.",
"local_ptr_default_resolver": "بشكل افتراضي ، يستخدم AdGuard Home محللات DNS العكسية التالية: {{ip}}.",
"local_ptr_no_default_resolver": "لم يتمكن AdGuard Home من تحديد محللات DNS العكسية المناسبة لهذا النظام.",
"local_ptr_placeholder": "أدخل عنوان خادم واحد لكل سطر",
"local_ptr_placeholder": "أدخل عنوان IP واحد لكل سطر",
"resolve_clients_title": "تفعيل التحليل العكسي لعناوين IP للعملاء",
"resolve_clients_desc": "حل عكسيًا لعناوين IP للعملاء في أسماء مضيفيهم عن طريق إرسال استعلامات PTR إلى أدوات الحل المقابلة (خوادم DNS الخاصة للعملاء المحليين ، والخوادم الأولية للعملاء الذين لديهم عناوين IP عامة).",
"use_private_ptr_resolvers_title": "استخدم محللات DNS العكسية الخاصة",
@@ -34,7 +38,7 @@
"dhcp_leases_not_found": "لم يتم العثور على عقود إيجار DHCP",
"dhcp_config_saved": "الإعدادات محفوظة لخادم DHCP",
"dhcp_ipv4_settings": "DHCP IPv4 إعدادات",
"dhcp_ipv6_settings": "DHCP IPv6 إعدادات",
"dhcp_ipv6_settings": "إعدادات DHCP IPv6",
"form_error_required": "الحقل مطلوب",
"form_error_ip4_format": "عنوان IPv4 غير صالح",
"form_error_ip4_gateway_format": "عنوان IPv4 غير صالح للبوابة",
@@ -63,14 +67,16 @@
"dhcp_ip_addresses": "عناوين الـIP",
"ip": "IP",
"dhcp_table_hostname": "اسم المضيف",
"dhcp_table_expires": "يتنهي في",
"dhcp_table_expires": "تنتهي",
"dhcp_warning": "إذا كنت تريد تمكين خادم DHCP على أي حال ، فتأكد من عدم وجود خادم DHCP نشط آخر في شبكتك. خلاف ذلك ، يمكن أن يعطل خدمة الإنترنت للأجهزة المتصلة!",
"dhcp_error": "لم نتمكن من تحديد ما إذا كان هناك خادم DHCP آخر في الشبكة.",
"dhcp_static_ip_error": "من أجل استخدام خادم DHCP ، يجب تعيين عنوان IP ثابت. فشلنا في تحديد ما إذا تم تكوين واجهة الشبكة هذه باستخدام عنوان IP ثابت. يرجى تعيين عنوان IP ثابت يدويًا.",
"dhcp_dynamic_ip_found": "يستخدم نظامك عنوان IP الديناميكي للواجهة <0>{{interfaceName}}</0>. من أجل استعمال خادم DHCP ، يجب تعيين عنوان IP ثابت. عنوان IP الحالي الخاص بك هو <0>{{ipAddress}}</0>. إذا ضغطت على زر تفعيل DHCP سنقوم تلقائيًا بتعيين عنوان الIP هذا على أنه ثابت.",
"dhcp_lease_added": "تمت أضافة مدة الايجار \"{{key}}\" بنجاح",
"dhcp_lease_deleted": "تمت ازالة مدة الايجار \"{{key}}\" بنجاح",
"dhcp_lease_updated": "Static lease \"{{key}}\" تمّ التحديث بنجاح",
"dhcp_new_static_lease": "عقد إيجار ثابت جديد",
"dhcp_edit_static_lease": "تحرير عقد الإيجار الثابت",
"dhcp_static_leases_not_found": "لم يتم العثور على عقود إيجار ثابتة DHCP",
"dhcp_add_static_lease": "إضافة عقد إيجار ثابت",
"dhcp_reset_leases": "إعادة تعيين كافة عقود الإيجار",
@@ -83,7 +89,7 @@
"form_enter_hostname": "أدخل اسم الhostname",
"error_details": "مزيد من التفاصيل حول الخطأ",
"response_details": "تفاصيل الاستجابة",
"request_details": "تفاصيل الطلب",
"request_details": "طلب التفاصيل",
"client_details": "تفاصيل العميل",
"details": "التفاصيل",
"back": "رجوع",
@@ -93,13 +99,13 @@
"filter": "فلتر",
"query_log": "سجل الQuery",
"compact": "المدمج",
"nothing_found": "لم يتم العثور علي شيء...",
"faq": "أسئلة مكررة",
"nothing_found": "لم يتم العثور على شيء",
"faq": "الأسئلة المتداولة",
"version": "الإصدار",
"address": "العناوين",
"address": "العنوان",
"protocol": "البروتوكول",
"on": "ON",
"off": "OFF",
"on": "قيد التشغيل",
"off": "قيد الإيقاف",
"copyright": "حقوق النشر",
"homepage": "الصفحة الرئيسية",
"report_an_issue": "الإبلاغ عن مشكلة",
@@ -114,7 +120,8 @@
"stats_malware_phishing": "حسر البرامج الضارة / والتصيّد",
"stats_adult": "حظر مواقع الويب الخاصة بالبالغين",
"stats_query_domain": "اعلى النطاقات التي تم الاستعلام عنها",
"for_last_24_hours": أخر 24 ساعة",
"for_last_hours": آخر {{count}} ساعة",
"for_last_hours_plural": "لآخر {{count}} ساعة",
"for_last_days": "لآخر {{value}} يوم",
"for_last_days_plural": "لآخر {{count}} ايام",
"stats_disabled": "تم تعطيل الإحصائيات. يمكنك تشغيله من <0> صفحة الإعدادات </0>.",
@@ -129,13 +136,16 @@
"no_upstreams_data_found": "لم يتم العثور على بيانات خوادم upstream",
"number_of_dns_query_days": "عدد استعلامات DNS التي تمت معالجتها لآخر {{count}} يوم",
"number_of_dns_query_days_plural": "عدد استعلامات DNS التي تمت معالجتها لآخر {{count}} أيام",
"number_of_dns_query_24_hours": "عدد استعلامات DNS التي تمت معالجتها لآخر 24 ساعة",
"number_of_dns_query_hours": "عدد استفسارات DNS التي تمت معالجتها لآخر {{count}} ساعة",
"number_of_dns_query_hours_plural": "عدد استعلامات DNS التي تمت معالجتها خلال آخر {{count}} ساعة",
"number_of_dns_query_blocked_24_hours": "عدد طلبات DNS المحظورة بواسطة فلاتر adblock وقوائم حظر المضيفين",
"number_of_dns_query_blocked_24_hours_by_sec": "عدد طلبات DNS التي تم حظرها من قبل وحدة أمان التصفح AdGuard",
"number_of_dns_query_blocked_24_hours_adult": "عدد من المواقع (الإباحية) للبالغين تم حجبها",
"enforced_save_search": "فرض البحث الآمن",
"number_of_dns_query_to_safe_search": "عدد طلبات DNS لمحركات البحث التي تم فرض البحث الآمن عنها",
"average_processing_time": "متوسط وقت المعالجة",
"average_upstream_response_time": "متوسط وقت استجابة المنبع",
"response_time": "وقت الاستجابة",
"average_processing_time_hint": "متوسط الوقت بالمللي ثانية عند معالجة طلب DNS",
"block_domain_use_filters_and_hosts": "حظر النطاقات باستخدام عوامل التصفية وملفات المضيفين",
"filters_block_toggle_hint": "يمكنك إعداد قواعد حظر في <a>المرشحات</a> اعدادات.",
@@ -170,8 +180,9 @@
"enabled_parental_toast": "تفعيل الرقابة الأبوية",
"disabled_safe_search_toast": "تعطيل البحث الآمن",
"enabled_save_search_toast": "تفعيل البحث الآمن",
"enabled_table_header": "تمكين",
"name_table_header": "الاسم",
"updated_save_search_toast": "تم تحديث إعدادات البحث الآمن",
"enabled_table_header": "قيد التشغيل",
"name_table_header": "الاِسْم",
"list_url_table_header": "قائمة الروابط",
"rules_count_table_header": "عدد القواعد",
"last_time_updated_table_header": "آخر تحديث",
@@ -225,6 +236,7 @@
"updated_upstream_dns_toast": "تم حفظ خوادم Upstream بنجاح",
"dns_test_ok_toast": "تعمل خوادم DNS المحددة بشكل صحيح",
"dns_test_not_ok_toast": "خادم \"{{key}}\": لا يمكن استخدامه يرجى التحقق من كتابته بشكل صحيح",
"dns_test_parsing_error_toast": "القسم {{section}}: السطر {{line}}: لا يمكن استخدامه، يرجى التحقق من أنك قد كتبته بشكل صحيح",
"dns_test_warning_toast": "المنبع \"{{key}}\" لا يستجيب لطلبات الاختبار وقد لا يعمل بشكل صحيح",
"unblock": "إلغاء الحظر",
"block": "حظر",
@@ -232,7 +244,8 @@
"allow_this_client": "السماح لهذا العميل",
"block_for_this_client_only": "احجب هذا العميل فقط",
"unblock_for_this_client_only": "إلغاء حجب هذا العميل فقط",
"time_table_header": "وقت",
"add_persistent_client": "إضافة كعميل دائم",
"time_table_header": "الوقت",
"date": "التاريخ",
"domain_name_table_header": "اسم النطاق",
"domain_or_client": "الدومين أو العميل",
@@ -247,7 +260,7 @@
"refresh_btn": "تحديث",
"previous_btn": "السابق",
"next_btn": "التالي",
"loading_table_status": "جار التحميل...",
"loading_table_status": "التحميل جارٍ...",
"page_table_footer_text": "الصفحة",
"rows_table_footer_text": "صفوف",
"updated_custom_filtering_toast": "تحديث قواعد الفلترة المخصصة",
@@ -259,12 +272,12 @@
"query_log_cleared": "تم مسح سجل الاستعلام بنجاح",
"query_log_updated": "تم تحديث سجل الاستعلام بنجاح",
"query_log_clear": "مسح سجلات الاستعلام",
"query_log_retention": "الاحتفاظ بسجلات الاستعلام",
"query_log_retention": "تناوب سجلات الاستعلام",
"query_log_enable": "تمكين السجل",
"query_log_configuration": "تكوين السجلات",
"query_log_disabled": "سجل الاستعلام معطل ويمكن تهيئته من<0>الاعدادات</0>",
"query_log_strict_search": "استخدم علامات الاقتباس المزدوجة للبحث الدقيق",
"query_log_retention_confirm": "هل أنت متأكد من أنك تريد تغيير الاحتفاظ بسجل الاستعلام؟ إذا قمت بتقليل قيمة الفاصل الزمني سيتم فقدان بعض البيانات",
"query_log_retention_confirm": "هل أنت متيقِّن من أنك تريد تغيير دوران سجل الاستعلام؟ إذا قمت بتقليل قيمة الفاصل الزمني، ستفقد بعض البيانات",
"anonymize_client_ip": "إخفاء عنوان IP العميل",
"anonymize_client_ip_desc": "لا تقم بحفظ كامل عنوان IP العميل في السجلات والإحصائيات",
"dns_config": "إعداد خادم DNS",
@@ -279,6 +292,8 @@
"blocking_ipv4": "حجب عنوان IPv4",
"blocking_ipv6": "حجب عنوان IPv6",
"blocked_response_ttl": "زمن حظر الاستجابة",
"blocked_response_ttl_desc": "تحديد عدد الثواني التي يجب على العملاء تخزين الاستجابة التي تمت تصفيتها مؤقتًا",
"form_enter_blocked_response_ttl": "أدخل وقت الاستجابة المحظورة TTL (بالثواني)",
"dnscrypt": "DNSCrypt",
"dns_over_https": "DNS-over-HTTPS",
"dns_over_tls": "DNS-over-TLS",
@@ -294,7 +309,19 @@
"rate_limit": "حدود التقييم",
"edns_enable": "فعل EDNS client subnet",
"edns_cs_desc": "أضف EDNS الشبكة الفرعية للعميل (ECS) إلى الطلبات الأولية وقم بتسجيل القيم المرسلة من قبل العملاء في سجل الاستعلام.",
"edns_use_custom_ip": "استخدام IP مخصص لـ EDNS",
"edns_use_custom_ip_desc": "السماح باستخدام IP مخصص لـ EDNS",
"rate_limit_desc": "عدد الطلبات في الثانية المسموح بها لكل عميل. جعله على 0 يعني عدم وجود حد.",
"rate_limit_subnet_len_ipv4": "طول بادئة الشبكة لعناوين IPv4",
"rate_limit_subnet_len_ipv4_desc": "طول بادئة الشبكة لعناوين IPv4 المستخدمة لتحديد معدل الحد الأقصى. الافتراضي هو 24",
"rate_limit_subnet_len_ipv4_error": "يجب أن يكون طول بادئة الشبكة IPv4 بين 0 و 32",
"rate_limit_subnet_len_ipv6": "طول بادئة الشبكة لعناوين IPv6",
"rate_limit_subnet_len_ipv6_desc": "طول بادئة الشبكة لعناوين IPv6 المستخدمة لتحديد معدل الحد الأقصى. الافتراضي هو 56",
"rate_limit_subnet_len_ipv6_error": "يجب أن يكون طول بادئة الشبكة IPv6 بين 0 و 128",
"form_enter_rate_limit_subnet_len": "أدخل طول بادئة الشبكة الفرعية لتحديد معدل الحد الأقصى",
"rate_limit_whitelist": "قائمة السماح بتحديد معدل الحد الأقصى",
"rate_limit_whitelist_desc": "عناوين IP المستبعدة من تحديد معدل الحد الأقصى",
"rate_limit_whitelist_placeholder": "أدخل عنوان IP واحد لكل سطر",
"blocking_ipv4_desc": "سيتم إرجاع عنوان IP لطلب محظور",
"blocking_ipv6_desc": "سيتم إرجاع عنوان IP لطلب AAAA محظور",
"blocking_mode_default": "الافتراضي: الرد بعنوان IP صفري (0.0.0.0 لـ A ؛ :: لـ AAAA) عند حظره بواسطة قاعدة نمط Adblock ؛ الرد بعنوان IP المحدد في القاعدة عند حظره بواسطة / etc / hosts-style rule",
@@ -302,14 +329,15 @@
"blocking_mode_nxdomain": "NXDOMAIN: الرد باستخدام رمز NXDOMAIN",
"blocking_mode_null_ip": "IP Null: الاستجابة بعنوان IP صفري (0.0.0.0 لـ A ؛ :: لـ AAAA)",
"blocking_mode_custom_ip": "استجابة IP مخصصة بعنوان IP تم تعيينه يدويًا",
"theme_auto": "تلقائي",
"theme_light": "فاتح",
"theme_dark": "ليلي",
"theme_dark": "داكن",
"upstream_dns_client_desc": "إذا احتفظت بهذا الحقل فارغًا ، فسيستخدم AdGuard Home الخوادم التي تم تكوينها في<0>DNS إعدادات</0>.",
"tracker_source": "مصدر المتعقب",
"source_label": "المصدر",
"found_in_known_domain_db": "تم العثور عليه في قاعدة بيانات دومينات معروفة.",
"category_label": "الفئة",
"rule_label": "قواعد",
"rule_label": اعدة (قواعد)",
"list_label": "قائمه",
"unknown_filter": "فلتر غير معروف {{filterId}}",
"known_tracker": "متعقب معروف",
@@ -320,14 +348,14 @@
"install_settings_port": "المنفذ",
"install_settings_interface_link": "ستكون واجهة الويب الخاصة بمسؤول AdGuard Home متاحة على العناوين التالية:",
"form_error_port": "أدخل رقم منفذ صالح",
"install_settings_dns": "خادم DNS",
"install_settings_dns": َادِم DNS",
"install_settings_dns_desc": "ستحتاج إلى ضبط أجهزتك أو جهاز التوجيه الخاص بك لاستخدام خادم DNS على العناوين التالية:",
"install_settings_all_interfaces": "جميع الواجهات",
"install_auth_title": "المصادقة",
"install_auth_desc": "يجب إعداد مصادقة كلمة المرور لواجهة ويب مسؤول AdGuard Home. في حال كان AdGuard Home لا يمكن الوصول إليه إلا في شبكتك المحلية ، فلا يزال من المهم حمايته من الوصول غير المقيد.",
"install_auth_username": "اسم المستخدم",
"install_auth_password": "الكلمة السرية",
"install_auth_confirm": اكيد كلمه المرور",
"install_auth_confirm": أكيد كلمة المرور",
"install_auth_username_enter": "أدخل اسم المستخدم",
"install_auth_password_enter": "أدخل كلمة المرور",
"install_step": "خطوة",
@@ -397,6 +425,9 @@
"encryption_hostnames": "اسم المستضيف",
"encryption_reset": "هل أنت متأكد أنك تريد إعادة تعيين إعدادات التشفير؟",
"encryption_warning": "تحذير",
"encryption_plain_dns_enable": "تمكين DNS العادي",
"encryption_plain_dns_desc": "الـDNS العادي مفعل افتراضيًا. يمكنك تعطيله لإجبار جميع الأجهزة على استخدام DNS المشفر. للقيام بذلك، يجب عليك تفعيل بروتوكول DNS المشفر على الأقل",
"encryption_plain_dns_error": "لتعطيل DNS العادي، قم بتمكين بروتوكول DNS المشفر على الأقل",
"topline_expiring_certificate": "شهادة SSL الخاصة بك على وشك الانتهاء. قم بتحديث <0>إعدادات التشفير</0>.",
"topline_expired_certificate": "انتهت صلاحية شهادة SSL الخاصة بك. قم بتحديث <0>إعدادات التشفير</0>.",
"form_error_port_range": "أدخل رقم المنفذ في النطاق 80-65535",
@@ -436,6 +467,7 @@
"form_add_id": "أضافة معّرف",
"form_client_name": "ادخل اسم العميل",
"name": "اسم",
"client_name": "العميل {{id}}",
"client_global_settings": "استخدم إعدادات عالمية",
"client_deleted": "تم حذف العميل \"{{key}}\" بنجاح",
"client_added": "تم اضافة العميل \"{{key}}\" بنجاح",
@@ -444,7 +476,7 @@
"client_confirm_delete": "هل أنت متأكد من أنك تريد حذف العميل \"{{key}}\"?",
"list_confirm_delete": "هل أنت متأكد أنك تريد حذف هذه القائمة؟",
"auto_clients_title": "Runtime clients",
"auto_clients_desc": "الأجهزة غير المدرجة في قائمة العملاء الدائمين الذين قد لا يزالون يستخدمون AdGuard Home",
"auto_clients_desc": "معلومات حول عناوين IP للأجهزة التي تستخدم أو قد تستخدم AdGuard Home. يتم جمع هذه المعلومات من عدة مصادر، بما في ذلك ملفات المضيفين، و DNS العكسي، إلخ.",
"access_title": "إعدادات الوصول",
"access_desc": "هنا يمكنك ضبط قواعد الوصول لخادم AdGuard Home DNS",
"access_allowed_title": "العملاء المسموحين",
@@ -457,6 +489,7 @@
"updates_checked": "يتوفر إصدار جديد من AdGuard Home",
"updates_version_equal": "AdGuard Home محدث",
"check_updates_now": "تحقق من وجود تحديثات الآن",
"version_request_error": "فشل التحقق من التحديث. يرجى التحقق من اتصالك بالإنترنت.",
"dns_privacy": "خصوصية DNS",
"setup_dns_privacy_1": "<0> DNS-over-TLS: </0> استخدم سلسلة <1> {{address}} </1>.",
"setup_dns_privacy_2": "<0> DNS-over-HTTPS: </0> استخدم سلسلة <1> {{address}} </1>.",
@@ -477,7 +510,9 @@
"setup_dns_notice": "من أجل استخدام <0> DNS-over-HTTPS </0> أو <1> DNS-over-TLS </1> ، تحتاج إلى <1> تكوين التشفير </1> في إعدادات AdGuard Home.",
"rewrite_added": "تمت إضافة إعادة كتابة DNS لـ \"{{key}}\" بنجاح",
"rewrite_deleted": "تم حذف إعادة كتابة DNS لـ \"{{key}}\" بنجاح",
"rewrite_updated": "تم تحديث إعادة كتابة DNS بنجاح",
"rewrite_add": "إضافة إعادة كتابة DNS",
"rewrite_edit": "تحرير إعادة كتابة DNS",
"rewrite_not_found": "لم يتم العثور على إعادة كتابة DNS",
"rewrite_confirm_delete": "هل أنت متأكد من أنك تريد حذف إعادة كتابة DNS لـ \"{{key}}\"؟",
"rewrite_desc": "يسمح بتكوين استجابة DNS المخصصة بسهولة لاسم نطاق معين.",
@@ -506,7 +541,7 @@
"encryption_key_source_content": "الصق محتويات المفتاح الخاص",
"stats_params": "ضبط الاحصائيات",
"config_successfully_saved": "تم حفظ الاعدادات بنجاح",
"interval_6_hour": "ساعات6",
"interval_6_hour": "6 ساعات",
"interval_24_hour": "24 ساعة",
"interval_days": "{{count}} يوم",
"interval_days_plural": "{{count}} الأيام",
@@ -517,14 +552,18 @@
"filter_added_successfully": "تم إضافة القائمة بنجاح",
"filter_removed_successfully": "تم ازالته من القائمة بنجاح",
"filter_updated": "تم تحديث القائمة بنجاح",
"statistics_configuration": "ضبط الاحصائيات",
"statistics_configuration": "تكوين الإحصائيات",
"statistics_retention": "الاحتفاظ بالإحصاءات",
"statistics_retention_desc": "إذا قمت بتقليل قيمة الفاصل الزمني ، فستفقد بعض البيانات",
"statistics_clear": "إعادة تعيين الإحصائيات",
"statistics_clear_confirm": "هل أنت متأكد من أنك تريد مسح الإحصاءات؟",
"statistics_clear_confirm": "هل أنت متيقِّن من أنك تريد مسح الإحصائيات؟",
"statistics_retention_confirm": "هل أنت متأكد أنك تريد تغيير الاحتفاظ بالإحصاءات؟ إذا قمت بتقليل قيمة الفاصل الزمني ، فستفقد بعض البيانات",
"statistics_cleared": "تم مسح الإحصائيات بنجاح",
"statistics_enable": "تفعيل الاحصائيات",
"ignore_domains": "المجالات التي تم تجاهلها (مفصولة بسطر جديد)",
"ignore_domains_title": "نطاقات تم تجاهلها",
"ignore_domains_desc_stats": "لا تتم كتابة الاستعلامات المطابقة لهذه القواعد في الإحصائيات",
"ignore_domains_desc_query": "لا تتم كتابة الاستعلامات المطابقة لهذه القواعد في سجل الاستعلامات",
"interval_hours": "{{count}} ساعة",
"interval_hours_plural": "{{count}} ساعات",
"filters_configuration": "اضبط الفلاتر",
@@ -597,20 +636,20 @@
"dnssec_enable_desc": "قم بتعيين علامة DNSSEC في استعلامات DNS الواردة وتحقق من النتيجة (مطلوب محلل يدعم DNSSEC).",
"validated_with_dnssec": "تم التحقق من صحتها باستخدام DNSSEC",
"all_queries": "كافة الاستفسارات",
"show_blocked_responses": "حظر",
"show_whitelisted_responses": "القائمة البيضاء",
"show_processed_responses": "معالجة",
"show_blocked_responses": "ما تمّ حظره",
"show_whitelisted_responses": "المسموح به",
"show_processed_responses": "تمت معالجتها",
"blocked_safebrowsing": "محظور بواسطة التصفح الآمن",
"blocked_adult_websites": "محظور بواسطة الرقابة الأبوية",
"blocked_adult_websites": "محظور بواسطة الرِّقابة الأبوية",
"blocked_threats": "التهديدات المحظورة",
"allowed": "القائمة البيضاء",
"allowed": "المسموح به",
"filtered": "تمت الفلترة",
"rewritten": "أعيدت كتابته",
"safe_search": "البحث الأمن",
"safe_search": "البحث الآمن",
"blocklist": "قائمة الحظر",
"milliseconds_abbreviation": "ms",
"cache_size": "حجم ذاكرة التخزين المؤقت",
"cache_size_desc": "حجم ذاكرة التخزين المؤقت لنظام أسماء النطاقات (بالبايت).",
"cache_size_desc": "حجم ذاكرة التخزين المؤقت لنظام أسماء النطاق (بالبايت). لتعطيل التخزين المؤقت، اتركه فارغًا.",
"cache_ttl_min_override": "تجاوز الحد الأدنى من مدة البقاء TTL",
"cache_ttl_max_override": "تجاوز الحد الاقصى من مدة البقاء TTL",
"enter_cache_size": "أدخل حجم ذاكرة التخزين المؤقت (بايت)",
@@ -621,14 +660,14 @@
"ttl_cache_validation": "يجب أن يكون الحد الأدنى لتجاوز TTL لذاكرة التخزين المؤقت أقل من أو يساوي الحد الأقصى",
"cache_optimistic": "متفائل التخزين المؤقت",
"cache_optimistic_desc": "اجعل AdGuard Home يستجيب من ذاكرة التخزين المؤقت حتى عندما تنتهي صلاحية الإدخالات وحاول أيضًا تحديثها.",
"filter_category_general": "General",
"filter_category_security": "الامان",
"filter_category_general": "عام",
"filter_category_security": "الأمان",
"filter_category_regional": "إقليمي",
"filter_category_other": "أخرى",
"filter_category_general_desc": "القوائم التي تمنع التتبع والإعلان على معظم الأجهزة",
"filter_category_general_desc": "القوائم التي تحظر التتبع والإعلانات على معظم الأجهزة",
"filter_category_security_desc": "القوائم المصممة خصيصًا لحظر النطاقات الخبيثة والتصيد الاحتيالي والخداع",
"filter_category_regional_desc": "القوائم التي تركز على الإعلانات الإقليمية وخوادم التتبع",
"filter_category_other_desc": "قوائم حظر أخرى",
"filter_category_other_desc": "قوائم الحظر الأخرى",
"setup_config_to_enable_dhcp_server": "أضبط الاعدادات لتمكين خادم DHCP",
"original_response": "الرد الأصلي",
"click_to_view_queries": "انقر لعرض الـ queries",
@@ -637,16 +676,72 @@
"filter_allowlist": "تحذير: سيؤدي هذا الإجراء أيضًا إلى استبعاد القاعدة \"{{disallowed_rule}}\" من قائمة العملاء المسموح لهم.",
"last_rule_in_allowlist": "لا يمكن منع هذا العميل لأن استبعاد القاعدة \"{{disallowed_rule}}\" سيؤدي إلى تعطيل قائمة \"العملاء المسموح لهم\".",
"use_saved_key": "استخدم المفتاح المحفوظ مسبقًا",
"parental_control": "الرقابة الابويه",
"parental_control": "الرِّقابة الأبوية",
"safe_browsing": "تصفح آمن",
"served_from_cache": "{{value}} <i>(يتم تقديمه من ذاكرة التخزين المؤقت)</i>",
"form_error_password_length": "يجب أن تتكون كلمة المرور من {{value}} من الأحرف على الأقل",
"served_from_cache_label": "يتم تقديمه من ذاكرة التخزين المؤقت",
"form_error_password_length": "يجب أن تتكون كلمة المرور من {{min}} إلى {{max}} من الأحرف في الأقل",
"anonymizer_notification": "<0>ملاحظة:</0> تم تمكين إخفاء هُوِيَّة IP. يمكنك تعطيله في <1>الإعدادات العامة</1>.",
"confirm_dns_cache_clear": "هل تريد بالتأكيد مسح ذاكرة التخزين المؤقت لنظام أسماء النطاقات DNS؟",
"cache_cleared": "تم مسح ذاكرة التخزين المؤقت لنظام أسماء النطاق بنجاح",
"clear_cache": "مسح ذاكرة التخزين المؤقت",
"make_static": "اجعلها ثابتة",
"theme_auto_desc": "تلقائي (بناءً على نظام ألوان جهازك)",
"theme_dark_desc": "المظهر الداكن",
"theme_light_desc": "المظهر فاتح",
"disable_for_seconds": "لـ {{count}} ثانية",
"disable_for_seconds_plural": "لمدة {{count}} ثانية",
"disable_for_minutes": "لمدة {{count}} دقيقة",
"disable_for_minutes_plural": "لمدة {{count}} دقيقة",
"disable_for_hours": "لمدة {{count}} ساعة",
"disable_for_hours_plural": "لمدة {{count}} ساعات",
"disable_until_tomorrow": "حتى الغد",
"disable_notify_for_seconds": "تعطيل الحماية لـ {{count}} ثانية",
"disable_notify_for_seconds_plural": "تعطيل الحماية ل {{count}} ثواني",
"disable_notify_for_minutes": "تعطيل الحماية لـ {{count}} دقيقة",
"disable_notify_for_minutes_plural": "تعطيل الحماية لـ {{count}} دقائق",
"disable_notify_for_hours": "تعطيل الحماية لـ {{count}} ساعة",
"disable_notify_for_hours_plural": "تعطيل الحماية لـ {{count}} ساعات",
"disable_notify_until_tomorrow": "تعطيل الحماية حتى الغد",
"enable_protection_timer": "سيتم تمكين الحماية في {{time}}",
"custom_retention_input": "أدخل الاحتفاظ بالساعات",
"custom_rotation_input": "أدخل التناوب بالساعات",
"protection_section_label": "الحماية",
"log_and_stats_section_label": "سجل الاستعلام والإحصائيات",
"ignore_query_log": "تجاهل هذا العميل في سجل الاستعلام",
"ignore_statistics": "تجاهل هذا العميل في الإحصائيات",
"schedule_services": "إيقاف حظر الخدمة مؤقتًا",
"schedule_services_desc": "تهيئة جدول إيقاف فلتر خدمة الحظر",
"schedule_services_desc_client": "تهيئة جدول إيقاف فلتر خدمة الحظر لهذا العميل",
"schedule_desc": "تعيين فترات عدم النشاط للخدمات المحظورة",
"schedule_invalid_select": "يجب أن يكون وقت البَدْء قبل وقت الانتهاء",
"schedule_select_days": "اختر الأيام",
"schedule_timezone": "قم باختيار منطقة زمنية",
"schedule_current_timezone": "المنطقة الزمنية الحالية: {{value}}",
"schedule_time_all_day": "طوال اليوم",
"schedule_modal_description": "سيحل هذا الجدول الزمني محل أي جداول موجودة لنفس اليوم من الأسبوع. يمكن أن يكون لكل يوم من أيام الأسبوع مدّة خمول واحدة فقط.",
"schedule_modal_time_off": "لا يوجد حظر للخدمة:",
"schedule_new": "جدول زمني جديد",
"schedule_edit": "تحرير الجدول الزمني",
"schedule_save": "حفظ الجدول الزمني",
"schedule_add": "إضافة جدول زمني",
"schedule_remove": "إزالة الجدول الزمني",
"schedule_from": "من",
"schedule_to": "إلى",
"sunday": "الأحد",
"monday": "الإثنين",
"tuesday": "الثلاثاء",
"wednesday": "الأربعاء",
"thursday": "الخميس",
"friday": "الجمعة",
"saturday": "السبت",
"sunday_short": "الاحد",
"monday_short": "الإثنين",
"tuesday_short": "الثلاثاء",
"wednesday_short": "الاربعاء",
"thursday_short": "الخميس",
"friday_short": "الجمعة",
"saturday_short": "السبت"
"saturday_short": "السبت",
"upstream_dns_cache_configuration": "تهيئة ذاكرة التخزين المؤقت لنظام أسماء النطاقات المستقبلي",
"enable_upstream_dns_cache": "تمكين التخزين المؤقت لنظام أسماء النطاقات DNS لتكوين المنبع المخصص لهذا العميل",
"dns_cache_size": "حجم ذاكرة التخزين المؤقت لنظام أسماء النطاقات، بالبايت"
}

View File

@@ -13,14 +13,14 @@
"fallback_dns_desc": "Seznam záložních DNS serverů používaných v případě, že odchozí DNS servery neodpovídají. Syntaxe je stejná jako v hlavním poli pro odchozí servery výše.",
"fallback_dns_placeholder": "Zadejte jeden záložní DNS server na řádek",
"local_ptr_title": "Soukromé reverzní DNS servery",
"local_ptr_desc": "Servery DNS, které AdGuard Home používá pro lokální dotazy PTR. Tyto servery se používají k řešení požadavků PTR na adresy v soukromých rozmezích IP, například \"192.168.12.34\", pomocí reverzního DNS. Pokud není nastaveno, AdGuard Home automaticky použije výchozí řešitele vašeho OS s výjimkou adres samotného AdGuard Home.",
"local_ptr_desc": "DNS servery používané AdGuard Home pro soukromé požadavky PTR, SOA a NS. Požadavek je považován za soukromý, pokud požaduje doménu ARPA obsahující podsíť v rámci soukromých IP rozsahů (například \"192.168.12.34\") a pochází od klienta se soukromou IP adresou. Pokud není nastaveno, budou použity výchozí DNS řešitele vašeho operačního systému, s výjimkou IP adres AdGuard Home.",
"local_ptr_default_resolver": "Ve výchozím nastavení používá AdGuard Home následující reverzní DNS řešitele: {{ip}}.",
"local_ptr_no_default_resolver": "AdGuard Home nemohl určit vhodné soukromé reverzní DNS řešitele pro tento systém.",
"local_ptr_placeholder": "Zadejte jednu IP adresu na řádek",
"resolve_clients_title": "Povolit zpětné řešení IP adres klientů",
"resolve_clients_desc": "Obráceně vyřešit IP adresy klientů na jejich názvy hostitelů zasláním dotazů PTR příslušným řešitelům (soukromé DNS servery pro místní klienty, odchozí servery pro klienty s veřejnou IP adresou).",
"use_private_ptr_resolvers_title": "Použít soukromé reverzní rDNS řešitele",
"use_private_ptr_resolvers_desc": "Realizuje reverzní DNS vyhledávání pro lokální adresy pomocí těchto odchozích serverů. Pokud je funkce vypnuta, Adguard Home reaguje s NXDOMAIN na všechny takové PTR dotazy kromě klientů známých z DHCP, /etc/hosts, atd.",
"use_private_ptr_resolvers_desc": "Řešení požadavků PTR, SOA a NS pro domény ARPA obsahující soukromé IP adresy prostřednictvím soukromých odchozích serverů, DHCP, /etc/hosts atd. Pokud je zakázáno, AdGuard Home bude na všechny takové požadavky odpovídat pomocí NXDOMAIN.",
"check_dhcp_servers": "Zkontrolovat DHCP servery",
"save_config": "Uložit konfiguraci",
"enabled_dhcp": "DHCP server zapnutý",
@@ -678,7 +678,7 @@
"use_saved_key": "Použít dříve uložený klíče",
"parental_control": "Rodičovská ochrana",
"safe_browsing": "Bezpečné prohlížení",
"served_from_cache": "{{value}} <i>(převzato z mezipaměti)</i>",
"served_from_cache_label": "Převzato z mezipaměti",
"form_error_password_length": "Heslo musí obsahovat od {{min}} do {{max}} znaků",
"anonymizer_notification": "<0>Poznámka:</0> Anonymizace IP je zapnuta. Můžete ji vypnout v <1>Obecných nastaveních</1>.",
"confirm_dns_cache_clear": "Opravdu chcete vymazat mezipaměť DNS?",

View File

@@ -13,14 +13,14 @@
"fallback_dns_desc": "Liste over reserve (fallback) DNS-servere, som bruges, når upstream DNS-servere ikke reagerer. Samme syntaks som i upstream-hovedfeltet ovenfor.",
"fallback_dns_placeholder": "Angiv én reserve DNS-server pr. linje",
"local_ptr_title": "Private reverse DNS-servere",
"local_ptr_desc": "DNS-servere brugt af AdGuard Home til lokale PTR-forespørgsler. Disse servere bruges til at opløse PTR-forespørgsler fra private IP-adresseområder, f.eks. \"192.168.12.34\", vha. reverse DNS. Hvis ikke opsat, bruger AdGuard Home operativsystems standard DNS-opløsere undtagen for sine egne adresser.",
"local_ptr_desc": "DNS-serverne brugt af AdGuard Home til private PTR-, SOA- og NS-forespørgsler. En forespørgsel anses som privat, hvis den omhandler et ARPA-domæne indeholdende et undernet i et privat IP-områder, (såsom \"192.168.12.34\") og kommer fra en klient med en privat adresse. Hvis ikke opsat, bruger AdGuard Home OS'ets adresser på standard DNS-opløserne, bortset fra AdGuard Home-adresserne.",
"local_ptr_default_resolver": "AdGuard Home bruger som standard flg. reverse DNS-opløsere: {{ip}}.",
"local_ptr_no_default_resolver": "AdGuard Home kunne ikke fastslå egnede private reverse DNS-opløsere for dette system.",
"local_ptr_placeholder": "Angiv én IP-adresse pr. linje",
"resolve_clients_title": "Aktivér omvendt løsning af klienters IP-adresser",
"resolve_clients_desc": "Opløs klienters IP-adresser reverseret til deres værtsnavne ved at sende PTR-forespørgsler til korresponderende opløsere (private DNS-servere til lokale klienter, upstream-servere til klienter med offentlige IP-adresser).",
"use_private_ptr_resolvers_title": "Brug private reverse DNS-opløsere",
"use_private_ptr_resolvers_desc": "Udfør reverse DNS-opslag for lokalt leverede adresser vha. disse upstream-servere. Hvis deaktiveret, svarer AdGuard Home med NXDOMAIN på alle sådanne PTR-forespørgsler bortset fra for klienter kendt via DHCP, /etc/hosts mv.",
"use_private_ptr_resolvers_desc": "Opløs PTR-, SOA- og NS-forespørgsler til ARPA-domæner indeholdende private adresser vha. private upstream-servere, DHCP, /etc/hosts mv. Hvis deaktiveret, besvarer AdGuard Home sådanne forespørgsler med NXDOMAIN.",
"check_dhcp_servers": "Søg efter DHCP-servere",
"save_config": "Gem opsætning",
"enabled_dhcp": "DHCP-server aktiveret",
@@ -678,7 +678,7 @@
"use_saved_key": "Brug den tidligere gemte nøgle",
"parental_control": "Forældrekontrol",
"safe_browsing": "Sikker Browsing",
"served_from_cache": "{{value}} <i>(leveret fra cache)</i>",
"served_from_cache_label": "Leveret fra cache",
"form_error_password_length": "Adgangskode skal udgøre fra {{min}} til {{max}} tegn",
"anonymizer_notification": "<0>Bemærk:</0> IP-anonymisering er aktiveret. Det kan deaktiveres via <1>Generelle indstillinger</1>.",
"confirm_dns_cache_clear": "Sikker på, at DNS-cache skal ryddes?",

View File

@@ -13,14 +13,14 @@
"fallback_dns_desc": "Liste der Fallback-DNS-Server, die verwendet werden, wenn die Upstream-DNS-Server nicht antworten. Die Syntax ist die gleiche wie im Hauptfeld für Upstream-Server oben.",
"fallback_dns_placeholder": "Geben Sie einen Fallback-DNS-Server pro Zeile ein",
"local_ptr_title": "Private inverse DNS-Server",
"local_ptr_desc": "Die DNS-Server, die AdGuard Home für lokale PTR-Abfragen verwendet. Diese Server werden verwendet, um die Hostnamen von Clients mit privaten IP-Adressen, z. B. „192.168.12.34“, per inverse DNS-Anfragen aufzulösen. Wenn nicht festgelegt, verwendet AdGuard Home die Adressen der Standard-DNS-Auflöser Ihres Betriebssystems mit Ausnahme der Adressen von AdGuard Home selbst.",
"local_ptr_desc": "Von AdGuard Home verwendete DNS-Server für private PTR-, SOA- und NS-Anfragen. Eine Anfrage gilt als privat, wenn sie nach einer ARPA-Domain fragt, die ein Subnetz innerhalb privater IP-Bereiche enthält (z. B. „192.168.12.34“) und von einem Client mit privater IP-Adresse stammt. Wenn nicht eingestellt, werden die Standard-DNS-Auflöser Ihres Betriebssystems verwendet, außer für die IP-Adressen von AdGuard Home.",
"local_ptr_default_resolver": "Standardmäßig verwendet AdGuard Home die folgenden inversen DNS-Resolver: {{ip}}.",
"local_ptr_no_default_resolver": "AdGuard Home konnte keine geeigneten privaten Invers-DNS-Resolver für dieses System ermitteln.",
"local_ptr_placeholder": "Geben Sie eine IP-Adresse pro Zeile ein",
"resolve_clients_title": "Hostnamenauflösung der Clients aktivieren",
"resolve_clients_desc": "Inverses Auflösen der IP-Adressen der Clients in ihre Hostnamen durch Senden von PTR-Anfragen an die entsprechenden Resolver (private DNS-Server für lokale Kunden, Upstream-Server für Kunden mit öffentlichen IP-Adressen).",
"use_private_ptr_resolvers_title": "Private Reverse-DNS-Resolver verwenden",
"use_private_ptr_resolvers_desc": "Führt inverse DNS-Abfragen für lokal bereitgestellte Adressen mit diesen Upstream-Servern durch. Wenn deaktiviert, antwortet AdGuard Home mit NXDOMAIN auf alle solchen PTR-Anfragen, außer für Clients, die über DHCP, /etc/hosts usw. bekannt sind.",
"use_private_ptr_resolvers_desc": "Löst PTR-, SOA- und NS-Anfragen für ARD-Domains mit privaten IP-Adressen über private Upstream-Server, DHCP, /etc/hosts usw. auf. Ist diese Option deaktiviert, antwortet AdGuard Home auf alle derartigen Anfragen mit NXDOMAIN.",
"check_dhcp_servers": "Auf DHCP-Server prüfen",
"save_config": "Konfiguration speichern",
"enabled_dhcp": "DHCP-Server aktiviert",
@@ -678,7 +678,7 @@
"use_saved_key": "Zuvor gespeicherten Schlüssel verwenden",
"parental_control": "Kindersicherung",
"safe_browsing": "Internetsicherheit",
"served_from_cache": "{{value}} <i>(aus dem Cache abgerufen)</i>",
"served_from_cache_label": "Aus dem Cache abgerufen",
"form_error_password_length": "Das Passwort muss zwischen {{min}} und {{max}} Zeichen enthalten",
"anonymizer_notification": "<0>Hinweis:</0> Die IP-Anonymisierung ist aktiviert. Sie können sie in den <1>Allgemeinen Einstellungen</1> deaktivieren.",
"confirm_dns_cache_clear": "Möchten Sie den DNS-Cache wirklich leeren?",

View File

@@ -13,14 +13,14 @@
"fallback_dns_desc": "List of fallback DNS servers used when upstream DNS servers are not responding. The syntax is the same as in the main upstreams field above.",
"fallback_dns_placeholder": "Enter one fallback DNS server per line",
"local_ptr_title": "Private reverse DNS servers",
"local_ptr_desc": "The DNS servers that AdGuard Home uses for local PTR queries. These servers are used to resolve PTR requests for addresses in private IP ranges, for example \"192.168.12.34\", using reverse DNS. If not set, AdGuard Home uses the addresses of the default DNS resolvers of your OS except for the addresses of AdGuard Home itself.",
"local_ptr_desc": "DNS servers used by AdGuard Home for private PTR, SOA, and NS requests. A request is considered private if it asks for an ARPA domain containing a subnet within private IP ranges (such as \"192.168.12.34\") and comes from a client with a private IP address. If not set, the default DNS resolvers of your OS will be used, except for the AdGuard Home IP addresses.",
"local_ptr_default_resolver": "By default, AdGuard Home uses the following reverse DNS resolvers: {{ip}}.",
"local_ptr_no_default_resolver": "AdGuard Home could not determine suitable private reverse DNS resolvers for this system.",
"local_ptr_placeholder": "Enter one IP address per line",
"resolve_clients_title": "Enable reverse resolving of clients' IP addresses",
"resolve_clients_desc": "Reversely resolve clients' IP addresses into their hostnames by sending PTR queries to corresponding resolvers (private DNS servers for local clients, upstream servers for clients with public IP addresses).",
"use_private_ptr_resolvers_title": "Use private reverse DNS resolvers",
"use_private_ptr_resolvers_desc": "Perform reverse DNS lookups for locally served addresses using these upstream servers. If disabled, AdGuard Home responds with NXDOMAIN to all such PTR requests except for clients known from DHCP, /etc/hosts, and so on.",
"use_private_ptr_resolvers_desc": "Resolve PTR, SOA, and NS requests for ARPA domains containing private IP addresses through private upstream servers, DHCP, /etc/hosts, etc. If disabled, AdGuard Home will respond to all such requests with NXDOMAIN.",
"check_dhcp_servers": "Check for DHCP servers",
"save_config": "Save configuration",
"enabled_dhcp": "DHCP server enabled",
@@ -678,7 +678,7 @@
"use_saved_key": "Use the previously saved key",
"parental_control": "Parental Control",
"safe_browsing": "Safe Browsing",
"served_from_cache": "{{value}} <i>(served from cache)</i>",
"served_from_cache_label": "Served from cache",
"form_error_password_length": "Password must be {{min}} to {{max}} characters long",
"anonymizer_notification": "<0>Note:</0> IP anonymization is enabled. You can disable it in <1>General settings</1>.",
"confirm_dns_cache_clear": "Are you sure you want to clear DNS cache?",

View File

@@ -13,14 +13,14 @@
"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",
"local_ptr_title": "Servidores DNS inversos y privados",
"local_ptr_desc": "Los servidores DNS que AdGuard Home utiliza para las consultas PTR locales. Estos servidores se utilizan para resolver las peticiones PTR de direcciones en rangos de IP privadas, por ejemplo \"192.168.12.34\", utilizando DNS inverso. Si no está establecido, AdGuard Home utiliza los resolutores 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.",
"local_ptr_default_resolver": "Por defecto, AdGuard Home utiliza los siguientes resolutores DNS inversos: {{ip}}.",
"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",
"resolve_clients_title": "Habilitar la resolución inversa de las direcciones IP de clientes",
"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_desc": "Realiza búsquedas DNS inversas para direcciones servidas localmente utilizando estos servidores DNS de subida. Si está deshabilitado, AdGuard Home responderá con NXDOMAIN a todas las peticiones PTR de este tipo, excepto para los clientes conocidos por DHCP, /etc/hosts, etc.",
"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",
"save_config": "Guardar configuración",
"enabled_dhcp": "Servidor DHCP habilitado",
@@ -678,7 +678,7 @@
"use_saved_key": "Usar la clave guardada previamente",
"parental_control": "Control parental",
"safe_browsing": "Navegación segura",
"served_from_cache": "{{value}} <i>(servido desde la caché)</i>",
"served_from_cache_label": "Servido desde la caché",
"form_error_password_length": "La contraseña debe tener entre {{min}} y {{max}} caracteres",
"anonymizer_notification": "<0>Nota:</0> La anonimización de IP está habilitada. Puedes deshabilitarla en <1>Configuración general</1>.",
"confirm_dns_cache_clear": "¿Estás seguro de que deseas borrar la caché DNS?",

View File

@@ -220,7 +220,7 @@
"updated_upstream_dns_toast": "سرورهای DNS جریان ارسالی بروز رسانی شده است",
"dns_test_ok_toast": "سرورهای DNS تعیین شده بدرستی کار می کنند",
"dns_test_not_ok_toast": "سرور \"{{key}}\": نمیتواند مورد استفاده قرار گیرد،لطفا بررسی کنید آن را بدرستی نوشته اید",
"dns_test_parsing_error_toast": "بخش {{section}}: خط {{line}}: نمیتواند مورد استفاده قرار گیرد،لطفا بررسی کنید آن را بدرستی نوشته اید",
"dns_test_parsing_error_toast": "بخش {{section}}: خط {{line}}: نمیتواند مورد استفاده قرار گیرد،لطفا بررسی کنید آن را بهدرستی نوشتهاید",
"unblock": "رفع انسداد",
"block": "مسدود کردن",
"disallow_this_client": "این مشتری را رد کنید",

View File

@@ -678,7 +678,7 @@
"use_saved_key": "Käytä aiemmin tallennettua avainta",
"parental_control": "Lapsilukko",
"safe_browsing": "Turvallinen selaus",
"served_from_cache": "{{value}} <i>(jaettu välimuistista)</i>",
"served_from_cache_label": "Toimitettu välimuistista",
"form_error_password_length": "Salasanan on oltava {{min}} - {{max}} merkkiä pitkä",
"anonymizer_notification": "<0>Huomioi:</0> IP-osoitteen anonymisointi on käytössä. Voit poistaa sen käytöstä <1>Yleisistä asetuksista</1>.",
"confirm_dns_cache_clear": "Haluatko varmasti tyhjentää DNS-välimuistin?",

View File

@@ -13,14 +13,14 @@
"fallback_dns_desc": "Liste des serveurs DNS de repli utilisés lorsque les serveurs DNS en amont ne répondent pas. La syntaxe est la même que dans le champ principal en amont ci-dessus.",
"fallback_dns_placeholder": "Saisissez un serveur DNS de repli par ligne",
"local_ptr_title": "Serveurs DNS privés inverses",
"local_ptr_desc": "Les serveurs DNS que AdGuard Home utilise pour les requêtes PTR locales. Ces serveurs sont utilisés pour résoudre les noms d'hôte des clients avec des adresses IP privées, par exemple « 192.168.12.34 », en utilisant le DNS inversé. Si ce paramètre n'est pas défini, AdGuard Home utilise les adresses des résolveurs DNS par défaut de votre système d'exploitation, à l'exception des adresses d'AdGuard Home lui-même.",
"local_ptr_desc": "Les serveurs DNS utilisés par AdGuard Home pour les requêtes privées PTR, SOA et NS. Une requête est considérée privée si elle demande un domaine ARPA contenant un sous-réseau entre les plages IP privées (par exemple \"192.168.12.34\") et provient d'un client avec une adresse IP privée. Sans réglages additionnels, les résolveurs DNS par défaut de votre système d'exploitation seront utilisés, sauf pour les adresses IP d'AdGuard Home.",
"local_ptr_default_resolver": "Par défaut, AdGuard Home utilise les résolveurs DNS inversés suivants : {{ip}}.",
"local_ptr_no_default_resolver": "AdGuard Home n'a pas pu déterminer de résolveurs DNS inversés privés appropriés pour ce système.",
"local_ptr_placeholder": "Saisissez une adresse IP par ligne",
"resolve_clients_title": "Activer la résolution inverse des adresses IP des clients",
"resolve_clients_desc": "Résoudre inversement les adresses IP des clients en leurs noms d'hôtes en envoyant des requêtes PTR aux résolveurs correspondants (serveurs DNS privés pour les clients locaux, serveurs en amont pour les clients ayant une adresse IP publique).",
"use_private_ptr_resolvers_title": "Utiliser des résolveurs DNS inversés privés",
"use_private_ptr_resolvers_desc": "Effectuer des recherches DNS inversées pour les adresses servies localement en utilisant ces serveurs en amont. S'il est désactivé, AdGuard Home répond avec NXDOMAIN à toutes les requêtes PTR de ce type, sauf pour les clients connus par DHCP, /etc/hosts, etc.",
"use_private_ptr_resolvers_desc": "Résolvez les requêtes PTR, SOA et NS pour les domaines ARPA contenant des adresses IP privées par aide des serveurs privés en amont, DHCP, /etc/hosts, etc. S'il est désactivé, AdGuard Home répondra à toutes ces requêtes avec NXDOMAIN.",
"check_dhcp_servers": "Rechercher les serveurs DHCP",
"save_config": "Sauvegarder la configuration",
"enabled_dhcp": "Serveur DHCP activé",
@@ -244,6 +244,7 @@
"allow_this_client": "Autoriser ce client",
"block_for_this_client_only": "Bloquer uniquement pour ce client",
"unblock_for_this_client_only": "Débloquer uniquement pour ce client",
"add_persistent_client": "Ajouter comme client persistant",
"time_table_header": "Temps",
"date": "Date",
"domain_name_table_header": "Nom de domaine",
@@ -466,6 +467,7 @@
"form_add_id": "Ajouter identifiant",
"form_client_name": "Saisissez le nom du client",
"name": "Nom",
"client_name": "Client {{id}}",
"client_global_settings": "Utiliser les paramètres généraux",
"client_deleted": "Le client « {{key}} » a été supprimé",
"client_added": "Le client « {{key}} » a été ajouté",
@@ -676,7 +678,7 @@
"use_saved_key": "Utiliser la clef précédemment enregistrée",
"parental_control": "Contrôle parental",
"safe_browsing": "Navigation sécurisée",
"served_from_cache": "{{value}} <i>(depuis le cache)</i>",
"served_from_cache_label": "Servi depuis le cache",
"form_error_password_length": "Le mot de passe doit comporter entre {{min}} et {{max}}  caractères",
"anonymizer_notification": "<0>Note :</0> L'anonymisation IP est activée. Vous pouvez la désactiver dans les <1>paramètres généraux</1>.",
"confirm_dns_cache_clear": "Voulez-vous vraiment vider le cache DNS ?",

View File

@@ -117,8 +117,8 @@
"refresh_statics": "Segarkan statistik",
"dns_query": "Kueri DNS",
"blocked_by": "<0>Diblokir oleh</0>",
"stats_malware_phishing": "Malware/phishing diblokir",
"stats_adult": "Situs dewasa diblokir",
"stats_malware_phishing": "Malware/phishing terblokir",
"stats_adult": "Situs dewasa terblokir",
"stats_query_domain": "Kueri domain teratas",
"for_last_hours": "selama {{count}} jam terakhir",
"for_last_hours_plural": "selama {{count}} jam terakhir",
@@ -138,9 +138,9 @@
"number_of_dns_query_days_plural": "Jumlah kueri DNS yang diproses selama {{count}} hari terakhir",
"number_of_dns_query_hours": "Jumlah kueri DNS diproses selama {{{count}} jam terakhir",
"number_of_dns_query_hours_plural": "Jumlah kueri DNS diproses selama {{count}} jam terakhir",
"number_of_dns_query_blocked_24_hours": "Julah DNS diblokir oleh penyaring adblock dan daftar blokir hosts",
"number_of_dns_query_blocked_24_hours_by_sec": "Jumlah perminataan DNS diblokir oleh modul Kemanan Penjelajahan AdGuard",
"number_of_dns_query_blocked_24_hours_adult": "Jumlah website dewasa diblokir",
"number_of_dns_query_blocked_24_hours": "Jumlah permintaan DNS yang diblokir oleh filter adblock dan daftar hitam host",
"number_of_dns_query_blocked_24_hours_by_sec": "Jumlah permintaan DNS yang diblokir oleh modul keamanan penjelajahan AdGuard",
"number_of_dns_query_blocked_24_hours_adult": "Jumlah situs web dewasa yang diblokir",
"enforced_save_search": "Paksa pencarian aman",
"number_of_dns_query_to_safe_search": "Jumlah perminataan DNS ke mesin pencari yang dipaksa Pencarian Aman",
"average_processing_time": "Rata-rata waktu pemrosesan",
@@ -148,7 +148,7 @@
"response_time": "Waktu respons",
"average_processing_time_hint": "Rata-rata waktu dalam milidetik untuk pemrosesan sebuah permintaan DNS",
"block_domain_use_filters_and_hosts": "Blokir domain menggunakan filter dan file hosts",
"filters_block_toggle_hint": "Anda dapat menyiapkan aturan pemblokiran di pengaturan <a>Penyaringan</a>.",
"filters_block_toggle_hint": "Anda dapat menyiapkan aturan pemblokiran dalam pengaturan <a>Filter</a>.",
"use_adguard_browsing_sec": "Gunakan layanan web Keamanan Penjelajahan AdGuard",
"use_adguard_browsing_sec_hint": "AdGuard Home akan memeriksa apakah domain diblokir oleh layanan web keamanan penjelajahan. Ini akan menggunakan API pencarian yang ramah privasi untuk melakukan pemeriksaan: hanya awalan singkat dari hash nama domain SHA256 yang dikirim ke server.",
"use_adguard_parental": "Gunakan layanan web kontrol orang tua AdGuard",
@@ -224,10 +224,10 @@
"example_upstream_regular": "DNS reguler (melalui UDP);",
"example_upstream_regular_port": "DNS biasa (lebih dari UDP, dengan port);",
"example_upstream_udp": "DNS biasa (lebih dari UDP, nama host);",
"example_upstream_dot": "terenkripsi <0>DNS-over-TLS</0>;",
"example_upstream_doh": "terenkripsi <0>DNS-over-HTTPS</0>;",
"example_upstream_doh3": "DNS-over-HTTPS terenkripsi dengan paksa <0>HTTP/3</0> dan tidak ada fallback ke HTTP/2 atau lebih rendah;",
"example_upstream_doq": "terenkripsi <0>DNS-over-QUIC</0>;",
"example_upstream_dot": "<0>DNS melalui TLS</0> terenkripsi;",
"example_upstream_doh": "<0>DNS melalui HTTPS</0> terenkripsi;",
"example_upstream_doh3": "DNS melalui HTTPS terenkripsi dengan <0>HTTP/3</0> secara paksa dan tidak ada cadangan ke HTTP/2 atau lebih rendah;",
"example_upstream_doq": "<0>DNS melalui QUIC</0> terenkripsi;",
"example_upstream_sdns": "<0>Stempel DNS</0> untuk <1>DNSCrypt</1> atau pengarah <2>DNS-over-HTTPS</2>;",
"example_upstream_tcp": "DNS reguler (melalui TCP);",
"example_upstream_tcp_port": "DNS biasa (melalui TCP, dengan port);",
@@ -291,7 +291,7 @@
"custom_ip": "Custom IP",
"blocking_ipv4": "Blokiran IPv4",
"blocking_ipv6": "Blokiran IPv6",
"blocked_response_ttl": "TTL Respons yang diblokir",
"blocked_response_ttl": "Respons TTL terblokir",
"blocked_response_ttl_desc": "Menentukan berapa detik klien harus menyimpan respons yang difilter dalam cache",
"form_enter_blocked_response_ttl": "Masukkan TTL respons yang diblokir (detik)",
"dnscrypt": "DNSCrypt",
@@ -322,9 +322,9 @@
"rate_limit_whitelist": "Daftar pembatasan tarif yang diizinkan",
"rate_limit_whitelist_desc": "Alamat IP dikecualikan dari pembatasan tarif",
"rate_limit_whitelist_placeholder": "Masukkan satu alamat IP per baris",
"blocking_ipv4_desc": "Alamat IP akan dikembalikan untuk permintaan A yang diblokir",
"blocking_ipv6_desc": "Alamat IP akan dipulihkan untuk permintaan AAAA yang diblokir",
"blocking_mode_default": "Default: Tanggapi dengan alamat IP nol (0.0.0.0 untuk A; :: untuk AAAA) saat diblokir oleh aturan gaya Adblock; tanggapi dengan alamat IP yang ditentukan dalam aturan ketika diblokir oleh aturan gaya host /etc/",
"blocking_ipv4_desc": "Alamat IP yang akan dikembalikan untuk permintaan A yang diblokir",
"blocking_ipv6_desc": "Alamat IP yang akan dikembalikan untuk permintaan AAAA yang diblokir",
"blocking_mode_default": "Standar: Tanggapi dengan alamat IP nol (0.0.0.0 untuk A; :: untuk AAAA) saat diblokir oleh aturan gaya Adblock; tanggapi dengan alamat IP yang ditentukan dalam aturan ketika diblokir oleh aturan /etc/hosts-style",
"blocking_mode_refused": "DITOLAK: Respon dengan kode DITOLAK",
"blocking_mode_nxdomain": "NXDOMAIN: Respon pakai kode NXDOMAIN",
"blocking_mode_null_ip": "Null IP: Respon pakai alamat IP kosong (0.0.0.0 untuk A; :: untuk AAAA)",
@@ -426,7 +426,7 @@
"encryption_reset": "Anda yakin ingin mengatur ulang pengaturan enkripsi?",
"encryption_warning": "Peringatan",
"encryption_plain_dns_enable": "Aktifkan DNS biasa",
"encryption_plain_dns_desc": "DNS Biasa diaktifkan secara standar. Anda dapat menonaktifkannya untuk memaksa semua perangkat menggunakan DNS terenkripsi. Untuk melakukan ini, Anda harus mengaktifkan setidaknya satu protokol DNS terenkripsi",
"encryption_plain_dns_desc": "DNS biasa diaktifkan secara standar. Anda dapat menonaktifkannya untuk memaksa semua perangkat menggunakan DNS terenkripsi. Untuk melakukan ini, Anda harus mengaktifkan setidaknya satu protokol DNS terenkripsi",
"encryption_plain_dns_error": "Untuk menonaktifkan DNS biasa, aktifkan setidaknya satu protokol DNS terenkripsi",
"topline_expiring_certificate": "Sertifikat SSL Anda hampir kedaluwarsa. Perbarui <0>Pengaturan enkripsi</0>.",
"topline_expired_certificate": "Sertifikat SSL Anda kedaluwarsa. Perbarui <0>Pengaturan enkripsi</0>.",
@@ -548,7 +548,7 @@
"domain": "Domain",
"ecs": "ECS",
"punycode": "Kode kecil",
"answer": "Jawab",
"answer": "Jawaban",
"filter_added_successfully": "Filter telah berhasil ditambahkan",
"filter_removed_successfully": "Daftar ini telah sukses dihapus",
"filter_updated": "Daftar telah sukses diperbarui",
@@ -621,8 +621,8 @@
"check_not_found": "Tidak di temukan di daftar penyaringan anda",
"client_confirm_block": "Apa anda yakin ingin mem-blokir klien ini \"{{ip}}\"?",
"client_confirm_unblock": "Apa anda yakin ingin meng-unblock klien ini \"{{ip}}\"?",
"client_blocked": "Klien \"{{ip}}\" sukses di blokir",
"client_unblocked": "Klien \"{{ip}}\" sukses di unblock",
"client_blocked": "Klien \"{{ip}}\" berhasil diblokir",
"client_unblocked": "Klien \"{{ip}}\" berhasil membuka blokir",
"static_ip": "Alamat IP statis",
"static_ip_desc": "AdGuard Home adalah server jadi perlu alamat IP statis agar berfungsi dengan benar. Jika tidak, pada titik tertentu, router Anda dapat menetapkan alamat IP yang berbeda untuk perangkat ini.",
"set_static_ip": "Atur alamat IP statik",
@@ -640,8 +640,8 @@
"show_whitelisted_responses": "Dalam Daftar Putih",
"show_processed_responses": "Terproses",
"blocked_safebrowsing": "Diblokir oleh Penjelajahan Aman",
"blocked_adult_websites": "Diblok oleh Kontrol Orang tua",
"blocked_threats": "Blokir Ancaman",
"blocked_adult_websites": "Diblokir oleh Kontrol Orang Tua",
"blocked_threats": "Ancaman terblokir",
"allowed": "Dibolehkan",
"filtered": "Tersaring",
"rewritten": "Tulis ulang",
@@ -682,7 +682,7 @@
"form_error_password_length": "Kata sandi harus terdiri dari {{min}} hingga {{max}}",
"anonymizer_notification": "<0>Catatan:</0> Anonimisasi IP diaktifkan. Anda dapat menonaktifkannya di <1>Pengaturan umum</1> .",
"confirm_dns_cache_clear": "Apakah Anda yakin ingin menghapus cache DNS?",
"cache_cleared": "Cache DNS berhasil dibersihkan",
"cache_cleared": "Cache DNS berhasil dihapus",
"clear_cache": "Hapus cache",
"make_static": "Jadikan statis",
"theme_auto_desc": "Otomatis (berdasarkan skema warna perangkat anda)",

View File

@@ -13,14 +13,14 @@
"fallback_dns_desc": "Elenco dei server DNS fallback utilizzati quando i server DNS upstream non rispondono. La sintassi è la stessa del campo principale upstream sopra.",
"fallback_dns_placeholder": "Inserisci un server DNS fallback per riga",
"local_ptr_title": "Server DNS privati inversi",
"local_ptr_desc": "I server DNS che AdGuard Home utilizza per le richieste PTR locali. Questi server vengono utilizzati per risolvere i nomi host dei client con indirizzi IP privati, ad esempio \"192.168.12.34\", utilizzando il DNS inverso. Se non è impostato, AdGuard Home utilizzerà gli indirizzi dei resolutori DNS predefiniti del tuo sistema operativo ad eccezione degli indirizzi di AdGuard Home stesso.",
"local_ptr_desc": "I server DNS usati da AdGuard Home per richieste private PTR, SOA e NS. Una richiesta è considerata privata se richiede un dominio ARPA contenente una sottorete all'interno di intervalli IP privati (come \"192.168.12.34\") e proviene da un client con un indirizzo IP privato. Se non impostato, saranno usati i risolutori DNS predefiniti del tuo sistema operativo, ad eccezione degli indirizzi IP di AdGuard Home.",
"local_ptr_default_resolver": "Per impostazione predefinita, AdGuard Home utilizzerà i seguenti risolutori DNS inversi: {{ip}}.",
"local_ptr_no_default_resolver": "AdGuard Home non è stato in grado di determinare i risolutori DNS inversi privati adatti per questo sistema.",
"local_ptr_placeholder": "Inserisci un indirizzo IP per riga",
"resolve_clients_title": "Attiva la risoluzione inversa degli indirizzi IP dei client",
"resolve_clients_desc": "Risolve inversamente gli indirizzi IP dei client nei loro nomi host inviando richieste PTR ai risolutori corrispondenti (server DNS privati per client locali, server upstream per client con indirizzi IP pubblici).",
"use_private_ptr_resolvers_title": "Utilizza dei resolver rDNS privati",
"use_private_ptr_resolvers_desc": "Esegue ricerche DNS inverse per indirizzi locali utilizzando questi server upstream. Se disattivata, AdGuard Home risponderà con NXDOMAIN a tutte le richieste PTR ad eccezione dei client noti da DHCP, /etc/hosts, e così via.",
"use_private_ptr_resolvers_desc": "Risolvi le richieste PTR, SOA e NS per domini ARPA contenenti indirizzi IP privati tramite server upstream privati, DHCP, /etc/hosts, ecc. Se disabilitato, AdGuard Home risponderà a tutte queste richieste con NXDOMAIN.",
"check_dhcp_servers": "Controlla la presenza di server DHCP",
"save_config": "Salva configurazione",
"enabled_dhcp": "Server DHCP attivo",
@@ -244,6 +244,7 @@
"allow_this_client": "Consenti questo client",
"block_for_this_client_only": "Blocca solo per questo client",
"unblock_for_this_client_only": "Sblocca solo per questo client",
"add_persistent_client": "Aggiungi come client persistente",
"time_table_header": "Ora",
"date": "Data",
"domain_name_table_header": "Nome dominio",
@@ -340,7 +341,7 @@
"list_label": "Elenco",
"unknown_filter": "Filtro sconosciuto {{filterId}}",
"known_tracker": "Tracciatore noto",
"install_welcome_title": "Benvenuto nella Home di AdGuard!",
"install_welcome_title": "Benvenuto in AdGuard Home!",
"install_welcome_desc": "AdGuard Home è un server DNS che blocca annunci e tracciatori a livello di rete. Il suo scopo è quello di permetterti il controllo dell'intera rete e di tutti i dispositivi, e non richiede l'utilizzo di un programma lato client.",
"install_settings_title": "Interfaccia Web dell'Admin",
"install_settings_listen": "Interfaccia d'ascolto",
@@ -445,7 +446,7 @@
"update_now": "Aggiorna ora",
"update_failed": "Aggiornamento automatico non riuscito. Ti suggeriamo di <a>seguire questi passaggi</a> per aggiornare manualmente.",
"manual_update": "Ti invitiamo a <a>seguire questi passaggi</a> per aggiornare manualmente.",
"processing_update": "Perfavore aspetta, AdGuard Home si sta aggiornando",
"processing_update": "Attendi per favore, AdGuard Home si sta aggiornando",
"clients_title": "Client persistenti",
"clients_desc": "Configura le registrazioni dei client persistenti per i dispositivi connessi ad AdGuard Home",
"settings_global": "Globale",
@@ -466,6 +467,7 @@
"form_add_id": "Aggiungi identificatore",
"form_client_name": "Inserisci nome client",
"name": "Nome",
"client_name": "Client {{id}}",
"client_global_settings": "Utilizza le impostazioni globali",
"client_deleted": "Client \"{{key}}\" eliminato correttamente",
"client_added": "Client \"{{key}}\" aggiunto correttamente",
@@ -676,7 +678,7 @@
"use_saved_key": "Utilizza la chiave salvata in precedenza",
"parental_control": "Controllo Parentale",
"safe_browsing": "Navigazione Sicura",
"served_from_cache": "{{value}} <i>(fornito dalla cache)</i>",
"served_from_cache_label": "Servito dalla cache",
"form_error_password_length": "La password deve contenere da {{min}} a {{max}} caratteri",
"anonymizer_notification": "<0>Attenzione:</0> L'anonimizzazione dell'IP è abilitata. Puoi disabilitarla in <1>Impostazioni generali</1>.",
"confirm_dns_cache_clear": "Sei sicuro di voler cancellare la cache DNS?",

View File

@@ -678,7 +678,7 @@
"use_saved_key": "以前に保存したキーを使用する",
"parental_control": "ペアレンタルコントロール",
"safe_browsing": "セーフブラウジング",
"served_from_cache": "{{value}} <i>(キャッシュから応答)</i>",
"served_from_cache_label": "キャッシュからの配信:",
"form_error_password_length": "パスワードの長さは{{min}}〜{{max}}文字にしてください。",
"anonymizer_notification": "【<0>注意</0>】IPの匿名化が有効になっています。 <1>一般設定</1>で無効にできます。",
"confirm_dns_cache_clear": "DNS キャッシュをクリアしてもよろしいですか?",

View File

@@ -13,14 +13,14 @@
"fallback_dns_desc": "업스트림 DNS 서버가 응답하지 않을 때 사용되는 폴백 DNS 서버 목록입니다. 구문은 위의 기본 업스트림 필드와 동일합니다.",
"fallback_dns_placeholder": "한 줄에 하나의 폴백 DNS 서버를 입력하세요.",
"local_ptr_title": "프라이빗 역방향 DNS 서버",
"local_ptr_desc": "AdGuard Home이 로컬 PTR 쿼리에 사용하는 DNS 서버입니다. 이러한 서버는 역방향 DNS를 사용하여 개인 IP 주소(예: '192.168.12.34')가 있는 클라이언트의 호스트 이름을 확인하는 데 사용됩니다. 설정지 않은 경우, AdGuard Home은 AdGuard Home의 주소를 제외하고 운영 체제의 기본 DNS 리졸버의 주소를 사용니다.",
"local_ptr_desc": "AdGuard Home에서 비공개 PTR, SOA 및 NS 요청에 사용하는 DNS 서버입니다. 요청이 비공개 IP 범위 내의 서브넷(예: \"192.168.12.34\")을 포함하는 ARPA 도메인을 요청하고 비공개 IP 주소를 가진 클라이언트로부터 오는 경우 비공개로 간주됩니다. 설정지 않으면 AdGuard Home IP 주소를 제외한 OS의 기본 DNS 리졸버 사용니다.",
"local_ptr_default_resolver": "기본적으로 AdGuard Home에서는 {{ip}} 역방향 DNS 서버를 이용합니다.",
"local_ptr_no_default_resolver": "AdGuard Home에서 이 시스템에 적합한 사설 역방향 프라이빗 DNS 서버를 결정할 수 없습니다.",
"local_ptr_placeholder": "한 줄에 하나씩 IP 주소를 입력하세요.",
"resolve_clients_title": "클라이언트 IP 주소에 대한 호스트명 확인 활성화",
"resolve_clients_desc": "해당 서버에 대한 PTR 쿼리를 통해 클라이언트의 도메인 이름을 정의합니다. (로컬 클라이언트의 경우 프라이빗 DNS 서버, 공용 IP 주소가 있는 클라이언트의 경우 업스트림 서버).",
"use_private_ptr_resolvers_title": "프라이빗 역방향 DNS 리졸버 사용",
"use_private_ptr_resolvers_desc": "업스트림 서버를 사용해 로컬로 제공되는 주소의 역방향 DNS를 조회합니다. 끄는 경우, AdGuard Home은 DHCP, /etc/hosts 등에서 알려진 클라이언트를 제외한 모든 PTR 요청에 NXDOMAIN으로 응답합니다.",
"use_private_ptr_resolvers_desc": "사설 업스트림 서버, DHCP, / etc/hosts 등을 통해 사설 IP 주소가 포함된 ARPA 도메인에 대한 PTR, SOA 및 NS 요청을 처리합니다. 비활성화하면 AdGuard Home은 NXDOMAIN을 사용하여 이러한 모든 요청에 응답합니다.",
"check_dhcp_servers": "DHCP 서버 체크",
"save_config": "구성 저장",
"enabled_dhcp": "DHCP 서버 활성화됨",
@@ -678,7 +678,7 @@
"use_saved_key": "이전에 저장했던 키 사용하기",
"parental_control": "자녀 보호",
"safe_browsing": "세이프 브라우징",
"served_from_cache": "{{value}} <i>(캐시에서 제공)</i>",
"served_from_cache_label": "캐시에서 가져옴",
"form_error_password_length": "비밀번호는 {{min}}~{{max}}자 길이여야 합니다.",
"anonymizer_notification": "<0>참고:</0> IP 익명화가 활성화되었습니다. <1>일반 설정</1>에서 비활성화할 수 있습니다.",
"confirm_dns_cache_clear": "정말로 DNS 캐시를 지우시겠습니까?",

View File

@@ -13,14 +13,14 @@
"fallback_dns_desc": "Lijst met DNS-back-up-noodservers die worden gebruikt wanneer upstream DNS-servers niet reageren. De syntaxis is hetzelfde als in het veld hoofdstroomopwaarts hierboven.",
"fallback_dns_placeholder": "Voer één DNS-back-upserver per regel in",
"local_ptr_title": "Private omgekeerde DNS-servers",
"local_ptr_desc": "De DNS-servers die AdGuard Home gebruikt voor lokale PTR-zoekopdrachten. Deze servers worden gebruikt om PTR-verzoeken voor adressen in privé-IP-bereiken op te lossen, bijvoorbeeld \"192.168.12.34\", met behulp van reverse DNS. Indien niet ingesteld, gebruikt AdGuard Home de adressen van de standaard DNS-resolvers van uw besturingssysteem, behalve de adressen van AdGuard Home zelf.",
"local_ptr_desc": "DNS-servers die door AdGuard Home worden gebruikt voor privé PTR-, SOA- en NS-verzoeken. Een verzoek wordt als privé beschouwd als het vraagt om een ARPA-domein dat een subnet binnen privé-IP-bereiken bevat (zoals \"192.168.12.34\") en afkomstig is van een client met een privé-IP-adres. Indien niet ingesteld, zullen de standaard DNS-resolvers van je besturingssysteem worden gebruikt, behalve de AdGuard Home IP-adressen.",
"local_ptr_default_resolver": "Standaard gebruikt AdGuard Home de volgende omgekeerde DNS-resolvers: {{ip}}.",
"local_ptr_no_default_resolver": "AdGuard Home kon voor dit systeem geen geschikte private omgekeerde DNS-resolvers bepalen.",
"local_ptr_placeholder": "Voer één IP-adres per regel in",
"resolve_clients_title": "Omzetten van hostnamen van clients inschakelen",
"resolve_clients_desc": "Indien ingeschakeld, zal AdGuard Home proberen om IP-adressen van apparaten te converteren in hun hostnamen door PTR-verzoeken te sturen naar overeenkomstige resolvers (privé-DNS-servers voor lokale apparaten, upstream-server voor apparaten met een openbaar IP-adres).",
"use_private_ptr_resolvers_title": "Private omgekeerde DNS-resolvers gebruiken",
"use_private_ptr_resolvers_desc": "Omgekeerde DNS opzoekingen uitvoeren voor locale adressen door deze upstream servers te gebruiken. Indien uitgeschakeld, reageert AdGuard Home met NXDOMAIN op al dergelijke PTR-verzoeken, uitgezonderd voor apparaten gekend van DHCP, /etc/hosts, enz.",
"use_private_ptr_resolvers_desc": "PTR-, SOA- en NS-verzoeken voor ARPA-domeinen die privé-IP-adressen bevatten oplossen via privé-upstreamservers, DHCP, /etc/hosts, enz. Indien uitgeschakeld, zal AdGuard Home op al dergelijke verzoeken reageren met NXDOMAIN.",
"check_dhcp_servers": "Zoek achter DHCP servers",
"save_config": "Configuratie opslaan",
"enabled_dhcp": "DHCP server inschakelen",
@@ -254,8 +254,8 @@
"response_code": "Reactiecode",
"client_table_header": "Gebruiker",
"empty_response_status": "Leeg",
"show_all_filter_type": "Toon alles",
"show_filtered_type": "Toon gefilterde",
"show_all_filter_type": "Alles weergeven",
"show_filtered_type": "Gefilterde weergeven",
"no_logs_found": "Geen logboeken gevonden",
"refresh_btn": "Verversen",
"previous_btn": "Vorige",
@@ -532,7 +532,7 @@
"blocked_services_global": "Gebruik algemeen geblokkeerde services",
"blocked_service": "Geblokkeerde service",
"block_all": "Blokkeer alles",
"unblock_all": "Deblokkeer alles",
"unblock_all": "Alles deblokkeren",
"encryption_certificate_path": "Certificaat pad",
"encryption_private_key_path": "Privé sleutel pad",
"encryption_certificates_source_path": "Certificaten bestandspad instellen",
@@ -678,7 +678,7 @@
"use_saved_key": "De eerder opgeslagen sleutel gebruiken",
"parental_control": "Ouderlijk toezicht",
"safe_browsing": "Veilig browsen",
"served_from_cache": "{{value}} <i>(geleverd vanuit cache)</i>",
"served_from_cache_label": "Geleverd vanuit cache",
"form_error_password_length": "Wachtwoord moet {{min}} tot {{max}} tekens lang zijn",
"anonymizer_notification": "<0>Opmerking:</0> IP-anonimisering is ingeschakeld. Je kunt het uitschakelen in <1>Algemene instellingen</1>.",
"confirm_dns_cache_clear": "Weet je zeker dat je de DNS-cache wilt wissen?",

View File

@@ -13,14 +13,14 @@
"fallback_dns_desc": "Lista rezerwowych serwerów DNS używanych, gdy nadrzędne serwery DNS nie odpowiadają. Składnia jest taka sama jak w głównym polu powyżej.",
"fallback_dns_placeholder": "Wprowadź jeden rezerwowy serwer DNS w każdym wierszu",
"local_ptr_title": "Prywatne odwrotne serwery DNS",
"local_ptr_desc": "Serwery DNS, których AdGuard Home używa do lokalnych zapytań PTR. Serwery te są używane do rozpoznawania nazw hostów klientów z prywatnymi adresami IP, na przykład „192.168.12.34”, przy użyciu odwrotnego DNS. Jeśli nie jest ustawiona, AdGuard Home używa adresów domyślnych resolwerów DNS systemu operacyjnego, z wyjątkiem adresów samego AdGuard Home.",
"local_ptr_desc": "Serwery DNS używane przez AdGuard Home do prywatnych żądań PTR, SOA i NS. Żądanie jest uważane za prywatne, jeśli prosi o domenę ARPA zawierającą podsieć w prywatnym zakresie adresów IP (np. „192.168.12.34”) i pochodzi od klienta z prywatnym adresem IP. Jeśli nie zostanie ustawione, zostaną użyte domyślne programy rozpoznawania nazw DNS Twojego systemu operacyjnego, z wyjątkiem domowych adresów IP AdGuard.",
"local_ptr_default_resolver": "Domyślnie AdGuard Home używa następujących odwrotnych resolwerów DNS: {{ip}}.",
"local_ptr_no_default_resolver": "AdGuard Home nie mógł określić odpowiednich prywatnych resolwerów DNS dla tego systemu.",
"local_ptr_placeholder": "Wprowadź po jednym adresie IP w każdym wierszu",
"resolve_clients_title": "Włącz odwrotne rozpoznawanie adresów IP klientów",
"resolve_clients_desc": "Odwróć adresy IP klientów na ich nazwy hostów, wysyłając zapytania PTR do odpowiednich programów tłumaczących (prywatne serwery DNS dla klientów lokalnych, serwery nadrzędne dla klientów z publicznymi adresami IP).",
"use_private_ptr_resolvers_title": "Użyj prywatnych odwrotnych resolwerów DNS",
"use_private_ptr_resolvers_desc": "Wykonuj odwrotne wyszukiwania DNS dla adresów obsługiwanych lokalnie przy użyciu tych serwerów nadrzędnych. Po wyłączeniu AdGuard Home odpowiada za pomocą NXDOMAIN na wszystkie takie żądania PTR, z wyjątkiem klientów znanych z DHCP, /etc/hosts i tak dalej.",
"use_private_ptr_resolvers_desc": "Rozwiązuj żądania PTR, SOA i NS dla domen ARPA zawierających prywatne adresy IP za pośrednictwem prywatnych serwerów nadrzędnych, DHCP, /etc/hosts itp. Jeśli ta opcja jest wyłączona, AdGuard Home będzie odpowiadać na wszystkie takie żądania za pomocą NXDOMAIN.",
"check_dhcp_servers": "Sprawdź serwery DHCP",
"save_config": "Zapisz konfigurację",
"enabled_dhcp": "Serwer DHCP włączony",
@@ -425,6 +425,9 @@
"encryption_hostnames": "Nazwy hostów",
"encryption_reset": "Czy na pewno chcesz zresetować ustawienia szyfrowania?",
"encryption_warning": "Ostrzeżenie",
"encryption_plain_dns_enable": "Włącz zwykły DNS",
"encryption_plain_dns_desc": "Zwykły DNS jest domyślnie włączony. Możesz go wyłączyć, aby zmusić wszystkie urządzenia do korzystania z szyfrowanego DNS. Aby to zrobić, musisz włączyć co najmniej jeden szyfrowany protokół DNS",
"encryption_plain_dns_error": "Aby wyłączyć zwykły DNS, włącz co najmniej jeden szyfrowany protokół DNS",
"topline_expiring_certificate": "Twój certyfikat SSL wkrótce wygaśnie. Zaktualizuj <0>Ustawienia szyfrowania</0>.",
"topline_expired_certificate": "Twój certyfikat SSL wygasł. Zaktualizuj <0>Ustawienia szyfrowania</0>.",
"form_error_port_range": "Wpisz numer portu z zakresu 80-65535",
@@ -675,7 +678,7 @@
"use_saved_key": "Użyj wcześniej zapisanego klucza",
"parental_control": "Kontrola rodzicielska",
"safe_browsing": "Bezpieczne przeglądanie",
"served_from_cache": "{{value}} <i>(podawane z pamięci podręcznej)</i>",
"served_from_cache_label": "Podano z pamięci podręcznej",
"form_error_password_length": "Hasło musi zawierać od {{min}} do {{max}} znaków",
"anonymizer_notification": "<0>Uwaga:</0> Anonimizacja IP jest włączona. Możesz ją wyłączyć w <1>Ustawieniach ogólnych</1>.",
"confirm_dns_cache_clear": "Czy na pewno chcesz wyczyścić pamięć podręczną DNS?",

View File

@@ -13,14 +13,14 @@
"fallback_dns_desc": "Lista de servidores DNS Fallback usados quando os servidores DNS primários não estão respondendo. A sintaxe é a mesma dos campos de servidores principais na seção acima.",
"fallback_dns_placeholder": "Insira um servidor DNS fallback por linha",
"local_ptr_title": "Servidores DNS reversos privados",
"local_ptr_desc": "Os servidores DNS que o AdGuard Home usa para consultas PTR locais. Esses servidores são usados para resolver solicitações de PTR para endereços em intervalos de IP privados, por exemplo \"192.168.12.34\", usando DNS reverso. Se não estiver definido, o AdGuard Home usa os endereços dos resolvedores de DNS padrão do seu sistema operacional, exceto os endereços do próprio AdGuard Home.",
"local_ptr_desc": "Os servidores DNS que o AdGuard Home utiliza para consultas privadas de PTR, SOA e NS. A solicitação é considerada privada se solicitar um domínio ARPA contendo uma sub-rede dentro de intervalos de IP privados, por exemplo \"192.168.12.34\", e vier de um cliente com endereço privado. Se não for definido, o AdGuard Home usa os endereços dos resolvedores DNS padrão do seu sistema operacional, exceto os endereços do próprio AdGuard Home.",
"local_ptr_default_resolver": "Por padrão, o AdGuard Home usa os seguintes resolvedores de DNS reverso: {{ip}}.",
"local_ptr_no_default_resolver": "A página inicial do AdGuard não conseguiu determinar resolvedores DNS reversos privados adequados para este sistema.",
"local_ptr_placeholder": "Insira um endereço IP por linha",
"resolve_clients_title": "Ativar resolução reversa de endereços IP de clientes",
"resolve_clients_desc": "Resolva reversamente os endereços IP dos clientes em seus nomes de host, enviando consultas PTR aos resolvedores correspondentes (servidores DNS privados para clientes locais, servidores upstream para clientes com endereços IP públicos).",
"use_private_ptr_resolvers_title": "Usar resolvedores DNS reversos privados",
"use_private_ptr_resolvers_desc": "Execute pesquisas reversas de DNS para endereços servidos localmente usando esses servidores DNS primário. Se desativado, o AdGuard Home responde com NXDOMAIN a todas essas solicitações PTR, exceto para clientes conhecidos de DHCP, /etc/hosts e assim por diante.",
"use_private_ptr_resolvers_desc": "Resolver solicitações PTR, SOA e NS para domínios ARPA contendo endereços privados usando servidores upstream privados, DHCP, /etc/hosts e assim por diante. Se desativado, o AdGuard Home responde a todas essas consultas com NXDOMAIN.",
"check_dhcp_servers": "Verificar por servidores DHCP",
"save_config": "Salvar configuração",
"enabled_dhcp": "Servidor DHCP ativado",
@@ -678,7 +678,7 @@
"use_saved_key": "Use a chave salva anteriormente",
"parental_control": "Controle parental",
"safe_browsing": "Navegação segura",
"served_from_cache": "{{value}} <i>(servido do cache)</i>",
"served_from_cache_label": "Servido a partir do cache",
"form_error_password_length": "A senha deve ter entre {{min}} e {{max}} caracteres",
"anonymizer_notification": "<0>Observação:</0> A anonimização de IP está ativada. Você pode desativá-lo em <1>Configurações gerais</1>.",
"confirm_dns_cache_clear": "Tem certeza de que deseja limpar o cache DNS?",

View File

@@ -13,14 +13,14 @@
"fallback_dns_desc": "Lista de servidores DNS de fallback usados quando os servidores DNS upstream não estão respondendo. A sintaxe é a mesma do campo principal de upstreams acima.",
"fallback_dns_placeholder": "Insira um servidor DNS de fallback por linha",
"local_ptr_title": "Servidores DNS reversos privados",
"local_ptr_desc": "Os servidores DNS que o AdGuard Home usa para consultas PTR locais. Esses servidores são usados para resolver solicitações de PTR para endereços em intervalos de IP privados, por exemplo \"192.168.12.34\", usando DNS reverso. Se não estiver definido, o AdGuard Home usa os endereços dos resolvedores de DNS padrão do seu sistema operacional, exceto os endereços do próprio AdGuard Home.",
"local_ptr_desc": "Os servidores DNS que o AdGuard Home utiliza para consultas privadas de PTR, SOA e NS. A solicitação é considerada privada se solicitar um domínio ARPA contendo uma sub-rede dentro de intervalos de IP privados, por exemplo \"192.168.12.34\", e vier de um cliente com endereço privado. Se não for definido, o AdGuard Home usa os endereços dos resolvedores DNS padrão do seu sistema operacional, exceto os endereços do próprio AdGuard Home.",
"local_ptr_default_resolver": "Por predefinição, o AdGuard Home usa os seguintes resolvedores de DNS reverso: {{ip}}.",
"local_ptr_no_default_resolver": "A página inicial do AdGuard não conseguiu determinar resolvedores DNS reversos privados adequados para este sistema.",
"local_ptr_placeholder": "Insira um endereço IP por linha",
"resolve_clients_title": "Ativar resolução reversa de endereços IP de clientes",
"resolve_clients_desc": "Resolva reversamente os endereços IP dos clientes em seus nomes de host, enviando consultas PTR aos resolvedores correspondentes (servidores DNS privados para clientes locais, servidores upstream para clientes com endereços IP públicos).",
"use_private_ptr_resolvers_title": "Usar resolvedores DNS reversos privados",
"use_private_ptr_resolvers_desc": "Execute pesquisas reversas de DNS para endereços servidos localmente usando esses servidores DNS primário. Se desativado, o AdGuard Home responde com NXDOMAIN a todas essas solicitações PTR, exceto para clientes conhecidos de DHCP, /etc/hosts e assim por diante.",
"use_private_ptr_resolvers_desc": "Resolver solicitações PTR, SOA e NS para domínios ARPA contendo endereços privados usando servidores upstream privados, DHCP, /etc/hosts e assim por diante. Se desativado, o AdGuard Home responde a todas essas consultas com NXDOMAIN.",
"check_dhcp_servers": "Verificar por servidores DHCP",
"save_config": "Guardar definição",
"enabled_dhcp": "Servidor DHCP ativado",
@@ -678,7 +678,7 @@
"use_saved_key": "Use a chave guardada anteriormente",
"parental_control": "Controlo parental",
"safe_browsing": "Navegação segura",
"served_from_cache": "{{value}} <i>(servido do cache)</i>",
"served_from_cache_label": "Servido a partir do cache",
"form_error_password_length": "A palavra-passe deve ter {{min}} a {{max}} caracteres",
"anonymizer_notification": "<0>Observação:</0> A anonimização de IP está ativada. Você pode desativá-la em <1>Definições gerais</1>.",
"confirm_dns_cache_clear": "Tem certeza de que quer limpar a cache DNS?",

View File

@@ -13,14 +13,14 @@
"fallback_dns_desc": "Список резервных DNS-серверов, используемых в тех случаях, когда вышестоящие DNS-серверы недоступны. Синтаксис такой же, как и в поле Upstream DNS-серверы выше.",
"fallback_dns_placeholder": "Введите один резервный DNS-сервер в каждой строке",
"local_ptr_title": "Приватные серверы для обратного DNS",
"local_ptr_desc": "DNS-серверы, которые AdGuard Home использует для локальных PTR-запросов. Эти серверы используются, чтобы получить доменные имена клиентов с приватными IP-адресами, например «192.168.12.34», с помощью обратного DNS. Если список пуст, AdGuard Home использует DNS-серверы по умолчанию вашей ОС.",
"local_ptr_desc": "DNS-серверы, которые AdGuard Home использует для локальных PTR, SOA и NS-запросов. Запрос считается локальным, если он запрашивает информацию об ARPA-домене, подсеть которого в локальном IP-диапазоне (например, «192.168.12.34»), и если при этом запрос пришел от клиента с локальным адресом. Если значение не установлено, AdGuard Home использует адреса DNS-серверы по умолчанию в вашей ОС, за исключением адресов самого AdGuard Home.",
"local_ptr_default_resolver": "По умолчанию AdGuard Home использует следующие обратные DNS-резолверы: {{ip}}.",
"local_ptr_no_default_resolver": "AdGuard Home не смог определить подходящие приватные обратные DNS-резолверы для этой системы.",
"local_ptr_placeholder": "Введите по одному адресу на строчку",
"resolve_clients_title": "Включить запрашивание доменных имён для IP-адресов клиентов",
"resolve_clients_desc": "Определять доменные имена клиентов через PTR-запросы к соответствующим серверам (приватные DNS-серверы для локальных клиентов, upstream-серверы для клиентов с публичным IP-адресом).",
"use_private_ptr_resolvers_title": "Использовать приватные обратные DNS-резолверы",
"use_private_ptr_resolvers_desc": "Посылать обратные DNS-запросы для локально обслуживаемых адресов на указанные серверы. Если отключено, AdGuard Home будет отвечать NXDOMAIN на все подобные PTR-запросы, кроме запросов о клиентах, уже известных по DHCP, /etc/hosts и так далее.",
"use_private_ptr_resolvers_desc": "Посылать PTR, SOA и NS-запросы для ARPA-доменов, содержащих локальные адреса, с помощью указанных upstream-серверов, DHCP, /etc/hosts и так далее. Если отключено, AdGuard Home отвечает NXDOMAIN на все подобные запросы.",
"check_dhcp_servers": "Проверить DHCP-серверы",
"save_config": "Сохранить конфигурацию",
"enabled_dhcp": "DHCP-сервер включён",
@@ -678,7 +678,7 @@
"use_saved_key": "Использовать сохранённый ранее ключ",
"parental_control": "Родительский контроль",
"safe_browsing": "Безопасный интернет",
"served_from_cache": "{{value}} <i>(получено из кеша)</i>",
"served_from_cache_label": "Получено из кеша",
"form_error_password_length": "Пароль должен содержать от {{min}} до {{max}} символов",
"anonymizer_notification": "<0>Внимание:</0> включена анонимизация IP-адресов. Вы можете отключить её в разделе <1>Основные настройки</1>.",
"confirm_dns_cache_clear": "Вы уверены, что хотите очистить кеш DNS?",

View File

@@ -13,20 +13,20 @@
"fallback_dns_desc": "Zoznam záložných serverov DNS, ktoré sa používajú, keď nadradený servery DNS neodpovedajú. Syntax je rovnaká ako v hlavnom poli vyššie.",
"fallback_dns_placeholder": "Zadajte jeden záložný server DNS na riadok",
"local_ptr_title": "Súkromné reverzné DNS servery",
"local_ptr_desc": "DNS servery, ktoré AdGuard Home používa pre miestne PTR dopyty. Tieto servery sa používajú na rozlíšenie názvov hostiteľov klientov so súkromnými adresami IP, napríklad \"192.168.12.34\", pomocou reverzného DNS. Ak nie je nastavené inak, AdGuard Home použije adresy predvolených prekladačov DNS Vášho operačného systému okrem adries samotného AdGuard Home.",
"local_ptr_desc": "Servery DNS, ktoré používa AdGuard Home na súkromné dopyty PTR, SOA a NS. Dopyt sa považuje za súkromný, ak požaduje doménu ARPA obsahujúcu podsieť v rozsahu súkromnej IP adresy (napríklad 192.168.12.34“) a pochádza od klienta so súkromnou IP adresou. Ak nie je nastavené, použijú sa predvolené DNS resolvery Vášho operačného systému, okrem AdGuard Home IP adries.",
"local_ptr_default_resolver": "V predvolenom nastavení používa AdGuard Home nasledujúce reverzné DNS prekladače: {{ip}}.",
"local_ptr_no_default_resolver": "AdGuard Home nemohol určiť vhodné súkromné reverzné DNS prekladače pre tento systém.",
"local_ptr_placeholder": "Na každý riadok zadajte IP adresu jedného servera",
"resolve_clients_title": "Povoliť spätný preklad IP adries klientov",
"resolve_clients_desc": "Reverzne rozlišuje adresy IP klientov na ich názvy hostiteľov odosielaním PTR dopytov príslušným prekladačom (súkromné DNS servery pre miestnych klientov, servery typu upstream pre klientov s verejnými IP adresami).",
"use_private_ptr_resolvers_title": "Použiť súkromné reverzné DNS resolvery",
"use_private_ptr_resolvers_desc": "Realizuje reverzné vyhľadávanie DNS pre lokálne adresy pomocou týchto upstream serverov. Ak je funkcia vypnutá, Adguard Home reaguje s NXDOMAIN na všetky takéto PTR dopyty okrem klientov známych z DHCP, /etc/hosts, a tak ďalej.",
"use_private_ptr_resolvers_desc": "Riešenie dopytov PTR, SOA a NS pre domény ARPA obsahujúce súkromné IP adresy prostredníctvom súkromných upstream serverov, DHCP, /etc/hosts atď. Ak je vypnuté, AdGuard Home bude na všetky takéto dopyty odpovedať pomocou NXDOMAIN.",
"check_dhcp_servers": "Skontrolovať DHCP servery",
"save_config": "Uložiť konfiguráciu",
"enabled_dhcp": "DHCP server zapnutý",
"disabled_dhcp": "DHCP server vypnutý",
"unavailable_dhcp": "DHCP nie je dostupné",
"unavailable_dhcp_desc": "AdGuard Home nemôže vo vašom OS prevádzkovať DHCP server",
"unavailable_dhcp_desc": "AdGuard Home nemôže vo Vašom OS prevádzkovať DHCP server",
"dhcp_title": "DHCP server (experimentálne!)",
"dhcp_description": "Ak Váš smerovač neposkytuje možnosť nastaviť DHCP, môžete použiť vlastný zabudovaný DHCP server AdGuard.",
"dhcp_enable": "Zapnúť DHCP server",
@@ -678,7 +678,7 @@
"use_saved_key": "Použiť predtým uložený kľúč",
"parental_control": "Rodičovská kontrola",
"safe_browsing": "Bezpečné prehliadanie",
"served_from_cache": "{{value}} <i>(prevzatá z cache pamäte)</i>",
"served_from_cache_label": "Prevzaté z cache pamäte",
"form_error_password_length": "Heslo musí mať od {{min}} do {{max}} znakov",
"anonymizer_notification": "<0>Poznámka:</0> Anonymizácia IP je zapnutá. Môžete ju vypnúť vo <1>Všeobecných nastaveniach</1>.",
"confirm_dns_cache_clear": "Naozaj chcete vymazať vyrovnávaciu pamäť DNS?",

View File

@@ -678,7 +678,7 @@
"use_saved_key": "Uporabi prej shranjeni ključ",
"parental_control": "Starševski nadzor",
"safe_browsing": "Varno brskanje",
"served_from_cache": "{{value}} <i>(postreženo iz predpomnilnika)</i>",
"served_from_cache_label": "Dostavljeno iz predpomnilnika",
"form_error_password_length": "Geslo mora vsebovati od {{min}} do {{max}} znakov",
"anonymizer_notification": "<0>Opomba:</0> Anonimizacija IP je omogočena. Onemogočite ga lahko v <1>Splošnih nastavitvah</1>.",
"confirm_dns_cache_clear": "Ali ste prepričani, da želite počistiti predpomnilnik DNS?",

View File

@@ -13,14 +13,14 @@
"fallback_dns_desc": "Yukarı akış DNS sunucuları yanıt vermediğinde kullanılan yedek DNS sunucularının listesi. Söz dizimi yukarıdaki ana üst kaynak alanıyla aynıdır.",
"fallback_dns_placeholder": "Her satıra bir yedek DNS sunucusu girin",
"local_ptr_title": "Özel ters DNS sunucuları",
"local_ptr_desc": "AdGuard Home'un yerel PTR sorguları için kullandığı DNS sunucuları. Bu sunucular, rDNS kullanarak, örneğin \"192.168.12.34\" gibi özel IP aralıklarındaki adresler için PTR isteklerini çözmek için kullanılır. Ayarlanmadığı durumda AdGuard Home, işletim sisteminizin varsayılan DNS çözümleme adreslerini kullanır.",
"local_ptr_desc": "AdGuard Home tarafından özel PTR, SOA ve NS istekleri için kullanılan DNS sunucuları. Bir istek, özel IP aralıkları (\"192.168.12.34\" gibi) içinde bir alt ağ içeren bir ARPA alan adı ister ve özel IP adresine sahip bir istemciden gelirse özel olarak kabul edilir. Ayarlanmadığı durumda AdGuard Home, IP adresleri dışında işletim sisteminizin varsayılan DNS çözümleyicileri kullanılır.",
"local_ptr_default_resolver": "AdGuard Home, varsayılan olarak aşağıdaki ters DNS çözümleyicilerini kullanır: {{ip}}.",
"local_ptr_no_default_resolver": "AdGuard Home, bu sistem için uygun olan özel ters DNS çözümleyicilerini belirleyemedi.",
"local_ptr_placeholder": "Her satıra bir IP adresi girin",
"resolve_clients_title": "İstemcilerin IP adreslerinin ters çözümlenmesini etkinleştir",
"resolve_clients_desc": "Karşılık gelen çözümleyicilere (yerel istemciler için özel DNS sunucuları, genel IP adresleri olan istemciler için üst sunucuları) PTR sorguları göndererek istemcilerin IP adreslerini ana makine adlarının tersine çözün.",
"use_private_ptr_resolvers_title": "Özel ters DNS çözümleyicileri kullan",
"use_private_ptr_resolvers_desc": "Bu üst kaynak sunucularını kullanarak yerel olarak sunulan adresler için ters DNS aramaları yapın. Devre dışı bırakılırsa, AdGuard Home, DHCP, /etc/hosts, vb. tarafından bilinen istemciler dışında bu tür tüm PTR isteklerine NXDOMAIN ile yanıt verir.",
"use_private_ptr_resolvers_desc": "Özel üst kaynak sunucuları, DHCP, /etc/hosts, vb. aracılığıyla özel IP adresleri içeren ARPA alan adları için PTR, SOA ve NS isteklerini çözümleyin. Devre dışı bırakılırsa, AdGuard Home bu tür tüm isteklere NXDOMAIN ile yanıt verir.",
"check_dhcp_servers": "DHCP sunucularını denetle",
"save_config": "Yapılandırmayı kaydet",
"enabled_dhcp": "DHCP sunucusu etkinleştirildi",
@@ -678,7 +678,7 @@
"use_saved_key": "Önceden kaydedilmiş anahtarı kullan",
"parental_control": "Ebeveyn Denetimi",
"safe_browsing": "Güvenli Gezinti",
"served_from_cache": "{{value}} <i>(önbellekten kullanıldı)</i>",
"served_from_cache_label": "Önbellekten kullanıldı",
"form_error_password_length": "Parola {{min}} ila {{max}} karakter uzunluğunda olmalıdır",
"anonymizer_notification": "<0>Not:</0> IP anonimleştirme etkinleştirildi. Bunu <1>Genel ayarlardan</1> devre dışı bırakabilirsiniz.",
"confirm_dns_cache_clear": "DNS önbelleğini temizlemek istediğinizden emin misiniz?",

View File

@@ -68,7 +68,7 @@
"ip": "IP",
"dhcp_table_hostname": "Назва вузла",
"dhcp_table_expires": "Закінчується",
"dhcp_warning": "Якщо ви однаково хочете увімкнути DHCP-сервер, переконайтеся, що у вашій мережі немає інших активних DHCP-серверів. Інакше, це може порушити роботу інтернету на під'єднаних пристроях!",
"dhcp_warning": "Якщо ви однаково хочете увімкнути DHCP-сервер, переконайтеся, що у вашій мережі немає інших активних DHCP-серверів. Інакше, це може порушити роботу інтернету на підʼєднаних пристроях!",
"dhcp_error": "AdGuard Home не зміг визначити, чи є в мережі інший DHCP-сервер",
"dhcp_static_ip_error": "Для використання DHCP-сервера необхідно встановити статичну IP-адресу. Нам не вдалося визначити, чи цей мережевий інтерфейс налаштовано для використання статичної IP-адреси. Встановіть статичну IP-адресу вручну.",
"dhcp_dynamic_ip_found": "Ваша система використовує конфігурацію з динамічною IP-адресою для інтерфейсу <0>{{interfaceName}}</0>. Для використання DHCP-сервера необхідно встановити статичну IP-адресу. Ваша поточна IP-адреса <0>{{ipAddress}}</0>. Ми автоматично встановимо цю IP-адресу як статичну, якщо ви натиснете кнопку «Увімкнути DHCP-сервер».",
@@ -364,7 +364,7 @@
"install_submit_title": "Вітаємо!",
"install_submit_desc": "Процедура налаштування завершена і тепер все готово, аби почати користуватися AdGuard Home.",
"install_devices_router": "Роутер",
"install_devices_router_desc": "Це налаштування буде автоматично охоплювати всі пристрої, що під'єднано до домашнього маршрутизатора. Вам не потрібно буде налаштовувати кожен з них вручну.",
"install_devices_router_desc": "Це налаштування буде автоматично охоплювати всі пристрої, що підʼєднано до домашнього маршрутизатора. Вам не потрібно буде налаштовувати кожен з них вручну.",
"install_devices_address": "DNS-сервер AdGuard Home прослуховує наступні адреси",
"install_devices_router_list_1": "Відкрийте налаштування маршрутизатора. Зазвичай ви можете отримати до нього доступ із браузера за допомогою URL-адреси, наприклад, http://192.168.0.1/ або http://192.168.1.1/. Можливо, треба буде ввести пароль. Якщо ви його не знаєте, часто можна скинути пароль, натиснувши кнопку на самому маршрутизаторі. Для деяких маршрутизаторів потрібна спеціальна програма, яка в такому випадку повинна бути вже встановлена на вашому комп’ютері чи телефоні.",
"install_devices_router_list_2": "Знайдіть налаштування DHCP/DNS. Шукайте літери DNS поруч із полем, в яке можна ввести два або три набори чисел, кожен з яких розбитий на чотири групи від однієї до трьох цифр.",
@@ -448,7 +448,7 @@
"manual_update": "Щоб оновити самостійно, <a>виконайте ці кроки</a>.",
"processing_update": "Зачекайте будь ласка, AdGuard Home оновлюється",
"clients_title": "Постійні клієнти",
"clients_desc": "Налаштуйте пристрої, під'єднані до AdGuard Home",
"clients_desc": "Налаштуйте пристрої, які підʼєднано до AdGuard Home",
"settings_global": "Загальні",
"settings_custom": "Власні",
"table_client": "Клієнт",
@@ -678,7 +678,7 @@
"use_saved_key": "Використати раніше збережений ключ",
"parental_control": "Батьківський контроль",
"safe_browsing": "Безпечний перегляд",
"served_from_cache": "{{value}} <i>(отримано з кешу)</i>",
"served_from_cache_label": "Отримано з кешу",
"form_error_password_length": "Пароль має містити від {{min}} до {{max}} символів",
"anonymizer_notification": "<0>Примітка:</0> IP-анонімізацію ввімкнено. Ви можете вимкнути його в <1>Загальні налаштування</1> .",
"confirm_dns_cache_clear": "Ви впевнені, що бажаєте очистити кеш DNS?",

View File

@@ -13,14 +13,14 @@
"fallback_dns_desc": "当上游 DNS 服务器没有响应时使用的后备 DNS 服务器列表。语法与上面的「主要上游」字段相同。",
"fallback_dns_placeholder": "每行输入一个后备 DNS 服务器",
"local_ptr_title": "私人反向 DNS 服务器",
"local_ptr_desc": "AdGuard Home 用于本地 PTR 查询的 DNS 服务器。这些服务器将使用反向 DNS 解析具有私人 IP 地址的客户机的主机名,比如 \"192.168.12.34\"。如果没有设置,除非是 AdGuard Home 里设置的地址AdGuard Home 都将自动使用您的操作系统的默认 DNS 解析器。",
"local_ptr_desc": "AdGuard Home 用于私人 PTR、SOA 和 NS 请求的 DNS 服务器。如果请求的 ARPA 域名包含私有 IP 范围内的子网(如 \"192.168.12.34\"),且请求来自具有私有 IP 地址的客户端,该请求被视为私有请求。如果未设置,将使用操作系统的默认 DNS 解析器AdGuard Home IP 地址除外。",
"local_ptr_default_resolver": "AdGuard Home 默认使用下列反向 DNS 解析器: {{ip}}",
"local_ptr_no_default_resolver": "AdGuard Home 无法为这个系统确定合适的私人反向 DNS 解析器。",
"local_ptr_placeholder": "每行输入一个 IP 地址",
"resolve_clients_title": "启用客户端的 IP 地址的反向解析",
"resolve_clients_desc": "通过发送 PTR 查询到对应的解析器 (本地客户端的私人 DNS 服务器,公共 IP 客户端的上游服务器) 将 IP 地址反向解析成其客户端主机名。",
"use_private_ptr_resolvers_title": "使用私人反向 DNS 解析器",
"use_private_ptr_resolvers_desc": "使用这些上游服务器对本地服务的地址执行反向 DNS 查找。 如果禁用,AdGuard Home 以 NXDOMAIN 应所有此类 PTR 请求,从 DHCP、/etc/hosts 等获知的客户端除外。",
"use_private_ptr_resolvers_desc": "使用私有上游服务器、DHCP、/etc/hosts 等解决包含私有 IP 地址的 ARPA 域名的 PTR、SOA 和 NS 请求。如果禁用AdGuard Home 以 NXDOMAIN 应所有此类请求。",
"check_dhcp_servers": "检查 DHCP 服务器",
"save_config": "保存配置",
"enabled_dhcp": "DHCP 服务器已启用",
@@ -678,7 +678,7 @@
"use_saved_key": "使用之前保存的密钥",
"parental_control": "家长控制",
"safe_browsing": "安全浏览",
"served_from_cache": "{{value}}<i>(由缓存提供)</i>",
"served_from_cache_label": "从缓存中",
"form_error_password_length": "密码长度必须为 {{min}} 到 {{max}} 个字符",
"anonymizer_notification": "<0>注意:</0> IP 匿名化已启用。您可以在<1>常规设置</1>中禁用它。",
"confirm_dns_cache_clear": "您确定要清除 DNS 缓存吗?",

View File

@@ -1,6 +1,7 @@
{
"client_settings": "用戶端設定",
"example_upstream_reserved": "您可以<0>指定網域</0>使用特定 DNS 查詢",
"example_multiple_upstreams_reserved": "多個上游 <0>for 特定網域</0>;",
"example_upstream_comment": "您可以指定註解",
"upstream_parallel": "使用平行查詢,同時查詢所有上游伺服器來加速解析結果",
"parallel_requests": "平行處理",
@@ -8,6 +9,9 @@
"load_balancing_desc": "一次只查詢一個伺服器。AdGuard Home 會使用加權隨機取樣來選擇使用的查詢結果,以確保速度最快的伺服器能被充分運用。",
"bootstrap_dns": "引導Boostrap DNS 伺服器",
"bootstrap_dns_desc": "Bootstrap DNS 伺服器用於解析您所設定的上游 DoH/DoT 解析器的 IP 地址",
"fallback_dns_title": "備用 DNS 伺服器",
"fallback_dns_desc": "備用 DNS 伺服器列表:於主要 DNS 伺服器沒有回應時使用。語法與主要 DNS 伺服器設定欄位相同。",
"fallback_dns_placeholder": "每行輸入一個備用 DNS 伺服器",
"local_ptr_title": "私人 DNS 伺服器",
"local_ptr_desc": "AdGuard Home 用於區域 PTR 查詢的 DNS 伺服器。這些伺服器將用於解析具有私人 IP 位址的用戶端的主機名稱,例如 \"192.168.12.34\",使用 rDNS。如果沒有設定AdGuard Home 將自動使用您的系統預設 DNS 解析。",
"local_ptr_default_resolver": "AdGuard Home 預設使用以下作為 DNS 反解器:{{ip}}",
@@ -37,17 +41,19 @@
"dhcp_ipv6_settings": "DHCP IPv6 設定",
"form_error_required": "必要欄位",
"form_error_ip4_format": "無效的 IPv4 格式",
"form_error_ip6_format": "無效的 IPv6 格式",
"form_error_ip4_gateway_format": "閘道的 IPv4 位址無效",
"form_error_ip_format": "無效的 IP 位址",
"form_error_ip6_format": "無效的 IPv6 格式",
"form_error_ip_format": "無效的 IP 格式",
"form_error_mac_format": "無效的 「MAC 位址」格式",
"form_error_client_id_format": "無效的「客戶端 ID」格式",
"form_error_server_name": "無效伺服器名稱",
"form_error_subnet": "子網路 \"{{cidr}}\" 不包含 IP 位址 \"{{ip}}\"",
"form_error_positive": "數值必須大於 0",
"form_error_gateway_ip": "租約不能使用閘道器的 IP 位址",
"out_of_range_error": "必須介於 \"{{start}}\" - \"{{end}}\" 範圍之外",
"lower_range_start_error": "必須小於起始值",
"greater_range_start_error": "必須大於起始值",
"subnet_error": "地址必須在同一個子網路中",
"gateway_or_subnet_invalid": "無效子網路",
"dhcp_form_gateway_input": "閘道 IP 位址",
"dhcp_form_subnet_input": "子網路遮罩",
@@ -68,7 +74,9 @@
"dhcp_dynamic_ip_found": "您的網路介面 <0>{{interfaceName}}</0> 正在使用動態 IP要使用 DHCP 伺服器必須指定靜態 IP 給 AdGuard。\n目前您的 IP 位址 <0>{{ipAddress}}</0>,啟用 DHCP 後此 IP 將自動設定為靜態 IP 位址。",
"dhcp_lease_added": "靜態租用 \"{{key}}\" 已新增成功",
"dhcp_lease_deleted": "靜態租用 \"{{key}}\" 已刪除成功",
"dhcp_lease_updated": "靜態租約 \"{{key}}\" 已成功更新",
"dhcp_new_static_lease": "新增靜態租用",
"dhcp_edit_static_lease": "編輯靜態租約",
"dhcp_static_leases_not_found": "找不到 DHCP 靜態租用",
"dhcp_add_static_lease": "新增靜態租用",
"dhcp_reset_leases": "重置所有 DHCP 租約",
@@ -112,7 +120,8 @@
"stats_malware_phishing": "已封鎖惡意軟體/網路釣魚",
"stats_adult": "已封鎖成人網站",
"stats_query_domain": "熱門查詢網域排行",
"for_last_24_hours": "過去 24 小時",
"for_last_hours": "過去 {{count}} 小時",
"for_last_hours_plural": "在過去 {{count}} 小時裡",
"for_last_days": "最近 {{count}} 天內",
"for_last_days_plural": "最近 {{count}} 天內",
"stats_disabled": "已禁用統計資料。您可以從<0>設定頁面</0>打開它。",
@@ -123,15 +132,20 @@
"top_clients": "熱門用戶端排行",
"no_clients_found": "找不到用戶端",
"general_statistics": "一般統計資料",
"top_upstreams": "熱門上游伺服器",
"no_upstreams_data_found": "找不到上游數據",
"number_of_dns_query_days": "過去 {{count}} 天內 DNS 查詢總數",
"number_of_dns_query_days_plural": "過去 {{count}} 天內 DNS 查詢總數",
"number_of_dns_query_24_hours": "過去 24小時內 DNS 查詢數",
"number_of_dns_query_hours": "過去 {{count}} 小時處理的 DNS 查詢數",
"number_of_dns_query_hours_plural": "過去 {{count}} 小時處理的 DNS 查詢數量",
"number_of_dns_query_blocked_24_hours": "已被廣告過濾器與主機黑名單封鎖 DNS 查詢總數",
"number_of_dns_query_blocked_24_hours_by_sec": "已被 AdGuard 瀏覽安全模組封鎖的 DNS 查詢總數",
"number_of_dns_query_blocked_24_hours_adult": "已封鎖成人網站總數",
"enforced_save_search": "強制使用安全搜尋",
"number_of_dns_query_to_safe_search": "已強制使用安全搜尋總數",
"average_processing_time": "平均的處理時間",
"average_upstream_response_time": "平均上游伺服器回應時間",
"response_time": "回應時間",
"average_processing_time_hint": "處理 DNS 請求的平均時間(毫秒)",
"block_domain_use_filters_and_hosts": "使用過濾器與 hosts 檔案阻擋網域查詢",
"filters_block_toggle_hint": "您可在<a>過濾器</a>設定中設定封鎖規則。",
@@ -156,6 +170,7 @@
"upstream_dns_configured_in_file": "設定在 {{path}}",
"test_upstream_btn": "測試上游 DNS",
"upstreams": "上游",
"upstream": "上游伺服器",
"apply_btn": "套用",
"disabled_filtering_toast": "已停用過濾",
"enabled_filtering_toast": "已啟用過濾",
@@ -164,6 +179,7 @@
"disabled_parental_toast": "已停用家長監護",
"enabled_parental_toast": "已啟用家長監護",
"disabled_safe_search_toast": "已停用安全搜尋",
"enabled_save_search_toast": "已啟用安全搜尋",
"updated_save_search_toast": "已更新安全搜尋設定",
"enabled_table_header": "啟用",
"name_table_header": "名稱",
@@ -206,24 +222,29 @@
"example_comment_hash": "# Also a comment",
"example_regex_meaning": "使用正規表示式Regular Expression來阻止對應的網域查詢",
"example_upstream_regular": "一般 DNS透過 UDP",
"example_upstream_regular_port": "一般 DNS透過 UDP連接埠",
"example_upstream_udp": "一般 DNS透過 UDP主機名稱",
"example_upstream_dot": "<0>DNS-over-TLS</0>(流量加密)",
"example_upstream_doh": "<0>DNS-over-HTTPS</0>(流量加密)",
"example_upstream_doh3": "使 DNS-over-HTTPS 強制使用 <0>HTTP/3</0> ,並禁止使用後備 HTTP/2 或更低版本;",
"example_upstream_doq": "加密 <0>DNS-over-QUIC</0>",
"example_upstream_sdns": "您可以使透過 <0>DNS Stamps</0> 來解析 <1>DNSCrypt</1> 或 <2>DNS-over-HTTPS</2>",
"example_upstream_tcp": "一般 DNS透過 TCP",
"example_upstream_regular_port": "一般 DNS透過 UDP連接埠",
"example_upstream_udp": "一般 DNS透過 UDP主機名稱",
"example_upstream_tcp_port": "一般 DNS透過 TCP連接埠",
"example_upstream_tcp_hostname": "一般 DNS透過 TCP主機名稱",
"all_lists_up_to_date_toast": "所有清單已更新至最新",
"updated_upstream_dns_toast": "已更新上游 DNS 伺服器",
"dns_test_ok_toast": "設定中的 DNS 上游運作正常",
"dns_test_not_ok_toast": "DNS 設定中的 \"{{key}}\" 出現錯誤,請確認是否正確輸入",
"dns_test_parsing_error_toast": "在 {{section}} 部分中:第 {{line}} 行:無法使用,請檢查您是否有正確地填寫",
"dns_test_warning_toast": "上游伺服器 \"{{key}}\" 沒有回應測試請求,可能無法正常運作",
"unblock": "解除封鎖",
"block": "封鎖",
"disallow_this_client": "不允許此用戶端",
"allow_this_client": "允許此用戶端",
"block_for_this_client_only": "僅封鎖此用戶端",
"unblock_for_this_client_only": "僅解除封鎖此用戶端",
"add_persistent_client": "加入到用戶端",
"time_table_header": "時間",
"date": "日期",
"domain_name_table_header": "域名",
@@ -270,6 +291,9 @@
"custom_ip": "自訂 IP 位址",
"blocking_ipv4": "封鎖 IPv4",
"blocking_ipv6": "封鎖 IPv6",
"blocked_response_ttl": "阻塞響應 TTL",
"blocked_response_ttl_desc": "指定客戶端應快取過濾回應的秒數",
"form_enter_blocked_response_ttl": "輸入已封鎖的回應 TTL",
"dnscrypt": "DNSCrypt",
"dns_over_https": "DNS-over-HTTPS",
"dns_over_tls": "DNS-over-TLS",
@@ -288,6 +312,16 @@
"edns_use_custom_ip": "使用自訂 EDNS IP",
"edns_use_custom_ip_desc": "允許使用自訂 EDNS IP",
"rate_limit_desc": "限制單一裝置每秒發出的查詢次數(設定為 0 即表示無限制)",
"rate_limit_subnet_len_ipv4": "IPv4 位址的子網路前綴長度",
"rate_limit_subnet_len_ipv4_desc": "用於速率限制的 IPv4 位址的子網路前綴長度。 預設為 24",
"rate_limit_subnet_len_ipv4_error": "IPv4 子網路前綴長度應介於 0 到 32 之間",
"rate_limit_subnet_len_ipv6": "IPv6 位址的子網路前綴長度",
"rate_limit_subnet_len_ipv6_desc": "用於速率限制的 IPv6 位址的子網路前綴長度。 預設為 56",
"rate_limit_subnet_len_ipv6_error": "IPv6 子網路前綴長度應介於 0 到 128 之間",
"form_enter_rate_limit_subnet_len": "輸入速率限制的子網路前綴長度",
"rate_limit_whitelist": "速率限制白名單",
"rate_limit_whitelist_desc": "排除在速率限制之外的 IP 位址",
"rate_limit_whitelist_placeholder": "每行輸入一個 IP 地址",
"blocking_ipv4_desc": "回覆指定 IPv4 位址給被封鎖的網域的 A 紀錄查詢",
"blocking_ipv6_desc": "回覆指定 IPv6 位址給被封鎖的網域的 AAAA 紀錄查詢",
"blocking_mode_default": "預設:被 Adblock 規則封鎖時回應零值的 IP 位址A 紀錄回應 0.0.0.0 AAAA 紀錄回應 ::);被 /etc/hosts 規則封鎖時回應規則中指定 IP 位址",
@@ -391,6 +425,9 @@
"encryption_hostnames": "主機名稱",
"encryption_reset": "您確定要重設加密設定嗎?",
"encryption_warning": "警告",
"encryption_plain_dns_enable": "啟用一般 DNS",
"encryption_plain_dns_desc": "預設情況下已啟用一般 DNS。您可以將其停用以強制所有裝置使用加密 DNS。要執行此操作您必須啟用至少一個加密的 DNS 協定。",
"encryption_plain_dns_error": "若要停用一般 DNS請啟用至少一個加密的 DNS 協定",
"topline_expiring_certificate": "您的 SSL 憑證即將到期。請前往<0>加密設定</0>更新。",
"topline_expired_certificate": "您的 SSL 憑證已到期。請前往<0>加密設定</0>更新。",
"form_error_port_range": "輸入範圍 80-65535 中的值",
@@ -430,6 +467,7 @@
"form_add_id": "新增識別碼",
"form_client_name": "輸入用戶端名稱",
"name": "名稱",
"client_name": "客戶端 {{id}}",
"client_global_settings": "使用全域設定",
"client_deleted": "已刪除「{{key}}」",
"client_added": "已新增「{{key}}」",
@@ -451,6 +489,7 @@
"updates_checked": "檢查更新成功",
"updates_version_equal": "AdGuard Home 是最新的版本",
"check_updates_now": "立即檢查更新",
"version_request_error": "更新檢查失敗。請檢查您的網絡連線。",
"dns_privacy": "DNS 隱私",
"setup_dns_privacy_1": "<0>DNS-over-TLS</0>使用 <1>{{address}}</1>。",
"setup_dns_privacy_2": "<0>DNS-over-HTTPS</0>使用 <1>{{address}}</1>。",
@@ -471,6 +510,7 @@
"setup_dns_notice": "要使用 <1>DNS-over-HTTPS</1> 或 <1>DNS-over-TLS</1>,您必須先在 AdGuard Home 完成 <0>加密設定</0>。",
"rewrite_added": "「{{key}}」的 DNS 覆寫新增成功",
"rewrite_deleted": "「{{key}}」的 DNS 覆寫刪除成功",
"rewrite_updated": "已更新 DNS 覆寫",
"rewrite_add": "新增 DNS 覆寫",
"rewrite_edit": "編輯 DNS 覆寫",
"rewrite_not_found": "找不到 DNS 覆寫",
@@ -522,6 +562,8 @@
"statistics_enable": "啟用統計數據",
"ignore_domains": "已忽略網域(每行一個)",
"ignore_domains_title": "已忽略網域",
"ignore_domains_desc_stats": "符合這些規則的查詢不會被計入統計資料中",
"ignore_domains_desc_query": "符合這些規則的查詢不會被寫入查詢記錄中",
"interval_hours": "{{count}} 小時",
"interval_hours_plural": "{{count}} 小時",
"filters_configuration": "過濾器設定",
@@ -631,10 +673,19 @@
"click_to_view_queries": "按一下以檢視查詢結果",
"port_53_faq_link": "連接埠 53 經常被「DNSStubListener」或「systemd-resolved」服務佔用。請閱讀下列有關解決<0>這個問題</0>的說明",
"adg_will_drop_dns_queries": "AdGuard Home 將停止回應此用戶端的所有 DNS 查詢。",
"filter_allowlist": "警告:此操作同時會將規則 \"{{disallowed_rule}}\" 從允許的客戶端清單中排除。",
"last_rule_in_allowlist": "無法停用此客戶端,因為排除規則「{{disallowed_rule}}」會導致「允許的用戶端」清單停用。",
"use_saved_key": "使用先前儲存的鍵",
"parental_control": "家長監護",
"safe_browsing": "安全瀏覽",
"served_from_cache": "{{value}} <i>(由快取回應)</i>",
"served_from_cache_label": "由快取回應",
"form_error_password_length": "密碼必須至少 {{value}} 個字元長度",
"anonymizer_notification": "<0>注意</0>: 已啟用 IP 去識別化。您可以在<1>一般設定</1>中停用它。",
"confirm_dns_cache_clear": "您確定要清除 DNS 快取嗎?",
"cache_cleared": "DNS 快取成功清除",
"clear_cache": "清除快取",
"make_static": "新增為靜態",
"theme_auto_desc": "自動(根據裝置調整)",
"theme_dark_desc": "深色主題",
"theme_light_desc": "淺色主題",
"disable_for_seconds": "{{count}} 秒",
@@ -649,11 +700,48 @@
"disable_notify_for_minutes": "暫停防護 {{count}} 分鐘",
"disable_notify_for_minutes_plural": "暫停防護 {{count}} 分鐘",
"disable_notify_for_hours": "暫停防護 {{count}} 小時",
"disable_notify_for_hours_plural": "停用保護 {{count}} 小時",
"disable_notify_until_tomorrow": "停用保護直至明天",
"enable_protection_timer": "保護功能將在 {{time}} 啟用",
"custom_retention_input": "輸入保存時長(單位:小時)",
"custom_rotation_input": "請輸入輪替週期(單位:小時)",
"protection_section_label": "保護",
"log_and_stats_section_label": "查詢日誌與統計資料",
"ignore_query_log": "在查詢日誌中忽略此客戶端",
"ignore_statistics": "在統計資料中忽略此客戶端",
"schedule_services": "暫停服務封鎖",
"schedule_services_desc": "設定服務封鎖過濾器的暫停排程",
"schedule_services_desc_client": "針對此用戶端,設定服務阻擋的暫停排程",
"schedule_desc": "設定已封鎖服務的閒置時段",
"schedule_invalid_select": "開始時間必須在結束時間之前",
"schedule_select_days": "選擇天數",
"schedule_timezone": "選擇時區",
"schedule_current_timezone": "目前時區:{{value}}",
"schedule_time_all_day": "全天",
"schedule_modal_description": "這個排程將會取代同一星期中所有現有的排程。每一天只能有一個閒置時段。",
"schedule_modal_time_off": "沒有封鎖服務:",
"schedule_new": "新排程",
"schedule_edit": "編輯排程",
"schedule_save": "儲存排程",
"schedule_add": "新增排程",
"schedule_remove": "移除排程",
"schedule_from": "從",
"schedule_to": "至",
"sunday": "星期日",
"monday": "星期一",
"tuesday": "星期二",
"wednesday": "星期三",
"thursday": "星期四",
"friday": "星期五",
"saturday": "星期六",
"sunday_short": "週日",
"monday_short": "週一",
"tuesday_short": "週二",
"wednesday_short": "週三",
"thursday_short": "週四",
"friday_short": "週五",
"saturday_short": "週六"
"saturday_short": "週六",
"upstream_dns_cache_configuration": "上游 DNS 快取設定",
"enable_upstream_dns_cache": "為此客戶端的自訂上游設定啟用 DNS 快取",
"dns_cache_size": "DNS 快取大小bytes"
}

View File

@@ -13,14 +13,14 @@
"fallback_dns_desc": "當上游 DNS 伺服器未回覆時被使用的應變 DNS 伺服器之清單。此語法與在上面主要上游欄位中的相同。",
"fallback_dns_placeholder": "每行輸入一個應變 DNS 伺服器",
"local_ptr_title": "私人反向的 DNS 伺服器",
"local_ptr_desc": "AdGuard Home 用於區域指標PTR查詢之 DNS 伺服器。這些伺服器被用於解析有關在私人 IP 範圍的位址之區域指標查詢,例如\"192.168.12.34\",使用反向的 DNS。如果未設定,AdGuard Home 使用您的作業系統預設 DNS 解析器位址。",
"local_ptr_desc": "AdGuard Home 使用的 DNS 伺服器用於私人 PTR、SOA 和 NS 請求。如果請求要求包含私人 IP 範圍內的子網域的 ARPA 網域(例如 \"192.168.12.34\"),並來自具有私人 IP 位址的用戶端,該請求被視為私人。如果未設定,使用您的作業系統預設 DNS 解析器,但不包括 AdGuard Home 的 IP 位址。",
"local_ptr_default_resolver": "預設下AdGuard Home 使用以下反向的 DNS 解析器:{{ip}}。",
"local_ptr_no_default_resolver": "AdGuard Home 無法為此系統決定合適的私人反向的 DNS 解析器。",
"local_ptr_placeholder": "每行輸入一個 IP 位址",
"resolve_clients_title": "啟用用戶端的 IP 位址之反向的解析",
"resolve_clients_desc": "透過傳送指標PTR查詢到對應的解析器私人 DNS 伺服器供區域的用戶端,上游的伺服器供有公共 IP 位址的用戶端),反向地解析用戶端的 IP 位址變為它們的主機名稱。",
"use_private_ptr_resolvers_title": "使用私人反向的 DNS 解析器",
"use_private_ptr_resolvers_desc": "對於正使用這些上游伺服器之區域服務的位址執行反向的 DNS 查找。如果被禁用,除已知來自 DHCP、/etc/hosts 等等的用戶端之外AdGuard Home 對所有此類的區域指標PTR請求以 NXDOMAIN 回。",
"use_private_ptr_resolvers_desc": "使用私人上游伺服器、DHCP、/etc/hosts 等方式解析包含私人 IP 位址的 ARPA 網域的 PTR、SOA 和 NS 請求。如果禁用AdGuard Home 對所有此類請求以 NXDOMAIN 回。",
"check_dhcp_servers": "檢查動態主機設定協定DHCP伺服器",
"save_config": "儲存配置",
"enabled_dhcp": "動態主機設定協定DHCP伺服器被啟用",
@@ -467,7 +467,7 @@
"form_add_id": "新增識別碼",
"form_client_name": "輸入用戶端名稱",
"name": "名稱",
"client_name": "戶端 {{id}}",
"client_name": "戶端 {{id}}",
"client_global_settings": "使用全域的設定",
"client_deleted": "用戶端 \"{{key}}\" 被成功地刪除",
"client_added": "用戶端 \"{{key}}\" 被成功地加入",
@@ -678,7 +678,7 @@
"use_saved_key": "使用該先前已儲存的金鑰",
"parental_control": "家長控制",
"safe_browsing": "安全瀏覽",
"served_from_cache": "{{value}} <i>(由快取提供)</i>",
"served_from_cache_label": "從快取中",
"form_error_password_length": "密碼長度必須為 {{min}} 到 {{max}} 個字符",
"anonymizer_notification": "<0>注意:</0>IP 匿名化被啟用。您可在<1>一般設定</1>中禁用它。",
"confirm_dns_cache_clear": "您確定您想要清除 DNS 快取嗎?",

View File

@@ -55,6 +55,12 @@ const Dashboard = ({
return t('stats_disabled_short');
}
const msIn7Days = 604800000;
if (stats.timeUnits === TIME_UNITS.HOURS && stats.interval === msIn7Days) {
return t('for_last_days', { count: msToDays(stats.interval) });
}
return stats.timeUnits === TIME_UNITS.HOURS
? t('for_last_hours', { count: msToHours(stats.interval) })
: t('for_last_days', { count: msToDays(stats.interval) });

View File

@@ -38,9 +38,6 @@ const ResponseCell = ({
const statusLabel = t(isBlockedByResponse ? 'blocked_by_cname_or_ip' : FILTERED_STATUS_TO_META_MAP[reason]?.LABEL || reason);
const boldStatusLabel = <span className="font-weight-bold">{statusLabel}</span>;
const upstreamString = cached
? t('served_from_cache', { value: upstream, i: <i /> })
: upstream;
const renderResponses = (responseArr) => {
if (!responseArr || responseArr.length === 0) {
@@ -58,7 +55,16 @@ const ResponseCell = ({
const COMMON_CONTENT = {
encryption_status: boldStatusLabel,
install_settings_dns: upstreamString,
install_settings_dns: upstream,
...(cached
&& {
served_from_cache_label: (
<svg className="icons icon--20 icon--green mb-1">
<use xlinkHref="#check" />
</svg>
),
}
),
elapsed: formattedElapsedMs,
response_code: status,
...(service_name && services.allServices

View File

@@ -118,9 +118,6 @@ const Row = memo(({
const blockingForClientKey = isFiltered ? 'unblock_for_this_client_only' : 'block_for_this_client_only';
const clientNameBlockingFor = getBlockingClientName(clients, client);
const upstreamString = cached
? t('served_from_cache', { value: upstream, i: <i /> })
: upstream;
const onBlockingForClientClick = () => {
dispatch(toggleBlockingForClient(buttonType, domain, clientNameBlockingFor));
@@ -192,7 +189,16 @@ const Row = memo(({
className="link--green">{sourceData.name}
</a>,
response_details: 'title',
install_settings_dns: upstreamString,
install_settings_dns: upstream,
...(cached
&& {
served_from_cache_label: (
<svg className="icons icon--20 icon--green">
<use xlinkHref="#check" />
</svg>
),
}
),
elapsed: formattedElapsedMs,
...(rules.length > 0
&& { rule_label: getRulesToFilterList(rules, filters, whitelistFilters) }

View File

@@ -62,7 +62,7 @@ class LogsConfig extends Component {
interval,
customInterval,
anonymize_client_ip,
ignored: ignored.join('\n'),
ignored: ignored?.join('\n'),
}}
onSubmit={this.handleFormSubmit}
processing={processing}

View File

@@ -245,6 +245,10 @@ const Icons = () => (
<path fillRule="evenodd" clipRule="evenodd" d="M12 13.5C11.1716 13.5 10.5 12.8284 10.5 12C10.5 11.1716 11.1716 10.5 12 10.5C12.8284 10.5 13.5 11.1716 13.5 12C13.5 12.8284 12.8284 13.5 12 13.5Z" fill="currentColor" />
<path fillRule="evenodd" clipRule="evenodd" d="M12 20C11.1716 20 10.5 19.3284 10.5 18.5C10.5 17.6716 11.1716 17 12 17C12.8284 17 13.5 17.6716 13.5 18.5C13.5 19.3284 12.8284 20 12 20Z" fill="currentColor" />
</symbol>
<symbol id="check" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M5 11.7665L10.5878 17L19 8" />
</symbol>
</svg>
);

View File

@@ -142,6 +142,12 @@ export default {
"homepage": "https://github.com/AdguardTeam/AdGuardSDNSFilter",
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_1.txt"
},
"adguard_popup_filter": {
"name": "AdGuard DNS Popup Hosts filter",
"categoryId": "general",
"homepage": "https://github.com/AdguardTeam/AdGuardSDNSFilter",
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_59.txt"
},
"awavenue_ads_rule": {
"name": "AWAvenue Ads Rule",
"categoryId": "general",
@@ -190,6 +196,12 @@ export default {
"homepage": "https://github.com/hagezi/dns-blocklists#piracy",
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_46.txt"
},
"hagezi_badware_hoster_blocklist": {
"name": "HaGeZi's Badware Hoster Blocklist",
"categoryId": "security",
"homepage": "https://github.com/hagezi/dns-blocklists",
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_55.txt"
},
"hagezi_dyndns_blocklist": {
"name": "HaGeZi's DynDNS Blocklist",
"categoryId": "security",
@@ -226,6 +238,12 @@ export default {
"homepage": "https://github.com/hagezi/dns-blocklists",
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_51.txt"
},
"hagezi_the_worlds_most_abused_tlds": {
"name": "HaGeZi's The World's Most Abused TLDs",
"categoryId": "security",
"homepage": "https://github.com/hagezi/dns-blocklists",
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_56.txt"
},
"hagezi_threat_intelligence_feeds": {
"name": "HaGeZi's Threat Intelligence Feeds",
"categoryId": "security",
@@ -286,6 +304,12 @@ export default {
"homepage": "https://github.com/durablenapkin/scamblocklist",
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_10.txt"
},
"shadowwhisperers_dating_list": {
"name": "ShadowWhisperer's Dating List",
"categoryId": "other",
"homepage": "https://github.com/ShadowWhisperer/BlockLists",
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_57.txt"
},
"shadowwhisperers_malware_list": {
"name": "ShadowWhisperer's Malware List",
"categoryId": "security",

View File

@@ -1,5 +1,5 @@
{
"timeUpdated": "2024-01-22T00:10:10.554Z",
"timeUpdated": "2024-03-01T00:10:14.031Z",
"categories": {
"0": "audio_video_player",
"1": "comments",
@@ -2732,6 +2732,13 @@
"url": "https://www.microsoft.com/",
"companyId": "microsoft"
},
"assemblyexchange": {
"name": "Assembly Exchange",
"categoryId": 4,
"url": "https://www.medialab.la/",
"companyId": "medialab",
"source": "AdGuard"
},
"astronomer": {
"name": "Astronomer",
"categoryId": 6,
@@ -20831,6 +20838,7 @@
"asambeauty.com": "asambeauty.com",
"ask.com": "ask.com",
"aspnetcdn.com": "aspnetcdn",
"ads.assemblyexchange.com": "assemblyexchange",
"cdn.astronomer.io": "astronomer",
"ati-host.net": "at_internet",
"aticdn.net": "at_internet",

50
go.mod
View File

@@ -1,13 +1,14 @@
module github.com/AdguardTeam/AdGuardHome
go 1.21.7
go 1.22.3
require (
github.com/AdguardTeam/dnsproxy v0.65.0
github.com/AdguardTeam/golibs v0.20.1
// TODO(a.garipov): Use a tagged version once released.
github.com/AdguardTeam/dnsproxy v0.71.2-0.20240524084138-ee010801be05
github.com/AdguardTeam/golibs v0.23.2
github.com/AdguardTeam/urlfilter v0.18.0
github.com/NYTimes/gziphandler v1.1.1
github.com/ameshkov/dnscrypt/v2 v2.2.7
github.com/ameshkov/dnscrypt/v2 v2.3.0
github.com/bluele/gcache v0.0.2
github.com/c2h5oh/datasize v0.0.0-20231215233829-aa82cc1e6500
github.com/digineo/go-ipset/v2 v2.2.1
@@ -18,7 +19,7 @@ require (
github.com/google/gopacket v1.1.19
github.com/google/renameio/v2 v2.0.0
github.com/google/uuid v1.6.0
github.com/insomniacslk/dhcp v0.0.0-20240204152450-ca2dc33955c1
github.com/insomniacslk/dhcp v0.0.0-20240419123447-f1cffa2c0c49
github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86
github.com/kardianos/service v1.2.2
github.com/mdlayher/ethernet v0.0.0-20220221185849-529eae5b6118
@@ -27,15 +28,15 @@ require (
// TODO(a.garipov): This package is deprecated; find a new one or use our
// own code for that. Perhaps, use gopacket.
github.com/mdlayher/raw v0.1.0
github.com/miekg/dns v1.1.58
github.com/quic-go/quic-go v0.41.0
github.com/stretchr/testify v1.8.4
github.com/ti-mo/netfilter v0.5.1
go.etcd.io/bbolt v1.3.8
golang.org/x/crypto v0.19.0
golang.org/x/exp v0.0.0-20240205201215-2c58cdc269a3
golang.org/x/net v0.21.0
golang.org/x/sys v0.17.0
github.com/miekg/dns v1.1.59
github.com/quic-go/quic-go v0.43.1
github.com/stretchr/testify v1.9.0
github.com/ti-mo/netfilter v0.5.2
go.etcd.io/bbolt v1.3.10
golang.org/x/crypto v0.23.0
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842
golang.org/x/net v0.25.0
golang.org/x/sys v0.20.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1
gopkg.in/yaml.v3 v3.0.1
howett.net/plist v1.0.1
@@ -47,20 +48,21 @@ require (
github.com/ameshkov/dnsstamps v1.0.3 // indirect
github.com/beefsack/go-rate v0.0.0-20220214233405-116f4ca011a0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/google/pprof v0.0.0-20240207164012-fb44976bdcd5 // indirect
github.com/mdlayher/socket v0.5.0 // indirect
github.com/onsi/ginkgo/v2 v2.15.0 // indirect
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
github.com/google/pprof v0.0.0-20240521024322-9665fa269a30 // indirect
github.com/jessevdk/go-flags v1.5.0 // indirect
github.com/mdlayher/socket v0.5.1 // indirect
github.com/onsi/ginkgo/v2 v2.17.3 // indirect
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
github.com/pierrec/lz4/v4 v4.1.21 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/quic-go/qpack v0.4.0 // indirect
github.com/u-root/uio v0.0.0-20240207234124-abbebccef0fd // indirect
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 // indirect
go.uber.org/mock v0.4.0 // indirect
golang.org/x/mod v0.15.0 // indirect
golang.org/x/sync v0.6.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/tools v0.18.0 // indirect
gonum.org/v1/gonum v0.14.0 // indirect
golang.org/x/mod v0.17.0 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/text v0.15.0 // indirect
golang.org/x/tools v0.21.0 // indirect
gonum.org/v1/gonum v0.15.0 // indirect
)

119
go.sum
View File

@@ -1,7 +1,9 @@
github.com/AdguardTeam/dnsproxy v0.65.0 h1:mqJjVSkqoqPwThY3tTvnLHQ/AYBYrfWmK2ER91fu4FE=
github.com/AdguardTeam/dnsproxy v0.65.0/go.mod h1:AGYMLPk2zX+I3NIUYS12KUI296mkCyfoMF/luy2uqdk=
github.com/AdguardTeam/golibs v0.20.1 h1:ol8qLjWGZhU9paMMwN+OLWVTUigGsXa29iVTyd62VKY=
github.com/AdguardTeam/golibs v0.20.1/go.mod h1:bgcMgRviCKyU6mkrX+RtT/OsKPFzyppelfRsksMG3KU=
github.com/AdguardTeam/dnsproxy v0.71.1 h1:R8jKmoE9HwqdTt7bm8irpvrQEOSmD+iGdNXbOg/uM8Y=
github.com/AdguardTeam/dnsproxy v0.71.1/go.mod h1:rCaCL4m4n63sgwTOyUVdc7MC42PlUYBt11Fz/UjD+kM=
github.com/AdguardTeam/dnsproxy v0.71.2-0.20240524084138-ee010801be05 h1:Ay61A/PPlfKedeDLDj1A086zVYivD5jezwYi24JH6/M=
github.com/AdguardTeam/dnsproxy v0.71.2-0.20240524084138-ee010801be05/go.mod h1:ejrJRou/fCxCtTR6ZYOPblzte2zzHnGLpaN2Yz80ZKk=
github.com/AdguardTeam/golibs v0.23.2 h1:rMjYantwtQ39e8G4zBQ6ZLlm4s3XH30Bc9VxhoOHwao=
github.com/AdguardTeam/golibs v0.23.2/go.mod h1:o9i55Sx6v7qogRQeqaBfmLbC/pZqeMBWi015U5PTDY0=
github.com/AdguardTeam/urlfilter v0.18.0 h1:ZZzwODC/ADpjJSODxySrrUnt/fvOCfGFaCW6j+wsGfQ=
github.com/AdguardTeam/urlfilter v0.18.0/go.mod h1:IXxBwedLiZA2viyHkaFxY/8mjub0li2PXRg8a3d9Z1s=
github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I=
@@ -10,8 +12,8 @@ github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmH
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.2.7 h1:aEitLIR8HcxVodZ79mgRcCiC0A0I5kZPBuWGFwwulAw=
github.com/ameshkov/dnscrypt/v2 v2.2.7/go.mod h1:qPWhwz6FdSmuK7W4sMyvogrez4MWdtzosdqlr0Rg3ow=
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/go.mod h1:Ii3eUu73dx4Vw5O4wjzmT5+lkCwovjzaEZZ4gKyIH5A=
github.com/beefsack/go-rate v0.0.0-20220214233405-116f4ca011a0 h1:0b2vaepXIfMsG++IsjHiI2p4bxALD1Y2nQKGMR5zDQM=
@@ -29,16 +31,14 @@ github.com/dimfeld/httptreemux/v5 v5.5.0 h1:p8jkiMrCuZ0CmhwYLcbNbl7DDo21fozhKHQ2
github.com/dimfeld/httptreemux/v5 v5.5.0/go.mod h1:QeEylH57C0v3VO0tkKraVz9oD3Uu93CKPnTLbsidvSw=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY=
github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-ping/ping v1.1.0 h1:3MCGhVX4fyEUuhsfwPrsEdQw6xspHkv5zHsiSoDFZYw=
github.com/go-ping/ping v1.1.0/go.mod h1:xIFjORFzTxqIV/tDVGO4eDy/bLuSyawEeojSm3GfRGk=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
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/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
@@ -46,8 +46,8 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
github.com/google/pprof v0.0.0-20240207164012-fb44976bdcd5 h1:E/LAvt58di64hlYjx7AsNS6C/ysHWYo+2qPCZKTQhRo=
github.com/google/pprof v0.0.0-20240207164012-fb44976bdcd5/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
github.com/google/pprof v0.0.0-20240521024322-9665fa269a30 h1:r6YdmbD41tGHeCWDyHF691LWtL7D1iSTyJaKejTWwVU=
github.com/google/pprof v0.0.0-20240521024322-9665fa269a30/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
github.com/google/renameio/v2 v2.0.0 h1:UifI23ZTGY8Tt29JbYFiuyIU3eX+RNFtUwefq9qAhxg=
github.com/google/renameio/v2 v2.0.0/go.mod h1:BtmJXm5YlszgC+TD4HOEEUFgkJP3nLxehU6hfe7jRt4=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@@ -55,9 +55,11 @@ 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/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714 h1:/jC7qQFrv8CrSJVmaolDVOxTfS9kc36uB6H40kdbQq8=
github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis=
github.com/insomniacslk/dhcp v0.0.0-20240204152450-ca2dc33955c1 h1:L3pm9Kf2G6gJVYawz2SrI5QnV1wzHYbqmKnSHHXJAb8=
github.com/insomniacslk/dhcp v0.0.0-20240204152450-ca2dc33955c1/go.mod h1:izxuNQZeFrbx2nK2fAyN5iNUB34Fe9j0nK4PwLzAkKw=
github.com/insomniacslk/dhcp v0.0.0-20240419123447-f1cffa2c0c49 h1:/OuvSMGT9+xnyZ+7MZQ1zdngaCCAdPoSw8B/uurZ7pg=
github.com/insomniacslk/dhcp v0.0.0-20240419123447-f1cffa2c0c49/go.mod h1:KclMyHxX06VrVr0DJmeFSUb1ankt7xTfoOA35pCkoic=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LFvc=
github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
github.com/josharian/native v1.0.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86 h1:elKwZS1OcdQ0WwEDBeqxKwb7WB62QX8bvZ/FJnVXIfk=
github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86/go.mod h1:aFAMtuldEgx/4q7iSGazk22+IcgvtiC+HIimFO9XlS8=
@@ -78,16 +80,16 @@ github.com/mdlayher/packet v1.1.2/go.mod h1:GEu1+n9sG5VtiRE4SydOmX5GTwyyYlteZiFU
github.com/mdlayher/raw v0.1.0 h1:K4PFMVy+AFsp0Zdlrts7yNhxc/uXoPVHi9RzRvtZF2Y=
github.com/mdlayher/raw v0.1.0/go.mod h1:yXnxvs6c0XoF/aK52/H5PjsVHmWBCFfZUfoh/Y5s9Sg=
github.com/mdlayher/socket v0.2.1/go.mod h1:QLlNPkFR88mRUNQIzRBMfXxwKal8H7u1h3bL1CV+f0E=
github.com/mdlayher/socket v0.5.0 h1:ilICZmJcQz70vrWVes1MFera4jGiWNocSkykwwoy3XI=
github.com/mdlayher/socket v0.5.0/go.mod h1:WkcBFfvyG8QENs5+hfQPl1X6Jpd2yeLIYgrGFmJiJxI=
github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4=
github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY=
github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos=
github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ=
github.com/miekg/dns v1.1.59 h1:C9EXc/UToRwKLhK5wKU/I4QVsBUc8kE6MkHBkeypWZs=
github.com/miekg/dns v1.1.59/go.mod h1:nZpewl5p6IvctfgrckopVx2OlSEHPRO/U4SYkRklrEk=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/onsi/ginkgo/v2 v2.15.0 h1:79HwNRBAZHOEwrczrgSOPy+eFTTlIGELKy5as+ClttY=
github.com/onsi/ginkgo/v2 v2.15.0/go.mod h1:HlxMHtYF57y6Dpf+mc5529KKmSq9h2FpCF+/ZkwUxKM=
github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8=
github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
github.com/onsi/ginkgo/v2 v2.17.3 h1:oJcvKpIb7/8uLpDDtnQuf18xVnwKp8DTD7DQ6gTd/MU=
github.com/onsi/ginkgo/v2 v2.17.3/go.mod h1:nP2DPOQoNsQmsVyv5rDA8JkXQoCs6goXIvr/PRJ1eCc=
github.com/onsi/gomega v1.33.0 h1:snPCflnZrpMsy94p4lXVEkHo12lmPnc3vY5XBbreexE=
github.com/onsi/gomega v1.33.0/go.mod h1:+925n5YtiFsLzzafLUHzVMBpvvRAzrydIBiSIxjX3wY=
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
@@ -101,56 +103,55 @@ github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
github.com/quic-go/quic-go v0.41.0 h1:aD8MmHfgqTURWNJy48IYFg2OnxwHT3JL7ahGs73lb4k=
github.com/quic-go/quic-go v0.41.0/go.mod h1:qCkNjqczPEvgsOnxZ0eCD14lv+B2LHlFAB++CNOh9hA=
github.com/quic-go/quic-go v0.43.1 h1:fLiMNfQVe9q2JvSsiXo4fXOEguXHGGl9+6gLp4RPeZQ=
github.com/quic-go/quic-go v0.43.1/go.mod h1:132kz4kL3F9vxhW3CtQJLDVwcFe5wdWeJXXijhsO57M=
github.com/shirou/gopsutil/v3 v3.23.7 h1:C+fHO8hfIppoJ1WdsVm1RoI0RwXoNdfTK7yWXV0wVj4=
github.com/shirou/gopsutil/v3 v3.23.7/go.mod h1:c4gnmoRC0hQuaLqvxnx1//VXQ0Ms/X9UnJF8pddY5z4=
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/ti-mo/netfilter v0.2.0/go.mod h1:8GbBGsY/8fxtyIdfwy29JiluNcPK4K7wIT+x42ipqUU=
github.com/ti-mo/netfilter v0.5.1 h1:cqamEd1c1zmpfpqvInLOro0Znq/RAfw2QL5wL2rAR/8=
github.com/ti-mo/netfilter v0.5.1/go.mod h1:h9UPQ3ZrTZGBitay+LETMxZvNgWGK/efTUcqES2YiLw=
github.com/ti-mo/netfilter v0.5.2 h1:CTjOwFuNNeZ9QPdRXt1MZFLFUf84cKtiQutNauHWd40=
github.com/ti-mo/netfilter v0.5.2/go.mod h1:Btx3AtFiOVdHReTDmP9AE+hlkOcvIy403u7BXXbWZKo=
github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM=
github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI=
github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms=
github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4=
github.com/u-root/uio v0.0.0-20240207234124-abbebccef0fd h1:BQJh5fdHsPa/YuMVrbcSxQKuowGCHYh0GD7hvLaHBK0=
github.com/u-root/uio v0.0.0-20240207234124-abbebccef0fd/go.mod h1:P3a5rG4X7tI17Nn3aOIAYr5HbIMukwXG0urG0WuL8OA=
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/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw=
github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
go.etcd.io/bbolt v1.3.8 h1:xs88BrvEv273UsB79e0hcVrlUWmS0a8upikMFhSyAtA=
go.etcd.io/bbolt v1.3.8/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
go.etcd.io/bbolt v1.3.10 h1:+BqfJTcCzTItrop8mq/lbzL8wSGtj94UO/3U31shqG0=
go.etcd.io/bbolt v1.3.10/go.mod h1:bK3UQLPJZly7IlNmV7uVHJDxfe5aK9Ll93e/74Y9oEQ=
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/exp v0.0.0-20240205201215-2c58cdc269a3 h1:/RIbNt/Zr7rVhIkQhooTxCxFcdWLGIKnZA4IXNFSrvo=
golang.org/x/exp v0.0.0-20240205201215-2c58cdc269a3/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08=
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM=
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
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.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
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.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190322080309-f49334f85ddc/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -158,33 +159,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-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-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.4.1-0.20230131160137-e7d7f63158de/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ=
golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg=
golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw=
golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.14.0 h1:2NiG67LD1tEH0D7kM+ps2V+fXmsAnpUeec7n8tcr4S0=
gonum.org/v1/gonum v0.14.0/go.mod h1:AoWeoz0becf9QMWtE8iWXNXc27fK4fNeHNf/oMejGfU=
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gonum.org/v1/gonum v0.15.0 h1:2lYxjRbTYyxkJxlhC+LvJIx3SsANPdRybu1tGj9/OrQ=
gonum.org/v1/gonum v0.15.0/go.mod h1:xzZVBJBtS+Mz4q0Yl2LJTk+OxOg4jiXZ7qBoM0uISGo=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
howett.net/plist v1.0.1 h1:37GdZ8tP09Q35o9ych3ehygcsL+HqKSwzctveSlarvM=

View File

@@ -10,29 +10,8 @@ import (
"golang.org/x/exp/constraints"
)
// Coalesce returns the first non-zero value. It is named after function
// COALESCE in SQL. If values or all its elements are empty, it returns a zero
// value.
//
// T is comparable, because Go currently doesn't have a comparableWithZeroValue
// constraint.
//
// TODO(a.garipov): Think of ways to merge with [CoalesceSlice].
func Coalesce[T comparable](values ...T) (res T) {
var zero T
for _, v := range values {
if v != zero {
return v
}
}
return zero
}
// CoalesceSlice returns the first non-zero value. It is named after function
// COALESCE in SQL. If values or all its elements are empty, it returns nil.
//
// TODO(a.garipov): Think of ways to merge with [Coalesce].
func CoalesceSlice[E any, S []E](values ...S) (res S) {
for _, v := range values {
if v != nil {

View File

@@ -33,7 +33,7 @@ func elements(b *aghalg.RingBuffer[int], n uint, reverse bool) (es []int) {
func TestNewRingBuffer(t *testing.T) {
t.Run("success_and_clear", func(t *testing.T) {
b := aghalg.NewRingBuffer[int](5)
for i := 0; i < 10; i++ {
for i := range 10 {
b.Append(i)
}
assert.Equal(t, []int{5, 6, 7, 8, 9}, elements(b, b.Len(), false))
@@ -44,7 +44,7 @@ func TestNewRingBuffer(t *testing.T) {
t.Run("zero", func(t *testing.T) {
b := aghalg.NewRingBuffer[int](0)
for i := 0; i < 10; i++ {
for i := range 10 {
b.Append(i)
bufLen := b.Len()
assert.EqualValues(t, 0, bufLen)
@@ -55,7 +55,7 @@ func TestNewRingBuffer(t *testing.T) {
t.Run("single", func(t *testing.T) {
b := aghalg.NewRingBuffer[int](1)
for i := 0; i < 10; i++ {
for i := range 10 {
b.Append(i)
bufLen := b.Len()
assert.EqualValues(t, 1, bufLen)
@@ -94,7 +94,7 @@ func TestRingBuffer_Range(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
for i := 0; i < tc.count; i++ {
for i := range tc.count {
b.Append(i)
}

View File

@@ -0,0 +1,86 @@
package aghalg
import (
"slices"
)
// SortedMap is a map that keeps elements in order with internal sorting
// function. Must be initialised by the [NewSortedMap].
type SortedMap[K comparable, V any] struct {
vals map[K]V
cmp func(a, b K) (res int)
keys []K
}
// NewSortedMap initializes the new instance of sorted map. cmp is a sort
// function to keep elements in order.
//
// TODO(s.chzhen): Use cmp.Compare in Go 1.21.
func NewSortedMap[K comparable, V any](cmp func(a, b K) (res int)) SortedMap[K, V] {
return SortedMap[K, V]{
vals: map[K]V{},
cmp: cmp,
}
}
// Set adds val with key to the sorted map. It panics if the m is nil.
func (m *SortedMap[K, V]) Set(key K, val V) {
m.vals[key] = val
i, has := slices.BinarySearchFunc(m.keys, key, m.cmp)
if has {
m.keys[i] = key
} else {
m.keys = slices.Insert(m.keys, i, key)
}
}
// Get returns val by key from the sorted map.
func (m *SortedMap[K, V]) Get(key K) (val V, ok bool) {
if m == nil {
return
}
val, ok = m.vals[key]
return val, ok
}
// Del removes the value by key from the sorted map.
func (m *SortedMap[K, V]) Del(key K) {
if m == nil {
return
}
if _, has := m.vals[key]; !has {
return
}
delete(m.vals, key)
i, _ := slices.BinarySearchFunc(m.keys, key, m.cmp)
m.keys = slices.Delete(m.keys, i, i+1)
}
// Clear removes all elements from the sorted map.
func (m *SortedMap[K, V]) Clear() {
if m == nil {
return
}
m.keys = nil
clear(m.vals)
}
// Range calls cb for each element of the map, sorted by m.cmp. If cb returns
// false it stops.
func (m *SortedMap[K, V]) Range(cb func(K, V) (cont bool)) {
if m == nil {
return
}
for _, k := range m.keys {
if !cb(k, m.vals[k]) {
return
}
}
}

View File

@@ -0,0 +1,95 @@
package aghalg
import (
"strings"
"testing"
"github.com/stretchr/testify/assert"
)
func TestNewSortedMap(t *testing.T) {
var m SortedMap[string, int]
letters := []string{}
for i := range 10 {
r := string('a' + rune(i))
letters = append(letters, r)
}
t.Run("create_and_fill", func(t *testing.T) {
m = NewSortedMap[string, int](strings.Compare)
nums := []int{}
for i, r := range letters {
m.Set(r, i)
nums = append(nums, i)
}
gotLetters := []string{}
gotNums := []int{}
m.Range(func(k string, v int) bool {
gotLetters = append(gotLetters, k)
gotNums = append(gotNums, v)
return true
})
assert.Equal(t, letters, gotLetters)
assert.Equal(t, nums, gotNums)
n, ok := m.Get(letters[0])
assert.True(t, ok)
assert.Equal(t, nums[0], n)
})
t.Run("clear", func(t *testing.T) {
lastLetter := letters[len(letters)-1]
m.Del(lastLetter)
_, ok := m.Get(lastLetter)
assert.False(t, ok)
m.Clear()
gotLetters := []string{}
m.Range(func(k string, _ int) bool {
gotLetters = append(gotLetters, k)
return true
})
assert.Len(t, gotLetters, 0)
})
}
func TestNewSortedMap_nil(t *testing.T) {
const (
key = "key"
val = "val"
)
var m SortedMap[string, string]
assert.Panics(t, func() {
m.Set(key, val)
})
assert.NotPanics(t, func() {
_, ok := m.Get(key)
assert.False(t, ok)
})
assert.NotPanics(t, func() {
m.Range(func(_, _ string) (cont bool) {
return true
})
})
assert.NotPanics(t, func() {
m.Del(key)
})
assert.NotPanics(t, func() {
m.Clear()
})
}

View File

@@ -1,10 +1,7 @@
package aghnet
import (
"fmt"
"strings"
"github.com/AdguardTeam/golibs/stringutil"
)
// NormalizeDomain returns a lowercased version of host without the final dot,
@@ -19,25 +16,3 @@ func NormalizeDomain(host string) (norm string) {
return strings.ToLower(strings.TrimSuffix(host, "."))
}
// NewDomainNameSet returns nil and error, if list has duplicate or empty domain
// name. Otherwise returns a set, which contains domain names normalized using
// [NormalizeDomain].
func NewDomainNameSet(list []string) (set *stringutil.Set, err error) {
set = stringutil.NewSet()
for i, host := range list {
if host == "" {
return nil, fmt.Errorf("at index %d: hostname is empty", i)
}
host = NormalizeDomain(host)
if set.Has(host) {
return nil, fmt.Errorf("duplicate hostname %q at index %d", host, i)
}
set.Add(host)
}
return set, nil
}

View File

@@ -1,59 +0,0 @@
package aghnet_test
import (
"testing"
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
"github.com/AdguardTeam/golibs/testutil"
"github.com/stretchr/testify/assert"
)
func TestNewDomainNameSet(t *testing.T) {
t.Parallel()
testCases := []struct {
name string
wantErrMsg string
in []string
}{{
name: "nil",
wantErrMsg: "",
in: nil,
}, {
name: "success",
wantErrMsg: "",
in: []string{
"Domain.Example",
".",
},
}, {
name: "dups",
wantErrMsg: `duplicate hostname "domain.example" at index 1`,
in: []string{
"Domain.Example",
"domain.example",
},
}, {
name: "bad_domain",
wantErrMsg: "at index 0: hostname is empty",
in: []string{
"",
},
}}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
set, err := aghnet.NewDomainNameSet(tc.in)
testutil.AssertErrorMsg(t, tc.wantErrMsg, err)
if err != nil {
return
}
for _, host := range tc.in {
assert.Truef(t, set.Has(aghnet.NormalizeDomain(host)), "%q not matched", host)
}
})
}
}

View File

@@ -5,8 +5,8 @@ import (
"io"
"io/fs"
"github.com/AdguardTeam/golibs/container"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/stringutil"
)
// FileWalker is the signature of a function called for files in the file tree.
@@ -56,7 +56,7 @@ func checkFile(
// srcSet. srcSet must be non-nil.
func handlePatterns(
fsys fs.FS,
srcSet *stringutil.Set,
srcSet *container.MapSet[string],
patterns ...string,
) (sub []string, err error) {
sub = make([]string, 0, len(patterns))
@@ -87,7 +87,7 @@ func handlePatterns(
func (fw FileWalker) Walk(fsys fs.FS, initial ...string) (ok bool, err error) {
// The slice of sources keeps the order in which the files are walked since
// srcSet.Values() returns strings in undefined order.
srcSet := stringutil.NewSet()
srcSet := container.NewMapSet[string]()
var src []string
src, err = handlePatterns(fsys, srcSet, initial...)
if err != nil {
@@ -97,6 +97,8 @@ func (fw FileWalker) Walk(fsys fs.FS, initial ...string) (ok bool, err error) {
var filename string
defer func() { err = errors.Annotate(err, "checking %q: %w", filename) }()
// TODO(e.burkov): Redo this loop, as it modifies the very same slice it
// iterates over.
for i := 0; i < len(src); i++ {
var patterns []string
var cont bool

View File

@@ -6,10 +6,10 @@ import (
"io/fs"
"path/filepath"
"github.com/AdguardTeam/golibs/container"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/osutil"
"github.com/AdguardTeam/golibs/stringutil"
"github.com/fsnotify/fsnotify"
)
@@ -46,7 +46,7 @@ type osWatcher struct {
events chan event
// files is the set of tracked files.
files *stringutil.Set
files *container.MapSet[string]
}
// osWatcherPref is a prefix for logging and wrapping errors in osWathcer's
@@ -67,7 +67,7 @@ func NewOSWritesWatcher() (w FSWatcher, err error) {
return &osWatcher{
watcher: watcher,
events: make(chan event, 1),
files: stringutil.NewSet(),
files: container.NewMapSet[string](),
}, nil
}

View File

@@ -159,21 +159,11 @@ func NotifyReconfigureSignal(c chan<- os.Signal) {
notifyReconfigureSignal(c)
}
// NotifyShutdownSignal notifies c on receiving shutdown signals.
func NotifyShutdownSignal(c chan<- os.Signal) {
notifyShutdownSignal(c)
}
// IsReconfigureSignal returns true if sig is a reconfigure signal.
func IsReconfigureSignal(sig os.Signal) (ok bool) {
return isReconfigureSignal(sig)
}
// IsShutdownSignal returns true if sig is a shutdown signal.
func IsShutdownSignal(sig os.Signal) (ok bool) {
return isShutdownSignal(sig)
}
// SendShutdownSignal sends the shutdown signal to the channel.
func SendShutdownSignal(c chan<- os.Signal) {
sendShutdownSignal(c)

View File

@@ -13,26 +13,10 @@ func notifyReconfigureSignal(c chan<- os.Signal) {
signal.Notify(c, unix.SIGHUP)
}
func notifyShutdownSignal(c chan<- os.Signal) {
signal.Notify(c, unix.SIGINT, unix.SIGQUIT, unix.SIGTERM)
}
func isReconfigureSignal(sig os.Signal) (ok bool) {
return sig == unix.SIGHUP
}
func isShutdownSignal(sig os.Signal) (ok bool) {
switch sig {
case
unix.SIGINT,
unix.SIGQUIT,
unix.SIGTERM:
return true
default:
return false
}
}
func sendShutdownSignal(_ chan<- os.Signal) {
// On Unix we are already notified by the system.
}

View File

@@ -5,7 +5,6 @@ package aghos
import (
"os"
"os/signal"
"syscall"
"golang.org/x/sys/windows"
)
@@ -43,25 +42,10 @@ func notifyReconfigureSignal(c chan<- os.Signal) {
signal.Notify(c, windows.SIGHUP)
}
func notifyShutdownSignal(c chan<- os.Signal) {
// syscall.SIGTERM is processed automatically. See go doc os/signal,
// section Windows.
signal.Notify(c, os.Interrupt)
}
func isReconfigureSignal(sig os.Signal) (ok bool) {
return sig == windows.SIGHUP
}
func isShutdownSignal(sig os.Signal) (ok bool) {
switch sig {
case os.Interrupt, syscall.SIGTERM:
return true
default:
return false
}
}
func sendShutdownSignal(c chan<- os.Signal) {
c <- os.Interrupt
}

View File

@@ -78,7 +78,6 @@ func TestWithDeferredCleanup(t *testing.T) {
}}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()

View File

@@ -9,8 +9,13 @@ import (
"net/netip"
"net/url"
"testing"
"time"
"github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/testutil"
"github.com/miekg/dns"
"github.com/stretchr/testify/require"
)
@@ -71,3 +76,49 @@ func StartHTTPServer(t testing.TB, data []byte) (c *http.Client, u *url.URL) {
return srv.Client(), u
}
// testTimeout is a timeout for tests.
//
// TODO(e.burkov): Move into agdctest.
const testTimeout = 1 * time.Second
// StartLocalhostUpstream is a test helper that starts a DNS server on
// localhost.
func StartLocalhostUpstream(t *testing.T, h dns.Handler) (addr *url.URL) {
t.Helper()
startCh := make(chan netip.AddrPort)
defer close(startCh)
errCh := make(chan error)
srv := &dns.Server{
Addr: "127.0.0.1:0",
Net: string(proxy.ProtoTCP),
Handler: h,
ReadTimeout: testTimeout,
WriteTimeout: testTimeout,
}
srv.NotifyStartedFunc = func() {
addrPort := srv.Listener.Addr()
startCh <- netutil.NetAddrToAddrPort(addrPort)
}
go func() { errCh <- srv.ListenAndServe() }()
select {
case addrPort := <-startCh:
addr = &url.URL{
Scheme: string(proxy.ProtoTCP),
Host: addrPort.String(),
}
testutil.CleanupAndRequireSuccess(t, func() (err error) { return <-errCh })
testutil.CleanupAndRequireSuccess(t, srv.Shutdown)
case err := <-errCh:
require.NoError(t, err)
case <-time.After(testTimeout):
require.FailNow(t, "timeout exceeded")
}
return addr
}

View File

@@ -7,7 +7,6 @@ import (
"time"
"github.com/AdguardTeam/AdGuardHome/internal/aghos"
"github.com/AdguardTeam/AdGuardHome/internal/client"
"github.com/AdguardTeam/AdGuardHome/internal/next/agh"
"github.com/AdguardTeam/AdGuardHome/internal/rdns"
"github.com/AdguardTeam/AdGuardHome/internal/whois"
@@ -94,9 +93,6 @@ type AddressProcessor struct {
OnClose func() (err error)
}
// type check
var _ client.AddressProcessor = (*AddressProcessor)(nil)
// Process implements the [client.AddressProcessor] interface for
// *AddressProcessor.
func (p *AddressProcessor) Process(ip netip.Addr) {
@@ -114,9 +110,6 @@ type AddressUpdater struct {
OnUpdateAddress func(ip netip.Addr, host string, info *whois.Info)
}
// type check
var _ client.AddressUpdater = (*AddressUpdater)(nil)
// UpdateAddress implements the [client.AddressUpdater] interface for
// *AddressUpdater.
func (p *AddressUpdater) UpdateAddress(ip netip.Addr, host string, info *whois.Info) {

View File

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

View File

@@ -91,8 +91,6 @@ func TestDefaultAddrProc_Process_rDNS(t *testing.T) {
}}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
@@ -186,8 +184,6 @@ func TestDefaultAddrProc_Process_WHOIS(t *testing.T) {
}}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()

View File

@@ -7,6 +7,7 @@ package client
import (
"encoding"
"fmt"
"net/netip"
"github.com/AdguardTeam/AdGuardHome/internal/whois"
)
@@ -56,6 +57,9 @@ func (cs Source) MarshalText() (text []byte, err error) {
// Runtime is a client information from different sources.
type Runtime struct {
// ip is an IP address of a client.
ip netip.Addr
// whois is the filtered WHOIS information of a client.
whois *whois.Info
@@ -80,6 +84,15 @@ type Runtime struct {
hostsFile []string
}
// NewRuntime constructs a new runtime client. ip must be valid IP address.
//
// TODO(s.chzhen): Validate IP address.
func NewRuntime(ip netip.Addr) (r *Runtime) {
return &Runtime{
ip: ip,
}
}
// Info returns a client information from the highest-priority source.
func (r *Runtime) Info() (cs Source, host string) {
info := []string{}
@@ -133,8 +146,8 @@ func (r *Runtime) SetWHOIS(info *whois.Info) {
r.whois = info
}
// Unset clears a cs information.
func (r *Runtime) Unset(cs Source) {
// unset clears a cs information.
func (r *Runtime) unset(cs Source) {
switch cs {
case SourceWHOIS:
r.whois = nil
@@ -149,11 +162,16 @@ func (r *Runtime) Unset(cs Source) {
}
}
// IsEmpty returns true if there is no information from any source.
func (r *Runtime) IsEmpty() (ok bool) {
// isEmpty returns true if there is no information from any source.
func (r *Runtime) isEmpty() (ok bool) {
return r.whois == nil &&
r.arp == nil &&
r.rdns == nil &&
r.dhcp == nil &&
r.hostsFile == nil
}
// Addr returns an IP address of the client.
func (r *Runtime) Addr() (ip netip.Addr) {
return r.ip
}

368
internal/client/index.go Normal file
View File

@@ -0,0 +1,368 @@
package client
import (
"fmt"
"net"
"net/netip"
"slices"
"strings"
"github.com/AdguardTeam/AdGuardHome/internal/aghalg"
"github.com/AdguardTeam/golibs/errors"
"golang.org/x/exp/maps"
)
// macKey contains MAC as byte array of 6, 8, or 20 bytes.
type macKey any
// macToKey converts mac into key of type macKey, which is used as the key of
// the [clientIndex.macToUID]. mac must be valid MAC address.
func macToKey(mac net.HardwareAddr) (key macKey) {
switch len(mac) {
case 6:
return [6]byte(mac)
case 8:
return [8]byte(mac)
case 20:
return [20]byte(mac)
default:
panic(fmt.Errorf("invalid mac address %#v", mac))
}
}
// Index stores all information about persistent clients.
type Index struct {
// nameToUID maps client name to UID.
nameToUID map[string]UID
// clientIDToUID maps client ID to UID.
clientIDToUID map[string]UID
// ipToUID maps IP address to UID.
ipToUID map[netip.Addr]UID
// macToUID maps MAC address to UID.
macToUID map[macKey]UID
// uidToClient maps UID to the persistent client.
uidToClient map[UID]*Persistent
// subnetToUID maps subnet to UID.
subnetToUID aghalg.SortedMap[netip.Prefix, UID]
}
// NewIndex initializes the new instance of client index.
func NewIndex() (ci *Index) {
return &Index{
nameToUID: map[string]UID{},
clientIDToUID: map[string]UID{},
ipToUID: map[netip.Addr]UID{},
subnetToUID: aghalg.NewSortedMap[netip.Prefix, UID](subnetCompare),
macToUID: map[macKey]UID{},
uidToClient: map[UID]*Persistent{},
}
}
// Add stores information about a persistent client in the index. c must be
// non-nil and contain UID.
func (ci *Index) Add(c *Persistent) {
if (c.UID == UID{}) {
panic("client must contain uid")
}
ci.nameToUID[c.Name] = c.UID
for _, id := range c.ClientIDs {
ci.clientIDToUID[id] = c.UID
}
for _, ip := range c.IPs {
ci.ipToUID[ip] = c.UID
}
for _, pref := range c.Subnets {
ci.subnetToUID.Set(pref, c.UID)
}
for _, mac := range c.MACs {
k := macToKey(mac)
ci.macToUID[k] = c.UID
}
ci.uidToClient[c.UID] = c
}
// ClashesUID returns existing persistent client with the same UID as c. Note
// that this is only possible when configuration contains duplicate fields.
func (ci *Index) ClashesUID(c *Persistent) (err error) {
p, ok := ci.uidToClient[c.UID]
if ok {
return fmt.Errorf("another client %q uses the same uid", p.Name)
}
return nil
}
// Clashes returns an error if the index contains a different persistent client
// with at least a single identifier contained by c. c must be non-nil.
func (ci *Index) Clashes(c *Persistent) (err error) {
if p := ci.clashesName(c); p != nil {
return fmt.Errorf("another client uses the same name %q", p.Name)
}
for _, id := range c.ClientIDs {
existing, ok := ci.clientIDToUID[id]
if ok && existing != c.UID {
p := ci.uidToClient[existing]
return fmt.Errorf("another client %q uses the same ClientID %q", p.Name, id)
}
}
p, ip := ci.clashesIP(c)
if p != nil {
return fmt.Errorf("another client %q uses the same IP %q", p.Name, ip)
}
p, s := ci.clashesSubnet(c)
if p != nil {
return fmt.Errorf("another client %q uses the same subnet %q", p.Name, s)
}
p, mac := ci.clashesMAC(c)
if p != nil {
return fmt.Errorf("another client %q uses the same MAC %q", p.Name, mac)
}
return nil
}
// clashesName returns existing persistent client with the same name as c or
// nil. c must be non-nil.
func (ci *Index) clashesName(c *Persistent) (existing *Persistent) {
existing, ok := ci.FindByName(c.Name)
if !ok {
return nil
}
if existing.UID != c.UID {
return existing
}
return nil
}
// clashesIP returns a previous client with the same IP address as c. c must be
// non-nil.
func (ci *Index) clashesIP(c *Persistent) (p *Persistent, ip netip.Addr) {
for _, ip := range c.IPs {
existing, ok := ci.ipToUID[ip]
if ok && existing != c.UID {
return ci.uidToClient[existing], ip
}
}
return nil, netip.Addr{}
}
// clashesSubnet returns a previous client with the same subnet as c. c must be
// non-nil.
func (ci *Index) clashesSubnet(c *Persistent) (p *Persistent, s netip.Prefix) {
for _, s = range c.Subnets {
var existing UID
var ok bool
ci.subnetToUID.Range(func(p netip.Prefix, uid UID) (cont bool) {
if s == p {
existing = uid
ok = true
return false
}
return true
})
if ok && existing != c.UID {
return ci.uidToClient[existing], s
}
}
return nil, netip.Prefix{}
}
// clashesMAC returns a previous client with the same MAC address as c. c must
// be non-nil.
func (ci *Index) clashesMAC(c *Persistent) (p *Persistent, mac net.HardwareAddr) {
for _, mac = range c.MACs {
k := macToKey(mac)
existing, ok := ci.macToUID[k]
if ok && existing != c.UID {
return ci.uidToClient[existing], mac
}
}
return nil, nil
}
// Find finds persistent client by string representation of the client ID, IP
// address, or MAC.
func (ci *Index) Find(id string) (c *Persistent, ok bool) {
uid, found := ci.clientIDToUID[id]
if found {
return ci.uidToClient[uid], true
}
ip, err := netip.ParseAddr(id)
if err == nil {
// MAC addresses can be successfully parsed as IP addresses.
c, found = ci.findByIP(ip)
if found {
return c, true
}
}
mac, err := net.ParseMAC(id)
if err == nil {
return ci.FindByMAC(mac)
}
return nil, false
}
// FindByName finds persistent client by name.
func (ci *Index) FindByName(name string) (c *Persistent, found bool) {
uid, found := ci.nameToUID[name]
if found {
return ci.uidToClient[uid], true
}
return nil, false
}
// findByIP finds persistent client by IP address.
func (ci *Index) findByIP(ip netip.Addr) (c *Persistent, found bool) {
uid, found := ci.ipToUID[ip]
if found {
return ci.uidToClient[uid], true
}
ipWithoutZone := ip.WithZone("")
ci.subnetToUID.Range(func(pref netip.Prefix, id UID) (cont bool) {
// Remove zone before checking because prefixes strip zones.
if pref.Contains(ipWithoutZone) {
uid, found = id, true
return false
}
return true
})
if found {
return ci.uidToClient[uid], true
}
return nil, false
}
// FindByMAC finds persistent client by MAC.
func (ci *Index) FindByMAC(mac net.HardwareAddr) (c *Persistent, found bool) {
k := macToKey(mac)
uid, found := ci.macToUID[k]
if found {
return ci.uidToClient[uid], true
}
return nil, false
}
// FindByIPWithoutZone finds a persistent client by IP address without zone. It
// strips the IPv6 zone index from the stored IP addresses before comparing,
// because querylog entries don't have it. See TODO on [querylog.logEntry.IP].
//
// Note that multiple clients can have the same IP address with different zones.
// Therefore, the result of this method is indeterminate.
func (ci *Index) FindByIPWithoutZone(ip netip.Addr) (c *Persistent) {
if (ip == netip.Addr{}) {
return nil
}
for addr, uid := range ci.ipToUID {
if addr.WithZone("") == ip {
return ci.uidToClient[uid]
}
}
return nil
}
// Delete removes information about persistent client from the index. c must be
// non-nil.
func (ci *Index) Delete(c *Persistent) {
delete(ci.nameToUID, c.Name)
for _, id := range c.ClientIDs {
delete(ci.clientIDToUID, id)
}
for _, ip := range c.IPs {
delete(ci.ipToUID, ip)
}
for _, pref := range c.Subnets {
ci.subnetToUID.Del(pref)
}
for _, mac := range c.MACs {
k := macToKey(mac)
delete(ci.macToUID, k)
}
delete(ci.uidToClient, c.UID)
}
// Size returns the number of persistent clients.
func (ci *Index) Size() (n int) {
return len(ci.uidToClient)
}
// Range calls f for each persistent client, unless cont is false. The order is
// undefined.
func (ci *Index) Range(f func(c *Persistent) (cont bool)) {
for _, c := range ci.uidToClient {
if !f(c) {
return
}
}
}
// RangeByName is like [Index.Range] but sorts the persistent clients by name
// before iterating ensuring a predictable order.
func (ci *Index) RangeByName(f func(c *Persistent) (cont bool)) {
cs := maps.Values(ci.uidToClient)
slices.SortFunc(cs, func(a, b *Persistent) (n int) {
return strings.Compare(a.Name, b.Name)
})
for _, c := range cs {
if !f(c) {
break
}
}
}
// 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

@@ -0,0 +1,350 @@
package client
import (
"net"
"net/netip"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// newIDIndex is a helper function that returns a client index filled with
// persistent clients from the m. It also generates a UID for each client.
func newIDIndex(m []*Persistent) (ci *Index) {
ci = NewIndex()
for _, c := range m {
c.UID = MustNewUID()
ci.Add(c)
}
return ci
}
func TestClientIndex_Find(t *testing.T) {
const (
cliIPNone = "1.2.3.4"
cliIP1 = "1.1.1.1"
cliIP2 = "2.2.2.2"
cliIPv6 = "1:2:3::4"
cliSubnet = "2.2.2.0/24"
cliSubnetIP = "2.2.2.222"
cliID = "client-id"
cliMAC = "11:11:11:11:11:11"
linkLocalIP = "fe80::abcd:abcd:abcd:ab%eth0"
linkLocalSubnet = "fe80::/16"
)
var (
clientWithBothFams = &Persistent{
Name: "client1",
IPs: []netip.Addr{
netip.MustParseAddr(cliIP1),
netip.MustParseAddr(cliIPv6),
},
}
clientWithSubnet = &Persistent{
Name: "client2",
IPs: []netip.Addr{netip.MustParseAddr(cliIP2)},
Subnets: []netip.Prefix{netip.MustParsePrefix(cliSubnet)},
}
clientWithMAC = &Persistent{
Name: "client_with_mac",
MACs: []net.HardwareAddr{mustParseMAC(cliMAC)},
}
clientWithID = &Persistent{
Name: "client_with_id",
ClientIDs: []string{cliID},
}
clientLinkLocal = &Persistent{
Name: "client_link_local",
Subnets: []netip.Prefix{netip.MustParsePrefix(linkLocalSubnet)},
}
)
clients := []*Persistent{
clientWithBothFams,
clientWithSubnet,
clientWithMAC,
clientWithID,
clientLinkLocal,
}
ci := newIDIndex(clients)
testCases := []struct {
want *Persistent
name string
ids []string
}{{
name: "ipv4_ipv6",
ids: []string{cliIP1, cliIPv6},
want: clientWithBothFams,
}, {
name: "ipv4_subnet",
ids: []string{cliIP2, cliSubnetIP},
want: clientWithSubnet,
}, {
name: "mac",
ids: []string{cliMAC},
want: clientWithMAC,
}, {
name: "client_id",
ids: []string{cliID},
want: clientWithID,
}, {
name: "client_link_local_subnet",
ids: []string{linkLocalIP},
want: clientLinkLocal,
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
for _, id := range tc.ids {
c, ok := ci.Find(id)
require.True(t, ok)
assert.Equal(t, tc.want, c)
}
})
}
t.Run("not_found", func(t *testing.T) {
_, ok := ci.Find(cliIPNone)
assert.False(t, ok)
})
}
func TestClientIndex_Clashes(t *testing.T) {
const (
cliIP1 = "1.1.1.1"
cliSubnet = "2.2.2.0/24"
cliSubnetIP = "2.2.2.222"
cliID = "client-id"
cliMAC = "11:11:11:11:11:11"
)
clients := []*Persistent{{
Name: "client_with_ip",
IPs: []netip.Addr{netip.MustParseAddr(cliIP1)},
}, {
Name: "client_with_subnet",
Subnets: []netip.Prefix{netip.MustParsePrefix(cliSubnet)},
}, {
Name: "client_with_mac",
MACs: []net.HardwareAddr{mustParseMAC(cliMAC)},
}, {
Name: "client_with_id",
ClientIDs: []string{cliID},
}}
ci := newIDIndex(clients)
testCases := []struct {
client *Persistent
name string
}{{
name: "ipv4",
client: clients[0],
}, {
name: "subnet",
client: clients[1],
}, {
name: "mac",
client: clients[2],
}, {
name: "client_id",
client: clients[3],
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
clone := tc.client.ShallowClone()
clone.UID = MustNewUID()
err := ci.Clashes(clone)
require.Error(t, err)
ci.Delete(tc.client)
err = ci.Clashes(clone)
require.NoError(t, err)
})
}
}
// mustParseMAC is wrapper around [net.ParseMAC] that panics if there is an
// error.
func mustParseMAC(s string) (mac net.HardwareAddr) {
mac, err := net.ParseMAC(s)
if err != nil {
panic(err)
}
return mac
}
func TestMACToKey(t *testing.T) {
testCases := []struct {
want any
name string
in string
}{{
name: "column6",
in: "00:00:5e:00:53:01",
want: [6]byte(mustParseMAC("00:00:5e:00:53:01")),
}, {
name: "column8",
in: "02:00:5e:10:00:00:00:01",
want: [8]byte(mustParseMAC("02:00:5e:10:00:00:00:01")),
}, {
name: "column20",
in: "00:00:00:00:fe:80:00:00:00:00:00:00:02:00:5e:10:00:00:00:01",
want: [20]byte(mustParseMAC("00:00:00:00:fe:80:00:00:00:00:00:00:02:00:5e:10:00:00:00:01")),
}, {
name: "hyphen6",
in: "00-00-5e-00-53-01",
want: [6]byte(mustParseMAC("00-00-5e-00-53-01")),
}, {
name: "hyphen8",
in: "02-00-5e-10-00-00-00-01",
want: [8]byte(mustParseMAC("02-00-5e-10-00-00-00-01")),
}, {
name: "hyphen20",
in: "00-00-00-00-fe-80-00-00-00-00-00-00-02-00-5e-10-00-00-00-01",
want: [20]byte(mustParseMAC("00-00-00-00-fe-80-00-00-00-00-00-00-02-00-5e-10-00-00-00-01")),
}, {
name: "dot6",
in: "0000.5e00.5301",
want: [6]byte(mustParseMAC("0000.5e00.5301")),
}, {
name: "dot8",
in: "0200.5e10.0000.0001",
want: [8]byte(mustParseMAC("0200.5e10.0000.0001")),
}, {
name: "dot20",
in: "0000.0000.fe80.0000.0000.0000.0200.5e10.0000.0001",
want: [20]byte(mustParseMAC("0000.0000.fe80.0000.0000.0000.0200.5e10.0000.0001")),
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
mac := mustParseMAC(tc.in)
key := macToKey(mac)
assert.Equal(t, tc.want, key)
})
}
assert.Panics(t, func() {
mac := net.HardwareAddr([]byte{1, 2, 3})
_ = macToKey(mac)
})
}
func TestIndex_FindByIPWithoutZone(t *testing.T) {
var (
ip = netip.MustParseAddr("fe80::a098:7654:32ef:ff1")
ipWithZone = netip.MustParseAddr("fe80::1ff:fe23:4567:890a%eth2")
)
var (
clientNoZone = &Persistent{
Name: "client",
IPs: []netip.Addr{ip},
}
clientWithZone = &Persistent{
Name: "client_with_zone",
IPs: []netip.Addr{ipWithZone},
}
)
ci := newIDIndex([]*Persistent{
clientNoZone,
clientWithZone,
})
testCases := []struct {
ip netip.Addr
want *Persistent
name string
}{{
name: "without_zone",
ip: ip,
want: clientNoZone,
}, {
name: "with_zone",
ip: ipWithZone,
want: clientWithZone,
}, {
name: "zero_address",
ip: netip.Addr{},
want: nil,
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
c := ci.FindByIPWithoutZone(tc.ip.WithZone(""))
require.Equal(t, tc.want, c)
})
}
}
func TestClientIndex_RangeByName(t *testing.T) {
sortedClients := []*Persistent{{
Name: "clientA",
ClientIDs: []string{"A"},
}, {
Name: "clientB",
ClientIDs: []string{"B"},
}, {
Name: "clientC",
ClientIDs: []string{"C"},
}, {
Name: "clientD",
ClientIDs: []string{"D"},
}, {
Name: "clientE",
ClientIDs: []string{"E"},
}}
testCases := []struct {
name string
want []*Persistent
}{{
name: "basic",
want: sortedClients,
}, {
name: "nil",
want: nil,
}, {
name: "one_element",
want: sortedClients[:1],
}, {
name: "two_elements",
want: sortedClients[:2],
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
ci := newIDIndex(tc.want)
var got []*Persistent
ci.RangeByName(func(c *Persistent) (cont bool) {
got = append(got, c)
return true
})
assert.Equal(t, tc.want, got)
})
}
}

View File

@@ -1,4 +1,4 @@
package home
package client
import (
"encoding"
@@ -9,13 +9,13 @@ import (
"strings"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/dnsforward"
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/AdGuardHome/internal/filtering/safesearch"
"github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/golibs/container"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/stringutil"
"github.com/AdguardTeam/golibs/netutil"
"github.com/google/uuid"
)
@@ -30,6 +30,16 @@ func NewUID() (uid UID, err error) {
return UID(uuidv7), err
}
// MustNewUID is a wrapper around [NewUID] that panics if there is an error.
func MustNewUID() (uid UID) {
uid, err := NewUID()
if err != nil {
panic(fmt.Errorf("unexpected uuidv7 error: %w", err))
}
return uid
}
// type check
var _ encoding.TextMarshaler = UID{}
@@ -46,17 +56,15 @@ func (uid *UID) UnmarshalText(data []byte) error {
return (*uuid.UUID)(uid).UnmarshalText(data)
}
// persistentClient contains information about persistent clients.
type persistentClient struct {
// upstreamConfig is the custom upstream configuration for this client. If
// Persistent contains information about persistent clients.
type Persistent struct {
// UpstreamConfig is the custom upstream configuration for this client. If
// it's nil, it has not been initialized yet. If it's non-nil and empty,
// there are no valid upstreams. If it's non-nil and non-empty, these
// upstream must be used.
upstreamConfig *proxy.CustomUpstreamConfig
UpstreamConfig *proxy.CustomUpstreamConfig
// TODO(d.kolyshev): Make safeSearchConf a pointer.
safeSearchConf filtering.SafeSearchConfig
SafeSearch filtering.SafeSearch
SafeSearch filtering.SafeSearch
// BlockedServices is the configuration of blocked services of a client.
BlockedServices *filtering.BlockedServices
@@ -85,10 +93,13 @@ type persistentClient struct {
UseOwnBlockedServices bool
IgnoreQueryLog bool
IgnoreStatistics bool
// TODO(d.kolyshev): Make SafeSearchConf a pointer.
SafeSearchConf filtering.SafeSearchConfig
}
// setTags sets the tags if they are known, otherwise logs an unknown tag.
func (c *persistentClient) setTags(tags []string, known *stringutil.Set) {
// SetTags sets the tags if they are known, otherwise logs an unknown tag.
func (c *Persistent) SetTags(tags []string, known *container.MapSet[string]) {
for _, t := range tags {
if !known.Has(t) {
log.Info("skipping unknown tag %q", t)
@@ -102,9 +113,9 @@ func (c *persistentClient) setTags(tags []string, known *stringutil.Set) {
slices.Sort(c.Tags)
}
// setIDs parses a list of strings into typed fields and returns an error if
// SetIDs parses a list of strings into typed fields and returns an error if
// there is one.
func (c *persistentClient) setIDs(ids []string) (err error) {
func (c *Persistent) SetIDs(ids []string) (err error) {
for _, id := range ids {
err = c.setID(id)
if err != nil {
@@ -144,7 +155,7 @@ func subnetCompare(x, y netip.Prefix) (cmp int) {
}
// setID parses id into typed field if there is no error.
func (c *persistentClient) setID(id string) (err error) {
func (c *Persistent) setID(id string) (err error) {
if id == "" {
return errors.Error("clientid is empty")
}
@@ -170,7 +181,7 @@ func (c *persistentClient) setID(id string) (err error) {
return nil
}
err = dnsforward.ValidateClientID(id)
err = ValidateClientID(id)
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return err
@@ -181,9 +192,23 @@ func (c *persistentClient) setID(id string) (err error) {
return nil
}
// ids returns a list of client ids containing at least one element.
func (c *persistentClient) ids() (ids []string) {
ids = make([]string, 0, c.idsLen())
// ValidateClientID returns an error if id is not a valid ClientID.
//
// TODO(s.chzhen): It's an exact copy of the [dnsforward.ValidateClientID] to
// avoid the import cycle. Remove it.
func ValidateClientID(id string) (err error) {
err = netutil.ValidateHostnameLabel(id)
if err != nil {
// Replace the domain name label wrapper with our own.
return fmt.Errorf("invalid clientid %q: %w", id, errors.Unwrap(err))
}
return nil
}
// IDs returns a list of client IDs containing at least one element.
func (c *Persistent) IDs() (ids []string) {
ids = make([]string, 0, c.IDsLen())
for _, ip := range c.IPs {
ids = append(ids, ip.String())
@@ -200,24 +225,24 @@ func (c *persistentClient) ids() (ids []string) {
return append(ids, c.ClientIDs...)
}
// idsLen returns a length of client ids.
func (c *persistentClient) idsLen() (n int) {
// IDsLen returns a length of client ids.
func (c *Persistent) IDsLen() (n int) {
return len(c.IPs) + len(c.Subnets) + len(c.MACs) + len(c.ClientIDs)
}
// equalIDs returns true if the ids of the current and previous clients are the
// EqualIDs returns true if the ids of the current and previous clients are the
// same.
func (c *persistentClient) equalIDs(prev *persistentClient) (equal bool) {
func (c *Persistent) EqualIDs(prev *Persistent) (equal bool) {
return slices.Equal(c.IPs, prev.IPs) &&
slices.Equal(c.Subnets, prev.Subnets) &&
slices.EqualFunc(c.MACs, prev.MACs, slices.Equal[net.HardwareAddr]) &&
slices.Equal(c.ClientIDs, prev.ClientIDs)
}
// shallowClone returns a deep copy of the client, except upstreamConfig,
// ShallowClone returns a deep copy of the client, except upstreamConfig,
// safeSearchConf, SafeSearch fields, because it's difficult to copy them.
func (c *persistentClient) shallowClone() (clone *persistentClient) {
clone = &persistentClient{}
func (c *Persistent) ShallowClone() (clone *Persistent) {
clone = &Persistent{}
*clone = *c
clone.BlockedServices = c.BlockedServices.Clone()
@@ -232,10 +257,10 @@ func (c *persistentClient) shallowClone() (clone *persistentClient) {
return clone
}
// closeUpstreams closes the client-specific upstream config of c if any.
func (c *persistentClient) closeUpstreams() (err error) {
if c.upstreamConfig != nil {
if err = c.upstreamConfig.Close(); err != nil {
// 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)
}
}
@@ -243,8 +268,8 @@ func (c *persistentClient) closeUpstreams() (err error) {
return nil
}
// setSafeSearch initializes and sets the safe search filter for this client.
func (c *persistentClient) setSafeSearch(
// SetSafeSearch initializes and sets the safe search filter for this client.
func (c *Persistent) SetSafeSearch(
conf filtering.SafeSearchConfig,
cacheSize uint,
cacheTTL time.Duration,

View File

@@ -1,4 +1,4 @@
package home
package client
import (
"testing"
@@ -27,10 +27,10 @@ func TestPersistentClient_EqualIDs(t *testing.T) {
)
testCases := []struct {
want assert.BoolAssertionFunc
name string
ids []string
prevIDs []string
want assert.BoolAssertionFunc
}{{
name: "single_ip",
ids: []string{ip1},
@@ -110,15 +110,15 @@ func TestPersistentClient_EqualIDs(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
c := &persistentClient{}
err := c.setIDs(tc.ids)
c := &Persistent{}
err := c.SetIDs(tc.ids)
require.NoError(t, err)
prev := &persistentClient{}
err = prev.setIDs(tc.prevIDs)
prev := &Persistent{}
err = prev.SetIDs(tc.prevIDs)
require.NoError(t, err)
tc.want(t, c.equalIDs(prev))
tc.want(t, c.EqualIDs(prev))
})
}
}

View File

@@ -0,0 +1,63 @@
package client
import "net/netip"
// RuntimeIndex stores information about runtime clients.
type RuntimeIndex struct {
// index maps IP address to runtime client.
index map[netip.Addr]*Runtime
}
// NewRuntimeIndex returns initialized runtime index.
func NewRuntimeIndex() (ri *RuntimeIndex) {
return &RuntimeIndex{
index: map[netip.Addr]*Runtime{},
}
}
// Client returns the saved runtime client by ip. If no such client exists,
// returns nil.
func (ri *RuntimeIndex) Client(ip netip.Addr) (rc *Runtime) {
return ri.index[ip]
}
// Add saves the runtime client in the index. IP address of a client must be
// unique. See [Runtime.Client]. rc must not be nil.
func (ri *RuntimeIndex) Add(rc *Runtime) {
ip := rc.Addr()
ri.index[ip] = rc
}
// Size returns the number of the runtime clients.
func (ri *RuntimeIndex) Size() (n int) {
return len(ri.index)
}
// Range calls f for each runtime client in an undefined order.
func (ri *RuntimeIndex) Range(f func(rc *Runtime) (cont bool)) {
for _, rc := range ri.index {
if !f(rc) {
return
}
}
}
// Delete removes the runtime client by ip.
func (ri *RuntimeIndex) Delete(ip netip.Addr) {
delete(ri.index, ip)
}
// DeleteBySource removes all runtime clients that have information only from
// the specified source and returns the number of removed clients.
func (ri *RuntimeIndex) DeleteBySource(src Source) (n int) {
for ip, rc := range ri.index {
rc.unset(src)
if rc.isEmpty() {
delete(ri.index, ip)
n++
}
}
return n
}

View File

@@ -0,0 +1,85 @@
package client_test
import (
"net/netip"
"testing"
"github.com/AdguardTeam/AdGuardHome/internal/client"
"github.com/stretchr/testify/assert"
)
func TestRuntimeIndex(t *testing.T) {
const cliSrc = client.SourceARP
var (
ip1 = netip.MustParseAddr("1.1.1.1")
ip2 = netip.MustParseAddr("2.2.2.2")
ip3 = netip.MustParseAddr("3.3.3.3")
)
ri := client.NewRuntimeIndex()
currentSize := 0
testCases := []struct {
ip netip.Addr
name string
hosts []string
src client.Source
}{{
src: cliSrc,
ip: ip1,
name: "1",
hosts: []string{"host1"},
}, {
src: cliSrc,
ip: ip2,
name: "2",
hosts: []string{"host2"},
}, {
src: cliSrc,
ip: ip3,
name: "3",
hosts: []string{"host3"},
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
rc := client.NewRuntime(tc.ip)
rc.SetInfo(tc.src, tc.hosts)
ri.Add(rc)
currentSize++
got := ri.Client(tc.ip)
assert.Equal(t, rc, got)
})
}
t.Run("size", func(t *testing.T) {
assert.Equal(t, currentSize, ri.Size())
})
t.Run("range", func(t *testing.T) {
s := 0
ri.Range(func(rc *client.Runtime) (cont bool) {
s++
return true
})
assert.Equal(t, currentSize, s)
})
t.Run("delete", func(t *testing.T) {
ri.Delete(ip1)
currentSize--
assert.Equal(t, currentSize, ri.Size())
})
t.Run("delete_by_src", func(t *testing.T) {
assert.Equal(t, currentSize, ri.DeleteBySource(cliSrc))
assert.Equal(t, 0, ri.Size())
})
}

View File

@@ -1,5 +1,7 @@
package configmigrate
import "github.com/AdguardTeam/golibs/errors"
// migrateTo15 performs the following changes:
//
// # BEFORE:
@@ -43,7 +45,7 @@ func migrateTo15(diskConf yobj) (err error) {
}
diskConf["querylog"] = qlog
return coalesceError(
return errors.Join(
moveVal[bool](dns, qlog, "querylog_enabled", "enabled"),
moveVal[bool](dns, qlog, "querylog_file_enabled", "file_enabled"),
moveVal[any](dns, qlog, "querylog_interval", "interval"),

View File

@@ -1,5 +1,7 @@
package configmigrate
import "github.com/AdguardTeam/golibs/errors"
// migrateTo24 performs the following changes:
//
// # BEFORE:
@@ -28,7 +30,7 @@ func migrateTo24(diskConf yobj) (err error) {
diskConf["schema_version"] = 24
logObj := yobj{}
err = coalesceError(
err = errors.Join(
moveVal[string](diskConf, logObj, "log_file", "file"),
moveVal[int](diskConf, logObj, "log_max_backups", "max_backups"),
moveVal[int](diskConf, logObj, "log_max_size", "max_size"),

View File

@@ -1,5 +1,7 @@
package configmigrate
import "github.com/AdguardTeam/golibs/errors"
// migrateTo26 performs the following changes:
//
// # BEFORE:
@@ -78,7 +80,7 @@ func migrateTo26(diskConf yobj) (err error) {
}
filteringObj := yobj{}
err = coalesceError(
err = errors.Join(
moveSameVal[bool](dns, filteringObj, "filtering_enabled"),
moveSameVal[int](dns, filteringObj, "filters_update_interval"),
moveSameVal[bool](dns, filteringObj, "parental_enabled"),

View File

@@ -1,5 +1,7 @@
package configmigrate
import "github.com/AdguardTeam/golibs/errors"
// migrateTo7 performs the following changes:
//
// # BEFORE:
@@ -37,7 +39,7 @@ func migrateTo7(diskConf yobj) (err error) {
}
dhcpv4 := yobj{}
err = coalesceError(
err = errors.Join(
moveSameVal[string](dhcp, dhcpv4, "gateway_ip"),
moveSameVal[string](dhcp, dhcpv4, "subnet_mask"),
moveSameVal[string](dhcp, dhcpv4, "range_start"),

View File

@@ -50,19 +50,3 @@ func moveVal[T any](src, dst yobj, srcKey, dstKey string) (err error) {
func moveSameVal[T any](src, dst yobj, key string) (err error) {
return moveVal[T](src, dst, key, key)
}
// coalesceError returns the first non-nil error. It is named after function
// COALESCE in SQL. If all errors are nil, it returns nil.
//
// TODO(e.burkov): Replace with [errors.Join].
//
// TODO(a.garipov): Think of ways to merge with [aghalg.Coalesce].
func coalesceError(errors ...error) (res error) {
for _, err := range errors {
if err != nil {
return err
}
}
return nil
}

View File

@@ -14,7 +14,9 @@ import (
// Interface is a DHCP service.
//
// TODO(e.burkov): Separate HostByIP, MACByIP, IPByHost into a separate
// interface. This is also valid for Enabled method.
// interface. This is also applicable to Enabled method.
//
// TODO(e.burkov): Reconsider the requirements for the leases validity.
type Interface interface {
agh.ServiceWithConfig[*Config]
@@ -29,6 +31,8 @@ type Interface interface {
// MACByIP returns the MAC address for the given IP address leased. It
// returns nil if there is no such client, due to an assumption that a DHCP
// client must always have a MAC address.
//
// TODO(e.burkov): Think of a contract for the returned value.
MACByIP(ip netip.Addr) (mac net.HardwareAddr)
// IPByHost returns the IP address of the DHCP client with the given
@@ -44,17 +48,17 @@ type Interface interface {
// signatures instead of cloning the whole list.
Leases() (ls []*Lease)
// AddLease adds a new DHCP lease. It returns an error if the lease is
// invalid or already exists.
// AddLease adds a new DHCP lease. l must be valid. It returns an error if
// l already exists.
AddLease(l *Lease) (err error)
// UpdateStaticLease changes an existing DHCP lease. It returns an error if
// there is no lease with such hardware addressor if new values are invalid
// or already exist.
// UpdateStaticLease replaces an existing static DHCP lease. l must be
// valid. It returns an error if the lease with the given hardware address
// doesn't exist or if other values match another existing lease.
UpdateStaticLease(l *Lease) (err error)
// RemoveLease removes an existing DHCP lease. It returns an error if there
// is no lease equal to l.
// RemoveLease removes an existing DHCP lease. l must be valid. It returns
// an error if there is no lease equal to l.
RemoveLease(l *Lease) (err error)
// Reset removes all the DHCP leases.

View File

@@ -38,3 +38,29 @@ func (iface *netInterface) insertLease(l *Lease) (err error) {
return nil
}
// updateLease replaces an existing lease within iface with the given one. It
// returns an error if there is no lease with such hardware address.
func (iface *netInterface) updateLease(l *Lease) (prev *Lease, err error) {
i, found := slices.BinarySearchFunc(iface.leases, l, compareLeaseMAC)
if !found {
return nil, fmt.Errorf("no lease for mac %s", l.HWAddr)
}
prev, iface.leases[i] = iface.leases[i], l
return prev, nil
}
// removeLease removes an existing lease from iface. It returns an error if
// there is no lease equal to l.
func (iface *netInterface) removeLease(l *Lease) (err error) {
i, found := slices.BinarySearchFunc(iface.leases, l, compareLeaseMAC)
if !found {
return fmt.Errorf("no lease for mac %s", l.HWAddr)
}
iface.leases = slices.Delete(iface.leases, i, i+1)
return nil
}

View File

@@ -0,0 +1,126 @@
package dhcpsvc
import (
"fmt"
"net/netip"
"slices"
"strings"
)
// leaseIndex is the set of leases indexed by their identifiers for quick
// lookup.
type leaseIndex struct {
// byAddr is a lookup shortcut for leases by their IP addresses.
byAddr map[netip.Addr]*Lease
// byName is a lookup shortcut for leases by their hostnames.
//
// TODO(e.burkov): Use a slice of leases with the same hostname?
byName map[string]*Lease
}
// newLeaseIndex returns a new index for [Lease]s.
func newLeaseIndex() *leaseIndex {
return &leaseIndex{
byAddr: map[netip.Addr]*Lease{},
byName: map[string]*Lease{},
}
}
// leaseByAddr returns a lease by its IP address.
func (idx *leaseIndex) leaseByAddr(addr netip.Addr) (l *Lease, ok bool) {
l, ok = idx.byAddr[addr]
return l, ok
}
// leaseByName returns a lease by its hostname.
func (idx *leaseIndex) leaseByName(name string) (l *Lease, ok bool) {
// TODO(e.burkov): Probably, use a case-insensitive comparison and store in
// slice. This would require a benchmark.
l, ok = idx.byName[strings.ToLower(name)]
return l, ok
}
// clear removes all leases from idx.
func (idx *leaseIndex) clear() {
clear(idx.byAddr)
clear(idx.byName)
}
// add adds l into idx and into iface. l must be valid, iface should be
// responsible for l's IP. It returns an error if l duplicates at least a
// single value of another lease.
func (idx *leaseIndex) add(l *Lease, iface *netInterface) (err error) {
loweredName := strings.ToLower(l.Hostname)
if _, ok := idx.byAddr[l.IP]; ok {
return fmt.Errorf("lease for ip %s already exists", l.IP)
} else if _, ok = idx.byName[loweredName]; ok {
return fmt.Errorf("lease for hostname %s already exists", l.Hostname)
}
err = iface.insertLease(l)
if err != nil {
return err
}
idx.byAddr[l.IP] = l
idx.byName[loweredName] = l
return nil
}
// remove removes l from idx and from iface. l must be valid, iface should
// contain the same lease or the lease itself. It returns an error if the lease
// not found.
func (idx *leaseIndex) remove(l *Lease, iface *netInterface) (err error) {
loweredName := strings.ToLower(l.Hostname)
if _, ok := idx.byAddr[l.IP]; !ok {
return fmt.Errorf("no lease for ip %s", l.IP)
} else if _, ok = idx.byName[loweredName]; !ok {
return fmt.Errorf("no lease for hostname %s", l.Hostname)
}
err = iface.removeLease(l)
if err != nil {
return err
}
delete(idx.byAddr, l.IP)
delete(idx.byName, loweredName)
return nil
}
// update updates l in idx and in iface. l must be valid, iface should be
// responsible for l's IP. It returns an error if l duplicates at least a
// single value of another lease, except for the updated lease itself.
func (idx *leaseIndex) update(l *Lease, iface *netInterface) (err error) {
loweredName := strings.ToLower(l.Hostname)
existing, ok := idx.byAddr[l.IP]
if ok && !slices.Equal(l.HWAddr, existing.HWAddr) {
return fmt.Errorf("lease for ip %s already exists", l.IP)
}
existing, ok = idx.byName[loweredName]
if ok && !slices.Equal(l.HWAddr, existing.HWAddr) {
return fmt.Errorf("lease for hostname %s already exists", l.Hostname)
}
prev, err := iface.updateLease(l)
if err != nil {
return err
}
delete(idx.byAddr, prev.IP)
delete(idx.byName, strings.ToLower(prev.Hostname))
idx.byAddr[l.IP] = l
idx.byName[loweredName] = l
return nil
}

View File

@@ -5,11 +5,11 @@ import (
"net"
"net/netip"
"slices"
"strings"
"sync"
"sync/atomic"
"time"
"github.com/AdguardTeam/golibs/errors"
"golang.org/x/exp/maps"
)
@@ -23,17 +23,11 @@ type DHCPServer struct {
// hostnames.
localTLD string
// leasesMu protects the ipIndex and nameIndex fields against concurrent
// access, as well as leaseHandlers within the interfaces.
// leasesMu protects the leases index as well as leases in the interfaces.
leasesMu *sync.RWMutex
// leaseByIP is a lookup shortcut for leases by their IP addresses.
leaseByIP map[netip.Addr]*Lease
// leaseByName is a lookup shortcut for leases by their hostnames.
//
// TODO(e.burkov): Use a slice of leases with the same hostname?
leaseByName map[string]*Lease
// leases stores the DHCP leases for quick lookups.
leases *leaseIndex
// interfaces4 is the set of IPv4 interfaces sorted by interface name.
interfaces4 netInterfacesV4
@@ -88,8 +82,7 @@ func New(conf *Config) (srv *DHCPServer, err error) {
enabled: enabled,
localTLD: conf.LocalDomainName,
leasesMu: &sync.RWMutex{},
leaseByIP: map[netip.Addr]*Lease{},
leaseByName: map[string]*Lease{},
leases: newLeaseIndex(),
interfaces4: ifaces4,
interfaces6: ifaces6,
icmpTimeout: conf.ICMPTimeout,
@@ -120,6 +113,11 @@ func (srv *DHCPServer) Leases() (leases []*Lease) {
leases = append(leases, lease.Clone())
}
}
for _, iface := range srv.interfaces6 {
for _, lease := range iface.leases {
leases = append(leases, lease.Clone())
}
}
return leases
}
@@ -129,7 +127,7 @@ func (srv *DHCPServer) HostByIP(ip netip.Addr) (host string) {
srv.leasesMu.RLock()
defer srv.leasesMu.RUnlock()
if l, ok := srv.leaseByIP[ip]; ok {
if l, ok := srv.leases.leaseByAddr(ip); ok {
return l.Hostname
}
@@ -141,7 +139,7 @@ func (srv *DHCPServer) MACByIP(ip netip.Addr) (mac net.HardwareAddr) {
srv.leasesMu.RLock()
defer srv.leasesMu.RUnlock()
if l, ok := srv.leaseByIP[ip]; ok {
if l, ok := srv.leases.leaseByAddr(ip); ok {
return l.HWAddr
}
@@ -150,12 +148,10 @@ func (srv *DHCPServer) MACByIP(ip netip.Addr) (mac net.HardwareAddr) {
// IPByHost implements the [Interface] interface for *DHCPServer.
func (srv *DHCPServer) IPByHost(host string) (ip netip.Addr) {
lowered := strings.ToLower(host)
srv.leasesMu.RLock()
defer srv.leasesMu.RUnlock()
if l, ok := srv.leaseByName[lowered]; ok {
if l, ok := srv.leases.leaseByName(host); ok {
return l.IP
}
@@ -173,39 +169,76 @@ func (srv *DHCPServer) Reset() (err error) {
for _, iface := range srv.interfaces6 {
iface.reset()
}
clear(srv.leaseByIP)
clear(srv.leaseByName)
srv.leases.clear()
return nil
}
// AddLease implements the [Interface] interface for *DHCPServer.
func (srv *DHCPServer) AddLease(l *Lease) (err error) {
var ok bool
var iface *netInterface
defer func() { err = errors.Annotate(err, "adding lease: %w") }()
addr := l.IP
iface, err := srv.ifaceForAddr(addr)
if err != nil {
// Don't wrap the error since there is already an annotation deferred.
return err
}
srv.leasesMu.Lock()
defer srv.leasesMu.Unlock()
return srv.leases.add(l, iface)
}
// UpdateStaticLease implements the [Interface] interface for *DHCPServer.
//
// TODO(e.burkov): Support moving leases between interfaces.
func (srv *DHCPServer) UpdateStaticLease(l *Lease) (err error) {
defer func() { err = errors.Annotate(err, "updating static lease: %w") }()
addr := l.IP
iface, err := srv.ifaceForAddr(addr)
if err != nil {
// Don't wrap the error since there is already an annotation deferred.
return err
}
srv.leasesMu.Lock()
defer srv.leasesMu.Unlock()
return srv.leases.update(l, iface)
}
// RemoveLease implements the [Interface] interface for *DHCPServer.
func (srv *DHCPServer) RemoveLease(l *Lease) (err error) {
defer func() { err = errors.Annotate(err, "removing lease: %w") }()
addr := l.IP
iface, err := srv.ifaceForAddr(addr)
if err != nil {
// Don't wrap the error since there is already an annotation deferred.
return err
}
srv.leasesMu.Lock()
defer srv.leasesMu.Unlock()
return srv.leases.remove(l, iface)
}
// ifaceForAddr returns the handled network interface for the given IP address,
// or an error if no such interface exists.
func (srv *DHCPServer) ifaceForAddr(addr netip.Addr) (iface *netInterface, err error) {
var ok bool
if addr.Is4() {
iface, ok = srv.interfaces4.find(addr)
} else {
iface, ok = srv.interfaces6.find(addr)
}
if !ok {
return fmt.Errorf("no interface for IP address %s", addr)
return nil, fmt.Errorf("no interface for ip %s", addr)
}
srv.leasesMu.Lock()
defer srv.leasesMu.Unlock()
err = iface.insertLease(l)
if err != nil {
return err
}
srv.leaseByIP[l.IP] = l
srv.leaseByName[strings.ToLower(l.Hostname)] = l
return nil
return iface, nil
}

View File

@@ -3,6 +3,7 @@ package dhcpsvc_test
import (
"net"
"net/netip"
"strings"
"testing"
"time"
@@ -15,6 +16,52 @@ import (
// testLocalTLD is a common local TLD for tests.
const testLocalTLD = "local"
// testInterfaceConf is a common set of interface configurations for tests.
var testInterfaceConf = map[string]*dhcpsvc.InterfaceConfig{
"eth0": {
IPv4: &dhcpsvc.IPv4Config{
Enabled: true,
GatewayIP: netip.MustParseAddr("192.168.0.1"),
SubnetMask: netip.MustParseAddr("255.255.255.0"),
RangeStart: netip.MustParseAddr("192.168.0.2"),
RangeEnd: netip.MustParseAddr("192.168.0.254"),
LeaseDuration: 1 * time.Hour,
},
IPv6: &dhcpsvc.IPv6Config{
Enabled: true,
RangeStart: netip.MustParseAddr("2001:db8::1"),
LeaseDuration: 1 * time.Hour,
RAAllowSLAAC: true,
RASLAACOnly: true,
},
},
"eth1": {
IPv4: &dhcpsvc.IPv4Config{
Enabled: true,
GatewayIP: netip.MustParseAddr("172.16.0.1"),
SubnetMask: netip.MustParseAddr("255.255.255.0"),
RangeStart: netip.MustParseAddr("172.16.0.2"),
RangeEnd: netip.MustParseAddr("172.16.0.255"),
LeaseDuration: 1 * time.Hour,
},
IPv6: &dhcpsvc.IPv6Config{
Enabled: true,
RangeStart: netip.MustParseAddr("2001:db9::1"),
LeaseDuration: 1 * time.Hour,
RAAllowSLAAC: true,
RASLAACOnly: true,
},
},
}
// mustParseMAC parses a hardware address from s and requires no errors.
func mustParseMAC(t require.TestingT, s string) (mac net.HardwareAddr) {
mac, err := net.ParseMAC(s)
require.NoError(t, err)
return mac
}
func TestNew(t *testing.T) {
validIPv4Conf := &dhcpsvc.IPv4Config{
Enabled: true,
@@ -117,46 +164,113 @@ func TestNew(t *testing.T) {
}
}
func TestDHCPServer_AddLease(t *testing.T) {
srv, err := dhcpsvc.New(&dhcpsvc.Config{
Enabled: true,
LocalDomainName: testLocalTLD,
Interfaces: testInterfaceConf,
})
require.NoError(t, err)
const (
host1 = "host1"
host2 = "host2"
host3 = "host3"
)
ip1 := netip.MustParseAddr("192.168.0.2")
ip2 := netip.MustParseAddr("192.168.0.3")
ip3 := netip.MustParseAddr("2001:db8::2")
mac1 := mustParseMAC(t, "01:02:03:04:05:06")
mac2 := mustParseMAC(t, "06:05:04:03:02:01")
mac3 := mustParseMAC(t, "02:03:04:05:06:07")
require.NoError(t, srv.AddLease(&dhcpsvc.Lease{
Hostname: host1,
IP: ip1,
HWAddr: mac1,
IsStatic: true,
}))
testCases := []struct {
name string
lease *dhcpsvc.Lease
wantErrMsg string
}{{
name: "outside_range",
lease: &dhcpsvc.Lease{
Hostname: host2,
IP: netip.MustParseAddr("1.2.3.4"),
HWAddr: mac2,
},
wantErrMsg: "adding lease: no interface for ip 1.2.3.4",
}, {
name: "duplicate_ip",
lease: &dhcpsvc.Lease{
Hostname: host2,
IP: ip1,
HWAddr: mac2,
},
wantErrMsg: "adding lease: lease for ip " + ip1.String() +
" already exists",
}, {
name: "duplicate_hostname",
lease: &dhcpsvc.Lease{
Hostname: host1,
IP: ip2,
HWAddr: mac2,
},
wantErrMsg: "adding lease: lease for hostname " + host1 +
" already exists",
}, {
name: "duplicate_hostname_case",
lease: &dhcpsvc.Lease{
Hostname: strings.ToUpper(host1),
IP: ip2,
HWAddr: mac2,
},
wantErrMsg: "adding lease: lease for hostname " +
strings.ToUpper(host1) + " already exists",
}, {
name: "duplicate_mac",
lease: &dhcpsvc.Lease{
Hostname: host2,
IP: ip2,
HWAddr: mac1,
},
wantErrMsg: "adding lease: lease for mac " + mac1.String() +
" already exists",
}, {
name: "valid",
lease: &dhcpsvc.Lease{
Hostname: host2,
IP: ip2,
HWAddr: mac2,
},
wantErrMsg: "",
}, {
name: "valid_v6",
lease: &dhcpsvc.Lease{
Hostname: host3,
IP: ip3,
HWAddr: mac3,
},
wantErrMsg: "",
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
testutil.AssertErrorMsg(t, tc.wantErrMsg, srv.AddLease(tc.lease))
})
}
}
func TestDHCPServer_index(t *testing.T) {
srv, err := dhcpsvc.New(&dhcpsvc.Config{
Enabled: true,
LocalDomainName: testLocalTLD,
Interfaces: map[string]*dhcpsvc.InterfaceConfig{
"eth0": {
IPv4: &dhcpsvc.IPv4Config{
Enabled: true,
GatewayIP: netip.MustParseAddr("192.168.0.1"),
SubnetMask: netip.MustParseAddr("255.255.255.0"),
RangeStart: netip.MustParseAddr("192.168.0.2"),
RangeEnd: netip.MustParseAddr("192.168.0.254"),
LeaseDuration: 1 * time.Hour,
},
IPv6: &dhcpsvc.IPv6Config{
Enabled: true,
RangeStart: netip.MustParseAddr("2001:db8::1"),
LeaseDuration: 1 * time.Hour,
RAAllowSLAAC: true,
RASLAACOnly: true,
},
},
"eth1": {
IPv4: &dhcpsvc.IPv4Config{
Enabled: true,
GatewayIP: netip.MustParseAddr("172.16.0.1"),
SubnetMask: netip.MustParseAddr("255.255.255.0"),
RangeStart: netip.MustParseAddr("172.16.0.2"),
RangeEnd: netip.MustParseAddr("172.16.0.255"),
LeaseDuration: 1 * time.Hour,
},
IPv6: &dhcpsvc.IPv6Config{
Enabled: true,
RangeStart: netip.MustParseAddr("2001:db9::1"),
LeaseDuration: 1 * time.Hour,
RAAllowSLAAC: true,
RASLAACOnly: true,
},
},
},
Interfaces: testInterfaceConf,
})
require.NoError(t, err)
@@ -173,9 +287,9 @@ func TestDHCPServer_index(t *testing.T) {
ip3 := netip.MustParseAddr("172.16.0.3")
ip4 := netip.MustParseAddr("172.16.0.4")
mac1 := net.HardwareAddr{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}
mac2 := net.HardwareAddr{0x06, 0x05, 0x04, 0x03, 0x02, 0x01}
mac3 := net.HardwareAddr{0x05, 0x04, 0x03, 0x02, 0x01, 0x00}
mac1 := mustParseMAC(t, "01:02:03:04:05:06")
mac2 := mustParseMAC(t, "06:05:04:03:02:01")
mac3 := mustParseMAC(t, "02:03:04:05:06:07")
leases := []*dhcpsvc.Lease{{
Hostname: host1,
@@ -226,3 +340,256 @@ func TestDHCPServer_index(t *testing.T) {
assert.Nil(t, srv.MACByIP(netip.Addr{}))
})
}
func TestDHCPServer_UpdateStaticLease(t *testing.T) {
srv, err := dhcpsvc.New(&dhcpsvc.Config{
Enabled: true,
LocalDomainName: testLocalTLD,
Interfaces: testInterfaceConf,
})
require.NoError(t, err)
const (
host1 = "host1"
host2 = "host2"
host3 = "host3"
host4 = "host4"
host5 = "host5"
host6 = "host6"
)
ip1 := netip.MustParseAddr("192.168.0.2")
ip2 := netip.MustParseAddr("192.168.0.3")
ip3 := netip.MustParseAddr("192.168.0.4")
ip4 := netip.MustParseAddr("2001:db8::2")
ip5 := netip.MustParseAddr("2001:db8::3")
mac1 := mustParseMAC(t, "01:02:03:04:05:06")
mac2 := mustParseMAC(t, "01:02:03:04:05:07")
mac3 := mustParseMAC(t, "06:05:04:03:02:01")
mac4 := mustParseMAC(t, "06:05:04:03:02:02")
leases := []*dhcpsvc.Lease{{
Hostname: host1,
IP: ip1,
HWAddr: mac1,
IsStatic: true,
}, {
Hostname: host2,
IP: ip2,
HWAddr: mac2,
IsStatic: true,
}, {
Hostname: host4,
IP: ip4,
HWAddr: mac4,
IsStatic: true,
}}
for _, l := range leases {
require.NoError(t, srv.AddLease(l))
}
testCases := []struct {
name string
lease *dhcpsvc.Lease
wantErrMsg string
}{{
name: "outside_range",
lease: &dhcpsvc.Lease{
Hostname: host1,
IP: netip.MustParseAddr("1.2.3.4"),
HWAddr: mac1,
},
wantErrMsg: "updating static lease: no interface for ip 1.2.3.4",
}, {
name: "not_found",
lease: &dhcpsvc.Lease{
Hostname: host3,
IP: ip3,
HWAddr: mac3,
},
wantErrMsg: "updating static lease: no lease for mac " + mac3.String(),
}, {
name: "duplicate_ip",
lease: &dhcpsvc.Lease{
Hostname: host1,
IP: ip2,
HWAddr: mac1,
},
wantErrMsg: "updating static lease: lease for ip " + ip2.String() +
" already exists",
}, {
name: "duplicate_hostname",
lease: &dhcpsvc.Lease{
Hostname: host2,
IP: ip1,
HWAddr: mac1,
},
wantErrMsg: "updating static lease: lease for hostname " + host2 +
" already exists",
}, {
name: "duplicate_hostname_case",
lease: &dhcpsvc.Lease{
Hostname: strings.ToUpper(host2),
IP: ip1,
HWAddr: mac1,
},
wantErrMsg: "updating static lease: lease for hostname " +
strings.ToUpper(host2) + " already exists",
}, {
name: "valid",
lease: &dhcpsvc.Lease{
Hostname: host3,
IP: ip3,
HWAddr: mac1,
},
wantErrMsg: "",
}, {
name: "valid_v6",
lease: &dhcpsvc.Lease{
Hostname: host6,
IP: ip5,
HWAddr: mac4,
},
wantErrMsg: "",
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
testutil.AssertErrorMsg(t, tc.wantErrMsg, srv.UpdateStaticLease(tc.lease))
})
}
}
func TestDHCPServer_RemoveLease(t *testing.T) {
srv, err := dhcpsvc.New(&dhcpsvc.Config{
Enabled: true,
LocalDomainName: testLocalTLD,
Interfaces: testInterfaceConf,
})
require.NoError(t, err)
const (
host1 = "host1"
host2 = "host2"
host3 = "host3"
)
ip1 := netip.MustParseAddr("192.168.0.2")
ip2 := netip.MustParseAddr("192.168.0.3")
ip3 := netip.MustParseAddr("2001:db8::2")
mac1 := mustParseMAC(t, "01:02:03:04:05:06")
mac2 := mustParseMAC(t, "02:03:04:05:06:07")
mac3 := mustParseMAC(t, "06:05:04:03:02:01")
leases := []*dhcpsvc.Lease{{
Hostname: host1,
IP: ip1,
HWAddr: mac1,
IsStatic: true,
}, {
Hostname: host3,
IP: ip3,
HWAddr: mac3,
IsStatic: true,
}}
for _, l := range leases {
require.NoError(t, srv.AddLease(l))
}
testCases := []struct {
name string
lease *dhcpsvc.Lease
wantErrMsg string
}{{
name: "not_found_mac",
lease: &dhcpsvc.Lease{
Hostname: host1,
IP: ip1,
HWAddr: mac2,
},
wantErrMsg: "removing lease: no lease for mac " + mac2.String(),
}, {
name: "not_found_ip",
lease: &dhcpsvc.Lease{
Hostname: host1,
IP: ip2,
HWAddr: mac1,
},
wantErrMsg: "removing lease: no lease for ip " + ip2.String(),
}, {
name: "not_found_host",
lease: &dhcpsvc.Lease{
Hostname: host2,
IP: ip1,
HWAddr: mac1,
},
wantErrMsg: "removing lease: no lease for hostname " + host2,
}, {
name: "valid",
lease: &dhcpsvc.Lease{
Hostname: host1,
IP: ip1,
HWAddr: mac1,
},
wantErrMsg: "",
}, {
name: "valid_v6",
lease: &dhcpsvc.Lease{
Hostname: host3,
IP: ip3,
HWAddr: mac3,
},
wantErrMsg: "",
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
testutil.AssertErrorMsg(t, tc.wantErrMsg, srv.RemoveLease(tc.lease))
})
}
assert.Empty(t, srv.Leases())
}
func TestDHCPServer_Reset(t *testing.T) {
srv, err := dhcpsvc.New(&dhcpsvc.Config{
Enabled: true,
LocalDomainName: testLocalTLD,
Interfaces: testInterfaceConf,
})
require.NoError(t, err)
leases := []*dhcpsvc.Lease{{
Hostname: "host1",
IP: netip.MustParseAddr("192.168.0.2"),
HWAddr: mustParseMAC(t, "01:02:03:04:05:06"),
IsStatic: true,
}, {
Hostname: "host2",
IP: netip.MustParseAddr("192.168.0.3"),
HWAddr: mustParseMAC(t, "06:05:04:03:02:01"),
IsStatic: true,
}, {
Hostname: "host3",
IP: netip.MustParseAddr("2001:db8::2"),
HWAddr: mustParseMAC(t, "02:03:04:05:06:07"),
IsStatic: true,
}, {
Hostname: "host4",
IP: netip.MustParseAddr("2001:db8::3"),
HWAddr: mustParseMAC(t, "06:05:04:03:02:02"),
IsStatic: true,
}}
for _, l := range leases {
require.NoError(t, srv.AddLease(l))
}
require.Len(t, srv.Leases(), len(leases))
require.NoError(t, srv.Reset())
assert.Empty(t, srv.Leases())
}

View File

@@ -150,7 +150,7 @@ func (ifaces netInterfacesV6) find(ip netip.Addr) (iface6 *netInterface, ok bool
const prefLen = netutil.IPv6BitLen - 8
i := slices.IndexFunc(ifaces, func(iface *netInterfaceV6) (contains bool) {
return !iface.rangeStart.Less(ip) &&
return !ip.Less(iface.rangeStart) &&
netip.PrefixFrom(iface.rangeStart, prefLen).Contains(ip)
})
if i < 0 {

View File

@@ -5,10 +5,12 @@ import (
"fmt"
"net/http"
"net/netip"
"slices"
"strings"
"github.com/AdguardTeam/AdGuardHome/internal/aghalg"
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
"github.com/AdguardTeam/golibs/container"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/stringutil"
"github.com/AdguardTeam/urlfilter"
@@ -16,22 +18,19 @@ import (
"github.com/AdguardTeam/urlfilter/rules"
)
// unit is a convenient alias for struct{}
type unit = struct{}
// accessManager controls IP and client blocking that takes place before all
// other processing. An accessManager is safe for concurrent use.
type accessManager struct {
allowedIPs map[netip.Addr]unit
blockedIPs map[netip.Addr]unit
allowedIPs *container.MapSet[netip.Addr]
blockedIPs *container.MapSet[netip.Addr]
allowedClientIDs *stringutil.Set
blockedClientIDs *stringutil.Set
allowedClientIDs *container.MapSet[string]
blockedClientIDs *container.MapSet[string]
// TODO(s.chzhen): Use [aghnet.IgnoreEngine].
blockedHostsEng *urlfilter.DNSEngine
// TODO(a.garipov): Create a type for a set of IP networks.
// TODO(a.garipov): Create a type for an efficient tree set of IP networks.
allowedNets []netip.Prefix
blockedNets []netip.Prefix
}
@@ -40,15 +39,15 @@ type accessManager struct {
// which may be an IP address, a CIDR, or a ClientID.
func processAccessClients(
clientStrs []string,
ips map[netip.Addr]unit,
ips *container.MapSet[netip.Addr],
nets *[]netip.Prefix,
clientIDs *stringutil.Set,
clientIDs *container.MapSet[string],
) (err error) {
for i, s := range clientStrs {
var ip netip.Addr
var ipnet netip.Prefix
if ip, err = netip.ParseAddr(s); err == nil {
ips[ip] = unit{}
ips.Add(ip)
} else if ipnet, err = netip.ParsePrefix(s); err == nil {
*nets = append(*nets, ipnet)
} else {
@@ -67,11 +66,11 @@ func processAccessClients(
// newAccessCtx creates a new accessCtx.
func newAccessCtx(allowed, blocked, blockedHosts []string) (a *accessManager, err error) {
a = &accessManager{
allowedIPs: map[netip.Addr]unit{},
blockedIPs: map[netip.Addr]unit{},
allowedIPs: container.NewMapSet[netip.Addr](),
blockedIPs: container.NewMapSet[netip.Addr](),
allowedClientIDs: stringutil.NewSet(),
blockedClientIDs: stringutil.NewSet(),
allowedClientIDs: container.NewMapSet[string](),
blockedClientIDs: container.NewMapSet[string](),
}
err = processAccessClients(allowed, a.allowedIPs, &a.allowedNets, a.allowedClientIDs)
@@ -109,7 +108,7 @@ func newAccessCtx(allowed, blocked, blockedHosts []string) (a *accessManager, er
// allowlistMode returns true if this *accessCtx is in the allowlist mode.
func (a *accessManager) allowlistMode() (ok bool) {
return len(a.allowedIPs) != 0 || a.allowedClientIDs.Len() != 0 || len(a.allowedNets) != 0
return a.allowedIPs.Len() != 0 || a.allowedClientIDs.Len() != 0 || len(a.allowedNets) != 0
}
// isBlockedClientID returns true if the ClientID should be blocked.
@@ -152,12 +151,15 @@ func (a *accessManager) isBlockedIP(ip netip.Addr) (blocked bool, rule string) {
ipnets = a.allowedNets
}
if _, ok := ips[ip]; ok {
if ips.Has(ip) {
return blocked, ip.String()
}
for _, ipnet := range ipnets {
if ipnet.Contains(ip) {
// Remove zone before checking because prefixes stip zones.
//
// TODO(d.kolyshev): Cover with tests.
if ipnet.Contains(ip.WithZone("")) {
return blocked, ipnet.String()
}
}
@@ -176,9 +178,9 @@ func (s *Server) accessListJSON() (j accessListJSON) {
defer s.serverLock.RUnlock()
return accessListJSON{
AllowedClients: stringutil.CloneSlice(s.conf.AllowedClients),
DisallowedClients: stringutil.CloneSlice(s.conf.DisallowedClients),
BlockedHosts: stringutil.CloneSlice(s.conf.BlockedHosts),
AllowedClients: slices.Clone(s.conf.AllowedClients),
DisallowedClients: slices.Clone(s.conf.DisallowedClients),
BlockedHosts: slices.Clone(s.conf.BlockedHosts),
}
}

View File

@@ -0,0 +1,116 @@
package dnsforward
import (
"encoding/binary"
"fmt"
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
"github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/miekg/dns"
)
// type check
var _ proxy.BeforeRequestHandler = (*Server)(nil)
// HandleBefore is the handler that is called before any other processing,
// including logs. It performs access checks and puts the client ID, if there
// is one, into the server's cache.
//
// TODO(d.kolyshev): Extract to separate package.
func (s *Server) HandleBefore(
_ *proxy.Proxy,
pctx *proxy.DNSContext,
) (err error) {
clientID, err := s.clientIDFromDNSContext(pctx)
if err != nil {
return &proxy.BeforeRequestError{
Err: fmt.Errorf("getting clientid: %w", err),
Response: s.NewMsgSERVFAIL(pctx.Req),
}
}
blocked, _ := s.IsBlockedClient(pctx.Addr.Addr(), clientID)
if blocked {
return s.preBlockedResponse(pctx)
}
if len(pctx.Req.Question) == 1 {
q := pctx.Req.Question[0]
qt := q.Qtype
host := aghnet.NormalizeDomain(q.Name)
if s.access.isBlockedHost(host, qt) {
log.Debug("access: request %s %s is in access blocklist", dns.Type(qt), host)
return s.preBlockedResponse(pctx)
}
}
if clientID != "" {
key := [8]byte{}
binary.BigEndian.PutUint64(key[:], pctx.RequestID)
s.clientIDCache.Set(key[:], []byte(clientID))
}
return nil
}
// clientIDFromDNSContext extracts the client's ID from the server name of the
// client's DoT or DoQ request or the path of the client's DoH. If the protocol
// is not one of these, clientID is an empty string and err is nil.
func (s *Server) clientIDFromDNSContext(pctx *proxy.DNSContext) (clientID string, err error) {
proto := pctx.Proto
if proto == proxy.ProtoHTTPS {
clientID, err = clientIDFromDNSContextHTTPS(pctx)
if err != nil {
return "", fmt.Errorf("checking url: %w", err)
} else if clientID != "" {
return clientID, nil
}
// Go on and check the domain name as well.
} else if proto != proxy.ProtoTLS && proto != proxy.ProtoQUIC {
return "", nil
}
hostSrvName := s.conf.ServerName
if hostSrvName == "" {
return "", nil
}
cliSrvName, err := clientServerName(pctx, proto)
if err != nil {
return "", err
}
clientID, err = clientIDFromClientServerName(
hostSrvName,
cliSrvName,
s.conf.StrictSNICheck,
)
if err != nil {
return "", fmt.Errorf("clientid check: %w", err)
}
return clientID, nil
}
// errAccessBlocked is a sentinel error returned when a request is blocked by
// access settings.
var errAccessBlocked errors.Error = "blocked by access settings"
// preBlockedResponse returns a protocol-appropriate response for a request that
// was blocked by access settings.
func (s *Server) preBlockedResponse(pctx *proxy.DNSContext) (err error) {
if pctx.Proto == proxy.ProtoUDP || pctx.Proto == proxy.ProtoDNSCrypt {
// Return nil so that dnsproxy drops the connection and thus
// prevent DNS amplification attacks.
return errAccessBlocked
}
return &proxy.BeforeRequestError{
Err: errAccessBlocked,
Response: s.makeResponseREFUSED(pctx.Req),
}
}

View File

@@ -0,0 +1,299 @@
package dnsforward
import (
"crypto/tls"
"net"
"testing"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/aghtest"
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/dnsproxy/proxy"
"github.com/miekg/dns"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
const (
blockedHost = "blockedhost.org"
testFQDN = "example.org."
dnsClientTimeout = 200 * time.Millisecond
)
func TestServer_HandleBefore_tls(t *testing.T) {
t.Parallel()
const clientID = "client-1"
testCases := []struct {
clientSrvName string
name string
host string
allowedClients []string
disallowedClients []string
blockedHosts []string
wantRCode int
}{{
clientSrvName: tlsServerName,
name: "allow_all",
host: testFQDN,
allowedClients: []string{},
disallowedClients: []string{},
blockedHosts: []string{},
wantRCode: dns.RcodeSuccess,
}, {
clientSrvName: "%" + "." + tlsServerName,
name: "invalid_client_id",
host: testFQDN,
allowedClients: []string{},
disallowedClients: []string{},
blockedHosts: []string{},
wantRCode: dns.RcodeServerFailure,
}, {
clientSrvName: clientID + "." + tlsServerName,
name: "allowed_client_allowed",
host: testFQDN,
allowedClients: []string{clientID},
disallowedClients: []string{},
blockedHosts: []string{},
wantRCode: dns.RcodeSuccess,
}, {
clientSrvName: "client-2." + tlsServerName,
name: "allowed_client_rejected",
host: testFQDN,
allowedClients: []string{clientID},
disallowedClients: []string{},
blockedHosts: []string{},
wantRCode: dns.RcodeRefused,
}, {
clientSrvName: tlsServerName,
name: "disallowed_client_allowed",
host: testFQDN,
allowedClients: []string{},
disallowedClients: []string{clientID},
blockedHosts: []string{},
wantRCode: dns.RcodeSuccess,
}, {
clientSrvName: clientID + "." + tlsServerName,
name: "disallowed_client_rejected",
host: testFQDN,
allowedClients: []string{},
disallowedClients: []string{clientID},
blockedHosts: []string{},
wantRCode: dns.RcodeRefused,
}, {
clientSrvName: tlsServerName,
name: "blocked_hosts_allowed",
host: testFQDN,
allowedClients: []string{},
disallowedClients: []string{},
blockedHosts: []string{blockedHost},
wantRCode: dns.RcodeSuccess,
}, {
clientSrvName: tlsServerName,
name: "blocked_hosts_rejected",
host: dns.Fqdn(blockedHost),
allowedClients: []string{},
disallowedClients: []string{},
blockedHosts: []string{blockedHost},
wantRCode: dns.RcodeRefused,
}}
localAns := []dns.RR{&dns.A{
Hdr: dns.RR_Header{
Name: testFQDN,
Rrtype: dns.TypeA,
Class: dns.ClassINET,
Ttl: 3600,
Rdlength: 4,
},
A: net.IP{1, 2, 3, 4},
}}
localUpsHdlr := dns.HandlerFunc(func(w dns.ResponseWriter, req *dns.Msg) {
resp := (&dns.Msg{}).SetReply(req)
resp.Answer = localAns
require.NoError(t, w.WriteMsg(resp))
})
localUpsAddr := aghtest.StartLocalhostUpstream(t, localUpsHdlr).String()
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
s, _ := createTestTLS(t, TLSConfig{
TLSListenAddrs: []*net.TCPAddr{{}},
ServerName: tlsServerName,
})
s.conf.UpstreamDNS = []string{localUpsAddr}
s.conf.AllowedClients = tc.allowedClients
s.conf.DisallowedClients = tc.disallowedClients
s.conf.BlockedHosts = tc.blockedHosts
err := s.Prepare(&s.conf)
require.NoError(t, err)
startDeferStop(t, s)
tlsConfig := &tls.Config{
InsecureSkipVerify: true,
ServerName: tc.clientSrvName,
}
client := &dns.Client{
Net: "tcp-tls",
TLSConfig: tlsConfig,
Timeout: dnsClientTimeout,
}
req := createTestMessage(tc.host)
addr := s.dnsProxy.Addr(proxy.ProtoTLS).String()
reply, _, err := client.Exchange(req, addr)
require.NoError(t, err)
assert.Equal(t, tc.wantRCode, reply.Rcode)
if tc.wantRCode == dns.RcodeSuccess {
assert.Equal(t, localAns, reply.Answer)
} else {
assert.Empty(t, reply.Answer)
}
})
}
}
func TestServer_HandleBefore_udp(t *testing.T) {
t.Parallel()
const (
clientIPv4 = "127.0.0.1"
clientIPv6 = "::1"
)
clientIPs := []string{clientIPv4, clientIPv6}
testCases := []struct {
name string
host string
allowedClients []string
disallowedClients []string
blockedHosts []string
wantTimeout bool
}{{
name: "allow_all",
host: testFQDN,
allowedClients: []string{},
disallowedClients: []string{},
blockedHosts: []string{},
wantTimeout: false,
}, {
name: "allowed_client_allowed",
host: testFQDN,
allowedClients: clientIPs,
disallowedClients: []string{},
blockedHosts: []string{},
wantTimeout: false,
}, {
name: "allowed_client_rejected",
host: testFQDN,
allowedClients: []string{"1:2:3::4"},
disallowedClients: []string{},
blockedHosts: []string{},
wantTimeout: true,
}, {
name: "disallowed_client_allowed",
host: testFQDN,
allowedClients: []string{},
disallowedClients: []string{"1:2:3::4"},
blockedHosts: []string{},
wantTimeout: false,
}, {
name: "disallowed_client_rejected",
host: testFQDN,
allowedClients: []string{},
disallowedClients: clientIPs,
blockedHosts: []string{},
wantTimeout: true,
}, {
name: "blocked_hosts_allowed",
host: testFQDN,
allowedClients: []string{},
disallowedClients: []string{},
blockedHosts: []string{blockedHost},
wantTimeout: false,
}, {
name: "blocked_hosts_rejected",
host: dns.Fqdn(blockedHost),
allowedClients: []string{},
disallowedClients: []string{},
blockedHosts: []string{blockedHost},
wantTimeout: true,
}}
localAns := []dns.RR{&dns.A{
Hdr: dns.RR_Header{
Name: testFQDN,
Rrtype: dns.TypeA,
Class: dns.ClassINET,
Ttl: 3600,
Rdlength: 4,
},
A: net.IP{1, 2, 3, 4},
}}
localUpsHdlr := dns.HandlerFunc(func(w dns.ResponseWriter, req *dns.Msg) {
resp := (&dns.Msg{}).SetReply(req)
resp.Answer = localAns
require.NoError(t, w.WriteMsg(resp))
})
localUpsAddr := aghtest.StartLocalhostUpstream(t, localUpsHdlr).String()
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
s := createTestServer(t, &filtering.Config{
BlockingMode: filtering.BlockingModeDefault,
}, ServerConfig{
UDPListenAddrs: []*net.UDPAddr{{}},
TCPListenAddrs: []*net.TCPAddr{{}},
Config: Config{
AllowedClients: tc.allowedClients,
DisallowedClients: tc.disallowedClients,
BlockedHosts: tc.blockedHosts,
UpstreamDNS: []string{localUpsAddr},
UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
},
ServePlainDNS: true,
})
startDeferStop(t, s)
client := &dns.Client{
Net: "udp",
Timeout: dnsClientTimeout,
}
req := createTestMessage(tc.host)
addr := s.dnsProxy.Addr(proxy.ProtoUDP).String()
reply, _, err := client.Exchange(req, addr)
if tc.wantTimeout {
wantErr := &net.OpError{}
require.ErrorAs(t, err, &wantErr)
assert.True(t, wantErr.Timeout())
assert.Nil(t, reply)
} else {
require.NoError(t, err)
require.NotNil(t, reply)
assert.Equal(t, dns.RcodeSuccess, reply.Rcode)
assert.Equal(t, localAns, reply.Answer)
}
})
}
}

View File

@@ -14,6 +14,8 @@ import (
)
// ValidateClientID returns an error if id is not a valid ClientID.
//
// Keep in sync with [client.ValidateClientID].
func ValidateClientID(id string) (err error) {
err = netutil.ValidateHostnameLabel(id)
if err != nil {
@@ -108,46 +110,6 @@ type quicConnection interface {
ConnectionState() (cs quic.ConnectionState)
}
// clientIDFromDNSContext extracts the client's ID from the server name of the
// client's DoT or DoQ request or the path of the client's DoH. If the protocol
// is not one of these, clientID is an empty string and err is nil.
func (s *Server) clientIDFromDNSContext(pctx *proxy.DNSContext) (clientID string, err error) {
proto := pctx.Proto
if proto == proxy.ProtoHTTPS {
clientID, err = clientIDFromDNSContextHTTPS(pctx)
if err != nil {
return "", fmt.Errorf("checking url: %w", err)
} else if clientID != "" {
return clientID, nil
}
// Go on and check the domain name as well.
} else if proto != proxy.ProtoTLS && proto != proxy.ProtoQUIC {
return "", nil
}
hostSrvName := s.conf.ServerName
if hostSrvName == "" {
return "", nil
}
cliSrvName, err := clientServerName(pctx, proto)
if err != nil {
return "", err
}
clientID, err = clientIDFromClientServerName(
hostSrvName,
cliSrvName,
s.conf.StrictSNICheck,
)
if err != nil {
return "", fmt.Errorf("clientid check: %w", err)
}
return clientID, nil
}
// clientServerName returns the TLS server name based on the protocol. For
// DNS-over-HTTPS requests, it will return the hostname part of the Host header
// if there is one.

View File

@@ -19,6 +19,7 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/dnsproxy/upstream"
"github.com/AdguardTeam/golibs/container"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/netutil"
@@ -234,9 +235,18 @@ type DNSCryptConfig struct {
// ServerConfig represents server configuration.
// The zero ServerConfig is empty and ready for use.
type ServerConfig struct {
UDPListenAddrs []*net.UDPAddr // UDP listen address
TCPListenAddrs []*net.TCPAddr // TCP listen address
UpstreamConfig *proxy.UpstreamConfig // Upstream DNS servers config
// UDPListenAddrs is the list of addresses to listen for DNS-over-UDP.
UDPListenAddrs []*net.UDPAddr
// TCPListenAddrs is the list of addresses to listen for DNS-over-TCP.
TCPListenAddrs []*net.TCPAddr
// UpstreamConfig is the general configuration of upstream DNS servers.
UpstreamConfig *proxy.UpstreamConfig
// PrivateRDNSUpstreamConfig is the configuration of upstream DNS servers
// for private reverse DNS.
PrivateRDNSUpstreamConfig *proxy.UpstreamConfig
// AddrProcConf defines the configuration for the client IP processor.
// If nil, [client.EmptyAddrProc] is used.
@@ -305,24 +315,28 @@ func (s *Server) newProxyConfig() (conf *proxy.Config, err error) {
trustedPrefixes := netutil.UnembedPrefixes(srvConf.TrustedProxies)
conf = &proxy.Config{
HTTP3: srvConf.ServeHTTP3,
Ratelimit: int(srvConf.Ratelimit),
RatelimitSubnetLenIPv4: srvConf.RatelimitSubnetLenIPv4,
RatelimitSubnetLenIPv6: srvConf.RatelimitSubnetLenIPv6,
RatelimitWhitelist: srvConf.RatelimitWhitelist,
RefuseAny: srvConf.RefuseAny,
TrustedProxies: netutil.SliceSubnetSet(trustedPrefixes),
CacheMinTTL: srvConf.CacheMinTTL,
CacheMaxTTL: srvConf.CacheMaxTTL,
CacheOptimistic: srvConf.CacheOptimistic,
UpstreamConfig: srvConf.UpstreamConfig,
BeforeRequestHandler: s.beforeRequestHandler,
RequestHandler: s.handleDNSRequest,
HTTPSServerName: aghhttp.UserAgent(),
EnableEDNSClientSubnet: srvConf.EDNSClientSubnet.Enabled,
MaxGoroutines: srvConf.MaxGoroutines,
UseDNS64: srvConf.UseDNS64,
DNS64Prefs: srvConf.DNS64Prefixes,
HTTP3: srvConf.ServeHTTP3,
Ratelimit: int(srvConf.Ratelimit),
RatelimitSubnetLenIPv4: srvConf.RatelimitSubnetLenIPv4,
RatelimitSubnetLenIPv6: srvConf.RatelimitSubnetLenIPv6,
RatelimitWhitelist: srvConf.RatelimitWhitelist,
RefuseAny: srvConf.RefuseAny,
TrustedProxies: netutil.SliceSubnetSet(trustedPrefixes),
CacheMinTTL: srvConf.CacheMinTTL,
CacheMaxTTL: srvConf.CacheMaxTTL,
CacheOptimistic: srvConf.CacheOptimistic,
UpstreamConfig: srvConf.UpstreamConfig,
PrivateRDNSUpstreamConfig: srvConf.PrivateRDNSUpstreamConfig,
BeforeRequestHandler: s,
RequestHandler: s.handleDNSRequest,
HTTPSServerName: aghhttp.UserAgent(),
EnableEDNSClientSubnet: srvConf.EDNSClientSubnet.Enabled,
MaxGoroutines: srvConf.MaxGoroutines,
UseDNS64: srvConf.UseDNS64,
DNS64Prefs: srvConf.DNS64Prefixes,
UsePrivateRDNS: srvConf.UsePrivateRDNS,
PrivateSubnets: s.privateNets,
MessageConstructor: s,
}
if srvConf.EDNSClientSubnet.UseCustom {
@@ -357,10 +371,6 @@ func (s *Server) newProxyConfig() (conf *proxy.Config, err error) {
conf.DNSCryptResolverCert = c.ResolverCert
}
if conf.UpstreamConfig == nil || len(conf.UpstreamConfig.Upstreams) == 0 {
return nil, errors.Error("no default upstream servers configured")
}
conf, err = prepareCacheConfig(conf,
srvConf.CacheSize,
srvConf.CacheMinTTL,
@@ -455,36 +465,58 @@ func (s *Server) prepareIpsetListSettings() (err error) {
}
ipsets := stringutil.SplitTrimmed(string(data), "\n")
ipsets = stringutil.FilterOut(ipsets, IsCommentOrEmpty)
log.Debug("dns: using %d ipset rules from file %q", len(ipsets), fn)
return s.ipset.init(ipsets)
}
// loadUpstreams parses upstream DNS servers from the configured file or from
// the configuration itself.
func (conf *ServerConfig) loadUpstreams() (upstreams []string, err error) {
if conf.UpstreamDNSFileName == "" {
return stringutil.FilterOut(conf.UpstreamDNS, IsCommentOrEmpty), nil
}
var data []byte
data, err = os.ReadFile(conf.UpstreamDNSFileName)
if err != nil {
return nil, fmt.Errorf("reading upstream from file: %w", err)
}
upstreams = stringutil.SplitTrimmed(string(data), "\n")
log.Debug("dnsforward: got %d upstreams in %q", len(upstreams), conf.UpstreamDNSFileName)
return stringutil.FilterOut(upstreams, IsCommentOrEmpty), nil
}
// collectListenAddr adds addrPort to addrs. It also adds its port to
// unspecPorts if its address is unspecified.
func collectListenAddr(
addrPort netip.AddrPort,
addrs map[netip.AddrPort]unit,
unspecPorts map[uint16]unit,
addrs *container.MapSet[netip.AddrPort],
unspecPorts *container.MapSet[uint16],
) {
if addrPort == (netip.AddrPort{}) {
return
}
addrs[addrPort] = unit{}
addrs.Add(addrPort)
if addrPort.Addr().IsUnspecified() {
unspecPorts[addrPort.Port()] = unit{}
unspecPorts.Add(addrPort.Port())
}
}
// collectDNSAddrs returns configured set of listening addresses. It also
// returns a set of ports of each unspecified listening address.
func (conf *ServerConfig) collectDNSAddrs() (addrs mapAddrPortSet, unspecPorts map[uint16]unit) {
// TODO(e.burkov): Perhaps, we shouldn't allocate as much memory, since the
// TCP and UDP listening addresses are currently the same.
addrs = make(map[netip.AddrPort]unit, len(conf.TCPListenAddrs)+len(conf.UDPListenAddrs))
unspecPorts = map[uint16]unit{}
func (conf *ServerConfig) collectDNSAddrs() (
addrs *container.MapSet[netip.AddrPort],
unspecPorts *container.MapSet[uint16],
) {
addrs = container.NewMapSet[netip.AddrPort]()
unspecPorts = container.NewMapSet[uint16]()
for _, laddr := range conf.TCPListenAddrs {
collectListenAddr(laddr.AddrPort(), addrs, unspecPorts)
@@ -515,26 +547,12 @@ type emptyAddrPortSet struct{}
// Has implements the [addrPortSet] interface for [emptyAddrPortSet].
func (emptyAddrPortSet) Has(_ netip.AddrPort) (ok bool) { return false }
// mapAddrPortSet is the [addrPortSet] containing values of [netip.AddrPort] as
// keys of a map.
type mapAddrPortSet map[netip.AddrPort]unit
// type check
var _ addrPortSet = mapAddrPortSet{}
// Has implements the [addrPortSet] interface for [mapAddrPortSet].
func (m mapAddrPortSet) Has(addrPort netip.AddrPort) (ok bool) {
_, ok = m[addrPort]
return ok
}
// combinedAddrPortSet is the [addrPortSet] defined by some IP addresses along
// with ports, any combination of which is considered being in the set.
type combinedAddrPortSet struct {
// TODO(e.burkov): Use sorted slices in combination with binary search.
ports map[uint16]unit
addrs []netip.Addr
// TODO(e.burkov): Use container.SliceSet when available.
ports *container.MapSet[uint16]
addrs *container.MapSet[netip.Addr]
}
// type check
@@ -542,13 +560,11 @@ var _ addrPortSet = (*combinedAddrPortSet)(nil)
// Has implements the [addrPortSet] interface for [*combinedAddrPortSet].
func (m *combinedAddrPortSet) Has(addrPort netip.AddrPort) (ok bool) {
_, ok = m.ports[addrPort.Port()]
return ok && slices.Contains(m.addrs, addrPort.Addr())
return m.ports.Has(addrPort.Port()) && m.addrs.Has(addrPort.Addr())
}
// filterOut filters out all the upstreams that match um. It returns all the
// closing errors joined.
// filterOutAddrs filters out all the upstreams that match um. It returns all
// the closing errors joined.
func filterOutAddrs(upsConf *proxy.UpstreamConfig, set addrPortSet) (err error) {
var errs []error
delFunc := func(u upstream.Upstream) (ok bool) {
@@ -582,11 +598,11 @@ func filterOutAddrs(upsConf *proxy.UpstreamConfig, set addrPortSet) (err error)
func (conf *ServerConfig) ourAddrsSet() (m addrPortSet, err error) {
addrs, unspecPorts := conf.collectDNSAddrs()
switch {
case len(addrs) == 0:
case addrs.Len() == 0:
log.Debug("dnsforward: no listen addresses")
return emptyAddrPortSet{}, nil
case len(unspecPorts) == 0:
case unspecPorts.Len() == 0:
log.Debug("dnsforward: filtering out addresses %s", addrs)
return addrs, nil
@@ -602,7 +618,7 @@ func (conf *ServerConfig) ourAddrsSet() (m addrPortSet, err error) {
return &combinedAddrPortSet{
ports: unspecPorts,
addrs: ifaceAddrs,
addrs: container.NewMapSet(ifaceAddrs...),
}, nil
}
}

View File

@@ -3,15 +3,14 @@ package dnsforward
import (
"net"
"testing"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/aghtest"
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/dnsproxy/upstream"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/testutil"
"github.com/miekg/dns"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -65,6 +64,8 @@ func newRR(t *testing.T, name string, qtype uint16, ttl uint32, val any) (rr dns
}
func TestServer_HandleDNSRequest_dns64(t *testing.T) {
t.Parallel()
const (
ipv4Domain = "ipv4.only."
ipv6Domain = "ipv6.only."
@@ -101,21 +102,6 @@ func TestServer_HandleDNSRequest_dns64(t *testing.T) {
type answerMap = map[uint16][sectionsNum][]dns.RR
pt := testutil.PanicT{}
newUps := func(answers answerMap) (u upstream.Upstream) {
return aghtest.NewUpstreamMock(func(req *dns.Msg) (resp *dns.Msg, err error) {
q := req.Question[0]
require.Contains(pt, answers, q.Qtype)
answer := answers[q.Qtype]
resp = (&dns.Msg{}).SetReply(req)
resp.Answer = answer[sectionAnswer]
resp.Ns = answer[sectionAuthority]
resp.Extra = answer[sectionAdditional]
return resp, nil
})
}
testCases := []struct {
name string
@@ -265,47 +251,113 @@ func TestServer_HandleDNSRequest_dns64(t *testing.T) {
}}
localRR := newRR(t, ptr64Domain, dns.TypePTR, 3600, pointedDomain)
localUps := aghtest.NewUpstreamMock(func(req *dns.Msg) (resp *dns.Msg, err error) {
require.Equal(pt, req.Question[0].Name, ptr64Domain)
resp = (&dns.Msg{}).SetReply(req)
localUpsHdlr := dns.HandlerFunc(func(w dns.ResponseWriter, m *dns.Msg) {
require.Len(pt, m.Question, 1)
require.Equal(pt, m.Question[0].Name, ptr64Domain)
resp := (&dns.Msg{}).SetReply(m)
resp.Answer = []dns.RR{localRR}
return resp, nil
require.NoError(t, w.WriteMsg(resp))
})
localUpsAddr := aghtest.StartLocalhostUpstream(t, localUpsHdlr).String()
client := &dns.Client{
Net: "tcp",
Timeout: 1 * time.Second,
Net: string(proxy.ProtoTCP),
Timeout: testTimeout,
}
for _, tc := range testCases {
// TODO(e.burkov): It seems [proxy.Proxy] isn't intended to be reused
// right after stop, due to a data race in [proxy.Proxy.Init] method
// when setting an OOB size. As a temporary workaround, recreate the
// whole server for each test case.
s := createTestServer(t, &filtering.Config{
BlockingMode: filtering.BlockingModeDefault,
}, ServerConfig{
UDPListenAddrs: []*net.UDPAddr{{}},
TCPListenAddrs: []*net.TCPAddr{{}},
UseDNS64: true,
Config: Config{
UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
},
ServePlainDNS: true,
}, localUps)
t.Run(tc.name, func(t *testing.T) {
s.conf.UpstreamConfig.Upstreams = []upstream.Upstream{newUps(tc.upsAns)}
t.Parallel()
upsHdlr := dns.HandlerFunc(func(w dns.ResponseWriter, req *dns.Msg) {
q := req.Question[0]
require.Contains(pt, tc.upsAns, q.Qtype)
answer := tc.upsAns[q.Qtype]
resp := (&dns.Msg{}).SetReply(req)
resp.Answer = answer[sectionAnswer]
resp.Ns = answer[sectionAuthority]
resp.Extra = answer[sectionAdditional]
require.NoError(pt, w.WriteMsg(resp))
})
upsAddr := aghtest.StartLocalhostUpstream(t, upsHdlr).String()
// TODO(e.burkov): It seems [proxy.Proxy] isn't intended to be
// reused right after stop, due to a data race in [proxy.Proxy.Init]
// method when setting an OOB size. As a temporary workaround,
// recreate the whole server for each test case.
s := createTestServer(t, &filtering.Config{
BlockingMode: filtering.BlockingModeDefault,
}, ServerConfig{
UDPListenAddrs: []*net.UDPAddr{{}},
TCPListenAddrs: []*net.TCPAddr{{}},
UseDNS64: true,
Config: Config{
UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
UpstreamDNS: []string{upsAddr},
},
UsePrivateRDNS: true,
LocalPTRResolvers: []string{localUpsAddr},
ServePlainDNS: true,
})
startDeferStop(t, s)
req := (&dns.Msg{}).SetQuestion(tc.qname, tc.qtype)
resp, _, excErr := client.Exchange(req, s.dnsProxy.Addr(proxy.ProtoTCP).String())
resp, _, excErr := client.Exchange(req, s.proxy().Addr(proxy.ProtoTCP).String())
require.NoError(t, excErr)
require.Equal(t, tc.wantAns, resp.Answer)
})
}
}
func TestServer_dns64WithDisabledRDNS(t *testing.T) {
t.Parallel()
// Shouldn't go to upstream at all.
panicHdlr := dns.HandlerFunc(func(w dns.ResponseWriter, m *dns.Msg) {
panic("not implemented")
})
upsAddr := aghtest.StartLocalhostUpstream(t, panicHdlr).String()
localUpsAddr := aghtest.StartLocalhostUpstream(t, panicHdlr).String()
s := createTestServer(t, &filtering.Config{
BlockingMode: filtering.BlockingModeDefault,
}, ServerConfig{
UDPListenAddrs: []*net.UDPAddr{{}},
TCPListenAddrs: []*net.TCPAddr{{}},
UseDNS64: true,
Config: Config{
UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
UpstreamDNS: []string{upsAddr},
},
UsePrivateRDNS: false,
LocalPTRResolvers: []string{localUpsAddr},
ServePlainDNS: true,
})
startDeferStop(t, s)
mappedIPv6 := net.ParseIP("64:ff9b::102:304")
arpa, err := netutil.IPToReversedAddr(mappedIPv6)
require.NoError(t, err)
req := (&dns.Msg{}).SetQuestion(dns.Fqdn(arpa), dns.TypePTR)
cli := &dns.Client{
Net: string(proxy.ProtoTCP),
Timeout: testTimeout,
}
resp, _, err := cli.Exchange(req, s.proxy().Addr(proxy.ProtoTCP).String())
require.NoError(t, err)
assert.Equal(t, dns.RcodeNameError, resp.Rcode)
}

View File

@@ -2,6 +2,7 @@
package dnsforward
import (
"cmp"
"context"
"fmt"
"io"
@@ -15,7 +16,6 @@ import (
"sync/atomic"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/aghalg"
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
"github.com/AdguardTeam/AdGuardHome/internal/client"
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
@@ -135,12 +135,6 @@ type Server struct {
// WHOIS, etc.
addrProc client.AddressProcessor
// localResolvers is a DNS proxy instance used to resolve PTR records for
// addresses considered private as per the [privateNets].
//
// TODO(e.burkov): Remove once the local resolvers logic moved to dnsproxy.
localResolvers *proxy.Proxy
// sysResolvers used to fetch system resolvers to use by default for private
// PTR resolving.
sysResolvers SystemResolvers
@@ -158,12 +152,6 @@ type Server struct {
// [upstream.Resolver] interface.
bootResolvers []*upstream.UpstreamResolver
// recDetector is a cache for recursive requests. It is used to detect and
// prevent recursive requests only for private upstreams.
//
// See https://github.com/adguardTeam/adGuardHome/issues/3185#issuecomment-851048135.
recDetector *recursionDetector
// dns64Pref is the NAT64 prefix used for DNS64 response mapping. The major
// part of DNS64 happens inside the [proxy] package, but there still are
// some places where response mapping is needed (e.g. DHCP).
@@ -212,14 +200,6 @@ type DNSCreateParams struct {
LocalDomain string
}
const (
// recursionTTL is the time recursive request is cached for.
recursionTTL = 1 * time.Second
// cachedRecurrentReqNum is the maximum number of cached recurrent
// requests.
cachedRecurrentReqNum = 1000
)
// NewServer creates a new instance of the dnsforward.Server
// Note: this function must be called only once
//
@@ -256,7 +236,6 @@ func NewServer(p DNSCreateParams) (s *Server, err error) {
// TODO(e.burkov): Use some case-insensitive string comparison.
localDomainSuffix: strings.ToLower(localDomainSuffix),
etcHosts: etcHosts,
recDetector: newRecursionDetector(recursionTTL, cachedRecurrentReqNum),
clientIDCache: cache.New(cache.Config{
EnableLRU: true,
MaxCount: defaultClientIDCacheCount,
@@ -308,13 +287,13 @@ func (s *Server) WriteDiskConfig(c *Config) {
sc := s.conf.Config
*c = sc
c.RatelimitWhitelist = slices.Clone(sc.RatelimitWhitelist)
c.BootstrapDNS = stringutil.CloneSlice(sc.BootstrapDNS)
c.FallbackDNS = stringutil.CloneSlice(sc.FallbackDNS)
c.AllowedClients = stringutil.CloneSlice(sc.AllowedClients)
c.DisallowedClients = stringutil.CloneSlice(sc.DisallowedClients)
c.BlockedHosts = stringutil.CloneSlice(sc.BlockedHosts)
c.BootstrapDNS = slices.Clone(sc.BootstrapDNS)
c.FallbackDNS = slices.Clone(sc.FallbackDNS)
c.AllowedClients = slices.Clone(sc.AllowedClients)
c.DisallowedClients = slices.Clone(sc.DisallowedClients)
c.BlockedHosts = slices.Clone(sc.BlockedHosts)
c.TrustedProxies = slices.Clone(sc.TrustedProxies)
c.UpstreamDNS = stringutil.CloneSlice(sc.UpstreamDNS)
c.UpstreamDNS = slices.Clone(sc.UpstreamDNS)
}
// LocalPTRResolvers returns the current local PTR resolver configuration.
@@ -322,7 +301,7 @@ func (s *Server) LocalPTRResolvers() (localPTRResolvers []string) {
s.serverLock.RLock()
defer s.serverLock.RUnlock()
return stringutil.CloneSlice(s.conf.LocalPTRResolvers)
return slices.Clone(s.conf.LocalPTRResolvers)
}
// AddrProcConfig returns the current address processing configuration. Only
@@ -366,6 +345,7 @@ func (s *Server) Exchange(ip netip.Addr) (host string, ttl time.Duration, err er
s.serverLock.RLock()
defer s.serverLock.RUnlock()
// TODO(e.burkov): Migrate to [netip.Addr] already.
arpa, err := netutil.IPToReversedAddr(ip.AsSlice())
if err != nil {
return "", 0, fmt.Errorf("reversing ip: %w", err)
@@ -386,25 +366,23 @@ func (s *Server) Exchange(ip netip.Addr) (host string, ttl time.Duration, err er
}
dctx := &proxy.DNSContext{
Proto: "udp",
Req: req,
Proto: proxy.ProtoUDP,
Req: req,
IsPrivateClient: true,
}
var resolver *proxy.Proxy
var errMsg string
if s.privateNets.Contains(ip) {
if !s.conf.UsePrivateRDNS {
return "", 0, nil
}
resolver = s.localResolvers
errMsg = "resolving a private address: %w"
s.recDetector.add(*req)
dctx.RequestedPrivateRDNS = netip.PrefixFrom(ip, ip.BitLen())
} else {
resolver = s.internalProxy
errMsg = "resolving an address: %w"
}
if err = resolver.Resolve(dctx); err != nil {
if err = s.internalProxy.Resolve(dctx); err != nil {
return "", 0, fmt.Errorf(errMsg, err)
}
@@ -464,7 +442,8 @@ func (s *Server) Start() error {
// startLocked starts the DNS server without locking. s.serverLock is expected
// to be locked.
func (s *Server) startLocked() error {
err := s.dnsProxy.Start()
// TODO(e.burkov): Use context properly.
err := s.dnsProxy.Start(context.Background())
if err == nil {
s.isRunning = true
}
@@ -472,82 +451,6 @@ func (s *Server) startLocked() error {
return err
}
// prepareLocalResolvers initializes the local upstreams configuration using
// boot as bootstrap. It assumes that s.serverLock is locked or s not running.
func (s *Server) prepareLocalResolvers(
boot upstream.Resolver,
) (uc *proxy.UpstreamConfig, err error) {
set, err := s.conf.ourAddrsSet()
if err != nil {
// Don't wrap the error because it's informative enough as is.
return nil, err
}
resolvers := s.conf.LocalPTRResolvers
confNeedsFiltering := len(resolvers) > 0
if confNeedsFiltering {
resolvers = stringutil.FilterOut(resolvers, IsCommentOrEmpty)
} else {
sysResolvers := slices.DeleteFunc(slices.Clone(s.sysResolvers.Addrs()), set.Has)
resolvers = make([]string, 0, len(sysResolvers))
for _, r := range sysResolvers {
resolvers = append(resolvers, r.String())
}
}
log.Debug("dnsforward: upstreams to resolve ptr for local addresses: %v", resolvers)
uc, err = s.prepareUpstreamConfig(resolvers, nil, &upstream.Options{
Bootstrap: boot,
Timeout: defaultLocalTimeout,
// TODO(e.burkov): Should we verify server's certificates?
PreferIPv6: s.conf.BootstrapPreferIPv6,
})
if err != nil {
return nil, fmt.Errorf("preparing private upstreams: %w", err)
}
if confNeedsFiltering {
err = filterOutAddrs(uc, set)
if err != nil {
return nil, fmt.Errorf("filtering private upstreams: %w", err)
}
}
return uc, nil
}
// setupLocalResolvers initializes and sets the resolvers for local addresses.
// It assumes s.serverLock is locked or s not running.
func (s *Server) setupLocalResolvers(boot upstream.Resolver) (err error) {
uc, err := s.prepareLocalResolvers(boot)
if err != nil {
// Don't wrap the error because it's informative enough as is.
return err
}
s.localResolvers = &proxy.Proxy{
Config: proxy.Config{
UpstreamConfig: uc,
},
}
err = s.localResolvers.Init()
if err != nil {
return fmt.Errorf("initializing proxy: %w", err)
}
// TODO(e.burkov): Should we also consider the DNS64 usage?
if s.conf.UsePrivateRDNS &&
// Only set the upstream config if there are any upstreams. It's safe
// to put nil into [proxy.Config.PrivateRDNSUpstreamConfig].
len(uc.Upstreams)+len(uc.DomainReservedUpstreams)+len(uc.SpecifiedDomainUpstreams) > 0 {
s.dnsProxy.PrivateRDNSUpstreamConfig = uc
}
return nil
}
// Prepare initializes parameters of s using data from conf. conf must not be
// nil.
func (s *Server) Prepare(conf *ServerConfig) (err error) {
@@ -564,7 +467,7 @@ func (s *Server) Prepare(conf *ServerConfig) (err error) {
s.initDefaultSettings()
boot, err := s.prepareInternalDNS()
err = s.prepareInternalDNS()
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return err
@@ -586,22 +489,17 @@ func (s *Server) Prepare(conf *ServerConfig) (err error) {
return fmt.Errorf("preparing access: %w", err)
}
// Set the proxy here because [setupLocalResolvers] sets its values.
//
// TODO(e.burkov): Remove once the local resolvers logic moved to dnsproxy.
s.dnsProxy = &proxy.Proxy{Config: *proxyConfig}
err = s.setupLocalResolvers(boot)
if err != nil {
return fmt.Errorf("setting up resolvers: %w", err)
}
err = s.setupFallbackDNS()
proxyConfig.Fallbacks, err = s.setupFallbackDNS()
if err != nil {
return fmt.Errorf("setting up fallback dns servers: %w", err)
}
s.recDetector.clear()
dnsProxy, err := proxy.New(proxyConfig)
if err != nil {
return fmt.Errorf("creating proxy: %w", err)
}
s.dnsProxy = dnsProxy
s.setupAddrProc()
@@ -610,59 +508,149 @@ func (s *Server) Prepare(conf *ServerConfig) (err error) {
return nil
}
// prepareInternalDNS initializes the internal state of s before initializing
// the primary DNS proxy instance. It assumes s.serverLock is locked or the
// Server not running.
func (s *Server) prepareInternalDNS() (boot upstream.Resolver, err error) {
err = s.prepareIpsetListSettings()
// prepareUpstreamSettings sets upstream DNS server settings.
func (s *Server) prepareUpstreamSettings(boot upstream.Resolver) (err error) {
// Load upstreams either from the file, or from the settings
var upstreams []string
upstreams, err = s.conf.loadUpstreams()
if err != nil {
return nil, fmt.Errorf("preparing ipset settings: %w", err)
return fmt.Errorf("loading upstreams: %w", err)
}
s.bootstrap, s.bootResolvers, err = s.createBootstrap(s.conf.BootstrapDNS, &upstream.Options{
Timeout: DefaultTimeout,
uc, err := newUpstreamConfig(upstreams, defaultDNS, &upstream.Options{
Bootstrap: boot,
Timeout: s.conf.UpstreamTimeout,
HTTPVersions: UpstreamHTTPVersions(s.conf.UseHTTP3Upstreams),
PreferIPv6: s.conf.BootstrapPreferIPv6,
// Use a customized set of RootCAs, because Go's default mechanism of
// loading TLS roots does not always work properly on some routers so we're
// loading roots manually and pass it here.
//
// See [aghtls.SystemRootCAs].
//
// TODO(a.garipov): Investigate if that's true.
RootCAs: s.conf.TLSv12Roots,
CipherSuites: s.conf.TLSCiphers,
})
if err != nil {
return fmt.Errorf("preparing upstream config: %w", err)
}
s.conf.UpstreamConfig = uc
return nil
}
// PrivateRDNSError is returned when the private rDNS upstreams are
// invalid but enabled.
//
// TODO(e.burkov): Consider allowing to use incomplete private rDNS upstreams
// configuration in proxy when the private rDNS function is enabled. In theory,
// proxy supports the case when no upstreams provided to resolve the private
// request, since it already supports this for DNS64-prefixed PTR requests.
type PrivateRDNSError struct {
err error
}
// Error implements the [errors.Error] interface.
func (e *PrivateRDNSError) Error() (s string) {
return e.err.Error()
}
func (e *PrivateRDNSError) Unwrap() (err error) {
return e.err
}
// prepareLocalResolvers initializes the private RDNS upstream configuration
// according to the server's settings. It assumes s.serverLock is locked or the
// Server not running.
func (s *Server) prepareLocalResolvers() (uc *proxy.UpstreamConfig, err error) {
if !s.conf.UsePrivateRDNS {
return nil, nil
}
var ownAddrs addrPortSet
ownAddrs, err = s.conf.ourAddrsSet()
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return nil, err
}
opts := &upstream.Options{
Bootstrap: s.bootstrap,
Timeout: defaultLocalTimeout,
// TODO(e.burkov): Should we verify server's certificates?
PreferIPv6: s.conf.BootstrapPreferIPv6,
}
addrs := s.conf.LocalPTRResolvers
uc, err = newPrivateConfig(addrs, ownAddrs, s.sysResolvers, s.privateNets, opts)
if err != nil {
return nil, fmt.Errorf("preparing resolvers: %w", err)
}
return uc, nil
}
// prepareInternalDNS initializes the internal state of s before initializing
// the primary DNS proxy instance. It assumes s.serverLock is locked or the
// Server not running.
func (s *Server) prepareInternalDNS() (err error) {
err = s.prepareIpsetListSettings()
if err != nil {
return fmt.Errorf("preparing ipset settings: %w", err)
}
bootOpts := &upstream.Options{
Timeout: DefaultTimeout,
HTTPVersions: UpstreamHTTPVersions(s.conf.UseHTTP3Upstreams),
}
s.bootstrap, s.bootResolvers, err = newBootstrap(s.conf.BootstrapDNS, s.etcHosts, bootOpts)
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return err
}
err = s.prepareUpstreamSettings(s.bootstrap)
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return s.bootstrap, err
return err
}
s.conf.PrivateRDNSUpstreamConfig, err = s.prepareLocalResolvers()
if err != nil {
return err
}
err = s.prepareInternalProxy()
if err != nil {
return s.bootstrap, fmt.Errorf("preparing internal proxy: %w", err)
return fmt.Errorf("preparing internal proxy: %w", err)
}
return s.bootstrap, nil
return nil
}
// setupFallbackDNS initializes the fallback DNS servers.
func (s *Server) setupFallbackDNS() (err error) {
func (s *Server) setupFallbackDNS() (uc *proxy.UpstreamConfig, err error) {
fallbacks := s.conf.FallbackDNS
fallbacks = stringutil.FilterOut(fallbacks, IsCommentOrEmpty)
if len(fallbacks) == 0 {
return nil
return nil, nil
}
uc, err := proxy.ParseUpstreamsConfig(fallbacks, &upstream.Options{
uc, err = proxy.ParseUpstreamsConfig(fallbacks, &upstream.Options{
// TODO(s.chzhen): Investigate if other options are needed.
Timeout: s.conf.UpstreamTimeout,
PreferIPv6: s.conf.BootstrapPreferIPv6,
// TODO(e.burkov): Use bootstrap.
})
if err != nil {
// Do not wrap the error because it's informative enough as is.
return err
return nil, err
}
s.dnsProxy.Fallbacks = uc
return nil
return uc, nil
}
// setupAddrProc initializes the address processor. It assumes s.serverLock is
@@ -719,10 +707,16 @@ func validateBlockingMode(
func (s *Server) prepareInternalProxy() (err error) {
srvConf := s.conf
conf := &proxy.Config{
CacheEnabled: true,
CacheSizeBytes: 4096,
UpstreamConfig: srvConf.UpstreamConfig,
MaxGoroutines: s.conf.MaxGoroutines,
CacheEnabled: true,
CacheSizeBytes: 4096,
PrivateRDNSUpstreamConfig: srvConf.PrivateRDNSUpstreamConfig,
UpstreamConfig: srvConf.UpstreamConfig,
MaxGoroutines: srvConf.MaxGoroutines,
UseDNS64: srvConf.UseDNS64,
DNS64Prefs: srvConf.DNS64Prefixes,
UsePrivateRDNS: srvConf.UsePrivateRDNS,
PrivateSubnets: s.privateNets,
MessageConstructor: s,
}
err = setProxyUpstreamMode(conf, srvConf.UpstreamMode, srvConf.FastestTimeout.Duration)
@@ -730,19 +724,9 @@ func (s *Server) prepareInternalProxy() (err error) {
return fmt.Errorf("invalid upstream mode: %w", err)
}
// TODO(a.garipov): Make a proper constructor for proxy.Proxy.
p := &proxy.Proxy{
Config: *conf,
}
s.internalProxy, err = proxy.New(conf)
err = p.Init()
if err != nil {
return err
}
s.internalProxy = p
return nil
return err
}
// Stop stops the DNS server.
@@ -761,15 +745,13 @@ func (s *Server) stopLocked() (err error) {
// [upstream.Upstream] implementations.
if s.dnsProxy != nil {
err = s.dnsProxy.Stop()
// TODO(e.burkov): Use context properly.
err = s.dnsProxy.Shutdown(context.Background())
if err != nil {
log.Error("dnsforward: closing primary resolvers: %s", err)
}
}
logCloserErr(s.internalProxy.UpstreamConfig, "dnsforward: closing internal resolvers: %s")
logCloserErr(s.localResolvers.UpstreamConfig, "dnsforward: closing local resolvers: %s")
for _, b := range s.bootResolvers {
logCloserErr(b, "dnsforward: closing bootstrap %s: %s", b.Address())
}
@@ -841,6 +823,8 @@ func (s *Server) Reconfigure(conf *ServerConfig) error {
}
}
// TODO(e.burkov): It seems an error here brings the server down, which is
// not reliable enough.
err = s.Prepare(conf)
if err != nil {
return fmt.Errorf("could not reconfigure the server: %w", err)
@@ -889,5 +873,5 @@ func (s *Server) IsBlockedClient(ip netip.Addr, clientID string) (blocked bool,
blocked = true
}
return blocked, aghalg.Coalesce(rule, clientID)
return blocked, cmp.Or(rule, clientID)
}

View File

@@ -1,13 +1,15 @@
package dnsforward
import (
"context"
"cmp"
"crypto/ecdsa"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/hex"
"encoding/pem"
"fmt"
"math/big"
@@ -19,7 +21,6 @@ import (
"testing/fstest"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/aghalg"
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
"github.com/AdguardTeam/AdGuardHome/internal/aghtest"
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
@@ -63,8 +64,7 @@ func startDeferStop(t *testing.T, s *Server) {
t.Helper()
err := s.Start()
require.NoErrorf(t, err, "failed to start server: %s", err)
require.NoError(t, err)
testutil.CleanupAndRequireSuccess(t, s.Stop)
}
@@ -72,7 +72,6 @@ func createTestServer(
t *testing.T,
filterConf *filtering.Config,
forwardConf ServerConfig,
localUps upstream.Upstream,
) (s *Server) {
t.Helper()
@@ -82,7 +81,8 @@ func createTestServer(
@@||whitelist.example.org^
||127.0.0.255`
filters := []filtering.Filter{{
ID: 0, Data: []byte(rules),
ID: 0,
Data: []byte(rules),
}}
f, err := filtering.New(filterConf, filters)
@@ -105,19 +105,6 @@ func createTestServer(
err = s.Prepare(&forwardConf)
require.NoError(t, err)
s.serverLock.Lock()
defer s.serverLock.Unlock()
// TODO(e.burkov): Try to move it higher.
if localUps != nil {
ups := []upstream.Upstream{localUps}
s.localResolvers.UpstreamConfig.Upstreams = ups
s.conf.UsePrivateRDNS = true
s.dnsProxy.PrivateRDNSUpstreamConfig = &proxy.UpstreamConfig{
Upstreams: ups,
}
}
return s
}
@@ -181,7 +168,7 @@ func createTestTLS(t *testing.T, tlsConf TLSConfig) (s *Server, certPem []byte)
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
},
ServePlainDNS: true,
}, nil)
})
tlsConf.CertificateChainData, tlsConf.PrivateKeyData = certPem, keyPem
s.conf.TLSConfig = tlsConf
@@ -202,7 +189,7 @@ func newGoogleUpstream() (u upstream.Upstream) {
return &aghtest.UpstreamMock{
OnAddress: func() (addr string) { return "google.upstream.example" },
OnExchange: func(req *dns.Msg) (resp *dns.Msg, err error) {
return aghalg.Coalesce(
return cmp.Or(
aghtest.MatchedResponse(req, dns.TypeA, googleDomainName, "8.8.8.8"),
new(dns.Msg).SetRcode(req, dns.RcodeNameError),
), nil
@@ -265,7 +252,7 @@ func sendTestMessagesAsync(t *testing.T, conn *dns.Conn) {
wg := &sync.WaitGroup{}
for i := 0; i < testMessagesCount; i++ {
for range testMessagesCount {
msg := createGoogleATestMessage()
wg.Add(1)
@@ -288,7 +275,7 @@ func sendTestMessagesAsync(t *testing.T, conn *dns.Conn) {
func sendTestMessages(t *testing.T, conn *dns.Conn) {
t.Helper()
for i := 0; i < testMessagesCount; i++ {
for i := range testMessagesCount {
req := createGoogleATestMessage()
err := conn.WriteMsg(req)
assert.NoErrorf(t, err, "cannot write message #%d: %s", i, err)
@@ -310,7 +297,7 @@ func TestServer(t *testing.T) {
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
},
ServePlainDNS: true,
}, nil)
})
s.conf.UpstreamConfig.Upstreams = []upstream.Upstream{newGoogleUpstream()}
startDeferStop(t, s)
@@ -410,7 +397,7 @@ func TestServerWithProtectionDisabled(t *testing.T) {
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
},
ServePlainDNS: true,
}, nil)
})
s.conf.UpstreamConfig.Upstreams = []upstream.Upstream{newGoogleUpstream()}
startDeferStop(t, s)
@@ -490,7 +477,7 @@ func TestServerRace(t *testing.T) {
ConfigModified: func() {},
ServePlainDNS: true,
}
s := createTestServer(t, filterConf, forwardConf, nil)
s := createTestServer(t, filterConf, forwardConf)
s.conf.UpstreamConfig.Upstreams = []upstream.Upstream{newGoogleUpstream()}
startDeferStop(t, s)
@@ -503,19 +490,10 @@ func TestServerRace(t *testing.T) {
}
func TestSafeSearch(t *testing.T) {
resolver := &aghtest.Resolver{
OnLookupIP: func(_ context.Context, _, host string) (ips []net.IP, err error) {
ip4, ip6 := aghtest.HostToIPs(host)
return []net.IP{ip4.AsSlice(), ip6.AsSlice()}, nil
},
}
safeSearchConf := filtering.SafeSearchConfig{
Enabled: true,
Google: true,
Yandex: true,
CustomResolver: resolver,
Enabled: true,
Google: true,
Yandex: true,
}
filterConf := &filtering.Config{
@@ -545,14 +523,13 @@ func TestSafeSearch(t *testing.T) {
},
ServePlainDNS: true,
}
s := createTestServer(t, filterConf, forwardConf, nil)
s := createTestServer(t, filterConf, forwardConf)
startDeferStop(t, s)
addr := s.dnsProxy.Addr(proxy.ProtoUDP).String()
client := &dns.Client{}
yandexIP := netip.AddrFrom4([4]byte{213, 180, 193, 56})
googleIP, _ := aghtest.HostToIPs("forcesafesearch.google.com")
testCases := []struct {
host string
@@ -576,19 +553,19 @@ func TestSafeSearch(t *testing.T) {
wantCNAME: "",
}, {
host: "www.google.com.",
want: googleIP,
want: netip.Addr{},
wantCNAME: "forcesafesearch.google.com.",
}, {
host: "www.google.com.af.",
want: googleIP,
want: netip.Addr{},
wantCNAME: "forcesafesearch.google.com.",
}, {
host: "www.google.be.",
want: googleIP,
want: netip.Addr{},
wantCNAME: "forcesafesearch.google.com.",
}, {
host: "www.google.by.",
want: googleIP,
want: netip.Addr{},
wantCNAME: "forcesafesearch.google.com.",
}}
@@ -605,12 +582,15 @@ func TestSafeSearch(t *testing.T) {
cname := testutil.RequireTypeAssert[*dns.CNAME](t, reply.Answer[0])
assert.Equal(t, tc.wantCNAME, cname.Target)
a := testutil.RequireTypeAssert[*dns.A](t, reply.Answer[1])
assert.NotEmpty(t, a.A)
} else {
require.Len(t, reply.Answer, 1)
}
a := testutil.RequireTypeAssert[*dns.A](t, reply.Answer[len(reply.Answer)-1])
assert.Equal(t, net.IP(tc.want.AsSlice()), a.A)
a := testutil.RequireTypeAssert[*dns.A](t, reply.Answer[0])
assert.Equal(t, net.IP(tc.want.AsSlice()), a.A)
}
})
}
}
@@ -628,7 +608,7 @@ func TestInvalidRequest(t *testing.T) {
},
},
ServePlainDNS: true,
}, nil)
})
startDeferStop(t, s)
addr := s.dnsProxy.Addr(proxy.ProtoUDP).String()
@@ -662,7 +642,7 @@ func TestBlockedRequest(t *testing.T) {
s := createTestServer(t, &filtering.Config{
ProtectionEnabled: true,
BlockingMode: filtering.BlockingModeDefault,
}, forwardConf, nil)
}, forwardConf)
startDeferStop(t, s)
addr := s.dnsProxy.Addr(proxy.ProtoUDP)
@@ -698,12 +678,12 @@ func TestServerCustomClientUpstream(t *testing.T) {
}
s := createTestServer(t, &filtering.Config{
BlockingMode: filtering.BlockingModeDefault,
}, forwardConf, nil)
}, forwardConf)
ups := aghtest.NewUpstreamMock(func(req *dns.Msg) (resp *dns.Msg, err error) {
atomic.AddUint32(&upsCalledCounter, 1)
return aghalg.Coalesce(
return cmp.Or(
aghtest.MatchedResponse(req, dns.TypeA, "host", "192.168.0.1"),
new(dns.Msg).SetRcode(req, dns.RcodeNameError),
), nil
@@ -773,7 +753,7 @@ func TestBlockCNAMEProtectionEnabled(t *testing.T) {
},
},
ServePlainDNS: true,
}, nil)
})
testUpstm := &aghtest.Upstream{
CName: testCNAMEs,
IPv4: testIPv4,
@@ -811,7 +791,7 @@ func TestBlockCNAME(t *testing.T) {
s := createTestServer(t, &filtering.Config{
ProtectionEnabled: true,
BlockingMode: filtering.BlockingModeDefault,
}, forwardConf, nil)
}, forwardConf)
s.conf.UpstreamConfig.Upstreams = []upstream.Upstream{
&aghtest.Upstream{
CName: testCNAMEs,
@@ -886,7 +866,7 @@ func TestClientRulesForCNAMEMatching(t *testing.T) {
}
s := createTestServer(t, &filtering.Config{
BlockingMode: filtering.BlockingModeDefault,
}, forwardConf, nil)
}, forwardConf)
s.conf.UpstreamConfig.Upstreams = []upstream.Upstream{
&aghtest.Upstream{
CName: testCNAMEs,
@@ -933,7 +913,7 @@ func TestNullBlockedRequest(t *testing.T) {
s := createTestServer(t, &filtering.Config{
ProtectionEnabled: true,
BlockingMode: filtering.BlockingModeNullIP,
}, forwardConf, nil)
}, forwardConf)
startDeferStop(t, s)
addr := s.dnsProxy.Addr(proxy.ProtoUDP)
@@ -1054,7 +1034,7 @@ func TestBlockedByHosts(t *testing.T) {
s := createTestServer(t, &filtering.Config{
ProtectionEnabled: true,
BlockingMode: filtering.BlockingModeDefault,
}, forwardConf, nil)
}, forwardConf)
startDeferStop(t, s)
addr := s.dnsProxy.Addr(proxy.ProtoUDP)
@@ -1102,7 +1082,7 @@ func TestBlockedBySafeBrowsing(t *testing.T) {
},
ServePlainDNS: true,
}
s := createTestServer(t, filterConf, forwardConf, nil)
s := createTestServer(t, filterConf, forwardConf)
startDeferStop(t, s)
addr := s.dnsProxy.Addr(proxy.ProtoUDP)
@@ -1164,7 +1144,7 @@ func TestRewrite(t *testing.T) {
}))
ups := aghtest.NewUpstreamMock(func(req *dns.Msg) (resp *dns.Msg, err error) {
return aghalg.Coalesce(
return cmp.Or(
aghtest.MatchedResponse(req, dns.TypeA, "example.org", "4.3.2.1"),
new(dns.Msg).SetRcode(req, dns.RcodeNameError),
), nil
@@ -1482,6 +1462,8 @@ func TestServer_Exchange(t *testing.T) {
onesIP = netip.MustParseAddr("1.1.1.1")
twosIP = netip.MustParseAddr("2.2.2.2")
localIP = netip.MustParseAddr("192.168.1.1")
pt = testutil.PanicT{}
)
onesRevExtIPv4, err := netutil.IPToReversedAddr(onesIP.AsSlice())
@@ -1490,72 +1472,73 @@ func TestServer_Exchange(t *testing.T) {
twosRevExtIPv4, err := netutil.IPToReversedAddr(twosIP.AsSlice())
require.NoError(t, err)
extUpstream := &aghtest.UpstreamMock{
OnAddress: func() (addr string) { return "external.upstream.example" },
OnExchange: func(req *dns.Msg) (resp *dns.Msg, err error) {
return aghalg.Coalesce(
aghtest.MatchedResponse(req, dns.TypePTR, onesRevExtIPv4, onesHost),
doubleTTL(aghtest.MatchedResponse(req, dns.TypePTR, twosRevExtIPv4, twosHost)),
new(dns.Msg).SetRcode(req, dns.RcodeNameError),
), nil
},
}
extUpsHdlr := dns.HandlerFunc(func(w dns.ResponseWriter, req *dns.Msg) {
resp := cmp.Or(
aghtest.MatchedResponse(req, dns.TypePTR, onesRevExtIPv4, dns.Fqdn(onesHost)),
doubleTTL(aghtest.MatchedResponse(req, dns.TypePTR, twosRevExtIPv4, dns.Fqdn(twosHost))),
new(dns.Msg).SetRcode(req, dns.RcodeNameError),
)
require.NoError(pt, w.WriteMsg(resp))
})
upsAddr := aghtest.StartLocalhostUpstream(t, extUpsHdlr).String()
revLocIPv4, err := netutil.IPToReversedAddr(localIP.AsSlice())
require.NoError(t, err)
locUpstream := &aghtest.UpstreamMock{
OnAddress: func() (addr string) { return "local.upstream.example" },
OnExchange: func(req *dns.Msg) (resp *dns.Msg, err error) {
return aghalg.Coalesce(
aghtest.MatchedResponse(req, dns.TypePTR, revLocIPv4, localDomainHost),
new(dns.Msg).SetRcode(req, dns.RcodeNameError),
), nil
},
}
locUpsHdlr := dns.HandlerFunc(func(w dns.ResponseWriter, req *dns.Msg) {
resp := cmp.Or(
aghtest.MatchedResponse(req, dns.TypePTR, revLocIPv4, dns.Fqdn(localDomainHost)),
new(dns.Msg).SetRcode(req, dns.RcodeNameError),
)
errUpstream := aghtest.NewErrorUpstream()
nonPtrUpstream := aghtest.NewBlockUpstream("some-host", true)
refusingUpstream := aghtest.NewUpstreamMock(func(req *dns.Msg) (resp *dns.Msg, err error) {
return new(dns.Msg).SetRcode(req, dns.RcodeRefused), nil
require.NoError(pt, w.WriteMsg(resp))
})
zeroTTLUps := &aghtest.UpstreamMock{
OnAddress: func() (addr string) { return "zero.ttl.example" },
OnExchange: func(req *dns.Msg) (resp *dns.Msg, err error) {
resp = new(dns.Msg).SetReply(req)
hdr := dns.RR_Header{
Name: req.Question[0].Name,
Rrtype: dns.TypePTR,
Class: dns.ClassINET,
Ttl: 0,
}
resp.Answer = []dns.RR{&dns.PTR{
Hdr: hdr,
Ptr: localDomainHost,
}}
return resp, nil
},
}
errUpsHdlr := dns.HandlerFunc(func(w dns.ResponseWriter, req *dns.Msg) {
require.NoError(pt, w.WriteMsg(new(dns.Msg).SetRcode(req, dns.RcodeServerFailure)))
})
srv := &Server{
recDetector: newRecursionDetector(0, 1),
internalProxy: &proxy.Proxy{
Config: proxy.Config{
UpstreamConfig: &proxy.UpstreamConfig{
Upstreams: []upstream.Upstream{extUpstream},
nonPtrHdlr := dns.HandlerFunc(func(w dns.ResponseWriter, req *dns.Msg) {
hash := sha256.Sum256([]byte("some-host"))
resp := (&dns.Msg{
Answer: []dns.RR{&dns.TXT{
Hdr: dns.RR_Header{
Name: req.Question[0].Name,
Rrtype: dns.TypeTXT,
Class: dns.ClassINET,
Ttl: 60,
},
},
},
}
srv.conf.UsePrivateRDNS = true
srv.privateNets = netutil.SubnetSetFunc(netutil.IsLocallyServed)
require.NoError(t, srv.internalProxy.Init())
Txt: []string{hex.EncodeToString(hash[:])},
}},
}).SetReply(req)
require.NoError(pt, w.WriteMsg(resp))
})
refusingHdlr := dns.HandlerFunc(func(w dns.ResponseWriter, req *dns.Msg) {
require.NoError(pt, w.WriteMsg(new(dns.Msg).SetRcode(req, dns.RcodeRefused)))
})
zeroTTLHdlr := dns.HandlerFunc(func(w dns.ResponseWriter, req *dns.Msg) {
resp := (&dns.Msg{
Answer: []dns.RR{&dns.PTR{
Hdr: dns.RR_Header{
Name: req.Question[0].Name,
Rrtype: dns.TypePTR,
Class: dns.ClassINET,
Ttl: 0,
},
Ptr: dns.Fqdn(localDomainHost),
}},
}).SetReply(req)
require.NoError(pt, w.WriteMsg(resp))
})
testCases := []struct {
req netip.Addr
wantErr error
locUpstream upstream.Upstream
locUpstream dns.Handler
name string
want string
wantTTL time.Duration
@@ -1570,35 +1553,35 @@ func TestServer_Exchange(t *testing.T) {
name: "local_good",
want: localDomainHost,
wantErr: nil,
locUpstream: locUpstream,
locUpstream: locUpsHdlr,
req: localIP,
wantTTL: defaultTTL,
}, {
name: "upstream_error",
want: "",
wantErr: aghtest.ErrUpstream,
locUpstream: errUpstream,
wantErr: ErrRDNSFailed,
locUpstream: errUpsHdlr,
req: localIP,
wantTTL: 0,
}, {
name: "empty_answer_error",
want: "",
wantErr: ErrRDNSNoData,
locUpstream: locUpstream,
locUpstream: locUpsHdlr,
req: netip.MustParseAddr("192.168.1.2"),
wantTTL: 0,
}, {
name: "invalid_answer",
want: "",
wantErr: ErrRDNSNoData,
locUpstream: nonPtrUpstream,
locUpstream: nonPtrHdlr,
req: localIP,
wantTTL: 0,
}, {
name: "refused",
want: "",
wantErr: ErrRDNSFailed,
locUpstream: refusingUpstream,
locUpstream: refusingHdlr,
req: localIP,
wantTTL: 0,
}, {
@@ -1612,23 +1595,28 @@ func TestServer_Exchange(t *testing.T) {
name: "zero_ttl",
want: localDomainHost,
wantErr: nil,
locUpstream: zeroTTLUps,
locUpstream: zeroTTLHdlr,
req: localIP,
wantTTL: 0,
}}
for _, tc := range testCases {
pcfg := proxy.Config{
UpstreamConfig: &proxy.UpstreamConfig{
Upstreams: []upstream.Upstream{tc.locUpstream},
},
}
srv.localResolvers = &proxy.Proxy{
Config: pcfg,
}
require.NoError(t, srv.localResolvers.Init())
localUpsAddr := aghtest.StartLocalhostUpstream(t, tc.locUpstream).String()
t.Run(tc.name, func(t *testing.T) {
srv := createTestServer(t, &filtering.Config{
BlockingMode: filtering.BlockingModeDefault,
}, ServerConfig{
Config: Config{
UpstreamDNS: []string{upsAddr},
UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
},
LocalPTRResolvers: []string{localUpsAddr},
UsePrivateRDNS: true,
ServePlainDNS: true,
})
host, ttl, eerr := srv.Exchange(tc.req)
require.ErrorIs(t, eerr, tc.wantErr)
@@ -1638,8 +1626,17 @@ func TestServer_Exchange(t *testing.T) {
}
t.Run("resolving_disabled", func(t *testing.T) {
srv.conf.UsePrivateRDNS = false
t.Cleanup(func() { srv.conf.UsePrivateRDNS = true })
srv := createTestServer(t, &filtering.Config{
BlockingMode: filtering.BlockingModeDefault,
}, ServerConfig{
Config: Config{
UpstreamDNS: []string{upsAddr},
UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
},
LocalPTRResolvers: []string{},
ServePlainDNS: true,
})
host, _, eerr := srv.Exchange(localIP)

View File

@@ -143,7 +143,7 @@ func (s *Server) filterDNSRewrite(
res *filtering.Result,
pctx *proxy.DNSContext,
) (err error) {
resp := s.makeResponse(req)
resp := s.replyCompressed(req)
dnsrr := res.DNSRewriteResult
if dnsrr == nil {
return errors.Error("no dns rewrite rule content")

View File

@@ -42,7 +42,7 @@ func TestServer_FilterDNSRewrite(t *testing.T) {
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
},
ServePlainDNS: true,
}, nil)
})
makeQ := func(qtype rules.RRType) (req *dns.Msg) {
return &dns.Msg{

View File

@@ -1,57 +1,17 @@
package dnsforward
import (
"encoding/binary"
"fmt"
"net"
"slices"
"strings"
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/urlfilter/rules"
"github.com/miekg/dns"
)
// beforeRequestHandler is the handler that is called before any other
// processing, including logs. It performs access checks and puts the client
// ID, if there is one, into the server's cache.
func (s *Server) beforeRequestHandler(
_ *proxy.Proxy,
pctx *proxy.DNSContext,
) (reply bool, err error) {
clientID, err := s.clientIDFromDNSContext(pctx)
if err != nil {
return false, fmt.Errorf("getting clientid: %w", err)
}
blocked, _ := s.IsBlockedClient(pctx.Addr.Addr(), clientID)
if blocked {
return s.preBlockedResponse(pctx)
}
if len(pctx.Req.Question) == 1 {
q := pctx.Req.Question[0]
qt := q.Qtype
host := aghnet.NormalizeDomain(q.Name)
if s.access.isBlockedHost(host, qt) {
log.Debug("access: request %s %s is in access blocklist", dns.Type(qt), host)
return s.preBlockedResponse(pctx)
}
}
if clientID != "" {
key := [8]byte{}
binary.BigEndian.PutUint64(key[:], pctx.RequestID)
s.clientIDCache.Set(key[:], []byte(clientID))
}
return true, nil
}
// clientRequestFilteringSettings looks up client filtering settings using the
// client's IP address and ID, if any, from dctx.
func (s *Server) clientRequestFilteringSettings(dctx *dnsContext) (setts *filtering.Settings) {
@@ -71,6 +31,7 @@ func (s *Server) filterDNSRequest(dctx *dnsContext) (res *filtering.Result, err
req := pctx.Req
q := req.Question[0]
host := strings.TrimSuffix(q.Name, ".")
resVal, err := s.dnsFilter.CheckHost(host, q.Qtype, dctx.setts)
if err != nil {
return nil, fmt.Errorf("checking host %q: %w", host, err)
@@ -79,22 +40,15 @@ func (s *Server) filterDNSRequest(dctx *dnsContext) (res *filtering.Result, err
// TODO(a.garipov): Make CheckHost return a pointer.
res = &resVal
switch {
case res.IsFiltered:
log.Debug(
"dnsforward: host %q is filtered, reason: %q; rule: %q",
host,
res.Reason,
res.Rules[0].Text,
)
pctx.Res = s.genDNSFilterMessage(pctx, res)
case res.Reason.In(filtering.Rewritten, filtering.RewrittenRule) &&
res.CanonName != "" &&
len(res.IPList) == 0:
case isRewrittenCNAME(res):
// Resolve the new canonical name, not the original host name. The
// original question is readded in processFilteringAfterResponse.
dctx.origQuestion = q
req.Question[0].Name = dns.Fqdn(res.CanonName)
case res.Reason == filtering.Rewritten:
case res.IsFiltered:
log.Debug("dnsforward: host %q is filtered, reason: %q", host, res.Reason)
pctx.Res = s.genDNSFilterMessage(pctx, res)
case res.Reason.In(filtering.Rewritten, filtering.FilteredSafeSearch):
pctx.Res = s.getCNAMEWithIPs(req, res.IPList, res.CanonName)
case res.Reason.In(filtering.RewrittenRule, filtering.RewrittenAutoHosts):
if err = s.filterDNSRewrite(req, res, pctx); err != nil {
@@ -105,6 +59,17 @@ func (s *Server) filterDNSRequest(dctx *dnsContext) (res *filtering.Result, err
return res, err
}
// isRewrittenCNAME returns true if the request considered to be rewritten with
// CNAME and has no resolved IPs.
func isRewrittenCNAME(res *filtering.Result) (ok bool) {
return res.Reason.In(
filtering.Rewritten,
filtering.RewrittenRule,
filtering.FilteredSafeSearch) &&
res.CanonName != "" &&
len(res.IPList) == 0
}
// checkHostRules checks the host against filters. It is safe for concurrent
// use.
func (s *Server) checkHostRules(

View File

@@ -1,6 +1,7 @@
package dnsforward
import (
"cmp"
"encoding/json"
"fmt"
"io"
@@ -261,55 +262,17 @@ func (req *jsonDNSConfig) checkUpstreamMode() (err error) {
}
}
// checkBootstrap returns an error if any bootstrap address is invalid.
func (req *jsonDNSConfig) checkBootstrap() (err error) {
if req.Bootstraps == nil {
return nil
}
var b string
defer func() { err = errors.Annotate(err, "checking bootstrap %s: %w", b) }()
for _, b = range *req.Bootstraps {
if b == "" {
return errors.Error("empty")
}
var resolver *upstream.UpstreamResolver
if resolver, err = upstream.NewUpstreamResolver(b, nil); err != nil {
// Don't wrap the error because it's informative enough as is.
return err
}
if err = resolver.Close(); err != nil {
return fmt.Errorf("closing %s: %w", b, err)
}
}
return nil
}
// checkFallbacks returns an error if any fallback address is invalid.
func (req *jsonDNSConfig) checkFallbacks() (err error) {
if req.Fallbacks == nil {
return nil
}
_, err = proxy.ParseUpstreamsConfig(*req.Fallbacks, &upstream.Options{})
if err != nil {
return fmt.Errorf("fallback servers: %w", err)
}
return nil
}
// validate returns an error if any field of req is invalid.
//
// TODO(s.chzhen): Parse, don't validate.
func (req *jsonDNSConfig) validate(privateNets netutil.SubnetSet) (err error) {
func (req *jsonDNSConfig) validate(
ownAddrs addrPortSet,
sysResolvers SystemResolvers,
privateNets netutil.SubnetSet,
) (err error) {
defer func() { err = errors.Annotate(err, "validating dns config: %w") }()
err = req.validateUpstreamDNSServers(privateNets)
err = req.validateUpstreamDNSServers(ownAddrs, sysResolvers, privateNets)
if err != nil {
// Don't wrap the error since it's informative enough as is.
return err
@@ -342,20 +305,84 @@ func (req *jsonDNSConfig) validate(privateNets netutil.SubnetSet) (err error) {
return nil
}
// checkBootstrap returns an error if any bootstrap address is invalid.
func (req *jsonDNSConfig) checkBootstrap() (err error) {
if req.Bootstraps == nil {
return nil
}
var b string
defer func() { err = errors.Annotate(err, "checking bootstrap %s: %w", b) }()
for _, b = range *req.Bootstraps {
if b == "" {
return errors.Error("empty")
}
var resolver *upstream.UpstreamResolver
if resolver, err = upstream.NewUpstreamResolver(b, nil); err != nil {
// Don't wrap the error because it's informative enough as is.
return err
}
if err = resolver.Close(); err != nil {
return fmt.Errorf("closing %s: %w", b, err)
}
}
return nil
}
// containsPrivateRDNS returns true if req contains private RDNS settings and
// should be validated.
func (req *jsonDNSConfig) containsPrivateRDNS() (ok bool) {
return (req.UsePrivateRDNS != nil && *req.UsePrivateRDNS) ||
(req.LocalPTRUpstreams != nil && len(*req.LocalPTRUpstreams) > 0)
}
// checkPrivateRDNS returns an error if the configuration of the private RDNS is
// not valid.
func (req *jsonDNSConfig) checkPrivateRDNS(
ownAddrs addrPortSet,
sysResolvers SystemResolvers,
privateNets netutil.SubnetSet,
) (err error) {
if !req.containsPrivateRDNS() {
return nil
}
addrs := cmp.Or(req.LocalPTRUpstreams, &[]string{})
uc, err := newPrivateConfig(*addrs, ownAddrs, sysResolvers, privateNets, &upstream.Options{})
err = errors.WithDeferred(err, uc.Close())
if err != nil {
return fmt.Errorf("private upstream servers: %w", err)
}
return nil
}
// validateUpstreamDNSServers returns an error if any field of req is invalid.
func (req *jsonDNSConfig) validateUpstreamDNSServers(privateNets netutil.SubnetSet) (err error) {
func (req *jsonDNSConfig) validateUpstreamDNSServers(
ownAddrs addrPortSet,
sysResolvers SystemResolvers,
privateNets netutil.SubnetSet,
) (err error) {
var uc *proxy.UpstreamConfig
opts := &upstream.Options{}
if req.Upstreams != nil {
_, err = proxy.ParseUpstreamsConfig(*req.Upstreams, &upstream.Options{})
uc, err = proxy.ParseUpstreamsConfig(*req.Upstreams, opts)
err = errors.WithDeferred(err, uc.Close())
if err != nil {
return fmt.Errorf("upstream servers: %w", err)
}
}
if req.LocalPTRUpstreams != nil {
err = ValidateUpstreamsPrivate(*req.LocalPTRUpstreams, privateNets)
if err != nil {
return fmt.Errorf("private upstream servers: %w", err)
}
err = req.checkPrivateRDNS(ownAddrs, sysResolvers, privateNets)
if err != nil {
// Don't wrap the error since it's informative enough as is.
return err
}
err = req.checkBootstrap()
@@ -364,10 +391,12 @@ func (req *jsonDNSConfig) validateUpstreamDNSServers(privateNets netutil.SubnetS
return err
}
err = req.checkFallbacks()
if err != nil {
// Don't wrap the error since it's informative enough as is.
return err
if req.Fallbacks != nil {
uc, err = proxy.ParseUpstreamsConfig(*req.Fallbacks, opts)
err = errors.WithDeferred(err, uc.Close())
if err != nil {
return fmt.Errorf("fallback servers: %w", err)
}
}
return nil
@@ -436,7 +465,16 @@ func (s *Server) handleSetConfig(w http.ResponseWriter, r *http.Request) {
return
}
err = req.validate(s.privateNets)
// TODO(e.burkov): Consider prebuilding this set on startup.
ourAddrs, err := s.conf.ourAddrsSet()
if err != nil {
// TODO(e.burkov): Put into openapi.
aghhttp.Error(r, w, http.StatusInternalServerError, "getting our addresses: %s", err)
return
}
err = req.validate(ourAddrs, s.sysResolvers, s.privateNets)
if err != nil {
aghhttp.Error(r, w, http.StatusBadRequest, "%s", err)
@@ -474,8 +512,6 @@ func (s *Server) setConfig(dc *jsonDNSConfig) (shouldRestart bool) {
if dc.UpstreamMode != nil {
s.conf.UpstreamMode = mustParseUpstreamMode(*dc.UpstreamMode)
} else {
s.conf.UpstreamMode = UpstreamModeLoadBalance
}
if dc.EDNSCSUseCustom != nil && *dc.EDNSCSUseCustom {
@@ -589,7 +625,7 @@ func (s *Server) handleTestUpstreamDNS(w http.ResponseWriter, r *http.Request) {
}
var boots []*upstream.UpstreamResolver
opts.Bootstrap, boots, err = s.createBootstrap(req.BootstrapDNS, opts)
opts.Bootstrap, boots, err = newBootstrap(req.BootstrapDNS, s.etcHosts, opts)
if err != nil {
aghhttp.Error(r, w, http.StatusBadRequest, "Failed to parse bootstrap servers: %s", err)

View File

@@ -29,6 +29,10 @@ import (
"github.com/stretchr/testify/require"
)
// TODO(e.burkov): Use the better approach to testdata with a separate
// directory for each test, and a separate file for each subtest. See the
// [configmigrate] package.
// emptySysResolvers is an empty [SystemResolvers] implementation that always
// returns nil.
type emptySysResolvers struct{}
@@ -83,7 +87,7 @@ func TestDNSForwardHTTP_handleGetConfig(t *testing.T) {
ConfigModified: func() {},
ServePlainDNS: true,
}
s := createTestServer(t, filterConf, forwardConf, nil)
s := createTestServer(t, filterConf, forwardConf)
s.sysResolvers = &emptySysResolvers{}
require.NoError(t, s.Start())
@@ -164,7 +168,7 @@ func TestDNSForwardHTTP_handleSetConfig(t *testing.T) {
ConfigModified: func() {},
ServePlainDNS: true,
}
s := createTestServer(t, filterConf, forwardConf, nil)
s := createTestServer(t, filterConf, forwardConf)
s.sysResolvers = &emptySysResolvers{}
defaultConf := s.conf
@@ -241,9 +245,8 @@ func TestDNSForwardHTTP_handleSetConfig(t *testing.T) {
wantSet: "",
}, {
name: "local_ptr_upstreams_bad",
wantSet: `validating dns config: ` +
`private upstream servers: checking domain-specific upstreams: ` +
`bad arpa domain name "non.arpa.": not a reversed ip network`,
wantSet: `validating dns config: private upstream servers: ` +
`bad arpa domain name "non.arpa": not a reversed ip network`,
}, {
name: "local_ptr_upstreams_null",
wantSet: "",
@@ -314,58 +317,6 @@ func TestIsCommentOrEmpty(t *testing.T) {
}
}
func TestValidateUpstreamsPrivate(t *testing.T) {
ss := netutil.SubnetSetFunc(netutil.IsLocallyServed)
testCases := []struct {
name string
wantErr string
u string
}{{
name: "success_address",
wantErr: ``,
u: "[/1.0.0.127.in-addr.arpa/]#",
}, {
name: "success_subnet",
wantErr: ``,
u: "[/127.in-addr.arpa/]#",
}, {
name: "not_arpa_subnet",
wantErr: `checking domain-specific upstreams: ` +
`bad arpa domain name "hello.world.": not a reversed ip network`,
u: "[/hello.world/]#",
}, {
name: "non-private_arpa_address",
wantErr: `checking domain-specific upstreams: ` +
`arpa domain "1.2.3.4.in-addr.arpa." should point to a locally-served network`,
u: "[/1.2.3.4.in-addr.arpa/]#",
}, {
name: "non-private_arpa_subnet",
wantErr: `checking domain-specific upstreams: ` +
`arpa domain "128.in-addr.arpa." should point to a locally-served network`,
u: "[/128.in-addr.arpa/]#",
}, {
name: "several_bad",
wantErr: `checking domain-specific upstreams: ` +
`arpa domain "1.2.3.4.in-addr.arpa." should point to a locally-served network` + "\n" +
`bad arpa domain name "non.arpa.": not a reversed ip network`,
u: "[/non.arpa/1.2.3.4.in-addr.arpa/127.in-addr.arpa/]#",
}, {
name: "partial_good",
wantErr: "",
u: "[/a.1.2.3.10.in-addr.arpa/a.10.in-addr.arpa/]#",
}}
for _, tc := range testCases {
set := []string{"192.168.0.1", tc.u}
t.Run(tc.name, func(t *testing.T) {
err := ValidateUpstreamsPrivate(set, ss)
testutil.AssertErrorMsg(t, tc.wantErr, err)
})
}
}
func newLocalUpstreamListener(t *testing.T, port uint16, handler dns.Handler) (real netip.AddrPort) {
t.Helper()
@@ -439,7 +390,7 @@ func TestServer_HandleTestUpstreamDNS(t *testing.T) {
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
},
ServePlainDNS: true,
}, nil)
})
srv.etcHosts = upstream.NewHostsResolver(hc)
startDeferStop(t, srv)

View File

@@ -11,17 +11,21 @@ import (
"github.com/miekg/dns"
)
// makeResponse creates a DNS response by req and sets necessary flags. It also
// guarantees that req.Question will be not empty.
func (s *Server) makeResponse(req *dns.Msg) (resp *dns.Msg) {
resp = &dns.Msg{
MsgHdr: dns.MsgHdr{
RecursionAvailable: true,
},
Compress: true,
}
// TODO(e.burkov): Name all the methods by a [proxy.MessageConstructor]
// template. Also extract all the methods to a separate entity.
resp.SetReply(req)
// reply creates a DNS response for req.
func (*Server) reply(req *dns.Msg, code int) (resp *dns.Msg) {
resp = (&dns.Msg{}).SetRcode(req, code)
resp.RecursionAvailable = true
return resp
}
// replyCompressed creates a DNS response for req and sets the compress flag.
func (s *Server) replyCompressed(req *dns.Msg) (resp *dns.Msg) {
resp = s.reply(req, dns.RcodeSuccess)
resp.Compress = true
return resp
}
@@ -48,10 +52,10 @@ func (s *Server) genDNSFilterMessage(
) (resp *dns.Msg) {
req := dctx.Req
qt := req.Question[0].Qtype
if qt != dns.TypeA && qt != dns.TypeAAAA {
if qt != dns.TypeA && qt != dns.TypeAAAA && qt != dns.TypeHTTPS {
m, _, _ := s.dnsFilter.BlockingMode()
if m == filtering.BlockingModeNullIP {
return s.makeResponse(req)
return s.replyCompressed(req)
}
return s.newMsgNODATA(req)
@@ -75,7 +79,7 @@ func (s *Server) genDNSFilterMessage(
// getCNAMEWithIPs generates a filtered response to req for with CNAME record
// and provided ips.
func (s *Server) getCNAMEWithIPs(req *dns.Msg, ips []netip.Addr, cname string) (resp *dns.Msg) {
resp = s.makeResponse(req)
resp = s.replyCompressed(req)
originalName := req.Question[0].Name
@@ -121,13 +125,13 @@ func (s *Server) genForBlockingMode(req *dns.Msg, ips []netip.Addr) (resp *dns.M
case filtering.BlockingModeNullIP:
return s.makeResponseNullIP(req)
case filtering.BlockingModeNXDOMAIN:
return s.genNXDomain(req)
return s.NewMsgNXDOMAIN(req)
case filtering.BlockingModeREFUSED:
return s.makeResponseREFUSED(req)
default:
log.Error("dnsforward: invalid blocking mode %q", mode)
return s.makeResponse(req)
return s.replyCompressed(req)
}
}
@@ -148,25 +152,18 @@ func (s *Server) makeResponseCustomIP(
// genDNSFilterMessage.
log.Error("dnsforward: invalid msg type %s for custom IP blocking mode", dns.Type(qt))
return s.makeResponse(req)
return s.replyCompressed(req)
}
}
func (s *Server) genServerFailure(request *dns.Msg) *dns.Msg {
resp := dns.Msg{}
resp.SetRcode(request, dns.RcodeServerFailure)
resp.RecursionAvailable = true
return &resp
}
func (s *Server) genARecord(request *dns.Msg, ip netip.Addr) *dns.Msg {
resp := s.makeResponse(request)
resp := s.replyCompressed(request)
resp.Answer = append(resp.Answer, s.genAnswerA(request, ip))
return resp
}
func (s *Server) genAAAARecord(request *dns.Msg, ip netip.Addr) *dns.Msg {
resp := s.makeResponse(request)
resp := s.replyCompressed(request)
resp.Answer = append(resp.Answer, s.genAnswerAAAA(request, ip))
return resp
}
@@ -252,7 +249,7 @@ func (s *Server) genResponseWithIPs(req *dns.Msg, ips []netip.Addr) (resp *dns.M
// Go on and return an empty response.
}
resp = s.makeResponse(req)
resp = s.replyCompressed(req)
resp.Answer = ans
return resp
@@ -288,7 +285,7 @@ func (s *Server) makeResponseNullIP(req *dns.Msg) (resp *dns.Msg) {
case dns.TypeAAAA:
resp = s.genResponseWithIPs(req, []netip.Addr{netip.IPv6Unspecified()})
default:
resp = s.makeResponse(req)
resp = s.replyCompressed(req)
}
return resp
@@ -298,7 +295,7 @@ func (s *Server) genBlockedHost(request *dns.Msg, newAddr string, d *proxy.DNSCo
if newAddr == "" {
log.Info("dnsforward: block host is not specified")
return s.genServerFailure(request)
return s.NewMsgSERVFAIL(request)
}
ip, err := netip.ParseAddr(newAddr)
@@ -321,17 +318,17 @@ func (s *Server) genBlockedHost(request *dns.Msg, newAddr string, d *proxy.DNSCo
if prx == nil {
log.Debug("dnsforward: %s", srvClosedErr)
return s.genServerFailure(request)
return s.NewMsgSERVFAIL(request)
}
err = prx.Resolve(newContext)
if err != nil {
log.Info("dnsforward: looking up replacement host %q: %s", newAddr, err)
return s.genServerFailure(request)
return s.NewMsgSERVFAIL(request)
}
resp := s.makeResponse(request)
resp := s.replyCompressed(request)
if newContext.Res != nil {
for _, answer := range newContext.Res.Answer {
answer.Header().Name = request.Question[0].Name
@@ -342,47 +339,21 @@ func (s *Server) genBlockedHost(request *dns.Msg, newAddr string, d *proxy.DNSCo
return resp
}
// preBlockedResponse returns a protocol-appropriate response for a request that
// was blocked by access settings.
func (s *Server) preBlockedResponse(pctx *proxy.DNSContext) (reply bool, err error) {
if pctx.Proto == proxy.ProtoUDP || pctx.Proto == proxy.ProtoDNSCrypt {
// Return nil so that dnsproxy drops the connection and thus
// prevent DNS amplification attacks.
return false, nil
}
pctx.Res = s.makeResponseREFUSED(pctx.Req)
return true, nil
}
// Create REFUSED DNS response
func (s *Server) makeResponseREFUSED(request *dns.Msg) *dns.Msg {
resp := dns.Msg{}
resp.SetRcode(request, dns.RcodeRefused)
resp.RecursionAvailable = true
return &resp
func (s *Server) makeResponseREFUSED(req *dns.Msg) *dns.Msg {
return s.reply(req, dns.RcodeRefused)
}
// newMsgNODATA returns a properly initialized NODATA response.
//
// See https://www.rfc-editor.org/rfc/rfc2308#section-2.2.
func (s *Server) newMsgNODATA(req *dns.Msg) (resp *dns.Msg) {
resp = (&dns.Msg{}).SetRcode(req, dns.RcodeSuccess)
resp.RecursionAvailable = true
resp = s.reply(req, dns.RcodeSuccess)
resp.Ns = s.genSOA(req)
return resp
}
func (s *Server) genNXDomain(request *dns.Msg) *dns.Msg {
resp := dns.Msg{}
resp.SetRcode(request, dns.RcodeNameError)
resp.RecursionAvailable = true
resp.Ns = s.genSOA(request)
return &resp
}
func (s *Server) genSOA(request *dns.Msg) []dns.RR {
zone := ""
if len(request.Question) > 0 {
@@ -414,5 +385,43 @@ func (s *Server) genSOA(request *dns.Msg) []dns.RR {
if len(zone) > 0 && zone[0] != '.' {
soa.Mbox += zone
}
return []dns.RR{&soa}
}
// type check
var _ proxy.MessageConstructor = (*Server)(nil)
// NewMsgNXDOMAIN implements the [proxy.MessageConstructor] interface for
// *Server.
func (s *Server) NewMsgNXDOMAIN(req *dns.Msg) (resp *dns.Msg) {
resp = s.reply(req, dns.RcodeNameError)
resp.Ns = s.genSOA(req)
return resp
}
// NewMsgSERVFAIL implements the [proxy.MessageConstructor] interface for
// *Server.
func (s *Server) NewMsgSERVFAIL(req *dns.Msg) (resp *dns.Msg) {
return s.reply(req, dns.RcodeServerFailure)
}
// NewMsgNOTIMPLEMENTED implements the [proxy.MessageConstructor] interface for
// *Server.
func (s *Server) NewMsgNOTIMPLEMENTED(req *dns.Msg) (resp *dns.Msg) {
resp = s.reply(req, dns.RcodeNotImplemented)
// Most of the Internet and especially the inner core has an MTU of at least
// 1500 octets. Maximum DNS/UDP payload size for IPv6 on MTU 1500 ethernet
// is 1452 (1500 minus 40 (IPv6 header size) minus 8 (UDP header size)).
//
// See appendix A of https://datatracker.ietf.org/doc/draft-ietf-dnsop-avoid-fragmentation/17.
const maxUDPPayload = 1452
// NOTIMPLEMENTED without EDNS is treated as 'we don't support EDNS', so
// explicitly set it.
resp.SetEdns0(maxUDPPayload, false)
return resp
}

View File

@@ -1,20 +1,17 @@
package dnsforward
import (
"cmp"
"encoding/binary"
"net"
"net/netip"
"strconv"
"strings"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/dnsproxy/upstream"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/stringutil"
"github.com/miekg/dns"
)
@@ -34,11 +31,6 @@ type dnsContext struct {
// response is modified by filters.
origResp *dns.Msg
// unreversedReqIP stores an IP address obtained from a PTR request if it
// was parsed successfully and belongs to one of the locally served IP
// ranges.
unreversedReqIP netip.Addr
// err is the error returned from a processing function.
err error
@@ -63,10 +55,6 @@ type dnsContext struct {
// responseAD shows if the response had the AD bit set.
responseAD bool
// isLocalClient shows if client's IP address is from locally served
// network.
isLocalClient bool
// isDHCPHost is true if the request for a local domain name and the DHCP is
// available for this request.
isDHCPHost bool
@@ -109,15 +97,11 @@ func (s *Server) handleDNSRequest(_ *proxy.Proxy, pctx *proxy.DNSContext) error
// (*proxy.Proxy).handleDNSRequest method performs it before calling the
// appropriate handler.
mods := []modProcessFunc{
s.processRecursion,
s.processInitial,
s.processDDRQuery,
s.processDetermineLocal,
s.processDHCPHosts,
s.processRestrictLocal,
s.processDHCPAddrs,
s.processFilteringBeforeRequest,
s.processLocalPTR,
s.processUpstream,
s.processFilteringAfterResponse,
s.ipset.process,
@@ -145,24 +129,6 @@ func (s *Server) handleDNSRequest(_ *proxy.Proxy, pctx *proxy.DNSContext) error
return nil
}
// processRecursion checks the incoming request and halts its handling by
// answering NXDOMAIN if s has tried to resolve it recently.
func (s *Server) processRecursion(dctx *dnsContext) (rc resultCode) {
log.Debug("dnsforward: started processing recursion")
defer log.Debug("dnsforward: finished processing recursion")
pctx := dctx.proxyCtx
if msg := pctx.Req; msg != nil && s.recDetector.check(*msg) {
log.Debug("dnsforward: recursion detected resolving %q", msg.Question[0].Name)
pctx.Res = s.genNXDomain(pctx.Req)
return resultCodeFinish
}
return resultCodeSuccess
}
// mozillaFQDN is the domain used to signal the Firefox browser to not use its
// own DoH server.
//
@@ -199,14 +165,14 @@ func (s *Server) processInitial(dctx *dnsContext) (rc resultCode) {
}
if (qt == dns.TypeA || qt == dns.TypeAAAA) && q.Name == mozillaFQDN {
pctx.Res = s.genNXDomain(pctx.Req)
pctx.Res = s.NewMsgNXDOMAIN(pctx.Req)
return resultCodeFinish
}
if q.Name == healthcheckFQDN {
// Generate a NODATA negative response to make nslookup exit with 0.
pctx.Res = s.makeResponse(pctx.Req)
pctx.Res = s.replyCompressed(pctx.Req)
return resultCodeFinish
}
@@ -272,7 +238,7 @@ func (s *Server) processDDRQuery(dctx *dnsContext) (rc resultCode) {
//
// [draft standard]: https://www.ietf.org/archive/id/draft-ietf-add-ddr-10.html.
func (s *Server) makeDDRResponse(req *dns.Msg) (resp *dns.Msg) {
resp = s.makeResponse(req)
resp = s.replyCompressed(req)
if req.Question[0].Qtype != dns.TypeSVCB {
return resp
}
@@ -339,19 +305,6 @@ func (s *Server) makeDDRResponse(req *dns.Msg) (resp *dns.Msg) {
return resp
}
// processDetermineLocal determines if the client's IP address is from locally
// served network and saves the result into the context.
func (s *Server) processDetermineLocal(dctx *dnsContext) (rc resultCode) {
log.Debug("dnsforward: started processing local detection")
defer log.Debug("dnsforward: finished processing local detection")
rc = resultCodeSuccess
dctx.isLocalClient = s.privateNets.Contains(dctx.proxyCtx.Addr.Addr())
return rc
}
// processDHCPHosts respond to A requests if the target hostname is known to
// the server. It responds with a mapped IP address if the DNS64 is enabled and
// the request is for AAAA.
@@ -370,9 +323,9 @@ func (s *Server) processDHCPHosts(dctx *dnsContext) (rc resultCode) {
return resultCodeSuccess
}
if !dctx.isLocalClient {
if !pctx.IsPrivateClient {
log.Debug("dnsforward: %q requests for dhcp host %q", pctx.Addr, dhcpHost)
pctx.Res = s.genNXDomain(req)
pctx.Res = s.NewMsgNXDOMAIN(req)
// Do not even put into query log.
return resultCodeFinish
@@ -389,7 +342,7 @@ func (s *Server) processDHCPHosts(dctx *dnsContext) (rc resultCode) {
log.Debug("dnsforward: dhcp record for %q is %s", dhcpHost, ip)
resp := s.makeResponse(req)
resp := s.replyCompressed(req)
switch q.Qtype {
case dns.TypeA:
a := &dns.A{
@@ -416,141 +369,6 @@ func (s *Server) processDHCPHosts(dctx *dnsContext) (rc resultCode) {
return resultCodeSuccess
}
// indexFirstV4Label returns the index at which the reversed IPv4 address
// starts, assuming the domain is pre-validated ARPA domain having in-addr and
// arpa labels removed.
func indexFirstV4Label(domain string) (idx int) {
idx = len(domain)
for labelsNum := 0; labelsNum < net.IPv4len && idx > 0; labelsNum++ {
curIdx := strings.LastIndexByte(domain[:idx-1], '.') + 1
_, parseErr := strconv.ParseUint(domain[curIdx:idx-1], 10, 8)
if parseErr != nil {
return idx
}
idx = curIdx
}
return idx
}
// indexFirstV6Label returns the index at which the reversed IPv6 address
// starts, assuming the domain is pre-validated ARPA domain having ip6 and arpa
// labels removed.
func indexFirstV6Label(domain string) (idx int) {
idx = len(domain)
for labelsNum := 0; labelsNum < net.IPv6len*2 && idx > 0; labelsNum++ {
curIdx := idx - len("a.")
if curIdx > 1 && domain[curIdx-1] != '.' {
return idx
}
nibble := domain[curIdx]
if (nibble < '0' || nibble > '9') && (nibble < 'a' || nibble > 'f') {
return idx
}
idx = curIdx
}
return idx
}
// extractARPASubnet tries to convert a reversed ARPA address being a part of
// domain to an IP network. domain must be an FQDN.
//
// TODO(e.burkov): Move to golibs.
func extractARPASubnet(domain string) (pref netip.Prefix, err error) {
err = netutil.ValidateDomainName(strings.TrimSuffix(domain, "."))
if err != nil {
// Don't wrap the error since it's informative enough as is.
return netip.Prefix{}, err
}
const (
v4Suffix = "in-addr.arpa."
v6Suffix = "ip6.arpa."
)
domain = strings.ToLower(domain)
var idx int
switch {
case strings.HasSuffix(domain, v4Suffix):
idx = indexFirstV4Label(domain[:len(domain)-len(v4Suffix)])
case strings.HasSuffix(domain, v6Suffix):
idx = indexFirstV6Label(domain[:len(domain)-len(v6Suffix)])
default:
return netip.Prefix{}, &netutil.AddrError{
Err: netutil.ErrNotAReversedSubnet,
Kind: netutil.AddrKindARPA,
Addr: domain,
}
}
return netutil.PrefixFromReversedAddr(domain[idx:])
}
// processRestrictLocal responds with NXDOMAIN to PTR requests for IP addresses
// in locally served network from external clients.
func (s *Server) processRestrictLocal(dctx *dnsContext) (rc resultCode) {
log.Debug("dnsforward: started processing local restriction")
defer log.Debug("dnsforward: finished processing local restriction")
pctx := dctx.proxyCtx
req := pctx.Req
q := req.Question[0]
if q.Qtype != dns.TypePTR {
// No need for restriction.
return resultCodeSuccess
}
subnet, err := extractARPASubnet(q.Name)
if err != nil {
if errors.Is(err, netutil.ErrNotAReversedSubnet) {
log.Debug("dnsforward: request is not for arpa domain")
return resultCodeSuccess
}
log.Debug("dnsforward: parsing reversed addr: %s", err)
return resultCodeError
}
// Restrict an access to local addresses for external clients. We also
// assume that all the DHCP leases we give are locally served or at least
// shouldn't be accessible externally.
subnetAddr := subnet.Addr()
if !s.privateNets.Contains(subnetAddr) {
return resultCodeSuccess
}
log.Debug("dnsforward: addr %s is from locally served network", subnetAddr)
if !dctx.isLocalClient {
log.Debug("dnsforward: %q requests an internal ip", pctx.Addr)
pctx.Res = s.genNXDomain(req)
// Do not even put into query log.
return resultCodeFinish
}
// Do not perform unreversing ever again.
dctx.unreversedReqIP = subnetAddr
// There is no need to filter request from external addresses since this
// code is only executed when the request is for locally served ARPA
// hostname so disable redundant filters.
dctx.setts.ParentalEnabled = false
dctx.setts.SafeBrowsingEnabled = false
dctx.setts.SafeSearchEnabled = false
dctx.setts.ServicesRules = nil
// Nothing to restrict.
return resultCodeSuccess
}
// processDHCPAddrs responds to PTR requests if the target IP is leased by the
// DHCP server.
func (s *Server) processDHCPAddrs(dctx *dnsContext) (rc resultCode) {
@@ -562,23 +380,27 @@ func (s *Server) processDHCPAddrs(dctx *dnsContext) (rc resultCode) {
return resultCodeSuccess
}
ipAddr := dctx.unreversedReqIP
if ipAddr == (netip.Addr{}) {
req := pctx.Req
q := req.Question[0]
pref := pctx.RequestedPrivateRDNS
// TODO(e.burkov): Consider answering authoritatively for SOA and NS
// queries.
if pref == (netip.Prefix{}) || q.Qtype != dns.TypePTR {
return resultCodeSuccess
}
host := s.dhcpServer.HostByIP(ipAddr)
addr := pref.Addr()
host := s.dhcpServer.HostByIP(addr)
if host == "" {
return resultCodeSuccess
}
log.Debug("dnsforward: dhcp client %s is %q", ipAddr, host)
log.Debug("dnsforward: dhcp client %s is %q", addr, host)
req := pctx.Req
resp := s.makeResponse(req)
resp := s.replyCompressed(req)
ptr := &dns.PTR{
Hdr: dns.RR_Header{
Name: req.Question[0].Name,
Name: q.Name,
Rrtype: dns.TypePTR,
// TODO(e.burkov): Use [dhcpsvc.Lease.Expiry]. See
// https://github.com/AdguardTeam/AdGuardHome/issues/3932.
@@ -593,62 +415,20 @@ func (s *Server) processDHCPAddrs(dctx *dnsContext) (rc resultCode) {
return resultCodeSuccess
}
// processLocalPTR responds to PTR requests if the target IP is detected to be
// inside the local network and the query was not answered from DHCP.
func (s *Server) processLocalPTR(dctx *dnsContext) (rc resultCode) {
log.Debug("dnsforward: started processing local ptr")
defer log.Debug("dnsforward: finished processing local ptr")
pctx := dctx.proxyCtx
if pctx.Res != nil {
return resultCodeSuccess
}
ip := dctx.unreversedReqIP
if ip == (netip.Addr{}) {
return resultCodeSuccess
}
s.serverLock.RLock()
defer s.serverLock.RUnlock()
if s.conf.UsePrivateRDNS {
s.recDetector.add(*pctx.Req)
if err := s.localResolvers.Resolve(pctx); err != nil {
log.Debug("dnsforward: resolving private address: %s", err)
// Generate the server failure if the private upstream configuration
// is empty.
//
// This is a crutch, see TODO at [Server.localResolvers].
if errors.Is(err, upstream.ErrNoUpstreams) {
pctx.Res = s.genServerFailure(pctx.Req)
// Do not even put into query log.
return resultCodeFinish
}
dctx.err = err
return resultCodeError
}
}
if pctx.Res == nil {
pctx.Res = s.genNXDomain(pctx.Req)
// Do not even put into query log.
return resultCodeFinish
}
return resultCodeSuccess
}
// Apply filtering logic
func (s *Server) processFilteringBeforeRequest(dctx *dnsContext) (rc resultCode) {
log.Debug("dnsforward: started processing filtering before req")
defer log.Debug("dnsforward: finished processing filtering before req")
if dctx.proxyCtx.RequestedPrivateRDNS != (netip.Prefix{}) {
// There is no need to filter request for locally served ARPA hostname
// so disable redundant filters.
dctx.setts.ParentalEnabled = false
dctx.setts.SafeBrowsingEnabled = false
dctx.setts.SafeSearchEnabled = false
dctx.setts.ServicesRules = nil
}
if dctx.proxyCtx.Res != nil {
// Go on since the response is already set.
return resultCodeSuccess
@@ -695,7 +475,7 @@ func (s *Server) processUpstream(dctx *dnsContext) (rc resultCode) {
// local domain name if there is one.
name := req.Question[0].Name
log.Debug("dnsforward: dhcp client hostname %q was not filtered", name[:len(name)-1])
pctx.Res = s.genNXDomain(req)
pctx.Res = s.NewMsgNXDOMAIN(req)
return resultCodeFinish
}
@@ -712,21 +492,7 @@ func (s *Server) processUpstream(dctx *dnsContext) (rc resultCode) {
return resultCodeError
}
if err := prx.Resolve(pctx); err != nil {
if errors.Is(err, upstream.ErrNoUpstreams) {
// Do not even put into querylog. Currently this happens either
// when the private resolvers enabled and the request is DNS64 PTR,
// or when the client isn't considered local by prx.
//
// TODO(e.burkov): Make proxy detect local client the same way as
// AGH does.
pctx.Res = s.genNXDomain(req)
return resultCodeFinish
}
dctx.err = err
if dctx.err = prx.Resolve(pctx); dctx.err != nil {
return resultCodeError
}
@@ -810,7 +576,7 @@ func (s *Server) setCustomUpstream(pctx *proxy.DNSContext, clientID string) {
}
// Use the ClientID first, since it has a higher priority.
id := stringutil.Coalesce(clientID, pctx.Addr.Addr().String())
id := cmp.Or(clientID, pctx.Addr.Addr().String())
upsConf, err := s.conf.ClientsContainer.UpstreamConfigByID(id, s.bootstrap)
if err != nil {
log.Error("dnsforward: getting custom upstreams for client %s: %s", id, err)
@@ -835,7 +601,8 @@ func (s *Server) processFilteringAfterResponse(dctx *dnsContext) (rc resultCode)
return resultCodeSuccess
case
filtering.Rewritten,
filtering.RewrittenRule:
filtering.RewrittenRule,
filtering.FilteredSafeSearch:
if dctx.origQuestion.Name == "" {
// origQuestion is set in case we get only CNAME without IP from
@@ -845,11 +612,10 @@ func (s *Server) processFilteringAfterResponse(dctx *dnsContext) (rc resultCode)
pctx := dctx.proxyCtx
pctx.Req.Question[0], pctx.Res.Question[0] = dctx.origQuestion, dctx.origQuestion
if len(pctx.Res.Answer) > 0 {
rr := s.genAnswerCNAME(pctx.Req, res.CanonName)
answer := append([]dns.RR{rr}, pctx.Res.Answer...)
pctx.Res.Answer = answer
}
rr := s.genAnswerCNAME(pctx.Req, res.CanonName)
answer := append([]dns.RR{rr}, pctx.Res.Answer...)
pctx.Res.Answer = answer
return resultCodeSuccess
default:

View File

@@ -1,11 +1,11 @@
package dnsforward
import (
"cmp"
"net"
"net/netip"
"testing"
"github.com/AdguardTeam/AdGuardHome/internal/aghalg"
"github.com/AdguardTeam/AdGuardHome/internal/aghtest"
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/dnsproxy/proxy"
@@ -71,8 +71,6 @@ func TestServer_ProcessInitial(t *testing.T) {
}}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
@@ -87,7 +85,7 @@ func TestServer_ProcessInitial(t *testing.T) {
s := createTestServer(t, &filtering.Config{
BlockingMode: filtering.BlockingModeDefault,
}, c, nil)
}, c)
var gotAddr netip.Addr
s.addrProc = &aghtest.AddressProcessor{
@@ -172,8 +170,6 @@ func TestServer_ProcessFilteringAfterResponse(t *testing.T) {
}}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
@@ -188,7 +184,7 @@ func TestServer_ProcessFilteringAfterResponse(t *testing.T) {
s := createTestServer(t, &filtering.Config{
BlockingMode: filtering.BlockingModeDefault,
}, c, nil)
}, c)
resp := newResp(dns.RcodeSuccess, tc.req, tc.respAns)
dctx := &dnsContext{
@@ -248,9 +244,9 @@ func TestServer_ProcessDDRQuery(t *testing.T) {
host string
want []*dns.SVCB
wantRes resultCode
portDoH int
portDoT int
portDoQ int
addrsDoH []*net.TCPAddr
addrsDoT []*net.TCPAddr
addrsDoQ []*net.UDPAddr
qtype uint16
ddrEnabled bool
}{{
@@ -259,14 +255,14 @@ func TestServer_ProcessDDRQuery(t *testing.T) {
host: testQuestionTarget,
qtype: dns.TypeSVCB,
ddrEnabled: true,
portDoH: 8043,
addrsDoH: []*net.TCPAddr{{Port: 8043}},
}, {
name: "pass_qtype",
wantRes: resultCodeFinish,
host: ddrHostFQDN,
qtype: dns.TypeA,
ddrEnabled: true,
portDoH: 8043,
addrsDoH: []*net.TCPAddr{{Port: 8043}},
}, {
name: "pass_disabled_tls",
wantRes: resultCodeFinish,
@@ -279,7 +275,7 @@ func TestServer_ProcessDDRQuery(t *testing.T) {
host: ddrHostFQDN,
qtype: dns.TypeSVCB,
ddrEnabled: false,
portDoH: 8043,
addrsDoH: []*net.TCPAddr{{Port: 8043}},
}, {
name: "dot",
wantRes: resultCodeFinish,
@@ -287,7 +283,7 @@ func TestServer_ProcessDDRQuery(t *testing.T) {
host: ddrHostFQDN,
qtype: dns.TypeSVCB,
ddrEnabled: true,
portDoT: 8043,
addrsDoT: []*net.TCPAddr{{Port: 8043}},
}, {
name: "doh",
wantRes: resultCodeFinish,
@@ -295,7 +291,7 @@ func TestServer_ProcessDDRQuery(t *testing.T) {
host: ddrHostFQDN,
qtype: dns.TypeSVCB,
ddrEnabled: true,
portDoH: 8044,
addrsDoH: []*net.TCPAddr{{Port: 8044}},
}, {
name: "doq",
wantRes: resultCodeFinish,
@@ -303,7 +299,7 @@ func TestServer_ProcessDDRQuery(t *testing.T) {
host: ddrHostFQDN,
qtype: dns.TypeSVCB,
ddrEnabled: true,
portDoQ: 8042,
addrsDoQ: []*net.UDPAddr{{Port: 8042}},
}, {
name: "dot_doh",
wantRes: resultCodeFinish,
@@ -311,13 +307,35 @@ func TestServer_ProcessDDRQuery(t *testing.T) {
host: ddrHostFQDN,
qtype: dns.TypeSVCB,
ddrEnabled: true,
portDoT: 8043,
portDoH: 8044,
addrsDoT: []*net.TCPAddr{{Port: 8043}},
addrsDoH: []*net.TCPAddr{{Port: 8044}},
}}
_, certPem, keyPem := createServerTLSConfig(t)
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
s := prepareTestServer(t, tc.portDoH, tc.portDoT, tc.portDoQ, tc.ddrEnabled)
s := createTestServer(t, &filtering.Config{
BlockingMode: filtering.BlockingModeDefault,
}, ServerConfig{
Config: Config{
HandleDDR: tc.ddrEnabled,
UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
},
TLSConfig: TLSConfig{
ServerName: ddrTestDomainName,
CertificateChainData: certPem,
PrivateKeyData: keyPem,
TLSListenAddrs: tc.addrsDoT,
HTTPSListenAddrs: tc.addrsDoH,
QUICListenAddrs: tc.addrsDoQ,
},
ServePlainDNS: true,
})
// TODO(e.burkov): Generate a certificate actually containing the
// IP addresses.
s.conf.hasIPAddrs = true
req := createTestMessageWithType(tc.host, tc.qtype)
@@ -358,79 +376,6 @@ func createTestDNSFilter(t *testing.T) (f *filtering.DNSFilter) {
return f
}
func prepareTestServer(t *testing.T, portDoH, portDoT, portDoQ int, ddrEnabled bool) (s *Server) {
t.Helper()
s = &Server{
dnsFilter: createTestDNSFilter(t),
dnsProxy: &proxy.Proxy{
Config: proxy.Config{},
},
conf: ServerConfig{
Config: Config{
HandleDDR: ddrEnabled,
},
TLSConfig: TLSConfig{
ServerName: ddrTestDomainName,
},
ServePlainDNS: true,
},
}
if portDoT > 0 {
s.dnsProxy.TLSListenAddr = []*net.TCPAddr{{Port: portDoT}}
s.conf.hasIPAddrs = true
}
if portDoQ > 0 {
s.dnsProxy.QUICListenAddr = []*net.UDPAddr{{Port: portDoQ}}
}
if portDoH > 0 {
s.conf.HTTPSListenAddrs = []*net.TCPAddr{{Port: portDoH}}
}
return s
}
func TestServer_ProcessDetermineLocal(t *testing.T) {
s := &Server{
privateNets: netutil.SubnetSetFunc(netutil.IsLocallyServed),
}
testCases := []struct {
want assert.BoolAssertionFunc
name string
cliAddr netip.AddrPort
}{{
want: assert.True,
name: "local",
cliAddr: netip.MustParseAddrPort("192.168.0.1:1"),
}, {
want: assert.False,
name: "external",
cliAddr: netip.MustParseAddrPort("250.249.0.1:1"),
}, {
want: assert.False,
name: "invalid",
cliAddr: netip.AddrPort{},
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
proxyCtx := &proxy.DNSContext{
Addr: tc.cliAddr,
}
dctx := &dnsContext{
proxyCtx: proxyCtx,
}
s.processDetermineLocal(dctx)
tc.want(t, dctx.isLocalClient)
})
}
}
func TestServer_ProcessDHCPHosts_localRestriction(t *testing.T) {
const (
localDomainSuffix = "lan"
@@ -500,9 +445,9 @@ func TestServer_ProcessDHCPHosts_localRestriction(t *testing.T) {
dctx := &dnsContext{
proxyCtx: &proxy.DNSContext{
Req: req,
Req: req,
IsPrivateClient: tc.isLocalCli,
},
isLocalClient: tc.isLocalCli,
}
res := s.processDHCPHosts(dctx)
@@ -635,9 +580,9 @@ func TestServer_ProcessDHCPHosts(t *testing.T) {
dctx := &dnsContext{
proxyCtx: &proxy.DNSContext{
Req: req,
Req: req,
IsPrivateClient: true,
},
isLocalClient: true,
}
t.Run(tc.name, func(t *testing.T) {
@@ -672,21 +617,33 @@ func TestServer_ProcessDHCPHosts(t *testing.T) {
}
}
func TestServer_ProcessRestrictLocal(t *testing.T) {
// TODO(e.burkov): Rewrite this test to use the whole server instead of just
// testing the [handleDNSRequest] method. See comment on
// "from_external_for_local" test case.
func TestServer_HandleDNSRequest_restrictLocal(t *testing.T) {
intAddr := netip.MustParseAddr("192.168.1.1")
intPTRQuestion, err := netutil.IPToReversedAddr(intAddr.AsSlice())
require.NoError(t, err)
extAddr := netip.MustParseAddr("254.253.252.1")
extPTRQuestion, err := netutil.IPToReversedAddr(extAddr.AsSlice())
require.NoError(t, err)
const (
extPTRQuestion = "251.252.253.254.in-addr.arpa."
extPTRAnswer = "host1.example.net."
intPTRQuestion = "1.1.168.192.in-addr.arpa."
intPTRAnswer = "some.local-client."
extPTRAnswer = "host1.example.net."
intPTRAnswer = "some.local-client."
)
ups := aghtest.NewUpstreamMock(func(req *dns.Msg) (resp *dns.Msg, err error) {
return aghalg.Coalesce(
localUpsHdlr := dns.HandlerFunc(func(w dns.ResponseWriter, req *dns.Msg) {
resp := cmp.Or(
aghtest.MatchedResponse(req, dns.TypePTR, extPTRQuestion, extPTRAnswer),
aghtest.MatchedResponse(req, dns.TypePTR, intPTRQuestion, intPTRAnswer),
new(dns.Msg).SetRcode(req, dns.RcodeNameError),
), nil
(&dns.Msg{}).SetRcode(req, dns.RcodeNameError),
)
require.NoError(testutil.PanicT{}, w.WriteMsg(resp))
})
localUpsAddr := aghtest.StartLocalhostUpstream(t, localUpsHdlr).String()
s := createTestServer(t, &filtering.Config{
BlockingMode: filtering.BlockingModeDefault,
@@ -696,126 +653,176 @@ func TestServer_ProcessRestrictLocal(t *testing.T) {
// TODO(s.chzhen): Add tests where EDNSClientSubnet.Enabled is true.
// Improve Config declaration for tests.
Config: Config{
UpstreamDNS: []string{localUpsAddr},
UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
},
ServePlainDNS: true,
}, ups)
s.conf.UpstreamConfig.Upstreams = []upstream.Upstream{ups}
UsePrivateRDNS: true,
LocalPTRResolvers: []string{localUpsAddr},
ServePlainDNS: true,
})
startDeferStop(t, s)
testCases := []struct {
name string
want string
question net.IP
cliAddr netip.AddrPort
wantLen int
name string
question string
wantErr error
wantAns []dns.RR
isPrivate bool
}{{
name: "from_local_to_external",
want: "host1.example.net.",
question: net.IP{254, 253, 252, 251},
cliAddr: netip.MustParseAddrPort("192.168.10.10:1"),
wantLen: 1,
name: "from_local_for_external",
question: extPTRQuestion,
wantErr: nil,
wantAns: []dns.RR{&dns.PTR{
Hdr: dns.RR_Header{
Name: dns.Fqdn(extPTRQuestion),
Rrtype: dns.TypePTR,
Class: dns.ClassINET,
Ttl: 60,
Rdlength: uint16(len(extPTRAnswer) + 1),
},
Ptr: dns.Fqdn(extPTRAnswer),
}},
isPrivate: true,
}, {
name: "from_external_for_local",
want: "",
question: net.IP{192, 168, 1, 1},
cliAddr: netip.MustParseAddrPort("254.253.252.251:1"),
wantLen: 0,
// In theory this case is not reproducible because [proxy.Proxy] should
// respond to such queries with NXDOMAIN before they reach
// [Server.handleDNSRequest].
name: "from_external_for_local",
question: intPTRQuestion,
wantErr: upstream.ErrNoUpstreams,
wantAns: nil,
isPrivate: false,
}, {
name: "from_local_for_local",
want: "some.local-client.",
question: net.IP{192, 168, 1, 1},
cliAddr: netip.MustParseAddrPort("192.168.1.2:1"),
wantLen: 1,
question: intPTRQuestion,
wantErr: nil,
wantAns: []dns.RR{&dns.PTR{
Hdr: dns.RR_Header{
Name: dns.Fqdn(intPTRQuestion),
Rrtype: dns.TypePTR,
Class: dns.ClassINET,
Ttl: 60,
Rdlength: uint16(len(intPTRAnswer) + 1),
},
Ptr: dns.Fqdn(intPTRAnswer),
}},
isPrivate: true,
}, {
name: "from_external_for_external",
want: "host1.example.net.",
question: net.IP{254, 253, 252, 251},
cliAddr: netip.MustParseAddrPort("254.253.252.255:1"),
wantLen: 1,
question: extPTRQuestion,
wantErr: nil,
wantAns: []dns.RR{&dns.PTR{
Hdr: dns.RR_Header{
Name: dns.Fqdn(extPTRQuestion),
Rrtype: dns.TypePTR,
Class: dns.ClassINET,
Ttl: 60,
Rdlength: uint16(len(extPTRAnswer) + 1),
},
Ptr: dns.Fqdn(extPTRAnswer),
}},
isPrivate: false,
}}
for _, tc := range testCases {
reqAddr, err := dns.ReverseAddr(tc.question.String())
require.NoError(t, err)
req := createTestMessageWithType(reqAddr, dns.TypePTR)
pref, extErr := netutil.ExtractReversedAddr(tc.question)
require.NoError(t, extErr)
req := createTestMessageWithType(dns.Fqdn(tc.question), dns.TypePTR)
pctx := &proxy.DNSContext{
Proto: proxy.ProtoTCP,
Req: req,
Addr: tc.cliAddr,
Req: req,
IsPrivateClient: tc.isPrivate,
}
// TODO(e.burkov): Configure the subnet set properly.
if netutil.IsLocallyServed(pref.Addr()) {
pctx.RequestedPrivateRDNS = pref
}
t.Run(tc.name, func(t *testing.T) {
err = s.handleDNSRequest(nil, pctx)
require.NoError(t, err)
require.NotNil(t, pctx.Res)
require.Len(t, pctx.Res.Answer, tc.wantLen)
err = s.handleDNSRequest(s.dnsProxy, pctx)
require.ErrorIs(t, err, tc.wantErr)
if tc.wantLen > 0 {
assert.Equal(t, tc.want, pctx.Res.Answer[0].(*dns.PTR).Ptr)
}
require.NotNil(t, pctx.Res)
assert.Equal(t, tc.wantAns, pctx.Res.Answer)
})
}
}
func TestServer_ProcessLocalPTR_usingResolvers(t *testing.T) {
func TestServer_ProcessUpstream_localPTR(t *testing.T) {
const locDomain = "some.local."
const reqAddr = "1.1.168.192.in-addr.arpa."
s := createTestServer(
t,
&filtering.Config{
BlockingMode: filtering.BlockingModeDefault,
},
ServerConfig{
UDPListenAddrs: []*net.UDPAddr{{}},
TCPListenAddrs: []*net.TCPAddr{{}},
Config: Config{
UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
},
ServePlainDNS: true,
},
aghtest.NewUpstreamMock(func(req *dns.Msg) (resp *dns.Msg, err error) {
return aghalg.Coalesce(
aghtest.MatchedResponse(req, dns.TypePTR, reqAddr, locDomain),
new(dns.Msg).SetRcode(req, dns.RcodeNameError),
), nil
}),
)
localUpsHdlr := dns.HandlerFunc(func(w dns.ResponseWriter, req *dns.Msg) {
resp := cmp.Or(
aghtest.MatchedResponse(req, dns.TypePTR, reqAddr, locDomain),
(&dns.Msg{}).SetRcode(req, dns.RcodeNameError),
)
var proxyCtx *proxy.DNSContext
var dnsCtx *dnsContext
setup := func(use bool) {
proxyCtx = &proxy.DNSContext{
Addr: testClientAddrPort,
Req: createTestMessageWithType(reqAddr, dns.TypePTR),
require.NoError(testutil.PanicT{}, w.WriteMsg(resp))
})
localUpsAddr := aghtest.StartLocalhostUpstream(t, localUpsHdlr).String()
newPrxCtx := func() (prxCtx *proxy.DNSContext) {
return &proxy.DNSContext{
Addr: testClientAddrPort,
Req: createTestMessageWithType(reqAddr, dns.TypePTR),
IsPrivateClient: true,
RequestedPrivateRDNS: netip.MustParsePrefix("192.168.1.1/32"),
}
dnsCtx = &dnsContext{
proxyCtx: proxyCtx,
unreversedReqIP: netip.MustParseAddr("192.168.1.1"),
}
s.conf.UsePrivateRDNS = use
}
t.Run("enabled", func(t *testing.T) {
setup(true)
s := createTestServer(
t,
&filtering.Config{
BlockingMode: filtering.BlockingModeDefault,
},
ServerConfig{
UDPListenAddrs: []*net.UDPAddr{{}},
TCPListenAddrs: []*net.TCPAddr{{}},
Config: Config{
UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
},
UsePrivateRDNS: true,
LocalPTRResolvers: []string{localUpsAddr},
ServePlainDNS: true,
},
)
pctx := newPrxCtx()
rc := s.processLocalPTR(dnsCtx)
rc := s.processUpstream(&dnsContext{proxyCtx: pctx})
require.Equal(t, resultCodeSuccess, rc)
require.NotEmpty(t, proxyCtx.Res.Answer)
require.NotEmpty(t, pctx.Res.Answer)
ptr := testutil.RequireTypeAssert[*dns.PTR](t, pctx.Res.Answer[0])
assert.Equal(t, locDomain, proxyCtx.Res.Answer[0].(*dns.PTR).Ptr)
assert.Equal(t, locDomain, ptr.Ptr)
})
t.Run("disabled", func(t *testing.T) {
setup(false)
s := createTestServer(
t,
&filtering.Config{
BlockingMode: filtering.BlockingModeDefault,
},
ServerConfig{
UDPListenAddrs: []*net.UDPAddr{{}},
TCPListenAddrs: []*net.TCPAddr{{}},
Config: Config{
UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
},
UsePrivateRDNS: false,
LocalPTRResolvers: []string{localUpsAddr},
ServePlainDNS: true,
},
)
pctx := newPrxCtx()
rc := s.processLocalPTR(dnsCtx)
require.Equal(t, resultCodeFinish, rc)
require.Empty(t, proxyCtx.Res.Answer)
rc := s.processUpstream(&dnsContext{proxyCtx: pctx})
require.Equal(t, resultCodeError, rc)
require.Empty(t, pctx.Res.Answer)
})
}
@@ -833,129 +840,3 @@ func TestIPStringFromAddr(t *testing.T) {
assert.Empty(t, ipStringFromAddr(nil))
})
}
// TODO(e.burkov): Add fuzzing when moving to golibs.
func TestExtractARPASubnet(t *testing.T) {
const (
v4Suf = `in-addr.arpa.`
v4Part = `2.1.` + v4Suf
v4Whole = `4.3.` + v4Part
v6Suf = `ip6.arpa.`
v6Part = `4.3.2.1.0.0.0.0.0.0.0.0.0.0.0.0.` + v6Suf
v6Whole = `f.e.d.c.0.0.0.0.0.0.0.0.0.0.0.0.` + v6Part
)
v4Pref := netip.MustParsePrefix("1.2.3.4/32")
v4PrefPart := netip.MustParsePrefix("1.2.0.0/16")
v6Pref := netip.MustParsePrefix("::1234:0:0:0:cdef/128")
v6PrefPart := netip.MustParsePrefix("0:0:0:1234::/64")
testCases := []struct {
want netip.Prefix
name string
domain string
wantErr string
}{{
want: netip.Prefix{},
name: "not_an_arpa",
domain: "some.domain.name.",
wantErr: `bad arpa domain name "some.domain.name.": ` +
`not a reversed ip network`,
}, {
want: netip.Prefix{},
name: "bad_domain_name",
domain: "abc.123.",
wantErr: `bad domain name "abc.123": ` +
`bad top-level domain name label "123": all octets are numeric`,
}, {
want: v4Pref,
name: "whole_v4",
domain: v4Whole,
wantErr: "",
}, {
want: v4PrefPart,
name: "partial_v4",
domain: v4Part,
wantErr: "",
}, {
want: v4Pref,
name: "whole_v4_within_domain",
domain: "a." + v4Whole,
wantErr: "",
}, {
want: v4Pref,
name: "whole_v4_additional_label",
domain: "5." + v4Whole,
wantErr: "",
}, {
want: v4PrefPart,
name: "partial_v4_within_domain",
domain: "a." + v4Part,
wantErr: "",
}, {
want: v4PrefPart,
name: "overflow_v4",
domain: "256." + v4Part,
wantErr: "",
}, {
want: v4PrefPart,
name: "overflow_v4_within_domain",
domain: "a.256." + v4Part,
wantErr: "",
}, {
want: netip.Prefix{},
name: "empty_v4",
domain: v4Suf,
wantErr: `bad arpa domain name "in-addr.arpa": ` +
`not a reversed ip network`,
}, {
want: netip.Prefix{},
name: "empty_v4_within_domain",
domain: "a." + v4Suf,
wantErr: `bad arpa domain name "in-addr.arpa": ` +
`not a reversed ip network`,
}, {
want: v6Pref,
name: "whole_v6",
domain: v6Whole,
wantErr: "",
}, {
want: v6PrefPart,
name: "partial_v6",
domain: v6Part,
}, {
want: v6Pref,
name: "whole_v6_within_domain",
domain: "g." + v6Whole,
wantErr: "",
}, {
want: v6Pref,
name: "whole_v6_additional_label",
domain: "1." + v6Whole,
wantErr: "",
}, {
want: v6PrefPart,
name: "partial_v6_within_domain",
domain: "label." + v6Part,
wantErr: "",
}, {
want: netip.Prefix{},
name: "empty_v6",
domain: v6Suf,
wantErr: `bad arpa domain name "ip6.arpa": not a reversed ip network`,
}, {
want: netip.Prefix{},
name: "empty_v6_within_domain",
domain: "g." + v6Suf,
wantErr: `bad arpa domain name "ip6.arpa": not a reversed ip network`,
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
subnet, err := extractARPASubnet(tc.domain)
testutil.AssertErrorMsg(t, tc.wantErr, err)
assert.Equal(t, tc.want, subnet)
})
}
}

View File

@@ -1,115 +0,0 @@
package dnsforward
import (
"bytes"
"encoding/binary"
"time"
"github.com/AdguardTeam/golibs/cache"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/netutil"
"github.com/miekg/dns"
)
// uint* sizes in bytes to improve readability.
//
// TODO(e.burkov): Remove when there will be a more regardful way to define
// those. See https://github.com/golang/go/issues/29982.
const (
uint16sz = 2
uint64sz = 8
)
// recursionDetector detects recursion in DNS forwarding.
type recursionDetector struct {
recentRequests cache.Cache
ttl time.Duration
}
// check checks if the passed req was already sent by the server.
func (rd *recursionDetector) check(msg dns.Msg) (ok bool) {
if len(msg.Question) == 0 {
return false
}
key := msgToSignature(msg)
expireData := rd.recentRequests.Get(key)
if expireData == nil {
return false
}
expire := time.Unix(0, int64(binary.BigEndian.Uint64(expireData)))
return time.Now().Before(expire)
}
// add caches the msg if it has anything in the questions section.
func (rd *recursionDetector) add(msg dns.Msg) {
now := time.Now()
if len(msg.Question) == 0 {
return
}
key := msgToSignature(msg)
expire64 := uint64(now.Add(rd.ttl).UnixNano())
expire := make([]byte, uint64sz)
binary.BigEndian.PutUint64(expire, expire64)
rd.recentRequests.Set(key, expire)
}
// clear clears the recent requests cache.
func (rd *recursionDetector) clear() {
rd.recentRequests.Clear()
}
// newRecursionDetector returns the initialized *recursionDetector.
func newRecursionDetector(ttl time.Duration, suspectsNum uint) (rd *recursionDetector) {
return &recursionDetector{
recentRequests: cache.New(cache.Config{
EnableLRU: true,
MaxCount: suspectsNum,
}),
ttl: ttl,
}
}
// msgToSignature converts msg into it's signature represented in bytes.
func msgToSignature(msg dns.Msg) (sig []byte) {
sig = make([]byte, uint16sz*2+netutil.MaxDomainNameLen)
// The binary.BigEndian byte order is used everywhere except when the real
// machine's endianness is needed.
byteOrder := binary.BigEndian
byteOrder.PutUint16(sig[0:], msg.Id)
q := msg.Question[0]
byteOrder.PutUint16(sig[uint16sz:], q.Qtype)
copy(sig[2*uint16sz:], []byte(q.Name))
return sig
}
// msgToSignatureSlow converts msg into it's signature represented in bytes in
// the less efficient way.
//
// See BenchmarkMsgToSignature.
func msgToSignatureSlow(msg dns.Msg) (sig []byte) {
type msgSignature struct {
name [netutil.MaxDomainNameLen]byte
id uint16
qtype uint16
}
b := bytes.NewBuffer(sig)
q := msg.Question[0]
signature := msgSignature{
id: msg.Id,
qtype: q.Qtype,
}
copy(signature.name[:], q.Name)
if err := binary.Write(b, binary.BigEndian, signature); err != nil {
log.Debug("writing message signature: %s", err)
}
return b.Bytes()
}

View File

@@ -1,148 +0,0 @@
package dnsforward
import (
"encoding/binary"
"testing"
"time"
"github.com/miekg/dns"
"github.com/stretchr/testify/assert"
)
func TestRecursionDetector_Check(t *testing.T) {
rd := newRecursionDetector(0, 2)
const (
recID = 1234
recTTL = time.Hour * 100
)
const nonRecID = recID * 2
sampleQuestion := dns.Question{
Name: "some.domain",
Qtype: dns.TypeAAAA,
}
sampleMsg := dns.Msg{
MsgHdr: dns.MsgHdr{
Id: recID,
},
Question: []dns.Question{sampleQuestion},
}
// Manually add the message with big ttl.
key := msgToSignature(sampleMsg)
expire := make([]byte, uint64sz)
binary.BigEndian.PutUint64(expire, uint64(time.Now().Add(recTTL).UnixNano()))
rd.recentRequests.Set(key, expire)
// Add an expired message.
sampleMsg.Id = nonRecID
rd.add(sampleMsg)
testCases := []struct {
name string
questions []dns.Question
id uint16
want bool
}{{
name: "recurrent",
questions: []dns.Question{sampleQuestion},
id: recID,
want: true,
}, {
name: "not_suspected",
questions: []dns.Question{sampleQuestion},
id: recID + 1,
want: false,
}, {
name: "expired",
questions: []dns.Question{sampleQuestion},
id: nonRecID,
want: false,
}, {
name: "empty",
questions: []dns.Question{},
id: nonRecID,
want: false,
}}
for _, tc := range testCases {
sampleMsg.Id = tc.id
sampleMsg.Question = tc.questions
t.Run(tc.name, func(t *testing.T) {
detected := rd.check(sampleMsg)
assert.Equal(t, tc.want, detected)
})
}
}
func TestRecursionDetector_Suspect(t *testing.T) {
rd := newRecursionDetector(0, 1)
testCases := []struct {
name string
msg dns.Msg
want int
}{{
name: "simple",
msg: dns.Msg{
MsgHdr: dns.MsgHdr{
Id: 1234,
},
Question: []dns.Question{{
Name: "some.domain",
Qtype: dns.TypeA,
}},
},
want: 1,
}, {
name: "unencumbered",
msg: dns.Msg{},
want: 0,
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
t.Cleanup(rd.clear)
rd.add(tc.msg)
assert.Equal(t, tc.want, rd.recentRequests.Stats().Count)
})
}
}
var sink []byte
func BenchmarkMsgToSignature(b *testing.B) {
const name = "some.not.very.long.host.name"
msg := dns.Msg{
MsgHdr: dns.MsgHdr{
Id: 1234,
},
Question: []dns.Question{{
Name: name,
Qtype: dns.TypeAAAA,
}},
}
b.Run("efficient", func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
sink = msgToSignature(msg)
}
assert.NotEmpty(b, sink)
})
b.Run("inefficient", func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
sink = msgToSignatureSlow(msg)
}
assert.NotEmpty(b, sink)
})
}

View File

@@ -29,7 +29,13 @@ func (s *Server) processQueryLogsAndStats(dctx *dnsContext) (rc resultCode) {
log.Debug("dnsforward: client ip for stats and querylog: %s", ipStr)
ids := []string{ipStr, dctx.clientID}
ids := []string{ipStr}
if dctx.clientID != "" {
// Use the ClientID first because it has a higher priority. Filters
// have the same priority, see applyAdditionalFiltering.
ids = []string{dctx.clientID, ipStr}
}
qt, cl := q.Qtype, q.Qclass
// Synchronize access to s.queryLog and s.stats so they won't be suddenly
@@ -46,7 +52,7 @@ func (s *Server) processQueryLogsAndStats(dctx *dnsContext) (rc resultCode) {
dns.Class(cl),
dns.Type(qt),
host,
ip,
ipStr,
)
}
@@ -58,7 +64,7 @@ func (s *Server) processQueryLogsAndStats(dctx *dnsContext) (rc resultCode) {
dns.Class(cl),
dns.Type(qt),
host,
ip,
ipStr,
)
}
@@ -124,7 +130,7 @@ func (s *Server) logQuery(dctx *dnsContext, ip net.IP, processingTime time.Durat
s.queryLog.Add(p)
}
// updatesStats writes the request into statistics.
// updateStats writes the request data into statistics.
func (s *Server) updateStats(dctx *dnsContext, clientIP string, processingTime time.Duration) {
pctx := dctx.proxyCtx

View File

@@ -21,7 +21,7 @@ func TestGenAnswerHTTPS_andSVCB(t *testing.T) {
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
},
ServePlainDNS: true,
}, nil)
})
req := &dns.Msg{
Question: []dns.Question{{

View File

@@ -2,90 +2,77 @@ package dnsforward
import (
"fmt"
"net/netip"
"os"
"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/log"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/stringutil"
"golang.org/x/exp/maps"
)
// loadUpstreams parses upstream DNS servers from the configured file or from
// the configuration itself.
func (s *Server) loadUpstreams() (upstreams []string, err error) {
if s.conf.UpstreamDNSFileName == "" {
return stringutil.FilterOut(s.conf.UpstreamDNS, IsCommentOrEmpty), nil
// newBootstrap returns a bootstrap resolver based on the configuration of s.
// boots are the upstream resolvers that should be closed after use. r is the
// actual bootstrap resolver, which may include the system hosts.
//
// TODO(e.burkov): This function currently returns a resolver and a slice of
// the upstream resolvers, which are essentially the same. boots are returned
// for being able to close them afterwards, but it introduces an implicit
// contract that r could only be used before that. Anyway, this code should
// improve when the [proxy.UpstreamConfig] will become an [upstream.Resolver]
// and be used here.
func newBootstrap(
addrs []string,
etcHosts upstream.Resolver,
opts *upstream.Options,
) (r upstream.Resolver, boots []*upstream.UpstreamResolver, err error) {
if len(addrs) == 0 {
addrs = defaultBootstrap
}
var data []byte
data, err = os.ReadFile(s.conf.UpstreamDNSFileName)
boots, err = aghnet.ParseBootstraps(addrs, opts)
if err != nil {
return nil, fmt.Errorf("reading upstream from file: %w", err)
// Don't wrap the error, since it's informative enough as is.
return nil, nil, err
}
upstreams = stringutil.SplitTrimmed(string(data), "\n")
var parallel upstream.ParallelResolver
for _, b := range boots {
parallel = append(parallel, upstream.NewCachingResolver(b))
}
log.Debug("dnsforward: got %d upstreams in %q", len(upstreams), s.conf.UpstreamDNSFileName)
if etcHosts != nil {
r = upstream.ConsequentResolver{etcHosts, parallel}
} else {
r = parallel
}
return stringutil.FilterOut(upstreams, IsCommentOrEmpty), nil
return r, boots, nil
}
// prepareUpstreamSettings sets upstream DNS server settings.
func (s *Server) prepareUpstreamSettings(boot upstream.Resolver) (err error) {
// Load upstreams either from the file, or from the settings
var upstreams []string
upstreams, err = s.loadUpstreams()
if err != nil {
return fmt.Errorf("loading upstreams: %w", err)
}
s.conf.UpstreamConfig, err = s.prepareUpstreamConfig(upstreams, defaultDNS, &upstream.Options{
Bootstrap: boot,
Timeout: s.conf.UpstreamTimeout,
HTTPVersions: UpstreamHTTPVersions(s.conf.UseHTTP3Upstreams),
PreferIPv6: s.conf.BootstrapPreferIPv6,
// Use a customized set of RootCAs, because Go's default mechanism of
// loading TLS roots does not always work properly on some routers so we're
// loading roots manually and pass it here.
//
// See [aghtls.SystemRootCAs].
//
// TODO(a.garipov): Investigate if that's true.
RootCAs: s.conf.TLSv12Roots,
CipherSuites: s.conf.TLSCiphers,
})
if err != nil {
return fmt.Errorf("preparing upstream config: %w", err)
}
return nil
}
// prepareUpstreamConfig returns the upstream configuration based on upstreams
// and configuration of s.
func (s *Server) prepareUpstreamConfig(
// newUpstreamConfig returns the upstream configuration based on upstreams. If
// upstreams slice specifies no default upstreams, defaultUpstreams are used to
// create upstreams with no domain specifications. opts are used when creating
// upstream configuration.
func newUpstreamConfig(
upstreams []string,
defaultUpstreams []string,
opts *upstream.Options,
) (uc *proxy.UpstreamConfig, err error) {
uc, err = proxy.ParseUpstreamsConfig(upstreams, opts)
if err != nil {
return nil, fmt.Errorf("parsing upstream config: %w", err)
return uc, fmt.Errorf("parsing upstreams: %w", err)
}
if len(uc.Upstreams) == 0 && defaultUpstreams != nil {
if len(uc.Upstreams) == 0 && len(defaultUpstreams) > 0 {
log.Info("dnsforward: warning: no default upstreams specified, using %v", defaultUpstreams)
var defaultUpstreamConfig *proxy.UpstreamConfig
defaultUpstreamConfig, err = proxy.ParseUpstreamsConfig(defaultUpstreams, opts)
if err != nil {
return nil, fmt.Errorf("parsing default upstreams: %w", err)
return uc, fmt.Errorf("parsing default upstreams: %w", err)
}
uc.Upstreams = defaultUpstreamConfig.Upstreams
@@ -94,6 +81,52 @@ func (s *Server) prepareUpstreamConfig(
return uc, nil
}
// newPrivateConfig creates an upstream configuration for resolving PTR records
// for local addresses. The configuration is built either from the provided
// addresses or from the system resolvers. unwanted filters the resulting
// upstream configuration.
func newPrivateConfig(
addrs []string,
unwanted addrPortSet,
sysResolvers SystemResolvers,
privateNets netutil.SubnetSet,
opts *upstream.Options,
) (uc *proxy.UpstreamConfig, err error) {
confNeedsFiltering := len(addrs) > 0
if confNeedsFiltering {
addrs = stringutil.FilterOut(addrs, IsCommentOrEmpty)
} else {
sysResolvers := slices.DeleteFunc(slices.Clone(sysResolvers.Addrs()), unwanted.Has)
addrs = make([]string, 0, len(sysResolvers))
for _, r := range sysResolvers {
addrs = append(addrs, r.String())
}
}
log.Debug("dnsforward: private-use upstreams: %v", addrs)
uc, err = proxy.ParseUpstreamsConfig(addrs, opts)
if err != nil {
return uc, fmt.Errorf("preparing private upstreams: %w", err)
}
if confNeedsFiltering {
err = filterOutAddrs(uc, unwanted)
if err != nil {
return uc, fmt.Errorf("filtering private upstreams: %w", err)
}
}
// Prevalidate the config to catch the exact error before creating proxy.
// See TODO on [PrivateRDNSError].
err = proxy.ValidatePrivateConfig(uc, privateNets)
if err != nil {
return uc, &PrivateRDNSError{err: err}
}
return uc, nil
}
// UpstreamHTTPVersions returns the HTTP versions for upstream configuration
// depending on configuration.
func UpstreamHTTPVersions(http3 bool) (v []upstream.HTTPVersion) {
@@ -130,85 +163,9 @@ func setProxyUpstreamMode(
return nil
}
// createBootstrap returns a bootstrap resolver based on the configuration of s.
// boots are the upstream resolvers that should be closed after use. r is the
// actual bootstrap resolver, which may include the system hosts.
//
// TODO(e.burkov): This function currently returns a resolver and a slice of
// the upstream resolvers, which are essentially the same. boots are returned
// for being able to close them afterwards, but it introduces an implicit
// contract that r could only be used before that. Anyway, this code should
// improve when the [proxy.UpstreamConfig] will become an [upstream.Resolver]
// and be used here.
func (s *Server) createBootstrap(
addrs []string,
opts *upstream.Options,
) (r upstream.Resolver, boots []*upstream.UpstreamResolver, err error) {
if len(addrs) == 0 {
addrs = defaultBootstrap
}
boots, err = aghnet.ParseBootstraps(addrs, opts)
if err != nil {
// Don't wrap the error, since it's informative enough as is.
return nil, nil, err
}
var parallel upstream.ParallelResolver
for _, b := range boots {
parallel = append(parallel, upstream.NewCachingResolver(b))
}
if s.etcHosts != nil {
r = upstream.ConsequentResolver{s.etcHosts, parallel}
} else {
r = parallel
}
return r, boots, nil
}
// IsCommentOrEmpty returns true if s starts with a "#" character or is empty.
// This function is useful for filtering out non-upstream lines from upstream
// configs.
func IsCommentOrEmpty(s string) (ok bool) {
return len(s) == 0 || s[0] == '#'
}
// ValidateUpstreamsPrivate validates each upstream and returns an error if any
// upstream is invalid or if there are no default upstreams specified. It also
// checks each domain of domain-specific upstreams for being ARPA pointing to
// a locally-served network. privateNets must not be nil.
func ValidateUpstreamsPrivate(upstreams []string, privateNets netutil.SubnetSet) (err error) {
conf, err := proxy.ParseUpstreamsConfig(upstreams, &upstream.Options{})
if err != nil {
return fmt.Errorf("creating config: %w", err)
}
if conf == nil {
return nil
}
keys := maps.Keys(conf.DomainReservedUpstreams)
slices.Sort(keys)
var errs []error
for _, domain := range keys {
var subnet netip.Prefix
subnet, err = extractARPASubnet(domain)
if err != nil {
errs = append(errs, err)
continue
}
if !privateNets.Contains(subnet.Addr()) {
errs = append(
errs,
fmt.Errorf("arpa domain %q should point to a locally-served network", domain),
)
}
}
return errors.Annotate(errors.Join(errs...), "checking domain-specific upstreams: %w")
}

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