Compare commits

..

232 Commits

Author SHA1 Message Date
Ainar Garipov
ce9bb588ed all: sync with master 2024-03-19 16:20:32 +03:00
Ainar Garipov
55fb914537 all: sync rc fix with master 2024-03-13 16:25:51 +03:00
Ainar Garipov
6f7bfd6c9c all: sync with master 2024-03-12 18:15:58 +03:00
Ainar Garipov
fbc0d981ba all: partially sync with master; upd chlog 2024-03-06 18:33:53 +03:00
Ainar Garipov
48d1c673a9 all: fix chlog 2024-02-06 15:21:25 +03:00
Ainar Garipov
889a0eb8b3 all: upd chlog, i18n; fix os resolv 2024-02-01 15:20:53 +03:00
Ainar Garipov
b01c10b73e all: sync with master 2024-01-30 18:44:31 +03:00
Eugene Burkov
f6ad64bf69 all: fix chlog link 2023-12-11 15:36:34 +03:00
Eugene Burkov
a5e8443735 all: fix chlog link 2023-12-11 15:26:48 +03:00
Eugene Burkov
2860929a47 all: sync with master; upd chlog 2023-12-11 14:26:01 +03:00
Ainar Garipov
ecdac56616 all: fix chlog link 2023-12-07 17:44:22 +03:00
Ainar Garipov
25918e56fa Pull request 2100: v0.107.42-rc
Squashed commit of the following:

commit 284190f748345c7556e60b67f051ec5f6f080948
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Dec 6 19:36:00 2023 +0300

    all: sync with master; upd chlog
2023-12-07 17:23:00 +03:00
Ainar Garipov
df91f016f2 all: fix chlog 2023-11-13 18:34:42 +03:00
Ainar Garipov
f7d259f653 all: fix chlog 2023-11-13 18:00:38 +03:00
Ainar Garipov
82ab4328d4 all: sync with master; upd chlog 2023-11-13 17:39:48 +03:00
Ainar Garipov
b21e19a223 all: sync with master; upd chlog 2023-10-18 17:04:44 +03:00
Ainar Garipov
c6aed4eb57 cherry-pick: 6180 revert 2023-10-11 18:35:46 +03:00
Ainar Garipov
760d466b38 all: sync with master; upd chlog 2023-10-11 17:31:41 +03:00
Ainar Garipov
258eecc55b all: sync with master; upd chlog 2023-09-11 17:51:50 +03:00
Ainar Garipov
7b93f5d7cf all: sync with master; upd chlog 2023-09-07 17:13:48 +03:00
Ainar Garipov
3be7676970 all: sync with master; upd chlog 2023-08-02 16:26:34 +03:00
Ainar Garipov
48ee2f8a42 all: sync with master; upd chlog 2023-07-26 13:18:44 +03:00
Ainar Garipov
ec83d0eb86 all: sync with master; upd chlog 2023-07-12 15:13:31 +03:00
Ainar Garipov
19347d263a cherry-pick: 5959-fix-error-days
Updates #5959.

* commit '4b9264531be50e81fe610050a12827b71bc3a9cd':
  clients: use constant a day in milliseconds
  clients: fix lint
  fix error days
2023-07-03 14:38:16 +03:00
Ainar Garipov
b22b16d98c all: sync with master; upd chlog 2023-07-03 14:10:40 +03:00
Ainar Garipov
cadb765b7d all: sync with master; upd chlog 2023-06-13 17:04:47 +03:00
Ainar Garipov
1116da8b83 all: sync with master; upd chlog 2023-06-08 19:48:01 +03:00
Ainar Garipov
c65700923a all: sync with master; upd chlog 2023-06-07 20:04:01 +03:00
Ainar Garipov
7030c7c24c all: imp chlog 2023-04-18 16:56:12 +03:00
Ainar Garipov
09718a2170 all: sync with master; upd chlog 2023-04-18 16:07:11 +03:00
Ainar Garipov
77cda2c2c5 all: imp chlog 2023-04-12 16:18:02 +03:00
Ainar Garipov
d9c57cdd9a all: sync with master; upd chlog 2023-04-12 14:48:42 +03:00
Ainar Garipov
0dad53b5f7 all: fix chlog 2023-04-05 16:38:18 +03:00
Ainar Garipov
9a7315dbea all: upd go, tools, ui; fix panics 2023-04-05 16:35:27 +03:00
Ainar Garipov
a21558f418 all: sync with master; upd chlog 2023-03-09 15:39:35 +03:00
Ainar Garipov
4f928be393 bamboo-specs: do not require make where not needed 2023-02-21 15:12:18 +03:00
Ainar Garipov
f543b47261 dnsforward: fix panic; take Host into account 2023-02-21 14:55:10 +03:00
Ainar Garipov
66b831072c all: sync with master; upd chlog 2023-02-15 16:53:29 +03:00
Ainar Garipov
80eb339896 all: sync with master; upd chlog 2023-02-01 15:41:34 +03:00
Ainar Garipov
c69639c013 all: imp chlog 2023-01-19 15:29:10 +03:00
Ainar Garipov
5f6fbe8e08 all: sync with master; upd chlog 2023-01-19 15:04:46 +03:00
Ainar Garipov
b40bbf0260 all: upd chlog 2023-01-19 15:00:14 +03:00
Ainar Garipov
a11c8e91ab all: sync with master 2022-12-15 17:50:08 +03:00
Ainar Garipov
618d0e596c all: fix chlog 2022-12-07 16:49:19 +03:00
Ainar Garipov
fde9ea5cb1 all: sync with master 2022-12-07 16:46:59 +03:00
Ainar Garipov
03d9803238 all: upd chlog 2022-11-23 17:00:27 +03:00
Ainar Garipov
bd64b8b014 all: sync with master 2022-11-23 16:52:05 +03:00
Ainar Garipov
67fe064fcf all: sync with master 2022-11-08 17:53:30 +03:00
Ainar Garipov
471668d19a all: fix chlog 2022-11-02 18:29:57 +03:00
Ainar Garipov
42762dfe54 all: upd chlog 2022-11-02 16:25:08 +03:00
Ainar Garipov
c9314610d4 all: sync with master 2022-11-02 16:18:02 +03:00
Ainar Garipov
16755c37d8 all: upd go 2022-10-07 15:57:26 +03:00
Ainar Garipov
73fcbd6ea2 all: sync with master 2022-10-03 18:52:20 +03:00
Ainar Garipov
30244f361f all: sync with master 2022-09-29 19:10:03 +03:00
Ainar Garipov
083991fb21 home: sync with master 2022-09-29 18:54:54 +03:00
Ainar Garipov
e3200d5046 all: upd chlog 2022-09-29 17:43:04 +03:00
Ainar Garipov
21f6ed36fe all: sync with master 2022-09-29 17:36:01 +03:00
Ainar Garipov
77d04d44eb all: sync with master 2022-09-14 16:36:29 +03:00
Ainar Garipov
b34d119255 all: imp chlog 2022-09-07 18:38:03 +03:00
Ainar Garipov
63bd71a10c all: imp chlog 2022-09-07 18:07:52 +03:00
Ainar Garipov
faf2b32389 all: sync with master 2022-09-07 18:03:18 +03:00
Ainar Garipov
d23da1b757 all: sync with master 2022-08-19 15:45:54 +03:00
Ainar Garipov
beb8e36eee cherry-pick: 4557-asuswrt-readme
Updates #4557.

* commit 'e3624ec5880361b8afccd0ddac9dc31fd7ce4a07':
  all: fix abbreviation
  Update README.md
2022-08-19 15:21:21 +03:00
Ainar Garipov
fe70161c01 cherry-pick: upd-dnsproxy
Merge in DNS/adguard-home from upd-dnsproxy to master

Squashed commit of the following:

commit 3c5b683e96191b9cf0abf35229c3c665370d782e
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Aug 18 18:04:13 2022 +0300

    all: upd dnsproxy
2022-08-19 15:20:59 +03:00
Ainar Garipov
39fa4b1f8e cherry-pick: 4846-migration-fix
Updates #4846.

Squashed commit of the following:

commit 22e2e89e5390c7b1486fb69064c55da40fc5c7e7
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Aug 18 16:25:07 2022 +0300

    home: fix yaml object type
2022-08-19 15:19:11 +03:00
Ainar Garipov
c7a8883201 cherry-pick: 4795-bilibili
Updates #4795.

* commit 'e6ebb8efef4430c48b06469ba566349bba3d9856':
  filtering: fmt
  filtering: add Bilibili and Weibo domains
  filtering: add Bilibili service
2022-08-19 15:18:14 +03:00
Ainar Garipov
3fd467413c cherry-pick: 4446-readme-fix
Updates #4446.

* commit 'ea5d165a703dd37ef40254f3f775e049b6cebf93':
  all: imp readme
  Enable code block syntax hightlight in README.md
2022-08-19 15:17:57 +03:00
Ainar Garipov
9728dd856f cherry-pick: 4387-fix-openapi-schema
Updates #4387.

* commit 'f54a2dc1da5dfd578f156cf1e0f53f32516eb844':
  home: imp filtering handling
  correct openapi schema
2022-08-19 15:17:43 +03:00
Ainar Garipov
ecadf78d60 all: upd chlog 2022-08-19 15:02:37 +03:00
Ainar Garipov
eba4612d72 all: fix chlog 2022-08-17 18:55:20 +03:00
Ainar Garipov
9200163f85 all: sync with master 2022-08-17 18:23:30 +03:00
Ainar Garipov
3c17853344 cherry-pick: 4844-snap-core22
Closes #4843.
Updates #4844.

* commit '385a873b0f006f26832e73744845fdbc2864aad0':
  all: chlog
  Update Snap to Ubuntu Core 22 #4843
2022-08-17 18:16:57 +03:00
Eugene Burkov
993a3fc42c cherry-pick: 4358 stats races
Merge in DNS/adguard-home from 4358-stats-races to master

Updates #4358

Squashed commit of the following:

commit 162d17b04d95adad21fb9b3c5a6fb64df2e037ec
Merge: 17732cfa d4c3a43b
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Aug 17 14:04:20 2022 +0300

    Merge branch 'master' into 4358-stats-races

commit 17732cfa0f3b2589bf2c252697eee1d6b358a66c
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Aug 17 13:53:42 2022 +0300

    stats: imp docs, locking

commit 4ee090869af0fa2b777c12027c3b77d5acd6e4de
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Aug 16 20:26:19 2022 +0300

    stats: revert const

commit a7681a1b882cef04511fcd5d569f5abe2f955239
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Aug 16 20:23:00 2022 +0300

    stats: imp concurrency

commit a6c6c1a0572e4201cd24644fd3f86f51fc27f633
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Aug 16 19:51:30 2022 +0300

    stats: imp code, tests, docs

commit 954196b49f5ad91d91f445ff656e63c318e4124c
Merge: 281e00da 6e63757f
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Aug 16 13:07:32 2022 +0300

    Merge branch 'master' into 4358-stats-races

commit 281e00daf781d045269584ce0158eed1d77918df
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Aug 12 16:22:18 2022 +0300

    stats: imp closing

commit ed036d9aa7e25498869edfb866b6e923538970eb
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Aug 12 16:11:12 2022 +0300

    stats: imp tests more

commit f848a12487ecd2afc8416e800510090cc1be7330
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Aug 12 13:54:19 2022 +0300

    stats: imp tests, code

commit 60e11f042d51ec68850143129e61c701c5e4f3a4
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Aug 11 16:36:07 2022 +0300

    stats: fix test

commit 6d97f1db093b5ce0d37984ff96a9ef6f4e02dba1
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Aug 11 14:53:21 2022 +0300

    stats: imp code, docs

commit 20c70c2847b0de6c7f9271a8d9a831175ed0c499
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Aug 10 20:53:36 2022 +0300

    stats: imp shared memory safety

commit 8b3945670a190bab070171e6b4976edab1e3e2a2
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Aug 10 17:22:55 2022 +0300

    stats: imp code
2022-08-17 18:15:41 +03:00
Ainar Garipov
7bb9b2416b cherry-pick: upd-specs
Merge in DNS/adguard-home from upd-specs to master

Squashed commit of the following:

commit d7ac1dc1ef305098ff741d557c13db8a60ffe1f9
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Mon Aug 15 19:16:51 2022 +0300

    bamboo-specs: allow larger keys
2022-08-17 18:15:16 +03:00
Eugene Burkov
2de321ce24 cherry-pick: Fix frontend CI build
Merge in DNS/adguard-home from fix-bamboo-specs to master

Squashed commit of the following:

commit e59b75ab9528bbe8fbf5e15054d848abffbae312
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Aug 15 18:52:10 2022 +0300

    all: fix ci frontend build
2022-08-17 18:14:59 +03:00
Eugene Burkov
30b2b85ff1 cherry-pick: Separate front- and back- end builds
Merge in DNS/adguard-home from imp-bamboo-specs to master

Squashed commit of the following:

commit 3415b650e48aefef3ad16030be3d797de4015403
Merge: e37c0a2b f58265ec
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Aug 15 18:42:42 2022 +0300

    Merge branch 'master' into imp-bamboo-specs

commit e37c0a2bb52fab98e133332e8f54d500d0f96b06
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Mon Aug 15 18:30:33 2022 +0300

    scripts: replace find with loop

commit 826a02f6a11000cce4b3205229d6bbb050c8dd73
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Aug 15 18:00:41 2022 +0300

    all: ...again

commit 54aebf5d4aeba35e3dc320436236759f4d1ccdad
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Aug 15 17:59:24 2022 +0300

    all: fix spec yaml

commit 87b92b30504f2427c40303265354afba4855e0bb
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Aug 15 17:48:19 2022 +0300

    all: separate front- and back-end builds
2022-08-17 18:14:44 +03:00
Ainar Garipov
6ea4788f56 cherry-pick: 4836-revert-dhcp-upd
Updates #4836.

Squashed commit of the following:

commit 6fe1721d44be1c23e524d477e28b5f7cc5dd2dc6
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Mon Aug 15 17:48:41 2022 +0300

    dhcpd: reverd mod upd
2022-08-17 18:13:27 +03:00
Ainar Garipov
3c52a021b9 cherry-pick: add-ar-i18n
Merge in DNS/adguard-home from add-ar-i18n to master

Squashed commit of the following:

commit 6ef7c70bceb6f6ebabd81011154022a75fc91bd3
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Aug 10 20:55:39 2022 +0300

    client: add ar locale
2022-08-17 18:12:15 +03:00
Ainar Garipov
0ceea9af5f cherry-pick: upd-yaml
Merge in DNS/adguard-home from upd-yaml to master

Squashed commit of the following:

commit f0c3a1896e7eba73b1c8a02533637cdabc89909b
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Mon Aug 8 15:28:02 2022 +0300

    home: restore indent lvl

commit b52c124d2e786e8575c58e75efa7d2cd2b70b67f
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Mon Aug 8 15:06:41 2022 +0300

    all: upd tools, yaml mod
2022-08-17 18:10:49 +03:00
Eugene Burkov
39b404be19 cherry-pick: 4358 fix stats
Merge in DNS/adguard-home from 4358-fix-stats to master

Updates #4358.
Updates #4342.

Squashed commit of the following:

commit 5683cb304688ea639e5ba7f219a7bf12370211a4
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Aug 4 18:20:54 2022 +0300

    stats: rm races test

commit 63dd67650ed64eaf9685b955a4fdf3c0067a7f8c
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Aug 4 17:13:36 2022 +0300

    stats: try to imp test

commit 59a0f249fc00566872db62e362c87bc0c201b333
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Aug 4 16:38:57 2022 +0300

    stats: fix nil ptr deref

commit 7fc3ff18a34a1d0e0fec3ca83a33f499ac752572
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Apr 7 16:02:51 2022 +0300

    stats: fix races finally, imp tests

commit c63f5f4e7929819fe79b3a1e392f6b91cd630846
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Aug 4 00:56:49 2022 +0300

    aghhttp: add register func

commit 61adc7f0e95279c1b7f4a0c0af5ab387ee461411
Merge: edbdb2d4 9b3adac1
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Aug 4 00:36:01 2022 +0300

    Merge branch 'master' into 4358-fix-stats

commit edbdb2d4c6a06dcbf8107a28c4c3a61ba394e907
Merge: a91e4d7a a481ff4c
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Aug 3 21:00:42 2022 +0300

    Merge branch 'master' into 4358-fix-stats

commit a91e4d7af13591eeef45cb7980d1ebc1650a5cb7
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Aug 3 18:46:19 2022 +0300

    stats: imp code, docs

commit c5f3814c5c1a734ca8ff6726cc9ffc1177a055cf
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Aug 3 18:16:13 2022 +0300

    all: log changes

commit 5e6caafc771dddc4c6be07c34658de359106fbe5
Merge: 091ba756 eb8e8166
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Aug 3 18:09:10 2022 +0300

    Merge branch 'master' into 4358-fix-stats

commit 091ba75618d3689b9c04f05431283417c8cc52f9
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Aug 3 18:07:39 2022 +0300

    stats: imp docs, code

commit f2b2de77ce5f0448d6df9232a614a3710f1e2e8a
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Aug 2 17:09:30 2022 +0300

    all: refactor stats & add mutexes

commit b3f11c455ceaa3738ec20eefc46f866ff36ed046
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Apr 27 15:30:09 2022 +0300

    WIP
2022-08-17 18:10:16 +03:00
Ainar Garipov
56dc3eab02 cherry-pick: 4801-hassio-link
Updates #4801.

* commit '73f935f3f370ad7e1dfb2495fe71d1dc5e415672':
  Update Hass.io AdGuard Home integration link
2022-08-17 18:07:06 +03:00
Ainar Garipov
554a38eeb1 cherry-pick: 4800-upd-link
Updates #4800.

* commit 'bbccd616148f63240afee6ccf643179ff322c6f4':
  Update RFC 9250 link
2022-08-17 18:06:29 +03:00
Ainar Garipov
c8d3afe869 cherry-pick: 4670-invalid-arg-cap-check
Updates #4670.

Squashed commit of the following:

commit 9c32739eb92ef57c78a4dc3ec3c0f280aebf7182
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Aug 3 20:04:54 2022 +0300

    aghnet: imp port check for older linuxes
2022-08-17 18:05:28 +03:00
Ainar Garipov
44222c604c all: upd chlog 2022-08-17 18:05:16 +03:00
Ainar Garipov
cbf221585e all: upd chlog 2022-08-03 16:22:44 +03:00
Ainar Garipov
48322f6d0d all: upd chlog 2022-08-03 16:21:12 +03:00
Ainar Garipov
d5a213c639 cherry-pick: upd-i18n
Merge in DNS/adguard-home from upd-i18n to master

Squashed commit of the following:

commit 366600a32ecbb163ab43b43145898bbadcfbc2e9
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Aug 3 15:09:16 2022 +0300

    client: fix si-lk

commit 2a55ee3846251e53529f4ef6562e5f4939381eae
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Aug 3 15:03:45 2022 +0300

    client: upd i18n
2022-08-03 16:01:22 +03:00
Ainar Garipov
8166c4bc33 cherry-pick: upd-go
Merge in DNS/adguard-home from upd-go to master

Squashed commit of the following:

commit 8edfb5cc3466c1e4ee2eacae5157bd93c135a284
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Aug 3 14:25:45 2022 +0300

    all: imp docs; fmt

commit 080b8a85c02afbdaa079c0da47cb7b6311d50fbe
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Tue Aug 2 20:51:20 2022 +0300

    all: upd go, imp generic code
2022-08-03 16:01:02 +03:00
Ildar Kamalov
133cd9ef6b cherry-pick: 4776 add word break for query log domains
Updates #4776

Squashed commit of the following:

commit 6f1778fbd11da529ae934ee33c8f1ad227cdfa66
Merge: 753bd44c 053bb72a
Author: Ildar Kamalov <ik@adguard.com>
Date:   Tue Aug 2 11:52:07 2022 +0300

    Merge branch 'master' into 4776-domains

commit 753bd44cbb592903ed996713a79e4dbf073d780b
Author: Ildar Kamalov <ik@adguard.com>
Date:   Mon Aug 1 16:58:07 2022 +0300

    client: add word break for query log domains
2022-08-03 15:59:23 +03:00
Ildar Kamalov
11146f73ed cherry-pick: 4775 fix query log issue on tablet devices
Updates #4775

Squashed commit of the following:

commit 9ad85d2306b68227e11c7b1dd792e3fe6389939d
Merge: 95aa29d6 41f081d8
Author: Ildar Kamalov <ik@adguard.com>
Date:   Tue Aug 2 11:44:04 2022 +0300

    Merge branch 'master' into 4775-popup

commit 95aa29d68bdf5e9c4e7aa59f42d04328b1872115
Author: Ildar Kamalov <ik@adguard.com>
Date:   Mon Aug 1 16:21:23 2022 +0300

    client: fix query log issue on tablet devices
2022-08-03 15:57:05 +03:00
Eugene Burkov
1beb18db47 cherry-pick: 4517 warning wording
Merge in DNS/adguard-home from 4517-warning-label to master

Updates #4517.

Squashed commit of the following:

commit 4987f63937253da2954cf20c7b99a3b8a0adf112
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Aug 1 13:59:28 2022 +0300

    client: imp wording
2022-08-03 15:56:45 +03:00
Eugene Burkov
f7bc2273a7 cherry-pick: 4517 domain specific test
Merge in DNS/adguard-home from 4517-domain-specific-test to master

Updates #4517.

Squashed commit of the following:

commit 03a803f831749a060923ec966592696f99591786
Merge: 8ea24170 f5959a0d
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Jul 29 19:17:28 2022 +0300

    Merge branch 'master' into 4517-domain-specific-test

commit 8ea2417036547996bb2d39b75b0ff31de4fe9b21
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Jul 29 18:44:26 2022 +0300

    all: log changes, imp docs

commit aa74c8be64f2796a2dfa7166f0155fff5bb395b6
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Jul 29 18:07:12 2022 +0300

    dnsforward: imp logging

commit 02dccca4e7d766bbfbe0826933e8be70fcd93f58
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Jul 29 17:24:08 2022 +0300

    all: imp code, docs

commit 3b21067d07b208baf574a34fb06ec808b37c4ee3
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Jul 29 13:34:55 2022 +0300

    client: add warning toast

commit ea2272dc77f87e34dc6aff0af99c7a51a04e3770
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Jul 28 20:11:55 2022 +0300

    dnsforward: imp err msg, docs

commit fd9ee82afef9d93961c30ebafcc7a11d984247b5
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Jul 28 19:24:58 2022 +0300

    dnsforward: test doain specific upstreams

commit 9a83ebfa7a73bf4e03eaf1ff4a33f79771159fc7
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Jul 28 18:22:49 2022 +0300

    dnsforward: merge some logic
2022-08-03 15:56:25 +03:00
Ainar Garipov
d1e735a003 cherry-pick: upd-links-etc
Merge in DNS/adguard-home from upd-links-etc to master

Squashed commit of the following:

commit 49856df394f1a2123a27afdb35047d3b1a49860f
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Tue Aug 2 20:43:10 2022 +0300

    all: revert cdn link revert

commit 59bbe4bbd300f48674c1a6224a91f9a567d6c79c
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Tue Aug 2 20:40:50 2022 +0300

    all: revert static link revert

commit fe2acc4a0d6d5ee31cb8dbb0d0e0984c3cd723db
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Tue Aug 2 18:24:02 2022 +0300

    all: revert links up in README; imp tools
2022-08-03 15:54:47 +03:00
Ainar Garipov
af4ff5c748 cherry-pick: upd-domains-and-links
Merge in DNS/adguard-home from upd-domains-and-links to master

Squashed commit of the following:

commit 5e5ff2fec358104995877da689da24749ac470ce
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Jul 28 19:53:19 2022 +0300

    all: upd urls

    Update domains and URLs to make them more resistant to state blocking.
2022-08-03 15:54:23 +03:00
Ainar Garipov
fc951c1226 cherry-pick: 4755-youtube-domain
Updates #4755.

Squashed commit of the following:

commit cb0ab8b26f6f277ef76ee3492c99870cbfc24666
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Jul 28 17:21:02 2022 +0300

    filtering: add another youtube domain
2022-08-03 15:52:00 +03:00
Ainar Garipov
f81fd42472 cherry-pick: imp-issue-tmpl
Merge in DNS/adguard-home from imp-issue-tmpl to master

Squashed commit of the following:

commit 3941dd135911d850f3ec9b01f55bc45269a7b91c
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Jul 28 15:24:26 2022 +0300

    all: fix links in issue tmpls

commit 438375a4666f951fc24ab47e4b0de5a61714973b
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Jul 28 15:23:00 2022 +0300

    all: imp issue tmpls
2022-08-03 15:51:37 +03:00
Ainar Garipov
1029ea5966 cherry-pick: issue-templates
Merge in DNS/adguard-home from issue-templates to master

Squashed commit of the following:

commit 989253530047a463804e81c8fda82ac268f39adc
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Jul 27 16:56:35 2022 +0300

    all: fix issue tmpl schema

commit e69df09ab4b4f713d124dc6eeb1ed34e0f4aaa70
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Jul 27 16:41:14 2022 +0300

    all: rename tmpl files

commit 542306da1ea1bdc09ca328856367c64139a8ec60
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Jul 27 16:37:02 2022 +0300

    all: imp github issue templates
2022-08-03 15:51:23 +03:00
Ainar Garipov
c0abdb4bc7 cherry-pick: 4782-server-name-label
Updates #4782.

Squashed commit of the following:

commit d350b3853bf722c0f2a8d1fc4a1c28dc384c5ca0
Author: Natalia Sokolova <n.sokolova@adguard.com>
Date:   Tue Jul 26 18:39:38 2022 +0300

    client: imp wording

commit d0785311bfe38fb10477bf8971a46d6c61aecfda
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Tue Jul 26 17:16:41 2022 +0300

    client: imp tls server name label
2022-08-03 15:50:48 +03:00
Ainar Garipov
6681178ad3 all: upd chlog 2022-08-03 15:43:45 +03:00
Ainar Garipov
e73605c4c5 all: add ms link 2022-07-13 15:24:17 +03:00
Ainar Garipov
c7017d49aa all: upd chlog 2022-07-13 15:22:20 +03:00
Ainar Garipov
191d3bde49 cherry-pick: home: fix exe path finding
Closes #4735.

Squashed commit of the following:

commit 8228e5f82c9d8056d5567a7f1b13b1365346c4d4
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Mon Jul 11 17:41:19 2022 +0300

    home: fix exe path finding
2022-07-13 15:15:10 +03:00
Ainar Garipov
18876a8e5c cherry-pick: aghalg: impl json.Marshaler for NullBool
Updates #4735.

Squashed commit of the following:

commit 93a0b1dc6b668f7d9fd89d06b8f0f24dcd345356
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Mon Jul 11 17:02:36 2022 +0300

    aghalg: impl json.Marshaler for NullBool
2022-07-13 15:14:54 +03:00
Eugene Burkov
aa4a0d9880 cherry-pick: 4698 Gateway IP in DHCP Lease
Closes #4698.

Squashed commit of the following:

commit 6be0caee58926f8cea1e10650fbde0c8d97d0dac
Author: Ildar Kamalov <ik@adguard.com>
Date:   Fri Jul 8 13:41:50 2022 +0300

    update translation

commit e0370656d05e8463d73ea73568cae81187c6b2e3
Author: Ildar Kamalov <ik@adguard.com>
Date:   Fri Jul 8 13:40:54 2022 +0300

    client: validate static lease ip

commit 7f4d00f9f3a54dc93ce5d5c45e9c21745f6e39d1
Merge: 2ee79626 77e5e27d
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Jul 8 13:20:15 2022 +0300

    Merge branch 'master' into 4698-lease-with-gateway

commit 2ee79626a1b0c7b113dbd22ba4ef6e85ea9913ec
Merge: 471b96b8 3505ce87
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Jul 7 19:34:33 2022 +0300

    Merge branch 'master' into 4698-lease-with-gateway

commit 471b96b81da8920c1e71b7110050154f912677d2
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Jul 7 16:07:23 2022 +0300

    dhcpd: imp docs

commit 67dd6c76f7d2df4712a57281e0f40f2ee1a1efa2
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Jul 7 15:48:47 2022 +0300

    dhcpd: restrict gateway ip for lease
2022-07-13 15:14:32 +03:00
Dimitry Kolyshev
d03d731d65 cherry-pick: all: updater exe name
Merge in DNS/adguard-home from 4219-updater to master

Squashed commit of the following:

commit f569a5f232330b83c234838a5bff8ae5277f152f
Merge: a90b4fa7 3505ce87
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Thu Jul 7 22:14:50 2022 +0530

    Merge remote-tracking branch 'origin/master' into 4219-updater

    # Conflicts:
    #	CHANGELOG.md

commit a90b4fa7782c5ec4531d8e305c0d448e84898239
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Thu Jul 7 21:56:17 2022 +0530

    home: imp code

commit da0f96b976e430fffc531072ef3e2384bc8b1f09
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Thu Jul 7 21:48:40 2022 +0530

    updater: exe name

commit 246dc9ca3b133cbc93ea59edd272674b87ff8de3
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Thu Jul 7 19:18:02 2022 +0530

    all: imp docs

commit 042382d170c4d68ff67fe5544a75371337529623
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Thu Jul 7 18:02:25 2022 +0530

    all: updater exe name

commit a180c4673ead66788969865784348634af1a739e
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Thu Jul 7 17:47:46 2022 +0530

    docs: updater exe name

commit 1a98a6eadbd96add0a488fb8f89fb7d8b0ffb3d0
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Thu Jul 7 17:40:44 2022 +0530

    all: updater exe name

commit 1b13f5d85550dc71b08fd8e5b4258f8414a38759
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Thu Jul 7 17:14:57 2022 +0530

    all: updater exe name
2022-07-13 15:14:06 +03:00
Ainar Garipov
33b58a42fe cherry-pick: all: use canonical names for hosts file runtime clients
Updates #4683.

Squashed commit of the following:

commit daa8fdaee574d4ac2171f6b13c5ce3f3fedd9801
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Jul 7 19:13:29 2022 +0300

    all: use canonical names for hosts file runtime clients
2022-07-13 15:13:06 +03:00
Eugene Burkov
2e9e708647 cherry-pick: 4699 dhcp ptr
Merge in DNS/adguard-home from 4699-dhcp-ptr to master

Closes #4699.

Squashed commit of the following:

commit 0a8e2b3e22b7fad28a53db65031cc39d8755ecf4
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Jun 28 18:40:53 2022 +0300

    dnsforward: imp naming again

commit 0b0884a8305f18f7f69560b86be8837933e220e9
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Jun 28 18:26:58 2022 +0300

    dnsforward: imp naming

commit e193f53d9a1dd76d41396c06e2ec5a1e7d176557
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Jun 28 17:26:00 2022 +0300

    all: imp chlog

commit 8ac9f84f086d9cb0b0f9da72bfc51f9b70a3dab7
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Jun 28 17:18:48 2022 +0300

    all: log changes

commit 7cdc175d02b6eacfcb6ba62a5424d11e2561a879
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Jun 28 17:03:52 2022 +0300

    dnsforward: add tld to dhcp leased hostnames
2022-07-13 15:11:37 +03:00
Eugene Burkov
8ad22841ab cherry-pick: 4677 openwrt service
Merge in DNS/adguard-home from 4677-openwrt-service to master

Updates #4677.

Squashed commit of the following:

commit 6aed4036d3338a601a7ec5ef1ca74a407ae4c0e2
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Jun 20 14:49:33 2022 +0300

    home: imp docs

commit 54e32fa47ed11e50c6405ced90a400e4e69f021d
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Jun 20 14:30:08 2022 +0300

    home: fix wrt svc
2022-07-13 15:06:04 +03:00
Ainar Garipov
32cf02264c cherry-pick: 4326 improve dockerfile
Updates #4326.

* commit 'f987c2559825923b22e910d01c2d42fb06231acc':
  scripts: imp docs; upd alpine
  Simplify Dockerfile Alpine Linux apk usage
2022-07-13 15:04:26 +03:00
Ildar Kamalov
0e8445b38f cherry-pick: 4659 fix url value in filter table actions
Updates #4659

Squashed commit of the following:

commit e1bcda9566bd9f1cca965f4308c337a9adf2ce04
Author: Ildar Kamalov <ik@adguard.com>
Date:   Tue Jun 14 17:40:09 2022 +0300

    client: fix url value in filter table actions
2022-07-13 15:03:54 +03:00
Eugene Burkov
cb27ecd6c0 cherry-pick: more sysv
Merge in DNS/adguard-home from 4480-sysv-again to master

Updates #4480.

Squashed commit of the following:

commit 263fa05ab19de95b18fb07f6c89e4b9a1b24657b
Merge: 360a6468 d3f39b0a
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Jun 14 13:36:15 2022 +0300

    Merge branch 'master' into 4480-sysv-again

commit 360a646833ca9e0e01cb6d085e70b898a30dc2d0
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Jun 9 18:15:41 2022 +0300

    home: rename linux file

commit c3032533b7e00136c25d15a4ad771bb8a9c13e31
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Jun 9 18:06:25 2022 +0300

    home: imp code

commit 2381c4a6ab4f6dca88123ff7b0a92f2cf9a420a8
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Jun 9 17:48:22 2022 +0300

    home: wrap sysv service
2022-07-13 15:03:38 +03:00
Ildar Kamalov
535220b3df cherry-pick: 4637 fix blocked services icons and actions highlight
Updates #4637

Squashed commit of the following:

commit d69887586d15582406fab642e576a46f8984107b
Merge: 65453371 e738508d
Author: Ildar Kamalov <ik@adguard.com>
Date:   Fri Jun 10 12:07:29 2022 +0300

    Merge branch 'master' into 4637-table

commit 65453371fc7309e772a12fb9f522247e1392a64a
Author: Ildar Kamalov <ik@adguard.com>
Date:   Thu Jun 9 18:43:44 2022 +0300

    client: fix blocked services icons and actions highlight
2022-07-13 15:03:18 +03:00
Ainar Garipov
7b9cfa94f8 cherry-pick: all: imp updater
Merge in DNS/adguard-home from imp-updater to master

Squashed commit of the following:

commit 6ed487359e56a35b36f13dcbf2efbf2a7a2d8734
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Jun 9 16:29:35 2022 +0300

    all: imp logs, err handling

commit e930044cb619a43e5a44c230dadbe2228e9a93f5
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Jun 9 15:53:35 2022 +0300

    all: imp updater
2022-07-13 15:02:51 +03:00
Ildar Kamalov
b3f2e88e9c cherry-pick: 4642 update dns addresses on encryption update
Updates #4642

Squashed commit of the following:

commit 75729120d3532dc2bd12b6c9e724a691043a1870
Merge: 5b681867 1c1ca1c6
Author: Ildar Kamalov <ik@adguard.com>
Date:   Thu Jun 9 11:58:13 2022 +0300

    Merge branch 'master' into 4642-dns-privacy

commit 5b68186705c3a9287a44e33c8cf7ab79060f35a4
Author: Ildar Kamalov <ik@adguard.com>
Date:   Tue Jun 7 18:39:02 2022 +0300

    fix

commit 46a9346154d33206e829a97021f3ef47ac2a5611
Author: Ildar Kamalov <ik@adguard.com>
Date:   Tue Jun 7 18:18:18 2022 +0300

    client: update dns addresses on encryption update
2022-07-13 15:02:25 +03:00
Ildar Kamalov
aa7a8d45e4 cherry-pick: 4641 fix button clickable area
Updates #4641

Squashed commit of the following:

commit f9f018388a198d7712e5caabba94035e42e393c4
Author: Ildar Kamalov <ik@adguard.com>
Date:   Tue Jun 7 16:21:37 2022 +0300

    client: fix button clickable area
2022-07-13 15:02:01 +03:00
Ainar Garipov
49cdef3d6a all: upd chlog, go 2022-07-13 14:46:30 +03:00
Ainar Garipov
fecd146552 client: upd i18n 2022-07-13 13:43:21 +03:00
Ainar Garipov
b01efd8c98 all: upd chlog 2022-06-06 18:10:40 +03:00
Ainar Garipov
bd4dfb261c cherry-pick: all: fix quic reply id
Merge in DNS/adguard-home from upd-dnsproxy-quic-fix to master

Squashed commit of the following:

commit a6ffa24769259c73e397e02d087dc155ed58a3e2
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Mon Jun 6 15:06:00 2022 +0300

    all: fix quic reply id
2022-06-06 16:46:20 +03:00
Ainar Garipov
e754e4d2f6 cherry-pick: client: upd i18n
Merge in DNS/adguard-home from upd-i18n to master

Squashed commit of the following:

commit 3feadfe31609ef52726b582ad6ba18bfa435a081
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Fri Jun 3 16:34:36 2022 +0300

    client: upd i18n
2022-06-03 16:40:11 +03:00
Ainar Garipov
b220e35c99 cherry-pick: all: replace uuid pkg; upd deps
Merge in DNS/adguard-home from 4622-upd-deps to master

Squashed commit of the following:

commit 36f407d8ab103da0f7eacdf91c153c23a5b7c3f2
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Fri Jun 3 15:22:47 2022 +0300

    home: imp mobileconfig uuid gen

commit dddd162461a4830f7c0636338430cd6e77199214
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Fri Jun 3 13:54:29 2022 +0300

    all: replace uuid pkg; upd deps
2022-06-03 16:31:56 +03:00
Ainar Garipov
4f5131f423 all: sync more 2022-06-02 17:55:48 +03:00
Ainar Garipov
dcb043df5f all: sync with master more 2022-06-02 17:28:16 +03:00
Ainar Garipov
86e5756262 client: sync with master 2022-06-02 17:23:58 +03:00
Eugene Burkov
ba0cf5739b cherry-pick: 3142 swap arp and rdns priority
Merge in DNS/adguard-home from 3142-fix-clients to master

Updates #3142.
Updates #3597.

Squashed commit of the following:

commit 4dcabedbfb1a4e4a0aaba588f708e4625442fce8
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Mar 22 15:13:15 2022 +0300

    all: imp log of changes

commit 481088d05eecac1109daf378e0b4d5f6b2cf099b
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Mar 22 14:36:44 2022 +0300

    all: swap arp and rdns priority
2022-06-02 17:11:09 +03:00
Eugene Burkov
c4a13b92d2 cherry-pick: 3157 excessive ptrs
Merge in DNS/adguard-home from 3157-excessive-ptrs to master

Updates #3157.

Squashed commit of the following:

commit 6803988240dca2f147bb80a5b3f78d7749d2fa14
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Apr 19 14:50:01 2022 +0300

    aghnet: and again

commit 1a7f4d1dbc8fd4d3ae620349917526a75fa71b47
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Apr 19 14:49:20 2022 +0300

    aghnet: docs again

commit d88da1fc7135f3cd03aff10b02d9957c8ffdfd30
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Apr 19 14:47:36 2022 +0300

    aghnet: imp docs

commit c45dbc7800e882c6c4110aab640c32b03046f89a
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Apr 19 14:41:19 2022 +0300

    aghnet: keep alphabetical order

commit b61781785d096ef43f60fb4f1905a4ed3cdf7c68
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Apr 19 13:50:56 2022 +0300

    aghnet: imp code quality

commit 578dbd71ed2f2089c69343d7d4bf8bbc29150ace
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Apr 12 17:02:38 2022 +0300

    aghnet: imp arp container
2022-06-02 17:05:18 +03:00
Dimitry Kolyshev
723279121a cherry-pick: whotracksme tracker links
Merge in DNS/adguard-home from 4416-ui-tracker-href to master

Squashed commit of the following:

commit 979ea82a3b4d2c2a895b81aacd613fb7e5bec586
Merge: 4fe6328b 12ee287d
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Tue Apr 19 15:03:13 2022 +0200

    Merge remote-tracking branch 'origin/master' into 4416-ui-tracker-href

commit 4fe6328b276e697a2aa351c6543d2efe6d2dc2e1
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Tue Apr 19 14:08:10 2022 +0200

    whotracksme tracker links
2022-06-02 16:53:32 +03:00
Ainar Garipov
3ad7649f7d cherry-pick: all: do not mark help-wanted issues as stale
Merge in DNS/adguard-home from help-wanted-stale to master

Squashed commit of the following:

commit 1c5ffcdd0153dd7d9d9bcc1e35dee4a0b3113f59
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Fri Apr 22 20:04:01 2022 +0300

    all: do not mark help-wanted issues as stale
2022-06-02 16:53:13 +03:00
Ainar Garipov
2898a49d86 cherry-pick: home: rm unnecessary locking in update; refactor
Merge in DNS/adguard-home from 4499-rm-unnecessary-locking to master

Squashed commit of the following:

commit 6d70472506dd0fd69225454c73d9f7f6a208b76b
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Mon Apr 25 17:26:54 2022 +0300

    home: rm unnecessary locking in update; refactor
2022-06-02 16:51:50 +03:00
Ildar Kamalov
1547f9d35e cherry-pick: client: fix constant loading for blocked requests
Updates #4420

Squashed commit of the following:

commit 461a59e1541626020bf0bcfaf34ba7d2f4509dc7
Merge: 5c5e7b5d 2a1ad532
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Mon Apr 25 18:46:02 2022 +0300

    Merge branch 'master' into 4420-loading-log

commit 5c5e7b5d1a69d30e40e71f49f46dea89fa8c40a2
Author: Ildar Kamalov <ik@adguard.com>
Date:   Sun Apr 24 22:18:22 2022 +0300

    client: fix constant loading for blocked requests
2022-06-02 16:45:06 +03:00
Eugene Burkov
adadd55c42 cherry-pick: 4525 fix panic
Merge in DNS/adguard-home from 3020-fix-panic to master

Closes #4525.

Squashed commit of the following:

commit f8d9e25eccb485269aa2f0275d4e08da767f9d05
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Apr 26 15:09:11 2022 +0300

    home: imp code

commit 8fe02c8f057c05b9e8ce1de056a92e7cd69ae4c6
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Apr 26 14:44:33 2022 +0300

    home: fix panic
2022-06-02 16:44:48 +03:00
Ainar Garipov
33b0225aa4 cherry-pick: home: imp client finding logging
Updates #4526.

Squashed commit of the following:

commit 970476ea238cbab797912e1c50eca35e3f74a52f
Merge: 3e2dde81 c4ff80fd
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Apr 27 14:01:17 2022 +0300

    Merge branch 'master' into 4526-add-client-logs

commit 3e2dde81d7325b75c257f333e2c4e417f4ae203d
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Apr 27 13:59:19 2022 +0300

    home: imp logs

commit 094bfe34770b4bdc504b5ae97dd2d3842b2f73cf
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Tue Apr 26 21:11:18 2022 +0300

    home: imp client finding logging
2022-06-02 16:43:14 +03:00
Ainar Garipov
97d4058d80 cherry-pick: home: imp openbsd init script
Closes #4533.

Squashed commit of the following:

commit 48ca9e100619e714eab565273daeb4ee9adb5b74
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Apr 28 20:25:15 2022 +0300

    home: imp openbsd init script
2022-06-02 16:42:57 +03:00
Eugene Burkov
86207e719d cherry-pick: 4542 clientid case
Merge in DNS/adguard-home from 4542-clientid-case to master

Updates #4542.

Squashed commit of the following:

commit 2a3111ebcef09460b407cd1c870cad2391cd5650
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed May 4 20:44:18 2022 +0300

    all: fix changelog link

commit 3732def83e2a36eeff2d682149dc4dcef4e92a7d
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed May 4 20:43:37 2022 +0300

    all: log changes

commit 9fe1001cf586669ae238c9c4818070cf94e23ce8
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed May 4 19:37:33 2022 +0300

    dnsforward: lowercase clientid
2022-06-02 16:42:15 +03:00
Eugene Burkov
113f94ff46 cherry-pick: all: log changes
Updates #4273.

Squashed commit of the following:

commit ebae1a4d0944fa348b7dcb7e73e59d083c7a5e97
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed May 18 14:48:16 2022 +0300

    all: log changes
2022-06-02 16:40:13 +03:00
Dimitry Kolyshev
5673deb391 cherry-pick: all: upd dnsproxy
Merge in DNS/adguard-home from 4503-upstream-conf to master

Squashed commit of the following:

commit c6cb1babd4cbf9aacafe902e3d54ce17e8d2cc81
Merge: 75d85ed1 79d85a24
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Mon May 23 13:06:00 2022 +0200

    Merge remote-tracking branch 'origin/master' into 4503-upstream-conf

commit 75d85ed1f4d8d5060800b2f8a4cde662db02ae30
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Fri May 20 13:14:16 2022 +0200

    all: upd dnsproxy

commit 781768d639388a60fc90631f819cfc5dd90b9eba
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Mon May 16 16:27:25 2022 +0200

    all: docs

commit 0dafb5b3fe11b1952d9a04294bcaaa8091b9c2a7
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Mon May 16 16:17:35 2022 +0200

    all: docs

commit 0d5463e4157132b0e6be78fd97eaf5a5cb8d1edc
Merge: e2c86909 f289f4b1
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Mon May 16 16:01:40 2022 +0200

    Merge remote-tracking branch 'origin/master' into 4503-upstream-conf

    # Conflicts:
    #	go.mod
    #	go.sum

commit e2c869091b1386065076f44dbf9498a31c9d5451
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Mon May 16 15:29:17 2022 +0200

    all: upd dnsrpoxy
2022-06-02 16:39:29 +03:00
Eugene Burkov
3548a393ed cherry-pick: 4480 fix sysv service script
Merge in DNS/adguard-home from 4480-sysv-boot to master

Updates #4480.

Squashed commit of the following:

commit c9645b1f3bd22a249c666e4485818bab6769f32d
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue May 24 14:25:09 2022 +0300

    home: imp sysv script

commit cc323364ba6cce0284cbc6be9133a50a51b71f56
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon May 23 21:13:06 2022 +0300

    home: fix sysv service script
2022-06-02 16:35:58 +03:00
Ainar Garipov
254515f274 cherry-pick: all: upd dnsproxy, supp rfc 9250
Updates #4592.

Squashed commit of the following:

commit 1a80875d6aa7811d7d1d978f6fa8d558dec1ca87
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Tue May 24 19:28:27 2022 +0300

    all: upd dnsproxy, supp rfc 9250
2022-06-02 16:34:36 +03:00
Dimitry Kolyshev
bccbecc6ea cherry-pick: all: filters json
Merge in DNS/adguard-home from 4581-filters-json to master

Squashed commit of the following:

commit da0b86983432ac1791645da328df5848daac5ea6
Merge: 62fa4fc6 a82ec09a
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Wed May 25 12:58:25 2022 +0200

    Merge remote-tracking branch 'origin/master' into 4581-filters-json

commit 62fa4fc6ff150ebb8dbd8888a58819fb644d43ad
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Wed May 25 11:55:52 2022 +0200

    all: filters json

commit 96486ffbb41947b5e748f6e35eb96ee73867eba1
Merge: 9956f0af c0ac82be
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Tue May 24 15:57:52 2022 +0200

    Merge branch 'master' into 4581-filters-json

commit 9956f0aff1b7029f336d22013a62f2871a964322
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Tue May 24 15:53:43 2022 +0200

    all: filters json
2022-06-02 16:33:33 +03:00
Ainar Garipov
66f53803af cherry-pick: querylog: fix oldest calc
Updates #4591.

Squashed commit of the following:

commit 70b70c78c85311363535536c7ea12336b21accf8
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed May 25 17:35:54 2022 +0300

    querylog: fix oldest calc
2022-06-02 16:32:45 +03:00
Ildar Kamalov
faef005ce7 cherry-pick: client: reset filtered logs on url params clear
Merge in DNS/adguard-home from fix-querylog-link to master

Squashed commit of the following:

commit fc4043258eb1e427a76ee44d2a4a525a6d659ab9
Merge: 25b91504 549b20bd
Author: Ildar Kamalov <ik@adguard.com>
Date:   Thu May 26 12:42:02 2022 +0300

    Merge branch 'master' into fix-querylog-link

commit 25b91504e8949bd381e6774148e4a7ecbb81610e
Author: Ildar Kamalov <ik@adguard.com>
Date:   Thu May 26 12:21:57 2022 +0300

    fix

commit f567b9b1e4eeb6499c79b05e4d837e905850a6b9
Author: Ildar Kamalov <ik@adguard.com>
Date:   Thu May 26 12:20:48 2022 +0300

    client: reset filtered logs on url params clear
2022-06-02 16:32:18 +03:00
Eugene Burkov
941cd2a562 cherry-pick: 4166 udp upstream
Merge in DNS/adguard-home from 4166-udp-upstream to master

Closes #4166.

Squashed commit of the following:

commit b8b6d1c7ac1e11e83c0c68e46e7f66fdc6043839
Merge: e5f01273 ea6e033d
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Mar 1 20:36:40 2022 +0300

    Merge branch 'master' into 4166-udp-upstream

commit e5f0127384d84c4395da5b79a1fd4a47acbe122c
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Mar 1 19:41:33 2022 +0300

    client: upd upstream examples

commit bd974f22231f11f4c57e19d6d13bc45dbfdf2fdf
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Mar 1 18:36:10 2022 +0300

    all: upd proxy

commit badf1325090ecd1dc86e42e7406dfb6653e07bf1
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Feb 4 14:36:50 2022 +0300

    WIP
2022-06-02 16:31:11 +03:00
Eugene Burkov
6a4a9a0239 cherry-pick: 3978 Query Log ECS
Merge in DNS/adguard-home from 3978-ecs-ip to master

Updates #3978.

Squashed commit of the following:

commit 915b94afa4b6d90169f73d4fa171bc81bcc267a7
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Mar 3 17:46:40 2022 +0300

    all: rm dot

commit 2dd2ed081b199de7e5d8269dae5d08d53b5eea6d
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Mar 3 17:42:45 2022 +0300

    client: imp txt

commit 8d5a23df739f0b650f9f3870141fd83e8fa0c1e0
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Mar 3 14:36:04 2022 +0300

    client: imp text

commit 69c856749a20144822ef3f1f67c5f3e3c24f5374
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Mar 3 14:24:56 2022 +0300

    client: imp description

commit cd0150128ad29d1874492735a5d621c0803ad0bd
Merge: 28181fbc e0b557ed
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Mar 2 21:02:16 2022 +0300

    Merge branch 'master' into 3978-ecs-ip

commit 28181fbc79eb22e7fd13cbd1d5a3c040af9fa2a4
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Mar 2 20:45:50 2022 +0300

    client: show ecs

commit cdc5e7f8c4155b798426d815eed0da547ef6efb7
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Feb 17 20:15:56 2022 +0300

    openapi: fix milestone

commit 404d6d822fa1ba4ed4cd41d92d4c1b805342fe55
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Feb 17 20:08:21 2022 +0300

    all: fix deps, docs

commit 8fb80526f1e251d3b7b193c53a4a6dee0e22c145
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Feb 17 19:39:34 2022 +0300

    all: add querylog ecs backend
2022-06-02 16:29:22 +03:00
Eugene Burkov
b9dbe6f1b6 cherry-pick: 4213 add bsd syslog
Merge in DNS/adguard-home from 4213-bsd-syslog to master

Updates #4046.
Closes #4213.

Squashed commit of the following:

commit 1e57c75c4184e83b09cfd27456340ca9447791be
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Feb 28 16:20:32 2022 +0300

    home: imp error msg

commit 63059d031153ff9b6dc9aecd9522d2ad4f8448da
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Feb 28 15:36:37 2022 +0300

    all: imp log of changes

commit 682c3c9e8986b6bdf2d0c665c9cad4a71fd2cc83
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Feb 28 15:29:29 2022 +0300

    home: imp code

commit 86c311a71d07823c18521890bea7c898c117466b
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Feb 28 15:03:02 2022 +0300

    home: add bsd syslog
2022-06-02 16:26:25 +03:00
Ainar Garipov
7fec111ef8 cherry-pick: home: imp openbsd init script
Closes #4533.

Squashed commit of the following:

commit 48ca9e100619e714eab565273daeb4ee9adb5b74
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Apr 28 20:25:15 2022 +0300

    home: imp openbsd init script
2022-06-02 16:25:01 +03:00
Eugene Burkov
5e1bd99718 cherry-pick: 4276 upd quic port
Merge in DNS/adguard-home from 4276-doq-port to master

Closes #4276.

Squashed commit of the following:

commit cbdde622b54d0d5d11d1b4809f95a41ace990a1b
Merge: d32c13e9 2c33ab6a
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Mar 23 15:47:43 2022 +0300

    Merge branch 'master' into 4276-doq-port

commit d32c13e98f0fed2c863160e4e2de02ae3038e3df
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Mar 21 21:55:09 2022 +0300

    all: fix link

commit 0afd702f5192d727927df2f8d95b9317811a1be0
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Mar 21 21:47:38 2022 +0300

    all: imp docs, log changes

commit 9a77fc3daf78d32c577f1bc49aa1f8bc352d44e3
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Mar 21 21:41:30 2022 +0300

    home: upd quic port
2022-06-02 16:23:15 +03:00
Eugene Burkov
9d75f72ceb cherry-pick: 1730 bogus cidr
Merge in DNS/adguard-home from 1730-bogus-cidr to master

Closes #1730.

Squashed commit of the following:

commit 0be54259ca4edb8752e9f7e5ea5104a2b51ed440
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Jan 25 18:50:01 2022 +0300

    all: imp log of changes

commit 59fb7a8c469216823ff54621ec40a4d084836132
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Jan 25 18:46:34 2022 +0300

    all: log changes

commit 9206b13dd715fdf1180d1d572d1b80024b9e6592
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Jan 25 18:41:26 2022 +0300

    all: upd dnsproxy
2022-06-02 16:20:41 +03:00
Ainar Garipov
d98d96db1a all: upd chlog 2022-06-02 16:09:50 +03:00
Ainar Garipov
6a0ef2df15 all: upd chlog, go 2022-04-13 14:30:17 +03:00
Dimitry Kolyshev
75c2eb4c8a cherry-pick: svcb dohpath support
Merge in DNS/adguard-home from 4463-ddr-support to master

Squashed commit of the following:

commit 99a149e9024354ad0341739c3c9b08cefbd74468
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Tue Apr 12 14:13:17 2022 +0200

    imp docs

commit 26150be8df8b35e47c108f6e3319c57b39fb8e38
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Mon Apr 11 20:36:18 2022 +0200

    imp code docs

commit 5a4607f71abba83a9ac8753abd74c9fb97e4a545
Merge: 00f0abf5 9f0fdc5e
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Mon Apr 11 16:14:49 2022 +0200

    Merge remote-tracking branch 'origin/master' into 4463-ddr-support

    # Conflicts:
    #	internal/dnsforward/svcbmsg.go

commit 00f0abf5eea07aeeebc2a856a958215021a51ab7
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Mon Apr 11 16:06:42 2022 +0200

    svcb dohpath support

commit ace81ce1ea2fb96c4434c6c1fded4a79427cf17e
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Thu Apr 7 14:31:32 2022 +0200

    svcb dohpath support

commit a1b5df4fb2e87dab265d6ca55928610a6acc1c00
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Wed Apr 6 16:53:17 2022 +0200

    svcb dohpath support
2022-04-12 21:09:28 +03:00
Dimitry Kolyshev
d021a67d66 cherry-pick: upd bamboo-specs snapcraft
Merge in DNS/adguard-home from upd-bamboo-spec to master

Squashed commit of the following:

commit c26c70f97cbce98afd5c7d4241188d6949869c2a
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Fri Apr 8 13:51:23 2022 +0200

    upd bamboo-specs snapcraft

commit afe40c03b70d2b2dff9c7c25044d7924bdd3c765
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Fri Apr 8 13:10:38 2022 +0200

    upd bamboo-specs snapcraft
2022-04-12 21:09:04 +03:00
Ainar Garipov
4ed97cab12 cherry-pick: dnsforward: upd svcp param ech name
Merge in DNS/adguard-home from upd-ech-dnsrewrite to master

Squashed commit of the following:

commit b5d9e8643fcb0d7fe7bc44c6d8fc8a9d3f2c9595
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Apr 7 18:01:18 2022 +0300

    all: imp chlog

commit 447c5ea6bc2031d4af46578bdb8d724bff001ca0
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Apr 7 15:40:18 2022 +0300

    dnsforward: upd svcp param ech name
2022-04-12 21:08:40 +03:00
Eugene Burkov
a38742eed7 cherry-pick: 4437 imp help output
Merge in DNS/adguard-home from imp-help to master

Updates #4437.

Squashed commit of the following:

commit 941338b93e19021c5b211e9e644387e4326533ce
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Apr 7 13:59:55 2022 +0300

    home: imp help output
2022-04-12 21:08:24 +03:00
Eugene Burkov
5efa95ed26 cherry-pick: 4437 depr memory opt
Merge in DNS/adguard-home from 4437-rm-mem-opt to master

Updates #4437.
Updates #2044.

Squashed commit of the following:

commit d1e5520213f6b68570d18a8d831d4923112901ba
Merge: 73a6b494 8bb95469
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Apr 6 19:37:09 2022 +0300

    Merge branch 'master' into 4437-rm-mem-opt

commit 73a6b4948cb32f1cb79a54b244018b29382fad76
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Apr 6 18:33:23 2022 +0300

    all: imp log of changes

commit a62efcdcd44de300726c906c7f6198c0a02d4ccf
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Apr 6 18:27:42 2022 +0300

    home: depr memory opt
2022-04-12 21:07:46 +03:00
Ildar Kamalov
04db7db607 cherry-pick: 700 validate only enabled encryption form
Merge in DNS/adguard-home from 700-validate to master

Updates #700.

Squashed commit of the following:

commit 9cd9ff2d23352e00c7782cf68195809111c832e5
Author: Ildar Kamalov <ik@adguard.com>
Date:   Wed Apr 6 18:50:11 2022 +0300

    client: validate only enabled encryption form
2022-04-12 21:07:26 +03:00
Ainar Garipov
d17c6c6bb3 all: upd go, chlog, tools 2022-04-06 18:27:21 +03:00
Ildar Kamalov
b2052f2ef1 cherry-pick: fix down flag
Squashed commit of the following:

commit ea446e844a21e7e7e0271d4d133c581014facda1
Merge: bb8cabfa 5e71f5df
Author: Ildar Kamalov <ik@adguard.com>
Date:   Thu Mar 31 10:49:20 2022 +0300

    Merge branch 'master' into client-down-flag

commit bb8cabfae8e2e3eaa09f48ffe7d2fb3b308d31fb
Author: Ildar Kamalov <ik@adguard.com>
Date:   Wed Mar 30 19:27:30 2022 +0300

    client: fix down flag
2022-04-06 17:50:54 +03:00
Eugene Burkov
cddcf852c2 cherry-pick: aghnet: fix catching timeout errors
Merge in DNS/adguard-home from fix-is-timeout to master

Squashed commit of the following:

commit b0fefd01f27a835a34e44beb2eb2c34027960a51
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Mar 29 15:57:06 2022 +0300

    aghnet: fix catching timeout errors
2022-04-06 17:50:27 +03:00
Eugene Burkov
1def426b45 cherry-pick: add go sumdb env
Merge in DNS/adguard-home from cn-sumdb to master

Squashed commit of the following:

commit 439973292f473efa72fb6a733a32be45e634274e
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Mar 28 16:51:28 2022 +0300

    Makefile: add go sumdb env
2022-04-06 17:50:08 +03:00
Ainar Garipov
b114fd5279 cherry-pick: home: fix types
Updates #4424.

Squashed commit of the following:

commit 784b4940d46ce74edbfbbde6e5b24f95dcb4bc70
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Mar 24 17:07:41 2022 +0300

    home: fix types
2022-04-06 17:49:44 +03:00
Eugene Burkov
d27c3284f6 cherry-pick: 4276 upd quic port
Merge in DNS/adguard-home from 4276-doq-port to master

Closes #4276.

Squashed commit of the following:

commit cbdde622b54d0d5d11d1b4809f95a41ace990a1b
Merge: d32c13e9 2c33ab6a
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Mar 23 15:47:43 2022 +0300

    Merge branch 'master' into 4276-doq-port

commit d32c13e98f0fed2c863160e4e2de02ae3038e3df
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Mar 21 21:55:09 2022 +0300

    all: fix link

commit 0afd702f5192d727927df2f8d95b9317811a1be0
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Mar 21 21:47:38 2022 +0300

    all: imp docs, log changes

commit 9a77fc3daf78d32c577f1bc49aa1f8bc352d44e3
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Mar 21 21:41:30 2022 +0300

    home: upd quic port
2022-04-06 17:49:12 +03:00
Ildar Kamalov
ba24a26b53 cherry-pick: 4409 fix icons height
Updates #4409

Squashed commit of the following:

commit 132073ccf00ba6eb6ddacfc82c8d2e01f3d4b011
Author: Ildar Kamalov <ik@adguard.com>
Date:   Mon Mar 21 15:22:33 2022 +0300

    client: remove height

commit 29970f33e7af26e406c442510d626fc0cfdae0ce
Merge: 96b3abcf 77858586
Author: Ildar Kamalov <ik@adguard.com>
Date:   Mon Mar 21 15:10:49 2022 +0300

    Merge branch 'master' into 4409-icon

commit 96b3abcfa4561da466cc53331b8f751d55f59351
Author: Ildar Kamalov <ik@adguard.com>
Date:   Mon Mar 21 10:22:55 2022 +0300

    client: fix icons height
2022-04-06 17:44:11 +03:00
Eugene Burkov
3e6678b6b4 cherry-pick: filtering: fix qq regex legacy
Merge in DNS/adguard-home from qq-rule to master

Updates #3717.

Squashed commit of the following:

commit 1e2d50077067e5f95da645091686349ce9c8a6bc
Merge: 7290a1c4 b16b1d1d
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Mar 23 14:14:10 2022 +0300

    Merge branch 'master' into qq-rule

commit 7290a1c456a7f47e91cc9485f5e112b92cb595ba
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Mar 18 20:36:17 2022 +0300

    filtering: fix qq regex legacy
2022-04-06 17:43:05 +03:00
Ainar Garipov
83fd6f9782 cherry-pick: Fix unsupported regex for QQ blocked rules
Updates #3717.

* commit 'ded9842cd7fbbae0c3a55cd1f468ade22cab0d97':
  Fix unsupported regex for QQ blocked rules
2022-04-06 17:42:49 +03:00
Ainar Garipov
52bc1b3f10 all: upd go, chlog 2022-03-04 15:38:59 +03:00
Ainar Garipov
dd2153b7ac cherry-pick: scripts: imp snap building
Closes #4239.

Squashed commit of the following:

commit 942c03bd88b81d813a12136a135ca6dc003fedf3
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Feb 9 20:38:36 2022 +0300

    scripts: imp snap building
2022-03-01 15:44:43 +03:00
Ainar Garipov
dd96a34861 all: upd chlog 2022-03-01 15:15:59 +03:00
Ainar Garipov
daf26ee25a all: upd chlog 2022-03-01 15:12:34 +03:00
Ainar Garipov
7e140eaaac cherry-pick: client: upd i18n
Merge in DNS/adguard-home from 2643-upd-i18n to master

Squashed commit of the following:

commit 1f36b960877ee2c30319e26132db892fb8a2ef71
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Tue Mar 1 15:05:24 2022 +0300

    client: upd i18n
2022-03-01 15:11:21 +03:00
Ainar Garipov
d07a712988 all: upd chlog 2022-02-28 19:15:59 +03:00
Ainar Garipov
95863288bf cherry-pick: client: fix link in client form
Updates #4244.

Squashed commit of the following:

commit 20d558e9e6935555a13e1aebc7d364e6f1910e9e
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Mon Feb 28 19:01:32 2022 +0300

    client: fix link in client form
2022-02-28 19:14:46 +03:00
Ainar Garipov
ea12be658b all: upd chlog 2022-02-21 17:10:19 +03:00
Ainar Garipov
faa7c9aae5 cherry-pick: client: upd i18n
Updates #2643.

Squashed commit of the following:

commit 048c245ab682f0799c2f7a7f0435a1898a482392
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Mon Feb 21 16:58:10 2022 +0300

    client: upd i18n
2022-02-21 17:08:25 +03:00
Ainar Garipov
e3653e8c25 all: upd chlog 2022-02-18 21:01:24 +03:00
Ainar Garipov
b40cb24822 all: upd chlog 2022-02-14 17:14:37 +03:00
Ainar Garipov
74004c1aa0 cherry-pick: client: use strict search by client
Updates #4271.

Squashed commit of the following:

commit 10a113126306fce51b4dd10a696b8c7d3213a445
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Fri Feb 11 18:37:18 2022 +0300

    client: more strict search

commit 7aa24129195c0eba442bfe43564469fdb2a5b138
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Fri Feb 11 18:22:18 2022 +0300

    client: use strict search by client
2022-02-14 17:06:16 +03:00
Ainar Garipov
3e240741f1 cherry-pick: scripts: imp mips compat
Updates #4269.

Squashed commit of the following:

commit f633e875f4f0ab767a0537d9bfe95734823f8a51
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Fri Feb 11 17:33:53 2022 +0300

    scripts: imp mips compat
2022-02-14 17:06:06 +03:00
Ainar Garipov
6cfdbef1a5 cherry-pick: client: imp validation texts
Merge in DNS/adguard-home from imp-i18n to master

Squashed commit of the following:

commit c58c00383824a88ea8e22a845e422ba2ff7d225e
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Feb 10 20:21:00 2022 +0300

    client: imp validation texts
2022-02-14 17:05:33 +03:00
Ainar Garipov
d9bde6425b cherry-pick: all: use "ClientID" consistently
Closes #4242.
Updates #4244.

Squashed commit of the following:

commit 3a2296a7a70006cf6777e54ce1e2fc3559aec5be
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Feb 9 21:23:43 2022 +0300

    client: imp more

commit 3aacc8696ac694ff459fd33ba7beeeabd2569a55
Merge: b28a120f 2a5b5f19
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Feb 9 21:21:59 2022 +0300

    Merge branch 'master' into 4244-imp-i18n

commit b28a120fe9aa68507b173717059b7b259097d6a4
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Feb 9 14:49:49 2022 +0300

    client: imp texts more

commit c1fa6ca336f2d5bdcc67836f348be4843a0a8f79
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Tue Feb 8 21:12:15 2022 +0300

    all: use "ClientID" consistently
2022-02-14 17:04:33 +03:00
Ainar Garipov
e2ae9e1591 cherry-pick: client: upd i18n
Merge in DNS/adguard-home from upd-i18n to master

Squashed commit of the following:

commit e2f9e9f52a424b7c13beebfc2f8fea3814d3b2f4
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Tue Feb 8 13:48:17 2022 +0300

    client: upd i18n
2022-02-14 17:04:18 +03:00
Ainar Garipov
5ebcbfa9ad all: upd go 2022-02-11 16:27:53 +03:00
Ainar Garipov
e276bd7a31 all: upd chlog, minimize diff to master 2022-02-07 20:35:33 +03:00
Eugene Burkov
659b2529bf cherry-pick: upd changelog
Merge in DNS/adguard-home from changelog-right-now to master

Squashed commit of the following:

commit b391a1f8ac666de67ad6d00c9cbf6e90614f16c7
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Feb 7 20:18:25 2022 +0300

    fix changelog

commit 39878b75c9ecc91668be759d4cc033961c91c2c5
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Feb 7 20:15:43 2022 +0300

    all: log changes
2022-02-07 20:26:43 +03:00
Eugene Burkov
97b3ed43ab cherry-pick: 4254 fix optimistic
Merge in DNS/adguard-home from 4254-fix-optimistic to master

Updates #4254.

Squashed commit of the following:

commit 652e2c2ab9405b9a6ed5d153b6b508e3b87ce66e
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Feb 7 18:55:34 2022 +0300

    all: upd proxy
2022-02-07 20:23:51 +03:00
Ainar Garipov
767d6d3f28 cherry-pick: all: add gh milestone links to chlog
Merge in DNS/adguard-home from chlog-ms-links to master

Squashed commit of the following:

commit 97156f1452a7713e5e8d66a9b5eeac25fb97ab04
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Fri Feb 4 17:56:58 2022 +0300

    all: add gh milestone links to chlog
2022-02-07 20:12:54 +03:00
Ainar Garipov
31fc9bfc52 cherry-pick: scripts: add link to platforms page
Closes #4209.

Squashed commit of the following:

commit 12d99e7454ff01e00f29e51d002147a04a77a2b3
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Tue Feb 1 19:55:31 2022 +0300

    scripts: imp docs

commit 12c4dabea2bac04601202a05d0c820ff2e32c93e
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Tue Feb 1 19:49:16 2022 +0300

    scripts: add link to platforms page
2022-02-07 20:10:11 +03:00
Ainar Garipov
3f06b02409 cherry-pick: all: imp ann url
Updates #4209.

Squashed commit of the following:

commit 0c31a59c5bf6bcc27a4779adf226d9a1ac9eece1
Merge: 803f32db 8455940b
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Tue Feb 1 19:33:55 2022 +0300

    Merge branch 'master' into 4209-ann-url

commit 803f32dbc7276077a4374ed0f5e0a1fa36f91c9b
Author: Ildar Kamalov <ik@adguard.com>
Date:   Tue Feb 1 14:46:47 2022 +0300

    client: add manual update link to update topline

commit ca375b52fa53503a3987b9723eb9a1d74878e890
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Mon Jan 31 20:49:42 2022 +0300

    all: imp ann url
2022-02-07 20:09:54 +03:00
Ildar Kamalov
5bf958ec6b cherry-pick: 4212 fix query log search results
Closes #4212.

Squashed commit of the following:

commit cd854e5bf71953c753c690c28b5571f2c8b1ea0f
Merge: 8532ca80 bf9b35b9
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Mon Jan 31 20:10:17 2022 +0300

    Merge branch 'master' into 4212-logs

commit 8532ca80d135e4c306ac4d0c999475d77ba51a02
Author: Ildar Kamalov <ik@adguard.com>
Date:   Mon Jan 31 19:22:52 2022 +0300

    fix lint

commit 1a85074180d95d7a7aad854c75a7a811aee719e9
Author: Ildar Kamalov <ik@adguard.com>
Date:   Mon Jan 31 19:14:54 2022 +0300

    client: fix query log search results
2022-02-07 20:09:27 +03:00
Ainar Garipov
959d9ff9a0 cherry-pick: client: upd manual upd link
Closes #4208.

Squashed commit of the following:

commit 4ae27b5f7cd6b0f4ec0c9041d92c4d1ac00dd622
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Mon Jan 31 18:34:18 2022 +0300

    client: upd manual upd link
2022-02-07 20:09:09 +03:00
Ainar Garipov
4813b4de25 all: upd chlog, minimize diff to master 2022-01-28 17:44:10 +03:00
Eugene Burkov
119100924c cherry-pick: 4216 simpl hosts
Merge in DNS/adguard-home from 4216-hosts-explode to master

Updates #4216.

Squashed commit of the following:

commit a6ed131923496d9bbd1d80c652d4584951528c4a
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Jan 27 19:11:23 2022 +0300

    aghnet: imp docs

commit 25cca065c3c6dc227288cdd0803dc3ff8f9c3ca4
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Jan 27 18:53:16 2022 +0300

    aghnet: simpl hosts container
2022-01-28 16:31:04 +03:00
Ainar Garipov
bd584de4ee cherry-pick: 4162 fix theme color
Updates #4162.

* commit '2263adbbe0c14cb914451d131d94ab6fd236852c':
  Update login.html
  Update install.html
  Update index.html
2022-01-28 16:30:35 +03:00
Ainar Garipov
ede85ab2f2 all: upd chlog 2022-01-25 14:12:12 +03:00
Ainar Garipov
12c20288e4 cherry-pick: client: upd i18n
Updates #2643.

Squashed commit of the following:

commit bd6bc0aeaa1bd928ae39642691b913befbc0f396
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Tue Jan 25 14:04:10 2022 +0300

    client: upd i18n
2022-01-25 14:11:12 +03:00
Ainar Garipov
5bbbf89c10 cherry-pick: all: upd dnsproxy
Merge in DNS/adguard-home from imp-logs to master

Squashed commit of the following:

commit bff4c3757b61db63320af72e1af56649f6f70a50
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Mon Jan 24 17:25:34 2022 +0300

    all: upd dnsproxy
2022-01-24 17:44:54 +03:00
Eugene Burkov
d55393ecd5 cherry-pick: client: upd i18n
Merge in DNS/adguard-home from upd-i18n to master

Squashed commit of the following:

commit e3dfb6cd66813d45591f74c9cdddab8b61143db3
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Jan 24 14:52:19 2022 +0300

    client: upd i18n
2022-01-24 17:41:04 +03:00
Eugene Burkov
2b5927306f cherry-pick: 2846 cover aghnet vol.1
Merge in DNS/adguard-home from 2846-cover-aghnet-vol.1 to master

Updates #2846.

Squashed commit of the following:

commit 368e75b0bacb290f9929b8a5a682b06f2d75df6a
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Jan 21 19:11:59 2022 +0300

    aghnet: imp tests

commit 8bb3e2a1680fd30294f7c82693891ffb19474c6a
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Jan 21 18:27:06 2022 +0300

    aghnet: rm unused test

commit 28d8e64880f845810d0af629e5d1f06b9bde5b28
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Jan 21 18:18:22 2022 +0300

    aghnet: cover with tests
2022-01-21 19:24:38 +03:00
Ainar Garipov
4f016b6ed7 all: upd chlog 2022-01-21 17:11:27 +03:00
Ildar Kamalov
3a2a6d10ec cherry-pick: 3971 fix client id error message
Updates #3971

Squashed commit of the following:

commit f6b855a16daaec7bfca1e1653b4b9c4180c2d80e
Merge: 0cb31dbb 5ec4a4da
Author: Ildar Kamalov <ik@adguard.com>
Date:   Thu Jan 20 18:19:20 2022 +0300

    Merge branch 'master' into 3971-client-id

commit 0cb31dbbea785fb5ba11a8efe2b6653aece7cd97
Author: Natalia Sokolova <n.sokolova@adguard.com>
Date:   Thu Jan 20 11:41:06 2022 +0300

    client/src/__locales/en.json edited online with Bitbucket

commit 7999f260d83adcb2fc8d5d5e40cb1934e0333873
Author: Ildar Kamalov <ik@adguard.com>
Date:   Wed Jan 19 15:58:18 2022 +0300

    client: fix client id error message
2022-01-21 17:08:07 +03:00
Eugene Burkov
2491426b09 cherry-pick: 4142 stats panic
Merge in DNS/adguard-home from 4142-stats-panic to master

Updates #4142.

Squashed commit of the following:

commit bf168f50ac86bdfdab73bf7285705f09f87b6c72
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Jan 20 17:13:41 2022 +0300

    stats: imp more

commit bb638211da7d0c51959ded2dacb72faea00befb4
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Jan 20 17:09:31 2022 +0300

    stats: imp code quality

commit 27ac52f15e4e0f4112ce7a6b47b03f963463393e
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Jan 20 17:00:09 2022 +0300

    stats: recover panic on init

commit 1ffcebbb9062438170b010e1c7bad3c6cef4cfc1
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Jan 20 14:19:01 2022 +0300

    all: fix some typos
2022-01-21 17:08:07 +03:00
Ildar Kamalov
5ebdd1390e cherry-pick: 4143 sort client ids
Merge in DNS/adguard-home from 4143-clients-sort to master

Updates #4143.

Squashed commit of the following:

commit a4b547eb46a54bdfdc7d342fab5f8ecfa54f5d06
Merge: d369c11c d82b2902
Author: Ildar Kamalov <ik@adguard.com>
Date:   Thu Jan 20 11:58:42 2022 +0300

    Merge branch 'master' into 4143-clients-sort

commit d369c11c69665510043f63e0283e1ca1b2974289
Author: Ildar Kamalov <ik@adguard.com>
Date:   Wed Jan 19 16:53:39 2022 +0300

    client: fix sort ip method

commit d767a1199c37ad9df7f3bc2d362d840b0226d836
Author: Ildar Kamalov <ik@adguard.com>
Date:   Wed Jan 19 16:23:23 2022 +0300

    client: sort client ids
2022-01-21 17:08:07 +03:00
Eugene Burkov
b7f0247575 cherry-pick: 4095 fix duplicating port
Merge in DNS/adguard-home from 4095-port-3000 to master

Updates #4095.

Squashed commit of the following:

commit 968cc806264898523d29c4ec20b3ce6a69abb09c
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Jan 19 20:26:33 2022 +0300

    home: fix typo

commit 03c6798db6a4ca726a7b5a683e475a8a74f79fe1
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Jan 19 20:20:34 2022 +0300

    all: more naming imps

commit d3d417fcb24a1859f53a743b3533faa81b6bef19
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Jan 19 20:10:14 2022 +0300

    aghalgo: rename into aghalg

commit 6e106006d07a747ff4ddf1271532106c3a3e2b20
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Jan 19 20:05:43 2022 +0300

    all: imp names, docs

commit 12c8d9fde0d0cc5b953da30b042171ba7c53da5d
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Jan 19 19:57:21 2022 +0300

    all: fix log of changes

commit 49c7a705b9b1ad8f2ef68fa807f9b6b8c447b421
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Jan 19 19:51:00 2022 +0300

    home: fix duplicating port 3000
2022-01-21 17:08:07 +03:00
Ainar Garipov
e28186a28a cherry-pick: scripts: imp sh lint 2022-01-21 17:08:03 +03:00
Eugene Burkov
de1a7ce48f cherry-pick: 4133 empty rewrite
Merge in DNS/adguard-home from 4133-empty-rewrite to master

Closes #4133.

Squashed commit of the following:

commit 4d2313c211c3955922d340c006b323c65e5e5ba4
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Jan 18 21:36:21 2022 +0300

    all: log changes

commit 5b8e392a2225c215fc117223d3f6553f8bdf21cd
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Jan 18 21:32:57 2022 +0300

    all: upd urlfilter
2022-01-21 17:02:42 +03:00
Ainar Garipov
48480fb33b cherry-pick: home: show version in install api
Closes #4026.

Squashed commit of the following:

commit bcd1315a10e819daee3aee323427d90a27860b4a
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Tue Jan 18 14:57:49 2022 +0300

    openapi: fix example

commit b56e27c5ac1fc7c3f595057d77607479d72ec50a
Author: Ildar Kamalov <ik@adguard.com>
Date:   Tue Jan 18 14:55:51 2022 +0300

    client: show version on install page

commit 95dfbfaa1235deef7b55e51457d11c677f6ef6b5
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Tue Jan 18 14:29:08 2022 +0300

    home: show version in install api
2022-01-21 16:59:57 +03:00
Eugene Burkov
f41332fe6b cherry-pick: 4120 service domain validation
Merge in DNS/adguard-home from 4120-fix-services to master

Closes #4120.

Squashed commit of the following:

commit ca2e5faf64f567cc6647a300181712236158e69d
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Jan 18 14:14:54 2022 +0300

    dnsforward: imp docs

commit 9ed5f536e691dcdee5b7c94e161c738d31ff8588
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Jan 18 13:50:33 2022 +0300

    dnsforward: fix reverse domain validation
2022-01-21 16:59:39 +03:00
Ainar Garipov
1f8b340b8f cherry-pick: all: upd dnsproxy
Updates #4065.

Squashed commit of the following:

commit d65d2e3a783910b9cb95c5bcfbcf1af11da666d5
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Mon Jan 17 18:47:17 2022 +0300

    all: upd dnsproxy
2022-01-21 16:57:39 +03:00
Eugene Burkov
fdaf1d09d3 cherry-pick: 4074 fix upstream test
Merge in DNS/adguard-home from 4074-upstream-test to master

Updates #4074.

Squashed commit of the following:

commit 0de155b1e175a892b259791ff6d6e6f351bcfcf2
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Jan 12 19:20:01 2022 +0500

    dnsforward: fix upstream test
2022-01-21 16:50:46 +03:00
Eugene Burkov
b9682c4f10 cherry-pick: 4079 fix hosts container aliases
Merge in DNS/adguard-home from 4079-hosts-again to master

Updates #4079.

Squashed commit of the following:

commit 6aa8cbf32e8e47ba46bf5fba7681a10b68b4bc01
Merge: 19dba371 34c95f99
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Jan 12 14:05:30 2022 +0500

    Merge branch 'master' into 4079-hosts-again

commit 19dba371cc30ab8b75b0116833f4ecf0ef0f182f
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Jan 12 14:05:20 2022 +0500

    aghnet: imp docs

commit 9f341eb8ee4ba8468240bc3eeeb4951a3f7f5e6d
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Jan 10 18:44:17 2022 +0500

    aghnet: fix races

commit fd66191c7637c8584711e5bb8186494327ce0f87
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Jan 6 17:21:14 2022 +0500

    aghnet: fix hosts container aliases
2022-01-21 16:48:17 +03:00
Eugene Burkov
69dcb4effd cherry-pick: 4046 darwin service message
Merge in DNS/adguard-home from 4046-log-dir to master

Closes #4046.

Squashed commit of the following:

commit 05140550b14f477f52487c575f56428ce9e6fa10
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Jan 5 17:54:11 2022 +0500

    all: add macOS service msg
2022-01-21 16:47:34 +03:00
Ainar Garipov
d50fd0ba91 all: upd chlog 2021-12-29 22:39:11 +03:00
Ainar Garipov
c2c7b4c731 cherry-pick: all: upd dnsproxy
Updates #4042.

Squashed commit of the following:

commit 7531b974a6142fafee825ce9ca2ea202619b95af
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Dec 29 22:01:54 2021 +0300

    all: upd dnsproxy
2021-12-29 22:38:22 +03:00
Ainar Garipov
952d5f3a3d all: fix release script 2021-12-29 19:20:26 +03:00
Ainar Garipov
3f126c9ec9 all: prepare chlog 2021-12-29 16:22:14 +03:00
Ainar Garipov
0be58ef918 all: imp chlog 2021-12-29 16:16:40 +03:00
Ainar Garipov
8f9053e2fc all: backport, prepare release 2021-12-29 16:09:01 +03:00
Ainar Garipov
68452e5330 cherry-pick: client: upd i18n
Updates #2643.

Squashed commit of the following:

commit bc3de579e00762bc2c4b62fb1f7ba73837c10bff
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Dec 29 15:37:29 2021 +0300

    client: upd si-lk i18n again

commit 2cd5436b6e8c1918855aff58dd0958fe47b47e90
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Dec 29 15:08:21 2021 +0300

    client: upd i18n
2021-12-29 16:03:34 +03:00
Ainar Garipov
2eacc46eaa cherry-pick: all: opt log levels more
Updates #3929.

Squashed commit of the following:

commit 0d4aadeff1c4de1440795faf83eb072c46392ff3
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Tue Dec 28 16:34:44 2021 +0300

    all: opt log levels more
2021-12-29 16:03:23 +03:00
Ainar Garipov
74dcc91ea7 cherry-pick: all: imp uniq validation err msgs
Updates #3975.

Squashed commit of the following:

commit f8578c2afb1bb5786e7b855a1715e0757bc08510
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Tue Dec 28 16:39:13 2021 +0300

    aghalgo: imp docs

commit d9fc625f7c4ede2cf4b0683ad5efd0ddf9b966b1
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Tue Dec 28 16:21:24 2021 +0300

    all: imp uniq validation err msgs
2021-12-29 16:03:03 +03:00
Ainar Garipov
dd7bf61323 cherry-pick: aghnet: fix ipset init errors
Updates #4027.

Squashed commit of the following:

commit 9ac0cc27ca94e630cc321c90b60b271499af4d9b
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Mon Dec 27 20:26:22 2021 +0300

    aghnet: fix ipset init errors
2021-12-29 16:02:50 +03:00
Ainar Garipov
2819d6cace cherry-pick: filtering: fix rw to subdomain
Updates #4016.

Squashed commit of the following:

commit 83bb15c5a5098103cd17e76b49f456fb4fa73408
Merge: 81905503 313555b1
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Mon Dec 27 19:36:44 2021 +0300

    Merge branch 'master' into 4016-rw-subdomain

commit 81905503c977c004d7ddca1d4e7537bf76443a6e
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Mon Dec 27 19:35:51 2021 +0300

    filtering: fix self reqs

commit b706f481f00232d28dade0bd747a7496753c7deb
Merge: 29cf83de 661f4ece
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Mon Dec 27 19:13:08 2021 +0300

    Merge branch 'master' into 4016-rw-subdomain

commit 29cf83de8e3ff60ea1c471c2a161055b1377392d
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Mon Dec 27 19:07:08 2021 +0300

    all: fix docs

commit 9213fd8ec2b81e65b1198ab241400065f14684b1
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Mon Dec 27 18:44:06 2021 +0300

    filtering: fix rw to subdomain
2021-12-29 16:02:04 +03:00
Eugene Burkov
75355a6883 cherry-pick: 3868 log freebsd reload fix
Merge in DNS/adguard-home from 3868-changelog to master

Squashed commit of the following:

commit 92ccf7422c4c1342c160e4806cbf9fb17c22749b
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Dec 27 19:22:47 2021 +0300

    all: log more changes
2021-12-29 16:01:31 +03:00
Eugene Burkov
e9c007d56b cherry-pick: 3868 imp service uninstall
Merge in DNS/adguard-home from 3868-imp-uninstall to master

Closes #3868.
Updates #3457.

Squashed commit of the following:

commit 6f50713407980c27e5b14bef4dc8839e134ec5c8
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Dec 27 19:06:13 2021 +0300

    all: imp openwrt

commit 59f058f8ec7f5ac8cb795bf837c396601652a6ff
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Dec 27 17:26:32 2021 +0300

    all: imp code && docs

commit bab95366b0ffa40d96de5bb8116ec14606e310ed
Merge: 92ebc210 52f36f20
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Dec 27 17:06:25 2021 +0300

    Merge branch 'master' into 3868-imp-uninstall

commit 92ebc210f04d5e02c3eef726017a0d5687f4bc4c
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Dec 27 13:18:58 2021 +0300

    home: imp freebsd script & log changes

commit 583ffc256e9f87cf19da2eca8bbefc9e00ea86cc
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Dec 16 14:08:46 2021 +0300

    all: imp service uninstall
2021-12-29 16:01:09 +03:00
Ainar Garipov
84c9085516 cherry-pick: filtering: restore rewrite behavior with other question types
Updates #4008.

Squashed commit of the following:

commit babbc29331cfc2603c0c3b0987f5ba926690ec3e
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Fri Dec 24 18:46:20 2021 +0300

    filtering: restore rewrite behavior with other question types
2021-12-24 22:41:10 +03:00
Ainar Garipov
9f36e57c1e cherry-pick: all: opt log levels
Updates #3929.

Squashed commit of the following:

commit bfb2361d81a0667c36193484ca125d08e5638b21
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Fri Dec 24 17:23:39 2021 +0300

    all: opt log levels
2021-12-24 22:41:10 +03:00
Eugene Burkov
7528699fc2 cherry-pick: 3987 Fix nil pointer dereference
Merge in DNS/adguard-home from 3987-fix-nil-deref to master

Updates #3987.
Updates #2846.

Squashed commit of the following:

commit d653e09ce88a8b10b2a17fea1563c419895c714c
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Dec 23 20:08:51 2021 +0300

    all: log changes

commit c47a4eeacf76fa7df2d01af166dee9d52528ac58
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Dec 23 19:22:39 2021 +0300

    aghnet: fix windows tests

commit 9c91f14ccfe967ada3c00ddb86d673238e52c12d
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Dec 23 19:09:49 2021 +0300

    aghnet: imp code readability, docs

commit d3df15d1892e4ebfe7f8ea7144e39a0c712fce52
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Dec 23 18:47:28 2021 +0300

    aghnet: fix nil pointer dereference
2021-12-24 22:41:10 +03:00
Eugene Burkov
d280151c18 cherry-pick: 3998 Make hosts rules match exactly
Merge in DNS/adguard-home from 3998-fix-hosts-gen to master

Closes #3998

Squashed commit of the following:

commit b565d51afb6c292dd16accd45b7d37ed386714e8
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Dec 23 16:25:02 2021 +0300

    aghnet: make hosts rules match exactly
2021-12-24 22:41:10 +03:00
Ainar Garipov
b44c755d25 cherry-pick: all: upd dnsproxy
Updates #3977.

Squashed commit of the following:

commit 3aaaacac102cdea04ae46b36d2dd3a3be7d50147
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Dec 23 16:15:11 2021 +0300

    all: upd dnsproxy
2021-12-24 22:41:05 +03:00
Ainar Garipov
e4078e87a1 cherry-pick: 3945 log success
Updates #3945.

* commit 'ebe86ce00ebca3431a96a44c3616af3ac42250ab':
  home: imp auth
  Log successful login attempts in addition to failed ones
2021-12-24 22:23:22 +03:00
Eugene Burkov
be36204756 cherry-pick: Update miekg/dns
Merge in DNS/adguard-home from upd-dns-lib to master

Updates #2275.

Squashed commit of the following:

commit 54d0485157ac4f08830ad7d8ca9be49eef87d678
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Dec 23 13:31:34 2021 +0300

    all: upd dns lib
2021-12-24 22:22:47 +03:00
Ainar Garipov
b5409d6d00 cherry-pick: client: imp en i18n
Merge in DNS/adguard-home from en-i18n-safe-browsing to master

Squashed commit of the following:

commit dd32a58c3761818a10386b4a1d9e6871da59c71e
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Dec 22 17:31:35 2021 +0300

    client: imp en i18n
2021-12-24 22:19:07 +03:00
Ainar Garipov
f3d6bce03e cherry-pick: scripts: add network-control plug
Updates #3976.

Squashed commit of the following:

commit 49d8a3a2d333c7896530c8a44c5ef06c396b5ae0
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Dec 22 16:20:45 2021 +0300

    scripts: add network-control plug
2021-12-24 22:18:19 +03:00
139 changed files with 3082 additions and 4123 deletions

View File

@@ -1,7 +1,7 @@
'name': 'build'
'env':
'GO_VERSION': '1.22.3'
'GO_VERSION': '1.21.8'
'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 tests'
- 'name': 'Run make ci'
'shell': 'bash'
'run': 'make VERBOSE=1 deps test go-bench go-fuzz'
'run': 'make VERBOSE=1 ci'
- '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.22.3'
'GO_VERSION': '1.21.8'
'on':
'push':

View File

@@ -14,132 +14,21 @@ and this project adheres to
<!--
## [v0.108.0] - TBA
## [v0.107.49] - 2024-04-24 (APPROX.)
## [v0.107.47] - 2024-04-03 (APPROX.)
See also the [v0.107.49 GitHub milestone][ms-v0.107.49].
See also the [v0.107.47 GitHub milestone][ms-v0.107.47].
[ms-v0.107.49]: https://github.com/AdguardTeam/AdGuardHome/milestone/84?closed=1
[ms-v0.107.47]: https://github.com/AdguardTeam/AdGuardHome/milestone/82?closed=1
NOTE: Add new changes BELOW THIS COMMENT.
-->
### Security
- Go version has been updated to prevent the possibility of exploiting the Go
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 was changed due to improve 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 inoperability.
- 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
<!--
NOTE: Add new changes ABOVE THIS COMMENT.
-->
## [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].
@@ -153,11 +42,11 @@ See also the [v0.107.46 GitHub milestone][ms-v0.107.46].
### Changed
- Private rDNS resolution (`dns.use_private_ptr_resolvers` in YAML
- 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
**NOTE:** Disabling private RDNS resolution behaves effectively the same as if
no private reverse DNS servers provided by user and by the OS.
### Fixed
@@ -2964,13 +2853,11 @@ See also the [v0.104.2 GitHub milestone][ms-v0.104.2].
<!--
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.49...HEAD
[v0.107.49]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.48...v0.107.49
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.47...HEAD
[v0.107.47]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.46...v0.107.46
-->
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.48...HEAD
[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
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.46...HEAD
[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

View File

@@ -1,57 +1,89 @@
# 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:
- 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.
## General recommendations
- 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].
Please don't:
- file issues about a particular filtering-rule list misbehaving. These are tracked through the [separate form for filtering issues][form].
* 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.
- 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.
* 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.
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
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.
## 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.
[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.
## Pull requests
### Follow the issue template
### Discuss your changes first
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.
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
We have a set of [code guidelines][hacking] that we expect the code to follow. Please make sure you follow it.
## 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.
[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`.
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.
### 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.

View File

@@ -1,56 +1,65 @@
# 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.22.3
GOTOOLCHAIN = go1.21.8
GPG_KEY = devteam@adguard.com
GPG_KEY_PASSPHRASE = not-a-real-password
NPM = npm
@@ -82,6 +82,8 @@ 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
@@ -96,10 +98,15 @@ 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
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
# 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
go-bench: ; $(ENV) "$(SHELL)" ./scripts/make/go-bench.sh
go-build: ; $(ENV) "$(SHELL)" ./scripts/make/go-build.sh

474
README.md
View File

@@ -1,75 +1,85 @@
&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>
### <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>
* [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>
To install with `curl` run the following command:
@@ -91,70 +101,95 @@ 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>
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="#alternative-methods" id="alternative-methods" name="alternative-methods">Alternative methods</a>
#### <a href="#docker" id="docker" name="docker">Docker</a>
#### <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>
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>
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].
### <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].
[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>
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" id="comparison" name="comparison">Comparing AdGuard Home to other solutions</a>
- Choose what exactly the server blocks and permits.
### <a href="#comparison-adguard-dns" id="comparison-adguard-dns" name="comparison-adguard-dns">How is this different from public AdGuard DNS servers?</a>
- Monitor your network activity.
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:
- Add your own custom filtering rules.
* Choose what exactly the server blocks and permits.
- **Most importantly, it's your own server, and you are the only one who's in control.**
* Monitor your network activity.
### <a href="#comparison-pi-hole" id="comparison-pi-hole" name="comparison-pi-hole">How does AdGuard Home compare to Pi-Hole</a>
* Add your own custom filtering rules.
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.
* **Most importantly, it's your own server, and you are the only one who's in
control.**
> [!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.
> [!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.
### <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.
| Feature | AdGuard&nbsp;Home | Pi-Hole |
|-------------------------------------------------------------------------|-------------------|-----------------------------------------------------------|
@@ -172,45 +207,68 @@ AdGuard Home provides a lot of features out-of-the-box with no need to install a
| Access settings (choose who can use AGH DNS) | ✅ | ❌ |
| Running [without root privileges][wiki-noroot] | ✅ | ❌ |
[wiki-noroot]: https://adguard-dns.io/kb/adguard-home/getting-started/#running-without-superuser
[wiki-noroot]: https://github.com/AdguardTeam/AdGuardHome/wiki/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="#prerequisites" id="prerequisites" name="prerequisites">Prerequisites</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>
Run `make init` to prepare the development environment.
You will need this to build AdGuard Home:
- [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.
* [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.
### <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:
@@ -220,22 +278,27 @@ 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
```
> [!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`.
**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`.
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:
@@ -249,9 +312,10 @@ 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='...'
@@ -259,39 +323,47 @@ 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/
@@ -299,120 +371,170 @@ When you need to debug the frontend without recompiling the production version e
[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>
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.
### <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.
[iss]: https://github.com/AdguardTeam/AdGuardHome/issues
### <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].
### <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].
[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>
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.
### <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.
[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>
Please note that these projects are not affiliated with AdGuard, but are made by third-party developers and fans.
<!--
TODO(a.garipov): Use reference links.
-->
- [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/).
- [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).
* [Browser Extension](https://github.com/satheshshiva/Adguard-Home-Browser-Ext) by
[@satheshshiva](https://github.com/satheshshiva/).
## <a href="#acknowledgments" id="acknowledgments" name="acknowledgments">Acknowledgments</a>
<!--
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:
- [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)
* [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)
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>
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.
## <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.
[privacy]: https://adguard.com/en/privacy/home.html

View File

@@ -1,13 +1,18 @@
# Security Policy
# Security Policy
## Reporting vulnerabilities
## Reporting a Vulnerability
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
1. Make sure that the message body contains a clear description of the vulnerability.
2. 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,8 +7,7 @@
# Make sure to sync any changes with the branch overrides below.
'variables':
'channel': 'edge'
'dockerFrontend': 'adguard/home-js-builder:1.1'
'dockerGo': 'adguard/go-builder:1.22.3--1'
'dockerGo': 'adguard/golang-ubuntu:8.1'
'stages':
- 'Build frontend':
@@ -41,14 +40,11 @@
'jobs':
- 'Publish to GitHub Releases'
# TODO(e.burkov): In jobs below find out why the explicit checkout is
# performed.
'Build frontend':
'artifacts':
- 'name': 'AdGuardHome frontend'
'pattern': 'build/**'
'shared': true
'required': true
'docker':
'image': '${bamboo.dockerFrontend}'
'image': '${bamboo.dockerGo}'
'volumes':
'${system.YARN_DIR}': '${bamboo.cacheYarn}'
'key': 'BF'
@@ -65,21 +61,19 @@
set -e -f -u -x
make\
VERBOSE=1\
js-deps js-build
# 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
'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':
@@ -99,6 +93,9 @@
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; }'\
@@ -111,6 +108,12 @@
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'
@@ -129,6 +132,13 @@
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 || :
@@ -149,7 +159,6 @@
# 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"\
@@ -249,7 +258,7 @@
'recipients':
- 'webhook':
'name': 'Build webhook'
'url': 'http://prod.jirahub.service.eu.consul/v1/webhook/bamboo?channel=adguard-qa-dns-builds'
'url': 'http://prod.jirahub.service.eu.consul/v1/webhook/bamboo?channel=adguard-qa'
'labels': []
'other':
@@ -265,8 +274,7 @@
# need to build a few of these.
'variables':
'channel': 'beta'
'dockerFrontend': 'adguard/home-js-builder:1.1'
'dockerGo': 'adguard/go-builder:1.22.3--1'
'dockerGo': 'adguard/golang-ubuntu:8.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]+':
@@ -281,5 +289,4 @@
# are the ones that actually get released.
'variables':
'channel': 'release'
'dockerFrontend': 'adguard/home-js-builder:1.1'
'dockerGo': 'adguard/go-builder:1.22.3--1'
'dockerGo': 'adguard/golang-ubuntu:8.1'

View File

@@ -10,7 +10,7 @@
# Make sure to sync any changes with the branch overrides below.
'variables':
'channel': 'edge'
'dockerSnap': 'adguard/snap-builder:1.1'
'dockerGo': 'adguard/golang-ubuntu:8.1'
'snapcraftChannel': 'edge'
'stages':
@@ -53,7 +53,7 @@
'shared': true
'required': true
'docker':
'image': '${bamboo.dockerSnap}'
'image': '${bamboo.dockerGo}'
'key': 'DR'
'other':
'clean-working-dir': true
@@ -99,7 +99,7 @@
'shared': true
'required': true
'docker':
'image': '${bamboo.dockerSnap}'
'image': '${bamboo.dockerGo}'
'key': 'BP'
'other':
'clean-working-dir': true
@@ -127,7 +127,7 @@
- 'artifact': 'armhf_snap'
- 'artifact': 'arm64_snap'
'docker':
'image': '${bamboo.dockerSnap}'
'image': '${bamboo.dockerGo}'
'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-dns-builds'
'url': 'http://prod.jirahub.service.eu.consul/v1/webhook/bamboo?channel=adguard-qa'
'labels': []
'other':
@@ -191,7 +191,7 @@
# need to build a few of these.
'variables':
'channel': 'beta'
'dockerSnap': 'adguard/snap-builder:1.1'
'dockerGo': 'adguard/golang-ubuntu:8.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'
'dockerSnap': 'adguard/snap-builder:1.1'
'dockerGo': 'adguard/golang-ubuntu:8.1'
'snapcraftChannel': 'candidate'

View File

@@ -5,8 +5,7 @@
'key': 'AHBRTSPECS'
'name': 'AdGuard Home - Build and run tests'
'variables':
'dockerFrontend': 'adguard/home-js-builder:1.1'
'dockerGo': 'adguard/go-builder:1.22.3--1'
'dockerGo': 'adguard/golang-ubuntu:8.1'
'channel': 'development'
'stages':
@@ -14,14 +13,7 @@
'manual': false
'final': false
'jobs':
- 'Test frontend'
- 'Test backend'
- 'Frontend':
manual: false
final: false
jobs:
- 'Build frontend'
- 'Test'
- 'Artifact':
manual: false
@@ -29,38 +21,14 @@
jobs:
- 'Artifact'
'Test frontend':
'docker':
'image': '${bamboo.dockerFrontend}'
'volumes':
'${system.YARN_DIR}': '${bamboo.cacheYarn}'
'key': 'JSTEST'
'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-lint js-test
'final-tasks':
- 'clean'
'requirements':
- 'adg-docker': 'true'
'Test backend':
'Test':
'docker':
'image': '${bamboo.dockerGo}'
'volumes':
'${system.YARN_DIR}': '${bamboo.cacheYarn}'
'${system.GO_CACHE_DIR}': '${bamboo.cacheGo}'
'${system.GO_PKG_CACHE_DIR}': '${bamboo.cacheGoPkg}'
'key': 'GOTEST'
'key': 'TEST'
'other':
'clean-working-dir': true
'tasks':
@@ -74,65 +42,13 @@
set -e -f -u -x
make\
GOMAXPROCS=1\
VERBOSE=1\
go-deps go-tools go-lint
make\
VERBOSE=1\
go-test
make VERBOSE=1 ci go-tools lint
'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':
@@ -154,13 +70,25 @@
make\
ARCH="amd64"\
CHANNEL=${bamboo.channel}\
FRONTEND_PREBUILT=1\
OS="windows darwin linux"\
PARALLELISM=1\
CHANNEL=${bamboo.channel}\
SIGN=0\
PARALLELISM=1\
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'
@@ -194,6 +122,5 @@
# 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'
'dockerGo': 'adguard/golang-ubuntu:8.1'
'channel': 'candidate'

View File

@@ -1,22 +1,18 @@
{
"client_settings": "إعدادات العميل",
"example_upstream_reserved": "من المنبع <0>لمجالات محددة</0>;",
"example_multiple_upstreams_reserved": "منابع متعددة <0>لمجالات محددة</0>;",
"example_upstream_comment": "تعليق.",
"upstream_parallel": "استخدم الاستعلامات المتوازية لتسريع عملية الحل عن طريق الاستعلام عن جميع الخوادم المنبع في وقت واحد.",
"parallel_requests": "الطلبات الموازية",
"load_balancing": "موازنة الأحمال",
"example_upstream_reserved": "يمكنك تحديد <0> DNS upstream لنطاق معين (نطاقات) </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": "أدخل عنوان IP واحد لكل سطر",
"local_ptr_placeholder": "أدخل عنوان خادم واحد لكل سطر",
"resolve_clients_title": "تفعيل التحليل العكسي لعناوين IP للعملاء",
"resolve_clients_desc": "حل عكسيًا لعناوين IP للعملاء في أسماء مضيفيهم عن طريق إرسال استعلامات PTR إلى أدوات الحل المقابلة (خوادم DNS الخاصة للعملاء المحليين ، والخوادم الأولية للعملاء الذين لديهم عناوين IP عامة).",
"use_private_ptr_resolvers_title": "استخدم محللات DNS العكسية الخاصة",
@@ -38,7 +34,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 غير صالح للبوابة",
@@ -67,16 +63,14 @@
"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": "إعادة تعيين كافة عقود الإيجار",
@@ -89,7 +83,7 @@
"form_enter_hostname": "أدخل اسم الhostname",
"error_details": "مزيد من التفاصيل حول الخطأ",
"response_details": "تفاصيل الاستجابة",
"request_details": "طلب التفاصيل",
"request_details": "تفاصيل الطلب",
"client_details": "تفاصيل العميل",
"details": "التفاصيل",
"back": "رجوع",
@@ -99,13 +93,13 @@
"filter": "فلتر",
"query_log": "سجل الQuery",
"compact": "المدمج",
"nothing_found": "لم يتم العثور على شيء",
"faq": "الأسئلة المتداولة",
"nothing_found": "لم يتم العثور علي شيء...",
"faq": "أسئلة مكررة",
"version": "الإصدار",
"address": "العنوان",
"address": "العناوين",
"protocol": "البروتوكول",
"on": "قيد التشغيل",
"off": "قيد الإيقاف",
"on": "ON",
"off": "OFF",
"copyright": "حقوق النشر",
"homepage": "الصفحة الرئيسية",
"report_an_issue": "الإبلاغ عن مشكلة",
@@ -120,8 +114,7 @@
"stats_malware_phishing": "حسر البرامج الضارة / والتصيّد",
"stats_adult": "حظر مواقع الويب الخاصة بالبالغين",
"stats_query_domain": "اعلى النطاقات التي تم الاستعلام عنها",
"for_last_hours": آخر {{count}} ساعة",
"for_last_hours_plural": "لآخر {{count}} ساعة",
"for_last_24_hours": أخر 24 ساعة",
"for_last_days": "لآخر {{value}} يوم",
"for_last_days_plural": "لآخر {{count}} ايام",
"stats_disabled": "تم تعطيل الإحصائيات. يمكنك تشغيله من <0> صفحة الإعدادات </0>.",
@@ -136,16 +129,13 @@
"no_upstreams_data_found": "لم يتم العثور على بيانات خوادم upstream",
"number_of_dns_query_days": "عدد استعلامات DNS التي تمت معالجتها لآخر {{count}} يوم",
"number_of_dns_query_days_plural": "عدد استعلامات DNS التي تمت معالجتها لآخر {{count}} أيام",
"number_of_dns_query_hours": "عدد استفسارات DNS التي تمت معالجتها لآخر {{count}} ساعة",
"number_of_dns_query_hours_plural": "عدد استعلامات DNS التي تمت معالجتها خلال آخر {{count}} ساعة",
"number_of_dns_query_24_hours": "عدد استعلامات DNS التي تمت معالجتها لآخر 24 ساعة",
"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> اعدادات.",
@@ -180,9 +170,8 @@
"enabled_parental_toast": "تفعيل الرقابة الأبوية",
"disabled_safe_search_toast": "تعطيل البحث الآمن",
"enabled_save_search_toast": "تفعيل البحث الآمن",
"updated_save_search_toast": "تم تحديث إعدادات البحث الآمن",
"enabled_table_header": "قيد التشغيل",
"name_table_header": "الاِسْم",
"enabled_table_header": "تمكين",
"name_table_header": "الاسم",
"list_url_table_header": "قائمة الروابط",
"rules_count_table_header": "عدد القواعد",
"last_time_updated_table_header": "آخر تحديث",
@@ -236,7 +225,6 @@
"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": "حظر",
@@ -244,8 +232,7 @@
"allow_this_client": "السماح لهذا العميل",
"block_for_this_client_only": "احجب هذا العميل فقط",
"unblock_for_this_client_only": "إلغاء حجب هذا العميل فقط",
"add_persistent_client": "إضافة كعميل دائم",
"time_table_header": "الوقت",
"time_table_header": "وقت",
"date": "التاريخ",
"domain_name_table_header": "اسم النطاق",
"domain_or_client": "الدومين أو العميل",
@@ -260,7 +247,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": "تحديث قواعد الفلترة المخصصة",
@@ -272,12 +259,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",
@@ -292,8 +279,6 @@
"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",
@@ -309,19 +294,7 @@
"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",
@@ -329,15 +302,14 @@
"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": "متعقب معروف",
@@ -348,14 +320,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": "خطوة",
@@ -425,9 +397,6 @@
"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",
@@ -467,7 +436,6 @@
"form_add_id": "أضافة معّرف",
"form_client_name": "ادخل اسم العميل",
"name": "اسم",
"client_name": "العميل {{id}}",
"client_global_settings": "استخدم إعدادات عالمية",
"client_deleted": "تم حذف العميل \"{{key}}\" بنجاح",
"client_added": "تم اضافة العميل \"{{key}}\" بنجاح",
@@ -476,7 +444,7 @@
"client_confirm_delete": "هل أنت متأكد من أنك تريد حذف العميل \"{{key}}\"?",
"list_confirm_delete": "هل أنت متأكد أنك تريد حذف هذه القائمة؟",
"auto_clients_title": "Runtime clients",
"auto_clients_desc": "معلومات حول عناوين IP للأجهزة التي تستخدم أو قد تستخدم AdGuard Home. يتم جمع هذه المعلومات من عدة مصادر، بما في ذلك ملفات المضيفين، و DNS العكسي، إلخ.",
"auto_clients_desc": "الأجهزة غير المدرجة في قائمة العملاء الدائمين الذين قد لا يزالون يستخدمون AdGuard Home",
"access_title": "إعدادات الوصول",
"access_desc": "هنا يمكنك ضبط قواعد الوصول لخادم AdGuard Home DNS",
"access_allowed_title": "العملاء المسموحين",
@@ -489,7 +457,6 @@
"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>.",
@@ -510,9 +477,7 @@
"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 المخصصة بسهولة لاسم نطاق معين.",
@@ -541,7 +506,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}} الأيام",
@@ -552,18 +517,14 @@
"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": "اضبط الفلاتر",
@@ -636,20 +597,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": "أدخل حجم ذاكرة التخزين المؤقت (بايت)",
@@ -660,14 +621,14 @@
"ttl_cache_validation": "يجب أن يكون الحد الأدنى لتجاوز TTL لذاكرة التخزين المؤقت أقل من أو يساوي الحد الأقصى",
"cache_optimistic": "متفائل التخزين المؤقت",
"cache_optimistic_desc": "اجعل AdGuard Home يستجيب من ذاكرة التخزين المؤقت حتى عندما تنتهي صلاحية الإدخالات وحاول أيضًا تحديثها.",
"filter_category_general": "عام",
"filter_category_security": "الأمان",
"filter_category_general": "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",
@@ -676,72 +637,16 @@
"filter_allowlist": "تحذير: سيؤدي هذا الإجراء أيضًا إلى استبعاد القاعدة \"{{disallowed_rule}}\" من قائمة العملاء المسموح لهم.",
"last_rule_in_allowlist": "لا يمكن منع هذا العميل لأن استبعاد القاعدة \"{{disallowed_rule}}\" سيؤدي إلى تعطيل قائمة \"العملاء المسموح لهم\".",
"use_saved_key": "استخدم المفتاح المحفوظ مسبقًا",
"parental_control": "الرِّقابة الأبوية",
"parental_control": "الرقابة الابويه",
"safe_browsing": "تصفح آمن",
"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": "أدخل التناوب بالساعات",
"served_from_cache": "{{value}} <i>(يتم تقديمه من ذاكرة التخزين المؤقت)</i>",
"form_error_password_length": "يجب أن تتكون كلمة المرور من {{value}} من الأحرف على الأقل",
"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": "السبت",
"upstream_dns_cache_configuration": "تهيئة ذاكرة التخزين المؤقت لنظام أسماء النطاقات المستقبلي",
"enable_upstream_dns_cache": "تمكين التخزين المؤقت لنظام أسماء النطاقات DNS لتكوين المنبع المخصص لهذا العميل",
"dns_cache_size": "حجم ذاكرة التخزين المؤقت لنظام أسماء النطاقات، بالبايت"
"saturday_short": "السبت"
}

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": "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_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_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": "Ř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.",
"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.",
"check_dhcp_servers": "Zkontrolovat DHCP servery",
"save_config": "Uložit konfiguraci",
"enabled_dhcp": "DHCP server zapnutý",

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-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_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_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": "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.",
"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.",
"check_dhcp_servers": "Søg efter DHCP-servere",
"save_config": "Gem opsætning",
"enabled_dhcp": "DHCP-server aktiveret",

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": "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_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_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": "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.",
"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.",
"check_dhcp_servers": "Auf DHCP-Server prüfen",
"save_config": "Konfiguration speichern",
"enabled_dhcp": "DHCP-Server aktiviert",

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": "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_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_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": "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.",
"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.",
"check_dhcp_servers": "Check for DHCP servers",
"save_config": "Save configuration",
"enabled_dhcp": "DHCP server enabled",

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 consultas PTR, SOA y NS privadas. La petición se considera privada si solicita un dominio ARPA que contiene una subred dentro de rangos IP privados, por ejemplo \"192.168.12.34\", y procede de un cliente con dirección privada. Si no se configura, AdGuard Home utiliza las direcciones de los resolvedores DNS predeterminados de tu sistema operativo, excepto las direcciones del propio AdGuard Home.",
"local_ptr_desc": "Los servidores DNS que AdGuard Home utiliza para 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_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": "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.",
"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.",
"check_dhcp_servers": "Comprobar si hay servidores DHCP",
"save_config": "Guardar configuración",
"enabled_dhcp": "Servidor DHCP habilitado",

View File

@@ -678,7 +678,7 @@
"use_saved_key": "Käytä aiemmin tallennettua avainta",
"parental_control": "Lapsilukko",
"safe_browsing": "Turvallinen selaus",
"served_from_cache_label": "Toimitettu välimuistista",
"served_from_cache": "{{value}} <i>(jaettu välimuistista)</i>",
"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 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_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_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": "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.",
"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.",
"check_dhcp_servers": "Rechercher les serveurs DHCP",
"save_config": "Sauvegarder la configuration",
"enabled_dhcp": "Serveur DHCP activé",

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 terblokir",
"stats_adult": "Situs dewasa terblokir",
"stats_malware_phishing": "Malware/phishing diblokir",
"stats_adult": "Situs dewasa diblokir",
"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": "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",
"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",
"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 dalam pengaturan <a>Filter</a>.",
"filters_block_toggle_hint": "Anda dapat menyiapkan aturan pemblokiran di pengaturan <a>Penyaringan</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",
@@ -291,7 +291,7 @@
"custom_ip": "Custom IP",
"blocking_ipv4": "Blokiran IPv4",
"blocking_ipv6": "Blokiran IPv6",
"blocked_response_ttl": "Respons TTL terblokir",
"blocked_response_ttl": "Respons TTL yang diblokir",
"blocked_response_ttl_desc": "Menentukan berapa detik klien harus menyimpan respons yang difilter dalam cache",
"form_enter_blocked_response_ttl": "Masukkan TTL respons yang diblokir (detik)",
"dnscrypt": "DNSCrypt",
@@ -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 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_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_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)",
@@ -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}}\" berhasil diblokir",
"client_unblocked": "Klien \"{{ip}}\" berhasil membuka blokir",
"client_blocked": "Klien \"{{ip}}\" sukses di blokir",
"client_unblocked": "Klien \"{{ip}}\" sukses di unblock",
"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": "Diblokir oleh Kontrol Orang Tua",
"blocked_threats": "Ancaman terblokir",
"blocked_adult_websites": "Diblok oleh Kontrol Orang tua",
"blocked_threats": "Blokir Ancaman",
"allowed": "Dibolehkan",
"filtered": "Tersaring",
"rewritten": "Tulis ulang",

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 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_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_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": "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.",
"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.",
"check_dhcp_servers": "Controlla la presenza di server DHCP",
"save_config": "Salva configurazione",
"enabled_dhcp": "Server DHCP attivo",
@@ -341,7 +341,7 @@
"list_label": "Elenco",
"unknown_filter": "Filtro sconosciuto {{filterId}}",
"known_tracker": "Tracciatore noto",
"install_welcome_title": "Benvenuto in AdGuard Home!",
"install_welcome_title": "Benvenuto nella Home di AdGuard!",
"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",
@@ -446,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": "Attendi per favore, AdGuard Home si sta aggiornando",
"processing_update": "Perfavore aspetta, 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",

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, SOA 및 NS 요청에 사용하는 DNS 서버입니다. 요청이 비공개 IP 범위 내의 서브넷(예: \"192.168.12.34\")을 포함하는 ARPA 도메인을 요청하고 비공개 IP 주소를 가진 클라이언트로부터 오는 경우 비공개로 간주됩니다. 설정지 않으면 AdGuard Home IP 주소를 제외한 OS의 기본 DNS 리졸버 사용니다.",
"local_ptr_desc": "AdGuard Home이 로컬 PTR 쿼리에 사용하는 DNS 서버입니다. 이러한 서버는 역방향 DNS를 사용하여 개인 IP 주소(예: '192.168.12.34')가 있는 클라이언트의 호스트 이름을 확인하는 데 사용됩니다. 설정지 않은 경우, AdGuard Home은 AdGuard Home의 주소를 제외하고 운영 체제의 기본 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": "사설 업스트림 서버, DHCP, / etc/hosts 등을 통해 사설 IP 주소가 포함된 ARPA 도메인에 대한 PTR, SOA 및 NS 요청을 처리합니다. 비활성화하면 AdGuard Home은 NXDOMAIN을 사용하여 이러한 모든 요청에 응답합니다.",
"use_private_ptr_resolvers_desc": "업스트림 서버를 사용해 로컬로 제공되는 주소의 역방향 DNS를 조회합니다. 끄는 경우, AdGuard Home은 DHCP, /etc/hosts 등에서 알려진 클라이언트를 제외한 모든 PTR 요청에 NXDOMAIN으로 응답합니다.",
"check_dhcp_servers": "DHCP 서버 체크",
"save_config": "구성 저장",
"enabled_dhcp": "DHCP 서버 활성화됨",

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": "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_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_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": "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.",
"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.",
"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": "Alles weergeven",
"show_filtered_type": "Gefilterde weergeven",
"show_all_filter_type": "Toon alles",
"show_filtered_type": "Toon gefilterde",
"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": "Alles deblokkeren",
"unblock_all": "Deblokkeer alles",
"encryption_certificate_path": "Certificaat pad",
"encryption_private_key_path": "Privé sleutel pad",
"encryption_certificates_source_path": "Certificaten bestandspad instellen",

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 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_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_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": "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.",
"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.",
"check_dhcp_servers": "Sprawdź serwery DHCP",
"save_config": "Zapisz konfigurację",
"enabled_dhcp": "Serwer DHCP włączony",
@@ -678,7 +678,7 @@
"use_saved_key": "Użyj wcześniej zapisanego klucza",
"parental_control": "Kontrola rodzicielska",
"safe_browsing": "Bezpieczne przeglądanie",
"served_from_cache_label": "Podano z pamięci podręcznej",
"served_from_cache": "{{value}} <i>(podawane z pamięci podręcznej)</i>",
"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 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_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_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": "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.",
"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.",
"check_dhcp_servers": "Verificar por servidores DHCP",
"save_config": "Salvar configuração",
"enabled_dhcp": "Servidor DHCP ativado",

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 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_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_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": "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.",
"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.",
"check_dhcp_servers": "Verificar por servidores DHCP",
"save_config": "Guardar definição",
"enabled_dhcp": "Servidor DHCP ativado",

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, SOA и NS-запросов. Запрос считается локальным, если он запрашивает информацию об ARPA-домене, подсеть которого в локальном IP-диапазоне (например, «192.168.12.34»), и если при этом запрос пришел от клиента с локальным адресом. Если значение не установлено, AdGuard Home использует адреса DNS-серверы по умолчанию в вашей ОС, за исключением адресов самого AdGuard Home.",
"local_ptr_desc": "DNS-серверы, которые AdGuard Home использует для локальных PTR-запросов. Эти серверы используются, чтобы получить доменные имена клиентов с приватными IP-адресами, например «192.168.12.34», с помощью обратного DNS. Если список пуст, AdGuard Home использует DNS-серверы по умолчанию вашей ОС.",
"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": "Посылать PTR, SOA и NS-запросы для ARPA-доменов, содержащих локальные адреса, с помощью указанных upstream-серверов, DHCP, /etc/hosts и так далее. Если отключено, AdGuard Home отвечает NXDOMAIN на все подобные запросы.",
"use_private_ptr_resolvers_desc": "Посылать обратные DNS-запросы для локально обслуживаемых адресов на указанные серверы. Если отключено, AdGuard Home будет отвечать NXDOMAIN на все подобные PTR-запросы, кроме запросов о клиентах, уже известных по DHCP, /etc/hosts и так далее.",
"check_dhcp_servers": "Проверить DHCP-серверы",
"save_config": "Сохранить конфигурацию",
"enabled_dhcp": "DHCP-сервер включён",

View File

@@ -13,14 +13,14 @@
"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": "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_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_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": "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.",
"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.",
"check_dhcp_servers": "Skontrolovať DHCP servery",
"save_config": "Uložiť konfiguráciu",
"enabled_dhcp": "DHCP server zapnutý",

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 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_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_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": "Ö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.",
"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.",
"check_dhcp_servers": "DHCP sunucularını denetle",
"save_config": "Yapılandırmayı kaydet",
"enabled_dhcp": "DHCP sunucusu etkinleştirildi",

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": "Клієнт",

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、SOA 和 NS 请求的 DNS 服务器。如果请求的 ARPA 域名包含私有 IP 范围内的子网(如 \"192.168.12.34\"),且请求来自具有私有 IP 地址的客户端,该请求被视为私有请求。如果设置,将使用操作系统的默认 DNS 解析器AdGuard Home IP 地址除外。",
"local_ptr_desc": "AdGuard Home 用于本地 PTR 查询的 DNS 服务器。这些服务器将使用反向 DNS 解析具有私人 IP 地址的客户机的主机名,比如 \"192.168.12.34\"。如果没有设置,除非是 AdGuard Home 里设置的地址AdGuard Home 都将自动使用您的操作系统的默认 DNS 解析器。",
"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": "使用私有上游服务器、DHCP、/etc/hosts 等解决包含私有 IP 地址的 ARPA 域名的 PTR、SOA 和 NS 请求。如果禁用AdGuard Home 以 NXDOMAIN 应所有此类请求。",
"use_private_ptr_resolvers_desc": "使用这些上游服务器对本地服务的地址执行反向 DNS 查找。 如果禁用,AdGuard Home 以 NXDOMAIN 应所有此类 PTR 请求,从 DHCP、/etc/hosts 等获知的客户端除外。",
"check_dhcp_servers": "检查 DHCP 服务器",
"save_config": "保存配置",
"enabled_dhcp": "DHCP 服务器已启用",

View File

@@ -1,7 +1,6 @@
{
"client_settings": "用戶端設定",
"example_upstream_reserved": "您可以<0>指定網域</0>使用特定 DNS 查詢",
"example_multiple_upstreams_reserved": "多個上游 <0>for 特定網域</0>;",
"example_upstream_comment": "您可以指定註解",
"upstream_parallel": "使用平行查詢,同時查詢所有上游伺服器來加速解析結果",
"parallel_requests": "平行處理",
@@ -9,9 +8,6 @@
"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}}",
@@ -41,19 +37,17 @@
"dhcp_ipv6_settings": "DHCP IPv6 設定",
"form_error_required": "必要欄位",
"form_error_ip4_format": "無效的 IPv4 格式",
"form_error_ip4_gateway_format": "閘道的 IPv4 位址無效",
"form_error_ip6_format": "無效的 IPv6 格式",
"form_error_ip_format": "無效的 IP 格式",
"form_error_ip4_gateway_format": "閘道的 IPv4 位址無效",
"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": "子網路遮罩",
@@ -74,9 +68,7 @@
"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 租約",
@@ -120,8 +112,7 @@
"stats_malware_phishing": "已封鎖惡意軟體/網路釣魚",
"stats_adult": "已封鎖成人網站",
"stats_query_domain": "熱門查詢網域排行",
"for_last_hours": "過去 {{count}} 小時",
"for_last_hours_plural": "在過去 {{count}} 小時裡",
"for_last_24_hours": "過去 24 小時",
"for_last_days": "最近 {{count}} 天內",
"for_last_days_plural": "最近 {{count}} 天內",
"stats_disabled": "已禁用統計資料。您可以從<0>設定頁面</0>打開它。",
@@ -132,20 +123,15 @@
"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_hours": "過去 {{count}} 小時處理的 DNS 查詢數",
"number_of_dns_query_hours_plural": "過去 {{count}} 小時處理的 DNS 查詢數量",
"number_of_dns_query_24_hours": "過去 24小時內 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>設定中設定封鎖規則。",
@@ -170,7 +156,6 @@
"upstream_dns_configured_in_file": "設定在 {{path}}",
"test_upstream_btn": "測試上游 DNS",
"upstreams": "上游",
"upstream": "上游伺服器",
"apply_btn": "套用",
"disabled_filtering_toast": "已停用過濾",
"enabled_filtering_toast": "已啟用過濾",
@@ -179,7 +164,6 @@
"disabled_parental_toast": "已停用家長監護",
"enabled_parental_toast": "已啟用家長監護",
"disabled_safe_search_toast": "已停用安全搜尋",
"enabled_save_search_toast": "已啟用安全搜尋",
"updated_save_search_toast": "已更新安全搜尋設定",
"enabled_table_header": "啟用",
"name_table_header": "名稱",
@@ -222,29 +206,24 @@
"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": "域名",
@@ -291,9 +270,6 @@
"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",
@@ -312,16 +288,6 @@
"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 位址",
@@ -425,9 +391,6 @@
"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 中的值",
@@ -467,7 +430,6 @@
"form_add_id": "新增識別碼",
"form_client_name": "輸入用戶端名稱",
"name": "名稱",
"client_name": "客戶端 {{id}}",
"client_global_settings": "使用全域設定",
"client_deleted": "已刪除「{{key}}」",
"client_added": "已新增「{{key}}」",
@@ -489,7 +451,6 @@
"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>。",
@@ -510,7 +471,6 @@
"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 覆寫",
@@ -562,8 +522,6 @@
"statistics_enable": "啟用統計數據",
"ignore_domains": "已忽略網域(每行一個)",
"ignore_domains_title": "已忽略網域",
"ignore_domains_desc_stats": "符合這些規則的查詢不會被計入統計資料中",
"ignore_domains_desc_query": "符合這些規則的查詢不會被寫入查詢記錄中",
"interval_hours": "{{count}} 小時",
"interval_hours_plural": "{{count}} 小時",
"filters_configuration": "過濾器設定",
@@ -673,19 +631,10 @@
"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_label": "由快取回應",
"served_from_cache": "{{value}} <i>(由快取回應)</i>",
"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}} 秒",
@@ -700,48 +649,11 @@
"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": "週六",
"upstream_dns_cache_configuration": "上游 DNS 快取設定",
"enable_upstream_dns_cache": "為此客戶端的自訂上游設定啟用 DNS 快取",
"dns_cache_size": "DNS 快取大小bytes"
"saturday_short": "週六"
}

View File

@@ -13,14 +13,14 @@
"fallback_dns_desc": "當上游 DNS 伺服器未回覆時被使用的應變 DNS 伺服器之清單。此語法與在上面主要上游欄位中的相同。",
"fallback_dns_placeholder": "每行輸入一個應變 DNS 伺服器",
"local_ptr_title": "私人反向的 DNS 伺服器",
"local_ptr_desc": "AdGuard Home 使用的 DNS 伺服器用於私人 PTR、SOA 和 NS 請求。如果請求要求包含私人 IP 範圍內的子網域的 ARPA 網域(例如 \"192.168.12.34\"),並來自具有私人 IP 位址的用戶端,該請求被視為私人。如果未設定,使用您的作業系統預設 DNS 解析器,但不包括 AdGuard Home 的 IP 位址。",
"local_ptr_desc": "AdGuard Home 用於區域指標PTR查詢之 DNS 伺服器。這些伺服器被用於解析有關在私人 IP 範圍的位址之區域指標查詢,例如\"192.168.12.34\",使用反向的 DNS。如果未設定,AdGuard Home 使用您的作業系統預設 DNS 解析器位址。",
"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": "使用私人上游伺服器、DHCP、/etc/hosts 等方式解析包含私人 IP 位址的 ARPA 網域的 PTR、SOA 和 NS 請求。如果禁用AdGuard Home 對所有此類請求以 NXDOMAIN 回。",
"use_private_ptr_resolvers_desc": "對於正使用這些上游伺服器之區域服務的位址執行反向的 DNS 查找。如果被禁用,除已知來自 DHCP、/etc/hosts 等等的用戶端之外AdGuard Home 對所有此類的區域指標PTR請求以 NXDOMAIN 回。",
"check_dhcp_servers": "檢查動態主機設定協定DHCP伺服器",
"save_config": "儲存配置",
"enabled_dhcp": "動態主機設定協定DHCP伺服器被啟用",

View File

@@ -142,12 +142,6 @@ 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",
@@ -196,12 +190,6 @@ 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",
@@ -238,12 +226,6 @@ 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",
@@ -304,12 +286,6 @@ 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",

25
go.mod
View File

@@ -1,10 +1,10 @@
module github.com/AdguardTeam/AdGuardHome
go 1.22.3
go 1.21.8
require (
github.com/AdguardTeam/dnsproxy v0.71.1
github.com/AdguardTeam/golibs v0.23.2
github.com/AdguardTeam/dnsproxy v0.66.0
github.com/AdguardTeam/golibs v0.20.2
github.com/AdguardTeam/urlfilter v0.18.0
github.com/NYTimes/gziphandler v1.1.1
github.com/ameshkov/dnscrypt/v2 v2.2.7
@@ -28,15 +28,14 @@ require (
// own code for that. Perhaps, use gopacket.
github.com/mdlayher/raw v0.1.0
github.com/miekg/dns v1.1.58
// TODO(a.garipov): Use release version.
github.com/quic-go/quic-go v0.42.1-0.20240424141022-12aa63824c7f
github.com/stretchr/testify v1.9.0
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.9
golang.org/x/crypto v0.22.0
golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8
golang.org/x/net v0.24.0
golang.org/x/sys v0.19.0
golang.org/x/crypto v0.21.0
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225
golang.org/x/net v0.22.0
golang.org/x/sys v0.18.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1
gopkg.in/yaml.v3 v3.0.1
howett.net/plist v1.0.1
@@ -59,9 +58,9 @@ require (
github.com/quic-go/qpack v0.4.0 // 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.17.0 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/mod v0.16.0 // indirect
golang.org/x/sync v0.6.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/tools v0.20.0 // indirect
golang.org/x/tools v0.19.0 // indirect
gonum.org/v1/gonum v0.14.0 // indirect
)

50
go.sum
View File

@@ -1,7 +1,7 @@
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/golibs v0.23.2 h1:rMjYantwtQ39e8G4zBQ6ZLlm4s3XH30Bc9VxhoOHwao=
github.com/AdguardTeam/golibs v0.23.2/go.mod h1:o9i55Sx6v7qogRQeqaBfmLbC/pZqeMBWi015U5PTDY0=
github.com/AdguardTeam/dnsproxy v0.66.0 h1:RyUbyDxRSXBFjVG1l2/4HV3I98DtfIgpnZkgXkgHKnc=
github.com/AdguardTeam/dnsproxy v0.66.0/go.mod h1:ZThEXbMUlP1RxfwtNW30ItPAHE6OF4YFygK8qjU/cvY=
github.com/AdguardTeam/golibs v0.20.2 h1:9gThBFyuELf2ohRnUNeQGQsVBYI7YslaRLUFwVaUj8E=
github.com/AdguardTeam/golibs v0.20.2/go.mod h1:/votX6WK1PdcZ3T2kBOPjPCGmfhlKixhI6ljYrFRPvI=
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=
@@ -101,19 +101,19 @@ 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.42.1-0.20240424141022-12aa63824c7f h1:L7x60Z6AW2giF/SvbDpMglGHJxtmFJV03khPwXLDScU=
github.com/quic-go/quic-go v0.42.1-0.20240424141022-12aa63824c7f/go.mod h1:132kz4kL3F9vxhW3CtQJLDVwcFe5wdWeJXXijhsO57M=
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/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.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
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/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.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
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/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=
@@ -131,26 +131,26 @@ 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.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8 h1:ESSUROHIBHg7USnszlcdmjBEwdMj9VUvU+OPk4yl2mc=
golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI=
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ=
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc=
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.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic=
golang.org/x/mod v0.16.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.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
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.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190322080309-f49334f85ddc/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -161,19 +161,17 @@ golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7w
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.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.18.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/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.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY=
golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg=
golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw=
golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc=
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=

View File

@@ -10,8 +10,29 @@ 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 := range 10 {
for i := 0; i < 10; i++ {
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 := range 10 {
for i := 0; i < 10; i++ {
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 := range 10 {
for i := 0; i < 10; i++ {
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 := range tc.count {
for i := 0; i < tc.count; i++ {
b.Append(i)
}

View File

@@ -11,7 +11,7 @@ func TestNewSortedMap(t *testing.T) {
var m SortedMap[string, int]
letters := []string{}
for i := range 10 {
for i := 0; i < 10; i++ {
r := string('a' + rune(i))
letters = append(letters, r)
}

View File

@@ -1,7 +1,10 @@
package aghnet
import (
"fmt"
"strings"
"github.com/AdguardTeam/golibs/stringutil"
)
// NormalizeDomain returns a lowercased version of host without the final dot,
@@ -16,3 +19,25 @@ 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

@@ -0,0 +1,59 @@
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 *container.MapSet[string],
srcSet *stringutil.Set,
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 := container.NewMapSet[string]()
srcSet := stringutil.NewSet()
var src []string
src, err = handlePatterns(fsys, srcSet, initial...)
if err != nil {
@@ -97,8 +97,6 @@ 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 *container.MapSet[string]
files *stringutil.Set
}
// 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: container.NewMapSet[string](),
files: stringutil.NewSet(),
}, nil
}

View File

@@ -159,11 +159,21 @@ 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,10 +13,26 @@ 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,6 +5,7 @@ package aghos
import (
"os"
"os/signal"
"syscall"
"golang.org/x/sys/windows"
)
@@ -42,10 +43,25 @@ 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,6 +78,7 @@ func TestWithDeferredCleanup(t *testing.T) {
}}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()

View File

@@ -91,6 +91,8 @@ func TestDefaultAddrProc_Process_rDNS(t *testing.T) {
}}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
@@ -184,6 +186,8 @@ 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,7 +7,6 @@ package client
import (
"encoding"
"fmt"
"net/netip"
"github.com/AdguardTeam/AdGuardHome/internal/whois"
)
@@ -57,9 +56,6 @@ 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
@@ -84,15 +80,6 @@ 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{}
@@ -146,8 +133,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
@@ -162,16 +149,11 @@ 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
}

View File

@@ -4,12 +4,8 @@ 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.
@@ -32,9 +28,6 @@ func macToKey(mac net.HardwareAddr) (key macKey) {
// 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
@@ -54,7 +47,6 @@ type Index struct {
// 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),
@@ -70,8 +62,6 @@ func (ci *Index) Add(c *Persistent) {
panic("client must contain uid")
}
ci.nameToUID[c.Name] = c.UID
for _, id := range c.ClientIDs {
ci.clientIDToUID[id] = c.UID
}
@@ -92,30 +82,15 @@ func (ci *Index) Add(c *Persistent) {
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)
return fmt.Errorf("another client %q uses the same ID %q", p.Name, id)
}
}
@@ -137,21 +112,6 @@ func (ci *Index) Clashes(c *Persistent) (err error) {
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) {
@@ -224,33 +184,21 @@ func (ci *Index) Find(id string) (c *Persistent, ok bool) {
mac, err := net.ParseMAC(id)
if err == nil {
return ci.FindByMAC(mac)
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.
// find 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) {
if pref.Contains(ip) {
uid, found = id, true
return false
@@ -266,8 +214,8 @@ func (ci *Index) findByIP(ip netip.Addr) (c *Persistent, found bool) {
return nil, false
}
// FindByMAC finds persistent client by MAC.
func (ci *Index) FindByMAC(mac net.HardwareAddr) (c *Persistent, found bool) {
// find 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 {
@@ -277,31 +225,9 @@ func (ci *Index) FindByMAC(mac net.HardwareAddr) (c *Persistent, found bool) {
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)
}
@@ -321,48 +247,3 @@ func (ci *Index) Delete(c *Persistent) {
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

@@ -22,7 +22,7 @@ func newIDIndex(m []*Persistent) (ci *Index) {
return ci
}
func TestClientIndex_Find(t *testing.T) {
func TestClientIndex(t *testing.T) {
const (
cliIPNone = "1.2.3.4"
cliIP1 = "1.1.1.1"
@@ -35,49 +35,26 @@ func TestClientIndex_Find(t *testing.T) {
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),
},
}
clients := []*Persistent{{
Name: "client1",
IPs: []netip.Addr{
netip.MustParseAddr(cliIP1),
netip.MustParseAddr(cliIPv6),
},
}, {
Name: "client2",
IPs: []netip.Addr{netip.MustParseAddr(cliIP2)},
Subnets: []netip.Prefix{netip.MustParsePrefix(cliSubnet)},
}, {
Name: "client_with_mac",
MACs: []net.HardwareAddr{mustParseMAC(cliMAC)},
}, {
Name: "client_with_id",
ClientIDs: []string{cliID},
}}
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 {
@@ -87,23 +64,19 @@ func TestClientIndex_Find(t *testing.T) {
}{{
name: "ipv4_ipv6",
ids: []string{cliIP1, cliIPv6},
want: clientWithBothFams,
want: clients[0],
}, {
name: "ipv4_subnet",
ids: []string{cliIP2, cliSubnetIP},
want: clientWithSubnet,
want: clients[1],
}, {
name: "mac",
ids: []string{cliMAC},
want: clientWithMAC,
want: clients[2],
}, {
name: "client_id",
ids: []string{cliID},
want: clientWithID,
}, {
name: "client_link_local_subnet",
ids: []string{linkLocalIP},
want: clientLinkLocal,
want: clients[3],
}}
for _, tc := range testCases {
@@ -248,103 +221,3 @@ func TestMACToKey(t *testing.T) {
_ = 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

@@ -12,10 +12,10 @@ import (
"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/netutil"
"github.com/AdguardTeam/golibs/stringutil"
"github.com/google/uuid"
)
@@ -64,7 +64,9 @@ type Persistent struct {
// upstream must be used.
UpstreamConfig *proxy.CustomUpstreamConfig
SafeSearch filtering.SafeSearch
// TODO(d.kolyshev): Make SafeSearchConf a pointer.
SafeSearchConf filtering.SafeSearchConfig
SafeSearch filtering.SafeSearch
// BlockedServices is the configuration of blocked services of a client.
BlockedServices *filtering.BlockedServices
@@ -93,13 +95,10 @@ type Persistent 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 *Persistent) SetTags(tags []string, known *container.MapSet[string]) {
func (c *Persistent) SetTags(tags []string, known *stringutil.Set) {
for _, t := range tags {
if !known.Has(t) {
log.Info("skipping unknown tag %q", t)

View File

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

@@ -1,85 +0,0 @@
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,7 +1,5 @@
package configmigrate
import "github.com/AdguardTeam/golibs/errors"
// migrateTo15 performs the following changes:
//
// # BEFORE:
@@ -45,7 +43,7 @@ func migrateTo15(diskConf yobj) (err error) {
}
diskConf["querylog"] = qlog
return errors.Join(
return coalesceError(
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,7 +1,5 @@
package configmigrate
import "github.com/AdguardTeam/golibs/errors"
// migrateTo24 performs the following changes:
//
// # BEFORE:
@@ -30,7 +28,7 @@ func migrateTo24(diskConf yobj) (err error) {
diskConf["schema_version"] = 24
logObj := yobj{}
err = errors.Join(
err = coalesceError(
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,7 +1,5 @@
package configmigrate
import "github.com/AdguardTeam/golibs/errors"
// migrateTo26 performs the following changes:
//
// # BEFORE:
@@ -80,7 +78,7 @@ func migrateTo26(diskConf yobj) (err error) {
}
filteringObj := yobj{}
err = errors.Join(
err = coalesceError(
moveSameVal[bool](dns, filteringObj, "filtering_enabled"),
moveSameVal[int](dns, filteringObj, "filters_update_interval"),
moveSameVal[bool](dns, filteringObj, "parental_enabled"),

View File

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

View File

@@ -50,3 +50,19 @@ 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

@@ -5,12 +5,10 @@ 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"
@@ -18,19 +16,22 @@ 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 *container.MapSet[netip.Addr]
blockedIPs *container.MapSet[netip.Addr]
allowedIPs map[netip.Addr]unit
blockedIPs map[netip.Addr]unit
allowedClientIDs *container.MapSet[string]
blockedClientIDs *container.MapSet[string]
allowedClientIDs *stringutil.Set
blockedClientIDs *stringutil.Set
// TODO(s.chzhen): Use [aghnet.IgnoreEngine].
blockedHostsEng *urlfilter.DNSEngine
// TODO(a.garipov): Create a type for an efficient tree set of IP networks.
// TODO(a.garipov): Create a type for a set of IP networks.
allowedNets []netip.Prefix
blockedNets []netip.Prefix
}
@@ -39,15 +40,15 @@ type accessManager struct {
// which may be an IP address, a CIDR, or a ClientID.
func processAccessClients(
clientStrs []string,
ips *container.MapSet[netip.Addr],
ips map[netip.Addr]unit,
nets *[]netip.Prefix,
clientIDs *container.MapSet[string],
clientIDs *stringutil.Set,
) (err error) {
for i, s := range clientStrs {
var ip netip.Addr
var ipnet netip.Prefix
if ip, err = netip.ParseAddr(s); err == nil {
ips.Add(ip)
ips[ip] = unit{}
} else if ipnet, err = netip.ParsePrefix(s); err == nil {
*nets = append(*nets, ipnet)
} else {
@@ -66,11 +67,11 @@ func processAccessClients(
// newAccessCtx creates a new accessCtx.
func newAccessCtx(allowed, blocked, blockedHosts []string) (a *accessManager, err error) {
a = &accessManager{
allowedIPs: container.NewMapSet[netip.Addr](),
blockedIPs: container.NewMapSet[netip.Addr](),
allowedIPs: map[netip.Addr]unit{},
blockedIPs: map[netip.Addr]unit{},
allowedClientIDs: container.NewMapSet[string](),
blockedClientIDs: container.NewMapSet[string](),
allowedClientIDs: stringutil.NewSet(),
blockedClientIDs: stringutil.NewSet(),
}
err = processAccessClients(allowed, a.allowedIPs, &a.allowedNets, a.allowedClientIDs)
@@ -108,7 +109,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 a.allowedIPs.Len() != 0 || a.allowedClientIDs.Len() != 0 || len(a.allowedNets) != 0
return len(a.allowedIPs) != 0 || a.allowedClientIDs.Len() != 0 || len(a.allowedNets) != 0
}
// isBlockedClientID returns true if the ClientID should be blocked.
@@ -151,15 +152,12 @@ func (a *accessManager) isBlockedIP(ip netip.Addr) (blocked bool, rule string) {
ipnets = a.allowedNets
}
if ips.Has(ip) {
if _, ok := ips[ip]; ok {
return blocked, ip.String()
}
for _, ipnet := range ipnets {
// Remove zone before checking because prefixes stip zones.
//
// TODO(d.kolyshev): Cover with tests.
if ipnet.Contains(ip.WithZone("")) {
if ipnet.Contains(ip) {
return blocked, ipnet.String()
}
}
@@ -178,9 +176,9 @@ func (s *Server) accessListJSON() (j accessListJSON) {
defer s.serverLock.RUnlock()
return accessListJSON{
AllowedClients: slices.Clone(s.conf.AllowedClients),
DisallowedClients: slices.Clone(s.conf.DisallowedClients),
BlockedHosts: slices.Clone(s.conf.BlockedHosts),
AllowedClients: stringutil.CloneSlice(s.conf.AllowedClients),
DisallowedClients: stringutil.CloneSlice(s.conf.DisallowedClients),
BlockedHosts: stringutil.CloneSlice(s.conf.BlockedHosts),
}
}

View File

@@ -1,116 +0,0 @@
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

@@ -1,299 +0,0 @@
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

@@ -110,6 +110,46 @@ 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,7 +19,6 @@ 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"
@@ -235,18 +234,9 @@ type DNSCryptConfig struct {
// ServerConfig represents server configuration.
// The zero ServerConfig is empty and ready for use.
type ServerConfig struct {
// 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
UDPListenAddrs []*net.UDPAddr // UDP listen address
TCPListenAddrs []*net.TCPAddr // TCP listen address
UpstreamConfig *proxy.UpstreamConfig // Upstream DNS servers config
// AddrProcConf defines the configuration for the client IP processor.
// If nil, [client.EmptyAddrProc] is used.
@@ -315,28 +305,24 @@ 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,
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,
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,
}
if srvConf.EDNSClientSubnet.UseCustom {
@@ -465,58 +451,36 @@ 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 *container.MapSet[netip.AddrPort],
unspecPorts *container.MapSet[uint16],
addrs map[netip.AddrPort]unit,
unspecPorts map[uint16]unit,
) {
if addrPort == (netip.AddrPort{}) {
return
}
addrs.Add(addrPort)
addrs[addrPort] = unit{}
if addrPort.Addr().IsUnspecified() {
unspecPorts.Add(addrPort.Port())
unspecPorts[addrPort.Port()] = unit{}
}
}
// collectDNSAddrs returns configured set of listening addresses. It also
// returns a set of ports of each unspecified listening address.
func (conf *ServerConfig) collectDNSAddrs() (
addrs *container.MapSet[netip.AddrPort],
unspecPorts *container.MapSet[uint16],
) {
addrs = container.NewMapSet[netip.AddrPort]()
unspecPorts = container.NewMapSet[uint16]()
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{}
for _, laddr := range conf.TCPListenAddrs {
collectListenAddr(laddr.AddrPort(), addrs, unspecPorts)
@@ -547,12 +511,26 @@ 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 container.SliceSet when available.
ports *container.MapSet[uint16]
addrs *container.MapSet[netip.Addr]
// TODO(e.burkov): Use sorted slices in combination with binary search.
ports map[uint16]unit
addrs []netip.Addr
}
// type check
@@ -560,11 +538,13 @@ var _ addrPortSet = (*combinedAddrPortSet)(nil)
// Has implements the [addrPortSet] interface for [*combinedAddrPortSet].
func (m *combinedAddrPortSet) Has(addrPort netip.AddrPort) (ok bool) {
return m.ports.Has(addrPort.Port()) && m.addrs.Has(addrPort.Addr())
_, ok = m.ports[addrPort.Port()]
return ok && slices.Contains(m.addrs, addrPort.Addr())
}
// filterOutAddrs filters out all the upstreams that match um. It returns all
// the closing errors joined.
// filterOut 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) {
@@ -598,11 +578,11 @@ func filterOutAddrs(upsConf *proxy.UpstreamConfig, set addrPortSet) (err error)
func (conf *ServerConfig) ourAddrsSet() (m addrPortSet, err error) {
addrs, unspecPorts := conf.collectDNSAddrs()
switch {
case addrs.Len() == 0:
case len(addrs) == 0:
log.Debug("dnsforward: no listen addresses")
return emptyAddrPortSet{}, nil
case unspecPorts.Len() == 0:
case len(unspecPorts) == 0:
log.Debug("dnsforward: filtering out addresses %s", addrs)
return addrs, nil
@@ -618,7 +598,7 @@ func (conf *ServerConfig) ourAddrsSet() (m addrPortSet, err error) {
return &combinedAddrPortSet{
ports: unspecPorts,
addrs: container.NewMapSet(ifaceAddrs...),
addrs: ifaceAddrs,
}, nil
}
}

View File

@@ -3,6 +3,7 @@ package dnsforward
import (
"net"
"testing"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/aghtest"
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
@@ -10,7 +11,6 @@ import (
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/testutil"
"github.com/miekg/dns"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -64,8 +64,6 @@ 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."
@@ -254,33 +252,33 @@ func TestServer_HandleDNSRequest_dns64(t *testing.T) {
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}
resp := (&dns.Msg{
Answer: []dns.RR{localRR},
}).SetReply(m)
require.NoError(t, w.WriteMsg(resp))
})
localUpsAddr := aghtest.StartLocalhostUpstream(t, localUpsHdlr).String()
client := &dns.Client{
Net: string(proxy.ProtoTCP),
Timeout: testTimeout,
Net: "tcp",
Timeout: 1 * time.Second,
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
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]
resp := (&dns.Msg{
Answer: answer[sectionAnswer],
Ns: answer[sectionAuthority],
Extra: answer[sectionAdditional],
}).SetReply(req)
require.NoError(pt, w.WriteMsg(resp))
})
@@ -310,54 +308,10 @@ func TestServer_HandleDNSRequest_dns64(t *testing.T) {
req := (&dns.Msg{}).SetQuestion(tc.qname, tc.qtype)
resp, _, excErr := client.Exchange(req, s.proxy().Addr(proxy.ProtoTCP).String())
resp, _, excErr := client.Exchange(req, s.dnsProxy.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,7 +2,6 @@
package dnsforward
import (
"cmp"
"context"
"fmt"
"io"
@@ -16,6 +15,7 @@ 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,6 +135,12 @@ 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
@@ -152,6 +158,12 @@ 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).
@@ -200,6 +212,14 @@ 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
//
@@ -236,6 +256,7 @@ 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,
@@ -287,13 +308,13 @@ func (s *Server) WriteDiskConfig(c *Config) {
sc := s.conf.Config
*c = sc
c.RatelimitWhitelist = slices.Clone(sc.RatelimitWhitelist)
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.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.TrustedProxies = slices.Clone(sc.TrustedProxies)
c.UpstreamDNS = slices.Clone(sc.UpstreamDNS)
c.UpstreamDNS = stringutil.CloneSlice(sc.UpstreamDNS)
}
// LocalPTRResolvers returns the current local PTR resolver configuration.
@@ -301,7 +322,7 @@ func (s *Server) LocalPTRResolvers() (localPTRResolvers []string) {
s.serverLock.RLock()
defer s.serverLock.RUnlock()
return slices.Clone(s.conf.LocalPTRResolvers)
return stringutil.CloneSlice(s.conf.LocalPTRResolvers)
}
// AddrProcConfig returns the current address processing configuration. Only
@@ -345,7 +366,6 @@ 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)
@@ -366,23 +386,25 @@ func (s *Server) Exchange(ip netip.Addr) (host string, ttl time.Duration, err er
}
dctx := &proxy.DNSContext{
Proto: proxy.ProtoUDP,
Req: req,
IsPrivateClient: true,
Proto: "udp",
Req: req,
}
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"
dctx.RequestedPrivateRDNS = netip.PrefixFrom(ip, ip.BitLen())
s.recDetector.add(*req)
} else {
resolver = s.internalProxy
errMsg = "resolving an address: %w"
}
if err = s.internalProxy.Resolve(dctx); err != nil {
if err = resolver.Resolve(dctx); err != nil {
return "", 0, fmt.Errorf(errMsg, err)
}
@@ -451,6 +473,103 @@ 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
}
// LocalResolversError is an error type for errors during local resolvers setup.
// This is only needed to distinguish these errors from errors returned by
// creating the proxy.
type LocalResolversError struct {
Err error
}
// type check
var _ error = (*LocalResolversError)(nil)
// Error implements the error interface for *LocalResolversError.
func (err *LocalResolversError) Error() (s string) {
return fmt.Sprintf("creating local resolvers: %s", err.Err)
}
// type check
var _ errors.Wrapper = (*LocalResolversError)(nil)
// Unwrap implements the [errors.Wrapper] interface for *LocalResolversError.
func (err *LocalResolversError) Unwrap() error {
return err.Err
}
// setupLocalResolvers initializes and sets the resolvers for local addresses.
// It assumes s.serverLock is locked or s not running. It returns the upstream
// configuration used for private PTR resolving, or nil if it's disabled. Note,
// that it's safe to put nil into [proxy.Config.PrivateRDNSUpstreamConfig].
func (s *Server) setupLocalResolvers(boot upstream.Resolver) (uc *proxy.UpstreamConfig, err error) {
if !s.conf.UsePrivateRDNS {
// It's safe to put nil into [proxy.Config.PrivateRDNSUpstreamConfig].
return nil, nil
}
uc, err = s.prepareLocalResolvers(boot)
if err != nil {
// Don't wrap the error because it's informative enough as is.
return nil, err
}
localResolvers, err := proxy.New(&proxy.Config{
UpstreamConfig: uc,
})
if err != nil {
return nil, &LocalResolversError{Err: err}
}
s.localResolvers = localResolvers
// TODO(e.burkov): Should we also consider the DNS64 usage?
return uc, nil
}
// Prepare initializes parameters of s using data from conf. conf must not be
// nil.
func (s *Server) Prepare(conf *ServerConfig) (err error) {
@@ -467,7 +586,7 @@ func (s *Server) Prepare(conf *ServerConfig) (err error) {
s.initDefaultSettings()
err = s.prepareInternalDNS()
boot, err := s.prepareInternalDNS()
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return err
@@ -489,6 +608,12 @@ func (s *Server) Prepare(conf *ServerConfig) (err error) {
return fmt.Errorf("preparing access: %w", err)
}
// TODO(e.burkov): Remove once the local resolvers logic moved to dnsproxy.
proxyConfig.PrivateRDNSUpstreamConfig, err = s.setupLocalResolvers(boot)
if err != nil {
return fmt.Errorf("setting up resolvers: %w", err)
}
proxyConfig.Fallbacks, err = s.setupFallbackDNS()
if err != nil {
return fmt.Errorf("setting up fallback dns servers: %w", err)
@@ -501,6 +626,8 @@ func (s *Server) Prepare(conf *ServerConfig) (err error) {
s.dnsProxy = dnsProxy
s.recDetector.clear()
s.setupAddrProc()
s.registerHandlers()
@@ -508,127 +635,36 @@ func (s *Server) Prepare(conf *ServerConfig) (err error) {
return 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.conf.loadUpstreams()
if err != nil {
return fmt.Errorf("loading upstreams: %w", err)
}
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
// 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) prepareLocalResolvers() (uc *proxy.UpstreamConfig, err error) {
if !s.conf.UsePrivateRDNS {
return nil, nil
func (s *Server) prepareInternalDNS() (boot upstream.Resolver, err error) {
err = s.prepareIpsetListSettings()
if err != nil {
return nil, fmt.Errorf("preparing ipset settings: %w", err)
}
var ownAddrs addrPortSet
ownAddrs, err = s.conf.ourAddrsSet()
s.bootstrap, s.bootResolvers, err = s.createBootstrap(s.conf.BootstrapDNS, &upstream.Options{
Timeout: DefaultTimeout,
HTTPVersions: UpstreamHTTPVersions(s.conf.UseHTTP3Upstreams),
})
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 err
}
s.conf.PrivateRDNSUpstreamConfig, err = s.prepareLocalResolvers()
if err != nil {
return err
return s.bootstrap, err
}
err = s.prepareInternalProxy()
if err != nil {
return fmt.Errorf("preparing internal proxy: %w", err)
return s.bootstrap, fmt.Errorf("preparing internal proxy: %w", err)
}
return nil
return s.bootstrap, nil
}
// setupFallbackDNS initializes the fallback DNS servers.
@@ -707,16 +743,10 @@ func validateBlockingMode(
func (s *Server) prepareInternalProxy() (err error) {
srvConf := s.conf
conf := &proxy.Config{
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,
CacheEnabled: true,
CacheSizeBytes: 4096,
UpstreamConfig: srvConf.UpstreamConfig,
MaxGoroutines: s.conf.MaxGoroutines,
}
err = setProxyUpstreamMode(conf, srvConf.UpstreamMode, srvConf.FastestTimeout.Duration)
@@ -752,6 +782,11 @@ func (s *Server) stopLocked() (err error) {
}
}
logCloserErr(s.internalProxy.UpstreamConfig, "dnsforward: closing internal resolvers: %s")
if s.localResolvers != nil {
logCloserErr(s.localResolvers.UpstreamConfig, "dnsforward: closing local resolvers: %s")
}
for _, b := range s.bootResolvers {
logCloserErr(b, "dnsforward: closing bootstrap %s: %s", b.Address())
}
@@ -873,5 +908,5 @@ func (s *Server) IsBlockedClient(ip netip.Addr, clientID string) (blocked bool,
blocked = true
}
return blocked, cmp.Or(rule, clientID)
return blocked, aghalg.Coalesce(rule, clientID)
}

View File

@@ -1,7 +1,7 @@
package dnsforward
import (
"cmp"
"context"
"crypto/ecdsa"
"crypto/rand"
"crypto/rsa"
@@ -21,6 +21,7 @@ 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"
@@ -189,7 +190,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 cmp.Or(
return aghalg.Coalesce(
aghtest.MatchedResponse(req, dns.TypeA, googleDomainName, "8.8.8.8"),
new(dns.Msg).SetRcode(req, dns.RcodeNameError),
), nil
@@ -252,7 +253,7 @@ func sendTestMessagesAsync(t *testing.T, conn *dns.Conn) {
wg := &sync.WaitGroup{}
for range testMessagesCount {
for i := 0; i < testMessagesCount; i++ {
msg := createGoogleATestMessage()
wg.Add(1)
@@ -275,7 +276,7 @@ func sendTestMessagesAsync(t *testing.T, conn *dns.Conn) {
func sendTestMessages(t *testing.T, conn *dns.Conn) {
t.Helper()
for i := range testMessagesCount {
for i := 0; i < testMessagesCount; i++ {
req := createGoogleATestMessage()
err := conn.WriteMsg(req)
assert.NoErrorf(t, err, "cannot write message #%d: %s", i, err)
@@ -490,10 +491,19 @@ 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,
Enabled: true,
Google: true,
Yandex: true,
CustomResolver: resolver,
}
filterConf := &filtering.Config{
@@ -530,6 +540,7 @@ func TestSafeSearch(t *testing.T) {
client := &dns.Client{}
yandexIP := netip.AddrFrom4([4]byte{213, 180, 193, 56})
googleIP, _ := aghtest.HostToIPs("forcesafesearch.google.com")
testCases := []struct {
host string
@@ -553,19 +564,19 @@ func TestSafeSearch(t *testing.T) {
wantCNAME: "",
}, {
host: "www.google.com.",
want: netip.Addr{},
want: googleIP,
wantCNAME: "forcesafesearch.google.com.",
}, {
host: "www.google.com.af.",
want: netip.Addr{},
want: googleIP,
wantCNAME: "forcesafesearch.google.com.",
}, {
host: "www.google.be.",
want: netip.Addr{},
want: googleIP,
wantCNAME: "forcesafesearch.google.com.",
}, {
host: "www.google.by.",
want: netip.Addr{},
want: googleIP,
wantCNAME: "forcesafesearch.google.com.",
}}
@@ -582,15 +593,12 @@ 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[0])
assert.Equal(t, net.IP(tc.want.AsSlice()), a.A)
}
a := testutil.RequireTypeAssert[*dns.A](t, reply.Answer[len(reply.Answer)-1])
assert.Equal(t, net.IP(tc.want.AsSlice()), a.A)
})
}
}
@@ -683,7 +691,7 @@ func TestServerCustomClientUpstream(t *testing.T) {
ups := aghtest.NewUpstreamMock(func(req *dns.Msg) (resp *dns.Msg, err error) {
atomic.AddUint32(&upsCalledCounter, 1)
return cmp.Or(
return aghalg.Coalesce(
aghtest.MatchedResponse(req, dns.TypeA, "host", "192.168.0.1"),
new(dns.Msg).SetRcode(req, dns.RcodeNameError),
), nil
@@ -1144,7 +1152,7 @@ func TestRewrite(t *testing.T) {
}))
ups := aghtest.NewUpstreamMock(func(req *dns.Msg) (resp *dns.Msg, err error) {
return cmp.Or(
return aghalg.Coalesce(
aghtest.MatchedResponse(req, dns.TypeA, "example.org", "4.3.2.1"),
new(dns.Msg).SetRcode(req, dns.RcodeNameError),
), nil
@@ -1473,7 +1481,7 @@ func TestServer_Exchange(t *testing.T) {
require.NoError(t, err)
extUpsHdlr := dns.HandlerFunc(func(w dns.ResponseWriter, req *dns.Msg) {
resp := cmp.Or(
resp := aghalg.Coalesce(
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),
@@ -1487,7 +1495,7 @@ func TestServer_Exchange(t *testing.T) {
require.NoError(t, err)
locUpsHdlr := dns.HandlerFunc(func(w dns.ResponseWriter, req *dns.Msg) {
resp := cmp.Or(
resp := aghalg.Coalesce(
aghtest.MatchedResponse(req, dns.TypePTR, revLocIPv4, dns.Fqdn(localDomainHost)),
new(dns.Msg).SetRcode(req, dns.RcodeNameError),
)

View File

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

View File

@@ -1,17 +1,57 @@
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) {
@@ -31,7 +71,6 @@ 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)
@@ -40,15 +79,22 @@ func (s *Server) filterDNSRequest(dctx *dnsContext) (res *filtering.Result, err
// TODO(a.garipov): Make CheckHost return a pointer.
res = &resVal
switch {
case isRewrittenCNAME(res):
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:
// 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.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):
case res.Reason == filtering.Rewritten:
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 {
@@ -59,17 +105,6 @@ 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,7 +1,6 @@
package dnsforward
import (
"cmp"
"encoding/json"
"fmt"
"io"
@@ -262,17 +261,55 @@ 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(
ownAddrs addrPortSet,
sysResolvers SystemResolvers,
privateNets netutil.SubnetSet,
) (err error) {
func (req *jsonDNSConfig) validate(privateNets netutil.SubnetSet) (err error) {
defer func() { err = errors.Annotate(err, "validating dns config: %w") }()
err = req.validateUpstreamDNSServers(ownAddrs, sysResolvers, privateNets)
err = req.validateUpstreamDNSServers(privateNets)
if err != nil {
// Don't wrap the error since it's informative enough as is.
return err
@@ -305,77 +342,20 @@ func (req *jsonDNSConfig) validate(
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
}
// 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.UsePrivateRDNS == nil || !*req.UsePrivateRDNS) && req.LocalPTRUpstreams == nil {
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(
ownAddrs addrPortSet,
sysResolvers SystemResolvers,
privateNets netutil.SubnetSet,
) (err error) {
var uc *proxy.UpstreamConfig
opts := &upstream.Options{}
func (req *jsonDNSConfig) validateUpstreamDNSServers(privateNets netutil.SubnetSet) (err error) {
if req.Upstreams != nil {
uc, err = proxy.ParseUpstreamsConfig(*req.Upstreams, opts)
err = errors.WithDeferred(err, uc.Close())
_, err = proxy.ParseUpstreamsConfig(*req.Upstreams, &upstream.Options{})
if err != nil {
return fmt.Errorf("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
if req.LocalPTRUpstreams != nil {
err = ValidateUpstreamsPrivate(*req.LocalPTRUpstreams, privateNets)
if err != nil {
return fmt.Errorf("private upstream servers: %w", err)
}
}
err = req.checkBootstrap()
@@ -384,12 +364,10 @@ func (req *jsonDNSConfig) validateUpstreamDNSServers(
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)
}
err = req.checkFallbacks()
if err != nil {
// Don't wrap the error since it's informative enough as is.
return err
}
return nil
@@ -458,16 +436,7 @@ func (s *Server) handleSetConfig(w http.ResponseWriter, r *http.Request) {
return
}
// 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)
err = req.validate(s.privateNets)
if err != nil {
aghhttp.Error(r, w, http.StatusBadRequest, "%s", err)
@@ -505,6 +474,8 @@ 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 {
@@ -618,7 +589,7 @@ func (s *Server) handleTestUpstreamDNS(w http.ResponseWriter, r *http.Request) {
}
var boots []*upstream.UpstreamResolver
opts.Bootstrap, boots, err = newBootstrap(req.BootstrapDNS, s.etcHosts, opts)
opts.Bootstrap, boots, err = s.createBootstrap(req.BootstrapDNS, opts)
if err != nil {
aghhttp.Error(r, w, http.StatusBadRequest, "Failed to parse bootstrap servers: %s", err)

View File

@@ -29,10 +29,6 @@ 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{}
@@ -245,8 +241,9 @@ func TestDNSForwardHTTP_handleSetConfig(t *testing.T) {
wantSet: "",
}, {
name: "local_ptr_upstreams_bad",
wantSet: `validating dns config: private upstream servers: ` +
`bad arpa domain name "non.arpa": not a reversed ip network`,
wantSet: `validating dns config: ` +
`private upstream servers: checking domain-specific upstreams: ` +
`bad arpa domain name "non.arpa.": not a reversed ip network`,
}, {
name: "local_ptr_upstreams_null",
wantSet: "",
@@ -317,6 +314,58 @@ 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()

View File

@@ -11,21 +11,17 @@ import (
"github.com/miekg/dns"
)
// TODO(e.burkov): Name all the methods by a [proxy.MessageConstructor]
// template. Also extract all the methods to a separate entity.
// 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,
}
// 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
resp.SetReply(req)
return resp
}
@@ -52,10 +48,10 @@ func (s *Server) genDNSFilterMessage(
) (resp *dns.Msg) {
req := dctx.Req
qt := req.Question[0].Qtype
if qt != dns.TypeA && qt != dns.TypeAAAA && qt != dns.TypeHTTPS {
if qt != dns.TypeA && qt != dns.TypeAAAA {
m, _, _ := s.dnsFilter.BlockingMode()
if m == filtering.BlockingModeNullIP {
return s.replyCompressed(req)
return s.makeResponse(req)
}
return s.newMsgNODATA(req)
@@ -79,7 +75,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.replyCompressed(req)
resp = s.makeResponse(req)
originalName := req.Question[0].Name
@@ -125,13 +121,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.NewMsgNXDOMAIN(req)
return s.genNXDomain(req)
case filtering.BlockingModeREFUSED:
return s.makeResponseREFUSED(req)
default:
log.Error("dnsforward: invalid blocking mode %q", mode)
return s.replyCompressed(req)
return s.makeResponse(req)
}
}
@@ -152,18 +148,25 @@ func (s *Server) makeResponseCustomIP(
// genDNSFilterMessage.
log.Error("dnsforward: invalid msg type %s for custom IP blocking mode", dns.Type(qt))
return s.replyCompressed(req)
return s.makeResponse(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.replyCompressed(request)
resp := s.makeResponse(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.replyCompressed(request)
resp := s.makeResponse(request)
resp.Answer = append(resp.Answer, s.genAnswerAAAA(request, ip))
return resp
}
@@ -249,7 +252,7 @@ func (s *Server) genResponseWithIPs(req *dns.Msg, ips []netip.Addr) (resp *dns.M
// Go on and return an empty response.
}
resp = s.replyCompressed(req)
resp = s.makeResponse(req)
resp.Answer = ans
return resp
@@ -285,7 +288,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.replyCompressed(req)
resp = s.makeResponse(req)
}
return resp
@@ -295,7 +298,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.NewMsgSERVFAIL(request)
return s.genServerFailure(request)
}
ip, err := netip.ParseAddr(newAddr)
@@ -318,17 +321,17 @@ func (s *Server) genBlockedHost(request *dns.Msg, newAddr string, d *proxy.DNSCo
if prx == nil {
log.Debug("dnsforward: %s", srvClosedErr)
return s.NewMsgSERVFAIL(request)
return s.genServerFailure(request)
}
err = prx.Resolve(newContext)
if err != nil {
log.Info("dnsforward: looking up replacement host %q: %s", newAddr, err)
return s.NewMsgSERVFAIL(request)
return s.genServerFailure(request)
}
resp := s.replyCompressed(request)
resp := s.makeResponse(request)
if newContext.Res != nil {
for _, answer := range newContext.Res.Answer {
answer.Header().Name = request.Question[0].Name
@@ -339,21 +342,47 @@ 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(req *dns.Msg) *dns.Msg {
return s.reply(req, dns.RcodeRefused)
func (s *Server) makeResponseREFUSED(request *dns.Msg) *dns.Msg {
resp := dns.Msg{}
resp.SetRcode(request, dns.RcodeRefused)
resp.RecursionAvailable = true
return &resp
}
// 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 = s.reply(req, dns.RcodeSuccess)
resp = (&dns.Msg{}).SetRcode(req, dns.RcodeSuccess)
resp.RecursionAvailable = true
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 {
@@ -385,43 +414,5 @@ 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,17 +1,20 @@
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"
)
@@ -31,6 +34,11 @@ 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
@@ -55,6 +63,10 @@ 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
@@ -97,11 +109,15 @@ 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,
@@ -129,6 +145,24 @@ 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.
//
@@ -165,14 +199,14 @@ func (s *Server) processInitial(dctx *dnsContext) (rc resultCode) {
}
if (qt == dns.TypeA || qt == dns.TypeAAAA) && q.Name == mozillaFQDN {
pctx.Res = s.NewMsgNXDOMAIN(pctx.Req)
pctx.Res = s.genNXDomain(pctx.Req)
return resultCodeFinish
}
if q.Name == healthcheckFQDN {
// Generate a NODATA negative response to make nslookup exit with 0.
pctx.Res = s.replyCompressed(pctx.Req)
pctx.Res = s.makeResponse(pctx.Req)
return resultCodeFinish
}
@@ -238,7 +272,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.replyCompressed(req)
resp = s.makeResponse(req)
if req.Question[0].Qtype != dns.TypeSVCB {
return resp
}
@@ -305,6 +339,19 @@ 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.
@@ -323,9 +370,9 @@ func (s *Server) processDHCPHosts(dctx *dnsContext) (rc resultCode) {
return resultCodeSuccess
}
if !pctx.IsPrivateClient {
if !dctx.isLocalClient {
log.Debug("dnsforward: %q requests for dhcp host %q", pctx.Addr, dhcpHost)
pctx.Res = s.NewMsgNXDOMAIN(req)
pctx.Res = s.genNXDomain(req)
// Do not even put into query log.
return resultCodeFinish
@@ -342,7 +389,7 @@ func (s *Server) processDHCPHosts(dctx *dnsContext) (rc resultCode) {
log.Debug("dnsforward: dhcp record for %q is %s", dhcpHost, ip)
resp := s.replyCompressed(req)
resp := s.makeResponse(req)
switch q.Qtype {
case dns.TypeA:
a := &dns.A{
@@ -369,6 +416,141 @@ 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) {
@@ -380,27 +562,23 @@ func (s *Server) processDHCPAddrs(dctx *dnsContext) (rc resultCode) {
return resultCodeSuccess
}
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 {
ipAddr := dctx.unreversedReqIP
if ipAddr == (netip.Addr{}) {
return resultCodeSuccess
}
addr := pref.Addr()
host := s.dhcpServer.HostByIP(addr)
host := s.dhcpServer.HostByIP(ipAddr)
if host == "" {
return resultCodeSuccess
}
log.Debug("dnsforward: dhcp client %s is %q", addr, host)
log.Debug("dnsforward: dhcp client %s is %q", ipAddr, host)
resp := s.replyCompressed(req)
req := pctx.Req
resp := s.makeResponse(req)
ptr := &dns.PTR{
Hdr: dns.RR_Header{
Name: q.Name,
Name: req.Question[0].Name,
Rrtype: dns.TypePTR,
// TODO(e.burkov): Use [dhcpsvc.Lease.Expiry]. See
// https://github.com/AdguardTeam/AdGuardHome/issues/3932.
@@ -415,20 +593,62 @@ 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
@@ -475,7 +695,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.NewMsgNXDOMAIN(req)
pctx.Res = s.genNXDomain(req)
return resultCodeFinish
}
@@ -492,7 +712,21 @@ func (s *Server) processUpstream(dctx *dnsContext) (rc resultCode) {
return resultCodeError
}
if dctx.err = prx.Resolve(pctx); dctx.err != nil {
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
return resultCodeError
}
@@ -576,7 +810,7 @@ func (s *Server) setCustomUpstream(pctx *proxy.DNSContext, clientID string) {
}
// Use the ClientID first, since it has a higher priority.
id := cmp.Or(clientID, pctx.Addr.Addr().String())
id := stringutil.Coalesce(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)
@@ -601,8 +835,7 @@ func (s *Server) processFilteringAfterResponse(dctx *dnsContext) (rc resultCode)
return resultCodeSuccess
case
filtering.Rewritten,
filtering.RewrittenRule,
filtering.FilteredSafeSearch:
filtering.RewrittenRule:
if dctx.origQuestion.Name == "" {
// origQuestion is set in case we get only CNAME without IP from
@@ -612,10 +845,11 @@ func (s *Server) processFilteringAfterResponse(dctx *dnsContext) (rc resultCode)
pctx := dctx.proxyCtx
pctx.Req.Question[0], pctx.Res.Question[0] = dctx.origQuestion, dctx.origQuestion
rr := s.genAnswerCNAME(pctx.Req, res.CanonName)
answer := append([]dns.RR{rr}, pctx.Res.Answer...)
pctx.Res.Answer = answer
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
}
return resultCodeSuccess
default:

View File

@@ -1,15 +1,14 @@
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"
"github.com/AdguardTeam/dnsproxy/upstream"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/testutil"
"github.com/AdguardTeam/urlfilter/rules"
@@ -71,6 +70,8 @@ func TestServer_ProcessInitial(t *testing.T) {
}}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
@@ -170,6 +171,8 @@ func TestServer_ProcessFilteringAfterResponse(t *testing.T) {
}}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
@@ -376,6 +379,44 @@ func createTestDNSFilter(t *testing.T) (f *filtering.DNSFilter) {
return f
}
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"
@@ -445,9 +486,9 @@ func TestServer_ProcessDHCPHosts_localRestriction(t *testing.T) {
dctx := &dnsContext{
proxyCtx: &proxy.DNSContext{
Req: req,
IsPrivateClient: tc.isLocalCli,
Req: req,
},
isLocalClient: tc.isLocalCli,
}
res := s.processDHCPHosts(dctx)
@@ -580,9 +621,9 @@ func TestServer_ProcessDHCPHosts(t *testing.T) {
dctx := &dnsContext{
proxyCtx: &proxy.DNSContext{
Req: req,
IsPrivateClient: true,
Req: req,
},
isLocalClient: true,
}
t.Run(tc.name, func(t *testing.T) {
@@ -617,28 +658,19 @@ func TestServer_ProcessDHCPHosts(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)
func TestServer_ProcessRestrictLocal(t *testing.T) {
const (
extPTRAnswer = "host1.example.net."
intPTRAnswer = "some.local-client."
extPTRQuestion = "251.252.253.254.in-addr.arpa."
extPTRAnswer = "host1.example.net."
intPTRQuestion = "1.1.168.192.in-addr.arpa."
intPTRAnswer = "some.local-client."
)
localUpsHdlr := dns.HandlerFunc(func(w dns.ResponseWriter, req *dns.Msg) {
resp := cmp.Or(
resp := aghalg.Coalesce(
aghtest.MatchedResponse(req, dns.TypePTR, extPTRQuestion, extPTRAnswer),
aghtest.MatchedResponse(req, dns.TypePTR, intPTRQuestion, intPTRAnswer),
(&dns.Msg{}).SetRcode(req, dns.RcodeNameError),
new(dns.Msg).SetRcode(req, dns.RcodeNameError),
)
require.NoError(testutil.PanicT{}, w.WriteMsg(resp))
@@ -664,165 +696,123 @@ func TestServer_HandleDNSRequest_restrictLocal(t *testing.T) {
startDeferStop(t, s)
testCases := []struct {
name string
question string
wantErr error
wantAns []dns.RR
isPrivate bool
name string
want string
question net.IP
cliAddr netip.AddrPort
wantLen int
}{{
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_local_to_external",
want: "host1.example.net.",
question: net.IP{254, 253, 252, 251},
cliAddr: netip.MustParseAddrPort("192.168.10.10:1"),
wantLen: 1,
}, {
// 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_external_for_local",
want: "",
question: net.IP{192, 168, 1, 1},
cliAddr: netip.MustParseAddrPort("254.253.252.251:1"),
wantLen: 0,
}, {
name: "from_local_for_local",
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,
want: "some.local-client.",
question: net.IP{192, 168, 1, 1},
cliAddr: netip.MustParseAddrPort("192.168.1.2:1"),
wantLen: 1,
}, {
name: "from_external_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: false,
want: "host1.example.net.",
question: net.IP{254, 253, 252, 251},
cliAddr: netip.MustParseAddrPort("254.253.252.255:1"),
wantLen: 1,
}}
for _, tc := range testCases {
pref, extErr := netutil.ExtractReversedAddr(tc.question)
require.NoError(t, extErr)
reqAddr, err := dns.ReverseAddr(tc.question.String())
require.NoError(t, err)
req := createTestMessageWithType(reqAddr, dns.TypePTR)
req := createTestMessageWithType(dns.Fqdn(tc.question), dns.TypePTR)
pctx := &proxy.DNSContext{
Req: req,
IsPrivateClient: tc.isPrivate,
}
// TODO(e.burkov): Configure the subnet set properly.
if netutil.IsLocallyServed(pref.Addr()) {
pctx.RequestedPrivateRDNS = pref
Proto: proxy.ProtoTCP,
Req: req,
Addr: tc.cliAddr,
}
t.Run(tc.name, func(t *testing.T) {
err = s.handleDNSRequest(s.dnsProxy, pctx)
require.ErrorIs(t, err, tc.wantErr)
err = s.handleDNSRequest(nil, pctx)
require.NoError(t, err)
require.NotNil(t, pctx.Res)
assert.Equal(t, tc.wantAns, pctx.Res.Answer)
require.Len(t, pctx.Res.Answer, tc.wantLen)
if tc.wantLen > 0 {
assert.Equal(t, tc.want, pctx.Res.Answer[0].(*dns.PTR).Ptr)
}
})
}
}
func TestServer_ProcessUpstream_localPTR(t *testing.T) {
func TestServer_ProcessLocalPTR_usingResolvers(t *testing.T) {
const locDomain = "some.local."
const reqAddr = "1.1.168.192.in-addr.arpa."
localUpsHdlr := dns.HandlerFunc(func(w dns.ResponseWriter, req *dns.Msg) {
resp := cmp.Or(
resp := aghalg.Coalesce(
aghtest.MatchedResponse(req, dns.TypePTR, reqAddr, locDomain),
(&dns.Msg{}).SetRcode(req, dns.RcodeNameError),
new(dns.Msg).SetRcode(req, dns.RcodeNameError),
)
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"),
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,
},
)
var proxyCtx *proxy.DNSContext
var dnsCtx *dnsContext
setup := func(use bool) {
proxyCtx = &proxy.DNSContext{
Addr: testClientAddrPort,
Req: createTestMessageWithType(reqAddr, dns.TypePTR),
}
dnsCtx = &dnsContext{
proxyCtx: proxyCtx,
unreversedReqIP: netip.MustParseAddr("192.168.1.1"),
}
s.conf.UsePrivateRDNS = use
}
t.Run("enabled", func(t *testing.T) {
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()
setup(true)
rc := s.processUpstream(&dnsContext{proxyCtx: pctx})
rc := s.processLocalPTR(dnsCtx)
require.Equal(t, resultCodeSuccess, rc)
require.NotEmpty(t, pctx.Res.Answer)
ptr := testutil.RequireTypeAssert[*dns.PTR](t, pctx.Res.Answer[0])
require.NotEmpty(t, proxyCtx.Res.Answer)
assert.Equal(t, locDomain, ptr.Ptr)
assert.Equal(t, locDomain, proxyCtx.Res.Answer[0].(*dns.PTR).Ptr)
})
t.Run("disabled", func(t *testing.T) {
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()
setup(false)
rc := s.processUpstream(&dnsContext{proxyCtx: pctx})
require.Equal(t, resultCodeError, rc)
require.Empty(t, pctx.Res.Answer)
rc := s.processLocalPTR(dnsCtx)
require.Equal(t, resultCodeFinish, rc)
require.Empty(t, proxyCtx.Res.Answer)
})
}
@@ -840,3 +830,129 @@ 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

@@ -0,0 +1,115 @@
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

@@ -0,0 +1,148 @@
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,13 +29,7 @@ func (s *Server) processQueryLogsAndStats(dctx *dnsContext) (rc resultCode) {
log.Debug("dnsforward: client ip for stats and querylog: %s", ipStr)
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}
}
ids := []string{ipStr, dctx.clientID}
qt, cl := q.Qtype, q.Qclass
// Synchronize access to s.queryLog and s.stats so they won't be suddenly
@@ -130,7 +124,7 @@ func (s *Server) logQuery(dctx *dnsContext, ip net.IP, processingTime time.Durat
s.queryLog.Add(p)
}
// updateStats writes the request data into statistics.
// updatesStats writes the request into statistics.
func (s *Server) updateStats(dctx *dnsContext, clientIP string, processingTime time.Duration) {
pctx := dctx.proxyCtx

View File

@@ -2,77 +2,90 @@ 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"
)
// 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
// 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
}
boots, err = aghnet.ParseBootstraps(addrs, opts)
var data []byte
data, err = os.ReadFile(s.conf.UpstreamDNSFileName)
if err != nil {
// Don't wrap the error, since it's informative enough as is.
return nil, nil, err
return nil, fmt.Errorf("reading upstream from file: %w", err)
}
var parallel upstream.ParallelResolver
for _, b := range boots {
parallel = append(parallel, upstream.NewCachingResolver(b))
}
upstreams = stringutil.SplitTrimmed(string(data), "\n")
if etcHosts != nil {
r = upstream.ConsequentResolver{etcHosts, parallel}
} else {
r = parallel
}
log.Debug("dnsforward: got %d upstreams in %q", len(upstreams), s.conf.UpstreamDNSFileName)
return r, boots, nil
return stringutil.FilterOut(upstreams, IsCommentOrEmpty), nil
}
// 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(
// 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(
upstreams []string,
defaultUpstreams []string,
opts *upstream.Options,
) (uc *proxy.UpstreamConfig, err error) {
uc, err = proxy.ParseUpstreamsConfig(upstreams, opts)
if err != nil {
return uc, fmt.Errorf("parsing upstreams: %w", err)
return nil, fmt.Errorf("parsing upstream config: %w", err)
}
if len(uc.Upstreams) == 0 && len(defaultUpstreams) > 0 {
if len(uc.Upstreams) == 0 && defaultUpstreams != nil {
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 uc, fmt.Errorf("parsing default upstreams: %w", err)
return nil, fmt.Errorf("parsing default upstreams: %w", err)
}
uc.Upstreams = defaultUpstreamConfig.Upstreams
@@ -81,54 +94,6 @@ func newUpstreamConfig(
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: upstreams to resolve ptr for local addresses: %v", addrs)
uc, err = proxy.ParseUpstreamsConfig(addrs, opts)
if err != nil {
return uc, fmt.Errorf("preparing private upstreams: %w", err)
}
if !confNeedsFiltering {
return uc, nil
}
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) {
@@ -165,9 +130,85 @@ 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")
}

View File

@@ -8,7 +8,6 @@ import (
"time"
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
"github.com/AdguardTeam/AdGuardHome/internal/filtering/rulelist"
"github.com/AdguardTeam/AdGuardHome/internal/schedule"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/urlfilter/rules"
@@ -29,7 +28,7 @@ func initBlockedServices() {
for i, s := range blockedServices {
netRules := make([]*rules.NetworkRule, 0, len(s.Rules))
for _, text := range s.Rules {
rule, err := rules.NewNetworkRule(text, rulelist.URLFilterIDBlockedService)
rule, err := rules.NewNetworkRule(text, BlockedSvcsListID)
if err != nil {
log.Error("parsing blocked service %q rule %q: %s", s.ID, text, err)

View File

@@ -30,7 +30,7 @@ func (d *DNSFilter) processDNSRewrites(dnsr []*rules.NetworkRule) (res Result) {
if dr.NewCNAME != "" {
// NewCNAME rules have a higher priority than other rules.
rules = []*ResultRule{{
FilterListID: nr.GetFilterListID(),
FilterListID: int64(nr.GetFilterListID()),
Text: nr.RuleText,
}}
@@ -46,14 +46,14 @@ func (d *DNSFilter) processDNSRewrites(dnsr []*rules.NetworkRule) (res Result) {
dnsrr.RCode = dr.RCode
dnsrr.Response[dr.RRType] = append(dnsrr.Response[dr.RRType], dr.Value)
rules = append(rules, &ResultRule{
FilterListID: nr.GetFilterListID(),
FilterListID: int64(nr.GetFilterListID()),
Text: nr.RuleText,
})
default:
// RcodeRefused and other such codes have higher priority. Return
// immediately.
rules = []*ResultRule{{
FilterListID: nr.GetFilterListID(),
FilterListID: int64(nr.GetFilterListID()),
Text: nr.RuleText,
}}
dnsrr = &DNSRewriteResult{

View File

@@ -13,15 +13,20 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/aghrenameio"
"github.com/AdguardTeam/AdGuardHome/internal/filtering/rulelist"
"github.com/AdguardTeam/golibs/container"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/stringutil"
)
// filterDir is the subdirectory of a data directory to store downloaded
// filters.
const filterDir = "filters"
// nextFilterID is a way to seed a unique ID generation.
//
// TODO(e.burkov): Use more deterministic approach.
var nextFilterID = time.Now().Unix()
// FilterYAML represents a filter list in the configuration file.
//
// TODO(e.burkov): Investigate if the field ordering is important.
@@ -45,10 +50,7 @@ func (filter *FilterYAML) unload() {
// Path to the filter contents
func (filter *FilterYAML) Path(dataDir string) string {
return filepath.Join(
dataDir,
filterDir,
strconv.FormatInt(int64(filter.ID), 10)+".txt")
return filepath.Join(dataDir, filterDir, strconv.FormatInt(filter.ID, 10)+".txt")
}
// ensureName sets provided title or default name for the filter if it doesn't
@@ -215,10 +217,7 @@ func (d *DNSFilter) loadFilters(array []FilterYAML) {
for i := range array {
filter := &array[i] // otherwise we're operating on a copy
if filter.ID == 0 {
newID := d.idGen.next()
log.Info("filtering: warning: filter at index %d has no id; assigning to %d", i, newID)
filter.ID = newID
filter.ID = assignUniqueFilterID()
}
if !filter.Enabled {
@@ -234,7 +233,7 @@ func (d *DNSFilter) loadFilters(array []FilterYAML) {
}
func deduplicateFilters(filters []FilterYAML) (deduplicated []FilterYAML) {
urls := container.NewMapSet[string]()
urls := stringutil.NewSet()
lastIdx := 0
for _, filter := range filters {
@@ -248,6 +247,22 @@ func deduplicateFilters(filters []FilterYAML) (deduplicated []FilterYAML) {
return filters[:lastIdx]
}
// Set the next filter ID to max(filter.ID) + 1
func updateUniqueFilterID(filters []FilterYAML) {
for _, filter := range filters {
if nextFilterID < filter.ID {
nextFilterID = filter.ID + 1
}
}
}
// TODO(e.burkov): Improve this inexhaustible source of races.
func assignUniqueFilterID() int64 {
value := nextFilterID
nextFilterID++
return value
}
// tryRefreshFilters is like [refreshFilters], but backs down if the update is
// already going on.
//
@@ -593,7 +608,7 @@ func (d *DNSFilter) EnableFilters(async bool) {
func (d *DNSFilter) enableFiltersLocked(async bool) {
filters := make([]Filter, 1, len(d.conf.Filters)+len(d.conf.WhitelistFilters)+1)
filters[0] = Filter{
ID: rulelist.URLFilterIDCustom,
ID: CustomListID,
Data: []byte(strings.Join(d.conf.UserRules, "\n")),
}

View File

@@ -20,11 +20,11 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
"github.com/AdguardTeam/AdGuardHome/internal/filtering/rulelist"
"github.com/AdguardTeam/golibs/container"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/hostsfile"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/mathutil"
"github.com/AdguardTeam/golibs/stringutil"
"github.com/AdguardTeam/golibs/syncutil"
"github.com/AdguardTeam/urlfilter"
"github.com/AdguardTeam/urlfilter/filterlist"
@@ -32,6 +32,19 @@ import (
"github.com/miekg/dns"
)
// The IDs of built-in filter lists.
//
// Keep in sync with client/src/helpers/constants.js.
// TODO(d.kolyshev): Add RewritesListID and don't forget to keep in sync.
const (
CustomListID = -iota
SysHostsListID
BlockedSvcsListID
ParentalListID
SafeBrowsingListID
SafeSearchListID
)
// ServiceEntry - blocked service array element
type ServiceEntry struct {
Name string
@@ -219,9 +232,6 @@ type Checker interface {
// DNSFilter matches hostnames and DNS requests against filtering rules.
type DNSFilter struct {
// idGen is used to generate IDs for package urlfilter.
idGen *idGenerator
// bufPool is a pool of buffers used for filtering-rule list parsing.
bufPool *syncutil.Pool[[]byte]
@@ -268,7 +278,7 @@ type Filter struct {
Data []byte `yaml:"-"`
// ID is automatically assigned when filter is added using nextFilterID.
ID rulelist.URLFilterID `yaml:"id"`
ID int64 `yaml:"id"`
}
// Reason holds an enum detailing why it was filtered or not filtered
@@ -520,13 +530,11 @@ func (d *DNSFilter) ParentalBlockHost() (host string) {
type ResultRule struct {
// Text is the text of the rule.
Text string `json:",omitempty"`
// IP is the host IP. It is nil unless the rule uses the
// /etc/hosts syntax or the reason is FilteredSafeSearch.
IP netip.Addr `json:",omitempty"`
// FilterListID is the ID of the rule's filter list.
FilterListID rulelist.URLFilterID `json:",omitempty"`
FilterListID int64 `json:",omitempty"`
}
// Result contains the result of a request check.
@@ -559,8 +567,6 @@ type Result struct {
Reason Reason `json:",omitempty"`
// IsFiltered is true if the request is filtered.
//
// TODO(d.kolyshev): Get rid of this flag.
IsFiltered bool `json:",omitempty"`
}
@@ -631,7 +637,7 @@ func (d *DNSFilter) processRewrites(host string, qtype uint16) (res Result) {
res.Reason = Rewritten
cnames := container.NewMapSet[string]()
cnames := stringutil.NewSet()
origHost := host
for matched && len(rewrites) > 0 && rewrites[0].Type == dns.TypeCNAME {
rw := rewrites[0]
@@ -699,7 +705,7 @@ func matchBlockedServicesRules(
ruleText := rule.Text()
res.Rules = []*ResultRule{{
FilterListID: rule.GetFilterListID(),
FilterListID: int64(rule.GetFilterListID()),
Text: ruleText,
}}
@@ -964,7 +970,7 @@ func makeResult(matchedRules []rules.Rule, reason Reason) (res Result) {
resRules := make([]*ResultRule, len(matchedRules))
for i, mr := range matchedRules {
resRules[i] = &ResultRule{
FilterListID: mr.GetFilterListID(),
FilterListID: int64(mr.GetFilterListID()),
Text: mr.Text(),
}
}
@@ -985,7 +991,6 @@ func InitModule() {
// be non-nil.
func New(c *Config, blockFilters []Filter) (d *DNSFilter, err error) {
d = &DNSFilter{
idGen: newIDGenerator(int32(time.Now().Unix())),
bufPool: syncutil.NewSlicePool[byte](rulelist.DefaultRuleBufSize),
refreshLock: &sync.Mutex{},
safeBrowsingChecker: c.SafeBrowsingChecker,
@@ -1049,8 +1054,8 @@ func New(c *Config, blockFilters []Filter) (d *DNSFilter, err error) {
d.conf.Filters = deduplicateFilters(d.conf.Filters)
d.conf.WhitelistFilters = deduplicateFilters(d.conf.WhitelistFilters)
d.idGen.fix(d.conf.Filters)
d.idGen.fix(d.conf.WhitelistFilters)
updateUniqueFilterID(d.conf.Filters)
updateUniqueFilterID(d.conf.WhitelistFilters)
return d, nil
}
@@ -1134,7 +1139,7 @@ func (d *DNSFilter) checkSafeBrowsing(
res = Result{
Rules: []*ResultRule{{
Text: "adguard-malware-shavar",
FilterListID: rulelist.URLFilterIDSafeBrowsing,
FilterListID: SafeBrowsingListID,
}},
Reason: FilteredSafeBrowsing,
IsFiltered: true,
@@ -1166,7 +1171,7 @@ func (d *DNSFilter) checkParental(
res = Result{
Rules: []*ResultRule{{
Text: "parental CATEGORY_BLACKLISTED",
FilterListID: rulelist.URLFilterIDParentalControl,
FilterListID: ParentalListID,
}},
Reason: FilteredParental,
IsFiltered: true,

View File

@@ -200,7 +200,7 @@ func TestParallelSB(t *testing.T) {
t.Cleanup(d.Close)
t.Run("group", func(t *testing.T) {
for i := range 100 {
for i := 0; i < 100; i++ {
t.Run(fmt.Sprintf("aaa%d", i), func(t *testing.T) {
t.Parallel()
d.checkMatch(t, sbBlocked, setts)
@@ -670,7 +670,7 @@ func BenchmarkSafeBrowsing(b *testing.B) {
}, nil)
b.Cleanup(d.Close)
for range b.N {
for n := 0; n < b.N; n++ {
res, err := d.CheckHost(sbBlocked, dns.TypeA, setts)
require.NoError(b, err)

View File

@@ -4,7 +4,6 @@ import (
"fmt"
"net/netip"
"github.com/AdguardTeam/AdGuardHome/internal/filtering/rulelist"
"github.com/AdguardTeam/golibs/hostsfile"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/netutil"
@@ -67,7 +66,7 @@ func hostsRewrites(
vals = append(vals, name)
rls = append(rls, &ResultRule{
Text: fmt.Sprintf("%s %s", addr, name),
FilterListID: rulelist.URLFilterIDEtcHosts,
FilterListID: SysHostsListID,
})
}
@@ -85,7 +84,7 @@ func hostsRewrites(
}
rls = append(rls, &ResultRule{
Text: fmt.Sprintf("%s %s", addr, host),
FilterListID: rulelist.URLFilterIDEtcHosts,
FilterListID: SysHostsListID,
})
}

View File

@@ -8,7 +8,6 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
"github.com/AdguardTeam/AdGuardHome/internal/aghtest"
"github.com/AdguardTeam/AdGuardHome/internal/filtering/rulelist"
"github.com/AdguardTeam/golibs/testutil"
"github.com/AdguardTeam/urlfilter/rules"
"github.com/miekg/dns"
@@ -72,7 +71,7 @@ func TestDNSFilter_CheckHost_hostsContainer(t *testing.T) {
dtyp: dns.TypeA,
wantRules: []*ResultRule{{
Text: "1.2.3.4 v4.host.example",
FilterListID: rulelist.URLFilterIDEtcHosts,
FilterListID: SysHostsListID,
}},
wantResps: []rules.RRValue{addrv4},
}, {
@@ -81,7 +80,7 @@ func TestDNSFilter_CheckHost_hostsContainer(t *testing.T) {
dtyp: dns.TypeAAAA,
wantRules: []*ResultRule{{
Text: "::1 v6.host.example",
FilterListID: rulelist.URLFilterIDEtcHosts,
FilterListID: SysHostsListID,
}},
wantResps: []rules.RRValue{addrv6},
}, {
@@ -90,7 +89,7 @@ func TestDNSFilter_CheckHost_hostsContainer(t *testing.T) {
dtyp: dns.TypeAAAA,
wantRules: []*ResultRule{{
Text: "::ffff:1.2.3.4 mapped.host.example",
FilterListID: rulelist.URLFilterIDEtcHosts,
FilterListID: SysHostsListID,
}},
wantResps: []rules.RRValue{addrMapped},
}, {
@@ -99,7 +98,7 @@ func TestDNSFilter_CheckHost_hostsContainer(t *testing.T) {
dtyp: dns.TypePTR,
wantRules: []*ResultRule{{
Text: "1.2.3.4 v4.host.example",
FilterListID: rulelist.URLFilterIDEtcHosts,
FilterListID: SysHostsListID,
}},
wantResps: []rules.RRValue{"v4.host.example"},
}, {
@@ -108,7 +107,7 @@ func TestDNSFilter_CheckHost_hostsContainer(t *testing.T) {
dtyp: dns.TypePTR,
wantRules: []*ResultRule{{
Text: "::ffff:1.2.3.4 mapped.host.example",
FilterListID: rulelist.URLFilterIDEtcHosts,
FilterListID: SysHostsListID,
}},
wantResps: []rules.RRValue{"mapped.host.example"},
}, {
@@ -135,7 +134,7 @@ func TestDNSFilter_CheckHost_hostsContainer(t *testing.T) {
dtyp: dns.TypeAAAA,
wantRules: []*ResultRule{{
Text: fmt.Sprintf("%s v4.host.example", addrv4),
FilterListID: rulelist.URLFilterIDEtcHosts,
FilterListID: SysHostsListID,
}},
wantResps: nil,
}, {
@@ -144,7 +143,7 @@ func TestDNSFilter_CheckHost_hostsContainer(t *testing.T) {
dtyp: dns.TypeA,
wantRules: []*ResultRule{{
Text: fmt.Sprintf("%s v6.host.example", addrv6),
FilterListID: rulelist.URLFilterIDEtcHosts,
FilterListID: SysHostsListID,
}},
wantResps: nil,
}, {
@@ -165,7 +164,7 @@ func TestDNSFilter_CheckHost_hostsContainer(t *testing.T) {
dtyp: dns.TypeA,
wantRules: []*ResultRule{{
Text: "4.3.2.1 v4.host.with-dup",
FilterListID: rulelist.URLFilterIDEtcHosts,
FilterListID: SysHostsListID,
}},
wantResps: []rules.RRValue{addrv4Dup},
}}

View File

@@ -13,7 +13,6 @@ import (
"time"
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
"github.com/AdguardTeam/AdGuardHome/internal/filtering/rulelist"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/miekg/dns"
@@ -87,7 +86,7 @@ func (d *DNSFilter) handleFilteringAddURL(w http.ResponseWriter, r *http.Request
Name: fj.Name,
white: fj.Whitelist,
Filter: Filter{
ID: d.idGen.next(),
ID: assignUniqueFilterID(),
},
}
@@ -308,12 +307,12 @@ func (d *DNSFilter) handleFilteringRefresh(w http.ResponseWriter, r *http.Reques
}
type filterJSON struct {
URL string `json:"url"`
Name string `json:"name"`
LastUpdated string `json:"last_updated,omitempty"`
ID rulelist.URLFilterID `json:"id"`
RulesCount uint32 `json:"rules_count"`
Enabled bool `json:"enabled"`
URL string `json:"url"`
Name string `json:"name"`
LastUpdated string `json:"last_updated,omitempty"`
ID int64 `json:"id"`
RulesCount uint32 `json:"rules_count"`
Enabled bool `json:"enabled"`
}
type filteringConfig struct {
@@ -389,8 +388,8 @@ func (d *DNSFilter) handleFilteringConfig(w http.ResponseWriter, r *http.Request
}
type checkHostRespRule struct {
Text string `json:"text"`
FilterListID rulelist.URLFilterID `json:"filter_list_id"`
Text string `json:"text"`
FilterListID int64 `json:"filter_list_id"`
}
type checkHostResp struct {
@@ -413,7 +412,7 @@ type checkHostResp struct {
// FilterID is the ID of the rule's filter list.
//
// Deprecated: Use Rules[*].FilterListID.
FilterID rulelist.URLFilterID `json:"filter_id"`
FilterID int64 `json:"filter_id"`
}
func (d *DNSFilter) handleCheckHost(w http.ResponseWriter, r *http.Request) {

View File

@@ -1,74 +0,0 @@
package filtering
import (
"fmt"
"sync/atomic"
"github.com/AdguardTeam/AdGuardHome/internal/filtering/rulelist"
"github.com/AdguardTeam/golibs/container"
"github.com/AdguardTeam/golibs/log"
)
// idGenerator generates filtering-list IDs in a way broadly compatible with the
// legacy approach of AdGuard Home.
//
// TODO(a.garipov): Get rid of this once we switch completely to the new
// rule-list architecture.
type idGenerator struct {
current *atomic.Int32
}
// newIDGenerator returns a new ID generator initialized with the given seed
// value.
func newIDGenerator(seed int32) (g *idGenerator) {
g = &idGenerator{
current: &atomic.Int32{},
}
g.current.Store(seed)
return g
}
// next returns the next ID from the generator. It is safe for concurrent use.
func (g *idGenerator) next() (id rulelist.URLFilterID) {
id32 := g.current.Add(1)
if id32 < 0 {
panic(fmt.Errorf("invalid current id value %d", id32))
}
return rulelist.URLFilterID(id32)
}
// fix ensures that flts all have unique IDs.
func (g *idGenerator) fix(flts []FilterYAML) {
set := container.NewMapSet[rulelist.URLFilterID]()
for i, f := range flts {
id := f.ID
if id == 0 {
id = g.next()
flts[i].ID = id
}
if !set.Has(id) {
set.Add(id)
continue
}
newID := g.next()
for set.Has(newID) {
newID = g.next()
}
log.Info(
"filtering: warning: filter at index %d has duplicate id %d; reassigning to %d",
i,
id,
newID,
)
flts[i].ID = newID
set.Add(newID)
}
}

View File

@@ -1,86 +0,0 @@
package filtering
import (
"testing"
"github.com/AdguardTeam/AdGuardHome/internal/aghalg"
"github.com/AdguardTeam/AdGuardHome/internal/filtering/rulelist"
"github.com/stretchr/testify/assert"
)
func TestIDGenerator_Fix(t *testing.T) {
t.Parallel()
testCases := []struct {
name string
in []FilterYAML
}{{
name: "nil",
in: nil,
}, {
name: "empty",
in: []FilterYAML{},
}, {
name: "one_zero",
in: []FilterYAML{{}},
}, {
name: "two_zeros",
in: []FilterYAML{{}, {}},
}, {
name: "many_good",
in: []FilterYAML{{
Filter: Filter{
ID: 1,
},
}, {
Filter: Filter{
ID: 2,
},
}, {
Filter: Filter{
ID: 3,
},
}},
}, {
name: "two_dups",
in: []FilterYAML{{
Filter: Filter{
ID: 1,
},
}, {
Filter: Filter{
ID: 3,
},
}, {
Filter: Filter{
ID: 1,
},
}, {
Filter: Filter{
ID: 2,
},
}},
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
g := newIDGenerator(1)
g.fix(tc.in)
assertUniqueIDs(t, tc.in)
})
}
}
// assertUniqueIDs is a test helper that asserts that the IDs of filters are
// unique.
func assertUniqueIDs(t testing.TB, flts []FilterYAML) {
t.Helper()
uc := aghalg.UniqChecker[rulelist.URLFilterID]{}
for _, f := range flts {
uc.Add(f.ID)
}
assert.NoError(t, uc.Validate())
}

View File

@@ -7,8 +7,8 @@ import (
"strings"
"sync"
"github.com/AdguardTeam/golibs/container"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/stringutil"
"github.com/AdguardTeam/urlfilter"
"github.com/AdguardTeam/urlfilter/filterlist"
"github.com/AdguardTeam/urlfilter/rules"
@@ -85,7 +85,7 @@ func (s *DefaultStorage) MatchRequest(dReq *urlfilter.DNSRequest) (rws []*rules.
}
// TODO(a.garipov): Check cnames for cycles on initialization.
cnames := container.NewMapSet[string]()
cnames := stringutil.NewSet()
host := dReq.Hostname
for len(rrules) > 0 && rrules[0].DNSRewrite != nil && rrules[0].DNSRewrite.NewCNAME != "" {
rule := rrules[0]

View File

@@ -1,254 +0,0 @@
package rulelist
import (
"context"
"fmt"
"net/http"
"sync"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/urlfilter"
"github.com/AdguardTeam/urlfilter/filterlist"
"github.com/c2h5oh/datasize"
)
// Engine is a single DNS filter based on one or more rule lists. This
// structure contains the filtering engine combining several rule lists.
//
// TODO(a.garipov): Merge with [TextEngine] in some way?
type Engine struct {
// mu protects engine and storage.
//
// TODO(a.garipov): See if anything else should be protected.
mu *sync.RWMutex
// engine is the filtering engine.
engine *urlfilter.DNSEngine
// storage is the filtering-rule storage. It is saved here to close it.
storage *filterlist.RuleStorage
// name is the human-readable name of the engine, like "allowed", "blocked",
// or "custom".
name string
// filters is the data about rule filters in this engine.
filters []*Filter
}
// EngineConfig is the configuration for rule-list filtering engines created by
// combining refreshable filters.
type EngineConfig struct {
// Name is the human-readable name of this engine, like "allowed",
// "blocked", or "custom".
Name string
// Filters is the data about rule lists in this engine. There must be no
// other references to the elements of this slice.
Filters []*Filter
}
// NewEngine returns a new rule-list filtering engine. The engine is not
// refreshed, so a refresh should be performed before use.
func NewEngine(c *EngineConfig) (e *Engine) {
return &Engine{
mu: &sync.RWMutex{},
name: c.Name,
filters: c.Filters,
}
}
// Close closes the underlying rule-list engine as well as the rule lists.
func (e *Engine) Close() (err error) {
e.mu.Lock()
defer e.mu.Unlock()
if e.storage == nil {
return nil
}
err = e.storage.Close()
if err != nil {
return fmt.Errorf("closing engine %q: %w", e.name, err)
}
return nil
}
// FilterRequest returns the result of filtering req using the DNS filtering
// engine.
func (e *Engine) FilterRequest(
req *urlfilter.DNSRequest,
) (res *urlfilter.DNSResult, hasMatched bool) {
return e.currentEngine().MatchRequest(req)
}
// currentEngine returns the current filtering engine.
func (e *Engine) currentEngine() (enging *urlfilter.DNSEngine) {
e.mu.RLock()
defer e.mu.RUnlock()
return e.engine
}
// Refresh updates all rule lists in e. ctx is used for cancellation.
// parseBuf, cli, cacheDir, and maxSize are used for updates of rule-list
// filters; see [Filter.Refresh].
//
// TODO(a.garipov): Unexport and test in an internal test or through enigne
// tests.
func (e *Engine) Refresh(
ctx context.Context,
parseBuf []byte,
cli *http.Client,
cacheDir string,
maxSize datasize.ByteSize,
) (err error) {
defer func() { err = errors.Annotate(err, "updating engine %q: %w", e.name) }()
var filtersToRefresh []*Filter
for _, f := range e.filters {
if f.enabled {
filtersToRefresh = append(filtersToRefresh, f)
}
}
if len(filtersToRefresh) == 0 {
log.Info("filtering: updating engine %q: no rule-list filters", e.name)
return nil
}
engRefr := &engineRefresh{
httpCli: cli,
cacheDir: cacheDir,
engineName: e.name,
parseBuf: parseBuf,
maxSize: maxSize,
}
ruleLists, errs := engRefr.process(ctx, e.filters)
if isOneTimeoutError(errs) {
// Don't wrap the error since it's informative enough as is.
return err
}
storage, err := filterlist.NewRuleStorage(ruleLists)
if err != nil {
errs = append(errs, fmt.Errorf("creating rule storage: %w", err))
return errors.Join(errs...)
}
e.resetStorage(storage)
return errors.Join(errs...)
}
// resetStorage sets e.storage and e.engine and closes the previous storage.
// Errors from closing the previous storage are logged.
func (e *Engine) resetStorage(storage *filterlist.RuleStorage) {
e.mu.Lock()
defer e.mu.Unlock()
prevStorage := e.storage
e.storage, e.engine = storage, urlfilter.NewDNSEngine(storage)
if prevStorage == nil {
return
}
err := prevStorage.Close()
if err != nil {
log.Error("filtering: engine %q: closing old storage: %s", e.name, err)
}
}
// isOneTimeoutError returns true if the sole error in errs is either
// [context.Canceled] or [context.DeadlineExceeded].
func isOneTimeoutError(errs []error) (ok bool) {
if len(errs) != 1 {
return false
}
err := errs[0]
return errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded)
}
// engineRefresh represents a single ongoing engine refresh.
type engineRefresh struct {
httpCli *http.Client
cacheDir string
engineName string
parseBuf []byte
maxSize datasize.ByteSize
}
// process runs updates of all given rule-list filters. All errors are logged
// as they appear, since the update can take a significant amount of time.
// errs contains all errors that happened during the update, unless the context
// is canceled or its deadline is reached, in which case errs will only contain
// a single timeout error.
//
// TODO(a.garipov): Think of a better way to communicate the timeout condition?
func (r *engineRefresh) process(
ctx context.Context,
filters []*Filter,
) (ruleLists []filterlist.RuleList, errs []error) {
ruleLists = make([]filterlist.RuleList, 0, len(filters))
for i, f := range filters {
select {
case <-ctx.Done():
return nil, []error{fmt.Errorf("timeout after updating %d filters: %w", i, ctx.Err())}
default:
// Go on.
}
err := r.processFilter(ctx, f)
if err == nil {
ruleLists = append(ruleLists, f.ruleList)
continue
}
errs = append(errs, err)
// Also log immediately, since the update can take a lot of time.
log.Error(
"filtering: updating engine %q: rule list %s from url %q: %s\n",
r.engineName,
f.uid,
f.url,
err,
)
}
return ruleLists, errs
}
// processFilter runs an update of a single rule-list filter.
func (r *engineRefresh) processFilter(ctx context.Context, f *Filter) (err error) {
prevChecksum := f.checksum
parseRes, err := f.Refresh(ctx, r.parseBuf, r.httpCli, r.cacheDir, r.maxSize)
if err != nil {
return fmt.Errorf("updating %s: %w", f.uid, err)
}
if prevChecksum == parseRes.Checksum {
log.Info("filtering: engine %q: filter %q: no change", r.engineName, f.uid)
return nil
}
log.Info(
"filtering: updated engine %q: filter %q: %d bytes, %d rules",
r.engineName,
f.uid,
parseRes.BytesWritten,
parseRes.RulesCount,
)
return nil
}

View File

@@ -1,60 +0,0 @@
package rulelist_test
import (
"net/http"
"testing"
"github.com/AdguardTeam/AdGuardHome/internal/filtering/rulelist"
"github.com/AdguardTeam/golibs/testutil"
"github.com/AdguardTeam/urlfilter"
"github.com/miekg/dns"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestEngine_Refresh(t *testing.T) {
cacheDir := t.TempDir()
fileURL, srvURL := newFilterLocations(t, cacheDir, testRuleTextBlocked, testRuleTextBlocked2)
fileFlt := newFilter(t, fileURL, "File Filter")
httpFlt := newFilter(t, srvURL, "HTTP Filter")
eng := rulelist.NewEngine(&rulelist.EngineConfig{
Name: "Engine",
Filters: []*rulelist.Filter{fileFlt, httpFlt},
})
require.NotNil(t, eng)
testutil.CleanupAndRequireSuccess(t, eng.Close)
buf := make([]byte, rulelist.DefaultRuleBufSize)
cli := &http.Client{
Timeout: testTimeout,
}
ctx := testutil.ContextWithTimeout(t, testTimeout)
err := eng.Refresh(ctx, buf, cli, cacheDir, rulelist.DefaultMaxRuleListSize)
require.NoError(t, err)
fltReq := &urlfilter.DNSRequest{
Hostname: "blocked.example",
Answer: false,
DNSType: dns.TypeA,
}
fltRes, hasMatched := eng.FilterRequest(fltReq)
assert.True(t, hasMatched)
require.NotNil(t, fltRes)
fltReq = &urlfilter.DNSRequest{
Hostname: "blocked-2.example",
Answer: false,
DNSType: dns.TypeA,
}
fltRes, hasMatched = eng.FilterRequest(fltReq)
assert.True(t, hasMatched)
require.NotNil(t, fltRes)
}

View File

@@ -14,6 +14,7 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/aghrenameio"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/ioutil"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/urlfilter/filterlist"
"github.com/c2h5oh/datasize"
)
@@ -51,6 +52,8 @@ type Filter struct {
checksum uint32
// enabled, if true, means that this rule-list filter is used for filtering.
//
// TODO(a.garipov): Take into account.
enabled bool
}
@@ -103,11 +106,6 @@ func NewFilter(c *FilterConfig) (f *Filter, err error) {
// Refresh updates the data in the rule-list filter. parseBuf is the initial
// buffer used to parse information from the data. cli and maxSize are only
// used when f is a URL-based list.
//
// TODO(a.garipov): Unexport and test in an internal test or through enigne
// tests.
//
// TODO(a.garipov): Consider not returning parseRes.
func (f *Filter) Refresh(
ctx context.Context,
parseBuf []byte,
@@ -302,3 +300,39 @@ func (f *Filter) Close() (err error) {
return f.ruleList.Close()
}
// filterUpdate represents a single ongoing rule-list filter update.
//
//lint:ignore U1000 TODO(a.garipov): Use.
type filterUpdate struct {
httpCli *http.Client
cacheDir string
name string
parseBuf []byte
maxSize datasize.ByteSize
}
// process runs an update of a single rule-list.
func (u *filterUpdate) process(ctx context.Context, f *Filter) (err error) {
prevChecksum := f.checksum
parseRes, err := f.Refresh(ctx, u.parseBuf, u.httpCli, u.cacheDir, u.maxSize)
if err != nil {
return fmt.Errorf("updating %s: %w", f.uid, err)
}
if prevChecksum == parseRes.Checksum {
log.Info("filtering: filter %q: filter %q: no change", u.name, f.uid)
return nil
}
log.Info(
"filtering: updated filter %q: filter %q: %d bytes, %d rules",
u.name,
f.uid,
parseRes.BytesWritten,
parseRes.RulesCount,
)
return nil
}

View File

@@ -1,7 +1,10 @@
package rulelist_test
import (
"context"
"io"
"net/http"
"net/http/httptest"
"net/url"
"os"
"path/filepath"
@@ -17,8 +20,23 @@ func TestFilter_Refresh(t *testing.T) {
cacheDir := t.TempDir()
uid := rulelist.MustNewUID()
const fltData = testRuleTextTitle + testRuleTextBlocked
fileURL, srvURL := newFilterLocations(t, cacheDir, fltData, fltData)
initialFile := filepath.Join(cacheDir, "initial.txt")
initialData := []byte(
testRuleTextTitle +
testRuleTextBlocked,
)
writeErr := os.WriteFile(initialFile, initialData, 0o644)
require.NoError(t, writeErr)
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
pt := testutil.PanicT{}
_, err := io.WriteString(w, testRuleTextTitle+testRuleTextBlocked)
require.NoError(pt, err)
}))
srvURL, urlErr := url.Parse(srv.URL)
require.NoError(t, urlErr)
testCases := []struct {
url *url.URL
@@ -38,7 +56,7 @@ func TestFilter_Refresh(t *testing.T) {
name: "file",
url: &url.URL{
Scheme: "file",
Path: fileURL.Path,
Path: initialFile,
},
wantNewErrMsg: "",
}, {
@@ -66,12 +84,14 @@ func TestFilter_Refresh(t *testing.T) {
require.NotNil(t, f)
ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
t.Cleanup(cancel)
buf := make([]byte, rulelist.DefaultRuleBufSize)
cli := &http.Client{
Timeout: testTimeout,
}
ctx := testutil.ContextWithTimeout(t, testTimeout)
res, err := f.Refresh(ctx, buf, cli, cacheDir, rulelist.DefaultMaxRuleListSize)
require.NoError(t, err)

View File

@@ -132,6 +132,7 @@ func TestParser_Parse(t *testing.T) {
}}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
@@ -215,7 +216,7 @@ func BenchmarkParser_Parse(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for range b.N {
for i := 0; i < b.N; i++ {
resSink, errSink = p.Parse(dst, src, buf)
dst.Reset()
}

View File

@@ -25,24 +25,6 @@ const DefaultMaxRuleListSize = 64 * datasize.MB
// urlfilter.
type URLFilterID = int
// The IDs of built-in filter lists.
//
// NOTE: Do not change without the need for it and keep in sync with
// client/src/helpers/constants.js.
//
// TODO(a.garipov): Add type [URLFilterID] once it is used consistently in
// package filtering.
//
// TODO(d.kolyshev): Add URLFilterIDLegacyRewrite here and to the UI.
const (
URLFilterIDCustom URLFilterID = 0
URLFilterIDEtcHosts URLFilterID = -1
URLFilterIDBlockedService URLFilterID = -2
URLFilterIDParentalControl URLFilterID = -3
URLFilterIDSafeBrowsing URLFilterID = -4
URLFilterIDSafeSearch URLFilterID = -5
)
// UID is the type for the unique IDs of filtering-rule lists.
type UID uuid.UUID

View File

@@ -1,19 +1,11 @@
package rulelist_test
import (
"io"
"net/http"
"net/http/httptest"
"net/url"
"os"
"path/filepath"
"sync/atomic"
"testing"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/filtering/rulelist"
"github.com/AdguardTeam/golibs/testutil"
"github.com/stretchr/testify/require"
)
func TestMain(m *testing.M) {
@@ -43,70 +35,3 @@ const (
// See https://github.com/AdguardTeam/AdGuardHome/issues/6003.
testRuleTextCosmetic = "||cosmetic.example## :has-text(/\u200c/i)\n"
)
// urlFilterIDCounter is the atomic integer used to create unique filter IDs.
var urlFilterIDCounter = &atomic.Int32{}
// newURLFilterID returns a new unique URLFilterID.
func newURLFilterID() (id rulelist.URLFilterID) {
return rulelist.URLFilterID(urlFilterIDCounter.Add(1))
}
// newFilter is a helper for creating new filters in tests. It does not
// register the closing of the filter using t.Cleanup; callers must do that
// either directly or by using the filter in an engine.
func newFilter(t testing.TB, u *url.URL, name string) (f *rulelist.Filter) {
t.Helper()
f, err := rulelist.NewFilter(&rulelist.FilterConfig{
URL: u,
Name: name,
UID: rulelist.MustNewUID(),
URLFilterID: newURLFilterID(),
Enabled: true,
})
require.NoError(t, err)
return f
}
// newFilterLocations is a test helper that sets up both the filtering-rule list
// file and the HTTP-server. It also registers file removal and server stopping
// using t.Cleanup.
func newFilterLocations(
t testing.TB,
cacheDir string,
fileData string,
httpData string,
) (fileURL, srvURL *url.URL) {
filePath := filepath.Join(cacheDir, "initial.txt")
err := os.WriteFile(filePath, []byte(fileData), 0o644)
require.NoError(t, err)
testutil.CleanupAndRequireSuccess(t, func() (err error) {
return os.Remove(filePath)
})
fileURL = &url.URL{
Scheme: "file",
Path: filePath,
}
srv := newStringHTTPServer(httpData)
t.Cleanup(srv.Close)
srvURL, err = url.Parse(srv.URL)
require.NoError(t, err)
return fileURL, srvURL
}
// newStringHTTPServer returns a new HTTP server that serves s.
func newStringHTTPServer(s string) (srv *httptest.Server) {
return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
pt := testutil.PanicT{}
_, err := io.WriteString(w, s)
require.NoError(pt, err)
}))
}

View File

@@ -1,98 +0,0 @@
package rulelist
import (
"fmt"
"strings"
"sync"
"github.com/AdguardTeam/urlfilter"
"github.com/AdguardTeam/urlfilter/filterlist"
)
// TextEngine is a single DNS filter based on a list of rules in text form.
type TextEngine struct {
// mu protects engine and storage.
mu *sync.RWMutex
// engine is the filtering engine.
engine *urlfilter.DNSEngine
// storage is the filtering-rule storage. It is saved here to close it.
storage *filterlist.RuleStorage
// name is the human-readable name of the engine, like "custom".
name string
}
// TextEngineConfig is the configuration for a rule-list filtering engine
// created from a filtering rule text.
type TextEngineConfig struct {
// Name is the human-readable name of this engine, like "allowed",
// "blocked", or "custom".
Name string
// Rules is the text of the filtering rules for this engine.
Rules []string
// ID is the ID to use inside a URL-filter engine.
ID URLFilterID
}
// NewTextEngine returns a new rule-list filtering engine that uses rules
// directly. The engine is ready to use and should not be refreshed.
func NewTextEngine(c *TextEngineConfig) (e *TextEngine, err error) {
text := strings.Join(c.Rules, "\n")
storage, err := filterlist.NewRuleStorage([]filterlist.RuleList{
&filterlist.StringRuleList{
RulesText: text,
ID: c.ID,
IgnoreCosmetic: true,
},
})
if err != nil {
return nil, fmt.Errorf("creating rule storage: %w", err)
}
engine := urlfilter.NewDNSEngine(storage)
return &TextEngine{
mu: &sync.RWMutex{},
engine: engine,
storage: storage,
name: c.Name,
}, nil
}
// FilterRequest returns the result of filtering req using the DNS filtering
// engine.
func (e *TextEngine) FilterRequest(
req *urlfilter.DNSRequest,
) (res *urlfilter.DNSResult, hasMatched bool) {
var engine *urlfilter.DNSEngine
func() {
e.mu.RLock()
defer e.mu.RUnlock()
engine = e.engine
}()
return engine.MatchRequest(req)
}
// Close closes the underlying rule list engine as well as the rule lists.
func (e *TextEngine) Close() (err error) {
e.mu.Lock()
defer e.mu.Unlock()
if e.storage == nil {
return nil
}
err = e.storage.Close()
if err != nil {
return fmt.Errorf("closing text engine %q: %w", e.name, err)
}
return nil
}

View File

@@ -1,40 +0,0 @@
package rulelist_test
import (
"testing"
"github.com/AdguardTeam/AdGuardHome/internal/filtering/rulelist"
"github.com/AdguardTeam/golibs/testutil"
"github.com/AdguardTeam/urlfilter"
"github.com/miekg/dns"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestNewTextEngine(t *testing.T) {
eng, err := rulelist.NewTextEngine(&rulelist.TextEngineConfig{
Name: "RulesEngine",
Rules: []string{
testRuleTextTitle,
testRuleTextBlocked,
},
ID: testURLFilterID,
})
require.NoError(t, err)
require.NotNil(t, eng)
testutil.CleanupAndRequireSuccess(t, eng.Close)
fltReq := &urlfilter.DNSRequest{
Hostname: "blocked.example",
Answer: false,
DNSType: dns.TypeA,
}
fltRes, hasMatched := eng.FilterRequest(fltReq)
assert.True(t, hasMatched)
require.NotNil(t, fltRes)
require.NotNil(t, fltRes.NetworkRule)
assert.Equal(t, fltRes.NetworkRule.FilterListID, testURLFilterID)
}

View File

@@ -1,5 +1,7 @@
package filtering
import "github.com/miekg/dns"
// SafeSearch interface describes a service for search engines hosts rewrites.
type SafeSearch interface {
// CheckHost checks host with safe search filter. CheckHost must be safe
@@ -14,6 +16,9 @@ type SafeSearch interface {
// SafeSearchConfig is a struct with safe search related settings.
type SafeSearchConfig struct {
// CustomResolver is the resolver used by safe search.
CustomResolver Resolver `yaml:"-" json:"-"`
// Enabled indicates if safe search is enabled entirely.
Enabled bool `yaml:"enabled" json:"enabled"`
@@ -35,7 +40,13 @@ func (d *DNSFilter) checkSafeSearch(
qtype uint16,
setts *Settings,
) (res Result, err error) {
if d.safeSearch == nil || !setts.ProtectionEnabled || !setts.SafeSearchEnabled {
if !setts.ProtectionEnabled ||
!setts.SafeSearchEnabled ||
(qtype != dns.TypeA && qtype != dns.TypeAAAA) {
return Result{}, nil
}
if d.safeSearch == nil {
return Result{}, nil
}

View File

@@ -3,16 +3,17 @@ package safesearch
import (
"bytes"
"context"
"encoding/binary"
"encoding/gob"
"fmt"
"net"
"net/netip"
"strings"
"sync"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/AdGuardHome/internal/filtering/rulelist"
"github.com/AdguardTeam/golibs/cache"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/urlfilter"
@@ -65,6 +66,7 @@ type Default struct {
engine *urlfilter.DNSEngine
cache cache.Cache
resolver filtering.Resolver
logPrefix string
cacheTTL time.Duration
}
@@ -77,6 +79,11 @@ func NewDefault(
cacheSize uint,
cacheTTL time.Duration,
) (ss *Default, err error) {
var resolver filtering.Resolver = net.DefaultResolver
if conf.CustomResolver != nil {
resolver = conf.CustomResolver
}
ss = &Default{
mu: &sync.RWMutex{},
@@ -84,13 +91,14 @@ func NewDefault(
EnableLRU: true,
MaxSize: cacheSize,
}),
resolver: resolver,
// Use %s, because the client safe-search names already contain double
// quotes.
logPrefix: fmt.Sprintf("safesearch %s: ", name),
cacheTTL: cacheTTL,
}
err = ss.resetEngine(rulelist.URLFilterIDSafeSearch, conf)
err = ss.resetEngine(filtering.SafeSearchListID, conf)
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return nil, err
@@ -161,11 +169,8 @@ func (ss *Default) CheckHost(host string, qtype rules.RRType) (res filtering.Res
ss.log(log.DEBUG, "lookup for %q finished in %s", host, time.Since(start))
}()
switch qtype {
case dns.TypeA, dns.TypeAAAA, dns.TypeHTTPS:
// Go on.
default:
return filtering.Result{}, nil
if qtype != dns.TypeA && qtype != dns.TypeAAAA {
return filtering.Result{}, fmt.Errorf("unsupported question type %s", dns.Type(qtype))
}
// Check cache. Return cached result if it was found
@@ -189,9 +194,6 @@ func (ss *Default) CheckHost(host string, qtype rules.RRType) (res filtering.Res
}
res = *fltRes
// TODO(a.garipov): Consider switch back to resolving CNAME records IPs and
// saving results to cache.
ss.setCacheResult(host, qtype, res)
return res, nil
@@ -220,13 +222,20 @@ func (ss *Default) searchHost(host string, qtype rules.RRType) (res *rules.DNSRe
}
// newResult creates Result object from rewrite rule. qtype must be either
// [dns.TypeA] or [dns.TypeAAAA], or [dns.TypeHTTPS]. If err is nil, res is
// never nil, so that the empty result is converted into a NODATA response.
// [dns.TypeA] or [dns.TypeAAAA]. If err is nil, res is never nil, so that the
// empty result is converted into a NODATA response.
//
// TODO(a.garipov): Use the main rewrite result mechanism used in
// [dnsforward.Server.filterDNSRequest]. Now we resolve IPs for CNAME to save
// them in the safe search cache.
func (ss *Default) newResult(
rewrite *rules.DNSRewrite,
qtype rules.RRType,
) (res *filtering.Result, err error) {
res = &filtering.Result{
Rules: []*filtering.ResultRule{{
FilterListID: filtering.SafeSearchListID,
}},
Reason: filtering.FilteredSafeSearch,
IsFiltered: true,
}
@@ -237,19 +246,69 @@ func (ss *Default) newResult(
return nil, fmt.Errorf("expected ip rewrite value, got %T(%[1]v)", rewrite.Value)
}
res.Rules = []*filtering.ResultRule{{
FilterListID: rulelist.URLFilterIDSafeSearch,
IP: ip,
}}
res.Rules[0].IP = ip
return res, nil
}
res.CanonName = rewrite.NewCNAME
host := rewrite.NewCNAME
if host == "" {
return res, nil
}
res.CanonName = host
ss.log(log.DEBUG, "resolving %q", host)
ips, err := ss.resolver.LookupIP(context.Background(), qtypeToProto(qtype), host)
if err != nil {
return nil, fmt.Errorf("resolving cname: %w", err)
}
ss.log(log.DEBUG, "resolved %s", ips)
for _, ip := range ips {
// TODO(a.garipov): Remove this filtering once the resolver we use
// actually learns about network.
addr := fitToProto(ip, qtype)
if addr == (netip.Addr{}) {
continue
}
// TODO(e.burkov): Rules[0]?
res.Rules[0].IP = addr
}
return res, nil
}
// qtypeToProto returns "ip4" for [dns.TypeA] and "ip6" for [dns.TypeAAAA].
// It panics for other types.
func qtypeToProto(qtype rules.RRType) (proto string) {
switch qtype {
case dns.TypeA:
return "ip4"
case dns.TypeAAAA:
return "ip6"
default:
panic(fmt.Errorf("safesearch: unsupported question type %s", dns.Type(qtype)))
}
}
// fitToProto returns a non-nil IP address if ip is the correct protocol version
// for qtype. qtype is expected to be either [dns.TypeA] or [dns.TypeAAAA].
func fitToProto(ip net.IP, qtype rules.RRType) (res netip.Addr) {
if ip4 := ip.To4(); qtype == dns.TypeA {
if ip4 != nil {
return netip.AddrFrom4([4]byte(ip4))
}
} else if ip = ip.To16(); ip != nil && qtype == dns.TypeAAAA {
return netip.AddrFrom16([16]byte(ip))
}
return netip.Addr{}
}
// setCacheResult stores data in cache for host. qtype is expected to be either
// [dns.TypeA] or [dns.TypeAAAA].
func (ss *Default) setCacheResult(host string, qtype rules.RRType, res filtering.Result) {
@@ -309,7 +368,7 @@ func (ss *Default) Update(conf filtering.SafeSearchConfig) (err error) {
ss.mu.Lock()
defer ss.mu.Unlock()
err = ss.resetEngine(rulelist.URLFilterIDSafeSearch, conf)
err = ss.resetEngine(filtering.SafeSearchListID, conf)
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return err

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