Compare commits

...

53 Commits

Author SHA1 Message Date
Ildar Kamalov
ef92c2e891 Merge branch 'master' into ADG-9565 2025-01-29 13:42:29 +03:00
Ildar Kamalov
a4364ff7ed ADG-9565 format large numbers in the upstream table and query log 2025-01-29 13:39:14 +03:00
Ainar Garipov
91270d0b61 Pull request 2336: 7600-fix-time-in-enable-protection-timer
Closes #7599.
Updates #7600.

* commit 'a4ea6c22335dff3a0f9cfba1ccf9db65a1e6db71':
  fix: time in `enable_protection_timer` translation
2025-01-28 21:39:16 +03:00
Ainar Garipov
a4ea6c2233 Merge branch 'master' into 7600-fix-time-in-enable-protection-timer 2025-01-28 21:30:43 +03:00
Eugene Burkov
c7b65fa522 Pull request 2335: Update changelog
Merge in DNS/adguard-home from upd-chlog to master

Squashed commit of the following:

commit ea263dbf3e2d409ae9a982b89ee7976de96138b1
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Jan 28 20:53:44 2025 +0300

    all: upd chlog
2025-01-28 21:10:48 +03:00
Jan Pieper
1a95161784 fix: time in enable_protection_timer translation 2025-01-28 09:53:58 +01:00
Stanislav Chzhen
6633ad6304 Pull request 2333: fix-test
Merge in DNS/adguard-home from fix-test to master

Squashed commit of the following:

commit cddaed40273e81677eec33e2ededfd4556c5b99f
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Jan 20 18:33:43 2025 +0300

    filtering: fix test
2025-01-20 18:57:51 +03:00
Eugene Burkov
dfa28af907 Pull request 2331: Update all
Merge in DNS/adguard-home from upd-all to master

Squashed commit of the following:

commit 6610425055168c577adb8fe6ccb0557ed867c059
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Jan 20 12:53:21 2025 +0300

    client: upd i18n

commit a1d669e3ec7cab80c4bc5fff0fbba9ff7dfdd136
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Jan 20 12:46:05 2025 +0300

    client: upd trackers
2025-01-20 13:08:43 +03:00
Eugene Burkov
8f75c6ac9d Pull request 2330: Upd Go
Merge in DNS/adguard-home from upd-go to master

Squashed commit of the following:

commit 58aad666b56d7100c7b5291fb8a100fe635e6d62
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Jan 17 19:02:06 2025 +0300

    all: upd go
2025-01-17 19:24:56 +03:00
Ildar Kamalov
446f21a511 Pull request 2328: ADG-9458 fix request count link in the clients table
Updates #7513.

Squashed commit of the following:

commit 2b7f6bd4d0e1d8855faacf08cc0dff3e4e6d2bbd
Author: Ildar Kamalov <ik@adguard.com>
Date:   Thu Jan 9 15:28:11 2025 +0300

    fix

commit 86f3f32272975a199a8fb3867f3e776f71704916
Author: Ildar Kamalov <ik@adguard.com>
Date:   Thu Jan 9 15:27:34 2025 +0300

    fix

commit 786c56828f95c66275a19d816759bacc137041f8
Author: Ildar Kamalov <ik@adguard.com>
Date:   Thu Jan 9 15:26:38 2025 +0300

    ADG-9458 fix request count link in the clients table
2025-01-10 16:21:34 +03:00
Stanislav Chzhen
0b25119c52 Pull request #2327: AGDNS-2622-fix-race
Merge in DNS/adguard-home from AGDNS-2622-fix-race to master

Squashed commit of the following:

commit 28170e67655048d7356d6cfce07e04017a933163
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Dec 23 17:24:28 2024 +0300

    all: upd proxy

commit 3bd9458220e04041c5135fa97d55e6e6e6c29071
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Fri Dec 20 21:19:20 2024 +0300

    all: upd proxy
2024-12-23 19:12:55 +03:00
Ainar Garipov
20e56b7171 Pull request 2326: more-md-lint
Merge in DNS/adguard-home from more-md-lint to master

Squashed commit of the following:

commit 39e7ea3b441ebf48c5b0d5c2b5b85620515bbea3
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Dec 19 17:03:36 2024 +0300

    all: imp docs more

commit 7aa08036b239d7eb19f674a6c4bfaf1325ff4bff
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Dec 19 16:08:13 2024 +0300

    all: add more docs to lint
2024-12-19 17:17:40 +03:00
Eugene Burkov
261c1599a5 Pull request #2325: 7510 Fix openapi spec
Merge in DNS/adguard-home from 7510-fix-openapi to master

Squashed commit of the following:

commit b67d6f964081cf12bea5c3406eba64a432d02e81
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Dec 19 15:28:46 2024 +0300

    openapi: fmt openapi chlog

commit 738becf0af96b3bfc4b549be9ad84cbce0d9cf39
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Dec 19 14:44:36 2024 +0300

    openapi: log changes

commit a398712ed1b5696f944f15404e340e002822c89f
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Dec 19 14:34:11 2024 +0300

    openapi: fix spec
2024-12-19 15:38:18 +03:00
Ainar Garipov
21945c6058 Pull request 2324: 7505-format-values
Closes #7329.
Updates #7505.

* commit '5c15d1bb16044bbaff1ff29e8bbe3ce2ff3bc982':
  all: upd chlog
  Format values in General Statistics
2024-12-17 16:55:02 +03:00
Ainar Garipov
5c15d1bb16 Merge branch 'master' into 7505-format-values 2024-12-17 16:37:01 +03:00
Ainar Garipov
efe2ed1304 all: upd chlog 2024-12-17 16:20:12 +03:00
Stanislav Chzhen
fe07786d2d Pull request #2323: AGDNS-2598-clients-search
Merge in DNS/adguard-home from AGDNS-2598-clients-search to master

Squashed commit of the following:

commit 9df3c19acad16203ccaa7752902bce8bc835c8fb
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Dec 16 19:11:43 2024 +0300

    home: imp code

commit 7bf8f0a516b57fab6c19c24e4a156c87d9c4d03f
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Dec 16 18:34:06 2024 +0300

    all: imp code

commit 2dd1c941232ceeaef4c506717096a8b8e8555e6e
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Fri Dec 13 17:35:11 2024 +0300

    all: clients search
2024-12-17 15:08:31 +03:00
Hermholtz
2b55e56306 Format values in General Statistics 2024-12-14 15:43:12 +01:00
Ainar Garipov
dedbadafc4 Pull request 2321: upd-go-deps
Merge in DNS/adguard-home from upd-go-deps to master

Squashed commit of the following:

commit 2c0b63da2ec8bbf19bc7dbb03c0166f6f9a5d822
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Dec 12 14:42:31 2024 +0300

    all: upd go deps, tools
2024-12-12 16:20:13 +03:00
Ainar Garipov
d3cc2dc930 Pull request 2319: websvc-patch
Merge in DNS/adguard-home from websvc-patch to master

Squashed commit of the following:

commit b2a10faf12b16f13f617b3ed3ef3e81cb0479ff8
Merge: 38f749106 8f53f6505
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Dec 11 17:33:15 2024 +0300

    Merge branch 'master' into websvc-patch

commit 38f7491069d90d4080e7ad98b09bf9ce19138195
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Tue Dec 10 19:57:54 2024 +0300

    next: add json merge patch utils
2024-12-11 17:41:00 +03:00
Ainar Garipov
8f53f6505b Pull request 2320: upd-chlog
Merge in DNS/adguard-home from upd-chlog to master

Squashed commit of the following:

commit 305ae5823fa2f61b5f58759a29330a65599c19ba
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Dec 11 17:19:51 2024 +0300

    all: upd and fmt chlog
2024-12-11 17:32:22 +03:00
Stanislav Chzhen
dab608292a Pull request 2314: AGDNS-2374-slog-home-webapi
Squashed commit of the following:

commit 7c457d92b5a2d931c22388d37ad30f10dc0ecd89
Merge: bcd3d29df 11dfc7a3e
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Dec 5 18:24:08 2024 +0300

    Merge branch 'master' into AGDNS-2374-slog-home-webapi

commit bcd3d29dfde4af7d223e2d2468dd01635f799bb9
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Dec 5 18:24:01 2024 +0300

    all: imp code

commit f3af1bf3dd476733d0286eeea9f6dd5ebbdb4950
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Dec 4 18:33:35 2024 +0300

    home: imp code

commit 035477513f433346c7bf3215083c317466b4b734
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Dec 3 19:01:55 2024 +0300

    home: imp code

commit 5368d8de50d266b438a4909d81b5fec76b5ca65c
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Dec 3 17:25:37 2024 +0300

    home: imp code

commit fce1bf475f33f6fe45252e2276ea8ebf2a82a8b2
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Dec 2 18:20:31 2024 +0300

    home: slog webapi
2024-12-06 16:01:58 +03:00
Ildar Kamalov
11dfc7a3e8 Pull request 2316: AGDNS-2239 fix setup guide list styles
Squashed commit of the following:

commit 52cc651e3b6a5fe7c46ad1fb41865f2dd1f84258
Merge: 324c4e102 c234e5dc3
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Dec 5 15:28:39 2024 +0300

    Merge branch 'master' into AGDNS-2239

commit 324c4e1024e6d1c4fc72eeae2baeee6565512a6c
Author: Ildar Kamalov <ik@adguard.com>
Date:   Wed Dec 4 17:03:46 2024 +0300

    fix mobile styles

commit 739ccfa6f1c99562c81c2ba947b3c91fb82ea572
Author: Ildar Kamalov <ik@adguard.com>
Date:   Wed Dec 4 16:45:48 2024 +0300

    AGDNS-2239 fix guide list padding
2024-12-05 15:36:40 +03:00
Eugene Burkov
c234e5dc31 Pull request 2317: Update all
Squashed commit of the following:

commit 832a5ac0c9d588428c7d030978769e510e011645
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Dec 4 17:38:20 2024 +0300

    client: upd i18n

commit 6fa7591109085b169b79bd771808922a1a53f875
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Dec 4 17:20:30 2024 +0300

    client: upd trackers

commit f4b692d37c1cc4d784b4a76f85198f4a1cfa7f83
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Dec 4 16:55:40 2024 +0300

    all: upd tools, go
2024-12-04 17:57:37 +03:00
Eugene Burkov
3895cfb4f0 Pull request 2312: 7400 Windows permcheck
Updates #7400.

Squashed commit of the following:

commit f50d7c200de545dc6c8ef70b39208f522033fb90
Merge: 47040a14c 37b16bcf7
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Dec 3 18:09:23 2024 +0300

    Merge branch 'master' into 7400-chown-permcheck

commit 47040a14cd50bf50429f44eba0acdcf736412b61
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Dec 3 14:26:43 2024 +0300

    permcheck: fix nil entries

commit e1d21c576d75a903b88db3b7beb82348cdcf60c9
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Dec 2 15:37:58 2024 +0300

    permcheck: fix nil owner

commit b1fc67c4d189293d0aee90c1905f7f387840643b
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Nov 29 18:07:15 2024 +0300

    permcheck: imp doc

commit 0b6a71326e249f0923e389aa1f6f164b02802a24
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Nov 29 17:16:24 2024 +0300

    permcheck: imp code

commit 7dfbeda179d0ddb81db54fa4e0dcff189b400215
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Nov 29 14:28:17 2024 +0300

    permcheck: imp code

commit 3a5b6aced948a2d09fdae823fc986266c9984b3d
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Nov 28 19:21:03 2024 +0300

    all: imp code, docs

commit c076c9366934303fa8c5909bd13770e367dca72e
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Nov 28 15:14:06 2024 +0300

    permcheck: imp code, docs

commit 09e4ae1ba12e195454f1db11fa2f5c9e8e170f06
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Nov 27 19:19:11 2024 +0300

    all: implement windows permcheck

commit b75ed7d4d30e289b8a99e68e6a5e94ab74cf49cb
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Nov 25 18:01:47 2024 +0300

    all: revert permissions
2024-12-03 18:26:00 +03:00
Ainar Garipov
37b16bcf79 Pull request 2315: 7479-readme-fix
Closes #7477.

* commit '4d470f94840c46d22f6dc1454a84a8fdc11eda6b':
  Update wiki-start in README.md
2024-12-03 14:22:32 +03:00
Margaret Fero
4d470f9484 Update wiki-start in README.md
When I clicked the link aliased as wiki-start, the article had a banner at the top redirecting readers to a newer version on the wiki. This commit replaces the link to the outdated version with the link it redirects to, to save future visitors a click.
2024-12-02 00:45:18 -08:00
Stanislav Chzhen
a8431297f7 Pull request 2313: 7357-upd-proxy
Updates #7357.

Squashed commit of the following:

commit 599dee0e5dd6d0af0e06ac97207a2677c32b03da
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Fri Nov 29 14:52:16 2024 +0300

    all: upd golibs

commit eb90c49d37bbe59ecb4867075d4b6fde70871fa2
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Fri Nov 29 14:32:57 2024 +0300

    all: upd proxy

commit 12b8e51c8738a4c94b257d9b4640bc63f9fa5d70
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Nov 28 20:04:58 2024 +0300

    all: upd proxy

commit 77fcabcf60f72c092a0a49075d1415ce370cdbb6
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Nov 28 17:13:30 2024 +0300

    all: upd chlog

commit 714f93d612abd705732cf76bee365272d15a11ce
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Nov 28 17:02:34 2024 +0300

    all: upd proxy
2024-11-29 15:57:06 +03:00
Eugene Burkov
9789e5b0fe Pull request 2311: 7465 Fix nil dereference
Squashed commit of the following:

commit e4943c2b064ff0647d0e3bd2e6431bb94f151ba5
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Nov 27 17:05:32 2024 +0300

    home: fix nil dereference
2024-11-27 17:16:45 +03:00
Eugene Burkov
4a49c4db96 Pull request 2307: AGDNS-2556 Custom updater URL
Squashed commit of the following:

commit 73f946138ccb4f89141f192b6cb1a21887604ab4
Merge: c58847bfb d578c713f
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Nov 26 17:42:29 2024 +0300

    Merge branch 'master' into AGDNS-2556-custom-update-url

commit c58847bfb08131263e1cff4813eb4a466f613d91
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Nov 26 17:34:11 2024 +0300

    home: imp logging

commit 0d451621d76fdf2c363d223eb29c4442d8f36dc8
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Nov 26 15:12:04 2024 +0300

    home: rename config field

commit c7f3822929e9199f8f411f1a0ad072c643feb42f
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Nov 26 15:07:09 2024 +0300

    all: enable updater for some cases

commit 872cd3a18c876076ea643624336cfc0a4296a81d
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Nov 22 19:09:18 2024 +0300

    updater: imp test

commit c9efb412e7411b769df54b7247fe168047fb9799
Merge: c989eef71 abb738013
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Nov 22 17:51:46 2024 +0300

    Merge branch 'master' into AGDNS-2556-custom-update-url

commit c989eef715ae7edd98d7b2d5df06fd3d04153209
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Nov 22 17:46:34 2024 +0300

    all: imp code

commit 0452d8b356e6d0b73b097d43b97b7027fcca752d
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Nov 22 15:37:21 2024 +0300

    all: add custom url to updater
2024-11-26 20:35:16 +03:00
Stanislav Chzhen
d578c713ff Pull request 2310: 6818-fix-goroutine-leak
Updates #6818.

Squashed commit of the following:

commit 3027fc4a615beba9138449d65994f1630b5a6cbf
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Nov 25 16:53:33 2024 +0300

    all: fixed goroutine leak
2024-11-26 13:34:16 +03:00
Stanislav Chzhen
d4ca14806e Pull request 2309: fix-safesearch-test
Squashed commit of the following:

commit a5b6c831d86ee5264c0918bf12ddef7c78f78f38
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Nov 25 13:45:17 2024 +0300

    dnsforward: imp tests

commit 1ca353423ca4dcc7616475fbf12c9b5b9450a5ea
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Fri Nov 22 20:08:28 2024 +0300

    dnsforward: imp tests

commit 82851aa825818159b5b1583e2716d6656d633b03
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Fri Nov 22 19:54:53 2024 +0300

    dnsforward: imp tests

commit efabbe318e7520eba65b595f11e2fb8effaeec13
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Fri Nov 22 19:36:27 2024 +0300

    dnsforward: fix safesearch test
2024-11-25 14:31:21 +03:00
Stanislav Chzhen
abb738013a Pull request 2308: AGDNS-2374-slog-client-storage
Squashed commit of the following:

commit 670ce6f7267ae6447c179ba491fc18c01cffa1ab
Merge: b3029a041 098cbab7e
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Fri Nov 22 17:09:08 2024 +0300

    Merge branch 'master' into AGDNS-2374-slog-client-storage

commit b3029a041aa0f4b536dd899f162fcee2b7d5127b
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Fri Nov 22 16:53:18 2024 +0300

    all: slog client storage
2024-11-22 17:18:47 +03:00
Eugene Burkov
098cbab7e6 Pull request 2305: 7400 Disable permcheck
Updates #7400.

Squashed commit of the following:

commit f6508d395288dfa5ed0b9aa2e714bc1eba72d243
Merge: aa7119648 d96e65cb0
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Nov 22 15:43:27 2024 +0300

    Merge branch 'master' into 7400-disable-perm

commit aa7119648bae3f2ff6af96215c3c8eab70b379ea
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Nov 20 16:51:37 2024 +0300

    next: add flag

commit c16b90918f5a9493876e3d16776fb18f67cbc09b
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Nov 20 16:42:47 2024 +0300

    home: fix help

commit 2e096c0e320599b9472f55a47c1abbc95e1a9f8e
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Nov 20 16:37:30 2024 +0300

    all: imp code, log changes

commit 368598819fc339cc1dbf788d092af9e4c5191f30
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Nov 20 16:12:18 2024 +0300

    home: add permcheck option
2024-11-22 17:03:09 +03:00
Stanislav Chzhen
d96e65cb0c Pull request 2304: AGDNS-2374-slog-querylog
Squashed commit of the following:

commit e98e5efaaf5388551322933321df0707ad7b2a9c
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Nov 21 13:15:51 2024 +0300

    all: imp code

commit fbe728c9aa03a325c2733c214412f9071faba5ed
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Nov 18 20:57:15 2024 +0300

    all: imp code

commit ef715c58cb6621236424f55268390aa3f997f883
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Nov 18 16:39:35 2024 +0300

    all: imp code

commit cbb993f7ae4311b2a73ace7066a5dabf190291be
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Nov 18 14:03:42 2024 +0300

    all: imp code

commit 8d88d799303c7e3d15322fee87780fedb408ea13
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Fri Nov 15 15:57:07 2024 +0300

    all: slog querylog
2024-11-21 20:19:39 +03:00
Ainar Garipov
1d6d85cff4 Pull request 2303: AGDNS-2505-upd-next
Squashed commit of the following:

commit 586b0eb180afc22d06d673756dd916c17a173361
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Tue Nov 12 19:58:56 2024 +0300

    next: upd more

commit d729aa150f7ac367255830cceca40b8880c51015
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Tue Nov 12 16:53:15 2024 +0300

    next/websvc: upd more

commit 0c64e6cfc66b9212f077b2de7450586fd4d02802
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Mon Nov 11 21:08:51 2024 +0300

    next: upd more

commit 05eec75222360708621c99d3eeac7c8d9f2a5080
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Fri Nov 8 19:20:02 2024 +0300

    next: upd code
2024-11-13 15:44:21 +03:00
Ainar Garipov
ac5a96fada Pull request 2302: upd-all
Squashed commit of the following:

commit f920006277f39b74c803139af2a9039aa45effae
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Fri Nov 8 16:14:41 2024 +0300

    all: fix pre-commit; upd dnsproxy

commit 391f79b244348c6075f5ba0fccfb8882791bf3f1
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Nov 7 18:53:28 2024 +0300

    scripts: imp install

commit 35324db80b591831c32b7ea45930eefee82a6320
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Nov 7 18:20:23 2024 +0300

    all: imp docs, scripts

commit d2724cfaefdb8659efbdb5bf181a28721a909f07
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Nov 7 17:26:23 2024 +0300

    all: upd go, deps, tools, scripts
2024-11-08 17:18:16 +03:00
Eugene Burkov
6673bb175a Pull request 2301: Update all
Squashed commit of the following:

commit df783c76335d8733eb385c047c491ed1bb8b0e51
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Nov 6 18:44:55 2024 +0300

    client: upd i18n

commit b72d4564baa8f8bab819d045aec29f9ab977538d
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Nov 6 18:06:27 2024 +0300

    client: upd filters and trackers
2024-11-06 19:26:57 +03:00
Eugene Burkov
b1a0f4fa44 Pull request 2299: AG-29637 Sign release
Squashed commit of the following:

commit 265097a29ed8e89933c08cb8094297ac89a8b140
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Nov 6 18:03:19 2024 +0300

    all: fix darwin find, log changes

commit 298b31c4078239ce83fdf7a3a741062e56f35b9d
Merge: 82c487518 d06b18a49
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Nov 6 18:01:28 2024 +0300

    Merge branch 'master' into AG-29637-sign-release

commit 82c487518bd21a995921a7d5265da6884aac9e9f
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Nov 6 17:34:12 2024 +0300

    scripts: imp fmt manually

commit 4e33ec1a77dd239df8b5eeec16843e517797d486
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Nov 6 14:42:09 2024 +0300

    scripts: imp fmt, sign releases
2024-11-06 18:45:21 +03:00
Ainar Garipov
d06b18a493 Pull request 2300: upd-chlog
Squashed commit of the following:

commit 20d68fb4bbb5874c816e4cfc8ff40ab16e7c8c46
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Nov 6 17:40:08 2024 +0300

    all: upd chlog
2024-11-06 17:56:48 +03:00
Eugene Burkov
80ec01dd49 Pull request 2298: 7314 Fix updater
Updates #7314.

Squashed commit of the following:

commit be214937fc391cf8c0c31e52a29b0adbdcd81f08
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Nov 5 18:42:25 2024 +0300

    updater: upd doc

commit bdb9df94eecb8b1845b1e65b39325f163541ee91
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Nov 5 18:41:26 2024 +0300

    all: rename const

commit 7fbabeb939ba37aba11852af3c4095363e4ce304
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Nov 5 18:34:37 2024 +0300

    all: add binary const

commit c9f1a9f747c9ec440a9ea5754eed6974eff4169d
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Nov 5 18:20:44 2024 +0300

    updater: fix binary perm
2024-11-05 19:21:29 +03:00
Ainar Garipov
47dfa44cf6 Pull request 2297: AG-20945-filter-storage
Squashed commit of the following:

commit 2611fd57815910c035621879094ab954389f31b4
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Fri Nov 1 16:29:06 2024 +0300

    dnsforward: imp test

commit 5efcfda937da2ce229ae1a3c57d7a4de35ee7caf
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Fri Nov 1 15:54:18 2024 +0300

    rulelist: imp docs, tests

commit 7a759c46997ed69e931a0a7c4f25820c52660a3f
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Fri Nov 1 14:36:08 2024 +0300

    all: add filtering storage; upd golibs
2024-11-05 12:25:39 +03:00
Ainar Garipov
1d2026bf7e Pull request 2295: upd-all
Squashed commit of the following:

commit c1f3375abc42809928ff80bede77f4099d2e7c1c
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Tue Oct 29 14:34:14 2024 +0300

    all: upd deps, lists, tools
2024-10-29 14:49:25 +03:00
Eugene Burkov
e77de2e67d Pull request 2294: AGDNS-2455 Windows permissions
Closes #7314.

Squashed commit of the following:

commit f8b6ffeec2f0f96c947cf896c75d05efaca77caf
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Oct 29 14:14:41 2024 +0300

    all: fix chlog

commit 9417b7dc510296c096f234e2f340dad5a6faf627
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Oct 28 19:41:30 2024 +0300

    aghos: imp doc

commit b91f0e72a70a8e1392bd07b50714d8b83cc4e33e
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Oct 28 19:26:15 2024 +0300

    all: rm bin

commit 9008ee93b181794c5082894bfa5ce4c76153f93d
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Oct 28 18:23:54 2024 +0300

    all: revert permcheck

commit bcc85d50f5f39269713979c6509a9acd220570b8
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Oct 28 17:48:55 2024 +0300

    all: use aghos more

commit 993e351712fbf004a6f96e06061ba2321c1c46e1
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Oct 28 16:24:56 2024 +0300

    all: fix more bugs

commit a22b0d265eb0fa747e136363558b97de54e593b8
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Oct 25 18:30:52 2024 +0300

    all: fix bugs

commit a2309f812ad3fd83d26c373b67756ea3074f4854
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Oct 25 17:05:08 2024 +0300

    all: fix chlog, imp api

commit 42c3f8e91c49998068bc208166de20efe49c3dcb
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Oct 25 16:04:47 2024 +0300

    scripts: fix docs

commit 9e781ff18db58ed9be35e259ecf3c669a4d41e02
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Oct 25 16:03:19 2024 +0300

    scripts: imp docs

commit 1dbc7849828cc4933bb5edc3257f158ac292d48e
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Oct 25 15:55:16 2024 +0300

    all: use new functions, add tests

commit dcbabaf4e37149a73969c52c9bfac2b9d9127a67
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Oct 25 13:23:50 2024 +0300

    aghos: add stat

commit 72d7c0f881835725e65db63ac2dd1c5f7a409036
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Oct 24 17:10:30 2024 +0300

    aghos: add windows functions
2024-10-29 14:28:59 +03:00
Stanislav Chzhen
e529d29e8a Pull request 2291: AGDNS-2374-slog-client
Squashed commit of the following:

commit e8e6dba18b8f44392bd88999e481723a00aa3042
Merge: 929283702 41cce6259
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Oct 22 13:46:26 2024 +0300

    Merge branch 'master' into AGDNS-2374-slog-client

commit 929283702a6a82163906e624f965b934e3b8074e
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Oct 15 14:30:00 2024 +0300

    client: imp tests

commit f29d8edb89e3f05e0bd9c3b0eccc0587882a3ed3
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Oct 14 15:03:08 2024 +0300

    client: imp docs

commit 0b4311ac26c704bbfa0edfc51767c9fc74f959a2
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Fri Oct 11 19:12:50 2024 +0300

    all: imp code

commit 1ad99ee3cb915bb19eff8b893deae1dd4d64b190
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Oct 10 20:59:46 2024 +0300

    all: slog client
2024-10-22 13:57:54 +03:00
Stanislav Chzhen
41cce62597 Pull request 2292: 7338-stats-log-msg
Updates #7338.

Squashed commit of the following:

commit a3ff2af22113d5d31f29f6626fb5b1897ca0d7b3
Merge: 9558de14e 5b45f6d50
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Oct 14 17:48:26 2024 +0300

    Merge branch 'master' into 7338-stats-log-msg

commit 9558de14ef7786cd37481fd13975b3c469c98083
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Oct 14 14:11:13 2024 +0300

    all: imp stats log msg
2024-10-14 18:14:09 +03:00
Stanislav Chzhen
5b45f6d508 Pull request 2293: fix-snap-build
Squashed commit of the following:

commit 44e617662007cf9dac5d11f67d8ea091564f6c2c
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Oct 14 17:15:51 2024 +0300

    all: fix typo

commit ed26f49be92161fcd7e6ffd3cf2c12c560ad2ab7
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Oct 14 17:14:05 2024 +0300

    all: fix snap build

commit 97d186d513c3ea600a8634cd5521b163bfd83394
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Oct 14 16:47:11 2024 +0300

    all: fix snap build
2024-10-14 17:47:47 +03:00
Stanislav Chzhen
73ff401b0f Pull request 2290: 7250-custom-client-cache
Updates #7250.

Squashed commit of the following:

commit 062660f17e164b92a3254c5b79543080c6884dd5
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Oct 9 17:42:17 2024 +0300

    all: upd chlog

commit 66fa039ede456454f26b3bfedefabfe71beda3ab
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Oct 9 17:30:58 2024 +0300

    home: custom client cache
2024-10-10 16:33:03 +03:00
Stanislav Chzhen
6363f8a2e7 Pull request 2286: AGDNS-2374-slog-safesearch
Squashed commit of the following:

commit 1909dfed99b8815c1215c709efcae77a70b52ea3
Merge: 3856fda5f 2c64ab5a5
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Oct 9 16:21:38 2024 +0300

    Merge branch 'master' into AGDNS-2374-slog-safesearch

commit 3856fda5f38a89d2df86bd8701e79d7f3fc02bb7
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Oct 8 20:04:34 2024 +0300

    home: imp code

commit de774009aa82bf45022fd9c359296e7ab45bf93d
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Oct 7 16:41:58 2024 +0300

    all: imp code

commit 038bae59d51497de1db7153e00e779db30f79721
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Oct 3 20:24:48 2024 +0300

    all: imp code

commit 792975e248bb04bce5a8ec767441fcf253c6d00f
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Oct 3 15:46:40 2024 +0300

    all: slog safesearch
2024-10-09 16:31:03 +03:00
Stanislav Chzhen
2c64ab5a51 Pull request 2289: upd-urlfilter
Updates #6818.

Squashed commit of the following:

commit d03b518457ef14d1c565a46c1dbfce1a47bf301d
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Fri Oct 4 18:58:10 2024 +0300

    all: upd chlog

commit 80ee9146998d622c173a9559b8d6af139b750d23
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Fri Oct 4 18:50:24 2024 +0300

    all: upd urlfilter
2024-10-04 19:10:32 +03:00
Stanislav Chzhen
df097341c3 Pull request 2287: 7315-fix-client-storage-panic
Closes #7315.

Squashed commit of the following:

commit 94c8473a14d98b45b5086de432bc68dd55eb654b
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Oct 3 19:09:23 2024 +0300

    all: upd chlog

commit 82b772b1dde19277832d44f9b22c47d22f90bf7e
Merge: e7f34cc43 4919630cc
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Oct 3 19:00:53 2024 +0300

    Merge branch 'master' into 7315-fix-client-storage-panic

commit e7f34cc435f53b80aca328a62e27c6aaa8fe6462
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Oct 3 17:55:39 2024 +0300

    home: fix client storage panic
2024-10-04 18:10:56 +03:00
Stanislav Chzhen
4919630ccc Pull request 2288: upd-chlog
Squashed commit of the following:

commit f064da09e4fa644c085b4e21f18bb27c65549b92
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Oct 3 18:33:58 2024 +0300

    all: imp chlog more

commit 662ba0f56995e9f955446b134f7c71e9d4d59ccd
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Oct 3 18:25:09 2024 +0300

    all: upd chlog
2024-10-03 18:47:28 +03:00
Ainar Garipov
b32b8f9c7e Pull request 2285: fix-test
Squashed commit of the following:

commit 006391c9c089a2d9fe7ad7157e609898be2ee225
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Oct 3 14:15:10 2024 +0300

    dnsforward: imp test more

commit ce743a35eff5271cb13a892373536ae815a74ebb
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Oct 2 21:13:02 2024 +0300

    dnsforward: fix test
2024-10-03 14:56:07 +03:00
193 changed files with 6483 additions and 5376 deletions

View File

@@ -1,7 +1,7 @@
'name': 'build'
'env':
'GO_VERSION': '1.23.2'
'GO_VERSION': '1.23.5'
'NODE_VERSION': '16'
'on':
@@ -95,7 +95,7 @@
'key': "${{ runner.os }}-node-${{ hashFiles('client/package-lock.json') }}"
'restore-keys': '${{ runner.os }}-node-'
- 'name': 'Set up Snapcraft'
'run': 'sudo apt-get -yq --no-install-suggests --no-install-recommends install snapcraft'
'run': 'sudo snap install snapcraft --classic'
- 'name': 'Set up QEMU'
'uses': 'docker/setup-qemu-action@v1'
- 'name': 'Set up Docker Buildx'

View File

@@ -1,7 +1,7 @@
'name': 'lint'
'env':
'GO_VERSION': '1.23.2'
'GO_VERSION': '1.23.5'
'on':
'push':

7
.gitignore vendored
View File

@@ -1,3 +1,8 @@
# This comment is used to simplify checking local copies of the file. Bump
# this number every time a significant change is made to this file.
#
# AdGuard-Project-Version: 1
# Please, DO NOT put your text editors' temporary files here. The more are
# added, the harder it gets to maintain and manage projects' gitignores. Put
# them into your global gitignore file instead.
@@ -8,6 +13,7 @@
# bottom to make sure they take effect.
*.db
*.log
*.out
*.snap
*.test
/agh-backup/
@@ -21,6 +27,7 @@
/launchpad_credentials
/querylog.json*
/snapcraft_login
/test-reports/
AdGuardHome
AdGuardHome.exe
AdGuardHome.yaml*

25
.markdownlint.json Normal file
View File

@@ -0,0 +1,25 @@
{
"ul-indent": {
"indent": 4
},
"ul-style": {
"style": "dash"
},
"emphasis-style": {
"style": "asterisk"
},
"no-duplicate-heading": {
"siblings_only": true
},
"no-inline-html": {
"allowed_elements": [
"a"
]
},
"no-trailing-spaces": {
"br_spaces": 0
},
"line-length": false,
"no-bare-urls": false,
"link-fragments": false
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,14 +1,14 @@
# Keep the Makefile POSIX-compliant. We currently allow hyphens in
# target names, but that may change in the future.
#
# See https://pubs.opengroup.org/onlinepubs/9699919799/utilities/make.html.
# See https://pubs.opengroup.org/onlinepubs/9799919799/utilities/make.html.
.POSIX:
# This comment is used to simplify checking local copies of the
# Makefile. Bump this number every time a significant change is made to
# this Makefile.
#
# AdGuard-Project-Version: 6
# AdGuard-Project-Version: 9
# Don't name these macros "GO" etc., because GNU Make apparently makes
# them exported environment variables with the literal value of
@@ -22,13 +22,12 @@ VERBOSE.MACRO = $${VERBOSE:-0}
CHANNEL = development
CLIENT_DIR = client
COMMIT = $$( git rev-parse --short HEAD )
DEPLOY_SCRIPT_PATH = not/a/real/path
DIST_DIR = dist
GOAMD64 = v1
GOPROXY = https://proxy.golang.org|direct
GOTOOLCHAIN = go1.23.2
GOTELEMETRY = off
GOTOOLCHAIN = go1.23.5
GPG_KEY = devteam@adguard.com
GPG_KEY_PASSPHRASE = not-a-real-password
NPM = npm
@@ -36,6 +35,7 @@ NPM_FLAGS = --prefix $(CLIENT_DIR)
NPM_INSTALL_FLAGS = $(NPM_FLAGS) --quiet --no-progress --ignore-engines\
--ignore-optional --ignore-platform --ignore-scripts
RACE = 0
REVISION = $${REVISION:-$$(git rev-parse --short HEAD)}
SIGN = 1
SIGNER_API_KEY = not-a-real-key
VERSION = v0.0.0
@@ -60,7 +60,6 @@ BUILD_RELEASE_DEPS_1 = go-deps
ENV = env\
CHANNEL='$(CHANNEL)'\
COMMIT='$(COMMIT)'\
DEPLOY_SCRIPT_PATH='$(DEPLOY_SCRIPT_PATH)' \
DIST_DIR='$(DIST_DIR)'\
GO="$(GO.MACRO)"\
@@ -70,17 +69,19 @@ ENV = env\
GOTOOLCHAIN='$(GOTOOLCHAIN)'\
GPG_KEY='$(GPG_KEY)'\
GPG_KEY_PASSPHRASE='$(GPG_KEY_PASSPHRASE)'\
NEXTAPI='$(NEXTAPI)'\
PATH="$${PWD}/bin:$$( "$(GO.MACRO)" env GOPATH )/bin:$${PATH}"\
RACE='$(RACE)'\
REVISION='$(REVISION)'\
SIGN='$(SIGN)'\
SIGNER_API_KEY='$(SIGNER_API_KEY)' \
NEXTAPI='$(NEXTAPI)'\
VERBOSE="$(VERBOSE.MACRO)"\
VERSION="$(VERSION)"\
# Keep the line above blank.
ENV_MISC = env\
PATH="$${PWD}/bin:$$("$(GO.MACRO)" env GOPATH)/bin:$${PATH}"\
VERBOSE="$(VERBOSE.MACRO)"\
# Keep the line above blank.
@@ -89,6 +90,8 @@ ENV_MISC = env\
# full build.
build: deps quick-build
init: ; git config core.hooksPath ./scripts/hooks
quick-build: js-build go-build
deps: js-deps go-deps
@@ -102,9 +105,6 @@ build-docker: ; $(ENV) "$(SHELL)" ./scripts/make/build-docker.sh
build-release: $(BUILD_RELEASE_DEPS_$(FRONTEND_PREBUILT))
$(ENV) "$(SHELL)" ./scripts/make/build-release.sh
clean: ; $(ENV) "$(SHELL)" ./scripts/make/clean.sh
init: ; git config core.hooksPath ./scripts/hooks
js-build: ; $(NPM) $(NPM_FLAGS) run build-prod
js-deps: ; $(NPM) $(NPM_INSTALL_FLAGS) ci
js-lint: ; $(NPM) $(NPM_FLAGS) run lint
@@ -127,17 +127,16 @@ go-check: go-tools go-lint go-test
# A quick check to make sure that all operating systems relevant to the
# development of the project can be typechecked and built successfully.
go-os-check:
env GOOS='darwin' "$(GO.MACRO)" vet ./internal/...
env GOOS='freebsd' "$(GO.MACRO)" vet ./internal/...
env GOOS='openbsd' "$(GO.MACRO)" vet ./internal/...
env GOOS='linux' "$(GO.MACRO)" vet ./internal/...
env GOOS='windows' "$(GO.MACRO)" vet ./internal/...
openapi-lint: ; cd ./openapi/ && $(YARN) test
openapi-show: ; cd ./openapi/ && $(YARN) start
$(ENV) GOOS='darwin' "$(GO.MACRO)" vet ./internal/...
$(ENV) GOOS='freebsd' "$(GO.MACRO)" vet ./internal/...
$(ENV) GOOS='openbsd' "$(GO.MACRO)" vet ./internal/...
$(ENV) GOOS='linux' "$(GO.MACRO)" vet ./internal/...
$(ENV) GOOS='windows' "$(GO.MACRO)" vet ./internal/...
txt-lint: ; $(ENV) "$(SHELL)" ./scripts/make/txt-lint.sh
md-lint: ; $(ENV_MISC) "$(SHELL)" ./scripts/make/md-lint.sh
sh-lint: ; $(ENV_MISC) "$(SHELL)" ./scripts/make/sh-lint.sh
openapi-lint: ; cd ./openapi/ && $(YARN) test
openapi-show: ; cd ./openapi/ && $(YARN) start

View File

@@ -114,7 +114,7 @@ If you're running **Linux,** there's a secure and easy way to install AdGuard Ho
[Docker Hub]: https://hub.docker.com/r/adguard/adguardhome
[Snap Store]: https://snapcraft.io/adguard-home
[wiki-start]: https://github.com/AdguardTeam/AdGuardHome/wiki/Getting-Started
[wiki-start]: https://adguard-dns.io/kb/adguard-home/getting-started/
### <a href="#guides" id="guides" name="guides">Guides</a>

View File

@@ -8,7 +8,7 @@
'variables':
'channel': 'edge'
'dockerFrontend': 'adguard/home-js-builder:2.0'
'dockerGo': 'adguard/go-builder:1.23.2--1'
'dockerGo': 'adguard/go-builder:1.23.5--1'
'stages':
- 'Build frontend':
@@ -142,13 +142,15 @@
# Install Qemu, create builder.
docker version -f '{{ .Server.Experimental }}'
docker buildx rm buildx-builder || :
docker buildx create --name buildx-builder --driver docker-container\
--use
docker buildx create \
--name buildx-builder \
--driver docker-container \
--use
docker buildx inspect --bootstrap
# Login to DockerHub.
docker login -u="${bamboo.dockerHubUsername}"\
-p="${bamboo.dockerHubPassword}"
docker login -u="${bamboo.dockerHubUsername}" \
-p="${bamboo.dockerHubPassword}"
# Boot the builder.
docker buildx inspect --bootstrap
@@ -157,14 +159,14 @@
docker info
# Prepare and push the build.
env\
CHANNEL="${bamboo.channel}"\
COMMIT="${bamboo.repository.revision.number}"\
DIST_DIR='dist'\
DOCKER_IMAGE_NAME='adguard/adguardhome'\
DOCKER_OUTPUT="type=image,name=adguard/adguardhome,push=true"\
VERBOSE='1'\
sh ./scripts/make/build-docker.sh
env \
CHANNEL="${bamboo.channel}" \
REVISION="${bamboo.repository.revision.number}" \
DIST_DIR='dist' \
DOCKER_IMAGE_NAME='adguard/adguardhome' \
DOCKER_OUTPUT="type=image,name=adguard/adguardhome,push=true" \
VERBOSE='1' \
sh ./scripts/make/build-docker.sh
'environment':
DOCKER_CLI_EXPERIMENTAL=enabled
'final-tasks':
@@ -276,7 +278,7 @@
'variables':
'channel': 'beta'
'dockerFrontend': 'adguard/home-js-builder:2.0'
'dockerGo': 'adguard/go-builder:1.23.2--1'
'dockerGo': 'adguard/go-builder:1.23.5--1'
# release-vX.Y.Z branches are the branches from which the actual final
# release is built.
- '^release-v[0-9]+\.[0-9]+\.[0-9]+':
@@ -292,4 +294,4 @@
'variables':
'channel': 'release'
'dockerFrontend': 'adguard/home-js-builder:2.0'
'dockerGo': 'adguard/go-builder:1.23.2--1'
'dockerGo': 'adguard/go-builder:1.23.5--1'

View File

@@ -6,7 +6,7 @@
'name': 'AdGuard Home - Build and run tests'
'variables':
'dockerFrontend': 'adguard/home-js-builder:2.0'
'dockerGo': 'adguard/go-builder:1.23.2--1'
'dockerGo': 'adguard/go-builder:1.23.5--1'
'channel': 'development'
'stages':
@@ -196,5 +196,5 @@
# may need to build a few of these.
'variables':
'dockerFrontend': 'adguard/home-js-builder:2.0'
'dockerGo': 'adguard/go-builder:1.23.2--1'
'dockerGo': 'adguard/go-builder:1.23.5--1'
'channel': 'candidate'

View File

@@ -641,7 +641,7 @@
"show_processed_responses": "Verarbeitet",
"blocked_safebrowsing": "Gesperrt durch Internetsicherheit",
"blocked_adult_websites": "Gesperrt durch Kindersicherung",
"blocked_threats": "Bedrohungen blockiert",
"blocked_threats": "Gesperrte Bedrohungen",
"allowed": "Zugelassen",
"filtered": "Gefiltert",
"rewritten": "Umgeschrieben",

View File

@@ -542,7 +542,7 @@
"stats_params": "Tilastoinnin määritys",
"config_successfully_saved": "Asetukset tallennettiin",
"interval_6_hour": "6 tuntia",
"interval_24_hour": "24 tuntia",
"interval_24_hour": "24 tunnilta",
"interval_days": "{{count}} päivä",
"interval_days_plural": "{{count}} päivää",
"domain": "Verkkotunnus",

View File

@@ -166,7 +166,7 @@
"encryption_settings": "Encryptie instellingen",
"dhcp_settings": "DHCP instellingen",
"upstream_dns": "Upstream DNS-servers",
"upstream_dns_help": "Een server-adres per regel invoeren. <a>Meer weten</a> over het configureren van upstream DNS-servers.",
"upstream_dns_help": "Een server-adres per regel invoeren. <a>Meer informatie</a> over het configureren van upstream DNS-servers.",
"upstream_dns_configured_in_file": "Geconfigureerd in {{path}}",
"test_upstream_btn": "Test upstream",
"upstreams": "Upstreams",

View File

@@ -122,7 +122,7 @@
"stats_query_domain": "Najczęściej wyszukiwane domeny",
"for_last_hours": "w ciągu ostatniej {{count}} godziny",
"for_last_hours_plural": "w ciągu ostatnich {{count}} godzin",
"for_last_days": "za ostatni dzień {{count}}",
"for_last_days": "za ostatni {{count}} dzień",
"for_last_days_plural": "z ostatnich {{count}} dni",
"stats_disabled": "Statystyki zostały wyłączone. Można je włączyć na <0>stronie ustawień</0>.",
"stats_disabled_short": "Statystyki zostały wyłączone",
@@ -130,7 +130,7 @@
"requests_count": "Licznik żądań",
"top_blocked_domains": "Najpopularniejsze zablokowane domeny",
"top_clients": "Główni klienci",
"no_clients_found": "Nie znaleziono klienta",
"no_clients_found": "Nie znaleziono klientów",
"general_statistics": "Ogólne statystyki",
"top_upstreams": "Często żądane serwery nadrzędne",
"no_upstreams_data_found": "Brak danych dotyczących serwerów nadrzędnych",

View File

@@ -7,7 +7,7 @@
"local_ptr_desc": "ස්ථානීය PTR විමසුම් සඳහා ඇඩ්ගාර්ඩ් හෝම් භාවිතා කරන ව.නා.ප. සේවාදායක. මෙම සේවාදායක පුද්ගලික අ.ජා.කෙ. ලිපින පරාසවල PTR විමසුම් විසඳීමට භාවිතා කරයි, උදාහරණයක් ලෙස ප්‍රතිවර්ත ව.නා.ප. භාවිතයෙන් \"192.168.12.34\". මෙය සකසා නැති නම්, ඇඩ්ගාර්ඩ් හෝම් හි ලිපින සඳහා හැරුනු විට ඔබගේ මෙහෙයුම් පද්ධතියේ පෙරනිමි ව.නා.ප. විසදුම්වල ලිපින භාවිතා කරයි.",
"local_ptr_default_resolver": "පෙරනිමි පරිදි, ඇඩ්ගාර්ඩ් හෝම් පහත ප්‍රතිවර්ත ව.නා.ප. පිළිවිසඳු භාවිතා කරයි: {{ip}}.",
"local_ptr_no_default_resolver": "ඇඩ්ගාර්ඩ් හෝම් හට මෙම පද්ධතිය සඳහා සුදුසු පුද්ගලික ප්‍රතිවර්ත ව.නා.ප. පිළිවිසඳු නිශ්චය කරගත නොහැකි විය.",
"local_ptr_placeholder": "පේළියකට එක් සේවාදායක ලිපිනය බැගින් යොදන්න",
"local_ptr_placeholder": "පේළියකට අ.ජා.කෙ. ලිපිනය බැගින් ලියන්න",
"resolve_clients_title": "අනුග්‍රාහකවල අ.ජා.කෙ. ලිපින ප්‍රතිවර්ත විසඳීම සබල කරන්න",
"use_private_ptr_resolvers_title": "පෞද්. ප්‍රතිවර්ත ව.නා.ප. පිළිවිසඳු භාවිතය",
"check_dhcp_servers": "ග.ධා.වි.කෙ. සේවාදායක පරීක්‍ෂා කරන්න",
@@ -102,7 +102,6 @@
"stats_malware_phishing": "අවහිර කළ ද්වේශාංග/තතුබෑම්",
"stats_adult": "අවහිර කළ වැඩිහිටි වියමන අඩවි",
"stats_query_domain": "ප්‍රචලිත විමසන ලද වසම්",
"for_last_24_hours": "පසුගිය පැය 24 සඳහා",
"for_last_days": "පසුගිය දවස් {{count}} සඳහා",
"for_last_days_plural": "පසුගිය දවස් {{count}} සඳහා",
"stats_disabled": "සංඛ්‍යාලේඛන අබල කර ඇත. එය <0>සැකසුම් පිටුවෙන්</0> සබල කළ හැකිය.",
@@ -115,13 +114,15 @@
"general_statistics": "පොදු සංඛ්‍යාලේඛන",
"number_of_dns_query_days": "පසුගිය දවස් {{count}} සඳහා සැකසූ ව.නා.ප. විමසුම් ගණන",
"number_of_dns_query_days_plural": "පසුගිය දවස් {{count}} සඳහා සැකසූ ව.නා.ප. විමසුම් ගණන",
"number_of_dns_query_24_hours": "පසුගිය පැය 24 සඳහා සැකසූ ව.නා.ප. විමසුම් ගණන",
"number_of_dns_query_hours": "පසුගිය පැය {{count}} සඳහා සැකසූ ව.නා.ප. විමසුම් ගණන",
"number_of_dns_query_hours_plural": "පසුගිය පැය {{count}} සඳහා සැකසූ ව.නා.ප. විමසුම් ගණන",
"number_of_dns_query_blocked_24_hours": "දැන්වීම් වාරණ පෙරහන් සහ සත්කාරක වාරණ ලැයිස්තු මගින් අවහිර කළ ව.නා.ප. ඉල්ලීම් ගණන",
"number_of_dns_query_blocked_24_hours_by_sec": "ඇඩ්ගාර්ඩ් පිරික්සුම් ආරක්‍ෂණ ඒකකය මගින් අවහිර කළ ව.නා.ප. ඉල්ලීම් ගණන",
"number_of_dns_query_blocked_24_hours_adult": "අවහිර කළ වැඩිහිටි වියමන අඩවි ගණන",
"enforced_save_search": "ආරක්‍ෂිත සෙවීම බලාත්මක කළ",
"number_of_dns_query_to_safe_search": "ආරක්‍ෂිත සෙවීම බලාත්මක කළ සෙවුම් යන්ත්‍ර සඳහා ව.නා.ප. ඉල්ලීම් ගණන",
"average_processing_time": "සාමාන්‍ය සැකසුම් කාලය",
"response_time": "ප්‍රතිචාර කාලය",
"average_processing_time_hint": "ව.නා.ප. ඉල්ලීමක් සැකසීමේ සාමාන්‍ය කාලය මිලි තත්පර වලින්",
"block_domain_use_filters_and_hosts": "පෙරහන් හා සත්කාරක ගොනු භාවිතයෙන් වසම් අවහිර කරන්න",
"filters_block_toggle_hint": "ඔබට අවහිර කිරීමේ නීති <a>පෙරහන්</a> තුළ පිහිටුවිය හැකිය.",
@@ -130,7 +131,7 @@
"use_adguard_parental": "ඇඩ්ගාර්ඩ් දෙමාපිය පාලන වියමන සේවාව භාවිතා කරන්න",
"use_adguard_parental_hint": "වසමේ වැඩිහිටියන්ට අදාල කරුණු අඩංගු දැයි ඇඩ්ගාර්ඩ් හෝම් විසින් පරීක්‍ෂා කරනු ඇත. එය පිරික්සුම් ආරක්‍ෂණ වියමන සේවාව මෙන් රහස්‍යතා හිතකාමී යෙ.ක්‍ර. අ.මු. (API) භාවිතා කරයි.",
"enforce_safe_search": "ආරක්‍ෂිත සෙවුම භාවිතා කරන්න",
"enforce_save_search_hint": "ඇඩ්ගාර්ඩ් හෝම් පහත සෙවුම් යන්ත්‍ර තුළ ආරක්‍ෂිත සෙවුම බලාත්මක කරනු ඇත: ගූගල්, යූටියුබ්, බින්ග්, ඩක්ඩක්ගෝ, යාන්ඩෙක්ස් සහ පික්සාබේ.",
"enforce_save_search_hint": "ඇඩ්ගාර්ඩ් හෝම් පහත සෙවුම් යන්ත්‍ර තුළ ආරක්‍ෂිත සෙවුම බලාත්මක කරනු ඇත: ගූගල්, යූටියුබ්, බින්ග්, ඩක්ඩක්ගෝ, එකොසියා, යාන්ඩෙක්ස් සහ පික්සාබේ.",
"no_servers_specified": "සේවාදායක කිසිවක් නිශ්චිතව දක්වා නැත",
"general_settings": "පොදු සැකසුම්",
"dns_settings": "ව.නා.ප. සැකසුම්",
@@ -196,12 +197,14 @@
"example_comment_hash": "# එසේම අදහස් දැක්වීමක්.",
"example_regex_meaning": "නිශ්චිතව දක්වා ඇති නිත්‍ය වාක්‍යවිධියට ගැළපෙන වසම් වෙත ප්‍රවේශය අවහිර කරයි.",
"example_upstream_regular": "සාමාන්‍ය ව.නා.ප. (UDP හරහා);",
"example_upstream_regular_port": "සාමාන්‍ය ව.නා.ප. (UDP හරහා, තොට සමඟ);",
"example_upstream_udp": "සාමාන්‍ය ව.නා.ප. (UDP, සත්කාරක-නම හරහා);",
"example_upstream_dot": "සංකේතිත <0>TLS-මගින්-ව.නා.ප.</0>;",
"example_upstream_doh": "සංකේතිත <0>HTTPS-මගින්-ව.නා.ප.</0>;",
"example_upstream_doq": "සංකේතිත <0>QUIC-මගින්-ව.නා.ප.</0>;",
"example_upstream_sdns": "<1>DNSCrypt</1> හෝ <2>HTTPS-මගින්-ව.නා.ප.</2> පිළිවිසඳු සඳහා <0>ව.නා.ප. මුද්දර</0>;",
"example_upstream_tcp": "සාමාන්‍ය ව.නා.ප. (TCP/ස.පා.කෙ. හරහා);",
"example_upstream_tcp_port": "සාමාන්‍ය ව.නා.ප. (TCP හරහා, තොට සමඟ);",
"example_upstream_tcp_hostname": "සාමාන්‍ය ව.නා.ප. (ස.පා.කෙ., සත්කාරක-නම හරහා);",
"all_lists_up_to_date_toast": "සියළුම ලැයිස්තු දැනටමත් යාවත්කාලීනයි",
"dns_test_ok_toast": "සඳහන් කළ ව.නා.ප. සේවාදායක නිවැරදිව ක්‍රියා කරයි",
@@ -275,6 +278,7 @@
"edns_use_custom_ip": "EDNS සඳහා අභිරුචි අ.ජා.කෙ. යොදාගන්න",
"edns_use_custom_ip_desc": "EDNS සඳහා අභිරුචි අ.ජා.කෙ. භාවිතයට ඉඩදෙන්න",
"rate_limit_desc": "එක් අනුග්‍රාහකයකට ඉඩ දී ඇති තත්පරයට ඉල්ලීම් ගණන. එය 0 ලෙස සැකසීම යනුවෙන් අදහස් කරන්නේ සීමාවක් නැති බවයි.",
"rate_limit_whitelist_placeholder": "පේළියකට අ.ජා.කෙ. ලිපිනය බැගින් ලියන්න",
"blocking_ipv4_desc": "අවහිර කළ A ඉල්ලීමක් සඳහා ආපසු එවිය යුතු අ.ජා.කෙ. (IP) ලිපිනය",
"blocking_ipv6_desc": "අවහිර කළ AAAA ඉල්ලීමක් සඳහා ආපසු එවිය යුතු අ.ජා.කෙ. (IP) ලිපිනය",
"blocking_mode_default": "පොදු: දැන්වීම් අවහිර කරන ආකාරයේ නීතියක් මගින් අවහිර කළ විට REFUSED සමඟ ප්‍රතිචාර දක්වයි; /etc/host-style ආකාරයේ නීතියක් මගින් අවහිර කළ විට නීතියේ දක්වා ඇති අ.ජා.කෙ. ලිපිනය සමඟ ප්‍රතිචාර දක්වයි",
@@ -505,8 +509,8 @@
"statistics_enable": "සංඛ්‍යාලේඛන සබල කරන්න",
"ignore_domains": "නොසලකන වසම් (පේළියකට එක බැගින්)",
"ignore_domains_title": "නොසලකන වසම්",
"ignore_domains_desc_stats": "සංඛ්‍යාලේඛනයෙහි මෙම වසම් සඳහා විමසුම් නොලියැවෙයි",
"ignore_domains_desc_query": "විමසුම් සටහනෙහි මෙම වසම් සඳහා විමසුම් නොලියැවෙයි",
"ignore_domains_desc_stats": "මෙම නීති වලට ගැළපෙන විමසුම් සංඛ්‍යාලේඛනයට නොලියැවෙයි",
"ignore_domains_desc_query": "විමසුම් සටහන මෙම නීති වලට ගැළපෙන විමසුම් නොලියැවෙයි",
"interval_hours": "පැය {{count}}",
"interval_hours_plural": "පැය {{count}}",
"filters_configuration": "පෙරහන් වින්‍යාසය",
@@ -615,8 +619,8 @@
"use_saved_key": "පෙර සුරැකි යතුර භාවිතා කරන්න",
"parental_control": "දෙමාපිය පාලනය",
"safe_browsing": "ආරක්‍ෂිත පිරික්සුම",
"served_from_cache": "{{value}} <i>(නිහිතයෙන් ගැනිණි)</i>",
"form_error_password_length": "මුරපදය අවම වශයෙන් අකුරු {{value}} ක් දිගු විය යුතුමයි",
"served_from_cache_label": "නිහිතයෙන් සැපයිණි",
"form_error_password_length": "මුරපදය අකුරු {{min}} සහ {{value}} ක් අතර විය යුතු",
"anonymizer_notification": "<0>සටහන:</0> අ.ජා.කෙ. නිර්නාමිකකරණය සබලයි. ඔබට එය <1>පොදු සැකසුම්</1> හරහා අබල කිරීමට හැකිය .",
"confirm_dns_cache_clear": "ඔබට ව.නා.ප. නිහිතය හිස් කිරීමට වුවමනාද?",
"cache_cleared": "ව.නා.ප. නිහිතය හිස් කෙරිණි",
@@ -646,6 +650,7 @@
"log_and_stats_section_label": "විමසුම් සටහන හා සංඛ්‍යාලේඛන",
"ignore_query_log": "විමසුම් සටහනට මෙම අනුග්‍රාහකය යොදන්න එපා",
"ignore_statistics": "සංඛ්‍යාලේඛනයට මෙම අනුග්‍රාහකය යොදන්න එපා",
"schedule_services": "සේවා අවහිර විරාමය",
"schedule_invalid_select": "ආරම්භක වේලාව අවසන් වේලාවට කලින් විය යුතුය",
"schedule_select_days": "දවස් තෝරන්න",
"schedule_timezone": "වේලා කලාපයක් තෝරන්න",

View File

@@ -461,7 +461,7 @@
"form_enter_mac": "Skriv in MAC",
"form_enter_id": "Ange identifierare",
"form_add_id": "Lägg till identifierare",
"form_client_name": "Skriv in klientnamn",
"form_client_name": "Ange klientnamn",
"name": "Namn",
"client_name": "Klient {{id}}",
"client_global_settings": "Använda globala inställningar",
@@ -674,7 +674,6 @@
"use_saved_key": "Använd den tidigare sparade nyckeln",
"parental_control": "Föräldrakontroll",
"safe_browsing": "Säker surfning",
"served_from_cache": "{{value}} <i>(levereras från cache)</i>",
"form_error_password_length": "Lösenordet måste vara {{min}} till {{max}} tecken långt",
"anonymizer_notification": "<0>Observera:</0> IP-anonymisering är aktiverad. Du kan inaktivera den i <1>Allmänna inställningar</1>.",
"confirm_dns_cache_clear": "Är du säker på att du vill rensa DNS-cache?",

View File

@@ -20,7 +20,7 @@
"resolve_clients_title": "Увімкнути зворотне вирішення IP-адрес клієнтів",
"resolve_clients_desc": "Визначати доменні імена клієнтів за допомогою PTR-запитів до відповідних серверів — приватних DNS-серверів для локальних клієнтів та upstream-серверів для клієнтів з публічними IP-адресами.",
"use_private_ptr_resolvers_title": "Використовувати приватні зворотні DNS-резолвери",
"use_private_ptr_resolvers_desc": "Надсилати зворотні DNS-запити до вказаних серверів для клієнтів, що обслуговуються локально. Якщо вимкнено, AdGuard Home буде відповідати NXDOMAIN на всі такі PTR-запити, окрім запитів про клієнтів, що уже відомі завдяки DHCP, /etc/hosts тощо.",
"use_private_ptr_resolvers_desc": "Розвʼязувати запити PTR, SOA та NS для доменів ARPA, що містять приватні IP-адреси, через приватні вихідні сервери, DHCP, /etc/hosts тощо. Якщо вимкнено, AdGuard Home відповідатиме на всі такі запити з NXDOMAIN.",
"check_dhcp_servers": "Перевірити DHCP-сервери",
"save_config": "Зберегти конфігурацію",
"enabled_dhcp": "DHCP-сервер увімкнено",
@@ -343,10 +343,10 @@
"known_tracker": "Відомі трекери",
"install_welcome_title": "Вітаємо в AdGuard Home!",
"install_welcome_desc": "AdGuard Home — це мережевий DNS-сервер, що блокує рекламу та відстеження. Його мета — надати вам контроль над усією мережею та всіма пристроями в ній без потреби використання програми на стороні клієнта.",
"install_settings_title": "Веб-інтерфейс адміністратора",
"install_settings_title": "Вебінтерфейс адміністратора",
"install_settings_listen": "Мережевий інтерфейс",
"install_settings_port": "Порт",
"install_settings_interface_link": "Веб-інтерфейс адміністратора AdGuard Home буде доступний за такими адресами:",
"install_settings_interface_link": "Вебінтерфейс адміністратора AdGuard Home буде доступний за такими адресами:",
"form_error_port": "Уведіть правильне значення порту",
"install_settings_dns": "DNS-сервер",
"install_settings_dns_desc": "Вам потрібно буде налаштувати свої пристрої або маршрутизатор для використання DNS-сервера за такими адресами:",

View File

@@ -20,7 +20,7 @@
"resolve_clients_title": "啟用用戶端的 IP 位址之反向的解析",
"resolve_clients_desc": "透過傳送指標PTR查詢到對應的解析器私人 DNS 伺服器供區域的用戶端,上游的伺服器供有公共 IP 位址的用戶端),反向地解析用戶端的 IP 位址變為它們的主機名稱。",
"use_private_ptr_resolvers_title": "使用私人反向的 DNS 解析器",
"use_private_ptr_resolvers_desc": "使用私人上游伺服器、DHCP、/etc/hosts 等方式解析包含私人 IP 位址的 ARPA 網域 PTR、SOA 和 NS 請求。如果禁用AdGuard Home 將對所有此類請求以 NXDOMAIN 回。",
"use_private_ptr_resolvers_desc": "通過私人上游伺服器、DHCP、/etc/hosts 等等,對包含私人 IP 位址的 ARPA 網域解析 PTR、SOA 和 NS 請求。如果禁用AdGuard Home 將對所有此類請求以 NXDOMAIN 回。",
"check_dhcp_servers": "檢查動態主機設定協定DHCP伺服器",
"save_config": "儲存配置",
"enabled_dhcp": "動態主機設定協定DHCP伺服器被啟用",

View File

@@ -46,7 +46,7 @@ export const getStats = () => async (dispatch: any) => {
const normalizedTopClients = normalizeTopStats(stats.top_clients);
const clientsParams = getParamsForClientsSearch(normalizedTopClients, 'name');
const clients = await apiClient.findClients(clientsParams);
const clients = await apiClient.searchClients(clientsParams);
const topClientsWithInfo = addClientInfo(normalizedTopClients, clients, 'name');
const normalizedStats = {

View File

@@ -415,7 +415,7 @@ class Api {
// Per-client settings
GET_CLIENTS = { path: 'clients', method: 'GET' };
FIND_CLIENTS = { path: 'clients/find', method: 'GET' };
SEARCH_CLIENTS = { path: 'clients/search', method: 'POST' };
ADD_CLIENT = { path: 'clients/add', method: 'POST' };
@@ -453,11 +453,12 @@ class Api {
return this.makeRequest(path, method, parameters);
}
findClients(params: any) {
const { path, method } = this.FIND_CLIENTS;
const url = getPathWithQueryString(path, params);
return this.makeRequest(url, method);
searchClients(config: any) {
const { path, method } = this.SEARCH_CLIENTS;
const parameters = {
data: config,
};
return this.makeRequest(path, method, parameters);
}
// DNS access settings

View File

@@ -23,7 +23,7 @@ interface RowProps {
const Row = ({ label, count, response_status, tooltipTitle, translationComponents }: RowProps) => {
const content = response_status ? (
<LogsSearchLink response_status={response_status}>{formatNumber(count)}</LogsSearchLink>
<LogsSearchLink response_status={response_status}>{count}</LogsSearchLink>
) : (
count
);
@@ -77,16 +77,16 @@ const Counters = ({ refreshButton, subtitle }: CountersProps) => {
? t('number_of_dns_query_hours', { count: msToHours(interval) })
: t('number_of_dns_query_days', { count: msToDays(interval) });
const rows = [
const rows: RowProps[] = [
{
label: 'dns_query',
count: numDnsQueries.toString(),
count: formatNumber(numDnsQueries),
tooltipTitle: dnsQueryTooltip,
response_status: RESPONSE_FILTER.ALL.QUERY,
},
{
label: 'blocked_by',
count: numBlockedFiltering.toString(),
count: formatNumber(numBlockedFiltering),
tooltipTitle: 'number_of_dns_query_blocked_24_hours',
response_status: RESPONSE_FILTER.BLOCKED.QUERY,
@@ -98,19 +98,19 @@ const Counters = ({ refreshButton, subtitle }: CountersProps) => {
},
{
label: 'stats_malware_phishing',
count: numReplacedSafebrowsing.toString(),
count: formatNumber(numReplacedSafebrowsing),
tooltipTitle: 'number_of_dns_query_blocked_24_hours_by_sec',
response_status: RESPONSE_FILTER.BLOCKED_THREATS.QUERY,
},
{
label: 'stats_adult',
count: numReplacedParental.toString(),
count: formatNumber(numReplacedParental),
tooltipTitle: 'number_of_dns_query_blocked_24_hours_adult',
response_status: RESPONSE_FILTER.BLOCKED_ADULT_WEBSITES.QUERY,
},
{
label: 'enforced_save_search',
count: numReplacedSafesearch.toString(),
count: formatNumber(numReplacedSafesearch),
tooltipTitle: 'number_of_dns_query_to_safe_search',
response_status: RESPONSE_FILTER.SAFE_SEARCH.QUERY,
},

View File

@@ -10,6 +10,7 @@ import Card from '../ui/Card';
import DomainCell from './DomainCell';
import { DASHBOARD_TABLES_DEFAULT_PAGE_SIZE, TABLES_MIN_ROWS } from '../../helpers/constants';
import { formatNumber } from '../../helpers/helpers';
interface TimeCellProps {
value?: string | number;
@@ -20,7 +21,7 @@ const TimeCell = ({ value }: TimeCellProps) => {
return '';
}
const valueInMilliseconds = round(Number(value) * 1000);
const valueInMilliseconds = formatNumber(round(Number(value) * 1000));
return (
<div className="logs__row o-hidden">

View File

@@ -154,7 +154,7 @@ const Dashboard = ({
}}
disabled={processingProtection}>
{protectionDisabledDuration
? `${t('enable_protection_timer')} ${getRemaningTimeText(protectionDisabledDuration)}`
? `${t('enable_protection_timer', { time: getRemaningTimeText(protectionDisabledDuration) })}`
: getProtectionBtnText(protectionEnabled)}
</button>

View File

@@ -306,7 +306,7 @@ const ClientsTable = ({
return content;
}
return <LogsSearchLink search={row.original.ids[0]}>{content}</LogsSearchLink>;
return <LogsSearchLink search={row.original.name}>{content}</LogsSearchLink>;
},
},
{

View File

@@ -14,6 +14,17 @@
font-size: 15px;
}
.guide__list {
margin-top: 16px;
padding-left: 0;
}
@media screen and (min-width: 768px) {
.guide__list {
padding-left: 24px;
}
}
.guide__address {
display: block;
margin-bottom: 7px;

View File

@@ -33,13 +33,13 @@ const SetupGuide = ({
<Trans>install_devices_address</Trans>:
</div>
<div className="mt-3">
<ul className="guide__list">
{dnsAddresses.map((ip: any) => (
<li key={ip} className="guide__address">
{ip}
</li>
))}
</div>
</ul>
</div>
<Guide dnsAddresses={dnsAddresses} />

View File

@@ -238,6 +238,12 @@ export default {
"homepage": "https://github.com/hagezi/dns-blocklists",
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_51.txt"
},
"hagezi_samsung_tracker_blocklist": {
"name": "HaGeZi's Samsung Tracker Blocklist",
"categoryId": "other",
"homepage": "https://github.com/hagezi/dns-blocklists",
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_61.txt"
},
"hagezi_the_worlds_most_abused_tlds": {
"name": "HaGeZi's The World's Most Abused TLDs",
"categoryId": "security",
@@ -256,6 +262,12 @@ export default {
"homepage": "https://github.com/hagezi/dns-blocklists",
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_49.txt"
},
"hagezi_windows_office_tracker_blocklist": {
"name": "HaGeZi's Windows/Office Tracker Blocklist",
"categoryId": "other",
"homepage": "https://github.com/hagezi/dns-blocklists",
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_63.txt"
},
"hagezi_xiaomi_tracking_blocklist": {
"name": "HaGeZi's Xiaomi Tracker Blocklist",
"categoryId": "other",
@@ -346,17 +358,17 @@ export default {
"homepage": "https://github.com/uBlockOrigin/uAssets",
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_50.txt"
},
"ukrainian_security_filter": {
"name": "Ukrainian Security Filter",
"categoryId": "other",
"homepage": "https://github.com/braveinnovators/ukrainian-security-filter",
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_62.txt"
},
"urlhaus_filter_online": {
"name": "Malicious URL Blocklist (URLHaus)",
"categoryId": "security",
"homepage": "https://urlhaus.abuse.ch/",
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_11.txt"
},
"windowsspyblocker_hosts_spy_rules": {
"name": "WindowsSpyBlocker - Hosts spy rules",
"categoryId": "other",
"homepage": "https://github.com/crazy-max/WindowsSpyBlocker",
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_23.txt"
}
}
}

View File

@@ -451,13 +451,10 @@ export const getParamsForClientsSearch = (data: any, param: any, additionalParam
clients.add(e[additionalParam]);
}
});
const params = {};
const ids = Array.from(clients.values());
ids.forEach((id, i) => {
params[`ip${i}`] = id;
});
return params;
return {
clients: Array.from(clients).map((id) => ({ id })),
};
};
/**
@@ -524,7 +521,7 @@ export const getObjDiff = (initialValues: any, values: any) =>
* @param num {number} to format
* @returns {string} Returns a string with a language-sensitive representation of this number
*/
export const formatNumber = (num: any) => {
export const formatNumber = (num: number): string => {
const currentLanguage = i18n.languages[0] || DEFAULT_LANGUAGE;
return num.toLocaleString(currentLanguage);
};
@@ -673,9 +670,16 @@ export const countClientsStatistics = (ids: any, autoClients: any) => {
* @param {function} t translate
* @returns {string}
*/
export const formatElapsedMs = (elapsedMs: any, t: any) => {
const formattedElapsedMs = parseInt(elapsedMs, 10) || parseFloat(elapsedMs).toFixed(2);
return `${formattedElapsedMs} ${t('milliseconds_abbreviation')}`;
export const formatElapsedMs = (elapsedMs: string, t: (key: string) => string) => {
const parsedElapsedMs = parseInt(elapsedMs, 10);
if (Number.isNaN(parsedElapsedMs)) {
return elapsedMs;
}
const formattedMs = formatNumber(parsedElapsedMs);
return `${formattedMs} ${t('milliseconds_abbreviation')}`;
};
/**
@@ -757,12 +761,9 @@ type NestedObject = {
order: number;
};
export const getObjectKeysSorted = <
T extends Record<string, NestedObject>,
K extends keyof NestedObject
>(
export const getObjectKeysSorted = <T extends Record<string, NestedObject>, K extends keyof NestedObject>(
object: T,
sortKey: K
sortKey: K,
): string[] => {
return Object.entries(object)
.sort(([, a], [, b]) => (a[sortKey] as number) - (b[sortKey] as number))

View File

@@ -1,5 +1,5 @@
{
"timeUpdated": "2024-09-30T10:04:46.112Z",
"timeUpdated": "2025-01-13T10:04:54.031Z",
"categories": {
"0": "audio_video_player",
"1": "comments",
@@ -2515,6 +2515,13 @@
"url": "http://www.ancoramediasolutions.com/",
"companyId": "ancora"
},
"android": {
"name": "Android",
"categoryId": 101,
"url": "https://www.android.com/",
"companyId": "google",
"source": "AdGuard"
},
"anetwork": {
"name": "Anetwork",
"categoryId": 4,
@@ -8195,7 +8202,7 @@
"google_dns": {
"name": "Google DNS",
"categoryId": 10,
"url": "hhttps://dns.google/",
"url": "https://dns.google/",
"companyId": "google",
"source": "AdGuard"
},
@@ -9765,6 +9772,13 @@
"url": "http://www.jetinteractive.com.au/",
"companyId": "jet_interactive"
},
"jetbrains": {
"name": "JetBrains",
"categoryId": 8,
"url": "https://www.jetbrains.com/",
"companyId": "jetbrains",
"source": "AdGuard"
},
"jetlore": {
"name": "Jetlore",
"categoryId": 6,
@@ -13980,6 +13994,13 @@
"url": "http://prostor-lite.ru/",
"companyId": "prostor"
},
"proton_ag": {
"name": "Proton AG",
"categoryId": 2,
"url": "https://proton.me/",
"companyId": "proton_foundation",
"source": "AdGuard"
},
"provide_support": {
"name": "Provide Support",
"categoryId": 2,
@@ -15654,7 +15675,7 @@
"shareaholic": {
"name": "Shareaholic",
"categoryId": 6,
"url": "hhttps://www.shareaholic.com/",
"url": "https://www.shareaholic.com/",
"companyId": "shareaholic"
},
"shareasale": {
@@ -20827,6 +20848,7 @@
"anametrix.net": "anametrix",
"ancestrycdn.com": "ancestry_cdn",
"ancoraplatform.com": "ancora",
"android.com": "android",
"anetwork.ir": "anetwork",
"aniview.com": "aniview.com",
"a-ads.com": "anonymousads",
@@ -21872,6 +21894,7 @@
"fastly-insights.com": "fastly_insights",
"fastly.net": "fastlylb.net",
"fastlylb.net": "fastlylb.net",
"fastly-edge.com": "fastlylb.net",
"fastly-masque.net": "fastlylb.net",
"fastpic.ru": "fastpic.ru",
"fmpub.net": "federated_media",
@@ -22394,6 +22417,7 @@
"adservice.google.pl": "google_marketing",
"adservice.google.ru": "google_marketing",
"adservice.google.vg": "google_marketing",
"adtrafficquality.google": "google_marketing",
"dai.google.com": "google_marketing",
"doubleclickbygoogle.com": "google_marketing",
"googlesyndication-cn.com": "google_marketing",
@@ -22747,6 +22771,22 @@
"jeeng.com": "jeeng",
"api.jeeng.com": "jeeng_widgets",
"phone-analytics.com": "jet_interactive",
"grazie.ai": "jetbrains",
"intellij.net": "jetbrains",
"jb.gg": "jetbrains",
"jetbrains.ai": "jetbrains",
"jetbrains.com": "jetbrains",
"jetbrains.com.cn": "jetbrains",
"jetbrains.dev": "jetbrains",
"jetbrains.net": "jetbrains",
"jetbrains.org": "jetbrains",
"jetbrains.ru": "jetbrains",
"jetbrains.space": "jetbrains",
"kotl.in": "jetbrains",
"kotlinconf.com": "jetbrains",
"kotlinlang.org": "jetbrains",
"myjetbrains.com": "jetbrains",
"talkingkotlin.com": "jetbrains",
"jetlore.com": "jetlore",
"pixel.wp.com": "jetpack",
"stats.wp.com": "jetpack",
@@ -23320,6 +23360,7 @@
"mrskincash.com": "mrskincash",
"a-msedge.net": "msedge",
"b-msedge.net": "msedge",
"dual-s-msedge.net": "msedge",
"e-msedge.net": "msedge",
"k-msedge.net": "msedge",
"l-msedge.net": "msedge",
@@ -23766,6 +23807,7 @@
"tr.prospecteye.com": "prospecteye",
"prosperent.com": "prosperent",
"prostor-lite.ru": "prostor",
"reports.proton.me": "proton_ag",
"providesupport.com": "provide_support",
"proximic.com": "proximic",
"proxistore.com": "proxistore.com",

52
go.mod
View File

@@ -1,24 +1,25 @@
module github.com/AdguardTeam/AdGuardHome
go 1.23.2
go 1.23.5
require (
github.com/AdguardTeam/dnsproxy v0.73.2
github.com/AdguardTeam/golibs v0.27.0
github.com/AdguardTeam/urlfilter v0.19.0
github.com/AdguardTeam/dnsproxy v0.73.5
github.com/AdguardTeam/golibs v0.31.0
github.com/AdguardTeam/urlfilter v0.20.0
github.com/NYTimes/gziphandler v1.1.1
github.com/ameshkov/dnscrypt/v2 v2.3.0
github.com/bluele/gcache v0.0.2
github.com/c2h5oh/datasize v0.0.0-20231215233829-aa82cc1e6500
github.com/digineo/go-ipset/v2 v2.2.1
github.com/dimfeld/httptreemux/v5 v5.5.0
github.com/fsnotify/fsnotify v1.7.0
github.com/go-ping/ping v1.1.0
github.com/fsnotify/fsnotify v1.8.0
// TODO(e.burkov): This package is deprecated; find a new one or use our
// own code for that. Perhaps, use gopacket.
github.com/go-ping/ping v1.2.0
github.com/google/go-cmp v0.6.0
github.com/google/gopacket v1.1.19
github.com/google/renameio/v2 v2.0.0
github.com/google/uuid v1.6.0
github.com/insomniacslk/dhcp v0.0.0-20240419123447-f1cffa2c0c49
github.com/insomniacslk/dhcp v0.0.0-20241203100832-a481575ed0ef
github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86
github.com/kardianos/service v1.2.2
github.com/mdlayher/ethernet v0.0.0-20220221185849-529eae5b6118
@@ -27,15 +28,15 @@ require (
// TODO(a.garipov): This package is deprecated; find a new one or use our
// own code for that. Perhaps, use gopacket.
github.com/mdlayher/raw v0.1.0
github.com/miekg/dns v1.1.61
github.com/quic-go/quic-go v0.47.0
github.com/stretchr/testify v1.9.0
github.com/miekg/dns v1.1.62
github.com/quic-go/quic-go v0.48.2
github.com/stretchr/testify v1.10.0
github.com/ti-mo/netfilter v0.5.2
go.etcd.io/bbolt v1.3.10
golang.org/x/crypto v0.26.0
golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa
golang.org/x/net v0.28.0
golang.org/x/sys v0.24.0
go.etcd.io/bbolt v1.3.11
golang.org/x/crypto v0.31.0
golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67
golang.org/x/net v0.33.0
golang.org/x/sys v0.28.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1
gopkg.in/yaml.v3 v3.0.1
howett.net/plist v1.0.1
@@ -48,22 +49,19 @@ require (
github.com/beefsack/go-rate v0.0.0-20220214233405-116f4ca011a0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
github.com/google/pprof v0.0.0-20240521024322-9665fa269a30 // indirect
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad // indirect
github.com/mdlayher/socket v0.5.1 // indirect
github.com/onsi/ginkgo/v2 v2.17.3 // indirect
github.com/onsi/ginkgo/v2 v2.22.1 // indirect
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
github.com/pierrec/lz4/v4 v4.1.21 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/quic-go/qpack v0.5.1 // indirect
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 // indirect
go.uber.org/mock v0.4.0 // indirect
golang.org/x/mod v0.20.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/text v0.17.0 // indirect
golang.org/x/tools v0.24.0 // indirect
gonum.org/v1/gonum v0.15.0 // indirect
go.uber.org/mock v0.5.0 // indirect
golang.org/x/mod v0.22.0 // indirect
golang.org/x/sync v0.10.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/tools v0.28.0 // indirect
gonum.org/v1/gonum v0.15.1 // indirect
)
// TODO(a.garipov): Remove once https://github.com/quic-go/quic-go/pull/4685 is merged.
replace github.com/quic-go/quic-go => github.com/ainar-g/quic-go v0.0.0-20240930125330-446bd86056fd

102
go.sum
View File

@@ -1,17 +1,15 @@
github.com/AdguardTeam/dnsproxy v0.73.2 h1:O6wRXzHsnWL5TkhYcuLWCShVFF0X5RFI6qUmq1ZFVsQ=
github.com/AdguardTeam/dnsproxy v0.73.2/go.mod h1:zD5WfTctbRvYYk8PS39h6/OT84NTu6QxKbAiBN5PUcI=
github.com/AdguardTeam/golibs v0.27.0 h1:YxCFK6HBGp/ZXp3bv5uei+oLH12UfIYB8u2rh1B6nnU=
github.com/AdguardTeam/golibs v0.27.0/go.mod h1:iWdjXPCwmK2g2FKIb/OwEPnovSXeMqRhI8FWLxF5oxE=
github.com/AdguardTeam/urlfilter v0.19.0 h1:q7eH13+yNETlpD/VD3u5rLQOripcUdEktqZFy+KiQLk=
github.com/AdguardTeam/urlfilter v0.19.0/go.mod h1:+N54ZvxqXYLnXuvpaUhK2exDQW+djZBRSb6F6j0rkBY=
github.com/AdguardTeam/dnsproxy v0.73.5 h1:3EiVaTfmuW6PcJGbqloR6ZdHACsrYkm9z0eH8ZQTZnQ=
github.com/AdguardTeam/dnsproxy v0.73.5/go.mod h1:Oqw+k7LyjDObfYzXYCkpgtirbzbUrmotr92jrb3N09I=
github.com/AdguardTeam/golibs v0.31.0 h1:Z0oPfLTLw6iZmpE58dePy2Bel0MaX+lnDwtFEE5EmIo=
github.com/AdguardTeam/golibs v0.31.0/go.mod h1:wIkZ9o2UnppeW6/YD7yJB71dYbMhiuC1Fh/I2ElW7GQ=
github.com/AdguardTeam/urlfilter v0.20.0 h1:X32qiuVCVd8WDYCEsbdZKfXMzwdVqrdulamtUi4rmzs=
github.com/AdguardTeam/urlfilter v0.20.0/go.mod h1:gjrywLTxfJh6JOkwi9SU+frhP7kVVEZ5exFGkR99qpk=
github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I=
github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY=
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA=
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 h1:52m0LGchQBBVqJRyYYufQuIbVqRawmubW3OFGqK1ekw=
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635/go.mod h1:lmLxL+FV291OopO93Bwf9fQLQeLyt33VJRUg5VJ30us=
github.com/ainar-g/quic-go v0.0.0-20240930125330-446bd86056fd h1:mw4LqrCiv3vcKuCxBRg7kA17xfHKM+9hZgFWmyhe/AY=
github.com/ainar-g/quic-go v0.0.0-20240930125330-446bd86056fd/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs=
github.com/ameshkov/dnscrypt/v2 v2.3.0 h1:pDXDF7eFa6Lw+04C0hoMh8kCAQM8NwUdFEllSP2zNLs=
github.com/ameshkov/dnscrypt/v2 v2.3.0/go.mod h1:N5hDwgx2cNb4Ay7AhvOSKst+eUiOZ/vbKRO9qMpQttE=
github.com/ameshkov/dnsstamps v1.0.3 h1:Srzik+J9mivH1alRACTbys2xOxs0lRH9qnTA7Y1OYVo=
@@ -27,16 +25,14 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/digineo/go-ipset/v2 v2.2.1 h1:k6skY+0fMqeUjjeWO/m5OuWPSZUAn7AucHMnQ1MX77g=
github.com/digineo/go-ipset/v2 v2.2.1/go.mod h1:wBsNzJlZlABHUITkesrggFnZQtgW5wkqw1uo8Qxe0VU=
github.com/dimfeld/httptreemux/v5 v5.5.0 h1:p8jkiMrCuZ0CmhwYLcbNbl7DDo21fozhKHQ2PccwOFQ=
github.com/dimfeld/httptreemux/v5 v5.5.0/go.mod h1:QeEylH57C0v3VO0tkKraVz9oD3Uu93CKPnTLbsidvSw=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-ping/ping v1.1.0 h1:3MCGhVX4fyEUuhsfwPrsEdQw6xspHkv5zHsiSoDFZYw=
github.com/go-ping/ping v1.1.0/go.mod h1:xIFjORFzTxqIV/tDVGO4eDy/bLuSyawEeojSm3GfRGk=
github.com/go-ping/ping v1.2.0 h1:vsJ8slZBZAXNCK4dPcI2PEE9eM9n9RbXbGouVQ/Y4yQ=
github.com/go-ping/ping v1.2.0/go.mod h1:xIFjORFzTxqIV/tDVGO4eDy/bLuSyawEeojSm3GfRGk=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
@@ -46,8 +42,8 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
github.com/google/pprof v0.0.0-20240521024322-9665fa269a30 h1:r6YdmbD41tGHeCWDyHF691LWtL7D1iSTyJaKejTWwVU=
github.com/google/pprof v0.0.0-20240521024322-9665fa269a30/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/ZoQgRgVIWFJljSWa/zetS2WTvg=
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/google/renameio/v2 v2.0.0 h1:UifI23ZTGY8Tt29JbYFiuyIU3eX+RNFtUwefq9qAhxg=
github.com/google/renameio/v2 v2.0.0/go.mod h1:BtmJXm5YlszgC+TD4HOEEUFgkJP3nLxehU6hfe7jRt4=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@@ -55,8 +51,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714 h1:/jC7qQFrv8CrSJVmaolDVOxTfS9kc36uB6H40kdbQq8=
github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis=
github.com/insomniacslk/dhcp v0.0.0-20240419123447-f1cffa2c0c49 h1:/OuvSMGT9+xnyZ+7MZQ1zdngaCCAdPoSw8B/uurZ7pg=
github.com/insomniacslk/dhcp v0.0.0-20240419123447-f1cffa2c0c49/go.mod h1:KclMyHxX06VrVr0DJmeFSUb1ankt7xTfoOA35pCkoic=
github.com/insomniacslk/dhcp v0.0.0-20241203100832-a481575ed0ef h1:NzQKDfd5ZOPnuZYf9MnRee8x2qecsVqzsnaLjEZiBko=
github.com/insomniacslk/dhcp v0.0.0-20241203100832-a481575ed0ef/go.mod h1:KclMyHxX06VrVr0DJmeFSUb1ankt7xTfoOA35pCkoic=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/josharian/native v1.0.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86 h1:elKwZS1OcdQ0WwEDBeqxKwb7WB62QX8bvZ/FJnVXIfk=
@@ -80,14 +76,14 @@ github.com/mdlayher/raw v0.1.0/go.mod h1:yXnxvs6c0XoF/aK52/H5PjsVHmWBCFfZUfoh/Y5
github.com/mdlayher/socket v0.2.1/go.mod h1:QLlNPkFR88mRUNQIzRBMfXxwKal8H7u1h3bL1CV+f0E=
github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos=
github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ=
github.com/miekg/dns v1.1.61 h1:nLxbwF3XxhwVSm8g9Dghm9MHPaUZuqhPiGL+675ZmEs=
github.com/miekg/dns v1.1.61/go.mod h1:mnAarhS3nWaW+NVP2wTkYVIZyHNJ098SJZUki3eykwQ=
github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ=
github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ=
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.17.3 h1:oJcvKpIb7/8uLpDDtnQuf18xVnwKp8DTD7DQ6gTd/MU=
github.com/onsi/ginkgo/v2 v2.17.3/go.mod h1:nP2DPOQoNsQmsVyv5rDA8JkXQoCs6goXIvr/PRJ1eCc=
github.com/onsi/gomega v1.33.0 h1:snPCflnZrpMsy94p4lXVEkHo12lmPnc3vY5XBbreexE=
github.com/onsi/gomega v1.33.0/go.mod h1:+925n5YtiFsLzzafLUHzVMBpvvRAzrydIBiSIxjX3wY=
github.com/onsi/ginkgo/v2 v2.22.1 h1:QW7tbJAUDyVDVOM5dFa7qaybo+CRfR7bemlQUN6Z8aM=
github.com/onsi/ginkgo/v2 v2.22.1/go.mod h1:S6aTpoRsSq2cZOd+pssHAlKW/Q/jZt6cPrPlnj4a1xM=
github.com/onsi/gomega v1.36.1 h1:bJDPBO7ibjxcbHMgSCoo4Yj18UWbKDlLwX1x9sybDcw=
github.com/onsi/gomega v1.36.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
@@ -101,6 +97,8 @@ github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
github.com/quic-go/quic-go v0.48.2 h1:wsKXZPeGWpMpCGSWqOcqpW2wZYic/8T3aqiOID0/KWE=
github.com/quic-go/quic-go v0.48.2/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs=
github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=
github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk=
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
@@ -109,8 +107,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/ti-mo/netfilter v0.2.0/go.mod h1:8GbBGsY/8fxtyIdfwy29JiluNcPK4K7wIT+x42ipqUU=
github.com/ti-mo/netfilter v0.5.2 h1:CTjOwFuNNeZ9QPdRXt1MZFLFUf84cKtiQutNauHWd40=
github.com/ti-mo/netfilter v0.5.2/go.mod h1:Btx3AtFiOVdHReTDmP9AE+hlkOcvIy403u7BXXbWZKo=
@@ -122,32 +120,32 @@ github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 h1:pyC9PaHYZFgEKFdlp3G8
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701/go.mod h1:P3a5rG4X7tI17Nn3aOIAYr5HbIMukwXG0urG0WuL8OA=
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
go.etcd.io/bbolt v1.3.10 h1:+BqfJTcCzTItrop8mq/lbzL8wSGtj94UO/3U31shqG0=
go.etcd.io/bbolt v1.3.10/go.mod h1:bK3UQLPJZly7IlNmV7uVHJDxfe5aK9Ll93e/74Y9oEQ=
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
go.etcd.io/bbolt v1.3.11 h1:yGEzV1wPz2yVCLsD8ZAiGHhHVlczyC9d1rP43/VCRJ0=
go.etcd.io/bbolt v1.3.11/go.mod h1:dksAq7YMXoljX0xu6VF5DMZGbhYYoLUalEiSySYAS4I=
go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa h1:ELnwvuAXPNtPk1TJRuGkI9fDTwym6AYBu0qzT8AcHdI=
golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ=
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 h1:1UoZQm6f0P/ZO0w1Ri+f+ifG/gXhegadRdwBIXEFWDo=
golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c=
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.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
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.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190322080309-f49334f85ddc/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -158,25 +156,25 @@ golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.4.1-0.20230131160137-e7d7f63158de/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8=
golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.15.0 h1:2lYxjRbTYyxkJxlhC+LvJIx3SsANPdRybu1tGj9/OrQ=
gonum.org/v1/gonum v0.15.0/go.mod h1:xzZVBJBtS+Mz4q0Yl2LJTk+OxOg4jiXZ7qBoM0uISGo=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gonum.org/v1/gonum v0.15.1 h1:FNy7N6OUZVUaWG9pTiD+jlhdQ3lMP+/LcTpJ6+a8sQ0=
gonum.org/v1/gonum v0.15.1/go.mod h1:eZTZuRFrzu5pcyjN5wJhcIhnUdNijYxX1T2IcrOGY0o=
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View File

@@ -14,12 +14,6 @@ import (
"github.com/AdguardTeam/golibs/logutil/slogutil"
)
// HTTP scheme constants.
const (
SchemeHTTP = "http"
SchemeHTTPS = "https"
)
// RegisterFunc is the function that sets the handler to handle the URL for the
// method.
//

View File

@@ -20,9 +20,10 @@ import (
"github.com/AdguardTeam/golibs/log"
)
// Default file and directory permissions.
// Default file, binary, and directory permissions.
const (
DefaultPermDir fs.FileMode = 0o700
DefaultPermExe fs.FileMode = 0o700
DefaultPermFile fs.FileMode = 0o600
)
@@ -145,16 +146,6 @@ func IsOpenWrt() (ok bool) {
return isOpenWrt()
}
// NotifyReconfigureSignal notifies c on receiving reconfigure signals.
func NotifyReconfigureSignal(c chan<- os.Signal) {
notifyReconfigureSignal(c)
}
// IsReconfigureSignal returns true if sig is a reconfigure signal.
func IsReconfigureSignal(sig os.Signal) (ok bool) {
return isReconfigureSignal(sig)
}
// SendShutdownSignal sends the shutdown signal to the channel.
func SendShutdownSignal(c chan<- os.Signal) {
sendShutdownSignal(c)

View File

@@ -1,22 +1,11 @@
//go:build darwin || freebsd || linux || openbsd
//go:build unix
package aghos
import (
"os"
"os/signal"
"golang.org/x/sys/unix"
)
func notifyReconfigureSignal(c chan<- os.Signal) {
signal.Notify(c, unix.SIGHUP)
}
func isReconfigureSignal(sig os.Signal) (ok bool) {
return sig == unix.SIGHUP
}
func sendShutdownSignal(_ chan<- os.Signal) {
// On Unix we are already notified by the system.
}

View File

@@ -4,12 +4,11 @@ package aghos
import (
"os"
"os/signal"
"golang.org/x/sys/windows"
)
func setRlimit(val uint64) (err error) {
func setRlimit(_ uint64) (err error) {
return Unsupported("setrlimit")
}
@@ -38,14 +37,6 @@ func isOpenWrt() (ok bool) {
return false
}
func notifyReconfigureSignal(c chan<- os.Signal) {
signal.Notify(c, windows.SIGHUP)
}
func isReconfigureSignal(sig os.Signal) (ok bool) {
return sig == windows.SIGHUP
}
func sendShutdownSignal(c chan<- os.Signal) {
c <- os.Interrupt
}

View File

@@ -62,7 +62,9 @@ func newPendingFile(filePath string, mode fs.FileMode) (f PendingFile, err error
return nil, fmt.Errorf("opening pending file: %w", err)
}
err = file.Chmod(mode)
// TODO(e.burkov): The [os.Chmod] implementation is useless on Windows,
// investigate if it can be removed.
err = os.Chmod(file.Name(), mode)
if err != nil {
return nil, fmt.Errorf("preparing pending file: %w", err)
}

View File

@@ -58,7 +58,7 @@ func (w *FSWatcher) Add(name string) (err error) {
// ServiceWithConfig is a fake [agh.ServiceWithConfig] implementation for tests.
type ServiceWithConfig[ConfigType any] struct {
OnStart func() (err error)
OnStart func(ctx context.Context) (err error)
OnShutdown func(ctx context.Context) (err error)
OnConfig func() (c ConfigType)
}
@@ -68,8 +68,8 @@ var _ agh.ServiceWithConfig[struct{}] = (*ServiceWithConfig[struct{}])(nil)
// Start implements the [agh.ServiceWithConfig] interface for
// *ServiceWithConfig.
func (s *ServiceWithConfig[_]) Start() (err error) {
return s.OnStart()
func (s *ServiceWithConfig[_]) Start(ctx context.Context) (err error) {
return s.OnStart(ctx)
}
// Shutdown implements the [agh.ServiceWithConfig] interface for
@@ -89,14 +89,14 @@ func (s *ServiceWithConfig[ConfigType]) Config() (c ConfigType) {
// AddressProcessor is a fake [client.AddressProcessor] implementation for
// tests.
type AddressProcessor struct {
OnProcess func(ip netip.Addr)
OnProcess func(ctx context.Context, ip netip.Addr)
OnClose func() (err error)
}
// Process implements the [client.AddressProcessor] interface for
// *AddressProcessor.
func (p *AddressProcessor) Process(ip netip.Addr) {
p.OnProcess(ip)
func (p *AddressProcessor) Process(ctx context.Context, ip netip.Addr) {
p.OnProcess(ctx, ip)
}
// Close implements the [client.AddressProcessor] interface for
@@ -107,13 +107,18 @@ func (p *AddressProcessor) Close() (err error) {
// AddressUpdater is a fake [client.AddressUpdater] implementation for tests.
type AddressUpdater struct {
OnUpdateAddress func(ip netip.Addr, host string, info *whois.Info)
OnUpdateAddress func(ctx context.Context, ip netip.Addr, host string, info *whois.Info)
}
// UpdateAddress implements the [client.AddressUpdater] interface for
// *AddressUpdater.
func (p *AddressUpdater) UpdateAddress(ip netip.Addr, host string, info *whois.Info) {
p.OnUpdateAddress(ip, host, info)
func (p *AddressUpdater) UpdateAddress(
ctx context.Context,
ip netip.Addr,
host string,
info *whois.Info,
) {
p.OnUpdateAddress(ctx, ip, host, info)
}
// Package dnsforward

View File

@@ -11,7 +11,6 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/rdns"
"github.com/AdguardTeam/AdGuardHome/internal/whois"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/netutil"
)
@@ -22,7 +21,7 @@ const ErrClosed errors.Error = "use of closed address processor"
// AddressProcessor is the interface for types that can process clients.
type AddressProcessor interface {
Process(ip netip.Addr)
Process(ctx context.Context, ip netip.Addr)
Close() (err error)
}
@@ -33,7 +32,7 @@ type EmptyAddrProc struct{}
var _ AddressProcessor = EmptyAddrProc{}
// Process implements the [AddressProcessor] interface for EmptyAddrProc.
func (EmptyAddrProc) Process(_ netip.Addr) {}
func (EmptyAddrProc) Process(_ context.Context, _ netip.Addr) {}
// Close implements the [AddressProcessor] interface for EmptyAddrProc.
func (EmptyAddrProc) Close() (_ error) { return nil }
@@ -90,12 +89,15 @@ type DefaultAddrProcConfig struct {
type AddressUpdater interface {
// UpdateAddress updates information about an IP address, setting host (if
// not empty) and WHOIS information (if not nil).
UpdateAddress(ip netip.Addr, host string, info *whois.Info)
UpdateAddress(ctx context.Context, ip netip.Addr, host string, info *whois.Info)
}
// DefaultAddrProc processes incoming client addresses with rDNS and WHOIS, if
// configured, and updates that information in a client storage.
type DefaultAddrProc struct {
// logger is used to log the operation of address processor.
logger *slog.Logger
// clientIPsMu serializes closure of clientIPs and access to isClosed.
clientIPsMu *sync.Mutex
@@ -142,6 +144,7 @@ const (
// not be nil.
func NewDefaultAddrProc(c *DefaultAddrProcConfig) (p *DefaultAddrProc) {
p = &DefaultAddrProc{
logger: c.BaseLogger.With(slogutil.KeyPrefix, "addrproc"),
clientIPsMu: &sync.Mutex{},
clientIPs: make(chan netip.Addr, defaultQueueSize),
rdns: &rdns.Empty{},
@@ -164,10 +167,13 @@ func NewDefaultAddrProc(c *DefaultAddrProcConfig) (p *DefaultAddrProc) {
p.whois = newWHOIS(c.BaseLogger.With(slogutil.KeyPrefix, "whois"), c.DialContext)
}
go p.process(c.CatchPanics)
// TODO(s.chzhen): Pass context.
ctx := context.TODO()
go p.process(ctx, c.CatchPanics)
for _, ip := range c.InitialAddresses {
p.Process(ip)
p.Process(ctx, ip)
}
return p
@@ -210,7 +216,7 @@ func newWHOIS(logger *slog.Logger, dialFunc aghnet.DialContextFunc) (w whois.Int
var _ AddressProcessor = (*DefaultAddrProc)(nil)
// Process implements the [AddressProcessor] interface for *DefaultAddrProc.
func (p *DefaultAddrProc) Process(ip netip.Addr) {
func (p *DefaultAddrProc) Process(ctx context.Context, ip netip.Addr) {
p.clientIPsMu.Lock()
defer p.clientIPsMu.Unlock()
@@ -222,38 +228,42 @@ func (p *DefaultAddrProc) Process(ip netip.Addr) {
case p.clientIPs <- ip:
// Go on.
default:
log.Debug("clients: ip channel is full; len: %d", len(p.clientIPs))
p.logger.DebugContext(ctx, "ip channel is full", "len", len(p.clientIPs))
}
}
// process processes the incoming client IP-address information. It is intended
// to be used as a goroutine. Once clientIPs is closed, process exits.
func (p *DefaultAddrProc) process(catchPanics bool) {
func (p *DefaultAddrProc) process(ctx context.Context, catchPanics bool) {
if catchPanics {
defer log.OnPanic("addrProcessor.process")
defer slogutil.RecoverAndLog(ctx, p.logger)
}
log.Info("clients: processing addresses")
ctx := context.TODO()
p.logger.InfoContext(ctx, "processing addresses")
for ip := range p.clientIPs {
host := p.processRDNS(ctx, ip)
info := p.processWHOIS(ctx, ip)
p.addrUpdater.UpdateAddress(ip, host, info)
p.addrUpdater.UpdateAddress(ctx, ip, host, info)
}
log.Info("clients: finished processing addresses")
p.logger.InfoContext(ctx, "finished processing addresses")
}
// processRDNS resolves the clients' IP addresses using reverse DNS. host is
// empty if there were errors or if the information hasn't changed.
func (p *DefaultAddrProc) processRDNS(ctx context.Context, ip netip.Addr) (host string) {
start := time.Now()
log.Debug("clients: processing %s with rdns", ip)
p.logger.DebugContext(ctx, "processing rdns", "ip", ip)
defer func() {
log.Debug("clients: finished processing %s with rdns in %s", ip, time.Since(start))
p.logger.DebugContext(
ctx,
"finished processing rdns",
"ip", ip,
"host", host,
"elapsed", time.Since(start),
)
}()
ok := p.shouldResolve(ip)
@@ -280,9 +290,15 @@ func (p *DefaultAddrProc) shouldResolve(ip netip.Addr) (ok bool) {
// hasn't changed.
func (p *DefaultAddrProc) processWHOIS(ctx context.Context, ip netip.Addr) (info *whois.Info) {
start := time.Now()
log.Debug("clients: processing %s with whois", ip)
p.logger.DebugContext(ctx, "processing whois", "ip", ip)
defer func() {
log.Debug("clients: finished processing %s with whois in %s", ip, time.Since(start))
p.logger.DebugContext(
ctx,
"finished processing whois",
"ip", ip,
"whois", info,
"elapsed", time.Since(start),
)
}()
// TODO(s.chzhen): Move the timeout logic from WHOIS configuration to the

View File

@@ -26,7 +26,8 @@ func TestEmptyAddrProc(t *testing.T) {
p := client.EmptyAddrProc{}
assert.NotPanics(t, func() {
p.Process(testIP)
ctx := testutil.ContextWithTimeout(t, testTimeout)
p.Process(ctx, testIP)
})
assert.NotPanics(t, func() {
@@ -120,7 +121,8 @@ func TestDefaultAddrProc_Process_rDNS(t *testing.T) {
})
testutil.CleanupAndRequireSuccess(t, p.Close)
p.Process(tc.ip)
ctx := testutil.ContextWithTimeout(t, testTimeout)
p.Process(ctx, tc.ip)
if !tc.wantUpd {
return
@@ -146,8 +148,8 @@ func newOnUpdateAddress(
ips chan<- netip.Addr,
hosts chan<- string,
infos chan<- *whois.Info,
) (f func(ip netip.Addr, host string, info *whois.Info)) {
return func(ip netip.Addr, host string, info *whois.Info) {
) (f func(ctx context.Context, ip netip.Addr, host string, info *whois.Info)) {
return func(ctx context.Context, ip netip.Addr, host string, info *whois.Info) {
if !want && (host != "" || info != nil) {
panic(fmt.Errorf("got unexpected update for %v with %q and %v", ip, host, info))
}
@@ -230,7 +232,8 @@ func TestDefaultAddrProc_Process_WHOIS(t *testing.T) {
})
testutil.CleanupAndRequireSuccess(t, p.Close)
p.Process(testIP)
ctx := testutil.ContextWithTimeout(t, testTimeout)
p.Process(ctx, testIP)
if !tc.wantUpd {
return
@@ -251,7 +254,9 @@ func TestDefaultAddrProc_Process_WHOIS(t *testing.T) {
func TestDefaultAddrProc_Close(t *testing.T) {
t.Parallel()
p := client.NewDefaultAddrProc(&client.DefaultAddrProcConfig{})
p := client.NewDefaultAddrProc(&client.DefaultAddrProcConfig{
BaseLogger: slogutil.NewDiscardLogger(),
})
err := p.Close()
assert.NoError(t, err)

View File

@@ -2,6 +2,7 @@ package client
import (
"fmt"
"maps"
"net"
"net/netip"
"slices"
@@ -9,7 +10,6 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/aghalg"
"github.com/AdguardTeam/golibs/errors"
"golang.org/x/exp/maps"
)
// macKey contains MAC as byte array of 6, 8, or 20 bytes.
@@ -330,12 +330,14 @@ func (ci *index) size() (n int) {
// rangeByName is like [Index.Range] but sorts the persistent clients by name
// before iterating ensuring a predictable order.
func (ci *index) rangeByName(f func(c *Persistent) (cont bool)) {
cs := maps.Values(ci.uidToClient)
slices.SortFunc(cs, func(a, b *Persistent) (n int) {
return strings.Compare(a.Name, b.Name)
})
clients := slices.SortedStableFunc(
maps.Values(ci.uidToClient),
func(a, b *Persistent) (res int) {
return strings.Compare(a.Name, b.Name)
},
)
for _, c := range cs {
for _, c := range clients {
if !f(c) {
break
}

View File

@@ -1,20 +1,20 @@
package client
import (
"context"
"encoding"
"fmt"
"log/slog"
"net"
"net/netip"
"slices"
"strings"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/AdGuardHome/internal/filtering/safesearch"
"github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/dnsproxy/upstream"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/netutil"
"github.com/google/uuid"
)
@@ -136,7 +136,7 @@ type Persistent struct {
// validate returns an error if persistent client information contains errors.
// allTags must be sorted.
func (c *Persistent) validate(allTags []string) (err error) {
func (c *Persistent) validate(ctx context.Context, l *slog.Logger, allTags []string) (err error) {
switch {
case c.Name == "":
return errors.Error("empty name")
@@ -153,7 +153,7 @@ func (c *Persistent) validate(allTags []string) (err error) {
err = conf.Close()
if err != nil {
log.Error("client: closing upstream config: %s", err)
l.ErrorContext(ctx, "client: closing upstream config", slogutil.KeyError, err)
}
for _, t := range c.Tags {
@@ -323,20 +323,3 @@ func (c *Persistent) CloseUpstreams() (err error) {
return nil
}
// SetSafeSearch initializes and sets the safe search filter for this client.
func (c *Persistent) SetSafeSearch(
conf filtering.SafeSearchConfig,
cacheSize uint,
cacheTTL time.Duration,
) (err error) {
ss, err := safesearch.NewDefault(conf, fmt.Sprintf("client %q", c.Name), cacheSize, cacheTTL)
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return err
}
c.SafeSearch = ss
return nil
}

View File

@@ -3,6 +3,7 @@ package client
import (
"context"
"fmt"
"log/slog"
"net"
"net/netip"
"slices"
@@ -14,7 +15,7 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/whois"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/hostsfile"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/logutil/slogutil"
)
// allowedTags is the list of available client tags.
@@ -83,6 +84,10 @@ type HostsContainer interface {
// StorageConfig is the client storage configuration structure.
type StorageConfig struct {
// Logger is used for logging the operation of the client storage. It must
// not be nil.
Logger *slog.Logger
// DHCP is used to match IPs against MACs of persistent clients and update
// [SourceDHCP] runtime client information. It must not be nil.
DHCP DHCP
@@ -108,6 +113,10 @@ type StorageConfig struct {
// Storage contains information about persistent and runtime clients.
type Storage struct {
// logger is used for logging the operation of the client storage. It must
// not be nil.
logger *slog.Logger
// mu protects indexes of persistent and runtime clients.
mu *sync.Mutex
@@ -145,12 +154,12 @@ type Storage struct {
}
// NewStorage returns initialized client storage. conf must not be nil.
func NewStorage(conf *StorageConfig) (s *Storage, err error) {
func NewStorage(ctx context.Context, conf *StorageConfig) (s *Storage, err error) {
tags := slices.Clone(allowedTags)
slices.Sort(tags)
s = &Storage{
allowedTags: tags,
logger: conf.Logger,
mu: &sync.Mutex{},
index: newIndex(),
runtimeIndex: newRuntimeIndex(),
@@ -158,18 +167,19 @@ func NewStorage(conf *StorageConfig) (s *Storage, err error) {
etcHosts: conf.EtcHosts,
arpDB: conf.ARPDB,
done: make(chan struct{}),
allowedTags: tags,
arpClientsUpdatePeriod: conf.ARPClientsUpdatePeriod,
runtimeSourceDHCP: conf.RuntimeSourceDHCP,
}
for i, p := range conf.InitialClients {
err = s.Add(p)
err = s.Add(ctx, p)
if err != nil {
return nil, fmt.Errorf("adding client %q at index %d: %w", p.Name, i, err)
}
}
s.ReloadARP()
s.ReloadARP(ctx)
return s, nil
}
@@ -177,9 +187,9 @@ func NewStorage(conf *StorageConfig) (s *Storage, err error) {
// Start starts the goroutines for updating the runtime client information.
//
// TODO(s.chzhen): Pass context.
func (s *Storage) Start(_ context.Context) (err error) {
go s.periodicARPUpdate()
go s.handleHostsUpdates()
func (s *Storage) Start(ctx context.Context) (err error) {
go s.periodicARPUpdate(ctx)
go s.handleHostsUpdates(ctx)
return nil
}
@@ -195,15 +205,15 @@ func (s *Storage) Shutdown(_ context.Context) (err error) {
// periodicARPUpdate periodically reloads runtime clients from ARP. It is
// intended to be used as a goroutine.
func (s *Storage) periodicARPUpdate() {
defer log.OnPanic("storage")
func (s *Storage) periodicARPUpdate(ctx context.Context) {
defer slogutil.RecoverAndLog(ctx, s.logger)
t := time.NewTicker(s.arpClientsUpdatePeriod)
for {
select {
case <-t.C:
s.ReloadARP()
s.ReloadARP(ctx)
case <-s.done:
return
}
@@ -211,28 +221,28 @@ func (s *Storage) periodicARPUpdate() {
}
// ReloadARP reloads runtime clients from ARP, if configured.
func (s *Storage) ReloadARP() {
func (s *Storage) ReloadARP(ctx context.Context) {
if s.arpDB != nil {
s.addFromSystemARP()
s.addFromSystemARP(ctx)
}
}
// addFromSystemARP adds the IP-hostname pairings from the output of the arp -a
// command.
func (s *Storage) addFromSystemARP() {
func (s *Storage) addFromSystemARP(ctx context.Context) {
s.mu.Lock()
defer s.mu.Unlock()
if err := s.arpDB.Refresh(); err != nil {
s.arpDB = arpdb.Empty{}
log.Error("refreshing arp container: %s", err)
s.logger.ErrorContext(ctx, "refreshing arp container", slogutil.KeyError, err)
return
}
ns := s.arpDB.Neighbors()
if len(ns) == 0 {
log.Debug("refreshing arp container: the update is empty")
s.logger.DebugContext(ctx, "refreshing arp container: the update is empty")
return
}
@@ -246,17 +256,22 @@ func (s *Storage) addFromSystemARP() {
removed := s.runtimeIndex.removeEmpty()
log.Debug("storage: added %d, removed %d client aliases from arp neighborhood", len(ns), removed)
s.logger.DebugContext(
ctx,
"updating client aliases from arp neighborhood",
"added", len(ns),
"removed", removed,
)
}
// handleHostsUpdates receives the updates from the hosts container and adds
// them to the clients storage. It is intended to be used as a goroutine.
func (s *Storage) handleHostsUpdates() {
func (s *Storage) handleHostsUpdates(ctx context.Context) {
if s.etcHosts == nil {
return
}
defer log.OnPanic("storage")
defer slogutil.RecoverAndLog(ctx, s.logger)
for {
select {
@@ -265,7 +280,7 @@ func (s *Storage) handleHostsUpdates() {
return
}
s.addFromHostsFile(upd)
s.addFromHostsFile(ctx, upd)
case <-s.done:
return
}
@@ -274,7 +289,7 @@ func (s *Storage) handleHostsUpdates() {
// addFromHostsFile fills the client-hostname pairing index from the system's
// hosts files.
func (s *Storage) addFromHostsFile(hosts *hostsfile.DefaultStorage) {
func (s *Storage) addFromHostsFile(ctx context.Context, hosts *hostsfile.DefaultStorage) {
s.mu.Lock()
defer s.mu.Unlock()
@@ -294,14 +309,19 @@ func (s *Storage) addFromHostsFile(hosts *hostsfile.DefaultStorage) {
})
removed := s.runtimeIndex.removeEmpty()
log.Debug("storage: added %d, removed %d client aliases from system hosts file", added, removed)
s.logger.DebugContext(
ctx,
"updating client aliases from system hosts file",
"added", added,
"removed", removed,
)
}
// type check
var _ AddressUpdater = (*Storage)(nil)
// UpdateAddress implements the [AddressUpdater] interface for *Storage
func (s *Storage) UpdateAddress(ip netip.Addr, host string, info *whois.Info) {
func (s *Storage) UpdateAddress(ctx context.Context, ip netip.Addr, host string, info *whois.Info) {
// Common fast path optimization.
if host == "" && info == nil {
return
@@ -315,12 +335,12 @@ func (s *Storage) UpdateAddress(ip netip.Addr, host string, info *whois.Info) {
}
if info != nil {
s.setWHOISInfo(ip, info)
s.setWHOISInfo(ctx, ip, info)
}
}
// UpdateDHCP updates [SourceDHCP] runtime client information.
func (s *Storage) UpdateDHCP() {
func (s *Storage) UpdateDHCP(ctx context.Context) {
if s.dhcp == nil || !s.runtimeSourceDHCP {
return
}
@@ -338,14 +358,23 @@ func (s *Storage) UpdateDHCP() {
}
removed := s.runtimeIndex.removeEmpty()
log.Debug("storage: added %d, removed %d client aliases from dhcp", added, removed)
s.logger.DebugContext(
ctx,
"updating client aliases from dhcp",
"added", added,
"removed", removed,
)
}
// setWHOISInfo sets the WHOIS information for a runtime client.
func (s *Storage) setWHOISInfo(ip netip.Addr, wi *whois.Info) {
func (s *Storage) setWHOISInfo(ctx context.Context, ip netip.Addr, wi *whois.Info) {
_, ok := s.index.findByIP(ip)
if ok {
log.Debug("storage: client for %s is already created, ignore whois info", ip)
s.logger.DebugContext(
ctx,
"persistent client is already created, ignore whois info",
"ip", ip,
)
return
}
@@ -358,14 +387,14 @@ func (s *Storage) setWHOISInfo(ip netip.Addr, wi *whois.Info) {
rc.setWHOIS(wi)
log.Debug("storage: set whois info for runtime client with ip %s: %+v", ip, wi)
s.logger.DebugContext(ctx, "set whois info for runtime client", "ip", ip, "whois", wi)
}
// Add stores persistent client information or returns an error.
func (s *Storage) Add(p *Persistent) (err error) {
func (s *Storage) Add(ctx context.Context, p *Persistent) (err error) {
defer func() { err = errors.Annotate(err, "adding client: %w") }()
err = p.validate(s.allowedTags)
err = p.validate(ctx, s.logger, s.allowedTags)
if err != nil {
// Don't wrap the error since there is already an annotation deferred.
return err
@@ -388,7 +417,13 @@ func (s *Storage) Add(p *Persistent) (err error) {
s.index.add(p)
log.Debug("client storage: added %q: IDs: %q [%d]", p.Name, p.IDs(), s.index.size())
s.logger.DebugContext(
ctx,
"client added",
"name", p.Name,
"ids", p.IDs(),
"clients_count", s.index.size(),
)
return nil
}
@@ -470,7 +505,7 @@ func (s *Storage) FindByMAC(mac net.HardwareAddr) (p *Persistent, ok bool) {
// RemoveByName removes persistent client information. ok is false if no such
// client exists by that name.
func (s *Storage) RemoveByName(name string) (ok bool) {
func (s *Storage) RemoveByName(ctx context.Context, name string) (ok bool) {
s.mu.Lock()
defer s.mu.Unlock()
@@ -480,7 +515,7 @@ func (s *Storage) RemoveByName(name string) (ok bool) {
}
if err := p.CloseUpstreams(); err != nil {
log.Error("client storage: removing client %q: %s", p.Name, err)
s.logger.ErrorContext(ctx, "removing client", "name", p.Name, slogutil.KeyError, err)
}
s.index.remove(p)
@@ -490,10 +525,10 @@ func (s *Storage) RemoveByName(name string) (ok bool) {
// Update finds the stored persistent client by its name and updates its
// information from p.
func (s *Storage) Update(name string, p *Persistent) (err error) {
func (s *Storage) Update(ctx context.Context, name string, p *Persistent) (err error) {
defer func() { err = errors.Annotate(err, "updating client: %w") }()
err = p.validate(s.allowedTags)
err = p.validate(ctx, s.logger, s.allowedTags)
if err != nil {
// Don't wrap the error since there is already an annotation deferred.
return err

View File

@@ -15,11 +15,25 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/dhcpsvc"
"github.com/AdguardTeam/AdGuardHome/internal/whois"
"github.com/AdguardTeam/golibs/hostsfile"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/testutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// newTestStorage is a helper function that returns initialized storage.
func newTestStorage(tb testing.TB) (s *client.Storage) {
tb.Helper()
ctx := testutil.ContextWithTimeout(tb, testTimeout)
s, err := client.NewStorage(ctx, &client.StorageConfig{
Logger: slogutil.NewDiscardLogger(),
})
require.NoError(tb, err)
return s
}
// testHostsContainer is a mock implementation of the [client.HostsContainer]
// interface.
type testHostsContainer struct {
@@ -110,7 +124,9 @@ func TestStorage_Add_hostsfile(t *testing.T) {
onUpd: func() (updates <-chan *hostsfile.DefaultStorage) { return hostCh },
}
storage, err := client.NewStorage(&client.StorageConfig{
ctx := testutil.ContextWithTimeout(t, testTimeout)
storage, err := client.NewStorage(ctx, &client.StorageConfig{
Logger: slogutil.NewDiscardLogger(),
DHCP: client.EmptyDHCP{},
EtcHosts: h,
ARPClientsUpdatePeriod: testTimeout / 10,
@@ -198,7 +214,9 @@ func TestStorage_Add_arp(t *testing.T) {
},
}
storage, err := client.NewStorage(&client.StorageConfig{
ctx := testutil.ContextWithTimeout(t, testTimeout)
storage, err := client.NewStorage(ctx, &client.StorageConfig{
Logger: slogutil.NewDiscardLogger(),
DHCP: client.EmptyDHCP{},
ARPDB: a,
ARPClientsUpdatePeriod: testTimeout / 10,
@@ -273,8 +291,10 @@ func TestStorage_Add_whois(t *testing.T) {
cliName3 = "client_three"
)
storage, err := client.NewStorage(&client.StorageConfig{
DHCP: client.EmptyDHCP{},
ctx := testutil.ContextWithTimeout(t, testTimeout)
storage, err := client.NewStorage(ctx, &client.StorageConfig{
Logger: slogutil.NewDiscardLogger(),
DHCP: client.EmptyDHCP{},
})
require.NoError(t, err)
@@ -284,7 +304,7 @@ func TestStorage_Add_whois(t *testing.T) {
}
t.Run("new_client", func(t *testing.T) {
storage.UpdateAddress(cliIP1, "", whois)
storage.UpdateAddress(ctx, cliIP1, "", whois)
cli1 := storage.ClientRuntime(cliIP1)
require.NotNil(t, cli1)
@@ -292,8 +312,8 @@ func TestStorage_Add_whois(t *testing.T) {
})
t.Run("existing_runtime_client", func(t *testing.T) {
storage.UpdateAddress(cliIP2, cliName2, nil)
storage.UpdateAddress(cliIP2, "", whois)
storage.UpdateAddress(ctx, cliIP2, cliName2, nil)
storage.UpdateAddress(ctx, cliIP2, "", whois)
cli2 := storage.ClientRuntime(cliIP2)
require.NotNil(t, cli2)
@@ -304,14 +324,14 @@ func TestStorage_Add_whois(t *testing.T) {
})
t.Run("can't_set_persistent_client", func(t *testing.T) {
err = storage.Add(&client.Persistent{
err = storage.Add(ctx, &client.Persistent{
Name: cliName3,
UID: client.MustNewUID(),
IPs: []netip.Addr{cliIP3},
})
require.NoError(t, err)
storage.UpdateAddress(cliIP3, "", whois)
storage.UpdateAddress(ctx, cliIP3, "", whois)
rc := storage.ClientRuntime(cliIP3)
require.Nil(t, rc)
})
@@ -364,7 +384,9 @@ func TestClientsDHCP(t *testing.T) {
},
}
storage, err := client.NewStorage(&client.StorageConfig{
ctx := testutil.ContextWithTimeout(t, testTimeout)
storage, err := client.NewStorage(ctx, &client.StorageConfig{
Logger: slogutil.NewDiscardLogger(),
DHCP: d,
RuntimeSourceDHCP: true,
})
@@ -378,7 +400,7 @@ func TestClientsDHCP(t *testing.T) {
})
t.Run("find_persistent", func(t *testing.T) {
err = storage.Add(&client.Persistent{
err = storage.Add(ctx, &client.Persistent{
Name: prsCliName,
UID: client.MustNewUID(),
MACs: []net.HardwareAddr{prsCliMAC},
@@ -393,7 +415,7 @@ func TestClientsDHCP(t *testing.T) {
t.Run("leases", func(t *testing.T) {
delete(ipToHost, cliIP1)
storage.UpdateDHCP()
storage.UpdateDHCP(ctx)
cli1 := storage.ClientRuntime(cliIP1)
require.Nil(t, cli1)
@@ -421,16 +443,19 @@ func TestClientsDHCP(t *testing.T) {
}
func TestClientsAddExisting(t *testing.T) {
ctx := testutil.ContextWithTimeout(t, testTimeout)
t.Run("simple", func(t *testing.T) {
storage, err := client.NewStorage(&client.StorageConfig{
DHCP: client.EmptyDHCP{},
storage, err := client.NewStorage(ctx, &client.StorageConfig{
Logger: slogutil.NewDiscardLogger(),
DHCP: client.EmptyDHCP{},
})
require.NoError(t, err)
ip := netip.MustParseAddr("1.1.1.1")
// Add a client.
err = storage.Add(&client.Persistent{
err = storage.Add(ctx, &client.Persistent{
Name: "client1",
UID: client.MustNewUID(),
IPs: []netip.Addr{ip, netip.MustParseAddr("1:2:3::4")},
@@ -440,7 +465,7 @@ func TestClientsAddExisting(t *testing.T) {
require.NoError(t, err)
// Now add an auto-client with the same IP.
storage.UpdateAddress(ip, "test", nil)
storage.UpdateAddress(ctx, ip, "test", nil)
rc := storage.ClientRuntime(ip)
assert.True(t, compareRuntimeInfo(rc, client.SourceRDNS, "test"))
})
@@ -468,8 +493,9 @@ func TestClientsAddExisting(t *testing.T) {
dhcpServer, err := dhcpd.Create(config)
require.NoError(t, err)
storage, err := client.NewStorage(&client.StorageConfig{
DHCP: dhcpServer,
storage, err := client.NewStorage(ctx, &client.StorageConfig{
Logger: slogutil.NewDiscardLogger(),
DHCP: dhcpServer,
})
require.NoError(t, err)
@@ -484,7 +510,7 @@ func TestClientsAddExisting(t *testing.T) {
require.NoError(t, err)
// Add a new client with the same IP as for a client with MAC.
err = storage.Add(&client.Persistent{
err = storage.Add(ctx, &client.Persistent{
Name: "client2",
UID: client.MustNewUID(),
IPs: []netip.Addr{ip},
@@ -492,7 +518,7 @@ func TestClientsAddExisting(t *testing.T) {
require.NoError(t, err)
// Add a new client with the IP from the first client's IP range.
err = storage.Add(&client.Persistent{
err = storage.Add(ctx, &client.Persistent{
Name: "client3",
UID: client.MustNewUID(),
IPs: []netip.Addr{netip.MustParseAddr("2.2.2.2")},
@@ -506,14 +532,16 @@ func TestClientsAddExisting(t *testing.T) {
func newStorage(tb testing.TB, m []*client.Persistent) (s *client.Storage) {
tb.Helper()
s, err := client.NewStorage(&client.StorageConfig{
DHCP: client.EmptyDHCP{},
ctx := testutil.ContextWithTimeout(tb, testTimeout)
s, err := client.NewStorage(ctx, &client.StorageConfig{
Logger: slogutil.NewDiscardLogger(),
DHCP: client.EmptyDHCP{},
})
require.NoError(tb, err)
for _, c := range m {
c.UID = client.MustNewUID()
require.NoError(tb, s.Add(c))
require.NoError(tb, s.Add(ctx, c))
}
require.Equal(tb, len(m), s.Size())
@@ -555,9 +583,8 @@ func TestStorage_Add(t *testing.T) {
UID: existingClientUID,
}
s, err := client.NewStorage(&client.StorageConfig{})
require.NoError(t, err)
ctx := testutil.ContextWithTimeout(t, testTimeout)
s := newTestStorage(t)
tags := s.AllowedTags()
require.NotZero(t, len(tags))
require.True(t, slices.IsSorted(tags))
@@ -568,7 +595,7 @@ func TestStorage_Add(t *testing.T) {
_, ok = slices.BinarySearch(tags, notAllowedTag)
require.False(t, ok)
err = s.Add(existingClient)
err := s.Add(ctx, existingClient)
require.NoError(t, err)
testCases := []struct {
@@ -669,7 +696,7 @@ func TestStorage_Add(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
err = s.Add(tc.cli)
err = s.Add(ctx, tc.cli)
testutil.AssertErrorMsg(t, tc.wantErrMsg, err)
})
@@ -687,10 +714,9 @@ func TestStorage_RemoveByName(t *testing.T) {
UID: client.MustNewUID(),
}
s, err := client.NewStorage(&client.StorageConfig{})
require.NoError(t, err)
err = s.Add(existingClient)
ctx := testutil.ContextWithTimeout(t, testTimeout)
s := newTestStorage(t)
err := s.Add(ctx, existingClient)
require.NoError(t, err)
testCases := []struct {
@@ -709,19 +735,17 @@ func TestStorage_RemoveByName(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
tc.want(t, s.RemoveByName(tc.cliName))
tc.want(t, s.RemoveByName(ctx, tc.cliName))
})
}
t.Run("duplicate_remove", func(t *testing.T) {
s, err = client.NewStorage(&client.StorageConfig{})
s = newTestStorage(t)
err = s.Add(ctx, existingClient)
require.NoError(t, err)
err = s.Add(existingClient)
require.NoError(t, err)
assert.True(t, s.RemoveByName(existingName))
assert.False(t, s.RemoveByName(existingName))
assert.True(t, s.RemoveByName(ctx, existingName))
assert.False(t, s.RemoveByName(ctx, existingName))
})
}
@@ -1080,6 +1104,7 @@ func TestStorage_Update(t *testing.T) {
`uses the same ClientID "obstructing_client_id"`,
}}
ctx := testutil.ContextWithTimeout(t, testTimeout)
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
s := newStorage(
@@ -1090,7 +1115,7 @@ func TestStorage_Update(t *testing.T) {
},
)
err := s.Update(clientName, tc.cli)
err := s.Update(ctx, clientName, tc.cli)
testutil.AssertErrorMsg(t, tc.wantErrMsg, err)
})
}

View File

@@ -521,7 +521,7 @@ func TestUpgradeSchema11to12(t *testing.T) {
name string
}{{
ivl: 1,
want: timeutil.Duration{Duration: timeutil.Day},
want: timeutil.Duration(timeutil.Day),
wantErr: "",
name: "success",
}, {
@@ -604,7 +604,7 @@ func TestUpgradeSchema11to12(t *testing.T) {
ivlVal, ok = ivl.(timeutil.Duration)
require.True(t, ok)
assert.Equal(t, 90*24*time.Hour, ivlVal.Duration)
assert.Equal(t, 90*24*time.Hour, time.Duration(ivlVal))
})
}
@@ -1055,12 +1055,12 @@ func TestUpgradeSchema19to20(t *testing.T) {
name string
}{{
ivl: 1,
want: timeutil.Duration{Duration: timeutil.Day},
want: timeutil.Duration(timeutil.Day),
wantErr: "",
name: "success",
}, {
ivl: 0,
want: timeutil.Duration{Duration: timeutil.Day},
want: timeutil.Duration(timeutil.Day),
wantErr: "",
name: "success",
}, {
@@ -1143,7 +1143,7 @@ func TestUpgradeSchema19to20(t *testing.T) {
ivlVal, ok = ivl.(timeutil.Duration)
require.True(t, ok)
assert.Equal(t, 24*time.Hour, ivlVal.Duration)
assert.Equal(t, 24*time.Hour, time.Duration(ivlVal))
})
}

View File

@@ -37,7 +37,7 @@ func migrateTo12(diskConf yobj) (err error) {
qlogIvl = 90
}
dns[field] = timeutil.Duration{Duration: time.Duration(qlogIvl) * timeutil.Day}
dns[field] = timeutil.Duration(time.Duration(qlogIvl) * timeutil.Day)
return nil
}

View File

@@ -38,7 +38,7 @@ func migrateTo20(diskConf yobj) (err error) {
ivl = 1
}
stats[field] = timeutil.Duration{Duration: time.Duration(ivl) * timeutil.Day}
stats[field] = timeutil.Duration(time.Duration(ivl) * timeutil.Day)
return nil
}

View File

@@ -48,7 +48,7 @@ func migrateTo23(diskConf yobj) (err error) {
diskConf["http"] = yobj{
"address": netip.AddrPortFrom(bindHostAddr, uint16(bindPort)).String(),
"session_ttl": timeutil.Duration{Duration: time.Duration(sessionTTL) * time.Hour}.String(),
"session_ttl": timeutil.Duration(time.Duration(sessionTTL) * time.Hour).String(),
}
delete(diskConf, "bind_host")

View File

@@ -1,61 +1,50 @@
# Testing DHCP Server
# Testing DHCP Server
Contents:
* [Test setup with Virtual Box](#vbox)
* [Quick test with DHCPTest](#dhcptest)
## <a href="#vbox" id="vbox" name="vbox">Test setup with Virtual Box</a>
- [Test setup with Virtual Box](#vbox)
- [Quick test with DHCPTest](#dhcptest)
### Prerequisites
## <a href="#vbox" id="vbox" name="vbox">Test setup with Virtual Box</a>
### Prerequisites
To set up a test environment for DHCP server you will need:
* Linux AG Home host machine (Virtual).
* 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 Virtual Box
### 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:
```sh
$ 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:
You can also set up Host-Only network using Virtual Box menu in *File → Host Network Manager.*
```
File -> Host Network Manager...
```
2. Create your virtual machine and set up its network in *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 addresses 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
1. Edit server configuration file `AdGuardHome.yaml`, for example:
```yaml
dhcp:
enabled: true
interface_name: vboxnet0
local_domain_name: lan
dhcpv4:
```yaml
dhcp:
enabled: true
interface_name: vboxnet0
local_domain_name: lan
dhcpv4:
gateway_ip: 192.168.56.1
subnet_mask: 255.255.255.0
range_start: 192.168.56.2
@@ -63,34 +52,33 @@ To set up a test environment for DHCP server you will need:
lease_duration: 86400
icmp_timeout_msec: 1000
options: []
dhcpv6:
dhcpv6:
range_start: 2001::1
lease_duration: 86400
ra_slaac_only: false
ra_allow_slaac: false
```
```
2. Start the server
2. Start the server:
```sh
./AdGuardHome -v
```
```sh
./AdGuardHome -v
```
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
```
```none
[info] dhcpv4: listening
```
## <a href="#dhcptest" id="dhcptest" name="dhcptest">Quick test with DHCPTest utility</a>
## <a href="#dhcptest" id="dhcptest" name="dhcptest">Quick test with DHCPTest utility</a>
### Prerequisites
### Prerequisites
* [DHCP test utility][dhcptest-gh].
- [DHCP test utility][dhcptest-gh].
### Quick test
### Quick test
The DHCP server could be tested for DISCOVER-OFFER packets with in
interactive mode.
The DHCP server could be tested for DISCOVER-OFFER packets with in interactive mode.
[dhcptest-gh]: https://github.com/CyberShadow/dhcptest

View File

@@ -84,7 +84,7 @@ func parseDHCPOptionDur(s string) (val dhcpv4.OptionValue, err error) {
return nil, fmt.Errorf("decoding dur: %w", err)
}
return dhcpv4.Duration(v.Duration), nil
return dhcpv4.Duration(v), nil
}
// parseDHCPOptionUint parses a DHCP option as an unsigned integer. bitSize is

View File

@@ -144,8 +144,8 @@ func TestParseOpt(t *testing.T) {
in: "24 dur 3y",
wantCode: nil,
wantVal: nil,
wantErrMsg: "invalid option string \"24 dur 3y\": decoding dur: " +
"unmarshaling duration: time: unknown unit \"y\" in duration \"3y\"",
wantErrMsg: `invalid option string "24 dur 3y": decoding dur: time: ` +
`unknown unit "y" in duration "3y"`,
}, {
name: "u8_error",
in: "23 u8 256",

View File

@@ -18,9 +18,11 @@ import (
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/timeutil"
"github.com/go-ping/ping"
"github.com/insomniacslk/dhcp/dhcpv4"
"github.com/insomniacslk/dhcp/dhcpv4/server4"
//lint:ignore SA1019 See the TODO in go.mod.
"github.com/go-ping/ping"
)
// v4Server is a DHCPv4 server.

View File

@@ -3,11 +3,12 @@ package dhcpsvc
import (
"fmt"
"log/slog"
"maps"
"os"
"slices"
"time"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/mapsutil"
"github.com/AdguardTeam/golibs/netutil"
)
@@ -78,14 +79,13 @@ func (conf *Config) Validate() (err error) {
return errors.Join(errs...)
}
mapsutil.SortedRange(conf.Interfaces, func(iface string, ic *InterfaceConfig) (ok bool) {
for _, iface := range slices.Sorted(maps.Keys(conf.Interfaces)) {
ic := conf.Interfaces[iface]
err = ic.validate()
if err != nil {
errs = append(errs, fmt.Errorf("interface %q: %w", iface, err))
}
return true
})
}
return errors.Join(errs...)
}

View File

@@ -82,7 +82,7 @@ type Empty struct{}
var _ agh.ServiceWithConfig[*Config] = Empty{}
// Start implements the [Service] interface for Empty.
func (Empty) Start() (err error) { return nil }
func (Empty) Start(_ context.Context) (err error) { return nil }
// Shutdown implements the [Service] interface for Empty.
func (Empty) Shutdown(_ context.Context) (err error) { return nil }

View File

@@ -4,14 +4,15 @@ import (
"context"
"fmt"
"log/slog"
"maps"
"net"
"net/netip"
"slices"
"sync"
"sync/atomic"
"time"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/mapsutil"
)
// DHCPServer is a DHCP server for both IPv4 and IPv6 address families.
@@ -107,7 +108,8 @@ func newInterfaces(
v6 = make(dhcpInterfacesV6, 0, len(ifaces))
var errs []error
mapsutil.SortedRange(ifaces, func(name string, iface *InterfaceConfig) (cont bool) {
for _, name := range slices.Sorted(maps.Keys(ifaces)) {
iface := ifaces[name]
var i4 *dhcpInterfaceV4
i4, err = newDHCPInterfaceV4(ctx, l, name, iface.IPv4)
if err != nil {
@@ -120,9 +122,8 @@ func newInterfaces(
if i6 != nil {
v6 = append(v6, i6)
}
}
return true
})
if err = errors.Join(errs...); err != nil {
return nil, nil, err
}

View File

@@ -81,7 +81,7 @@ func (s *Server) clientIDFromDNSContext(pctx *proxy.DNSContext) (clientID string
cliSrvName, err := clientServerName(pctx, proto)
if err != nil {
return "", err
return "", fmt.Errorf("getting client server-name: %w", err)
}
clientID, err = clientIDFromClientServerName(

View File

@@ -3,6 +3,7 @@ package dnsforward
import (
"crypto/tls"
"fmt"
"net/http"
"path"
"strings"
@@ -118,17 +119,13 @@ func clientServerName(pctx *proxy.DNSContext, proto proxy.Proto) (srvName string
switch proto {
case proxy.ProtoHTTPS:
r := pctx.HTTPRequest
if connState := r.TLS; connState != nil {
srvName = connState.ServerName
} else if r.Host != "" {
var host string
host, err = netutil.SplitHost(r.Host)
if err != nil {
return "", fmt.Errorf("parsing host: %w", err)
}
var fromHost bool
srvName, fromHost, err = clientServerNameFromHTTP(pctx.HTTPRequest)
if err != nil {
return "", fmt.Errorf("from http: %w", err)
}
srvName = host
if fromHost {
from = "host header"
}
case proxy.ProtoQUIC:
@@ -153,3 +150,23 @@ func clientServerName(pctx *proxy.DNSContext, proto proxy.Proto) (srvName string
return srvName, nil
}
// clientServerNameFromHTTP returns the TLS server name or the value of the host
// header depending on the protocol. fromHost is true if srvName comes from the
// "Host" HTTP header.
func clientServerNameFromHTTP(r *http.Request) (srvName string, fromHost bool, err error) {
if connState := r.TLS; connState != nil {
return connState.ServerName, false, nil
}
if r.Host == "" {
return "", false, nil
}
srvName, err = netutil.SplitHost(r.Host)
if err != nil {
return "", false, fmt.Errorf("parsing host: %w", err)
}
return srvName, true, nil
}

View File

@@ -348,7 +348,7 @@ func (s *Server) newProxyConfig() (conf *proxy.Config, err error) {
conf.EDNSAddr = net.IP(srvConf.EDNSClientSubnet.CustomIP.AsSlice())
}
err = setProxyUpstreamMode(conf, srvConf.UpstreamMode, srvConf.FastestTimeout.Duration)
err = setProxyUpstreamMode(conf, srvConf.UpstreamMode, time.Duration(srvConf.FastestTimeout))
if err != nil {
return nil, fmt.Errorf("upstream mode: %w", err)
}

View File

@@ -740,7 +740,7 @@ func (s *Server) prepareInternalProxy() (err error) {
MessageConstructor: s,
}
err = setProxyUpstreamMode(conf, srvConf.UpstreamMode, srvConf.FastestTimeout.Duration)
err = setProxyUpstreamMode(conf, srvConf.UpstreamMode, time.Duration(srvConf.FastestTimeout))
if err != nil {
return fmt.Errorf("invalid upstream mode: %w", err)
}
@@ -818,6 +818,8 @@ func (s *Server) proxy() (p *proxy.Proxy) {
}
// Reconfigure applies the new configuration to the DNS server.
//
// TODO(a.garipov): This whole piece of API is weird and needs to be remade.
func (s *Server) Reconfigure(conf *ServerConfig) error {
s.serverLock.Lock()
defer s.serverLock.Unlock()
@@ -831,14 +833,15 @@ func (s *Server) Reconfigure(conf *ServerConfig) error {
// We wait for some time and hope that this fd will be closed.
time.Sleep(100 * time.Millisecond)
// TODO(a.garipov): This whole piece of API is weird and needs to be remade.
if s.addrProc != nil {
err := s.addrProc.Close()
if err != nil {
log.Error("dnsforward: closing address processor: %s", err)
}
}
if conf == nil {
conf = &s.conf
} else {
closeErr := s.addrProc.Close()
if closeErr != nil {
log.Error("dnsforward: closing address processor: %s", closeErr)
}
}
// TODO(e.burkov): It seems an error here brings the server down, which is

View File

@@ -500,6 +500,10 @@ func TestServerRace(t *testing.T) {
}
func TestSafeSearch(t *testing.T) {
const (
googleSafeSearch = "forcesafesearch.google.com."
)
safeSearchConf := filtering.SafeSearchConfig{
Enabled: true,
Google: true,
@@ -513,12 +517,14 @@ func TestSafeSearch(t *testing.T) {
SafeSearchCacheSize: 1000,
CacheTime: 30,
}
safeSearch, err := safesearch.NewDefault(
safeSearchConf,
"",
filterConf.SafeSearchCacheSize,
time.Minute*time.Duration(filterConf.CacheTime),
)
ctx := testutil.ContextWithTimeout(t, testTimeout)
safeSearch, err := safesearch.NewDefault(ctx, &safesearch.DefaultConfig{
Logger: slogutil.NewDiscardLogger(),
ServicesConfig: safeSearchConf,
CacheSize: filterConf.SafeSearchCacheSize,
CacheTTL: time.Minute * time.Duration(filterConf.CacheTime),
})
require.NoError(t, err)
filterConf.SafeSearch = safeSearch
@@ -534,10 +540,17 @@ func TestSafeSearch(t *testing.T) {
ServePlainDNS: true,
}
s := createTestServer(t, filterConf, forwardConf)
startDeferStop(t, s)
ups := aghtest.NewUpstreamMock(func(req *dns.Msg) (resp *dns.Msg, err error) {
pt := testutil.PanicT{}
assert.Equal(pt, googleSafeSearch, req.Question[0].Name)
return aghtest.MatchedResponse(req, dns.TypeA, googleSafeSearch, "1.2.3.4"), nil
})
s.conf.UpstreamConfig.Upstreams = []upstream.Upstream{ups}
startDeferStop(t, s)
addr := s.dnsProxy.Addr(proxy.ProtoUDP).String()
client := &dns.Client{}
yandexIP := netip.AddrFrom4([4]byte{213, 180, 193, 56})
@@ -584,12 +597,8 @@ func TestSafeSearch(t *testing.T) {
req := createTestMessage(tc.host)
var reply *dns.Msg
require.Eventually(t, func() (ok bool) {
reply, _, err = client.Exchange(req, addr)
return err == nil
}, testTimeout*10, testTimeout)
require.NoErrorf(t, err, "couldn't talk to server %s: %s", addr, err)
reply, err = dns.Exchange(req, addr)
require.NoError(t, err)
if tc.wantCNAME != "" {
require.Len(t, reply.Answer, 2)

View File

@@ -2,6 +2,7 @@ package dnsforward
import (
"cmp"
"context"
"encoding/binary"
"net"
"net/netip"
@@ -203,7 +204,8 @@ func (s *Server) processClientIP(addr netip.Addr) {
s.serverLock.RLock()
defer s.serverLock.RUnlock()
s.addrProc.Process(addr)
// TODO(s.chzhen): Pass context.
s.addrProc.Process(context.TODO(), addr)
}
// processDDRQuery responds to Discovery of Designated Resolvers (DDR) SVCB

View File

@@ -2,6 +2,7 @@ package dnsforward
import (
"cmp"
"context"
"net"
"net/netip"
"testing"
@@ -90,7 +91,7 @@ func TestServer_ProcessInitial(t *testing.T) {
var gotAddr netip.Addr
s.addrProc = &aghtest.AddressProcessor{
OnProcess: func(ip netip.Addr) { gotAddr = ip },
OnProcess: func(ctx context.Context, ip netip.Addr) { gotAddr = ip },
OnClose: func() (err error) { panic("not implemented") },
}

View File

@@ -1,70 +0,0 @@
# AdGuard Home's DNS filtering go library
Example use:
```bash
[ -z "$GOPATH" ] && export GOPATH=$HOME/go
go get -d github.com/AdguardTeam/AdGuardHome/filtering
```
Create file filter.go
```filter.go
package main
import (
"github.com/AdguardTeam/AdGuardHome/filtering"
"log"
)
func main() {
filter := filtering.New()
filter.AddRule("||dou*ck.net^")
host := "www.doubleclick.net"
res, err := filter.CheckHost(host)
if err != nil {
// temporary failure
log.Fatalf("Failed to check host %q: %s", host, err)
}
if res.IsFiltered {
log.Printf("Host %s is filtered, reason - %q, matched rule: %q", host, res.Reason, res.Rule)
} else {
log.Printf("Host %s is not filtered, reason - %q", host, res.Reason)
}
}
```
And then run it:
```bash
go run filter.go
```
You will get:
```
2000/01/01 00:00:00 Host www.doubleclick.net is filtered, reason - 'FilteredBlackList', matched rule: '||dou*ck.net^'
```
You can also enable checking against AdGuard's SafeBrowsing:
```go
package main
import (
"github.com/AdguardTeam/AdGuardHome/filtering"
"log"
)
func main() {
filter := filtering.New()
filter.EnableSafeBrowsing()
host := "wmconvirus.narod.ru" // hostname for testing safebrowsing
res, err := filter.CheckHost(host)
if err != nil {
// temporary failure
log.Fatalf("Failed to check host %q: %s", host, err)
}
if res.IsFiltered {
log.Printf("Host %s is filtered, reason - %q, matched rule: %q", host, res.Reason, res.Rule)
} else {
log.Printf("Host %s is not filtered, reason - %q", host, res.Reason)
}
}
```

View File

@@ -9,7 +9,7 @@ import (
"testing"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
"github.com/AdguardTeam/golibs/netutil/urlutil"
"github.com/AdguardTeam/golibs/testutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -33,7 +33,7 @@ func serveHTTPLocally(t *testing.T, h http.Handler) (urlStr string) {
require.IsType(t, (*net.TCPAddr)(nil), addr)
return (&url.URL{
Scheme: aghhttp.SchemeHTTP,
Scheme: urlutil.SchemeHTTP,
Host: addr.String(),
}).String()
}

View File

@@ -16,6 +16,7 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/filtering/rulelist"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/netutil/urlutil"
"github.com/miekg/dns"
)
@@ -40,19 +41,14 @@ func (d *DNSFilter) validateFilterURL(urlStr string) (err error) {
u, err := url.ParseRequestURI(urlStr)
if err != nil {
// Don't wrap the error since it's informative enough as is.
// Don't wrap the error, because it's informative enough as is.
return err
}
if s := u.Scheme; s != aghhttp.SchemeHTTP && s != aghhttp.SchemeHTTPS {
return &url.Error{
Op: "Check scheme",
URL: urlStr,
Err: fmt.Errorf("only %v allowed", []string{
aghhttp.SchemeHTTP,
aghhttp.SchemeHTTPS,
}),
}
err = urlutil.ValidateHTTPURL(u)
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return err
}
return nil

View File

@@ -3,11 +3,12 @@ package rulelist
import (
"context"
"fmt"
"log/slog"
"net/http"
"sync"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/urlfilter"
"github.com/AdguardTeam/urlfilter/filterlist"
"github.com/c2h5oh/datasize"
@@ -18,6 +19,9 @@ import (
//
// TODO(a.garipov): Merge with [TextEngine] in some way?
type Engine struct {
// logger is used to log the operation of the engine and its refreshes.
logger *slog.Logger
// mu protects engine and storage.
//
// TODO(a.garipov): See if anything else should be protected.
@@ -29,8 +33,7 @@ type Engine struct {
// storage is the filtering-rule storage. It is saved here to close it.
storage *filterlist.RuleStorage
// name is the human-readable name of the engine, like "allowed", "blocked",
// or "custom".
// name is the human-readable name of the engine.
name string
// filters is the data about rule filters in this engine.
@@ -40,12 +43,15 @@ type Engine struct {
// EngineConfig is the configuration for rule-list filtering engines created by
// combining refreshable filters.
type EngineConfig struct {
// Name is the human-readable name of this engine, like "allowed",
// "blocked", or "custom".
// Logger is used to log the operation of the engine. It must not be nil.
Logger *slog.Logger
// name is the human-readable name of the engine; see [EngineNameAllow] and
// similar constants.
Name string
// Filters is the data about rule lists in this engine. There must be no
// other references to the elements of this slice.
// other references to the items of this slice. Each item must not be nil.
Filters []*Filter
}
@@ -53,6 +59,7 @@ type EngineConfig struct {
// refreshed, so a refresh should be performed before use.
func NewEngine(c *EngineConfig) (e *Engine) {
return &Engine{
logger: c.Logger,
mu: &sync.RWMutex{},
name: c.Name,
filters: c.Filters,
@@ -85,7 +92,7 @@ func (e *Engine) FilterRequest(
}
// currentEngine returns the current filtering engine.
func (e *Engine) currentEngine() (enging *urlfilter.DNSEngine) {
func (e *Engine) currentEngine() (engine *urlfilter.DNSEngine) {
e.mu.RLock()
defer e.mu.RUnlock()
@@ -96,7 +103,7 @@ func (e *Engine) currentEngine() (enging *urlfilter.DNSEngine) {
// parseBuf, cli, cacheDir, and maxSize are used for updates of rule-list
// filters; see [Filter.Refresh].
//
// TODO(a.garipov): Unexport and test in an internal test or through enigne
// TODO(a.garipov): Unexport and test in an internal test or through engine
// tests.
func (e *Engine) Refresh(
ctx context.Context,
@@ -115,20 +122,20 @@ func (e *Engine) Refresh(
}
if len(filtersToRefresh) == 0 {
log.Info("filtering: updating engine %q: no rule-list filters", e.name)
e.logger.InfoContext(ctx, "updating: no rule-list filters")
return nil
}
engRefr := &engineRefresh{
httpCli: cli,
cacheDir: cacheDir,
engineName: e.name,
parseBuf: parseBuf,
maxSize: maxSize,
logger: e.logger,
httpCli: cli,
cacheDir: cacheDir,
parseBuf: parseBuf,
maxSize: maxSize,
}
ruleLists, errs := engRefr.process(ctx, e.filters)
ruleLists, errs := engRefr.process(ctx, filtersToRefresh)
if isOneTimeoutError(errs) {
// Don't wrap the error since it's informative enough as is.
return err
@@ -141,14 +148,14 @@ func (e *Engine) Refresh(
return errors.Join(errs...)
}
e.resetStorage(storage)
e.resetStorage(ctx, storage)
return errors.Join(errs...)
}
// resetStorage sets e.storage and e.engine and closes the previous storage.
// Errors from closing the previous storage are logged.
func (e *Engine) resetStorage(storage *filterlist.RuleStorage) {
func (e *Engine) resetStorage(ctx context.Context, storage *filterlist.RuleStorage) {
e.mu.Lock()
defer e.mu.Unlock()
@@ -161,7 +168,7 @@ func (e *Engine) resetStorage(storage *filterlist.RuleStorage) {
err := prevStorage.Close()
if err != nil {
log.Error("filtering: engine %q: closing old storage: %s", e.name, err)
e.logger.WarnContext(ctx, "closing old storage", slogutil.KeyError, err)
}
}
@@ -179,11 +186,11 @@ func isOneTimeoutError(errs []error) (ok bool) {
// engineRefresh represents a single ongoing engine refresh.
type engineRefresh struct {
httpCli *http.Client
cacheDir string
engineName string
parseBuf []byte
maxSize datasize.ByteSize
logger *slog.Logger
httpCli *http.Client
cacheDir string
parseBuf []byte
maxSize datasize.ByteSize
}
// process runs updates of all given rule-list filters. All errors are logged
@@ -216,12 +223,12 @@ func (r *engineRefresh) process(
errs = append(errs, err)
// Also log immediately, since the update can take a lot of time.
log.Error(
"filtering: updating engine %q: rule list %s from url %q: %s\n",
r.engineName,
f.uid,
f.url,
err,
r.logger.ErrorContext(
ctx,
"updating rule list",
"uid", f.uid,
"url", f.url,
slogutil.KeyError, err,
)
}
@@ -237,17 +244,17 @@ func (r *engineRefresh) processFilter(ctx context.Context, f *Filter) (err error
}
if prevChecksum == parseRes.Checksum {
log.Info("filtering: engine %q: filter %q: no change", r.engineName, f.uid)
r.logger.InfoContext(ctx, "no change in filter", "uid", f.uid)
return nil
}
log.Info(
"filtering: updated engine %q: filter %q: %d bytes, %d rules",
r.engineName,
f.uid,
parseRes.BytesWritten,
parseRes.RulesCount,
r.logger.InfoContext(
ctx,
"filter updated",
"uid", f.uid,
"bytes", parseRes.BytesWritten,
"rules", parseRes.RulesCount,
)
return nil

View File

@@ -5,6 +5,7 @@ import (
"testing"
"github.com/AdguardTeam/AdGuardHome/internal/filtering/rulelist"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/testutil"
"github.com/AdguardTeam/urlfilter"
"github.com/miekg/dns"
@@ -13,6 +14,8 @@ import (
)
func TestEngine_Refresh(t *testing.T) {
t.Parallel()
cacheDir := t.TempDir()
fileURL, srvURL := newFilterLocations(t, cacheDir, testRuleTextBlocked, testRuleTextBlocked2)
@@ -21,6 +24,7 @@ func TestEngine_Refresh(t *testing.T) {
httpFlt := newFilter(t, srvURL, "HTTP Filter")
eng := rulelist.NewEngine(&rulelist.EngineConfig{
Logger: slogutil.NewDiscardLogger(),
Name: "Engine",
Filters: []*rulelist.Filter{fileFlt, httpFlt},
})

View File

@@ -105,7 +105,7 @@ func NewFilter(c *FilterConfig) (f *Filter, err error) {
// buffer used to parse information from the data. cli and maxSize are only
// used when f is a URL-based list.
//
// TODO(a.garipov): Unexport and test in an internal test or through enigne
// TODO(a.garipov): Unexport and test in an internal test or through engine
// tests.
//
// TODO(a.garipov): Consider not returning parseRes.

View File

@@ -8,14 +8,16 @@ import (
"testing"
"github.com/AdguardTeam/AdGuardHome/internal/filtering/rulelist"
"github.com/AdguardTeam/golibs/netutil/urlutil"
"github.com/AdguardTeam/golibs/testutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestFilter_Refresh(t *testing.T) {
t.Parallel()
cacheDir := t.TempDir()
uid := rulelist.MustNewUID()
const fltData = testRuleTextTitle + testRuleTextBlocked
fileURL, srvURL := newFilterLocations(t, cacheDir, fltData, fltData)
@@ -37,7 +39,7 @@ func TestFilter_Refresh(t *testing.T) {
}, {
name: "file",
url: &url.URL{
Scheme: "file",
Scheme: urlutil.SchemeFile,
Path: fileURL.Path,
},
wantNewErrMsg: "",
@@ -49,6 +51,9 @@ func TestFilter_Refresh(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
uid := rulelist.MustNewUID()
f, err := rulelist.NewFilter(&rulelist.FilterConfig{
URL: tc.url,
Name: tc.name,

View File

@@ -71,3 +71,10 @@ var _ fmt.Stringer = UID{}
func (id UID) String() (s string) {
return uuid.UUID(id).String()
}
// Common engine names.
const (
EngineNameAllow = "allow"
EngineNameBlock = "block"
EngineNameCustom = "custom"
)

View File

@@ -6,20 +6,16 @@ import (
"net/http/httptest"
"net/url"
"os"
"path/filepath"
"sync/atomic"
"testing"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/filtering/rulelist"
"github.com/AdguardTeam/golibs/netutil/urlutil"
"github.com/AdguardTeam/golibs/testutil"
"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
@@ -31,6 +27,7 @@ const testTitle = "Test Title"
// Common rule texts for tests.
const (
testRuleTextAllowed = "||allowed.example^\n"
testRuleTextBadTab = "||bad-tab-and-comment.example^\t# A comment.\n"
testRuleTextBlocked = "||blocked.example^\n"
testRuleTextBlocked2 = "||blocked-2.example^\n"
@@ -79,8 +76,16 @@ func newFilterLocations(
fileData string,
httpData string,
) (fileURL, srvURL *url.URL) {
filePath := filepath.Join(cacheDir, "initial.txt")
err := os.WriteFile(filePath, []byte(fileData), 0o644)
t.Helper()
f, err := os.CreateTemp(cacheDir, "")
require.NoError(t, err)
err = f.Close()
require.NoError(t, err)
filePath := f.Name()
err = os.WriteFile(filePath, []byte(fileData), 0o644)
require.NoError(t, err)
testutil.CleanupAndRequireSuccess(t, func() (err error) {
@@ -88,7 +93,7 @@ func newFilterLocations(
})
fileURL = &url.URL{
Scheme: "file",
Scheme: urlutil.SchemeFile,
Path: filePath,
}

View File

@@ -0,0 +1,112 @@
package rulelist
import (
"context"
"fmt"
"log/slog"
"net/http"
"sync"
"github.com/AdguardTeam/golibs/errors"
"github.com/c2h5oh/datasize"
)
// Storage contains the main filtering engines, including the allowlist, the
// blocklist, and the user's custom filtering rules.
type Storage struct {
// refreshMu makes sure that only one update takes place at a time.
refreshMu *sync.Mutex
allow *Engine
block *Engine
custom *TextEngine
httpCli *http.Client
cacheDir string
parseBuf []byte
maxSize datasize.ByteSize
}
// StorageConfig is the configuration for the filtering-engine storage.
type StorageConfig struct {
// Logger is used to log the operation of the storage. It must not be nil.
Logger *slog.Logger
// HTTPClient is the HTTP client used to perform updates of rule lists.
// It must not be nil.
HTTPClient *http.Client
// CacheDir is the path to the directory used to cache rule-list files.
// It must be set.
CacheDir string
// AllowFilters are the filtering-rule lists used to exclude domain names
// from the filtering. Each item must not be nil.
AllowFilters []*Filter
// BlockFilters are the filtering-rule lists used to block domain names.
// Each item must not be nil.
BlockFilters []*Filter
// CustomRules contains custom rules of the user. They have priority over
// both allow- and blacklist rules.
CustomRules []string
// MaxRuleListTextSize is the maximum size of a rule-list file. It must be
// greater than zero.
MaxRuleListTextSize datasize.ByteSize
}
// NewStorage creates a new filtering-engine storage. The engines are not
// refreshed, so a refresh should be performed before use.
func NewStorage(c *StorageConfig) (s *Storage, err error) {
custom, err := NewTextEngine(&TextEngineConfig{
Name: EngineNameCustom,
Rules: c.CustomRules,
ID: URLFilterIDCustom,
})
if err != nil {
return nil, fmt.Errorf("creating custom engine: %w", err)
}
return &Storage{
refreshMu: &sync.Mutex{},
allow: NewEngine(&EngineConfig{
Logger: c.Logger.With("engine", EngineNameAllow),
Name: EngineNameAllow,
Filters: c.AllowFilters,
}),
block: NewEngine(&EngineConfig{
Logger: c.Logger.With("engine", EngineNameBlock),
Name: EngineNameBlock,
Filters: c.BlockFilters,
}),
custom: custom,
httpCli: c.HTTPClient,
cacheDir: c.CacheDir,
parseBuf: make([]byte, DefaultRuleBufSize),
maxSize: c.MaxRuleListTextSize,
}, nil
}
// Close closes the underlying rule-list engines.
func (s *Storage) Close() (err error) {
// Don't wrap the errors since they are informative enough as is.
return errors.Join(
s.allow.Close(),
s.block.Close(),
)
}
// Refresh updates all engines in s.
//
// TODO(a.garipov): Refresh allow and block separately?
func (s *Storage) Refresh(ctx context.Context) (err error) {
s.refreshMu.Lock()
defer s.refreshMu.Unlock()
// Don't wrap the errors since they are informative enough as is.
return errors.Join(
s.allow.Refresh(ctx, s.parseBuf, s.httpCli, s.cacheDir, s.maxSize),
s.block.Refresh(ctx, s.parseBuf, s.httpCli, s.cacheDir, s.maxSize),
)
}

View File

@@ -0,0 +1,49 @@
package rulelist_test
import (
"net/http"
"testing"
"github.com/AdguardTeam/AdGuardHome/internal/filtering/rulelist"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/testutil"
"github.com/c2h5oh/datasize"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestStorage_Refresh(t *testing.T) {
t.Parallel()
cacheDir := t.TempDir()
allowedFileURL, _ := newFilterLocations(t, cacheDir, testRuleTextAllowed, "")
allowedFlt := newFilter(t, allowedFileURL, "Allowed 1")
blockedFileURL, _ := newFilterLocations(t, cacheDir, testRuleTextBlocked, "")
blockedFlt := newFilter(t, blockedFileURL, "Blocked 1")
strg, err := rulelist.NewStorage(&rulelist.StorageConfig{
Logger: slogutil.NewDiscardLogger(),
HTTPClient: &http.Client{
Timeout: testTimeout,
},
CacheDir: cacheDir,
AllowFilters: []*rulelist.Filter{
allowedFlt,
},
BlockFilters: []*rulelist.Filter{
blockedFlt,
},
CustomRules: []string{
testRuleTextBlocked2,
},
MaxRuleListTextSize: 1 * datasize.KB,
})
require.NoError(t, err)
testutil.CleanupAndRequireSuccess(t, strg.Close)
ctx := testutil.ContextWithTimeout(t, testTimeout)
err = strg.Refresh(ctx)
assert.NoError(t, err)
}

View File

@@ -20,15 +20,15 @@ type TextEngine struct {
// storage is the filtering-rule storage. It is saved here to close it.
storage *filterlist.RuleStorage
// name is the human-readable name of the engine, like "custom".
// name is the human-readable name of the engine.
name string
}
// TextEngineConfig is the configuration for a rule-list filtering engine
// created from a filtering rule text.
type TextEngineConfig struct {
// Name is the human-readable name of this engine, like "allowed",
// "blocked", or "custom".
// name is the human-readable name of the engine; see [EngineNameAllow] and
// similar constants.
Name string
// Rules is the text of the filtering rules for this engine.

View File

@@ -12,6 +12,8 @@ import (
)
func TestNewTextEngine(t *testing.T) {
t.Parallel()
eng, err := rulelist.NewTextEngine(&rulelist.TextEngineConfig{
Name: "RulesEngine",
Rules: []string{

View File

@@ -1,15 +1,17 @@
package filtering
import "context"
// SafeSearch interface describes a service for search engines hosts rewrites.
type SafeSearch interface {
// CheckHost checks host with safe search filter. CheckHost must be safe
// for concurrent use. qtype must be either [dns.TypeA] or [dns.TypeAAAA].
CheckHost(host string, qtype uint16) (res Result, err error)
CheckHost(ctx context.Context, host string, qtype uint16) (res Result, err error)
// Update updates the configuration of the safe search filter. Update must
// be safe for concurrent use. An implementation of Update may ignore some
// fields, but it must document which.
Update(conf SafeSearchConfig) (err error)
Update(ctx context.Context, conf SafeSearchConfig) (err error)
}
// SafeSearchConfig is a struct with safe search related settings.
@@ -40,10 +42,13 @@ func (d *DNSFilter) checkSafeSearch(
return Result{}, nil
}
// TODO(s.chzhen): Pass context.
ctx := context.TODO()
clientSafeSearch := setts.ClientSafeSearch
if clientSafeSearch != nil {
return clientSafeSearch.CheckHost(host, qtype)
return clientSafeSearch.CheckHost(ctx, host, qtype)
}
return d.safeSearch.CheckHost(host, qtype)
return d.safeSearch.CheckHost(ctx, host, qtype)
}

View File

@@ -3,9 +3,11 @@ package safesearch
import (
"bytes"
"context"
"encoding/binary"
"encoding/gob"
"fmt"
"log/slog"
"net/netip"
"strings"
"sync"
@@ -14,13 +16,20 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/AdGuardHome/internal/filtering/rulelist"
"github.com/AdguardTeam/golibs/cache"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/urlfilter"
"github.com/AdguardTeam/urlfilter/filterlist"
"github.com/AdguardTeam/urlfilter/rules"
"github.com/c2h5oh/datasize"
"github.com/miekg/dns"
)
// Attribute keys and values for logging.
const (
LogPrefix = "safesearch"
LogKeyClient = "client"
)
// Service is a enum with service names used as search providers.
type Service string
@@ -57,9 +66,32 @@ func isServiceProtected(s filtering.SafeSearchConfig, service Service) (ok bool)
}
}
// DefaultConfig is the configuration structure for [Default].
type DefaultConfig struct {
// Logger is used for logging the operation of the safe search filter.
Logger *slog.Logger
// ClientName is the name of the persistent client associated with the safe
// search filter, if there is one.
ClientName string
// CacheSize is the size of the filter results cache.
CacheSize uint
// CacheTTL is the Time to Live duration for cached items.
CacheTTL time.Duration
// ServicesConfig contains safe search settings for services. It must not
// be nil.
ServicesConfig filtering.SafeSearchConfig
}
// Default is the default safe search filter that uses filtering rules with the
// dnsrewrite modifier.
type Default struct {
// logger is used for logging the operation of the safe search filter.
logger *slog.Logger
// mu protects engine.
mu *sync.RWMutex
@@ -67,33 +99,28 @@ type Default struct {
// engine may be nil, which means that this safe search filter is disabled.
engine *urlfilter.DNSEngine
cache cache.Cache
logPrefix string
cacheTTL time.Duration
// cache stores safe search filtering results.
cache cache.Cache
// cacheTTL is the Time to Live duration for cached items.
cacheTTL time.Duration
}
// NewDefault returns an initialized default safe search filter. name is used
// for logging.
func NewDefault(
conf filtering.SafeSearchConfig,
name string,
cacheSize uint,
cacheTTL time.Duration,
) (ss *Default, err error) {
// NewDefault returns an initialized default safe search filter. ctx is used
// to log the initial refresh.
func NewDefault(ctx context.Context, conf *DefaultConfig) (ss *Default, err error) {
ss = &Default{
mu: &sync.RWMutex{},
logger: conf.Logger,
mu: &sync.RWMutex{},
cache: cache.New(cache.Config{
EnableLRU: true,
MaxSize: cacheSize,
MaxSize: conf.CacheSize,
}),
// Use %s, because the client safe-search names already contain double
// quotes.
logPrefix: fmt.Sprintf("safesearch %s: ", name),
cacheTTL: cacheTTL,
cacheTTL: conf.CacheTTL,
}
err = ss.resetEngine(rulelist.URLFilterIDSafeSearch, conf)
// TODO(s.chzhen): Move to [Default.InitialRefresh].
err = ss.resetEngine(ctx, rulelist.URLFilterIDSafeSearch, conf.ServicesConfig)
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return nil, err
@@ -102,29 +129,15 @@ func NewDefault(
return ss, nil
}
// log is a helper for logging that includes the name of the safe search
// filter. level must be one of [log.DEBUG], [log.INFO], and [log.ERROR].
func (ss *Default) log(level log.Level, msg string, args ...any) {
switch level {
case log.DEBUG:
log.Debug(ss.logPrefix+msg, args...)
case log.INFO:
log.Info(ss.logPrefix+msg, args...)
case log.ERROR:
log.Error(ss.logPrefix+msg, args...)
default:
panic(fmt.Errorf("safesearch: unsupported logging level %d", level))
}
}
// resetEngine creates new engine for provided safe search configuration and
// sets it in ss.
func (ss *Default) resetEngine(
ctx context.Context,
listID int,
conf filtering.SafeSearchConfig,
) (err error) {
if !conf.Enabled {
ss.log(log.INFO, "disabled")
ss.logger.DebugContext(ctx, "disabled")
return nil
}
@@ -149,7 +162,7 @@ func (ss *Default) resetEngine(
ss.engine = urlfilter.NewDNSEngine(rs)
ss.log(log.INFO, "reset %d rules", ss.engine.RulesCount)
ss.logger.InfoContext(ctx, "reset rules", "count", ss.engine.RulesCount)
return nil
}
@@ -158,10 +171,14 @@ func (ss *Default) resetEngine(
var _ filtering.SafeSearch = (*Default)(nil)
// CheckHost implements the [filtering.SafeSearch] interface for *Default.
func (ss *Default) CheckHost(host string, qtype rules.RRType) (res filtering.Result, err error) {
func (ss *Default) CheckHost(
ctx context.Context,
host string,
qtype rules.RRType,
) (res filtering.Result, err error) {
start := time.Now()
defer func() {
ss.log(log.DEBUG, "lookup for %q finished in %s", host, time.Since(start))
ss.logger.DebugContext(ctx, "lookup finished", "host", host, "elapsed", time.Since(start))
}()
switch qtype {
@@ -172,9 +189,9 @@ func (ss *Default) CheckHost(host string, qtype rules.RRType) (res filtering.Res
}
// Check cache. Return cached result if it was found
cachedValue, isFound := ss.getCachedResult(host, qtype)
cachedValue, isFound := ss.getCachedResult(ctx, host, qtype)
if isFound {
ss.log(log.DEBUG, "found in cache: %q", host)
ss.logger.DebugContext(ctx, "found in cache", "host", host)
return cachedValue, nil
}
@@ -186,7 +203,7 @@ func (ss *Default) CheckHost(host string, qtype rules.RRType) (res filtering.Res
fltRes, err := ss.newResult(rewrite, qtype)
if err != nil {
ss.log(log.DEBUG, "looking up addresses for %q: %s", host, err)
ss.logger.ErrorContext(ctx, "looking up addresses", "host", host, slogutil.KeyError, err)
return filtering.Result{}, err
}
@@ -195,7 +212,7 @@ func (ss *Default) CheckHost(host string, qtype rules.RRType) (res filtering.Res
// TODO(a.garipov): Consider switch back to resolving CNAME records IPs and
// saving results to cache.
ss.setCacheResult(host, qtype, res)
ss.setCacheResult(ctx, host, qtype, res)
return res, nil
}
@@ -255,7 +272,12 @@ func (ss *Default) newResult(
// setCacheResult stores data in cache for host. qtype is expected to be either
// [dns.TypeA] or [dns.TypeAAAA].
func (ss *Default) setCacheResult(host string, qtype rules.RRType, res filtering.Result) {
func (ss *Default) setCacheResult(
ctx context.Context,
host string,
qtype rules.RRType,
res filtering.Result,
) {
expire := uint32(time.Now().Add(ss.cacheTTL).Unix())
exp := make([]byte, 4)
binary.BigEndian.PutUint32(exp, expire)
@@ -263,7 +285,7 @@ func (ss *Default) setCacheResult(host string, qtype rules.RRType, res filtering
err := gob.NewEncoder(buf).Encode(res)
if err != nil {
ss.log(log.ERROR, "cache encoding: %s", err)
ss.logger.ErrorContext(ctx, "cache encoding", slogutil.KeyError, err)
return
}
@@ -271,12 +293,18 @@ func (ss *Default) setCacheResult(host string, qtype rules.RRType, res filtering
val := buf.Bytes()
_ = ss.cache.Set([]byte(dns.Type(qtype).String()+" "+host), val)
ss.log(log.DEBUG, "stored in cache: %q, %d bytes", host, len(val))
ss.logger.DebugContext(
ctx,
"stored in cache",
"host", host,
"entry_size", datasize.ByteSize(len(val)),
)
}
// getCachedResult returns stored data from cache for host. qtype is expected
// to be either [dns.TypeA] or [dns.TypeAAAA].
func (ss *Default) getCachedResult(
ctx context.Context,
host string,
qtype rules.RRType,
) (res filtering.Result, ok bool) {
@@ -298,7 +326,7 @@ func (ss *Default) getCachedResult(
err := gob.NewDecoder(buf).Decode(&res)
if err != nil {
ss.log(log.ERROR, "cache decoding: %s", err)
ss.logger.ErrorContext(ctx, "cache decoding", slogutil.KeyError, err)
return filtering.Result{}, false
}
@@ -308,11 +336,11 @@ func (ss *Default) getCachedResult(
// Update implements the [filtering.SafeSearch] interface for *Default. Update
// ignores the CustomResolver and Enabled fields.
func (ss *Default) Update(conf filtering.SafeSearchConfig) (err error) {
func (ss *Default) Update(ctx context.Context, conf filtering.SafeSearchConfig) (err error) {
ss.mu.Lock()
defer ss.mu.Unlock()
err = ss.resetEngine(rulelist.URLFilterIDSafeSearch, conf)
err = ss.resetEngine(ctx, rulelist.URLFilterIDSafeSearch, conf)
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return err

View File

@@ -6,6 +6,8 @@ import (
"time"
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/testutil"
"github.com/AdguardTeam/urlfilter/rules"
"github.com/miekg/dns"
"github.com/stretchr/testify/assert"
@@ -21,6 +23,9 @@ const (
testCacheTTL = 30 * time.Minute
)
// testTimeout is the common timeout for tests and contexts.
const testTimeout = 1 * time.Second
var defaultSafeSearchConf = filtering.SafeSearchConfig{
Enabled: true,
Bing: true,
@@ -35,7 +40,12 @@ var defaultSafeSearchConf = filtering.SafeSearchConfig{
var yandexIP = netip.AddrFrom4([4]byte{213, 180, 193, 56})
func newForTest(t testing.TB, ssConf filtering.SafeSearchConfig) (ss *Default) {
ss, err := NewDefault(ssConf, "", testCacheSize, testCacheTTL)
ss, err := NewDefault(testutil.ContextWithTimeout(t, testTimeout), &DefaultConfig{
Logger: slogutil.NewDiscardLogger(),
ServicesConfig: ssConf,
CacheSize: testCacheSize,
CacheTTL: testCacheTTL,
})
require.NoError(t, err)
return ss
@@ -52,16 +62,17 @@ func TestSafeSearchCacheYandex(t *testing.T) {
const domain = "yandex.ru"
ss := newForTest(t, filtering.SafeSearchConfig{Enabled: false})
ctx := testutil.ContextWithTimeout(t, testTimeout)
// Check host with disabled safesearch.
res, err := ss.CheckHost(domain, testQType)
res, err := ss.CheckHost(ctx, domain, testQType)
require.NoError(t, err)
assert.False(t, res.IsFiltered)
assert.Empty(t, res.Rules)
ss = newForTest(t, defaultSafeSearchConf)
res, err = ss.CheckHost(domain, testQType)
res, err = ss.CheckHost(ctx, domain, testQType)
require.NoError(t, err)
// For yandex we already know valid IP.
@@ -70,7 +81,7 @@ func TestSafeSearchCacheYandex(t *testing.T) {
assert.Equal(t, res.Rules[0].IP, yandexIP)
// Check cache.
cachedValue, isFound := ss.getCachedResult(domain, testQType)
cachedValue, isFound := ss.getCachedResult(ctx, domain, testQType)
require.True(t, isFound)
require.Len(t, cachedValue.Rules, 1)

View File

@@ -10,15 +10,15 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/AdGuardHome/internal/filtering/rulelist"
"github.com/AdguardTeam/AdGuardHome/internal/filtering/safesearch"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"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 and contexts.
const testTimeout = 1 * time.Second
// Common test constants.
const (
@@ -47,7 +47,13 @@ var yandexIP = netip.AddrFrom4([4]byte{213, 180, 193, 56})
func TestDefault_CheckHost_yandex(t *testing.T) {
conf := testConf
ss, err := safesearch.NewDefault(conf, "", testCacheSize, testCacheTTL)
ctx := testutil.ContextWithTimeout(t, testTimeout)
ss, err := safesearch.NewDefault(ctx, &safesearch.DefaultConfig{
Logger: slogutil.NewDiscardLogger(),
ServicesConfig: conf,
CacheSize: testCacheSize,
CacheTTL: testCacheTTL,
})
require.NoError(t, err)
hosts := []string{
@@ -82,7 +88,7 @@ func TestDefault_CheckHost_yandex(t *testing.T) {
for _, host := range hosts {
// Check host for each domain.
var res filtering.Result
res, err = ss.CheckHost(host, tc.qt)
res, err = ss.CheckHost(ctx, host, tc.qt)
require.NoError(t, err)
assert.True(t, res.IsFiltered)
@@ -103,7 +109,13 @@ func TestDefault_CheckHost_yandex(t *testing.T) {
}
func TestDefault_CheckHost_google(t *testing.T) {
ss, err := safesearch.NewDefault(testConf, "", testCacheSize, testCacheTTL)
ctx := testutil.ContextWithTimeout(t, testTimeout)
ss, err := safesearch.NewDefault(ctx, &safesearch.DefaultConfig{
Logger: slogutil.NewDiscardLogger(),
ServicesConfig: testConf,
CacheSize: testCacheSize,
CacheTTL: testCacheTTL,
})
require.NoError(t, err)
// Check host for each domain.
@@ -118,7 +130,7 @@ func TestDefault_CheckHost_google(t *testing.T) {
} {
t.Run(host, func(t *testing.T) {
var res filtering.Result
res, err = ss.CheckHost(host, testQType)
res, err = ss.CheckHost(ctx, host, testQType)
require.NoError(t, err)
assert.True(t, res.IsFiltered)
@@ -149,13 +161,19 @@ func (r *testResolver) LookupIP(
}
func TestDefault_CheckHost_duckduckgoAAAA(t *testing.T) {
ss, err := safesearch.NewDefault(testConf, "", testCacheSize, testCacheTTL)
ctx := testutil.ContextWithTimeout(t, testTimeout)
ss, err := safesearch.NewDefault(ctx, &safesearch.DefaultConfig{
Logger: slogutil.NewDiscardLogger(),
ServicesConfig: testConf,
CacheSize: testCacheSize,
CacheTTL: testCacheTTL,
})
require.NoError(t, err)
// The DuckDuckGo safe-search addresses are resolved through CNAMEs, but
// DuckDuckGo doesn't have a safe-search IPv6 address. The result should be
// the same as the one for Yandex IPv6. That is, a NODATA response.
res, err := ss.CheckHost("www.duckduckgo.com", dns.TypeAAAA)
res, err := ss.CheckHost(ctx, "www.duckduckgo.com", dns.TypeAAAA)
require.NoError(t, err)
assert.True(t, res.IsFiltered)
@@ -166,32 +184,38 @@ func TestDefault_CheckHost_duckduckgoAAAA(t *testing.T) {
func TestDefault_Update(t *testing.T) {
conf := testConf
ss, err := safesearch.NewDefault(conf, "", testCacheSize, testCacheTTL)
ctx := testutil.ContextWithTimeout(t, testTimeout)
ss, err := safesearch.NewDefault(ctx, &safesearch.DefaultConfig{
Logger: slogutil.NewDiscardLogger(),
ServicesConfig: conf,
CacheSize: testCacheSize,
CacheTTL: testCacheTTL,
})
require.NoError(t, err)
res, err := ss.CheckHost("www.yandex.com", testQType)
res, err := ss.CheckHost(ctx, "www.yandex.com", testQType)
require.NoError(t, err)
assert.True(t, res.IsFiltered)
err = ss.Update(filtering.SafeSearchConfig{
err = ss.Update(ctx, filtering.SafeSearchConfig{
Enabled: true,
Google: false,
})
require.NoError(t, err)
res, err = ss.CheckHost("www.yandex.com", testQType)
res, err = ss.CheckHost(ctx, "www.yandex.com", testQType)
require.NoError(t, err)
assert.False(t, res.IsFiltered)
err = ss.Update(filtering.SafeSearchConfig{
err = ss.Update(ctx, filtering.SafeSearchConfig{
Enabled: false,
Google: true,
})
require.NoError(t, err)
res, err = ss.CheckHost("www.yandex.com", testQType)
res, err = ss.CheckHost(ctx, "www.yandex.com", testQType)
require.NoError(t, err)
assert.False(t, res.IsFiltered)

View File

@@ -51,7 +51,7 @@ func (d *DNSFilter) handleSafeSearchSettings(w http.ResponseWriter, r *http.Requ
}
conf := *req
err = d.safeSearch.Update(conf)
err = d.safeSearch.Update(r.Context(), conf)
if err != nil {
aghhttp.Error(r, w, http.StatusBadRequest, "updating: %s", err)

View File

@@ -2450,7 +2450,7 @@ var blockedServices = []blockedService{{
},
}, {
ID: "telegram",
Name: "Telegram",
Name: "Telegram (Web)",
IconSVG: []byte("<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" viewBox=\"0 0 50 50\"><path d=\"M46.137,6.552c-0.75-0.636-1.928-0.727-3.146-0.238l-0.002,0C41.708,6.828,6.728,21.832,5.304,22.445 c-0.259,0.09-2.521,0.934-2.288,2.814c0.208,1.695,2.026,2.397,2.248,2.478l8.893,3.045c0.59,1.964,2.765,9.21,3.246,10.758 c0.3,0.965,0.789,2.233,1.646,2.494c0.752,0.29,1.5,0.025,1.984-0.355l5.437-5.043l8.777,6.845l0.209,0.125 c0.596,0.264,1.167,0.396,1.712,0.396c0.421,0,0.825-0.079,1.211-0.237c1.315-0.54,1.841-1.793,1.896-1.935l6.556-34.077 C47.231,7.933,46.675,7.007,46.137,6.552z M22,32l-3,8l-3-10l23-17L22,32z\" /></svg>"),
Rules: []string{
"||comments.app^",

View File

@@ -90,6 +90,7 @@ func InitAuth(
trustedProxies: trustedProxies,
}
var err error
a.db, err = bbolt.Open(dbFilename, aghos.DefaultPermFile, nil)
if err != nil {
log.Error("auth: open DB: %s: %s", dbFilename, err)

View File

@@ -3,6 +3,7 @@ package home
import (
"context"
"fmt"
"log/slog"
"net/netip"
"slices"
"sync"
@@ -13,17 +14,23 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/client"
"github.com/AdguardTeam/AdGuardHome/internal/dnsforward"
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/AdGuardHome/internal/filtering/safesearch"
"github.com/AdguardTeam/AdGuardHome/internal/querylog"
"github.com/AdguardTeam/AdGuardHome/internal/schedule"
"github.com/AdguardTeam/AdGuardHome/internal/whois"
"github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/dnsproxy/upstream"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/stringutil"
)
// clientsContainer is the storage of all runtime and persistent clients.
type clientsContainer struct {
// baseLogger is used to create loggers with custom prefixes for safe search
// filter. It must not be nil.
baseLogger *slog.Logger
// storage stores information about persistent clients.
storage *client.Storage
@@ -61,6 +68,8 @@ type BlockedClientChecker interface {
// dhcpServer: optional
// Note: this function must be called only once
func (clients *clientsContainer) Init(
ctx context.Context,
baseLogger *slog.Logger,
objects []*clientObject,
dhcpServer client.DHCP,
etcHosts *aghnet.HostsContainer,
@@ -72,13 +81,14 @@ func (clients *clientsContainer) Init(
return errors.Error("clients container already initialized")
}
clients.baseLogger = baseLogger
clients.safeSearchCacheSize = filteringConf.SafeSearchCacheSize
clients.safeSearchCacheTTL = time.Minute * time.Duration(filteringConf.CacheTime)
confClients := make([]*client.Persistent, 0, len(objects))
for i, o := range objects {
var p *client.Persistent
p, err = o.toPersistent(clients.safeSearchCacheSize, clients.safeSearchCacheTTL)
p, err = o.toPersistent(ctx, baseLogger, clients.safeSearchCacheSize, clients.safeSearchCacheTTL)
if err != nil {
return fmt.Errorf("init persistent client at index %d: %w", i, err)
}
@@ -92,12 +102,13 @@ func (clients *clientsContainer) Init(
// TODO(e.burkov): The option should probably be returned, since hosts file
// currently used not only for clients' information enrichment, but also in
// the filtering module and upstream addresses resolution.
var hosts client.HostsContainer = etcHosts
if !config.Clients.Sources.HostsFile {
hosts = nil
var hosts client.HostsContainer
if config.Clients.Sources.HostsFile && etcHosts != nil {
hosts = etcHosts
}
clients.storage, err = client.NewStorage(&client.StorageConfig{
clients.storage, err = client.NewStorage(ctx, &client.StorageConfig{
Logger: baseLogger.With(slogutil.KeyPrefix, "client_storage"),
InitialClients: confClients,
DHCP: dhcpServer,
EtcHosts: hosts,
@@ -168,6 +179,8 @@ type clientObject struct {
// toPersistent returns an initialized persistent client if there are no errors.
func (o *clientObject) toPersistent(
ctx context.Context,
baseLogger *slog.Logger,
safeSearchCacheSize uint,
safeSearchCacheTTL time.Duration,
) (cli *client.Persistent, err error) {
@@ -203,14 +216,23 @@ func (o *clientObject) toPersistent(
}
if o.SafeSearchConf.Enabled {
err = cli.SetSafeSearch(
o.SafeSearchConf,
safeSearchCacheSize,
safeSearchCacheTTL,
logger := baseLogger.With(
slogutil.KeyPrefix, safesearch.LogPrefix,
safesearch.LogKeyClient, cli.Name,
)
var ss *safesearch.Default
ss, err = safesearch.NewDefault(ctx, &safesearch.DefaultConfig{
Logger: logger,
ServicesConfig: o.SafeSearchConf,
ClientName: cli.Name,
CacheSize: safeSearchCacheSize,
CacheTTL: safeSearchCacheTTL,
})
if err != nil {
return nil, fmt.Errorf("init safesearch %q: %w", cli.Name, err)
}
cli.SafeSearch = ss
}
if o.BlockedServices == nil {
@@ -378,7 +400,7 @@ func (clients *clientsContainer) UpstreamConfigByID(
upstreams,
&upstream.Options{
Bootstrap: bootstrap,
Timeout: config.DNS.UpstreamTimeout.Duration,
Timeout: time.Duration(config.DNS.UpstreamTimeout),
HTTPVersions: dnsforward.UpstreamHTTPVersions(config.DNS.UseHTTP3Upstreams),
PreferIPv6: config.DNS.BootstrapPreferIPv6,
},
@@ -396,6 +418,12 @@ func (clients *clientsContainer) UpstreamConfigByID(
)
c.UpstreamConfig = conf
// TODO(s.chzhen): Pass context.
err = clients.storage.Update(context.TODO(), c.Name, c)
if err != nil {
return nil, fmt.Errorf("setting upstream config: %w", err)
}
return conf, nil
}
@@ -404,8 +432,13 @@ var _ client.AddressUpdater = (*clientsContainer)(nil)
// UpdateAddress implements the [client.AddressUpdater] interface for
// *clientsContainer
func (clients *clientsContainer) UpdateAddress(ip netip.Addr, host string, info *whois.Info) {
clients.storage.UpdateAddress(ip, host, info)
func (clients *clientsContainer) UpdateAddress(
ctx context.Context,
ip netip.Addr,
host string,
info *whois.Info,
) {
clients.storage.UpdateAddress(ctx, ip, host, info)
}
// close gracefully closes all the client-specific upstream configurations of

View File

@@ -7,6 +7,8 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/client"
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/testutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -20,16 +22,28 @@ func newClientsContainer(t *testing.T) (c *clientsContainer) {
testing: true,
}
require.NoError(t, c.Init(nil, client.EmptyDHCP{}, nil, nil, &filtering.Config{}))
ctx := testutil.ContextWithTimeout(t, testTimeout)
err := c.Init(
ctx,
slogutil.NewDiscardLogger(),
nil,
client.EmptyDHCP{},
nil,
nil,
&filtering.Config{},
)
require.NoError(t, err)
return c
}
func TestClientsCustomUpstream(t *testing.T) {
clients := newClientsContainer(t)
ctx := testutil.ContextWithTimeout(t, testTimeout)
// Add client with upstreams.
err := clients.storage.Add(&client.Persistent{
err := clients.storage.Add(ctx, &client.Persistent{
Name: "client1",
UID: client.MustNewUID(),
IPs: []netip.Addr{netip.MustParseAddr("1.1.1.1"), netip.MustParseAddr("1:2:3::4")},

View File

@@ -1,6 +1,7 @@
package home
import (
"context"
"encoding/json"
"fmt"
"net/http"
@@ -10,8 +11,10 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
"github.com/AdguardTeam/AdGuardHome/internal/client"
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/AdGuardHome/internal/filtering/safesearch"
"github.com/AdguardTeam/AdGuardHome/internal/schedule"
"github.com/AdguardTeam/AdGuardHome/internal/whois"
"github.com/AdguardTeam/golibs/logutil/slogutil"
)
// clientJSON is a common structure used by several handlers to deal with
@@ -103,7 +106,7 @@ func (clients *clientsContainer) handleGetClients(w http.ResponseWriter, r *http
return true
})
clients.storage.UpdateDHCP()
clients.storage.UpdateDHCP(r.Context())
clients.storage.RangeRuntime(func(rc *client.Runtime) (cont bool) {
src, host := rc.Info()
@@ -181,6 +184,7 @@ func initPrev(cj clientJSON, prev *client.Persistent) (c *client.Persistent, err
// jsonToClient converts JSON object to persistent client object if there are no
// errors.
func (clients *clientsContainer) jsonToClient(
ctx context.Context,
cj clientJSON,
prev *client.Persistent,
) (c *client.Persistent, err error) {
@@ -207,14 +211,23 @@ func (clients *clientsContainer) jsonToClient(
c.UseOwnBlockedServices = !cj.UseGlobalBlockedServices
if c.SafeSearchConf.Enabled {
err = c.SetSafeSearch(
c.SafeSearchConf,
clients.safeSearchCacheSize,
clients.safeSearchCacheTTL,
logger := clients.baseLogger.With(
slogutil.KeyPrefix, safesearch.LogPrefix,
safesearch.LogKeyClient, c.Name,
)
var ss *safesearch.Default
ss, err = safesearch.NewDefault(ctx, &safesearch.DefaultConfig{
Logger: logger,
ServicesConfig: c.SafeSearchConf,
ClientName: c.Name,
CacheSize: clients.safeSearchCacheSize,
CacheTTL: clients.safeSearchCacheTTL,
})
if err != nil {
return nil, fmt.Errorf("creating safesearch for client %q: %w", c.Name, err)
}
c.SafeSearch = ss
}
return c, nil
@@ -321,14 +334,14 @@ func (clients *clientsContainer) handleAddClient(w http.ResponseWriter, r *http.
return
}
c, err := clients.jsonToClient(cj, nil)
c, err := clients.jsonToClient(r.Context(), cj, nil)
if err != nil {
aghhttp.Error(r, w, http.StatusBadRequest, "%s", err)
return
}
err = clients.storage.Add(c)
err = clients.storage.Add(r.Context(), c)
if err != nil {
aghhttp.Error(r, w, http.StatusBadRequest, "%s", err)
@@ -356,7 +369,7 @@ func (clients *clientsContainer) handleDelClient(w http.ResponseWriter, r *http.
return
}
if !clients.storage.RemoveByName(cj.Name) {
if !clients.storage.RemoveByName(r.Context(), cj.Name) {
aghhttp.Error(r, w, http.StatusBadRequest, "Client not found")
return
@@ -391,14 +404,14 @@ func (clients *clientsContainer) handleUpdateClient(w http.ResponseWriter, r *ht
return
}
c, err := clients.jsonToClient(dj.Data, nil)
c, err := clients.jsonToClient(r.Context(), dj.Data, nil)
if err != nil {
aghhttp.Error(r, w, http.StatusBadRequest, "%s", err)
return
}
err = clients.storage.Update(dj.Name, c)
err = clients.storage.Update(r.Context(), dj.Name, c)
if err != nil {
aghhttp.Error(r, w, http.StatusBadRequest, "%s", err)
@@ -411,6 +424,8 @@ func (clients *clientsContainer) handleUpdateClient(w http.ResponseWriter, r *ht
}
// handleFindClient is the handler for GET /control/clients/find HTTP API.
//
// Deprecated: Remove it when migration to the new API is over.
func (clients *clientsContainer) handleFindClient(w http.ResponseWriter, r *http.Request) {
q := r.URL.Query()
data := []map[string]*clientJSON{}
@@ -420,19 +435,58 @@ func (clients *clientsContainer) handleFindClient(w http.ResponseWriter, r *http
break
}
ip, _ := netip.ParseAddr(idStr)
c, ok := clients.storage.Find(idStr)
var cj *clientJSON
if !ok {
cj = clients.findRuntime(ip, idStr)
} else {
cj = clientToJSON(c)
disallowed, rule := clients.clientChecker.IsBlockedClient(ip, idStr)
cj.Disallowed, cj.DisallowedRule = &disallowed, &rule
}
data = append(data, map[string]*clientJSON{
idStr: cj,
idStr: clients.findClient(idStr),
})
}
aghhttp.WriteJSONResponseOK(w, r, data)
}
// findClient returns available information about a client by idStr from the
// client's storage or access settings. cj is guaranteed to be non-nil.
func (clients *clientsContainer) findClient(idStr string) (cj *clientJSON) {
ip, _ := netip.ParseAddr(idStr)
c, ok := clients.storage.Find(idStr)
if !ok {
return clients.findRuntime(ip, idStr)
}
cj = clientToJSON(c)
disallowed, rule := clients.clientChecker.IsBlockedClient(ip, idStr)
cj.Disallowed, cj.DisallowedRule = &disallowed, &rule
return cj
}
// searchQueryJSON is a request to the POST /control/clients/search HTTP API.
//
// TODO(s.chzhen): Add UIDs.
type searchQueryJSON struct {
Clients []searchClientJSON `json:"clients"`
}
// searchClientJSON is a part of [searchQueryJSON] that contains a string
// representation of the client's IP address, CIDR, MAC address, or ClientID.
type searchClientJSON struct {
ID string `json:"id"`
}
// handleSearchClient is the handler for the POST /control/clients/search HTTP API.
func (clients *clientsContainer) handleSearchClient(w http.ResponseWriter, r *http.Request) {
q := searchQueryJSON{}
err := json.NewDecoder(r.Body).Decode(&q)
if err != nil {
aghhttp.Error(r, w, http.StatusBadRequest, "failed to process request body: %s", err)
return
}
data := []map[string]*clientJSON{}
for _, c := range q.Clients {
idStr := c.ID
data = append(data, map[string]*clientJSON{
idStr: clients.findClient(idStr),
})
}
@@ -480,5 +534,8 @@ func (clients *clientsContainer) registerWebHandlers() {
httpRegister(http.MethodPost, "/control/clients/add", clients.handleAddClient)
httpRegister(http.MethodPost, "/control/clients/delete", clients.handleDelClient)
httpRegister(http.MethodPost, "/control/clients/update", clients.handleUpdateClient)
httpRegister(http.MethodPost, "/control/clients/search", clients.handleSearchClient)
// Deprecated handler.
httpRegister(http.MethodGet, "/control/clients/find", clients.handleFindClient)
}

View File

@@ -11,14 +11,20 @@ import (
"net/url"
"slices"
"testing"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/client"
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/AdGuardHome/internal/schedule"
"github.com/AdguardTeam/AdGuardHome/internal/whois"
"github.com/AdguardTeam/golibs/testutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// testTimeout is the common timeout for tests and contexts.
const testTimeout = 1 * time.Second
const (
testClientIP1 = "1.1.1.1"
testClientIP2 = "2.2.2.2"
@@ -103,9 +109,10 @@ func assertPersistentClients(tb testing.TB, clients *clientsContainer, want []*c
require.NoError(tb, err)
var got []*client.Persistent
ctx := testutil.ContextWithTimeout(tb, testTimeout)
for _, cj := range clientList.Clients {
var c *client.Persistent
c, err = clients.jsonToClient(*cj, nil)
c, err = clients.jsonToClient(ctx, *cj, nil)
require.NoError(tb, err)
got = append(got, c)
@@ -125,10 +132,11 @@ func assertPersistentClientsData(
tb.Helper()
var got []*client.Persistent
ctx := testutil.ContextWithTimeout(tb, testTimeout)
for _, cm := range data {
for _, cj := range cm {
var c *client.Persistent
c, err := clients.jsonToClient(*cj, nil)
c, err := clients.jsonToClient(ctx, *cj, nil)
require.NoError(tb, err)
got = append(got, c)
@@ -196,13 +204,14 @@ func TestClientsContainer_HandleAddClient(t *testing.T) {
func TestClientsContainer_HandleDelClient(t *testing.T) {
clients := newClientsContainer(t)
ctx := testutil.ContextWithTimeout(t, testTimeout)
clientOne := newPersistentClientWithIDs(t, "client1", []string{testClientIP1})
err := clients.storage.Add(clientOne)
err := clients.storage.Add(ctx, clientOne)
require.NoError(t, err)
clientTwo := newPersistentClientWithIDs(t, "client2", []string{testClientIP2})
err = clients.storage.Add(clientTwo)
err = clients.storage.Add(ctx, clientTwo)
require.NoError(t, err)
assertPersistentClients(t, clients, []*client.Persistent{clientOne, clientTwo})
@@ -258,9 +267,10 @@ func TestClientsContainer_HandleDelClient(t *testing.T) {
func TestClientsContainer_HandleUpdateClient(t *testing.T) {
clients := newClientsContainer(t)
ctx := testutil.ContextWithTimeout(t, testTimeout)
clientOne := newPersistentClientWithIDs(t, "client1", []string{testClientIP1})
err := clients.storage.Add(clientOne)
err := clients.storage.Add(ctx, clientOne)
require.NoError(t, err)
assertPersistentClients(t, clients, []*client.Persistent{clientOne})
@@ -341,12 +351,14 @@ func TestClientsContainer_HandleFindClient(t *testing.T) {
},
}
ctx := testutil.ContextWithTimeout(t, testTimeout)
clientOne := newPersistentClientWithIDs(t, "client1", []string{testClientIP1})
err := clients.storage.Add(clientOne)
err := clients.storage.Add(ctx, clientOne)
require.NoError(t, err)
clientTwo := newPersistentClientWithIDs(t, "client2", []string{testClientIP2})
err = clients.storage.Add(clientTwo)
err = clients.storage.Add(ctx, clientTwo)
require.NoError(t, err)
assertPersistentClients(t, clients, []*client.Persistent{clientOne, clientTwo})
@@ -397,3 +409,145 @@ func TestClientsContainer_HandleFindClient(t *testing.T) {
})
}
}
func TestClientsContainer_HandleSearchClient(t *testing.T) {
var (
runtimeCli = "runtime_client1"
runtimeCliIP = "3.3.3.3"
blockedCliIP = "4.4.4.4"
nonExistentCliIP = "5.5.5.5"
allowed = false
dissallowed = true
emptyRule = ""
disallowedRule = "disallowed_rule"
)
clients := newClientsContainer(t)
clients.clientChecker = &testBlockedClientChecker{
onIsBlockedClient: func(ip netip.Addr, _ string) (ok bool, rule string) {
if ip == netip.MustParseAddr(blockedCliIP) {
return true, disallowedRule
}
return false, emptyRule
},
}
ctx := testutil.ContextWithTimeout(t, testTimeout)
clientOne := newPersistentClientWithIDs(t, "client1", []string{testClientIP1})
err := clients.storage.Add(ctx, clientOne)
require.NoError(t, err)
clientTwo := newPersistentClientWithIDs(t, "client2", []string{testClientIP2})
err = clients.storage.Add(ctx, clientTwo)
require.NoError(t, err)
assertPersistentClients(t, clients, []*client.Persistent{clientOne, clientTwo})
clients.UpdateAddress(ctx, netip.MustParseAddr(runtimeCliIP), runtimeCli, nil)
testCases := []struct {
name string
query *searchQueryJSON
wantPersistent []*client.Persistent
wantRuntime *clientJSON
}{{
name: "single",
query: &searchQueryJSON{
Clients: []searchClientJSON{{
ID: testClientIP1,
}},
},
wantPersistent: []*client.Persistent{clientOne},
}, {
name: "multiple",
query: &searchQueryJSON{
Clients: []searchClientJSON{{
ID: testClientIP1,
}, {
ID: testClientIP2,
}},
},
wantPersistent: []*client.Persistent{clientOne, clientTwo},
}, {
name: "runtime",
query: &searchQueryJSON{
Clients: []searchClientJSON{{
ID: runtimeCliIP,
}},
},
wantRuntime: &clientJSON{
Name: runtimeCli,
IDs: []string{runtimeCliIP},
Disallowed: &allowed,
DisallowedRule: &emptyRule,
WHOIS: &whois.Info{},
},
}, {
name: "blocked_access",
query: &searchQueryJSON{
Clients: []searchClientJSON{{
ID: blockedCliIP,
}},
},
wantRuntime: &clientJSON{
IDs: []string{blockedCliIP},
Disallowed: &dissallowed,
DisallowedRule: &disallowedRule,
WHOIS: &whois.Info{},
},
}, {
name: "non_existing_client",
query: &searchQueryJSON{
Clients: []searchClientJSON{{
ID: nonExistentCliIP,
}},
},
wantRuntime: &clientJSON{
IDs: []string{nonExistentCliIP},
Disallowed: &allowed,
DisallowedRule: &emptyRule,
WHOIS: &whois.Info{},
},
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
var body []byte
body, err = json.Marshal(tc.query)
require.NoError(t, err)
var r *http.Request
r, err = http.NewRequest(http.MethodPost, "", bytes.NewReader(body))
require.NoError(t, err)
rw := httptest.NewRecorder()
clients.handleSearchClient(rw, r)
require.NoError(t, err)
require.Equal(t, http.StatusOK, rw.Code)
body, err = io.ReadAll(rw.Body)
require.NoError(t, err)
clientData := []map[string]*clientJSON{}
err = json.Unmarshal(body, &clientData)
require.NoError(t, err)
if tc.wantPersistent != nil {
assertPersistentClientsData(t, clients, clientData, tc.wantPersistent)
return
}
require.Len(t, clientData, 1)
require.Len(t, clientData[0], 1)
rc := clientData[0][tc.wantRuntime.IDs[0]]
assert.Equal(t, tc.wantRuntime, rc)
})
}
}

View File

@@ -162,6 +162,12 @@ type configuration struct {
// SchemaVersion is the version of the configuration schema. See
// [configmigrate.LastSchemaVersion].
SchemaVersion uint `yaml:"schema_version"`
// UnsafeUseCustomUpdateIndexURL is the URL to the custom update index.
//
// NOTE: It's only exists for testing purposes and should not be used in
// release.
UnsafeUseCustomUpdateIndexURL bool `yaml:"unsafe_use_custom_update_index_url,omitempty"`
}
// httpConfig is a block with HTTP configuration params.
@@ -333,7 +339,7 @@ var config = &configuration{
AuthBlockMin: 15,
HTTPConfig: httpConfig{
Address: netip.AddrPortFrom(netip.IPv4Unspecified(), 3000),
SessionTTL: timeutil.Duration{Duration: 30 * timeutil.Day},
SessionTTL: timeutil.Duration(30 * timeutil.Day),
Pprof: &httpPprofConfig{
Enabled: false,
Port: 6060,
@@ -349,9 +355,7 @@ var config = &configuration{
RefuseAny: true,
UpstreamMode: dnsforward.UpstreamModeLoadBalance,
HandleDDR: true,
FastestTimeout: timeutil.Duration{
Duration: fastip.DefaultPingWaitTimeout,
},
FastestTimeout: timeutil.Duration(fastip.DefaultPingWaitTimeout),
TrustedProxies: []netutil.Prefix{{
Prefix: netip.MustParsePrefix("127.0.0.0/8"),
@@ -372,7 +376,7 @@ var config = &configuration{
// was later increased to 300 due to https://github.com/AdguardTeam/AdGuardHome/issues/2257
MaxGoroutines: 300,
},
UpstreamTimeout: timeutil.Duration{Duration: dnsforward.DefaultTimeout},
UpstreamTimeout: timeutil.Duration(dnsforward.DefaultTimeout),
UsePrivateRDNS: true,
ServePlainDNS: true,
HostsFileEnabled: true,
@@ -385,13 +389,13 @@ var config = &configuration{
QueryLog: queryLogConfig{
Enabled: true,
FileEnabled: true,
Interval: timeutil.Duration{Duration: 90 * timeutil.Day},
Interval: timeutil.Duration(90 * timeutil.Day),
MemSize: 1000,
Ignored: []string{},
},
Stats: statsConfig{
Enabled: true,
Interval: timeutil.Duration{Duration: 1 * timeutil.Day},
Interval: timeutil.Duration(1 * timeutil.Day),
Ignored: []string{},
},
// NOTE: Keep these parameters in sync with the one put into
@@ -559,8 +563,8 @@ func parseConfig() (err error) {
return err
}
if config.DNS.UpstreamTimeout.Duration == 0 {
config.DNS.UpstreamTimeout = timeutil.Duration{Duration: dnsforward.DefaultTimeout}
if config.DNS.UpstreamTimeout == 0 {
config.DNS.UpstreamTimeout = timeutil.Duration(dnsforward.DefaultTimeout)
}
// Do not wrap the error because it's informative enough as is.
@@ -653,7 +657,7 @@ func (c *configuration) write() (err error) {
if Context.stats != nil {
statsConf := stats.Config{}
Context.stats.WriteDiskConfig(&statsConf)
config.Stats.Interval = timeutil.Duration{Duration: statsConf.Limit}
config.Stats.Interval = timeutil.Duration(statsConf.Limit)
config.Stats.Enabled = statsConf.Enabled
config.Stats.Ignored = statsConf.Ignored.Values()
}
@@ -664,7 +668,7 @@ func (c *configuration) write() (err error) {
config.DNS.AnonymizeClientIP = dc.AnonymizeClientIP
config.QueryLog.Enabled = dc.Enabled
config.QueryLog.FileEnabled = dc.FileEnabled
config.QueryLog.Interval = timeutil.Duration{Duration: dc.RotationIvl}
config.QueryLog.Interval = timeutil.Duration(dc.RotationIvl)
config.QueryLog.MemSize = dc.MemSize
config.QueryLog.Ignored = dc.Ignored.Values()
}

View File

@@ -16,6 +16,7 @@ import (
"github.com/AdguardTeam/golibs/httphdr"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/netutil/urlutil"
"github.com/NYTimes/gziphandler"
)
@@ -376,7 +377,7 @@ func handleHTTPSRedirect(w http.ResponseWriter, r *http.Request) (proceed bool)
//
// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin.
originURL := &url.URL{
Scheme: aghhttp.SchemeHTTP,
Scheme: urlutil.SchemeHTTP,
Host: r.Host,
}
@@ -395,7 +396,7 @@ func httpsURL(u *url.URL, host string, portHTTPS uint16) (redirectURL *url.URL)
}
return &url.URL{
Scheme: aghhttp.SchemeHTTPS,
Scheme: urlutil.SchemeHTTPS,
Host: hostPort,
Path: u.Path,
RawQuery: u.RawQuery,

View File

@@ -5,6 +5,7 @@ import (
"encoding/json"
"fmt"
"io"
"log/slog"
"net/http"
"net/netip"
"os"
@@ -19,7 +20,7 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
"github.com/AdguardTeam/AdGuardHome/internal/version"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/quic-go/quic-go/http3"
)
@@ -124,6 +125,8 @@ func (req *checkConfReq) validateWeb(tcpPorts aghalg.UniqChecker[tcpPort]) (err
// be set. canAutofix is true if the port can be unbound by AdGuard Home
// automatically.
func (req *checkConfReq) validateDNS(
ctx context.Context,
l *slog.Logger,
tcpPorts aghalg.UniqChecker[tcpPort],
) (canAutofix bool, err error) {
defer func() { err = errors.Annotate(err, "validating ports: %w") }()
@@ -154,10 +157,10 @@ func (req *checkConfReq) validateDNS(
}
// Try to fix automatically.
canAutofix = checkDNSStubListener()
canAutofix = checkDNSStubListener(ctx, l)
if canAutofix && req.DNS.Autofix {
if derr := disableDNSStubListener(); derr != nil {
log.Error("disabling DNSStubListener: %s", err)
if derr := disableDNSStubListener(ctx, l); derr != nil {
l.ErrorContext(ctx, "disabling DNSStubListener", slogutil.KeyError, err)
}
err = aghnet.CheckPort("udp", netip.AddrPortFrom(req.DNS.IP, port))
@@ -184,7 +187,7 @@ func (web *webAPI) handleInstallCheckConfig(w http.ResponseWriter, r *http.Reque
resp.Web.Status = err.Error()
}
if resp.DNS.CanAutofix, err = req.validateDNS(tcpPorts); err != nil {
if resp.DNS.CanAutofix, err = req.validateDNS(r.Context(), web.logger, tcpPorts); err != nil {
resp.DNS.Status = err.Error()
} else if !req.DNS.IP.IsUnspecified() {
resp.StaticIP = handleStaticIP(req.DNS.IP, req.SetStaticIP)
@@ -233,27 +236,39 @@ func handleStaticIP(ip netip.Addr, set bool) staticIPJSON {
return resp
}
// Check if DNSStubListener is active
func checkDNSStubListener() bool {
// checkDNSStubListener returns true if DNSStubListener is active.
func checkDNSStubListener(ctx context.Context, l *slog.Logger) (ok bool) {
if runtime.GOOS != "linux" {
return false
}
cmd := exec.Command("systemctl", "is-enabled", "systemd-resolved")
log.Tracef("executing %s %v", cmd.Path, cmd.Args)
l.DebugContext(ctx, "executing", "cmd", cmd.Path, "args", cmd.Args)
_, err := cmd.Output()
if err != nil || cmd.ProcessState.ExitCode() != 0 {
log.Info("command %s has failed: %v code:%d",
cmd.Path, err, cmd.ProcessState.ExitCode())
l.InfoContext(
ctx,
"execution failed",
"cmd", cmd.Path,
"code", cmd.ProcessState.ExitCode(),
slogutil.KeyError, err,
)
return false
}
cmd = exec.Command("grep", "-E", "#?DNSStubListener=yes", "/etc/systemd/resolved.conf")
log.Tracef("executing %s %v", cmd.Path, cmd.Args)
l.DebugContext(ctx, "executing", "cmd", cmd.Path, "args", cmd.Args)
_, err = cmd.Output()
if err != nil || cmd.ProcessState.ExitCode() != 0 {
log.Info("command %s has failed: %v code:%d",
cmd.Path, err, cmd.ProcessState.ExitCode())
l.InfoContext(
ctx,
"execution failed",
"cmd", cmd.Path,
"code", cmd.ProcessState.ExitCode(),
slogutil.KeyError, err,
)
return false
}
@@ -269,8 +284,9 @@ DNSStubListener=no
)
const resolvConfPath = "/etc/resolv.conf"
// Deactivate DNSStubListener
func disableDNSStubListener() (err error) {
// disableDNSStubListener deactivates DNSStubListerner and returns an error, if
// any.
func disableDNSStubListener(ctx context.Context, l *slog.Logger) (err error) {
dir := filepath.Dir(resolvedConfPath)
err = os.MkdirAll(dir, 0o755)
if err != nil {
@@ -290,7 +306,7 @@ func disableDNSStubListener() (err error) {
}
cmd := exec.Command("systemctl", "reload-or-restart", "systemd-resolved")
log.Tracef("executing %s %v", cmd.Path, cmd.Args)
l.DebugContext(ctx, "executing", "cmd", cmd.Path, "args", cmd.Args)
_, err = cmd.Output()
if err != nil {
return err
@@ -327,9 +343,9 @@ func copyInstallSettings(dst, src *configuration) {
// shutdownTimeout is the timeout for shutting HTTP server down operation.
const shutdownTimeout = 5 * time.Second
// shutdownSrv shuts srv down and prints error messages to the log.
func shutdownSrv(ctx context.Context, srv *http.Server) {
defer log.OnPanic("")
// shutdownSrv shuts down srv and logs the error, if any. l must not be nil.
func shutdownSrv(ctx context.Context, l *slog.Logger, srv *http.Server) {
defer slogutil.RecoverAndLog(ctx, l)
if srv == nil {
return
@@ -340,19 +356,19 @@ func shutdownSrv(ctx context.Context, srv *http.Server) {
return
}
const msgFmt = "shutting down http server %q: %s"
if errors.Is(err, context.Canceled) {
log.Debug(msgFmt, srv.Addr, err)
} else {
log.Error(msgFmt, srv.Addr, err)
lvl := slog.LevelDebug
if !errors.Is(err, context.Canceled) {
lvl = slog.LevelError
}
l.Log(ctx, lvl, "shutting down http server", "addr", srv.Addr, slogutil.KeyError, err)
}
// shutdownSrv3 shuts srv down and prints error messages to the log.
// shutdownSrv3 shuts down srv and logs the error, if any. l must not be nil.
//
// TODO(a.garipov): Think of a good way to merge with [shutdownSrv].
func shutdownSrv3(srv *http3.Server) {
defer log.OnPanic("")
func shutdownSrv3(ctx context.Context, l *slog.Logger, srv *http3.Server) {
defer slogutil.RecoverAndLog(ctx, l)
if srv == nil {
return
@@ -363,12 +379,12 @@ func shutdownSrv3(srv *http3.Server) {
return
}
const msgFmt = "shutting down http/3 server %q: %s"
if errors.Is(err, context.Canceled) {
log.Debug(msgFmt, srv.Addr, err)
} else {
log.Error(msgFmt, srv.Addr, err)
lvl := slog.LevelDebug
if !errors.Is(err, context.Canceled) {
lvl = slog.LevelError
}
l.Log(ctx, lvl, "shutting down http/3 server", "addr", srv.Addr, slogutil.KeyError, err)
}
// PasswordMinRunes is the minimum length of user's password in runes.
@@ -436,7 +452,7 @@ func (web *webAPI) handleInstallConfigure(w http.ResponseWriter, r *http.Request
// moment we'll allow setting up TLS in the initial configuration or the
// configuration itself will use HTTPS protocol, because the underlying
// functions potentially restart the HTTPS server.
err = startMods(web.logger)
err = startMods(web.baseLogger)
if err != nil {
Context.firstRun = true
copyInstallSettings(config, curConfig)
@@ -472,12 +488,11 @@ func (web *webAPI) handleInstallConfigure(w http.ResponseWriter, r *http.Request
// and with its own context, because it waits until all requests are handled
// and will be blocked by it's own caller.
go func(timeout time.Duration) {
defer log.OnPanic("web")
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer slogutil.RecoverAndLog(ctx, web.logger)
defer cancel()
shutdownSrv(ctx, web.httpServer)
shutdownSrv(ctx, web.logger, web.httpServer)
}(shutdownTimeout)
}

View File

@@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"fmt"
"log/slog"
"net/http"
"os"
"os/exec"
@@ -16,7 +17,8 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
"github.com/AdguardTeam/AdGuardHome/internal/updater"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/osutil"
)
// temporaryError is the interface for temporary errors from the Go standard
@@ -52,7 +54,7 @@ func (web *webAPI) handleVersionJSON(w http.ResponseWriter, r *http.Request) {
}
}
err = web.requestVersionInfo(resp, req.Recheck)
err = web.requestVersionInfo(r.Context(), resp, req.Recheck)
if err != nil {
// Don't wrap the error, because it's informative enough as is.
aghhttp.Error(r, w, http.StatusBadGateway, "%s", err)
@@ -73,32 +75,39 @@ func (web *webAPI) handleVersionJSON(w http.ResponseWriter, r *http.Request) {
// requestVersionInfo sets the VersionInfo field of resp if it can reach the
// update server.
func (web *webAPI) requestVersionInfo(resp *versionResponse, recheck bool) (err error) {
func (web *webAPI) requestVersionInfo(
ctx context.Context,
resp *versionResponse,
recheck bool,
) (err error) {
updater := web.conf.updater
for i := 0; i != 3; i++ {
for range 3 {
resp.VersionInfo, err = updater.VersionInfo(recheck)
if err != nil {
var terr temporaryError
if errors.As(err, &terr) && terr.Temporary() {
// Temporary network error. This case may happen while we're
// restarting our DNS server. Log and sleep for some time.
//
// See https://github.com/AdguardTeam/AdGuardHome/issues/934.
d := time.Duration(i) * time.Second
log.Info("update: temp net error: %q; sleeping for %s and retrying", err, d)
time.Sleep(d)
if err == nil {
return nil
}
continue
}
var terr temporaryError
if errors.As(err, &terr) && terr.Temporary() {
// Temporary network error. This case may happen while we're
// restarting our DNS server. Log and sleep for some time.
//
// See https://github.com/AdguardTeam/AdGuardHome/issues/934.
const sleepTime = 2 * time.Second
err = fmt.Errorf("temp net error: %w; sleeping for %s and retrying", err, sleepTime)
web.logger.InfoContext(ctx, "updating version info", slogutil.KeyError, err)
time.Sleep(sleepTime)
continue
}
break
}
if err != nil {
vcu := updater.VersionCheckURL()
return fmt.Errorf("getting version info from %s: %w", vcu, err)
return fmt.Errorf("getting version info: %w", err)
}
return nil
@@ -139,7 +148,7 @@ func (web *webAPI) handleUpdate(w http.ResponseWriter, r *http.Request) {
// The background context is used because the underlying functions wrap it
// with timeout and shut down the server, which handles current request. It
// also should be done in a separate goroutine for the same reason.
go finishUpdate(context.Background(), execPath, web.conf.runningAsService)
go finishUpdate(context.Background(), web.logger, execPath, web.conf.runningAsService)
}
// versionResponse is the response for /control/version.json endpoint.
@@ -179,15 +188,17 @@ func tlsConfUsesPrivilegedPorts(c *tlsConfigSettings) (ok bool) {
return c.Enabled && (c.PortHTTPS < 1024 || c.PortDNSOverTLS < 1024 || c.PortDNSOverQUIC < 1024)
}
// finishUpdate completes an update procedure.
func finishUpdate(ctx context.Context, execPath string, runningAsService bool) {
var err error
// finishUpdate completes an update procedure. It is intended to be used as a
// goroutine.
func finishUpdate(ctx context.Context, l *slog.Logger, execPath string, runningAsService bool) {
defer slogutil.RecoverAndExit(ctx, l, osutil.ExitCodeFailure)
log.Info("stopping all tasks")
l.InfoContext(ctx, "stopping all tasks")
cleanup(ctx)
cleanupAlways()
var err error
if runtime.GOOS == "windows" {
if runningAsService {
// NOTE: We can't restart the service via "kardianos/service"
@@ -198,28 +209,28 @@ func finishUpdate(ctx context.Context, execPath string, runningAsService bool) {
cmd := exec.Command("cmd", "/c", "net stop AdGuardHome & net start AdGuardHome")
err = cmd.Start()
if err != nil {
log.Fatalf("restarting: stopping: %s", err)
panic(fmt.Errorf("restarting service: %w", err))
}
os.Exit(0)
os.Exit(osutil.ExitCodeSuccess)
}
cmd := exec.Command(execPath, os.Args[1:]...)
log.Info("restarting: %q %q", execPath, os.Args[1:])
l.InfoContext(ctx, "restarting", "exec_path", execPath, "args", os.Args[1:])
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Start()
if err != nil {
log.Fatalf("restarting:: %s", err)
panic(fmt.Errorf("restarting: %w", err))
}
os.Exit(0)
os.Exit(osutil.ExitCodeSuccess)
}
log.Info("restarting: %q %q", execPath, os.Args[1:])
l.InfoContext(ctx, "restarting", "exec_path", execPath, "args", os.Args[1:])
err = syscall.Exec(execPath, os.Args, os.Environ())
if err != nil {
log.Fatalf("restarting: %s", err)
panic(fmt.Errorf("restarting: %w", err))
}
}

View File

@@ -23,6 +23,7 @@ import (
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/netutil/urlutil"
"github.com/ameshkov/dnscrypt/v2"
yaml "gopkg.in/yaml.v3"
)
@@ -46,14 +47,14 @@ func onConfigModified() {
// initDNS updates all the fields of the [Context] needed to initialize the DNS
// server and initializes it at last. It also must not be called unless
// [config] and [Context] are initialized. l must not be nil.
func initDNS(l *slog.Logger, statsDir, querylogDir string) (err error) {
// [config] and [Context] are initialized. baseLogger must not be nil.
func initDNS(baseLogger *slog.Logger, statsDir, querylogDir string) (err error) {
anonymizer := config.anonymizer()
statsConf := stats.Config{
Logger: l.With(slogutil.KeyPrefix, "stats"),
Logger: baseLogger.With(slogutil.KeyPrefix, "stats"),
Filename: filepath.Join(statsDir, "stats.db"),
Limit: config.Stats.Interval.Duration,
Limit: time.Duration(config.Stats.Interval),
ConfigModified: onConfigModified,
HTTPRegister: httpRegister,
Enabled: config.Stats.Enabled,
@@ -72,13 +73,14 @@ func initDNS(l *slog.Logger, statsDir, querylogDir string) (err error) {
}
conf := querylog.Config{
Logger: baseLogger.With(slogutil.KeyPrefix, "querylog"),
Anonymizer: anonymizer,
ConfigModified: onConfigModified,
HTTPRegister: httpRegister,
FindClient: Context.clients.findMultiple,
BaseDir: querylogDir,
AnonymizeClientIP: config.DNS.AnonymizeClientIP,
RotationIvl: config.QueryLog.Interval.Duration,
RotationIvl: time.Duration(config.QueryLog.Interval),
MemSize: config.QueryLog.MemSize,
Enabled: config.QueryLog.Enabled,
FileEnabled: config.QueryLog.FileEnabled,
@@ -112,7 +114,7 @@ func initDNS(l *slog.Logger, statsDir, querylogDir string) (err error) {
anonymizer,
httpRegister,
tlsConf,
l,
baseLogger,
)
}
@@ -241,7 +243,7 @@ func newServerConfig(
Config: fwdConf,
TLSConfig: newDNSTLSConfig(tlsConf, hosts),
TLSAllowUnencryptedDoH: tlsConf.AllowUnencryptedDoH,
UpstreamTimeout: dnsConf.UpstreamTimeout.Duration,
UpstreamTimeout: time.Duration(dnsConf.UpstreamTimeout),
TLSv12Roots: Context.tlsRoots,
ConfigModified: onConfigModified,
HTTPRegister: httpReg,
@@ -371,7 +373,7 @@ func getDNSEncryption() (de dnsEncryption) {
}
de.https = (&url.URL{
Scheme: "https",
Scheme: urlutil.SchemeHTTPS,
Host: addr,
Path: "/dns-query",
}).String()
@@ -456,7 +458,8 @@ func startDNSServer() error {
Context.filters.EnableFilters(false)
// TODO(s.chzhen): Pass context.
err := Context.clients.Start(context.TODO())
ctx := context.TODO()
err := Context.clients.Start(ctx)
if err != nil {
return fmt.Errorf("starting clients container: %w", err)
}
@@ -468,7 +471,11 @@ func startDNSServer() error {
Context.filters.Start()
Context.stats.Start()
Context.queryLog.Start()
err = Context.queryLog.Start(ctx)
if err != nil {
return fmt.Errorf("starting query log: %w", err)
}
return nil
}
@@ -524,12 +531,16 @@ func closeDNSServer() {
if Context.stats != nil {
err := Context.stats.Close()
if err != nil {
log.Debug("closing stats: %s", err)
log.Error("closing stats: %s", err)
}
}
if Context.queryLog != nil {
Context.queryLog.Close()
// TODO(s.chzhen): Pass context.
err := Context.queryLog.Shutdown(context.TODO())
if err != nil {
log.Error("closing query log: %s", err)
}
}
log.Debug("all dns modules are closed")

View File

@@ -7,6 +7,8 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/client"
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/AdGuardHome/internal/schedule"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/testutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -18,12 +20,15 @@ var testIPv4 = netip.AddrFrom4([4]byte{1, 2, 3, 4})
func newStorage(tb testing.TB, clients []*client.Persistent) (s *client.Storage) {
tb.Helper()
s, err := client.NewStorage(&client.StorageConfig{})
ctx := testutil.ContextWithTimeout(tb, testTimeout)
s, err := client.NewStorage(ctx, &client.StorageConfig{
Logger: slogutil.NewDiscardLogger(),
})
require.NoError(tb, err)
for _, p := range clients {
p.UID = client.MustNewUID()
require.NoError(tb, s.Add(p))
require.NoError(tb, s.Add(ctx, p))
}
return s

View File

@@ -12,7 +12,6 @@ import (
"net/url"
"os"
"os/signal"
"path"
"path/filepath"
"runtime"
"slices"
@@ -21,7 +20,6 @@ import (
"time"
"github.com/AdguardTeam/AdGuardHome/internal/aghalg"
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
"github.com/AdguardTeam/AdGuardHome/internal/aghos"
"github.com/AdguardTeam/AdGuardHome/internal/aghtls"
@@ -42,6 +40,7 @@ import (
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/netutil/urlutil"
"github.com/AdguardTeam/golibs/osutil"
)
@@ -115,15 +114,16 @@ func Main(clientBuildFS fs.FS) {
signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP, syscall.SIGQUIT)
go func() {
ctx := context.Background()
for {
sig := <-signals
log.Info("Received signal %q", sig)
switch sig {
case syscall.SIGHUP:
Context.clients.storage.ReloadARP()
Context.clients.storage.ReloadARP(ctx)
Context.tls.reload()
default:
cleanup(context.Background())
cleanup(ctx)
cleanupAlways()
close(done)
}
@@ -148,9 +148,17 @@ func setupContext(opts options) (err error) {
Context.tlsRoots = aghtls.SystemRootCAs()
Context.mux = http.NewServeMux()
if !opts.noEtcHosts {
err = setupHostsContainer()
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return err
}
}
if Context.firstRun {
log.Info("This is the first time AdGuard Home is launched")
checkPermissions()
checkNetworkPermissions()
return nil
}
@@ -159,21 +167,13 @@ func setupContext(opts options) (err error) {
if err != nil {
log.Error("parsing configuration file: %s", err)
os.Exit(1)
os.Exit(osutil.ExitCodeFailure)
}
if opts.checkConfig {
log.Info("configuration file is ok")
os.Exit(0)
}
if !opts.noEtcHosts {
err = setupHostsContainer()
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return err
}
os.Exit(osutil.ExitCodeSuccess)
}
return nil
@@ -278,8 +278,8 @@ func setupOpts(opts options) (err error) {
}
// initContextClients initializes Context clients and related fields.
func initContextClients(logger *slog.Logger) (err error) {
err = setupDNSFilteringConf(config.Filtering)
func initContextClients(ctx context.Context, logger *slog.Logger) (err error) {
err = setupDNSFilteringConf(ctx, logger, config.Filtering)
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return err
@@ -306,6 +306,8 @@ func initContextClients(logger *slog.Logger) (err error) {
}
return Context.clients.Init(
ctx,
logger,
config.Clients.Persistent,
Context.dhcpServer,
Context.etcHosts,
@@ -355,7 +357,11 @@ func setupBindOpts(opts options) (err error) {
}
// setupDNSFilteringConf sets up DNS filtering configuration settings.
func setupDNSFilteringConf(conf *filtering.Config) (err error) {
func setupDNSFilteringConf(
ctx context.Context,
baseLogger *slog.Logger,
conf *filtering.Config,
) (err error) {
const (
dnsTimeout = 3 * time.Second
@@ -446,12 +452,13 @@ func setupDNSFilteringConf(conf *filtering.Config) (err error) {
conf.ParentalBlockHost = host
}
conf.SafeSearch, err = safesearch.NewDefault(
conf.SafeSearchConf,
"default",
conf.SafeSearchCacheSize,
cacheTime,
)
logger := baseLogger.With(slogutil.KeyPrefix, safesearch.LogPrefix)
conf.SafeSearch, err = safesearch.NewDefault(ctx, &safesearch.DefaultConfig{
Logger: logger,
ServicesConfig: conf.SafeSearchConf,
CacheSize: conf.SafeSearchCacheSize,
CacheTTL: cacheTime,
})
if err != nil {
return fmt.Errorf("initializing safesearch: %w", err)
}
@@ -487,15 +494,48 @@ func checkPorts() (err error) {
return nil
}
// isUpdateEnabled returns true if the update is enabled for current
// configuration. It also logs the decision. customURL should be true if the
// updater is using a custom URL.
func isUpdateEnabled(ctx context.Context, l *slog.Logger, opts *options, customURL bool) (ok bool) {
if opts.disableUpdate {
l.DebugContext(ctx, "updates are disabled by command-line option")
return false
}
switch version.Channel() {
case
version.ChannelDevelopment,
version.ChannelCandidate:
if customURL {
l.DebugContext(ctx, "updates are enabled because custom url is used")
} else {
l.DebugContext(ctx, "updates are disabled for development and candidate builds")
}
return customURL
default:
l.DebugContext(ctx, "updates are enabled")
return true
}
}
// initWeb initializes the web module. upd and baseLogger must not be nil.
func initWeb(
ctx context.Context,
opts options,
clientBuildFS fs.FS,
upd *updater.Updater,
l *slog.Logger,
baseLogger *slog.Logger,
customURL bool,
) (web *webAPI, err error) {
logger := baseLogger.With(slogutil.KeyPrefix, "webapi")
var clientFS fs.FS
if opts.localFrontend {
log.Info("warning: using local frontend files")
logger.WarnContext(ctx, "using local frontend files")
clientFS = os.DirFS("build/static")
} else {
@@ -505,20 +545,12 @@ func initWeb(
}
}
disableUpdate := opts.disableUpdate
switch version.Channel() {
case
version.ChannelDevelopment,
version.ChannelCandidate:
disableUpdate = true
}
if disableUpdate {
log.Info("AdGuard Home updates are disabled")
}
disableUpdate := !isUpdateEnabled(ctx, baseLogger, &opts, customURL)
webConf := &webConfig{
updater: upd,
updater: upd,
logger: logger,
baseLogger: baseLogger,
clientFS: clientFS,
@@ -534,9 +566,9 @@ func initWeb(
serveHTTP3: config.DNS.ServeHTTP3,
}
web = newWebAPI(webConf, l)
web = newWebAPI(ctx, webConf)
if web == nil {
return nil, fmt.Errorf("initializing web: %w", err)
return nil, errors.Error("can not initialize web")
}
return web, nil
@@ -549,6 +581,8 @@ func fatalOnError(err error) {
}
// run configures and starts AdGuard Home.
//
// TODO(e.burkov): Make opts a pointer.
func run(opts options, clientBuildFS fs.FS, done chan struct{}) {
// Configure working dir.
err := initWorkingDir(opts)
@@ -584,7 +618,10 @@ func run(opts options, clientBuildFS fs.FS, done chan struct{}) {
// data first, but also to avoid relying on automatic Go init() function.
filtering.InitModule()
err = initContextClients(slogLogger)
// TODO(s.chzhen): Use it for the entire initialization process.
ctx := context.Background()
err = initContextClients(ctx, slogLogger)
fatalOnError(err)
err = setupOpts(opts)
@@ -593,33 +630,13 @@ func run(opts options, clientBuildFS fs.FS, done chan struct{}) {
execPath, err := os.Executable()
fatalOnError(errors.Annotate(err, "getting executable path: %w"))
u := &url.URL{
Scheme: "https",
// TODO(a.garipov): Make configurable.
Host: "static.adtidy.org",
Path: path.Join("adguardhome", version.Channel(), "version.json"),
}
confPath := configFilePath()
log.Debug("using config path %q for updater", confPath)
upd := updater.NewUpdater(&updater.Config{
Client: config.Filtering.HTTPClient,
Version: version.Version(),
Channel: version.Channel(),
GOARCH: runtime.GOARCH,
GOOS: runtime.GOOS,
GOARM: version.GOARM(),
GOMIPS: version.GOMIPS(),
WorkDir: Context.workDir,
ConfName: confPath,
ExecPath: execPath,
VersionCheckURL: u.String(),
})
upd, customURL := newUpdater(ctx, slogLogger, Context.workDir, confPath, execPath, config)
// TODO(e.burkov): This could be made earlier, probably as the option's
// effect.
cmdlineUpdate(opts, upd, slogLogger)
cmdlineUpdate(ctx, slogLogger, opts, upd)
if !Context.firstRun {
// Save the updated config.
@@ -627,7 +644,7 @@ func run(opts options, clientBuildFS fs.FS, done chan struct{}) {
fatalOnError(err)
if config.HTTPConfig.Pprof.Enabled {
startPprof(config.HTTPConfig.Pprof.Port)
startPprof(slogLogger, config.HTTPConfig.Pprof.Port)
}
}
@@ -647,7 +664,7 @@ func run(opts options, clientBuildFS fs.FS, done chan struct{}) {
onConfigModified()
}
Context.web, err = initWeb(opts, clientBuildFS, upd, slogLogger)
Context.web, err = initWeb(ctx, opts, clientBuildFS, upd, slogLogger, customURL)
fatalOnError(err)
statsDir, querylogDir, err := checkStatsAndQuerylogDirs(&Context, config)
@@ -675,18 +692,87 @@ func run(opts options, clientBuildFS fs.FS, done chan struct{}) {
}
}
if permcheck.NeedsMigration(confPath) {
permcheck.Migrate(Context.workDir, dataDir, statsDir, querylogDir, confPath)
if !opts.noPermCheck {
checkPermissions(ctx, slogLogger, Context.workDir, confPath, dataDir, statsDir, querylogDir)
}
permcheck.Check(Context.workDir, dataDir, statsDir, querylogDir, confPath)
Context.web.start()
Context.web.start(ctx)
// Wait for other goroutines to complete their job.
<-done
}
// newUpdater creates a new AdGuard Home updater. customURL is true if the user
// has specified a custom version announcement URL.
func newUpdater(
ctx context.Context,
l *slog.Logger,
workDir string,
confPath string,
execPath string,
config *configuration,
) (upd *updater.Updater, customURL bool) {
// envName is the name of the environment variable that can be used to
// override the default version check URL.
const envName = "ADGUARD_HOME_TEST_UPDATE_VERSION_URL"
customURLStr := os.Getenv(envName)
var versionURL *url.URL
switch {
case version.Channel() == version.ChannelRelease:
// Only enable custom version URL for development builds.
l.DebugContext(ctx, "custom version url is disabled for release builds")
case !config.UnsafeUseCustomUpdateIndexURL:
l.DebugContext(ctx, "custom version url is disabled in config")
default:
versionURL, _ = url.Parse(customURLStr)
}
err := urlutil.ValidateHTTPURL(versionURL)
if customURL = err == nil; !customURL {
l.DebugContext(ctx, "parsing custom version url", slogutil.KeyError, err)
versionURL = updater.DefaultVersionURL()
}
l.DebugContext(ctx, "creating updater", "config_path", confPath)
return updater.NewUpdater(&updater.Config{
Client: config.Filtering.HTTPClient,
Version: version.Version(),
Channel: version.Channel(),
GOARCH: runtime.GOARCH,
GOOS: runtime.GOOS,
GOARM: version.GOARM(),
GOMIPS: version.GOMIPS(),
WorkDir: workDir,
ConfName: confPath,
ExecPath: execPath,
VersionCheckURL: versionURL,
}), customURL
}
// checkPermissions checks and migrates permissions of the files and directories
// used by AdGuard Home, if needed.
func checkPermissions(
ctx context.Context,
baseLogger *slog.Logger,
workDir string,
confPath string,
dataDir string,
statsDir string,
querylogDir string,
) {
l := baseLogger.With(slogutil.KeyPrefix, "permcheck")
if permcheck.NeedsMigration(ctx, l, workDir, confPath) {
permcheck.Migrate(ctx, l, workDir, dataDir, statsDir, querylogDir, confPath)
}
permcheck.Check(ctx, l, workDir, dataDir, statsDir, querylogDir, confPath)
}
// initUsers initializes context auth module. Clears config users field.
func initUsers() (auth *Auth, err error) {
sessFilename := filepath.Join(Context.getDataDir(), "sessions.db")
@@ -701,7 +787,7 @@ func initUsers() (auth *Auth, err error) {
trustedProxies := netutil.SliceSubnetSet(netutil.UnembedPrefixes(config.DNS.TrustedProxies))
sessionTTL := config.HTTPConfig.SessionTTL.Seconds()
sessionTTL := time.Duration(config.HTTPConfig.SessionTTL).Seconds()
auth = InitAuth(sessFilename, config.Users, uint32(sessionTTL), rateLimiter, trustedProxies)
if auth == nil {
return nil, errors.Error("initializing auth module failed")
@@ -721,15 +807,15 @@ func (c *configuration) anonymizer() (ipmut *aghnet.IPMut) {
return aghnet.NewIPMut(anonFunc)
}
// startMods initializes and starts the DNS server after installation. l must
// not be nil.
func startMods(l *slog.Logger) (err error) {
// startMods initializes and starts the DNS server after installation.
// baseLogger must not be nil.
func startMods(baseLogger *slog.Logger) (err error) {
statsDir, querylogDir, err := checkStatsAndQuerylogDirs(&Context, config)
if err != nil {
return err
}
err = initDNS(l, statsDir, querylogDir)
err = initDNS(baseLogger, statsDir, querylogDir)
if err != nil {
return err
}
@@ -746,8 +832,9 @@ func startMods(l *slog.Logger) (err error) {
return nil
}
// Check if the current user permissions are enough to run AdGuard Home
func checkPermissions() {
// checkNetworkPermissions checks if the current user permissions are enough to
// use the required networking functionality.
func checkNetworkPermissions() {
log.Info("Checking if AdGuard Home has necessary permissions")
if ok, err := aghnet.CanBindPrivilegedPorts(); !ok || err != nil {
@@ -901,7 +988,7 @@ func loadCmdLineOpts() (opts options) {
exitWithError()
}
os.Exit(0)
os.Exit(osutil.ExitCodeSuccess)
}
return opts
@@ -925,12 +1012,12 @@ func printHTTPAddresses(proto string) {
}
port := config.HTTPConfig.Address.Port()
if proto == aghhttp.SchemeHTTPS {
if proto == urlutil.SchemeHTTPS {
port = tlsConf.PortHTTPS
}
// TODO(e.burkov): Inspect and perhaps merge with the previous condition.
if proto == aghhttp.SchemeHTTPS && tlsConf.ServerName != "" {
if proto == urlutil.SchemeHTTPS && tlsConf.ServerName != "" {
printWebAddrs(proto, tlsConf.ServerName, tlsConf.PortHTTPS)
return
@@ -990,7 +1077,7 @@ type jsonError struct {
}
// cmdlineUpdate updates current application and exits. l must not be nil.
func cmdlineUpdate(opts options, upd *updater.Updater, l *slog.Logger) {
func cmdlineUpdate(ctx context.Context, l *slog.Logger, opts options, upd *updater.Updater) {
if !opts.performUpdate {
return
}
@@ -1003,20 +1090,19 @@ func cmdlineUpdate(opts options, upd *updater.Updater, l *slog.Logger) {
err := initDNSServer(nil, nil, nil, nil, nil, nil, &tlsConfigSettings{}, l)
fatalOnError(err)
log.Info("cmdline update: performing update")
l.InfoContext(ctx, "performing update via cli")
info, err := upd.VersionInfo(true)
if err != nil {
vcu := upd.VersionCheckURL()
log.Error("getting version info from %s: %s", vcu, err)
l.ErrorContext(ctx, "getting version info", slogutil.KeyError, err)
os.Exit(1)
os.Exit(osutil.ExitCodeFailure)
}
if info.NewVersion == version.Version() {
log.Info("no updates available")
l.InfoContext(ctx, "no updates available")
os.Exit(0)
os.Exit(osutil.ExitCodeSuccess)
}
err = upd.Update(Context.firstRun)
@@ -1024,10 +1110,10 @@ func cmdlineUpdate(opts options, upd *updater.Updater, l *slog.Logger) {
err = restartService()
if err != nil {
log.Debug("restarting service: %s", err)
log.Info("AdGuard Home was not installed as a service. " +
l.DebugContext(ctx, "restarting service", slogutil.KeyError, err)
l.InfoContext(ctx, "AdGuard Home was not installed as a service. "+
"Please restart running instances of AdGuardHome manually.")
}
os.Exit(0)
os.Exit(osutil.ExitCodeSuccess)
}

View File

@@ -24,10 +24,15 @@ func newSlogLogger(ls *logSettings) (l *slog.Logger) {
return slogutil.NewDiscardLogger()
}
lvl := slog.LevelInfo
if ls.Verbose {
lvl = slog.LevelDebug
}
return slogutil.New(&slogutil.Config{
Format: slogutil.FormatAdGuardLegacy,
Level: lvl,
AddTimestamp: true,
Verbose: ls.Verbose,
})
}

View File

@@ -8,11 +8,11 @@ import (
"net/url"
"path"
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
"github.com/AdguardTeam/AdGuardHome/internal/dnsforward"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/httphdr"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/netutil/urlutil"
"github.com/google/uuid"
"howett.net/plist"
)
@@ -84,7 +84,7 @@ func encodeMobileConfig(d *dnsSettings, clientID string) ([]byte, error) {
case dnsProtoHTTPS:
dspName = fmt.Sprintf("%s DoH", d.ServerName)
u := &url.URL{
Scheme: aghhttp.SchemeHTTPS,
Scheme: urlutil.SchemeHTTPS,
Host: d.ServerName,
Path: path.Join("/dns-query", clientID),
}

View File

@@ -10,6 +10,7 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/configmigrate"
"github.com/AdguardTeam/AdGuardHome/internal/version"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/osutil"
"github.com/AdguardTeam/golibs/stringutil"
)
@@ -78,6 +79,10 @@ type options struct {
// localFrontend forces AdGuard Home to use the frontend files from disk
// rather than the ones that have been compiled into the binary.
localFrontend bool
// noPermCheck disables checking and migration of permissions for the
// security-sensitive files.
noPermCheck bool
}
// initCmdLineOpts completes initialization of the global command-line option
@@ -305,6 +310,15 @@ var cmdLineOpts = []cmdLineOpt{{
description: "Run in GL-Inet compatibility mode.",
longName: "glinet",
shortName: "",
}, {
updateWithValue: nil,
updateNoValue: func(o options) (options, error) { o.noPermCheck = true; return o, nil },
effect: nil,
serialize: func(o options) (val string, ok bool) { return "", o.noPermCheck },
description: "Skip checking and migration of permissions " +
"of security-sensitive files.",
longName: "no-permcheck",
shortName: "",
}, {
updateWithValue: nil,
updateNoValue: nil,
@@ -316,7 +330,7 @@ var cmdLineOpts = []cmdLineOpt{{
fmt.Println(version.Full())
}
os.Exit(0)
os.Exit(osutil.ExitCodeSuccess)
return nil
}, nil

View File

@@ -10,11 +10,11 @@ import (
"syscall"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
"github.com/AdguardTeam/AdGuardHome/internal/aghos"
"github.com/AdguardTeam/AdGuardHome/internal/version"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/netutil/urlutil"
"github.com/kardianos/service"
)
@@ -336,7 +336,7 @@ AdGuard Home is successfully installed and will automatically start on boot.
There are a few more things that must be configured before you can use it.
Click on the link below and follow the Installation Wizard steps to finish setup.
AdGuard Home is now available at the following addresses:`)
printHTTPAddresses(aghhttp.SchemeHTTP)
printHTTPAddresses(urlutil.SchemeHTTP)
}
}

View File

@@ -3,6 +3,7 @@ package home
import (
"context"
"crypto/tls"
"fmt"
"io/fs"
"log/slog"
"net/http"
@@ -11,13 +12,15 @@ import (
"sync"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
"github.com/AdguardTeam/AdGuardHome/internal/updater"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/netutil/httputil"
"github.com/AdguardTeam/golibs/netutil/urlutil"
"github.com/AdguardTeam/golibs/osutil"
"github.com/NYTimes/gziphandler"
"github.com/quic-go/quic-go/http3"
"golang.org/x/net/http2"
@@ -39,6 +42,13 @@ const (
type webConfig struct {
updater *updater.Updater
// logger is a slog logger used in webAPI. It must not be nil.
logger *slog.Logger
// baseLogger is used to create loggers for other entities. It must not be
// nil.
baseLogger *slog.Logger
clientFS fs.FS
// BindAddr is the binding address with port for plain HTTP web interface.
@@ -94,19 +104,26 @@ type webAPI struct {
// logger is a slog logger used in webAPI. It must not be nil.
logger *slog.Logger
// baseLogger is used to create loggers for other entities. It must not be
// nil.
baseLogger *slog.Logger
// httpsServer is the server that handles HTTPS traffic. If it is not nil,
// [Web.http3Server] must also not be nil.
httpsServer httpsServer
}
// newWebAPI creates a new instance of the web UI and API server. l must not be
// nil.
func newWebAPI(conf *webConfig, l *slog.Logger) (w *webAPI) {
log.Info("web: initializing")
// newWebAPI creates a new instance of the web UI and API server. conf must be
// valid.
//
// TODO(a.garipov): Return a proper error.
func newWebAPI(ctx context.Context, conf *webConfig) (w *webAPI) {
conf.logger.InfoContext(ctx, "initializing")
w = &webAPI{
conf: conf,
logger: l,
conf: conf,
logger: conf.logger,
baseLogger: conf.baseLogger,
}
clientFS := http.FileServer(http.FS(conf.clientFS))
@@ -116,7 +133,11 @@ func newWebAPI(conf *webConfig, l *slog.Logger) (w *webAPI) {
// add handlers for /install paths, we only need them when we're not configured yet
if conf.firstRun {
log.Info("This is the first launch of AdGuard Home, redirecting everything to /install.html ")
conf.logger.InfoContext(
ctx,
"This is the first launch of AdGuard Home, redirecting everything to /install.html",
)
Context.mux.Handle("/install.html", preInstallHandler(clientFS))
w.registerInstallHandlers()
} else {
@@ -152,7 +173,9 @@ func webCheckPortAvailable(port uint16) (ok bool) {
// tlsConfigChanged updates the TLS configuration and restarts the HTTPS server
// if necessary.
func (web *webAPI) tlsConfigChanged(ctx context.Context, tlsConf tlsConfigSettings) {
log.Debug("web: applying new tls configuration")
defer slogutil.RecoverAndExit(ctx, web.logger, osutil.ExitCodeFailure)
web.logger.DebugContext(ctx, "applying new tls configuration")
enabled := tlsConf.Enabled &&
tlsConf.PortHTTPS != 0 &&
@@ -163,7 +186,7 @@ func (web *webAPI) tlsConfigChanged(ctx context.Context, tlsConf tlsConfigSettin
if enabled {
cert, err = tls.X509KeyPair(tlsConf.CertificateChainData, tlsConf.PrivateKeyData)
if err != nil {
log.Fatal(err)
panic(err)
}
}
@@ -171,8 +194,8 @@ func (web *webAPI) tlsConfigChanged(ctx context.Context, tlsConf tlsConfigSettin
if web.httpsServer.server != nil {
var cancel context.CancelFunc
ctx, cancel = context.WithTimeout(ctx, shutdownTimeout)
shutdownSrv(ctx, web.httpsServer.server)
shutdownSrv3(web.httpsServer.server3)
shutdownSrv(ctx, web.logger, web.httpsServer.server)
shutdownSrv3(ctx, web.logger, web.httpsServer.server3)
cancel()
}
@@ -183,32 +206,39 @@ func (web *webAPI) tlsConfigChanged(ctx context.Context, tlsConf tlsConfigSettin
web.httpsServer.cond.L.Unlock()
}
// loggerKeyServer is the key used by [webAPI] to identify servers.
const loggerKeyServer = "server"
// start - start serving HTTP requests
func (web *webAPI) start() {
log.Println("AdGuard Home is available at the following addresses:")
func (web *webAPI) start(ctx context.Context) {
defer slogutil.RecoverAndExit(ctx, web.logger, osutil.ExitCodeFailure)
web.logger.InfoContext(ctx, "AdGuard Home is available at the following addresses:")
// for https, we have a separate goroutine loop
go web.tlsServerLoop()
go web.tlsServerLoop(ctx)
// this loop is used as an ability to change listening host and/or port
for !web.httpsServer.inShutdown {
printHTTPAddresses(aghhttp.SchemeHTTP)
printHTTPAddresses(urlutil.SchemeHTTP)
errs := make(chan error, 2)
// Use an h2c handler to support unencrypted HTTP/2, e.g. for proxies.
hdlr := h2c.NewHandler(withMiddlewares(Context.mux, limitRequestBody), &http2.Server{})
logger := web.baseLogger.With(loggerKeyServer, "plain")
// Create a new instance, because the Web is not usable after Shutdown.
web.httpServer = &http.Server{
ErrorLog: log.StdLog("web: plain", log.DEBUG),
Addr: web.conf.BindAddr.String(),
Handler: hdlr,
ReadTimeout: web.conf.ReadTimeout,
ReadHeaderTimeout: web.conf.ReadHeaderTimeout,
WriteTimeout: web.conf.WriteTimeout,
ErrorLog: slog.NewLogLogger(logger.Handler(), slog.LevelError),
}
go func() {
defer log.OnPanic("web: plain")
defer slogutil.RecoverAndLog(ctx, web.logger)
errs <- web.httpServer.ListenAndServe()
}()
@@ -216,7 +246,7 @@ func (web *webAPI) start() {
err := <-errs
if !errors.Is(err, http.ErrServerClosed) {
cleanupAlways()
log.Fatal(err)
panic(err)
}
// We use ErrServerClosed as a sign that we need to rebind on a new
@@ -226,7 +256,7 @@ func (web *webAPI) start() {
// close gracefully shuts down the HTTP servers.
func (web *webAPI) close(ctx context.Context) {
log.Info("stopping http server...")
web.logger.InfoContext(ctx, "stopping http server")
web.httpsServer.cond.L.Lock()
web.httpsServer.inShutdown = true
@@ -236,14 +266,16 @@ func (web *webAPI) close(ctx context.Context) {
ctx, cancel = context.WithTimeout(ctx, shutdownTimeout)
defer cancel()
shutdownSrv(ctx, web.httpsServer.server)
shutdownSrv3(web.httpsServer.server3)
shutdownSrv(ctx, web.httpServer)
shutdownSrv(ctx, web.logger, web.httpsServer.server)
shutdownSrv3(ctx, web.logger, web.httpsServer.server3)
shutdownSrv(ctx, web.logger, web.httpServer)
log.Info("stopped http server")
web.logger.InfoContext(ctx, "stopped http server")
}
func (web *webAPI) tlsServerLoop() {
func (web *webAPI) tlsServerLoop(ctx context.Context) {
defer slogutil.RecoverAndExit(ctx, web.logger, osutil.ExitCodeFailure)
for {
web.httpsServer.cond.L.Lock()
if web.httpsServer.inShutdown {
@@ -271,38 +303,40 @@ func (web *webAPI) tlsServerLoop() {
}()
addr := netip.AddrPortFrom(web.conf.BindAddr.Addr(), portHTTPS).String()
logger := web.baseLogger.With(loggerKeyServer, "https")
web.httpsServer.server = &http.Server{
ErrorLog: log.StdLog("web: https", log.DEBUG),
Addr: addr,
Addr: addr,
Handler: withMiddlewares(Context.mux, limitRequestBody),
TLSConfig: &tls.Config{
Certificates: []tls.Certificate{web.httpsServer.cert},
RootCAs: Context.tlsRoots,
CipherSuites: Context.tlsCipherIDs,
MinVersion: tls.VersionTLS12,
},
Handler: withMiddlewares(Context.mux, limitRequestBody),
ReadTimeout: web.conf.ReadTimeout,
ReadHeaderTimeout: web.conf.ReadHeaderTimeout,
WriteTimeout: web.conf.WriteTimeout,
ErrorLog: slog.NewLogLogger(logger.Handler(), slog.LevelError),
}
printHTTPAddresses(aghhttp.SchemeHTTPS)
printHTTPAddresses(urlutil.SchemeHTTPS)
if web.conf.serveHTTP3 {
go web.mustStartHTTP3(addr)
go web.mustStartHTTP3(ctx, addr)
}
log.Debug("web: starting https server")
web.logger.DebugContext(ctx, "starting https server")
err := web.httpsServer.server.ListenAndServeTLS("", "")
if !errors.Is(err, http.ErrServerClosed) {
cleanupAlways()
log.Fatalf("web: https: %s", err)
panic(fmt.Errorf("https: %w", err))
}
}
}
func (web *webAPI) mustStartHTTP3(address string) {
defer log.OnPanic("web: http3")
func (web *webAPI) mustStartHTTP3(ctx context.Context, address string) {
defer slogutil.RecoverAndExit(ctx, web.logger, osutil.ExitCodeFailure)
web.httpsServer.server3 = &http3.Server{
// TODO(a.garipov): See if there is a way to use the error log as
@@ -317,16 +351,16 @@ func (web *webAPI) mustStartHTTP3(address string) {
Handler: withMiddlewares(Context.mux, limitRequestBody),
}
log.Debug("web: starting http/3 server")
web.logger.DebugContext(ctx, "starting http/3 server")
err := web.httpsServer.server3.ListenAndServe()
if !errors.Is(err, http.ErrServerClosed) {
cleanupAlways()
log.Fatalf("web: http3: %s", err)
panic(fmt.Errorf("http3: %w", err))
}
}
// startPprof launches the debug and profiling server on the provided port.
func startPprof(port uint16) {
func startPprof(baseLogger *slog.Logger, port uint16) {
addr := netip.AddrPortFrom(netutil.IPv4Localhost(), port)
runtime.SetBlockProfileRate(1)
@@ -335,13 +369,16 @@ func startPprof(port uint16) {
mux := http.NewServeMux()
httputil.RoutePprof(mux)
go func() {
defer log.OnPanic("pprof server")
ctx := context.Background()
logger := baseLogger.With(slogutil.KeyPrefix, "pprof")
log.Info("pprof: listening on %q", addr)
go func() {
defer slogutil.RecoverAndLog(ctx, logger)
logger.InfoContext(ctx, "listening", "addr", addr)
err := http.ListenAndServe(addr.String(), mux)
if !errors.Is(err, http.ErrServerClosed) {
log.Error("pprof: shutting down: %s", err)
logger.ErrorContext(ctx, "shutting down", slogutil.KeyError, err)
}
}()
}

View File

@@ -1,36 +1,9 @@
// 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 }
import (
"github.com/AdguardTeam/golibs/service"
)
// ServiceWithConfig is an extension of the [Service] interface for services
// that can return their configuration.
@@ -38,8 +11,9 @@ func (EmptyService) Shutdown(_ context.Context) (err error) { return nil }
// 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
service.Interface
// Config returns a deep clone of the configuration of the service.
Config() (c ConfigType)
}
@@ -51,7 +25,7 @@ var _ ServiceWithConfig[struct{}] = (*EmptyServiceWithConfig[struct{}])(nil)
//
// TODO(a.garipov): Remove if unnecessary.
type EmptyServiceWithConfig[ConfigType any] struct {
EmptyService
service.Empty
Conf ConfigType
}

View File

@@ -1,15 +1,17 @@
# AdGuard Home v0.108.0 Changelog DRAFT
This changelog should be merged into the main one once the next API matures
enough.
This changelog should be merged into the main one once the next API matures enough.
## [v0.108.0] - TODO
### Added
- The ability to change the port of the pprof debug API.
- The ability to log to stderr using `--logFile=stderr`.
- The new `--web-addr` flag to set the Web UI address in a `host:port` form.
- `SIGHUP` now reloads all configuration from the configuration file ([#5676]).
### Changed
@@ -20,20 +22,21 @@ enough.
#### Other changes
- `-h` is now an alias for `--help` instead of the removed `--host`, see below.
Use `--web-addr=host:port` to set an address on which to serve the Web UI.
- `-h` is now an alias for `--help` instead of the removed `--host`, see below. Use `--web-addr=host:port` to set an address on which to serve the Web UI.
### Fixed
- `--check-config` breaking the configuration file ([#4067]).
- Inconsistent application of `--work-dir/-w` ([#2598], [#2902]).
- The order of `-v/--verbose` and `--version` being significant ([#2893]).
### Removed
- The deprecated `--no-mem-optimization` and `--no-etc-hosts` flags.
- `--host` and `-p/--port` flags. Use `--web-addr=host:port` to set an address
on which to serve the Web UI. `-h` is now an alias for `--help`, see above.
- `--host` and `-p/--port` flags. Use `--web-addr=host:port` to set an address on which to serve the Web UI. `-h` is now an alias for `--help`, see above.
[#2598]: https://github.com/AdguardTeam/AdGuardHome/issues/2598
[#2893]: https://github.com/AdguardTeam/AdGuardHome/issues/2893

View File

@@ -12,11 +12,15 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/next/configmgr"
"github.com/AdguardTeam/AdGuardHome/internal/version"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/service"
)
// Main is the entry point of AdGuard Home.
func Main(embeddedFrontend fs.FS) {
ctx := context.Background()
start := time.Now()
cmdName := os.Args[0]
@@ -26,70 +30,69 @@ func Main(embeddedFrontend fs.FS) {
os.Exit(exitCode)
}
err = setLog(opts)
check(err)
baseLogger := newBaseLogger(opts)
log.Info("starting adguard home, version %s, pid %d", version.Version(), os.Getpid())
baseLogger.InfoContext(
ctx,
"starting adguard home",
"version", version.Version(),
"pid", os.Getpid(),
)
if opts.workDir != "" {
log.Info("changing working directory to %q", opts.workDir)
baseLogger.InfoContext(ctx, "changing working directory", "dir", opts.workDir)
err = os.Chdir(opts.workDir)
check(err)
errors.Check(err)
}
frontend, err := frontendFromOpts(opts, embeddedFrontend)
check(err)
frontend, err := frontendFromOpts(ctx, baseLogger, opts, embeddedFrontend)
errors.Check(err)
startCtx, startCancel := context.WithTimeout(ctx, defaultTimeoutStart)
defer startCancel()
confMgrConf := &configmgr.Config{
Frontend: frontend,
WebAddr: opts.webAddr,
Start: start,
FileName: opts.confFile,
BaseLogger: baseLogger,
Logger: baseLogger.With(slogutil.KeyPrefix, "configmgr"),
Frontend: frontend,
WebAddr: opts.webAddr,
Start: start,
FileName: opts.confFile,
}
confMgr, err := newConfigMgr(confMgrConf)
check(err)
confMgr, err := configmgr.New(startCtx, confMgrConf)
errors.Check(err)
web := confMgr.Web()
err = web.Start()
check(err)
err = web.Start(startCtx)
errors.Check(err)
dns := confMgr.DNS()
err = dns.Start()
check(err)
err = dns.Start(startCtx)
errors.Check(err)
sigHdlr := newSignalHandler(
baseLogger.With(slogutil.KeyPrefix, service.SignalHandlerPrefix),
confMgrConf,
opts.pidFile,
web,
dns,
)
sigHdlr.handle()
os.Exit(sigHdlr.handle(ctx))
}
// defaultTimeout is the timeout used for some operations where another timeout
// hasn't been defined yet.
const defaultTimeout = 5 * 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)
}
// Default timeouts.
//
// TODO(a.garipov): Make configurable.
const (
defaultTimeoutStart = 1 * time.Minute
defaultTimeoutShutdown = 5 * time.Second
)
// newConfigMgr returns a new configuration manager using defaultTimeout as the
// context timeout.
func newConfigMgr(c *configmgr.Config) (m *configmgr.Manager, err error) {
ctx, cancel := ctxWithDefaultTimeout()
defer cancel()
func newConfigMgr(ctx context.Context, c *configmgr.Config) (m *configmgr.Manager, err error) {
return configmgr.New(ctx, c)
}
// check is a simple error-checking helper. It must only be used within Main.
func check(err error) {
if err != nil {
panic(err)
}
}

View File

@@ -1,39 +1,39 @@
package cmd
import (
"fmt"
"io"
"log/slog"
"os"
"github.com/AdguardTeam/AdGuardHome/internal/aghos"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/logutil/slogutil"
)
// syslogServiceName is the name of the AdGuard Home service used for writing
// logs to the system log.
const syslogServiceName = "AdGuardHome"
// setLog sets up the text logging.
//
// TODO(a.garipov): Add parameters from configuration file.
func setLog(opts *options) (err error) {
// newBaseLogger constructs a base logger based on the command-line options.
// opts must not be nil.
func newBaseLogger(opts *options) (baseLogger *slog.Logger) {
var output io.Writer
switch opts.confFile {
case "stdout":
log.SetOutput(os.Stdout)
output = os.Stdout
case "stderr":
log.SetOutput(os.Stderr)
output = os.Stderr
case "syslog":
err = aghos.ConfigureSyslog(syslogServiceName)
if err != nil {
return fmt.Errorf("initializing syslog: %w", err)
}
// TODO(a.garipov): Add a syslog handler to golibs.
default:
// TODO(a.garipov): Use the path.
// TODO(a.garipov): Use the path.
}
lvl := slog.LevelInfo
if opts.verbose {
log.SetLevel(log.DEBUG)
log.Debug("verbose logging enabled")
lvl = slog.LevelDebug
}
return nil
return slogutil.New(&slogutil.Config{
Output: output,
// TODO(a.garipov): Get from config?
Format: slogutil.FormatText,
Level: lvl,
// TODO(a.garipov): Get from config.
AddTimestamp: true,
})
}

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