Compare commits

..

229 Commits

Author SHA1 Message Date
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
126 changed files with 1438 additions and 3565 deletions

View File

@@ -30,27 +30,13 @@ NOTE: Add new changes BELOW THIS COMMENT.
- Ability to define custom directories for storage of query log files and
statistics ([#5992]).
### Changed
- Private RDNS resolution (`dns.use_private_ptr_resolvers` in YAML
configuration) now requires a valid "Private reverse DNS servers", when
enabled ([#6820]).
**NOTE:** Disabling private RDNS resolution behaves effectively the same as if
no private reverse DNS servers provided by user and by the OS.
### Fixed
- Statistics for 7 days displayed by day on the dashboard graph ([#6712]).
- Missing "served from cache" label on long DNS server strings ([#6740]).
- Incorrect tracking of the system hosts file's changes ([#6711]).
[#5992]: https://github.com/AdguardTeam/AdGuardHome/issues/5992
[#6610]: https://github.com/AdguardTeam/AdGuardHome/issues/6610
[#6711]: https://github.com/AdguardTeam/AdGuardHome/issues/6711
[#6712]: https://github.com/AdguardTeam/AdGuardHome/issues/6712
[#6740]: https://github.com/AdguardTeam/AdGuardHome/issues/6740
[#6820]: https://github.com/AdguardTeam/AdGuardHome/issues/6820
<!--
NOTE: Add new changes ABOVE THIS COMMENT.

View File

@@ -473,9 +473,6 @@ bug or implementing the feature.
[@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).
* [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)

View File

@@ -7,9 +7,7 @@
# Make sure to sync any changes with the branch overrides below.
'variables':
'channel': 'edge'
# TODO(a.garipov): Split away the frontend image.
'dockerFrontend': 'adguard/golang-ubuntu:9.0'
'dockerGo': 'adguard/go-builder:1.21.8--1'
'dockerGo': 'adguard/golang-ubuntu:8.1'
'stages':
- 'Build frontend':
@@ -42,11 +40,9 @@
'jobs':
- 'Publish to GitHub Releases'
# TODO(e.burkov): In jobs below find out why the explicit checkout is
# performed.
'Build frontend':
'docker':
'image': '${bamboo.dockerFrontend}'
'image': '${bamboo.dockerGo}'
'volumes':
'${system.YARN_DIR}': '${bamboo.cacheYarn}'
'key': 'BF'
@@ -276,8 +272,7 @@
# need to build a few of these.
'variables':
'channel': 'beta'
'dockerFrontend': 'adguard/golang-ubuntu:9.0'
'dockerGo': 'adguard/go-builder:1.21.8--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]+':
@@ -292,5 +287,4 @@
# are the ones that actually get released.
'variables':
'channel': 'release'
'dockerFrontend': 'adguard/golang-ubuntu:9.0'
'dockerGo': 'adguard/go-builder:1.21.8--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.0'
'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
@@ -191,7 +191,7 @@
# need to build a few of these.
'variables':
'channel': 'beta'
'dockerSnap': 'adguard/snap-builder:1.0'
'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.0'
'dockerGo': 'adguard/golang-ubuntu:8.1'
'snapcraftChannel': 'candidate'

View File

@@ -5,8 +5,7 @@
'key': 'AHBRTSPECS'
'name': 'AdGuard Home - Build and run tests'
'variables':
# TODO(a.garipov): Split away the frontend image and stages.
'dockerGo': 'adguard/golang-ubuntu:9.0'
'dockerGo': 'adguard/golang-ubuntu:8.1'
'channel': 'development'
'stages':
@@ -69,6 +68,9 @@
set -e -f -u -x
# Explicitly checkout the revision that we need.
git checkout "${bamboo.repository.revision.number}"
make\
ARCH="amd64"\
OS="windows darwin linux"\
@@ -120,8 +122,10 @@
# from the release branch and are used to build the release candidate
# images.
- '^rc-v[0-9]+\.[0-9]+\.[0-9]+':
# Build betas on release branches manually.
'triggers': []
# Set the default release channel on the release branch to beta, as we
# may need to build a few of these.
'variables':
'dockerGo': 'adguard/golang-ubuntu:9.0'
'dockerGo': 'adguard/golang-ubuntu:8.1'
'channel': 'candidate'

View File

@@ -678,7 +678,7 @@
"use_saved_key": "Použít dříve uložený klíče",
"parental_control": "Rodičovská ochrana",
"safe_browsing": "Bezpečné prohlížení",
"served_from_cache_label": "Převzato z mezipaměti",
"served_from_cache": "{{value}} <i>(převzato z mezipaměti)</i>",
"form_error_password_length": "Heslo musí obsahovat od {{min}} do {{max}} znaků",
"anonymizer_notification": "<0>Poznámka:</0> Anonymizace IP je zapnuta. Můžete ji vypnout v <1>Obecných nastaveních</1>.",
"confirm_dns_cache_clear": "Opravdu chcete vymazat mezipaměť DNS?",

View File

@@ -678,7 +678,7 @@
"use_saved_key": "Brug den tidligere gemte nøgle",
"parental_control": "Forældrekontrol",
"safe_browsing": "Sikker Browsing",
"served_from_cache_label": "Leveret fra cache",
"served_from_cache": "{{value}} <i>(leveret fra cache)</i>",
"form_error_password_length": "Adgangskode skal udgøre fra {{min}} til {{max}} tegn",
"anonymizer_notification": "<0>Bemærk:</0> IP-anonymisering er aktiveret. Det kan deaktiveres via <1>Generelle indstillinger</1>.",
"confirm_dns_cache_clear": "Sikker på, at DNS-cache skal ryddes?",

View File

@@ -678,7 +678,7 @@
"use_saved_key": "Zuvor gespeicherten Schlüssel verwenden",
"parental_control": "Kindersicherung",
"safe_browsing": "Internetsicherheit",
"served_from_cache_label": "Aus dem Cache abgerufen",
"served_from_cache": "{{value}} <i>(aus dem Cache abgerufen)</i>",
"form_error_password_length": "Das Passwort muss zwischen {{min}} und {{max}} Zeichen enthalten",
"anonymizer_notification": "<0>Hinweis:</0> Die IP-Anonymisierung ist aktiviert. Sie können sie in den <1>Allgemeinen Einstellungen</1> deaktivieren.",
"confirm_dns_cache_clear": "Möchten Sie den DNS-Cache wirklich leeren?",

View File

@@ -678,7 +678,7 @@
"use_saved_key": "Use the previously saved key",
"parental_control": "Parental Control",
"safe_browsing": "Safe Browsing",
"served_from_cache_label": "Served from cache",
"served_from_cache": "{{value}} <i>(served from cache)</i>",
"form_error_password_length": "Password must be {{min}} to {{max}} characters long",
"anonymizer_notification": "<0>Note:</0> IP anonymization is enabled. You can disable it in <1>General settings</1>.",
"confirm_dns_cache_clear": "Are you sure you want to clear DNS cache?",

View File

@@ -678,7 +678,7 @@
"use_saved_key": "Usar la clave guardada previamente",
"parental_control": "Control parental",
"safe_browsing": "Navegación segura",
"served_from_cache_label": "Servido desde la caché",
"served_from_cache": "{{value}} <i>(servido desde la caché)</i>",
"form_error_password_length": "La contraseña debe tener entre {{min}} y {{max}} caracteres",
"anonymizer_notification": "<0>Nota:</0> La anonimización de IP está habilitada. Puedes deshabilitarla en <1>Configuración general</1>.",
"confirm_dns_cache_clear": "¿Estás seguro de que deseas borrar la caché DNS?",

View File

@@ -678,7 +678,7 @@
"use_saved_key": "Utiliser la clef précédemment enregistrée",
"parental_control": "Contrôle parental",
"safe_browsing": "Navigation sécurisée",
"served_from_cache_label": "Servi depuis le cache",
"served_from_cache": "{{value}} <i>(depuis le cache)</i>",
"form_error_password_length": "Le mot de passe doit comporter entre {{min}} et {{max}}  caractères",
"anonymizer_notification": "<0>Note :</0> L'anonymisation IP est activée. Vous pouvez la désactiver dans les <1>paramètres généraux</1>.",
"confirm_dns_cache_clear": "Voulez-vous vraiment vider le cache DNS ?",

View File

@@ -678,7 +678,7 @@
"use_saved_key": "Utilizza la chiave salvata in precedenza",
"parental_control": "Controllo Parentale",
"safe_browsing": "Navigazione Sicura",
"served_from_cache_label": "Servito dalla cache",
"served_from_cache": "{{value}} <i>(fornito dalla cache)</i>",
"form_error_password_length": "La password deve contenere da {{min}} a {{max}} caratteri",
"anonymizer_notification": "<0>Attenzione:</0> L'anonimizzazione dell'IP è abilitata. Puoi disabilitarla in <1>Impostazioni generali</1>.",
"confirm_dns_cache_clear": "Sei sicuro di voler cancellare la cache DNS?",

View File

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

View File

@@ -678,7 +678,7 @@
"use_saved_key": "이전에 저장했던 키 사용하기",
"parental_control": "자녀 보호",
"safe_browsing": "세이프 브라우징",
"served_from_cache_label": "캐시에서 가져옴",
"served_from_cache": "{{value}} <i>(캐시에서 제공)</i>",
"form_error_password_length": "비밀번호는 {{min}}~{{max}}자 길이여야 합니다.",
"anonymizer_notification": "<0>참고:</0> IP 익명화가 활성화되었습니다. <1>일반 설정</1>에서 비활성화할 수 있습니다.",
"confirm_dns_cache_clear": "정말로 DNS 캐시를 지우시겠습니까?",

View File

@@ -678,7 +678,7 @@
"use_saved_key": "De eerder opgeslagen sleutel gebruiken",
"parental_control": "Ouderlijk toezicht",
"safe_browsing": "Veilig browsen",
"served_from_cache_label": "Geleverd vanuit cache",
"served_from_cache": "{{value}} <i>(geleverd vanuit cache)</i>",
"form_error_password_length": "Wachtwoord moet {{min}} tot {{max}} tekens lang zijn",
"anonymizer_notification": "<0>Opmerking:</0> IP-anonimisering is ingeschakeld. Je kunt het uitschakelen in <1>Algemene instellingen</1>.",
"confirm_dns_cache_clear": "Weet je zeker dat je de DNS-cache wilt wissen?",

View File

@@ -678,7 +678,7 @@
"use_saved_key": "Use a chave salva anteriormente",
"parental_control": "Controle parental",
"safe_browsing": "Navegação segura",
"served_from_cache_label": "Servido a partir do cache",
"served_from_cache": "{{value}} <i>(servido do cache)</i>",
"form_error_password_length": "A senha deve ter entre {{min}} e {{max}} caracteres",
"anonymizer_notification": "<0>Observação:</0> A anonimização de IP está ativada. Você pode desativá-lo em <1>Configurações gerais</1>.",
"confirm_dns_cache_clear": "Tem certeza de que deseja limpar o cache DNS?",

View File

@@ -678,7 +678,7 @@
"use_saved_key": "Use a chave guardada anteriormente",
"parental_control": "Controlo parental",
"safe_browsing": "Navegação segura",
"served_from_cache_label": "Servido a partir do cache",
"served_from_cache": "{{value}} <i>(servido do cache)</i>",
"form_error_password_length": "A palavra-passe deve ter {{min}} a {{max}} caracteres",
"anonymizer_notification": "<0>Observação:</0> A anonimização de IP está ativada. Você pode desativá-la em <1>Definições gerais</1>.",
"confirm_dns_cache_clear": "Tem certeza de que quer limpar a cache DNS?",

View File

@@ -678,7 +678,7 @@
"use_saved_key": "Использовать сохранённый ранее ключ",
"parental_control": "Родительский контроль",
"safe_browsing": "Безопасный интернет",
"served_from_cache_label": "Получено из кеша",
"served_from_cache": "{{value}} <i>(получено из кеша)</i>",
"form_error_password_length": "Пароль должен содержать от {{min}} до {{max}} символов",
"anonymizer_notification": "<0>Внимание:</0> включена анонимизация IP-адресов. Вы можете отключить её в разделе <1>Основные настройки</1>.",
"confirm_dns_cache_clear": "Вы уверены, что хотите очистить кеш DNS?",

View File

@@ -678,7 +678,7 @@
"use_saved_key": "Použiť predtým uložený kľúč",
"parental_control": "Rodičovská kontrola",
"safe_browsing": "Bezpečné prehliadanie",
"served_from_cache_label": "Prevzaté z cache pamäte",
"served_from_cache": "{{value}} <i>(prevzatá z cache pamäte)</i>",
"form_error_password_length": "Heslo musí mať od {{min}} do {{max}} znakov",
"anonymizer_notification": "<0>Poznámka:</0> Anonymizácia IP je zapnutá. Môžete ju vypnúť vo <1>Všeobecných nastaveniach</1>.",
"confirm_dns_cache_clear": "Naozaj chcete vymazať vyrovnávaciu pamäť DNS?",

View File

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

View File

@@ -678,7 +678,7 @@
"use_saved_key": "Önceden kaydedilmiş anahtarı kullan",
"parental_control": "Ebeveyn Denetimi",
"safe_browsing": "Güvenli Gezinti",
"served_from_cache_label": "Önbellekten kullanıldı",
"served_from_cache": "{{value}} <i>(önbellekten kullanıldı)</i>",
"form_error_password_length": "Parola {{min}} ila {{max}} karakter uzunluğunda olmalıdır",
"anonymizer_notification": "<0>Not:</0> IP anonimleştirme etkinleştirildi. Bunu <1>Genel ayarlardan</1> devre dışı bırakabilirsiniz.",
"confirm_dns_cache_clear": "DNS önbelleğini temizlemek istediğinizden emin misiniz?",

View File

@@ -678,7 +678,7 @@
"use_saved_key": "Використати раніше збережений ключ",
"parental_control": "Батьківський контроль",
"safe_browsing": "Безпечний перегляд",
"served_from_cache_label": "Отримано з кешу",
"served_from_cache": "{{value}} <i>(отримано з кешу)</i>",
"form_error_password_length": "Пароль має містити від {{min}} до {{max}} символів",
"anonymizer_notification": "<0>Примітка:</0> IP-анонімізацію ввімкнено. Ви можете вимкнути його в <1>Загальні налаштування</1> .",
"confirm_dns_cache_clear": "Ви впевнені, що бажаєте очистити кеш DNS?",

View File

@@ -678,7 +678,7 @@
"use_saved_key": "使用之前保存的密钥",
"parental_control": "家长控制",
"safe_browsing": "安全浏览",
"served_from_cache_label": "从缓存中",
"served_from_cache": "{{value}}<i>(由缓存提供)</i>",
"form_error_password_length": "密码长度必须为 {{min}} 到 {{max}} 个字符",
"anonymizer_notification": "<0>注意:</0> IP 匿名化已启用。您可以在<1>常规设置</1>中禁用它。",
"confirm_dns_cache_clear": "您确定要清除 DNS 缓存吗?",

View File

@@ -678,7 +678,7 @@
"use_saved_key": "使用該先前已儲存的金鑰",
"parental_control": "家長控制",
"safe_browsing": "安全瀏覽",
"served_from_cache_label": "從快取中",
"served_from_cache": "{{value}} <i>(由快取提供)</i>",
"form_error_password_length": "密碼長度必須為 {{min}} 到 {{max}} 個字符",
"anonymizer_notification": "<0>注意:</0>IP 匿名化被啟用。您可在<1>一般設定</1>中禁用它。",
"confirm_dns_cache_clear": "您確定您想要清除 DNS 快取嗎?",

View File

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

View File

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

View File

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

View File

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

26
go.mod
View File

@@ -3,8 +3,8 @@ module github.com/AdguardTeam/AdGuardHome
go 1.21.8
require (
github.com/AdguardTeam/dnsproxy v0.66.0
github.com/AdguardTeam/golibs v0.20.2
github.com/AdguardTeam/dnsproxy v0.65.2
github.com/AdguardTeam/golibs v0.20.1
github.com/AdguardTeam/urlfilter v0.18.0
github.com/NYTimes/gziphandler v1.1.1
github.com/ameshkov/dnscrypt/v2 v2.2.7
@@ -18,7 +18,7 @@ require (
github.com/google/gopacket v1.1.19
github.com/google/renameio/v2 v2.0.0
github.com/google/uuid v1.6.0
github.com/insomniacslk/dhcp v0.0.0-20240227161007-c728f5dd21c8
github.com/insomniacslk/dhcp v0.0.0-20240204152450-ca2dc33955c1
github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86
github.com/kardianos/service v1.2.2
github.com/mdlayher/ethernet v0.0.0-20220221185849-529eae5b6118
@@ -31,11 +31,11 @@ require (
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.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
go.etcd.io/bbolt v1.3.8
golang.org/x/crypto v0.19.0
golang.org/x/exp v0.0.0-20240205201215-2c58cdc269a3
golang.org/x/net v0.21.0
golang.org/x/sys v0.17.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1
gopkg.in/yaml.v3 v3.0.1
howett.net/plist v1.0.1
@@ -48,19 +48,19 @@ require (
github.com/beefsack/go-rate v0.0.0-20220214233405-116f4ca011a0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7 // indirect
github.com/google/pprof v0.0.0-20240207164012-fb44976bdcd5 // indirect
github.com/mdlayher/socket v0.5.0 // indirect
github.com/onsi/ginkgo/v2 v2.16.0 // indirect
github.com/onsi/ginkgo/v2 v2.15.0 // indirect
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
github.com/pierrec/lz4/v4 v4.1.21 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/quic-go/qpack v0.4.0 // indirect
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 // indirect
github.com/u-root/uio v0.0.0-20240207234124-abbebccef0fd // indirect
go.uber.org/mock v0.4.0 // indirect
golang.org/x/mod v0.16.0 // indirect
golang.org/x/mod v0.15.0 // indirect
golang.org/x/sync v0.6.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/tools v0.19.0 // indirect
golang.org/x/tools v0.18.0 // indirect
gonum.org/v1/gonum v0.14.0 // indirect
)

56
go.sum
View File

@@ -1,7 +1,7 @@
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/dnsproxy v0.65.2 h1:D+BMw0Vu2lbQrYpoPctG2Xr+24KdfhgkzZb6QgPZheM=
github.com/AdguardTeam/dnsproxy v0.65.2/go.mod h1:8NQTTNZY+qR9O1Fzgz3WQv30knfSgms68SRlzSnX74A=
github.com/AdguardTeam/golibs v0.20.1 h1:ol8qLjWGZhU9paMMwN+OLWVTUigGsXa29iVTyd62VKY=
github.com/AdguardTeam/golibs v0.20.1/go.mod h1:bgcMgRviCKyU6mkrX+RtT/OsKPFzyppelfRsksMG3KU=
github.com/AdguardTeam/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=
@@ -29,8 +29,8 @@ github.com/dimfeld/httptreemux/v5 v5.5.0 h1:p8jkiMrCuZ0CmhwYLcbNbl7DDo21fozhKHQ2
github.com/dimfeld/httptreemux/v5 v5.5.0/go.mod h1:QeEylH57C0v3VO0tkKraVz9oD3Uu93CKPnTLbsidvSw=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY=
github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-ping/ping v1.1.0 h1:3MCGhVX4fyEUuhsfwPrsEdQw6xspHkv5zHsiSoDFZYw=
@@ -46,8 +46,8 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7 h1:y3N7Bm7Y9/CtpiVkw/ZWj6lSlDF3F74SfKwfTCer72Q=
github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
github.com/google/pprof v0.0.0-20240207164012-fb44976bdcd5 h1:E/LAvt58di64hlYjx7AsNS6C/ysHWYo+2qPCZKTQhRo=
github.com/google/pprof v0.0.0-20240207164012-fb44976bdcd5/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
github.com/google/renameio/v2 v2.0.0 h1:UifI23ZTGY8Tt29JbYFiuyIU3eX+RNFtUwefq9qAhxg=
github.com/google/renameio/v2 v2.0.0/go.mod h1:BtmJXm5YlszgC+TD4HOEEUFgkJP3nLxehU6hfe7jRt4=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@@ -55,8 +55,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714 h1:/jC7qQFrv8CrSJVmaolDVOxTfS9kc36uB6H40kdbQq8=
github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis=
github.com/insomniacslk/dhcp v0.0.0-20240227161007-c728f5dd21c8 h1:V3plQrMHRWOB5zMm3yNqvBxDQVW1+/wHBSok5uPdmVs=
github.com/insomniacslk/dhcp v0.0.0-20240227161007-c728f5dd21c8/go.mod h1:izxuNQZeFrbx2nK2fAyN5iNUB34Fe9j0nK4PwLzAkKw=
github.com/insomniacslk/dhcp v0.0.0-20240204152450-ca2dc33955c1 h1:L3pm9Kf2G6gJVYawz2SrI5QnV1wzHYbqmKnSHHXJAb8=
github.com/insomniacslk/dhcp v0.0.0-20240204152450-ca2dc33955c1/go.mod h1:izxuNQZeFrbx2nK2fAyN5iNUB34Fe9j0nK4PwLzAkKw=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/josharian/native v1.0.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86 h1:elKwZS1OcdQ0WwEDBeqxKwb7WB62QX8bvZ/FJnVXIfk=
@@ -84,8 +84,8 @@ github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4=
github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/onsi/ginkgo/v2 v2.16.0 h1:7q1w9frJDzninhXxjZd+Y/x54XNjG/UlRLIYPZafsPM=
github.com/onsi/ginkgo/v2 v2.16.0/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs=
github.com/onsi/ginkgo/v2 v2.15.0 h1:79HwNRBAZHOEwrczrgSOPy+eFTTlIGELKy5as+ClttY=
github.com/onsi/ginkgo/v2 v2.15.0/go.mod h1:HlxMHtYF57y6Dpf+mc5529KKmSq9h2FpCF+/ZkwUxKM=
github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8=
github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
@@ -121,32 +121,32 @@ github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+Kd
github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI=
github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms=
github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4=
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 h1:pyC9PaHYZFgEKFdlp3G8RaCKgVpHZnecvArXvPXcFkM=
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701/go.mod h1:P3a5rG4X7tI17Nn3aOIAYr5HbIMukwXG0urG0WuL8OA=
github.com/u-root/uio v0.0.0-20240207234124-abbebccef0fd h1:BQJh5fdHsPa/YuMVrbcSxQKuowGCHYh0GD7hvLaHBK0=
github.com/u-root/uio v0.0.0-20240207234124-abbebccef0fd/go.mod h1:P3a5rG4X7tI17Nn3aOIAYr5HbIMukwXG0urG0WuL8OA=
github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw=
github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI=
go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE=
go.etcd.io/bbolt v1.3.8 h1:xs88BrvEv273UsB79e0hcVrlUWmS0a8upikMFhSyAtA=
go.etcd.io/bbolt v1.3.8/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
go.uber.org/mock v0.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.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/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/exp v0.0.0-20240205201215-2c58cdc269a3 h1:/RIbNt/Zr7rVhIkQhooTxCxFcdWLGIKnZA4IXNFSrvo=
golang.org/x/exp v0.0.0-20240205201215-2c58cdc269a3/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08=
golang.org/x/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.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic=
golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/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.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
@@ -161,8 +161,8 @@ 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.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/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=
@@ -170,8 +170,8 @@ golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw=
golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc=
golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ=
golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg=
golang.org/x/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

@@ -5,9 +5,9 @@ package aghalg
import (
"fmt"
"slices"
"golang.org/x/exp/constraints"
"golang.org/x/exp/slices"
)
// Coalesce returns the first non-zero value. It is named after function

View File

@@ -1,11 +1,11 @@
package aghalg_test
import (
"slices"
"testing"
"github.com/AdguardTeam/AdGuardHome/internal/aghalg"
"github.com/stretchr/testify/assert"
"golang.org/x/exp/slices"
)
// elements is a helper function that returns n elements of the buffer.

View File

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

View File

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

View File

@@ -154,8 +154,8 @@ func pathsToPatterns(fsys fs.FS, paths []string) (patterns []string, err error)
}
// handleEvents concurrently handles the file system events. It closes the
// update channel of HostsContainer when finishes. It is intended to be used as
// a goroutine.
// update channel of HostsContainer when finishes. It's used to be called
// within a separate goroutine.
func (hc *HostsContainer) handleEvents() {
defer log.OnPanic(fmt.Sprintf("%s: handling events", hostsContainerPrefix))

View File

@@ -67,7 +67,6 @@ func TestNewHostsContainer(t *testing.T) {
}
hc, err := aghnet.NewHostsContainer(testFS, &aghtest.FSWatcher{
OnStart: func() (_ error) { panic("not implemented") },
OnEvents: onEvents,
OnAdd: onAdd,
OnClose: func() (err error) { return nil },
@@ -94,7 +93,6 @@ func TestNewHostsContainer(t *testing.T) {
t.Run("nil_fs", func(t *testing.T) {
require.Panics(t, func() {
_, _ = aghnet.NewHostsContainer(nil, &aghtest.FSWatcher{
OnStart: func() (_ error) { panic("not implemented") },
// Those shouldn't panic.
OnEvents: func() (e <-chan struct{}) { return nil },
OnAdd: func(name string) (err error) { return nil },
@@ -113,7 +111,6 @@ func TestNewHostsContainer(t *testing.T) {
const errOnAdd errors.Error = "error"
errWatcher := &aghtest.FSWatcher{
OnStart: func() (_ error) { panic("not implemented") },
OnEvents: func() (e <-chan struct{}) { panic("not implemented") },
OnAdd: func(name string) (err error) { return errOnAdd },
OnClose: func() (err error) { return nil },
@@ -158,7 +155,6 @@ func TestHostsContainer_refresh(t *testing.T) {
t.Cleanup(func() { close(eventsCh) })
w := &aghtest.FSWatcher{
OnStart: func() (_ error) { panic("not implemented") },
OnEvents: func() (e <-chan event) { return eventsCh },
OnAdd: func(name string) (err error) {
assert.Equal(t, "dir", name)

View File

@@ -1,11 +1,11 @@
package aghnet
import (
"slices"
"strings"
"github.com/AdguardTeam/urlfilter"
"github.com/AdguardTeam/urlfilter/filterlist"
"golang.org/x/exp/slices"
)
// IgnoreEngine contains the list of rules for ignoring hostnames and matches

View File

@@ -17,7 +17,6 @@ import (
"github.com/AdguardTeam/dnsproxy/upstream"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/osutil"
)
// DialContextFunc is the semantic alias for dialing functions, such as
@@ -33,7 +32,7 @@ var (
netInterfaceAddrs = net.InterfaceAddrs
// rootDirFS is the filesystem pointing to the root directory.
rootDirFS = osutil.RootDirFS()
rootDirFS = aghos.RootDirFS()
)
// ErrNoStaticIPInfo is returned by IfaceHasStaticIP when no information about

View File

@@ -8,8 +8,6 @@ import (
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/osutil"
"github.com/AdguardTeam/golibs/stringutil"
"github.com/fsnotify/fsnotify"
)
@@ -20,38 +18,31 @@ type event = struct{}
// FSWatcher tracks all the fyle system events and notifies about those.
//
// TODO(e.burkov, a.garipov): Move into another package like aghfs.
//
// TODO(e.burkov): Add tests.
type FSWatcher interface {
// Start starts watching the added files.
Start() (err error)
// Close stops watching the files and closes an update channel.
io.Closer
// Events returns the channel to notify about the file system events.
// Events should return a read-only channel which notifies about events.
Events() (e <-chan event)
// Add starts tracking the file. It returns an error if the file can't be
// tracked. It must not be called after Start.
// Add should check if the file named name is accessible and starts tracking
// it.
Add(name string) (err error)
}
// osWatcher tracks the file system provided by the OS.
type osWatcher struct {
// watcher is the actual notifier that is handled by osWatcher.
watcher *fsnotify.Watcher
// w is the actual notifier that is handled by osWatcher.
w *fsnotify.Watcher
// events is the channel to notify.
events chan event
// files is the set of tracked files.
files *stringutil.Set
}
// osWatcherPref is a prefix for logging and wrapping errors in osWathcer's
// methods.
const osWatcherPref = "os watcher"
const (
// osWatcherPref is a prefix for logging and wrapping errors in osWathcer's
// methods.
osWatcherPref = "os watcher"
)
// NewOSWritesWatcher creates FSWatcher that tracks the real file system of the
// OS and notifies only about writing events.
@@ -64,27 +55,25 @@ func NewOSWritesWatcher() (w FSWatcher, err error) {
return nil, fmt.Errorf("creating watcher: %w", err)
}
return &osWatcher{
watcher: watcher,
events: make(chan event, 1),
files: stringutil.NewSet(),
}, nil
fsw := &osWatcher{
w: watcher,
events: make(chan event, 1),
}
go fsw.handleErrors()
go fsw.handleEvents()
return fsw, nil
}
// type check
var _ FSWatcher = (*osWatcher)(nil)
// handleErrors handles accompanying errors. It used to be called in a separate
// goroutine.
func (w *osWatcher) handleErrors() {
defer log.OnPanic(fmt.Sprintf("%s: handling errors", osWatcherPref))
// Start implements the FSWatcher interface for *osWatcher.
func (w *osWatcher) Start() (err error) {
go w.handleErrors()
go w.handleEvents()
return nil
}
// Close implements the FSWatcher interface for *osWatcher.
func (w *osWatcher) Close() (err error) {
return w.watcher.Close()
for err := range w.w.Errors {
log.Error("%s: %s", osWatcherPref, err)
}
}
// Events implements the FSWatcher interface for *osWatcher.
@@ -92,42 +81,34 @@ func (w *osWatcher) Events() (e <-chan event) {
return w.events
}
// Add implements the [FSWatcher] interface for *osWatcher.
// Add implements the FSWatcher interface for *osWatcher.
//
// TODO(e.burkov): Make it accept non-existing files to detect it's creating.
func (w *osWatcher) Add(name string) (err error) {
defer func() { err = errors.Annotate(err, "%s: %w", osWatcherPref) }()
fi, err := fs.Stat(osutil.RootDirFS(), name)
if err != nil {
if _, err = fs.Stat(RootDirFS(), name); err != nil {
return fmt.Errorf("checking file %q: %w", name, err)
}
name = filepath.Join("/", name)
w.files.Add(name)
return w.w.Add(filepath.Join("/", name))
}
// Watch the directory and filter the events by the file name, since the
// common recomendation to the fsnotify package is to watch the directory
// instead of the file itself.
//
// See https://pkg.go.dev/github.com/fsnotify/fsnotify@v1.7.0#readme-watching-a-file-doesn-t-work-well.
if !fi.IsDir() {
name = filepath.Dir(name)
}
return w.watcher.Add(name)
// Close implements the FSWatcher interface for *osWatcher.
func (w *osWatcher) Close() (err error) {
return w.w.Close()
}
// handleEvents notifies about the received file system's event if needed. It
// is intended to be used as a goroutine.
// used to be called in a separate goroutine.
func (w *osWatcher) handleEvents() {
defer log.OnPanic(fmt.Sprintf("%s: handling events", osWatcherPref))
defer close(w.events)
ch := w.watcher.Events
ch := w.w.Events
for e := range ch {
if e.Op&fsnotify.Write == 0 || !w.files.Has(e.Name) {
if e.Op&fsnotify.Write == 0 {
continue
}
@@ -150,13 +131,3 @@ func (w *osWatcher) handleEvents() {
}
}
}
// handleErrors handles accompanying errors. It used to be called in a separate
// goroutine.
func (w *osWatcher) handleErrors() {
defer log.OnPanic(fmt.Sprintf("%s: handling errors", osWatcherPref))
for err := range w.watcher.Errors {
log.Error("%s: %s", osWatcherPref, err)
}
}

View File

@@ -7,16 +7,17 @@ import (
"bufio"
"fmt"
"io"
"io/fs"
"os"
"os/exec"
"path"
"runtime"
"slices"
"strconv"
"strings"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"golang.org/x/exp/slices"
)
// UnsupportedError is returned by functions and methods when a particular
@@ -154,6 +155,13 @@ func IsOpenWrt() (ok bool) {
return isOpenWrt()
}
// RootDirFS returns the [fs.FS] rooted at the operating system's root. On
// Windows it returns the fs.FS rooted at the volume of the system directory
// (usually, C:).
func RootDirFS() (fsys fs.FS) {
return rootDirFS()
}
// NotifyReconfigureSignal notifies c on receiving reconfigure signals.
func NotifyReconfigureSignal(c chan<- os.Signal) {
notifyReconfigureSignal(c)

View File

@@ -7,7 +7,6 @@ import (
"os"
"syscall"
"github.com/AdguardTeam/golibs/osutil"
"github.com/AdguardTeam/golibs/stringutil"
)
@@ -41,7 +40,7 @@ func isOpenWrt() (ok bool) {
}
return nil, !stringutil.ContainsFold(string(data), osNameData), nil
}).Walk(osutil.RootDirFS(), etcReleasePattern)
}).Walk(RootDirFS(), etcReleasePattern)
return err == nil && ok
}

View File

@@ -3,12 +3,17 @@
package aghos
import (
"io/fs"
"os"
"os/signal"
"golang.org/x/sys/unix"
)
func rootDirFS() (fsys fs.FS) {
return os.DirFS("/")
}
func notifyReconfigureSignal(c chan<- os.Signal) {
signal.Notify(c, unix.SIGHUP)
}

View File

@@ -3,13 +3,29 @@
package aghos
import (
"io/fs"
"os"
"os/signal"
"path/filepath"
"syscall"
"github.com/AdguardTeam/golibs/log"
"golang.org/x/sys/windows"
)
func rootDirFS() (fsys fs.FS) {
// TODO(a.garipov): Use a better way if golang/go#44279 is ever resolved.
sysDir, err := windows.GetSystemDirectory()
if err != nil {
log.Error("aghos: getting root filesystem: %s; using C:", err)
// Assume that C: is the safe default.
return os.DirFS("C:")
}
return os.DirFS(filepath.VolumeName(sysDir))
}
func setRlimit(val uint64) (err error) {
return Unsupported("setrlimit")
}

View File

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

View File

@@ -7,6 +7,7 @@ import (
"time"
"github.com/AdguardTeam/AdGuardHome/internal/aghos"
"github.com/AdguardTeam/AdGuardHome/internal/client"
"github.com/AdguardTeam/AdGuardHome/internal/next/agh"
"github.com/AdguardTeam/AdGuardHome/internal/rdns"
"github.com/AdguardTeam/AdGuardHome/internal/whois"
@@ -25,25 +26,14 @@ import (
// FSWatcher is a fake [aghos.FSWatcher] implementation for tests.
type FSWatcher struct {
OnStart func() (err error)
OnClose func() (err error)
OnEvents func() (e <-chan struct{})
OnAdd func(name string) (err error)
OnClose func() (err error)
}
// type check
var _ aghos.FSWatcher = (*FSWatcher)(nil)
// Start implements the [aghos.FSWatcher] interface for *FSWatcher.
func (w *FSWatcher) Start() (err error) {
return w.OnStart()
}
// Close implements the [aghos.FSWatcher] interface for *FSWatcher.
func (w *FSWatcher) Close() (err error) {
return w.OnClose()
}
// Events implements the [aghos.FSWatcher] interface for *FSWatcher.
func (w *FSWatcher) Events() (e <-chan struct{}) {
return w.OnEvents()
@@ -54,6 +44,11 @@ func (w *FSWatcher) Add(name string) (err error) {
return w.OnAdd(name)
}
// Close implements the [aghos.FSWatcher] interface for *FSWatcher.
func (w *FSWatcher) Close() (err error) {
return w.OnClose()
}
// Package agh
// ServiceWithConfig is a fake [agh.ServiceWithConfig] implementation for tests.
@@ -93,6 +88,9 @@ type AddressProcessor struct {
OnClose func() (err error)
}
// type check
var _ client.AddressProcessor = (*AddressProcessor)(nil)
// Process implements the [client.AddressProcessor] interface for
// *AddressProcessor.
func (p *AddressProcessor) Process(ip netip.Addr) {
@@ -110,6 +108,9 @@ type AddressUpdater struct {
OnUpdateAddress func(ip netip.Addr, host string, info *whois.Info)
}
// type check
var _ client.AddressUpdater = (*AddressUpdater)(nil)
// UpdateAddress implements the [client.AddressUpdater] interface for
// *AddressUpdater.
func (p *AddressUpdater) UpdateAddress(ip netip.Addr, host string, info *whois.Info) {

View File

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

View File

@@ -7,14 +7,13 @@ import (
"fmt"
"net"
"net/netip"
"slices"
"sync"
"github.com/AdguardTeam/AdGuardHome/internal/aghos"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/osutil"
"golang.org/x/exp/slices"
)
// Variables and functions to substitute in tests.
@@ -23,7 +22,7 @@ var (
aghosRunCommand = aghos.RunCommand
// rootDirFS is the filesystem pointing to the root directory.
rootDirFS = osutil.RootDirFS()
rootDirFS = aghos.RootDirFS()
)
// Interface stores and refreshes the network neighborhood reported by ARP

View File

@@ -1,249 +0,0 @@
package client
import (
"fmt"
"net"
"net/netip"
"github.com/AdguardTeam/AdGuardHome/internal/aghalg"
)
// macKey contains MAC as byte array of 6, 8, or 20 bytes.
type macKey any
// macToKey converts mac into key of type macKey, which is used as the key of
// the [clientIndex.macToUID]. mac must be valid MAC address.
func macToKey(mac net.HardwareAddr) (key macKey) {
switch len(mac) {
case 6:
return [6]byte(mac)
case 8:
return [8]byte(mac)
case 20:
return [20]byte(mac)
default:
panic(fmt.Errorf("invalid mac address %#v", mac))
}
}
// Index stores all information about persistent clients.
type Index struct {
// clientIDToUID maps client ID to UID.
clientIDToUID map[string]UID
// ipToUID maps IP address to UID.
ipToUID map[netip.Addr]UID
// macToUID maps MAC address to UID.
macToUID map[macKey]UID
// uidToClient maps UID to the persistent client.
uidToClient map[UID]*Persistent
// subnetToUID maps subnet to UID.
subnetToUID aghalg.SortedMap[netip.Prefix, UID]
}
// NewIndex initializes the new instance of client index.
func NewIndex() (ci *Index) {
return &Index{
clientIDToUID: map[string]UID{},
ipToUID: map[netip.Addr]UID{},
subnetToUID: aghalg.NewSortedMap[netip.Prefix, UID](subnetCompare),
macToUID: map[macKey]UID{},
uidToClient: map[UID]*Persistent{},
}
}
// Add stores information about a persistent client in the index. c must be
// non-nil and contain UID.
func (ci *Index) Add(c *Persistent) {
if (c.UID == UID{}) {
panic("client must contain uid")
}
for _, id := range c.ClientIDs {
ci.clientIDToUID[id] = c.UID
}
for _, ip := range c.IPs {
ci.ipToUID[ip] = c.UID
}
for _, pref := range c.Subnets {
ci.subnetToUID.Set(pref, c.UID)
}
for _, mac := range c.MACs {
k := macToKey(mac)
ci.macToUID[k] = c.UID
}
ci.uidToClient[c.UID] = c
}
// 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) {
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 ID %q", p.Name, id)
}
}
p, ip := ci.clashesIP(c)
if p != nil {
return fmt.Errorf("another client %q uses the same IP %q", p.Name, ip)
}
p, s := ci.clashesSubnet(c)
if p != nil {
return fmt.Errorf("another client %q uses the same subnet %q", p.Name, s)
}
p, mac := ci.clashesMAC(c)
if p != nil {
return fmt.Errorf("another client %q uses the same MAC %q", p.Name, mac)
}
return nil
}
// clashesIP returns a previous client with the same IP address as c. c must be
// non-nil.
func (ci *Index) clashesIP(c *Persistent) (p *Persistent, ip netip.Addr) {
for _, ip := range c.IPs {
existing, ok := ci.ipToUID[ip]
if ok && existing != c.UID {
return ci.uidToClient[existing], ip
}
}
return nil, netip.Addr{}
}
// clashesSubnet returns a previous client with the same subnet as c. c must be
// non-nil.
func (ci *Index) clashesSubnet(c *Persistent) (p *Persistent, s netip.Prefix) {
for _, s = range c.Subnets {
var existing UID
var ok bool
ci.subnetToUID.Range(func(p netip.Prefix, uid UID) (cont bool) {
if s == p {
existing = uid
ok = true
return false
}
return true
})
if ok && existing != c.UID {
return ci.uidToClient[existing], s
}
}
return nil, netip.Prefix{}
}
// clashesMAC returns a previous client with the same MAC address as c. c must
// be non-nil.
func (ci *Index) clashesMAC(c *Persistent) (p *Persistent, mac net.HardwareAddr) {
for _, mac = range c.MACs {
k := macToKey(mac)
existing, ok := ci.macToUID[k]
if ok && existing != c.UID {
return ci.uidToClient[existing], mac
}
}
return nil, nil
}
// Find finds persistent client by string representation of the client ID, IP
// address, or MAC.
func (ci *Index) Find(id string) (c *Persistent, ok bool) {
uid, found := ci.clientIDToUID[id]
if found {
return ci.uidToClient[uid], true
}
ip, err := netip.ParseAddr(id)
if err == nil {
// MAC addresses can be successfully parsed as IP addresses.
c, found = ci.findByIP(ip)
if found {
return c, true
}
}
mac, err := net.ParseMAC(id)
if err == nil {
return ci.findByMAC(mac)
}
return nil, false
}
// 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
}
ci.subnetToUID.Range(func(pref netip.Prefix, id UID) (cont bool) {
if pref.Contains(ip) {
uid, found = id, true
return false
}
return true
})
if found {
return ci.uidToClient[uid], true
}
return nil, false
}
// 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 {
return ci.uidToClient[uid], true
}
return nil, false
}
// Delete removes information about persistent client from the index. c must be
// non-nil.
func (ci *Index) Delete(c *Persistent) {
for _, id := range c.ClientIDs {
delete(ci.clientIDToUID, id)
}
for _, ip := range c.IPs {
delete(ci.ipToUID, ip)
}
for _, pref := range c.Subnets {
ci.subnetToUID.Del(pref)
}
for _, mac := range c.MACs {
k := macToKey(mac)
delete(ci.macToUID, k)
}
delete(ci.uidToClient, c.UID)
}

View File

@@ -1,223 +0,0 @@
package client
import (
"net"
"net/netip"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// newIDIndex is a helper function that returns a client index filled with
// persistent clients from the m. It also generates a UID for each client.
func newIDIndex(m []*Persistent) (ci *Index) {
ci = NewIndex()
for _, c := range m {
c.UID = MustNewUID()
ci.Add(c)
}
return ci
}
func TestClientIndex(t *testing.T) {
const (
cliIPNone = "1.2.3.4"
cliIP1 = "1.1.1.1"
cliIP2 = "2.2.2.2"
cliIPv6 = "1:2:3::4"
cliSubnet = "2.2.2.0/24"
cliSubnetIP = "2.2.2.222"
cliID = "client-id"
cliMAC = "11:11:11:11:11:11"
)
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},
}}
ci := newIDIndex(clients)
testCases := []struct {
want *Persistent
name string
ids []string
}{{
name: "ipv4_ipv6",
ids: []string{cliIP1, cliIPv6},
want: clients[0],
}, {
name: "ipv4_subnet",
ids: []string{cliIP2, cliSubnetIP},
want: clients[1],
}, {
name: "mac",
ids: []string{cliMAC},
want: clients[2],
}, {
name: "client_id",
ids: []string{cliID},
want: clients[3],
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
for _, id := range tc.ids {
c, ok := ci.Find(id)
require.True(t, ok)
assert.Equal(t, tc.want, c)
}
})
}
t.Run("not_found", func(t *testing.T) {
_, ok := ci.Find(cliIPNone)
assert.False(t, ok)
})
}
func TestClientIndex_Clashes(t *testing.T) {
const (
cliIP1 = "1.1.1.1"
cliSubnet = "2.2.2.0/24"
cliSubnetIP = "2.2.2.222"
cliID = "client-id"
cliMAC = "11:11:11:11:11:11"
)
clients := []*Persistent{{
Name: "client_with_ip",
IPs: []netip.Addr{netip.MustParseAddr(cliIP1)},
}, {
Name: "client_with_subnet",
Subnets: []netip.Prefix{netip.MustParsePrefix(cliSubnet)},
}, {
Name: "client_with_mac",
MACs: []net.HardwareAddr{mustParseMAC(cliMAC)},
}, {
Name: "client_with_id",
ClientIDs: []string{cliID},
}}
ci := newIDIndex(clients)
testCases := []struct {
client *Persistent
name string
}{{
name: "ipv4",
client: clients[0],
}, {
name: "subnet",
client: clients[1],
}, {
name: "mac",
client: clients[2],
}, {
name: "client_id",
client: clients[3],
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
clone := tc.client.ShallowClone()
clone.UID = MustNewUID()
err := ci.Clashes(clone)
require.Error(t, err)
ci.Delete(tc.client)
err = ci.Clashes(clone)
require.NoError(t, err)
})
}
}
// mustParseMAC is wrapper around [net.ParseMAC] that panics if there is an
// error.
func mustParseMAC(s string) (mac net.HardwareAddr) {
mac, err := net.ParseMAC(s)
if err != nil {
panic(err)
}
return mac
}
func TestMACToKey(t *testing.T) {
testCases := []struct {
want any
name string
in string
}{{
name: "column6",
in: "00:00:5e:00:53:01",
want: [6]byte(mustParseMAC("00:00:5e:00:53:01")),
}, {
name: "column8",
in: "02:00:5e:10:00:00:00:01",
want: [8]byte(mustParseMAC("02:00:5e:10:00:00:00:01")),
}, {
name: "column20",
in: "00:00:00:00:fe:80:00:00:00:00:00:00:02:00:5e:10:00:00:00:01",
want: [20]byte(mustParseMAC("00:00:00:00:fe:80:00:00:00:00:00:00:02:00:5e:10:00:00:00:01")),
}, {
name: "hyphen6",
in: "00-00-5e-00-53-01",
want: [6]byte(mustParseMAC("00-00-5e-00-53-01")),
}, {
name: "hyphen8",
in: "02-00-5e-10-00-00-00-01",
want: [8]byte(mustParseMAC("02-00-5e-10-00-00-00-01")),
}, {
name: "hyphen20",
in: "00-00-00-00-fe-80-00-00-00-00-00-00-02-00-5e-10-00-00-00-01",
want: [20]byte(mustParseMAC("00-00-00-00-fe-80-00-00-00-00-00-00-02-00-5e-10-00-00-00-01")),
}, {
name: "dot6",
in: "0000.5e00.5301",
want: [6]byte(mustParseMAC("0000.5e00.5301")),
}, {
name: "dot8",
in: "0200.5e10.0000.0001",
want: [8]byte(mustParseMAC("0200.5e10.0000.0001")),
}, {
name: "dot20",
in: "0000.0000.fe80.0000.0000.0000.0200.5e10.0000.0001",
want: [20]byte(mustParseMAC("0000.0000.fe80.0000.0000.0000.0200.5e10.0000.0001")),
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
mac := mustParseMAC(tc.in)
key := macToKey(mac)
assert.Equal(t, tc.want, key)
})
}
assert.Panics(t, func() {
mac := net.HardwareAddr([]byte{1, 2, 3})
_ = macToKey(mac)
})
}

View File

@@ -8,7 +8,6 @@ import (
"net"
"net/netip"
"os"
"slices"
"strings"
"time"
@@ -16,6 +15,7 @@ import (
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/google/renameio/v2/maybe"
"golang.org/x/exp/slices"
)
const (

View File

@@ -6,7 +6,6 @@ import (
"net"
"net/netip"
"path/filepath"
"slices"
"testing"
"time"
@@ -14,6 +13,7 @@ import (
"github.com/AdguardTeam/golibs/testutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/exp/slices"
)
func TestMain(m *testing.M) {

View File

@@ -10,7 +10,6 @@ import (
"net/http"
"net/netip"
"os"
"slices"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/aghalg"
@@ -20,6 +19,7 @@ import (
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/netutil"
"golang.org/x/exp/slices"
)
type v4ServerConfJSON struct {
@@ -592,7 +592,7 @@ func setOtherDHCPResult(ifaceName string, result *dhcpSearchResult) {
}
// parseLease parses a lease from r. If there is no error returns DHCPServer
// and *Lease. r must be non-nil.
// and *Lease. r must be non-nil.
func (s *server) parseLease(r io.Reader) (srv DHCPServer, lease *dhcpsvc.Lease, err error) {
l := &leaseStatic{}
err = json.NewDecoder(r).Decode(l)

View File

@@ -7,7 +7,6 @@ import (
"fmt"
"net"
"net/netip"
"slices"
"strings"
"sync"
"time"
@@ -21,6 +20,7 @@ import (
"github.com/go-ping/ping"
"github.com/insomniacslk/dhcp/dhcpv4"
"github.com/insomniacslk/dhcp/dhcpv4/server4"
"golang.org/x/exp/slices"
)
// v4Server is a DHCPv4 server.

View File

@@ -2,11 +2,11 @@ package dhcpsvc
import (
"fmt"
"slices"
"time"
"github.com/AdguardTeam/golibs/netutil"
"golang.org/x/exp/maps"
"golang.org/x/exp/slices"
)
// Config is the configuration for the DHCP service.
@@ -19,8 +19,6 @@ type Config struct {
// clients' hostnames.
LocalDomainName string
// TODO(e.burkov): Add DB path.
// ICMPTimeout is the timeout for checking another DHCP server's presence.
ICMPTimeout time.Duration
@@ -70,6 +68,12 @@ func (conf *Config) Validate() (err error) {
return nil
}
// newMustErr returns an error that indicates that valName must be as must
// describes.
func newMustErr(valName, must string, val fmt.Stringer) (err error) {
return fmt.Errorf("%s %s must %s", valName, val, must)
}
// validate returns an error in ic, if any.
func (ic *InterfaceConfig) validate() (err error) {
if ic == nil {

View File

@@ -7,16 +7,48 @@ import (
"context"
"net"
"net/netip"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/next/agh"
"golang.org/x/exp/slices"
)
// Interface is a DHCP service.
// Lease is a DHCP lease.
//
// TODO(e.burkov): Separate HostByIP, MACByIP, IPByHost into a separate
// interface. This is also applicable to Enabled method.
//
// TODO(e.burkov): Reconsider the requirements for the leases validity.
// TODO(e.burkov): Consider moving it to [agh], since it also may be needed in
// [websvc].
type Lease struct {
// IP is the IP address leased to the client.
IP netip.Addr
// Expiry is the expiration time of the lease.
Expiry time.Time
// Hostname of the client.
Hostname string
// HWAddr is the physical hardware address (MAC address).
HWAddr net.HardwareAddr
// IsStatic defines if the lease is static.
IsStatic bool
}
// Clone returns a deep copy of l.
func (l *Lease) Clone() (clone *Lease) {
if l == nil {
return nil
}
return &Lease{
Expiry: l.Expiry,
Hostname: l.Hostname,
HWAddr: slices.Clone(l.HWAddr),
IP: l.IP,
IsStatic: l.IsStatic,
}
}
type Interface interface {
agh.ServiceWithConfig[*Config]
@@ -31,8 +63,6 @@ type Interface interface {
// MACByIP returns the MAC address for the given IP address leased. It
// returns nil if there is no such client, due to an assumption that a DHCP
// client must always have a MAC address.
//
// TODO(e.burkov): Think of a contract for the returned value.
MACByIP(ip netip.Addr) (mac net.HardwareAddr)
// IPByHost returns the IP address of the DHCP client with the given
@@ -41,29 +71,26 @@ type Interface interface {
// hostname, either set or generated.
IPByHost(host string) (ip netip.Addr)
// Leases returns all the active DHCP leases. The returned slice should be
// a clone.
// Leases returns all the active DHCP leases.
//
// TODO(e.burkov): Consider implementing iterating methods with appropriate
// signatures instead of cloning the whole list.
Leases() (ls []*Lease)
// AddLease adds a new DHCP lease. l must be valid. It returns an error if
// l already exists.
// AddLease adds a new DHCP lease. It returns an error if the lease is
// invalid or already exists.
AddLease(l *Lease) (err error)
// UpdateStaticLease replaces an existing static DHCP lease. l must be
// valid. It returns an error if the lease with the given hardware address
// doesn't exist or if other values match another existing lease.
// UpdateStaticLease changes an existing DHCP lease. It returns an error if
// there is no lease with such hardware addressor if new values are invalid
// or already exist.
UpdateStaticLease(l *Lease) (err error)
// RemoveLease removes an existing DHCP lease. l must be valid. It returns
// an error if there is no lease equal to l.
// RemoveLease removes an existing DHCP lease. It returns an error if there
// is no lease equal to l.
RemoveLease(l *Lease) (err error)
// Reset removes all the DHCP leases.
//
// TODO(e.burkov): If it's really needed?
Reset() (err error)
}

View File

@@ -1,10 +1,6 @@
package dhcpsvc
import (
"fmt"
"github.com/AdguardTeam/golibs/errors"
)
import "github.com/AdguardTeam/golibs/errors"
const (
// errNilConfig is returned when a nil config met.
@@ -13,9 +9,3 @@ const (
// errNoInterfaces is returned when no interfaces found in configuration.
errNoInterfaces errors.Error = "no interfaces specified"
)
// newMustErr returns an error that indicates that valName must be as must
// describes.
func newMustErr(valName, must string, val fmt.Stringer) (err error) {
return fmt.Errorf("%s %s must %s", valName, val, must)
}

View File

@@ -1,66 +0,0 @@
package dhcpsvc
import (
"fmt"
"slices"
"time"
)
// netInterface is a common part of any network interface within the DHCP
// server.
//
// TODO(e.burkov): Add other methods as [DHCPServer] evolves.
type netInterface struct {
// name is the name of the network interface.
name string
// leases is a set of leases sorted by hardware address.
leases []*Lease
// leaseTTL is the default Time-To-Live value for leases.
leaseTTL time.Duration
}
// reset clears all the slices in iface for reuse.
func (iface *netInterface) reset() {
iface.leases = iface.leases[:0]
}
// insertLease inserts the given lease into iface. It returns an error if the
// lease can't be inserted.
func (iface *netInterface) insertLease(l *Lease) (err error) {
i, found := slices.BinarySearchFunc(iface.leases, l, compareLeaseMAC)
if found {
return fmt.Errorf("lease for mac %s already exists", l.HWAddr)
}
iface.leases = slices.Insert(iface.leases, i, l)
return nil
}
// updateLease replaces an existing lease within iface with the given one. It
// returns an error if there is no lease with such hardware address.
func (iface *netInterface) updateLease(l *Lease) (prev *Lease, err error) {
i, found := slices.BinarySearchFunc(iface.leases, l, compareLeaseMAC)
if !found {
return nil, fmt.Errorf("no lease for mac %s", l.HWAddr)
}
prev, iface.leases[i] = iface.leases[i], l
return prev, nil
}
// removeLease removes an existing lease from iface. It returns an error if
// there is no lease equal to l.
func (iface *netInterface) removeLease(l *Lease) (err error) {
i, found := slices.BinarySearchFunc(iface.leases, l, compareLeaseMAC)
if !found {
return fmt.Errorf("no lease for mac %s", l.HWAddr)
}
iface.leases = slices.Delete(iface.leases, i, i+1)
return nil
}

View File

@@ -1,52 +0,0 @@
package dhcpsvc
import (
"bytes"
"net"
"net/netip"
"slices"
"time"
)
// Lease is a DHCP lease.
//
// TODO(e.burkov): Consider moving it to [agh], since it also may be needed in
// [websvc].
//
// TODO(e.burkov): Add validation method.
type Lease struct {
// IP is the IP address leased to the client.
IP netip.Addr
// Expiry is the expiration time of the lease.
Expiry time.Time
// Hostname of the client.
Hostname string
// HWAddr is the physical hardware address (MAC address).
HWAddr net.HardwareAddr
// IsStatic defines if the lease is static.
IsStatic bool
}
// Clone returns a deep copy of l.
func (l *Lease) Clone() (clone *Lease) {
if l == nil {
return nil
}
return &Lease{
Expiry: l.Expiry,
Hostname: l.Hostname,
HWAddr: slices.Clone(l.HWAddr),
IP: l.IP,
IsStatic: l.IsStatic,
}
}
// compareLeaseMAC compares two [Lease]s by hardware address.
func compareLeaseMAC(a, b *Lease) (res int) {
return bytes.Compare(a.HWAddr, b.HWAddr)
}

View File

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

View File

@@ -2,15 +2,11 @@ package dhcpsvc
import (
"fmt"
"net"
"net/netip"
"slices"
"sync"
"sync/atomic"
"time"
"github.com/AdguardTeam/golibs/errors"
"golang.org/x/exp/maps"
"golang.org/x/exp/slices"
)
// DHCPServer is a DHCP server for both IPv4 and IPv6 address families.
@@ -19,21 +15,18 @@ type DHCPServer struct {
// information about its clients.
enabled *atomic.Bool
// localTLD is the top-level domain name to use for resolving DHCP clients'
// hostnames.
// localTLD is the top-level domain name to use for resolving DHCP
// clients' hostnames.
localTLD string
// leasesMu protects the leases index as well as leases in the interfaces.
leasesMu *sync.RWMutex
// leases stores the DHCP leases for quick lookups.
leases *leaseIndex
// interfaces4 is the set of IPv4 interfaces sorted by interface name.
interfaces4 netInterfacesV4
interfaces4 []*iface4
// interfaces6 is the set of IPv6 interfaces sorted by interface name.
interfaces6 netInterfacesV6
interfaces6 []*iface6
// leases is the set of active DHCP leases.
leases []*Lease
// icmpTimeout is the timeout for checking another DHCP server's presence.
icmpTimeout time.Duration
@@ -49,27 +42,26 @@ func New(conf *Config) (srv *DHCPServer, err error) {
return nil, nil
}
// TODO(e.burkov): Add validations scoped to the network interfaces set.
ifaces4 := make(netInterfacesV4, 0, len(conf.Interfaces))
ifaces6 := make(netInterfacesV6, 0, len(conf.Interfaces))
ifaces4 := make([]*iface4, len(conf.Interfaces))
ifaces6 := make([]*iface6, len(conf.Interfaces))
ifaceNames := maps.Keys(conf.Interfaces)
slices.Sort(ifaceNames)
var i4 *netInterfaceV4
var i6 *netInterfaceV6
var i4 *iface4
var i6 *iface6
for _, ifaceName := range ifaceNames {
iface := conf.Interfaces[ifaceName]
i4, err = newNetInterfaceV4(ifaceName, iface.IPv4)
i4, err = newIface4(ifaceName, iface.IPv4)
if err != nil {
return nil, fmt.Errorf("interface %q: ipv4: %w", ifaceName, err)
} else if i4 != nil {
ifaces4 = append(ifaces4, i4)
}
i6 = newNetInterfaceV6(ifaceName, iface.IPv6)
i6 = newIface6(ifaceName, iface.IPv6)
if i6 != nil {
ifaces6 = append(ifaces6, i6)
}
@@ -78,19 +70,13 @@ func New(conf *Config) (srv *DHCPServer, err error) {
enabled := &atomic.Bool{}
enabled.Store(conf.Enabled)
srv = &DHCPServer{
return &DHCPServer{
enabled: enabled,
localTLD: conf.LocalDomainName,
leasesMu: &sync.RWMutex{},
leases: newLeaseIndex(),
interfaces4: ifaces4,
interfaces6: ifaces6,
localTLD: conf.LocalDomainName,
icmpTimeout: conf.ICMPTimeout,
}
// TODO(e.burkov): Load leases.
return srv, nil
}, nil
}
// type check
@@ -105,140 +91,10 @@ func (srv *DHCPServer) Enabled() (ok bool) {
// Leases implements the [Interface] interface for *DHCPServer.
func (srv *DHCPServer) Leases() (leases []*Lease) {
srv.leasesMu.RLock()
defer srv.leasesMu.RUnlock()
for _, iface := range srv.interfaces4 {
for _, lease := range iface.leases {
leases = append(leases, lease.Clone())
}
}
for _, iface := range srv.interfaces6 {
for _, lease := range iface.leases {
leases = append(leases, lease.Clone())
}
leases = make([]*Lease, 0, len(srv.leases))
for _, lease := range srv.leases {
leases = append(leases, lease.Clone())
}
return leases
}
// HostByIP implements the [Interface] interface for *DHCPServer.
func (srv *DHCPServer) HostByIP(ip netip.Addr) (host string) {
srv.leasesMu.RLock()
defer srv.leasesMu.RUnlock()
if l, ok := srv.leases.leaseByAddr(ip); ok {
return l.Hostname
}
return ""
}
// MACByIP implements the [Interface] interface for *DHCPServer.
func (srv *DHCPServer) MACByIP(ip netip.Addr) (mac net.HardwareAddr) {
srv.leasesMu.RLock()
defer srv.leasesMu.RUnlock()
if l, ok := srv.leases.leaseByAddr(ip); ok {
return l.HWAddr
}
return nil
}
// IPByHost implements the [Interface] interface for *DHCPServer.
func (srv *DHCPServer) IPByHost(host string) (ip netip.Addr) {
srv.leasesMu.RLock()
defer srv.leasesMu.RUnlock()
if l, ok := srv.leases.leaseByName(host); ok {
return l.IP
}
return netip.Addr{}
}
// Reset implements the [Interface] interface for *DHCPServer.
func (srv *DHCPServer) Reset() (err error) {
srv.leasesMu.Lock()
defer srv.leasesMu.Unlock()
for _, iface := range srv.interfaces4 {
iface.reset()
}
for _, iface := range srv.interfaces6 {
iface.reset()
}
srv.leases.clear()
return nil
}
// AddLease implements the [Interface] interface for *DHCPServer.
func (srv *DHCPServer) AddLease(l *Lease) (err error) {
defer func() { err = errors.Annotate(err, "adding lease: %w") }()
addr := l.IP
iface, err := srv.ifaceForAddr(addr)
if err != nil {
// Don't wrap the error since there is already an annotation deferred.
return err
}
srv.leasesMu.Lock()
defer srv.leasesMu.Unlock()
return srv.leases.add(l, iface)
}
// UpdateStaticLease implements the [Interface] interface for *DHCPServer.
//
// TODO(e.burkov): Support moving leases between interfaces.
func (srv *DHCPServer) UpdateStaticLease(l *Lease) (err error) {
defer func() { err = errors.Annotate(err, "updating static lease: %w") }()
addr := l.IP
iface, err := srv.ifaceForAddr(addr)
if err != nil {
// Don't wrap the error since there is already an annotation deferred.
return err
}
srv.leasesMu.Lock()
defer srv.leasesMu.Unlock()
return srv.leases.update(l, iface)
}
// RemoveLease implements the [Interface] interface for *DHCPServer.
func (srv *DHCPServer) RemoveLease(l *Lease) (err error) {
defer func() { err = errors.Annotate(err, "removing lease: %w") }()
addr := l.IP
iface, err := srv.ifaceForAddr(addr)
if err != nil {
// Don't wrap the error since there is already an annotation deferred.
return err
}
srv.leasesMu.Lock()
defer srv.leasesMu.Unlock()
return srv.leases.remove(l, iface)
}
// ifaceForAddr returns the handled network interface for the given IP address,
// or an error if no such interface exists.
func (srv *DHCPServer) ifaceForAddr(addr netip.Addr) (iface *netInterface, err error) {
var ok bool
if addr.Is4() {
iface, ok = srv.interfaces4.find(addr)
} else {
iface, ok = srv.interfaces6.find(addr)
}
if !ok {
return nil, fmt.Errorf("no interface for ip %s", addr)
}
return iface, nil
}

View File

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

View File

@@ -4,12 +4,12 @@ import (
"fmt"
"net"
"net/netip"
"slices"
"time"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/netutil"
"github.com/google/gopacket/layers"
"golang.org/x/exp/slices"
)
// IPv4Config is the interface-specific configuration for DHCPv4.
@@ -64,6 +64,69 @@ func (conf *IPv4Config) validate() (err error) {
}
}
// iface4 is a DHCP interface for IPv4 address family.
type iface4 struct {
// gateway is the IP address of the network gateway.
gateway netip.Addr
// subnet is the network subnet.
subnet netip.Prefix
// addrSpace is the IPv4 address space allocated for leasing.
addrSpace ipRange
// name is the name of the interface.
name string
// implicitOpts are the options listed in Appendix A of RFC 2131 and
// initialized with default values. It must not have intersections with
// explicitOpts.
implicitOpts layers.DHCPOptions
// explicitOpts are the user-configured options. It must not have
// intersections with implicitOpts.
explicitOpts layers.DHCPOptions
// leaseTTL is the time-to-live of dynamic leases on this interface.
leaseTTL time.Duration
}
// newIface4 creates a new DHCP interface for IPv4 address family with the given
// configuration. It returns an error if the given configuration can't be used.
func newIface4(name string, conf *IPv4Config) (i *iface4, err error) {
if !conf.Enabled {
return nil, nil
}
maskLen, _ := net.IPMask(conf.SubnetMask.AsSlice()).Size()
subnet := netip.PrefixFrom(conf.GatewayIP, maskLen)
switch {
case !subnet.Contains(conf.RangeStart):
return nil, fmt.Errorf("range start %s is not within %s", conf.RangeStart, subnet)
case !subnet.Contains(conf.RangeEnd):
return nil, fmt.Errorf("range end %s is not within %s", conf.RangeEnd, subnet)
}
addrSpace, err := newIPRange(conf.RangeStart, conf.RangeEnd)
if err != nil {
return nil, err
} else if addrSpace.contains(conf.GatewayIP) {
return nil, fmt.Errorf("gateway ip %s in the ip range %s", conf.GatewayIP, addrSpace)
}
i = &iface4{
name: name,
gateway: conf.GatewayIP,
subnet: subnet,
addrSpace: addrSpace,
leaseTTL: conf.LeaseDuration,
}
i.implicitOpts, i.explicitOpts = conf.options()
return i, nil
}
// options returns the implicit and explicit options for the interface. The two
// lists are disjoint and the implicit options are initialized with default
// values.
@@ -255,83 +318,3 @@ func (conf *IPv4Config) options() (implicit, explicit layers.DHCPOptions) {
func compareV4OptionCodes(a, b layers.DHCPOption) (res int) {
return int(a.Type) - int(b.Type)
}
// netInterfaceV4 is a DHCP interface for IPv4 address family.
type netInterfaceV4 struct {
// gateway is the IP address of the network gateway.
gateway netip.Addr
// subnet is the network subnet.
subnet netip.Prefix
// addrSpace is the IPv4 address space allocated for leasing.
addrSpace ipRange
// implicitOpts are the options listed in Appendix A of RFC 2131 and
// initialized with default values. It must not have intersections with
// explicitOpts.
implicitOpts layers.DHCPOptions
// explicitOpts are the user-configured options. It must not have
// intersections with implicitOpts.
explicitOpts layers.DHCPOptions
// netInterface is embedded here to provide some common network interface
// logic.
netInterface
}
// newNetInterfaceV4 creates a new DHCP interface for IPv4 address family with
// the given configuration. It returns an error if the given configuration
// can't be used.
func newNetInterfaceV4(name string, conf *IPv4Config) (i *netInterfaceV4, err error) {
if !conf.Enabled {
return nil, nil
}
maskLen, _ := net.IPMask(conf.SubnetMask.AsSlice()).Size()
subnet := netip.PrefixFrom(conf.GatewayIP, maskLen)
switch {
case !subnet.Contains(conf.RangeStart):
return nil, fmt.Errorf("range start %s is not within %s", conf.RangeStart, subnet)
case !subnet.Contains(conf.RangeEnd):
return nil, fmt.Errorf("range end %s is not within %s", conf.RangeEnd, subnet)
}
addrSpace, err := newIPRange(conf.RangeStart, conf.RangeEnd)
if err != nil {
return nil, err
} else if addrSpace.contains(conf.GatewayIP) {
return nil, fmt.Errorf("gateway ip %s in the ip range %s", conf.GatewayIP, addrSpace)
}
i = &netInterfaceV4{
gateway: conf.GatewayIP,
subnet: subnet,
addrSpace: addrSpace,
netInterface: netInterface{
name: name,
leaseTTL: conf.LeaseDuration,
},
}
i.implicitOpts, i.explicitOpts = conf.options()
return i, nil
}
// netInterfacesV4 is a slice of network interfaces of IPv4 address family.
type netInterfacesV4 []*netInterfaceV4
// find returns the first network interface within ifaces containing ip. It
// returns false if there is no such interface.
func (ifaces netInterfacesV4) find(ip netip.Addr) (iface4 *netInterface, ok bool) {
i := slices.IndexFunc(ifaces, func(iface *netInterfaceV4) (contains bool) {
return iface.subnet.Contains(ip)
})
if i < 0 {
return nil, false
}
return &ifaces[i].netInterface, true
}

View File

@@ -3,12 +3,11 @@ package dhcpsvc
import (
"fmt"
"net/netip"
"slices"
"time"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/netutil"
"github.com/google/gopacket/layers"
"golang.org/x/exp/slices"
)
// IPv6Config is the interface-specific configuration for DHCPv6.
@@ -53,6 +52,57 @@ func (conf *IPv6Config) validate() (err error) {
}
}
// iface6 is a DHCP interface for IPv6 address family.
//
// TODO(e.burkov): Add options.
type iface6 struct {
// rangeStart is the first IP address in the range.
rangeStart netip.Addr
// name is the name of the interface.
name string
// implicitOpts are the DHCPv6 options listed in RFC 8415 (and others) and
// initialized with default values. It must not have intersections with
// explicitOpts.
implicitOpts layers.DHCPv6Options
// explicitOpts are the user-configured options. It must not have
// intersections with implicitOpts.
explicitOpts layers.DHCPv6Options
// leaseTTL is the time-to-live of dynamic leases on this interface.
leaseTTL time.Duration
// raSLAACOnly defines if DHCP should send ICMPv6.RA packets without MO
// flags.
raSLAACOnly bool
// raAllowSLAAC defines if DHCP should send ICMPv6.RA packets with MO flags.
raAllowSLAAC bool
}
// newIface6 creates a new DHCP interface for IPv6 address family with the given
// configuration.
//
// TODO(e.burkov): Validate properly.
func newIface6(name string, conf *IPv6Config) (i *iface6) {
if !conf.Enabled {
return nil
}
i = &iface6{
name: name,
rangeStart: conf.RangeStart,
leaseTTL: conf.LeaseDuration,
raSLAACOnly: conf.RASLAACOnly,
raAllowSLAAC: conf.RAAllowSLAAC,
}
i.implicitOpts, i.explicitOpts = conf.options()
return i
}
// options returns the implicit and explicit options for the interface. The two
// lists are disjoint and the implicit options are initialized with default
// values.
@@ -83,79 +133,3 @@ func (conf *IPv6Config) options() (implicit, explicit layers.DHCPv6Options) {
func compareV6OptionCodes(a, b layers.DHCPv6Option) (res int) {
return int(a.Code) - int(b.Code)
}
// netInterfaceV6 is a DHCP interface for IPv6 address family.
//
// TODO(e.burkov): Add options.
type netInterfaceV6 struct {
// rangeStart is the first IP address in the range.
rangeStart netip.Addr
// implicitOpts are the DHCPv6 options listed in RFC 8415 (and others) and
// initialized with default values. It must not have intersections with
// explicitOpts.
implicitOpts layers.DHCPv6Options
// explicitOpts are the user-configured options. It must not have
// intersections with implicitOpts.
explicitOpts layers.DHCPv6Options
// netInterface is embedded here to provide some common network interface
// logic.
netInterface
// raSLAACOnly defines if DHCP should send ICMPv6.RA packets without MO
// flags.
raSLAACOnly bool
// raAllowSLAAC defines if DHCP should send ICMPv6.RA packets with MO flags.
raAllowSLAAC bool
}
// newNetInterfaceV6 creates a new DHCP interface for IPv6 address family with
// the given configuration.
//
// TODO(e.burkov): Validate properly.
func newNetInterfaceV6(name string, conf *IPv6Config) (i *netInterfaceV6) {
if !conf.Enabled {
return nil
}
i = &netInterfaceV6{
rangeStart: conf.RangeStart,
netInterface: netInterface{
name: name,
leaseTTL: conf.LeaseDuration,
},
raSLAACOnly: conf.RASLAACOnly,
raAllowSLAAC: conf.RAAllowSLAAC,
}
i.implicitOpts, i.explicitOpts = conf.options()
return i
}
// netInterfacesV4 is a slice of network interfaces of IPv4 address family.
type netInterfacesV6 []*netInterfaceV6
// find returns the first network interface within ifaces containing ip. It
// returns false if there is no such interface.
func (ifaces netInterfacesV6) find(ip netip.Addr) (iface6 *netInterface, ok bool) {
// prefLen is the length of prefix to match ip against.
//
// TODO(e.burkov): DHCPv6 inherits the weird behavior of legacy
// implementation where the allocated range constrained by the first address
// and the first address with last byte set to 0xff. Proper prefixes should
// be used instead.
const prefLen = netutil.IPv6BitLen - 8
i := slices.IndexFunc(ifaces, func(iface *netInterfaceV6) (contains bool) {
return !ip.Less(iface.rangeStart) &&
netip.PrefixFrom(iface.rangeStart, prefLen).Contains(ip)
})
if i < 0 {
return nil, false
}
return &ifaces[i].netInterface, true
}

View File

@@ -14,8 +14,6 @@ import (
)
// ValidateClientID returns an error if id is not a valid ClientID.
//
// Keep in sync with [client.ValidateClientID].
func ValidateClientID(id string) (err error) {
err = netutil.ValidateHostnameLabel(id)
if err != nil {

View File

@@ -7,7 +7,6 @@ import (
"net"
"net/netip"
"os"
"slices"
"strings"
"time"
@@ -25,6 +24,7 @@ import (
"github.com/AdguardTeam/golibs/stringutil"
"github.com/AdguardTeam/golibs/timeutil"
"github.com/ameshkov/dnscrypt/v2"
"golang.org/x/exp/slices"
)
// ClientsContainer provides information about preconfigured DNS clients.
@@ -40,7 +40,7 @@ type ClientsContainer interface {
) (conf *proxy.CustomUpstreamConfig, err error)
}
// Config represents the DNS filtering configuration of AdGuard Home. The zero
// Config represents the DNS filtering configuration of AdGuard Home. The zero
// Config is empty and ready for use.
type Config struct {
// Callbacks for other modules
@@ -357,6 +357,10 @@ func (s *Server) newProxyConfig() (conf *proxy.Config, err error) {
conf.DNSCryptResolverCert = c.ResolverCert
}
if conf.UpstreamConfig == nil || len(conf.UpstreamConfig.Upstreams) == 0 {
return nil, errors.Error("no default upstream servers configured")
}
conf, err = prepareCacheConfig(conf,
srvConf.CacheSize,
srvConf.CacheMinTTL,

View File

@@ -1,10 +1,10 @@
package dnsforward
import (
"slices"
"testing"
"github.com/stretchr/testify/assert"
"golang.org/x/exp/slices"
)
func TestAnyNameMatches(t *testing.T) {

View File

@@ -2,56 +2,54 @@ package dnsforward
import (
"fmt"
"strings"
"sync"
"github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/dnsproxy/upstream"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/miekg/dns"
"golang.org/x/exp/slices"
)
// upstreamConfigValidator parses each section of an upstream configuration into
// a corresponding [*proxy.UpstreamConfig] and checks the actual DNS
// availability of each upstream.
// upstreamConfigValidator parses the [*proxy.UpstreamConfig] and checks the
// actual DNS availability of each upstream.
type upstreamConfigValidator struct {
// generalUpstreamResults contains upstream results of a general section.
generalUpstreamResults map[string]*upstreamResult
// general is the general upstream configuration.
general []*upstreamResult
// fallbackUpstreamResults contains upstream results of a fallback section.
fallbackUpstreamResults map[string]*upstreamResult
// fallback is the fallback upstream configuration.
fallback []*upstreamResult
// privateUpstreamResults contains upstream results of a private section.
privateUpstreamResults map[string]*upstreamResult
// generalParseResults contains parsing results of a general section.
generalParseResults []*parseResult
// fallbackParseResults contains parsing results of a fallback section.
fallbackParseResults []*parseResult
// privateParseResults contains parsing results of a private section.
privateParseResults []*parseResult
// private is the private upstream configuration.
private []*upstreamResult
}
// upstreamResult is a result of parsing of an [upstream.Upstream] within an
// upstreamResult is a result of validation of an [upstream.Upstream] within an
// [proxy.UpstreamConfig].
type upstreamResult struct {
// server is the parsed upstream.
// server is the parsed upstream. It is nil when there was an error during
// parsing.
server upstream.Upstream
// err is the upstream check error.
// err is the error either from parsing or from checking the upstream.
err error
// original is the piece of configuration that have either been turned to an
// upstream or caused an error.
original string
// isSpecific is true if the upstream is domain-specific.
isSpecific bool
}
// parseResult contains a original piece of upstream configuration and a
// corresponding error.
type parseResult struct {
err *proxy.ParseError
original string
// compare compares two [upstreamResult]s. It returns 0 if they are equal, -1
// if ur should be sorted before other, and 1 otherwise.
//
// TODO(e.burkov): Perhaps it makes sense to sort the results with errors near
// the end.
func (ur *upstreamResult) compare(other *upstreamResult) (res int) {
return strings.Compare(ur.original, other.original)
}
// newUpstreamConfigValidator parses the upstream configuration and returns a
@@ -63,100 +61,98 @@ func newUpstreamConfigValidator(
private []string,
opts *upstream.Options,
) (cv *upstreamConfigValidator) {
cv = &upstreamConfigValidator{
generalUpstreamResults: map[string]*upstreamResult{},
fallbackUpstreamResults: map[string]*upstreamResult{},
privateUpstreamResults: map[string]*upstreamResult{},
cv = &upstreamConfigValidator{}
for _, line := range general {
cv.general = cv.insertLineResults(cv.general, line, opts)
}
for _, line := range fallback {
cv.fallback = cv.insertLineResults(cv.fallback, line, opts)
}
for _, line := range private {
cv.private = cv.insertLineResults(cv.private, line, opts)
}
conf, err := proxy.ParseUpstreamsConfig(general, opts)
cv.generalParseResults = collectErrResults(general, err)
insertConfResults(conf, cv.generalUpstreamResults)
conf, err = proxy.ParseUpstreamsConfig(fallback, opts)
cv.fallbackParseResults = collectErrResults(fallback, err)
insertConfResults(conf, cv.fallbackUpstreamResults)
conf, err = proxy.ParseUpstreamsConfig(private, opts)
cv.privateParseResults = collectErrResults(private, err)
insertConfResults(conf, cv.privateUpstreamResults)
return cv
}
// collectErrResults parses err and returns parsing results containing the
// original upstream configuration line and the corresponding error. err can be
// nil.
func collectErrResults(lines []string, err error) (results []*parseResult) {
if err == nil {
return nil
}
// limit is a maximum length for upstream configuration lines.
const limit = 80
wrapper, ok := err.(errors.WrapperSlice)
if !ok {
log.Debug("dnsforward: configvalidator: unwrapping: %s", err)
return nil
}
errs := wrapper.Unwrap()
results = make([]*parseResult, 0, len(errs))
for i, e := range errs {
var parseErr *proxy.ParseError
if !errors.As(e, &parseErr) {
log.Debug("dnsforward: configvalidator: inserting unexpected error %d: %s", i, err)
continue
}
idx := parseErr.Idx
line := []rune(lines[idx])
if len(line) > limit {
line = line[:limit]
line[limit-1] = '…'
}
results = append(results, &parseResult{
original: string(line),
err: parseErr,
// insertLineResults parses line and inserts the result into s. It can insert
// multiple results as well as none.
func (cv *upstreamConfigValidator) insertLineResults(
s []*upstreamResult,
line string,
opts *upstream.Options,
) (result []*upstreamResult) {
upstreams, isSpecific, err := splitUpstreamLine(line)
if err != nil {
return cv.insert(s, &upstreamResult{
err: err,
original: line,
})
}
return results
}
// insertConfResults parses conf and inserts the upstream result into results.
// It can insert multiple results as well as none.
func insertConfResults(conf *proxy.UpstreamConfig, results map[string]*upstreamResult) {
insertListResults(conf.Upstreams, results, false)
for _, ups := range conf.DomainReservedUpstreams {
insertListResults(ups, results, true)
}
for _, ups := range conf.SpecifiedDomainUpstreams {
insertListResults(ups, results, true)
}
}
// insertListResults constructs upstream results from the upstream list and
// inserts them into results. It can insert multiple results as well as none.
func insertListResults(ups []upstream.Upstream, results map[string]*upstreamResult, specific bool) {
for _, u := range ups {
addr := u.Address()
_, ok := results[addr]
if ok {
for _, upstreamAddr := range upstreams {
var res *upstreamResult
if upstreamAddr != "#" {
res = cv.parseUpstream(upstreamAddr, opts)
} else if !isSpecific {
res = &upstreamResult{
err: errNotDomainSpecific,
original: upstreamAddr,
}
} else {
continue
}
results[addr] = &upstreamResult{
server: u,
isSpecific: specific,
res.isSpecific = isSpecific
s = cv.insert(s, res)
}
return s
}
// insert inserts r into slice in a sorted order, except duplicates. slice must
// not be nil.
func (cv *upstreamConfigValidator) insert(
s []*upstreamResult,
r *upstreamResult,
) (result []*upstreamResult) {
i, has := slices.BinarySearchFunc(s, r, (*upstreamResult).compare)
if has {
log.Debug("dnsforward: duplicate configuration %q", r.original)
return s
}
return slices.Insert(s, i, r)
}
// parseUpstream parses addr and returns the result of parsing. It returns nil
// if the specified server points at the default upstream server which is
// validated separately.
func (cv *upstreamConfigValidator) parseUpstream(
addr string,
opts *upstream.Options,
) (r *upstreamResult) {
// Check if the upstream has a valid protocol prefix.
//
// TODO(e.burkov): Validate the domain name.
if proto, _, ok := strings.Cut(addr, "://"); ok {
if !slices.Contains(protocols, proto) {
return &upstreamResult{
err: fmt.Errorf("bad protocol %q", proto),
original: addr,
}
}
}
ups, err := upstream.AddressToUpstream(addr, opts)
return &upstreamResult{
server: ups,
err: err,
original: addr,
}
}
// check tries to exchange with each successfully parsed upstream and enriches
@@ -191,30 +187,35 @@ func (cv *upstreamConfigValidator) check() {
}
wg := &sync.WaitGroup{}
wg.Add(len(cv.generalUpstreamResults) +
len(cv.fallbackUpstreamResults) +
len(cv.privateUpstreamResults))
wg.Add(len(cv.general) + len(cv.fallback) + len(cv.private))
for _, res := range cv.generalUpstreamResults {
go checkSrv(res, wg, commonChecker)
for _, res := range cv.general {
go cv.checkSrv(res, wg, commonChecker)
}
for _, res := range cv.fallbackUpstreamResults {
go checkSrv(res, wg, commonChecker)
for _, res := range cv.fallback {
go cv.checkSrv(res, wg, commonChecker)
}
for _, res := range cv.privateUpstreamResults {
go checkSrv(res, wg, arpaChecker)
for _, res := range cv.private {
go cv.checkSrv(res, wg, arpaChecker)
}
wg.Wait()
}
// checkSrv runs hc on the server from res, if any, and stores any occurred
// error in res. wg is always marked done in the end. It is intended to be
// used as a goroutine.
func checkSrv(res *upstreamResult, wg *sync.WaitGroup, hc *healthchecker) {
defer log.OnPanic(fmt.Sprintf("dnsforward: checking upstream %s", res.server.Address()))
// error in res. wg is always marked done in the end. It used to be called in
// a separate goroutine.
func (cv *upstreamConfigValidator) checkSrv(
res *upstreamResult,
wg *sync.WaitGroup,
hc *healthchecker,
) {
defer wg.Done()
if res.server == nil {
return
}
res.err = hc.check(res.server)
if res.err != nil && res.isSpecific {
res.err = domainSpecificTestError{Err: res.err}
@@ -224,126 +225,65 @@ func checkSrv(res *upstreamResult, wg *sync.WaitGroup, hc *healthchecker) {
// close closes all the upstreams that were successfully parsed. It enriches
// the results with deferred closing errors.
func (cv *upstreamConfigValidator) close() {
all := []map[string]*upstreamResult{
cv.generalUpstreamResults,
cv.fallbackUpstreamResults,
cv.privateUpstreamResults,
}
for _, m := range all {
for _, r := range m {
r.err = errors.WithDeferred(r.err, r.server.Close())
for _, slice := range [][]*upstreamResult{cv.general, cv.fallback, cv.private} {
for _, r := range slice {
if r.server != nil {
r.err = errors.WithDeferred(r.err, r.server.Close())
}
}
}
}
// sections of the upstream configuration according to the text label of the
// localization.
//
// Keep in sync with client/src/__locales/en.json.
//
// TODO(s.chzhen): Refactor.
const (
generalTextLabel = "upstream_dns"
fallbackTextLabel = "fallback_dns_title"
privateTextLabel = "local_ptr_title"
)
// status returns all the data collected during parsing, healthcheck, and
// closing of the upstreams. The returned map is keyed by the original upstream
// configuration piece and contains the corresponding error or "OK" if there was
// no error.
func (cv *upstreamConfigValidator) status() (results map[string]string) {
// Names of the upstream configuration sections for logging.
const (
generalSection = "general"
fallbackSection = "fallback"
privateSection = "private"
)
result := map[string]string{}
results = map[string]string{}
for original, res := range cv.generalUpstreamResults {
upstreamResultToStatus(generalSection, string(original), res, results)
for _, res := range cv.general {
resultToStatus("general", res, result)
}
for original, res := range cv.fallbackUpstreamResults {
upstreamResultToStatus(fallbackSection, string(original), res, results)
for _, res := range cv.fallback {
resultToStatus("fallback", res, result)
}
for original, res := range cv.privateUpstreamResults {
upstreamResultToStatus(privateSection, string(original), res, results)
for _, res := range cv.private {
resultToStatus("private", res, result)
}
parseResultToStatus(generalTextLabel, generalSection, cv.generalParseResults, results)
parseResultToStatus(fallbackTextLabel, fallbackSection, cv.fallbackParseResults, results)
parseResultToStatus(privateTextLabel, privateSection, cv.privateParseResults, results)
return results
return result
}
// upstreamResultToStatus puts "OK" or an error message from res into resMap.
// section is the name of the upstream configuration section, i.e. "general",
// resultToStatus puts "OK" or an error message from res into resMap. section
// is the name of the upstream configuration section, i.e. "general",
// "fallback", or "private", and only used for logging.
//
// TODO(e.burkov): Currently, the HTTP handler expects that all the results are
// put together in a single map, which may lead to collisions, see AG-27539.
// Improve the results compilation.
func upstreamResultToStatus(
section string,
original string,
res *upstreamResult,
resMap map[string]string,
) {
func resultToStatus(section string, res *upstreamResult, resMap map[string]string) {
val := "OK"
if res.err != nil {
val = res.err.Error()
}
prevVal := resMap[original]
prevVal := resMap[res.original]
switch prevVal {
case "":
resMap[original] = val
resMap[res.original] = val
case val:
log.Debug("dnsforward: duplicating %s config line %q", section, original)
log.Debug("dnsforward: duplicating %s config line %q", section, res.original)
default:
log.Debug(
"dnsforward: warning: %s config line %q (%v) had different result %v",
section,
val,
original,
res.original,
prevVal,
)
}
}
// parseResultToStatus puts parsing error messages from results into resMap.
// section is the name of the upstream configuration section, i.e. "general",
// "fallback", or "private", and only used for logging.
//
// Parsing error message has the following format:
//
// sectionTextLabel line: parsing error
//
// Where sectionTextLabel is a section text label of a localization and line is
// a line number.
func parseResultToStatus(
textLabel string,
section string,
results []*parseResult,
resMap map[string]string,
) {
for _, res := range results {
original := res.original
_, ok := resMap[original]
if ok {
log.Debug("dnsforward: duplicating %s parsing error %q", section, original)
continue
}
resMap[original] = fmt.Sprintf("%s %d: parsing error", textLabel, res.err.Idx+1)
}
}
// domainSpecificTestError is a wrapper for errors returned by checkDNS to mark
// the tested upstream domain-specific and therefore consider its errors
// non-critical.
@@ -402,7 +342,7 @@ func (h *healthchecker) check(u upstream.Upstream) (err error) {
if err != nil {
return fmt.Errorf("couldn't communicate with upstream: %w", err)
} else if h.ansEmpty && len(reply.Answer) > 0 {
return errors.Error("wrong response")
return errWrongResponse
}
return nil

View File

@@ -8,6 +8,7 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/aghtest"
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/dnsproxy/upstream"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/testutil"
"github.com/miekg/dns"
@@ -100,6 +101,21 @@ func TestServer_HandleDNSRequest_dns64(t *testing.T) {
type answerMap = map[uint16][sectionsNum][]dns.RR
pt := testutil.PanicT{}
newUps := func(answers answerMap) (u upstream.Upstream) {
return aghtest.NewUpstreamMock(func(req *dns.Msg) (resp *dns.Msg, err error) {
q := req.Question[0]
require.Contains(pt, answers, q.Qtype)
answer := answers[q.Qtype]
resp = (&dns.Msg{}).SetReply(req)
resp.Answer = answer[sectionAnswer]
resp.Ns = answer[sectionAuthority]
resp.Extra = answer[sectionAdditional]
return resp, nil
})
}
testCases := []struct {
name string
@@ -249,16 +265,13 @@ func TestServer_HandleDNSRequest_dns64(t *testing.T) {
}}
localRR := newRR(t, ptr64Domain, dns.TypePTR, 3600, pointedDomain)
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{
Answer: []dns.RR{localRR},
}).SetReply(m)
localUps := aghtest.NewUpstreamMock(func(req *dns.Msg) (resp *dns.Msg, err error) {
require.Equal(pt, req.Question[0].Name, ptr64Domain)
resp = (&dns.Msg{}).SetReply(req)
resp.Answer = []dns.RR{localRR}
require.NoError(t, w.WriteMsg(resp))
return resp, nil
})
localUpsAddr := aghtest.StartLocalhostUpstream(t, localUpsHdlr).String()
client := &dns.Client{
Net: "tcp",
@@ -266,44 +279,25 @@ func TestServer_HandleDNSRequest_dns64(t *testing.T) {
}
for _, tc := range testCases {
tc := tc
// TODO(e.burkov): It seems [proxy.Proxy] isn't intended to be reused
// right after stop, due to a data race in [proxy.Proxy.Init] method
// when setting an OOB size. As a temporary workaround, recreate the
// whole server for each test case.
s := createTestServer(t, &filtering.Config{
BlockingMode: filtering.BlockingModeDefault,
}, ServerConfig{
UDPListenAddrs: []*net.UDPAddr{{}},
TCPListenAddrs: []*net.TCPAddr{{}},
UseDNS64: true,
Config: Config{
UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
},
ServePlainDNS: true,
}, localUps)
t.Run(tc.name, func(t *testing.T) {
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{
Answer: answer[sectionAnswer],
Ns: answer[sectionAuthority],
Extra: answer[sectionAdditional],
}).SetReply(req)
require.NoError(pt, w.WriteMsg(resp))
})
upsAddr := aghtest.StartLocalhostUpstream(t, upsHdlr).String()
// TODO(e.burkov): It seems [proxy.Proxy] isn't intended to be
// reused right after stop, due to a data race in [proxy.Proxy.Init]
// method when setting an OOB size. As a temporary workaround,
// recreate the whole server for each test case.
s := createTestServer(t, &filtering.Config{
BlockingMode: filtering.BlockingModeDefault,
}, ServerConfig{
UDPListenAddrs: []*net.UDPAddr{{}},
TCPListenAddrs: []*net.TCPAddr{{}},
UseDNS64: true,
Config: Config{
UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
UpstreamDNS: []string{upsAddr},
},
UsePrivateRDNS: true,
LocalPTRResolvers: []string{localUpsAddr},
ServePlainDNS: true,
})
s.conf.UpstreamConfig.Upstreams = []upstream.Upstream{newUps(tc.upsAns)}
startDeferStop(t, s)
req := (&dns.Msg{}).SetQuestion(tc.qname, tc.qtype)

View File

@@ -9,7 +9,6 @@ import (
"net/http"
"net/netip"
"runtime"
"slices"
"strings"
"sync"
"sync/atomic"
@@ -31,6 +30,7 @@ import (
"github.com/AdguardTeam/golibs/netutil/sysresolv"
"github.com/AdguardTeam/golibs/stringutil"
"github.com/miekg/dns"
"golang.org/x/exp/slices"
)
// DefaultTimeout is the default upstream timeout
@@ -464,8 +464,7 @@ func (s *Server) Start() error {
// startLocked starts the DNS server without locking. s.serverLock is expected
// to be locked.
func (s *Server) startLocked() error {
// TODO(e.burkov): Use context properly.
err := s.dnsProxy.Start(context.Background())
err := s.dnsProxy.Start()
if err == nil {
s.isRunning = true
}
@@ -518,56 +517,35 @@ func (s *Server) prepareLocalResolvers(
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)
// It assumes s.serverLock is locked or s not running.
func (s *Server) setupLocalResolvers(boot upstream.Resolver) (err error) {
uc, err := s.prepareLocalResolvers(boot)
if err != nil {
// Don't wrap the error because it's informative enough as is.
return nil, err
return err
}
localResolvers, err := proxy.New(&proxy.Config{
UpstreamConfig: uc,
})
s.localResolvers = &proxy.Proxy{
Config: proxy.Config{
UpstreamConfig: uc,
},
}
err = s.localResolvers.Init()
if err != nil {
return nil, &LocalResolversError{Err: err}
return fmt.Errorf("initializing proxy: %w", err)
}
s.localResolvers = localResolvers
// TODO(e.burkov): Should we also consider the DNS64 usage?
return uc, nil
if s.conf.UsePrivateRDNS &&
// Only set the upstream config if there are any upstreams. It's safe
// to put nil into [proxy.Config.PrivateRDNSUpstreamConfig].
len(uc.Upstreams)+len(uc.DomainReservedUpstreams)+len(uc.SpecifiedDomainUpstreams) > 0 {
s.dnsProxy.PrivateRDNSUpstreamConfig = uc
}
return nil
}
// Prepare initializes parameters of s using data from conf. conf must not be
@@ -608,24 +586,21 @@ func (s *Server) Prepare(conf *ServerConfig) (err error) {
return fmt.Errorf("preparing access: %w", err)
}
// Set the proxy here because [setupLocalResolvers] sets its values.
//
// TODO(e.burkov): Remove once the local resolvers logic moved to dnsproxy.
proxyConfig.PrivateRDNSUpstreamConfig, err = s.setupLocalResolvers(boot)
s.dnsProxy = &proxy.Proxy{Config: *proxyConfig}
err = s.setupLocalResolvers(boot)
if err != nil {
return fmt.Errorf("setting up resolvers: %w", err)
}
proxyConfig.Fallbacks, err = s.setupFallbackDNS()
err = s.setupFallbackDNS()
if err != nil {
return fmt.Errorf("setting up fallback dns servers: %w", err)
}
dnsProxy, err := proxy.New(proxyConfig)
if err != nil {
return fmt.Errorf("creating proxy: %w", err)
}
s.dnsProxy = dnsProxy
s.recDetector.clear()
s.setupAddrProc()
@@ -668,25 +643,26 @@ func (s *Server) prepareInternalDNS() (boot upstream.Resolver, err error) {
}
// setupFallbackDNS initializes the fallback DNS servers.
func (s *Server) setupFallbackDNS() (uc *proxy.UpstreamConfig, err error) {
func (s *Server) setupFallbackDNS() (err error) {
fallbacks := s.conf.FallbackDNS
fallbacks = stringutil.FilterOut(fallbacks, IsCommentOrEmpty)
if len(fallbacks) == 0 {
return nil, nil
return nil
}
uc, err = proxy.ParseUpstreamsConfig(fallbacks, &upstream.Options{
uc, err := proxy.ParseUpstreamsConfig(fallbacks, &upstream.Options{
// TODO(s.chzhen): Investigate if other options are needed.
Timeout: s.conf.UpstreamTimeout,
PreferIPv6: s.conf.BootstrapPreferIPv6,
// TODO(e.burkov): Use bootstrap.
})
if err != nil {
// Do not wrap the error because it's informative enough as is.
return nil, err
return err
}
return uc, nil
s.dnsProxy.Fallbacks = uc
return nil
}
// setupAddrProc initializes the address processor. It assumes s.serverLock is
@@ -754,9 +730,19 @@ func (s *Server) prepareInternalProxy() (err error) {
return fmt.Errorf("invalid upstream mode: %w", err)
}
s.internalProxy, err = proxy.New(conf)
// TODO(a.garipov): Make a proper constructor for proxy.Proxy.
p := &proxy.Proxy{
Config: *conf,
}
return err
err = p.Init()
if err != nil {
return err
}
s.internalProxy = p
return nil
}
// Stop stops the DNS server.
@@ -775,17 +761,14 @@ func (s *Server) stopLocked() (err error) {
// [upstream.Upstream] implementations.
if s.dnsProxy != nil {
// TODO(e.burkov): Use context properly.
err = s.dnsProxy.Shutdown(context.Background())
err = s.dnsProxy.Stop()
if err != nil {
log.Error("dnsforward: closing primary resolvers: %s", err)
}
}
logCloserErr(s.internalProxy.UpstreamConfig, "dnsforward: closing internal resolvers: %s")
if s.localResolvers != nil {
logCloserErr(s.localResolvers.UpstreamConfig, "dnsforward: closing local resolvers: %s")
}
logCloserErr(s.localResolvers.UpstreamConfig, "dnsforward: closing local resolvers: %s")
for _, b := range s.bootResolvers {
logCloserErr(b, "dnsforward: closing bootstrap %s: %s", b.Address())
@@ -858,8 +841,6 @@ func (s *Server) Reconfigure(conf *ServerConfig) error {
}
}
// TODO(e.burkov): It seems an error here brings the server down, which is
// not reliable enough.
err = s.Prepare(conf)
if err != nil {
return fmt.Errorf("could not reconfigure the server: %w", err)

View File

@@ -5,11 +5,9 @@ import (
"crypto/ecdsa"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/hex"
"encoding/pem"
"fmt"
"math/big"
@@ -65,7 +63,8 @@ func startDeferStop(t *testing.T, s *Server) {
t.Helper()
err := s.Start()
require.NoError(t, err)
require.NoErrorf(t, err, "failed to start server: %s", err)
testutil.CleanupAndRequireSuccess(t, s.Stop)
}
@@ -73,6 +72,7 @@ func createTestServer(
t *testing.T,
filterConf *filtering.Config,
forwardConf ServerConfig,
localUps upstream.Upstream,
) (s *Server) {
t.Helper()
@@ -82,8 +82,7 @@ func createTestServer(
@@||whitelist.example.org^
||127.0.0.255`
filters := []filtering.Filter{{
ID: 0,
Data: []byte(rules),
ID: 0, Data: []byte(rules),
}}
f, err := filtering.New(filterConf, filters)
@@ -106,6 +105,19 @@ func createTestServer(
err = s.Prepare(&forwardConf)
require.NoError(t, err)
s.serverLock.Lock()
defer s.serverLock.Unlock()
// TODO(e.burkov): Try to move it higher.
if localUps != nil {
ups := []upstream.Upstream{localUps}
s.localResolvers.UpstreamConfig.Upstreams = ups
s.conf.UsePrivateRDNS = true
s.dnsProxy.PrivateRDNSUpstreamConfig = &proxy.UpstreamConfig{
Upstreams: ups,
}
}
return s
}
@@ -169,7 +181,7 @@ func createTestTLS(t *testing.T, tlsConf TLSConfig) (s *Server, certPem []byte)
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
},
ServePlainDNS: true,
})
}, nil)
tlsConf.CertificateChainData, tlsConf.PrivateKeyData = certPem, keyPem
s.conf.TLSConfig = tlsConf
@@ -298,7 +310,7 @@ func TestServer(t *testing.T) {
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
},
ServePlainDNS: true,
})
}, nil)
s.conf.UpstreamConfig.Upstreams = []upstream.Upstream{newGoogleUpstream()}
startDeferStop(t, s)
@@ -398,7 +410,7 @@ func TestServerWithProtectionDisabled(t *testing.T) {
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
},
ServePlainDNS: true,
})
}, nil)
s.conf.UpstreamConfig.Upstreams = []upstream.Upstream{newGoogleUpstream()}
startDeferStop(t, s)
@@ -478,7 +490,7 @@ func TestServerRace(t *testing.T) {
ConfigModified: func() {},
ServePlainDNS: true,
}
s := createTestServer(t, filterConf, forwardConf)
s := createTestServer(t, filterConf, forwardConf, nil)
s.conf.UpstreamConfig.Upstreams = []upstream.Upstream{newGoogleUpstream()}
startDeferStop(t, s)
@@ -533,7 +545,7 @@ func TestSafeSearch(t *testing.T) {
},
ServePlainDNS: true,
}
s := createTestServer(t, filterConf, forwardConf)
s := createTestServer(t, filterConf, forwardConf, nil)
startDeferStop(t, s)
addr := s.dnsProxy.Addr(proxy.ProtoUDP).String()
@@ -616,7 +628,7 @@ func TestInvalidRequest(t *testing.T) {
},
},
ServePlainDNS: true,
})
}, nil)
startDeferStop(t, s)
addr := s.dnsProxy.Addr(proxy.ProtoUDP).String()
@@ -650,7 +662,7 @@ func TestBlockedRequest(t *testing.T) {
s := createTestServer(t, &filtering.Config{
ProtectionEnabled: true,
BlockingMode: filtering.BlockingModeDefault,
}, forwardConf)
}, forwardConf, nil)
startDeferStop(t, s)
addr := s.dnsProxy.Addr(proxy.ProtoUDP)
@@ -686,7 +698,7 @@ func TestServerCustomClientUpstream(t *testing.T) {
}
s := createTestServer(t, &filtering.Config{
BlockingMode: filtering.BlockingModeDefault,
}, forwardConf)
}, forwardConf, nil)
ups := aghtest.NewUpstreamMock(func(req *dns.Msg) (resp *dns.Msg, err error) {
atomic.AddUint32(&upsCalledCounter, 1)
@@ -761,7 +773,7 @@ func TestBlockCNAMEProtectionEnabled(t *testing.T) {
},
},
ServePlainDNS: true,
})
}, nil)
testUpstm := &aghtest.Upstream{
CName: testCNAMEs,
IPv4: testIPv4,
@@ -799,7 +811,7 @@ func TestBlockCNAME(t *testing.T) {
s := createTestServer(t, &filtering.Config{
ProtectionEnabled: true,
BlockingMode: filtering.BlockingModeDefault,
}, forwardConf)
}, forwardConf, nil)
s.conf.UpstreamConfig.Upstreams = []upstream.Upstream{
&aghtest.Upstream{
CName: testCNAMEs,
@@ -874,7 +886,7 @@ func TestClientRulesForCNAMEMatching(t *testing.T) {
}
s := createTestServer(t, &filtering.Config{
BlockingMode: filtering.BlockingModeDefault,
}, forwardConf)
}, forwardConf, nil)
s.conf.UpstreamConfig.Upstreams = []upstream.Upstream{
&aghtest.Upstream{
CName: testCNAMEs,
@@ -921,7 +933,7 @@ func TestNullBlockedRequest(t *testing.T) {
s := createTestServer(t, &filtering.Config{
ProtectionEnabled: true,
BlockingMode: filtering.BlockingModeNullIP,
}, forwardConf)
}, forwardConf, nil)
startDeferStop(t, s)
addr := s.dnsProxy.Addr(proxy.ProtoUDP)
@@ -1042,7 +1054,7 @@ func TestBlockedByHosts(t *testing.T) {
s := createTestServer(t, &filtering.Config{
ProtectionEnabled: true,
BlockingMode: filtering.BlockingModeDefault,
}, forwardConf)
}, forwardConf, nil)
startDeferStop(t, s)
addr := s.dnsProxy.Addr(proxy.ProtoUDP)
@@ -1090,7 +1102,7 @@ func TestBlockedBySafeBrowsing(t *testing.T) {
},
ServePlainDNS: true,
}
s := createTestServer(t, filterConf, forwardConf)
s := createTestServer(t, filterConf, forwardConf, nil)
startDeferStop(t, s)
addr := s.dnsProxy.Addr(proxy.ProtoUDP)
@@ -1318,7 +1330,6 @@ func TestPTRResponseFromHosts(t *testing.T) {
var eventsCalledCounter uint32
hc, err := aghnet.NewHostsContainer(testFS, &aghtest.FSWatcher{
OnStart: func() (_ error) { panic("not implemented") },
OnEvents: func() (e <-chan struct{}) {
assert.Equal(t, uint32(1), atomic.AddUint32(&eventsCalledCounter, 1))
@@ -1470,8 +1481,6 @@ func TestServer_Exchange(t *testing.T) {
onesIP = netip.MustParseAddr("1.1.1.1")
twosIP = netip.MustParseAddr("2.2.2.2")
localIP = netip.MustParseAddr("192.168.1.1")
pt = testutil.PanicT{}
)
onesRevExtIPv4, err := netutil.IPToReversedAddr(onesIP.AsSlice())
@@ -1480,73 +1489,72 @@ func TestServer_Exchange(t *testing.T) {
twosRevExtIPv4, err := netutil.IPToReversedAddr(twosIP.AsSlice())
require.NoError(t, err)
extUpsHdlr := dns.HandlerFunc(func(w dns.ResponseWriter, req *dns.Msg) {
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),
)
require.NoError(pt, w.WriteMsg(resp))
})
upsAddr := aghtest.StartLocalhostUpstream(t, extUpsHdlr).String()
extUpstream := &aghtest.UpstreamMock{
OnAddress: func() (addr string) { return "external.upstream.example" },
OnExchange: func(req *dns.Msg) (resp *dns.Msg, err error) {
return aghalg.Coalesce(
aghtest.MatchedResponse(req, dns.TypePTR, onesRevExtIPv4, onesHost),
doubleTTL(aghtest.MatchedResponse(req, dns.TypePTR, twosRevExtIPv4, twosHost)),
new(dns.Msg).SetRcode(req, dns.RcodeNameError),
), nil
},
}
revLocIPv4, err := netutil.IPToReversedAddr(localIP.AsSlice())
require.NoError(t, err)
locUpsHdlr := dns.HandlerFunc(func(w dns.ResponseWriter, req *dns.Msg) {
resp := aghalg.Coalesce(
aghtest.MatchedResponse(req, dns.TypePTR, revLocIPv4, dns.Fqdn(localDomainHost)),
new(dns.Msg).SetRcode(req, dns.RcodeNameError),
)
locUpstream := &aghtest.UpstreamMock{
OnAddress: func() (addr string) { return "local.upstream.example" },
OnExchange: func(req *dns.Msg) (resp *dns.Msg, err error) {
return aghalg.Coalesce(
aghtest.MatchedResponse(req, dns.TypePTR, revLocIPv4, localDomainHost),
new(dns.Msg).SetRcode(req, dns.RcodeNameError),
), nil
},
}
require.NoError(pt, w.WriteMsg(resp))
errUpstream := aghtest.NewErrorUpstream()
nonPtrUpstream := aghtest.NewBlockUpstream("some-host", true)
refusingUpstream := aghtest.NewUpstreamMock(func(req *dns.Msg) (resp *dns.Msg, err error) {
return new(dns.Msg).SetRcode(req, dns.RcodeRefused), nil
})
zeroTTLUps := &aghtest.UpstreamMock{
OnAddress: func() (addr string) { return "zero.ttl.example" },
OnExchange: func(req *dns.Msg) (resp *dns.Msg, err error) {
resp = new(dns.Msg).SetReply(req)
hdr := dns.RR_Header{
Name: req.Question[0].Name,
Rrtype: dns.TypePTR,
Class: dns.ClassINET,
Ttl: 0,
}
resp.Answer = []dns.RR{&dns.PTR{
Hdr: hdr,
Ptr: localDomainHost,
}}
errUpsHdlr := dns.HandlerFunc(func(w dns.ResponseWriter, req *dns.Msg) {
require.NoError(pt, w.WriteMsg(new(dns.Msg).SetRcode(req, dns.RcodeServerFailure)))
})
return resp, nil
},
}
nonPtrHdlr := dns.HandlerFunc(func(w dns.ResponseWriter, req *dns.Msg) {
hash := sha256.Sum256([]byte("some-host"))
resp := (&dns.Msg{
Answer: []dns.RR{&dns.TXT{
Hdr: dns.RR_Header{
Name: req.Question[0].Name,
Rrtype: dns.TypeTXT,
Class: dns.ClassINET,
Ttl: 60,
srv := &Server{
recDetector: newRecursionDetector(0, 1),
internalProxy: &proxy.Proxy{
Config: proxy.Config{
UpstreamConfig: &proxy.UpstreamConfig{
Upstreams: []upstream.Upstream{extUpstream},
},
Txt: []string{hex.EncodeToString(hash[:])},
}},
}).SetReply(req)
require.NoError(pt, w.WriteMsg(resp))
})
refusingHdlr := dns.HandlerFunc(func(w dns.ResponseWriter, req *dns.Msg) {
require.NoError(pt, w.WriteMsg(new(dns.Msg).SetRcode(req, dns.RcodeRefused)))
})
zeroTTLHdlr := dns.HandlerFunc(func(w dns.ResponseWriter, req *dns.Msg) {
resp := (&dns.Msg{
Answer: []dns.RR{&dns.PTR{
Hdr: dns.RR_Header{
Name: req.Question[0].Name,
Rrtype: dns.TypePTR,
Class: dns.ClassINET,
Ttl: 0,
},
Ptr: dns.Fqdn(localDomainHost),
}},
}).SetReply(req)
require.NoError(pt, w.WriteMsg(resp))
})
},
},
}
srv.conf.UsePrivateRDNS = true
srv.privateNets = netutil.SubnetSetFunc(netutil.IsLocallyServed)
require.NoError(t, srv.internalProxy.Init())
testCases := []struct {
req netip.Addr
wantErr error
locUpstream dns.Handler
locUpstream upstream.Upstream
name string
want string
wantTTL time.Duration
@@ -1561,35 +1569,35 @@ func TestServer_Exchange(t *testing.T) {
name: "local_good",
want: localDomainHost,
wantErr: nil,
locUpstream: locUpsHdlr,
locUpstream: locUpstream,
req: localIP,
wantTTL: defaultTTL,
}, {
name: "upstream_error",
want: "",
wantErr: ErrRDNSFailed,
locUpstream: errUpsHdlr,
wantErr: aghtest.ErrUpstream,
locUpstream: errUpstream,
req: localIP,
wantTTL: 0,
}, {
name: "empty_answer_error",
want: "",
wantErr: ErrRDNSNoData,
locUpstream: locUpsHdlr,
locUpstream: locUpstream,
req: netip.MustParseAddr("192.168.1.2"),
wantTTL: 0,
}, {
name: "invalid_answer",
want: "",
wantErr: ErrRDNSNoData,
locUpstream: nonPtrHdlr,
locUpstream: nonPtrUpstream,
req: localIP,
wantTTL: 0,
}, {
name: "refused",
want: "",
wantErr: ErrRDNSFailed,
locUpstream: refusingHdlr,
locUpstream: refusingUpstream,
req: localIP,
wantTTL: 0,
}, {
@@ -1603,28 +1611,23 @@ func TestServer_Exchange(t *testing.T) {
name: "zero_ttl",
want: localDomainHost,
wantErr: nil,
locUpstream: zeroTTLHdlr,
locUpstream: zeroTTLUps,
req: localIP,
wantTTL: 0,
}}
for _, tc := range testCases {
localUpsAddr := aghtest.StartLocalhostUpstream(t, tc.locUpstream).String()
pcfg := proxy.Config{
UpstreamConfig: &proxy.UpstreamConfig{
Upstreams: []upstream.Upstream{tc.locUpstream},
},
}
srv.localResolvers = &proxy.Proxy{
Config: pcfg,
}
require.NoError(t, srv.localResolvers.Init())
t.Run(tc.name, func(t *testing.T) {
srv := createTestServer(t, &filtering.Config{
BlockingMode: filtering.BlockingModeDefault,
}, ServerConfig{
Config: Config{
UpstreamDNS: []string{upsAddr},
UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
},
LocalPTRResolvers: []string{localUpsAddr},
UsePrivateRDNS: true,
ServePlainDNS: true,
})
host, ttl, eerr := srv.Exchange(tc.req)
require.ErrorIs(t, eerr, tc.wantErr)
@@ -1634,17 +1637,8 @@ func TestServer_Exchange(t *testing.T) {
}
t.Run("resolving_disabled", func(t *testing.T) {
srv := createTestServer(t, &filtering.Config{
BlockingMode: filtering.BlockingModeDefault,
}, ServerConfig{
Config: Config{
UpstreamDNS: []string{upsAddr},
UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
},
LocalPTRResolvers: []string{},
ServePlainDNS: true,
})
srv.conf.UsePrivateRDNS = false
t.Cleanup(func() { srv.conf.UsePrivateRDNS = true })
host, _, eerr := srv.Exchange(localIP)

View File

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

View File

@@ -4,7 +4,6 @@ import (
"encoding/binary"
"fmt"
"net"
"slices"
"strings"
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
@@ -13,6 +12,7 @@ import (
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/urlfilter/rules"
"github.com/miekg/dns"
"golang.org/x/exp/slices"
)
// beforeRequestHandler is the handler that is called before any other

View File

@@ -6,17 +6,16 @@ import (
"io"
"net/http"
"net/netip"
"slices"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
"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"
"golang.org/x/exp/slices"
)
// jsonDNSConfig is the JSON representation of the DNS server configuration.
@@ -295,7 +294,7 @@ func (req *jsonDNSConfig) checkFallbacks() (err error) {
return nil
}
_, err = proxy.ParseUpstreamsConfig(*req.Fallbacks, &upstream.Options{})
err = ValidateUpstreams(*req.Fallbacks)
if err != nil {
return fmt.Errorf("fallback servers: %w", err)
}
@@ -345,7 +344,7 @@ func (req *jsonDNSConfig) validate(privateNets netutil.SubnetSet) (err error) {
// validateUpstreamDNSServers returns an error if any field of req is invalid.
func (req *jsonDNSConfig) validateUpstreamDNSServers(privateNets netutil.SubnetSet) (err error) {
if req.Upstreams != nil {
_, err = proxy.ParseUpstreamsConfig(*req.Upstreams, &upstream.Options{})
err = ValidateUpstreams(*req.Upstreams)
if err != nil {
return fmt.Errorf("upstream servers: %w", err)
}
@@ -581,6 +580,9 @@ func (s *Server) handleTestUpstreamDNS(w http.ResponseWriter, r *http.Request) {
return
}
req.Upstreams = stringutil.FilterOut(req.Upstreams, IsCommentOrEmpty)
req.FallbackDNS = stringutil.FilterOut(req.FallbackDNS, IsCommentOrEmpty)
req.PrivateUpstreams = stringutil.FilterOut(req.PrivateUpstreams, IsCommentOrEmpty)
req.BootstrapDNS = stringutil.FilterOut(req.BootstrapDNS, IsCommentOrEmpty)
opts := &upstream.Options{

View File

@@ -83,7 +83,7 @@ func TestDNSForwardHTTP_handleGetConfig(t *testing.T) {
ConfigModified: func() {},
ServePlainDNS: true,
}
s := createTestServer(t, filterConf, forwardConf)
s := createTestServer(t, filterConf, forwardConf, nil)
s.sysResolvers = &emptySysResolvers{}
require.NoError(t, s.Start())
@@ -164,7 +164,7 @@ func TestDNSForwardHTTP_handleSetConfig(t *testing.T) {
ConfigModified: func() {},
ServePlainDNS: true,
}
s := createTestServer(t, filterConf, forwardConf)
s := createTestServer(t, filterConf, forwardConf, nil)
s.sysResolvers = &emptySysResolvers{}
defaultConf := s.conf
@@ -223,9 +223,8 @@ func TestDNSForwardHTTP_handleSetConfig(t *testing.T) {
wantSet: "",
}, {
name: "upstream_dns_bad",
wantSet: `validating dns config: upstream servers: parsing error at index 0: ` +
`cannot prepare the upstream: invalid address !!!: bad hostname "!!!": ` +
`bad top-level domain name label "!!!": bad top-level domain name label rune '!'`,
wantSet: `validating dns config: ` +
`upstream servers: validating upstream "!!!": not an ip:port`,
}, {
name: "bootstraps_bad",
wantSet: `validating dns config: checking bootstrap a: not a bootstrap: ParseAddr("a"): ` +
@@ -314,6 +313,98 @@ func TestIsCommentOrEmpty(t *testing.T) {
}
}
func TestValidateUpstreams(t *testing.T) {
const sdnsStamp = `sdns://AQMAAAAAAAAAFDE3Ni4xMDMuMTMwLjEzMDo1NDQzINErR_J` +
`S3PLCu_iZEIbq95zkSV2LFsigxDIuUso_OQhzIjIuZG5zY3J5cHQuZGVmYXVsdC5uczE` +
`uYWRndWFyZC5jb20`
testCases := []struct {
name string
wantErr string
set []string
}{{
name: "empty",
wantErr: ``,
set: nil,
}, {
name: "comment",
wantErr: ``,
set: []string{"# comment"},
}, {
name: "no_default",
wantErr: `no default upstreams specified`,
set: []string{
"[/host.com/]1.1.1.1",
"[//]tls://1.1.1.1",
"[/www.host.com/]#",
"[/host.com/google.com/]8.8.8.8",
"[/host/]" + sdnsStamp,
},
}, {
name: "with_default",
wantErr: ``,
set: []string{
"[/host.com/]1.1.1.1",
"[//]tls://1.1.1.1",
"[/www.host.com/]#",
"[/host.com/google.com/]8.8.8.8",
"[/host/]" + sdnsStamp,
"8.8.8.8",
},
}, {
name: "invalid",
wantErr: `validating upstream "dhcp://fake.dns": bad protocol "dhcp"`,
set: []string{"dhcp://fake.dns"},
}, {
name: "invalid",
wantErr: `validating upstream "1.2.3.4.5": not an ip:port`,
set: []string{"1.2.3.4.5"},
}, {
name: "invalid",
wantErr: `validating upstream "123.3.7m": not an ip:port`,
set: []string{"123.3.7m"},
}, {
name: "invalid",
wantErr: `splitting upstream line "[/host.com]tls://dns.adguard.com": ` +
`missing separator`,
set: []string{"[/host.com]tls://dns.adguard.com"},
}, {
name: "invalid",
wantErr: `validating upstream "[host.ru]#": not an ip:port`,
set: []string{"[host.ru]#"},
}, {
name: "valid_default",
wantErr: ``,
set: []string{
"1.1.1.1",
"tls://1.1.1.1",
"https://dns.adguard.com/dns-query",
sdnsStamp,
"udp://dns.google",
"udp://8.8.8.8",
"[/host.com/]1.1.1.1",
"[//]tls://1.1.1.1",
"[/www.host.com/]#",
"[/host.com/google.com/]8.8.8.8",
"[/host/]" + sdnsStamp,
"[/пример.рф/]8.8.8.8",
},
}, {
name: "bad_domain",
wantErr: `splitting upstream line "[/!/]8.8.8.8": domain at index 0: ` +
`bad domain name "!": bad top-level domain name label "!": ` +
`bad top-level domain name label rune '!'`,
set: []string{"[/!/]8.8.8.8"},
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
err := ValidateUpstreams(tc.set)
testutil.AssertErrorMsg(t, tc.wantErr, err)
})
}
}
func TestValidateUpstreamsPrivate(t *testing.T) {
ss := netutil.SubnetSetFunc(netutil.IsLocallyServed)
@@ -418,7 +509,6 @@ func TestServer_HandleTestUpstreamDNS(t *testing.T) {
},
},
&aghtest.FSWatcher{
OnStart: func() (_ error) { panic("not implemented") },
OnEvents: func() (e <-chan struct{}) { return nil },
OnAdd: func(_ string) (err error) { return nil },
OnClose: func() (err error) { return nil },
@@ -439,7 +529,7 @@ func TestServer_HandleTestUpstreamDNS(t *testing.T) {
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
},
ServePlainDNS: true,
})
}, nil)
srv.etcHosts = upstream.NewHostsResolver(hc)
startDeferStop(t, srv)

View File

@@ -2,13 +2,13 @@ package dnsforward
import (
"net/netip"
"slices"
"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"
"golang.org/x/exp/slices"
)
// makeResponse creates a DNS response by req and sets necessary flags. It also

View File

@@ -9,6 +9,7 @@ import (
"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"
@@ -86,7 +87,7 @@ func TestServer_ProcessInitial(t *testing.T) {
s := createTestServer(t, &filtering.Config{
BlockingMode: filtering.BlockingModeDefault,
}, c)
}, c, nil)
var gotAddr netip.Addr
s.addrProc = &aghtest.AddressProcessor{
@@ -187,7 +188,7 @@ func TestServer_ProcessFilteringAfterResponse(t *testing.T) {
s := createTestServer(t, &filtering.Config{
BlockingMode: filtering.BlockingModeDefault,
}, c)
}, c, nil)
resp := newResp(dns.RcodeSuccess, tc.req, tc.respAns)
dctx := &dnsContext{
@@ -247,9 +248,9 @@ func TestServer_ProcessDDRQuery(t *testing.T) {
host string
want []*dns.SVCB
wantRes resultCode
addrsDoH []*net.TCPAddr
addrsDoT []*net.TCPAddr
addrsDoQ []*net.UDPAddr
portDoH int
portDoT int
portDoQ int
qtype uint16
ddrEnabled bool
}{{
@@ -258,14 +259,14 @@ func TestServer_ProcessDDRQuery(t *testing.T) {
host: testQuestionTarget,
qtype: dns.TypeSVCB,
ddrEnabled: true,
addrsDoH: []*net.TCPAddr{{Port: 8043}},
portDoH: 8043,
}, {
name: "pass_qtype",
wantRes: resultCodeFinish,
host: ddrHostFQDN,
qtype: dns.TypeA,
ddrEnabled: true,
addrsDoH: []*net.TCPAddr{{Port: 8043}},
portDoH: 8043,
}, {
name: "pass_disabled_tls",
wantRes: resultCodeFinish,
@@ -278,7 +279,7 @@ func TestServer_ProcessDDRQuery(t *testing.T) {
host: ddrHostFQDN,
qtype: dns.TypeSVCB,
ddrEnabled: false,
addrsDoH: []*net.TCPAddr{{Port: 8043}},
portDoH: 8043,
}, {
name: "dot",
wantRes: resultCodeFinish,
@@ -286,7 +287,7 @@ func TestServer_ProcessDDRQuery(t *testing.T) {
host: ddrHostFQDN,
qtype: dns.TypeSVCB,
ddrEnabled: true,
addrsDoT: []*net.TCPAddr{{Port: 8043}},
portDoT: 8043,
}, {
name: "doh",
wantRes: resultCodeFinish,
@@ -294,7 +295,7 @@ func TestServer_ProcessDDRQuery(t *testing.T) {
host: ddrHostFQDN,
qtype: dns.TypeSVCB,
ddrEnabled: true,
addrsDoH: []*net.TCPAddr{{Port: 8044}},
portDoH: 8044,
}, {
name: "doq",
wantRes: resultCodeFinish,
@@ -302,7 +303,7 @@ func TestServer_ProcessDDRQuery(t *testing.T) {
host: ddrHostFQDN,
qtype: dns.TypeSVCB,
ddrEnabled: true,
addrsDoQ: []*net.UDPAddr{{Port: 8042}},
portDoQ: 8042,
}, {
name: "dot_doh",
wantRes: resultCodeFinish,
@@ -310,35 +311,13 @@ func TestServer_ProcessDDRQuery(t *testing.T) {
host: ddrHostFQDN,
qtype: dns.TypeSVCB,
ddrEnabled: true,
addrsDoT: []*net.TCPAddr{{Port: 8043}},
addrsDoH: []*net.TCPAddr{{Port: 8044}},
portDoT: 8043,
portDoH: 8044,
}}
_, certPem, keyPem := createServerTLSConfig(t)
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
s := createTestServer(t, &filtering.Config{
BlockingMode: filtering.BlockingModeDefault,
}, ServerConfig{
Config: Config{
HandleDDR: tc.ddrEnabled,
UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
},
TLSConfig: TLSConfig{
ServerName: ddrTestDomainName,
CertificateChainData: certPem,
PrivateKeyData: keyPem,
TLSListenAddrs: tc.addrsDoT,
HTTPSListenAddrs: tc.addrsDoH,
QUICListenAddrs: tc.addrsDoQ,
},
ServePlainDNS: true,
})
// TODO(e.burkov): Generate a certificate actually containing the
// IP addresses.
s.conf.hasIPAddrs = true
s := prepareTestServer(t, tc.portDoH, tc.portDoT, tc.portDoQ, tc.ddrEnabled)
req := createTestMessageWithType(tc.host, tc.qtype)
@@ -379,6 +358,41 @@ func createTestDNSFilter(t *testing.T) (f *filtering.DNSFilter) {
return f
}
func prepareTestServer(t *testing.T, portDoH, portDoT, portDoQ int, ddrEnabled bool) (s *Server) {
t.Helper()
s = &Server{
dnsFilter: createTestDNSFilter(t),
dnsProxy: &proxy.Proxy{
Config: proxy.Config{},
},
conf: ServerConfig{
Config: Config{
HandleDDR: ddrEnabled,
},
TLSConfig: TLSConfig{
ServerName: ddrTestDomainName,
},
ServePlainDNS: true,
},
}
if portDoT > 0 {
s.dnsProxy.TLSListenAddr = []*net.TCPAddr{{Port: portDoT}}
s.conf.hasIPAddrs = true
}
if portDoQ > 0 {
s.dnsProxy.QUICListenAddr = []*net.UDPAddr{{Port: portDoQ}}
}
if portDoH > 0 {
s.conf.HTTPSListenAddrs = []*net.TCPAddr{{Port: portDoH}}
}
return s
}
func TestServer_ProcessDetermineLocal(t *testing.T) {
s := &Server{
privateNets: netutil.SubnetSetFunc(netutil.IsLocallyServed),
@@ -666,16 +680,13 @@ func TestServer_ProcessRestrictLocal(t *testing.T) {
intPTRAnswer = "some.local-client."
)
localUpsHdlr := dns.HandlerFunc(func(w dns.ResponseWriter, req *dns.Msg) {
resp := aghalg.Coalesce(
ups := aghtest.NewUpstreamMock(func(req *dns.Msg) (resp *dns.Msg, err error) {
return aghalg.Coalesce(
aghtest.MatchedResponse(req, dns.TypePTR, extPTRQuestion, extPTRAnswer),
aghtest.MatchedResponse(req, dns.TypePTR, intPTRQuestion, intPTRAnswer),
new(dns.Msg).SetRcode(req, dns.RcodeNameError),
)
require.NoError(testutil.PanicT{}, w.WriteMsg(resp))
), nil
})
localUpsAddr := aghtest.StartLocalhostUpstream(t, localUpsHdlr).String()
s := createTestServer(t, &filtering.Config{
BlockingMode: filtering.BlockingModeDefault,
@@ -685,14 +696,12 @@ func TestServer_ProcessRestrictLocal(t *testing.T) {
// TODO(s.chzhen): Add tests where EDNSClientSubnet.Enabled is true.
// Improve Config declaration for tests.
Config: Config{
UpstreamDNS: []string{localUpsAddr},
UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
},
UsePrivateRDNS: true,
LocalPTRResolvers: []string{localUpsAddr},
ServePlainDNS: true,
})
ServePlainDNS: true,
}, ups)
s.conf.UpstreamConfig.Upstreams = []upstream.Upstream{ups}
startDeferStop(t, s)
testCases := []struct {
@@ -755,16 +764,6 @@ 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 := aghalg.Coalesce(
aghtest.MatchedResponse(req, dns.TypePTR, reqAddr, locDomain),
new(dns.Msg).SetRcode(req, dns.RcodeNameError),
)
require.NoError(testutil.PanicT{}, w.WriteMsg(resp))
})
localUpsAddr := aghtest.StartLocalhostUpstream(t, localUpsHdlr).String()
s := createTestServer(
t,
&filtering.Config{
@@ -777,10 +776,14 @@ func TestServer_ProcessLocalPTR_usingResolvers(t *testing.T) {
UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
},
UsePrivateRDNS: true,
LocalPTRResolvers: []string{localUpsAddr},
ServePlainDNS: true,
ServePlainDNS: true,
},
aghtest.NewUpstreamMock(func(req *dns.Msg) (resp *dns.Msg, err error) {
return aghalg.Coalesce(
aghtest.MatchedResponse(req, dns.TypePTR, reqAddr, locDomain),
new(dns.Msg).SetRcode(req, dns.RcodeNameError),
), nil
}),
)
var proxyCtx *proxy.DNSContext

View File

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

View File

@@ -2,9 +2,10 @@ package dnsforward
import (
"fmt"
"net"
"net/netip"
"os"
"slices"
"strings"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
@@ -15,6 +16,29 @@ import (
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/stringutil"
"golang.org/x/exp/maps"
"golang.org/x/exp/slices"
)
const (
// errNotDomainSpecific is returned when the upstream should be
// domain-specific, but isn't.
errNotDomainSpecific errors.Error = "not a domain-specific upstream"
// errMissingSeparator is returned when the domain-specific part of the
// upstream configuration line isn't closed.
errMissingSeparator errors.Error = "missing separator"
// errDupSeparator is returned when the domain-specific part of the upstream
// configuration line contains more than one ending separator.
errDupSeparator errors.Error = "duplicated separator"
// errNoDefaultUpstreams is returned when there are no default upstreams
// specified in the upstream configuration.
errNoDefaultUpstreams errors.Error = "no default upstreams specified"
// errWrongResponse is returned when the checked upstream replies in an
// unexpected way.
errWrongResponse errors.Error = "wrong response"
)
// loadUpstreams parses upstream DNS servers from the configured file or from
@@ -175,12 +199,84 @@ func IsCommentOrEmpty(s string) (ok bool) {
return len(s) == 0 || s[0] == '#'
}
// newUpstreamConfig validates upstreams and returns an appropriate upstream
// configuration or nil if it can't be built.
//
// TODO(e.burkov): Perhaps proxy.ParseUpstreamsConfig should validate upstreams
// slice already so that this function may be considered useless.
func newUpstreamConfig(upstreams []string) (conf *proxy.UpstreamConfig, err error) {
// No need to validate comments and empty lines.
upstreams = stringutil.FilterOut(upstreams, IsCommentOrEmpty)
if len(upstreams) == 0 {
// Consider this case valid since it means the default server should be
// used.
return nil, nil
}
err = validateUpstreamConfig(upstreams)
if err != nil {
// Don't wrap the error since it's informative enough as is.
return nil, err
}
conf, err = proxy.ParseUpstreamsConfig(
upstreams,
&upstream.Options{
Bootstrap: net.DefaultResolver,
Timeout: DefaultTimeout,
},
)
if err != nil {
// Don't wrap the error since it's informative enough as is.
return nil, err
} else if len(conf.Upstreams) == 0 {
return nil, errNoDefaultUpstreams
}
return conf, nil
}
// validateUpstreamConfig validates each upstream from the upstream
// configuration and returns an error if any upstream is invalid.
//
// TODO(e.burkov): Merge with [upstreamConfigValidator] somehow.
func validateUpstreamConfig(conf []string) (err error) {
for _, u := range conf {
var ups []string
var isSpecific bool
ups, isSpecific, err = splitUpstreamLine(u)
if err != nil {
// Don't wrap the error since it's informative enough as is.
return err
}
for _, addr := range ups {
_, err = validateUpstream(addr, isSpecific)
if err != nil {
return fmt.Errorf("validating upstream %q: %w", addr, err)
}
}
}
return nil
}
// ValidateUpstreams validates each upstream and returns an error if any
// upstream is invalid or if there are no default upstreams specified.
//
// TODO(e.burkov): Merge with [upstreamConfigValidator] somehow.
func ValidateUpstreams(upstreams []string) (err error) {
_, err = newUpstreamConfig(upstreams)
return err
}
// 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{})
conf, err := newUpstreamConfig(upstreams)
if err != nil {
return fmt.Errorf("creating config: %w", err)
}
@@ -212,3 +308,66 @@ func ValidateUpstreamsPrivate(upstreams []string, privateNets netutil.SubnetSet)
return errors.Annotate(errors.Join(errs...), "checking domain-specific upstreams: %w")
}
// protocols are the supported URL schemes for upstreams.
var protocols = []string{"h3", "https", "quic", "sdns", "tcp", "tls", "udp"}
// validateUpstream returns an error if u alongside with domains is not a valid
// upstream configuration. useDefault is true if the upstream is
// domain-specific and is configured to point at the default upstream server
// which is validated separately. The upstream is considered domain-specific
// only if domains is at least not nil.
func validateUpstream(u string, isSpecific bool) (useDefault bool, err error) {
// The special server address '#' means that default server must be used.
if useDefault = u == "#" && isSpecific; useDefault {
return useDefault, nil
}
// Check if the upstream has a valid protocol prefix.
//
// TODO(e.burkov): Validate the domain name.
if proto, _, ok := strings.Cut(u, "://"); ok {
if !slices.Contains(protocols, proto) {
return false, fmt.Errorf("bad protocol %q", proto)
}
} else if _, err = netip.ParseAddr(u); err == nil {
return false, nil
} else if _, err = netip.ParseAddrPort(u); err == nil {
return false, nil
}
return false, err
}
// splitUpstreamLine returns the upstreams and the specified domains. domains
// is nil when the upstream is not domains-specific. Otherwise it may also be
// empty.
func splitUpstreamLine(upstreamStr string) (upstreams []string, isSpecific bool, err error) {
if !strings.HasPrefix(upstreamStr, "[/") {
return []string{upstreamStr}, false, nil
}
defer func() { err = errors.Annotate(err, "splitting upstream line %q: %w", upstreamStr) }()
doms, ups, found := strings.Cut(upstreamStr[2:], "/]")
if !found {
return nil, false, errMissingSeparator
} else if strings.Contains(ups, "/]") {
return nil, false, errDupSeparator
}
for i, host := range strings.Split(doms, "/") {
if host == "" {
continue
}
err = netutil.ValidateDomainName(strings.TrimPrefix(host, "*."))
if err != nil {
return nil, false, fmt.Errorf("domain at index %d: %w", i, err)
}
isSpecific = true
}
return strings.Fields(ups), isSpecific, nil
}

View File

@@ -100,7 +100,8 @@ func TestUpstreamConfigValidator(t *testing.T) {
name: "bad_specification",
general: []string{"[/domain.example/]/]1.2.3.4"},
want: map[string]string{
"[/domain.example/]/]1.2.3.4": generalTextLabel + " 1: parsing error",
"[/domain.example/]/]1.2.3.4": `splitting upstream line ` +
`"[/domain.example/]/]1.2.3.4": duplicated separator`,
},
}, {
name: "all_different",
@@ -119,9 +120,23 @@ func TestUpstreamConfigValidator(t *testing.T) {
fallback: []string{"[/example/" + goodUps},
private: []string{"[/example//bad.123/]" + goodUps},
want: map[string]string{
"[/example/]/]" + goodUps: generalTextLabel + " 1: parsing error",
"[/example/" + goodUps: fallbackTextLabel + " 1: parsing error",
"[/example//bad.123/]" + goodUps: privateTextLabel + " 1: parsing error",
`[/example/]/]` + goodUps: `splitting upstream line ` +
`"[/example/]/]` + goodUps + `": duplicated separator`,
`[/example/` + goodUps: `splitting upstream line ` +
`"[/example/` + goodUps + `": missing separator`,
`[/example//bad.123/]` + goodUps: `splitting upstream line ` +
`"[/example//bad.123/]` + goodUps + `": domain at index 2: ` +
`bad domain name "bad.123": ` +
`bad top-level domain name label "123": all octets are numeric`,
},
}, {
name: "non-specific_default",
general: []string{
"#",
"[/example/]#",
},
want: map[string]string{
"#": "not a domain-specific upstream",
},
}, {
name: "bad_proto",
@@ -129,15 +144,7 @@ func TestUpstreamConfigValidator(t *testing.T) {
"bad://1.2.3.4",
},
want: map[string]string{
"bad://1.2.3.4": generalTextLabel + " 1: parsing error",
},
}, {
name: "truncated_line",
general: []string{
"This is a very long line. It will cause a parsing error and will be truncated here.",
},
want: map[string]string{
"This is a very long line. It will cause a parsing error and will be truncated …": "upstream_dns 1: parsing error",
"bad://1.2.3.4": `bad protocol "bad"`,
},
}}

View File

@@ -4,14 +4,13 @@ import (
"encoding/json"
"fmt"
"net/http"
"slices"
"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"
"golang.org/x/exp/slices"
)
// serviceRules maps a service ID to its filtering 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

@@ -6,7 +6,6 @@ import (
"net/http"
"os"
"path/filepath"
"slices"
"strconv"
"strings"
"time"
@@ -16,6 +15,7 @@ import (
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/stringutil"
"golang.org/x/exp/slices"
)
// filterDir is the subdirectory of a data directory to store downloaded
@@ -608,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

@@ -12,7 +12,6 @@ import (
"path/filepath"
"runtime"
"runtime/debug"
"slices"
"strings"
"sync"
"sync/atomic"
@@ -30,6 +29,20 @@ import (
"github.com/AdguardTeam/urlfilter/filterlist"
"github.com/AdguardTeam/urlfilter/rules"
"github.com/miekg/dns"
"golang.org/x/exp/slices"
)
// 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
@@ -1126,7 +1139,7 @@ func (d *DNSFilter) checkSafeBrowsing(
res = Result{
Rules: []*ResultRule{{
Text: "adguard-malware-shavar",
FilterListID: rulelist.URLFilterIDSafeBrowsing,
FilterListID: SafeBrowsingListID,
}},
Reason: FilteredSafeBrowsing,
IsFiltered: true,
@@ -1158,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

@@ -5,7 +5,6 @@ import (
"crypto/sha256"
"encoding/hex"
"fmt"
"slices"
"strings"
"time"
@@ -15,6 +14,7 @@ import (
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/stringutil"
"github.com/miekg/dns"
"golang.org/x/exp/slices"
"golang.org/x/net/publicsuffix"
)

View File

@@ -3,7 +3,6 @@ package hashprefix
import (
"crypto/sha256"
"encoding/hex"
"slices"
"strings"
"testing"
"time"
@@ -13,6 +12,7 @@ import (
"github.com/miekg/dns"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/exp/slices"
)
const (

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"
@@ -41,7 +40,6 @@ func TestDNSFilter_CheckHost_hostsContainer(t *testing.T) {
},
}
watcher := &aghtest.FSWatcher{
OnStart: func() (_ error) { panic("not implemented") },
OnEvents: func() (e <-chan struct{}) { return nil },
OnAdd: func(name string) (err error) { return nil },
OnClose: func() (err error) { return nil },
@@ -72,7 +70,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 +79,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 +88,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 +97,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 +106,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 +133,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 +142,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 +163,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

@@ -8,7 +8,6 @@ import (
"net/url"
"os"
"path/filepath"
"slices"
"sync"
"time"
@@ -16,6 +15,7 @@ import (
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/miekg/dns"
"golang.org/x/exp/slices"
)
// validateFilterURL validates the filter list URL or file name.

View File

@@ -3,7 +3,6 @@ package rewrite
import (
"fmt"
"slices"
"strings"
"sync"
@@ -13,6 +12,7 @@ import (
"github.com/AdguardTeam/urlfilter/filterlist"
"github.com/AdguardTeam/urlfilter/rules"
"github.com/miekg/dns"
"golang.org/x/exp/slices"
)
// Storage is a storage for rewrite rules.

View File

@@ -3,10 +3,10 @@ package filtering
import (
"encoding/json"
"net/http"
"slices"
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
"github.com/AdguardTeam/golibs/log"
"golang.org/x/exp/slices"
)
// TODO(d.kolyshev): Use [rewrite.Item] instead.

View File

@@ -3,12 +3,12 @@ package filtering
import (
"fmt"
"net/netip"
"slices"
"strings"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/miekg/dns"
"golang.org/x/exp/slices"
)
// Legacy DNS rewrites

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,63 +0,0 @@
package rulelist_test
import (
"context"
"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)
ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
t.Cleanup(cancel)
buf := make([]byte, rulelist.DefaultRuleBufSize)
cli := &http.Client{
Timeout: 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

@@ -2,7 +2,9 @@ package rulelist_test
import (
"context"
"io"
"net/http"
"net/http/httptest"
"net/url"
"os"
"path/filepath"
@@ -18,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
@@ -39,7 +56,7 @@ func TestFilter_Refresh(t *testing.T) {
name: "file",
url: &url.URL{
Scheme: "file",
Path: fileURL.Path,
Path: initialFile,
},
wantNewErrMsg: "",
}, {

View File

@@ -6,9 +6,9 @@ import (
"fmt"
"hash/crc32"
"io"
"slices"
"github.com/AdguardTeam/golibs/errors"
"golang.org/x/exp/slices"
)
// Parser is a filtering-rule parser that collects data, such as the checksum

View File

@@ -23,28 +23,8 @@ const DefaultMaxRuleListSize = 64 * datasize.MB
// URLFilterID is a semantic type-alias for IDs used for working with package
// urlfilter.
//
// TODO(a.garipov): Use everywhere in package filtering.
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 = 0
URLFilterIDEtcHosts = -1
URLFilterIDBlockedService = -2
URLFilterIDParentalControl = -3
URLFilterIDSafeBrowsing = -4
URLFilterIDSafeSearch = -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

@@ -14,7 +14,6 @@ import (
"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"
@@ -99,7 +98,7 @@ func NewDefault(
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
@@ -235,7 +234,7 @@ func (ss *Default) newResult(
) (res *filtering.Result, err error) {
res = &filtering.Result{
Rules: []*filtering.ResultRule{{
FilterListID: rulelist.URLFilterIDSafeSearch,
FilterListID: filtering.SafeSearchListID,
}},
Reason: filtering.FilteredSafeSearch,
IsFiltered: true,
@@ -369,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