Compare commits

..

203 Commits

Author SHA1 Message Date
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
112 changed files with 2023 additions and 10836 deletions

View File

@@ -1,142 +1,109 @@
'body':
- 'attributes':
'description': >
Please make sure that the issue is not a duplicate or a question.
If it's a duplicate, please react to the original issue with a
thumbs up. If it's a question, please post it to the GitHub
Discussions page.
'label': 'Prerequisites'
'options':
- 'label': >
I have checked the
[Wiki](https://github.com/AdguardTeam/AdGuardHome/wiki) and
[Discussions](https://github.com/AdguardTeam/AdGuardHome/discussions)
and found no answer
'required': true
- 'label': >
I have searched other issues and found no duplicates
'required': true
- 'label': >
I want to report a bug and not ask a question
'required': true
'description': >
Please make sure that the issue is not a duplicate or a question.
If it's a duplicate, please react to the original issue with a
thumbs up. If it's a question, please post it to the GitHub
Discussions page.
'label': 'Prerequisites'
'options':
- 'label': >
I have checked the
[Wiki](https://github.com/AdguardTeam/AdGuardHome/wiki) and
[Discussions](https://github.com/AdguardTeam/AdGuardHome/discussions)
and found no answer
'required': true
- 'label': >
I have searched other issues and found no duplicates
'required': true
- 'label': >
I want to report a bug and not ask a question
'required': true
'id': 'prerequisites'
'type': 'checkboxes'
- 'attributes':
'description': 'On which operating system type does the issue occur?'
'label': 'Operating system type'
'options':
- 'FreeBSD'
- 'Linux, OpenWrt'
- 'Linux, Other (please mention the version in the description)'
- 'macOS (aka Darwin)'
- 'OpenBSD'
- 'Windows'
- 'Other (please mention in the description)'
'description': 'On which operating system type does the issue occur?'
'label': 'Operating system type'
'options':
- 'FreeBSD'
- 'Linux, OpenWrt'
- 'Linux, Other (please mention the version in the description)'
- 'macOS (aka Darwin)'
- 'OpenBSD'
- 'Windows'
- 'Other (please mention in the description)'
'id': 'os'
'type': 'dropdown'
'validations':
'required': true
'required': true
- 'attributes':
'description': 'On which CPU architecture does the issue occur?'
'label': 'CPU architecture'
'options':
- 'AMD64'
- 'x86'
- '64-bit ARM'
- 'ARMv5'
- 'ARMv6'
- 'ARMv7'
- '64-bit MIPS'
- '64-bit MIPS LE'
- '32-bit MIPS'
- '32-bit MIPS LE'
- '64-bit PowerPC LE'
- 'Other (please mention in the description)'
'description': 'On which CPU architecture does the issue occur?'
'label': 'CPU architecture'
'options':
- 'AMD64'
- 'x86'
- '64-bit ARM'
- 'ARMv5'
- 'ARMv6'
- 'ARMv7'
- '64-bit MIPS'
- '64-bit MIPS LE'
- '32-bit MIPS'
- '32-bit MIPS LE'
- '64-bit PowerPC LE'
- 'Other (please mention in the description)'
'id': 'arch'
'type': 'dropdown'
'validations':
'required': true
'required': true
- 'attributes':
'description': 'How did you install AdGuard Home?'
'label': 'Installation'
'options':
- 'GitHub releases or script from README'
- 'Docker'
- 'Snapcraft'
- 'Custom port'
- 'Other (please mention in the description)'
'description': 'How did you install AdGuard Home?'
'label': 'Installation'
'options':
- 'GitHub releases or script from README'
- 'Docker'
- 'Snapcraft'
- 'Custom port'
- 'Other (please mention in the description)'
'id': 'install'
'type': 'dropdown'
'validations':
'required': true
'required': true
- 'attributes':
'description': 'How did you setup AdGuard Home?'
'label': 'Setup'
'options':
- 'On one machine'
- 'On a router, DHCP is handled by the router'
- 'On a router, DHCP is handled by AdGuard Home'
- 'Other (please mention in the description)'
'description': 'How did you setup AdGuard Home?'
'label': 'Setup'
'options':
- 'On one machine'
- 'On a router, DHCP is handled by the router'
- 'On a router, DHCP is handled by AdGuard Home'
- 'Other (please mention in the description)'
'id': 'setup'
'type': 'dropdown'
'validations':
'required': true
'required': true
- 'attributes':
'description': 'What version of AdGuard Home are you using?'
'label': 'AdGuard Home version'
'description': 'What version of AdGuard Home are you using?'
'label': 'AdGuard Home version'
'id': 'version'
'type': 'input'
'validations':
'required': true
'required': true
- 'attributes':
'description': 'Please provide a set of steps to reproduce the issue'
'label': 'Issue Details'
'placeholder':
'value': |
Steps to reproduce:
1.
2.
3.
'id': 'what-happened'
'description': 'Please describe the bug'
'label': 'Description'
'value': |
#### What did you do?
#### Expected result
#### Actual result
#### Screenshots (if applicable)
#### Additional information
'id': 'description'
'type': 'textarea'
'validations':
'required': true
- 'attributes':
'label': 'Expected Behavior'
'description': 'Describe the expected behavior'
'placeholder': |
A clear and concise description of what you expected to happen.
'id': 'expected-behavior'
'type': 'textarea'
'validations':
'required': false
- 'attributes':
'label': 'Actual Behavior'
'description': 'Describe the actual behavior'
'placeholder': 'A clear description of what happened instead.'
'id': 'actual-behavior'
'type': 'textarea'
'validations':
'required': true
- 'attributes':
'label': 'Screenshots'
'description': |
If applicable add screenshots explaining your problem.
You can drag and drop images or paste them from clipboard.
Use `<details> </details>` tag to hide screenshots under the spoiler.
'placeholder': 'If applicable add screenshots explaining your problem.'
'value': |
<details><summary>Screenshot 1:</summary>
<!-- paste screenshot here -->
</details>
'id': 'screenshots'
'type': 'textarea'
'validations':
'required': false
'description': >
Open a bug report. Please do not open bug reports for questions. If you
want to ask a question, ask in in the Discussions section.
'name': 'Bug report'
'labels': [ 'bug' ]
'required': true
'description': 'File a bug report'
'name': 'Bug'

View File

@@ -1,54 +1,41 @@
'body':
- 'attributes':
'description': >
Please make sure that the issue is not a duplicate or a question.
If it's a duplicate, please react to the original issue with a
thumbs up. If it's a question, please post it to the GitHub
Discussions page.
'label': 'Prerequisites'
'options':
- 'label': >
I have checked the
[Wiki](https://github.com/AdguardTeam/AdGuardHome/wiki) and
[Discussions](https://github.com/AdguardTeam/AdGuardHome/discussions)
and found no answer
'required': true
- 'label': >
I have searched other issues and found no duplicates
'required': true
- 'label': >
I want to request a feature or enhancement and not ask a
question
'required': true
'description': >
Please make sure that the issue is not a duplicate or a question.
If it's a duplicate, please react to the original issue with a
thumbs up. If it's a question, please post it to the GitHub
Discussions page.
'label': 'Prerequisites'
'options':
- 'label': >
I have checked the
[Wiki](https://github.com/AdguardTeam/AdGuardHome/wiki) and
[Discussions](https://github.com/AdguardTeam/AdGuardHome/discussions)
and found no answer
'required': true
- 'label': >
I have searched other issues and found no duplicates
'required': true
- 'label': >
I want to request a feature or enhancement and not ask a
question
'required': true
'id': 'prerequisites'
'type': 'checkboxes'
- 'attributes':
'description': 'What happened?'
'placeholder': |
Is your feature request related to a problem? Please add a clear and
concise description of what the problem is.
'label': 'Issue Details'
'id': 'what-happened'
'description': 'Please describe the request'
'label': 'Description'
'value': |
#### What problem are you trying to solve?
#### Proposed solution
#### Alternatives considered
#### Additional information
'id': 'description'
'type': 'textarea'
'validations':
'required': true
- 'attributes':
'description': 'What do you propose to change in AdGuard Home?'
'placeholder': |
Describe the solution you'd like in a clear and concise manner.
'label': 'Proposed solution'
'id': 'proposed-solution'
'type': 'textarea'
'validations':
'required': true
- 'attributes':
'description': 'Are there any other ways to solve the problem?'
'placeholder': |
A clear and concise description of any alternative solutions or features
you've considered.
'label': 'Alternative solution'
'id': 'alternative-solution'
'type': 'textarea'
'required': true
'description': 'Suggest a feature or an enhancement for AdGuard Home'
'name': 'Feature request or enhancement'
'labels': [ 'feature request' ]

View File

@@ -1,7 +1,7 @@
'name': 'build'
'env':
'GO_VERSION': '1.19.10'
'GO_VERSION': '1.19.8'
'NODE_VERSION': '14'
'on':

View File

@@ -1,7 +1,7 @@
'name': 'lint'
'env':
'GO_VERSION': '1.19.10'
'GO_VERSION': '1.19.8'
'on':
'push':

View File

@@ -1,34 +0,0 @@
'name': 'potential-duplicates'
'on':
'issues':
'types': [ 'opened', 'edited' ]
'jobs':
'run':
'runs-on': 'ubuntu-latest'
'steps':
- 'uses': 'wow-actions/potential-duplicates@v1'
'with':
'GITHUB_TOKEN': ${{ secrets.GITHUB_TOKEN }}
# Issue title filter work with https://www.npmjs.com/package/anymatch.
# Any matched issue will stop detection immediately.
# You can specify multi filters in each line.
# 'filter': ''
# Exclude keywords in title before detecting.
# 'exclude': ''
# Label to set, when potential duplicates are detected.
'label': 'potential-duplicate'
# Get issues with state to compare. Supported state: 'all', 'closed',
# 'open'.
'state': 'all'
# If similarity is higher than this threshold([0,1]), issue will be
# marked as duplicate.
'threshold': 0.6
# Reactions to be add to comment when potential duplicates are
# detected. Available reactions: "-1", "+1", "confused", "laugh",
# "heart", "hooray", "rocket", "eyes".
# 'reactions': 'eyes, confused'
# Comment to post when potential duplicates are detected.
'comment': >
Potential duplicates: {{#issues}}
- [#{{ number }}] {{ title }} ({{ accuracy }}%)
{{/issues}}

View File

@@ -14,80 +14,21 @@ and this project adheres to
<!--
## [v0.108.0] - TBA
## [v0.107.32] - 2023-06-28 (APPROX.)
## [v0.107.30] - 2023-04-26 (APPROX.)
See also the [v0.107.32 GitHub milestone][ms-v0.107.32].
See also the [v0.107.30 GitHub milestone][ms-v0.107.30].
[ms-v0.107.32]: https://github.com/AdguardTeam/AdGuardHome/milestone/68?closed=1
[ms-v0.107.30]: https://github.com/AdguardTeam/AdGuardHome/milestone/66?closed=1
NOTE: Add new changes BELOW THIS COMMENT.
-->
### Added
- The ability to edit rewrite rules via `PUT /control/rewrite/update` HTTP API
([#1577]).
### Fixed
- DNSCrypt upstream not resetting the client and resolver information on
dialing errors ([#5872]).
[#1577]: https://github.com/AdguardTeam/AdGuardHome/issues/1577
<!--
NOTE: Add new changes ABOVE THIS COMMENT.
-->
## [v0.107.31] - 2023-06-08
See also the [v0.107.31 GitHub milestone][ms-v0.107.31].
### Fixed
- Startup errors on OpenWrt ([#5872]).
- Plain-UDP upstreams always falling back to TCP, causing outages and slowdowns
([#5873], [#5874]).
[#5872]: https://github.com/AdguardTeam/AdGuardHome/issues/5872
[#5873]: https://github.com/AdguardTeam/AdGuardHome/issues/5873
[#5874]: https://github.com/AdguardTeam/AdGuardHome/issues/5874
[ms-v0.107.31]: https://github.com/AdguardTeam/AdGuardHome/milestone/67?closed=1
## [v0.107.30] - 2023-06-07
See also the [v0.107.30 GitHub milestone][ms-v0.107.30].
### Security
- Go version has been updated to prevent the possibility of exploiting the
CVE-2023-29402, CVE-2023-29403, and CVE-2023-29404 Go vulnerabilities fixed in
[Go 1.19.10][go-1.19.10].
### Fixed
- Unquoted IPv6 bind hosts with trailing colons erroneously considered
unspecified addresses are now properly validated ([#5752]).
**NOTE:** the Docker healthcheck script now also doesn't interpret the `""`
value as unspecified address.
- Incorrect `Content-Type` header value in `POST /control/version.json` and `GET
/control/dhcp/interfaces` HTTP APIs ([#5716]).
- Provided bootstrap servers are now used to resolve the hostnames of plain
UDP/TCP upstream servers.
[#5716]: https://github.com/AdguardTeam/AdGuardHome/issues/5716
[go-1.19.10]: https://groups.google.com/g/golang-announce/c/q5135a9d924/m/j0ZoAJOHAwAJ
[ms-v0.107.30]: https://github.com/AdguardTeam/AdGuardHome/milestone/66?closed=1
## [v0.107.29] - 2023-04-18
See also the [v0.107.29 GitHub milestone][ms-v0.107.29].
@@ -95,7 +36,8 @@ See also the [v0.107.29 GitHub milestone][ms-v0.107.29].
### Added
- The ability to exclude client activity from the query log or statistics by
editing client's settings on the respective page in the UI ([#1717], [#4299]).
editing client's settings on the Clients settings page in the UI ([#1717],
[#4299]).
### Changed
@@ -114,7 +56,6 @@ See also the [v0.107.29 GitHub milestone][ms-v0.107.29].
[#5712]: https://github.com/AdguardTeam/AdGuardHome/issues/5712
[#5721]: https://github.com/AdguardTeam/AdGuardHome/issues/5721
[#5725]: https://github.com/AdguardTeam/AdGuardHome/issues/5725
[#5752]: https://github.com/AdguardTeam/AdGuardHome/issues/5752
[ms-v0.107.29]: https://github.com/AdguardTeam/AdGuardHome/milestone/65?closed=1
@@ -2009,13 +1950,11 @@ See also the [v0.104.2 GitHub milestone][ms-v0.104.2].
<!--
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.32...HEAD
[v0.107.32]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.31...v0.107.32
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.30...HEAD
[v0.107.30]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.29...v0.107.30
-->
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.31...HEAD
[v0.107.31]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.30...v0.107.31
[v0.107.30]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.29...v0.107.30
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.29...HEAD
[v0.107.29]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.28...v0.107.29
[v0.107.28]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.27...v0.107.28
[v0.107.27]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.26...v0.107.27

View File

@@ -37,8 +37,6 @@ SIGN = 1
VERSION = v0.0.0
YARN = yarn
NEXTAPI = 0
# Macros for the build-release target. If FRONTEND_PREBUILT is 0, the
# default, the macro $(BUILD_RELEASE_DEPS_$(FRONTEND_PREBUILT)) expands
# into BUILD_RELEASE_DEPS_0, and so both frontend and backend
@@ -66,7 +64,6 @@ ENV = env\
PATH="$${PWD}/bin:$$( "$(GO.MACRO)" env GOPATH )/bin:$${PATH}"\
RACE='$(RACE)'\
SIGN='$(SIGN)'\
NEXTAPI='$(NEXTAPI)'\
VERBOSE="$(VERBOSE.MACRO)"\
VERSION='$(VERSION)'\

View File

@@ -466,10 +466,6 @@ bug or implementing the feature.
Home](https://github.com/ebrianne/adguard-exporter) by
[@ebrianne](https://github.com/ebrianne).
* [Terminal-based, real-time traffic monitoring and statistics for your AdGuard Home
instance](https://github.com/Lissy93/AdGuardian-Term) by
[@Lissy93](https://github.com/Lissy93)
* [AdGuard Home on GLInet
routers](https://forum.gl-inet.com/t/adguardhome-on-gl-routers/10664) by
[Gl-Inet](https://gl-inet.com/).

View File

@@ -7,7 +7,7 @@
# Make sure to sync any changes with the branch overrides below.
'variables':
'channel': 'edge'
'dockerGo': 'adguard/golang-ubuntu:6.7'
'dockerGo': 'adguard/golang-ubuntu:6.3'
'stages':
- 'Build frontend':
@@ -232,24 +232,25 @@
case "$channel"
in
('release')
snapchannel='candidate'
;;
snapchannel='candidate'
;;
('beta')
snapchannel='beta'
;;
snapchannel='beta'
;;
('edge')
snapchannel='edge'
;;
snapchannel='edge'
;;
(*)
echo "invalid channel '$channel'"
exit 1
;;
echo "invalid channel '$channel'"
exit 1
;;
esac
env\
SNAPCRAFT_CHANNEL="$snapchannel"\
SNAPCRAFT_EMAIL="${bamboo.snapcraftEmail}"\
SNAPCRAFT_STORE_CREDENTIALS="${bamboo.snapcraftMacaroonPassword}"\
SNAPCRAFT_MACAROON="${bamboo.snapcraftMacaroonPassword}"\
SNAPCRAFT_UBUNTU_DISCHARGE="${bamboo.snapcraftUbuntuDischargePassword}"\
../bamboo-deploy-publisher/deploy.sh adguard-home-snap
'final-tasks':
- 'clean'
@@ -279,9 +280,9 @@
if [ "$channel" != 'release' ] && [ "${channel}" != 'beta' ]
then
echo "don't publish to GitHub Releases for this channel"
echo "don't publish to GitHub Releases for this channel"
exit 0
exit 0
fi
cd ./dist/
@@ -330,7 +331,7 @@
# need to build a few of these.
'variables':
'channel': 'beta'
'dockerGo': 'adguard/golang-ubuntu:6.7'
'dockerGo': 'adguard/golang-ubuntu:6.3'
# release-vX.Y.Z branches are the branches from which the actual final release
# is built.
- '^release-v[0-9]+\.[0-9]+\.[0-9]+':
@@ -345,4 +346,4 @@
# are the ones that actually get released.
'variables':
'channel': 'release'
'dockerGo': 'adguard/golang-ubuntu:6.7'
'dockerGo': 'adguard/golang-ubuntu:6.3'

View File

@@ -5,7 +5,7 @@
'key': 'AHBRTSPECS'
'name': 'AdGuard Home - Build and run tests'
'variables':
'dockerGo': 'adguard/golang-ubuntu:6.7'
'dockerGo': 'adguard/golang-ubuntu:6.3'
'stages':
- 'Tests':

View File

@@ -150,7 +150,7 @@
"dns_allowlists": "Белыя спісы DNS",
"dns_blocklists_desc": "AdGuard Home будзе блакаваць дамены з чорных спісаў.",
"dns_allowlists_desc": "Дамены з белых спісаў DNS будуць дазволены, нават калі яны знаходзяцца ў любым з чорных спісаў.",
"custom_filtering_rules": "Карыстальніцкія правілы фільтрацыі",
"custom_filtering_rules": "Карыстацкія правілы фільтрацыі",
"encryption_settings": "Налады шыфравання",
"dhcp_settings": "Налады DHCP",
"upstream_dns": "Upstream DNS-серверы",
@@ -247,7 +247,7 @@
"loading_table_status": "Загрузка...",
"page_table_footer_text": "Старонка",
"rows_table_footer_text": "радкоў",
"updated_custom_filtering_toast": "Карыстальніцкія правілы паспяхова захаваны",
"updated_custom_filtering_toast": "Занесены змены ў карыстацкія правілы",
"rule_removed_from_custom_filtering_toast": "Карыстацкае правіла выдалена: {{rule}}",
"rule_added_to_custom_filtering_toast": "Карыстацкае правіла дададзена: {{rule}}",
"query_log_response_status": "Статус: {{value}}",
@@ -568,7 +568,7 @@
"check_desc": "Праверыць фільтрацыю імя хаста",
"check": "Праверыць",
"form_enter_host": "Увядзіце імя хаста",
"filtered_custom_rules": "Адфільтраваны з дапамогай карыстальніцкіх правіл фільтрацыі",
"filtered_custom_rules": "Адфільтраваны з дапамогай карыстацкіх правілаў фільтрацыі",
"choose_from_list": "Абраць са спіса",
"add_custom_list": "Дадаць свой спіс",
"host_whitelisted": "Хост занесены ў белы спіс",

View File

@@ -268,8 +268,6 @@
"blocking_mode_nxdomain": "NXDOMAIN: پاسخ با کُد NXDOMAIN",
"blocking_mode_null_ip": "Null IP: پاسخ با آدرس آی پی صفر(0.0.0.0 برای A; :: برای AAAA)",
"blocking_mode_custom_ip": "آی پی دستی: پاسخ با آدرس آی پی دستی تنظیم شده",
"theme_light": "پوسته روشن",
"theme_dark": "پوسته تیره",
"upstream_dns_client_desc": "اگر این فیلد را خالی نگه دارید، AdGuard Home از سرور پیکربندی شده در <0> تنظیماتDNS </0> استفاده می کند.",
"tracker_source": "منبع ردیاب",
"source_label": "منبع",
@@ -569,6 +567,5 @@
"use_saved_key": "از کلید ذخیره شده قبلی استفاده کنید",
"parental_control": "نظارت والدین",
"safe_browsing": "وب گردی اَمن",
"form_error_password_length": "رمزعبور باید حداقل {{value}} کاراکتر باشد.",
"protection_section_label": "حفاظت"
"form_error_password_length": "رمزعبور باید حداقل {{value}} کاراکتر باشد."
}

View File

@@ -86,7 +86,7 @@
"request_details": "Pyynnön tiedot",
"client_details": "Päätelaitteen tiedot",
"details": "Yksityiskohdat",
"back": "Palaa takaisin",
"back": "Takaisin",
"dashboard": "Tila",
"settings": "Asetukset",
"filters": "Suodattimet",
@@ -146,8 +146,8 @@
"no_servers_specified": "Palvelimia ei ole määritetty",
"general_settings": "Yleiset asetukset",
"dns_settings": "DNS-asetukset",
"dns_blocklists": "DNS-estot",
"dns_allowlists": "DNS-sallinnat",
"dns_blocklists": "DNS-estolistat",
"dns_allowlists": "DNS-sallittujen listat",
"dns_blocklists_desc": "AdGuard Home estää estolistalla olevat verkkotunnukset.",
"dns_allowlists_desc": "DNS-sallittujen listalla olevat verkkotunnukset sallitaan myös silloin, jos ne ovat jollain muulla estolistalla.",
"custom_filtering_rules": "Omat suodatussäännöt",
@@ -627,7 +627,7 @@
"cache_optimistic": "Optimistinen välimuisti",
"cache_optimistic_desc": "Pakota AdGuard Home vastaamaan välimuistista vaikka tiedot olisivat vanhentuneet. Pyri samalla myös päivittämään tiedot.",
"filter_category_general": "Yleiset",
"filter_category_security": "Tietoturva",
"filter_category_security": "Turvallisuus",
"filter_category_regional": "Alueelliset",
"filter_category_other": "Muut",
"filter_category_general_desc": "Listat, jotka estävät seurannan ja mainokset useimmilla laitteilla",

View File

@@ -282,8 +282,6 @@
"blocking_mode_null_ip": "Null IP: Svar med en 0-IP-adresse (0.0.0.0 for A; :: for AAAA)",
"blocking_mode_custom_ip": "Tilpasset IP: Svar med en manuelt valgt IP-adresse",
"theme_auto": "Auto",
"theme_light": "Lyst tema",
"theme_dark": "Mørkt tema",
"upstream_dns_client_desc": "Hvis dette feltet holdes tomt, vil AdGuard Home bruke tjenerne som er satt opp i <0>DNS-innstillingene</0>.",
"tracker_source": "Sporerkilde",
"source_label": "Kilde",

View File

@@ -222,7 +222,7 @@
"all_lists_up_to_date_toast": "Wszystkie listy są już aktualne",
"updated_upstream_dns_toast": "Serwery nadrzędne zostały pomyślnie zapisane",
"dns_test_ok_toast": "Określone serwery DNS działają poprawnie",
"dns_test_not_ok_toast": "Serwer \"{{key}}\": nie może być użyte, sprawdź, czy zapisano go poprawnie",
"dns_test_not_ok_toast": "Serwer \"{{key}}\": nie można go użyć, sprawdź, czy napisałeś go poprawnie",
"dns_test_warning_toast": "Upstream \"{{key}}\" nie odpowiada na zapytania testowe i może nie działać prawidłowo",
"unblock": "Odblokuj",
"block": "Zablokuj",
@@ -346,7 +346,7 @@
"install_devices_windows_list_2": "Przejdź do kategorii Sieć i Internet, a następnie do Centrum sieci i udostępniania.",
"install_devices_windows_list_3": "W lewym panelu kliknij \"Zmień ustawienia adaptera\".",
"install_devices_windows_list_4": "Kliknij prawym przyciskiem myszy aktywne połączenie i wybierz Właściwości.",
"install_devices_windows_list_5": "Znajdź na liście \"Protokół internetowy w wersji 4 (TCP/IPv4)\" (lub w przypadku IPv6 \"Protokół internetowy w wersji 6 (TCP/IPv6)\"), zaznacz go i ponownie kliknij Właściwości.",
"install_devices_windows_list_5": "Znajdź na liście \"Protokół internetowy w wersji 4 (TCP/IPv4)\" (lub w przypadku IPv6 \"Protokół internetowy w wersji 6 (TCP/IPv6)\"), zaznacz go i ponownie kliknij na Właściwości.",
"install_devices_windows_list_6": "Wybierz opcję \"Użyj następujących adresów serwerów DNS\" i wprowadź adresy serwerów AdGuard Home.",
"install_devices_macos_list_1": "Kliknij ikonę Apple i przejdź do Preferencje systemowe.",
"install_devices_macos_list_2": "Kliknij Sieć.",
@@ -396,7 +396,7 @@
"encryption_issuer": "Zgłaszający",
"encryption_hostnames": "Nazwy hostów",
"encryption_reset": "Czy na pewno chcesz zresetować ustawienia szyfrowania?",
"encryption_warning": "Uwaga",
"encryption_warning": "Uwaga!",
"topline_expiring_certificate": "Twój certyfikat SSL wkrótce wygaśnie. Zaktualizuj <0>Ustawienia szyfrowania</0>.",
"topline_expired_certificate": "Twój certyfikat SSL wygasł. Zaktualizuj <0>Ustawienia szyfrowania</0>.",
"form_error_port_range": "Wpisz numer portu z zakresu 80-65535",
@@ -542,7 +542,7 @@
"password_placeholder": "Wpisz hasło",
"sign_in": "Zaloguj się",
"sign_out": "Wyloguj się",
"forgot_password": "Zapomniano hasła?",
"forgot_password": "Zapomniałeś hasła?",
"forgot_password_desc": "Wykonaj <0>te kroki</0>, aby utworzyć nowe hasło do konta użytkownika.",
"location": "Lokalizacja",
"orgname": "Nazwa firmy",

View File

@@ -529,7 +529,7 @@
"ignore_domains": "Domínios ignorados (separados por nova linha)",
"ignore_domains_title": "Domínios ignorados",
"ignore_domains_desc_stats": "As consultas para esses domínios não são gravadas nas estatísticas",
"ignore_domains_desc_query": "As consultas para esses domínios não são gravadas no registro de consulta",
"ignore_domains_desc_query": "As consultas para esses domínios não são gravadas no log de consulta",
"interval_hours": "{{count}} hora",
"interval_hours_plural": "{{count}} horas",
"filters_configuration": "Configuração de filtros",

View File

@@ -529,7 +529,7 @@
"ignore_domains": "Domínios ignorados (separados por nova linha)",
"ignore_domains_title": "Domínios ignorados",
"ignore_domains_desc_stats": "As consultas para estes domínios não aparecem nas estatísticas",
"ignore_domains_desc_query": "As consultas para estes domínios não aparecem no registo de consultas",
"ignore_domains_desc_query": "As consultas para estes domínios nãoaparecem no registo de consultas",
"interval_hours": "{{count}} hora",
"interval_hours_plural": "{{count}} horas",
"filters_configuration": "Definição dos filtros",

View File

@@ -167,7 +167,6 @@
"enabled_parental_toast": "«Батьківський контроль» увімкнено",
"disabled_safe_search_toast": "Безпечний пошук вимкнено",
"enabled_save_search_toast": "Безпечний пошук увімкнено",
"updated_save_search_toast": "Налаштування Безпечного пошуку оновлено",
"enabled_table_header": "Увімкнено",
"name_table_header": "Назва",
"list_url_table_header": "URL списку",
@@ -291,8 +290,6 @@
"rate_limit": "Обмеження швидкості",
"edns_enable": "Увімкнути відправку EDNS Client Subnet",
"edns_cs_desc": "Додавати параметр EDNS Client Subnet (ECS) до запитів до upstream-серверів, а також записувати в журнал значення, що надсилаються клієнтами.",
"edns_use_custom_ip": "Використання користувацької IP-адреси для EDNS",
"edns_use_custom_ip_desc": "Дозволити використовувати користувацьку IP-адресу для EDNS",
"rate_limit_desc": "Кількість запитів в секунду, які може робити один клієнт. Встановлене значення «0» означатиме необмежену кількість.",
"blocking_ipv4_desc": "IP-адреса, яку потрібно видати для заблокованого A запиту",
"blocking_ipv6_desc": "IP-адреса, яку потрібно видати для заблокованого АААА запиту",
@@ -526,10 +523,6 @@
"statistics_retention_confirm": "Ви впевнені, що хочете змінити тривалість статистики? Якщо зменшити значення інтервалу, деякі дані будуть втрачені",
"statistics_cleared": "Статистику успішно очищено",
"statistics_enable": "Увімкнути статистику",
"ignore_domains": "Ігноровані домени (по одному на рядок)",
"ignore_domains_title": "Ігноровані домени",
"ignore_domains_desc_stats": "Запити для цих доменів в статистику не пишуться",
"ignore_domains_desc_query": "Запити для цих доменів не записуються до журналу запитів",
"interval_hours": "{{count}} година",
"interval_hours_plural": "{{count}} годин(и)",
"filters_configuration": "Конфігурація фільтрів",
@@ -650,29 +643,5 @@
"confirm_dns_cache_clear": "Ви впевнені, що бажаєте очистити кеш DNS?",
"cache_cleared": "Кеш DNS успішно очищено",
"clear_cache": "Очистити кеш",
"make_static": "Зробити статичним",
"theme_auto_desc": "Автоматична (на основі теми вашого пристрою)",
"theme_dark_desc": "Темна тема",
"theme_light_desc": "Світла тема",
"disable_for_seconds": "На {{count}} секунду",
"disable_for_seconds_plural": "На {{count}} секунд",
"disable_for_minutes": "На {{count}} хвилину",
"disable_for_minutes_plural": "На {{count}} хвилин",
"disable_for_hours": "На {{count}} годину",
"disable_for_hours_plural": "На {{count}} годин",
"disable_until_tomorrow": "До завтра",
"disable_notify_for_seconds": "Вимкнення захисту на {{count}} секунду",
"disable_notify_for_seconds_plural": "Вимкнення захисту на {{count}} секунд",
"disable_notify_for_minutes": "Вимкнення захисту на {{count}} хвилину",
"disable_notify_for_minutes_plural": "Вимкнення захисту на {{count}} хвилин",
"disable_notify_for_hours": "Вимкнення захисту на {{count}} годину",
"disable_notify_for_hours_plural": "Вимкнення захисту на {{count}} годин",
"disable_notify_until_tomorrow": "Відключення захисту до завтра",
"enable_protection_timer": "Захист буде ввімкнено о {{time}}",
"custom_retention_input": "Введіть час в годинах",
"custom_rotation_input": "Введіть час в годинах",
"protection_section_label": "Захист",
"log_and_stats_section_label": "Журнал запитів і статистика",
"ignore_query_log": "Ігнорувати цей клієнт у журналі запитів",
"ignore_statistics": "Ігноруйте цей клієнт в статистиці"
"protection_section_label": "Захист"
}

View File

@@ -100,12 +100,6 @@ export default {
"homepage": "https://github.com/DandelionSprout/adfilt",
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_13.txt"
},
"POL_cert_polska_list_of_malicious_domains": {
"name": "POL: CERT Polska List of malicious domains",
"categoryId": "regional",
"homepage": "https://cert.pl/posts/2020/03/ostrzezenia_phishing/",
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_41.txt"
},
"POL_polish_filters_for_pi_hole": {
"name": "POL: Polish filters for Pi-hole",
"categoryId": "regional",
@@ -124,12 +118,6 @@ export default {
"homepage": "https://github.com/bkrucarci/turk-adlist",
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_26.txt"
},
"TUR_turkish_ad_hosts": {
"name": "TUR: Turkish Ad Hosts",
"categoryId": "regional",
"homepage": "https://github.com/symbuzzer/Turkish-Ad-Hosts",
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_40.txt"
},
"VNM_abpvn": {
"name": "VNM: ABPVN List",
"categoryId": "regional",
@@ -226,12 +214,6 @@ export default {
"homepage": "https://github.com/durablenapkin/scamblocklist",
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_10.txt"
},
"shadowwhisperers_malware_list": {
"name": "ShadowWhisperer's Malware List",
"categoryId": "security",
"homepage": "https://github.com/ShadowWhisperer/BlockLists",
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_42.txt"
},
"staklerware_indicators_list": {
"name": "Stalkerware Indicators List",
"categoryId": "security",

View File

@@ -1,5 +1,5 @@
{
"timeUpdated": "2023-06-01T00:12:12.660Z",
"timeUpdated": "2023-04-06T10:46:09.881Z",
"categories": {
"0": "audio_video_player",
"1": "comments",
@@ -19526,13 +19526,6 @@
"companyId": "qualcomm",
"source": "AdGuard"
},
"qualcomm_location_service": {
"name": "Qualcomm Location Service",
"categoryId": 15,
"url": "https://www.qualcomm.com/site/privacy/services",
"companyId": "qualcomm",
"source": "AdGuard"
},
"recaptcha": {
"name": "reCAPTCHA",
"categoryId": 8,
@@ -19540,55 +19533,6 @@
"companyId": "google",
"source": "AdGuard"
},
"samsung": {
"name": "Samsung",
"categoryId": 8,
"url": "https://www.samsung.com/",
"companyId": "samsung",
"source": "AdGuard"
},
"samsungads": {
"name": "Samsung Ads",
"categoryId": 4,
"url": "https://www.samsung.com/business/samsungads/",
"companyId": "samsung",
"source": "AdGuard"
},
"samsungapps": {
"name": "Samsung Apps",
"categoryId": 101,
"url": "https://www.samsung.com/au/apps/",
"companyId": "samsung",
"source": "AdGuard"
},
"samsungmobile": {
"name": "Samsung Mobile",
"categoryId": 101,
"url": "https://www.samsung.com/mobile/",
"companyId": "samsung",
"source": "AdGuard"
},
"samsungpush": {
"name": "Samsung Push",
"categoryId": 8,
"url": null,
"companyId": "samsung",
"source": "AdGuard"
},
"samsungsds": {
"name": "Samsung SDS",
"categoryId": 10,
"url": "https://www.samsungsds.com/",
"companyId": "samsung",
"source": "AdGuard"
},
"samsungtv": {
"name": "Samsung TV",
"categoryId": 15,
"url": "https://www.samsung.com/au/tvs/",
"companyId": "samsung",
"source": "AdGuard"
},
"sectigo": {
"name": "Sectigo Limited",
"categoryId": 5,
@@ -19645,13 +19589,6 @@
"companyId": "telstra",
"source": "AdGuard"
},
"ubuntu": {
"name": "Ubuntu",
"categoryId": 8,
"url": "https://ubuntu.com/",
"companyId": "ubuntu",
"source": "AdGuard"
},
"unity_ads": {
"name": "Unity Ads",
"categoryId": 4,
@@ -19714,13 +19651,6 @@
"url": "https://www.3gpp.org/",
"companyId": "3gpp",
"source": "AdGuard"
},
"7plus": {
"name": "7plus",
"categoryId": 0,
"url": "https://7plus.com.au/",
"companyId": "7plus",
"source": "AdGuard"
}
},
"trackerDomains": {
@@ -19913,8 +19843,8 @@
"adfreestyle.pl": "adfreestyle",
"adfront.org": "adfront",
"adfrontiers.com": "adfrontiers",
"adgear.com": "samsungads",
"adgrx.com": "samsungads",
"adgear.com": "adgear",
"adgrx.com": "adgear",
"adgebra.co.in": "adgebra",
"adgenie.co.uk": "adgenie",
"ad.adgile.com": "adgile",
@@ -24126,10 +24056,6 @@
"safebrowsing.g.applimg.com": "apple",
"applvn.com": "applovin",
"applovin.com": "applovin",
"bitbucket.org": "atlassian.net",
"jira.com": "atlassian.net",
"ss-inf.net": "atlassian.net",
"stspg-customer.com": "statuspage.io",
"blob.core.windows.net": "azure_blob_storage",
"azure.com": "azure",
"trafficmanager.net": "azure",
@@ -24137,21 +24063,6 @@
"mobileapptracking.com": "branch",
"bttn.io": "button",
"cloudflare-dns.com": "cloudflare",
"cloudflare-dm-cmpimg.com": "cloudflare",
"cloudflare-ipfs.com": "cloudflare",
"cloudflare-quic.com": "cloudflare",
"cloudflare-terms-of-service-abuse.com": "cloudflare",
"cloudflare.tv": "cloudflare",
"cloudflareaccess.com": "cloudflare",
"cloudflareclient.com": "cloudflare",
"cloudflareinsights.com": "cloudflare",
"cloudflareok.com": "cloudflare",
"cloudflareportal.com": "cloudflare",
"cloudflareresolve.com": "cloudflare",
"cloudflaressl.com": "cloudflare",
"cloudflarestatus.com": "cloudflare",
"pacloudflare.com": "cloudflare",
"sn-cloudflare.com": "cloudflare",
"crashlytics.com": "crashlytics",
"phicdn.net": "digicert_trust_seal",
"domain.glass": "domainglass",
@@ -24181,9 +24092,6 @@
"qy.net": "iqiyi",
"iqiyi.com": "iqiyi",
"iq.com": "iqiyi",
"ironsrc.com": "ironsource",
"ironsrc.net": "ironsource",
"supersonicads.com": "ironsource",
"karambasecurity.com": "karambasecurity",
"kik.com": "kik",
"apikik.com": "kik",
@@ -24213,23 +24121,6 @@
"mozilla.com": "mozilla",
"mozilla.net": "mozilla",
"mozilla.org": "mozilla",
"flxvpn.net": "netflix",
"netflix.ca": "netflix",
"netflix.com.au": "netflix",
"netflix.net": "netflix",
"netflixdnstest1.com": "netflix",
"netflixdnstest10.com": "netflix",
"netflixdnstest2.com": "netflix",
"netflixdnstest3.com": "netflix",
"netflixdnstest4.com": "netflix",
"netflixdnstest5.com": "netflix",
"netflixdnstest6.com": "netflix",
"netflixdnstest7.com": "netflix",
"netflixdnstest8.com": "netflix",
"netflixdnstest9.com": "netflix",
"netflixinvestor.com": "netflix",
"netflixstudios.com": "netflix",
"netflixtechblog.com": "netflix",
"nflximg.com": "netflix",
"netify.ai": "netify",
"nab.com": "nab",
@@ -24253,69 +24144,9 @@
"oztam.com.au": "oztam",
"plex.tv": "plex",
"plex.direct": "plex",
"xtracloud.net": "qualcomm",
"qualcomm.com": "qualcomm",
"gpsonextra.net": "qualcomm_location_service",
"izatcloud.net": "qualcomm_location_service",
"xtracloud.net": "qualcomm_location_service",
"recaptcha.net": "recaptcha",
"samsungacr.com": "samsungads",
"samsungadhub.com": "samsungads",
"samsungads.com": "samsungads",
"samsungtifa.com": "samsungads",
"game-mode.net": "samsung",
"gos-gsp.io": "samsung",
"lldns.net": "samsung",
"pavv.co.kr": "samsung",
"remotesamsung.com": "samsung",
"samsung-gamelauncher.com": "samsung",
"samsung.co.kr": "samsung",
"samsung.com": "samsung",
"samsung.com.cn": "samsung",
"samsungcloud.com": "samsung",
"samsungcloudcdn.com": "samsung",
"samsungcloudprint.com": "samsung",
"samsungcloudsolution.com": "samsung",
"samsungcloudsolution.net": "samsung",
"samsungelectronics.com": "samsung",
"samsunghealth.com": "samsung",
"samsungiotcloud.com": "samsung",
"samsungknox.com": "samsung",
"samsungnyc.com": "samsung",
"samsungosp.com": "samsung",
"samsungotn.net": "samsung",
"samsungpositioning.com": "samsung",
"samsungqbe.com": "samsung",
"samsungrm.net": "samsung",
"samsungrs.com": "samsung",
"samsungsemi.com": "samsung",
"samsungsetup.com": "samsung",
"samsungusa.com": "samsung",
"secb2b.com": "samsung",
"smartthings.com": "samsung",
"ospserver.net": "samsungmobile",
"samsungdms.net": "samsungmobile",
"samsungmax.com": "samsungmobile",
"samsungmobile.com": "samsungmobile",
"secmobilesvc.com": "samsungmobile",
"internetat.tv": "samsungtv",
"samsungcloud.tv": "samsungtv",
"samsungsds.com": "samsungsds",
"push.samsungosp.com": "samsungpush",
"pushmessage.samsung.com": "samsungpush",
"scs.samsungqbe.com": "samsungpush",
"ssp.samsung.com": "samsungpush",
"aibixby.com": "samsungapps",
"findmymobile.samsung.com": "samsungapps",
"samsapps.cust.lldns.net": "samsungapps",
"samsung-omc.com": "samsungapps",
"samsungapps.com": "samsungapps",
"samsungdiroute.net": "samsungapps",
"samsungdive.com": "samsungapps",
"samsungdm.com": "samsungapps",
"samsungdmroute.com": "samsungapps",
"samsungmdec.com": "samsungapps",
"samsungvisioncloud.com": "samsungapps",
"sbixby.com": "samsungapps",
"sectigo.com": "sectigo",
"showrss.info": "showrss",
"similarweb.io": "similarweb",
@@ -24340,13 +24171,6 @@
"telstra.com.au": "telstra",
"telstra.com": "telstra",
"usertrust.com": "trustlogo",
"canonical.com": "ubuntu",
"launchpad.net": "ubuntu",
"launchpadcontent.net": "ubuntu",
"snapcraft.io": "ubuntu",
"snapcraftcontent.com": "ubuntu",
"ubuntu.com": "ubuntu",
"ubuntucompanyservices.co.za": "ubuntu",
"unityads.unity3d.com": "unity_ads",
"exp-tas.com": "vscode",
"vscode-unpkg.net": "vscode",
@@ -24366,7 +24190,6 @@
"yandex.kz": "yandex",
"appmetrica.yandex.com": "yandex_appmetrica",
"3gppnetwork.org": "3gpp",
"3gpp.org": "3gpp",
"swm.digital": "7plus"
"3gpp.org": "3gpp"
}
}

View File

@@ -7,10 +7,11 @@
addrs[$2] = true
prev_line = FNR
if ($2 == "0.0.0.0" || $2 == "'::'") {
# Drop all the other addresses.
if ($2 == "0.0.0.0" || $2 == "::") {
delete addrs
addrs[""] = true
addrs["localhost"] = true
# Drop all the other addresses.
prev_line = -1
}
}

View File

@@ -61,11 +61,8 @@ then
error_exit "no DNS bindings could be retrieved from $filename"
fi
first_dns="$( echo "$dns_hosts" | head -n 1 )"
readonly first_dns
# TODO(e.burkov): Deal with 0 port.
case "$first_dns"
case "$( echo "$dns_hosts" | head -n 1 )"
in
(*':0')
error_exit '0 in DNS port is not supported by healthcheck'
@@ -85,23 +82,8 @@ esac
# See https://github.com/AdguardTeam/AdGuardHome/issues/5642.
wget --no-check-certificate "$web_url" -O /dev/null -q || exit 1
test_fqdn="healthcheck.adguardhome.test."
readonly test_fqdn
# The awk script currently returns only port prefixed with colon in case of
# unspecified address.
case "$first_dns"
in
(':'*)
nslookup -type=a "$test_fqdn" "127.0.0.1${first_dns}" > /dev/null ||\
nslookup -type=a "$test_fqdn" "[::1]${first_dns}" > /dev/null ||\
echo "$dns_hosts" | while read -r host
do
nslookup -type=a healthcheck.adguardhome.test. "$host" > /dev/null ||\
error_exit "nslookup failed for $host"
;;
(*)
echo "$dns_hosts" | while read -r host
do
nslookup -type=a "$test_fqdn" "$host" > /dev/null ||\
error_exit "nslookup failed for $host"
done
;;
esac
done

30
go.mod
View File

@@ -3,7 +3,7 @@ module github.com/AdguardTeam/AdGuardHome
go 1.19
require (
github.com/AdguardTeam/dnsproxy v0.50.2
github.com/AdguardTeam/dnsproxy v0.48.3
github.com/AdguardTeam/golibs v0.13.2
github.com/AdguardTeam/urlfilter v0.16.1
github.com/NYTimes/gziphandler v1.1.1
@@ -16,24 +16,24 @@ require (
github.com/google/gopacket v1.1.19
github.com/google/renameio v1.0.1
github.com/google/uuid v1.3.0
github.com/insomniacslk/dhcp v0.0.0-20230516061539-49801966e6cb
github.com/insomniacslk/dhcp v0.0.0-20221215072855-de60144f33f8
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
github.com/mdlayher/netlink v1.7.2
github.com/mdlayher/packet v1.1.2
github.com/mdlayher/netlink v1.7.1
github.com/mdlayher/packet v1.1.1
// TODO(a.garipov): This package is deprecated; find a new one or use our
// own code for that. Perhaps, use gopacket.
github.com/mdlayher/raw v0.1.0
github.com/miekg/dns v1.1.54
github.com/quic-go/quic-go v0.35.1
github.com/miekg/dns v1.1.53
github.com/quic-go/quic-go v0.33.0
github.com/stretchr/testify v1.8.2
github.com/ti-mo/netfilter v0.5.0
go.etcd.io/bbolt v1.3.7
golang.org/x/crypto v0.9.0
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1
golang.org/x/net v0.10.0
golang.org/x/sys v0.8.0
golang.org/x/crypto v0.8.0
golang.org/x/exp v0.0.0-20230321023759-10a507213a29
golang.org/x/net v0.9.0
golang.org/x/sys v0.7.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1
gopkg.in/yaml.v3 v3.0.1
howett.net/plist v1.0.0
@@ -48,9 +48,9 @@ require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/google/pprof v0.0.0-20230602150820-91b7bce49751 // indirect
github.com/mdlayher/socket v0.4.1 // indirect
github.com/onsi/ginkgo/v2 v2.10.0 // indirect
github.com/google/pprof v0.0.0-20230406165453-00490a63f317 // indirect
github.com/mdlayher/socket v0.4.0 // indirect
github.com/onsi/ginkgo/v2 v2.9.2 // indirect
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
github.com/pierrec/lz4/v4 v4.1.17 // indirect
github.com/pkg/errors v0.9.1 // indirect
@@ -60,7 +60,7 @@ require (
github.com/quic-go/qtls-go1-20 v0.2.2 // indirect
github.com/u-root/uio v0.0.0-20230305220412-3e8cd9d6bf63 // indirect
golang.org/x/mod v0.10.0 // indirect
golang.org/x/sync v0.2.0 // indirect
golang.org/x/sync v0.1.0 // indirect
golang.org/x/text v0.9.0 // indirect
golang.org/x/tools v0.9.3 // indirect
golang.org/x/tools v0.8.0 // indirect
)

103
go.sum
View File

@@ -1,5 +1,5 @@
github.com/AdguardTeam/dnsproxy v0.50.2 h1:p1471SsMZ6SMo7T51Olw4aNluahvMwSLMorwxYV18ts=
github.com/AdguardTeam/dnsproxy v0.50.2/go.mod h1:CQhZTkqC8X0ID6glrtyaxgqRRdiYfn1gJulC1cZ5Dn8=
github.com/AdguardTeam/dnsproxy v0.48.3 h1:h9xgDSmd1MqsPFNApyaPVXolmSTtzOWOcfWvPeDEP6s=
github.com/AdguardTeam/dnsproxy v0.48.3/go.mod h1:Y7g7jRTd/u7+KJ/QvnGI2PCE8vnisp6EsW47/Sz0DZw=
github.com/AdguardTeam/golibs v0.4.0/go.mod h1:skKsDKIBB7kkFflLJBpfGX+G8QFTx0WKUzB6TIgtUj4=
github.com/AdguardTeam/golibs v0.10.4/go.mod h1:rSfQRGHIdgfxriDDNgNJ7HmE5zRoURq8R+VdR81Zuzw=
github.com/AdguardTeam/golibs v0.13.2 h1:BPASsyQKmb+b8VnvsNOHp7bKfcZl9Z+Z2UhPjOiupSc=
@@ -31,9 +31,10 @@ github.com/digineo/go-ipset/v2 v2.2.1 h1:k6skY+0fMqeUjjeWO/m5OuWPSZUAn7AucHMnQ1M
github.com/digineo/go-ipset/v2 v2.2.1/go.mod h1:wBsNzJlZlABHUITkesrggFnZQtgW5wkqw1uo8Qxe0VU=
github.com/dimfeld/httptreemux/v5 v5.5.0 h1:p8jkiMrCuZ0CmhwYLcbNbl7DDo21fozhKHQ2PccwOFQ=
github.com/dimfeld/httptreemux/v5 v5.5.0/go.mod h1:QeEylH57C0v3VO0tkKraVz9oD3Uu93CKPnTLbsidvSw=
github.com/fanliao/go-promise v0.0.0-20141029170127-1890db352a72/go.mod h1:PjfxuH4FZdUyfMdtBio2lsRr1AKEaVPwelzuHuh8Lqc=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
github.com/go-ole/go-ole v1.2.5 h1:t4MGB5xEDZvXI+0rMjjsfBsD7yAgp/s9ZDkL1JndXwY=
github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-ping/ping v1.1.0 h1:3MCGhVX4fyEUuhsfwPrsEdQw6xspHkv5zHsiSoDFZYw=
@@ -44,54 +45,72 @@ github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/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-20230602150820-91b7bce49751 h1:hR7/MlvK23p6+lIw9SN1TigNLn9ZnF3W4SYRKq2gAHs=
github.com/google/pprof v0.0.0-20230602150820-91b7bce49751/go.mod h1:Jh3hGz2jkYak8qXPD19ryItVnUgpgeqzdkY/D0EaeuA=
github.com/google/pprof v0.0.0-20230406165453-00490a63f317 h1:hFhpt7CTmR3DX+b4R19ydQFtofxT0Sv3QsKNMVQYTMQ=
github.com/google/pprof v0.0.0-20230406165453-00490a63f317/go.mod h1:79YE0hCXdHag9sBkw2o+N/YnZtTkXi0UT9Nnixa5eYk=
github.com/google/renameio v1.0.1 h1:Lh/jXZmvZxb0BBeSY5VKEfidcbcbenKjZFzM/q0fSeU=
github.com/google/renameio v1.0.1/go.mod h1:t/HQoYBZSsWSNK35C6CO/TpPLDVWvxOHboWUAweKUpk=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714 h1:/jC7qQFrv8CrSJVmaolDVOxTfS9kc36uB6H40kdbQq8=
github.com/insomniacslk/dhcp v0.0.0-20230516061539-49801966e6cb h1:6fDKEAXwe3rsfS4khW3EZ8kEqmSiV9szhMPcDrD+Y7Q=
github.com/insomniacslk/dhcp v0.0.0-20230516061539-49801966e6cb/go.mod h1:7474bZ1YNCvarT6WFKie4kEET6J0KYRDC4XJqqXzQW4=
github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis=
github.com/insomniacslk/dhcp v0.0.0-20221215072855-de60144f33f8 h1:Z72DOke2yOK0Ms4Z2LK1E1OrRJXOxSj5DllTz2FYTRg=
github.com/insomniacslk/dhcp v0.0.0-20221215072855-de60144f33f8/go.mod h1:m5WMe03WCvWcXjRnhvaAbAAXdCnu20J5P+mmH44ZzpE=
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.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86 h1:elKwZS1OcdQ0WwEDBeqxKwb7WB62QX8bvZ/FJnVXIfk=
github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86/go.mod h1:aFAMtuldEgx/4q7iSGazk22+IcgvtiC+HIimFO9XlS8=
github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw=
github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ=
github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok=
github.com/jsimonetti/rtnetlink v0.0.0-20201110080708-d2c240429e6c/go.mod h1:huN4d1phzjhlOsNIjFsw2SVRbwIHj3fJDMEU2SDPTmg=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/kardianos/service v1.2.2 h1:ZvePhAHfvo0A7Mftk/tEzqEZ7Q4lgnR8sGz4xu1YX60=
github.com/kardianos/service v1.2.2/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mdlayher/ethernet v0.0.0-20190606142754-0394541c37b7/go.mod h1:U6ZQobyTjI/tJyq2HG+i/dfSoFUt8/aZCM+GKtmFk/Y=
github.com/mdlayher/ethernet v0.0.0-20220221185849-529eae5b6118 h1:2oDp6OOhLxQ9JBoUuysVz9UZ9uI6oLUbvAZu0x8o+vE=
github.com/mdlayher/ethernet v0.0.0-20220221185849-529eae5b6118/go.mod h1:ZFUnHIVchZ9lJoWoEGUg8Q3M4U8aNNWA3CVSUTkW4og=
github.com/mdlayher/netlink v0.0.0-20190313131330-258ea9dff42c/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA=
github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g=
github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw=
github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA=
github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M=
github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY=
github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o=
github.com/mdlayher/netlink v1.7.1 h1:FdUaT/e33HjEXagwELR8R3/KL1Fq5x3G5jgHLp/BTmg=
github.com/mdlayher/netlink v1.7.1/go.mod h1:nKO5CSjE/DJjVhk/TNp6vCE1ktVxEA8VEh8drhZzxsQ=
github.com/mdlayher/packet v1.0.0/go.mod h1:eE7/ctqDhoiRhQ44ko5JZU2zxB88g+JH/6jmnjzPjOU=
github.com/mdlayher/packet v1.1.2 h1:3Up1NG6LZrsgDVn6X4L9Ge/iyRyxFEFD9o6Pr3Q1nQY=
github.com/mdlayher/packet v1.1.2/go.mod h1:GEu1+n9sG5VtiRE4SydOmX5GTwyyYlteZiFU+x0kew4=
github.com/mdlayher/packet v1.1.1 h1:7Fv4OEMYqPl7//uBm04VgPpnSNi8fbBZznppgh6WMr8=
github.com/mdlayher/packet v1.1.1/go.mod h1:DRvYY5mH4M4lUqAnMg04E60U4fjUKMZ/4g2cHElZkKo=
github.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg=
github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg=
github.com/mdlayher/raw v0.1.0 h1:K4PFMVy+AFsp0Zdlrts7yNhxc/uXoPVHi9RzRvtZF2Y=
github.com/mdlayher/raw v0.1.0/go.mod h1:yXnxvs6c0XoF/aK52/H5PjsVHmWBCFfZUfoh/Y5s9Sg=
github.com/mdlayher/socket v0.2.1/go.mod h1:QLlNPkFR88mRUNQIzRBMfXxwKal8H7u1h3bL1CV+f0E=
github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U=
github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA=
github.com/mdlayher/socket v0.4.0 h1:280wsy40IC9M9q1uPGcLBwXpcTQDtoGwVt+BNoITxIw=
github.com/mdlayher/socket v0.4.0/go.mod h1:xxFqz5GRCUN3UEOm9CZqEJsAbe1C8OwSK46NlmWuVoc=
github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
github.com/miekg/dns v1.1.54 h1:5jon9mWcb0sFJGpnI99tOMhCPyJ+RPVz5b63MQG0VWI=
github.com/miekg/dns v1.1.54/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
github.com/miekg/dns v1.1.53 h1:ZBkuHr5dxHtB1caEOlZTLPo7D3L3TWckgUUs/RHfDxw=
github.com/miekg/dns v1.1.53/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
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.10.0 h1:sfUl4qgLdvkChZrWCYndY2EAu9BRIw1YphNAzy1VNWs=
github.com/onsi/ginkgo/v2 v2.10.0/go.mod h1:UDQOh5wbQUlMnkLfVaIUMtQ1Vus92oM+P2JX1aulgcE=
github.com/onsi/gomega v1.27.7 h1:fVih9JD6ogIiHUN6ePK7HJidyEDpWGVB5mzM7cWNXoU=
github.com/onsi/ginkgo/v2 v2.9.2 h1:BA2GMJOtfGAfagzYtrAlufIP0lq6QERkFmHLMLPwFSU=
github.com/onsi/ginkgo/v2 v2.9.2/go.mod h1:WHcJJG2dIlcCqVfBAwUCrJxSPFb6v4azBwgxeMeDuts=
github.com/onsi/gomega v1.27.4 h1:Z2AnStgsdSayCMDiCU42qIz+HLqEPcgiOCXjAU/w+8E=
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
@@ -108,10 +127,12 @@ github.com/quic-go/qtls-go1-19 v0.3.2 h1:tFxjCFcTQzK+oMxG6Zcvp4Dq8dx4yD3dDiIiyc8
github.com/quic-go/qtls-go1-19 v0.3.2/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI=
github.com/quic-go/qtls-go1-20 v0.2.2 h1:WLOPx6OY/hxtTxKV1Zrq20FtXtDEkeY00CGQm8GEa3E=
github.com/quic-go/qtls-go1-20 v0.2.2/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM=
github.com/quic-go/quic-go v0.35.1 h1:b0kzj6b/cQAf05cT0CkQubHM31wiA+xH3IBkxP62poo=
github.com/quic-go/quic-go v0.35.1/go.mod h1:+4CVgVppm0FNjpG3UcX8Joi/frKOH7/ciD5yGcwOO1g=
github.com/quic-go/quic-go v0.33.0 h1:ItNoTDN/Fm/zBlq769lLJc8ECe9gYaW40veHCCco7y0=
github.com/quic-go/quic-go v0.33.0/go.mod h1:YMuhaAV9/jIu0XclDXwZPAsP/2Kgr5yMYhe9oxhhOFA=
github.com/shirou/gopsutil/v3 v3.21.8 h1:nKct+uP0TV8DjjNiHanKf8SAuub+GNsbrOtM9Nl9biA=
github.com/shirou/gopsutil/v3 v3.21.8/go.mod h1:YWp/H8Qs5fVmf17v7JNZzA0mPJ+mS2e9JdiUF9LlKzQ=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
@@ -131,6 +152,7 @@ github.com/tklauser/go-sysconf v0.3.9 h1:JeUVdAOWhhxVcU6Eqr/ATFHgXk/mmiItdKeJPev
github.com/tklauser/go-sysconf v0.3.9/go.mod h1:11DU/5sG7UexIrp/O6g35hrWzu0JxlwQ3LSFUzyeuhs=
github.com/tklauser/numcpus v0.3.0 h1:ILuRUQBtssgnxw0XXIjKUC56fgnOrFoQQ/4+DeU2biQ=
github.com/tklauser/numcpus v0.3.0/go.mod h1:yFGUr7TUHQRAhyqBcEg0Ge34zDBAsIvJJcyE6boqnA8=
github.com/u-root/uio v0.0.0-20221213070652-c3537552635f/go.mod h1:IogEAUBXDEwX7oR/BMmCctShYs80ql4hF0ySdzGxf7E=
github.com/u-root/uio v0.0.0-20230305220412-3e8cd9d6bf63 h1:YcojQL98T/OO+rybuzn2+5KrD5dBwXIvYBvQ2cD3Avg=
github.com/u-root/uio v0.0.0-20230305220412-3e8cd9d6bf63/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
@@ -138,10 +160,11 @@ go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ=
go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
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.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc=
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ=
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug=
golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
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.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
@@ -149,25 +172,40 @@ golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk=
golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
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-20190419010253-1f3472d942ba/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-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210929193557-e81a3d93ecf6/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
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.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI=
golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190322080309-f49334f85ddc/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190418153312-f0ce4c0180be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606122018-79a91cf218c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -181,8 +219,8 @@ golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/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.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
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.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
@@ -192,11 +230,12 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.9.3 h1:Gn1I8+64MsuTb/HpH+LmQtNas23LhUVr3rYZ0eKuaMM=
golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
golang.org/x/tools v0.8.0 h1:vSDcovVPld282ceKgDimkRSC8kpaH1dgyc9UMzlt84Y=
golang.org/x/tools v0.8.0/go.mod h1:JxBZ99ISMI5ViVkT1tr6tdNmXeTrcpVSD3vZ1RsRdN4=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@@ -72,8 +72,8 @@ func WriteJSONResponse(w http.ResponseWriter, r *http.Request, resp any) (err er
// WriteJSONResponseCode is like [WriteJSONResponse] but adds the ability to
// redefine the status code.
func WriteJSONResponseCode(w http.ResponseWriter, r *http.Request, code int, resp any) (err error) {
w.Header().Set(httphdr.ContentType, HdrValApplicationJSON)
w.WriteHeader(code)
w.Header().Set(httphdr.ContentType, HdrValApplicationJSON)
err = json.NewEncoder(w).Encode(resp)
if err != nil {
Error(r, w, http.StatusInternalServerError, "encoding resp: %s", err)

View File

@@ -304,7 +304,7 @@ func tryConn6(req *dhcpv6.Message, c net.PacketConn) (ok, next bool, err error)
if !(response.Type() == dhcpv6.MessageTypeAdvertise &&
msg.TransactionID == req.TransactionID &&
rcid != nil &&
cid.Equal(rcid)) {
cid.Equal(*rcid)) {
log.Debug("dhcpv6: received message from server doesn't match our request")

View File

@@ -1,6 +0,0 @@
package aghos
// PreCheckActionStart performs the service start action pre-check.
func PreCheckActionStart() (err error) {
return preCheckActionStart()
}

View File

@@ -1,32 +0,0 @@
//go:build darwin
package aghos
import (
"fmt"
"os"
"path/filepath"
"strings"
"github.com/AdguardTeam/golibs/log"
)
// preCheckActionStart performs the service start action pre-check. It warns
// user that the service should be installed into Applications directory.
func preCheckActionStart() (err error) {
exe, err := os.Executable()
if err != nil {
return fmt.Errorf("getting executable path: %v", err)
}
exe, err = filepath.EvalSymlinks(exe)
if err != nil {
return fmt.Errorf("evaluating executable symlinks: %v", err)
}
if !strings.HasPrefix(exe, "/Applications/") {
log.Info("warning: service must be started from within the /Applications directory")
}
return err
}

View File

@@ -1,8 +0,0 @@
//go:build !darwin
package aghos
// preCheckActionStart performs the service start action pre-check.
func preCheckActionStart() (err error) {
return nil
}

View File

@@ -1,12 +1,10 @@
package aghtest
import (
"context"
"io/fs"
"net"
"github.com/AdguardTeam/AdGuardHome/internal/aghos"
"github.com/AdguardTeam/AdGuardHome/internal/next/agh"
"github.com/AdguardTeam/dnsproxy/upstream"
"github.com/miekg/dns"
)
@@ -118,36 +116,6 @@ func (w *FSWatcher) Close() (err error) {
return w.OnClose()
}
// Package agh
// type check
var _ agh.ServiceWithConfig[struct{}] = (*ServiceWithConfig[struct{}])(nil)
// ServiceWithConfig is a mock [agh.ServiceWithConfig] implementation for tests.
type ServiceWithConfig[ConfigType any] struct {
OnStart func() (err error)
OnShutdown func(ctx context.Context) (err error)
OnConfig func() (c ConfigType)
}
// Start implements the [agh.ServiceWithConfig] interface for
// *ServiceWithConfig.
func (s *ServiceWithConfig[_]) Start() (err error) {
return s.OnStart()
}
// Shutdown implements the [agh.ServiceWithConfig] interface for
// *ServiceWithConfig.
func (s *ServiceWithConfig[_]) Shutdown(ctx context.Context) (err error) {
return s.OnShutdown(ctx)
}
// Config implements the [agh.ServiceWithConfig] interface for
// *ServiceWithConfig.
func (s *ServiceWithConfig[ConfigType]) Config() (c ConfigType) {
return s.OnConfig()
}
// Module dnsproxy
// Package upstream

View File

@@ -1,60 +1,46 @@
# Testing DHCP Server
# DHCP server
Contents:
* [Test setup with Virtual Box](#vbox)
* [Quick test with DHCPTest](#dhcptest)
* [Test setup with Virtual Box](#vbox)
## <a href="#vbox" id="vbox" name="vbox">Test setup with Virtual Box</a>
<a id="vbox"></a>
## Test setup with Virtual Box
### Prerequisites
To set up a test environment for DHCP server you need:
To set up a test environment for DHCP server you will need:
* Linux host machine
* Virtual Box
* Virtual machine (guest OS doesn't matter)
* Linux AG Home host machine (Virtual).
* Virtual Box.
* Virtual machine (guest OS doesn't matter).
### Configure client
### Configure Virtual Box
1. Install Virtual Box and run the following command to create a Host-Only network:
1. Install Virtual Box and run the following command to create a Host-Only
network:
$ VBoxManage hostonlyif create
```sh
$ VBoxManage hostonlyif create
```
You can check its status by `ip a` command.
You can check its status by `ip a` command.
You can also set up Host-Only network using Virtual Box menu:
```
File -> Host Network Manager...
```
You can also set up Host-Only network using Virtual Box menu:
2. Create your virtual machine and set up its network:
File -> Host Network Manager...
```
VM Settings -> Network -> Host-only Adapter
```
2. Create your virtual machine and set up its network:
3. Start your VM, install an OS. Configure your network interface to use
DHCP and the OS should ask for a IP address from our DHCP server.
VM Settings -> Network -> Host-only Adapter
4. To see the current IP addresses on client OS you can use `ip a` command on
Linux or `ipconfig` on Windows.
3. Start your VM, install an OS. Configure your network interface to use DHCP and the OS should ask for a IP address from our DHCP server.
5. To force the client OS to request an IP from DHCP server again, you can
use `dhclient` on Linux or `ipconfig /release` on Windows.
4. To see the current IP address on client OS you can use `ip a` command on Linux or `ipconfig` on Windows.
### Configure server
5. To force the client OS to request an IP from DHCP server again, you can use `dhclient` on Linux or `ipconfig /release` on Windows.
1. Edit server configuration file `AdGuardHome.yaml`, for example:
### Configure server
```yaml
dhcp:
1. Edit server configuration file 'AdGuardHome.yaml', for example:
dhcp:
enabled: true
interface_name: vboxnet0
local_domain_name: lan
dhcpv4:
gateway_ip: 192.168.56.1
subnet_mask: 255.255.255.0
@@ -68,29 +54,11 @@ To set up a test environment for DHCP server you will need:
lease_duration: 86400
ra_slaac_only: false
ra_allow_slaac: false
```
2. Start the server
2. Start the server
```sh
./AdGuardHome -v
```
./AdGuardHome
There should be a message in log which shows that DHCP server is ready:
There should be a message in log which shows that DHCP server is ready:
```
[info] DHCP: listening on 0.0.0.0:67
```
## <a href="#dhcptest" id="dhcptest" name="dhcptest">Quick test with DHCPTest utility</a>
### Prerequisites
* [DHCP test utility][dhcptest-gh].
### Quick test
The DHCP server could be tested for DISCOVER-OFFER packets with in
interactive mode.
[dhcptest-gh]: https://github.com/CyberShadow/dhcptest
[info] DHCP: listening on 0.0.0.0:67

View File

@@ -1,4 +1,4 @@
//go:build darwin || freebsd || openbsd
//go:build darwin
package dhcpd

View File

@@ -1,4 +1,4 @@
//go:build darwin || freebsd || openbsd
//go:build darwin
package dhcpd

View File

@@ -1,4 +1,4 @@
//go:build linux
//go:build freebsd || linux || openbsd
package dhcpd

View File

@@ -1,4 +1,4 @@
//go:build linux
//go:build freebsd || linux || openbsd
package dhcpd

View File

@@ -239,16 +239,36 @@ func Create(conf *ServerConfig) (s *server, err error) {
// [aghhttp.RegisterFunc].
s.registerHandlers()
v4Enabled, v6Enabled, err := s.setServers(conf)
v4conf := conf.Conf4
v4conf.InterfaceName = s.conf.InterfaceName
v4conf.notify = s.onNotify
v4conf.Enabled = s.conf.Enabled && v4conf.RangeStart.IsValid()
s.srv4, err = v4Create(&v4conf)
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return nil, err
if v4conf.Enabled {
return nil, fmt.Errorf("creating dhcpv4 srv: %w", err)
}
log.Debug("dhcpd: warning: creating dhcpv4 srv: %s", err)
}
v6conf := conf.Conf6
v6conf.Enabled = s.conf.Enabled
if len(v6conf.RangeStart) == 0 {
v6conf.Enabled = false
}
v6conf.InterfaceName = s.conf.InterfaceName
v6conf.notify = s.onNotify
s.srv6, err = v6Create(v6conf)
if err != nil {
return nil, fmt.Errorf("creating dhcpv6 srv: %w", err)
}
s.conf.Conf4 = conf.Conf4
s.conf.Conf6 = conf.Conf6
if s.conf.Enabled && !v4Enabled && !v6Enabled {
if s.conf.Enabled && !v4conf.Enabled && !v6conf.Enabled {
return nil, fmt.Errorf("neither dhcpv4 nor dhcpv6 srv is configured")
}
@@ -269,39 +289,6 @@ func Create(conf *ServerConfig) (s *server, err error) {
return s, nil
}
// setServers updates DHCPv4 and DHCPv6 servers created from the provided
// configuration conf.
func (s *server) setServers(conf *ServerConfig) (v4Enabled, v6Enabled bool, err error) {
v4conf := conf.Conf4
v4conf.InterfaceName = s.conf.InterfaceName
v4conf.notify = s.onNotify
v4conf.Enabled = s.conf.Enabled && v4conf.RangeStart.IsValid()
s.srv4, err = v4Create(&v4conf)
if err != nil {
if v4conf.Enabled {
return true, false, fmt.Errorf("creating dhcpv4 srv: %w", err)
}
log.Debug("dhcpd: warning: creating dhcpv4 srv: %s", err)
}
v6conf := conf.Conf6
v6conf.InterfaceName = s.conf.InterfaceName
v6conf.notify = s.onNotify
v6conf.Enabled = s.conf.Enabled
if len(v6conf.RangeStart) == 0 {
v6conf.Enabled = false
}
s.srv6, err = v6Create(v6conf)
if err != nil {
return v4conf.Enabled, v6conf.Enabled, fmt.Errorf("creating dhcpv6 srv: %w", err)
}
return v4conf.Enabled, v6conf.Enabled, nil
}
// Enabled returns true when the server is enabled.
func (s *server) Enabled() (ok bool) {
return s.conf.Enabled

View File

@@ -16,7 +16,6 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/netutil"
)
type v4ServerConfJSON struct {
@@ -264,28 +263,6 @@ func (s *server) handleDHCPSetConfigV6(
return srv6, enabled, err
}
// createServers returns DHCPv4 and DHCPv6 servers created from the provided
// configuration conf.
func (s *server) createServers(conf *dhcpServerConfigJSON) (srv4, srv6 DHCPServer, err error) {
srv4, v4Enabled, err := s.handleDHCPSetConfigV4(conf)
if err != nil {
return nil, nil, fmt.Errorf("bad dhcpv4 configuration: %s", err)
}
srv6, v6Enabled, err := s.handleDHCPSetConfigV6(conf)
if err != nil {
return nil, nil, fmt.Errorf("bad dhcpv6 configuration: %s", err)
}
if conf.Enabled == aghalg.NBTrue && !v4Enabled && !v6Enabled {
return nil, nil, fmt.Errorf("dhcpv4 or dhcpv6 configuration must be complete")
}
return srv4, srv6, nil
}
// handleDHCPSetConfig is the handler for the POST /control/dhcp/set_config
// HTTP API.
func (s *server) handleDHCPSetConfig(w http.ResponseWriter, r *http.Request) {
conf := &dhcpServerConfigJSON{}
conf.Enabled = aghalg.BoolToNullBool(s.conf.Enabled)
@@ -298,9 +275,22 @@ func (s *server) handleDHCPSetConfig(w http.ResponseWriter, r *http.Request) {
return
}
srv4, srv6, err := s.createServers(conf)
srv4, v4Enabled, err := s.handleDHCPSetConfigV4(conf)
if err != nil {
aghhttp.Error(r, w, http.StatusBadRequest, "%s", err)
aghhttp.Error(r, w, http.StatusBadRequest, "bad dhcpv4 configuration: %s", err)
return
}
srv6, v6Enabled, err := s.handleDHCPSetConfigV6(conf)
if err != nil {
aghhttp.Error(r, w, http.StatusBadRequest, "bad dhcpv6 configuration: %s", err)
return
}
if conf.Enabled == aghalg.NBTrue && !v4Enabled && !v6Enabled {
aghhttp.Error(r, w, http.StatusBadRequest, "dhcpv4 or dhcpv6 configuration must be complete")
return
}
@@ -360,10 +350,8 @@ type netInterfaceJSON struct {
Addrs6 []netip.Addr `json:"ipv6_addresses"`
}
// handleDHCPInterfaces is the handler for the GET /control/dhcp/interfaces
// HTTP API.
func (s *server) handleDHCPInterfaces(w http.ResponseWriter, r *http.Request) {
resp := map[string]*netInterfaceJSON{}
response := map[string]netInterfaceJSON{}
ifaces, err := net.Interfaces()
if err != nil {
@@ -374,86 +362,82 @@ func (s *server) handleDHCPInterfaces(w http.ResponseWriter, r *http.Request) {
for _, iface := range ifaces {
if iface.Flags&net.FlagLoopback != 0 {
// It's a loopback, skip it.
// it's a loopback, skip it
continue
}
if iface.Flags&net.FlagBroadcast == 0 {
// This interface doesn't support broadcast, skip it.
// this interface doesn't support broadcast, skip it
continue
}
jsonIface, iErr := newNetInterfaceJSON(iface)
if iErr != nil {
aghhttp.Error(r, w, http.StatusInternalServerError, "%s", iErr)
var addrs []net.Addr
addrs, err = iface.Addrs()
if err != nil {
aghhttp.Error(
r,
w,
http.StatusInternalServerError,
"Failed to get addresses for interface %s: %s",
iface.Name,
err,
)
return
}
if jsonIface != nil {
resp[iface.Name] = jsonIface
jsonIface := netInterfaceJSON{
Name: iface.Name,
HardwareAddr: iface.HardwareAddr.String(),
}
if iface.Flags != 0 {
jsonIface.Flags = iface.Flags.String()
}
// we don't want link-local addresses in json, so skip them
for _, addr := range addrs {
ipnet, ok := addr.(*net.IPNet)
if !ok {
// not an IPNet, should not happen
aghhttp.Error(
r,
w,
http.StatusInternalServerError,
"got iface.Addrs() element %[1]s that is not net.IPNet, it is %[1]T",
addr)
return
}
// ignore link-local
//
// TODO(e.burkov): Try to listen DHCP on LLA as well.
if ipnet.IP.IsLinkLocalUnicast() {
continue
}
if ip4 := ipnet.IP.To4(); ip4 != nil {
addr := netip.AddrFrom4(*(*[4]byte)(ip4))
jsonIface.Addrs4 = append(jsonIface.Addrs4, addr)
} else {
addr := netip.AddrFrom16(*(*[16]byte)(ipnet.IP))
jsonIface.Addrs6 = append(jsonIface.Addrs6, addr)
}
}
if len(jsonIface.Addrs4)+len(jsonIface.Addrs6) != 0 {
jsonIface.GatewayIP = aghnet.GatewayIP(iface.Name)
response[iface.Name] = jsonIface
}
}
_ = aghhttp.WriteJSONResponse(w, r, resp)
}
// newNetInterfaceJSON creates a JSON object from a [net.Interface] iface.
func newNetInterfaceJSON(iface net.Interface) (out *netInterfaceJSON, err error) {
addrs, err := iface.Addrs()
err = json.NewEncoder(w).Encode(response)
if err != nil {
return nil, fmt.Errorf(
"failed to get addresses for interface %s: %s",
iface.Name,
aghhttp.Error(
r,
w,
http.StatusInternalServerError,
"Failed to marshal json with available interfaces: %s",
err,
)
}
out = &netInterfaceJSON{
Name: iface.Name,
HardwareAddr: iface.HardwareAddr.String(),
}
if iface.Flags != 0 {
out.Flags = iface.Flags.String()
}
// We don't want link-local addresses in JSON, so skip them.
for _, addr := range addrs {
ipNet, ok := addr.(*net.IPNet)
if !ok {
// Not an IPNet, should not happen.
return nil, fmt.Errorf("got iface.Addrs() element %[1]s that is not"+
" net.IPNet, it is %[1]T", addr)
}
// Ignore link-local.
//
// TODO(e.burkov): Try to listen DHCP on LLA as well.
if ipNet.IP.IsLinkLocalUnicast() {
continue
}
vAddr, iErr := netutil.IPToAddrNoMapped(ipNet.IP)
if iErr != nil {
// Not an IPNet, should not happen.
return nil, fmt.Errorf("failed to convert IP address %[1]s: %w", addr, iErr)
}
if vAddr.Is4() {
out.Addrs4 = append(out.Addrs4, vAddr)
} else {
out.Addrs6 = append(out.Addrs6, vAddr)
}
}
if len(out.Addrs4)+len(out.Addrs6) == 0 {
return nil, nil
}
out.GatewayIP = aghnet.GatewayIP(iface.Name)
return out, nil
}
// dhcpSearchOtherResult contains information about other DHCP server for

View File

@@ -7,7 +7,6 @@ import (
"sync/atomic"
"time"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/netutil"
"golang.org/x/net/icmp"
@@ -196,7 +195,7 @@ func createICMPv6RAPacket(params icmpv6RA) (data []byte, err error) {
return data, nil
}
// Init initializes RA module.
// Init - initialize RA module
func (ra *raCtx) Init() (err error) {
ra.stop.Store(0)
ra.conn = nil
@@ -204,7 +203,8 @@ func (ra *raCtx) Init() (err error) {
return nil
}
log.Debug("dhcpv6 ra: source IP address: %s DNS IP address: %s", ra.ipAddr, ra.dnsIPAddr)
log.Debug("dhcpv6 ra: source IP address: %s DNS IP address: %s",
ra.ipAddr, ra.dnsIPAddr)
params := icmpv6RA{
managedAddressConfiguration: !ra.raSLAACOnly,
@@ -223,15 +223,18 @@ func (ra *raCtx) Init() (err error) {
return fmt.Errorf("creating packet: %w", err)
}
success := false
ipAndScope := ra.ipAddr.String() + "%" + ra.ifaceName
ra.conn, err = icmp.ListenPacket("ip6:ipv6-icmp", ipAndScope)
if err != nil {
return fmt.Errorf("dhcpv6 ra: icmp.ListenPacket: %w", err)
}
defer func() {
if err != nil {
err = errors.WithDeferred(err, ra.Close())
if !success {
derr := ra.Close()
if derr != nil {
log.Error("closing context: %s", derr)
}
}
}()
@@ -266,6 +269,7 @@ func (ra *raCtx) Init() (err error) {
log.Debug("dhcpv6 ra: loop exit")
}()
success = true
return nil
}

View File

@@ -342,8 +342,8 @@ func (s *v4Server) rmLease(lease *Lease) (err error) {
// server to be configured and it's not.
const ErrUnconfigured errors.Error = "server is unconfigured"
// AddStaticLease implements the DHCPServer interface for *v4Server. It is
// safe for concurrent use.
// AddStaticLease implements the DHCPServer interface for *v4Server. It is safe
// for concurrent use.
func (s *v4Server) AddStaticLease(l *Lease) (err error) {
defer func() { err = errors.Annotate(err, "dhcpv4: adding static lease: %w") }()
@@ -354,23 +354,21 @@ func (s *v4Server) AddStaticLease(l *Lease) (err error) {
l.IP = l.IP.Unmap()
if !l.IP.Is4() {
return fmt.Errorf("invalid IP %q: only IPv4 is supported", l.IP)
return fmt.Errorf("invalid ip %q, only ipv4 is supported", l.IP)
} else if gwIP := s.conf.GatewayIP; gwIP == l.IP {
return fmt.Errorf("can't assign the gateway IP %q to the lease", gwIP)
return fmt.Errorf("can't assign the gateway IP %s to the lease", gwIP)
}
l.IsStatic = true
err = netutil.ValidateMAC(l.HWAddr)
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return err
}
if hostname := l.Hostname; hostname != "" {
hostname, err = normalizeHostname(hostname)
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return err
}
@@ -388,9 +386,32 @@ func (s *v4Server) AddStaticLease(l *Lease) (err error) {
l.Hostname = hostname
}
err = s.updateStaticLease(l)
// Perform the following actions in an anonymous function to make sure
// that the lock gets unlocked before the notification step.
func() {
s.leasesLock.Lock()
defer s.leasesLock.Unlock()
err = s.rmDynamicLease(l)
if err != nil {
err = fmt.Errorf(
"removing dynamic leases for %s (%s): %w",
l.IP,
l.HWAddr,
err,
)
return
}
err = s.addLease(l)
if err != nil {
err = fmt.Errorf("adding static lease for %s (%s): %w", l.IP, l.HWAddr, err)
return
}
}()
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return err
}
@@ -400,25 +421,6 @@ func (s *v4Server) AddStaticLease(l *Lease) (err error) {
return nil
}
// updateStaticLease safe removes dynamic lease with the same properties and
// then adds a static lease l.
func (s *v4Server) updateStaticLease(l *Lease) (err error) {
s.leasesLock.Lock()
defer s.leasesLock.Unlock()
err = s.rmDynamicLease(l)
if err != nil {
return fmt.Errorf("removing dynamic leases for %s (%s): %w", l.IP, l.HWAddr, err)
}
err = s.addLease(l)
if err != nil {
return fmt.Errorf("adding static lease for %s (%s): %w", l.IP, l.HWAddr, err)
}
return nil
}
// RemoveStaticLease removes a static lease. It is safe for concurrent use.
func (s *v4Server) RemoveStaticLease(l *Lease) (err error) {
defer func() { err = errors.Annotate(err, "dhcpv4: %w") }()
@@ -892,9 +894,24 @@ func (s *v4Server) handleDecline(req, resp *dhcpv4.DHCPv4) (err error) {
reqIP = req.ClientIPAddr
}
oldLease := s.findLeaseForIP(reqIP, mac)
netIP, ok := netip.AddrFromSlice(reqIP)
if !ok {
log.Info("dhcpv4: invalid IP: %s", reqIP)
return nil
}
var oldLease *Lease
for _, l := range s.leases {
if bytes.Equal(l.HWAddr, mac) && l.IP == netIP {
oldLease = l
break
}
}
if oldLease == nil {
log.Info("dhcpv4: lease with IP %s for %s not found", reqIP, mac)
log.Info("dhcpv4: lease with ip %s for %s not found", reqIP, mac)
return nil
}
@@ -908,7 +925,7 @@ func (s *v4Server) handleDecline(req, resp *dhcpv4.DHCPv4) (err error) {
if err != nil {
return fmt.Errorf("allocating new lease for %s: %w", mac, err)
} else if newLease == nil {
log.Info("dhcpv4: allocating new lease for %s: no more IP addresses", mac)
log.Info("dhcpv4: allocating new lease for %s: no more ip addresses", mac)
resp.YourIPAddr = make([]byte, 4)
resp.UpdateOption(dhcpv4.OptMessageType(dhcpv4.MessageTypeAck))
@@ -924,32 +941,15 @@ func (s *v4Server) handleDecline(req, resp *dhcpv4.DHCPv4) (err error) {
return fmt.Errorf("adding new lease for %s: %w", mac, err)
}
log.Info("dhcpv4: changed IP from %s to %s for %s", reqIP, newLease.IP, mac)
log.Info("dhcpv4: changed ip from %s to %s for %s", reqIP, newLease.IP, mac)
resp.YourIPAddr = net.IP(newLease.IP.AsSlice())
resp.YourIPAddr = newLease.IP.AsSlice()
resp.UpdateOption(dhcpv4.OptMessageType(dhcpv4.MessageTypeAck))
return nil
}
// findLeaseForIP returns a lease for provided ip and mac.
func (s *v4Server) findLeaseForIP(ip net.IP, mac net.HardwareAddr) (l *Lease) {
netIP, ok := netip.AddrFromSlice(ip)
if !ok {
log.Info("dhcpv4: invalid IP: %s", ip)
return nil
}
for _, il := range s.leases {
if bytes.Equal(il.HWAddr, mac) && il.IP == netIP {
return il
}
}
return nil
}
// handleRelease is the handler for the DHCP Release request.
func (s *v4Server) handleRelease(req, resp *dhcpv4.DHCPv4) (err error) {
mac := req.ClientHWAddr
@@ -995,80 +995,11 @@ func (s *v4Server) handleRelease(req, resp *dhcpv4.DHCPv4) (err error) {
return nil
}
// messageHandler describes a DHCPv4 message handler function.
type messageHandler func(s *v4Server, req, resp *dhcpv4.DHCPv4) (rCode int, l *Lease, err error)
// messageHandlers is a map of handlers for various messages with message types
// keys.
var messageHandlers = map[dhcpv4.MessageType]messageHandler{
dhcpv4.MessageTypeDiscover: func(
s *v4Server,
req *dhcpv4.DHCPv4,
resp *dhcpv4.DHCPv4,
) (rCode int, l *Lease, err error) {
l, err = s.handleDiscover(req, resp)
if err != nil {
return 0, nil, fmt.Errorf("handling discover: %s", err)
}
if l == nil {
return 0, nil, nil
}
return 1, l, nil
},
dhcpv4.MessageTypeRequest: func(
s *v4Server,
req *dhcpv4.DHCPv4,
resp *dhcpv4.DHCPv4,
) (rCode int, l *Lease, err error) {
var toReply bool
l, toReply = s.handleRequest(req, resp)
if l == nil {
if toReply {
return 0, nil, nil
}
// Drop the packet.
return -1, nil, nil
}
return 1, l, nil
},
dhcpv4.MessageTypeDecline: func(
s *v4Server,
req *dhcpv4.DHCPv4,
resp *dhcpv4.DHCPv4,
) (rCode int, l *Lease, err error) {
err = s.handleDecline(req, resp)
if err != nil {
return 0, nil, fmt.Errorf("handling decline: %s", err)
}
return 1, nil, nil
},
dhcpv4.MessageTypeRelease: func(
s *v4Server,
req *dhcpv4.DHCPv4,
resp *dhcpv4.DHCPv4,
) (rCode int, l *Lease, err error) {
err = s.handleRelease(req, resp)
if err != nil {
return 0, nil, fmt.Errorf("handling release: %s", err)
}
return 1, nil, nil
},
}
// handle processes request, it finds a lease associated with MAC address and
// prepares response.
//
// Possible return values are:
// - "1": OK,
// - "0": error, reply with Nak,
// - "-1": error, don't reply.
func (s *v4Server) handle(req, resp *dhcpv4.DHCPv4) (rCode int) {
// Find a lease associated with MAC and prepare response
// Return 1: OK
// Return 0: error; reply with Nak
// Return -1: error; don't reply
func (s *v4Server) handle(req, resp *dhcpv4.DHCPv4) int {
var err error
// Include server's identifier option since any reply should contain it.
@@ -1076,26 +1007,47 @@ func (s *v4Server) handle(req, resp *dhcpv4.DHCPv4) (rCode int) {
// See https://datatracker.ietf.org/doc/html/rfc2131#page-29.
resp.UpdateOption(dhcpv4.OptServerIdentifier(s.conf.dnsIPAddrs[0].AsSlice()))
handler := messageHandlers[req.MessageType()]
if handler == nil {
s.updateOptions(req, resp)
// TODO(a.garipov): Refactor this into handlers.
var l *Lease
switch mt := req.MessageType(); mt {
case dhcpv4.MessageTypeDiscover:
l, err = s.handleDiscover(req, resp)
if err != nil {
log.Error("dhcpv4: handling discover: %s", err)
return 1
}
return 0
}
rCode, l, err := handler(s, req, resp)
if err != nil {
log.Error("dhcpv4: %s", err)
if l == nil {
return 0
}
case dhcpv4.MessageTypeRequest:
var toReply bool
l, toReply = s.handleRequest(req, resp)
if l == nil {
if toReply {
return 0
}
return -1 // drop packet
}
case dhcpv4.MessageTypeDecline:
err = s.handleDecline(req, resp)
if err != nil {
log.Error("dhcpv4: handling decline: %s", err)
return 0
}
return 0
}
case dhcpv4.MessageTypeRelease:
err = s.handleRelease(req, resp)
if err != nil {
log.Error("dhcpv4: handling release: %s", err)
if rCode != 1 {
return rCode
return 0
}
}
if l != nil {
resp.YourIPAddr = l.IP.AsSlice()
resp.YourIPAddr = net.IP(l.IP.AsSlice())
}
s.updateOptions(req, resp)
@@ -1210,8 +1162,23 @@ func (s *v4Server) Start() (err error) {
// No available IP addresses which may appear later.
return nil
}
// Update the value of Domain Name Server option separately from others if
// not assigned yet since its value is available only at server's start.
//
// TODO(e.burkov): Initialize as implicit option with the rest of default
// options when it will be possible to do before the call to Start.
if !s.explicitOpts.Has(dhcpv4.OptionDomainNameServer) {
s.implicitOpts.Update(dhcpv4.OptDNS(dnsIPAddrs...))
}
s.configureDNSIPAddrs(dnsIPAddrs)
for _, ip := range dnsIPAddrs {
ip = ip.To4()
if ip == nil {
continue
}
s.conf.dnsIPAddrs = append(s.conf.dnsIPAddrs, netip.AddrFrom4(*(*[4]byte)(ip)))
}
var c net.PacketConn
if c, err = s.newDHCPConn(iface); err != nil {
@@ -1232,10 +1199,10 @@ func (s *v4Server) Start() (err error) {
log.Info("dhcpv4: listening")
go func() {
if sErr := s.srv.Serve(); errors.Is(sErr, net.ErrClosed) {
if serr := s.srv.Serve(); errors.Is(serr, net.ErrClosed) {
log.Info("dhcpv4: server is closed")
} else if sErr != nil {
log.Error("dhcpv4: srv.Serve: %s", sErr)
} else if serr != nil {
log.Error("dhcpv4: srv.Serve: %s", serr)
}
}()
@@ -1246,28 +1213,6 @@ func (s *v4Server) Start() (err error) {
return nil
}
// configureDNSIPAddrs updates v4Server configuration with provided slice of
// dns IP addresses.
func (s *v4Server) configureDNSIPAddrs(dnsIPAddrs []net.IP) {
// Update the value of Domain Name Server option separately from others if
// not assigned yet since its value is available only at server's start.
//
// TODO(e.burkov): Initialize as implicit option with the rest of default
// options when it will be possible to do before the call to Start.
if !s.explicitOpts.Has(dhcpv4.OptionDomainNameServer) {
s.implicitOpts.Update(dhcpv4.OptDNS(dnsIPAddrs...))
}
for _, ip := range dnsIPAddrs {
vAddr, err := netutil.IPToAddr(ip, netutil.AddrFamilyIPv4)
if err != nil {
continue
}
s.conf.dnsIPAddrs = append(s.conf.dnsIPAddrs, vAddr)
}
}
// Stop - stop server
func (s *v4Server) Stop() (err error) {
if s.srv == nil {

View File

@@ -227,7 +227,7 @@ func TestV4Server_AddRemove_static(t *testing.T) {
},
name: "with_gateway_ip",
wantErrMsg: "dhcpv4: adding static lease: " +
`can't assign the gateway IP "192.168.10.1" to the lease`,
"can't assign the gateway IP 192.168.10.1 to the lease",
}, {
lease: &Lease{
Hostname: "ip6.local",
@@ -236,7 +236,7 @@ func TestV4Server_AddRemove_static(t *testing.T) {
},
name: "ipv6",
wantErrMsg: `dhcpv4: adding static lease: ` +
`invalid IP "ffff::1": only IPv4 is supported`,
`invalid ip "ffff::1", only ipv4 is supported`,
}, {
lease: &Lease{
Hostname: "bad-mac.local",

View File

@@ -30,7 +30,7 @@ type v6Server struct {
leasesLock sync.Mutex
leases []*Lease
ipAddrs [256]byte
sid dhcpv6.DUID
sid dhcpv6.Duid
ra raCtx // RA module
@@ -586,31 +586,9 @@ func (s *v6Server) packetHandler(conn net.PacketConn, peer net.Addr, req dhcpv6.
}
}
// configureDNSIPAddrs updates v6Server configuration with the slice of DNS IP
// addresses of provided interface iface. Initializes RA module.
func (s *v6Server) configureDNSIPAddrs(iface *net.Interface) (ok bool, err error) {
dnsIPAddrs, err := aghnet.IfaceDNSIPAddrs(
iface,
aghnet.IPVersion6,
defaultMaxAttempts,
defaultBackoff,
)
if err != nil {
return false, fmt.Errorf("interface %s: %w", iface.Name, err)
}
if len(dnsIPAddrs) == 0 {
return false, nil
}
s.conf.dnsIPAddrs = dnsIPAddrs
return true, s.initRA(iface)
}
// initRA initializes RA module.
func (s *v6Server) initRA(iface *net.Interface) (err error) {
// Choose the source IP address - should be link-local-unicast.
// initialize RA module
func (s *v6Server) initRA(iface *net.Interface) error {
// choose the source IP address - should be link-local-unicast
s.ra.ipAddr = s.conf.dnsIPAddrs[0]
for _, ip := range s.conf.dnsIPAddrs {
if ip.IsLinkLocalUnicast() {
@@ -626,7 +604,6 @@ func (s *v6Server) initRA(iface *net.Interface) (err error) {
s.ra.ifaceName = s.conf.InterfaceName
s.ra.iface = iface
s.ra.packetSendPeriod = 1 * time.Second
return s.ra.Init()
}
@@ -646,47 +623,63 @@ func (s *v6Server) Start() (err error) {
log.Debug("dhcpv6: starting...")
ok, err := s.configureDNSIPAddrs(iface)
dnsIPAddrs, err := aghnet.IfaceDNSIPAddrs(
iface,
aghnet.IPVersion6,
defaultMaxAttempts,
defaultBackoff,
)
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return err
return fmt.Errorf("interface %s: %w", ifaceName, err)
}
if !ok {
if len(dnsIPAddrs) == 0 {
// No available IP addresses which may appear later.
return nil
}
// Don't initialize DHCPv6 server if we must force the clients to use SLAAC.
s.conf.dnsIPAddrs = dnsIPAddrs
err = s.initRA(iface)
if err != nil {
return err
}
// don't initialize DHCPv6 server if we must force the clients to use SLAAC
if s.conf.RASLAACOnly {
log.Debug("not starting dhcpv6 server due to ra_slaac_only=true")
return nil
}
log.Debug("dhcpv6: listening...")
err = netutil.ValidateMAC(iface.HardwareAddr)
if err != nil {
return fmt.Errorf("validating interface %s: %w", iface.Name, err)
}
s.sid = &dhcpv6.DUIDLLT{
HWType: iana.HWTypeEthernet,
s.sid = dhcpv6.Duid{
Type: dhcpv6.DUID_LLT,
HwType: iana.HWTypeEthernet,
LinkLayerAddr: iface.HardwareAddr,
Time: dhcpv6.GetTime(),
}
s.srv, err = server6.NewServer(iface.Name, nil, s.packetHandler, server6.WithDebugLogger())
laddr := &net.UDPAddr{
IP: net.ParseIP("::"),
Port: dhcpv6.DefaultServerPort,
}
s.srv, err = server6.NewServer(iface.Name, laddr, s.packetHandler, server6.WithDebugLogger())
if err != nil {
return err
}
log.Debug("dhcpv6: listening...")
go func() {
if sErr := s.srv.Serve(); errors.Is(sErr, net.ErrClosed) {
if serr := s.srv.Serve(); errors.Is(serr, net.ErrClosed) {
log.Info("dhcpv6: server is closed")
} else if sErr != nil {
log.Error("dhcpv6: srv.Serve: %s", sErr)
} else if serr != nil {
log.Error("dhcpv6: srv.Serve: %s", serr)
}
}()

View File

@@ -121,8 +121,9 @@ func TestV6GetLease(t *testing.T) {
dnsAddr := net.ParseIP("2000::1")
s.conf.dnsIPAddrs = []net.IP{dnsAddr}
s.sid = &dhcpv6.DUIDLL{
HWType: iana.HWTypeEthernet,
s.sid = dhcpv6.Duid{
Type: dhcpv6.DUID_LLT,
HwType: iana.HWTypeEthernet,
LinkLayerAddr: net.HardwareAddr{0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA},
}
@@ -215,8 +216,9 @@ func TestV6GetDynamicLease(t *testing.T) {
dnsAddr := net.ParseIP("2000::1")
s.conf.dnsIPAddrs = []net.IP{dnsAddr}
s.sid = &dhcpv6.DUIDLL{
HWType: iana.HWTypeEthernet,
s.sid = dhcpv6.Duid{
Type: dhcpv6.DUID_LLT,
HwType: iana.HWTypeEthernet,
LinkLayerAddr: net.HardwareAddr{0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA},
}

View File

@@ -23,7 +23,6 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/aghtest"
"github.com/AdguardTeam/AdGuardHome/internal/dhcpd"
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/AdGuardHome/internal/filtering/hashprefix"
"github.com/AdguardTeam/AdGuardHome/internal/filtering/safesearch"
"github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/dnsproxy/upstream"
@@ -916,23 +915,13 @@ func TestBlockedByHosts(t *testing.T) {
}
func TestBlockedBySafeBrowsing(t *testing.T) {
const (
hostname = "wmconvirus.narod.ru"
cacheTime = 10 * time.Minute
cacheSize = 10000
)
sbChecker := hashprefix.New(&hashprefix.Config{
CacheTime: cacheTime,
CacheSize: cacheSize,
Upstream: aghtest.NewBlockUpstream(hostname, true),
})
const hostname = "wmconvirus.narod.ru"
sbUps := aghtest.NewBlockUpstream(hostname, true)
ans4, _ := (&aghtest.TestResolver{}).HostToIPs(hostname)
filterConf := &filtering.Config{
SafeBrowsingEnabled: true,
SafeBrowsingChecker: sbChecker,
}
forwardConf := ServerConfig{
UDPListenAddrs: []*net.UDPAddr{{}},
@@ -946,6 +935,7 @@ func TestBlockedBySafeBrowsing(t *testing.T) {
},
}
s := createTestServer(t, filterConf, forwardConf, nil)
s.dnsFilter.SetSafeBrowsingUpstream(sbUps)
startDeferStop(t, s)
addr := s.dnsProxy.Addr(proxy.ProtoUDP)

View File

@@ -205,8 +205,8 @@ func TestDNSForwardHTTP_handleSetConfig(t *testing.T) {
wantSet: `validating upstream servers: validating upstream "!!!": not an ip:port`,
}, {
name: "bootstraps_bad",
wantSet: `checking bootstrap a: invalid address: bootstrap a:53: ` +
`ParseAddr("a"): unable to parse IP`,
wantSet: `checking bootstrap a: invalid address: ` +
`Resolver a is not eligible to be a bootstrap DNS server`,
}, {
name: "cache_bad_ttl",
wantSet: `cache_ttl_min must be less or equal than cache_ttl_max`,
@@ -487,8 +487,7 @@ func TestServer_handleTestUpstreaDNS(t *testing.T) {
},
wantResp: map[string]any{
badUps: `upstream "` + badUps + `" fails to exchange: ` +
`couldn't communicate with upstream: exchanging with ` +
badUps + ` over tcp: dns: id mismatch`,
`couldn't communicate with upstream: dns: id mismatch`,
},
name: "broken",
}, {
@@ -498,8 +497,7 @@ func TestServer_handleTestUpstreaDNS(t *testing.T) {
wantResp: map[string]any{
goodUps: "OK",
badUps: `upstream "` + badUps + `" fails to exchange: ` +
`couldn't communicate with upstream: exchanging with ` +
badUps + ` over tcp: dns: id mismatch`,
`couldn't communicate with upstream: dns: id mismatch`,
},
name: "both",
}}

View File

@@ -18,6 +18,8 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
"github.com/AdguardTeam/dnsproxy/upstream"
"github.com/AdguardTeam/golibs/cache"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/mathutil"
@@ -73,12 +75,6 @@ type Resolver interface {
// Config allows you to configure DNS filtering with New() or just change variables directly.
type Config struct {
// SafeBrowsingChecker is the safe browsing hash-prefix checker.
SafeBrowsingChecker Checker `yaml:"-"`
// ParentControl is the parental control hash-prefix checker.
ParentalControlChecker Checker `yaml:"-"`
// enabled is used to be returned within Settings.
//
// It is of type uint32 to be accessed by atomic.
@@ -162,22 +158,8 @@ type hostChecker struct {
name string
}
// Checker is used for safe browsing or parental control hash-prefix filtering.
type Checker interface {
// Check returns true if request for the host should be blocked.
Check(host string) (block bool, err error)
}
// DNSFilter matches hostnames and DNS requests against filtering rules.
type DNSFilter struct {
safeSearch SafeSearch
// safeBrowsingChecker is the safe browsing hash-prefix checker.
safeBrowsingChecker Checker
// parentalControl is the parental control hash-prefix checker.
parentalControlChecker Checker
rulesStorage *filterlist.RuleStorage
filteringEngine *urlfilter.DNSEngine
@@ -186,6 +168,14 @@ type DNSFilter struct {
engineLock sync.RWMutex
parentalServer string // access via methods
safeBrowsingServer string // access via methods
parentalUpstream upstream.Upstream
safeBrowsingUpstream upstream.Upstream
safebrowsingCache cache.Cache
parentalCache cache.Cache
Config // for direct access by library users, even a = assignment
// confLock protects Config.
confLock sync.RWMutex
@@ -202,6 +192,7 @@ type DNSFilter struct {
// TODO(e.burkov): Don't use regexp for such a simple text processing task.
filterTitleRegexp *regexp.Regexp
safeSearch SafeSearch
hostCheckers []hostChecker
}
@@ -949,12 +940,19 @@ func InitModule() {
// be non-nil.
func New(c *Config, blockFilters []Filter) (d *DNSFilter, err error) {
d = &DNSFilter{
refreshLock: &sync.Mutex{},
filterTitleRegexp: regexp.MustCompile(`^! Title: +(.*)$`),
safeBrowsingChecker: c.SafeBrowsingChecker,
parentalControlChecker: c.ParentalControlChecker,
refreshLock: &sync.Mutex{},
filterTitleRegexp: regexp.MustCompile(`^! Title: +(.*)$`),
}
d.safebrowsingCache = cache.New(cache.Config{
EnableLRU: true,
MaxSize: c.SafeBrowsingCacheSize,
})
d.parentalCache = cache.New(cache.Config{
EnableLRU: true,
MaxSize: c.ParentalCacheSize,
})
d.safeSearch = c.SafeSearch
d.hostCheckers = []hostChecker{{
@@ -979,6 +977,11 @@ func New(c *Config, blockFilters []Filter) (d *DNSFilter, err error) {
defer func() { err = errors.Annotate(err, "filtering: %w") }()
err = d.initSecurityServices()
if err != nil {
return nil, fmt.Errorf("initializing services: %s", err)
}
d.Config = *c
d.filtersMu = &sync.RWMutex{}
@@ -1035,69 +1038,3 @@ func (d *DNSFilter) Start() {
// So for now we just start this periodic task from here.
go d.periodicallyRefreshFilters()
}
// Safe browsing and parental control methods.
// TODO(a.garipov): Unify with checkParental.
func (d *DNSFilter) checkSafeBrowsing(
host string,
_ uint16,
setts *Settings,
) (res Result, err error) {
if !setts.ProtectionEnabled || !setts.SafeBrowsingEnabled {
return Result{}, nil
}
if log.GetLevel() >= log.DEBUG {
timer := log.StartTimer()
defer timer.LogElapsed("safebrowsing lookup for %q", host)
}
res = Result{
Rules: []*ResultRule{{
Text: "adguard-malware-shavar",
FilterListID: SafeBrowsingListID,
}},
Reason: FilteredSafeBrowsing,
IsFiltered: true,
}
block, err := d.safeBrowsingChecker.Check(host)
if !block || err != nil {
return Result{}, err
}
return res, nil
}
// TODO(a.garipov): Unify with checkSafeBrowsing.
func (d *DNSFilter) checkParental(
host string,
_ uint16,
setts *Settings,
) (res Result, err error) {
if !setts.ProtectionEnabled || !setts.ParentalEnabled {
return Result{}, nil
}
if log.GetLevel() >= log.DEBUG {
timer := log.StartTimer()
defer timer.LogElapsed("parental lookup for %q", host)
}
res = Result{
Rules: []*ResultRule{{
Text: "parental CATEGORY_BLACKLISTED",
FilterListID: ParentalListID,
}},
Reason: FilteredParental,
IsFiltered: true,
}
block, err := d.parentalControlChecker.Check(host)
if !block || err != nil {
return Result{}, err
}
return res, nil
}

View File

@@ -7,7 +7,7 @@ import (
"testing"
"github.com/AdguardTeam/AdGuardHome/internal/aghtest"
"github.com/AdguardTeam/AdGuardHome/internal/filtering/hashprefix"
"github.com/AdguardTeam/golibs/cache"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/testutil"
"github.com/AdguardTeam/urlfilter/rules"
@@ -27,6 +27,17 @@ const (
// Helpers.
func purgeCaches(d *DNSFilter) {
for _, c := range []cache.Cache{
d.safebrowsingCache,
d.parentalCache,
} {
if c != nil {
c.Clear()
}
}
}
func newForTest(t testing.TB, c *Config, filters []Filter) (f *DNSFilter, setts *Settings) {
setts = &Settings{
ProtectionEnabled: true,
@@ -47,15 +58,9 @@ func newForTest(t testing.TB, c *Config, filters []Filter) (f *DNSFilter, setts
f, err := New(c, filters)
require.NoError(t, err)
return f, setts
}
purgeCaches(f)
func newChecker(host string) Checker {
return hashprefix.New(&hashprefix.Config{
CacheTime: 10,
CacheSize: 100000,
Upstream: aghtest.NewBlockUpstream(host, true),
})
return f, setts
}
func (d *DNSFilter) checkMatch(t *testing.T, hostname string, setts *Settings) {
@@ -170,14 +175,10 @@ func TestSafeBrowsing(t *testing.T) {
aghtest.ReplaceLogWriter(t, logOutput)
aghtest.ReplaceLogLevel(t, log.DEBUG)
sbChecker := newChecker(sbBlocked)
d, setts := newForTest(t, &Config{
SafeBrowsingEnabled: true,
SafeBrowsingChecker: sbChecker,
}, nil)
d, setts := newForTest(t, &Config{SafeBrowsingEnabled: true}, nil)
t.Cleanup(d.Close)
d.SetSafeBrowsingUpstream(aghtest.NewBlockUpstream(sbBlocked, true))
d.checkMatch(t, sbBlocked, setts)
require.Contains(t, logOutput.String(), fmt.Sprintf("safebrowsing lookup for %q", sbBlocked))
@@ -187,17 +188,18 @@ func TestSafeBrowsing(t *testing.T) {
d.checkMatchEmpty(t, pcBlocked, setts)
// Cached result.
d.safeBrowsingServer = "127.0.0.1"
d.checkMatch(t, sbBlocked, setts)
d.checkMatchEmpty(t, pcBlocked, setts)
d.safeBrowsingServer = defaultSafebrowsingServer
}
func TestParallelSB(t *testing.T) {
d, setts := newForTest(t, &Config{
SafeBrowsingEnabled: true,
SafeBrowsingChecker: newChecker(sbBlocked),
}, nil)
d, setts := newForTest(t, &Config{SafeBrowsingEnabled: true}, nil)
t.Cleanup(d.Close)
d.SetSafeBrowsingUpstream(aghtest.NewBlockUpstream(sbBlocked, true))
t.Run("group", func(t *testing.T) {
for i := 0; i < 100; i++ {
t.Run(fmt.Sprintf("aaa%d", i), func(t *testing.T) {
@@ -218,12 +220,10 @@ func TestParentalControl(t *testing.T) {
aghtest.ReplaceLogWriter(t, logOutput)
aghtest.ReplaceLogLevel(t, log.DEBUG)
d, setts := newForTest(t, &Config{
ParentalEnabled: true,
ParentalControlChecker: newChecker(pcBlocked),
}, nil)
d, setts := newForTest(t, &Config{ParentalEnabled: true}, nil)
t.Cleanup(d.Close)
d.SetParentalUpstream(aghtest.NewBlockUpstream(pcBlocked, true))
d.checkMatch(t, pcBlocked, setts)
require.Contains(t, logOutput.String(), fmt.Sprintf("parental lookup for %q", pcBlocked))
@@ -233,6 +233,7 @@ func TestParentalControl(t *testing.T) {
d.checkMatchEmpty(t, "api.jquery.com", setts)
// Test cached result.
d.parentalServer = "127.0.0.1"
d.checkMatch(t, pcBlocked, setts)
d.checkMatchEmpty(t, "yandex.ru", setts)
}
@@ -592,10 +593,8 @@ func applyClientSettings(setts *Settings) {
func TestClientSettings(t *testing.T) {
d, setts := newForTest(t,
&Config{
ParentalEnabled: true,
SafeBrowsingEnabled: false,
SafeBrowsingChecker: newChecker(sbBlocked),
ParentalControlChecker: newChecker(pcBlocked),
ParentalEnabled: true,
SafeBrowsingEnabled: false,
},
[]Filter{{
ID: 0, Data: []byte("||example.org^\n"),
@@ -603,6 +602,9 @@ func TestClientSettings(t *testing.T) {
)
t.Cleanup(d.Close)
d.SetParentalUpstream(aghtest.NewBlockUpstream(pcBlocked, true))
d.SetSafeBrowsingUpstream(aghtest.NewBlockUpstream(sbBlocked, true))
type testCase struct {
name string
host string
@@ -663,12 +665,11 @@ func TestClientSettings(t *testing.T) {
// Benchmarks.
func BenchmarkSafeBrowsing(b *testing.B) {
d, setts := newForTest(b, &Config{
SafeBrowsingEnabled: true,
SafeBrowsingChecker: newChecker(sbBlocked),
}, nil)
d, setts := newForTest(b, &Config{SafeBrowsingEnabled: true}, nil)
b.Cleanup(d.Close)
d.SetSafeBrowsingUpstream(aghtest.NewBlockUpstream(sbBlocked, true))
for n := 0; n < b.N; n++ {
res, err := d.CheckHost(sbBlocked, dns.TypeA, setts)
require.NoError(b, err)
@@ -678,12 +679,11 @@ func BenchmarkSafeBrowsing(b *testing.B) {
}
func BenchmarkSafeBrowsingParallel(b *testing.B) {
d, setts := newForTest(b, &Config{
SafeBrowsingEnabled: true,
SafeBrowsingChecker: newChecker(sbBlocked),
}, nil)
d, setts := newForTest(b, &Config{SafeBrowsingEnabled: true}, nil)
b.Cleanup(d.Close)
d.SetSafeBrowsingUpstream(aghtest.NewBlockUpstream(sbBlocked, true))
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
res, err := d.CheckHost(sbBlocked, dns.TypeA, setts)

View File

@@ -1,130 +0,0 @@
package hashprefix
import (
"encoding/binary"
"time"
"github.com/AdguardTeam/golibs/log"
)
// expirySize is the size of expiry in cacheItem.
const expirySize = 8
// cacheItem represents an item that we will store in the cache.
type cacheItem struct {
// expiry is the time when cacheItem will expire.
expiry time.Time
// hashes is the hashed hostnames.
hashes []hostnameHash
}
// toCacheItem decodes cacheItem from data. data must be at least equal to
// expiry size.
func toCacheItem(data []byte) *cacheItem {
t := time.Unix(int64(binary.BigEndian.Uint64(data)), 0)
data = data[expirySize:]
hashes := make([]hostnameHash, len(data)/hashSize)
for i := 0; i < len(data); i += hashSize {
var hash hostnameHash
copy(hash[:], data[i:i+hashSize])
hashes = append(hashes, hash)
}
return &cacheItem{
expiry: t,
hashes: hashes,
}
}
// fromCacheItem encodes cacheItem into data.
func fromCacheItem(item *cacheItem) (data []byte) {
data = make([]byte, len(item.hashes)*hashSize+expirySize)
expiry := item.expiry.Unix()
binary.BigEndian.PutUint64(data[:expirySize], uint64(expiry))
for _, v := range item.hashes {
// nolint:looppointer // The subsilce is used for a copy.
data = append(data, v[:]...)
}
return data
}
// findInCache finds hashes in the cache. If nothing found returns list of
// hashes, prefixes of which will be sent to upstream.
func (c *Checker) findInCache(
hashes []hostnameHash,
) (found, blocked bool, hashesToRequest []hostnameHash) {
now := time.Now()
i := 0
for _, hash := range hashes {
// nolint:looppointer // The subsilce is used for a safe cache lookup.
data := c.cache.Get(hash[:prefixLen])
if data == nil {
hashes[i] = hash
i++
continue
}
item := toCacheItem(data)
if now.After(item.expiry) {
hashes[i] = hash
i++
continue
}
if ok := findMatch(hashes, item.hashes); ok {
return true, true, nil
}
}
if i == 0 {
return true, false, nil
}
return false, false, hashes[:i]
}
// storeInCache caches hashes.
func (c *Checker) storeInCache(hashesToRequest, respHashes []hostnameHash) {
hashToStore := make(map[prefix][]hostnameHash)
for _, hash := range respHashes {
var pref prefix
// nolint:looppointer // The subsilce is used for a copy.
copy(pref[:], hash[:])
hashToStore[pref] = append(hashToStore[pref], hash)
}
for pref, hash := range hashToStore {
// nolint:looppointer // The subsilce is used for a safe cache lookup.
c.setCache(pref[:], hash)
}
for _, hash := range hashesToRequest {
// nolint:looppointer // The subsilce is used for a safe cache lookup.
pref := hash[:prefixLen]
val := c.cache.Get(pref)
if val == nil {
c.setCache(pref, nil)
}
}
}
// setCache stores hash in cache.
func (c *Checker) setCache(pref []byte, hashes []hostnameHash) {
item := &cacheItem{
expiry: time.Now().Add(c.cacheTime),
hashes: hashes,
}
c.cache.Set(pref, fromCacheItem(item))
log.Debug("%s: stored in cache: %v", c.svc, pref)
}

View File

@@ -1,245 +0,0 @@
// Package hashprefix used for safe browsing and parent control.
package hashprefix
import (
"crypto/sha256"
"encoding/hex"
"fmt"
"strings"
"time"
"github.com/AdguardTeam/dnsproxy/upstream"
"github.com/AdguardTeam/golibs/cache"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/stringutil"
"github.com/miekg/dns"
"golang.org/x/exp/slices"
"golang.org/x/net/publicsuffix"
)
const (
// prefixLen is the length of the hash prefix of the filtered hostname.
prefixLen = 2
// hashSize is the size of hashed hostname.
hashSize = sha256.Size
// hexSize is the size of hexadecimal representation of hashed hostname.
hexSize = hashSize * 2
)
// prefix is the type of the SHA256 hash prefix used to match against the
// domain-name database.
type prefix [prefixLen]byte
// hostnameHash is the hashed hostname.
//
// TODO(s.chzhen): Split into prefix and suffix.
type hostnameHash [hashSize]byte
// findMatch returns true if one of the a hostnames matches one of the b.
func findMatch(a, b []hostnameHash) (matched bool) {
for _, hash := range a {
if slices.Contains(b, hash) {
return true
}
}
return false
}
// Config is the configuration structure for safe browsing and parental
// control.
type Config struct {
// Upstream is the upstream DNS server.
Upstream upstream.Upstream
// ServiceName is the name of the service.
ServiceName string
// TXTSuffix is the TXT suffix for DNS request.
TXTSuffix string
// CacheTime is the time period to store hash.
CacheTime time.Duration
// CacheSize is the maximum size of the cache. If it's zero, cache size is
// unlimited.
CacheSize uint
}
type Checker struct {
// upstream is the upstream DNS server.
upstream upstream.Upstream
// cache stores hostname hashes.
cache cache.Cache
// svc is the name of the service.
svc string
// txtSuffix is the TXT suffix for DNS request.
txtSuffix string
// cacheTime is the time period to store hash.
cacheTime time.Duration
}
// New returns Checker.
func New(conf *Config) (c *Checker) {
return &Checker{
upstream: conf.Upstream,
cache: cache.New(cache.Config{
EnableLRU: true,
MaxSize: conf.CacheSize,
}),
svc: conf.ServiceName,
txtSuffix: conf.TXTSuffix,
cacheTime: conf.CacheTime,
}
}
// Check returns true if request for the host should be blocked.
func (c *Checker) Check(host string) (ok bool, err error) {
hashes := hostnameToHashes(host)
found, blocked, hashesToRequest := c.findInCache(hashes)
if found {
log.Debug("%s: found %q in cache, blocked: %t", c.svc, host, blocked)
return blocked, nil
}
question := c.getQuestion(hashesToRequest)
log.Debug("%s: checking %s: %s", c.svc, host, question)
req := (&dns.Msg{}).SetQuestion(question, dns.TypeTXT)
resp, err := c.upstream.Exchange(req)
if err != nil {
return false, fmt.Errorf("getting hashes: %w", err)
}
matched, receivedHashes := c.processAnswer(hashesToRequest, resp, host)
c.storeInCache(hashesToRequest, receivedHashes)
return matched, nil
}
// hostnameToHashes returns hashes that should be checked by the hash prefix
// filter.
func hostnameToHashes(host string) (hashes []hostnameHash) {
// subDomainNum defines how many labels should be hashed to match against a
// hash prefix filter.
const subDomainNum = 4
pubSuf, icann := publicsuffix.PublicSuffix(host)
if !icann {
// Check the full private domain space.
pubSuf = ""
}
nDots := 0
i := strings.LastIndexFunc(host, func(r rune) (ok bool) {
if r == '.' {
nDots++
}
return nDots == subDomainNum
})
if i != -1 {
host = host[i+1:]
}
sub := netutil.Subdomains(host)
for _, s := range sub {
if s == pubSuf {
break
}
sum := sha256.Sum256([]byte(s))
hashes = append(hashes, sum)
}
return hashes
}
// getQuestion combines hexadecimal encoded prefixes of hashed hostnames into
// string.
func (c *Checker) getQuestion(hashes []hostnameHash) (q string) {
b := &strings.Builder{}
for _, hash := range hashes {
// nolint:looppointer // The subsilce is used for safe hex encoding.
stringutil.WriteToBuilder(b, hex.EncodeToString(hash[:prefixLen]), ".")
}
stringutil.WriteToBuilder(b, c.txtSuffix)
return b.String()
}
// processAnswer returns true if DNS response matches the hash, and received
// hashed hostnames from the upstream.
func (c *Checker) processAnswer(
hashesToRequest []hostnameHash,
resp *dns.Msg,
host string,
) (matched bool, receivedHashes []hostnameHash) {
txtCount := 0
for _, a := range resp.Answer {
txt, ok := a.(*dns.TXT)
if !ok {
continue
}
txtCount++
receivedHashes = c.appendHashesFromTXT(receivedHashes, txt, host)
}
log.Debug("%s: received answer for %s with %d TXT count", c.svc, host, txtCount)
matched = findMatch(hashesToRequest, receivedHashes)
if matched {
log.Debug("%s: matched %s", c.svc, host)
return true, receivedHashes
}
return false, receivedHashes
}
// appendHashesFromTXT appends received hashed hostnames.
func (c *Checker) appendHashesFromTXT(
hashes []hostnameHash,
txt *dns.TXT,
host string,
) (receivedHashes []hostnameHash) {
log.Debug("%s: received hashes for %s: %v", c.svc, host, txt.Txt)
for _, t := range txt.Txt {
if len(t) != hexSize {
log.Debug("%s: wrong hex size %d for %s %s", c.svc, len(t), host, t)
continue
}
buf, err := hex.DecodeString(t)
if err != nil {
log.Debug("%s: decoding hex string %s: %s", c.svc, t, err)
continue
}
var hash hostnameHash
copy(hash[:], buf)
hashes = append(hashes, hash)
}
return hashes
}

View File

@@ -1,248 +0,0 @@
package hashprefix
import (
"crypto/sha256"
"encoding/hex"
"strings"
"testing"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/aghtest"
"github.com/AdguardTeam/golibs/cache"
"github.com/miekg/dns"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/exp/slices"
)
const (
cacheTime = 10 * time.Minute
cacheSize = 10000
)
func TestChcker_getQuestion(t *testing.T) {
const suf = "sb.dns.adguard.com."
// test hostnameToHashes()
hashes := hostnameToHashes("1.2.3.sub.host.com")
assert.Len(t, hashes, 3)
hash := sha256.Sum256([]byte("3.sub.host.com"))
hexPref1 := hex.EncodeToString(hash[:prefixLen])
assert.True(t, slices.Contains(hashes, hash))
hash = sha256.Sum256([]byte("sub.host.com"))
hexPref2 := hex.EncodeToString(hash[:prefixLen])
assert.True(t, slices.Contains(hashes, hash))
hash = sha256.Sum256([]byte("host.com"))
hexPref3 := hex.EncodeToString(hash[:prefixLen])
assert.True(t, slices.Contains(hashes, hash))
hash = sha256.Sum256([]byte("com"))
assert.False(t, slices.Contains(hashes, hash))
c := &Checker{
svc: "SafeBrowsing",
txtSuffix: suf,
}
q := c.getQuestion(hashes)
assert.Contains(t, q, hexPref1)
assert.Contains(t, q, hexPref2)
assert.Contains(t, q, hexPref3)
assert.True(t, strings.HasSuffix(q, suf))
}
func TestHostnameToHashes(t *testing.T) {
testCases := []struct {
name string
host string
wantLen int
}{{
name: "basic",
host: "example.com",
wantLen: 1,
}, {
name: "sub_basic",
host: "www.example.com",
wantLen: 2,
}, {
name: "private_domain",
host: "foo.co.uk",
wantLen: 1,
}, {
name: "sub_private_domain",
host: "bar.foo.co.uk",
wantLen: 2,
}, {
name: "private_domain_v2",
host: "foo.blogspot.co.uk",
wantLen: 4,
}, {
name: "sub_private_domain_v2",
host: "bar.foo.blogspot.co.uk",
wantLen: 4,
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
hashes := hostnameToHashes(tc.host)
assert.Len(t, hashes, tc.wantLen)
})
}
}
func TestChecker_storeInCache(t *testing.T) {
c := &Checker{
svc: "SafeBrowsing",
cacheTime: cacheTime,
}
conf := cache.Config{}
c.cache = cache.New(conf)
// store in cache hashes for "3.sub.host.com" and "host.com"
// and empty data for hash-prefix for "sub.host.com"
hashes := []hostnameHash{}
hash := sha256.Sum256([]byte("sub.host.com"))
hashes = append(hashes, hash)
var hashesArray []hostnameHash
hash4 := sha256.Sum256([]byte("3.sub.host.com"))
hashesArray = append(hashesArray, hash4)
hash2 := sha256.Sum256([]byte("host.com"))
hashesArray = append(hashesArray, hash2)
c.storeInCache(hashes, hashesArray)
// match "3.sub.host.com" or "host.com" from cache
hashes = []hostnameHash{}
hash = sha256.Sum256([]byte("3.sub.host.com"))
hashes = append(hashes, hash)
hash = sha256.Sum256([]byte("sub.host.com"))
hashes = append(hashes, hash)
hash = sha256.Sum256([]byte("host.com"))
hashes = append(hashes, hash)
found, blocked, _ := c.findInCache(hashes)
assert.True(t, found)
assert.True(t, blocked)
// match "sub.host.com" from cache
hashes = []hostnameHash{}
hash = sha256.Sum256([]byte("sub.host.com"))
hashes = append(hashes, hash)
found, blocked, _ = c.findInCache(hashes)
assert.True(t, found)
assert.False(t, blocked)
// Match "sub.host.com" from cache. Another hash for "host.example" is not
// in the cache, so get data for it from the server.
hashes = []hostnameHash{}
hash = sha256.Sum256([]byte("sub.host.com"))
hashes = append(hashes, hash)
hash = sha256.Sum256([]byte("host.example"))
hashes = append(hashes, hash)
found, _, hashesToRequest := c.findInCache(hashes)
assert.False(t, found)
hash = sha256.Sum256([]byte("sub.host.com"))
ok := slices.Contains(hashesToRequest, hash)
assert.False(t, ok)
hash = sha256.Sum256([]byte("host.example"))
ok = slices.Contains(hashesToRequest, hash)
assert.True(t, ok)
c = &Checker{
svc: "SafeBrowsing",
cacheTime: cacheTime,
}
c.cache = cache.New(cache.Config{})
hashes = []hostnameHash{}
hash = sha256.Sum256([]byte("sub.host.com"))
hashes = append(hashes, hash)
c.cache.Set(hash[:prefixLen], make([]byte, expirySize+hashSize))
found, _, _ = c.findInCache(hashes)
assert.False(t, found)
}
func TestChecker_Check(t *testing.T) {
const hostname = "example.org"
testCases := []struct {
name string
wantBlock bool
}{{
name: "sb_no_block",
wantBlock: false,
}, {
name: "sb_block",
wantBlock: true,
}, {
name: "pc_no_block",
wantBlock: false,
}, {
name: "pc_block",
wantBlock: true,
}}
for _, tc := range testCases {
c := New(&Config{
CacheTime: cacheTime,
CacheSize: cacheSize,
})
// Prepare the upstream.
ups := aghtest.NewBlockUpstream(hostname, tc.wantBlock)
var numReq int
onExchange := ups.OnExchange
ups.OnExchange = func(req *dns.Msg) (resp *dns.Msg, err error) {
numReq++
return onExchange(req)
}
c.upstream = ups
t.Run(tc.name, func(t *testing.T) {
// Firstly, check the request blocking.
hits := 0
res := false
res, err := c.Check(hostname)
require.NoError(t, err)
if tc.wantBlock {
assert.True(t, res)
hits++
} else {
require.False(t, res)
}
// Check the cache state, check the response is now cached.
assert.Equal(t, 1, c.cache.Stats().Count)
assert.Equal(t, hits, c.cache.Stats().Hit)
// There was one request to an upstream.
assert.Equal(t, 1, numReq)
// Now make the same request to check the cache was used.
res, err = c.Check(hostname)
require.NoError(t, err)
if tc.wantBlock {
assert.True(t, res)
} else {
require.False(t, res)
}
// Check the cache state, it should've been used.
assert.Equal(t, 1, c.cache.Stats().Count)
assert.Equal(t, hits+1, c.cache.Stats().Hit)
// Check that there were no additional requests.
assert.Equal(t, 1, numReq)
})
}
}

View File

@@ -8,7 +8,6 @@ import (
"net/url"
"os"
"path/filepath"
"sync"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
@@ -459,80 +458,6 @@ func (d *DNSFilter) handleCheckHost(w http.ResponseWriter, r *http.Request) {
_ = aghhttp.WriteJSONResponse(w, r, resp)
}
// setProtectedBool sets the value of a boolean pointer under a lock. l must
// protect the value under ptr.
//
// TODO(e.burkov): Make it generic?
func setProtectedBool(mu *sync.RWMutex, ptr *bool, val bool) {
mu.Lock()
defer mu.Unlock()
*ptr = val
}
// protectedBool gets the value of a boolean pointer under a read lock. l must
// protect the value under ptr.
//
// TODO(e.burkov): Make it generic?
func protectedBool(mu *sync.RWMutex, ptr *bool) (val bool) {
mu.RLock()
defer mu.RUnlock()
return *ptr
}
// handleSafeBrowsingEnable is the handler for the POST
// /control/safebrowsing/enable HTTP API.
func (d *DNSFilter) handleSafeBrowsingEnable(w http.ResponseWriter, r *http.Request) {
setProtectedBool(&d.confLock, &d.Config.SafeBrowsingEnabled, true)
d.Config.ConfigModified()
}
// handleSafeBrowsingDisable is the handler for the POST
// /control/safebrowsing/disable HTTP API.
func (d *DNSFilter) handleSafeBrowsingDisable(w http.ResponseWriter, r *http.Request) {
setProtectedBool(&d.confLock, &d.Config.SafeBrowsingEnabled, false)
d.Config.ConfigModified()
}
// handleSafeBrowsingStatus is the handler for the GET
// /control/safebrowsing/status HTTP API.
func (d *DNSFilter) handleSafeBrowsingStatus(w http.ResponseWriter, r *http.Request) {
resp := &struct {
Enabled bool `json:"enabled"`
}{
Enabled: protectedBool(&d.confLock, &d.Config.SafeBrowsingEnabled),
}
_ = aghhttp.WriteJSONResponse(w, r, resp)
}
// handleParentalEnable is the handler for the POST /control/parental/enable
// HTTP API.
func (d *DNSFilter) handleParentalEnable(w http.ResponseWriter, r *http.Request) {
setProtectedBool(&d.confLock, &d.Config.ParentalEnabled, true)
d.Config.ConfigModified()
}
// handleParentalDisable is the handler for the POST /control/parental/disable
// HTTP API.
func (d *DNSFilter) handleParentalDisable(w http.ResponseWriter, r *http.Request) {
setProtectedBool(&d.confLock, &d.Config.ParentalEnabled, false)
d.Config.ConfigModified()
}
// handleParentalStatus is the handler for the GET /control/parental/status
// HTTP API.
func (d *DNSFilter) handleParentalStatus(w http.ResponseWriter, r *http.Request) {
resp := &struct {
Enabled bool `json:"enabled"`
}{
Enabled: protectedBool(&d.confLock, &d.Config.ParentalEnabled),
}
_ = aghhttp.WriteJSONResponse(w, r, resp)
}
// RegisterFilteringHandlers - register handlers
func (d *DNSFilter) RegisterFilteringHandlers() {
registerHTTP := d.HTTPRegister
@@ -555,7 +480,6 @@ func (d *DNSFilter) RegisterFilteringHandlers() {
registerHTTP(http.MethodGet, "/control/rewrite/list", d.handleRewriteList)
registerHTTP(http.MethodPost, "/control/rewrite/add", d.handleRewriteAdd)
registerHTTP(http.MethodPut, "/control/rewrite/update", d.handleRewriteUpdate)
registerHTTP(http.MethodPost, "/control/rewrite/delete", d.handleRewriteDelete)
registerHTTP(http.MethodGet, "/control/blocked_services/services", d.handleBlockedServicesIDs)

View File

@@ -8,7 +8,6 @@ import (
"testing"
"time"
"github.com/AdguardTeam/golibs/testutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -137,171 +136,3 @@ func TestDNSFilter_handleFilteringSetURL(t *testing.T) {
})
}
}
func TestDNSFilter_handleSafeBrowsingStatus(t *testing.T) {
const (
testTimeout = time.Second
statusURL = "/control/safebrowsing/status"
)
confModCh := make(chan struct{})
filtersDir := t.TempDir()
testCases := []struct {
name string
url string
enabled bool
wantStatus assert.BoolAssertionFunc
}{{
name: "enable_off",
url: "/control/safebrowsing/enable",
enabled: false,
wantStatus: assert.True,
}, {
name: "enable_on",
url: "/control/safebrowsing/enable",
enabled: true,
wantStatus: assert.True,
}, {
name: "disable_on",
url: "/control/safebrowsing/disable",
enabled: true,
wantStatus: assert.False,
}, {
name: "disable_off",
url: "/control/safebrowsing/disable",
enabled: false,
wantStatus: assert.False,
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
handlers := make(map[string]http.Handler)
d, err := New(&Config{
ConfigModified: func() {
testutil.RequireSend(testutil.PanicT{}, confModCh, struct{}{}, testTimeout)
},
DataDir: filtersDir,
HTTPRegister: func(_, url string, handler http.HandlerFunc) {
handlers[url] = handler
},
SafeBrowsingEnabled: tc.enabled,
}, nil)
require.NoError(t, err)
t.Cleanup(d.Close)
d.RegisterFilteringHandlers()
require.NotEmpty(t, handlers)
require.Contains(t, handlers, statusURL)
r := httptest.NewRequest(http.MethodPost, tc.url, nil)
w := httptest.NewRecorder()
go handlers[tc.url].ServeHTTP(w, r)
testutil.RequireReceive(t, confModCh, testTimeout)
r = httptest.NewRequest(http.MethodGet, statusURL, nil)
w = httptest.NewRecorder()
handlers[statusURL].ServeHTTP(w, r)
require.Equal(t, http.StatusOK, w.Code)
status := struct {
Enabled bool `json:"enabled"`
}{
Enabled: false,
}
err = json.NewDecoder(w.Body).Decode(&status)
require.NoError(t, err)
tc.wantStatus(t, status.Enabled)
})
}
}
func TestDNSFilter_handleParentalStatus(t *testing.T) {
const (
testTimeout = time.Second
statusURL = "/control/parental/status"
)
confModCh := make(chan struct{})
filtersDir := t.TempDir()
testCases := []struct {
name string
url string
enabled bool
wantStatus assert.BoolAssertionFunc
}{{
name: "enable_off",
url: "/control/parental/enable",
enabled: false,
wantStatus: assert.True,
}, {
name: "enable_on",
url: "/control/parental/enable",
enabled: true,
wantStatus: assert.True,
}, {
name: "disable_on",
url: "/control/parental/disable",
enabled: true,
wantStatus: assert.False,
}, {
name: "disable_off",
url: "/control/parental/disable",
enabled: false,
wantStatus: assert.False,
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
handlers := make(map[string]http.Handler)
d, err := New(&Config{
ConfigModified: func() {
testutil.RequireSend(testutil.PanicT{}, confModCh, struct{}{}, testTimeout)
},
DataDir: filtersDir,
HTTPRegister: func(_, url string, handler http.HandlerFunc) {
handlers[url] = handler
},
ParentalEnabled: tc.enabled,
}, nil)
require.NoError(t, err)
t.Cleanup(d.Close)
d.RegisterFilteringHandlers()
require.NotEmpty(t, handlers)
require.Contains(t, handlers, statusURL)
r := httptest.NewRequest(http.MethodPost, tc.url, nil)
w := httptest.NewRecorder()
go handlers[tc.url].ServeHTTP(w, r)
testutil.RequireReceive(t, confModCh, testTimeout)
r = httptest.NewRequest(http.MethodGet, statusURL, nil)
w = httptest.NewRecorder()
handlers[statusURL].ServeHTTP(w, r)
require.Equal(t, http.StatusOK, w.Code)
status := struct {
Enabled bool `json:"enabled"`
}{
Enabled: false,
}
err = json.NewDecoder(w.Body).Decode(&status)
require.NoError(t, err)
tc.wantStatus(t, status.Enabled)
})
}
}

View File

@@ -6,7 +6,6 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
"github.com/AdguardTeam/golibs/log"
"golang.org/x/exp/slices"
)
// TODO(d.kolyshev): Use [rewrite.Item] instead.
@@ -92,62 +91,3 @@ func (d *DNSFilter) handleRewriteDelete(w http.ResponseWriter, r *http.Request)
d.Config.ConfigModified()
}
// rewriteUpdateJSON is a struct for JSON object with rewrite rule update info.
type rewriteUpdateJSON struct {
Target rewriteEntryJSON `json:"target"`
Update rewriteEntryJSON `json:"update"`
}
// handleRewriteUpdate is the handler for the PUT /control/rewrite/update HTTP
// API.
func (d *DNSFilter) handleRewriteUpdate(w http.ResponseWriter, r *http.Request) {
updateJSON := rewriteUpdateJSON{}
err := json.NewDecoder(r.Body).Decode(&updateJSON)
if err != nil {
aghhttp.Error(r, w, http.StatusBadRequest, "json.Decode: %s", err)
return
}
rwDel := &LegacyRewrite{
Domain: updateJSON.Target.Domain,
Answer: updateJSON.Target.Answer,
}
rwAdd := &LegacyRewrite{
Domain: updateJSON.Update.Domain,
Answer: updateJSON.Update.Answer,
}
err = rwAdd.normalize()
if err != nil {
// Shouldn't happen currently, since normalize only returns a non-nil
// error when a rewrite is nil, but be change-proof.
aghhttp.Error(r, w, http.StatusBadRequest, "normalizing: %s", err)
return
}
index := -1
defer func() {
if index >= 0 {
d.Config.ConfigModified()
}
}()
d.confLock.Lock()
defer d.confLock.Unlock()
index = slices.IndexFunc(d.Config.Rewrites, rwDel.equal)
if index == -1 {
aghhttp.Error(r, w, http.StatusBadRequest, "target rule not found")
return
}
d.Config.Rewrites = slices.Replace(d.Config.Rewrites, index, index+1, rwAdd)
log.Debug("rewrite: removed element: %s -> %s", rwDel.Domain, rwDel.Answer)
log.Debug("rewrite: added element: %s -> %s", rwAdd.Domain, rwAdd.Answer)
}

View File

@@ -1,237 +0,0 @@
package filtering_test
import (
"bytes"
"encoding/json"
"io"
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/golibs/testutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// TODO(d.kolyshev): Use [rewrite.Item] instead.
type rewriteJSON struct {
Domain string `json:"domain"`
Answer string `json:"answer"`
}
type rewriteUpdateJSON struct {
Target rewriteJSON `json:"target"`
Update rewriteJSON `json:"update"`
}
const (
// testTimeout is the common timeout for tests.
testTimeout = 100 * time.Millisecond
listURL = "/control/rewrite/list"
addURL = "/control/rewrite/add"
deleteURL = "/control/rewrite/delete"
updateURL = "/control/rewrite/update"
decodeErrorMsg = "json.Decode: json: cannot unmarshal string into Go value of type" +
" filtering.rewriteEntryJSON\n"
)
func TestDNSFilter_handleRewriteHTTP(t *testing.T) {
confModCh := make(chan struct{})
reqCh := make(chan struct{})
testRewrites := []*rewriteJSON{
{Domain: "example.local", Answer: "example.rewrite"},
{Domain: "one.local", Answer: "one.rewrite"},
}
testRewritesJSON, mErr := json.Marshal(testRewrites)
require.NoError(t, mErr)
testCases := []struct {
reqData any
name string
url string
method string
wantList []*rewriteJSON
wantBody string
wantConfMod bool
wantStatus int
}{{
name: "list",
url: listURL,
method: http.MethodGet,
reqData: nil,
wantConfMod: false,
wantStatus: http.StatusOK,
wantBody: string(testRewritesJSON) + "\n",
wantList: testRewrites,
}, {
name: "add",
url: addURL,
method: http.MethodPost,
reqData: rewriteJSON{Domain: "add.local", Answer: "add.rewrite"},
wantConfMod: true,
wantStatus: http.StatusOK,
wantBody: "",
wantList: append(
testRewrites,
&rewriteJSON{Domain: "add.local", Answer: "add.rewrite"},
),
}, {
name: "add_error",
url: addURL,
method: http.MethodPost,
reqData: "invalid_json",
wantConfMod: false,
wantStatus: http.StatusBadRequest,
wantBody: decodeErrorMsg,
wantList: testRewrites,
}, {
name: "delete",
url: deleteURL,
method: http.MethodPost,
reqData: rewriteJSON{Domain: "one.local", Answer: "one.rewrite"},
wantConfMod: true,
wantStatus: http.StatusOK,
wantBody: "",
wantList: []*rewriteJSON{{Domain: "example.local", Answer: "example.rewrite"}},
}, {
name: "delete_error",
url: deleteURL,
method: http.MethodPost,
reqData: "invalid_json",
wantConfMod: false,
wantStatus: http.StatusBadRequest,
wantBody: decodeErrorMsg,
wantList: testRewrites,
}, {
name: "update",
url: updateURL,
method: http.MethodPut,
reqData: rewriteUpdateJSON{
Target: rewriteJSON{Domain: "one.local", Answer: "one.rewrite"},
Update: rewriteJSON{Domain: "upd.local", Answer: "upd.rewrite"},
},
wantConfMod: true,
wantStatus: http.StatusOK,
wantBody: "",
wantList: []*rewriteJSON{
{Domain: "example.local", Answer: "example.rewrite"},
{Domain: "upd.local", Answer: "upd.rewrite"},
},
}, {
name: "update_error",
url: updateURL,
method: http.MethodPut,
reqData: "invalid_json",
wantConfMod: false,
wantStatus: http.StatusBadRequest,
wantBody: "json.Decode: json: cannot unmarshal string into Go value of type" +
" filtering.rewriteUpdateJSON\n",
wantList: testRewrites,
}, {
name: "update_error_target",
url: updateURL,
method: http.MethodPut,
reqData: rewriteUpdateJSON{
Target: rewriteJSON{Domain: "inv.local", Answer: "inv.rewrite"},
Update: rewriteJSON{Domain: "upd.local", Answer: "upd.rewrite"},
},
wantConfMod: false,
wantStatus: http.StatusBadRequest,
wantBody: "target rule not found\n",
wantList: testRewrites,
}}
for _, tc := range testCases {
onConfModified := func() {
if !tc.wantConfMod {
panic("config modified has been fired")
}
testutil.RequireSend(testutil.PanicT{}, confModCh, struct{}{}, testTimeout)
}
t.Run(tc.name, func(t *testing.T) {
handlers := make(map[string]http.Handler)
d, err := filtering.New(&filtering.Config{
ConfigModified: onConfModified,
HTTPRegister: func(_, url string, handler http.HandlerFunc) {
handlers[url] = handler
},
Rewrites: rewriteEntriesToLegacyRewrites(testRewrites),
}, nil)
require.NoError(t, err)
t.Cleanup(d.Close)
d.RegisterFilteringHandlers()
require.NotEmpty(t, handlers)
require.Contains(t, handlers, listURL)
require.Contains(t, handlers, tc.url)
var body io.Reader
if tc.reqData != nil {
data, rErr := json.Marshal(tc.reqData)
require.NoError(t, rErr)
body = bytes.NewReader(data)
}
r := httptest.NewRequest(tc.method, tc.url, body)
w := httptest.NewRecorder()
go func() {
handlers[tc.url].ServeHTTP(w, r)
testutil.RequireSend(testutil.PanicT{}, reqCh, struct{}{}, testTimeout)
}()
if tc.wantConfMod {
testutil.RequireReceive(t, confModCh, testTimeout)
}
testutil.RequireReceive(t, reqCh, testTimeout)
assert.Equal(t, tc.wantStatus, w.Code)
respBody, err := io.ReadAll(w.Body)
require.NoError(t, err)
assert.Equal(t, []byte(tc.wantBody), respBody)
assertRewritesList(t, handlers[listURL], tc.wantList)
})
}
}
// assertRewritesList checks if rewrites list equals the list received from the
// handler by listURL.
func assertRewritesList(t *testing.T, handler http.Handler, wantList []*rewriteJSON) {
t.Helper()
r := httptest.NewRequest(http.MethodGet, listURL, nil)
w := httptest.NewRecorder()
handler.ServeHTTP(w, r)
require.Equal(t, http.StatusOK, w.Code)
var actual []*rewriteJSON
err := json.NewDecoder(w.Body).Decode(&actual)
require.NoError(t, err)
assert.Equal(t, wantList, actual)
}
// rewriteEntriesToLegacyRewrites gets legacy rewrites from json entries.
func rewriteEntriesToLegacyRewrites(entries []*rewriteJSON) (rw []*filtering.LegacyRewrite) {
for _, entry := range entries {
rw = append(rw, &filtering.LegacyRewrite{
Domain: entry.Domain,
Answer: entry.Answer,
})
}
return rw
}

View File

@@ -0,0 +1,433 @@
package filtering
import (
"bytes"
"crypto/sha256"
"encoding/binary"
"encoding/hex"
"fmt"
"net"
"net/http"
"strings"
"sync"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
"github.com/AdguardTeam/dnsproxy/upstream"
"github.com/AdguardTeam/golibs/cache"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/stringutil"
"github.com/miekg/dns"
"golang.org/x/exp/slices"
"golang.org/x/net/publicsuffix"
)
// Safe browsing and parental control methods.
// TODO(a.garipov): Make configurable.
const (
dnsTimeout = 3 * time.Second
defaultSafebrowsingServer = `https://family.adguard-dns.com/dns-query`
defaultParentalServer = `https://family.adguard-dns.com/dns-query`
sbTXTSuffix = `sb.dns.adguard.com.`
pcTXTSuffix = `pc.dns.adguard.com.`
)
// SetParentalUpstream sets the parental upstream for *DNSFilter.
//
// TODO(e.burkov): Remove this in v1 API to forbid the direct access.
func (d *DNSFilter) SetParentalUpstream(u upstream.Upstream) {
d.parentalUpstream = u
}
// SetSafeBrowsingUpstream sets the safe browsing upstream for *DNSFilter.
//
// TODO(e.burkov): Remove this in v1 API to forbid the direct access.
func (d *DNSFilter) SetSafeBrowsingUpstream(u upstream.Upstream) {
d.safeBrowsingUpstream = u
}
func (d *DNSFilter) initSecurityServices() error {
var err error
d.safeBrowsingServer = defaultSafebrowsingServer
d.parentalServer = defaultParentalServer
opts := &upstream.Options{
Timeout: dnsTimeout,
ServerIPAddrs: []net.IP{
{94, 140, 14, 15},
{94, 140, 15, 16},
net.ParseIP("2a10:50c0::bad1:ff"),
net.ParseIP("2a10:50c0::bad2:ff"),
},
}
parUps, err := upstream.AddressToUpstream(d.parentalServer, opts)
if err != nil {
return fmt.Errorf("converting parental server: %w", err)
}
d.SetParentalUpstream(parUps)
sbUps, err := upstream.AddressToUpstream(d.safeBrowsingServer, opts)
if err != nil {
return fmt.Errorf("converting safe browsing server: %w", err)
}
d.SetSafeBrowsingUpstream(sbUps)
return nil
}
/*
expire byte[4]
hash byte[32]
...
*/
func (c *sbCtx) setCache(prefix, hashes []byte) {
d := make([]byte, 4+len(hashes))
expire := uint(time.Now().Unix()) + c.cacheTime*60
binary.BigEndian.PutUint32(d[:4], uint32(expire))
copy(d[4:], hashes)
c.cache.Set(prefix, d)
log.Debug("%s: stored in cache: %v", c.svc, prefix)
}
// findInHash returns 32-byte hash if it's found in hashToHost.
func (c *sbCtx) findInHash(val []byte) (hash32 [32]byte, found bool) {
for i := 4; i < len(val); i += 32 {
hash := val[i : i+32]
copy(hash32[:], hash[0:32])
_, found = c.hashToHost[hash32]
if found {
return hash32, found
}
}
return [32]byte{}, false
}
func (c *sbCtx) getCached() int {
now := time.Now().Unix()
hashesToRequest := map[[32]byte]string{}
for k, v := range c.hashToHost {
// nolint:looppointer // The subsilce is used for a safe cache lookup.
val := c.cache.Get(k[0:2])
if val == nil || now >= int64(binary.BigEndian.Uint32(val)) {
hashesToRequest[k] = v
continue
}
if hash32, found := c.findInHash(val); found {
log.Debug("%s: found in cache: %s: blocked by %v", c.svc, c.host, hash32)
return 1
}
}
if len(hashesToRequest) == 0 {
log.Debug("%s: found in cache: %s: not blocked", c.svc, c.host)
return -1
}
c.hashToHost = hashesToRequest
return 0
}
type sbCtx struct {
host string
svc string
hashToHost map[[32]byte]string
cache cache.Cache
cacheTime uint
}
func hostnameToHashes(host string) map[[32]byte]string {
hashes := map[[32]byte]string{}
tld, icann := publicsuffix.PublicSuffix(host)
if !icann {
// private suffixes like cloudfront.net
tld = ""
}
curhost := host
nDots := 0
for i := len(curhost) - 1; i >= 0; i-- {
if curhost[i] == '.' {
nDots++
if nDots == 4 {
curhost = curhost[i+1:] // "xxx.a.b.c.d" -> "a.b.c.d"
break
}
}
}
for {
if curhost == "" {
// we've reached end of string
break
}
if tld != "" && curhost == tld {
// we've reached the TLD, don't hash it
break
}
sum := sha256.Sum256([]byte(curhost))
hashes[sum] = curhost
pos := strings.IndexByte(curhost, byte('.'))
if pos < 0 {
break
}
curhost = curhost[pos+1:]
}
return hashes
}
// convert hash array to string
func (c *sbCtx) getQuestion() string {
b := &strings.Builder{}
for hash := range c.hashToHost {
// nolint:looppointer // The subsilce is used for safe hex encoding.
stringutil.WriteToBuilder(b, hex.EncodeToString(hash[0:2]), ".")
}
if c.svc == "SafeBrowsing" {
stringutil.WriteToBuilder(b, sbTXTSuffix)
return b.String()
}
stringutil.WriteToBuilder(b, pcTXTSuffix)
return b.String()
}
// Find the target hash in TXT response
func (c *sbCtx) processTXT(resp *dns.Msg) (bool, [][]byte) {
matched := false
hashes := [][]byte{}
for _, a := range resp.Answer {
txt, ok := a.(*dns.TXT)
if !ok {
continue
}
log.Debug("%s: received hashes for %s: %v", c.svc, c.host, txt.Txt)
for _, t := range txt.Txt {
if len(t) != 32*2 {
continue
}
hash, err := hex.DecodeString(t)
if err != nil {
continue
}
hashes = append(hashes, hash)
if !matched {
var hash32 [32]byte
copy(hash32[:], hash)
var hashHost string
hashHost, ok = c.hashToHost[hash32]
if ok {
log.Debug("%s: matched %s by %s/%s", c.svc, c.host, hashHost, t)
matched = true
}
}
}
}
return matched, hashes
}
func (c *sbCtx) storeCache(hashes [][]byte) {
slices.SortFunc(hashes, func(a, b []byte) (sortsBefore bool) {
return bytes.Compare(a, b) == -1
})
var curData []byte
var prevPrefix []byte
for i, hash := range hashes {
// nolint:looppointer // The subsilce is used for a safe comparison.
if !bytes.Equal(hash[0:2], prevPrefix) {
if i != 0 {
c.setCache(prevPrefix, curData)
curData = nil
}
prevPrefix = hashes[i][0:2]
}
curData = append(curData, hash...)
}
if len(prevPrefix) != 0 {
c.setCache(prevPrefix, curData)
}
for hash := range c.hashToHost {
// nolint:looppointer // The subsilce is used for a safe cache lookup.
prefix := hash[0:2]
val := c.cache.Get(prefix)
if val == nil {
c.setCache(prefix, nil)
}
}
}
func check(c *sbCtx, r Result, u upstream.Upstream) (Result, error) {
c.hashToHost = hostnameToHashes(c.host)
switch c.getCached() {
case -1:
return Result{}, nil
case 1:
return r, nil
}
question := c.getQuestion()
log.Tracef("%s: checking %s: %s", c.svc, c.host, question)
req := (&dns.Msg{}).SetQuestion(question, dns.TypeTXT)
resp, err := u.Exchange(req)
if err != nil {
return Result{}, err
}
matched, receivedHashes := c.processTXT(resp)
c.storeCache(receivedHashes)
if matched {
return r, nil
}
return Result{}, nil
}
// TODO(a.garipov): Unify with checkParental.
func (d *DNSFilter) checkSafeBrowsing(
host string,
_ uint16,
setts *Settings,
) (res Result, err error) {
if !setts.ProtectionEnabled || !setts.SafeBrowsingEnabled {
return Result{}, nil
}
if log.GetLevel() >= log.DEBUG {
timer := log.StartTimer()
defer timer.LogElapsed("safebrowsing lookup for %q", host)
}
sctx := &sbCtx{
host: host,
svc: "SafeBrowsing",
cache: d.safebrowsingCache,
cacheTime: d.Config.CacheTime,
}
res = Result{
Rules: []*ResultRule{{
Text: "adguard-malware-shavar",
FilterListID: SafeBrowsingListID,
}},
Reason: FilteredSafeBrowsing,
IsFiltered: true,
}
return check(sctx, res, d.safeBrowsingUpstream)
}
// TODO(a.garipov): Unify with checkSafeBrowsing.
func (d *DNSFilter) checkParental(
host string,
_ uint16,
setts *Settings,
) (res Result, err error) {
if !setts.ProtectionEnabled || !setts.ParentalEnabled {
return Result{}, nil
}
if log.GetLevel() >= log.DEBUG {
timer := log.StartTimer()
defer timer.LogElapsed("parental lookup for %q", host)
}
sctx := &sbCtx{
host: host,
svc: "Parental",
cache: d.parentalCache,
cacheTime: d.Config.CacheTime,
}
res = Result{
Rules: []*ResultRule{{
Text: "parental CATEGORY_BLACKLISTED",
FilterListID: ParentalListID,
}},
Reason: FilteredParental,
IsFiltered: true,
}
return check(sctx, res, d.parentalUpstream)
}
// setProtectedBool sets the value of a boolean pointer under a lock. l must
// protect the value under ptr.
//
// TODO(e.burkov): Make it generic?
func setProtectedBool(mu *sync.RWMutex, ptr *bool, val bool) {
mu.Lock()
defer mu.Unlock()
*ptr = val
}
// protectedBool gets the value of a boolean pointer under a read lock. l must
// protect the value under ptr.
//
// TODO(e.burkov): Make it generic?
func protectedBool(mu *sync.RWMutex, ptr *bool) (val bool) {
mu.RLock()
defer mu.RUnlock()
return *ptr
}
func (d *DNSFilter) handleSafeBrowsingEnable(w http.ResponseWriter, r *http.Request) {
setProtectedBool(&d.confLock, &d.Config.SafeBrowsingEnabled, true)
d.Config.ConfigModified()
}
func (d *DNSFilter) handleSafeBrowsingDisable(w http.ResponseWriter, r *http.Request) {
setProtectedBool(&d.confLock, &d.Config.SafeBrowsingEnabled, false)
d.Config.ConfigModified()
}
func (d *DNSFilter) handleSafeBrowsingStatus(w http.ResponseWriter, r *http.Request) {
resp := &struct {
Enabled bool `json:"enabled"`
}{
Enabled: protectedBool(&d.confLock, &d.Config.SafeBrowsingEnabled),
}
_ = aghhttp.WriteJSONResponse(w, r, resp)
}
func (d *DNSFilter) handleParentalEnable(w http.ResponseWriter, r *http.Request) {
setProtectedBool(&d.confLock, &d.Config.ParentalEnabled, true)
d.Config.ConfigModified()
}
func (d *DNSFilter) handleParentalDisable(w http.ResponseWriter, r *http.Request) {
setProtectedBool(&d.confLock, &d.Config.ParentalEnabled, false)
d.Config.ConfigModified()
}
func (d *DNSFilter) handleParentalStatus(w http.ResponseWriter, r *http.Request) {
resp := &struct {
Enabled bool `json:"enabled"`
}{
Enabled: protectedBool(&d.confLock, &d.Config.ParentalEnabled),
}
_ = aghhttp.WriteJSONResponse(w, r, resp)
}

View File

@@ -0,0 +1,226 @@
package filtering
import (
"crypto/sha256"
"strings"
"testing"
"github.com/AdguardTeam/AdGuardHome/internal/aghtest"
"github.com/AdguardTeam/golibs/cache"
"github.com/miekg/dns"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestSafeBrowsingHash(t *testing.T) {
// test hostnameToHashes()
hashes := hostnameToHashes("1.2.3.sub.host.com")
assert.Len(t, hashes, 3)
_, ok := hashes[sha256.Sum256([]byte("3.sub.host.com"))]
assert.True(t, ok)
_, ok = hashes[sha256.Sum256([]byte("sub.host.com"))]
assert.True(t, ok)
_, ok = hashes[sha256.Sum256([]byte("host.com"))]
assert.True(t, ok)
_, ok = hashes[sha256.Sum256([]byte("com"))]
assert.False(t, ok)
c := &sbCtx{
svc: "SafeBrowsing",
hashToHost: hashes,
}
q := c.getQuestion()
assert.Contains(t, q, "7a1b.")
assert.Contains(t, q, "af5a.")
assert.Contains(t, q, "eb11.")
assert.True(t, strings.HasSuffix(q, "sb.dns.adguard.com."))
}
func TestSafeBrowsingCache(t *testing.T) {
c := &sbCtx{
svc: "SafeBrowsing",
cacheTime: 100,
}
conf := cache.Config{}
c.cache = cache.New(conf)
// store in cache hashes for "3.sub.host.com" and "host.com"
// and empty data for hash-prefix for "sub.host.com"
hash := sha256.Sum256([]byte("sub.host.com"))
c.hashToHost = make(map[[32]byte]string)
c.hashToHost[hash] = "sub.host.com"
var hashesArray [][]byte
hash4 := sha256.Sum256([]byte("3.sub.host.com"))
hashesArray = append(hashesArray, hash4[:])
hash2 := sha256.Sum256([]byte("host.com"))
hashesArray = append(hashesArray, hash2[:])
c.storeCache(hashesArray)
// match "3.sub.host.com" or "host.com" from cache
c.hashToHost = make(map[[32]byte]string)
hash = sha256.Sum256([]byte("3.sub.host.com"))
c.hashToHost[hash] = "3.sub.host.com"
hash = sha256.Sum256([]byte("sub.host.com"))
c.hashToHost[hash] = "sub.host.com"
hash = sha256.Sum256([]byte("host.com"))
c.hashToHost[hash] = "host.com"
assert.Equal(t, 1, c.getCached())
// match "sub.host.com" from cache
c.hashToHost = make(map[[32]byte]string)
hash = sha256.Sum256([]byte("sub.host.com"))
c.hashToHost[hash] = "sub.host.com"
assert.Equal(t, -1, c.getCached())
// Match "sub.host.com" from cache. Another hash for "host.example" is not
// in the cache, so get data for it from the server.
c.hashToHost = make(map[[32]byte]string)
hash = sha256.Sum256([]byte("sub.host.com"))
c.hashToHost[hash] = "sub.host.com"
hash = sha256.Sum256([]byte("host.example"))
c.hashToHost[hash] = "host.example"
assert.Empty(t, c.getCached())
hash = sha256.Sum256([]byte("sub.host.com"))
_, ok := c.hashToHost[hash]
assert.False(t, ok)
hash = sha256.Sum256([]byte("host.example"))
_, ok = c.hashToHost[hash]
assert.True(t, ok)
c = &sbCtx{
svc: "SafeBrowsing",
cacheTime: 100,
}
conf = cache.Config{}
c.cache = cache.New(conf)
hash = sha256.Sum256([]byte("sub.host.com"))
c.hashToHost = make(map[[32]byte]string)
c.hashToHost[hash] = "sub.host.com"
c.cache.Set(hash[0:2], make([]byte, 32))
assert.Empty(t, c.getCached())
}
func TestSBPC_checkErrorUpstream(t *testing.T) {
d, _ := newForTest(t, &Config{SafeBrowsingEnabled: true}, nil)
t.Cleanup(d.Close)
ups := aghtest.NewErrorUpstream()
d.SetSafeBrowsingUpstream(ups)
d.SetParentalUpstream(ups)
setts := &Settings{
ProtectionEnabled: true,
SafeBrowsingEnabled: true,
ParentalEnabled: true,
}
_, err := d.checkSafeBrowsing("smthng.com", dns.TypeA, setts)
assert.Error(t, err)
_, err = d.checkParental("smthng.com", dns.TypeA, setts)
assert.Error(t, err)
}
func TestSBPC(t *testing.T) {
d, _ := newForTest(t, &Config{SafeBrowsingEnabled: true}, nil)
t.Cleanup(d.Close)
const hostname = "example.org"
setts := &Settings{
ProtectionEnabled: true,
SafeBrowsingEnabled: true,
ParentalEnabled: true,
}
testCases := []struct {
testCache cache.Cache
testFunc func(host string, _ uint16, _ *Settings) (res Result, err error)
name string
block bool
}{{
testCache: d.safebrowsingCache,
testFunc: d.checkSafeBrowsing,
name: "sb_no_block",
block: false,
}, {
testCache: d.safebrowsingCache,
testFunc: d.checkSafeBrowsing,
name: "sb_block",
block: true,
}, {
testCache: d.parentalCache,
testFunc: d.checkParental,
name: "pc_no_block",
block: false,
}, {
testCache: d.parentalCache,
testFunc: d.checkParental,
name: "pc_block",
block: true,
}}
for _, tc := range testCases {
// Prepare the upstream.
ups := aghtest.NewBlockUpstream(hostname, tc.block)
var numReq int
onExchange := ups.OnExchange
ups.OnExchange = func(req *dns.Msg) (resp *dns.Msg, err error) {
numReq++
return onExchange(req)
}
d.SetSafeBrowsingUpstream(ups)
d.SetParentalUpstream(ups)
t.Run(tc.name, func(t *testing.T) {
// Firstly, check the request blocking.
hits := 0
res, err := tc.testFunc(hostname, dns.TypeA, setts)
require.NoError(t, err)
if tc.block {
assert.True(t, res.IsFiltered)
require.Len(t, res.Rules, 1)
hits++
} else {
require.False(t, res.IsFiltered)
}
// Check the cache state, check the response is now cached.
assert.Equal(t, 1, tc.testCache.Stats().Count)
assert.Equal(t, hits, tc.testCache.Stats().Hit)
// There was one request to an upstream.
assert.Equal(t, 1, numReq)
// Now make the same request to check the cache was used.
res, err = tc.testFunc(hostname, dns.TypeA, setts)
require.NoError(t, err)
if tc.block {
assert.True(t, res.IsFiltered)
require.Len(t, res.Rules, 1)
} else {
require.False(t, res.IsFiltered)
}
// Check the cache state, it should've been used.
assert.Equal(t, 1, tc.testCache.Stats().Count)
assert.Equal(t, hits+1, tc.testCache.Stats().Hit)
// Check that there were no additional requests.
assert.Equal(t, 1, numReq)
})
purgeCaches(d)
}
}

View File

@@ -12,14 +12,6 @@ type blockedService struct {
// blockedServices contains raw blocked service data.
var blockedServices = []blockedService{{
ID: "500px",
Name: "500px",
IconSVG: []byte("<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" viewBox=\"0 0 50 50\"><path d=\"M 5 14 L 2.5 26 L 6.800781 26 C 6.800781 26 7.699219 24.300781 10.199219 24.300781 C 12.699219 24.300781 14 26.199219 14 28.300781 C 14 30.402344 12.5 32.800781 10.199219 32.800781 C 7.898438 32.800781 6.5 30.398438 6.5 29 L 2 29 C 2 30.199219 3 36 10.199219 36 C 15.15625 36 17.417969 33.121094 18.015625 31.898438 C 19.386719 34.34375 21.992188 36 24.984375 36 C 27.253906 36 29.777344 34.808594 32.5 32.453125 C 35.222656 34.808594 37.746094 36 40.015625 36 C 44.417969 36 48 32.410156 48 28 C 48 23.589844 44.417969 20 40.015625 20 C 37.746094 20 35.222656 21.191406 32.5 23.546875 C 29.777344 21.191406 27.253906 20 24.984375 20 C 21.832031 20 19.105469 21.847656 17.8125 24.511719 C 17.113281 23.382813 15.414063 21 11.902344 21 C 8.101563 21 7.300781 22.597656 7.300781 22.597656 C 7.300781 22.597656 7.699219 21.300781 8.300781 18 L 17 18 L 17 14 Z M 24.984375 25 C 25.453125 25 26.800781 25.226563 29.230469 27.328125 L 30.011719 28 L 29.230469 28.671875 C 26.800781 30.773438 25.453125 31 24.984375 31 C 23.339844 31 22 29.652344 22 28 C 22 26.347656 23.339844 25 24.984375 25 Z M 40.015625 25 C 41.660156 25 43 26.347656 43 28 C 43 29.652344 41.660156 31 40.015625 31 C 39.546875 31 38.199219 30.773438 35.769531 28.671875 L 34.988281 28 L 35.769531 27.328125 C 38.199219 25.226563 39.546875 25 40.015625 25 Z\"/></svg>"),
Rules: []string{
"||500px.com^",
"||500px.org^",
},
}, {
ID: "9gag",
Name: "9GAG",
IconSVG: []byte("<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" viewBox=\"0 0 50 50\"><path d=\"M 44 14 C 44 13.644531 43.8125 13.316406 43.507813 13.136719 C 40.453125 11.347656 28.46875 4.847656 25.535156 3.136719 C 25.222656 2.957031 24.839844 2.957031 24.527344 3.136719 C 21.128906 5.117188 10.089844 11.621094 7.496094 13.136719 C 7.1875 13.316406 7 13.644531 7 14 L 7 20 C 7 20.378906 7.214844 20.722656 7.550781 20.894531 C 7.660156 20.949219 18.597656 26.453125 24.5 29.867188 C 24.8125 30.046875 25.195313 30.046875 25.507813 29.863281 C 27.269531 28.828125 29.117188 27.859375 30.902344 26.921875 C 32.253906 26.214844 33.636719 25.488281 35.003906 24.722656 C 35.007813 26.820313 35.003906 29.296875 35 30.40625 L 25 35.859375 L 14.480469 30.121094 C 14.144531 29.9375 13.730469 29.964844 13.417969 30.1875 L 6.417969 35.1875 C 6.140625 35.386719 5.980469 35.714844 6.003906 36.054688 C 6.023438 36.398438 6.214844 36.707031 6.515625 36.871094 L 24.542969 46.871094 C 24.695313 46.957031 24.859375 47 25.027344 47 C 25.195313 47 25.363281 46.957031 25.515625 46.875 L 43.484375 36.875 C 43.804688 36.695313 44 36.363281 44 36 C 44 36 43.992188 21.011719 44 14 Z M 25 20 L 18 16 L 25 12 L 32 16 Z\" /></svg>"),
@@ -1188,18 +1180,6 @@ var blockedServices = []blockedService{{
"||zuckerberg.com^",
"||zuckerberg.net^",
},
}, {
ID: "flickr",
Name: "Flickr",
IconSVG: []byte("<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" viewBox=\"0 0 50 50\"><path d=\"M 9 4 C 6.2504839 4 4 6.2504839 4 9 L 4 41 C 4 43.749516 6.2504839 46 9 46 L 41 46 C 43.749516 46 46 43.749516 46 41 L 46 9 C 46 6.2504839 43.749516 4 41 4 L 9 4 z M 9 6 L 41 6 C 42.668484 6 44 7.3315161 44 9 L 44 41 C 44 42.668484 42.668484 44 41 44 L 9 44 C 7.3315161 44 6 42.668484 6 41 L 6 9 C 6 7.3315161 7.3315161 6 9 6 z M 16 17 C 11.59 17 8 20.59 8 25 C 8 29.41 11.59 33 16 33 C 20.41 33 24 29.41 24 25 C 24 20.59 20.41 17 16 17 z M 34 17 C 29.59 17 26 20.59 26 25 C 26 29.41 29.59 33 34 33 C 38.41 33 42 29.41 42 25 C 42 20.59 38.41 17 34 17 z\"/></svg>"),
Rules: []string{
"||flic.kr^",
"||flickr.com^",
"||flickr.net^",
"||flickrprints.com^",
"||flickrpro.com^",
"||staticflickr.com^",
},
}, {
ID: "gog",
Name: "GOG",
@@ -1345,13 +1325,6 @@ var blockedServices = []blockedService{{
"||kakao.com^",
"||kgslb.com^",
},
}, {
ID: "kik",
Name: "Kik",
IconSVG: []byte("<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" viewBox=\"0 0 50 50\"><path d=\"M 3.5039062 12 C 1.9347705 11.994817 0.87857579 12.97636 0.4453125 13.849609 C 0.01204921 14.722858 0 15.564453 0 15.564453 A 1.0001 1.0001 0 0 0 0 15.59375 L 0 35 A 1.0001 1.0001 0 0 0 0.00390625 35.078125 C 0.00390625 35.078125 0.05696144 35.828363 0.5390625 36.554688 C 1.0211636 37.281011 2.0459252 38.004441 3.5019531 38.001953 C 4.8916439 38.000053 5.8837351 37.273604 6.3769531 36.578125 C 6.8701712 35.882646 6.9863281 35.166016 6.9863281 35.166016 A 1.0001 1.0001 0 0 0 7 35 L 7 31.802734 L 10.167969 36.554688 L 10.130859 36.494141 C 10.511831 37.164615 11.143097 37.525465 11.742188 37.730469 C 12.341278 37.935473 12.950104 38.001953 13.5 38.001953 C 15.411725 38.001953 17 36.431487 17 34.5 C 17 34.056649 16.90825 34.03442 16.851562 33.912109 C 16.794882 33.789799 16.730864 33.671331 16.654297 33.537109 C 16.501163 33.268666 16.298339 32.944015 16.058594 32.572266 C 15.579103 31.828767 14.950355 30.90254 14.322266 29.992188 C 13.310206 28.525308 12.655222 27.610988 12.300781 27.113281 L 14.707031 24.707031 A 1.0001 1.0001 0 0 0 14.738281 24.673828 C 14.738281 24.673828 15.354706 24.012223 15.748047 23.042969 C 16.141388 22.073714 16.298687 20.56089 15.259766 19.349609 C 14.281705 18.208994 12.842689 18.141009 11.925781 18.416016 C 11.008874 18.691022 10.371094 19.222656 10.371094 19.222656 A 1.0001 1.0001 0 0 0 10.292969 19.292969 L 6.9980469 22.587891 L 6.9921875 15.646484 A 1.0001 1.0001 0 0 0 6.9902344 15.580078 C 6.9902344 15.580078 6.9441634 14.743069 6.5058594 13.875 C 6.0675579 13.006938 5.0412971 12.005313 3.5039062 12 z M 30.503906 12 C 28.93477 11.9948 27.878577 12.97636 27.445312 13.849609 C 27.012049 14.722858 27 15.564453 27 15.564453 A 1.0001 1.0001 0 0 0 27 15.59375 L 27 35 A 1.0001 1.0001 0 0 0 27.003906 35.078125 C 27.003906 35.078125 27.056966 35.828363 27.539062 36.554688 C 28.021165 37.281011 29.045925 38.004441 30.501953 38.001953 C 31.891644 38.000053 32.883735 37.273604 33.376953 36.578125 C 33.870171 35.882646 33.986328 35.166016 33.986328 35.166016 A 1.0001 1.0001 0 0 0 34 35 L 34 31.802734 L 37.167969 36.554688 L 37.130859 36.494141 C 37.511831 37.164615 38.143096 37.525465 38.742188 37.730469 C 39.341277 37.935473 39.950104 38.001953 40.5 38.001953 C 42.411725 38.001953 44 36.431487 44 34.5 C 44 34.056649 43.908251 34.03442 43.851562 33.912109 C 43.794882 33.789799 43.730864 33.671331 43.654297 33.537109 C 43.501163 33.268666 43.298339 32.944015 43.058594 32.572266 C 42.579103 31.828767 41.950355 30.90254 41.322266 29.992188 C 40.310206 28.525308 39.655222 27.610988 39.300781 27.113281 L 41.707031 24.707031 A 1.0001 1.0001 0 0 0 41.738281 24.673828 C 41.738281 24.673828 42.354706 24.012223 42.748047 23.042969 C 43.141388 22.073714 43.298687 20.56089 42.259766 19.349609 C 41.281705 18.208994 39.842689 18.141009 38.925781 18.416016 C 38.008874 18.691022 37.371094 19.222656 37.371094 19.222656 A 1.0001 1.0001 0 0 0 37.292969 19.292969 L 33.998047 22.587891 L 33.992188 15.646484 A 1.0001 1.0001 0 0 0 33.990234 15.580078 C 33.990234 15.580078 33.944164 14.743069 33.505859 13.875 C 33.067647 13.006938 32.041297 12.005313 30.503906 12 z M 21.507812 18 C 19.85324 17.98686 18.785557 19.124468 18.382812 20.09375 C 18.181441 20.578391 18.090615 21.031738 18.044922 21.375 C 18.022072 21.546631 18.011459 21.69063 18.005859 21.796875 C 18.000252 21.90312 18 22.065333 18 21.984375 L 17.982422 34.998047 A 1.0001 1.0001 0 0 0 17.990234 35.134766 C 17.990234 35.134766 18.085674 35.862804 18.576172 36.568359 C 19.06667 37.273915 20.071581 37.997467 21.486328 38 C 22.885358 38.0026 23.885897 37.278643 24.380859 36.580078 C 24.875822 35.881513 24.986328 35.160156 24.986328 35.160156 A 1.0001 1.0001 0 0 0 25 35 L 25 21.996094 C 25 21.996094 25.02572 21.084043 24.625 20.117188 C 24.224283 19.150332 23.164841 18.013078 21.507812 18 z M 46.5 24 C 44.578848 24 43 25.578848 43 27.5 C 43 29.421152 44.578848 31 46.5 31 C 48.421152 31 50 29.421152 50 27.5 C 50 25.578848 48.421152 24 46.5 24 z M 46.5 26 C 47.340272 26 48 26.659728 48 27.5 C 48 28.340272 47.340272 29 46.5 29 C 45.659728 29 45 28.340272 45 27.5 C 45 26.659728 45.659728 26 46.5 26 z\"/></svg>"),
Rules: []string{
"||kik.com^",
},
}, {
ID: "lazada",
Name: "Lazada",
@@ -1412,7 +1385,6 @@ var blockedServices = []blockedService{{
Rules: []string{
"||aus.social^",
"||awscommunity.social^",
"||climatejustice.social^",
"||cyberplace.social^",
"||defcon.social^",
"||det.social^",
@@ -1470,13 +1442,13 @@ var blockedServices = []blockedService{{
"||mstdn.plus^",
"||mstdn.social^",
"||muenchen.social^",
"||muenster.im^",
"||newsie.social^",
"||noc.social^",
"||norden.social^",
"||nrw.social^",
"||o3o.ca^",
"||ohai.social^",
"||pewtix.com^",
"||piaille.fr^",
"||pol.social^",
"||ravenation.club^",
@@ -1508,6 +1480,7 @@ var blockedServices = []blockedService{{
"||union.place^",
"||universeodon.com^",
"||urbanists.social^",
"||wien.rocks^",
"||wxw.moe^",
},
}, {
@@ -1854,13 +1827,6 @@ var blockedServices = []blockedService{{
"||tx.me^",
"||usercontent.dev^",
},
}, {
ID: "tidal",
Name: "Tidal",
IconSVG: []byte("<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" viewBox=\"0 0 50 50\"><path d=\"M 9 12 C 8.7615 12 8.5237969 12.091437 8.3417969 12.273438 L 1.2734375 19.341797 C 0.9094375 19.705797 0.9094375 20.294203 1.2734375 20.658203 L 8.3417969 27.726562 C 8.7057969 28.090563 9.2942031 28.090563 9.6582031 27.726562 L 16.726562 20.658203 C 16.908563 20.476203 17 20.2385 17 20 C 17 19.7615 16.908563 19.523797 16.726562 19.341797 L 9.6582031 12.273438 C 9.4762031 12.091437 9.2385 12 9 12 z M 17 20 C 17 20.2385 17.091438 20.476203 17.273438 20.658203 L 24.341797 27.726562 C 24.523797 27.908563 24.7615 28 25 28 C 25.2385 28 25.476203 27.908563 25.658203 27.726562 L 32.726562 20.658203 C 32.908563 20.476203 33 20.2385 33 20 C 33 19.7615 32.908563 19.523797 32.726562 19.341797 L 25.658203 12.273438 C 25.294203 11.909437 24.705797 11.909437 24.341797 12.273438 L 17.273438 19.341797 C 17.091437 19.523797 17 19.7615 17 20 z M 33 20 C 33 20.2385 33.091437 20.476203 33.273438 20.658203 L 40.341797 27.726562 C 40.705797 28.090563 41.294203 28.090563 41.658203 27.726562 L 48.726562 20.658203 C 49.090563 20.294203 49.090563 19.705797 48.726562 19.341797 L 41.658203 12.273438 C 41.294203 11.909437 40.705797 11.909437 40.341797 12.273438 L 33.273438 19.341797 C 33.091437 19.523797 33 19.7615 33 20 z M 25 28 C 24.7615 28 24.523797 28.091437 24.341797 28.273438 L 17.273438 35.341797 C 16.909437 35.705797 16.909437 36.294203 17.273438 36.658203 L 24.341797 43.726562 C 24.705797 44.090562 25.294203 44.090562 25.658203 43.726562 L 32.726562 36.658203 C 33.090563 36.294203 33.090563 35.705797 32.726562 35.341797 L 25.658203 28.273438 C 25.476203 28.091437 25.2385 28 25 28 z\"/></svg>"),
Rules: []string{
"||tidal.com^",
},
}, {
ID: "tiktok",
Name: "TikTok",

View File

@@ -8,7 +8,6 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/AdGuardHome/internal/filtering/safesearch"
"github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/golibs/stringutil"
)
// Client contains information about persistent clients.
@@ -38,19 +37,6 @@ type Client struct {
IgnoreStatistics bool
}
// ShallowClone returns a deep copy of the client, except upstreamConfig,
// safeSearchConf, SafeSearch fields, because it's difficult to copy them.
func (c *Client) ShallowClone() (sh *Client) {
clone := *c
clone.IDs = stringutil.CloneSlice(c.IDs)
clone.Tags = stringutil.CloneSlice(c.Tags)
clone.BlockedServices = stringutil.CloneSlice(c.BlockedServices)
clone.Upstreams = stringutil.CloneSlice(c.Upstreams)
return &clone
}
// closeUpstreams closes the client-specific upstream config of c if any.
func (c *Client) closeUpstreams() (err error) {
if c.upstreamConfig != nil {

View File

@@ -378,7 +378,6 @@ func (clients *clientsContainer) clientOrArtificial(
}, true
}
// Find returns a shallow copy of the client if there is one found.
func (clients *clientsContainer) Find(id string) (c *Client, ok bool) {
clients.lock.Lock()
defer clients.lock.Unlock()
@@ -388,18 +387,20 @@ func (clients *clientsContainer) Find(id string) (c *Client, ok bool) {
return nil, false
}
return c.ShallowClone(), true
c.IDs = stringutil.CloneSlice(c.IDs)
c.Tags = stringutil.CloneSlice(c.Tags)
c.BlockedServices = stringutil.CloneSlice(c.BlockedServices)
c.Upstreams = stringutil.CloneSlice(c.Upstreams)
return c, true
}
// shouldCountClient is a wrapper around Find to make it a valid client
// information finder for the statistics. If no information about the client
// is found, it returns true.
func (clients *clientsContainer) shouldCountClient(ids []string) (y bool) {
clients.lock.Lock()
defer clients.lock.Unlock()
for _, id := range ids {
client, ok := clients.findLocked(id)
client, ok := clients.Find(id)
if ok {
return !client.IgnoreStatistics
}
@@ -616,15 +617,6 @@ func (clients *clientsContainer) Add(c *Client) (ok bool, err error) {
}
}
clients.add(c)
log.Debug("clients: added %q: ID:%q [%d]", c.Name, c.IDs, len(clients.list))
return true, nil
}
// add c to the indexes. clients.lock is expected to be locked.
func (clients *clientsContainer) add(c *Client) {
// update Name index
clients.list[c.Name] = c
@@ -632,6 +624,10 @@ func (clients *clientsContainer) add(c *Client) {
for _, id := range c.IDs {
clients.idIndex[id] = c
}
log.Debug("clients: added %q: ID:%q [%d]", c.Name, c.IDs, len(clients.list))
return true, nil
}
// Del removes a client. ok is false if there is no such client.
@@ -649,53 +645,86 @@ func (clients *clientsContainer) Del(name string) (ok bool) {
log.Error("client container: removing client %s: %s", name, err)
}
clients.del(c)
return true
}
// del removes c from the indexes. clients.lock is expected to be locked.
func (clients *clientsContainer) del(c *Client) {
// update Name index
delete(clients.list, c.Name)
delete(clients.list, name)
// update ID index
for _, id := range c.IDs {
delete(clients.idIndex, id)
}
return true
}
// Update updates a client by its name.
func (clients *clientsContainer) Update(prev, c *Client) (err error) {
func (clients *clientsContainer) Update(name string, c *Client) (err error) {
err = clients.check(c)
if err != nil {
// Don't wrap the error since it's informative enough as is.
return err
}
clients.lock.Lock()
defer clients.lock.Unlock()
// Check the name index.
prev, ok := clients.list[name]
if !ok {
return errors.Error("client not found")
}
// First, check the name index.
if prev.Name != c.Name {
_, ok := clients.list[c.Name]
_, ok = clients.list[c.Name]
if ok {
return errors.Error("client already exists")
}
}
// Check the ID index.
if !slices.Equal(prev.IDs, c.IDs) {
for _, id := range c.IDs {
existing, ok := clients.idIndex[id]
if ok && existing != prev {
return fmt.Errorf("id %q is used by client with name %q", id, existing.Name)
}
// Second, update the ID index.
err = clients.updateIDIndex(prev, c.IDs)
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return err
}
// Update name index.
if prev.Name != c.Name {
delete(clients.list, prev.Name)
clients.list[c.Name] = prev
}
// Update upstreams cache.
err = c.closeUpstreams()
if err != nil {
return err
}
*prev = *c
return nil
}
// updateIDIndex updates the ID index data for cli using the information from
// newIDs.
func (clients *clientsContainer) updateIDIndex(cli *Client, newIDs []string) (err error) {
if slices.Equal(cli.IDs, newIDs) {
return nil
}
for _, id := range newIDs {
existing, ok := clients.idIndex[id]
if ok && existing != cli {
return fmt.Errorf("id %q is used by client with name %q", id, existing.Name)
}
}
clients.del(prev)
clients.add(c)
// Update the IDs in the index.
for _, id := range cli.IDs {
delete(clients.idIndex, id)
}
for _, id := range newIDs {
clients.idIndex[id] = cli
}
return nil
}

View File

@@ -98,8 +98,22 @@ func TestClients(t *testing.T) {
assert.False(t, ok)
})
t.Run("update_fail_name", func(t *testing.T) {
err := clients.Update("client3", &Client{
IDs: []string{"1.2.3.0"},
Name: "client3",
})
require.Error(t, err)
err = clients.Update("client3", &Client{
IDs: []string{"1.2.3.0"},
Name: "client2",
})
assert.Error(t, err)
})
t.Run("update_fail_ip", func(t *testing.T) {
err := clients.Update(&Client{Name: "client1"}, &Client{
err := clients.Update("client1", &Client{
IDs: []string{"2.2.2.2"},
Name: "client1",
})
@@ -115,10 +129,7 @@ func TestClients(t *testing.T) {
cliNewIP = netip.MustParseAddr(cliNew)
)
prev, ok := clients.list["client1"]
require.True(t, ok)
err := clients.Update(prev, &Client{
err := clients.Update("client1", &Client{
IDs: []string{cliNew},
Name: "client1",
})
@@ -127,10 +138,7 @@ func TestClients(t *testing.T) {
assert.Equal(t, clients.clientSource(cliOldIP), ClientSourceNone)
assert.Equal(t, clients.clientSource(cliNewIP), ClientSourcePersistent)
prev, ok = clients.list["client1"]
require.True(t, ok)
err = clients.Update(prev, &Client{
err = clients.Update("client1", &Client{
IDs: []string{cliNew},
Name: "client1-renamed",
UseOwnSettings: true,

View File

@@ -289,7 +289,7 @@ func (clients *clientsContainer) handleUpdateClient(w http.ResponseWriter, r *ht
return
}
err = clients.Update(prev, c)
err = clients.Update(dj.Name, c)
if err != nil {
aghhttp.Error(r, w, http.StatusBadRequest, "%s", err)

View File

@@ -399,39 +399,19 @@ func (c *configuration) getConfigFilename() string {
return configFile
}
// readLogSettings reads logging settings from the config file. We do it in a
// separate method in order to configure logger before the actual configuration
// is parsed and applied.
func readLogSettings() (ls *logSettings) {
ls = &logSettings{}
// getLogSettings reads logging settings from the config file.
// we do it in a separate method in order to configure logger before the actual configuration is parsed and applied.
func getLogSettings() logSettings {
l := logSettings{}
yamlFile, err := readConfigFile()
if err != nil {
return ls
return l
}
err = yaml.Unmarshal(yamlFile, ls)
err = yaml.Unmarshal(yamlFile, &l)
if err != nil {
log.Error("Couldn't get logging settings from the configuration: %s", err)
}
return ls
}
// validateBindHosts returns error if any of binding hosts from configuration is
// not a valid IP address.
func validateBindHosts(conf *configuration) (err error) {
if !conf.BindHost.IsValid() {
return errors.Error("bind_host is not a valid ip address")
}
for i, addr := range conf.DNS.BindHosts {
if !addr.IsValid() {
return fmt.Errorf("dns.bind_hosts at index %d is not a valid ip address", i)
}
}
return nil
return l
}
// parseConfig loads configuration from the YAML file
@@ -445,13 +425,6 @@ func parseConfig() (err error) {
config.fileData = nil
err = yaml.Unmarshal(fileData, &config)
if err != nil {
// Don't wrap the error since it's informative enough as is.
return err
}
err = validateBindHosts(config)
if err != nil {
// Don't wrap the error since it's informative enough as is.
return err
}

View File

@@ -180,7 +180,7 @@ func registerControlHandlers() {
httpRegister(http.MethodGet, "/control/status", handleStatus)
httpRegister(http.MethodPost, "/control/i18n/change_language", handleI18nChangeLanguage)
httpRegister(http.MethodGet, "/control/i18n/current_language", handleI18nCurrentLanguage)
Context.mux.HandleFunc("/control/version.json", postInstall(optionalAuth(handleVersionJSON)))
Context.mux.HandleFunc("/control/version.json", postInstall(optionalAuth(handleGetVersionJSON)))
httpRegister(http.MethodPost, "/control/update", handleUpdate)
httpRegister(http.MethodGet, "/control/profile", handleGetProfile)
httpRegister(http.MethodPut, "/control/profile/update", handlePutProfile)

View File

@@ -26,14 +26,15 @@ type temporaryError interface {
Temporary() (ok bool)
}
// handleVersionJSON is the handler for the POST /control/version.json HTTP API.
//
// TODO(a.garipov): Find out if this API used with a GET method by anyone.
func handleVersionJSON(w http.ResponseWriter, r *http.Request) {
// Get the latest available version from the Internet
func handleGetVersionJSON(w http.ResponseWriter, r *http.Request) {
resp := &versionResponse{}
if Context.disableUpdate {
resp.Disabled = true
_ = aghhttp.WriteJSONResponse(w, r, resp)
err := json.NewEncoder(w).Encode(resp)
if err != nil {
aghhttp.Error(r, w, http.StatusInternalServerError, "writing body: %s", err)
}
return
}

View File

@@ -27,17 +27,14 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/dhcpd"
"github.com/AdguardTeam/AdGuardHome/internal/dnsforward"
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/AdGuardHome/internal/filtering/hashprefix"
"github.com/AdguardTeam/AdGuardHome/internal/filtering/safesearch"
"github.com/AdguardTeam/AdGuardHome/internal/querylog"
"github.com/AdguardTeam/AdGuardHome/internal/stats"
"github.com/AdguardTeam/AdGuardHome/internal/updater"
"github.com/AdguardTeam/AdGuardHome/internal/version"
"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"
"gopkg.in/natefinch/lumberjack.v2"
)
@@ -146,9 +143,7 @@ func Main(clientBuildFS fs.FS) {
run(opts, clientBuildFS)
}
// setupContext initializes [Context] fields. It also reads and upgrades
// config file if necessary.
func setupContext(opts options) (err error) {
func setupContext(opts options) {
setupContextFlags(opts)
Context.tlsRoots = aghtls.SystemRootCAs()
@@ -165,15 +160,10 @@ func setupContext(opts options) (err error) {
},
}
Context.mux = http.NewServeMux()
if !Context.firstRun {
// Do the upgrade if necessary.
err = upgradeConfig()
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return err
}
err := upgradeConfig()
fatalOnError(err)
if err = parseConfig(); err != nil {
log.Error("parsing configuration file: %s", err)
@@ -189,14 +179,11 @@ func setupContext(opts options) (err error) {
if !opts.noEtcHosts && config.Clients.Sources.HostsFile {
err = setupHostsContainer()
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return err
}
fatalOnError(err)
}
}
return nil
Context.mux = http.NewServeMux()
}
// setupContextFlags sets global flags and prints their status to the log.
@@ -298,27 +285,25 @@ func setupHostsContainer() (err error) {
return nil
}
// setupOpts sets up command-line options.
func setupOpts(opts options) (err error) {
err = setupBindOpts(opts)
func setupConfig(opts options) (err error) {
config.DNS.DnsfilterConf.EtcHosts = Context.etcHosts
config.DNS.DnsfilterConf.ConfigModified = onConfigModified
config.DNS.DnsfilterConf.HTTPRegister = httpRegister
config.DNS.DnsfilterConf.DataDir = Context.getDataDir()
config.DNS.DnsfilterConf.Filters = slices.Clone(config.Filters)
config.DNS.DnsfilterConf.WhitelistFilters = slices.Clone(config.WhitelistFilters)
config.DNS.DnsfilterConf.UserRules = slices.Clone(config.UserRules)
config.DNS.DnsfilterConf.HTTPClient = Context.client
config.DNS.DnsfilterConf.SafeSearchConf.CustomResolver = safeSearchResolver{}
config.DNS.DnsfilterConf.SafeSearch, err = safesearch.NewDefault(
config.DNS.DnsfilterConf.SafeSearchConf,
"default",
config.DNS.DnsfilterConf.SafeSearchCacheSize,
time.Minute*time.Duration(config.DNS.DnsfilterConf.CacheTime),
)
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return err
}
if len(opts.pidFile) != 0 && writePIDFile(opts.pidFile) {
Context.pidFileName = opts.pidFile
}
return nil
}
// initContextClients initializes Context clients and related fields.
func initContextClients() (err error) {
err = setupDNSFilteringConf(config.DNS.DnsfilterConf)
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return err
return fmt.Errorf("initializing safesearch: %w", err)
}
//lint:ignore SA1019 Migration is not over.
@@ -353,19 +338,8 @@ func initContextClients() (err error) {
arpdb = aghnet.NewARPDB()
}
Context.clients.Init(
config.Clients.Persistent,
Context.dhcpServer,
Context.etcHosts,
arpdb,
config.DNS.DnsfilterConf,
)
Context.clients.Init(config.Clients.Persistent, Context.dhcpServer, Context.etcHosts, arpdb, config.DNS.DnsfilterConf)
return nil
}
// setupBindOpts overrides bind host/port from the opts.
func setupBindOpts(opts options) (err error) {
if opts.bindPort != 0 {
config.BindPort = opts.bindPort
@@ -376,83 +350,12 @@ func setupBindOpts(opts options) (err error) {
}
}
// override bind host/port from the console
if opts.bindHost.IsValid() {
config.BindHost = opts.bindHost
}
return nil
}
// setupDNSFilteringConf sets up DNS filtering configuration settings.
func setupDNSFilteringConf(conf *filtering.Config) (err error) {
const (
dnsTimeout = 3 * time.Second
sbService = "safe browsing"
defaultSafeBrowsingServer = `https://family.adguard-dns.com/dns-query`
sbTXTSuffix = `sb.dns.adguard.com.`
pcService = "parental control"
defaultParentalServer = `https://family.adguard-dns.com/dns-query`
pcTXTSuffix = `pc.dns.adguard.com.`
)
conf.EtcHosts = Context.etcHosts
conf.ConfigModified = onConfigModified
conf.HTTPRegister = httpRegister
conf.DataDir = Context.getDataDir()
conf.Filters = slices.Clone(config.Filters)
conf.WhitelistFilters = slices.Clone(config.WhitelistFilters)
conf.UserRules = slices.Clone(config.UserRules)
conf.HTTPClient = Context.client
cacheTime := time.Duration(conf.CacheTime) * time.Minute
upsOpts := &upstream.Options{
Timeout: dnsTimeout,
ServerIPAddrs: []net.IP{
{94, 140, 14, 15},
{94, 140, 15, 16},
net.ParseIP("2a10:50c0::bad1:ff"),
net.ParseIP("2a10:50c0::bad2:ff"),
},
}
sbUps, err := upstream.AddressToUpstream(defaultSafeBrowsingServer, upsOpts)
if err != nil {
return fmt.Errorf("converting safe browsing server: %w", err)
}
conf.SafeBrowsingChecker = hashprefix.New(&hashprefix.Config{
Upstream: sbUps,
ServiceName: sbService,
TXTSuffix: sbTXTSuffix,
CacheTime: cacheTime,
CacheSize: conf.SafeBrowsingCacheSize,
})
parUps, err := upstream.AddressToUpstream(defaultParentalServer, upsOpts)
if err != nil {
return fmt.Errorf("converting parental server: %w", err)
}
conf.ParentalControlChecker = hashprefix.New(&hashprefix.Config{
Upstream: parUps,
ServiceName: pcService,
TXTSuffix: pcTXTSuffix,
CacheTime: cacheTime,
CacheSize: conf.SafeBrowsingCacheSize,
})
conf.SafeSearchConf.CustomResolver = safeSearchResolver{}
conf.SafeSearch, err = safesearch.NewDefault(
conf.SafeSearchConf,
"default",
conf.SafeSearchCacheSize,
cacheTime,
)
if err != nil {
return fmt.Errorf("initializing safesearch: %w", err)
if len(opts.pidFile) != 0 && writePIDFile(opts.pidFile) {
Context.pidFileName = opts.pidFile
}
return nil
@@ -529,16 +432,14 @@ func fatalOnError(err error) {
// run configures and starts AdGuard Home.
func run(opts options, clientBuildFS fs.FS) {
// Configure config filename.
// configure config filename
initConfigFilename(opts)
// Configure working dir and config path.
err := initWorkingDir(opts)
fatalOnError(err)
// configure working dir and config path
initWorkingDir(opts)
// Configure log level and output.
err = configureLogger(opts)
fatalOnError(err)
// configure log level and output
configureLogger(opts)
// Print the first message after logger is configured.
log.Info(version.Full())
@@ -547,29 +448,25 @@ func run(opts options, clientBuildFS fs.FS) {
log.Info("AdGuard Home is running as a service")
}
err = setupContext(opts)
setupContext(opts)
err := configureOS(config)
fatalOnError(err)
err = configureOS(config)
fatalOnError(err)
// Clients package uses filtering package's static data
// (filtering.BlockedSvcKnown()), so we have to initialize filtering static
// data first, but also to avoid relying on automatic Go init() function.
// clients package uses filtering package's static data (filtering.BlockedSvcKnown()),
// so we have to initialize filtering's static data first,
// but also avoid relying on automatic Go init() function
filtering.InitModule()
err = initContextClients()
err = setupConfig(opts)
fatalOnError(err)
err = setupOpts(opts)
fatalOnError(err)
// TODO(e.burkov): This could be made earlier, probably as the option's
// TODO(e.burkov): This could be made earlier, probably as the option's
// effect.
cmdlineUpdate(opts)
if !Context.firstRun {
// Save the updated config.
// Save the updated config
err = config.write()
fatalOnError(err)
@@ -579,15 +476,33 @@ func run(opts options, clientBuildFS fs.FS) {
}
}
dir := Context.getDataDir()
err = os.MkdirAll(dir, 0o755)
fatalOnError(errors.Annotate(err, "creating DNS data dir at %s: %w", dir))
err = os.MkdirAll(Context.getDataDir(), 0o755)
if err != nil {
log.Fatalf("Cannot create DNS data dir at %s: %s", Context.getDataDir(), err)
}
sessFilename := filepath.Join(Context.getDataDir(), "sessions.db")
GLMode = opts.glinetMode
var rateLimiter *authRateLimiter
if config.AuthAttempts > 0 && config.AuthBlockMin > 0 {
rateLimiter = newAuthRateLimiter(
time.Duration(config.AuthBlockMin)*time.Minute,
config.AuthAttempts,
)
} else {
log.Info("authratelimiter is disabled")
}
// Init auth module.
Context.auth, err = initUsers()
fatalOnError(err)
Context.auth = InitAuth(
sessFilename,
config.Users,
config.WebSessionTTLHours*60*60,
rateLimiter,
)
if Context.auth == nil {
log.Fatalf("Couldn't initialize Auth module")
}
config.Users = nil
Context.tls, err = newTLSManager(config.TLS)
if err != nil {
@@ -605,10 +520,10 @@ func run(opts options, clientBuildFS fs.FS) {
Context.tls.start()
go func() {
sErr := startDNSServer()
if sErr != nil {
serr := startDNSServer()
if serr != nil {
closeDNSServer()
fatalOnError(sErr)
fatalOnError(serr)
}
}()
@@ -622,33 +537,10 @@ func run(opts options, clientBuildFS fs.FS) {
Context.web.start()
// Wait indefinitely for other goroutines to complete their job.
// wait indefinitely for other go-routines to complete their job
select {}
}
// initUsers initializes context auth module. Clears config users field.
func initUsers() (auth *Auth, err error) {
sessFilename := filepath.Join(Context.getDataDir(), "sessions.db")
var rateLimiter *authRateLimiter
if config.AuthAttempts > 0 && config.AuthBlockMin > 0 {
blockDur := time.Duration(config.AuthBlockMin) * time.Minute
rateLimiter = newAuthRateLimiter(blockDur, config.AuthAttempts)
} else {
log.Info("authratelimiter is disabled")
}
sessionTTL := config.WebSessionTTLHours * 60 * 60
auth = InitAuth(sessFilename, config.Users, sessionTTL, rateLimiter)
if auth == nil {
return nil, errors.Error("initializing auth module failed")
}
config.Users = nil
return auth, nil
}
func (c *configuration) anonymizer() (ipmut *aghnet.IPMut) {
var anonFunc aghnet.IPMutFunc
if c.DNS.AnonymizeClientIP {
@@ -721,19 +613,22 @@ func writePIDFile(fn string) bool {
return true
}
// initConfigFilename sets up context config file path. This file path can be
// overridden by command-line arguments, or is set to default.
func initConfigFilename(opts options) {
Context.configFilename = stringutil.Coalesce(opts.confFilename, "AdGuardHome.yaml")
// config file path can be overridden by command-line arguments:
if opts.confFilename != "" {
Context.configFilename = opts.confFilename
} else {
// Default config file name
Context.configFilename = "AdGuardHome.yaml"
}
}
// initWorkingDir initializes the workDir. If no command-line arguments are
// specified, the directory with the binary file is used.
func initWorkingDir(opts options) (err error) {
// initWorkingDir initializes the workDir
// if no command-line arguments specified, we use the directory where our binary file is located
func initWorkingDir(opts options) {
execPath, err := os.Executable()
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return err
panic(err)
}
if opts.workDir != "" {
@@ -745,20 +640,34 @@ func initWorkingDir(opts options) (err error) {
workDir, err := filepath.EvalSymlinks(Context.workDir)
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return err
panic(err)
}
Context.workDir = workDir
return nil
}
// configureLogger configures logger level and output.
func configureLogger(opts options) (err error) {
ls := getLogSettings(opts)
// configureLogger configures logger level and output
func configureLogger(opts options) {
ls := getLogSettings()
// Configure logger level.
// command-line arguments can override config settings
if opts.verbose || config.Verbose {
ls.Verbose = true
}
if opts.logFile != "" {
ls.File = opts.logFile
} else if config.File != "" {
ls.File = config.File
}
// Handle default log settings overrides
ls.Compress = config.Compress
ls.LocalTime = config.LocalTime
ls.MaxBackups = config.MaxBackups
ls.MaxSize = config.MaxSize
ls.MaxAge = config.MaxAge
// log.SetLevel(log.INFO) - default
if ls.Verbose {
log.SetLevel(log.DEBUG)
}
@@ -767,63 +676,38 @@ func configureLogger(opts options) (err error) {
// happen pretty quickly.
log.SetFlags(log.LstdFlags | log.Lmicroseconds)
// Write logs to stdout by default.
if ls.File == "" {
return nil
}
if ls.File == configSyslog {
// Use syslog where it is possible and eventlog on Windows.
err = aghos.ConfigureSyslog(serviceName)
if err != nil {
return fmt.Errorf("cannot initialize syslog: %w", err)
}
return nil
}
logFilePath := ls.File
if !filepath.IsAbs(logFilePath) {
logFilePath = filepath.Join(Context.workDir, logFilePath)
}
log.SetOutput(&lumberjack.Logger{
Filename: logFilePath,
Compress: ls.Compress,
LocalTime: ls.LocalTime,
MaxBackups: ls.MaxBackups,
MaxSize: ls.MaxSize,
MaxAge: ls.MaxAge,
})
return nil
}
// getLogSettings returns a log settings object properly initialized from opts.
func getLogSettings(opts options) (ls *logSettings) {
ls = readLogSettings()
// Command-line arguments can override config settings.
if opts.verbose || config.Verbose {
ls.Verbose = true
}
ls.File = stringutil.Coalesce(opts.logFile, config.File, ls.File)
// Handle default log settings overrides.
ls.Compress = config.Compress
ls.LocalTime = config.LocalTime
ls.MaxBackups = config.MaxBackups
ls.MaxSize = config.MaxSize
ls.MaxAge = config.MaxAge
if opts.runningAsService && ls.File == "" && runtime.GOOS == "windows" {
// When running as a Windows service, use eventlog by default if
// nothing else is configured. Otherwise, we'll lose the log output.
// When running as a Windows service, use eventlog by default if nothing
// else is configured. Otherwise, we'll simply lose the log output.
ls.File = configSyslog
}
return ls
// logs are written to stdout (default)
if ls.File == "" {
return
}
if ls.File == configSyslog {
// Use syslog where it is possible and eventlog on Windows
err := aghos.ConfigureSyslog(serviceName)
if err != nil {
log.Fatalf("cannot initialize syslog: %s", err)
}
} else {
logFilePath := ls.File
if !filepath.IsAbs(logFilePath) {
logFilePath = filepath.Join(Context.workDir, logFilePath)
}
log.SetOutput(&lumberjack.Logger{
Filename: logFilePath,
Compress: ls.Compress, // disabled by default
LocalTime: ls.LocalTime,
MaxBackups: ls.MaxBackups,
MaxSize: ls.MaxSize, // megabytes
MaxAge: ls.MaxAge, // days
})
}
}
// cleanup stops and resets all the modules.

View File

@@ -4,6 +4,7 @@ import (
"fmt"
"io/fs"
"os"
"path/filepath"
"runtime"
"strconv"
"strings"
@@ -83,9 +84,14 @@ func svcStatus(s service.Service) (status service.Status, err error) {
// On OpenWrt, the service utility may not exist. We use our service script
// directly in this case.
func svcAction(s service.Service, action string) (err error) {
if action == "start" {
if err = aghos.PreCheckActionStart(); err != nil {
log.Error("starting service: %s", err)
if runtime.GOOS == "darwin" && action == "start" {
var exe string
if exe, err = os.Executable(); err != nil {
log.Error("starting service: getting executable path: %s", err)
} else if exe, err = filepath.EvalSymlinks(exe); err != nil {
log.Error("starting service: evaluating executable symlinks: %s", err)
} else if !strings.HasPrefix(exe, "/Applications/") {
log.Info("warning: service must be started from within the /Applications directory")
}
}
@@ -93,6 +99,8 @@ func svcAction(s service.Service, action string) (err error) {
if err != nil && service.Platform() == "unix-systemv" &&
(action == "start" || action == "stop" || action == "restart") {
_, err = runInitdCommand(action)
return err
}
return err
@@ -216,7 +224,6 @@ func handleServiceControlAction(opts options, clientBuildFS fs.FS) {
runOpts := opts
runOpts.serviceControlAction = "run"
svcConfig := &service.Config{
Name: serviceName,
DisplayName: serviceDisplayName,
@@ -226,48 +233,35 @@ func handleServiceControlAction(opts options, clientBuildFS fs.FS) {
}
configureService(svcConfig)
s, err := service.New(&program{clientBuildFS: clientBuildFS, opts: runOpts}, svcConfig)
if err != nil {
prg := &program{
clientBuildFS: clientBuildFS,
opts: runOpts,
}
var s service.Service
if s, err = service.New(prg, svcConfig); err != nil {
log.Fatalf("service: initializing service: %s", err)
}
err = handleServiceCommand(s, action, opts)
if err != nil {
log.Fatalf("service: %s", err)
}
log.Printf(
"service: action %s has been done successfully on %s",
action,
service.ChosenSystem(),
)
}
// handleServiceCommand handles service command.
func handleServiceCommand(s service.Service, action string, opts options) (err error) {
switch action {
case "status":
handleServiceStatusCommand(s)
case "run":
if err = s.Run(); err != nil {
return fmt.Errorf("failed to run service: %w", err)
log.Fatalf("service: failed to run service: %s", err)
}
case "install":
initConfigFilename(opts)
if err = initWorkingDir(opts); err != nil {
return fmt.Errorf("failed to init working dir: %w", err)
}
initWorkingDir(opts)
handleServiceInstallCommand(s)
case "uninstall":
handleServiceUninstallCommand(s)
default:
if err = svcAction(s, action); err != nil {
return fmt.Errorf("executing action %q: %w", action, err)
log.Fatalf("service: executing action %q: %s", action, err)
}
}
return nil
log.Printf("service: action %s has been done successfully on %s", action, service.ChosenSystem())
}
// handleServiceStatusCommand handles service "status" command.

View File

@@ -172,32 +172,9 @@ func loadTLSConf(tlsConf *tlsConfigSettings, status *tlsConfigStatus) (err error
}
}()
err = loadCertificateChainData(tlsConf, status)
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return err
}
err = loadPrivateKeyData(tlsConf, status)
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return err
}
err = validateCertificates(
status,
tlsConf.CertificateChainData,
tlsConf.PrivateKeyData,
tlsConf.ServerName,
)
return errors.Annotate(err, "validating certificate pair: %w")
}
// loadCertificateChainData loads PEM-encoded certificates chain data to the
// TLS configuration.
func loadCertificateChainData(tlsConf *tlsConfigSettings, status *tlsConfigStatus) (err error) {
tlsConf.CertificateChainData = []byte(tlsConf.CertificateChain)
tlsConf.PrivateKeyData = []byte(tlsConf.PrivateKey)
if tlsConf.CertificatePath != "" {
if tlsConf.CertificateChain != "" {
return errors.Error("certificate data and file can't be set together")
@@ -213,13 +190,6 @@ func loadCertificateChainData(tlsConf *tlsConfigSettings, status *tlsConfigStatu
status.ValidCert = true
}
return nil
}
// loadPrivateKeyData loads PEM-encoded private key data to the TLS
// configuration.
func loadPrivateKeyData(tlsConf *tlsConfigSettings, status *tlsConfigStatus) (err error) {
tlsConf.PrivateKeyData = []byte(tlsConf.PrivateKey)
if tlsConf.PrivateKeyPath != "" {
if tlsConf.PrivateKey != "" {
return errors.Error("private key data and file can't be set together")
@@ -233,6 +203,16 @@ func loadPrivateKeyData(tlsConf *tlsConfigSettings, status *tlsConfigStatus) (er
status.ValidKey = true
}
err = validateCertificates(
status,
tlsConf.CertificateChainData,
tlsConf.PrivateKeyData,
tlsConf.ServerName,
)
if err != nil {
return fmt.Errorf("validating certificate pair: %w", err)
}
return nil
}

View File

@@ -41,8 +41,7 @@ func upgradeConfig() error {
err = yaml.Unmarshal(body, &diskConf)
if err != nil {
log.Printf("parsing config file for upgrade: %s", err)
log.Printf("Couldn't parse config file: %s", err)
return err
}
@@ -294,61 +293,71 @@ func upgradeSchema4to5(diskConf yobj) error {
return nil
}
// upgradeSchema5to6 performs the following changes:
// clients:
// ...
//
// # BEFORE:
// 'clients':
// ...
// 'ip': 127.0.0.1
// 'mac': ...
// ip: 127.0.0.1
// mac: ...
//
// # AFTER:
// 'clients':
// ...
// 'ids':
// - 127.0.0.1
// - ...
// ->
//
// clients:
// ...
//
// ids:
// - 127.0.0.1
// - ...
func upgradeSchema5to6(diskConf yobj) error {
log.Printf("Upgrade yaml: 5 to 6")
log.Printf("%s(): called", funcName())
diskConf["schema_version"] = 6
clientsVal, ok := diskConf["clients"]
clients, ok := diskConf["clients"]
if !ok {
return nil
}
clients, ok := clientsVal.([]yobj)
if !ok {
return fmt.Errorf("unexpected type of clients: %T", clientsVal)
}
switch arr := clients.(type) {
case []any:
for i := range arr {
switch c := arr[i].(type) {
case map[any]any:
var ipVal any
ipVal, ok = c["ip"]
ids := []string{}
if ok {
var ip string
ip, ok = ipVal.(string)
if !ok {
log.Fatalf("client.ip is not a string: %v", ipVal)
return nil
}
if len(ip) != 0 {
ids = append(ids, ip)
}
}
for i := range clients {
c := clients[i]
var ids []string
var macVal any
macVal, ok = c["mac"]
if ok {
var mac string
mac, ok = macVal.(string)
if !ok {
log.Fatalf("client.mac is not a string: %v", macVal)
return nil
}
if len(mac) != 0 {
ids = append(ids, mac)
}
}
if ipVal, hasIP := c["ip"]; hasIP {
var ip string
if ip, ok = ipVal.(string); !ok {
return fmt.Errorf("client.ip is not a string: %v", ipVal)
}
if ip != "" {
ids = append(ids, ip)
c["ids"] = ids
default:
continue
}
}
if macVal, hasMac := c["mac"]; hasMac {
var mac string
if mac, ok = macVal.(string); !ok {
return fmt.Errorf("client.mac is not a string: %v", macVal)
}
if mac != "" {
ids = append(ids, mac)
}
}
c["ids"] = ids
default:
return nil
}
return nil

View File

@@ -68,95 +68,6 @@ func TestUpgradeSchema2to3(t *testing.T) {
assertEqualExcept(t, oldDiskConf, diskConf, excludedEntries, excludedEntries)
}
func TestUpgradeSchema5to6(t *testing.T) {
const newSchemaVer = 6
testCases := []struct {
in yobj
want yobj
wantErr string
name string
}{{
in: yobj{
"clients": []yobj{},
},
want: yobj{
"clients": []yobj{},
"schema_version": newSchemaVer,
},
wantErr: "",
name: "no_clients",
}, {
in: yobj{
"clients": []yobj{{"ip": "127.0.0.1"}},
},
want: yobj{
"clients": []yobj{{
"ids": []string{"127.0.0.1"},
"ip": "127.0.0.1",
}},
"schema_version": newSchemaVer,
},
wantErr: "",
name: "client_ip",
}, {
in: yobj{
"clients": []yobj{{"mac": "mac"}},
},
want: yobj{
"clients": []yobj{{
"ids": []string{"mac"},
"mac": "mac",
}},
"schema_version": newSchemaVer,
},
wantErr: "",
name: "client_mac",
}, {
in: yobj{
"clients": []yobj{{"ip": "127.0.0.1", "mac": "mac"}},
},
want: yobj{
"clients": []yobj{{
"ids": []string{"127.0.0.1", "mac"},
"ip": "127.0.0.1",
"mac": "mac",
}},
"schema_version": newSchemaVer,
},
wantErr: "",
name: "client_ip_mac",
}, {
in: yobj{
"clients": []yobj{{"ip": 1, "mac": "mac"}},
},
want: yobj{
"clients": []yobj{{"ip": 1, "mac": "mac"}},
"schema_version": newSchemaVer,
},
wantErr: "client.ip is not a string: 1",
name: "inv_client_ip",
}, {
in: yobj{
"clients": []yobj{{"ip": "127.0.0.1", "mac": 1}},
},
want: yobj{
"clients": []yobj{{"ip": "127.0.0.1", "mac": 1}},
"schema_version": newSchemaVer,
},
wantErr: "client.mac is not a string: 1",
name: "inv_client_mac",
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
err := upgradeSchema5to6(tc.in)
testutil.AssertErrorMsg(t, tc.wantErr, err)
assert.Equal(t, tc.want, tc.in)
})
}
}
func TestUpgradeSchema7to8(t *testing.T) {
const host = "1.2.3.4"
oldConf := yobj{

View File

@@ -1,63 +0,0 @@
// Package agh contains common entities and interfaces of AdGuard Home.
package agh
import "context"
// Service is the interface for API servers.
//
// TODO(a.garipov): Consider adding a context to Start.
//
// TODO(a.garipov): Consider adding a Wait method or making an extension
// interface for that.
type Service interface {
// Start starts the service. It does not block.
Start() (err error)
// Shutdown gracefully stops the service. ctx is used to determine
// a timeout before trying to stop the service less gracefully.
Shutdown(ctx context.Context) (err error)
}
// type check
var _ Service = EmptyService{}
// EmptyService is a [Service] that does nothing.
//
// TODO(a.garipov): Remove if unnecessary.
type EmptyService struct{}
// Start implements the [Service] interface for EmptyService.
func (EmptyService) Start() (err error) { return nil }
// Shutdown implements the [Service] interface for EmptyService.
func (EmptyService) Shutdown(_ context.Context) (err error) { return nil }
// ServiceWithConfig is an extension of the [Service] interface for services
// that can return their configuration.
//
// TODO(a.garipov): Consider removing this generic interface if we figure out
// how to make it testable in a better way.
type ServiceWithConfig[ConfigType any] interface {
Service
Config() (c ConfigType)
}
// type check
var _ ServiceWithConfig[struct{}] = (*EmptyServiceWithConfig[struct{}])(nil)
// EmptyServiceWithConfig is a ServiceWithConfig that does nothing. Its Config
// method returns Conf.
//
// TODO(a.garipov): Remove if unnecessary.
type EmptyServiceWithConfig[ConfigType any] struct {
EmptyService
Conf ConfigType
}
// Config implements the [ServiceWithConfig] interface for
// *EmptyServiceWithConfig.
func (s *EmptyServiceWithConfig[ConfigType]) Config() (conf ConfigType) {
return s.Conf
}

View File

@@ -1,77 +0,0 @@
// Package cmd is the AdGuard Home entry point. It contains the on-disk
// configuration file utilities, signal processing logic, and so on.
//
// TODO(a.garipov): Move to the upper-level internal/.
package cmd
import (
"context"
"io/fs"
"math/rand"
"os"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/next/configmgr"
"github.com/AdguardTeam/AdGuardHome/internal/version"
"github.com/AdguardTeam/golibs/log"
)
// Main is the entry point of application.
func Main(clientBuildFS fs.FS) {
// Initial Configuration
start := time.Now()
rand.Seed(start.UnixNano())
// TODO(a.garipov): Set up logging.
log.Info("starting adguard home, version %s, pid %d", version.Version(), os.Getpid())
// Web Service
// TODO(a.garipov): Use in the Web service.
_ = clientBuildFS
// TODO(a.garipov): Set up configuration file name.
const confFile = "AdGuardHome.1.yaml"
confMgr, err := configmgr.New(confFile, start)
fatalOnError(err)
web := confMgr.Web()
err = web.Start()
fatalOnError(err)
dns := confMgr.DNS()
err = dns.Start()
fatalOnError(err)
sigHdlr := newSignalHandler(
confFile,
start,
web,
dns,
)
go sigHdlr.handle()
select {}
}
// defaultTimeout is the timeout used for some operations where another timeout
// hasn't been defined yet.
const defaultTimeout = 15 * time.Second
// ctxWithDefaultTimeout is a helper function that returns a context with
// timeout set to defaultTimeout.
func ctxWithDefaultTimeout() (ctx context.Context, cancel context.CancelFunc) {
return context.WithTimeout(context.Background(), defaultTimeout)
}
// fatalOnError is a helper that exits the program with an error code if err is
// not nil. It must only be used within Main.
func fatalOnError(err error) {
if err != nil {
log.Fatal(err)
}
}

View File

@@ -1,118 +0,0 @@
package cmd
import (
"os"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/aghos"
"github.com/AdguardTeam/AdGuardHome/internal/next/agh"
"github.com/AdguardTeam/AdGuardHome/internal/next/configmgr"
"github.com/AdguardTeam/golibs/log"
)
// signalHandler processes incoming signals and shuts services down.
type signalHandler struct {
// signal is the channel to which OS signals are sent.
signal chan os.Signal
// confFile is the path to the configuration file.
confFile string
// start is the time at which AdGuard Home has been started.
start time.Time
// services are the services that are shut down before application exiting.
services []agh.Service
}
// handle processes OS signals.
func (h *signalHandler) handle() {
defer log.OnPanic("signalHandler.handle")
for sig := range h.signal {
log.Info("sighdlr: received signal %q", sig)
if aghos.IsReconfigureSignal(sig) {
h.reconfigure()
} else if aghos.IsShutdownSignal(sig) {
status := h.shutdown()
log.Info("sighdlr: exiting with status %d", status)
os.Exit(status)
}
}
}
// reconfigure rereads the configuration file and updates and restarts services.
func (h *signalHandler) reconfigure() {
log.Info("sighdlr: reconfiguring adguard home")
status := h.shutdown()
if status != statusSuccess {
log.Info("sighdlr: reconfiruging: exiting with status %d", status)
os.Exit(status)
}
// TODO(a.garipov): This is a very rough way to do it. Some services can be
// reconfigured without the full shutdown, and the error handling is
// currently not the best.
confMgr, err := configmgr.New(h.confFile, h.start)
fatalOnError(err)
web := confMgr.Web()
err = web.Start()
fatalOnError(err)
dns := confMgr.DNS()
err = dns.Start()
fatalOnError(err)
h.services = []agh.Service{
dns,
web,
}
log.Info("sighdlr: successfully reconfigured adguard home")
}
// Exit status constants.
const (
statusSuccess = 0
statusError = 1
)
// shutdown gracefully shuts down all services.
func (h *signalHandler) shutdown() (status int) {
ctx, cancel := ctxWithDefaultTimeout()
defer cancel()
status = statusSuccess
log.Info("sighdlr: shutting down services")
for i, service := range h.services {
err := service.Shutdown(ctx)
if err != nil {
log.Error("sighdlr: shutting down service at index %d: %s", i, err)
status = statusError
}
}
return status
}
// newSignalHandler returns a new signalHandler that shuts down svcs.
func newSignalHandler(confFile string, start time.Time, svcs ...agh.Service) (h *signalHandler) {
h = &signalHandler{
signal: make(chan os.Signal, 1),
confFile: confFile,
start: start,
services: svcs,
}
aghos.NotifyShutdownSignal(h.signal)
aghos.NotifyReconfigureSignal(h.signal)
return h
}

View File

@@ -1,40 +0,0 @@
package configmgr
import (
"net/netip"
"github.com/AdguardTeam/golibs/timeutil"
)
// Configuration Structures
// config is the top-level on-disk configuration structure.
type config struct {
DNS *dnsConfig `yaml:"dns"`
HTTP *httpConfig `yaml:"http"`
// TODO(a.garipov): Use.
SchemaVersion int `yaml:"schema_version"`
// TODO(a.garipov): Use.
DebugPprof bool `yaml:"debug_pprof"`
Verbose bool `yaml:"verbose"`
}
// dnsConfig is the on-disk DNS configuration.
//
// TODO(a.garipov): Validate.
type dnsConfig struct {
Addresses []netip.AddrPort `yaml:"addresses"`
BootstrapDNS []string `yaml:"bootstrap_dns"`
UpstreamDNS []string `yaml:"upstream_dns"`
UpstreamTimeout timeutil.Duration `yaml:"upstream_timeout"`
}
// httpConfig is the on-disk web API configuration.
//
// TODO(a.garipov): Validate.
type httpConfig struct {
Addresses []netip.AddrPort `yaml:"addresses"`
SecureAddresses []netip.AddrPort `yaml:"secure_addresses"`
Timeout timeutil.Duration `yaml:"timeout"`
ForceHTTPS bool `yaml:"force_https"`
}

View File

@@ -1,205 +0,0 @@
// Package configmgr defines the AdGuard Home on-disk configuration entities and
// configuration manager.
package configmgr
import (
"context"
"fmt"
"os"
"sync"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/next/agh"
"github.com/AdguardTeam/AdGuardHome/internal/next/dnssvc"
"github.com/AdguardTeam/AdGuardHome/internal/next/websvc"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"gopkg.in/yaml.v3"
)
// Configuration Manager
// Manager handles full and partial changes in the configuration, persisting
// them to disk if necessary.
type Manager struct {
// updMu makes sure that at most one reconfiguration is performed at a time.
// updMu protects all fields below.
updMu *sync.RWMutex
// dns is the DNS service.
dns *dnssvc.Service
// Web is the Web API service.
web *websvc.Service
// current is the current configuration.
current *config
// fileName is the name of the configuration file.
fileName string
}
// New creates a new *Manager that persists changes to the file pointed to by
// fileName. It reads the configuration file and populates the service fields.
// start is the startup time of AdGuard Home.
func New(fileName string, start time.Time) (m *Manager, err error) {
defer func() { err = errors.Annotate(err, "reading config") }()
conf := &config{}
f, err := os.Open(fileName)
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return nil, err
}
defer func() { err = errors.WithDeferred(err, f.Close()) }()
err = yaml.NewDecoder(f).Decode(conf)
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return nil, err
}
// TODO(a.garipov): Move into a separate function and add other logging
// settings.
if conf.Verbose {
log.SetLevel(log.DEBUG)
}
// TODO(a.garipov): Validate the configuration structure. Return an error
// if it's incorrect.
m = &Manager{
updMu: &sync.RWMutex{},
current: conf,
fileName: fileName,
}
// TODO(a.garipov): Get the context with the timeout from the arguments?
const assemblyTimeout = 5 * time.Second
ctx, cancel := context.WithTimeout(context.Background(), assemblyTimeout)
defer cancel()
err = m.assemble(ctx, conf, start)
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return nil, err
}
return m, nil
}
// assemble creates all services and puts them into the corresponding fields.
// The fields of conf must not be modified after calling assemble.
func (m *Manager) assemble(ctx context.Context, conf *config, start time.Time) (err error) {
dnsConf := &dnssvc.Config{
Addresses: conf.DNS.Addresses,
BootstrapServers: conf.DNS.BootstrapDNS,
UpstreamServers: conf.DNS.UpstreamDNS,
UpstreamTimeout: conf.DNS.UpstreamTimeout.Duration,
}
err = m.updateDNS(ctx, dnsConf)
if err != nil {
return fmt.Errorf("assembling dnssvc: %w", err)
}
webSvcConf := &websvc.Config{
ConfigManager: m,
// TODO(a.garipov): Fill from config file.
TLS: nil,
Start: start,
Addresses: conf.HTTP.Addresses,
SecureAddresses: conf.HTTP.SecureAddresses,
Timeout: conf.HTTP.Timeout.Duration,
ForceHTTPS: conf.HTTP.ForceHTTPS,
}
err = m.updateWeb(ctx, webSvcConf)
if err != nil {
return fmt.Errorf("assembling websvc: %w", err)
}
return nil
}
// DNS returns the current DNS service. It is safe for concurrent use.
func (m *Manager) DNS() (dns agh.ServiceWithConfig[*dnssvc.Config]) {
m.updMu.RLock()
defer m.updMu.RUnlock()
return m.dns
}
// UpdateDNS implements the [websvc.ConfigManager] interface for *Manager. The
// fields of c must not be modified after calling UpdateDNS.
func (m *Manager) UpdateDNS(ctx context.Context, c *dnssvc.Config) (err error) {
m.updMu.Lock()
defer m.updMu.Unlock()
// TODO(a.garipov): Update and write the configuration file. Return an
// error if something went wrong.
err = m.updateDNS(ctx, c)
if err != nil {
return fmt.Errorf("reassembling dnssvc: %w", err)
}
return nil
}
// updateDNS recreates the DNS service. m.updMu is expected to be locked.
func (m *Manager) updateDNS(ctx context.Context, c *dnssvc.Config) (err error) {
if prev := m.dns; prev != nil {
err = prev.Shutdown(ctx)
if err != nil {
return fmt.Errorf("shutting down dns svc: %w", err)
}
}
svc, err := dnssvc.New(c)
if err != nil {
return fmt.Errorf("creating dns svc: %w", err)
}
m.dns = svc
return nil
}
// Web returns the current web service. It is safe for concurrent use.
func (m *Manager) Web() (web agh.ServiceWithConfig[*websvc.Config]) {
m.updMu.RLock()
defer m.updMu.RUnlock()
return m.web
}
// UpdateWeb implements the [websvc.ConfigManager] interface for *Manager. The
// fields of c must not be modified after calling UpdateWeb.
func (m *Manager) UpdateWeb(ctx context.Context, c *websvc.Config) (err error) {
m.updMu.Lock()
defer m.updMu.Unlock()
// TODO(a.garipov): Update and write the configuration file. Return an
// error if something went wrong.
err = m.updateWeb(ctx, c)
if err != nil {
return fmt.Errorf("reassembling websvc: %w", err)
}
return nil
}
// updateWeb recreates the web service. m.upd is expected to be locked.
func (m *Manager) updateWeb(ctx context.Context, c *websvc.Config) (err error) {
if prev := m.web; prev != nil {
err = prev.Shutdown(ctx)
if err != nil {
return fmt.Errorf("shutting down web svc: %w", err)
}
}
m.web = websvc.New(c)
return nil
}

View File

@@ -1,216 +0,0 @@
// Package dnssvc contains the AdGuard Home DNS service.
//
// TODO(a.garipov): Define, if all methods of a *Service should work with a nil
// receiver.
package dnssvc
import (
"context"
"fmt"
"net"
"net/netip"
"sync/atomic"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/next/agh"
// TODO(a.garipov): Add a “dnsproxy proxy” package to shield us from changes
// and replacement of module dnsproxy.
"github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/dnsproxy/upstream"
)
// Config is the AdGuard Home DNS service configuration structure.
//
// TODO(a.garipov): Add timeout for incoming requests.
type Config struct {
// Addresses are the addresses on which to serve plain DNS queries.
Addresses []netip.AddrPort
// Upstreams are the DNS upstreams to use. If not set, upstreams are
// created using data from BootstrapServers, UpstreamServers, and
// UpstreamTimeout.
//
// TODO(a.garipov): Think of a better scheme. Those other three parameters
// are here only to make Config work properly.
Upstreams []upstream.Upstream
// BootstrapServers are the addresses for bootstrapping the upstream DNS
// server addresses.
BootstrapServers []string
// UpstreamServers are the upstream DNS server addresses to use.
UpstreamServers []string
// UpstreamTimeout is the timeout for upstream requests.
UpstreamTimeout time.Duration
}
// Service is the AdGuard Home DNS service. A nil *Service is a valid
// [agh.Service] that does nothing.
type Service struct {
proxy *proxy.Proxy
bootstraps []string
upstreams []string
upsTimeout time.Duration
running atomic.Bool
}
// New returns a new properly initialized *Service. If c is nil, svc is a nil
// *Service that does nothing. The fields of c must not be modified after
// calling New.
func New(c *Config) (svc *Service, err error) {
if c == nil {
return nil, nil
}
svc = &Service{
bootstraps: c.BootstrapServers,
upstreams: c.UpstreamServers,
upsTimeout: c.UpstreamTimeout,
}
var upstreams []upstream.Upstream
if len(c.Upstreams) > 0 {
upstreams = c.Upstreams
} else {
upstreams, err = addressesToUpstreams(
c.UpstreamServers,
c.BootstrapServers,
c.UpstreamTimeout,
)
if err != nil {
return nil, fmt.Errorf("converting upstreams: %w", err)
}
}
svc.proxy = &proxy.Proxy{
Config: proxy.Config{
UDPListenAddr: udpAddrs(c.Addresses),
TCPListenAddr: tcpAddrs(c.Addresses),
UpstreamConfig: &proxy.UpstreamConfig{
Upstreams: upstreams,
},
},
}
err = svc.proxy.Init()
if err != nil {
return nil, fmt.Errorf("proxy: %w", err)
}
return svc, nil
}
// addressesToUpstreams is a wrapper around [upstream.AddressToUpstream]. It
// accepts a slice of addresses and other upstream parameters, and returns a
// slice of upstreams.
func addressesToUpstreams(
upsStrs []string,
bootstraps []string,
timeout time.Duration,
) (upstreams []upstream.Upstream, err error) {
upstreams = make([]upstream.Upstream, len(upsStrs))
for i, upsStr := range upsStrs {
upstreams[i], err = upstream.AddressToUpstream(upsStr, &upstream.Options{
Bootstrap: bootstraps,
Timeout: timeout,
})
if err != nil {
return nil, fmt.Errorf("upstream at index %d: %w", i, err)
}
}
return upstreams, nil
}
// tcpAddrs converts []netip.AddrPort into []*net.TCPAddr.
func tcpAddrs(addrPorts []netip.AddrPort) (tcpAddrs []*net.TCPAddr) {
if addrPorts == nil {
return nil
}
tcpAddrs = make([]*net.TCPAddr, len(addrPorts))
for i, a := range addrPorts {
tcpAddrs[i] = net.TCPAddrFromAddrPort(a)
}
return tcpAddrs
}
// udpAddrs converts []netip.AddrPort into []*net.UDPAddr.
func udpAddrs(addrPorts []netip.AddrPort) (udpAddrs []*net.UDPAddr) {
if addrPorts == nil {
return nil
}
udpAddrs = make([]*net.UDPAddr, len(addrPorts))
for i, a := range addrPorts {
udpAddrs[i] = net.UDPAddrFromAddrPort(a)
}
return udpAddrs
}
// type check
var _ agh.Service = (*Service)(nil)
// Start implements the [agh.Service] interface for *Service. svc may be nil.
// After Start exits, all DNS servers have tried to start, but there is no
// guarantee that they did. Errors from the servers are written to the log.
func (svc *Service) Start() (err error) {
if svc == nil {
return nil
}
defer func() {
// TODO(a.garipov): [proxy.Proxy.Start] doesn't actually have any way to
// tell when all servers are actually up, so at best this is merely an
// assumption.
svc.running.Store(err == nil)
}()
return svc.proxy.Start()
}
// Shutdown implements the [agh.Service] interface for *Service. svc may be
// nil.
func (svc *Service) Shutdown(ctx context.Context) (err error) {
if svc == nil {
return nil
}
return svc.proxy.Stop()
}
// Config returns the current configuration of the web service. Config must not
// be called simultaneously with Start. If svc was initialized with ":0"
// addresses, addrs will not return the actual bound ports until Start is
// finished.
func (svc *Service) Config() (c *Config) {
// TODO(a.garipov): Do we need to get the TCP addresses separately?
var addrs []netip.AddrPort
if svc.running.Load() {
udpAddrs := svc.proxy.Addrs(proxy.ProtoUDP)
addrs = make([]netip.AddrPort, len(udpAddrs))
for i, a := range udpAddrs {
addrs[i] = a.(*net.UDPAddr).AddrPort()
}
} else {
conf := svc.proxy.Config
udpAddrs := conf.UDPListenAddr
addrs = make([]netip.AddrPort, len(udpAddrs))
for i, a := range udpAddrs {
addrs[i] = a.AddrPort()
}
}
c = &Config{
Addresses: addrs,
BootstrapServers: svc.bootstraps,
UpstreamServers: svc.upstreams,
UpstreamTimeout: svc.upsTimeout,
}
return c
}

View File

@@ -1,96 +0,0 @@
package dnssvc_test
import (
"context"
"net/netip"
"testing"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/aghtest"
"github.com/AdguardTeam/AdGuardHome/internal/next/dnssvc"
"github.com/AdguardTeam/dnsproxy/upstream"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/testutil"
"github.com/miekg/dns"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestMain(m *testing.M) {
testutil.DiscardLogOutput(m)
}
// testTimeout is the common timeout for tests.
const testTimeout = 100 * time.Millisecond
func TestService(t *testing.T) {
const (
bootstrapAddr = "bootstrap.example"
upstreamAddr = "upstream.example"
closeErr errors.Error = "closing failed"
)
ups := &aghtest.UpstreamMock{
OnAddress: func() (addr string) {
return upstreamAddr
},
OnExchange: func(req *dns.Msg) (resp *dns.Msg, err error) {
resp = (&dns.Msg{}).SetReply(req)
return resp, nil
},
OnClose: func() (err error) {
return closeErr
},
}
c := &dnssvc.Config{
Addresses: []netip.AddrPort{netip.MustParseAddrPort("127.0.0.1:0")},
Upstreams: []upstream.Upstream{ups},
BootstrapServers: []string{bootstrapAddr},
UpstreamServers: []string{upstreamAddr},
UpstreamTimeout: testTimeout,
}
svc, err := dnssvc.New(c)
require.NoError(t, err)
err = svc.Start()
require.NoError(t, err)
gotConf := svc.Config()
require.NotNil(t, gotConf)
require.Len(t, gotConf.Addresses, 1)
addr := gotConf.Addresses[0]
t.Run("dns", func(t *testing.T) {
req := &dns.Msg{
MsgHdr: dns.MsgHdr{
Id: dns.Id(),
RecursionDesired: true,
},
Question: []dns.Question{{
Name: "example.com.",
Qtype: dns.TypeA,
Qclass: dns.ClassINET,
}},
}
ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
defer cancel()
cli := &dns.Client{}
resp, _, excErr := cli.ExchangeContext(ctx, req, addr.String())
require.NoError(t, excErr)
assert.NotNil(t, resp)
})
ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
defer cancel()
err = svc.Shutdown(ctx)
require.ErrorIs(t, err, closeErr)
}

View File

@@ -1,84 +0,0 @@
package websvc
import (
"encoding/json"
"fmt"
"net/http"
"net/netip"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/next/dnssvc"
)
// DNS Settings Handlers
// ReqPatchSettingsDNS describes the request to the PATCH /api/v1/settings/dns
// HTTP API.
type ReqPatchSettingsDNS struct {
// TODO(a.garipov): Add more as we go.
Addresses []netip.AddrPort `json:"addresses"`
BootstrapServers []string `json:"bootstrap_servers"`
UpstreamServers []string `json:"upstream_servers"`
UpstreamTimeout JSONDuration `json:"upstream_timeout"`
}
// HTTPAPIDNSSettings are the DNS settings as used by the HTTP API. See the
// DnsSettings object in the OpenAPI specification.
type HTTPAPIDNSSettings struct {
// TODO(a.garipov): Add more as we go.
Addresses []netip.AddrPort `json:"addresses"`
BootstrapServers []string `json:"bootstrap_servers"`
UpstreamServers []string `json:"upstream_servers"`
UpstreamTimeout JSONDuration `json:"upstream_timeout"`
}
// handlePatchSettingsDNS is the handler for the PATCH /api/v1/settings/dns HTTP
// API.
func (svc *Service) handlePatchSettingsDNS(w http.ResponseWriter, r *http.Request) {
req := &ReqPatchSettingsDNS{
Addresses: []netip.AddrPort{},
BootstrapServers: []string{},
UpstreamServers: []string{},
}
// TODO(a.garipov): Validate nulls and proper JSON patch.
err := json.NewDecoder(r.Body).Decode(&req)
if err != nil {
writeJSONErrorResponse(w, r, fmt.Errorf("decoding: %w", err))
return
}
newConf := &dnssvc.Config{
Addresses: req.Addresses,
BootstrapServers: req.BootstrapServers,
UpstreamServers: req.UpstreamServers,
UpstreamTimeout: time.Duration(req.UpstreamTimeout),
}
ctx := r.Context()
err = svc.confMgr.UpdateDNS(ctx, newConf)
if err != nil {
writeJSONErrorResponse(w, r, fmt.Errorf("updating: %w", err))
return
}
newSvc := svc.confMgr.DNS()
err = newSvc.Start()
if err != nil {
writeJSONErrorResponse(w, r, fmt.Errorf("starting new service: %w", err))
return
}
writeJSONOKResponse(w, r, &HTTPAPIDNSSettings{
Addresses: newConf.Addresses,
BootstrapServers: newConf.BootstrapServers,
UpstreamServers: newConf.UpstreamServers,
UpstreamTimeout: JSONDuration(newConf.UpstreamTimeout),
})
}

View File

@@ -1,68 +0,0 @@
package websvc_test
import (
"context"
"encoding/json"
"net/http"
"net/netip"
"net/url"
"sync/atomic"
"testing"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/aghtest"
"github.com/AdguardTeam/AdGuardHome/internal/next/agh"
"github.com/AdguardTeam/AdGuardHome/internal/next/dnssvc"
"github.com/AdguardTeam/AdGuardHome/internal/next/websvc"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestService_HandlePatchSettingsDNS(t *testing.T) {
wantDNS := &websvc.HTTPAPIDNSSettings{
Addresses: []netip.AddrPort{netip.MustParseAddrPort("127.0.1.1:53")},
BootstrapServers: []string{"1.0.0.1"},
UpstreamServers: []string{"1.1.1.1"},
UpstreamTimeout: websvc.JSONDuration(2 * time.Second),
}
var started atomic.Bool
confMgr := newConfigManager()
confMgr.onDNS = func() (s agh.ServiceWithConfig[*dnssvc.Config]) {
return &aghtest.ServiceWithConfig[*dnssvc.Config]{
OnStart: func() (err error) {
started.Store(true)
return nil
},
OnShutdown: func(_ context.Context) (err error) { panic("not implemented") },
OnConfig: func() (c *dnssvc.Config) { panic("not implemented") },
}
}
confMgr.onUpdateDNS = func(ctx context.Context, c *dnssvc.Config) (err error) {
return nil
}
_, addr := newTestServer(t, confMgr)
u := &url.URL{
Scheme: "http",
Host: addr.String(),
Path: websvc.PathV1SettingsDNS,
}
req := jobj{
"addresses": wantDNS.Addresses,
"bootstrap_servers": wantDNS.BootstrapServers,
"upstream_servers": wantDNS.UpstreamServers,
"upstream_timeout": wantDNS.UpstreamTimeout,
}
respBody := httpPatch(t, u, req, http.StatusOK)
resp := &websvc.HTTPAPIDNSSettings{}
err := json.Unmarshal(respBody, resp)
require.NoError(t, err)
assert.True(t, started.Load())
assert.Equal(t, wantDNS, resp)
assert.Equal(t, wantDNS, resp)
}

View File

@@ -1,110 +0,0 @@
package websvc
import (
"context"
"encoding/json"
"fmt"
"net/http"
"net/netip"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/next/agh"
"github.com/AdguardTeam/golibs/log"
)
// HTTP Settings Handlers
// ReqPatchSettingsHTTP describes the request to the PATCH /api/v1/settings/http
// HTTP API.
type ReqPatchSettingsHTTP struct {
// TODO(a.garipov): Add more as we go.
//
// TODO(a.garipov): Add wait time.
Addresses []netip.AddrPort `json:"addresses"`
SecureAddresses []netip.AddrPort `json:"secure_addresses"`
Timeout JSONDuration `json:"timeout"`
}
// HTTPAPIHTTPSettings are the HTTP settings as used by the HTTP API. See the
// HttpSettings object in the OpenAPI specification.
type HTTPAPIHTTPSettings struct {
// TODO(a.garipov): Add more as we go.
Addresses []netip.AddrPort `json:"addresses"`
SecureAddresses []netip.AddrPort `json:"secure_addresses"`
Timeout JSONDuration `json:"timeout"`
ForceHTTPS bool `json:"force_https"`
}
// handlePatchSettingsHTTP is the handler for the PATCH /api/v1/settings/http
// HTTP API.
func (svc *Service) handlePatchSettingsHTTP(w http.ResponseWriter, r *http.Request) {
req := &ReqPatchSettingsHTTP{}
// TODO(a.garipov): Validate nulls and proper JSON patch.
err := json.NewDecoder(r.Body).Decode(&req)
if err != nil {
writeJSONErrorResponse(w, r, fmt.Errorf("decoding: %w", err))
return
}
newConf := &Config{
ConfigManager: svc.confMgr,
TLS: svc.tls,
Addresses: req.Addresses,
SecureAddresses: req.SecureAddresses,
Timeout: time.Duration(req.Timeout),
ForceHTTPS: svc.forceHTTPS,
}
writeJSONOKResponse(w, r, &HTTPAPIHTTPSettings{
Addresses: newConf.Addresses,
SecureAddresses: newConf.SecureAddresses,
Timeout: JSONDuration(newConf.Timeout),
ForceHTTPS: newConf.ForceHTTPS,
})
cancelUpd := func() {}
updCtx := context.Background()
ctx := r.Context()
if deadline, ok := ctx.Deadline(); ok {
updCtx, cancelUpd = context.WithDeadline(updCtx, deadline)
}
// Launch the new HTTP service in a separate goroutine to let this handler
// finish and thus, this server to shutdown.
go func() {
defer cancelUpd()
updErr := svc.confMgr.UpdateWeb(updCtx, newConf)
if updErr != nil {
writeJSONErrorResponse(w, r, fmt.Errorf("updating: %w", updErr))
return
}
// TODO(a.garipov): Consider better ways to do this.
const maxUpdDur = 10 * time.Second
updStart := time.Now()
var newSvc agh.ServiceWithConfig[*Config]
for newSvc = svc.confMgr.Web(); newSvc == svc; {
if time.Since(updStart) >= maxUpdDur {
log.Error("websvc: failed to update svc after %s", maxUpdDur)
return
}
log.Debug("websvc: waiting for new websvc to be configured")
time.Sleep(1 * time.Second)
}
updErr = newSvc.Start()
if updErr != nil {
log.Error("websvc: new svc failed to start with error: %s", updErr)
}
}()
}

View File

@@ -1,63 +0,0 @@
package websvc_test
import (
"context"
"crypto/tls"
"encoding/json"
"net/http"
"net/netip"
"net/url"
"testing"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/next/agh"
"github.com/AdguardTeam/AdGuardHome/internal/next/websvc"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestService_HandlePatchSettingsHTTP(t *testing.T) {
wantWeb := &websvc.HTTPAPIHTTPSettings{
Addresses: []netip.AddrPort{netip.MustParseAddrPort("127.0.1.1:80")},
SecureAddresses: []netip.AddrPort{netip.MustParseAddrPort("127.0.1.1:443")},
Timeout: websvc.JSONDuration(10 * time.Second),
ForceHTTPS: false,
}
confMgr := newConfigManager()
confMgr.onWeb = func() (s agh.ServiceWithConfig[*websvc.Config]) {
return websvc.New(&websvc.Config{
TLS: &tls.Config{
Certificates: []tls.Certificate{{}},
},
Addresses: []netip.AddrPort{netip.MustParseAddrPort("127.0.0.1:80")},
SecureAddresses: []netip.AddrPort{netip.MustParseAddrPort("127.0.0.1:443")},
Timeout: 5 * time.Second,
ForceHTTPS: true,
})
}
confMgr.onUpdateWeb = func(ctx context.Context, c *websvc.Config) (err error) {
return nil
}
_, addr := newTestServer(t, confMgr)
u := &url.URL{
Scheme: "http",
Host: addr.String(),
Path: websvc.PathV1SettingsHTTP,
}
req := jobj{
"addresses": wantWeb.Addresses,
"secure_addresses": wantWeb.SecureAddresses,
"timeout": wantWeb.Timeout,
"force_https": wantWeb.ForceHTTPS,
}
respBody := httpPatch(t, u, req, http.StatusOK)
resp := &websvc.HTTPAPIHTTPSettings{}
err := json.Unmarshal(respBody, resp)
require.NoError(t, err)
assert.Equal(t, wantWeb, resp)
}

View File

@@ -1,144 +0,0 @@
package websvc
import (
"encoding/json"
"fmt"
"net/http"
"strconv"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
"github.com/AdguardTeam/golibs/httphdr"
"github.com/AdguardTeam/golibs/log"
)
// JSON Utilities
// nsecPerMsec is the number of nanoseconds in a millisecond.
const nsecPerMsec = float64(time.Millisecond / time.Nanosecond)
// JSONDuration is a time.Duration that can be decoded from JSON and encoded
// into JSON according to our API conventions.
type JSONDuration time.Duration
// type check
var _ json.Marshaler = JSONDuration(0)
// MarshalJSON implements the json.Marshaler interface for JSONDuration. err is
// always nil.
func (d JSONDuration) MarshalJSON() (b []byte, err error) {
msec := float64(time.Duration(d)) / nsecPerMsec
b = strconv.AppendFloat(nil, msec, 'f', -1, 64)
return b, nil
}
// type check
var _ json.Unmarshaler = (*JSONDuration)(nil)
// UnmarshalJSON implements the json.Marshaler interface for *JSONDuration.
func (d *JSONDuration) UnmarshalJSON(b []byte) (err error) {
if d == nil {
return fmt.Errorf("json duration is nil")
}
msec, err := strconv.ParseFloat(string(b), 64)
if err != nil {
return fmt.Errorf("parsing json time: %w", err)
}
*d = JSONDuration(int64(msec * nsecPerMsec))
return nil
}
// JSONTime is a time.Time that can be decoded from JSON and encoded into JSON
// according to our API conventions.
type JSONTime time.Time
// type check
var _ json.Marshaler = JSONTime{}
// MarshalJSON implements the json.Marshaler interface for JSONTime. err is
// always nil.
func (t JSONTime) MarshalJSON() (b []byte, err error) {
msec := float64(time.Time(t).UnixNano()) / nsecPerMsec
b = strconv.AppendFloat(nil, msec, 'f', -1, 64)
return b, nil
}
// type check
var _ json.Unmarshaler = (*JSONTime)(nil)
// UnmarshalJSON implements the json.Marshaler interface for *JSONTime.
func (t *JSONTime) UnmarshalJSON(b []byte) (err error) {
if t == nil {
return fmt.Errorf("json time is nil")
}
msec, err := strconv.ParseFloat(string(b), 64)
if err != nil {
return fmt.Errorf("parsing json time: %w", err)
}
*t = JSONTime(time.Unix(0, int64(msec*nsecPerMsec)).UTC())
return nil
}
// writeJSONOKResponse writes headers with the code 200 OK, encodes v into w,
// and logs any errors it encounters. r is used to get additional information
// from the request.
func writeJSONOKResponse(w http.ResponseWriter, r *http.Request, v any) {
writeJSONResponse(w, r, v, http.StatusOK)
}
// writeJSONResponse writes headers with code, encodes v into w, and logs any
// errors it encounters. r is used to get additional information from the
// request.
func writeJSONResponse(w http.ResponseWriter, r *http.Request, v any, code int) {
// TODO(a.garipov): Put some of these to a middleware.
h := w.Header()
h.Set(httphdr.ContentType, aghhttp.HdrValApplicationJSON)
h.Set(httphdr.Server, aghhttp.UserAgent())
w.WriteHeader(code)
err := json.NewEncoder(w).Encode(v)
if err != nil {
log.Error("websvc: writing resp to %s %s: %s", r.Method, r.URL.Path, err)
}
}
// ErrorCode is the error code as used by the HTTP API. See the ErrorCode
// definition in the OpenAPI specification.
type ErrorCode string
// ErrorCode constants.
//
// TODO(a.garipov): Expand and document codes.
const (
// ErrorCodeTMP000 is the temporary error code used for all errors.
ErrorCodeTMP000 = ""
)
// HTTPAPIErrorResp is the error response as used by the HTTP API. See the
// BadRequestResp, InternalServerErrorResp, and similar objects in the OpenAPI
// specification.
type HTTPAPIErrorResp struct {
Code ErrorCode `json:"code"`
Msg string `json:"msg"`
}
// writeJSONErrorResponse encodes err as a JSON error into w, and logs any
// errors it encounters. r is used to get additional information from the
// request.
func writeJSONErrorResponse(w http.ResponseWriter, r *http.Request, err error) {
log.Error("websvc: %s %s: %s", r.Method, r.URL.Path, err)
writeJSONResponse(w, r, &HTTPAPIErrorResp{
Code: ErrorCodeTMP000,
Msg: err.Error(),
}, http.StatusUnprocessableEntity)
}

View File

@@ -1,114 +0,0 @@
package websvc_test
import (
"encoding/json"
"testing"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/next/websvc"
"github.com/AdguardTeam/golibs/testutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// testJSONTime is the JSON time for tests.
var testJSONTime = websvc.JSONTime(time.Unix(1_234_567_890, 123_456_000).UTC())
// testJSONTimeStr is the string with the JSON encoding of testJSONTime.
const testJSONTimeStr = "1234567890123.456"
func TestJSONTime_MarshalJSON(t *testing.T) {
testCases := []struct {
name string
wantErrMsg string
in websvc.JSONTime
want []byte
}{{
name: "unix_zero",
wantErrMsg: "",
in: websvc.JSONTime(time.Unix(0, 0)),
want: []byte("0"),
}, {
name: "empty",
wantErrMsg: "",
in: websvc.JSONTime{},
want: []byte("-6795364578871.345"),
}, {
name: "time",
wantErrMsg: "",
in: testJSONTime,
want: []byte(testJSONTimeStr),
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
got, err := tc.in.MarshalJSON()
testutil.AssertErrorMsg(t, tc.wantErrMsg, err)
assert.Equal(t, tc.want, got)
})
}
t.Run("json", func(t *testing.T) {
in := &struct {
A websvc.JSONTime
}{
A: testJSONTime,
}
got, err := json.Marshal(in)
require.NoError(t, err)
assert.Equal(t, []byte(`{"A":`+testJSONTimeStr+`}`), got)
})
}
func TestJSONTime_UnmarshalJSON(t *testing.T) {
testCases := []struct {
name string
wantErrMsg string
want websvc.JSONTime
data []byte
}{{
name: "time",
wantErrMsg: "",
want: testJSONTime,
data: []byte(testJSONTimeStr),
}, {
name: "bad",
wantErrMsg: `parsing json time: strconv.ParseFloat: parsing "{}": ` +
`invalid syntax`,
want: websvc.JSONTime{},
data: []byte(`{}`),
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
var got websvc.JSONTime
err := got.UnmarshalJSON(tc.data)
testutil.AssertErrorMsg(t, tc.wantErrMsg, err)
assert.Equal(t, tc.want, got)
})
}
t.Run("nil", func(t *testing.T) {
err := (*websvc.JSONTime)(nil).UnmarshalJSON([]byte("0"))
require.Error(t, err)
msg := err.Error()
assert.Equal(t, "json time is nil", msg)
})
t.Run("json", func(t *testing.T) {
want := testJSONTime
var got struct {
A websvc.JSONTime
}
err := json.Unmarshal([]byte(`{"A":`+testJSONTimeStr+`}`), &got)
require.NoError(t, err)
assert.Equal(t, want, got.A)
})
}

View File

@@ -1,21 +0,0 @@
package websvc
import (
"net/http"
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
"github.com/AdguardTeam/golibs/httphdr"
)
// Middlewares
// jsonMw sets the content type of the response to application/json.
func jsonMw(h http.Handler) (wrapped http.HandlerFunc) {
f := func(w http.ResponseWriter, r *http.Request) {
w.Header().Set(httphdr.ContentType, aghhttp.HdrValApplicationJSON)
h.ServeHTTP(w, r)
}
return http.HandlerFunc(f)
}

View File

@@ -1,11 +0,0 @@
package websvc
// Path constants
const (
PathHealthCheck = "/health-check"
PathV1SettingsAll = "/api/v1/settings/all"
PathV1SettingsDNS = "/api/v1/settings/dns"
PathV1SettingsHTTP = "/api/v1/settings/http"
PathV1SystemInfo = "/api/v1/system/info"
)

View File

@@ -1,42 +0,0 @@
package websvc
import (
"net/http"
)
// All Settings Handlers
// RespGetV1SettingsAll describes the response of the GET /api/v1/settings/all
// HTTP API.
type RespGetV1SettingsAll struct {
// TODO(a.garipov): Add more as we go.
DNS *HTTPAPIDNSSettings `json:"dns"`
HTTP *HTTPAPIHTTPSettings `json:"http"`
}
// handleGetSettingsAll is the handler for the GET /api/v1/settings/all HTTP
// API.
func (svc *Service) handleGetSettingsAll(w http.ResponseWriter, r *http.Request) {
dnsSvc := svc.confMgr.DNS()
dnsConf := dnsSvc.Config()
webSvc := svc.confMgr.Web()
httpConf := webSvc.Config()
// TODO(a.garipov): Add all currently supported parameters.
writeJSONOKResponse(w, r, &RespGetV1SettingsAll{
DNS: &HTTPAPIDNSSettings{
Addresses: dnsConf.Addresses,
BootstrapServers: dnsConf.BootstrapServers,
UpstreamServers: dnsConf.UpstreamServers,
UpstreamTimeout: JSONDuration(dnsConf.UpstreamTimeout),
},
HTTP: &HTTPAPIHTTPSettings{
Addresses: httpConf.Addresses,
SecureAddresses: httpConf.SecureAddresses,
Timeout: JSONDuration(httpConf.Timeout),
ForceHTTPS: httpConf.ForceHTTPS,
},
})
}

View File

@@ -1,75 +0,0 @@
package websvc_test
import (
"crypto/tls"
"encoding/json"
"net/http"
"net/netip"
"net/url"
"testing"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/next/agh"
"github.com/AdguardTeam/AdGuardHome/internal/next/dnssvc"
"github.com/AdguardTeam/AdGuardHome/internal/next/websvc"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestService_HandleGetSettingsAll(t *testing.T) {
// TODO(a.garipov): Add all currently supported parameters.
wantDNS := &websvc.HTTPAPIDNSSettings{
Addresses: []netip.AddrPort{netip.MustParseAddrPort("127.0.0.1:53")},
BootstrapServers: []string{"94.140.14.140", "94.140.14.141"},
UpstreamServers: []string{"94.140.14.14", "1.1.1.1"},
UpstreamTimeout: websvc.JSONDuration(1 * time.Second),
}
wantWeb := &websvc.HTTPAPIHTTPSettings{
Addresses: []netip.AddrPort{netip.MustParseAddrPort("127.0.0.1:80")},
SecureAddresses: []netip.AddrPort{netip.MustParseAddrPort("127.0.0.1:443")},
Timeout: websvc.JSONDuration(5 * time.Second),
ForceHTTPS: true,
}
confMgr := newConfigManager()
confMgr.onDNS = func() (s agh.ServiceWithConfig[*dnssvc.Config]) {
c, err := dnssvc.New(&dnssvc.Config{
Addresses: wantDNS.Addresses,
UpstreamServers: wantDNS.UpstreamServers,
BootstrapServers: wantDNS.BootstrapServers,
UpstreamTimeout: time.Duration(wantDNS.UpstreamTimeout),
})
require.NoError(t, err)
return c
}
confMgr.onWeb = func() (s agh.ServiceWithConfig[*websvc.Config]) {
return websvc.New(&websvc.Config{
TLS: &tls.Config{
Certificates: []tls.Certificate{{}},
},
Addresses: wantWeb.Addresses,
SecureAddresses: wantWeb.SecureAddresses,
Timeout: time.Duration(wantWeb.Timeout),
ForceHTTPS: true,
})
}
_, addr := newTestServer(t, confMgr)
u := &url.URL{
Scheme: "http",
Host: addr.String(),
Path: websvc.PathV1SettingsAll,
}
body := httpGet(t, u, http.StatusOK)
resp := &websvc.RespGetV1SettingsAll{}
err := json.Unmarshal(body, resp)
require.NoError(t, err)
assert.Equal(t, wantDNS, resp.DNS)
assert.Equal(t, wantWeb, resp.HTTP)
}

View File

@@ -1,35 +0,0 @@
package websvc
import (
"net/http"
"runtime"
"github.com/AdguardTeam/AdGuardHome/internal/version"
)
// System Handlers
// RespGetV1SystemInfo describes the response of the GET /api/v1/system/info
// HTTP API.
type RespGetV1SystemInfo struct {
Arch string `json:"arch"`
Channel string `json:"channel"`
OS string `json:"os"`
NewVersion string `json:"new_version,omitempty"`
Start JSONTime `json:"start"`
Version string `json:"version"`
}
// handleGetV1SystemInfo is the handler for the GET /api/v1/system/info HTTP
// API.
func (svc *Service) handleGetV1SystemInfo(w http.ResponseWriter, r *http.Request) {
writeJSONOKResponse(w, r, &RespGetV1SystemInfo{
Arch: runtime.GOARCH,
Channel: version.Channel(),
OS: runtime.GOOS,
// TODO(a.garipov): Fill this when we have an updater.
NewVersion: "",
Start: JSONTime(svc.start),
Version: version.Version(),
})
}

View File

@@ -1,37 +0,0 @@
package websvc_test
import (
"encoding/json"
"net/http"
"net/url"
"runtime"
"testing"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/next/websvc"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestService_handleGetV1SystemInfo(t *testing.T) {
confMgr := newConfigManager()
_, addr := newTestServer(t, confMgr)
u := &url.URL{
Scheme: "http",
Host: addr.String(),
Path: websvc.PathV1SystemInfo,
}
body := httpGet(t, u, http.StatusOK)
resp := &websvc.RespGetV1SystemInfo{}
err := json.Unmarshal(body, resp)
require.NoError(t, err)
// TODO(a.garipov): Consider making version.Channel and version.Version
// testable and test these better.
assert.NotEmpty(t, resp.Channel)
assert.Equal(t, resp.Arch, runtime.GOARCH)
assert.Equal(t, resp.OS, runtime.GOOS)
assert.Equal(t, testStart, time.Time(resp.Start))
}

View File

@@ -1,31 +0,0 @@
package websvc
import (
"net"
"sync"
)
// Wait Listener
// waitListener is a wrapper around a listener that also calls wg.Done() on the
// first call to Accept. It is useful in situations where it is important to
// catch the precise moment of the first call to Accept, for example when
// starting an HTTP server.
//
// TODO(a.garipov): Move to aghnet?
type waitListener struct {
net.Listener
firstAcceptWG *sync.WaitGroup
firstAcceptOnce sync.Once
}
// type check
var _ net.Listener = (*waitListener)(nil)
// Accept implements the [net.Listener] interface for *waitListener.
func (l *waitListener) Accept() (conn net.Conn, err error) {
l.firstAcceptOnce.Do(l.firstAcceptWG.Done)
return l.Listener.Accept()
}

View File

@@ -1,45 +0,0 @@
package websvc
import (
"net"
"sync"
"sync/atomic"
"testing"
"github.com/AdguardTeam/AdGuardHome/internal/aghchan"
"github.com/AdguardTeam/AdGuardHome/internal/aghtest"
"github.com/stretchr/testify/assert"
)
func TestWaitListener_Accept(t *testing.T) {
var accepted atomic.Bool
var l net.Listener = &aghtest.Listener{
OnAccept: func() (conn net.Conn, err error) {
accepted.Store(true)
return nil, nil
},
OnAddr: func() (addr net.Addr) { panic("not implemented") },
OnClose: func() (err error) { panic("not implemented") },
}
wg := &sync.WaitGroup{}
wg.Add(1)
done := make(chan struct{})
go aghchan.MustReceive(done, testTimeout)
go func() {
var wrapper net.Listener = &waitListener{
Listener: l,
firstAcceptWG: wg,
}
_, _ = wrapper.Accept()
}()
wg.Wait()
close(done)
assert.True(t, accepted.Load())
}

View File

@@ -1,305 +0,0 @@
// Package websvc contains the AdGuard Home HTTP API service.
//
// NOTE: Packages other than cmd must not import this package, as it imports
// most other packages.
//
// TODO(a.garipov): Add tests.
package websvc
import (
"context"
"crypto/tls"
"fmt"
"io"
"net"
"net/http"
"net/netip"
"sync"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/next/agh"
"github.com/AdguardTeam/AdGuardHome/internal/next/dnssvc"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
httptreemux "github.com/dimfeld/httptreemux/v5"
)
// ConfigManager is the configuration manager interface.
type ConfigManager interface {
DNS() (svc agh.ServiceWithConfig[*dnssvc.Config])
Web() (svc agh.ServiceWithConfig[*Config])
UpdateDNS(ctx context.Context, c *dnssvc.Config) (err error)
UpdateWeb(ctx context.Context, c *Config) (err error)
}
// Config is the AdGuard Home web service configuration structure.
type Config struct {
// ConfigManager is used to show information about services as well as
// dynamically reconfigure them.
ConfigManager ConfigManager
// TLS is the optional TLS configuration. If TLS is not nil,
// SecureAddresses must not be empty.
TLS *tls.Config
// Start is the time of start of AdGuard Home.
Start time.Time
// Addresses are the addresses on which to serve the plain HTTP API.
Addresses []netip.AddrPort
// SecureAddresses are the addresses on which to serve the HTTPS API. If
// SecureAddresses is not empty, TLS must not be nil.
SecureAddresses []netip.AddrPort
// Timeout is the timeout for all server operations.
Timeout time.Duration
// ForceHTTPS tells if all requests to Addresses should be redirected to a
// secure address instead.
//
// TODO(a.garipov): Use; define rules, which address to redirect to.
ForceHTTPS bool
}
// Service is the AdGuard Home web service. A nil *Service is a valid
// [agh.Service] that does nothing.
type Service struct {
confMgr ConfigManager
tls *tls.Config
start time.Time
servers []*http.Server
timeout time.Duration
forceHTTPS bool
}
// New returns a new properly initialized *Service. If c is nil, svc is a nil
// *Service that does nothing. The fields of c must not be modified after
// calling New.
func New(c *Config) (svc *Service) {
if c == nil {
return nil
}
svc = &Service{
confMgr: c.ConfigManager,
tls: c.TLS,
start: c.Start,
timeout: c.Timeout,
forceHTTPS: c.ForceHTTPS,
}
mux := newMux(svc)
for _, a := range c.Addresses {
addr := a.String()
errLog := log.StdLog("websvc: plain http: "+addr, log.ERROR)
svc.servers = append(svc.servers, &http.Server{
Addr: addr,
Handler: mux,
ErrorLog: errLog,
ReadTimeout: c.Timeout,
WriteTimeout: c.Timeout,
IdleTimeout: c.Timeout,
ReadHeaderTimeout: c.Timeout,
})
}
for _, a := range c.SecureAddresses {
addr := a.String()
errLog := log.StdLog("websvc: https: "+addr, log.ERROR)
svc.servers = append(svc.servers, &http.Server{
Addr: addr,
Handler: mux,
TLSConfig: c.TLS,
ErrorLog: errLog,
ReadTimeout: c.Timeout,
WriteTimeout: c.Timeout,
IdleTimeout: c.Timeout,
ReadHeaderTimeout: c.Timeout,
})
}
return svc
}
// newMux returns a new HTTP request multiplexor for the AdGuard Home web
// service.
func newMux(svc *Service) (mux *httptreemux.ContextMux) {
mux = httptreemux.NewContextMux()
routes := []struct {
handler http.HandlerFunc
method string
path string
isJSON bool
}{{
handler: svc.handleGetHealthCheck,
method: http.MethodGet,
path: PathHealthCheck,
isJSON: false,
}, {
handler: svc.handleGetSettingsAll,
method: http.MethodGet,
path: PathV1SettingsAll,
isJSON: true,
}, {
handler: svc.handlePatchSettingsDNS,
method: http.MethodPatch,
path: PathV1SettingsDNS,
isJSON: true,
}, {
handler: svc.handlePatchSettingsHTTP,
method: http.MethodPatch,
path: PathV1SettingsHTTP,
isJSON: true,
}, {
handler: svc.handleGetV1SystemInfo,
method: http.MethodGet,
path: PathV1SystemInfo,
isJSON: true,
}}
for _, r := range routes {
if r.isJSON {
mux.Handle(r.method, r.path, jsonMw(r.handler))
} else {
mux.Handle(r.method, r.path, r.handler)
}
}
return mux
}
// addrs returns all addresses on which this server serves the HTTP API. addrs
// must not be called simultaneously with Start. If svc was initialized with
// ":0" addresses, addrs will not return the actual bound ports until Start is
// finished.
func (svc *Service) addrs() (addrs, secureAddrs []netip.AddrPort) {
for _, srv := range svc.servers {
addrPort, err := netip.ParseAddrPort(srv.Addr)
if err != nil {
// Technically shouldn't happen, since all servers must have a valid
// address.
panic(fmt.Errorf("websvc: server %q: bad address: %w", srv.Addr, err))
}
// srv.Serve will set TLSConfig to an almost empty value, so, instead of
// relying only on the nilness of TLSConfig, check the length of the
// certificates field as well.
if srv.TLSConfig == nil || len(srv.TLSConfig.Certificates) == 0 {
addrs = append(addrs, addrPort)
} else {
secureAddrs = append(secureAddrs, addrPort)
}
}
return addrs, secureAddrs
}
// handleGetHealthCheck is the handler for the GET /health-check HTTP API.
func (svc *Service) handleGetHealthCheck(w http.ResponseWriter, _ *http.Request) {
_, _ = io.WriteString(w, "OK")
}
// type check
var _ agh.Service = (*Service)(nil)
// Start implements the [agh.Service] interface for *Service. svc may be nil.
// After Start exits, all HTTP servers have tried to start, possibly failing and
// writing error messages to the log.
func (svc *Service) Start() (err error) {
if svc == nil {
return nil
}
wg := &sync.WaitGroup{}
wg.Add(len(svc.servers))
for _, srv := range svc.servers {
go serve(srv, wg)
}
wg.Wait()
return nil
}
// serve starts and runs srv and writes all errors into its log.
func serve(srv *http.Server, wg *sync.WaitGroup) {
addr := srv.Addr
defer log.OnPanic(addr)
var proto string
var l net.Listener
var err error
if srv.TLSConfig == nil {
proto = "http"
l, err = net.Listen("tcp", addr)
} else {
proto = "https"
l, err = tls.Listen("tcp", addr, srv.TLSConfig)
}
if err != nil {
srv.ErrorLog.Printf("starting srv %s: binding: %s", addr, err)
}
// Update the server's address in case the address had the port zero, which
// would mean that a random available port was automatically chosen.
srv.Addr = l.Addr().String()
log.Info("websvc: starting srv %s://%s", proto, srv.Addr)
l = &waitListener{
Listener: l,
firstAcceptWG: wg,
}
err = srv.Serve(l)
if err != nil && !errors.Is(err, http.ErrServerClosed) {
srv.ErrorLog.Printf("starting srv %s: %s", addr, err)
}
}
// Shutdown implements the [agh.Service] interface for *Service. svc may be
// nil.
func (svc *Service) Shutdown(ctx context.Context) (err error) {
if svc == nil {
return nil
}
var errs []error
for _, srv := range svc.servers {
serr := srv.Shutdown(ctx)
if serr != nil {
errs = append(errs, fmt.Errorf("shutting down srv %s: %w", srv.Addr, serr))
}
}
if len(errs) > 0 {
return errors.List("shutting down", errs...)
}
return nil
}
// Config returns the current configuration of the web service. Config must not
// be called simultaneously with Start. If svc was initialized with ":0"
// addresses, addrs will not return the actual bound ports until Start is
// finished.
func (svc *Service) Config() (c *Config) {
c = &Config{
ConfigManager: svc.confMgr,
TLS: svc.tls,
// Leave Addresses and SecureAddresses empty and get the actual
// addresses that include the :0 ones later.
Start: svc.start,
Timeout: svc.timeout,
ForceHTTPS: svc.forceHTTPS,
}
c.Addresses, c.SecureAddresses = svc.addrs()
return c
}

View File

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

View File

@@ -1,187 +0,0 @@
package websvc_test
import (
"bytes"
"context"
"encoding/json"
"io"
"net/http"
"net/netip"
"net/url"
"testing"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/next/agh"
"github.com/AdguardTeam/AdGuardHome/internal/next/dnssvc"
"github.com/AdguardTeam/AdGuardHome/internal/next/websvc"
"github.com/AdguardTeam/golibs/testutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestMain(m *testing.M) {
testutil.DiscardLogOutput(m)
}
// testTimeout is the common timeout for tests.
const testTimeout = 1 * time.Second
// testStart is the server start value for tests.
var testStart = time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC)
// type check
var _ websvc.ConfigManager = (*configManager)(nil)
// configManager is a [websvc.ConfigManager] for tests.
type configManager struct {
onDNS func() (svc agh.ServiceWithConfig[*dnssvc.Config])
onWeb func() (svc agh.ServiceWithConfig[*websvc.Config])
onUpdateDNS func(ctx context.Context, c *dnssvc.Config) (err error)
onUpdateWeb func(ctx context.Context, c *websvc.Config) (err error)
}
// DNS implements the [websvc.ConfigManager] interface for *configManager.
func (m *configManager) DNS() (svc agh.ServiceWithConfig[*dnssvc.Config]) {
return m.onDNS()
}
// Web implements the [websvc.ConfigManager] interface for *configManager.
func (m *configManager) Web() (svc agh.ServiceWithConfig[*websvc.Config]) {
return m.onWeb()
}
// UpdateDNS implements the [websvc.ConfigManager] interface for *configManager.
func (m *configManager) UpdateDNS(ctx context.Context, c *dnssvc.Config) (err error) {
return m.onUpdateDNS(ctx, c)
}
// UpdateWeb implements the [websvc.ConfigManager] interface for *configManager.
func (m *configManager) UpdateWeb(ctx context.Context, c *websvc.Config) (err error) {
return m.onUpdateWeb(ctx, c)
}
// newConfigManager returns a *configManager all methods of which panic.
func newConfigManager() (m *configManager) {
return &configManager{
onDNS: func() (svc agh.ServiceWithConfig[*dnssvc.Config]) { panic("not implemented") },
onWeb: func() (svc agh.ServiceWithConfig[*websvc.Config]) { panic("not implemented") },
onUpdateDNS: func(_ context.Context, _ *dnssvc.Config) (err error) {
panic("not implemented")
},
onUpdateWeb: func(_ context.Context, _ *websvc.Config) (err error) {
panic("not implemented")
},
}
}
// newTestServer creates and starts a new web service instance as well as its
// sole address. It also registers a cleanup procedure, which shuts the
// instance down.
//
// TODO(a.garipov): Use svc or remove it.
func newTestServer(
t testing.TB,
confMgr websvc.ConfigManager,
) (svc *websvc.Service, addr netip.AddrPort) {
t.Helper()
c := &websvc.Config{
ConfigManager: confMgr,
TLS: nil,
Addresses: []netip.AddrPort{netip.MustParseAddrPort("127.0.0.1:0")},
SecureAddresses: nil,
Timeout: testTimeout,
Start: testStart,
ForceHTTPS: false,
}
svc = websvc.New(c)
err := svc.Start()
require.NoError(t, err)
t.Cleanup(func() {
ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
t.Cleanup(cancel)
err = svc.Shutdown(ctx)
require.NoError(t, err)
})
c = svc.Config()
require.NotNil(t, c)
require.Len(t, c.Addresses, 1)
return svc, c.Addresses[0]
}
// jobj is a utility alias for JSON objects.
type jobj map[string]any
// httpGet is a helper that performs an HTTP GET request and returns the body of
// the response as well as checks that the status code is correct.
//
// TODO(a.garipov): Add helpers for other methods.
func httpGet(t testing.TB, u *url.URL, wantCode int) (body []byte) {
t.Helper()
req, err := http.NewRequest(http.MethodGet, u.String(), nil)
require.NoErrorf(t, err, "creating req")
httpCli := &http.Client{
Timeout: testTimeout,
}
resp, err := httpCli.Do(req)
require.NoErrorf(t, err, "performing req")
require.Equal(t, wantCode, resp.StatusCode)
testutil.CleanupAndRequireSuccess(t, resp.Body.Close)
body, err = io.ReadAll(resp.Body)
require.NoErrorf(t, err, "reading body")
return body
}
// httpPatch is a helper that performs an HTTP PATCH request with JSON-encoded
// reqBody as the request body and returns the body of the response as well as
// checks that the status code is correct.
//
// TODO(a.garipov): Add helpers for other methods.
func httpPatch(t testing.TB, u *url.URL, reqBody any, wantCode int) (body []byte) {
t.Helper()
b, err := json.Marshal(reqBody)
require.NoErrorf(t, err, "marshaling reqBody")
req, err := http.NewRequest(http.MethodPatch, u.String(), bytes.NewReader(b))
require.NoErrorf(t, err, "creating req")
httpCli := &http.Client{
Timeout: testTimeout,
}
resp, err := httpCli.Do(req)
require.NoErrorf(t, err, "performing req")
require.Equal(t, wantCode, resp.StatusCode)
testutil.CleanupAndRequireSuccess(t, resp.Body.Close)
body, err = io.ReadAll(resp.Body)
require.NoErrorf(t, err, "reading body")
return body
}
func TestService_Start_getHealthCheck(t *testing.T) {
confMgr := newConfigManager()
_, addr := newTestServer(t, confMgr)
u := &url.URL{
Scheme: "http",
Host: addr.String(),
Path: websvc.PathHealthCheck,
}
body := httpGet(t, u, http.StatusOK)
assert.Equal(t, []byte("OK"), body)
}

View File

@@ -3,24 +3,19 @@ package querylog
import (
"encoding/base64"
"encoding/json"
"fmt"
"io"
"net"
"strings"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/urlfilter/rules"
"github.com/miekg/dns"
)
// logEntryHandler represents a handler for decoding json token to the logEntry
// struct.
type logEntryHandler func(t json.Token, ent *logEntry) error
// logEntryHandlers is the map of log entry decode handlers for various keys.
var logEntryHandlers = map[string]logEntryHandler{
"CID": func(t json.Token, ent *logEntry) error {
v, ok := t.(string)
@@ -171,7 +166,6 @@ var logEntryHandlers = map[string]logEntryHandler{
},
}
// decodeResultRuleKey decodes the token of "Rules" type to logEntry struct.
func decodeResultRuleKey(key string, i int, dec *json.Decoder, ent *logEntry) {
var vToken json.Token
switch key {
@@ -195,8 +189,6 @@ func decodeResultRuleKey(key string, i int, dec *json.Decoder, ent *logEntry) {
}
}
// decodeVTokenAndAddRule decodes the "Rules" toke as [filtering.ResultRule]
// and then adds the decoded object to the slice of result rules.
func decodeVTokenAndAddRule(
key string,
i int,
@@ -221,8 +213,6 @@ func decodeVTokenAndAddRule(
return newRules, vToken
}
// decodeResultRules parses the dec's tokens into logEntry ent interpreting it
// as a slice of the result rules.
func decodeResultRules(dec *json.Decoder, ent *logEntry) {
for {
delimToken, err := dec.Token()
@@ -234,53 +224,48 @@ func decodeResultRules(dec *json.Decoder, ent *logEntry) {
return
}
if d, ok := delimToken.(json.Delim); !ok {
return
} else if d != '[' {
log.Debug("decodeResultRules: unexpected delim %q", d)
}
err = decodeResultRuleToken(dec, ent)
if err != nil {
if err != io.EOF && !errors.Is(err, ErrEndOfToken) {
log.Debug("decodeResultRules err: %s", err)
if d, ok := delimToken.(json.Delim); ok {
if d != '[' {
log.Debug("decodeResultRules: unexpected delim %q", d)
}
} else {
return
}
}
}
// decodeResultRuleToken decodes the tokens of "Rules" type to the logEntry ent.
func decodeResultRuleToken(dec *json.Decoder, ent *logEntry) (err error) {
i := 0
for {
var keyToken json.Token
keyToken, err = dec.Token()
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return err
}
i := 0
for {
var keyToken json.Token
keyToken, err = dec.Token()
if err != nil {
if err != io.EOF {
log.Debug("decodeResultRules err: %s", err)
}
if d, ok := keyToken.(json.Delim); ok {
switch d {
case '}':
i++
case ']':
return ErrEndOfToken
default:
// Go on.
return
}
continue
}
if d, ok := keyToken.(json.Delim); ok {
switch d {
case '}':
i++
case ']':
return
default:
// Go on.
}
key, ok := keyToken.(string)
if !ok {
return fmt.Errorf("keyToken is %T (%[1]v) and not string", keyToken)
}
continue
}
decodeResultRuleKey(key, i, dec, ent)
key, ok := keyToken.(string)
if !ok {
log.Debug("decodeResultRules: keyToken is %T (%[1]v) and not string", keyToken)
return
}
decodeResultRuleKey(key, i, dec, ent)
}
}
}
@@ -337,8 +322,6 @@ func decodeResultReverseHosts(dec *json.Decoder, ent *logEntry) {
}
}
// decodeResultIPList parses the dec's tokens into logEntry ent interpreting it
// as the result IP addresses list.
func decodeResultIPList(dec *json.Decoder, ent *logEntry) {
for {
itemToken, err := dec.Token()
@@ -372,8 +355,6 @@ func decodeResultIPList(dec *json.Decoder, ent *logEntry) {
}
}
// decodeResultDNSRewriteResultKey decodes the token of "DNSRewriteResult" type
// to the logEntry struct.
func decodeResultDNSRewriteResultKey(key string, dec *json.Decoder, ent *logEntry) {
var err error
@@ -414,29 +395,50 @@ func decodeResultDNSRewriteResultKey(key string, dec *json.Decoder, ent *logEntr
log.Debug("decodeResultDNSRewriteResultKey response err: %s", err)
}
ent.parseDNSRewriteResultIPs()
for rrType, rrValues := range ent.Result.DNSRewriteResult.Response {
switch rrType {
case
dns.TypeA,
dns.TypeAAAA:
for i, v := range rrValues {
s, _ := v.(string)
rrValues[i] = net.ParseIP(s)
}
default:
// Go on.
}
}
default:
// Go on.
}
}
// decodeResultDNSRewriteResult parses the dec's tokens into logEntry ent
// interpreting it as the result DNSRewriteResult.
func decodeResultDNSRewriteResult(dec *json.Decoder, ent *logEntry) {
for {
key, err := parseKeyToken(dec)
keyToken, err := dec.Token()
if err != nil {
if err != io.EOF && !errors.Is(err, ErrEndOfToken) {
log.Debug("decodeResultDNSRewriteResult: %s", err)
if err != io.EOF {
log.Debug("decodeResultDNSRewriteResult err: %s", err)
}
return
}
if key == "" {
if d, ok := keyToken.(json.Delim); ok {
if d == '}' {
return
}
continue
}
key, ok := keyToken.(string)
if !ok {
log.Debug("decodeResultDNSRewriteResult: keyToken is %T (%[1]v) and not string", keyToken)
return
}
decodeResultDNSRewriteResultKey(key, dec, ent)
}
}
@@ -472,51 +474,34 @@ func translateResult(ent *logEntry) {
res.IPList = nil
}
// ErrEndOfToken is an error returned by parse key token when the closing
// bracket is found.
const ErrEndOfToken errors.Error = "end of token"
// parseKeyToken parses the dec's token key.
func parseKeyToken(dec *json.Decoder) (key string, err error) {
keyToken, err := dec.Token()
if err != nil {
return "", err
}
if d, ok := keyToken.(json.Delim); ok {
if d == '}' {
return "", ErrEndOfToken
}
return "", nil
}
key, ok := keyToken.(string)
if !ok {
return "", fmt.Errorf("keyToken is %T (%[1]v) and not string", keyToken)
}
return key, nil
}
// decodeResult decodes a token of "Result" type to logEntry struct.
func decodeResult(dec *json.Decoder, ent *logEntry) {
defer translateResult(ent)
for {
key, err := parseKeyToken(dec)
keyToken, err := dec.Token()
if err != nil {
if err != io.EOF && !errors.Is(err, ErrEndOfToken) {
log.Debug("decodeResult: %s", err)
if err != io.EOF {
log.Debug("decodeResult err: %s", err)
}
return
}
if key == "" {
if d, ok := keyToken.(json.Delim); ok {
if d == '}' {
return
}
continue
}
key, ok := keyToken.(string)
if !ok {
log.Debug("decodeResult: keyToken is %T (%[1]v) and not string", keyToken)
return
}
decHandler, ok := resultDecHandlers[key]
if ok {
decHandler(dec, ent)
@@ -542,16 +527,13 @@ func decodeResult(dec *json.Decoder, ent *logEntry) {
}
}
// resultHandlers is the map of log entry decode handlers for various keys.
var resultHandlers = map[string]logEntryHandler{
"IsFiltered": func(t json.Token, ent *logEntry) error {
v, ok := t.(bool)
if !ok {
return nil
}
ent.Result.IsFiltered = v
return nil
},
"Rule": func(t json.Token, ent *logEntry) error {
@@ -596,14 +578,11 @@ var resultHandlers = map[string]logEntryHandler{
if !ok {
return nil
}
i, err := v.Int64()
if err != nil {
return err
}
ent.Result.Reason = filtering.Reason(i)
return nil
},
"ServiceName": func(t json.Token, ent *logEntry) error {
@@ -628,7 +607,6 @@ var resultHandlers = map[string]logEntryHandler{
},
}
// resultDecHandlers is the map of decode handlers for various keys.
var resultDecHandlers = map[string]func(dec *json.Decoder, ent *logEntry){
"ReverseHosts": decodeResultReverseHosts,
"IPList": decodeResultIPList,
@@ -636,11 +614,9 @@ var resultDecHandlers = map[string]func(dec *json.Decoder, ent *logEntry){
"DNSRewriteResult": decodeResultDNSRewriteResult,
}
// decodeLogEntry decodes string str to logEntry ent.
func decodeLogEntry(ent *logEntry, str string) {
dec := json.NewDecoder(strings.NewReader(str))
dec.UseNumber()
for {
keyToken, err := dec.Token()
if err != nil {

View File

@@ -182,7 +182,8 @@ func TestDecodeLogEntry(t *testing.T) {
if tc.want == "" {
assert.Empty(t, s)
} else {
assert.True(t, strings.HasSuffix(s, tc.want), "got %q", s)
assert.True(t, strings.HasSuffix(s, tc.want),
"got %q", s)
}
logOutput.Reset()

View File

@@ -68,19 +68,3 @@ func (e *logEntry) addResponse(resp *dns.Msg, isOrig bool) {
log.Error("querylog: %s", err)
}
}
// parseDNSRewriteResultIPs fills logEntry's DNSRewriteResult response records
// with the IP addresses parsed from the raw strings.
func (e *logEntry) parseDNSRewriteResultIPs() {
for rrType, rrValues := range e.Result.DNSRewriteResult.Response {
switch rrType {
case dns.TypeA, dns.TypeAAAA:
for i, v := range rrValues {
s, _ := v.(string)
rrValues[i] = net.ParseIP(s)
}
default:
// Go on.
}
}
}

View File

@@ -16,35 +16,32 @@ import (
"github.com/miekg/dns"
)
// queryLogFileName is a name of the log file. ".gz" extension is added later
// during compression.
const queryLogFileName = "querylog.json"
const (
queryLogFileName = "querylog.json" // .gz added during compression
)
// queryLog is a structure that writes and reads the DNS query log.
// queryLog is a structure that writes and reads the DNS query log
type queryLog struct {
findClient func(ids []string) (c *Client, err error)
// confMu protects conf.
confMu *sync.RWMutex
conf *Config
anonymizer *aghnet.IPMut
findClient func(ids []string) (c *Client, err error)
conf *Config
// logFile is the path to the log file.
logFile string
// bufferLock protects buffer.
bufferLock sync.RWMutex
// buffer contains recent log entries. The entries in this buffer must not
// be modified.
buffer []*logEntry
// bufferLock protects buffer.
bufferLock sync.RWMutex
// fileFlushLock synchronizes a file-flushing goroutine and main thread.
fileFlushLock sync.Mutex
fileFlushLock sync.Mutex // synchronize a file-flushing goroutine and main thread
flushPending bool // don't start another goroutine while the previous one is still running
fileWriteLock sync.Mutex
flushPending bool
anonymizer *aghnet.IPMut
}
// ClientProto values are names of the client protocols.
@@ -158,43 +155,6 @@ func (l *queryLog) clear() {
log.Debug("querylog: cleared")
}
// newLogEntry creates an instance of logEntry from parameters.
func newLogEntry(params *AddParams) (entry *logEntry) {
q := params.Question.Question[0]
entry = &logEntry{
// TODO(d.kolyshev): Export this timestamp to func params.
Time: time.Now(),
QHost: strings.ToLower(q.Name[:len(q.Name)-1]),
QType: dns.Type(q.Qtype).String(),
QClass: dns.Class(q.Qclass).String(),
ClientID: params.ClientID,
ClientProto: params.ClientProto,
Result: *params.Result,
Upstream: params.Upstream,
IP: params.ClientIP,
Elapsed: params.Elapsed,
Cached: params.Cached,
AuthenticatedData: params.AuthenticatedData,
}
if params.ReqECS != nil {
entry.ReqECS = params.ReqECS.String()
}
entry.addResponse(params.Answer, false)
entry.addResponse(params.OrigAnswer, true)
return entry
}
// Add implements the [QueryLog] interface for *queryLog.
func (l *queryLog) Add(params *AddParams) {
var isEnabled, fileIsEnabled bool
var memSize uint32
@@ -221,7 +181,35 @@ func (l *queryLog) Add(params *AddParams) {
params.Result = &filtering.Result{}
}
entry := newLogEntry(params)
now := time.Now()
q := params.Question.Question[0]
entry := &logEntry{
Time: now,
QHost: strings.ToLower(q.Name[:len(q.Name)-1]),
QType: dns.Type(q.Qtype).String(),
QClass: dns.Class(q.Qclass).String(),
ClientID: params.ClientID,
ClientProto: params.ClientProto,
Result: *params.Result,
Upstream: params.Upstream,
IP: params.ClientIP,
Elapsed: params.Elapsed,
Cached: params.Cached,
AuthenticatedData: params.AuthenticatedData,
}
if params.ReqECS != nil {
entry.ReqECS = params.ReqECS.String()
}
entry.addResponse(params.Answer, false)
entry.addResponse(params.OrigAnswer, true)
needFlush := false
func() {

View File

@@ -6,6 +6,7 @@ import (
"testing"
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/dnsproxy/proxyutil"
"github.com/AdguardTeam/golibs/stringutil"
"github.com/AdguardTeam/golibs/testutil"
"github.com/AdguardTeam/golibs/timeutil"
@@ -45,10 +46,9 @@ func TestQueryLog(t *testing.T) {
addEntry(l, "example.com", net.IPv4(1, 1, 1, 4), net.IPv4(2, 2, 2, 4))
type tcAssertion struct {
host string
answer net.IP
client net.IP
num int
num int
host string
answer, client net.IP
}
testCases := []struct {
@@ -367,6 +367,6 @@ func assertLogEntry(t *testing.T, entry *logEntry, host string, answer, client n
require.NoError(t, msg.Unpack(entry.Answer))
require.Len(t, msg.Answer, 1)
a := testutil.RequireTypeAssert[*dns.A](t, msg.Answer[0])
assert.Equal(t, answer, a.A.To16())
ip := proxyutil.IPFromRR(msg.Answer[0]).To16()
assert.Equal(t, answer, ip)
}

View File

@@ -12,181 +12,141 @@ import (
"github.com/AdguardTeam/golibs/log"
)
// Timestamp not found errors.
const (
// Timestamp not found errors.
errTSNotFound errors.Error = "ts not found"
errTSTooLate errors.Error = "ts too late"
errTSTooEarly errors.Error = "ts too early"
// maxEntrySize is a maximum size of the entry.
//
// TODO: Find a way to grow buffer instead of relying on this value when
// reading strings.
maxEntrySize = 16 * 1024
// bufferSize should be enough for at least this number of entries.
bufferSize = 100 * maxEntrySize
ErrTSNotFound errors.Error = "ts not found"
ErrTSTooLate errors.Error = "ts too late"
ErrTSTooEarly errors.Error = "ts too early"
)
// qLogFile represents a single query log file. It allows reading from the
// file in the reverse order.
// TODO: Find a way to grow buffer instead of relying on this value when reading strings
const maxEntrySize = 16 * 1024
// buffer should be enough for at least this number of entries
const bufferSize = 100 * maxEntrySize
// QLogFile represents a single query log file
// It allows reading from the file in the reverse order
//
// Please note, that this is a stateful object. Internally, it contains a
// pointer to a specific position in the file, and it reads lines in reverse
// order starting from that position.
type qLogFile struct {
// file is the query log file.
file *os.File
// Please note that this is a stateful object.
// Internally, it contains a pointer to a specific position in the file,
// and it reads lines in reverse order starting from that position.
type QLogFile struct {
file *os.File // the query log file
position int64 // current position in the file
// buffer that we've read from the file.
buffer []byte
buffer []byte // buffer that we've read from the file
bufferStart int64 // start of the buffer (in the file)
bufferLen int // buffer len
// lock is a mutex to make it thread-safe.
lock sync.Mutex
// position is the position in the file.
position int64
// bufferStart is the start of the buffer (in the file).
bufferStart int64
// bufferLen is the length of the buffer.
bufferLen int
lock sync.Mutex // We use mutex to make it thread-safe
}
// newQLogFile initializes a new instance of the qLogFile.
func newQLogFile(path string) (qf *qLogFile, err error) {
// NewQLogFile initializes a new instance of the QLogFile
func NewQLogFile(path string) (*QLogFile, error) {
f, err := os.OpenFile(path, os.O_RDONLY, 0o644)
if err != nil {
return nil, err
}
return &qLogFile{file: f}, nil
}
// validateQLogLineIdx returns error if the line index is not valid to continue
// search.
func (q *qLogFile) validateQLogLineIdx(lineIdx, lastProbeLineIdx, ts, fSize int64) (err error) {
if lineIdx == lastProbeLineIdx {
if lineIdx == 0 {
return errTSTooEarly
}
// If we're testing the same line twice then most likely the scope is
// too narrow and we won't find anything anymore in any other file.
return fmt.Errorf("looking up timestamp %d in %q: %w", ts, q.file.Name(), errTSNotFound)
} else if lineIdx == fSize {
return errTSTooLate
}
return nil
return &QLogFile{
file: f,
}, nil
}
// seekTS performs binary search in the query log file looking for a record
// with the specified timestamp. Once the record is found, it sets "position"
// so that the next ReadNext call returned that record.
// with the specified timestamp. Once the record is found, it sets
// "position" so that the next ReadNext call returned that record.
//
// The algorithm is rather simple:
// 1. It starts with the position in the middle of a file.
// 2. Shifts back to the beginning of the line.
// 3. Checks the log record timestamp.
// 4. If it is lower than the timestamp we are looking for, it shifts seek
// position to 3/4 of the file. Otherwise, to 1/4 of the file.
// 5. It performs the search again, every time the search scope is narrowed
// twice.
// 1. It starts with the position in the middle of a file
// 2. Shifts back to the beginning of the line
// 3. Checks the log record timestamp
// 4. If it is lower than the timestamp we are looking for,
// it shifts seek position to 3/4 of the file. Otherwise, to 1/4 of the file.
// 5. It performs the search again, every time the search scope is narrowed twice.
//
// Returns:
// - It returns the position of the line with the timestamp we were looking
// for so that when we call "ReadNext" this line was returned.
// - Depth of the search (how many times we compared timestamps).
// - If we could not find it, it returns one of the errors described above.
func (q *qLogFile) seekTS(timestamp int64) (pos int64, depth int, err error) {
// * It returns the position of the the line with the timestamp we were looking for
// so that when we call "ReadNext" this line was returned.
// * Depth of the search (how many times we compared timestamps).
// * If we could not find it, it returns one of the errors described above.
func (q *QLogFile) seekTS(timestamp int64) (int64, int, error) {
q.lock.Lock()
defer q.lock.Unlock()
// Empty the buffer.
// Empty the buffer
q.buffer = nil
// First of all, check the file size.
// First of all, check the file size
fileInfo, err := q.file.Stat()
if err != nil {
return 0, 0, err
}
// Define the search scope.
// Start of the search interval (position in the file).
start := int64(0)
// End of the search interval (position in the file).
end := fileInfo.Size()
// Probe is the approximate index of the line we'll try to check.
probe := (end - start) / 2
// Define the search scope
start := int64(0) // start of the search interval (position in the file)
end := fileInfo.Size() // end of the search interval (position in the file)
probe := (end - start) / 2 // probe -- approximate index of the line we'll try to check
var line string
// Index of the probe line in the file.
var lineIdx int64
var lineIdx int64 // index of the probe line in the file
var lineEndIdx int64
// Index of the last probe line.
var lastProbeLineIdx int64
var lastProbeLineIdx int64 // index of the last probe line
lastProbeLineIdx = -1
// Count seek depth in order to detect mistakes. If depth is too large,
// we should stop the search.
// Count seek depth in order to detect mistakes
// If depth is too large, we should stop the search
depth := 0
for {
// Get the line at the specified position.
// Get the line at the specified position
line, lineIdx, lineEndIdx, err = q.readProbeLine(probe)
if err != nil {
return 0, depth, err
}
// Check if the line index if invalid.
err = q.validateQLogLineIdx(lineIdx, lastProbeLineIdx, timestamp, fileInfo.Size())
if err != nil {
return 0, depth, err
if lineIdx == lastProbeLineIdx {
if lineIdx == 0 {
return 0, depth, ErrTSTooEarly
}
// If we're testing the same line twice then most likely
// the scope is too narrow and we won't find anything
// anymore in any other file.
return 0, depth, fmt.Errorf("looking up timestamp %d in %q: %w", timestamp, q.file.Name(), ErrTSNotFound)
} else if lineIdx == fileInfo.Size() {
return 0, depth, ErrTSTooLate
}
// Save the last found idx.
// Save the last found idx
lastProbeLineIdx = lineIdx
// Get the timestamp from the query log record.
// Get the timestamp from the query log record
ts := readQLogTimestamp(line)
if ts == 0 {
return 0, depth, fmt.Errorf(
"looking up timestamp %d in %q: record %q has empty timestamp",
timestamp,
q.file.Name(),
line,
)
return 0, depth, fmt.Errorf("looking up timestamp %d in %q: record %q has empty timestamp", timestamp, q.file.Name(), line)
}
if ts == timestamp {
// Hurray, returning the result.
// Hurray, returning the result
break
}
// Narrow the scope and repeat the search.
// Narrow the scope and repeat the search
if ts > timestamp {
// If the timestamp we're looking for is OLDER than what we found,
// then the line is somewhere on the LEFT side from the current
// probe position.
// If the timestamp we're looking for is OLDER than what we found
// Then the line is somewhere on the LEFT side from the current probe position
end = lineIdx
} else {
// If the timestamp we're looking for is NEWER than what we found,
// then the line is somewhere on the RIGHT side from the current
// probe position.
// If the timestamp we're looking for is NEWER than what we found
// Then the line is somewhere on the RIGHT side from the current probe position
start = lineEndIdx
}
probe = start + (end-start)/2
depth++
if depth >= 100 {
return 0, depth, fmt.Errorf(
"looking up timestamp %d in %q: depth %d too high: %w",
timestamp,
q.file.Name(),
depth,
errTSNotFound,
)
return 0, depth, fmt.Errorf("looking up timestamp %d in %q: depth %d too high: %w", timestamp, q.file.Name(), depth, ErrTSNotFound)
}
}
@@ -194,39 +154,37 @@ func (q *qLogFile) seekTS(timestamp int64) (pos int64, depth int, err error) {
return q.position, depth, nil
}
// SeekStart changes the current position to the end of the file. Please note,
// that we're reading query log in the reverse order and that's why log start
// is actually the end of file.
// SeekStart changes the current position to the end of the file
// Please note that we're reading query log in the reverse order
// and that's why log start is actually the end of file
//
// Returns nil if we were able to change the current position. Returns error
// in any other case.
func (q *qLogFile) SeekStart() (int64, error) {
// Returns nil if we were able to change the current position.
// Returns error in any other case.
func (q *QLogFile) SeekStart() (int64, error) {
q.lock.Lock()
defer q.lock.Unlock()
// Empty the buffer.
// Empty the buffer
q.buffer = nil
// First of all, check the file size.
// First of all, check the file size
fileInfo, err := q.file.Stat()
if err != nil {
return 0, err
}
// Place the position to the very end of file.
// Place the position to the very end of file
q.position = fileInfo.Size() - 1
if q.position < 0 {
q.position = 0
}
return q.position, nil
}
// ReadNext reads the next line (in the reverse order) from the file and shifts
// the current position left to the next (actually prev) line.
//
// Returns io.EOF if there's nothing more to read.
func (q *qLogFile) ReadNext() (string, error) {
// ReadNext reads the next line (in the reverse order) from the file
// and shifts the current position left to the next (actually prev) line.
// returns io.EOF if there's nothing to read more
func (q *QLogFile) ReadNext() (string, error) {
q.lock.Lock()
defer q.lock.Unlock()
@@ -239,34 +197,35 @@ func (q *qLogFile) ReadNext() (string, error) {
return "", err
}
// Shift position.
// Shift position
if lineIdx == 0 {
q.position = 0
} else {
// There's usually a line break before the line, so we should shift one
// more char left from the line "\nline".
// there's usually a line break before the line
// so we should shift one more char left from the line
// line\nline
q.position = lineIdx - 1
}
return line, err
}
// Close frees the underlying resources.
func (q *qLogFile) Close() error {
// Close frees the underlying resources
func (q *QLogFile) Close() error {
return q.file.Close()
}
// readNextLine reads the next line from the specified position. This line
// actually have to END on that position.
// readNextLine reads the next line from the specified position
// this line actually have to END on that position.
//
// The algorithm is:
// 1. Check if we have the buffer initialized.
// 2. If it is so, scan it and look for the line there.
// 3. If we cannot find the line there, read the prev chunk into the buffer.
// 4. Read the line from the buffer.
func (q *qLogFile) readNextLine(position int64) (string, int64, error) {
// the algorithm is:
// 1. check if we have the buffer initialized
// 2. if it is, scan it and look for the line there
// 3. if we cannot find the line there, read the prev chunk into the buffer
// 4. read the line from the buffer
func (q *QLogFile) readNextLine(position int64) (string, int64, error) {
relativePos := position - q.bufferStart
if q.buffer == nil || (relativePos < maxEntrySize && q.bufferStart != 0) {
// Time to re-init the buffer.
// Time to re-init the buffer
err := q.initBuffer(position)
if err != nil {
return "", 0, err
@@ -274,7 +233,8 @@ func (q *qLogFile) readNextLine(position int64) (string, int64, error) {
relativePos = position - q.bufferStart
}
// Look for the end of the prev line, this is where we'll read from.
// Look for the end of the prev line
// This is where we'll read from
startLine := int64(0)
for i := relativePos - 1; i >= 0; i-- {
if q.buffer[i] == '\n' {
@@ -285,19 +245,18 @@ func (q *qLogFile) readNextLine(position int64) (string, int64, error) {
line := string(q.buffer[startLine:relativePos])
lineIdx := q.bufferStart + startLine
return line, lineIdx, nil
}
// initBuffer initializes the qLogFile buffer. The goal is to read a chunk of
// file that includes the line with the specified position.
func (q *qLogFile) initBuffer(position int64) error {
// initBuffer initializes the QLogFile buffer.
// the goal is to read a chunk of file that includes the line with the specified position.
func (q *QLogFile) initBuffer(position int64) error {
q.bufferStart = int64(0)
if position > bufferSize {
q.bufferStart = position - bufferSize
}
// Seek to this position.
// Seek to this position
_, err := q.file.Seek(q.bufferStart, io.SeekStart)
if err != nil {
return err
@@ -312,35 +271,34 @@ func (q *qLogFile) initBuffer(position int64) error {
return err
}
// readProbeLine reads a line that includes the specified position. This
// method is supposed to be used when we use binary search in the Seek method.
// In the case of consecutive reads, use readNext, cause it uses better buffer.
func (q *qLogFile) readProbeLine(position int64) (string, int64, int64, error) {
// First of all, we should read a buffer that will include the query log
// line. In order to do this, we'll define the boundaries.
// readProbeLine reads a line that includes the specified position
// this method is supposed to be used when we use binary search in the Seek method
// in the case of consecutive reads, use readNext (it uses a better buffer)
func (q *QLogFile) readProbeLine(position int64) (string, int64, int64, error) {
// First of all, we should read a buffer that will include the query log line
// In order to do this, we'll define the boundaries
seekPosition := int64(0)
// Position relative to the buffer we're going to read.
relativePos := position
relativePos := position // position relative to the buffer we're going to read
if position > maxEntrySize {
seekPosition = position - maxEntrySize
relativePos = maxEntrySize
}
// Seek to this position.
// Seek to this position
_, err := q.file.Seek(seekPosition, io.SeekStart)
if err != nil {
return "", 0, 0, err
}
// The buffer size is 2*maxEntrySize.
// The buffer size is 2*maxEntrySize
buffer := make([]byte, maxEntrySize*2)
bufferLen, err := q.file.Read(buffer)
if err != nil {
return "", 0, 0, err
}
// Now start looking for the new line character starting from the
// relativePos and going left.
// Now start looking for the new line character starting
// from the relativePos and going left
startLine := int64(0)
for i := relativePos - 1; i >= 0; i-- {
if buffer[i] == '\n' {
@@ -348,7 +306,7 @@ func (q *qLogFile) readProbeLine(position int64) (string, int64, int64, error) {
break
}
}
// Looking for the end of line now.
// Looking for the end of line now
endLine := int64(bufferLen)
lineEndIdx := endLine + seekPosition
for i := relativePos; i < int64(bufferLen); i++ {
@@ -359,13 +317,13 @@ func (q *qLogFile) readProbeLine(position int64) (string, int64, int64, error) {
}
}
// Finally we can return the string we were looking for.
// Finally we can return the string we were looking for
lineIdx := startLine + seekPosition
return string(buffer[startLine:endLine]), lineIdx, lineEndIdx, nil
}
// readJSONValue reads a JSON string in form of '"key":"value"'. prefix must
// be of the form '"key":"' to generate less garbage.
// readJSONvalue reads a JSON string in form of '"key":"value"'. prefix must be
// of the form '"key":"' to generate less garbage.
func readJSONValue(s, prefix string) string {
i := strings.Index(s, prefix)
if i == -1 {
@@ -382,7 +340,7 @@ func readJSONValue(s, prefix string) string {
return s[start:end]
}
// readQLogTimestamp reads the timestamp field from the query log line.
// readQLogTimestamp reads the timestamp field from the query log line
func readQLogTimestamp(str string) int64 {
val := readJSONValue(str, `"T":"`)
if len(val) == 0 {
@@ -393,12 +351,10 @@ func readQLogTimestamp(str string) int64 {
log.Error("Couldn't find timestamp: %s", str)
return 0
}
tm, err := time.Parse(time.RFC3339Nano, val)
if err != nil {
log.Error("Couldn't parse timestamp: %s", val)
return 0
}
return tm.UnixNano()
}

View File

@@ -72,15 +72,15 @@ func prepareTestFiles(t *testing.T, filesNum, linesNum int) []string {
return files
}
// newTestQLogFile creates new *qLogFile for tests and registers the required
// newTestQLogFile creates new *QLogFile for tests and registers the required
// cleanup functions.
func newTestQLogFile(t *testing.T, linesNum int) (file *qLogFile) {
func newTestQLogFile(t *testing.T, linesNum int) (file *QLogFile) {
t.Helper()
testFile := prepareTestFiles(t, 1, linesNum)[0]
// Create the new qLogFile instance.
file, err := newQLogFile(testFile)
// Create the new QLogFile instance.
file, err := NewQLogFile(testFile)
require.NoError(t, err)
assert.NotNil(t, file)
@@ -240,7 +240,7 @@ func TestQLogFile_SeekTS_bad(t *testing.T) {
}
}
func getQLogFileLine(q *qLogFile, lineNumber int) (line string, err error) {
func getQLogFileLine(q *QLogFile, lineNumber int) (line string, err error) {
if _, err = q.SeekStart(); err != nil {
return line, err
}
@@ -256,7 +256,7 @@ func getQLogFileLine(q *qLogFile, lineNumber int) (line string, err error) {
// Check adding and loading (with filtering) entries from disk and memory.
func TestQLogFile(t *testing.T) {
// Create the new qLogFile instance.
// Create the new QLogFile instance.
q := newTestQLogFile(t, 2)
// Seek to the start.
@@ -285,7 +285,7 @@ func TestQLogFile(t *testing.T) {
assert.Empty(t, line)
}
func newTestQLogFileData(t *testing.T, data string) (file *qLogFile) {
func NewTestQLogFileData(t *testing.T, data string) (file *QLogFile) {
f, err := os.CreateTemp(t.TempDir(), "*.txt")
require.NoError(t, err)
testutil.CleanupAndRequireSuccess(t, f.Close)
@@ -293,7 +293,7 @@ func newTestQLogFileData(t *testing.T, data string) (file *qLogFile) {
_, err = f.WriteString(data)
require.NoError(t, err)
file, err = newQLogFile(f.Name())
file, err = NewQLogFile(f.Name())
require.NoError(t, err)
testutil.CleanupAndRequireSuccess(t, file.Close)
@@ -309,9 +309,9 @@ func TestQLog_Seek(t *testing.T) {
timestamp, _ := time.Parse(time.RFC3339Nano, "2020-08-31T18:44:25.376690873+03:00")
testCases := []struct {
wantErr error
name string
delta int
wantErr error
wantDepth int
}{{
name: "ok",
@@ -321,12 +321,12 @@ func TestQLog_Seek(t *testing.T) {
}, {
name: "too_late",
delta: 2,
wantErr: errTSTooLate,
wantErr: ErrTSTooLate,
wantDepth: 2,
}, {
name: "too_early",
delta: -2,
wantErr: errTSTooEarly,
wantErr: ErrTSTooEarly,
wantDepth: 1,
}}
@@ -338,7 +338,7 @@ func TestQLog_Seek(t *testing.T) {
timestamp.Add(time.Second).Format(time.RFC3339Nano),
)
q := newTestQLogFileData(t, data)
q := NewTestQLogFileData(t, data)
_, depth, err := q.seekTS(timestamp.Add(time.Second * time.Duration(tc.delta)).UnixNano())
require.Truef(t, errors.Is(err, tc.wantErr), "%v", err)

View File

@@ -9,36 +9,36 @@ import (
"github.com/AdguardTeam/golibs/log"
)
// qLogReader allows reading from multiple query log files in the reverse
// order.
// QLogReader allows reading from multiple query log files in the reverse order.
//
// Please note that this is a stateful object. Internally, it contains a
// pointer to a particular query log file, and to a specific position in this
// file, and it reads lines in reverse order starting from that position.
type qLogReader struct {
// qFiles is an array with the query log files. The order is from oldest
// to newest.
qFiles []*qLogFile
// Please note that this is a stateful object.
// Internally, it contains a pointer to a particular query log file, and
// to a specific position in this file, and it reads lines in reverse order
// starting from that position.
type QLogReader struct {
// qFiles - array with the query log files
// The order is - from oldest to newest
qFiles []*QLogFile
// currentFile is the index of the current file.
currentFile int
currentFile int // Index of the current file
}
// newQLogReader initializes a qLogReader instance with the specified files.
func newQLogReader(files []string) (*qLogReader, error) {
qFiles := make([]*qLogFile, 0)
// NewQLogReader initializes a QLogReader instance
// with the specified files
func NewQLogReader(files []string) (*QLogReader, error) {
qFiles := make([]*QLogFile, 0)
for _, f := range files {
q, err := newQLogFile(f)
q, err := NewQLogFile(f)
if err != nil {
if errors.Is(err, os.ErrNotExist) {
continue
}
// Close what we've already opened.
cErr := closeQFiles(qFiles)
if cErr != nil {
log.Debug("querylog: closing files: %s", cErr)
cerr := closeQFiles(qFiles)
if cerr != nil {
log.Debug("querylog: closing files: %s", cerr)
}
return nil, err
@@ -47,28 +47,31 @@ func newQLogReader(files []string) (*qLogReader, error) {
qFiles = append(qFiles, q)
}
return &qLogReader{qFiles: qFiles, currentFile: len(qFiles) - 1}, nil
return &QLogReader{
qFiles: qFiles,
currentFile: (len(qFiles) - 1),
}, nil
}
// seekTS performs binary search of a query log record with the specified
// timestamp. If the record is found, it sets qLogReader's position to point
// to that line, so that the next ReadNext call returned this line.
func (r *qLogReader) seekTS(timestamp int64) (err error) {
// timestamp. If the record is found, it sets QLogReader's position to point to
// that line, so that the next ReadNext call returned this line.
func (r *QLogReader) seekTS(timestamp int64) (err error) {
for i := len(r.qFiles) - 1; i >= 0; i-- {
q := r.qFiles[i]
_, _, err = q.seekTS(timestamp)
if err != nil {
if errors.Is(err, errTSTooEarly) {
if errors.Is(err, ErrTSTooEarly) {
// Look at the next file, since we've reached the end of this
// one. If there is no next file, it's not found.
err = errTSNotFound
err = ErrTSNotFound
continue
} else if errors.Is(err, errTSTooLate) {
} else if errors.Is(err, ErrTSTooLate) {
// Just seek to the start then. timestamp is probably between
// the end of the previous one and the start of this one.
return r.SeekStart()
} else if errors.Is(err, errTSNotFound) {
} else if errors.Is(err, ErrTSNotFound) {
return err
} else {
return fmt.Errorf("seekts: file at index %d: %w", i, err)
@@ -77,7 +80,7 @@ func (r *qLogReader) seekTS(timestamp int64) (err error) {
// The search is finished, and the searched element has been found.
// Update currentFile only, position is already set properly in
// qLogFile.
// QLogFile.
r.currentFile = i
return nil
@@ -90,13 +93,13 @@ func (r *qLogReader) seekTS(timestamp int64) (err error) {
return nil
}
// SeekStart changes the current position to the end of the newest file.
// Please note that we're reading query log in the reverse order and that's why
// the log starts actually at the end of file.
// SeekStart changes the current position to the end of the newest file
// Please note that we're reading query log in the reverse order
// and that's why log start is actually the end of file
//
// Returns nil if we were able to change the current position. Returns error
// in any other cases.
func (r *qLogReader) SeekStart() error {
// Returns nil if we were able to change the current position.
// Returns error in any other case.
func (r *QLogReader) SeekStart() error {
if len(r.qFiles) == 0 {
return nil
}
@@ -107,12 +110,10 @@ func (r *qLogReader) SeekStart() error {
return err
}
// ReadNext reads the next line (in the reverse order) from the query log
// files. Then shifts the current position left to the next (actually prev)
// line (or the next file).
//
// Returns io.EOF if there is nothing more to read.
func (r *qLogReader) ReadNext() (string, error) {
// ReadNext reads the next line (in the reverse order) from the query log files.
// and shifts the current position left to the next (actually prev) line (or the next file).
// returns io.EOF if there's nothing to read more.
func (r *QLogReader) ReadNext() (string, error) {
if len(r.qFiles) == 0 {
return "", io.EOF
}
@@ -121,7 +122,7 @@ func (r *qLogReader) ReadNext() (string, error) {
q := r.qFiles[r.currentFile]
line, err := q.ReadNext()
if err != nil {
// Shift to the older file.
// Shift to the older file
r.currentFile--
if r.currentFile < 0 {
break
@@ -129,10 +130,10 @@ func (r *qLogReader) ReadNext() (string, error) {
q = r.qFiles[r.currentFile]
// Set its position to the start right away.
// Set it's position to the start right away
_, err = q.SeekStart()
// This is unexpected, return an error right away.
// This is unexpected, return an error right away
if err != nil {
return "", err
}
@@ -141,17 +142,17 @@ func (r *qLogReader) ReadNext() (string, error) {
}
}
// Nothing to read anymore.
// Nothing to read anymore
return "", io.EOF
}
// Close closes the qLogReader.
func (r *qLogReader) Close() error {
// Close closes the QLogReader
func (r *QLogReader) Close() error {
return closeQFiles(r.qFiles)
}
// closeQFiles is a helper method to close multiple qLogFile instances.
func closeQFiles(qFiles []*qLogFile) error {
// closeQFiles - helper method to close multiple QLogFile instances
func closeQFiles(qFiles []*QLogFile) error {
var errs []error
for _, q := range qFiles {
@@ -162,7 +163,7 @@ func closeQFiles(qFiles []*qLogFile) error {
}
if len(errs) > 0 {
return errors.List("error while closing qLogReader", errs...)
return errors.List("error while closing QLogReader", errs...)
}
return nil

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