Compare commits

..

36 Commits

Author SHA1 Message Date
Dimitry Kolyshev
d80e56bcd7 client: upd locales 2024-08-07 10:46:09 +05:00
Eugene Burkov
edfa8c147f Pull request 2263: AGDNS-2280 Upd dnsproxy, golibs
Squashed commit of the following:

commit 8d83eebba851e8e09bb08b1c94a247cb049a1b75
Merge: c6574a33c b6ed76965
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Aug 5 16:59:50 2024 +0300

    Merge branch 'master' into AGDNS-2280-upd-golibs

commit c6574a33c62171190199c8c07118d0ecd2174801
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Jul 31 19:56:58 2024 +0300

    all: upd proxy, golibs
2024-08-05 17:12:33 +03:00
Dimitry Kolyshev
b6ed769652 Pull request: 5009-ecosia-safesearch
Squashed commit of the following:

commit 787b5d40394889510c24f4abc3a08410ecc96e5e
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Wed Jul 31 15:09:18 2024 +0300

    configmigrate: revert

commit a036638c05967298d13ef435dc5b377103cfe163
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Wed Jul 31 09:37:25 2024 +0300

    docs

commit a3b2e8de4bab7214dd6b260655bba9f45eee6bd2
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Wed Jul 31 09:18:25 2024 +0300

    locales

commit a01a22019ef02ae77e9006dd9444da462419908f
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Wed Jul 31 09:02:14 2024 +0300

    filtering: imp code

commit bc268cdd526c82cb605b1a474d51b79f593cd3da
Merge: 5ad88d914 bc6d20ff1
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Wed Jul 31 08:00:05 2024 +0300

    Merge remote-tracking branch 'origin/master' into 5009-ecosia-safesearch

commit 5ad88d914cf9b03f399efd481ae39ebd56243e66
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Tue Jul 30 13:49:51 2024 +0300

    all: ecosia safesearch
2024-08-02 09:10:19 +03:00
Dimitry Kolyshev
bc6d20ff10 Pull request: 7155-imp-google-safesearch
* commit '9ea4838c07c5e3431260e275bf5c5dc35601fe6c':
  docs: changelog
  chore: update Google DNS list
2024-07-31 07:58:55 +03:00
Dimitry Kolyshev
9ea4838c07 Merge remote-tracking branch 'origin/master' into 7155-imp-google-safesearch
# Conflicts:
#	CHANGELOG.md
2024-07-30 17:19:45 +03:00
Dimitry Kolyshev
0e459a7369 Pull request: 7154-imp-bing-safesearch
* commit '2af8595363866f6fd3b55f6de29fd235f1078864':
  docs: changelog
  fix: enforce Bing safe search from Edge sidebar
2024-07-30 17:18:55 +03:00
Dimitry Kolyshev
c48cc980fc docs: changelog 2024-07-30 13:21:22 +03:00
Dimitry Kolyshev
2af8595363 docs: changelog 2024-07-30 13:15:03 +03:00
Cédrik
af0f43c0f8 chore: update Google DNS list
Add 3 new domains that appear in https://www.google.com/supported_domains but not in this list.
For the technically minded, generate your own list with:
curl -fsSL https://www.google.com/supported_domains | sort | sed -E 's%^(.*)$%|www\1^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com%'
:-)
2024-07-27 16:19:10 +00:00
Cédrik
d860764498 fix: enforce Bing safe search from Edge sidebar
Edge sidebar uses a different search endpoint, as per https://support.microsoft.com/en-us/topic/blocking-adult-content-with-safesearch-or-blocking-chat-946059ed-992b-46a0-944a-28e8fb8f1814;
2024-07-27 15:56:33 +00:00
Igor Lobanov
bf1101b460 Pull request #2258: ADG-8813 query log client column style fix
Merge in DNS/adguard-home from ADG-8813 to master

Squashed commit of the following:

commit ce36a973aed56960aa20720ab576ced9cc3a51b8
Merge: de0ea6edd 5c2ecfaa4
Author: Igor Lobanov <bniwredyc@gmail.com>
Date:   Mon Jul 22 11:05:59 2024 +0200

    Merge remote-tracking branch 'origin/master' into ADG-8813

commit de0ea6eddf3e409d0caabf4dbcd4e4dcce47c969
Author: Igor Lobanov <bniwredyc@gmail.com>
Date:   Thu Jul 18 16:27:00 2024 +0200

    changelog fix

commit 598e3ce1748a1f5e10ef31bbac002ff2579fb849
Author: Igor Lobanov <bniwredyc@gmail.com>
Date:   Thu Jul 18 16:24:16 2024 +0200

    fixed changelog

commit c4378e31a58a1a291e5933c854a39724e90a720d
Author: Igor Lobanov <bniwredyc@gmail.com>
Date:   Thu Jul 18 16:18:35 2024 +0200

    changelog

commit f9608325e869f7fa0798b46e041c30b100df3a3e
Author: Igor Lobanov <bniwredyc@gmail.com>
Date:   Wed Jul 17 16:12:11 2024 +0200

    query log client column style fix
2024-07-22 12:12:28 +03:00
Eugene Burkov
5c2ecfaa41 Pull request 2259: Upd proxy
Squashed commit of the following:

commit e0d4bc34ee9c9cb8ba058dce904f389087487526
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Jul 19 15:54:26 2024 +0300

    all: upd proxy
2024-07-19 16:33:25 +03:00
Eugene Burkov
f29a1cf23a Pull request 2257: 7113 Fix changelog
Updates #7113.

Squashed commit of the following:

commit aea0433099604cd23fd03cf920a4cae9031f1d92
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Jul 15 15:22:17 2024 +0300

    all: fix chlog
2024-07-15 16:04:42 +03:00
Eugene Burkov
42c7cd6f8e Pull request 2256: 4923 Better interfaces
Updates #4923.

Squashed commit of the following:

commit 0e40b41aa1e517a62d6076c4e7a57c607792ef01
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Jul 10 15:28:16 2024 +0300

    dhcpsvc: imp code, docs

commit 5463fdde473f84caaca229b53027e8183d5c6bdc
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Jul 9 20:31:20 2024 +0300

    dhcpsvc: imp ifaces
2024-07-10 16:17:56 +03:00
Dimitry Kolyshev
c0a33ce708 Pull request: upd-dnsproxy
Squashed commit of the following:

commit 463811748fa5a1f52e084c782e94f268b00b3abc
Merge: 3de62244e 130560b10
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Wed Jul 10 15:06:01 2024 +0300

    Merge remote-tracking branch 'origin/master' into upd-dnsproxy

commit 3de62244ee10fce9fb97c73c2955479883ce34eb
Merge: e2de50bf9 e269260fb
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Wed Jul 10 09:13:40 2024 +0300

    Merge remote-tracking branch 'origin/master' into upd-dnsproxy

commit e2de50bf9cf4eddaa0d87c20c8c1605bf4630fce
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Wed Jul 10 09:11:25 2024 +0300

    home: todos

commit 58fe497eecf614ba61e81f55504eb3ec5e537e10
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Tue Jul 9 13:29:19 2024 +0300

    home: imp code

commit 4db7cdc0c48533932b7c6de073dff9b0d1606fa9
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Tue Jul 9 11:31:12 2024 +0300

    all: imp code

commit 7e8d3b50e76634b83077bfb13a312adcb6d41189
Merge: 559c3b79d 9a6dd0dc5
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Mon Jul 8 10:56:14 2024 +0300

    Merge remote-tracking branch 'origin/master' into upd-dnsproxy

commit 559c3b79d7752021e9e75daf9f78af64ba0114fd
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Mon Jul 8 10:54:03 2024 +0300

    dnsforward: imp code

commit ba4a7e1c70f91ea2b004b164f2687a7a3107b0e8
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Mon Jul 8 10:49:46 2024 +0300

    aghos: imp code

commit cdf9ccd371317f49c78fa06795d6ba2d360ac40f
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Fri Jul 5 16:19:27 2024 +0300

    all: partial revert slog logger usage

commit f16cddbb8c63cefa0efc107e1e9fc43922c4aab6
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Fri Jul 5 13:01:37 2024 +0300

    all: upd dnsproxy

commit 5932c8d102d2b8e5f5aee1c8646aa774e2274501
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Fri Jul 5 11:49:37 2024 +0300

    dnsforward: slog logger

commit 3d7f734ac98b74ad3fa149498b881f30ff6b4805
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Fri Jul 5 11:05:14 2024 +0300

    all: slog logger

commit 9a74d5d98b6ee9d186eba3bc89de0d3736e4a649
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Thu Jul 4 12:16:21 2024 +0300

    all: upd dnsproxy

commit 537bdacec88f16ab1d6d6cc3748d39df00976dea
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Thu Jul 4 12:10:30 2024 +0300

    all: upd dnsproxy

commit 38e10dee48c8dc55606e0d99dd9cdf7719786f3a
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Thu Jul 4 10:37:50 2024 +0300

    dnsforward: upstream mode
2024-07-10 15:18:46 +03:00
Eugene Burkov
130560b104 Pull request 2255: 4923 Fix tests
Updates #4923.

Squashed commit of the following:

commit 064d4aa30ba73c57a6ede8cced348fc7daf1aab8
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Jul 9 20:51:41 2024 +0300

    dhcpsvc: close db

commit d08b70983de237a4a96e8a32360a8533c69d6868
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Jul 9 20:37:42 2024 +0300

    dhcpsvc: fix test paths
2024-07-10 13:03:36 +03:00
Eugene Burkov
e269260fbe Pull request 2254: 4923 gopacket DHCP vol.9
Updates #4923.

Squashed commit of the following:

commit 05322419156d18502f3f937e789df02d78971b30
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Jul 9 18:18:35 2024 +0300

    dhcpsvc: imp docs

commit 083da3671320f7774db9c5b854e663162da9d214
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Jul 9 15:28:52 2024 +0300

    dhcpsvc: imp code, tests

commit 22e37e587e29c781abd4f2486f282dcfbffb4e6b
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Jul 8 19:31:43 2024 +0300

    dhcpsvc: imp tests

commit 83ec7c54ef1e689a1f887e78e3055522539222d5
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Jul 8 18:56:21 2024 +0300

    dhcpsvc: add db
2024-07-09 20:04:24 +03:00
Ainar Garipov
9a6dd0dc55 Pull request 2253: upg-chlog
Squashed commit of the following:

commit 3548d7a3e8a44afd1bc5fd8de7bf28fb9941e033
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Fri Jul 5 15:46:37 2024 +0300

    all: imp chlog better

commit c465b408e9a8da536dd97d884e391a03e1db293b
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Fri Jul 5 15:41:11 2024 +0300

    all: imp chlog

commit 6b2de2834e9ece13cf3343107dafeea3b206dac9
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Fri Jul 5 15:33:05 2024 +0300

    all: upd chlog
2024-07-05 15:55:53 +03:00
Igor Lobanov
9a29aa9232 Pull request #2251: ADG-8776 clear query log filter fix
Merge in DNS/adguard-home from ADG-8776 to master

Squashed commit of the following:

commit d9f3acf7298c1cd39c980b9d6743624e18cdd8bf
Author: Igor Lobanov <bniwredyc@gmail.com>
Date:   Wed Jul 3 17:55:18 2024 +0200

    clear query log filter fix
2024-07-04 12:43:47 +03:00
Eugene Burkov
93e4005125 Pull request 2249: Fix bamboo-specs
Squashed commit of the following:

commit 66127b1b457d092537eec3b386108200f999cc02
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Jul 3 15:54:04 2024 +0300

    bamboo-specs: fix paths
2024-07-03 16:05:17 +03:00
Eugene Burkov
beeb8f0522 Pull request 2245: 4923 gopacket DHCP vol.8
Updates #4923.

Squashed commit of the following:

commit 0bfccf8bc1e63c4f5a01ce7f268e767969b368a0
Merge: 305f9fe2f 0e5e8e4dd
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Jul 3 15:12:43 2024 +0300

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

commit 305f9fe2fec033f28385dfe2bbee6a27a83b9702
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Jul 2 17:03:01 2024 +0300

    dhcpsvc: adjust interface

commit f05b9f42e2f50325ddaf09b5fed84b62e48c5120
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Jul 2 16:59:39 2024 +0300

    dhcpsvc: use logger

commit 4779f945baf9c8722d07d589928a86290a37d3ab
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Jul 2 14:59:22 2024 +0300

    dhcpsvc: add todo

commit ae1713e5f717a66863eb0289e3aa66c7069ac8bf
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Jul 1 15:49:22 2024 +0300

    dhcpsvc: use slog
2024-07-03 15:29:54 +03:00
Eugene Burkov
0e5e8e4dde Pull request 2247: Upd Go
Squashed commit of the following:

commit 9dc0d489601764003adc2d7c4ae73c45c9d07bba
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Jul 3 14:23:03 2024 +0300

    client: fix some locales

commit a130e205509cbd423c9c9793824a84b550cee0e5
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Jul 3 14:09:09 2024 +0300

    client: upd translations

commit aeccb20b6172fb019cee8820a9087a573a4fbacf
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Jul 3 14:04:12 2024 +0300

    all: upd services, trackers

commit f6a7f34e17b89f22fcfaed5001c256f12653663b
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Jul 3 13:32:55 2024 +0300

    all: imp fmt

commit e8b561175c0bfd6415dcb97c98400543906fb097
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Jul 3 13:27:47 2024 +0300

    all: add linebreak

commit 2b28fa107bf43eb3b7a1878716fb5cc0ec2204d2
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Jul 3 13:17:30 2024 +0300

    all: upd go
2024-07-03 15:08:17 +03:00
Ildar Kamalov
fcdebfa4d4 Pull request: ADG-8737 fixed missing version in the footer, unnecessary validation call on the encryption page
Squashed commit of the following:

commit 1c4a15f2f32cd8bfbe0878f79feee4f581f0f5a8
Merge: 399d28e67 9d1c45fd9
Author: Ildar Kamalov <ik@adguard.com>
Date:   Tue Jul 2 13:08:21 2024 +0300

    Merge branch 'master' into ADG-8737

commit 399d28e67ff8f8886d5552fea96017e83a122306
Author: Ildar Kamalov <ik@adguard.com>
Date:   Mon Jul 1 19:37:05 2024 +0300

    fix install

commit 91d5dd23cea0dd5cde1bf5979bad181229d36f1a
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Mon Jul 1 17:40:22 2024 +0300

    home: imp logs

commit 06917df08b2154c726ca2de7ed7fda1de4269655
Author: Ildar Kamalov <ik@adguard.com>
Date:   Mon Jul 1 16:38:38 2024 +0300

    ADG-8737 add missing version, remove validation call
2024-07-02 13:30:55 +03:00
Stanislav Chzhen
9d1c45fd94 Pull request 2244: AG-27492-client-storage-usage
Squashed commit of the following:

commit 46956d0f5a36fbcd2324125bcc146bdb57cb3f7e
Merge: 85ccad786 3993f4c47
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Jul 1 16:11:59 2024 +0300

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

commit 85ccad7862a35cbebc935776ac057f6bb6cfc75f
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Fri Jun 28 18:05:37 2024 +0300

    all: imp docs

commit e7043efcda32635b4260344f11b5a61b5120a9d2
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Jun 27 20:10:26 2024 +0300

    all: client storage usage
2024-07-01 17:34:47 +03:00
Ainar Garipov
3993f4c476 Pull request 2243: AG-33443-upd-vetted-script
Squashed commit of the following:

commit 85ce53f0fc53c5422d49dc50d9017a7dd009f7ba
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Jun 27 20:11:01 2024 +0300

    client: upd

commit 2588807dfdf4e7e0159ada57d6973194eaf3e286
Merge: c5fc7fbf4 a1a31cd91
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Jun 27 20:10:34 2024 +0300

    Merge branch 'master' into AG-33443-upd-vetted-script

commit c5fc7fbf4f85cb7e123a58f42c1ee83b1b369013
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Mon Jun 24 13:39:46 2024 +0300

    scripts: imp log

commit af420878b4f5753187b7afa6c2c3f3db54cf7711
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Mon Jun 24 13:12:12 2024 +0300

    scripts: upd vetter-filters
2024-06-27 20:18:17 +03:00
Stanislav Chzhen
a1a31cd916 Pull request 2221: AG-27492-client-persistent-runtime-storage
Squashed commit of the following:

commit a2b1e829f57fa7411354d882ec67d0c8736efbac
Merge: 5fde76bb2 65b7d232a
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Jun 25 16:12:17 2024 +0300

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

commit 5fde76bb20f818f052fe89dc90c2b3ea790da4d2
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Fri Jun 21 16:58:17 2024 +0300

    all: imp code

commit eae49f91bc1b5eedae3d03b0b6c782afa11896d8
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Jun 19 20:10:55 2024 +0300

    all: use storage

commit 2c7efa46099d9b8ffe297ce247aff0aa8f45dff7
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Jun 18 20:14:34 2024 +0300

    client: add tests

commit d59bd7a24e273e58737c3efa832adabc57495bed
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Jun 18 18:31:23 2024 +0300

    client: add tests

commit 045b83882380a8e181f6892cc3245944e4c9fd52
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Jun 18 15:18:08 2024 +0300

    client: add tests

commit 702467f7cadf3574c4a1b7b441ac02e26581bfcf
Merge: 4abc23bf8 1c82be295
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Jun 17 18:40:43 2024 +0300

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

commit 4abc23bf84dd8de02a1b805afba4d5a724b39d0c
Merge: e268abf92 bed86d57f
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Jun 13 15:19:47 2024 +0300

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

commit e268abf9268aef7a5386b5e126b01b249c590f49
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Jun 13 15:19:36 2024 +0300

    client: add tests

commit 5601cfce39599337aaf04688ffe2b14b49f856e5
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon May 27 14:27:53 2024 +0300

    client: runtime index

commit bde3baa5da85dd5404f78bd79a6a3e85c55cf7fc
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon May 20 14:39:35 2024 +0300

    all: persistent client storage
2024-06-26 14:30:02 +03:00
Stanislav Chzhen
65b7d232ab Pull request 2242: 7079-log-enabled
Updates #7079.

Squashed commit of the following:

commit 477c5ed29c8066dc9cb175809572f613b2a0dfa0
Merge: d3c64e03a 647214092
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Jun 20 19:31:54 2024 +0300

    Merge branch 'master' into 7079-log-enabled

commit d3c64e03a514e39d70e6b79209be1f8cf052a25d
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Jun 20 16:02:42 2024 +0300

    home: imp docs

commit 7658c9b107f109c391b6bd5407098e8e53754842
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Jun 20 14:17:08 2024 +0300

    all: log enabled
2024-06-20 19:44:51 +03:00
Eugene Burkov
6472140920 Pull request 2241: 7075 Doc load-balancing
Updates #7075.

Squashed commit of the following:

commit cd889bd828bcfaf71087727f169f05b69c508a45
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Jun 20 17:20:13 2024 +0300

    client: imp text

commit 3d51d48037bb85df5f04b6d3f83b1857253adf88
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Jun 19 13:47:48 2024 +0300

    client: imp ui text
2024-06-20 17:34:21 +03:00
Stanislav Chzhen
08d863dd3a Pull request 2235: 7069-fix-blocked-services
Updates #7069.

Squashed commit of the following:

commit 0f87493966124af4fa4b28eb1f67281343dd8242
Merge: 87f06b864 28a6c24db
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Jun 18 14:09:18 2024 +0300

    Merge branch 'master' into 7069-fix-blocked-services

commit 87f06b86432bba4d2e2583010784746452b7690f
Merge: c2440752c 66877c92d
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Jun 18 13:47:28 2024 +0300

    Merge branch 'master' into 7069-fix-blocked-services

commit c2440752c8a223e8ab2e4a519da9259d888ca06f
Merge: 17a9c14e2 1c82be295
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Jun 17 18:42:48 2024 +0300

    Merge branch 'master' into 7069-fix-blocked-services

commit 17a9c14e29a38ed513a827d9b5351d66f1e6cb48
Merge: 11160bc62 bed86d57f
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Jun 11 13:41:00 2024 +0300

    Merge branch 'master' into 7069-fix-blocked-services

commit 11160bc62b08fd6e984d38d5600fa62c3564668f
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Jun 11 13:36:56 2024 +0300

    all: imp docs

commit 491287164d31606c93dcbeb4f1e351df0960309b
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Jun 10 14:03:22 2024 +0300

    home: imp code

commit 0caf8b15797c28447039736cc9641ccddee5cac0
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Jun 10 13:35:54 2024 +0300

    all: upd chlog

commit 46f793b2591dce54a9566ccb9cb723ab5c60cd6a
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Jun 10 13:27:22 2024 +0300

    home: fix blocked services
2024-06-18 14:27:25 +03:00
Dimitry Kolyshev
28a6c24db2 Pull request: AG-33515-upd-deps
Squashed commit of the following:

commit 52698fe8d64ede24f9259810b8e31bc4e5f3682a
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Tue Jun 18 10:18:26 2024 +0300

    all: upd deps
2024-06-18 14:07:35 +03:00
Eugene Burkov
66877c92d9 Pull request 2239: 7076 Empty FSWatcher
Updates #7076.

Squashed commit of the following:

commit 6d99de9bcd1a4882f96639cf7e54fe0f33cfbfd3
Merge: c545152fa 1c82be295
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Jun 17 18:41:49 2024 +0300

    Merge branch 'master' into 7076-empty-fswatcher

commit c545152fa157e52f9ac5ebf2e58fdc1a254faf91
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Jun 14 14:57:01 2024 +0300

    all: imp code

commit e033558d7027a40a6996c08b5125e45141192071
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Jun 14 14:39:58 2024 +0300

    all: add & use empty fswatcher
2024-06-17 19:34:46 +03:00
Eugene Burkov
1c82be2950 Pull request 2237: 7053 journald Log
Updates #7053.

Squashed commit of the following:

commit f763a229660c00013fbd51cf7a3deabf00a01787
Merge: 06fc83d08 8432593be
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Jun 17 17:52:11 2024 +0300

    Merge branch 'master' into 7053-journald-log

commit 06fc83d08e45ebb2a09382f2b6961bb18a98906a
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Jun 13 15:59:35 2024 +0300

    all: imp chlog

commit 1f57a7e84aee7827f9b25f232ca46152a15da371
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Jun 11 20:36:55 2024 +0300

    all: imp chlog

commit 7c9a2547a82b4737e1785bb56ede2bf01bfb8533
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Jun 11 18:33:26 2024 +0300

    home: imp doc

commit 63731e7ba5e4085d071092ff4c7d712a30e07f28
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Jun 11 18:27:31 2024 +0300

    home: imp systemd script
2024-06-17 18:33:16 +03:00
Dimitry Kolyshev
8432593be1 Pull request: AG-33410-aghos-err
Merge in DNS/adguard-home from AG-33410-aghos-err to master

Squashed commit of the following:

commit 6014ea1e919ea685475561e4a46284847f67ac99
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Fri Jun 14 08:30:30 2024 +0300

    all: imp code

commit 232b207d8da42dad297f2730c42e5e84f9049ab9
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Thu Jun 13 12:18:41 2024 +0300

    all: rm aghos unsupported err
2024-06-14 12:32:19 +03:00
Ainar Garipov
bed86d57f3 Pull request 2236: fix-chlog
Squashed commit of the following:

commit e92093842b8c8627125ec16d1f5898dd2bc6d996
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Mon Jun 10 20:42:50 2024 +0300

    all: fix chlog
2024-06-10 20:49:47 +03:00
Igor Lobanov
1afe226ce8 Pull request #2231: ADG-8368 Frontend rewritten in TypeScript, added Node 18 support
Merge in DNS/adguard-home from ADG-8368-typescript-node-18 to master

Squashed commit of the following:

commit daa288ae0d76178af24595cc807055902e6f09ab
Merge: 4c89cf720 1085d59a6
Author: Igor Lobanov <bniwredyc@gmail.com>
Date:   Mon Jun 10 17:22:20 2024 +0200

    merge

commit 4c89cf7209
Author: Ildar Kamalov <ik@adguard.com>
Date:   Thu Jun 6 13:27:18 2024 +0300

    remove install from initial state

commit b943f2011f
Author: Igor Lobanov <bniwredyc@gmail.com>
Date:   Wed Jun 5 23:10:55 2024 +0200

    frontend production build fix

commit cd1be2d66d
Author: Igor Lobanov <bniwredyc@gmail.com>
Date:   Wed Jun 5 20:23:14 2024 +0200

    production build quickfix

commit 7b8ac01fc2
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Jun 5 19:57:31 2024 +0300

    all: upd node docker

commit 02afed66d5
Author: Igor Lobanov <bniwredyc@gmail.com>
Date:   Wed Jun 5 18:23:12 2024 +0200

    changelog fixes

commit 9c0f736f0c
Merge: 62c4fbf1e e04775c4f
Author: Igor Lobanov <bniwredyc@gmail.com>
Date:   Wed Jun 5 18:18:29 2024 +0200

    merge

commit 62c4fbf1e3
Author: Igor Lobanov <bniwredyc@gmail.com>
Date:   Wed Jun 5 16:22:22 2024 +0200

    empty line in changelog

commit 76b1e44a93
Author: Igor Lobanov <bniwredyc@gmail.com>
Date:   Wed Jun 5 16:20:37 2024 +0200

    changelog

commit f783e90040
Author: Igor Lobanov <bniwredyc@gmail.com>
Date:   Wed Jun 5 16:19:13 2024 +0200

    filters.js -> filters.ts

commit 3d4ce6554c
Author: Igor Lobanov <bniwredyc@gmail.com>
Date:   Wed Jun 5 16:18:03 2024 +0200

    generated file removed

commit e35ba58f2a
Author: Igor Lobanov <bniwredyc@gmail.com>
Date:   Wed Jun 5 15:45:21 2024 +0200

    rollback unwanted changes

commit 1f30d4216d
Author: Igor Lobanov <bniwredyc@gmail.com>
Date:   Wed Jun 5 15:27:36 2024 +0200

    review fix

commit 6cd4e44f07
Author: Igor Lobanov <bniwredyc@gmail.com>
Date:   Wed Jun 5 11:55:39 2024 +0200

    missing generated file restoresd

commit 2ab738b303
Author: Igor Lobanov <bniwredyc@gmail.com>
Date:   Wed Jun 5 11:40:32 2024 +0200

    Frontend rewritten in TypeScript, added Node 18 support
2024-06-10 18:42:23 +03:00
Ainar Garipov
1085d59a65 Pull request 2234: all: upd chlog
Squashed commit of the following:

commit baca5552390129e3ae24e19ab45746ea861fd4c4
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Jun 6 17:55:07 2024 +0300

    all: upd chlog
2024-06-06 18:25:07 +03:00
127 changed files with 2789 additions and 1257 deletions

View File

@@ -1,7 +1,7 @@
'name': 'build'
'env':
'GO_VERSION': '1.22.4'
'GO_VERSION': '1.22.5'
'NODE_VERSION': '16'
'on':

View File

@@ -1,7 +1,7 @@
'name': 'lint'
'env':
'GO_VERSION': '1.22.4'
'GO_VERSION': '1.22.5'
'on':
'push':

View File

@@ -7,6 +7,10 @@ The format is based on
and this project adheres to
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
<!--
TODO(a.garipov): Use the common markdown formatting tools.
-->
## [Unreleased]
@@ -14,15 +18,95 @@ and this project adheres to
<!--
## [v0.108.0] - TBA
## [v0.107.51] - 2024-06-22 (APPROX.)
## [v0.107.53] - 2024-07-24 (APPROX.)
See also the [v0.107.51 GitHub milestone][ms-v0.107.51].
See also the [v0.107.53 GitHub milestone][ms-v0.107.53].
[ms-v0.107.51]: https://github.com/AdguardTeam/AdGuardHome/milestone/86?closed=1
[ms-v0.107.53]: https://github.com/AdguardTeam/AdGuardHome/milestone/88?closed=1
NOTE: Add new changes BELOW THIS COMMENT.
-->
### Added
- Ecosia search engine is now supported in safe search ([#5009]).
### Fixed
- Update Google safe search domains list ([#7155]).
- Enforce Bing safe search from Edge sidebar ([#7154]).
- Text overflow on the query log page ([#7119]).
[#5009]: https://github.com/AdguardTeam/AdGuardHome/issues/5009
[#7119]: https://github.com/AdguardTeam/AdGuardHome/issues/7119
[#7154]: https://github.com/AdguardTeam/AdGuardHome/pull/7154
[#7155]: https://github.com/AdguardTeam/AdGuardHome/pull/7155
<!--
NOTE: Add new changes ABOVE THIS COMMENT.
-->
## [v0.107.52] - 2024-07-04
See also the [v0.107.52 GitHub milestone][ms-v0.107.52].
### Security
- Go version has been updated to prevent the possibility of exploiting the Go
vulnerabilities fixed in [Go 1.22.5][go-1.22.5].
### Added
- The ability to disable logging using the new `log.enabled` configuration
property ([#7079]).
### Changed
- Frontend rewritten in TypeScript.
- The `systemd`-based service now uses `journal` for logging by default. It
also doesn't create the `/var/log/` directory anymore ([#7053]).
**NOTE:** With an installed service for changes to take effect, you need to
reinstall the service using `-r` flag of the [install script][install-script]
or via the CLI (with root privileges):
```sh
./AdGuardHome -s uninstall
./AdGuardHome -s install
```
Don't forget to backup your configuration file and other important data before
reinstalling the service.
### Deprecated
- Node 18 support, Node 20 will be required in future releases.
### Fixed
- Panic caused by missing user-specific blocked services object in configuration
file ([#7069]).
- Tracking `/etc/hosts` file changes causing panics within particular
filesystems on start ([#7076]).
[#7053]: https://github.com/AdguardTeam/AdGuardHome/issues/7053
[#7069]: https://github.com/AdguardTeam/AdGuardHome/issues/7069
[#7076]: https://github.com/AdguardTeam/AdGuardHome/issues/7076
[#7079]: https://github.com/AdguardTeam/AdGuardHome/issues/7079
[go-1.22.5]: https://groups.google.com/g/golang-announce/c/gyb7aM1C9H4
[install-script]: https://github.com/AdguardTeam/AdGuardHome/?tab=readme-ov-file#automated-install-linux-and-mac
[ms-v0.107.52]: https://github.com/AdguardTeam/AdGuardHome/milestone/87?closed=1
## [v0.107.51] - 2024-06-06
See also the [v0.107.51 GitHub milestone][ms-v0.107.51].
### Security
- Go version has been updated to prevent the possibility of exploiting the Go
@@ -30,22 +114,14 @@ NOTE: Add new changes BELOW THIS COMMENT.
### Changed
- Frontend rewritten in TypeScript.
- The HTTP server's write timeout has been increased from 1 minute to 5 minutes
to match the one used by AdGuard Home's HTTP client to fetch filtering-list
data ([#7041]).
### Deprecated
- Node 18 support, Node 20 will be required in future releases.
[#7041]: https://github.com/AdguardTeam/AdGuardHome/issues/7041
[go-1.22.4]: https://groups.google.com/g/golang-announce/c/XbxouI9gY7k/
<!--
NOTE: Add new changes ABOVE THIS COMMENT.
-->
[go-1.22.4]: https://groups.google.com/g/golang-announce/c/XbxouI9gY7k/
[ms-v0.107.51]: https://github.com/AdguardTeam/AdGuardHome/milestone/86?closed=1
@@ -3006,11 +3082,13 @@ See also the [v0.104.2 GitHub milestone][ms-v0.104.2].
<!--
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.51...HEAD
[v0.107.51]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.50...v0.107.51
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.53...HEAD
[v0.107.53]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.52...v0.107.53
-->
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.50...HEAD
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.52...HEAD
[v0.107.52]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.51...v0.107.52
[v0.107.51]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.50...v0.107.51
[v0.107.50]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.49...v0.107.50
[v0.107.49]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.48...v0.107.49
[v0.107.48]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.47...v0.107.48

View File

@@ -27,7 +27,7 @@ DIST_DIR = dist
GOAMD64 = v1
GOPROXY = https://goproxy.cn|https://proxy.golang.org|direct
GOSUMDB = sum.golang.google.cn
GOTOOLCHAIN = go1.22.4
GOTOOLCHAIN = go1.22.5
GPG_KEY = devteam@adguard.com
GPG_KEY_PASSPHRASE = not-a-real-password
NPM = npm

View File

@@ -7,8 +7,8 @@
# Make sure to sync any changes with the branch overrides below.
'variables':
'channel': 'edge'
'dockerFrontend': '${bamboo.adguardRegistryBasePath}/home-js-builder:2.0'
'dockerGo': '${bamboo.adguardRegistryBasePath}/go-builder:1.22.4--1'
'dockerFrontend': 'adguard/home-js-builder:2.0'
'dockerGo': 'adguard/go-builder:1.22.5--1'
'stages':
- 'Build frontend':
@@ -265,8 +265,8 @@
# need to build a few of these.
'variables':
'channel': 'beta'
'dockerFrontend': '${bamboo.adguardRegistryBasePath}/home-js-builder:2.0'
'dockerGo': '${bamboo.adguardRegistryBasePath}/go-builder:1.22.4--1'
'dockerFrontend': 'adguard/home-js-builder:2.0'
'dockerGo': 'adguard/go-builder:1.22.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]+':
@@ -281,5 +281,5 @@
# are the ones that actually get released.
'variables':
'channel': 'release'
'dockerFrontend': '${bamboo.adguardRegistryBasePath}/home-js-builder:2.0'
'dockerGo': '${bamboo.adguardRegistryBasePath}/go-builder:1.22.4--1'
'dockerFrontend': 'adguard/home-js-builder:2.0'
'dockerGo': 'adguard/go-builder:1.22.5--1'

View File

@@ -5,8 +5,8 @@
'key': 'AHBRTSPECS'
'name': 'AdGuard Home - Build and run tests'
'variables':
'dockerFrontend': '${bamboo.adguardRegistryBasePath}/home-js-builder:2.0'
'dockerGo': '${bamboo.adguardRegistryBasePath}/go-builder:1.22.4--1'
'dockerFrontend': 'adguard/home-js-builder:2.0'
'dockerGo': 'adguard/go-builder:1.22.5--1'
'channel': 'development'
'stages':
@@ -194,6 +194,6 @@
# Set the default release channel on the release branch to beta, as we
# may need to build a few of these.
'variables':
'dockerFrontend': '${bamboo.adguardRegistryBasePath}/home-js-builder:2.0'
'dockerGo': '${bamboo.adguardRegistryBasePath}/go-builder:1.22.4--1'
'dockerFrontend': 'adguard/home-js-builder:2.0'
'dockerGo': 'adguard/go-builder:1.22.5--1'
'channel': 'candidate'

View File

@@ -6,21 +6,18 @@
"upstream_parallel": "استخدم الاستعلامات المتوازية لتسريع عملية الحل عن طريق الاستعلام عن جميع الخوادم المنبع في وقت واحد.",
"parallel_requests": "الطلبات الموازية",
"load_balancing": "موازنة الأحمال",
"load_balancing_desc": "الاستعلام عن خادم واحد في كل مرة سيستخدم AdGuard الرئيسية الخوارزمية العشوائية الموزونة لاختيار الخادم بحيث يتم استخدام أسرع خادم في كثير من الأحيان",
"bootstrap_dns": "خوادم Bootstrap DNS",
"bootstrap_dns_desc": "عناوين IP لخوادم DNS المستخدمة لحل عناوين IP الخاصة بمحللات DoH/DoT التي تحددها كمصدرين رئيسيين. التعليقات غير مسموح بها.",
"fallback_dns_title": "خوادم DNS الاحتياطية",
"fallback_dns_desc": "قائمة الخوادم الاحتياطية المستخدمة في حالة عدم الاستجابة من خوادم DNS الرئيسية. تمتلك تلك الخوادم والخوادم الرئيسية نفس الأوامر.",
"fallback_dns_placeholder": "أدخل خادم DNS احتياطي واحد لكل سطر",
"local_ptr_title": "خوادم DNS العكسية الخاصة",
"local_ptr_desc": "خوادم DNS التي يستخدمها AdGuard Home لاستعلامات PTR المحلية. تُستخدم هذه الخوادم لحل أسماء المضيفين للعملاء بعناوين IP خاصة ، على سبيل المثال \"192.168.12.34\" ، باستخدام DNS العكسي. في حالة عدم التعيين ، يستخدم AdGuard Home عناوين محللات DNS الافتراضية لنظام التشغيل الخاص بك باستثناء عناوين AdGuard Home نفسها.",
"local_ptr_default_resolver": "بشكل افتراضي ، يستخدم AdGuard Home محللات DNS العكسية التالية: {{ip}}.",
"local_ptr_no_default_resolver": "لم يتمكن AdGuard Home من تحديد محللات DNS العكسية المناسبة لهذا النظام.",
"local_ptr_placeholder": "أدخل عنوان IP واحد لكل سطر",
"resolve_clients_title": "تفعيل التحليل العكسي لعناوين IP للعملاء",
"resolve_clients_desc": "حل عكسيًا لعناوين IP للعملاء في أسماء مضيفيهم عن طريق إرسال استعلامات PTR إلى أدوات الحل المقابلة (خوادم DNS الخاصة للعملاء المحليين ، والخوادم الأولية للعملاء الذين لديهم عناوين IP عامة).",
"use_private_ptr_resolvers_title": "استخدم محللات DNS العكسية الخاصة",
"use_private_ptr_resolvers_desc": "قم بإجراء عمليات بحث DNS عكسية عن العناوين التي يتم تقديمها محليًا باستخدام هذه الخوادم الأولية. في حالة التعطيل ، يستجيب AdGuard Home مع NXDOMAIN لجميع طلبات PTR هذه باستثناء العملاء المعروفين من DHCP و / etc / hosts وما إلى ذلك.",
"check_dhcp_servers": "تحقق من خوادم DHCP",
"save_config": "حفظ الإعدادات",
"enabled_dhcp": "خادم DHCP مفعل",
@@ -154,7 +151,6 @@
"use_adguard_parental": "استخدام خدمة AdGuard للرقابة الأبوية على الويب",
"use_adguard_parental_hint": "سيتحقق AdGuard Home مما إذا كان النطاق يحتوي على محتوى للبالغين. إنه يستخدم نفس واجهة برمجة التطبيقات الصديقة للخصوصية مثل خدمة الويب الأمنية للتصفح.",
"enforce_safe_search": "استخدم البحث الآمن",
"enforce_save_search_hint": "سيفرض AdGuard Home البحث الآمن في محركات البحث التالية: Google وYouTube وBing وDuckDuckGo وYandex وPixabay.",
"no_servers_specified": "لم يتم تحديد خوادم",
"general_settings": "الإعدادات العامة",
"dns_settings": "إعدادات الـ DNS",

View File

@@ -6,21 +6,18 @@
"upstream_parallel": "Ужыць адначасныя запыты да ўсіх сервераў для паскарэння апрацоўкі запыту",
"parallel_requests": "Паралельныя запыты",
"load_balancing": "Размеркаванне нагрузкі",
"load_balancing_desc": "Запытвайце па адным серверы за раз. AdGuard Home будзе выкарыстоўваць выпадковы алгарытм для выбару сервера, так што самы хуткі сервер будзе выкарыстоўвацца часцей.",
"bootstrap_dns": "Bootstrap DNS-серверы",
"bootstrap_dns_desc": "IP-адрасы DNS-сервераў, якія выкарыстоўваюцца для вырашэння IP-адрасоў распознавальнікаў DoH/DoT, якія вы ўказваеце ў якасці перадачы. Каментары не дапускаюцца.",
"fallback_dns_title": "Рэзервовыя DNS-серверы",
"fallback_dns_desc": "Спіс рэзервовых DNS-сервераў, якія выкарыстоўваюцца, калі вышэйшыя DNS-серверы не адказваюць. Сінтаксіс такі ж, як і ў галоўным полі ўверх.",
"fallback_dns_placeholder": "Увядзіце па адным рэзервовым серверы DNS у радку",
"local_ptr_title": "Прыватныя DNS-серверы",
"local_ptr_desc": "DNS-серверы, якія AdGuard Home выкарыстоўвае для лакальных PTR-запытаў. Гэтыя серверы выкарыстоўваюцца, каб атрымаць даменавыя імёны кліентаў з прыватнымі IP-адрасамі, напрыклад «192.168.12.34», з дапамогай rDNS. Калі спіс пусты, AdGuard Home выкарыстоўвае прадвызначаныя DNS-серверы вашай АС.",
"local_ptr_default_resolver": "Па змаўчанні AdGuard Home выкарыстоўвае наступныя зваротныя DNS-рэзолверы: {{ip}}.",
"local_ptr_no_default_resolver": "AdGuard Home не змог вызначыць прыдатныя прыватныя адваротныя DNS-рэзолверы для гэтай сістэмы.",
"local_ptr_placeholder": "Увядзіце па адным адрасе на радок",
"resolve_clients_title": "Уключыць запытванне даменавых імёнаў для кліентаў",
"resolve_clients_desc": "AdGuard Home будзе спрабаваць аўтаматычна вызначыць даменавыя імёны кліентаў праз PTR-запыты да адпаведных сервераў (прыватны DNS-сервер для лакальных кліентаў, upstream-серверы для кліентаў з публічным IP-адрасам).",
"use_private_ptr_resolvers_title": "Ужываць прыватныя адваротныя DNS-рэзолверы",
"use_private_ptr_resolvers_desc": "Пасылаць адваротныя DNS-запыты для лакальна абслугоўных адрасоў на паказаныя серверы. Калі адключана, AdGuard Home будзе адказваць NXDOMAIN на ўсе падобныя PTR-запыты, апроч запытаў пра кліентаў, ужо вядомых па DHCP, /etc/hosts і гэтак далей.",
"check_dhcp_servers": "Праверыць DHCP-серверы",
"save_config": "Захаваць канфігурацыю",
"enabled_dhcp": "DHCP-сервер улучаны",
@@ -154,7 +151,6 @@
"use_adguard_parental": "Ужывайце модуль Бацькоўскага кантролю AdGuard ",
"use_adguard_parental_hint": "AdGuard Home праверыць, ці ўтрымвае дамен матэрыялы 18+. Ён выкарыстоўвае той жа API для забеспячэння прыватнасці, што і ўэб-служба бяспекі браўзара.",
"enforce_safe_search": "Узмацніць бяспечны пошук",
"enforce_save_search_hint": "AdGuard Home можа забяспечыць бяспечны пошук у наступных пошукавых сістэмах: Google, Youtube, Bing, DuckDuckGo, Yandex і Pixabay.",
"no_servers_specified": "Не паказаных сервераў",
"general_settings": "Асноўныя налады",
"dns_settings": "Налады DNS",
@@ -675,7 +671,6 @@
"use_saved_key": "Скарыстаць захаваны раней ключ",
"parental_control": "Бацькоўскі кантроль",
"safe_browsing": "Бяспечны інтэрнэт",
"served_from_cache": "{{value}} <i>(атрымана з кэша)</i>",
"form_error_password_length": "Пароль павінен утрымліваць ад {{min}} да {{max}} сімвалаў",
"anonymizer_notification": "<0>Заўвага:</0> Ананімізацыя IP уключана. Вы можаце адключыць яе ў <1>Агульных наладах</1>.",
"confirm_dns_cache_clear": "Вы ўпэўнены, што хочаце ачысціць кэш DNS?",

View File

@@ -65,14 +65,12 @@
"stats_malware_phishing": "вируси/атаки",
"stats_adult": "сайтове за възрастни",
"stats_query_domain": "Най-отваряни страници",
"for_last_24_hours": "за последните 24 часа",
"no_domains_found": "Няма намерени резултати",
"requests_count": "Сума на заявките",
"top_blocked_domains": "Най-блокирани страници",
"top_clients": "Най-активни IP адреси",
"no_clients_found": "Нямa намерени адреси",
"general_statistics": "Обща статисика",
"number_of_dns_query_24_hours": "Сума на DNS заявки за последните 24 часа",
"number_of_dns_query_blocked_24_hours": "Сума на блокирани DNS заявки от филтрите за реклама и местни",
"number_of_dns_query_blocked_24_hours_by_sec": "Сума на блокирани DNS заявки от AdGuard свързани със сигурността",
"number_of_dns_query_blocked_24_hours_adult": "Сума на блокирани сайтове за възрастни",
@@ -87,7 +85,6 @@
"use_adguard_parental": "Включи AdGuard Родителски Надзор",
"use_adguard_parental_hint": "Модул XXX в AdGuard Home ще провери дали страницата има материали за възвъстни. Използва се същия API за анонимност като при модула за Сигурност.",
"enforce_safe_search": "Включи Безопасно Търсене",
"enforce_save_search_hint": "AdGuard Home прилага Безопасно Търсене в следните търсачки и сайтове: Google, Youtube, Bing, и Yandex.",
"no_servers_specified": "Няма избрани услуги",
"general_settings": "Общи настройки",
"custom_filtering_rules": "Местни правила за филтриране",
@@ -209,7 +206,6 @@
"install_devices_android_list_5": "Променете стойностите на DNS 1 и DNS 2 да използват AdGuard Home сървъра.",
"install_devices_ios_list_1": "От начален екран, цъкнете на Settings.",
"install_devices_ios_list_2": "Изберете Wi-Fi от лявото меню (там няма възможност за въвеждане на DNS настройки).",
"install_devices_ios_list_3": "Клинете на името на активната мрежа към която сте свързани.",
"install_devices_ios_list_4": "В полето за DNS изберете ръчно и въведете адреса на AdGuard Home сървъра.",
"get_started": "Да започваме",
"next": "Следващ",
@@ -220,7 +216,6 @@
"encryption_config_saved": "Конфигурацията е успешно записана",
"encryption_server": "Име на сървъра",
"encryption_server_enter": "Въведете име на домейна",
"encryption_server_desc": "За да използвате HTTPS, трябва името на сървъра да съвпада с това на SSL сертификата.",
"encryption_redirect": "Автоматично пренасочване към HTTPS",
"encryption_redirect_desc": "Служи за автоматично пренасочване от HTTP към HTTPS на страницата за Администрация в AdGuard Home.",
"encryption_https": "HTTPS порт",

View File

@@ -6,7 +6,7 @@
"upstream_parallel": "Použijte paralelní požadavky na urychlení řešení simultánním dotazováním na všechny navazující servery.",
"parallel_requests": "Paralelní požadavky",
"load_balancing": "Optimalizace vytížení",
"load_balancing_desc": "Optimalizovaný dotaz na odchozí server. AdGuard Home použije vážený náhodný algoritmus k výběru serveru, takže nejrychlejší server je používán častěji.",
"load_balancing_desc": "Dotazy jednoho odchozího serveru ve stejný čas. AdGuard Home používá náhodný algoritmus pro výběr serverů s nejnižším počtem neúspěšných vyhledávání a nejnižší průměrnou dobou vyhledávání.",
"bootstrap_dns": "Bootstrap DNS servery",
"bootstrap_dns_desc": "IP adresy DNS serverů používaných k překladu IP adres řešitelů DoH/DoT, které zadáte jako odchozí servery. Komentáře nejsou povoleny.",
"fallback_dns_title": "Záložní DNS servery",
@@ -154,7 +154,7 @@
"use_adguard_parental": "Použít službu AdGuard Rodičovská kontrola",
"use_adguard_parental_hint": "AdGuard Home zkontroluje, zda doména obsahuje materiály pro dospělé. Používá stejné API přátelské k ochraně osobních údajů jako služba Bezpečnost prohlížení.",
"enforce_safe_search": "Použít bezpečné vyhledávání",
"enforce_save_search_hint": "AdGuard Home vynutí bezpečné vyhledávání v následujících vyhledávačích: Google, YouTube, Bing, DuckDuckGo, Yandex, Pixabay.",
"enforce_save_search_hint": "AdGuard Home vynutí bezpečné vyhledávání v následujících vyhledávačích: Google, YouTube, Bing, DuckDuckGo, Ecosia, Yandex, Pixabay.",
"no_servers_specified": "Nebyly specifikovány žádné servery",
"general_settings": "Obecná nastavení",
"dns_settings": "Nastavení DNS",

View File

@@ -6,7 +6,7 @@
"upstream_parallel": "Brug parallelforespørgsler til at accelerere fortolkningen ved at forespørge alle upstream-servere samtidigt.",
"parallel_requests": "Parallelle forespørgsler",
"load_balancing": "Belastningsfordeling",
"load_balancing_desc": "Forespørg én server ad gangen. AdGuard Home vil bruge en vægtet randomiseringsalgoritme til valg af server, så den hurtigste server oftere anvendes.",
"load_balancing_desc": "Forespørg én upstream-server ad gangen. AdGuard Home bruger en vægtet tilfældighedsalgoritme til vælg af servere med det laveste antal fejlslagne opslag og den laveste gennemsnitlige opslagstid.",
"bootstrap_dns": "Bootstrap DNS-servere",
"bootstrap_dns_desc": "IP-adresser på DNS-servere, som bruges til at opløse IP-adresser på de DoH/DoT-opløsere, som angives som upstreams. Kommentarer er ikke tilladt.",
"fallback_dns_title": "Reserve DNS-servere",
@@ -154,7 +154,7 @@
"use_adguard_parental": "Brug AdGuards forældrekontrolwebtjeneste",
"use_adguard_parental_hint": "AdGuard Home vil tjekke, om domænet indeholder voksenindhold vha. den samme fortrolighedsvenlige API som browsingsikkerhedswebtjenesten.",
"enforce_safe_search": "Brug sikker søgning",
"enforce_save_search_hint": "AdGuard Home vil håndhæve sikker søgning i flg. søgemaskiner: Google, YouTube, Bing, DuckDuckGo, Yandex og Pixabay.",
"enforce_save_search_hint": "AdGuard Home vil håndhæve sikker søgning i flg. søgemaskiner: Google, YouTube, Bing, DuckDuckGo, Ecosia, Yandex, Pixabay.",
"no_servers_specified": "Ingen servere angivet",
"general_settings": "Generelle indstillinger",
"dns_settings": "DNS-indstillinger",

View File

@@ -6,7 +6,7 @@
"upstream_parallel": "Parallele Abfragen verwenden, um das Auflösen zu beschleunigen, indem alle Upstream-Server gleichzeitig abgefragt werden.",
"parallel_requests": "Paralleles Abfragen",
"load_balancing": "Lastverteilung",
"load_balancing_desc": "Einen Server nach dem anderen abfragen. AdGuard Home verwendet den gewichteten Zufallsalgorithmus, um den Server so auszuwählen, dass der schnellste Server häufiger verwendet wird.",
"load_balancing_desc": "Es wird jeweils ein Upstream-Server abgefragt. AdGuard Home verwendet einen gewichteten Zufallsalgorithmus, um die Server mit der geringsten Anzahl an fehlgeschlagenen Suchvorgängen und der niedrigsten durchschnittlichen Suchzeit auszuwählen.",
"bootstrap_dns": "Bootstrap-DNS-Server",
"bootstrap_dns_desc": "IP-Adressen der DNS-Server, die zum Auflösen der IP-Adressen von DoH/DoT Upstream-Servern verwendet werden, die Sie angegeben haben. Kommentare sind nicht erlaubt.",
"fallback_dns_title": "Fallback-DNS-Server",
@@ -154,7 +154,7 @@
"use_adguard_parental": "AdGuard Webservice für Kindersicherung verwenden",
"use_adguard_parental_hint": "AdGuard Home wird prüfen, ob die Domain jugendgefährdende Inhalte enthält. Zum Schutz Ihrer Privatsphäre wird die selbe API wie für den Webservice für Internetsicherheit verwendet.",
"enforce_safe_search": "Sichere Suche verwenden",
"enforce_save_search_hint": "AdGuard kann Sichere Suche für folgende Suchmaschinen erzwingen: Google, YouTube, Bing, DuckDuckGo, Yandex und Pixabay.",
"enforce_save_search_hint": "AdGuard kann Sichere Suche für folgende Suchmaschinen erzwingen: Google, YouTube, Bing, DuckDuckGo, Ecosia, Yandex und Pixabay.",
"no_servers_specified": "Keine Server festgelegt",
"general_settings": "Allgemeine Einstellungen",
"dns_settings": "DNS-Einstellungen",
@@ -641,7 +641,7 @@
"show_processed_responses": "Verarbeitet",
"blocked_safebrowsing": "Gesperrt durch Internetsicherheit",
"blocked_adult_websites": "Gesperrt durch Kindersicherung",
"blocked_threats": "Gesperrte Bedrohungen",
"blocked_threats": "Bedrohungen blockiert",
"allowed": "Zugelassen",
"filtered": "Gefiltert",
"rewritten": "Umgeschrieben",

View File

@@ -6,7 +6,7 @@
"upstream_parallel": "Use parallel queries to speed up resolving by querying all upstream servers simultaneously.",
"parallel_requests": "Parallel requests",
"load_balancing": "Load-balancing",
"load_balancing_desc": "Query one upstream server at a time. AdGuard Home uses its weighted random algorithm to pick the server so that the fastest server is used more often.",
"load_balancing_desc": "Query one upstream server at a time. AdGuard Home uses a weighted random algorithm to select servers with the lowest number of failed lookups and the lowest average lookup time.",
"bootstrap_dns": "Bootstrap DNS servers",
"bootstrap_dns_desc": "IP addresses of DNS servers used to resolve IP addresses of the DoH/DoT resolvers you specify as upstreams. Comments are not permitted.",
"fallback_dns_title": "Fallback DNS servers",
@@ -154,7 +154,7 @@
"use_adguard_parental": "Use AdGuard parental control web service",
"use_adguard_parental_hint": "AdGuard Home will check if domain contains adult materials. It uses the same privacy-friendly API as the browsing security web service.",
"enforce_safe_search": "Use Safe Search",
"enforce_save_search_hint": "AdGuard Home will enforce safe search in the following search engines: Google, YouTube, Bing, DuckDuckGo, Yandex, Pixabay.",
"enforce_save_search_hint": "AdGuard Home will enforce safe search in the following search engines: Google, YouTube, Bing, DuckDuckGo, Ecosia, Yandex, Pixabay.",
"no_servers_specified": "No servers specified",
"general_settings": "General settings",
"dns_settings": "DNS settings",

View File

@@ -6,7 +6,7 @@
"upstream_parallel": "Usar consultas paralelas para acelerar la resolución al consultar simultáneamente a todos los servidores DNS de subida.",
"parallel_requests": "Consultas paralelas",
"load_balancing": "Balanceo de carga",
"load_balancing_desc": "Consulta un servidor DNS de subida a la vez. AdGuard Home utiliza su algoritmo aleatorio ponderado para elegir el servidor más rápido y sea utilizado con más frecuencia.",
"load_balancing_desc": "Consulta un servidor upstream a la vez. AdGuard Home utiliza un algoritmo aleatorio ponderado para seleccionar los servidores con el menor número de fallos y el menor tiempo medio de búsqueda.",
"bootstrap_dns": "Servidores DNS de arranque",
"bootstrap_dns_desc": "Direcciones IP de servidores DNS utilizadas para resolver direcciones IP de los solucionadores DoH/DoT que especifiques como ascendentes. No se permiten comentarios.",
"fallback_dns_title": "Servidores DNS alternativos",
@@ -154,7 +154,7 @@
"use_adguard_parental": "Usar el control parental de AdGuard",
"use_adguard_parental_hint": "AdGuard Home comprobará si el dominio contiene materiales para adultos. Utiliza la misma API amigable con la privacidad del servicio web de seguridad de navegación.",
"enforce_safe_search": "Usar búsqueda segura",
"enforce_save_search_hint": "AdGuard Home reforzará la búsqueda segura en los siguientes motores de búsqueda: Google, YouTube, Bing, DuckDuckGo, Yandex y Pixabay.",
"enforce_save_search_hint": "AdGuard Home reforzará la búsqueda segura en los siguientes motores de búsqueda: Google, YouTube, Bing, DuckDuckGo, Ecosia, Yandex y Pixabay.",
"no_servers_specified": "No hay servidores especificados",
"general_settings": "Configuración general",
"dns_settings": "Configuración del DNS",

View File

@@ -5,21 +5,17 @@
"upstream_parallel": "استفاده از جستار موازی برای سرعت دادن به تفکیک با جستار همزمان همه جریان های ارسالی",
"parallel_requests": "درخواست های موازی",
"load_balancing": "متعادل کننده بار",
"load_balancing_desc": "یک سرور بالادستی را در یک زمان پرس و جو کنید. AdGuard Home از الگوریتم تصادفی وزنی خود برای انتخاب سرور استفاده می کند تا سریع ترین سرور بیشتر مورد استفاده قرار گیرد.",
"bootstrap_dns": "خودراه انداز سرورهای DNS",
"bootstrap_dns_desc": "آدرس‌های IP سرورهای DNS که برای حل کردن آدرس‌های IP حل‌کننده‌های DoH/DoT که به‌عنوان upstream مشخص می‌کنید، استفاده می‌شوند. اظهار نظر مجاز نیست.",
"fallback_dns_title": "سرورهای DNS بازگشتی",
"fallback_dns_desc": "لیست سرورهای DNS بازگشتی که در زمانی که سرورهای DNS بالادستی پاسخ نمی‌دهند استفاده می‌شوند. نحو مانند فیلد بالادستی اصلی بالا است.",
"fallback_dns_placeholder": "یک سرور DNS بازگشتی در هر خط وارد کنید",
"local_ptr_title": "سرورهای خصوصی DNS",
"local_ptr_desc": "سرور یا سرور های DNS ای که AdGuard Home برای درخواست های منابع محلی ارائه شده مورد استفاده قرار خواهد داد. برای مثال، این سرور برای تعیین نام های سرویس دهنده برای سرویس گیرنده با آدرس های آی پی خصوصی مورد استفاده قرار خواهد گرفت. اگر تعیین نشود،AdGuard Home به طور خودکار از تعیین کننده ی DNS پیش فرض شما استفاده خواهد کرد.",
"local_ptr_default_resolver": "به طور پیش فرض، AdGuard Home از تعیین کننده های DNS معکوس زیر استفاده می کند: {{ip}}.",
"local_ptr_no_default_resolver": "AdGuard Home نتوانست برای این دستگاه تعیین کننده های DNS معکوس محرمانه مناسب را معین کند.",
"local_ptr_placeholder": "در هر خط یک آدرس سرور را وارد کنید",
"resolve_clients_title": "فعال سازی تعیین نام های سرویس دهنده ی سرویس گیرندگان",
"resolve_clients_desc": "در صورت فعال بودن،AdGuard Home به طور خودکار اقدام به تعیین نام های سرویس دهنده ی سرویس گیرندگان از آدرس های آی پی با ارسال یک درخواست PTR به یک تعیین کننده ی همتا خواهد کرد (سرور خصوصی DNS برای سرویس گیرندگان محلی،سرور مادر برای سرویس گیرندگان با آی پی عمومی).",
"use_private_ptr_resolvers_title": "از تعیین کننده های rDNS محرمانه استفاده کنید",
"use_private_ptr_resolvers_desc": "داده گیری های دی ان اس معکوس را برای آدرس های اینترنتی خدمات منظقه ای با استفاده از سرور های مادر اجرا کنید. چنانچه غیر فعال باشد،AdGuard Home با NXDOMAIN به همه چنین درخواست های PTR پاسخ می دهد به استثناء خدمات گیرنده های شناخته شده از طرف DHCP،/و غیره/هوست ها، و سایر.",
"check_dhcp_servers": "بررسی برای سرورهای DHCP",
"save_config": "ذخیره پیکربندی",
"enabled_dhcp": "سرور DHCP فعال شده است",
@@ -117,7 +113,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> آن را روشن نمایید.",
@@ -132,7 +127,6 @@
"no_upstreams_data_found": "هیچ اطلاعاتی در مورد سرورهای بالادست یافت نشد",
"number_of_dns_query_days": "تعداد جستار DNS پردازش شده در {{count}} روز آخر",
"number_of_dns_query_days_plural": "تعداد جستار DNS پردازش شده در {{count}} روز گذشته",
"number_of_dns_query_24_hours": "تعداد جستار DNS پردازش شده در 24 ساعت گذشته",
"number_of_dns_query_blocked_24_hours": "تعداد درخواست DNS مسدود شده با فیلترهای مسدودساز تبلیغ و لیست سیاه میزبان",
"number_of_dns_query_blocked_24_hours_by_sec": "تعداد درخواست DNS مسدود شده با مدل امنیت وب گردی AdGuard",
"number_of_dns_query_blocked_24_hours_adult": "تعداد وبسایت های غیر اخلاقی مسدود شده",
@@ -147,7 +141,6 @@
"use_adguard_parental": "از سرویس وب نظارت والدین AdGuard استفاده کن",
"use_adguard_parental_hint": "AdGuard Home بررسی می کند اگر دامنه حاوی موارد غیر اخلاقی است.آن از همان اِی پی آی دارای حریم خصوصی سرویس وب امنیت وب گردی استفاده می کند.",
"enforce_safe_search": "اجبار جستجوی اَمن",
"enforce_save_search_hint": "AdGuard Home میتواند جستجوی اَمن را در موتور جستجوهای زیر اعمال کند:گوگل،یوتوب،بینگ و یاندکس",
"no_servers_specified": "سروری تعیین نشده است",
"general_settings": "تنظیمات عمومی",
"dns_settings": "تنظیمات DNS",
@@ -430,7 +423,6 @@
"client_confirm_delete": "آیا واقعا میخواهید \"{{key}}\" کلاینت را حذف کنید؟",
"list_confirm_delete": "آیا واقعا میخواهید این لیست را حذف کنید؟",
"auto_clients_title": "کلاینت ها (زمان اِجرا)",
"auto_clients_desc": "داده در کلاینت ها که از AdGuard Home استفاده می کند،اما در پیکربندی ذخیره نمی شود",
"access_title": "تنظیمات دسترسی",
"access_desc": "در اینجا میتوانید دستورات دسترسی را برای DNS سرور AdGuard Home وارد کنید.",
"access_allowed_title": "کلاینت های مجاز",
@@ -589,11 +581,11 @@
"cache_optimistic_desc": "AdGuard Home را وادار می کند که از سمت حافظه پنهان پاسخ دهد حتی وقتی که موارد وارد شده منقضی شده باشد و همچنین سعی بر تازه کردن آنها می کند.",
"filter_category_general": "General",
"filter_category_security": "مسدودسازی بدافزار و فیشینگ",
"filter_category_regional": "منطقه‌ای",
"filter_category_other": "ساير",
"use_saved_key": "از کلید ذخیره شده قبلی استفاده کنید",
"parental_control": "نظارت والدین",
"safe_browsing": "وب گردی اَمن",
"form_error_password_length": "رمزعبور باید حداقل {{value}} کاراکتر باشد.",
"protection_section_label": "حفاظت",
"log_and_stats_section_label": "گزارش پرس و جو و آمار",
"ignore_query_log": "این مشتری را در گزارش پرس و جو نادیده بگیرید",

View File

@@ -6,7 +6,6 @@
"upstream_parallel": "Käytä rinnakkaisia pyyntöjä ja nopeuta selvitystä käyttämällä kaikkia ylävirtapalvelimia samanaikaisesti.",
"parallel_requests": "Rinnakkaiset pyynnöt",
"load_balancing": "Kuormantasaus",
"load_balancing_desc": "Lähetä pyyntö yhdelle ylävirtapalvelimelle kerrallaan. AdGuard Home pyrkii valitsemaan nopeimman palvelimen painotetun satunnaisalgoritminsa avulla.",
"bootstrap_dns": "Bootstrap DNS-palvelimet",
"bootstrap_dns_desc": "Ylävirroiksi määrittämiesi DoH/DoT-resolverien IP-osoitteiden selvitykseen käytettävien DNS-palvelimien IP-osoitteet. Kommentteja ei sallita.",
"fallback_dns_title": "DNS-varapalvelimet",
@@ -154,7 +153,6 @@
"use_adguard_parental": "Käytä AdGuardin lapsilukko-palvelua",
"use_adguard_parental_hint": "AdGuard Home tarkistaa, sisältääkö verkkotunnus aikuisille tarkoitettua sisältöä. Se käyttää samaa tietosuojapainotteista rajapintaa, kuin turvallisen selauksen palvelu.",
"enforce_safe_search": "Käytä turvallista hakua",
"enforce_save_search_hint": "AdGuard Home voi pakottaa turvallisen haun käyttöön seuraavissa hakukoneissa: Google, YouTube, Bing, DuckDuckGo, Yandex, Pixabay.",
"no_servers_specified": "Palvelimia ei ole määritetty",
"general_settings": "Yleiset asetukset",
"dns_settings": "DNS-asetukset",
@@ -709,9 +707,9 @@
"log_and_stats_section_label": "Pyyntöhistoria ja tilastot",
"ignore_query_log": "Älä huomioi tätä päätelaitetta pyyntöhistoriassa",
"ignore_statistics": "Älä huomioi tätä päätettä tilastoissa",
"schedule_services": "Keskeytä palveluesto",
"schedule_services_desc": "Määritä palvelunestosuodattimen keskeytysajoitus.",
"schedule_services_desc_client": "Määritä palvelunestosuodattimen keskeytysajoitus tälle päätteelle.",
"schedule_services": "Pysäytä palveluesto",
"schedule_services_desc": "Määritä palvelunestosuodattimen pysäytysajoitus.",
"schedule_services_desc_client": "Määritä palvelunestosuodattimen pysäytysajoitus tälle päätteelle.",
"schedule_desc": "Aseta estettujen palveluiden käyttämättömyysjaksot",
"schedule_invalid_select": "Aloitusaika on oltava ennen lopetusaikaa",
"schedule_select_days": "Valitse päivät",

View File

@@ -6,7 +6,7 @@
"upstream_parallel": "Utilisez des requêtes parallèles pour accélérer la résolution en requêtant simultanément tous les serveurs en amont.",
"parallel_requests": "Requêtes en parallèle",
"load_balancing": "Équilibrage de charge",
"load_balancing_desc": "Interroger un serveur en amont à la fois. AdGuard Home utilise son algorithme aléatoire pondéré pour choisir le serveur de sorte que le serveur le plus rapide soit utilisé plus souvent.",
"load_balancing_desc": "Une requête par serveur en amont à la fois. AdGuard Home utilise un algorithme aléatoire pondéré pour sélectionner les serveurs avec le plus petit nombre d'échecs de recherche et le temps de recherche moyen le plus bas.",
"bootstrap_dns": "Serveurs DNS d'amorçage",
"bootstrap_dns_desc": "Les adresses IP des serveurs DNS utilisées pour résoudre les adresses IP des résolveurs DoH/DoT que vous spécifiez comme en amont. Les commentaires ne sont pas autorisés.",
"fallback_dns_title": "Serveurs DNS de repli",
@@ -154,7 +154,7 @@
"use_adguard_parental": "Utiliser le contrôle parental d'AdGuard",
"use_adguard_parental_hint": "AdGuard Home va vérifier s'il y a du contenu pour adultes sur le domaine. Ce sera fait par aide du même API discret que celui utilisé par le service de Sécurité de navigation.",
"enforce_safe_search": "Utiliser la Recherche Sécurisée",
"enforce_save_search_hint": "AdGuard Home appliquera la recherche sécurisée dans les moteurs de recherche suivants : Google, YouTube, Bing, DuckDuckGo, Yandex, Pixabay.",
"enforce_save_search_hint": "AdGuard Home appliquera la recherche sécurisée dans les moteurs de recherche suivants : Google, YouTube, Bing, DuckDuckGo, Ecosia, Yandex, Pixabay.",
"no_servers_specified": "Pas de serveurs spécifiés",
"general_settings": "Paramètres généraux",
"dns_settings": "Paramètres DNS",
@@ -676,7 +676,7 @@
"filter_allowlist": "ATTENTION : Cette action exclura également la règle « {{disallowed_rule}} » de la liste des clients autorisés.",
"last_rule_in_allowlist": "Impossible dinterdire ce client, car lexclusion de la règle « {{disallowed_rule}} » DÉSACTIVERA la liste des « clients autorisés ».",
"use_saved_key": "Utiliser la clef précédemment enregistrée",
"parental_control": "Contrôle parental",
"parental_control": "Contrôle Parental",
"safe_browsing": "Navigation sécurisée",
"served_from_cache_label": "Servi depuis le cache",
"form_error_password_length": "Le mot de passe doit comporter entre {{min}} et {{max}}  caractères",

View File

@@ -6,21 +6,20 @@
"upstream_parallel": "Koristi paralelne upite kako bi ubrzali rješavanje istovremenim ispitavanjem svih upstream poslužitelja.",
"parallel_requests": "Paralelni zahtjevi",
"load_balancing": "Load-balancing",
"load_balancing_desc": "Pitajte jedan po jedan uzvodni poslužitelj. AdGuard Home koristi svoj ponderirani slučajni algoritam za odabir poslužitelja tako da se najbrži poslužitelj koristi češće.",
"bootstrap_dns": "Bootstrap DNS poslužitelji",
"bootstrap_dns_desc": "IP adrese DNS poslužitelja koji se koriste za rješavanje IP adresa DoH/DoT razrjeđivača koje navedete kao uzvodne. Komentari nisu dopušteni.",
"fallback_dns_title": "Rezervni DNS poslužitelji",
"fallback_dns_desc": "Popis rezervnih DNS poslužitelja koji se koriste kada uzvodni DNS poslužitelji ne odgovaraju. Sintaksa je ista kao u gornjem polju glavnog uzvodnog toka.",
"fallback_dns_placeholder": "Unesite jedan rezervni DNS poslužitelj po retku",
"local_ptr_title": "Privatni obrnuti DNS poslužitelji",
"local_ptr_desc": "DNS poslužitelji koje AdGuard Home koristi za lokalne PTR upite. Ti se poslužitelji koriste za razrješavanje naziva glavnog računala klijenata s privatnim IP adresama, na primjer \"192.168.12.34\", koristeći obrnuti DNS. Ako nije postavljeno, AdGuard Home koristi adrese zadanih DNS razrješivača vašeg OS-a, osim za adrese samog AdGuard Homea.",
"local_ptr_desc": "DNS poslužitelji koje koristi AdGuard Home za privatne PTR, SOA i NS zahtjeve. Zahtjev se smatra privatnim ako traži ARPA domenu koja sadrži podmrežu unutar privatnih IP raspona (kao što je \"192.168.12.34\") i dolazi od klijenta s privatnom IP adresom. Ako nije postavljeno, koristit će se zadani DNS rezolveri vašeg OS-a, osim za AdGuard Home IP adrese.",
"local_ptr_default_resolver": "Prema zadanim postavkama AdGuard Home koristi sljedeće obrnute DNS razrješivače: {{ip}}.",
"local_ptr_no_default_resolver": "AdGuard Home nije mogao odrediti prikladne privatne obrnute DNS razrješivače za ovaj sustav.",
"local_ptr_placeholder": "Unesite jednu adresu poslužitelja po retku",
"resolve_clients_title": "Omogući obrnuto rješavanje IP adresa klijenata",
"resolve_clients_desc": "Obrnuto razriješite IP adrese klijenata u nazive glavnih računala slanjem PTR upita odgovarajućim razrješivačima (privatni DNS poslužitelji za lokalne klijente, uzvodni poslužitelji za klijente s javnim IP adresama).",
"use_private_ptr_resolvers_title": "Koristi privatne reverzne DNS razrješivače",
"use_private_ptr_resolvers_desc": "Izvršite obrnuta DNS traženja za lokalno poslužene adrese pomoću ovih uzlaznih poslužitelja. Ako je onemogućen, AdGuard Home odgovara S NXDOMAIN-om na sve takve PTR zahtjeve osim za klijente poznate iz DHCP-a, /etc/hosts i tako dalje.",
"use_private_ptr_resolvers_desc": "Razriješi PTR, SOA i NS zahtjeve za ARPA domene koje sadrže privatne IP adrese putem privatnih uzvodnih poslužitelja, DHCP-a, /etc/hostova itd. Ako je onemogućeno, AdGuard Home će na sve takve zahtjeve odgovoriti s NXDOMAIN.",
"check_dhcp_servers": "Provjera DHCP poslužitelja",
"save_config": "Spremi konfiguraciju",
"enabled_dhcp": "DHCP poslužitelj je omogućen",
@@ -154,7 +153,6 @@
"use_adguard_parental": "Koristi web uslugu AdGuard roditeljske zaštite",
"use_adguard_parental_hint": "AdGuard Home provjeriti će sadrži li domena sadržaj za odrasle. Koristi isti API za zaštitu privatnosti kao i naša usluga zaštite pregledavanja.",
"enforce_safe_search": "Koristi sigurno pretraživanje",
"enforce_save_search_hint": "AdGuard Home provodit će sigurno pretraživanje u sljedećim tražilicama: Google, YouTube, Bing, DuckDuckGo, Yandex, Pixabay.",
"no_servers_specified": "Nije odabran nijedan poslužitelj",
"general_settings": "Opće postavke",
"dns_settings": "DNS postavke",
@@ -425,6 +423,9 @@
"encryption_hostnames": "Nazivi računala",
"encryption_reset": "Jeste li sigurni da želite poništiti postavke šifriranja?",
"encryption_warning": "Upozorenje",
"encryption_plain_dns_enable": "Omogući obični DNS",
"encryption_plain_dns_desc": "Obični DNS je omogućen prema zadanim postavkama. Možete ga onemogućiti kako biste prisilili sve uređaje da koriste šifrirani DNS. Da biste to učinili, morate omogućiti barem jedan kriptirani DNS protokol",
"encryption_plain_dns_error": "Da biste onemogućili obični DNS, omogućite barem jedan kriptirani DNS protokol",
"topline_expiring_certificate": "Vaš SSL certifikat uskoro ističe. Ažurirajte <0>Postavke šifriranja</0>.",
"topline_expired_certificate": "Vaš SSL certifikat je istekao. Ažurirajte <0>Postavke šifriranja</0>.",
"form_error_port_range": "Unesite broj porta od 80 do 65536",
@@ -675,7 +676,7 @@
"use_saved_key": "Korištenje prethodno spremljenog ključa",
"parental_control": "Roditeljska zaštita",
"safe_browsing": "Sigurno surfanje",
"served_from_cache": "{{value}} <i>(dohvaćeno iz predmemorije)</i>",
"served_from_cache_label": "Posluženo iz predmemorije",
"form_error_password_length": "Lozinka mora sadržavati od {{min}} do {{max}} znakova",
"anonymizer_notification": "<0>Napomena:</0>IP anonimizacija je omogućena. Možete ju onemogućiti u <1>općim postavkama</1>.",
"confirm_dns_cache_clear": "Jeste li sigurni da želite očistiti DNS predmemoriju?",

View File

@@ -6,21 +6,18 @@
"upstream_parallel": "Használjon párhuzamos lekéréseket a domainek feloldásának felgyorsításához az összes upstream kiszolgálóra való egyidejű lekérdezéssel.",
"parallel_requests": "Párhuzamos lekérések",
"load_balancing": "Terheléselosztás",
"load_balancing_desc": "Egyszerre csak egy szerverről történjen lekérdezés. Az AdGuard Home egy súlyozott, véletlenszerű algoritmust fog használni a megfelelő szerver kiválasztására, így a leggyorsabb szervert fogja a leggyakrabban használni.",
"bootstrap_dns": "Bootstrap DNS kiszolgálók",
"bootstrap_dns_desc": "A DNS-kiszolgálók IP-címei, amelyek a DoH/DoT-feloldók IP-címeinek feloldására szolgálnak, amelyeket upstreamként megadott. Megjegyzések nem megengedettek.",
"fallback_dns_title": "Tartalék DNS-szerverek",
"fallback_dns_desc": "Azoknak a tartalék DNS-szervereknek a listája, amelyeket akkor használnak, ha a felsőbbrendű DNS-szerverek nem válaszolnak. A szintaxis ugyanaz, mint a fő felsőbbrendű mezőben.",
"fallback_dns_placeholder": "Adjon meg egy alternatív DNS szervert soronként",
"local_ptr_title": "Privát DNS szerverek",
"local_ptr_desc": "Azok a DNS szerverek, amiket az AdGuard Home a helyi PTR kérésekhez használ. ",
"local_ptr_default_resolver": "Alapesetben az AdGuard Home a következő reverse DNS feloldókat használja: {{ip}}.",
"local_ptr_no_default_resolver": "Az AdGuard Home nem tudta meghatározni a privát reverse DNS feloldókat ehhez a rendszerhez.",
"local_ptr_placeholder": "Adjon meg egy IP-címet soronként",
"resolve_clients_title": "Kliensek IP címeinek fordított feloldása",
"resolve_clients_desc": "Fordítva oldja fel a kliensek IP címeit a hosztneveikre azáltal, hogy PTR lekérdezéseket küld a megfelelő feloldóknak (privát DNS szerverek a helyi kliensek számára, upstream szerverek a nyilvános IP címmel rendelkező kliensek számára).",
"use_private_ptr_resolvers_title": "Privát reverse DNS feloldók használata",
"use_private_ptr_resolvers_desc": "Reverse DNS keresések végzése a helyileg kiszolgált címekre ezeknek a szervereknek a használatával. Ha le van tiltva, az AdGuard Home az NXDOMAIN használatával válaszol minden ilyen PTR kérésre, kivéve azokat a klienseket, amelyeket már ismer a DHCP, /etc/hosts, stb. által.",
"check_dhcp_servers": "DHCP szerverek keresése",
"save_config": "Konfiguráció mentése",
"enabled_dhcp": "DHCP szerver engedélyezve",
@@ -154,7 +151,6 @@
"use_adguard_parental": "Használja az AdGuard szülői felügyelet webszolgáltatását",
"use_adguard_parental_hint": "Az AdGuard Home ellenőrzi, hogy a domain tartalmaz-e felnőtteknek szóló anyagokat. Ugyanazokat az adatvédelmi API-kat használja, mint a böngésző biztonsági webszolgáltatás.",
"enforce_safe_search": "Biztonságos keresés használata",
"enforce_save_search_hint": "Az AdGuard Home a következő keresőmotorokban biztosíthatja a biztonságos keresést: Google, Youtube, Bing, DuckDuckGo, Yandex és Pixabay.",
"no_servers_specified": "Nincsenek megadott kiszolgálók",
"general_settings": "Általános beállítások",
"dns_settings": "DNS beállítások",
@@ -675,7 +671,6 @@
"use_saved_key": "Előzőleg mentett kulcs használata",
"parental_control": "Szülői felügyelet",
"safe_browsing": "Biztonságos böngészés",
"served_from_cache": "{{value}} <i>(gyorsítótárból kiszolgálva)</i>",
"form_error_password_length": "A jelszó legyen {{min}} és {{max}} karakter között",
"anonymizer_notification": "<0>Megjegyzés:</0> Az IP anonimizálás engedélyezve van. Az <1>Általános beállításoknál letilthatja</1> .",
"confirm_dns_cache_clear": "Biztos benne, hogy törölni szeretné a DNS-gyorsítótárat?",

View File

@@ -6,21 +6,18 @@
"upstream_parallel": "Gunakan kueri paralel untuk mempercepat resoluasi dengan menanyakan semua server upstream secara bersamaan",
"parallel_requests": "Permintaan paralel",
"load_balancing": "Penyeimbang beban",
"load_balancing_desc": "Permintaan satu server pada satu waktu. AdGuard Home akan menggunakan algoritma acak tertimbang untuk memilih server sehingga server tercepat akan lebih sering digunakan.",
"bootstrap_dns": "Server DNS bootstrap",
"bootstrap_dns_desc": "Alamat IP server DNS yang digunakan untuk menyelesaikan alamat IP resolver DoH/DoT yang Anda tentukan sebagai upstream. Komentar tidak diizinkan.",
"fallback_dns_title": "Server DNS cadangan",
"fallback_dns_desc": "Daftar server DNS cadangan yang digunakan ketika server hulu DNS tidak merespons. Sintaksnya sama dengan kolom hulu utama di atas.",
"fallback_dns_placeholder": "Masukkan satu server DNS cadangan per baris",
"local_ptr_title": "Server pembalik DNS pribadi",
"local_ptr_desc": "Server DNS yang digunakan AdGuard Home untuk kueri PTR lokal. Server ini digunakan untuk menyelesaikan nama host klien dengan alamat IP pribadi, misalnya \"192.168.12.34\", menggunakan DNS terbalik. Jika tidak disetel, AdGuard Home menggunakan alamat resolver DNS default OS Anda kecuali untuk alamat AdGuard Home itu sendiri.",
"local_ptr_default_resolver": "Secara bawaan, AdGuard Home menggunakan pemecah DNS terbalik: {{ip}}.",
"local_ptr_no_default_resolver": "AdGuard Home tidak dapat menentukan pemecah DNS terbalik yang sesuai untuk sistem ini.",
"local_ptr_placeholder": "Masukkan satu alamat IP per baris",
"resolve_clients_title": "Aktifkan resolusi hostname klien",
"resolve_clients_desc": "Menyelesaikan alamat IP klien secara terbalik ke nama host mereka dengan mengirimkan kueri PTR ke resolver yang sesuai (server DNS pribadi untuk klien lokal, server upstream untuk klien dengan alamat IP publik).",
"use_private_ptr_resolvers_title": "Gunakan server pembalik DNS pribadi",
"use_private_ptr_resolvers_desc": "Lakukan pencarian DNS terbalik untuk alamat yang disajikan secara lokal menggunakan server hulu ini. Jika dinonaktifkan, Adguard Home merespons dengan NXDOMAIN untuk semua permintaan PTR tersebut kecuali untuk klien yang diketahui dari DHCP, /etc/hosts, dan seterusnya.",
"check_dhcp_servers": "Cek untuk server DHCP",
"save_config": "Simpan pengaturan",
"enabled_dhcp": "Server DHCP diaktifkan",
@@ -72,13 +69,11 @@
"dhcp_error": "AdGuard Home tidak dapat menentukan apakah ada server DHCP aktif lain pada jaringan",
"dhcp_static_ip_error": "Jika ingin menggunakan server DHCP, alamat IP statis harus diatur. AdGuard Home gagal menentukan jika antarmuka jaringan ini dikonfigurasi menggunakan alamat IP statis. Silakan atur alamat IP statis secara manual.",
"dhcp_dynamic_ip_found": "Sistem Anda menggunakan konfigurasi alamat IP dinamis untuk antarmuka <0>{{interfaceName}}</0>. Untuk menggunakan server DHCP, alamat IP statis harus ditetapkan. Alamat IP Anda saat ini adalah <0>{{ipAddress}}</0>. AdGuard Home akan secara otomatis menetapkan alamat IP ini sebagai statis jika Anda menekan tombol Aktifkan DHCP.",
"dhcp_lease_added": "Static lease \"{{key}}\" berhasil ditambahkan",
"dhcp_lease_deleted": "Static lease \"{{key}}\" berhasil dihapus",
"dhcp_lease_updated": "Static lease \"{{key}}\" berhasil diperbarui",
"dhcp_new_static_lease": "Static lease baru",
"dhcp_edit_static_lease": "Mengedit static lease",
"dhcp_static_leases_not_found": "DHCP static lease tidak ditemukan",
"dhcp_add_static_lease": "Tambah static lease",
"dhcp_reset_leases": "Atur ulang semua kontrak",
"dhcp_reset_leases_confirm": "Apakah Anda yakin ingin mengatur ulang kontrak Anda?",
"dhcp_reset_leases_success": "Kontrak DHCP berhasil diatur ulang",
@@ -147,14 +142,13 @@
"average_upstream_response_time": "Rata-rata waktu respons hulu",
"response_time": "Waktu respons",
"average_processing_time_hint": "Rata-rata waktu dalam milidetik untuk pemrosesan sebuah permintaan DNS",
"block_domain_use_filters_and_hosts": "Blokir domain menggunakan filter dan file hosts",
"block_domain_use_filters_and_hosts": "Blokir domain menggunakan filter dan berkas host",
"filters_block_toggle_hint": "Anda dapat menyiapkan aturan pemblokiran dalam pengaturan <a>Filter</a>.",
"use_adguard_browsing_sec": "Gunakan layanan web Keamanan Penjelajahan AdGuard",
"use_adguard_browsing_sec_hint": "AdGuard Home akan memeriksa apakah domain diblokir oleh layanan web keamanan penjelajahan. Ini akan menggunakan API pencarian yang ramah privasi untuk melakukan pemeriksaan: hanya awalan singkat dari hash nama domain SHA256 yang dikirim ke server.",
"use_adguard_parental": "Gunakan layanan web kontrol orang tua AdGuard",
"use_adguard_parental_hint": "AdGuard Home akan mengecek jika domain mengandung materi dewasa. Akan menggunakan API yang ramah privasi yang sama sebagai layanan web keamanan penjelajahan.",
"enforce_safe_search": "Pakai pencarian aman",
"enforce_save_search_hint": "AdGuard Home dapat memaksa penelusuran aman pada mesin pencari berikut: Google, Youtube, Bing, DuckDuckGo, Yandex, dan Pixabay.",
"no_servers_specified": "Sever tidak disebutkan",
"general_settings": "Pengaturan umum",
"dns_settings": "Pengaturan DNS",
@@ -191,7 +185,7 @@
"edit_table_action": "Ubah",
"delete_table_action": "Hapus",
"elapsed": "Berlalu",
"filters_and_hosts_hint": "AdGuard Home memahami aturan dasar adblock dan sintak file hosts.",
"filters_and_hosts_hint": "AdGuard Home memahami aturan dasar adblock dan sintak berkas host.",
"no_blocklist_added": "Tidak ada daftar hitam yang ditambahkan",
"no_whitelist_added": "Tidak ada daftar putih yang ditambahkan",
"add_blocklist": "Tambahkan daftar hitam",
@@ -211,8 +205,8 @@
"form_error_url_format": "Format URL tidak valid",
"form_error_url_or_path_format": "URL atau jalur absolut dari daftar tidak valid",
"custom_filter_rules": "Aturan penyaringan khusus",
"custom_filter_rules_hint": "Masukkan satu aturan dalam sebuah baris. Anda dapat menggunakan baik aturan adblock maupun sintaks file hosts.",
"system_host_files": "File host sistem",
"custom_filter_rules_hint": "Masukkan satu aturan pada satu baris. Anda dapat menggunakan aturan adblock atau sintaks berkas host.",
"system_host_files": "Berkas host sistem",
"examples_title": "Contoh",
"example_meaning_filter_block": "blokir akses ke example.org dan seluruh subdomainnya;",
"example_meaning_filter_whitelist": "buka blokir akses ke domain example.orf dan seluruh subdomainnya;",
@@ -476,7 +470,7 @@
"client_confirm_delete": "Apakah anda yakin ingin menghapus klien \"{{key}}\"?",
"list_confirm_delete": "Anda yakin ingin menghapus daftar ini?",
"auto_clients_title": "Klien (waktu berjalan)",
"auto_clients_desc": "Informasi tentang alamat IP perangkat yang menggunakan atau mungkin menggunakan AdGuard Home. Informasi ini dikumpulkan dari beberapa sumber, termasuk file host, reverse DNS, dll.",
"auto_clients_desc": "Informasi tentang alamat IP perangkat yang menggunakan atau mungkin menggunakan AdGuard Home. Informasi ini dikumpulkan dari beberapa sumber, termasuk berkas host, DNS terbalik, dll.",
"access_title": "Pengaturan akses",
"access_desc": "Disini anda dapat mengatur aturan akses untuk server AdGuard Home DNS",
"access_allowed_title": "Klien yang diizinkan",
@@ -494,7 +488,7 @@
"setup_dns_privacy_1": "<0>DNS melalui TLS:</0> Gunakan <1>{{address}}</1> string.",
"setup_dns_privacy_2": "<0>DNS-over-TLS:</0> Memakai <1>{{address}}</1> string.",
"setup_dns_privacy_3": "<0>Berikut daftar perangkat lunak yang dapat Anda gunakan.</0>",
"setup_dns_privacy_4": "Di perangkat iOS 14 atau macOS Big Sur, Anda dapat mengunduh file '.mobileconfig' khusus yang menambahkan server <highlight>DNS-over-HTTPS</highlight> atau <highlight>DNS-over-TLS</highlight> ke pengaturan DNS.",
"setup_dns_privacy_4": "Pada perangkat iOS 14 atau macOS Big Sur, Anda dapat mengunduh berkas khusus '.mobileconfig' yang menambahkan server <highlight>DNS melalui HTTPS</highlight> atau <highlight>DNS melalui TLS</highlight> ke pengaturan DNS.",
"setup_dns_privacy_android_1": "Android 9 mendukung DNS-over-TLS secara asli. Untuk mengkonfigurasinya, buka Pengaturan → Jaringan & internet → Tingkat Lanjut → DNS Pribadi dan masukkan nama domain Anda di sana.",
"setup_dns_privacy_android_2": "<0>AdGuard untuk Android</0> mendukung <1>DNS-over-HTTPS</1> dan <1>DNS-over-TLS</1>.",
"setup_dns_privacy_android_3": "<0>Intra</0> menambahkan dukungan <1>DNS-over-HTTPS</1> untuk Android.",
@@ -517,7 +511,7 @@
"rewrite_confirm_delete": "Apakah anda yakin ingin menghapus DNS rewrite untuk \"{{key}}\"?",
"rewrite_desc": "Memungkinkan untuk dengan mudah mengkonfigurasi respons DNS kustom untuk nama domain tertentu.",
"rewrite_applied": "Aturan Rewrite yang diterapkan",
"rewrite_hosts_applied": "Ditulis ulang oleh aturan file hosts",
"rewrite_hosts_applied": "Ditulis ulang oleh aturan berkas host",
"dns_rewrites": "DNS rewrite",
"form_domain": "Masukkan nama domain",
"form_answer": "Masaukan alamat IP atau nama domain",
@@ -676,9 +670,8 @@
"filter_allowlist": "PERINGATAN: Tindakan ini juga akan mengecualikan aturan \"{{disallowed_rule}}\" dari daftar klien yang diizinkan.",
"last_rule_in_allowlist": "Tidak dapat melarang klien ini karena mengecualikan aturan \"{{disallowed_rule}}\" akan MENONAKTIFKAN daftar \"Klien yang diizinkan\".",
"use_saved_key": "Gunakan kunci yang disimpan sebelumnya",
"parental_control": "Kontrol Orang Tua",
"parental_control": "Pengawasan Orang Tua",
"safe_browsing": "Penjelajahan Aman",
"served_from_cache": "{{value}} <i>(disajikan dari cache)</i>",
"form_error_password_length": "Kata sandi harus terdiri dari {{min}} hingga {{max}}",
"anonymizer_notification": "<0>Catatan:</0> Anonimisasi IP diaktifkan. Anda dapat menonaktifkannya di <1>Pengaturan umum</1> .",
"confirm_dns_cache_clear": "Apakah Anda yakin ingin menghapus cache DNS?",

View File

@@ -6,7 +6,7 @@
"upstream_parallel": "Utilizza richieste parallele per accelerare la risoluzione interrogando simultaneamente tutti i server upstream.",
"parallel_requests": "Richieste parallele",
"load_balancing": "Bilanciamento del carico",
"load_balancing_desc": "Interroga un server upstream per volta. AdGuard Home utilizzerà un algoritmo casuale ponderato per la selezione del server, in maniera tale da scegliere spesso il più veloce.",
"load_balancing_desc": "Esegui una query su un server upstream alla volta. AdGuard Home utilizza un algoritmo casuale ponderato per selezionare i server con il minor numero di ricerche fallite e il tempo medio di ricerca più basso.",
"bootstrap_dns": "Server DNS bootstrap",
"bootstrap_dns_desc": "Indirizzi IP dei server DNS utilizzati per risolvere gli indirizzi IP dei resolver DoH/DoT specificati come upstream. I commenti non sono ammessi.",
"fallback_dns_title": "Server DNS di fallback",
@@ -154,7 +154,7 @@
"use_adguard_parental": "Utilizza il Controllo Parentale di AdGuard",
"use_adguard_parental_hint": "AdGuard Home verificherà se il dominio contiene materiale per adulti. Utilizza le stesse API privacy-friendly del servizio web 'sicurezza di navigazione'.",
"enforce_safe_search": "Utilizza Ricerca Sicura",
"enforce_save_search_hint": "AdGuard Home forzerà la ricerca sicura sui seguenti motori di ricerca: Google, YouTube, Bing, DuckDuckGo, Yandex, Pixabay.",
"enforce_save_search_hint": "AdGuard Home applicherà la ricerca sicura nei seguenti motori di ricerca: Google, YouTube, Bing, DuckDuckGo, Ecosia, Yandex, Pixabay.",
"no_servers_specified": "Nessun server specificato",
"general_settings": "Impostazioni generali",
"dns_settings": "Impostazioni DNS",

View File

@@ -6,7 +6,7 @@
"upstream_parallel": "並列リクエストを使用する(同時にすべてのアップストリームサーバーに処理要求することで解決スピードが向上)",
"parallel_requests": "並列リクエスト",
"load_balancing": "ロードバランシング",
"load_balancing_desc": "一度に1つのアップストリームサーバに処理要求します。 AdGuard Homeは、重み付きランダムアルゴリズムweighted random algorithmを使用してサーバを選択するため、最速のサーバがより頻繁に使用されます。",
"load_balancing_desc": "一度に1つのアップストリームサーバーをクエリします。AdGuard Home は、重み付き乱択アルゴリズムを使用して、ルックアップに失敗した回数が最も少なく、平均ルックアップ時間が最も短いサーバーを選択します。",
"bootstrap_dns": "ブートストラップDNSサーバ",
"bootstrap_dns_desc": "アップストリームとして指定したDoH/DoTリゾルバのIPアドレスを解決するために使用されるDNSサーバーのIPアドレスです。コメントは許可されていません",
"fallback_dns_title": "フォールバックDNSサーバー",
@@ -154,7 +154,7 @@
"use_adguard_parental": "AdGuardペアレンタルコントロール・ウェブサービスを使用する",
"use_adguard_parental_hint": "AdGuard Homeは、ドメインにアダルトコンテンツが含まれているかどうかを確認します。 ブラウジングセキュリティ・ウェブサービスと同じプライバシーに優しいAPIを使用します。",
"enforce_safe_search": "セーフサーチを使用する",
"enforce_save_search_hint": "AdGuard Homeは、次の検索エンジンでセーフサーチを強制適用します: Google, YouTube, Bing, DuckDuckGo, Yandex, Pixabay",
"enforce_save_search_hint": "AdGuard Homeは、次の検索エンジンでセーフサーチを強制適用します: Google, YouTube, Bing, DuckDuckGo, Ecosia, Yandex, Pixabay",
"no_servers_specified": "サーバが指定されていません",
"general_settings": "一般設定",
"dns_settings": "DNS設定",

View File

@@ -6,7 +6,7 @@
"upstream_parallel": "쿼리 처리 속도를 높이려면 모든 업스트림 서버에서 동시에 병렬 쿼리를 사용해주세요.",
"parallel_requests": "병렬 처리 요청",
"load_balancing": "로드 밸런싱",
"load_balancing_desc": "한 번에 하나의 서버씩 질의합니다. AdGuard Home은 가중 랜덤 알고리즘 사용해서 가장 빠른 서버가 자주 사용되도록 서버를 선택합니다.",
"load_balancing_desc": "한 번에 하나의 업스트림 서버를 쿼리합니다. AdGuard Home은 가중 무작위 알고리즘 사용하여 조회 실패 횟수가 가장 적고 평균 조회 시간이 가장 짧은 서버를 선택합니다.",
"bootstrap_dns": "부트스트랩 DNS 서버",
"bootstrap_dns_desc": "업스트림으로 지정한 DoH/DoT 리졸버의 IP 주소를 확인하는 데 사용되는 DNS 서버의 IP 주소입니다. 주석은 허용되지 않습니다.",
"fallback_dns_title": "폴백 DNS 서버",
@@ -108,7 +108,7 @@
"off": "OFF",
"copyright": "Copyright",
"homepage": "홈페이지",
"report_an_issue": "문제를 보고합니다",
"report_an_issue": "문제 신고",
"privacy_policy": "개인정보취급방침",
"enable_protection": "보호 활성화",
"enabled_protection": "보호 활성화됨",
@@ -154,7 +154,7 @@
"use_adguard_parental": "AdGuard 자녀 보호 웹 서비스 사용",
"use_adguard_parental_hint": "AdGuard Home은 도메인에 성인 자료가 포함되어 있는지 확인합니다. 브라우징 보안 웹 서비스와 동일한 개인정보 보호 API를 사용함.",
"enforce_safe_search": "세이프서치 사용",
"enforce_save_search_hint": "AdGuard Home은 Google, YouTube, Bing, DuckDuckGo, Yandex, Pixabay와 같은 검색 엔진에서 세이프서치를 시행합니다.",
"enforce_save_search_hint": "AdGuard Home은 Google, YouTube, Bing, DuckDuckGo, Ecosia, Yandex, Pixabay와 같은 검색 엔진에서 세이프서치를 시행합니다.",
"no_servers_specified": "지정된 서버 없음",
"general_settings": "일반 설정",
"dns_settings": "DNS 설정",

View File

@@ -6,7 +6,7 @@
"upstream_parallel": "Parallelle verzoeken gebruiken om te versnellen door gelijktijdig verzoeken te sturen naar alle upstream servers.",
"parallel_requests": "Parallelle verzoeken",
"load_balancing": "Volume balanceren",
"load_balancing_desc": "Eén server per keer bevragen. AdGuard Home gebruikt hiervoor een gewogen willekeurig algoritme om de server te kiezen zodat de snelste server meer zal gebruikt worden.",
"load_balancing_desc": "Voer zoekopdrachten uit op één upstream-server tegelijk. AdGuard Home gebruikt een gewogen willekeurig algoritme om servers te selecteren met het laagste aantal mislukte zoekopdrachten en de laagste gemiddelde opzoektijd.",
"bootstrap_dns": "Bootstrap DNS-servers",
"bootstrap_dns_desc": "IP-adressen van DNS-servers die worden gebruikt om IP-adressen om te zetten van de DoH/DoT-resolvers die je opgeeft als upstreams. Opmerkingen zijn niet toegestaan.",
"fallback_dns_title": "Back-up DNS-servers",
@@ -154,7 +154,7 @@
"use_adguard_parental": "Gebruik AdGuard Ouderlijk toezicht web service",
"use_adguard_parental_hint": "AdGuard Home controleert of het domein 18+ content bevat. Dit gebeurt dmv dezelfde privacy vriendelijke API als de Browsing Security web service.",
"enforce_safe_search": "Veilig zoeken gebruiken",
"enforce_save_search_hint": "AdGuard Home kan veilig zoeken forceren voor de volgende zoekmachines: Google, Youtube, Bing, en DuckDuckGo, Yandex, Pixabay.",
"enforce_save_search_hint": "AdGuard Home dwingt veilig zoeken af in de volgende zoekmachines: Google, YouTube, Bing, DuckDuckGo, Ecosia, Yandex, Pixabay.",
"no_servers_specified": "Geen servers gespecificeerd",
"general_settings": "Algemene instellingen",
"dns_settings": "DNS instellingen",
@@ -495,7 +495,7 @@
"setup_dns_privacy_2": "<0>DNS-via-HTTPS:</0> Gebruik <1>{{address}}</1> string.",
"setup_dns_privacy_3": "<0>Hou er rekening mee dat het beveiligde DNS protocol alleen beschikbaar is voor Android 9. U moet dus extra software installeren voor andere besturingssystemen.</0><0>Hier is een lijst van te gebruiken software.</0>",
"setup_dns_privacy_4": "Op een iOS 14 of macOS Big Sur apparaat kan je een speciaal '.mobileconfig'-bestand downloaden dat <highlight>DNS-via-HTTPS</highlight> of <highlight>DNS-via-TLS</highlight> servers aan de DNS-instellingen toevoegt.",
"setup_dns_privacy_android_1": "Android 9 ondersteunt native DNS-via-TLS. Om het te configureren, ga naar Instellingen → Netwerk & internet → Geavanceerd → Privé DNS en voer daar je domeinnaam in.",
"setup_dns_privacy_android_1": "Android 9 ondersteunt native DNS-via-TLS. Om het te configureren, ga naar Instellingen → Netwerk & internet → Geavanceerd → Privé-DNS en voer daar je domeinnaam in.",
"setup_dns_privacy_android_2": "<0>AdGuard voor Android</0>ondersteunt<1>DNS-via-HTTPS </1>en<1>DNS-via-TLS</1>.",
"setup_dns_privacy_android_3": "<0> Intra </0> voegt <1> DNS-via-HTTPS</1> ondersteuning toe aan Android.",
"setup_dns_privacy_ios_1": "<0>DNSCloak</0> ondersteunt <1> DNS-via-HTTPS </1>, maar om het te configureren op jouw eigen server moet er een <2> DNS-stempel </2> gegenereerd worden.",
@@ -676,7 +676,7 @@
"filter_allowlist": "WAARSCHUWING: Deze actie zal ook de regel \"{{disallowed_rule}}\" uitsluiten van de lijst met toegestane clients.",
"last_rule_in_allowlist": "Kan deze client niet weigeren omdat het uitsluiten van de regel \"{{disallowed_rule}}\" de lijst \"Toegestane clients\" zal UITSCHAKELEN.",
"use_saved_key": "De eerder opgeslagen sleutel gebruiken",
"parental_control": "Ouderlijk toezicht",
"parental_control": "Ouderlijk Toezicht ",
"safe_browsing": "Veilig browsen",
"served_from_cache_label": "Geleverd vanuit cache",
"form_error_password_length": "Wachtwoord moet {{min}} tot {{max}} tekens lang zijn",

View File

@@ -5,17 +5,14 @@
"upstream_parallel": "Bruk parallele forespørsler for å få oppfarten på behandlinger, ved å forespørre til alle oppstrømstjenerne samtidig",
"parallel_requests": "Parallelle forespørsler",
"load_balancing": "Pågangstrykk-utjevning",
"load_balancing_desc": "Forespør én tjener om gangen. AdGuard Home vil bruke en 'vektlagt tilfeldig valg'-algoritme for å velge tjener, slik at den raskeste tjeneren blir brukt oftere.",
"bootstrap_dns": "Bootstrap-DNS-tjenere",
"bootstrap_dns_desc": "IP-adresser til DNS-servere som brukes til å løse IP-adresser til DoH/DoT-løsere du spesifiserer som oppstrøms. Kommentarer er ikke tillatt.",
"fallback_dns_title": "Reserve DNS-servere",
"fallback_dns_desc": "Liste over reserve-DNS-servere som brukes når oppstrøms DNS-servere ikke svarer. Syntaksen er den samme som i hovedoppstrømsfeltet ovenfor.",
"fallback_dns_placeholder": "Angi én reserve-DNS-server per linje",
"local_ptr_title": "Private DNS-tjenere",
"local_ptr_desc": "DNS-tjenerne som AdGuard Home bruker for lokale PTR-spørringer. Disse tjenerne brukes til å løse vertsnavnene til klienter med private IP-adresser, for eksempel \"192.168.12.34\", ved bruk av omvendt DNS. Hvis det ikke er angitt, bruker AdGuard Home adressene til standard-DNS-løserne til operativsystemet ditt, bortsett fra adressene til selve AdGuard Home.",
"local_ptr_default_resolver": "Som standard, bruker AdGuard Home følgende revers-DNS-oppletere: {{ip}}.",
"local_ptr_no_default_resolver": "AdGuard Home klarte ikke å finne egnede private revers-DNS-oppletere for dette systemet.",
"local_ptr_placeholder": "Skriv inn én tjeneradresse per linje",
"resolve_clients_title": "Skru på revers-oppleting av klienters IP-adresser",
"use_private_ptr_resolvers_title": "Bruk private omvendte DNS-løsere",
"check_dhcp_servers": "Se etter DHCP-tjenere",
@@ -106,7 +103,6 @@
"stats_malware_phishing": "Blokkert skadevare/phishing",
"stats_adult": "Blokkerte voksennettsteder",
"stats_query_domain": "Mest forespurte domener",
"for_last_24_hours": "de siste 24 timene",
"for_last_days": "for den siste {{count}} dagen",
"for_last_days_plural": "de siste {{count}} dagene",
"stats_disabled": "Statistikkene har blitt skrudd av. Du kan skru den på fra <0>innstillingssiden</0>.",
@@ -121,13 +117,13 @@
"no_upstreams_data_found": "Ingen oppstrøms servere data funnet",
"number_of_dns_query_days": "Antall DNS-spørringer behandlet for de siste {{count}} dagene",
"number_of_dns_query_days_plural": "Antall DNS-forespørsler som ble behandlet de siste {{count}} dagene",
"number_of_dns_query_24_hours": "Antall DNS-forespørsler som ble behandlet de siste 24 timene",
"number_of_dns_query_blocked_24_hours": "Antall DNS-forespørsler som ble blokkert av adblock-filtre, hosts-lister, og domene-lister",
"number_of_dns_query_blocked_24_hours_by_sec": "Antall DNS-forespørsler som ble blokkert av AdGuard sin nettlesersikkerhetsmodul",
"number_of_dns_query_blocked_24_hours_adult": "Antall voksennettsteder som ble blokkert",
"enforced_save_search": "Påtvungede barnevennlige søk",
"number_of_dns_query_to_safe_search": "Antall DNS-forespørsler til søkemotorer der \"Safe Search\" ble fremtvunget",
"average_processing_time": "Gjennomsnittlig behandlingstid",
"response_time": "Responstid",
"average_processing_time_hint": "Gjennomsnittstid for behandling av DNS-forespørsler i millisekunder",
"block_domain_use_filters_and_hosts": "Blokker domener ved hjelp av filtre, «hosts»-filer, og rå domener",
"filters_block_toggle_hint": "Du kan sette opp blokkeringsoppføringer i <a>Filtre</a>-innstillingene.",
@@ -136,7 +132,6 @@
"use_adguard_parental": "Benytt AdGuard sin foreldrekontroll-nettjeneste",
"use_adguard_parental_hint": "AdGuard Home vil sjekke om domenet inneholder erotisk materiale. Den benytter den samme privatlivsvennlige API-en som nettlesersikkerhetstjenesten.",
"enforce_safe_search": "Påtving barnevennlige søk",
"enforce_save_search_hint": "AdGuard Home kan fremtvinge \"Safe Search\" i de følgende søkemotorene: Google, YouTube, Bing, DuckDuckGo, Yandex, og Pixabay.",
"no_servers_specified": "Ingen tjenere er spesifisert",
"general_settings": "Generelle innstillinger",
"dns_settings": "DNS-innstillinger",
@@ -626,7 +621,6 @@
"use_saved_key": "Bruk den tidligere lagrede nøkkelen",
"parental_control": "Foreldrekontroll",
"safe_browsing": "Sikker surfing",
"served_from_cache": "{{value}} <i>(formidlet fra mellomlageret)</i>",
"theme_dark_desc": "Mørkt tema",
"theme_light_desc": "Lyst tema",
"disable_notify_until_tomorrow": "Deaktiver beskyttelsen til i morgen",

View File

@@ -6,7 +6,6 @@
"upstream_parallel": "Użyj zapytań równoległych, aby przyspieszyć rozwiązywanie przez jednoczesne wysyłanie zapytań do wszystkich serwerów nadrzędnych.",
"parallel_requests": "Równoległe żądania",
"load_balancing": "Równoważenie obciążenia",
"load_balancing_desc": "Wysyłaj zapytania do jednego serwera nadrzędnego na raz. AdGuard Home używa swojego losowego algorytmu ważonego, aby wybrać serwer, tak aby najszybszy serwer był używany częściej.",
"bootstrap_dns": "Serwery DNS Bootstrap",
"bootstrap_dns_desc": "Adresy IP serwerów DNS używanych do rozpoznawania adresów IP programów rozpoznawania nazw DoH/DoT określonych jako nadrzędne. Komentarze są niedozwolone.",
"fallback_dns_title": "Rezerwowe serwery DNS",
@@ -154,7 +153,6 @@
"use_adguard_parental": "Użyj usługi Kontrola Rodzicielska AdGuard",
"use_adguard_parental_hint": "AdGuard Home sprawdzi, czy domena zawiera materiały dla dorosłych. Używa tego samego interfejsu API przyjaznego prywatności, co usługa sieciowa Bezpieczne Przeglądanie. ",
"enforce_safe_search": "Użyj bezpiecznego wyszukiwania",
"enforce_save_search_hint": "AdGuard Home wymusza bezpieczne wyszukiwanie w następujących wyszukiwarkach: Google, YouTube, Bing, DuckDuckGo, Yandex, Pixabay.",
"no_servers_specified": "Nie określono serwerów",
"general_settings": "Ustawienia główne",
"dns_settings": "Ustawienia DNS",

View File

@@ -6,7 +6,7 @@
"upstream_parallel": "Usar consultas paralelas para acelerar a resolução consultando simultaneamente todos os servidores DNS primário",
"parallel_requests": "Solicitações paralelas",
"load_balancing": "Balanceamento de carga",
"load_balancing_desc": "Consulte um servidor DNS primário por vez. O AdGuard Home usa seu algoritmo aleatório ponderado para escolher o servidor para que o servidor mais rápido seja usado com mais frequência.",
"load_balancing_desc": "Consulte um servidor upstream por vez. O AdGuard Home usa um algoritmo aleatório ponderado para selecionar servidores com o menor número de falhas e o menor tempo médio de consulta.",
"bootstrap_dns": "Servidores DNS de inicialização",
"bootstrap_dns_desc": "Endereços IP de servidores DNS usados para resolver endereços IP dos resolvedores DoH/DoT que você especifica como upstreams. Comentários não são permitidos.",
"fallback_dns_title": "Servidores DNS Fallback",
@@ -154,7 +154,7 @@
"use_adguard_parental": "Usar o serviço de controle parental do AdGuard",
"use_adguard_parental_hint": "O AdGuard Home irá verificar se o domínio contém conteúdo adulto. Ele usa a mesma API amigável de privacidade que o serviço de segurança da navegação.",
"enforce_safe_search": "Usar pesquisa segura",
"enforce_save_search_hint": "O AdGuard Home forcará a pesquisa segura nos seguintes motores de busca: Google, YouTube, Bing, DuckDuckGo, Yandex, Pixabay.",
"enforce_save_search_hint": "O AdGuard Home forcará a pesquisa segura nos seguintes motores de busca: Google, YouTube, Bing, DuckDuckGo, Ecosia, Yandex, Pixabay.",
"no_servers_specified": "Nenhum servidor especificado",
"general_settings": "Configurações gerais",
"dns_settings": "Configurações de DNS",

View File

@@ -6,7 +6,7 @@
"upstream_parallel": "Usar consultas paralelas para acelerar a resolução consultando simultaneamente todos os servidores DNS",
"parallel_requests": "Solicitações paralelas",
"load_balancing": "Balanceamento de carga",
"load_balancing_desc": "Consulte um servidor DNS primário por vez. O AdGuard Home usa seu algoritmo aleatório ponderado para escolher o servidor para que o servidor mais rápido seja usado com mais frequência.",
"load_balancing_desc": "Consulta um servidor a montante de cada vez. O AdGuard Home usa um algoritmo aleatório ponderado para selecionar servidores com o menor número de pesquisas com falha e o menor tempo médio de pesquisa.",
"bootstrap_dns": "Servidores DNS de arranque",
"bootstrap_dns_desc": "Endereços IP de servidores DNS usados para resolver endereços IP dos resolvedores DoH/DoT que você especifica como upstreams. Comentários não são permitidos.",
"fallback_dns_title": "Servidores DNS de fallback",
@@ -154,7 +154,7 @@
"use_adguard_parental": "Usar o serviço de controlo parental do AdGuard",
"use_adguard_parental_hint": "O AdGuard Home irá verificar se o domínio contém conteúdo adulto. Usa a mesma API amigável de privacidade que o serviço de segurança da navegação.",
"enforce_safe_search": "Usar pesquisa segura",
"enforce_save_search_hint": "O AdGuard Home forçará a pesquisa segura nos seguintes motores de busca: Google, Youtube, Bing, DuckDuckGo, Yandex, Pixabay.",
"enforce_save_search_hint": "O AdGuard Home aplicará pesquisa segura nos seguintes motores de busca: Google, YouTube, Bing, DuckDuckGo, Ecosia, Yandex, Pixabay.",
"no_servers_specified": "Nenhum servidor especificado",
"general_settings": "Definições gerais",
"dns_settings": "Definições de DNS",

View File

@@ -6,21 +6,18 @@
"upstream_parallel": "Folosiți interogări paralele pentru a accelera rezolvarea, interogând simultan toate serverele în amonte.",
"parallel_requests": "Solicitări paralele",
"load_balancing": "Echilibrare-sarcini",
"load_balancing_desc": "Interoghează câte un server în amonte la un moment dat. AdGuard Home utilizează un algoritm de randomizare ponderat pentru a alege serverul, astfel încât cel mai rapid server să fie utilizat mai des.",
"bootstrap_dns": "Serverele DNS Bootstrap",
"bootstrap_dns_desc": "Adresele IP ale serverelor DNS utilizate pentru a rezolva adresele IP ale soluțiilor DoH/DoT pe care le specificați ca fiind în amonte. Comentariile nu sunt permise.",
"fallback_dns_title": "Servere DNS de rezervă",
"fallback_dns_desc": "Lista serverelor DNS de rezervă utilizate atunci când serverele DNS din amonte nu răspund. Sintaxa este aceeași ca în câmpul principal din amonte de mai sus.",
"fallback_dns_placeholder": "Introduceți un server DNS de rezervă pe linie",
"local_ptr_title": "Servere DNS inverse private",
"local_ptr_desc": "Serverele DNS pe care AdGuard Home le utilizează pentru interogările PTR locale. Aceste servere sunt utilizate pentru a rezolva solicitările PTR pentru adrese din intervale IP private, de exemplu „192.168.12.34”, utilizând DNS invers. Dacă nu este configurat, AdGuard Home utilizează adresele rezolvatorilor DNS impliciți ai sistemului dvs. de operare, cu excepția adreselor AdGuard Home în sine.",
"local_ptr_default_resolver": "În mod implicit, AdGuard Home utilizează următorii rezolvatori DNS inverși: {{ip}}.",
"local_ptr_no_default_resolver": "AdGuard Home nu a putut determina rezolvatorii DNS privați adecvați pentru acest sistem.",
"local_ptr_placeholder": "Introduceți o adresă IP per linie",
"resolve_clients_title": "Permiteți rezolvarea inversa a adreselor IP ale clienților",
"resolve_clients_desc": "Rezolvă invers adresele IP ale clienților în numele lor de gazde prin trimiterea interogărilor PTR la rezolvatorii corespunzători (servere DNS private pentru clienți locali, servere în amonte pentru clienți cu adrese IP publice).",
"use_private_ptr_resolvers_title": "Utilizați rezolvatori DNS inverși privați",
"use_private_ptr_resolvers_desc": "Efectuează examinări DNS inverse pentru adresele deservite local folosind aceste servere în amonte. Dacă este dezactivată, AdGuard Home răspunde cu NXDOMAIN la toate aceste cereri PTR, cu excepția clienților cunoscuți din DHCP, /etc/hosts și așa mai departe.",
"check_dhcp_servers": "Căutați servere DHCP",
"save_config": "Salvare configurare",
"enabled_dhcp": "Server DHCP activat",
@@ -154,7 +151,6 @@
"use_adguard_parental": "Utilizați Controlul Parental AdGuard",
"use_adguard_parental_hint": "AdGuard Home va verifica pentru conținut adult pe domeniu. Utilizează același API discret ca cel utilizat de serviciul de securitate de navigare.",
"enforce_safe_search": "Folosiți Căutarea Sigură",
"enforce_save_search_hint": "AdGuard Home va impune Căutarea Sigură în următoarele motoare de căutare: Google, YouTube, Bing, DuckDuckGo, Yandex, Pixabay.",
"no_servers_specified": "Nu sunt specificate servere",
"general_settings": "Setări Generale",
"dns_settings": "Setări DNS",
@@ -675,7 +671,6 @@
"use_saved_key": "Folosiți cheia salvată anterior",
"parental_control": "Control Parental",
"safe_browsing": "Navigare în siguranță",
"served_from_cache": "{{value}} <i>(furnizat din cache)</i>",
"form_error_password_length": "Parola trebuie să aibă între {{min}} și {{max}} caractere",
"anonymizer_notification": "<0>Nota:</0> Anonimizarea IP este activată. Puteți să o dezactivați în <1>Setări generale</1>.",
"confirm_dns_cache_clear": "Sunteți sigur că doriți să ștergeți memoria cache DNS?",

View File

@@ -6,7 +6,7 @@
"upstream_parallel": "Использовать параллельные запросы ко всем серверам одновременно для ускорения обработки запроса.",
"parallel_requests": "Параллельные запросы",
"load_balancing": "Распределение нагрузки\n",
"load_balancing_desc": "Запрашивать по одному серверу за раз. AdGuard Home использует алгоритм взвешенного случайного выбора сервера, так что самый быстрый сервер используется чаще.",
"load_balancing_desc": "Запрашивайте по одному серверу за раз. AdGuard Home использует алгоритм случайной выборки с учётом веса для выбора серверов с наименьшим количеством неудачных запросов и наименьшим средним временем выполнения запроса.",
"bootstrap_dns": "Bootstrap DNS-серверы",
"bootstrap_dns_desc": "IP-адреса DNS-серверов, используемых для поиска IP-адресов DoH/DoT upstream-серверов, которые вы указали. Комментарии не допускаются.",
"fallback_dns_title": "Резервные DNS-серверы",
@@ -154,7 +154,7 @@
"use_adguard_parental": "Включить модуль Родительского контроля AdGuard ",
"use_adguard_parental_hint": "AdGuard Home проверит, содержит ли домен материалы 18+. Он использует тот же API для обеспечения конфиденциальности, что и веб-служба безопасности браузера.",
"enforce_safe_search": "Включить безопасный поиск",
"enforce_save_search_hint": "AdGuard Home может обеспечить безопасный поиск в следующих поисковых системах: Google, YouTube, Bing, DuckDuckGo, Yandex и Pixabay.",
"enforce_save_search_hint": "AdGuard Home будет обеспечивать безопасный поиск в следующих поисковых системах: Google, YouTube, Bing, DuckDuckGo, Ecosia, Yandex, Pixabay.",
"no_servers_specified": "Нет указанных серверов",
"general_settings": "Основные настройки",
"dns_settings": "Настройки DNS",

View File

@@ -4,10 +4,8 @@
"parallel_requests": "සමාන්තර ඉල්ලීම්",
"load_balancing": "ධාරිතාව තුලනය",
"local_ptr_title": "පෞද්ගලික ප්‍රතිවර්ත ව.නා.ප. සේවාදායක",
"local_ptr_desc": "ස්ථානීය PTR විමසුම් සඳහා ඇඩ්ගාර්ඩ් හෝම් භාවිතා කරන ව.නා.ප. සේවාදායක. මෙම සේවාදායක පුද්ගලික අ.ජා.කෙ. ලිපින පරාසවල PTR විමසුම් විසඳීමට භාවිතා කරයි, උදාහරණයක් ලෙස ප්‍රතිවර්ත ව.නා.ප. භාවිතයෙන් \"192.168.12.34\". මෙය සකසා නැති නම්, ඇඩ්ගාර්ඩ් හෝම් හි ලිපින සඳහා හැරුනු විට ඔබගේ මෙහෙයුම් පද්ධතියේ පෙරනිමි ව.නා.ප. විසදුම්වල ලිපින භාවිතා කරයි.",
"local_ptr_default_resolver": "පෙරනිමි පරිදි, ඇඩ්ගාර්ඩ් හෝම් පහත ප්‍රතිවර්ත ව.නා.ප. පිළිවිසඳු භාවිතා කරයි: {{ip}}.",
"local_ptr_no_default_resolver": "ඇඩ්ගාර්ඩ් හෝම් හට මෙම පද්ධතිය සඳහා සුදුසු පුද්ගලික ප්‍රතිවර්ත ව.නා.ප. පිළිවිසඳු නිශ්චය කරගත නොහැකි විය.",
"local_ptr_placeholder": "පේළියකට එක් සේවාදායක ලිපිනය බැගින් යොදන්න",
"resolve_clients_title": "අනුග්‍රාහකවල අ.ජා.කෙ. ලිපින ප්‍රතිවර්ත විසඳීම සබල කරන්න",
"use_private_ptr_resolvers_title": "පෞද්. ප්‍රතිවර්ත ව.නා.ප. පිළිවිසඳු භාවිතය",
"check_dhcp_servers": "ග.ධා.වි.කෙ. සේවාදායක පරීක්‍ෂා කරන්න",
@@ -102,7 +100,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,7 +112,6 @@
"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_blocked_24_hours": "දැන්වීම් වාරණ පෙරහන් සහ සත්කාරක වාරණ ලැයිස්තු මගින් අවහිර කළ ව.නා.ප. ඉල්ලීම් ගණන",
"number_of_dns_query_blocked_24_hours_by_sec": "ඇඩ්ගාර්ඩ් පිරික්සුම් ආරක්‍ෂණ ඒකකය මගින් අවහිර කළ ව.නා.ප. ඉල්ලීම් ගණන",
"number_of_dns_query_blocked_24_hours_adult": "අවහිර කළ වැඩිහිටි වියමන අඩවි ගණන",
@@ -130,7 +126,6 @@
"use_adguard_parental": "ඇඩ්ගාර්ඩ් දෙමාපිය පාලන වියමන සේවාව භාවිතා කරන්න",
"use_adguard_parental_hint": "වසමේ වැඩිහිටියන්ට අදාල කරුණු අඩංගු දැයි ඇඩ්ගාර්ඩ් හෝම් විසින් පරීක්‍ෂා කරනු ඇත. එය පිරික්සුම් ආරක්‍ෂණ වියමන සේවාව මෙන් රහස්‍යතා හිතකාමී යෙ.ක්‍ර. අ.මු. (API) භාවිතා කරයි.",
"enforce_safe_search": "ආරක්‍ෂිත සෙවුම භාවිතා කරන්න",
"enforce_save_search_hint": "ඇඩ්ගාර්ඩ් හෝම් පහත සෙවුම් යන්ත්‍ර තුළ ආරක්‍ෂිත සෙවුම බලාත්මක කරනු ඇත: ගූගල්, යූටියුබ්, බින්ග්, ඩක්ඩක්ගෝ, යාන්ඩෙක්ස් සහ පික්සාබේ.",
"no_servers_specified": "සේවාදායක කිසිවක් නිශ්චිතව දක්වා නැත",
"general_settings": "පොදු සැකසුම්",
"dns_settings": "ව.නා.ප. සැකසුම්",
@@ -505,8 +500,6 @@
"statistics_enable": "සංඛ්‍යාලේඛන සබල කරන්න",
"ignore_domains": "නොසලකන වසම් (පේළියකට එක බැගින්)",
"ignore_domains_title": "නොසලකන වසම්",
"ignore_domains_desc_stats": "සංඛ්‍යාලේඛනයෙහි මෙම වසම් සඳහා විමසුම් නොලියැවෙයි",
"ignore_domains_desc_query": "විමසුම් සටහනෙහි මෙම වසම් සඳහා විමසුම් නොලියැවෙයි",
"interval_hours": "පැය {{count}}",
"interval_hours_plural": "පැය {{count}}",
"filters_configuration": "පෙරහන් වින්‍යාසය",
@@ -615,8 +608,6 @@
"use_saved_key": "පෙර සුරැකි යතුර භාවිතා කරන්න",
"parental_control": "දෙමාපිය පාලනය",
"safe_browsing": "ආරක්‍ෂිත පිරික්සුම",
"served_from_cache": "{{value}} <i>(නිහිතයෙන් ගැනිණි)</i>",
"form_error_password_length": "මුරපදය අවම වශයෙන් අකුරු {{value}} ක් දිගු විය යුතුමයි",
"anonymizer_notification": "<0>සටහන:</0> අ.ජා.කෙ. නිර්නාමිකකරණය සබලයි. ඔබට එය <1>පොදු සැකසුම්</1> හරහා අබල කිරීමට හැකිය .",
"confirm_dns_cache_clear": "ඔබට ව.නා.ප. නිහිතය හිස් කිරීමට වුවමනාද?",
"cache_cleared": "ව.නා.ප. නිහිතය හිස් කෙරිණි",

View File

@@ -6,7 +6,7 @@
"upstream_parallel": "Používať paralelné dopyty na zrýchlenie súčasným dopytovaním všetkých upstream serverov súčasne.",
"parallel_requests": "Paralelné dopyty",
"load_balancing": "Vyrovnávanie záťaže",
"load_balancing_desc": "Dopytovať len jeden server v danom čase. AdGuard Home použije na výber servera vážený náhodný algoritmus, aby sa najrýchlejší server používal častejšie.",
"load_balancing_desc": "Dopytuje sa súčasne len jeden upstream server. AdGuard Home používa vážený náhodný algoritmus na výber serverov s najnižším počtom neúspešných vyhľadávaní a najnižším priemerným časom vyhľadávania.",
"bootstrap_dns": "Bootstrap DNS servery",
"bootstrap_dns_desc": "IP adresy serverov DNS používaných na rozlíšenie IP adries prekladačov DoH/DoT, ktoré zadáte ako upstream. Komentáre nie sú povolené.",
"fallback_dns_title": "Záložné servery DNS",
@@ -89,7 +89,7 @@
"form_enter_hostname": "Zadajte meno hostiteľa",
"error_details": "Podrobnosti chyby",
"response_details": "Podrobnosti odpovede",
"request_details": "Podrobnosti požiadavky",
"request_details": "Podrobnosti dopytu",
"client_details": "Podrobnosti klienta",
"details": "Podrobnosti",
"back": "Naspäť",
@@ -154,7 +154,7 @@
"use_adguard_parental": "Použiť AdGuard službu Rodičovská kontrola",
"use_adguard_parental_hint": "AdGuard Home skontroluje, či doména obsahuje materiály pre dospelých. Používa rovnaké API priateľské k ochrane osobných údajov ako služba Bezpečného prehliadania.",
"enforce_safe_search": "Používať bezpečné vyhľadávanie",
"enforce_save_search_hint": "AdGuard Home vynucuje bezpečné vyhľadávanie v nasledujúcich vyhľadávačoch: Google, YouTube, Bing, DuckDuckGo, Yandex a Pixabay.",
"enforce_save_search_hint": "AdGuard Home vynúti bezpečné vyhľadávanie v nasledujúcich vyhľadávačoch: Google, YouTube, Bing, DuckDuckGo, Ecosia, Yandex, Pixabay.",
"no_servers_specified": "Neboli špecifikované žiadne servery",
"general_settings": "Všeobecné nastavenia",
"dns_settings": "Nastavenia DNS",
@@ -308,7 +308,7 @@
"form_enter_rate_limit": "Zadajte rýchlostný limit",
"rate_limit": "Rýchlostný limit",
"edns_enable": "Povoliť klientsku podsiete EDNS",
"edns_cs_desc": "Pridáva možnosť EDNS Client Subnet (ECS) do upstream požiadaviek a zapíše hodnoty odoslané klientmi do denníka dopytov.",
"edns_cs_desc": "Pridáva možnosť EDNS Client Subnet (ECS) do upstream dopytov a zapíše hodnoty odoslané klientami do denníka dopytov.",
"edns_use_custom_ip": "Použiť vlastnú IP adresu pre EDNS",
"edns_use_custom_ip_desc": "Povoliť používanie vlastnej IP adresy pre EDNS",
"rate_limit_desc": "Počet požiadaviek za sekundu, ktoré môže jeden klient vykonať. Nastavenie na hodnotu 0 znamená neobmedzene.",
@@ -480,9 +480,9 @@
"access_title": "Nastavenia prístupu",
"access_desc": "Tu môžete konfigurovať pravidlá prístupu pre server DNS AdGuard Home.",
"access_allowed_title": "Povolení klienti",
"access_allowed_desc": "Zoznam CIDR, IP adries alebo <a>ClientID</a>. Ak tento zoznam obsahuje položky, AdGuard Home bude akceptovať požiadavky iba od týchto klientov.",
"access_allowed_desc": "Zoznam CIDR, IP adries alebo <a>ClientID</a>. Ak tento zoznam obsahuje položky, AdGuard Home bude akceptovať dopyty iba od týchto klientov.",
"access_disallowed_title": "Nepovolení klienti",
"access_disallowed_desc": "Zoznam CIDR, IP adries alebo <a>ClientID</a>. Ak tento zoznam obsahuje položky, AdGuard Home zruší požiadavky od týchto klientov. Toto pole sa ignoruje, ak sú v poli Povolení klienti položky.",
"access_disallowed_desc": "Zoznam CIDR, IP adries alebo <a>ClientID</a>. Ak tento zoznam obsahuje položky, AdGuard Home zruší dopyty od týchto klientov. Toto pole sa ignoruje, ak sú v poli Povolení klienti položky.",
"access_blocked_title": "Nepovolené domény",
"access_blocked_desc": "Nesmie byť zamieňaná s filtrami. AdGuard Home zruší DNS dopyty, ktoré sa zhodujú s týmito doménami, a tieto dopyty sa nezobrazia ani v denníku dopytov. Môžete určiť presné názvy domén, zástupné znaky alebo pravidlá filtrovania URL adries, napr. \"example.org\", \"*.example.org\" alebo ||example.org^\" zodpovedajúcim spôsobom.",
"access_settings_saved": "Nastavenia prístupu úspešne uložené",

View File

@@ -6,21 +6,18 @@
"upstream_parallel": "Uporabite vzporedne zahteve za pospešitev reševanja s hkratnim poizvedovanjem vseh gorvodnih strežnikov.",
"parallel_requests": "Vzporedne zahteve",
"load_balancing": "Uravnavanje obremenitve",
"load_balancing_desc": "Poizvedujte po enem strežniku navzgor. AdGuard Home s pomočjo tehtanega naključnega algoritma izbere strežnik, tako da se najpogosteje uporablja najhitrejši strežnik.",
"bootstrap_dns": "Zagonski DNS strežniki",
"bootstrap_dns_desc": "Naslovi IP strežnikov DNS, ki se uporabljajo za razreševanje naslovov IP razreševalcev DoH/DoT, ki jih določite kot navzgor. Komentarji niso dovoljeni.",
"fallback_dns_title": "Rezervni strežniki DNS",
"fallback_dns_desc": "Seznam rezervnih strežnikov DNS, ki se uporabljajo, ko se gorvodni strežniki DNS ne odzivajo. Sintaksa je enaka kot v zgornjem gorvodnem polju.",
"fallback_dns_placeholder": "Vnesite en rezervni strežnik DNS na vrstico",
"local_ptr_title": "Zasebni povratni strežniki DNS",
"local_ptr_desc": "Strežniki DNS, ki jih AdGuard Home uporablja za lokalne PTR poizvedbe. Ti strežniki se uporabljajo za reševanje zahtev PTR za naslove v zasebnih obsegih IP, na primer \"192.168.12.34\", z uporabo obratnega DNS. Če ni nastavljen, AdGuard Home uporablja naslove privzetih razreševalnikov DNS vašega OS, razen naslovov samega AdGuard Home.",
"local_ptr_default_resolver": "AdGuard Home privzeto uporablja te povratne razreševalnike DNS: {{ip}}.",
"local_ptr_no_default_resolver": "AdGuard Home ni mogel določiti ustreznih zasebnih povratnih reševalcev DNS za ta sistem.",
"local_ptr_placeholder": "Vnesite en naslov IP na vrstico",
"resolve_clients_title": "Omogoči obratno reševanje naslovov IP gostiteljev",
"resolve_clients_desc": "Povratno razrešite naslove IP odjemalcev v njihova gostiteljska imena, tako da pošljete poizvedbe PTR ustreznim razreševalcem (zasebni strežniki DNS za lokalne odjemalce, gorvodni strežniki za odjemalce z javnimi naslovi IP).",
"use_private_ptr_resolvers_title": "Uporabi zasebne povratne razreševalnike rDNS",
"use_private_ptr_resolvers_desc": "Opravi povratne preglede DNS za lokalno vročene naslove s pomočjo teh strežnikov. Če je AdGuard onemogočen, se z NXDOMAIN odzove na vse takšne zahteve PTR, razen za odjemalce, znane iz DHCP/etc/hosts itd.",
"check_dhcp_servers": "Preveri strežnike DHCP",
"save_config": "Shrani nastavitve",
"enabled_dhcp": "Strežnik DHCP je omogočen",
@@ -154,7 +151,6 @@
"use_adguard_parental": "Uporabi AdGuardovo spletno storitev 'Starševski nadzor'",
"use_adguard_parental_hint": "AdGuard Home bo preveril, če domena vsebuje vsebine za odrasle. Uporablja enako, za zasebnost prijazen API, kot spletno storitev za varnost brskanja.",
"enforce_safe_search": "Uporabi Varno iskanje",
"enforce_save_search_hint": "AdGuard Home bo vsilil varno iskanje v naslednjih iskalnikih: Google, YouTube, Bing, DuckDuckGo, Yandex, Pixabay.",
"no_servers_specified": "Ni določenih strežnikov",
"general_settings": "Splošne nastavitve",
"dns_settings": "Nastavitve DNS",

View File

@@ -6,21 +6,18 @@
"upstream_parallel": "Koristite paralelne upite da biste ubrzali rešavanje tako što ćete istovremeno ispitati sve uzvodne servere.",
"parallel_requests": "Paralelni zahtevi",
"load_balancing": "Load-balancing",
"load_balancing_desc": "Koristi jedan upstream server. AdGuard Home koristi najnoviji nasumični algoritam da izabere server tako da se najbrži server češće koristi.",
"bootstrap_dns": "Bootstrap DNS serveri",
"bootstrap_dns_desc": "IP adrese DNS servera koje se koriste za rešavanje IP adresa DoH/DoT razrešivača koje navodite kao uzvodne. Komentari nisu dozvoljeni.",
"fallback_dns_title": "Odstupajući DNS serveri",
"fallback_dns_desc": "Lista povratnih DNS servera koji se koriste kada se uzvodni DNS serveri ne odaziva. Sintaksa je ista kao u glavnom uzvodnom polju iznad.",
"fallback_dns_placeholder": "Unesite jedan povratni DNS server po liniji",
"local_ptr_title": "Private reverse DNS serveri",
"local_ptr_desc": "DNS serveri koje AdGuard Home koristi za lokalne PTR upite. Ovi serveri se koriste za rešavanje imena domaćina klijenata sa privatnim IP adresama, na primer \"192.168.12.34\", koristeći obrnuti DNS. Ako nije podešen, AdGuard Home koristi adrese podrazumevanih DNS razrešivača vašeg OS-a osim adresa samog AdGuard Home-a.",
"local_ptr_default_resolver": "Podrazumevano, AdGuard Home koristi sledeće obrnute DNS razrešivače: {{ip}}.",
"local_ptr_no_default_resolver": "AdGuard Home ne može da odredi pogodne privatne obrnute DNS razrešivače za ovaj sistem.",
"local_ptr_placeholder": "Unesite jednu IP adresu servera po redu",
"resolve_clients_title": "Uključi obrnuto razrešavanje klijentskih IP adresa",
"resolve_clients_desc": "Obrnuto razrešite IP adrese klijenata u njihova imena domaćina slanjem PTR upita odgovarajućim razrešivačima (privatni DNS serveri za lokalne klijente, uzvodni serveri za klijente sa javnim IP adresama).",
"use_private_ptr_resolvers_title": "Koristi privatne obrnute razrešivače",
"use_private_ptr_resolvers_desc": "Izvršavanje obrnutih DNS izgleda za lokalno servirane adrese pomoću ovih uzvodnih servera. Ako je onemogućen, AdGuard Home odgovara sa NXDOMAIN na sve takve PTR zahteve osim klijenata poznatih iz DHCP- a, /etc/hosts itd.",
"check_dhcp_servers": "Proveri DHCP servere",
"save_config": "Sačuvaj konfiguraciju",
"enabled_dhcp": "DHCP server uključen",
@@ -154,7 +151,6 @@
"use_adguard_parental": "Koristi AdGuard-ovu uslugu roditeljske kontrole",
"use_adguard_parental_hint": "AdGuard Home će proveriti da li domen sadrži sadržaj za odrasle. Koristi se isti privatni prijateljski API kao i kod usluge bezbednog pregledanja.",
"enforce_safe_search": "Nametni sigurno pretraživanje",
"enforce_save_search_hint": "AdGuard Home može nametnuti sigurno pretraživanje u sledećim pretraživačima: Google, Youtube, Bing, DuckDuckGo i Yandex.",
"no_servers_specified": "Serveri nisu određeni",
"general_settings": "Opšte postavke",
"dns_settings": "DNS postavke",
@@ -675,7 +671,6 @@
"use_saved_key": "Koristi prethodno sačuvan ključ",
"parental_control": "Roditeljska kontrola",
"safe_browsing": "Sigurno pregledanje",
"served_from_cache": "{{value}} <i>(posluženo iz predmemorije)</i>",
"form_error_password_length": "Lozinka mora imati od {{min}} do {{max}} znakova",
"anonymizer_notification": "<0>Nota:</0> IP prepoznavanje je omogućeno. Možete ga onemogućiti u opštim <1>postavkama</1>.",
"confirm_dns_cache_clear": "Želite li zaista da obrišite DNS keš?",

View File

@@ -6,21 +6,18 @@
"upstream_parallel": "Använd parallella förfrågningar för att snabba upp dessa genom att fråga alla uppströmsservrar samtidigt.",
"parallel_requests": "Parallella förfrågningar",
"load_balancing": "Lastbalansering",
"load_balancing_desc": "Fråga en uppströmsserver åt gången. AdGuard Home använder sin viktade slumpmässiga algoritm för att välja server så att den snabbaste servern används oftare.",
"bootstrap_dns": "Bootstrap-DNS-servrar",
"bootstrap_dns_desc": "IP-adresser för DNS-servrar som används för att lösa IP-adresser för de DoH/DoT-resolvers som du anger som uppströms. Kommentarer är inte tillåtna.",
"fallback_dns_title": "Reserv DNS-servrar",
"fallback_dns_desc": "Lista över reserv-DNS-servrar som används när uppströms DNS-servrar inte svarar. Syntaxen är densamma som i huvuduppströmsfältet ovan.",
"fallback_dns_placeholder": "Ange en reserv-DNS-server per rad",
"local_ptr_title": "Privata omvända DNS-servrar",
"local_ptr_desc": "DNS servrarna som AdGuard Home använder för lokala PTR frågor. Dessa servrar används för att lösa värdnamnen på klienter med privata IP-adresser, till exempel \"192.168.12.34\", genom omvänd DNS. Om inga servrar angetts använder AdGuard Home adresserna till standard DNS servrar för ditt operativsystem förutom adresserna till AdGuard Home själv.",
"local_ptr_default_resolver": "Som standard använder AdGuard Home följande omvända DNS upplösare: {{ip}}.",
"local_ptr_no_default_resolver": "AdGuard Home kunde inte fastställa lämpliga privata omvända DNS upplösare för detta system.",
"local_ptr_placeholder": "Ange en IP-adress per rad",
"resolve_clients_title": "Aktivera omvänd upplösning av klienters IP-adresser",
"resolve_clients_desc": "Lös upp klienternas värdnamn med omvänt uppslag av klienternas IP-adresser genom att skicka PTR-frågor till motsvarande upplösare (privata DNS-servrar för lokala klienter, uppströmsservrar för klienter med offentliga IP-adresser).",
"use_private_ptr_resolvers_title": "Använd privata omvända DNS upplösare",
"use_private_ptr_resolvers_desc": "Utför omvända DNS-sökningar för lokalt betjänade adresser med dessa uppströmsservrar. Om det är inaktiverat svarar AdGuard Home med NXDOMAIN på alla sådana PTR-förfrågningar förutom klienter kända från DHCP, /etc/hosts, och så vidare.",
"check_dhcp_servers": "Letar efter DHCP-servrar",
"save_config": "Spara konfiguration",
"enabled_dhcp": "DHCP-server aktiverad",
@@ -154,7 +151,6 @@
"use_adguard_parental": "Använda AdGuards webbservice för föräldrakontroll",
"use_adguard_parental_hint": "AdGuard Home kommer att kontrollera domäner för innehåll av vuxenmaterial . Samma integritetsvänliga metod för API-lookup som tillämpas i webbservicens surfsäkerhet används.",
"enforce_safe_search": "Använd SafeSearch",
"enforce_save_search_hint": "AdGuard Home kommer tvinga säker surf på följande sökmotorer: Google, Youtube, Bing, DuckDuckGo, Yandex, Pixabay.",
"no_servers_specified": "Inga servrar angivna",
"general_settings": "Allmänna inställningar",
"dns_settings": "DNS-inställningar",
@@ -674,7 +670,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

@@ -1,7 +1,6 @@
{
"client_settings": "การตั้งค่าไคลเอนต์",
"bootstrap_dns": "Bootstrap เซิร์ฟเวอร์ DNS",
"bootstrap_dns_desc": "เซิร์ฟเวอร์ Bootstrap DNS ใช้เพื่อแก้ไขที่อยู่ IP ของตัวแก้ไข DoH / DoT ที่คุณระบุว่าเป็น upstreams",
"check_dhcp_servers": "ตรวจสอบ DHCP servers",
"enabled_dhcp": "เปิดการใช้งาน DHCP server แล้ว",
"disabled_dhcp": "ปิดการใช้งาน DHCP server แล้ว",
@@ -13,13 +12,6 @@
"dhcp_leases": "สัญญาเช่า DHCP",
"dhcp_static_leases": "DHCP แบบกำหนด",
"dhcp_leases_not_found": "ไม่พบสัญญาเช่า DHCP",
"form_error_required": "ช่องที่ต้องกรอก",
"form_error_ip4_format": "รูปแบบ IPv4 ไม่ถูกต้อง",
"form_error_ip6_format": "รูปแบบ IPv6 ไม่ถูกต้อง",
"form_error_ip_format": "รูปแบบ IP ไม่ถูกต้อง",
"form_error_mac_format": "รูปแบบ MAC ไม่ถูกต้อง",
"form_error_client_id_format": "รูปแบบ ID ลูกค้าไม่ถูกต้อง",
"form_error_positive": "ต้องมากกว่า 0",
"dhcp_form_gateway_input": "IP ของเกตเวย์",
"dhcp_form_subnet_input": "ซับเน็ตมาสก์",
"dhcp_form_range_title": "ช่วงของที่อยู่ IP",
@@ -54,7 +46,6 @@
"copyright": "ลิขสิทธิ์",
"homepage": "หน้าหลัก",
"report_an_issue": "รายงานปัญหา",
"privacy_policy": "นโยบายความเป็นส่วนตัว",
"enable_protection": "เปิดใช้งานการป้องกัน",
"enabled_protection": "เปิดใช้งานการป้องกันแล้ว",
"disable_protection": "ปิดใช้งานการป้องกัน",
@@ -65,7 +56,6 @@
"stats_malware_phishing": "ปิดกั้นมัลแวร์/ฟิชชิ่ง แล้ว",
"stats_adult": "ปิดกั้นเว็บไซต์สำหรับผู้ใหญ่แล้ว",
"stats_query_domain": "โดเมนที่เข้าบ่อยสุด",
"for_last_24_hours": "ในช่วง 24 ชั่วโมงที่ผ่านมา",
"for_last_days": "สำหรับ {{count}} วันสุดท้าย",
"for_last_days_plural": "สำหรับ {{count}} วันล่าสุด",
"no_domains_found": "ไม่พบโดเมน",
@@ -80,10 +70,8 @@
"block_domain_use_filters_and_hosts": "ปิดกั้นโดเมนโดยใช้ตัวกรองและไฟล์โฮสต์",
"filters_block_toggle_hint": "คุณสามารถตั้งค่ากฎการปิดกั้นในการตั้งค่า<a>ตัวกรอง</a>",
"use_adguard_browsing_sec": "ใช้บริการเว็บการรักษาความปลอดภัยการเรียกดู AdGuard",
"use_adguard_browsing_sec_hint": "AdGuard Home จะตรวจสอบว่าโดเมนอยู่ในรายการที่ไม่อนุญาตโดยเว็บเซอร์วิสความปลอดภัยการสืบค้นหรือไม่ จะใช้ API การค้นหาที่เป็นมิตรกับข้อมูลส่วนบุคคลเพื่อทำการตรวจสอบ: มีการส่งคำนำหน้าสั้น ๆ ของชื่อโดเมน SHA256 แฮชไปยังเซิร์ฟเวอร์",
"use_adguard_parental": "ใช้บริการเว็บการควบคุมโดยผู้ปกครองของ AdGuard",
"use_adguard_parental_hint": "AdGuard Home จะตรวจสอบว่าโดเมนมีเนื้อหาสำหรับผู้ใหญ่หรือไม่ มันใช้ API ความเป็นส่วนตัวเช่นเดียวกับบริการเว็บการรักษาความปลอดภัยการท่องเว็บ",
"enforce_safe_search": "บังคับใช้การค้นหาที่ปลอดภัย",
"no_servers_specified": "ไม่ได้ระบุเซิร์ฟเวอร์",
"general_settings": "การตั้งค่าทั่วไป",
"dns_settings": "การตั้งค่า DNS",
@@ -96,12 +84,6 @@
"apply_btn": "นำไปใช้",
"disabled_filtering_toast": "ปิดใช้งานการกรอง",
"enabled_filtering_toast": "เปิดใช้งานการกรอง",
"disabled_safe_browsing_toast": "ปิดใช้งานการเรียกดูอย่างปลอดภัย",
"enabled_safe_browsing_toast": "เปิดการใช้งาน safebrowsing",
"disabled_parental_toast": "ปิดใช้งานการควบคุมโดยผู้ปกครอง",
"enabled_parental_toast": "เปิดการใช้งานเข้าเว็บไม่พึงประสงค์",
"disabled_safe_search_toast": "ปิดใช้งานการค้นหาที่ปลอดภัย",
"enabled_save_search_toast": "เปิดใช้งานการค้นหาที่ปลอดภัย",
"enabled_table_header": "เปิดใช้งาน",
"name_table_header": "ชื่อ",
"rules_count_table_header": "กฎการนับ",
@@ -116,18 +98,6 @@
"custom_filter_rules": "กฎการกรองที่กำหนดเอง",
"custom_filter_rules_hint": "ป้อนหนึ่งกฎในหนึ่งบรรทัด คุณสามารถใช้กฎปิดกั้นโฆษณาหรือโฮสต์ไฟล์ไวยากรณ์",
"examples_title": "ตัวอย่าง",
"example_meaning_filter_block": "ปิดกั้นการเข้าถึงโดเมน example.org และโดเมนย่อยทั้งหมด",
"example_meaning_filter_whitelist": "เลิกปิดกั้นการเข้าถึงโดเมน example.org และโดเมนย่อยทั้งหมด",
"example_meaning_host_block": "ตอนนี้ AdGuard Home จะส่งคืนที่อยู่ 127.0.0.1 สำหรับโดเมน example.org (แต่ไม่ใช่โดเมนย่อย)",
"example_comment": "! นี่ความคิดเห็น",
"example_comment_meaning": "เพียงความคิดเห็น",
"example_comment_hash": "# นอกจากนี้ยังมีความคิดเห็น",
"example_regex_meaning": "ปิดกั้นการเข้าถึงโดเมนที่ตรงกับนิพจน์ทั่วไปที่ระบุ",
"example_upstream_regular": "DNS ปกติ (มากกว่า UDP)",
"example_upstream_dot": "encrypted <0>DNS-over-TLS</0> แล้ว",
"example_upstream_doh": "เข้ารหัส <0>DNS-over-HTTPS</0> แล้ว",
"example_upstream_sdns": "คุณสามรถใช้ <0>DNS Stamps</0> กับ <1>DNSCrypt</1> หรือ <2>DNS-over-HTTPS</2> ตัวแก้ปัญหา",
"example_upstream_tcp": "dNS ปกติ (ผ่าน TCP)",
"dns_test_ok_toast": "เซิร์ฟเวอร์ DNS ที่ระบุทำงานอย่างถูกต้อง",
"dns_test_not_ok_toast": "เซิร์ฟเวอร์ \"{{key}}\": ไม่สามารถใช้งานได้ โปรดตรวจสอบว่าคุณเขียนถูกต้อง",
"unblock": "เลิกปิดกั้น",
@@ -147,7 +117,6 @@
"loading_table_status": "กำลังโหลด...",
"page_table_footer_text": "หน้า",
"rows_table_footer_text": "ตาราง",
"updated_custom_filtering_toast": "อัปเดตกฎการกรองที่กำหนดเอง",
"rule_removed_from_custom_filtering_toast": "ลบกฎออกจากกฎการกรองที่กำหนดเองแล้ว {{rule}}",
"rule_added_to_custom_filtering_toast": "เพิ่มกฎในกฎการกรองที่กำหนดเองแล้ว {{rule}}",
"query_log_response_status": "สถานะ: {{value}}",
@@ -155,12 +124,10 @@
"query_log_confirm_clear": "คุณแน่ใจหรือไม่ว่าต้องการลบบันทึกการใช้งานทั้งหมด?",
"query_log_cleared": "บันทึกการใช้งานได้รับการล้างเรียบร้อยแล้ว",
"query_log_clear": "ล้างบันทึกการสืบค้น",
"query_log_retention": "แบบสอบถามบันทึกการเก็บรักษา",
"query_log_enable": "เปิดใช้งานบันทึก",
"query_log_configuration": "บันทึกการกำหนดค่า",
"query_log_disabled": "บันทึกแบบสอบถามถูกปิดใช้งานและสามารถกำหนดค่าใน <0>การตั้งค่า</0>",
"query_log_strict_search": "ใช้เครื่องหมายคำพูดคู่เพื่อการค้นหาที่จำกัด",
"query_log_retention_confirm": "คุณแน่ใจหรือไม่ว่าต้องการเปลี่ยนการเก็บข้อมูลบันทึกแบบสอบถาม? หากคุณลดค่าช่วงเวลา ข้อมูลบางอย่างจะหายไป",
"dns_config": "การกำหนดค่าเซิร์ฟเวอร์ DNS",
"blocking_mode": "โหมดการปิดกั้น",
"default": "ค่าเริ่มต้น",
@@ -175,8 +142,6 @@
"dns_over_quic": "DNS-over-QUIC",
"form_enter_rate_limit": "ป้อนขีดจำกัดอัตรา",
"rate_limit": "จำกัดอัตรา",
"edns_enable": "เปิดใช้งานซับเน็ตไคลเอ็นต์ EDNS",
"edns_cs_desc": "หากเปิดใช้งาน AdGuard Home จะส่งซับเน็ตของไคลเอนต์ไปยังเซิร์ฟเวอร์ DNS",
"blocking_ipv4_desc": "ที่อยู่ IP ที่จะส่งคืนสำหรับคำขอที่ถูกปิดกั้น",
"blocking_ipv6_desc": "ที่อยู่ IP ที่จะส่งคืนสำหรับคำขอ AAAA ที่ถูกปิดกั้น",
"blocking_mode_nxdomain": "NXDOMAIN: ตอบสนองด้วยรหัส NXDOMAIN",
@@ -197,7 +162,6 @@
"install_settings_dns_desc": "คุณจะต้องกำหนดค่าอุปกรณ์หรือเราเตอร์ของคุณเพื่อใช้เซิร์ฟเวอร์ DNS ตามที่อยู่ต่อไปนี้:",
"install_settings_all_interfaces": "อินเทอร์เฟซทั้งหมด",
"install_auth_title": "การตรวจสอบสิทธิ์",
"install_auth_desc": "ขอแนะนำอย่างยิ่งให้กำหนดค่าการตรวจสอบรหัสผ่านให้กับส่วนต่อประสานเว็บผู้ดูแลระบบ AdGuard Home ของคุณ แม้ว่ามันจะสามารถเข้าถึงได้เฉพาะในเครือข่ายท้องถิ่นของคุณก็ยังคงเป็นสิ่งสำคัญที่จะปกป้องมันจากการเข้าถึงที่ไม่จำกัด",
"install_auth_username": "ชื่อผู้ใช้",
"install_auth_password": "รหัสผ่าน",
"install_auth_confirm": "ยืนยันรหัสผ่าน",
@@ -207,37 +171,26 @@
"install_devices_title": "กำหนดค่าอุปกรณ์ของคุณ",
"install_devices_desc": "ในการเริ่มใช้งาน AdGuard Home คุณต้องกำหนดค่าอุปกรณ์ของคุณเพื่อใช้งาน",
"install_submit_title": "ยินดีด้วย!",
"install_submit_desc": "ขั้นตอนการตั้งค่าเสร็จสิ้นและคุณพร้อมที่จะเริ่มใช้งาน AdGuard Home",
"install_devices_router": "เราเตอร์",
"install_devices_router_desc": "การตั้งค่านี้จะครอบคลุมอุปกรณ์ทั้งหมดที่เชื่อมต่อกับเราเตอร์ที่บ้านของคุณโดยอัตโนมัติและคุณไม่จำเป็นต้องกำหนดค่าแต่ละอุปกรณ์ด้วยตนเอง",
"install_devices_address": "เซิร์ฟเวอร์ DNS ของ AdGuard Home กำลังรับฟังตามที่อยู่ต่อไปนี้",
"install_devices_router_list_2": "ค้นหาการตั้งค่า DHCP/DNS ค้นหาตัวอักษร DNS ที่อยู่ถัดจากช่องที่อนุญาตให้มีตัวเลขสองหรือสามชุดโดยแต่ละกลุ่มแบ่งออกเป็นสี่กลุ่มหนึ่งถึงสามหลัก",
"install_devices_router_list_3": "ป้อนที่อยู่เซิร์ฟเวอร์ AdGuard Home ของคุณที่นั่น",
"install_devices_windows_list_1": "เปิด Control Panel โดยใช้ Start menu หรือ Windows search",
"install_devices_windows_list_2": "ไปที่หมวด Network and Internet แล้วเลือก Network and Sharing Center",
"install_devices_windows_list_3": "ทางด้านซ้ายจะมีคำว่า Change adapter settings ให้กดเข้าไป",
"install_devices_windows_list_4": "เลือกการเชื่อมต่อที่ใช้งานอยู่ คลิกขวาแล้วเลือก Properties",
"install_devices_windows_list_5": "ค้นหา Internet Protocol Version 4 (TCP/IP) แล้วคลิก Properties อีกครั้ง",
"install_devices_windows_list_6": "ค้นหา DNS server addresses ให้ทำการกรอกหมายเลข AdGuard Home ลงไปในช่อง",
"install_devices_macos_list_1": "คลิกโลโก้แอปเปิ้ลแล้วกด System Preferences",
"install_devices_macos_list_2": "คลิก Network",
"install_devices_macos_list_3": "เลือกการเชื่อมต่อแล้วคลิก Advanced",
"install_devices_macos_list_4": "ค้นหาแท็บ DNS แล้วกรอกหมาเลย AdGuard Home",
"install_devices_android_list_1": "เข้าหน้าเมนู(บางรุ่นจะมีตรงแท็บการแจ้งเตือน) เลือกการตั้งค่า",
"install_devices_android_list_2": "เลือกเมนู Wi-Fi แล้วค้นหา Wi-Fi ที่จะเชื่อมต่อ (ไม่สารถตั้งค่ากับเน็ตมือถือได้)",
"install_devices_android_list_3": "แตะชื่อWi-Fi ที่จะเชื่อมต่อค้างไว้(บางรุ่นให้เลื่อนจอลงไปล่างสุด) เลือกการตั้งค่าเพิ่มเติม",
"install_devices_android_list_4": "ในอุปกรณ์บางอย่างคุณอาจต้องทำเครื่องหมายในช่องสำหรับขั้นสูงเพื่อดูการตั้งค่าเพิ่มเติม หากต้องการปรับการตั้งค่า Android DNS ของคุณคุณจะต้องเปลี่ยนการตั้งค่า IP จาก DHCP เป็นแบบคงที่",
"install_devices_android_list_5": "เปลี่ยนการตั้งค่า DNS ที่ 1 และค่า DNS 2 ถึงที่อยู่เซิร์ฟเวอร์ AdGuard Home ของคุณ",
"install_devices_ios_list_1": "เลือกการตั้งค่า",
"install_devices_ios_list_2": "เลือก Wi-Fi ด้านซ้าย (ไม่สามรถใช้งานได้กับดาต้ามือถือ)",
"install_devices_ios_list_3": "เลือกชื่อที่จะเชื่อมต่อ",
"install_devices_ios_list_4": "กรอก DNS AdGuard Home Server ลงไปในช่อง",
"get_started": "เริ่มต้นการใช้งาน",
"next": "ถัดไป",
"open_dashboard": "เปิดหน้าควบคุม",
"install_saved": "บันทึกเรียบร้อยแล้ว",
"encryption_title": "การเข้ารหัส",
"encryption_desc": "การดข้ารหัส (HTTPS/TLS) รองรับทั้ง DNS และหน้าเว็บแอดมิน",
"encryption_server": "ชื่อเซิร์ฟเวอร์",
"encryption_server_enter": "ป้อนชื่อโดเมน",
"encryption_redirect": "ไปเส้นทาง HTTPS อัตโนมัติ",
@@ -255,10 +208,6 @@
"encryption_key_input": "คัดลอก/วาง PEM-encoded private key ของคุณตรงนี้",
"encryption_enable": "เปิดการเข้ารหัส (HTTPS, DNS-over-HTTPS, และ DNS-over-TLS)",
"encryption_enable_desc": "หากเปิดใช้งานการเข้ารหัสอินเทอร์เฟซผู้ดูแลระบบของ AdGuard Home จะทำงานผ่าน HTTPS และเซิร์ฟเวอร์ DNS จะรับฟังคำร้องขอผ่านทาง DNS-over-HTTPS และ DNS-over-TLS",
"encryption_chain_valid": "ใบรับรองมีความน่าเชื่อถือ",
"encryption_chain_invalid": "ใบรับรองไม่มีความน่าเชื่อถือแต่สามรถใช้ได้",
"encryption_key_valid": "นี่เป็นคีย์ส่วนตัว {{type}} ที่ถูกต้อง",
"encryption_key_invalid": "นี่เป็นคีย์ส่วนตัว {{type}} ที่ไม่ถูกต้อง",
"encryption_subject": "เรื่อง:",
"encryption_issuer": "ผู้ออกใบรับรอง:",
"encryption_hostnames": "ชื่อโฮส",
@@ -266,21 +215,16 @@
"encryption_warning": "คำเตือน",
"topline_expiring_certificate": "ใบรับรอง SSL ของคุณกำลังจะหมดอายุ กรุณาอัปเดท <0>การตั้งค่าเข้ารหัส</0>.",
"topline_expired_certificate": "ใบรับรอง SSL ของคุณหมดอายุแล้ว กรุณาอัปเดท <0>การตั้งค่าเข้ารหัส</0>.",
"form_error_port_unsafe": "เป็นพอร์ทที่ไม่ปลอดภัย",
"form_error_password": "รหัสผ่านไม่ตรงกัน",
"reset_settings": "รีเซ็ตการตั้งค่า",
"update_announcement": "AdGuard Home {{version}} พร้อมแล้ว <0>กดตรงนี้</0> สำหรับข้อมูลเพิ่มเติม",
"dns_addresses": "ที่อยู่ DNS",
"dns_start": "เซิร์ฟเวอร์ DNS เริ่มทำงาน",
"dns_status_error": "เกิดข้อผิดพลาดในการตรวจสอบสถานะเซิร์ฟเวอร์ DNS",
"down": "ดับ",
"fix": "ซ่อม",
"dns_providers": "นี่คือรายการ <0>ของผู้ให้บริการ DNS ที่เป็นที่รู้จัก</0> ให้เลือก",
"update_now": "อัปเดตตอนนี้",
"update_failed": "อัปเดทล้มเหลว กรุณา <a> ทำตามขั้นตอน </a> เพื่ออัพเดทด้วยตนเอง",
"processing_update": "รอซักครู่ AdGuard Home กำลังอัปเดท",
"clients_title": "เครื่องลูกข่าย",
"clients_desc": "ตั้งค่าอุปกรณ์เพื่อเชื่อมต่อ AdGuard Home",
"settings_global": "ทั่วโลก",
"settings_custom": "กำหนดเอง",
"table_client": "เครื่องลูกข่าย",
@@ -302,14 +246,9 @@
"client_updated": "อัปเดตเครื่อง \"{{key}}\" สำเร็จแล้ว",
"clients_not_found": "ไม่มีเครื่องลูกข่าย",
"client_confirm_delete": "คุณแน่ใจนะว่าจะลบเครื่อง \"{{key}}\"?",
"auto_clients_title": "เครื่อง (runtime)",
"auto_clients_desc": "ข้อมูลเกี่ยวกับไคลเอนต์ที่ใช้ AdGuard Home แต่ไม่ได้เก็บไว้ในการกำหนดค่า",
"access_title": "เข้าถึงการตั้งค่า",
"access_desc": "ที่นี่คุณสามารถกำหนดค่ากฎการเข้าถึงสำหรับเซิร์ฟเวอร์ AdGuard Home DNS",
"access_allowed_title": "ลูกค้าที่ได้รับอนุญาต",
"access_allowed_desc": "รายการ CIDR หรือที่อยู่ IP หากกำหนดค่า AdGuard Home จะยอมรับคำขอจากที่อยู่ IP เหล่านี้เท่านั้น",
"access_disallowed_title": "ลูกค้าไม่ได้รับอนุญาต",
"access_disallowed_desc": "รายการ CIDR หรือที่อยู่ IP หากกำหนดค่าไว้ AdGuard Home จะส่งคำขอจากที่อยู่ IP เหล่านี้",
"access_blocked_title": "โดเมนที่ถูกปิดกั้น",
"check_updates_now": "ตรวจสอบการปรับปรุง",
"setup_dns_privacy_other_title": "การใช้งานอื่น ๆ",
@@ -318,8 +257,6 @@
"rewrite_add": "เพิ่ม DNS rewrite",
"form_domain": "ป้อนชื่อโดเมน",
"form_answer": "ป้อนชื่อโดเมนหรือ IP",
"form_error_domain_format": "รูปแบบ Domain ไม่ถูกต้อง",
"form_error_answer_format": "รูปแบบคำตอบไม่ถูกต้อง",
"configure": "กำหนดค่า",
"main_settings": "ตั้งค่าหลัก",
"block_services": "ปิดกั้นบริการเฉพาะ",
@@ -348,8 +285,6 @@
"filter_updated": "อัปเดตตัวกรองสำเร็จแล้ว",
"statistics_configuration": "การกำหนดค่าสถิติ",
"statistics_retention": "การเก็บรักษาสถิติ",
"statistics_retention_desc": "หากคุณลดค่าช่วงเวลาข้อมูลบางอย่างจะหายไป",
"statistics_clear": " ล้างค่าสถิติ",
"statistics_clear_confirm": "คุณแน่ใจหรือไม่ว่าต้องการล้างสถิติ?",
"statistics_retention_confirm": "คุณแน่ใจหรือไม่ว่าต้องการเปลี่ยนการเก็บรักษาสถิติ? หากคุณลดค่าช่วงเวลา ข้อมูลบางอย่างจะหายไป",
"statistics_cleared": "สถิติได้ถูกล้างเรียบร้อยแล้ว",
@@ -357,7 +292,6 @@
"interval_hours_plural": "{{count}} ชั่วโมง",
"filters_configuration": "การกำหนดค่าตัวกรอง",
"filters_enable": "เปิดใช้งานตัวกรอง",
"filters_interval": "ตัวกรองช่วงเวลาการอัปเดต",
"disabled": "ปิดใช้งาน",
"username_label": "ชื่อผู้ใช้",
"username_placeholder": "ป้อนชื่อผู้ใช้",
@@ -372,30 +306,21 @@
"netname": "ชื่อเครือข่าย",
"network": "เครือข่าย",
"descr": "คำอธิบาย",
"whois": "Whois",
"filtering_rules_learn_more": "<0>เรียนรู้เพิ่มเติม</0> เกี่ยวกับการสร้างรายการปิดกั้นโฮสต์ของคุณเอง",
"blocked_by_response": "ปิดกั้นโดย CNAME หรือ IP ในการตอบกลับ",
"try_again": "ลองอีกครั้ง",
"domain_desc": "ป้อนชื่อโดเมนหรือไวด์การ์ดที่คุณต้องการเขียนใหม่",
"example_rewrite_domain": "เขียนคำตอบซ้ำสำหรับชื่อโดเมนนี้เท่านั้น",
"example_rewrite_wildcard": "เขียนคำตอบใหม่ทั้งหมดสำหรับ <0>example.org</0> โดเมนย่อย",
"disable_ipv6": "ปิดใช้งาน IPv6",
"disable_ipv6_desc": "หากเปิดใช้งานคุณสมบัตินี้การสืบค้น DNS ทั้งหมดสำหรับที่อยู่ IPv6 (ประเภท AAAA) จะถูกทิ้ง",
"autofix_warning_text": "หากคุณคลิก \"แก้ไข\" AdGuardHome จะกำหนดค่าระบบของคุณเพื่อใช้เซิร์ฟเวอร์ AdGuardHome",
"autofix_warning_list": "มันจะทำงานเหล่านี้: <0>ปิดการใช้งานระบบ DNSStubListener</0> <0>ตั้งที่อยู่เซิร์ฟเวอร์ DNS เป็น 127.0.0.1</0> <0>แทนที่เป้าหมายลิงก์สัญลักษณ์ของ /etc/resolv.conf เป็น /run/systemd/resolve/resolv.conf</0> <0>หยุด DNSStubListener (โหลดบริการแก้ไขระบบซ้ำ)</0>",
"autofix_warning_result": "ดังนั้น AdGuardHome จะประมวลผลคำขอ DNS ทั้งหมดจากระบบของคุณตามค่าเริ่มต้น",
"tags_title": "แท็ก",
"tags_desc": "คุณสามารถเลือกแท็กที่สอดคล้องกับลูกค้า แท็กสามารถรวมอยู่ในกฎการกรองและอนุญาตให้คุณใช้งานได้อย่างถูกต้องมากขึ้น <0>เรียนรู้เพิ่มเติม</0>",
"form_select_tags": "เลือกแท็กเครื่อง",
"check_title": "ตรวจสอบการกรอง",
"check_desc": "ตรวจสอบว่าชื่อโฮสต์ถูกกรอง",
"form_enter_host": "ป้อนชื่อโฮสต์",
"show_processed_responses": "การประมวลผล",
"blocked_adult_websites": "ถูกปิดกั้นโดยการควบคุมของผู้ปกครอง",
"safe_search": "ค้นหาอย่างปลอดภัย",
"blocklist": "บัญชีดำ",
"filter_category_other": "อื่น ๆ",
"parental_control": "ควบคุมโดยผู้ปกครอง",
"sunday_short": "อาทิตย์",
"monday_short": "จันทร์",
"tuesday_short": "อังคาร",

View File

@@ -6,7 +6,7 @@
"upstream_parallel": "Tüm üst sunucuları eş zamanlı sorgulayarak çözümlemeyi hızlandırmak için paralel sorgular kullanın.",
"parallel_requests": "Paralel istekler",
"load_balancing": "Yük dengeleme",
"load_balancing_desc": "Her seferde bir üst sunucuyu sorgulayın. AdGuard Home, sunucuyu seçmek için ağırlıklı rastgele algoritmasını kullanır, böylece en hızlı sunucu daha sık kullanılır.",
"load_balancing_desc": "Aynı anda bir üst kaynak sunucusunu sorgulayın. AdGuard Home, en düşük başarısız arama sayısına ve en düşük ortalama arama süresine sahip sunucuları seçmek için ağırlıklı rastgele bir algoritma kullanır.",
"bootstrap_dns": "DNS Önyükleme sunucuları",
"bootstrap_dns_desc": "Üst kaynak olarak belirttiğiniz DoH/DoT çözümleyicilerin IP adreslerini çözümlemek için kullanılan DNS sunucularının IP adresleri. Yorumlara izin verilmez.",
"fallback_dns_title": "Yedek DNS sunucuları",
@@ -68,7 +68,7 @@
"ip": "IP",
"dhcp_table_hostname": "Ana makine Adı",
"dhcp_table_expires": "Bitiş tarihi",
"dhcp_warning": "DHCP sunucusunu yine de etkinleştirmek istiyorsanız, ağınızda başka aktif DHCP sunucusu olmadığından emin olun, aksi takdirde ağa bağlı cihazların İnternet bağlantısı kesilebilir!",
"dhcp_warning": "DHCP sunucusunu yine de etkinleştirmek istiyorsanız, ağınızda başka aktif DHCP sunucusu olmadığından emin olun, aksi takdirde ağa bağlı cihazların internet bağlantısı kesilebilir!",
"dhcp_error": "AdGuard Home, ağda başka bir etkin DHCP sunucusu olup olmadığını belirleyemedi",
"dhcp_static_ip_error": "DHCP sunucusunu kullanmak için sabit bir IP adresi ayarlanmalıdır. AdGuard Home, bu ağ arayüzünün sabit bir IP adresi kullanılarak yapılandırılıp yapılandırılmadığını belirleyemedi. Lütfen sabit IP adresini elle ayarlayın.",
"dhcp_dynamic_ip_found": "Sisteminiz, <0>{{interfaceName}}</0> arayüzü için dinamik IP adresi yapılandırması kullanıyor. DHCP sunucusunu kullanmak için sabit bir IP adresi ayarlanmalıdır. Geçerli olan IP adresiniz <0>{{ipAddress}}</0>. \"DHCP sunucusunu etkinleştir\" düğmesine basarsanız, AdGuard Home bu IP adresini otomatik bir şekilde sabit olarak ayarlayacaktır.",
@@ -154,7 +154,7 @@
"use_adguard_parental": "AdGuard ebeveyn denetimi web hizmetini kullan",
"use_adguard_parental_hint": "AdGuard Home, alan adının yetişkin içerik bulundurup bulundurmadığını kontrol eder. Gezinti koruması web hizmeti ile kullandığımız aynı gizlilik dostu API'yi kullanır.",
"enforce_safe_search": "Güvenli Aramayı kullan",
"enforce_save_search_hint": "AdGuard Home, şu arama motorlarında güvenli aramayı uygular: Google, YouTube, Bing, DuckDuckGo, Yandex ve Pixabay.",
"enforce_save_search_hint": "AdGuard Home, şu arama motorlarında güvenli aramayı uygular: Google, YouTube, Bing, DuckDuckGo, Ecosia, Yandex, Pixabay.",
"no_servers_specified": "Sunucu belirtilmedi",
"general_settings": "Genel ayarlar",
"dns_settings": "DNS ayarları",
@@ -476,7 +476,7 @@
"client_confirm_delete": "\"{{key}}\" istemcisini silmek istediğinizden emin misiniz?",
"list_confirm_delete": "Bu listeyi silmek istediğinizden emin misiniz?",
"auto_clients_title": "Çalışma zamanı istemcileri",
"auto_clients_desc": "AdGuard Home'u kullanan veya kullanabilecek cihazların IP adresleri hakkında bilgiler. Bu bilgiler, hosts dosyaları, ters DNS, vb. dahil olmak üzere çeşitli kaynaklardan toplanır.",
"auto_clients_desc": "AdGuard Home'u kullanan veya kullanabilecek cihazların IP adresleri hakkında bilgiler. Bu bilgiler, hosts dosyaları, ters DNS, vb. dâhil olmak üzere çeşitli kaynaklardan toplanır.",
"access_title": "Erişim ayarları",
"access_desc": "AdGuard Home DNS sunucusu için erişim kurallarını buradan yapılandırabilirsiniz",
"access_allowed_title": "İzin verilen istemciler",
@@ -603,7 +603,7 @@
"autofix_warning_list": "Bu görevleri gerçekleştirir: <0>Sistem DNSStubListener'ı devre dışı bırakın</0> <0>DNS sunucusu adresini 127.0.0.1 olarak ayarlayın</0> <0>/etc/resolv.conf'un sembolik bağlantı hedefini /run/systemd/resolve/resolv.conf ile değiştirin<0> <0>DNSStubListener'ı durdurun (systemd çözümlenmiş hizmeti yeniden yükleyin)</0>",
"autofix_warning_result": "Sonuç olarak, sisteminizden gelen tüm DNS istekleri varsayılan olarak AdGuard Home tarafından işlenecektir.",
"tags_title": "Etiketler",
"tags_desc": "İstemciye karşılık gelen etiketleri seçebilirsiniz. Etiketleri daha kesin olarak uygulamak için filtreleme kurallarına dahil edin. <0>Daha fazla bilgi edinin</0>.",
"tags_desc": "İstemciye karşılık gelen etiketleri seçebilirsiniz. Etiketleri daha kesin olarak uygulamak için filtreleme kurallarına dâhil edin. <0>Daha fazla bilgi edinin</0>.",
"form_select_tags": "İstemci etiketlerini seçin",
"check_title": "Filtrelemeyi denetleyin",
"check_desc": "Ana makine adının filtreleme durumunu kontrol edin.",

View File

@@ -6,21 +6,18 @@
"upstream_parallel": "Використовувати паралельні запити, щоб пришвидшити вирішення одночасною чергою всіх оригінальних серверів.",
"parallel_requests": "Паралельні запити",
"load_balancing": "Балансування навантаження",
"load_balancing_desc": "Запитувати один сервер за раз. AdGuard Home використовуватиме зважений випадковий алгоритм для вибору сервера, щоб найшвидший сервер використовувався частіше.",
"bootstrap_dns": "Bootstrap DNS-сервери",
"bootstrap_dns_desc": "IP-адреси DNS-серверів, які використовуються для визначення IP-адрес DoH/DoT-розпізнавачів, які ви вказуєте як висхідні. Коментарі заборонені.",
"fallback_dns_title": "Резервні DNS-сервери",
"fallback_dns_desc": "Список резервних DNS-серверів, які використовуються, коли upstream DNS-сервери не відповідають. Синтаксис такий самий, як і в полі upstream сервера вище.",
"fallback_dns_placeholder": "Вводьте один резервний DNS-сервер на рядок",
"local_ptr_title": "Приватні сервери для зворотного DNS",
"local_ptr_desc": "DNS-сервери, які AdGuard Home використовує для локальних PTR-запитів. Ці сервери використовуються для вирішення PTR-запитів для адрес у приватних мережах, наприклад, «192.168.12.34». Якщо список порожній, AdGuard Home буде усталено використовувати системний DNS-сервер.",
"local_ptr_default_resolver": "Стандартно AdGuard Home користується такими зворотними DNS-вирішувачами: {{ip}}.",
"local_ptr_no_default_resolver": "AdGuard Home не зміг визначити приватні зворотні DNS-вирішувачі, які підійшли б для цієї системи.",
"local_ptr_placeholder": "Вводьте одну адресу на рядок",
"resolve_clients_title": "Увімкнути зворотне вирішення IP-адрес клієнтів",
"resolve_clients_desc": "Визначати доменні імена клієнтів за допомогою PTR-запитів до відповідних серверів — приватних DNS-серверів для локальних клієнтів та upstream-серверів для клієнтів з публічними IP-адресами.",
"use_private_ptr_resolvers_title": "Використовувати приватні зворотні DNS-резолвери",
"use_private_ptr_resolvers_desc": "Надсилати зворотні DNS-запити до вказаних серверів для клієнтів, що обслуговуються локально. Якщо вимкнено, AdGuard Home буде відповідати NXDOMAIN на всі такі PTR-запити, окрім запитів про клієнтів, що уже відомі завдяки DHCP, /etc/hosts тощо.",
"check_dhcp_servers": "Перевірити DHCP-сервери",
"save_config": "Зберегти конфігурацію",
"enabled_dhcp": "DHCP-сервер увімкнено",
@@ -154,7 +151,6 @@
"use_adguard_parental": "Використовувати вебслужбу «Батьківський контроль» AdGuard",
"use_adguard_parental_hint": "AdGuard Home перевірить, чи містить домен матеріали для дорослих. Буде використано той же безпечний API, що й для «Безпеки перегляду» AdGuard.",
"enforce_safe_search": "Використовувати Безпечний пошук",
"enforce_save_search_hint": "AdGuard Home може примусово застосовувати безпечний пошук в таких пошукових системах: Google, YouTube, Bing, DuckDuckGo, Yandex, Pixabay.",
"no_servers_specified": "Сервери не вказано",
"general_settings": "Загальні налаштування",
"dns_settings": "Налаштування DNS",

View File

@@ -6,21 +6,18 @@
"upstream_parallel": "Sử dụng truy vấn song song để tăng tốc độ giải quyết bằng cách truy vấn đồng thời tất cả các máy chủ ngược tuyến",
"parallel_requests": "Yêu cầu song song",
"load_balancing": "Cân bằng tải",
"load_balancing_desc": "Chỉ truy xuất một máy chủ trong cùng thời điểm. AdGuard Home sẽ sử dụng thuật toán trọng số ngẫu nhiên để chọn một máy chủ nhanh nhất và sử dụng máy chủ đó thường xuyên hơn.",
"bootstrap_dns": "Máy chủ DNS Bootstrap",
"bootstrap_dns_desc": "Địa chỉ IP của máy chủ DNS được sử dụng để phân giải địa chỉ IP của trình phân giải DoH/DoT mà bạn chỉ định làm thượng nguồn. Bình luận không được phép.",
"fallback_dns_title": "Máy chủ DNS dự phòng",
"fallback_dns_desc": "Danh sách máy chủ DNS dự phòng được sử dụng khi máy chủ DNS ngược tuyến không phản hồi. Cú pháp tương tự như trong trường ngược dòng chính ở trên.",
"fallback_dns_placeholder": "Nhập một máy chủ DNS dự phòng trên mỗi dòng",
"local_ptr_title": "Máy chủ DNS riêng tư",
"local_ptr_desc": "Máy chủ DNS hoặc các máy chủ mà AdGuard Home sẽ sử dụng cho các truy vấn về tài nguyên được phân phối cục bộ. Ví dụ: máy chủ này sẽ được sử dụng để phân giải tên máy khách của máy khách cho các máy khách có địa chỉ IP riêng. Nếu không được cài đặt, AdGuard Home sẽ tự động sử dụng trình phân giải DNS mặc định của bạn.",
"local_ptr_default_resolver": "Theo mặc định, AdGuard Home sử dụng các hệ thống phân giải tên miền ngược sau: {{ip}}.",
"local_ptr_no_default_resolver": "AdGuard Home không thể xác định hệ thống phân giải tên miền ngược riêng phù hợp cho hệ thống này.",
"local_ptr_placeholder": "Nhập một địa chỉ IP trên mỗi dòng",
"resolve_clients_title": "Kích hoạt cho phép phân giải ngược về địa chỉ IP của máy khách",
"resolve_clients_desc": "Nếu được bật, AdGuard Home sẽ cố gắng phân giải ngược lại địa chỉ IP của khách hàng thành tên máy chủ của họ bằng cách gửi các truy vấn PTR tới trình phân giải tương ứng (máy chủ DNS riêng cho máy khách cục bộ, máy chủ ngược dòng cho máy khách có địa chỉ IP công cộng).",
"use_private_ptr_resolvers_title": "Sử dụng trình rDNS riêng tư",
"use_private_ptr_resolvers_desc": "Thực hiện tra cứu ngược DNS cho các địa chỉ được phân phối cục bộ bằng cách sử dụng các máy chủ nguồn. Nếu bị vô hiệu hóa, AdGuard Home sẽ phản hồi với NXDOMAIN cho tất cả các yêu cầu PTR ngoại trừ các ứng dụng khách được biết đến bởi DHCP, / etc / hosts, v. v.",
"check_dhcp_servers": "Kiểm tra máy chủ DHCP",
"save_config": "Lưu thiết lập",
"enabled_dhcp": "Máy chủ DHCP đã kích hoạt",
@@ -154,7 +151,6 @@
"use_adguard_parental": "Sử dụng dịch vụ quản lý của phụ huynh AdGuard",
"use_adguard_parental_hint": "AdGuard Home sẽ kiểm tra nếu tên miền chứa từ khoá người lớn. Tính năng sử dụng API thân thiện với quyền riêng tư tương tự với dịch vụ bảo vệ duyệt web",
"enforce_safe_search": "Bắt buộc tìm kiếm an toàn",
"enforce_save_search_hint": "AdGuard Home có thể bắt buộc tìm kiếm an toàn với các dịch vụ tìm kiếm: Google, Youtube, Bing, Yandex.",
"no_servers_specified": "Không có máy chủ nào được liệt kê",
"general_settings": "Cài đặt chung",
"dns_settings": "Cài đặt DNS",
@@ -675,7 +671,6 @@
"use_saved_key": "Sử dụng khóa đã lưu trước đó",
"parental_control": "Quản lý của phụ huynh",
"safe_browsing": "Duyệt web an toàn",
"served_from_cache": "{{value}} <i>(được phục vụ từ bộ nhớ cache)</i>",
"form_error_password_length": "Mật khẩu phải dài từ {{min}} đến {{max}} ký tự",
"anonymizer_notification": "<0> Lưu ý:</0> Tính năng ẩn danh IP được bật. Bạn có thể tắt nó trong <1> Cài đặt chung</1>.",
"confirm_dns_cache_clear": "Bạn có chắc chắn muốn xóa bộ đệm ẩn DNS không?",

View File

@@ -6,7 +6,7 @@
"upstream_parallel": "使用并行请求以同时查询所有上游服务器来加快解析速度。",
"parallel_requests": "并行请求",
"load_balancing": "负载均衡",
"load_balancing_desc": "一次查询一台服务器。AdGuard Home 使用加权随机算法来选择服务器,以便更常使用最快的服务器。",
"load_balancing_desc": "一次查询一台服务器。AdGuard Home 使用加权随机算法来选择具有最少失败查找和最低平均查找时间的服务器。",
"bootstrap_dns": "Bootstrap DNS 服务器",
"bootstrap_dns_desc": "DNS 服务器的 IP 地址,用于解析指定为上游的 DoH/DoT 解析器的 IP 地址。不允许添加注释。",
"fallback_dns_title": "后备 DNS 服务器",
@@ -154,7 +154,7 @@
"use_adguard_parental": "使用 AdGuard 【家长控制】服务",
"use_adguard_parental_hint": "AdGuard Home 将使用与浏览安全服务相同的隐私性强的 API 来检查域名指向的网站是否包含成人内容。",
"enforce_safe_search": "使用安全搜索",
"enforce_save_search_hint": "AdGuard Home 对以下搜索引擎强制启用安全搜索Google、YouTube、Bing、DuckDuckGo、Yandex、Pixabay。",
"enforce_save_search_hint": "AdGuard Home 将会在下列搜索引擎强制启用安全搜索Google、YouTube、Bing、DuckDuckGo、Ecosia、Yandex、Pixabay。",
"no_servers_specified": "未找到指定的服务器",
"general_settings": "常规设置",
"dns_settings": "DNS 设置",

View File

@@ -6,21 +6,18 @@
"upstream_parallel": "使用平行查詢,同時查詢所有上游伺服器來加速解析結果",
"parallel_requests": "平行處理",
"load_balancing": "負載平衡",
"load_balancing_desc": "一次只查詢一個伺服器。AdGuard Home 會使用加權隨機取樣來選擇使用的查詢結果,以確保速度最快的伺服器能被充分運用。",
"bootstrap_dns": "引導Boostrap DNS 伺服器",
"bootstrap_dns_desc": "Bootstrap DNS 伺服器用於解析您所設定的上游 DoH/DoT 解析器的 IP 地址",
"fallback_dns_title": "備用 DNS 伺服器",
"fallback_dns_desc": "備用 DNS 伺服器列表:於主要 DNS 伺服器沒有回應時使用。語法與主要 DNS 伺服器設定欄位相同。",
"fallback_dns_placeholder": "每行輸入一個備用 DNS 伺服器",
"local_ptr_title": "私人 DNS 伺服器",
"local_ptr_desc": "AdGuard Home 用於區域 PTR 查詢的 DNS 伺服器。這些伺服器將用於解析具有私人 IP 位址的用戶端的主機名稱,例如 \"192.168.12.34\",使用 rDNS。如果沒有設定AdGuard Home 將自動使用您的系統預設 DNS 解析。",
"local_ptr_default_resolver": "AdGuard Home 預設使用以下作為 DNS 反解器:{{ip}}",
"local_ptr_no_default_resolver": "AdGuard Home 無法為此系統確定適合私有反解析器。",
"local_ptr_placeholder": "每行輸入一個伺服器位址",
"resolve_clients_title": "啟用用戶端的 IP 位址的反向解析",
"resolve_clients_desc": "透過相應的伺服器傳送 PTR 查詢(本機用戶端使用私人 DNS 伺服器,公共 IP 使用上游伺服器),將客戶端的 IP 反解為主機名稱。",
"use_private_ptr_resolvers_title": "使用私人 DNS 反解器",
"use_private_ptr_resolvers_desc": "使用這些上游為本機提供反解 DNS 查詢。如果禁用 AdGuard Home 將會對相關 PTR 請求回應 NXDOMAIN但 DHCP、/etc/hosts 等已知的用戶端除外。",
"check_dhcp_servers": "檢查 DHCP 伺服器",
"save_config": "儲存設定",
"enabled_dhcp": "DHCP 伺服器已啟動",
@@ -154,7 +151,6 @@
"use_adguard_parental": "使用 AdGuard 家長監護功能",
"use_adguard_parental_hint": "AdGuard Home 將比對查詢網域是否含有成人內容。它使用與 AdGuard 瀏覽安全一樣的尊重個人隱私的 API 來進行檢查。",
"enforce_safe_search": "強制使用安全搜尋",
"enforce_save_search_hint": "AdGuard Home 可在下列搜尋引擎使用強制安全搜尋Google、YouTube、Bing、DuckDuckGo, Yandex 和 Pixabay。",
"no_servers_specified": "沒有指定的伺服器",
"general_settings": "一般設定",
"dns_settings": "DNS 設定",

View File

@@ -6,7 +6,7 @@
"upstream_parallel": "透過同時地查詢所有上游的伺服器,使用並行的查詢以加速解析。",
"parallel_requests": "並行的請求",
"load_balancing": "負載平衡",
"load_balancing_desc": "次查詢一個上游伺服器。AdGuard Home 使用它的加權隨機演算法來選擇伺服器,以便最快的伺服器被更常使用。",
"load_balancing_desc": "次查詢一伺服器。AdGuard Home 使用加權隨機演算法來選擇具有最少失敗查詢和最低平均查詢時間的伺服器。",
"bootstrap_dns": "自我啟動BootstrapDNS 伺服器",
"bootstrap_dns_desc": "DNS 伺服器的 IP 位址,用於解析您指定為上游伺服器的 DoH/DoT 解析器的 IP 位址。不允許註釋。",
"fallback_dns_title": "應變 DNS 伺服器",
@@ -154,7 +154,7 @@
"use_adguard_parental": "使用 AdGuard 家長控制之網路服務",
"use_adguard_parental_hint": "AdGuard Home 將檢查網域是否包含成人資料。它使用如同瀏覽安全網路服務一樣之對隱私友好的應用程式介面API。",
"enforce_safe_search": "使用安全搜尋",
"enforce_save_search_hint": "AdGuard Home 將在下列的搜尋引擎Google、YouTube、Bing、DuckDuckGo、Yandex 和 Pixabay 中強制執行安全搜尋。",
"enforce_save_search_hint": "AdGuard Home 將在下列的搜尋引擎Google、YouTube、Bing、DuckDuckGo、Ecosia、Yandex 和 Pixabay 中強制執行安全搜尋。",
"no_servers_specified": "無已明確指定的伺服器",
"general_settings": "一般設定",
"dns_settings": "DNS 設定",

View File

@@ -1,6 +1,6 @@
import React, { useEffect } from 'react';
import { Field, reduxForm } from 'redux-form';
import { Field, type InjectedFormProps, reduxForm } from 'redux-form';
import { useTranslation } from 'react-i18next';
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
@@ -104,14 +104,13 @@ const FORM_NAMES = {
response_status: 'response_status',
};
interface FiltersFormProps {
type FiltersFormProps = {
className?: string;
responseStatusClass?: string;
change: (...args: unknown[]) => unknown;
setIsLoading?: (...args: unknown[]) => unknown;
}
setIsLoading: (...args: unknown[]) => unknown;
};
const Form = (props: FiltersFormProps) => {
const Form = (props: FiltersFormProps & InjectedFormProps) => {
const { className = '', responseStatusClass, setIsLoading, change } = props;
const { t } = useTranslation();
@@ -142,7 +141,6 @@ const Form = (props: FiltersFormProps) => {
const onInputClear = async () => {
setIsLoading(true);
change(FORM_NAMES.search, DEFAULT_LOGS_FILTER[FORM_NAMES.search]);
setIsLoading(false);
};
@@ -195,7 +193,7 @@ const Form = (props: FiltersFormProps) => {
);
};
export default reduxForm({
export const FiltersForm = reduxForm<Record<string, any>, FiltersFormProps>({
form: FORM_NAME.LOGS_FILTER,
enableReinitialize: true,
})(Form);

View File

@@ -2,7 +2,7 @@ import React from 'react';
import { useTranslation } from 'react-i18next';
import { useDispatch } from 'react-redux';
import Form from './Form';
import { FiltersForm } from './Form';
import { refreshFilteredLogs } from '../../../actions/queryLogs';
import { addSuccessToast } from '../../../actions/toasts';
@@ -38,12 +38,7 @@ const Filters = ({ filter, setIsLoading }: FiltersProps) => {
</svg>
</button>
</h1>
<Form
// responseStatusClass="d-sm-block"
// setIsLoading={setIsLoading}
initialValues={filter}
/>
<FiltersForm responseStatusClass="d-sm-block" setIsLoading={setIsLoading} initialValues={filter} />
</div>
);
};

View File

@@ -38,7 +38,7 @@ class Encryption extends Component<EncryptionProps> {
handleFormChange = debounce((values) => {
const submitValues = this.getSubmitValues(values);
if (submitValues.enabled || submitValues.serve_plain_dns) {
if (submitValues.enabled) {
this.props.validateTlsConfig(submitValues);
}
}, DEBOUNCE_TIMEOUT);

View File

@@ -12,12 +12,11 @@ const Version = () => {
const dashboard = useSelector((state: RootState) => state.dashboard, shallowEqual);
const install = useSelector((state: RootState) => state.install, shallowEqual);
if (!dashboard || !install) {
if (!dashboard && !install) {
return null;
}
const { dnsVersion, processingVersion, checkUpdateFlag } = dashboard;
const version = dnsVersion || install?.dnsVersion;
const version = dashboard?.dnsVersion || install?.dnsVersion;
const onClick = () => {
dispatch(getVersion(true));
@@ -35,12 +34,12 @@ const Version = () => {
</>
)}
{checkUpdateFlag && (
{dashboard?.checkUpdateFlag && (
<button
type="button"
className="btn btn-icon btn-icon-sm btn-outline-primary btn-sm ml-2"
onClick={onClick}
disabled={processingVersion}
disabled={dashboard?.processingVersion}
title={t('check_updates_now')}>
<svg className="icons icon12">
<use xlinkHref="#refresh" />

View File

@@ -295,7 +295,7 @@ export default {
"phishing_army": {
"name": "Phishing Army",
"categoryId": "security",
"homepage": "https://gitlab.com/malware-filter/phishing-filter",
"homepage": "https://phishing.army/",
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_18.txt"
},
"scam_blocklist_by_durablenapkin": {

View File

@@ -66,7 +66,7 @@ export const renderFormattedClientCell = (value: any, info: any, isDetailed = fa
}
return (
<div className="logs__text mw-100" title={value}>
<div className="logs__text logs__text--client mw-100" title={value}>
<Link to={`logs?search="${encodeURIComponent(value)}"`}>{nameContainer}</Link>
{whoisContainer}
</div>

View File

@@ -1,5 +1,5 @@
{
"timeUpdated": "2024-03-01T00:10:14.031Z",
"timeUpdated": "2024-07-01T00:11:48.891Z",
"categories": {
"0": "audio_video_player",
"1": "comments",
@@ -1182,6 +1182,13 @@
"url": "https://www.admitad.com/en/#",
"companyId": "admitad"
},
"admixer": {
"name": "Admixer",
"categoryId": 4,
"url": "https://admixer.com/",
"companyId": "admixer",
"source": "AdGuard"
},
"admixer.net": {
"name": "Admixer",
"categoryId": 4,
@@ -1473,6 +1480,13 @@
"url": "http://www.adplus.co.id/",
"companyId": "adplus"
},
"adprofex": {
"name": "AdProfex",
"categoryId": 4,
"url": "https://adprofex.com/",
"companyId": "adprofex",
"source": "AdGuard"
},
"adprofy": {
"name": "AdProfy",
"categoryId": 4,
@@ -2019,6 +2033,13 @@
"url": "https://hybridtheory.com/",
"companyId": "affectv"
},
"affilbox": {
"name": "Affilbox",
"categoryId": 4,
"url": "https://affilbox.com/",
"companyId": "affilbox",
"source": "AdGuard"
},
"affiliate-b": {
"name": "Affiliate-B",
"categoryId": 4,
@@ -2373,6 +2394,13 @@
"url": "http://www.amadesa.com/",
"companyId": "amadesa"
},
"amap": {
"name": "Amap",
"categoryId": 2,
"url": "https://www.amap.com/",
"companyId": "softbank",
"source": "AdGuard"
},
"amazon": {
"name": "Amazon.com",
"categoryId": 8,
@@ -4591,6 +4619,13 @@
"url": "http://clickaider.com/",
"companyId": "clickaider"
},
"clickaine": {
"name": "Clickaine",
"categoryId": 4,
"url": "https://clickaine.com/",
"companyId": "clickaine",
"source": "AdGuard"
},
"clickbank": {
"name": "ClickBank",
"categoryId": 4,
@@ -12470,6 +12505,13 @@
"url": "http://nolix.ru/",
"companyId": "nolix"
},
"nonli": {
"name": "Nonli",
"categoryId": 4,
"url": "https://www.nonli.com/",
"companyId": "nonli",
"source": "AdGuard"
},
"nonstop_consulting": {
"name": "Resolution Media",
"categoryId": 4,
@@ -12869,6 +12911,13 @@
"url": "http://opensharecount.com/",
"companyId": "open_share_count"
},
"openai": {
"name": "OpenAI",
"categoryId": 8,
"url": "https://openai.com/",
"companyId": "openai",
"source": "AdGuard"
},
"openload": {
"name": "Openload",
"categoryId": 9,
@@ -20339,6 +20388,7 @@
"adguard.org": "adguard",
"adtidy.org": "adguard",
"agrd.io": "adguard",
"agrd.eu": "adguard",
"adguard-dns.com": "adguard_dns",
"adguard-dns.io": "adguard_dns",
"adguard-vpn.com": "adguard_vpn",
@@ -20413,7 +20463,8 @@
"admicro.vn": "admicro",
"vcmedia.vn": "admicro",
"admitad.com": "admitad.com",
"admixer.net": "admixer.net",
"admixer.net": "admixer",
"admixer.com": "admixer",
"admized.com": "admized",
"admo.tv": "admo.tv",
"a.admob.com": "admob",
@@ -20493,6 +20544,8 @@
"advg.jp": "adplan",
"c.p-advg.com": "adplan",
"adplus.co.id": "adplus",
"adprofex.com": "adprofex",
"ads2.bid": "adprofex",
"adframesrc.com": "adprofy",
"adserve.adpulse.ir": "adpulse",
"ads.adpv.com": "adpv",
@@ -20616,6 +20669,8 @@
"affectv.com": "affectv",
"go.affec.tv": "affectv",
"hybridtheory.com": "affectv",
"affilbox.com": "affilbox",
"affilbox.cz": "affilbox",
"track.affiliate-b.com": "affiliate-b",
"affiliate4you.nl": "affiliate4you",
"ads.affbuzzads.com": "affiliatebuzz",
@@ -20704,6 +20759,7 @@
"inputs.alooma.com": "alooma",
"arena.altitude-arena.com": "altitude_digital",
"amadesa.com": "amadesa",
"amap.com": "amap",
"amazon.ca": "amazon",
"amazon.co.jp": "amazon",
"amazon.co.uk": "amazon",
@@ -21231,6 +21287,7 @@
"clickandchat.com": "click_and_chat",
"software.clickback.com": "click_back",
"hit.clickaider.com": "clickaider",
"clickaine.com": "clickaine",
"clickbank.net": "clickbank",
"cbproads.com": "clickbank_proads",
"adtoll.com": "clickbooth",
@@ -22242,6 +22299,7 @@
"withgoogle.com": "google",
"googleadservices.com": "google_adservices",
"google-analytics.com": "google_analytics",
"app-analytics-services.com": "google_analytics",
"ssl-google-analytics.l.google.com": "google_analytics",
"www-googletagmanager.l.google.com": "google_analytics",
"appspot.com": "google_appspot",
@@ -23396,6 +23454,8 @@
"noaa.gov": "noaa.gov",
"track.noddus.com": "noddus",
"contextbar.ru": "nolix",
"nonli.com": "nonli",
"non.li": "nonli",
"trkme.net": "nonstop_consulting",
"noop.style": "noop.style",
"nosto.com": "nosto.com",
@@ -23473,6 +23533,10 @@
"realmedia.com": "open_adstream",
"realmediadigital.com": "open_adstream",
"opensharecount.com": "open_share_count",
"chatgpt.com": "openai",
"oaistatic.com": "openai",
"oaiusercontent.com": "openai",
"openai.com": "openai",
"oloadcdn.net": "openload",
"openload.co": "openload",
"openstat.net": "openstat",
@@ -23649,6 +23713,7 @@
"popads.net": "popads",
"popadscdn.net": "popads",
"popcash.net": "popcash",
"popcashjs.b-cdn.net": "popcash",
"desv383oqqc0.cloudfront.net": "popcorn_metrics",
"popin.cc": "popin.cc",
"cdn.popmyads.com": "popmyads",
@@ -24356,6 +24421,10 @@
"spoteffects.net": "spoteffect",
"scdn.co": "spotify",
"spotify.com": "spotify",
"pscdn.co": "spotify",
"spotifycdn.com": "spotify",
"spotifycdn.net": "spotify",
"spotilocal.com": "spotify",
"embed.spotify.com": "spotify_embed",
"spotscenered.info": "spotscenered.info",
"spotx.tv": "spotxchange",
@@ -24675,6 +24744,8 @@
"t.co": "twitter",
"twimg.com": "twitter",
"twitter.com": "twitter",
"twttr.com": "twitter",
"x.com": "twitter",
"ads-twitter.com": "twitter_ads",
"analytics.twitter.com": "twitter_analytics",
"tellapart.com": "twitter_for_business",

24
go.mod
View File

@@ -1,11 +1,11 @@
module github.com/AdguardTeam/AdGuardHome
go 1.22.4
go 1.22.5
require (
github.com/AdguardTeam/dnsproxy v0.71.2
github.com/AdguardTeam/golibs v0.23.2
github.com/AdguardTeam/urlfilter v0.18.0
github.com/AdguardTeam/dnsproxy v0.72.2
github.com/AdguardTeam/golibs v0.25.1
github.com/AdguardTeam/urlfilter v0.19.0
github.com/NYTimes/gziphandler v1.1.1
github.com/ameshkov/dnscrypt/v2 v2.3.0
github.com/bluele/gcache v0.0.2
@@ -27,15 +27,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.59
github.com/miekg/dns v1.1.61
github.com/quic-go/quic-go v0.44.0
github.com/stretchr/testify v1.9.0
github.com/ti-mo/netfilter v0.5.2
go.etcd.io/bbolt v1.3.10
golang.org/x/crypto v0.23.0
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842
golang.org/x/net v0.25.0
golang.org/x/sys v0.20.0
golang.org/x/crypto v0.25.0
golang.org/x/exp v0.0.0-20240707233637-46b078467d37
golang.org/x/net v0.27.0
golang.org/x/sys v0.22.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1
gopkg.in/yaml.v3 v3.0.1
howett.net/plist v1.0.1
@@ -58,9 +58,9 @@ require (
github.com/quic-go/qpack v0.4.0 // indirect
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 // indirect
go.uber.org/mock v0.4.0 // indirect
golang.org/x/mod v0.17.0 // indirect
golang.org/x/mod v0.19.0 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/text v0.15.0 // indirect
golang.org/x/tools v0.21.0 // indirect
golang.org/x/text v0.16.0 // indirect
golang.org/x/tools v0.23.0 // indirect
gonum.org/v1/gonum v0.15.0 // indirect
)

60
go.sum
View File

@@ -1,9 +1,9 @@
github.com/AdguardTeam/dnsproxy v0.71.2 h1:dFG2wga4GDdj1eI3rU2wqjQ6QGQm9MjLRb5ZzyH3Vgg=
github.com/AdguardTeam/dnsproxy v0.71.2/go.mod h1:huI5zyWhlimHBhg0jt2CMinXzsEHymI+WlvxIfmfEGA=
github.com/AdguardTeam/golibs v0.23.2 h1:rMjYantwtQ39e8G4zBQ6ZLlm4s3XH30Bc9VxhoOHwao=
github.com/AdguardTeam/golibs v0.23.2/go.mod h1:o9i55Sx6v7qogRQeqaBfmLbC/pZqeMBWi015U5PTDY0=
github.com/AdguardTeam/urlfilter v0.18.0 h1:ZZzwODC/ADpjJSODxySrrUnt/fvOCfGFaCW6j+wsGfQ=
github.com/AdguardTeam/urlfilter v0.18.0/go.mod h1:IXxBwedLiZA2viyHkaFxY/8mjub0li2PXRg8a3d9Z1s=
github.com/AdguardTeam/dnsproxy v0.72.2 h1:0uItzXnUIuC9r+ZvPbNquGaAHvdWnWLbhSDdxsZk5og=
github.com/AdguardTeam/dnsproxy v0.72.2/go.mod h1:PA1UiTtTHMbXPv9NjHat+zrsgK8S7p/RJ+j/3tNqtUE=
github.com/AdguardTeam/golibs v0.25.1 h1:po5dBbFCoZAySsbsMN/ZRB0WTLYDA1d8BxPgvriu/EA=
github.com/AdguardTeam/golibs v0.25.1/go.mod h1:HaTyS2wCbxFudjht9N/+/Qf1b5cMad2BAYSwe7DPCXI=
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/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=
@@ -78,8 +78,8 @@ 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.59 h1:C9EXc/UToRwKLhK5wKU/I4QVsBUc8kE6MkHBkeypWZs=
github.com/miekg/dns v1.1.59/go.mod h1:nZpewl5p6IvctfgrckopVx2OlSEHPRO/U4SYkRklrEk=
github.com/miekg/dns v1.1.61 h1:nLxbwF3XxhwVSm8g9Dghm9MHPaUZuqhPiGL+675ZmEs=
github.com/miekg/dns v1.1.61/go.mod h1:mnAarhS3nWaW+NVP2wTkYVIZyHNJ098SJZUki3eykwQ=
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=
@@ -101,8 +101,8 @@ github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
github.com/quic-go/quic-go v0.44.0 h1:So5wOr7jyO4vzL2sd8/pD9Kesciv91zSk8BoFngItQ0=
github.com/quic-go/quic-go v0.44.0/go.mod h1:z4cx/9Ny9UtGITIPzmPTXh1ULfOyWh4qGQlpnPcWmek=
github.com/shirou/gopsutil/v3 v3.23.7 h1:C+fHO8hfIppoJ1WdsVm1RoI0RwXoNdfTK7yWXV0wVj4=
github.com/shirou/gopsutil/v3 v3.23.7/go.mod h1:c4gnmoRC0hQuaLqvxnx1//VXQ0Ms/X9UnJF8pddY5z4=
github.com/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=
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@@ -114,36 +114,36 @@ github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8
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=
github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM=
github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI=
github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms=
github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4=
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 h1:pyC9PaHYZFgEKFdlp3G8RaCKgVpHZnecvArXvPXcFkM=
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701/go.mod h1:P3a5rG4X7tI17Nn3aOIAYr5HbIMukwXG0urG0WuL8OA=
github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw=
github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
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=
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.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM=
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
golang.org/x/exp v0.0.0-20240707233637-46b078467d37 h1:uLDX+AfeFCct3a2C7uIWBKMJIR3CJMhcgfrUAqjRK6w=
golang.org/x/exp v0.0.0-20240707233637-46b078467d37/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8=
golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
@@ -158,19 +158,19 @@ 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.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
golang.org/x/sys v0.22.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.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
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.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw=
golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg=
golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI=
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=

View File

@@ -161,7 +161,8 @@ func (hc *HostsContainer) handleEvents() {
defer close(hc.updates)
ok, eventsCh := true, hc.watcher.Events()
eventsCh := hc.watcher.Events()
ok := eventsCh != nil
for ok {
select {
case _, ok = <-eventsCh:

View File

@@ -6,10 +6,10 @@ import (
"bufio"
"fmt"
"io"
"net"
"strings"
"github.com/AdguardTeam/AdGuardHome/internal/aghos"
"github.com/AdguardTeam/golibs/netutil"
)
func ifaceHasStaticIP(ifaceName string) (ok bool, err error) {
@@ -38,9 +38,13 @@ func (n interfaceName) rcConfStaticConfig(r io.Reader) (_ []string, cont bool, e
// TODO(e.burkov): Expand the check to cover possible
// configurations from man rc.conf(5).
fields := strings.Fields(line[cfgLeft:cfgRight])
if len(fields) >= 2 &&
strings.EqualFold(fields[0], "inet") &&
net.ParseIP(fields[1]) != nil {
switch {
case
len(fields) < 2,
!strings.EqualFold(fields[0], "inet"),
!netutil.IsValidIPString(fields[1]):
continue
default:
return nil, false, s.Err()
}
}

View File

@@ -6,10 +6,10 @@ import (
"bufio"
"fmt"
"io"
"net"
"strings"
"github.com/AdguardTeam/AdGuardHome/internal/aghos"
"github.com/AdguardTeam/golibs/netutil"
)
func ifaceHasStaticIP(ifaceName string) (ok bool, err error) {
@@ -25,7 +25,13 @@ func hostnameIfStaticConfig(r io.Reader) (_ []string, ok bool, err error) {
for s.Scan() {
line := strings.TrimSpace(s.Text())
fields := strings.Fields(line)
if len(fields) >= 2 && fields[0] == "inet" && net.ParseIP(fields[1]) != nil {
switch {
case
len(fields) < 2,
fields[0] != "inet",
!netutil.IsValidIPString(fields[1]):
continue
default:
return nil, false, s.Err()
}
}

View File

@@ -160,3 +160,34 @@ func (w *osWatcher) handleErrors() {
log.Error("%s: %s", osWatcherPref, err)
}
}
// EmptyFSWatcher is a no-op implementation of the [FSWatcher] interface. It
// may be used on systems not supporting filesystem events.
type EmptyFSWatcher struct{}
// type check
var _ FSWatcher = EmptyFSWatcher{}
// Start implements the [FSWatcher] interface for EmptyFSWatcher. It always
// returns nil error.
func (EmptyFSWatcher) Start() (err error) {
return nil
}
// Close implements the [FSWatcher] interface for EmptyFSWatcher. It always
// returns nil error.
func (EmptyFSWatcher) Close() (err error) {
return nil
}
// Events implements the [FSWatcher] interface for EmptyFSWatcher. It always
// returns nil channel.
func (EmptyFSWatcher) Events() (e <-chan event) {
return nil
}
// Add implements the [FSWatcher] interface for EmptyFSWatcher. It always
// returns nil error.
func (EmptyFSWatcher) Add(_ string) (err error) {
return nil
}

View File

@@ -19,25 +19,9 @@ import (
"github.com/AdguardTeam/golibs/log"
)
// UnsupportedError is returned by functions and methods when a particular
// operation Op cannot be performed on the current OS.
type UnsupportedError struct {
Op string
OS string
}
// Error implements the error interface for *UnsupportedError.
func (err *UnsupportedError) Error() (msg string) {
return fmt.Sprintf("%s is unsupported on %s", err.Op, err.OS)
}
// Unsupported is a helper that returns an *UnsupportedError with the Op field
// set to op and the OS field set to the current OS.
// Unsupported is a helper that returns a wrapped [errors.ErrUnsupported].
func Unsupported(op string) (err error) {
return &UnsupportedError{
Op: op,
OS: runtime.GOOS,
}
return fmt.Errorf("%s: not supported on %s: %w", op, runtime.GOOS, errors.ErrUnsupported)
}
// SetRlimit sets user-specified limit of how many fd's we can use.

View File

@@ -1,6 +1,6 @@
package aghos
// ConfigureSyslog reroutes standard logger output to syslog.
func ConfigureSyslog(serviceName string) error {
func ConfigureSyslog(serviceName string) (err error) {
return configureSyslog(serviceName)
}

View File

@@ -8,11 +8,15 @@ import (
"github.com/AdguardTeam/golibs/log"
)
func configureSyslog(serviceName string) error {
// configureSyslog sets standard log output to syslog.
func configureSyslog(serviceName string) (err error) {
w, err := syslog.New(syslog.LOG_NOTICE|syslog.LOG_USER, serviceName)
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return err
}
log.SetOutput(w)
return nil
}

View File

@@ -19,23 +19,30 @@ func (w *eventLogWriter) Write(b []byte) (int, error) {
return len(b), w.el.Info(1, string(b))
}
func configureSyslog(serviceName string) error {
// Note that the eventlog src is the same as the service name
// Otherwise, we will get "the description for event id cannot be found" warning in every log record
// configureSyslog sets standard log output to event log.
func configureSyslog(serviceName string) (err error) {
// Note that the eventlog src is the same as the service name, otherwise we
// will get "the description for event id cannot be found" warning in every
// log record.
// Continue if we receive "registry key already exists" or if we get
// ERROR_ACCESS_DENIED so that we can log without administrative permissions
// for pre-existing eventlog sources.
if err := eventlog.InstallAsEventCreate(serviceName, eventlog.Info|eventlog.Warning|eventlog.Error); err != nil {
if !strings.Contains(err.Error(), "registry key already exists") && err != windows.ERROR_ACCESS_DENIED {
return err
}
err = eventlog.InstallAsEventCreate(serviceName, eventlog.Info|eventlog.Warning|eventlog.Error)
if err != nil &&
!strings.Contains(err.Error(), "registry key already exists") &&
err != windows.ERROR_ACCESS_DENIED {
// Don't wrap the error, because it's informative enough as is.
return err
}
el, err := eventlog.Open(serviceName)
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return err
}
log.SetOutput(&eventLogWriter{el: el})
return nil
}

View File

@@ -5,9 +5,10 @@ import (
"crypto/tls"
"crypto/x509"
"fmt"
"net/netip"
"slices"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/netutil"
)
// init makes sure that the cipher name map is filled.
@@ -75,15 +76,5 @@ func SaferCipherSuites() (safe []uint16) {
// CertificateHasIP returns true if cert has at least a single IP address among
// its subjectAltNames.
func CertificateHasIP(cert *x509.Certificate) (ok bool) {
if len(cert.IPAddresses) > 0 {
return true
}
for _, name := range cert.DNSNames {
if _, err := netip.ParseAddr(name); err == nil {
return true
}
}
return false
return len(cert.IPAddresses) > 0 || slices.ContainsFunc(cert.DNSNames, netutil.IsValidIPString)
}

View File

@@ -30,8 +30,8 @@ func macToKey(mac net.HardwareAddr) (key macKey) {
}
}
// Index stores all information about persistent clients.
type Index struct {
// index stores all information about persistent clients.
type index struct {
// nameToUID maps client name to UID.
nameToUID map[string]UID
@@ -51,9 +51,9 @@ type Index struct {
subnetToUID aghalg.SortedMap[netip.Prefix, UID]
}
// NewIndex initializes the new instance of client index.
func NewIndex() (ci *Index) {
return &Index{
// newIndex initializes the new instance of client index.
func newIndex() (ci *index) {
return &index{
nameToUID: map[string]UID{},
clientIDToUID: map[string]UID{},
ipToUID: map[netip.Addr]UID{},
@@ -63,9 +63,9 @@ func NewIndex() (ci *Index) {
}
}
// Add stores information about a persistent client in the index. c must be
// non-nil and contain UID.
func (ci *Index) Add(c *Persistent) {
// add stores information about a persistent client in the index. c must be
// non-nil, have a UID, and contain at least one identifier.
func (ci *index) add(c *Persistent) {
if (c.UID == UID{}) {
panic("client must contain uid")
}
@@ -92,9 +92,9 @@ func (ci *Index) Add(c *Persistent) {
ci.uidToClient[c.UID] = c
}
// ClashesUID returns existing persistent client with the same UID as c. Note
// clashesUID returns existing persistent client with the same UID as c. Note
// that this is only possible when configuration contains duplicate fields.
func (ci *Index) ClashesUID(c *Persistent) (err error) {
func (ci *index) clashesUID(c *Persistent) (err error) {
p, ok := ci.uidToClient[c.UID]
if ok {
return fmt.Errorf("another client %q uses the same uid", p.Name)
@@ -103,9 +103,9 @@ func (ci *Index) ClashesUID(c *Persistent) (err error) {
return nil
}
// Clashes returns an error if the index contains a different persistent client
// clashes returns an error if the index contains a different persistent client
// with at least a single identifier contained by c. c must be non-nil.
func (ci *Index) Clashes(c *Persistent) (err error) {
func (ci *index) clashes(c *Persistent) (err error) {
if p := ci.clashesName(c); p != nil {
return fmt.Errorf("another client uses the same name %q", p.Name)
}
@@ -139,8 +139,8 @@ func (ci *Index) Clashes(c *Persistent) (err error) {
// clashesName returns existing persistent client with the same name as c or
// nil. c must be non-nil.
func (ci *Index) clashesName(c *Persistent) (existing *Persistent) {
existing, ok := ci.FindByName(c.Name)
func (ci *index) clashesName(c *Persistent) (existing *Persistent) {
existing, ok := ci.findByName(c.Name)
if !ok {
return nil
}
@@ -154,7 +154,7 @@ func (ci *Index) clashesName(c *Persistent) (existing *Persistent) {
// clashesIP returns a previous client with the same IP address as c. c must be
// non-nil.
func (ci *Index) clashesIP(c *Persistent) (p *Persistent, ip netip.Addr) {
func (ci *index) clashesIP(c *Persistent) (p *Persistent, ip netip.Addr) {
for _, ip := range c.IPs {
existing, ok := ci.ipToUID[ip]
if ok && existing != c.UID {
@@ -167,7 +167,7 @@ func (ci *Index) clashesIP(c *Persistent) (p *Persistent, ip netip.Addr) {
// clashesSubnet returns a previous client with the same subnet as c. c must be
// non-nil.
func (ci *Index) clashesSubnet(c *Persistent) (p *Persistent, s netip.Prefix) {
func (ci *index) clashesSubnet(c *Persistent) (p *Persistent, s netip.Prefix) {
for _, s = range c.Subnets {
var existing UID
var ok bool
@@ -193,7 +193,7 @@ func (ci *Index) clashesSubnet(c *Persistent) (p *Persistent, s netip.Prefix) {
// clashesMAC returns a previous client with the same MAC address as c. c must
// be non-nil.
func (ci *Index) clashesMAC(c *Persistent) (p *Persistent, mac net.HardwareAddr) {
func (ci *index) clashesMAC(c *Persistent) (p *Persistent, mac net.HardwareAddr) {
for _, mac = range c.MACs {
k := macToKey(mac)
existing, ok := ci.macToUID[k]
@@ -205,9 +205,9 @@ func (ci *Index) clashesMAC(c *Persistent) (p *Persistent, mac net.HardwareAddr)
return nil, nil
}
// Find finds persistent client by string representation of the client ID, IP
// find finds persistent client by string representation of the client ID, IP
// address, or MAC.
func (ci *Index) Find(id string) (c *Persistent, ok bool) {
func (ci *index) find(id string) (c *Persistent, ok bool) {
uid, found := ci.clientIDToUID[id]
if found {
return ci.uidToClient[uid], true
@@ -224,14 +224,14 @@ func (ci *Index) Find(id string) (c *Persistent, ok bool) {
mac, err := net.ParseMAC(id)
if err == nil {
return ci.FindByMAC(mac)
return ci.findByMAC(mac)
}
return nil, false
}
// FindByName finds persistent client by name.
func (ci *Index) FindByName(name string) (c *Persistent, found bool) {
// findByName finds persistent client by name.
func (ci *index) findByName(name string) (c *Persistent, found bool) {
uid, found := ci.nameToUID[name]
if found {
return ci.uidToClient[uid], true
@@ -241,7 +241,7 @@ func (ci *Index) FindByName(name string) (c *Persistent, found bool) {
}
// findByIP finds persistent client by IP address.
func (ci *Index) findByIP(ip netip.Addr) (c *Persistent, found bool) {
func (ci *index) findByIP(ip netip.Addr) (c *Persistent, found bool) {
uid, found := ci.ipToUID[ip]
if found {
return ci.uidToClient[uid], true
@@ -266,8 +266,8 @@ func (ci *Index) findByIP(ip netip.Addr) (c *Persistent, found bool) {
return nil, false
}
// FindByMAC finds persistent client by MAC.
func (ci *Index) FindByMAC(mac net.HardwareAddr) (c *Persistent, found bool) {
// findByMAC finds persistent client by MAC.
func (ci *index) findByMAC(mac net.HardwareAddr) (c *Persistent, found bool) {
k := macToKey(mac)
uid, found := ci.macToUID[k]
if found {
@@ -277,13 +277,13 @@ func (ci *Index) FindByMAC(mac net.HardwareAddr) (c *Persistent, found bool) {
return nil, false
}
// FindByIPWithoutZone finds a persistent client by IP address without zone. It
// findByIPWithoutZone finds a persistent client by IP address without zone. It
// strips the IPv6 zone index from the stored IP addresses before comparing,
// because querylog entries don't have it. See TODO on [querylog.logEntry.IP].
//
// Note that multiple clients can have the same IP address with different zones.
// Therefore, the result of this method is indeterminate.
func (ci *Index) FindByIPWithoutZone(ip netip.Addr) (c *Persistent) {
func (ci *index) findByIPWithoutZone(ip netip.Addr) (c *Persistent) {
if (ip == netip.Addr{}) {
return nil
}
@@ -297,9 +297,9 @@ func (ci *Index) FindByIPWithoutZone(ip netip.Addr) (c *Persistent) {
return nil
}
// Delete removes information about persistent client from the index. c must be
// remove removes information about persistent client from the index. c must be
// non-nil.
func (ci *Index) Delete(c *Persistent) {
func (ci *index) remove(c *Persistent) {
delete(ci.nameToUID, c.Name)
for _, id := range c.ClientIDs {
@@ -322,24 +322,14 @@ func (ci *Index) Delete(c *Persistent) {
delete(ci.uidToClient, c.UID)
}
// Size returns the number of persistent clients.
func (ci *Index) Size() (n int) {
// size returns the number of persistent clients.
func (ci *index) size() (n int) {
return len(ci.uidToClient)
}
// Range calls f for each persistent client, unless cont is false. The order is
// undefined.
func (ci *Index) Range(f func(c *Persistent) (cont bool)) {
for _, c := range ci.uidToClient {
if !f(c) {
return
}
}
}
// RangeByName is like [Index.Range] but sorts the persistent clients by name
// 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)) {
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)
@@ -352,10 +342,10 @@ func (ci *Index) RangeByName(f func(c *Persistent) (cont bool)) {
}
}
// CloseUpstreams closes upstream configurations of persistent clients.
func (ci *Index) CloseUpstreams() (err error) {
// closeUpstreams closes upstream configurations of persistent clients.
func (ci *index) closeUpstreams() (err error) {
var errs []error
ci.RangeByName(func(c *Persistent) (cont bool) {
ci.rangeByName(func(c *Persistent) (cont bool) {
err = c.CloseUpstreams()
if err != nil {
errs = append(errs, err)

View File

@@ -11,17 +11,18 @@ import (
// newIDIndex is a helper function that returns a client index filled with
// persistent clients from the m. It also generates a UID for each client.
func newIDIndex(m []*Persistent) (ci *Index) {
ci = NewIndex()
func newIDIndex(m []*Persistent) (ci *index) {
ci = newIndex()
for _, c := range m {
c.UID = MustNewUID()
ci.Add(c)
ci.add(c)
}
return ci
}
// TODO(s.chzhen): Remove.
func TestClientIndex_Find(t *testing.T) {
const (
cliIPNone = "1.2.3.4"
@@ -109,7 +110,7 @@ func TestClientIndex_Find(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
for _, id := range tc.ids {
c, ok := ci.Find(id)
c, ok := ci.find(id)
require.True(t, ok)
assert.Equal(t, tc.want, c)
@@ -118,7 +119,7 @@ func TestClientIndex_Find(t *testing.T) {
}
t.Run("not_found", func(t *testing.T) {
_, ok := ci.Find(cliIPNone)
_, ok := ci.find(cliIPNone)
assert.False(t, ok)
})
}
@@ -170,11 +171,11 @@ func TestClientIndex_Clashes(t *testing.T) {
clone := tc.client.ShallowClone()
clone.UID = MustNewUID()
err := ci.Clashes(clone)
err := ci.clashes(clone)
require.Error(t, err)
ci.Delete(tc.client)
err = ci.Clashes(clone)
ci.remove(tc.client)
err = ci.clashes(clone)
require.NoError(t, err)
})
}
@@ -292,7 +293,7 @@ func TestIndex_FindByIPWithoutZone(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
c := ci.FindByIPWithoutZone(tc.ip.WithZone(""))
c := ci.findByIPWithoutZone(tc.ip.WithZone(""))
require.Equal(t, tc.want, c)
})
}
@@ -338,7 +339,7 @@ func TestClientIndex_RangeByName(t *testing.T) {
ci := newIDIndex(tc.want)
var got []*Persistent
ci.RangeByName(func(c *Persistent) (cont bool) {
ci.rangeByName(func(c *Persistent) (cont bool) {
got = append(got, c)
return true

View File

@@ -12,6 +12,7 @@ import (
"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/container"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
@@ -64,53 +65,107 @@ type Persistent struct {
// upstream must be used.
UpstreamConfig *proxy.CustomUpstreamConfig
// SafeSearch handles search engine hosts rewrites.
SafeSearch filtering.SafeSearch
// BlockedServices is the configuration of blocked services of a client.
// BlockedServices is the configuration of blocked services of a client. It
// must not be nil after initialization.
BlockedServices *filtering.BlockedServices
// Name of the persistent client. Must not be empty.
Name string
Tags []string
// Tags is a list of client tags that categorize the client.
Tags []string
// Upstreams is a list of custom upstream DNS servers for the client.
Upstreams []string
// IPs is a list of IP addresses that identify the client. The client must
// have at least one ID (IP, subnet, MAC, or ClientID).
IPs []netip.Addr
// Subnets identifying the client. The client must have at least one ID
// (IP, subnet, MAC, or ClientID).
//
// TODO(s.chzhen): Use netutil.Prefix.
Subnets []netip.Prefix
MACs []net.HardwareAddr
Subnets []netip.Prefix
// MACs identifying the client. The client must have at least one ID (IP,
// subnet, MAC, or ClientID).
MACs []net.HardwareAddr
// ClientIDs identifying the client. The client must have at least one ID
// (IP, subnet, MAC, or ClientID).
ClientIDs []string
// UID is the unique identifier of the persistent client.
UID UID
UpstreamsCacheSize uint32
// UpstreamsCacheSize is the cache size for custom upstreams.
UpstreamsCacheSize uint32
// UpstreamsCacheEnabled specifies whether custom upstreams are used.
UpstreamsCacheEnabled bool
UseOwnSettings bool
FilteringEnabled bool
SafeBrowsingEnabled bool
ParentalEnabled bool
UseOwnBlockedServices bool
IgnoreQueryLog bool
IgnoreStatistics bool
// UseOwnSettings specifies whether custom filtering settings are used.
UseOwnSettings bool
// FilteringEnabled specifies whether filtering is enabled.
FilteringEnabled bool
// SafeBrowsingEnabled specifies whether safe browsing is enabled.
SafeBrowsingEnabled bool
// ParentalEnabled specifies whether parental control is enabled.
ParentalEnabled bool
// UseOwnBlockedServices specifies whether custom services are blocked.
UseOwnBlockedServices bool
// IgnoreQueryLog specifies whether the client requests are logged.
IgnoreQueryLog bool
// IgnoreStatistics specifies whether the client requests are counted.
IgnoreStatistics bool
// SafeSearchConf is the safe search filtering configuration.
//
// TODO(d.kolyshev): Make SafeSearchConf a pointer.
SafeSearchConf filtering.SafeSearchConfig
}
// SetTags sets the tags if they are known, otherwise logs an unknown tag.
func (c *Persistent) SetTags(tags []string, known *container.MapSet[string]) {
for _, t := range tags {
if !known.Has(t) {
log.Info("skipping unknown tag %q", t)
continue
}
c.Tags = append(c.Tags, t)
// validate returns an error if persistent client information contains errors.
func (c *Persistent) validate(allTags *container.MapSet[string]) (err error) {
switch {
case c.Name == "":
return errors.Error("empty name")
case c.IDsLen() == 0:
return errors.Error("id required")
case c.UID == UID{}:
return errors.Error("uid required")
}
conf, err := proxy.ParseUpstreamsConfig(c.Upstreams, &upstream.Options{})
if err != nil {
return fmt.Errorf("invalid upstream servers: %w", err)
}
err = conf.Close()
if err != nil {
log.Error("client: closing upstream config: %s", err)
}
for _, t := range c.Tags {
if !allTags.Has(t) {
return fmt.Errorf("invalid tag: %q", t)
}
}
// TODO(s.chzhen): Move to the constructor.
slices.Sort(c.Tags)
return nil
}
// SetIDs parses a list of strings into typed fields and returns an error if

View File

@@ -1,13 +1,16 @@
package client
import (
"net/netip"
"testing"
"github.com/AdguardTeam/golibs/container"
"github.com/AdguardTeam/golibs/testutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestPersistentClient_EqualIDs(t *testing.T) {
func TestPersistent_EqualIDs(t *testing.T) {
const (
ip = "0.0.0.0"
ip1 = "1.1.1.1"
@@ -122,3 +125,69 @@ func TestPersistentClient_EqualIDs(t *testing.T) {
})
}
}
func TestPersistent_Validate(t *testing.T) {
const (
allowedTag = "allowed_tag"
notAllowedTag = "not_allowed_tag"
)
allowedTags := container.NewMapSet(allowedTag)
testCases := []struct {
name string
cli *Persistent
wantErrMsg string
}{{
name: "success",
cli: &Persistent{
Name: "basic",
IPs: []netip.Addr{
netip.MustParseAddr("1.2.3.4"),
},
UID: MustNewUID(),
},
wantErrMsg: "",
}, {
name: "empty_name",
cli: &Persistent{
Name: "",
},
wantErrMsg: "empty name",
}, {
name: "no_id",
cli: &Persistent{
Name: "no_id",
},
wantErrMsg: "id required",
}, {
name: "no_uid",
cli: &Persistent{
Name: "no_uid",
IPs: []netip.Addr{
netip.MustParseAddr("1.2.3.4"),
},
},
wantErrMsg: "uid required",
}, {
name: "not_allowed_tag",
cli: &Persistent{
Name: "basic",
IPs: []netip.Addr{
netip.MustParseAddr("1.2.3.4"),
},
UID: MustNewUID(),
Tags: []string{
notAllowedTag,
},
},
wantErrMsg: `invalid tag: "` + notAllowedTag + `"`,
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
err := tc.cli.validate(allowedTags)
testutil.AssertErrorMsg(t, tc.wantErrMsg, err)
})
}
}

289
internal/client/storage.go Normal file
View File

@@ -0,0 +1,289 @@
package client
import (
"fmt"
"net"
"net/netip"
"sync"
"github.com/AdguardTeam/golibs/container"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
)
// Config is the client storage configuration structure.
//
// TODO(s.chzhen): Expand.
type Config struct {
// AllowedTags is a list of all allowed client tags.
AllowedTags []string
}
// Storage contains information about persistent and runtime clients.
type Storage struct {
// allowedTags is a set of all allowed tags.
allowedTags *container.MapSet[string]
// mu protects indexes of persistent and runtime clients.
mu *sync.Mutex
// index contains information about persistent clients.
index *index
// runtimeIndex contains information about runtime clients.
//
// TODO(s.chzhen): Use it.
runtimeIndex *RuntimeIndex
}
// NewStorage returns initialized client storage. conf must not be nil.
func NewStorage(conf *Config) (s *Storage) {
allowedTags := container.NewMapSet(conf.AllowedTags...)
return &Storage{
allowedTags: allowedTags,
mu: &sync.Mutex{},
index: newIndex(),
runtimeIndex: NewRuntimeIndex(),
}
}
// Add stores persistent client information or returns an error.
func (s *Storage) Add(p *Persistent) (err error) {
defer func() { err = errors.Annotate(err, "adding client: %w") }()
err = p.validate(s.allowedTags)
if err != nil {
// Don't wrap the error since there is already an annotation deferred.
return err
}
s.mu.Lock()
defer s.mu.Unlock()
err = s.index.clashesUID(p)
if err != nil {
// Don't wrap the error since there is already an annotation deferred.
return err
}
err = s.index.clashes(p)
if err != nil {
// Don't wrap the error since there is already an annotation deferred.
return err
}
s.index.add(p)
log.Debug("client storage: added %q: IDs: %q [%d]", p.Name, p.IDs(), s.index.size())
return nil
}
// FindByName finds persistent client by name. And returns its shallow copy.
func (s *Storage) FindByName(name string) (p *Persistent, ok bool) {
s.mu.Lock()
defer s.mu.Unlock()
p, ok = s.index.findByName(name)
if ok {
return p.ShallowClone(), ok
}
return nil, false
}
// Find finds persistent client by string representation of the client ID, IP
// address, or MAC. And returns its shallow copy.
func (s *Storage) Find(id string) (p *Persistent, ok bool) {
s.mu.Lock()
defer s.mu.Unlock()
p, ok = s.index.find(id)
if ok {
return p.ShallowClone(), ok
}
return nil, false
}
// FindLoose is like [Storage.Find] but it also tries to find a persistent
// client by IP address without zone. It strips the IPv6 zone index from the
// stored IP addresses before comparing, because querylog entries don't have it.
// See TODO on [querylog.logEntry.IP].
//
// Note that multiple clients can have the same IP address with different zones.
// Therefore, the result of this method is indeterminate.
func (s *Storage) FindLoose(ip netip.Addr, id string) (p *Persistent, ok bool) {
s.mu.Lock()
defer s.mu.Unlock()
p, ok = s.index.find(id)
if ok {
return p.ShallowClone(), ok
}
p = s.index.findByIPWithoutZone(ip)
if p != nil {
return p.ShallowClone(), true
}
return nil, false
}
// FindByMAC finds persistent client by MAC and returns its shallow copy.
func (s *Storage) FindByMAC(mac net.HardwareAddr) (p *Persistent, ok bool) {
s.mu.Lock()
defer s.mu.Unlock()
p, ok = s.index.findByMAC(mac)
if ok {
return p.ShallowClone(), ok
}
return nil, false
}
// RemoveByName removes persistent client information. ok is false if no such
// client exists by that name.
func (s *Storage) RemoveByName(name string) (ok bool) {
s.mu.Lock()
defer s.mu.Unlock()
p, ok := s.index.findByName(name)
if !ok {
return false
}
if err := p.CloseUpstreams(); err != nil {
log.Error("client storage: removing client %q: %s", p.Name, err)
}
s.index.remove(p)
return true
}
// 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) {
defer func() { err = errors.Annotate(err, "updating client: %w") }()
err = p.validate(s.allowedTags)
if err != nil {
// Don't wrap the error since there is already an annotation deferred.
return err
}
s.mu.Lock()
defer s.mu.Unlock()
stored, ok := s.index.findByName(name)
if !ok {
return fmt.Errorf("client %q is not found", name)
}
// Client p has a newly generated UID, so replace it with the stored one.
//
// TODO(s.chzhen): Remove when frontend starts handling UIDs.
p.UID = stored.UID
err = s.index.clashes(p)
if err != nil {
// Don't wrap the error since there is already an annotation deferred.
return err
}
s.index.remove(stored)
s.index.add(p)
return nil
}
// RangeByName calls f for each persistent client sorted by name, unless cont is
// false.
func (s *Storage) RangeByName(f func(c *Persistent) (cont bool)) {
s.mu.Lock()
defer s.mu.Unlock()
s.index.rangeByName(f)
}
// Size returns the number of persistent clients.
func (s *Storage) Size() (n int) {
s.mu.Lock()
defer s.mu.Unlock()
return s.index.size()
}
// CloseUpstreams closes upstream configurations of persistent clients.
func (s *Storage) CloseUpstreams() (err error) {
s.mu.Lock()
defer s.mu.Unlock()
return s.index.closeUpstreams()
}
// ClientRuntime returns a copy of the saved runtime client by ip. If no such
// client exists, returns nil.
//
// TODO(s.chzhen): Use it.
func (s *Storage) ClientRuntime(ip netip.Addr) (rc *Runtime) {
s.mu.Lock()
defer s.mu.Unlock()
return s.runtimeIndex.Client(ip)
}
// AddRuntime saves the runtime client information in the storage. IP address
// of a client must be unique. rc must not be nil.
//
// TODO(s.chzhen): Use it.
func (s *Storage) AddRuntime(rc *Runtime) {
s.mu.Lock()
defer s.mu.Unlock()
s.runtimeIndex.Add(rc)
}
// SizeRuntime returns the number of the runtime clients.
//
// TODO(s.chzhen): Use it.
func (s *Storage) SizeRuntime() (n int) {
s.mu.Lock()
defer s.mu.Unlock()
return s.runtimeIndex.Size()
}
// RangeRuntime calls f for each runtime client in an undefined order.
//
// TODO(s.chzhen): Use it.
func (s *Storage) RangeRuntime(f func(rc *Runtime) (cont bool)) {
s.mu.Lock()
defer s.mu.Unlock()
s.runtimeIndex.Range(f)
}
// DeleteRuntime removes the runtime client by ip.
//
// TODO(s.chzhen): Use it.
func (s *Storage) DeleteRuntime(ip netip.Addr) {
s.mu.Lock()
defer s.mu.Unlock()
s.runtimeIndex.Delete(ip)
}
// DeleteBySource removes all runtime clients that have information only from
// the specified source and returns the number of removed clients.
//
// TODO(s.chzhen): Use it.
func (s *Storage) DeleteBySource(src Source) (n int) {
s.mu.Lock()
defer s.mu.Unlock()
return s.runtimeIndex.DeleteBySource(src)
}

View File

@@ -0,0 +1,481 @@
package client_test
import (
"net"
"net/netip"
"testing"
"github.com/AdguardTeam/AdGuardHome/internal/client"
"github.com/AdguardTeam/golibs/testutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// newStorage is a helper function that returns a client storage filled with
// persistent clients from the m. It also generates a UID for each client.
func newStorage(tb testing.TB, m []*client.Persistent) (s *client.Storage) {
tb.Helper()
s = client.NewStorage(&client.Config{
AllowedTags: nil,
})
for _, c := range m {
c.UID = client.MustNewUID()
require.NoError(tb, s.Add(c))
}
return s
}
// mustParseMAC is wrapper around [net.ParseMAC] that panics if there is an
// error.
func mustParseMAC(s string) (mac net.HardwareAddr) {
mac, err := net.ParseMAC(s)
if err != nil {
panic(err)
}
return mac
}
func TestStorage_Add(t *testing.T) {
const (
existingName = "existing_name"
existingClientID = "existing_client_id"
)
var (
existingClientUID = client.MustNewUID()
existingIP = netip.MustParseAddr("1.2.3.4")
existingSubnet = netip.MustParsePrefix("1.2.3.0/24")
)
existingClient := &client.Persistent{
Name: existingName,
IPs: []netip.Addr{existingIP},
Subnets: []netip.Prefix{existingSubnet},
ClientIDs: []string{existingClientID},
UID: existingClientUID,
}
s := client.NewStorage(&client.Config{
AllowedTags: nil,
})
err := s.Add(existingClient)
require.NoError(t, err)
testCases := []struct {
name string
cli *client.Persistent
wantErrMsg string
}{{
name: "basic",
cli: &client.Persistent{
Name: "basic",
IPs: []netip.Addr{netip.MustParseAddr("1.1.1.1")},
UID: client.MustNewUID(),
},
wantErrMsg: "",
}, {
name: "duplicate_uid",
cli: &client.Persistent{
Name: "no_uid",
IPs: []netip.Addr{netip.MustParseAddr("2.2.2.2")},
UID: existingClientUID,
},
wantErrMsg: `adding client: another client "existing_name" uses the same uid`,
}, {
name: "duplicate_name",
cli: &client.Persistent{
Name: existingName,
IPs: []netip.Addr{netip.MustParseAddr("3.3.3.3")},
UID: client.MustNewUID(),
},
wantErrMsg: `adding client: another client uses the same name "existing_name"`,
}, {
name: "duplicate_ip",
cli: &client.Persistent{
Name: "duplicate_ip",
IPs: []netip.Addr{existingIP},
UID: client.MustNewUID(),
},
wantErrMsg: `adding client: another client "existing_name" uses the same IP "1.2.3.4"`,
}, {
name: "duplicate_subnet",
cli: &client.Persistent{
Name: "duplicate_subnet",
Subnets: []netip.Prefix{existingSubnet},
UID: client.MustNewUID(),
},
wantErrMsg: `adding client: another client "existing_name" ` +
`uses the same subnet "1.2.3.0/24"`,
}, {
name: "duplicate_client_id",
cli: &client.Persistent{
Name: "duplicate_client_id",
ClientIDs: []string{existingClientID},
UID: client.MustNewUID(),
},
wantErrMsg: `adding client: another client "existing_name" ` +
`uses the same ClientID "existing_client_id"`,
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
err = s.Add(tc.cli)
testutil.AssertErrorMsg(t, tc.wantErrMsg, err)
})
}
}
func TestStorage_RemoveByName(t *testing.T) {
const (
existingName = "existing_name"
)
existingClient := &client.Persistent{
Name: existingName,
IPs: []netip.Addr{netip.MustParseAddr("1.2.3.4")},
UID: client.MustNewUID(),
}
s := client.NewStorage(&client.Config{
AllowedTags: nil,
})
err := s.Add(existingClient)
require.NoError(t, err)
testCases := []struct {
want assert.BoolAssertionFunc
name string
cliName string
}{{
name: "existing_client",
cliName: existingName,
want: assert.True,
}, {
name: "non_existing_client",
cliName: "non_existing_client",
want: assert.False,
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
tc.want(t, s.RemoveByName(tc.cliName))
})
}
t.Run("duplicate_remove", func(t *testing.T) {
s = client.NewStorage(&client.Config{
AllowedTags: nil,
})
err = s.Add(existingClient)
require.NoError(t, err)
assert.True(t, s.RemoveByName(existingName))
assert.False(t, s.RemoveByName(existingName))
})
}
func TestStorage_Find(t *testing.T) {
const (
cliIPNone = "1.2.3.4"
cliIP1 = "1.1.1.1"
cliIP2 = "2.2.2.2"
cliIPv6 = "1:2:3::4"
cliSubnet = "2.2.2.0/24"
cliSubnetIP = "2.2.2.222"
cliID = "client-id"
cliMAC = "11:11:11:11:11:11"
linkLocalIP = "fe80::abcd:abcd:abcd:ab%eth0"
linkLocalSubnet = "fe80::/16"
)
var (
clientWithBothFams = &client.Persistent{
Name: "client1",
IPs: []netip.Addr{
netip.MustParseAddr(cliIP1),
netip.MustParseAddr(cliIPv6),
},
}
clientWithSubnet = &client.Persistent{
Name: "client2",
IPs: []netip.Addr{netip.MustParseAddr(cliIP2)},
Subnets: []netip.Prefix{netip.MustParsePrefix(cliSubnet)},
}
clientWithMAC = &client.Persistent{
Name: "client_with_mac",
MACs: []net.HardwareAddr{mustParseMAC(cliMAC)},
}
clientWithID = &client.Persistent{
Name: "client_with_id",
ClientIDs: []string{cliID},
}
clientLinkLocal = &client.Persistent{
Name: "client_link_local",
Subnets: []netip.Prefix{netip.MustParsePrefix(linkLocalSubnet)},
}
)
clients := []*client.Persistent{
clientWithBothFams,
clientWithSubnet,
clientWithMAC,
clientWithID,
clientLinkLocal,
}
s := newStorage(t, clients)
testCases := []struct {
want *client.Persistent
name string
ids []string
}{{
name: "ipv4_ipv6",
ids: []string{cliIP1, cliIPv6},
want: clientWithBothFams,
}, {
name: "ipv4_subnet",
ids: []string{cliIP2, cliSubnetIP},
want: clientWithSubnet,
}, {
name: "mac",
ids: []string{cliMAC},
want: clientWithMAC,
}, {
name: "client_id",
ids: []string{cliID},
want: clientWithID,
}, {
name: "client_link_local_subnet",
ids: []string{linkLocalIP},
want: clientLinkLocal,
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
for _, id := range tc.ids {
c, ok := s.Find(id)
require.True(t, ok)
assert.Equal(t, tc.want, c)
}
})
}
t.Run("not_found", func(t *testing.T) {
_, ok := s.Find(cliIPNone)
assert.False(t, ok)
})
}
func TestStorage_FindLoose(t *testing.T) {
const (
nonExistingClientID = "client_id"
)
var (
ip = netip.MustParseAddr("fe80::a098:7654:32ef:ff1")
ipWithZone = netip.MustParseAddr("fe80::1ff:fe23:4567:890a%eth2")
)
var (
clientNoZone = &client.Persistent{
Name: "client",
IPs: []netip.Addr{ip},
}
clientWithZone = &client.Persistent{
Name: "client_with_zone",
IPs: []netip.Addr{ipWithZone},
}
)
s := newStorage(
t,
[]*client.Persistent{
clientNoZone,
clientWithZone,
},
)
testCases := []struct {
ip netip.Addr
want assert.BoolAssertionFunc
wantCli *client.Persistent
name string
}{{
name: "without_zone",
ip: ip,
wantCli: clientNoZone,
want: assert.True,
}, {
name: "with_zone",
ip: ipWithZone,
wantCli: clientWithZone,
want: assert.True,
}, {
name: "zero_address",
ip: netip.Addr{},
wantCli: nil,
want: assert.False,
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
c, ok := s.FindLoose(tc.ip.WithZone(""), nonExistingClientID)
assert.Equal(t, tc.wantCli, c)
tc.want(t, ok)
})
}
}
func TestStorage_Update(t *testing.T) {
const (
clientName = "client_name"
obstructingName = "obstructing_name"
obstructingClientID = "obstructing_client_id"
)
var (
obstructingIP = netip.MustParseAddr("1.2.3.4")
obstructingSubnet = netip.MustParsePrefix("1.2.3.0/24")
)
obstructingClient := &client.Persistent{
Name: obstructingName,
IPs: []netip.Addr{obstructingIP},
Subnets: []netip.Prefix{obstructingSubnet},
ClientIDs: []string{obstructingClientID},
}
clientToUpdate := &client.Persistent{
Name: clientName,
IPs: []netip.Addr{netip.MustParseAddr("1.1.1.1")},
}
testCases := []struct {
name string
cli *client.Persistent
wantErrMsg string
}{{
name: "basic",
cli: &client.Persistent{
Name: "basic",
IPs: []netip.Addr{netip.MustParseAddr("1.1.1.1")},
UID: client.MustNewUID(),
},
wantErrMsg: "",
}, {
name: "duplicate_name",
cli: &client.Persistent{
Name: obstructingName,
IPs: []netip.Addr{netip.MustParseAddr("3.3.3.3")},
UID: client.MustNewUID(),
},
wantErrMsg: `updating client: another client uses the same name "obstructing_name"`,
}, {
name: "duplicate_ip",
cli: &client.Persistent{
Name: "duplicate_ip",
IPs: []netip.Addr{obstructingIP},
UID: client.MustNewUID(),
},
wantErrMsg: `updating client: another client "obstructing_name" uses the same IP "1.2.3.4"`,
}, {
name: "duplicate_subnet",
cli: &client.Persistent{
Name: "duplicate_subnet",
Subnets: []netip.Prefix{obstructingSubnet},
UID: client.MustNewUID(),
},
wantErrMsg: `updating client: another client "obstructing_name" ` +
`uses the same subnet "1.2.3.0/24"`,
}, {
name: "duplicate_client_id",
cli: &client.Persistent{
Name: "duplicate_client_id",
ClientIDs: []string{obstructingClientID},
UID: client.MustNewUID(),
},
wantErrMsg: `updating client: another client "obstructing_name" ` +
`uses the same ClientID "obstructing_client_id"`,
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
s := newStorage(
t,
[]*client.Persistent{
clientToUpdate,
obstructingClient,
},
)
err := s.Update(clientName, tc.cli)
testutil.AssertErrorMsg(t, tc.wantErrMsg, err)
})
}
}
func TestStorage_RangeByName(t *testing.T) {
sortedClients := []*client.Persistent{{
Name: "clientA",
ClientIDs: []string{"A"},
}, {
Name: "clientB",
ClientIDs: []string{"B"},
}, {
Name: "clientC",
ClientIDs: []string{"C"},
}, {
Name: "clientD",
ClientIDs: []string{"D"},
}, {
Name: "clientE",
ClientIDs: []string{"E"},
}}
testCases := []struct {
name string
want []*client.Persistent
}{{
name: "basic",
want: sortedClients,
}, {
name: "nil",
want: nil,
}, {
name: "one_element",
want: sortedClients[:1],
}, {
name: "two_elements",
want: sortedClients[:2],
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
s := newStorage(t, tc.want)
var got []*client.Persistent
s.RangeByName(func(c *client.Persistent) (cont bool) {
got = append(got, c)
return true
})
assert.Equal(t, tc.want, got)
})
}
}

View File

@@ -2,11 +2,13 @@ package dhcpsvc
import (
"fmt"
"slices"
"log/slog"
"os"
"time"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/mapsutil"
"github.com/AdguardTeam/golibs/netutil"
"golang.org/x/exp/maps"
)
// Config is the configuration for the DHCP service.
@@ -15,11 +17,15 @@ type Config struct {
// interface identified by its name.
Interfaces map[string]*InterfaceConfig
// Logger will be used to log the DHCP events.
Logger *slog.Logger
// LocalDomainName is the top-level domain name to use for resolving DHCP
// clients' hostnames.
LocalDomainName string
// TODO(e.burkov): Add DB path.
// DBFilePath is the path to the database file containing the DHCP leases.
DBFilePath string
// ICMPTimeout is the timeout for checking another DHCP server's presence.
ICMPTimeout time.Duration
@@ -38,36 +44,50 @@ type InterfaceConfig struct {
}
// Validate returns an error in conf if any.
//
// TODO(e.burkov): Unexport and rewrite the test.
func (conf *Config) Validate() (err error) {
switch {
case conf == nil:
return errNilConfig
case !conf.Enabled:
return nil
case conf.ICMPTimeout < 0:
return newMustErr("icmp timeout", "be non-negative", conf.ICMPTimeout)
}
var errs []error
if conf.ICMPTimeout < 0 {
err = newMustErr("icmp timeout", "be non-negative", conf.ICMPTimeout)
errs = append(errs, err)
}
err = netutil.ValidateDomainName(conf.LocalDomainName)
if err != nil {
// Don't wrap the error since it's informative enough as is.
return err
errs = append(errs, err)
}
// This is a best-effort check for the file accessibility. The file will be
// checked again when it is opened later.
if _, err = os.Stat(conf.DBFilePath); err != nil && !errors.Is(err, os.ErrNotExist) {
errs = append(errs, fmt.Errorf("db file path %q: %w", conf.DBFilePath, err))
}
if len(conf.Interfaces) == 0 {
return errNoInterfaces
errs = append(errs, errNoInterfaces)
return errors.Join(errs...)
}
ifaces := maps.Keys(conf.Interfaces)
slices.Sort(ifaces)
for _, iface := range ifaces {
if err = conf.Interfaces[iface].validate(); err != nil {
return fmt.Errorf("interface %q: %w", iface, err)
mapsutil.SortedRange(conf.Interfaces, func(iface string, ic *InterfaceConfig) (ok bool) {
err = ic.validate()
if err != nil {
errs = append(errs, fmt.Errorf("interface %q: %w", iface, err))
}
}
return nil
return true
})
return errors.Join(errs...)
}
// validate returns an error in ic, if any.

View File

@@ -1,6 +1,7 @@
package dhcpsvc_test
import (
"path/filepath"
"testing"
"github.com/AdguardTeam/AdGuardHome/internal/dhcpsvc"
@@ -8,6 +9,8 @@ import (
)
func TestConfig_Validate(t *testing.T) {
leasesPath := filepath.Join(t.TempDir(), "leases.json")
testCases := []struct {
name string
conf *dhcpsvc.Config
@@ -23,7 +26,9 @@ func TestConfig_Validate(t *testing.T) {
}, {
name: "empty",
conf: &dhcpsvc.Config{
Enabled: true,
Enabled: true,
Interfaces: testInterfaceConf,
DBFilePath: leasesPath,
},
wantErrMsg: `bad domain name "": domain name is empty`,
}, {
@@ -31,6 +36,7 @@ func TestConfig_Validate(t *testing.T) {
Enabled: true,
LocalDomainName: testLocalTLD,
Interfaces: nil,
DBFilePath: leasesPath,
},
name: "no_interfaces",
wantErrMsg: "no interfaces specified",
@@ -39,6 +45,7 @@ func TestConfig_Validate(t *testing.T) {
Enabled: true,
LocalDomainName: testLocalTLD,
Interfaces: nil,
DBFilePath: leasesPath,
},
name: "no_interfaces",
wantErrMsg: "no interfaces specified",
@@ -49,6 +56,7 @@ func TestConfig_Validate(t *testing.T) {
Interfaces: map[string]*dhcpsvc.InterfaceConfig{
"eth0": nil,
},
DBFilePath: leasesPath,
},
name: "nil_interface",
wantErrMsg: `interface "eth0": config is nil`,
@@ -62,6 +70,7 @@ func TestConfig_Validate(t *testing.T) {
IPv6: &dhcpsvc.IPv6Config{Enabled: false},
},
},
DBFilePath: leasesPath,
},
name: "nil_ipv4",
wantErrMsg: `interface "eth0": ipv4: config is nil`,
@@ -75,6 +84,7 @@ func TestConfig_Validate(t *testing.T) {
IPv6: nil,
},
},
DBFilePath: leasesPath,
},
name: "nil_ipv6",
wantErrMsg: `interface "eth0": ipv6: config is nil`,

195
internal/dhcpsvc/db.go Normal file
View File

@@ -0,0 +1,195 @@
package dhcpsvc
import (
"context"
"encoding/json"
"fmt"
"io/fs"
"net"
"net/netip"
"os"
"slices"
"strings"
"time"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/google/renameio/v2/maybe"
)
// dataVersion is the current version of the stored DHCP leases structure.
const dataVersion = 1
// databasePerm is the permissions for the database file.
const databasePerm fs.FileMode = 0o640
// dataLeases is the structure of the stored DHCP leases.
type dataLeases struct {
// Leases is the list containing stored DHCP leases.
Leases []*dbLease `json:"leases"`
// Version is the current version of the structure.
Version int `json:"version"`
}
// dbLease is the structure of stored lease.
type dbLease struct {
Expiry string `json:"expires"`
IP netip.Addr `json:"ip"`
Hostname string `json:"hostname"`
HWAddr string `json:"mac"`
IsStatic bool `json:"static"`
}
// compareNames returns the result of comparing the hostnames of dl and other
// lexicographically.
func (dl *dbLease) compareNames(other *dbLease) (res int) {
return strings.Compare(dl.Hostname, other.Hostname)
}
// toDBLease converts *Lease to *dbLease.
func toDBLease(l *Lease) (dl *dbLease) {
var expiryStr string
if !l.IsStatic {
// The front-end is waiting for RFC 3999 format of the time value. It
// also shouldn't got an Expiry field for static leases.
//
// See https://github.com/AdguardTeam/AdGuardHome/issues/2692.
expiryStr = l.Expiry.Format(time.RFC3339)
}
return &dbLease{
Expiry: expiryStr,
Hostname: l.Hostname,
HWAddr: l.HWAddr.String(),
IP: l.IP,
IsStatic: l.IsStatic,
}
}
// toInternal converts dl to *Lease.
func (dl *dbLease) toInternal() (l *Lease, err error) {
mac, err := net.ParseMAC(dl.HWAddr)
if err != nil {
return nil, fmt.Errorf("parsing hardware address: %w", err)
}
expiry := time.Time{}
if !dl.IsStatic {
expiry, err = time.Parse(time.RFC3339, dl.Expiry)
if err != nil {
return nil, fmt.Errorf("parsing expiry time: %w", err)
}
}
return &Lease{
Expiry: expiry,
IP: dl.IP,
Hostname: dl.Hostname,
HWAddr: mac,
IsStatic: dl.IsStatic,
}, nil
}
// dbLoad loads stored leases. It must only be called before the service has
// been started.
func (srv *DHCPServer) dbLoad(ctx context.Context) (err error) {
defer func() { err = errors.Annotate(err, "loading db: %w") }()
file, err := os.Open(srv.dbFilePath)
if err != nil {
if !errors.Is(err, os.ErrNotExist) {
return fmt.Errorf("reading db: %w", err)
}
srv.logger.DebugContext(ctx, "no db file found")
return nil
}
defer func() {
err = errors.WithDeferred(err, file.Close())
}()
dl := &dataLeases{}
err = json.NewDecoder(file).Decode(dl)
if err != nil {
return fmt.Errorf("decoding db: %w", err)
}
srv.resetLeases()
srv.addDBLeases(ctx, dl.Leases)
return nil
}
// addDBLeases adds leases to the server.
func (srv *DHCPServer) addDBLeases(ctx context.Context, leases []*dbLease) {
var v4, v6 uint
for i, l := range leases {
lease, err := l.toInternal()
if err != nil {
srv.logger.WarnContext(ctx, "converting lease", "idx", i, slogutil.KeyError, err)
continue
}
iface, err := srv.ifaceForAddr(l.IP)
if err != nil {
srv.logger.WarnContext(ctx, "searching lease iface", "idx", i, slogutil.KeyError, err)
continue
}
err = srv.leases.add(lease, iface)
if err != nil {
srv.logger.WarnContext(ctx, "adding lease", "idx", i, slogutil.KeyError, err)
continue
}
if l.IP.Is4() {
v4++
} else {
v6++
}
}
// TODO(e.burkov): Group by interface.
srv.logger.InfoContext(ctx, "loaded leases", "v4", v4, "v6", v6, "total", len(leases))
}
// writeDB writes leases to the database file. It expects the
// [DHCPServer.leasesMu] to be locked.
func (srv *DHCPServer) dbStore(ctx context.Context) (err error) {
defer func() { err = errors.Annotate(err, "writing db: %w") }()
dl := &dataLeases{
// Avoid writing null into the database file if there are no leases.
Leases: make([]*dbLease, 0, srv.leases.len()),
Version: dataVersion,
}
srv.leases.rangeLeases(func(l *Lease) (cont bool) {
lease := toDBLease(l)
i, _ := slices.BinarySearchFunc(dl.Leases, lease, (*dbLease).compareNames)
dl.Leases = slices.Insert(dl.Leases, i, lease)
return true
})
buf, err := json.Marshal(dl)
if err != nil {
// Don't wrap the error since it's informative enough as is.
return err
}
err = maybe.WriteFile(srv.dbFilePath, buf, databasePerm)
if err != nil {
// Don't wrap the error since it's informative enough as is.
return err
}
srv.logger.InfoContext(ctx, "stored leases", "num", len(dl.Leases), "file", srv.dbFilePath)
return nil
}

View File

@@ -0,0 +1,4 @@
package dhcpsvc
// DatabasePerm is the permissions for the test database file.
const DatabasePerm = databasePerm

View File

@@ -11,6 +11,14 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/next/agh"
)
const (
// keyInterface is the key for logging the network interface name.
keyInterface = "iface"
// keyFamily is the key for logging the handled address family.
keyFamily = "family"
)
// Interface is a DHCP service.
//
// TODO(e.burkov): Separate HostByIP, MACByIP, IPByHost into a separate
@@ -42,7 +50,7 @@ type Interface interface {
IPByHost(host string) (ip netip.Addr)
// Leases returns all the active DHCP leases. The returned slice should be
// a clone.
// a clone. The order of leases is undefined.
//
// TODO(e.burkov): Consider implementing iterating methods with appropriate
// signatures instead of cloning the whole list.
@@ -50,21 +58,21 @@ type Interface interface {
// AddLease adds a new DHCP lease. l must be valid. It returns an error if
// l already exists.
AddLease(l *Lease) (err error)
AddLease(ctx context.Context, l *Lease) (err error)
// UpdateStaticLease replaces an existing static DHCP lease. l must be
// valid. It returns an error if the lease with the given hardware address
// doesn't exist or if other values match another existing lease.
UpdateStaticLease(l *Lease) (err error)
UpdateStaticLease(ctx context.Context, l *Lease) (err error)
// RemoveLease removes an existing DHCP lease. l must be valid. It returns
// an error if there is no lease equal to l.
RemoveLease(l *Lease) (err error)
RemoveLease(ctx context.Context, l *Lease) (err error)
// Reset removes all the DHCP leases.
//
// TODO(e.burkov): If it's really needed?
Reset() (err error)
Reset(ctx context.Context) (err error)
}
// Empty is an [Interface] implementation that does nothing.
@@ -101,13 +109,13 @@ func (Empty) IPByHost(_ string) (ip netip.Addr) { return netip.Addr{} }
func (Empty) Leases() (leases []*Lease) { return nil }
// AddLease implements the [Interface] interface for Empty.
func (Empty) AddLease(_ *Lease) (err error) { return nil }
func (Empty) AddLease(_ context.Context, _ *Lease) (err error) { return nil }
// UpdateStaticLease implements the [Interface] interface for Empty.
func (Empty) UpdateStaticLease(_ *Lease) (err error) { return nil }
func (Empty) UpdateStaticLease(_ context.Context, _ *Lease) (err error) { return nil }
// RemoveLease implements the [Interface] interface for Empty.
func (Empty) RemoveLease(_ *Lease) (err error) { return nil }
func (Empty) RemoveLease(_ context.Context, _ *Lease) (err error) { return nil }
// Reset implements the [Interface] interface for Empty.
func (Empty) Reset() (err error) { return nil }
func (Empty) Reset(_ context.Context) (err error) { return nil }

View File

@@ -0,0 +1,66 @@
package dhcpsvc_test
import (
"net"
"net/netip"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/dhcpsvc"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/stretchr/testify/require"
)
// testLocalTLD is a common local TLD for tests.
const testLocalTLD = "local"
// testTimeout is a common timeout for tests and contexts.
const testTimeout time.Duration = 10 * time.Second
// discardLog is a logger to discard test output.
var discardLog = slogutil.NewDiscardLogger()
// testInterfaceConf is a common set of interface configurations for tests.
var testInterfaceConf = map[string]*dhcpsvc.InterfaceConfig{
"eth0": {
IPv4: &dhcpsvc.IPv4Config{
Enabled: true,
GatewayIP: netip.MustParseAddr("192.168.0.1"),
SubnetMask: netip.MustParseAddr("255.255.255.0"),
RangeStart: netip.MustParseAddr("192.168.0.2"),
RangeEnd: netip.MustParseAddr("192.168.0.254"),
LeaseDuration: 1 * time.Hour,
},
IPv6: &dhcpsvc.IPv6Config{
Enabled: true,
RangeStart: netip.MustParseAddr("2001:db8::1"),
LeaseDuration: 1 * time.Hour,
RAAllowSLAAC: true,
RASLAACOnly: true,
},
},
"eth1": {
IPv4: &dhcpsvc.IPv4Config{
Enabled: true,
GatewayIP: netip.MustParseAddr("172.16.0.1"),
SubnetMask: netip.MustParseAddr("255.255.255.0"),
RangeStart: netip.MustParseAddr("172.16.0.2"),
RangeEnd: netip.MustParseAddr("172.16.0.255"),
LeaseDuration: 1 * time.Hour,
},
IPv6: &dhcpsvc.IPv6Config{
Enabled: true,
RangeStart: netip.MustParseAddr("2001:db9::1"),
LeaseDuration: 1 * time.Hour,
RAAllowSLAAC: true,
RASLAACOnly: true,
},
},
}
// mustParseMAC parses a hardware address from s and requires no errors.
func mustParseMAC(t require.TestingT, s string) (mac net.HardwareAddr) {
mac, err := net.ParseMAC(s)
require.NoError(t, err)
return mac
}

View File

@@ -2,39 +2,75 @@ package dhcpsvc
import (
"fmt"
"slices"
"log/slog"
"net"
"time"
)
// netInterface is a common part of any network interface within the DHCP
// server.
// macKey contains hardware address as byte array of 6, 8, or 20 bytes.
//
// TODO(e.burkov): Move to aghnet or even to netutil.
type macKey any
// macToKey converts mac into macKey, which is used as the key for the lease
// maps. mac must be a valid hardware address of length 6, 8, or 20 bytes, see
// [netutil.ValidateMAC].
func macToKey(mac net.HardwareAddr) (key macKey) {
switch len(mac) {
case 6:
return [6]byte(mac)
case 8:
return [8]byte(mac)
case 20:
return [20]byte(mac)
default:
panic(fmt.Errorf("invalid mac address %#v", mac))
}
}
// netInterface is a common part of any interface within the DHCP server.
//
// TODO(e.burkov): Add other methods as [DHCPServer] evolves.
type netInterface struct {
// logger logs the events related to the network interface.
logger *slog.Logger
// leases is the set of DHCP leases assigned to this interface.
leases map[macKey]*Lease
// name is the name of the network interface.
name string
// leases is a set of leases sorted by hardware address.
leases []*Lease
// leaseTTL is the default Time-To-Live value for leases.
leaseTTL time.Duration
}
// reset clears all the slices in iface for reuse.
func (iface *netInterface) reset() {
iface.leases = iface.leases[:0]
// newNetInterface creates a new netInterface with the given name, leaseTTL, and
// logger.
func newNetInterface(name string, l *slog.Logger, leaseTTL time.Duration) (iface *netInterface) {
return &netInterface{
logger: l,
leases: map[macKey]*Lease{},
name: name,
leaseTTL: leaseTTL,
}
}
// insertLease inserts the given lease into iface. It returns an error if the
// reset clears all the slices in iface for reuse.
func (iface *netInterface) reset() {
clear(iface.leases)
}
// addLease inserts the given lease into iface. It returns an error if the
// lease can't be inserted.
func (iface *netInterface) insertLease(l *Lease) (err error) {
i, found := slices.BinarySearchFunc(iface.leases, l, compareLeaseMAC)
func (iface *netInterface) addLease(l *Lease) (err error) {
mk := macToKey(l.HWAddr)
_, found := iface.leases[mk]
if found {
return fmt.Errorf("lease for mac %s already exists", l.HWAddr)
}
iface.leases = slices.Insert(iface.leases, i, l)
iface.leases[mk] = l
return nil
}
@@ -42,12 +78,13 @@ func (iface *netInterface) insertLease(l *Lease) (err error) {
// updateLease replaces an existing lease within iface with the given one. It
// returns an error if there is no lease with such hardware address.
func (iface *netInterface) updateLease(l *Lease) (prev *Lease, err error) {
i, found := slices.BinarySearchFunc(iface.leases, l, compareLeaseMAC)
mk := macToKey(l.HWAddr)
prev, found := iface.leases[mk]
if !found {
return nil, fmt.Errorf("no lease for mac %s", l.HWAddr)
}
prev, iface.leases[i] = iface.leases[i], l
iface.leases[mk] = l
return prev, nil
}
@@ -55,12 +92,13 @@ func (iface *netInterface) updateLease(l *Lease) (prev *Lease, err error) {
// removeLease removes an existing lease from iface. It returns an error if
// there is no lease equal to l.
func (iface *netInterface) removeLease(l *Lease) (err error) {
i, found := slices.BinarySearchFunc(iface.leases, l, compareLeaseMAC)
mk := macToKey(l.HWAddr)
_, found := iface.leases[mk]
if !found {
return fmt.Errorf("no lease for mac %s", l.HWAddr)
}
iface.leases = slices.Delete(iface.leases, i, i+1)
delete(iface.leases, mk)
return nil
}

View File

@@ -1,7 +1,6 @@
package dhcpsvc
import (
"bytes"
"net"
"net/netip"
"slices"
@@ -45,8 +44,3 @@ func (l *Lease) Clone() (clone *Lease) {
IsStatic: l.IsStatic,
}
}
// compareLeaseMAC compares two [Lease]s by hardware address.
func compareLeaseMAC(a, b *Lease) (res int) {
return bytes.Compare(a.HWAddr, b.HWAddr)
}

View File

@@ -61,7 +61,7 @@ func (idx *leaseIndex) add(l *Lease, iface *netInterface) (err error) {
return fmt.Errorf("lease for hostname %s already exists", l.Hostname)
}
err = iface.insertLease(l)
err = iface.addLease(l)
if err != nil {
return err
}
@@ -124,3 +124,18 @@ func (idx *leaseIndex) update(l *Lease, iface *netInterface) (err error) {
return nil
}
// rangeLeases calls f for each lease in idx in an unspecified order until f
// returns false.
func (idx *leaseIndex) rangeLeases(f func(l *Lease) (cont bool)) {
for _, l := range idx.byName {
if !f(l) {
break
}
}
}
// len returns the number of leases in idx.
func (idx *leaseIndex) len() (l uint) {
return uint(len(idx.byAddr))
}

View File

@@ -1,16 +1,17 @@
package dhcpsvc
import (
"context"
"fmt"
"log/slog"
"net"
"net/netip"
"slices"
"sync"
"sync/atomic"
"time"
"github.com/AdguardTeam/golibs/errors"
"golang.org/x/exp/maps"
"github.com/AdguardTeam/golibs/mapsutil"
)
// DHCPServer is a DHCP server for both IPv4 and IPv6 address families.
@@ -19,10 +20,20 @@ type DHCPServer struct {
// information about its clients.
enabled *atomic.Bool
// logger logs common DHCP events.
logger *slog.Logger
// localTLD is the top-level domain name to use for resolving DHCP clients'
// hostnames.
localTLD string
// dbFilePath is the path to the database file containing the DHCP leases.
//
// TODO(e.burkov): Consider extracting the database logic into a separate
// interface to prevent packages that only need lease data from depending on
// the entire server and to simplify testing.
dbFilePath string
// leasesMu protects the leases index as well as leases in the interfaces.
leasesMu *sync.RWMutex
@@ -30,10 +41,10 @@ type DHCPServer struct {
leases *leaseIndex
// interfaces4 is the set of IPv4 interfaces sorted by interface name.
interfaces4 netInterfacesV4
interfaces4 dhcpInterfacesV4
// interfaces6 is the set of IPv6 interfaces sorted by interface name.
interfaces6 netInterfacesV6
interfaces6 dhcpInterfacesV6
// icmpTimeout is the timeout for checking another DHCP server's presence.
icmpTimeout time.Duration
@@ -43,36 +54,19 @@ type DHCPServer struct {
// error if the given configuration can't be used.
//
// TODO(e.burkov): Use.
func New(conf *Config) (srv *DHCPServer, err error) {
func New(ctx context.Context, conf *Config) (srv *DHCPServer, err error) {
l := conf.Logger
if !conf.Enabled {
l.DebugContext(ctx, "disabled")
// TODO(e.burkov): Perhaps return [Empty]?
return nil, nil
}
// TODO(e.burkov): Add validations scoped to the network interfaces set.
ifaces4 := make(netInterfacesV4, 0, len(conf.Interfaces))
ifaces6 := make(netInterfacesV6, 0, len(conf.Interfaces))
ifaceNames := maps.Keys(conf.Interfaces)
slices.Sort(ifaceNames)
var i4 *netInterfaceV4
var i6 *netInterfaceV6
for _, ifaceName := range ifaceNames {
iface := conf.Interfaces[ifaceName]
i4, err = newNetInterfaceV4(ifaceName, iface.IPv4)
if err != nil {
return nil, fmt.Errorf("interface %q: ipv4: %w", ifaceName, err)
} else if i4 != nil {
ifaces4 = append(ifaces4, i4)
}
i6 = newNetInterfaceV6(ifaceName, iface.IPv6)
if i6 != nil {
ifaces6 = append(ifaces6, i6)
}
ifaces4, ifaces6, err := newInterfaces(ctx, l, conf.Interfaces)
if err != nil {
// Don't wrap the error since it's informative enough as is.
return nil, err
}
enabled := &atomic.Bool{}
@@ -80,19 +74,62 @@ func New(conf *Config) (srv *DHCPServer, err error) {
srv = &DHCPServer{
enabled: enabled,
logger: l,
localTLD: conf.LocalDomainName,
leasesMu: &sync.RWMutex{},
leases: newLeaseIndex(),
interfaces4: ifaces4,
interfaces6: ifaces6,
icmpTimeout: conf.ICMPTimeout,
dbFilePath: conf.DBFilePath,
}
// TODO(e.burkov): Load leases.
err = srv.dbLoad(ctx)
if err != nil {
// Don't wrap the error since it's informative enough as is.
return nil, err
}
return srv, nil
}
// newInterfaces creates interfaces for the given map of interface names to
// their configurations.
func newInterfaces(
ctx context.Context,
l *slog.Logger,
ifaces map[string]*InterfaceConfig,
) (v4 dhcpInterfacesV4, v6 dhcpInterfacesV6, err error) {
defer func() { err = errors.Annotate(err, "creating interfaces: %w") }()
// TODO(e.burkov): Add validations scoped to the network interfaces set.
v4 = make(dhcpInterfacesV4, 0, len(ifaces))
v6 = make(dhcpInterfacesV6, 0, len(ifaces))
var errs []error
mapsutil.SortedRange(ifaces, func(name string, iface *InterfaceConfig) (cont bool) {
var i4 *dhcpInterfaceV4
i4, err = newDHCPInterfaceV4(ctx, l, name, iface.IPv4)
if err != nil {
errs = append(errs, fmt.Errorf("interface %q: ipv4: %w", name, err))
} else if i4 != nil {
v4 = append(v4, i4)
}
i6 := newDHCPInterfaceV6(ctx, l, name, iface.IPv6)
if i6 != nil {
v6 = append(v6, i6)
}
return true
})
if err = errors.Join(errs...); err != nil {
return nil, nil, err
}
return v4, v6, nil
}
// type check
//
// TODO(e.burkov): Uncomment when the [Interface] interface is implemented.
@@ -108,16 +145,11 @@ func (srv *DHCPServer) Leases() (leases []*Lease) {
srv.leasesMu.RLock()
defer srv.leasesMu.RUnlock()
for _, iface := range srv.interfaces4 {
for _, lease := range iface.leases {
leases = append(leases, lease.Clone())
}
}
for _, iface := range srv.interfaces6 {
for _, lease := range iface.leases {
leases = append(leases, lease.Clone())
}
}
srv.leases.rangeLeases(func(l *Lease) (cont bool) {
leases = append(leases, l.Clone())
return true
})
return leases
}
@@ -159,72 +191,147 @@ func (srv *DHCPServer) IPByHost(host string) (ip netip.Addr) {
}
// Reset implements the [Interface] interface for *DHCPServer.
func (srv *DHCPServer) Reset() (err error) {
func (srv *DHCPServer) Reset(ctx context.Context) (err error) {
defer func() { err = errors.Annotate(err, "resetting leases: %w") }()
srv.leasesMu.Lock()
defer srv.leasesMu.Unlock()
for _, iface := range srv.interfaces4 {
iface.reset()
srv.resetLeases()
err = srv.dbStore(ctx)
if err != nil {
// Don't wrap the error since there is already an annotation deferred.
return err
}
for _, iface := range srv.interfaces6 {
iface.reset()
}
srv.leases.clear()
srv.logger.DebugContext(ctx, "reset leases")
return nil
}
// resetLeases resets the leases for all network interfaces of the server. It
// expects the DHCPServer.leasesMu to be locked.
func (srv *DHCPServer) resetLeases() {
for _, iface := range srv.interfaces4 {
iface.common.reset()
}
for _, iface := range srv.interfaces6 {
iface.common.reset()
}
srv.leases.clear()
}
// AddLease implements the [Interface] interface for *DHCPServer.
func (srv *DHCPServer) AddLease(l *Lease) (err error) {
func (srv *DHCPServer) AddLease(ctx context.Context, l *Lease) (err error) {
defer func() { err = errors.Annotate(err, "adding lease: %w") }()
addr := l.IP
iface, err := srv.ifaceForAddr(addr)
if err != nil {
// Don't wrap the error since there is already an annotation deferred.
// Don't wrap the error since it's already informative enough as is.
return err
}
srv.leasesMu.Lock()
defer srv.leasesMu.Unlock()
return srv.leases.add(l, iface)
err = srv.leases.add(l, iface)
if err != nil {
// Don't wrap the error since there is already an annotation deferred.
return err
}
err = srv.dbStore(ctx)
if err != nil {
// Don't wrap the error since it's already informative enough as is.
return err
}
iface.logger.DebugContext(
ctx, "added lease",
"hostname", l.Hostname,
"ip", l.IP,
"mac", l.HWAddr,
"static", l.IsStatic,
)
return nil
}
// UpdateStaticLease implements the [Interface] interface for *DHCPServer.
//
// TODO(e.burkov): Support moving leases between interfaces.
func (srv *DHCPServer) UpdateStaticLease(l *Lease) (err error) {
func (srv *DHCPServer) UpdateStaticLease(ctx context.Context, l *Lease) (err error) {
defer func() { err = errors.Annotate(err, "updating static lease: %w") }()
addr := l.IP
iface, err := srv.ifaceForAddr(addr)
if err != nil {
// Don't wrap the error since there is already an annotation deferred.
// Don't wrap the error since it's already informative enough as is.
return err
}
srv.leasesMu.Lock()
defer srv.leasesMu.Unlock()
return srv.leases.update(l, iface)
}
// RemoveLease implements the [Interface] interface for *DHCPServer.
func (srv *DHCPServer) RemoveLease(l *Lease) (err error) {
defer func() { err = errors.Annotate(err, "removing lease: %w") }()
addr := l.IP
iface, err := srv.ifaceForAddr(addr)
err = srv.leases.update(l, iface)
if err != nil {
// Don't wrap the error since there is already an annotation deferred.
return err
}
err = srv.dbStore(ctx)
if err != nil {
// Don't wrap the error since it's already informative enough as is.
return err
}
iface.logger.DebugContext(
ctx, "updated lease",
"hostname", l.Hostname,
"ip", l.IP,
"mac", l.HWAddr,
"static", l.IsStatic,
)
return nil
}
// RemoveLease implements the [Interface] interface for *DHCPServer.
func (srv *DHCPServer) RemoveLease(ctx context.Context, l *Lease) (err error) {
defer func() { err = errors.Annotate(err, "removing lease: %w") }()
addr := l.IP
iface, err := srv.ifaceForAddr(addr)
if err != nil {
// Don't wrap the error since it's already informative enough as is.
return err
}
srv.leasesMu.Lock()
defer srv.leasesMu.Unlock()
return srv.leases.remove(l, iface)
err = srv.leases.remove(l, iface)
if err != nil {
// Don't wrap the error since there is already an annotation deferred.
return err
}
err = srv.dbStore(ctx)
if err != nil {
// Don't wrap the error since it's already informative enough as is.
return err
}
iface.logger.DebugContext(
ctx, "removed lease",
"hostname", l.Hostname,
"ip", l.IP,
"mac", l.HWAddr,
"static", l.IsStatic,
)
return nil
}
// ifaceForAddr returns the handled network interface for the given IP address,

View File

@@ -1,8 +1,11 @@
package dhcpsvc_test
import (
"net"
"io/fs"
"net/netip"
"os"
"path"
"path/filepath"
"strings"
"testing"
"time"
@@ -13,53 +16,26 @@ import (
"github.com/stretchr/testify/require"
)
// testLocalTLD is a common local TLD for tests.
const testLocalTLD = "local"
// testdata is a filesystem containing data for tests.
var testdata = os.DirFS("testdata")
// testInterfaceConf is a common set of interface configurations for tests.
var testInterfaceConf = map[string]*dhcpsvc.InterfaceConfig{
"eth0": {
IPv4: &dhcpsvc.IPv4Config{
Enabled: true,
GatewayIP: netip.MustParseAddr("192.168.0.1"),
SubnetMask: netip.MustParseAddr("255.255.255.0"),
RangeStart: netip.MustParseAddr("192.168.0.2"),
RangeEnd: netip.MustParseAddr("192.168.0.254"),
LeaseDuration: 1 * time.Hour,
},
IPv6: &dhcpsvc.IPv6Config{
Enabled: true,
RangeStart: netip.MustParseAddr("2001:db8::1"),
LeaseDuration: 1 * time.Hour,
RAAllowSLAAC: true,
RASLAACOnly: true,
},
},
"eth1": {
IPv4: &dhcpsvc.IPv4Config{
Enabled: true,
GatewayIP: netip.MustParseAddr("172.16.0.1"),
SubnetMask: netip.MustParseAddr("255.255.255.0"),
RangeStart: netip.MustParseAddr("172.16.0.2"),
RangeEnd: netip.MustParseAddr("172.16.0.255"),
LeaseDuration: 1 * time.Hour,
},
IPv6: &dhcpsvc.IPv6Config{
Enabled: true,
RangeStart: netip.MustParseAddr("2001:db9::1"),
LeaseDuration: 1 * time.Hour,
RAAllowSLAAC: true,
RASLAACOnly: true,
},
},
}
// newTempDB copies the leases database file located in the testdata FS, under
// tb.Name()/leases.json, to a temporary directory and returns the path to the
// copied file.
func newTempDB(tb testing.TB) (dst string) {
tb.Helper()
// mustParseMAC parses a hardware address from s and requires no errors.
func mustParseMAC(t require.TestingT, s string) (mac net.HardwareAddr) {
mac, err := net.ParseMAC(s)
require.NoError(t, err)
const filename = "leases.json"
return mac
data, err := fs.ReadFile(testdata, path.Join(tb.Name(), filename))
require.NoError(tb, err)
dst = filepath.Join(tb.TempDir(), filename)
err = os.WriteFile(dst, data, dhcpsvc.DatabasePerm)
require.NoError(tb, err)
return dst
}
func TestNew(t *testing.T) {
@@ -96,6 +72,8 @@ func TestNew(t *testing.T) {
RASLAACOnly: true,
}
leasesPath := filepath.Join(t.TempDir(), "leases.json")
testCases := []struct {
conf *dhcpsvc.Config
name string
@@ -103,6 +81,7 @@ func TestNew(t *testing.T) {
}{{
conf: &dhcpsvc.Config{
Enabled: true,
Logger: discardLog,
LocalDomainName: testLocalTLD,
Interfaces: map[string]*dhcpsvc.InterfaceConfig{
"eth0": {
@@ -110,12 +89,14 @@ func TestNew(t *testing.T) {
IPv6: validIPv6Conf,
},
},
DBFilePath: leasesPath,
},
name: "valid",
wantErrMsg: "",
}, {
conf: &dhcpsvc.Config{
Enabled: true,
Logger: discardLog,
LocalDomainName: testLocalTLD,
Interfaces: map[string]*dhcpsvc.InterfaceConfig{
"eth0": {
@@ -123,12 +104,14 @@ func TestNew(t *testing.T) {
IPv6: &dhcpsvc.IPv6Config{Enabled: false},
},
},
DBFilePath: leasesPath,
},
name: "disabled_interfaces",
wantErrMsg: "",
}, {
conf: &dhcpsvc.Config{
Enabled: true,
Logger: discardLog,
LocalDomainName: testLocalTLD,
Interfaces: map[string]*dhcpsvc.InterfaceConfig{
"eth0": {
@@ -136,13 +119,15 @@ func TestNew(t *testing.T) {
IPv6: validIPv6Conf,
},
},
DBFilePath: leasesPath,
},
name: "gateway_within_range",
wantErrMsg: `interface "eth0": ipv4: ` +
wantErrMsg: `creating interfaces: interface "eth0": ipv4: ` +
`gateway ip 192.168.0.100 in the ip range 192.168.0.1-192.168.0.254`,
}, {
conf: &dhcpsvc.Config{
Enabled: true,
Logger: discardLog,
LocalDomainName: testLocalTLD,
Interfaces: map[string]*dhcpsvc.InterfaceConfig{
"eth0": {
@@ -150,46 +135,56 @@ func TestNew(t *testing.T) {
IPv6: validIPv6Conf,
},
},
DBFilePath: leasesPath,
},
name: "bad_start",
wantErrMsg: `interface "eth0": ipv4: ` +
wantErrMsg: `creating interfaces: interface "eth0": ipv4: ` +
`range start 127.0.0.1 is not within 192.168.0.1/24`,
}}
ctx := testutil.ContextWithTimeout(t, testTimeout)
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
_, err := dhcpsvc.New(tc.conf)
_, err := dhcpsvc.New(ctx, tc.conf)
testutil.AssertErrorMsg(t, tc.wantErrMsg, err)
})
}
}
func TestDHCPServer_AddLease(t *testing.T) {
srv, err := dhcpsvc.New(&dhcpsvc.Config{
ctx := testutil.ContextWithTimeout(t, testTimeout)
leasesPath := filepath.Join(t.TempDir(), "leases.json")
srv, err := dhcpsvc.New(ctx, &dhcpsvc.Config{
Enabled: true,
Logger: discardLog,
LocalDomainName: testLocalTLD,
Interfaces: testInterfaceConf,
DBFilePath: leasesPath,
})
require.NoError(t, err)
const (
host1 = "host1"
host2 = "host2"
host3 = "host3"
existHost = "host1"
newHost = "host2"
ipv6Host = "host3"
)
ip1 := netip.MustParseAddr("192.168.0.2")
ip2 := netip.MustParseAddr("192.168.0.3")
ip3 := netip.MustParseAddr("2001:db8::2")
var (
existIP = netip.MustParseAddr("192.168.0.2")
newIP = netip.MustParseAddr("192.168.0.3")
newIPv6 = netip.MustParseAddr("2001:db8::2")
mac1 := mustParseMAC(t, "01:02:03:04:05:06")
mac2 := mustParseMAC(t, "06:05:04:03:02:01")
mac3 := mustParseMAC(t, "02:03:04:05:06:07")
existMAC = mustParseMAC(t, "01:02:03:04:05:06")
newMAC = mustParseMAC(t, "06:05:04:03:02:01")
ipv6MAC = mustParseMAC(t, "02:03:04:05:06:07")
)
require.NoError(t, srv.AddLease(&dhcpsvc.Lease{
Hostname: host1,
IP: ip1,
HWAddr: mac1,
require.NoError(t, srv.AddLease(ctx, &dhcpsvc.Lease{
Hostname: existHost,
IP: existIP,
HWAddr: existMAC,
IsStatic: true,
}))
@@ -200,77 +195,85 @@ func TestDHCPServer_AddLease(t *testing.T) {
}{{
name: "outside_range",
lease: &dhcpsvc.Lease{
Hostname: host2,
Hostname: newHost,
IP: netip.MustParseAddr("1.2.3.4"),
HWAddr: mac2,
HWAddr: newMAC,
},
wantErrMsg: "adding lease: no interface for ip 1.2.3.4",
}, {
name: "duplicate_ip",
lease: &dhcpsvc.Lease{
Hostname: host2,
IP: ip1,
HWAddr: mac2,
Hostname: newHost,
IP: existIP,
HWAddr: newMAC,
},
wantErrMsg: "adding lease: lease for ip " + ip1.String() +
wantErrMsg: "adding lease: lease for ip " + existIP.String() +
" already exists",
}, {
name: "duplicate_hostname",
lease: &dhcpsvc.Lease{
Hostname: host1,
IP: ip2,
HWAddr: mac2,
Hostname: existHost,
IP: newIP,
HWAddr: newMAC,
},
wantErrMsg: "adding lease: lease for hostname " + host1 +
wantErrMsg: "adding lease: lease for hostname " + existHost +
" already exists",
}, {
name: "duplicate_hostname_case",
lease: &dhcpsvc.Lease{
Hostname: strings.ToUpper(host1),
IP: ip2,
HWAddr: mac2,
Hostname: strings.ToUpper(existHost),
IP: newIP,
HWAddr: newMAC,
},
wantErrMsg: "adding lease: lease for hostname " +
strings.ToUpper(host1) + " already exists",
strings.ToUpper(existHost) + " already exists",
}, {
name: "duplicate_mac",
lease: &dhcpsvc.Lease{
Hostname: host2,
IP: ip2,
HWAddr: mac1,
Hostname: newHost,
IP: newIP,
HWAddr: existMAC,
},
wantErrMsg: "adding lease: lease for mac " + mac1.String() +
wantErrMsg: "adding lease: lease for mac " + existMAC.String() +
" already exists",
}, {
name: "valid",
lease: &dhcpsvc.Lease{
Hostname: host2,
IP: ip2,
HWAddr: mac2,
Hostname: newHost,
IP: newIP,
HWAddr: newMAC,
},
wantErrMsg: "",
}, {
name: "valid_v6",
lease: &dhcpsvc.Lease{
Hostname: host3,
IP: ip3,
HWAddr: mac3,
Hostname: ipv6Host,
IP: newIPv6,
HWAddr: ipv6MAC,
},
wantErrMsg: "",
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
testutil.AssertErrorMsg(t, tc.wantErrMsg, srv.AddLease(tc.lease))
testutil.AssertErrorMsg(t, tc.wantErrMsg, srv.AddLease(ctx, tc.lease))
})
}
assert.NotEmpty(t, srv.Leases())
assert.FileExists(t, leasesPath)
}
func TestDHCPServer_index(t *testing.T) {
srv, err := dhcpsvc.New(&dhcpsvc.Config{
ctx := testutil.ContextWithTimeout(t, testTimeout)
leasesPath := newTempDB(t)
srv, err := dhcpsvc.New(ctx, &dhcpsvc.Config{
Enabled: true,
Logger: discardLog,
LocalDomainName: testLocalTLD,
Interfaces: testInterfaceConf,
DBFilePath: leasesPath,
})
require.NoError(t, err)
@@ -282,46 +285,23 @@ func TestDHCPServer_index(t *testing.T) {
host5 = "host5"
)
ip1 := netip.MustParseAddr("192.168.0.2")
ip2 := netip.MustParseAddr("192.168.0.3")
ip3 := netip.MustParseAddr("172.16.0.3")
ip4 := netip.MustParseAddr("172.16.0.4")
var (
ip1 = netip.MustParseAddr("192.168.0.2")
ip2 = netip.MustParseAddr("192.168.0.3")
ip3 = netip.MustParseAddr("172.16.0.3")
ip4 = netip.MustParseAddr("172.16.0.4")
mac1 := mustParseMAC(t, "01:02:03:04:05:06")
mac2 := mustParseMAC(t, "06:05:04:03:02:01")
mac3 := mustParseMAC(t, "02:03:04:05:06:07")
leases := []*dhcpsvc.Lease{{
Hostname: host1,
IP: ip1,
HWAddr: mac1,
IsStatic: true,
}, {
Hostname: host2,
IP: ip2,
HWAddr: mac2,
IsStatic: true,
}, {
Hostname: host3,
IP: ip3,
HWAddr: mac3,
IsStatic: true,
}, {
Hostname: host4,
IP: ip4,
HWAddr: mac1,
IsStatic: true,
}}
for _, l := range leases {
require.NoError(t, srv.AddLease(l))
}
mac1 = mustParseMAC(t, "01:02:03:04:05:06")
mac2 = mustParseMAC(t, "06:05:04:03:02:01")
mac3 = mustParseMAC(t, "02:03:04:05:06:07")
)
t.Run("ip_idx", func(t *testing.T) {
assert.Equal(t, ip1, srv.IPByHost(host1))
assert.Equal(t, ip2, srv.IPByHost(host2))
assert.Equal(t, ip3, srv.IPByHost(host3))
assert.Equal(t, ip4, srv.IPByHost(host4))
assert.Equal(t, netip.Addr{}, srv.IPByHost(host5))
assert.Zero(t, srv.IPByHost(host5))
})
t.Run("name_idx", func(t *testing.T) {
@@ -329,7 +309,7 @@ func TestDHCPServer_index(t *testing.T) {
assert.Equal(t, host2, srv.HostByIP(ip2))
assert.Equal(t, host3, srv.HostByIP(ip3))
assert.Equal(t, host4, srv.HostByIP(ip4))
assert.Equal(t, "", srv.HostByIP(netip.Addr{}))
assert.Zero(t, srv.HostByIP(netip.Addr{}))
})
t.Run("mac_idx", func(t *testing.T) {
@@ -337,15 +317,20 @@ func TestDHCPServer_index(t *testing.T) {
assert.Equal(t, mac2, srv.MACByIP(ip2))
assert.Equal(t, mac3, srv.MACByIP(ip3))
assert.Equal(t, mac1, srv.MACByIP(ip4))
assert.Nil(t, srv.MACByIP(netip.Addr{}))
assert.Zero(t, srv.MACByIP(netip.Addr{}))
})
}
func TestDHCPServer_UpdateStaticLease(t *testing.T) {
srv, err := dhcpsvc.New(&dhcpsvc.Config{
ctx := testutil.ContextWithTimeout(t, testTimeout)
leasesPath := newTempDB(t)
srv, err := dhcpsvc.New(ctx, &dhcpsvc.Config{
Enabled: true,
Logger: discardLog,
LocalDomainName: testLocalTLD,
Interfaces: testInterfaceConf,
DBFilePath: leasesPath,
})
require.NoError(t, err)
@@ -358,36 +343,16 @@ func TestDHCPServer_UpdateStaticLease(t *testing.T) {
host6 = "host6"
)
ip1 := netip.MustParseAddr("192.168.0.2")
ip2 := netip.MustParseAddr("192.168.0.3")
ip3 := netip.MustParseAddr("192.168.0.4")
ip4 := netip.MustParseAddr("2001:db8::2")
ip5 := netip.MustParseAddr("2001:db8::3")
var (
ip1 = netip.MustParseAddr("192.168.0.2")
ip2 = netip.MustParseAddr("192.168.0.3")
ip3 = netip.MustParseAddr("192.168.0.4")
ip4 = netip.MustParseAddr("2001:db8::3")
mac1 := mustParseMAC(t, "01:02:03:04:05:06")
mac2 := mustParseMAC(t, "01:02:03:04:05:07")
mac3 := mustParseMAC(t, "06:05:04:03:02:01")
mac4 := mustParseMAC(t, "06:05:04:03:02:02")
leases := []*dhcpsvc.Lease{{
Hostname: host1,
IP: ip1,
HWAddr: mac1,
IsStatic: true,
}, {
Hostname: host2,
IP: ip2,
HWAddr: mac2,
IsStatic: true,
}, {
Hostname: host4,
IP: ip4,
HWAddr: mac4,
IsStatic: true,
}}
for _, l := range leases {
require.NoError(t, srv.AddLease(l))
}
mac1 = mustParseMAC(t, "01:02:03:04:05:06")
mac2 = mustParseMAC(t, "06:05:04:03:02:01")
mac3 = mustParseMAC(t, "06:05:04:03:02:02")
)
testCases := []struct {
name string
@@ -406,9 +371,9 @@ func TestDHCPServer_UpdateStaticLease(t *testing.T) {
lease: &dhcpsvc.Lease{
Hostname: host3,
IP: ip3,
HWAddr: mac3,
HWAddr: mac2,
},
wantErrMsg: "updating static lease: no lease for mac " + mac3.String(),
wantErrMsg: "updating static lease: no lease for mac " + mac2.String(),
}, {
name: "duplicate_ip",
lease: &dhcpsvc.Lease{
@@ -448,24 +413,31 @@ func TestDHCPServer_UpdateStaticLease(t *testing.T) {
name: "valid_v6",
lease: &dhcpsvc.Lease{
Hostname: host6,
IP: ip5,
HWAddr: mac4,
IP: ip4,
HWAddr: mac3,
},
wantErrMsg: "",
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
testutil.AssertErrorMsg(t, tc.wantErrMsg, srv.UpdateStaticLease(tc.lease))
testutil.AssertErrorMsg(t, tc.wantErrMsg, srv.UpdateStaticLease(ctx, tc.lease))
})
}
assert.FileExists(t, leasesPath)
}
func TestDHCPServer_RemoveLease(t *testing.T) {
srv, err := dhcpsvc.New(&dhcpsvc.Config{
ctx := testutil.ContextWithTimeout(t, testTimeout)
leasesPath := newTempDB(t)
srv, err := dhcpsvc.New(ctx, &dhcpsvc.Config{
Enabled: true,
Logger: discardLog,
LocalDomainName: testLocalTLD,
Interfaces: testInterfaceConf,
DBFilePath: leasesPath,
})
require.NoError(t, err)
@@ -475,28 +447,15 @@ func TestDHCPServer_RemoveLease(t *testing.T) {
host3 = "host3"
)
ip1 := netip.MustParseAddr("192.168.0.2")
ip2 := netip.MustParseAddr("192.168.0.3")
ip3 := netip.MustParseAddr("2001:db8::2")
var (
existIP = netip.MustParseAddr("192.168.0.2")
newIP = netip.MustParseAddr("192.168.0.3")
newIPv6 = netip.MustParseAddr("2001:db8::2")
mac1 := mustParseMAC(t, "01:02:03:04:05:06")
mac2 := mustParseMAC(t, "02:03:04:05:06:07")
mac3 := mustParseMAC(t, "06:05:04:03:02:01")
leases := []*dhcpsvc.Lease{{
Hostname: host1,
IP: ip1,
HWAddr: mac1,
IsStatic: true,
}, {
Hostname: host3,
IP: ip3,
HWAddr: mac3,
IsStatic: true,
}}
for _, l := range leases {
require.NoError(t, srv.AddLease(l))
}
existMAC = mustParseMAC(t, "01:02:03:04:05:06")
newMAC = mustParseMAC(t, "02:03:04:05:06:07")
ipv6MAC = mustParseMAC(t, "06:05:04:03:02:01")
)
testCases := []struct {
name string
@@ -506,90 +465,108 @@ func TestDHCPServer_RemoveLease(t *testing.T) {
name: "not_found_mac",
lease: &dhcpsvc.Lease{
Hostname: host1,
IP: ip1,
HWAddr: mac2,
IP: existIP,
HWAddr: newMAC,
},
wantErrMsg: "removing lease: no lease for mac " + mac2.String(),
wantErrMsg: "removing lease: no lease for mac " + newMAC.String(),
}, {
name: "not_found_ip",
lease: &dhcpsvc.Lease{
Hostname: host1,
IP: ip2,
HWAddr: mac1,
IP: newIP,
HWAddr: existMAC,
},
wantErrMsg: "removing lease: no lease for ip " + ip2.String(),
wantErrMsg: "removing lease: no lease for ip " + newIP.String(),
}, {
name: "not_found_host",
lease: &dhcpsvc.Lease{
Hostname: host2,
IP: ip1,
HWAddr: mac1,
IP: existIP,
HWAddr: existMAC,
},
wantErrMsg: "removing lease: no lease for hostname " + host2,
}, {
name: "valid",
lease: &dhcpsvc.Lease{
Hostname: host1,
IP: ip1,
HWAddr: mac1,
IP: existIP,
HWAddr: existMAC,
},
wantErrMsg: "",
}, {
name: "valid_v6",
lease: &dhcpsvc.Lease{
Hostname: host3,
IP: ip3,
HWAddr: mac3,
IP: newIPv6,
HWAddr: ipv6MAC,
},
wantErrMsg: "",
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
testutil.AssertErrorMsg(t, tc.wantErrMsg, srv.RemoveLease(tc.lease))
testutil.AssertErrorMsg(t, tc.wantErrMsg, srv.RemoveLease(ctx, tc.lease))
})
}
assert.FileExists(t, leasesPath)
assert.Empty(t, srv.Leases())
}
func TestDHCPServer_Reset(t *testing.T) {
srv, err := dhcpsvc.New(&dhcpsvc.Config{
leasesPath := newTempDB(t)
conf := &dhcpsvc.Config{
Enabled: true,
Logger: discardLog,
LocalDomainName: testLocalTLD,
Interfaces: testInterfaceConf,
})
require.NoError(t, err)
leases := []*dhcpsvc.Lease{{
Hostname: "host1",
IP: netip.MustParseAddr("192.168.0.2"),
HWAddr: mustParseMAC(t, "01:02:03:04:05:06"),
IsStatic: true,
}, {
Hostname: "host2",
IP: netip.MustParseAddr("192.168.0.3"),
HWAddr: mustParseMAC(t, "06:05:04:03:02:01"),
IsStatic: true,
}, {
Hostname: "host3",
IP: netip.MustParseAddr("2001:db8::2"),
HWAddr: mustParseMAC(t, "02:03:04:05:06:07"),
IsStatic: true,
}, {
Hostname: "host4",
IP: netip.MustParseAddr("2001:db8::3"),
HWAddr: mustParseMAC(t, "06:05:04:03:02:02"),
IsStatic: true,
}}
for _, l := range leases {
require.NoError(t, srv.AddLease(l))
DBFilePath: leasesPath,
}
require.Len(t, srv.Leases(), len(leases))
ctx := testutil.ContextWithTimeout(t, testTimeout)
srv, err := dhcpsvc.New(ctx, conf)
require.NoError(t, err)
require.NoError(t, srv.Reset())
const leasesNum = 4
require.Len(t, srv.Leases(), leasesNum)
require.NoError(t, srv.Reset(ctx))
assert.FileExists(t, leasesPath)
assert.Empty(t, srv.Leases())
}
func TestServer_Leases(t *testing.T) {
leasesPath := newTempDB(t)
conf := &dhcpsvc.Config{
Enabled: true,
Logger: discardLog,
LocalDomainName: testLocalTLD,
Interfaces: testInterfaceConf,
DBFilePath: leasesPath,
}
ctx := testutil.ContextWithTimeout(t, testTimeout)
srv, err := dhcpsvc.New(ctx, conf)
require.NoError(t, err)
expiry, err := time.Parse(time.RFC3339, "2042-01-02T03:04:05Z")
require.NoError(t, err)
wantLeases := []*dhcpsvc.Lease{{
Expiry: expiry,
IP: netip.MustParseAddr("192.168.0.3"),
Hostname: "example.host",
HWAddr: mustParseMAC(t, "AA:AA:AA:AA:AA:AA"),
IsStatic: false,
}, {
Expiry: time.Time{},
IP: netip.MustParseAddr("192.168.0.4"),
Hostname: "example.static.host",
HWAddr: mustParseMAC(t, "BB:BB:BB:BB:BB:BB"),
IsStatic: true,
}}
assert.ElementsMatch(t, wantLeases, srv.Leases())
}

View File

@@ -0,0 +1,19 @@
{
"leases": [
{
"expires": "",
"ip": "192.168.0.2",
"hostname": "host1",
"mac": "01:02:03:04:05:06",
"static": true
},
{
"expires": "",
"ip": "2001:db8::2",
"hostname": "host3",
"mac": "06:05:04:03:02:01",
"static": true
}
],
"version": 1
}

View File

@@ -0,0 +1,33 @@
{
"leases": [
{
"expires": "",
"ip": "192.168.0.2",
"hostname": "host1",
"mac": "01:02:03:04:05:06",
"static": true
},
{
"expires": "",
"ip": "192.168.0.3",
"hostname": "host2",
"mac": "06:05:04:03:02:01",
"static": true
},
{
"expires": "",
"ip": "2001:db8::2",
"hostname": "host3",
"mac": "02:03:04:05:06:07",
"static": true
},
{
"expires": "",
"ip": "2001:db8::3",
"hostname": "host4",
"mac": "06:05:04:03:02:02",
"static": true
}
],
"version": 1
}

View File

@@ -0,0 +1,26 @@
{
"leases": [
{
"expires": "",
"ip": "192.168.0.2",
"hostname": "host1",
"mac": "01:02:03:04:05:06",
"static": true
},
{
"expires": "",
"ip": "192.168.0.3",
"hostname": "host2",
"mac": "01:02:03:04:05:07",
"static": true
},
{
"expires": "",
"ip": "2001:db8::2",
"hostname": "host4",
"mac": "06:05:04:03:02:02",
"static": true
}
],
"version": 1
}

View File

@@ -0,0 +1,33 @@
{
"leases": [
{
"expires": "",
"ip": "192.168.0.2",
"hostname": "host1",
"mac": "01:02:03:04:05:06",
"static": true
},
{
"expires": "",
"ip": "192.168.0.3",
"hostname": "host2",
"mac": "06:05:04:03:02:01",
"static": true
},
{
"expires": "",
"ip": "172.16.0.3",
"hostname": "host3",
"mac": "02:03:04:05:06:07",
"static": true
},
{
"expires": "",
"ip": "172.16.0.4",
"hostname": "host4",
"mac": "01:02:03:04:05:06",
"static": true
}
],
"version": 1
}

View File

@@ -0,0 +1,15 @@
{
"leases": [{
"expires": "2042-01-02T03:04:05Z",
"ip": "192.168.0.3",
"hostname": "example.host",
"mac": "AA:AA:AA:AA:AA:AA",
"static": false
}, {
"ip": "192.168.0.4",
"hostname": "example.static.host",
"mac": "BB:BB:BB:BB:BB:BB",
"static": true
}],
"version": 1
}

View File

@@ -1,13 +1,15 @@
package dhcpsvc
import (
"context"
"fmt"
"log/slog"
"net"
"net/netip"
"slices"
"time"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/netutil"
"github.com/google/gopacket/layers"
)
@@ -43,25 +45,130 @@ type IPv4Config struct {
}
// validate returns an error in conf if any.
func (conf *IPv4Config) validate() (err error) {
switch {
case conf == nil:
func (c *IPv4Config) validate() (err error) {
if c == nil {
return errNilConfig
case !conf.Enabled:
return nil
case !conf.GatewayIP.Is4():
return newMustErr("gateway ip", "be a valid ipv4", conf.GatewayIP)
case !conf.SubnetMask.Is4():
return newMustErr("subnet mask", "be a valid ipv4 cidr mask", conf.SubnetMask)
case !conf.RangeStart.Is4():
return newMustErr("range start", "be a valid ipv4", conf.RangeStart)
case !conf.RangeEnd.Is4():
return newMustErr("range end", "be a valid ipv4", conf.RangeEnd)
case conf.LeaseDuration <= 0:
return newMustErr("lease duration", "be less than %d", conf.LeaseDuration)
default:
} else if !c.Enabled {
return nil
}
var errs []error
if !c.GatewayIP.Is4() {
err = newMustErr("gateway ip", "be a valid ipv4", c.GatewayIP)
errs = append(errs, err)
}
if !c.SubnetMask.Is4() {
err = newMustErr("subnet mask", "be a valid ipv4 cidr mask", c.SubnetMask)
errs = append(errs, err)
}
if !c.RangeStart.Is4() {
err = newMustErr("range start", "be a valid ipv4", c.RangeStart)
errs = append(errs, err)
}
if !c.RangeEnd.Is4() {
err = newMustErr("range end", "be a valid ipv4", c.RangeEnd)
errs = append(errs, err)
}
if c.LeaseDuration <= 0 {
err = newMustErr("icmp timeout", "be positive", c.LeaseDuration)
errs = append(errs, err)
}
return errors.Join(errs...)
}
// dhcpInterfaceV4 is a DHCP interface for IPv4 address family.
type dhcpInterfaceV4 struct {
// common is the common part of any network interface within the DHCP
// server.
common *netInterface
// gateway is the IP address of the network gateway.
gateway netip.Addr
// subnet is the network subnet.
subnet netip.Prefix
// addrSpace is the IPv4 address space allocated for leasing.
addrSpace ipRange
// implicitOpts are the options listed in Appendix A of RFC 2131 and
// initialized with default values. It must not have intersections with
// explicitOpts.
implicitOpts layers.DHCPOptions
// explicitOpts are the user-configured options. It must not have
// intersections with implicitOpts.
explicitOpts layers.DHCPOptions
}
// newDHCPInterfaceV4 creates a new DHCP interface for IPv4 address family with
// the given configuration. It returns an error if the given configuration
// can't be used.
func newDHCPInterfaceV4(
ctx context.Context,
l *slog.Logger,
name string,
conf *IPv4Config,
) (i *dhcpInterfaceV4, err error) {
l = l.With(
keyInterface, name,
keyFamily, netutil.AddrFamilyIPv4,
)
if !conf.Enabled {
l.DebugContext(ctx, "disabled")
return nil, nil
}
maskLen, _ := net.IPMask(conf.SubnetMask.AsSlice()).Size()
subnet := netip.PrefixFrom(conf.GatewayIP, maskLen)
switch {
case !subnet.Contains(conf.RangeStart):
return nil, fmt.Errorf("range start %s is not within %s", conf.RangeStart, subnet)
case !subnet.Contains(conf.RangeEnd):
return nil, fmt.Errorf("range end %s is not within %s", conf.RangeEnd, subnet)
}
addrSpace, err := newIPRange(conf.RangeStart, conf.RangeEnd)
if err != nil {
return nil, err
} else if addrSpace.contains(conf.GatewayIP) {
return nil, fmt.Errorf("gateway ip %s in the ip range %s", conf.GatewayIP, addrSpace)
}
i = &dhcpInterfaceV4{
gateway: conf.GatewayIP,
subnet: subnet,
addrSpace: addrSpace,
common: newNetInterface(name, l, conf.LeaseDuration),
}
i.implicitOpts, i.explicitOpts = conf.options(ctx, l)
return i, nil
}
// dhcpInterfacesV4 is a slice of network interfaces of IPv4 address family.
type dhcpInterfacesV4 []*dhcpInterfaceV4
// find returns the first network interface within ifaces containing ip. It
// returns false if there is no such interface.
func (ifaces dhcpInterfacesV4) find(ip netip.Addr) (iface4 *netInterface, ok bool) {
i := slices.IndexFunc(ifaces, func(iface *dhcpInterfaceV4) (contains bool) {
return iface.subnet.Contains(ip)
})
if i < 0 {
return nil, false
}
return ifaces[i].common, true
}
// options returns the implicit and explicit options for the interface. The two
@@ -69,14 +176,14 @@ func (conf *IPv4Config) validate() (err error) {
// values.
//
// TODO(e.burkov): DRY with the IPv6 version.
func (conf *IPv4Config) options() (implicit, explicit layers.DHCPOptions) {
func (c *IPv4Config) options(ctx context.Context, l *slog.Logger) (imp, exp layers.DHCPOptions) {
// Set default values of host configuration parameters listed in Appendix A
// of RFC-2131.
implicit = layers.DHCPOptions{
imp = layers.DHCPOptions{
// Values From Configuration
layers.NewDHCPOption(layers.DHCPOptSubnetMask, conf.SubnetMask.AsSlice()),
layers.NewDHCPOption(layers.DHCPOptRouter, conf.GatewayIP.AsSlice()),
layers.NewDHCPOption(layers.DHCPOptSubnetMask, c.SubnetMask.AsSlice()),
layers.NewDHCPOption(layers.DHCPOptRouter, c.GatewayIP.AsSlice()),
// IP-Layer Per Host
@@ -228,110 +335,29 @@ func (conf *IPv4Config) options() (implicit, explicit layers.DHCPOptions) {
// See https://datatracker.ietf.org/doc/html/rfc1122#section-4.2.3.6.
layers.NewDHCPOption(layers.DHCPOptTCPKeepAliveGarbage, []byte{0x1}),
}
slices.SortFunc(implicit, compareV4OptionCodes)
slices.SortFunc(imp, compareV4OptionCodes)
// Set values for explicitly configured options.
for _, exp := range conf.Options {
i, found := slices.BinarySearchFunc(implicit, exp, compareV4OptionCodes)
for _, o := range c.Options {
i, found := slices.BinarySearchFunc(imp, o, compareV4OptionCodes)
if found {
implicit = slices.Delete(implicit, i, i+1)
imp = slices.Delete(imp, i, i+1)
}
i, found = slices.BinarySearchFunc(explicit, exp, compareV4OptionCodes)
if exp.Length > 0 {
explicit = slices.Insert(explicit, i, exp)
i, found = slices.BinarySearchFunc(exp, o, compareV4OptionCodes)
if o.Length > 0 {
exp = slices.Insert(exp, i, o)
} else if found {
explicit = slices.Delete(explicit, i, i+1)
exp = slices.Delete(exp, i, i+1)
}
}
log.Debug("dhcpsvc: v4: implicit options: %s", implicit)
log.Debug("dhcpsvc: v4: explicit options: %s", explicit)
l.DebugContext(ctx, "options", "implicit", imp, "explicit", exp)
return implicit, explicit
return imp, exp
}
// compareV4OptionCodes compares option codes of a and b.
func compareV4OptionCodes(a, b layers.DHCPOption) (res int) {
return int(a.Type) - int(b.Type)
}
// netInterfaceV4 is a DHCP interface for IPv4 address family.
type netInterfaceV4 struct {
// gateway is the IP address of the network gateway.
gateway netip.Addr
// subnet is the network subnet.
subnet netip.Prefix
// addrSpace is the IPv4 address space allocated for leasing.
addrSpace ipRange
// implicitOpts are the options listed in Appendix A of RFC 2131 and
// initialized with default values. It must not have intersections with
// explicitOpts.
implicitOpts layers.DHCPOptions
// explicitOpts are the user-configured options. It must not have
// intersections with implicitOpts.
explicitOpts layers.DHCPOptions
// netInterface is embedded here to provide some common network interface
// logic.
netInterface
}
// newNetInterfaceV4 creates a new DHCP interface for IPv4 address family with
// the given configuration. It returns an error if the given configuration
// can't be used.
func newNetInterfaceV4(name string, conf *IPv4Config) (i *netInterfaceV4, err error) {
if !conf.Enabled {
return nil, nil
}
maskLen, _ := net.IPMask(conf.SubnetMask.AsSlice()).Size()
subnet := netip.PrefixFrom(conf.GatewayIP, maskLen)
switch {
case !subnet.Contains(conf.RangeStart):
return nil, fmt.Errorf("range start %s is not within %s", conf.RangeStart, subnet)
case !subnet.Contains(conf.RangeEnd):
return nil, fmt.Errorf("range end %s is not within %s", conf.RangeEnd, subnet)
}
addrSpace, err := newIPRange(conf.RangeStart, conf.RangeEnd)
if err != nil {
return nil, err
} else if addrSpace.contains(conf.GatewayIP) {
return nil, fmt.Errorf("gateway ip %s in the ip range %s", conf.GatewayIP, addrSpace)
}
i = &netInterfaceV4{
gateway: conf.GatewayIP,
subnet: subnet,
addrSpace: addrSpace,
netInterface: netInterface{
name: name,
leaseTTL: conf.LeaseDuration,
},
}
i.implicitOpts, i.explicitOpts = conf.options()
return i, nil
}
// netInterfacesV4 is a slice of network interfaces of IPv4 address family.
type netInterfacesV4 []*netInterfaceV4
// find returns the first network interface within ifaces containing ip. It
// returns false if there is no such interface.
func (ifaces netInterfacesV4) find(ip netip.Addr) (iface4 *netInterface, ok bool) {
i := slices.IndexFunc(ifaces, func(iface *netInterfaceV4) (contains bool) {
return iface.subnet.Contains(ip)
})
if i < 0 {
return nil, false
}
return &ifaces[i].netInterface, true
}

View File

@@ -3,7 +3,10 @@ package dhcpsvc
import (
"net/netip"
"testing"
"time"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/testutil"
"github.com/google/gopacket/layers"
"github.com/stretchr/testify/assert"
)
@@ -75,9 +78,12 @@ func TestIPv4Config_Options(t *testing.T) {
wantExplicit: layers.DHCPOptions{opt1},
}}
ctx := testutil.ContextWithTimeout(t, time.Second)
l := slogutil.NewDiscardLogger()
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
imp, exp := tc.conf.options()
imp, exp := tc.conf.options(ctx, l)
assert.Equal(t, tc.wantExplicit, exp)
for c := range exp {

View File

@@ -1,12 +1,14 @@
package dhcpsvc
import (
"context"
"fmt"
"log/slog"
"net/netip"
"slices"
"time"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/netutil"
"github.com/google/gopacket/layers"
)
@@ -38,56 +40,34 @@ type IPv6Config struct {
}
// validate returns an error in conf if any.
func (conf *IPv6Config) validate() (err error) {
switch {
case conf == nil:
func (c *IPv6Config) validate() (err error) {
if c == nil {
return errNilConfig
case !conf.Enabled:
return nil
case !conf.RangeStart.Is6():
return fmt.Errorf("range start %s should be a valid ipv6", conf.RangeStart)
case conf.LeaseDuration <= 0:
return fmt.Errorf("lease duration %s must be positive", conf.LeaseDuration)
default:
} else if !c.Enabled {
return nil
}
}
// options returns the implicit and explicit options for the interface. The two
// lists are disjoint and the implicit options are initialized with default
// values.
//
// TODO(e.burkov): Add implicit options according to RFC.
func (conf *IPv6Config) options() (implicit, explicit layers.DHCPv6Options) {
// Set default values of host configuration parameters listed in RFC 8415.
implicit = layers.DHCPv6Options{}
slices.SortFunc(implicit, compareV6OptionCodes)
var errs []error
// Set values for explicitly configured options.
for _, exp := range conf.Options {
i, found := slices.BinarySearchFunc(implicit, exp, compareV6OptionCodes)
if found {
implicit = slices.Delete(implicit, i, i+1)
}
explicit = append(explicit, exp)
if !c.RangeStart.Is6() {
err = fmt.Errorf("range start %s should be a valid ipv6", c.RangeStart)
errs = append(errs, err)
}
log.Debug("dhcpsvc: v6: implicit options: %s", implicit)
log.Debug("dhcpsvc: v6: explicit options: %s", explicit)
if c.LeaseDuration <= 0 {
err = fmt.Errorf("lease duration %s must be positive", c.LeaseDuration)
errs = append(errs, err)
}
return implicit, explicit
return errors.Join(errs...)
}
// compareV6OptionCodes compares option codes of a and b.
func compareV6OptionCodes(a, b layers.DHCPv6Option) (res int) {
return int(a.Code) - int(b.Code)
}
// dhcpInterfaceV6 is a DHCP interface for IPv6 address family.
type dhcpInterfaceV6 struct {
// common is the common part of any network interface within the DHCP
// server.
common *netInterface
// netInterfaceV6 is a DHCP interface for IPv6 address family.
//
// TODO(e.burkov): Add options.
type netInterfaceV6 struct {
// rangeStart is the first IP address in the range.
rangeStart netip.Addr
@@ -100,10 +80,6 @@ type netInterfaceV6 struct {
// intersections with implicitOpts.
explicitOpts layers.DHCPv6Options
// netInterface is embedded here to provide some common network interface
// logic.
netInterface
// raSLAACOnly defines if DHCP should send ICMPv6.RA packets without MO
// flags.
raSLAACOnly bool
@@ -112,35 +88,40 @@ type netInterfaceV6 struct {
raAllowSLAAC bool
}
// newNetInterfaceV6 creates a new DHCP interface for IPv6 address family with
// newDHCPInterfaceV6 creates a new DHCP interface for IPv6 address family with
// the given configuration.
//
// TODO(e.burkov): Validate properly.
func newNetInterfaceV6(name string, conf *IPv6Config) (i *netInterfaceV6) {
func newDHCPInterfaceV6(
ctx context.Context,
l *slog.Logger,
name string,
conf *IPv6Config,
) (i *dhcpInterfaceV6) {
l = l.With(keyInterface, name, keyFamily, netutil.AddrFamilyIPv6)
if !conf.Enabled {
l.DebugContext(ctx, "disabled")
return nil
}
i = &netInterfaceV6{
rangeStart: conf.RangeStart,
netInterface: netInterface{
name: name,
leaseTTL: conf.LeaseDuration,
},
i = &dhcpInterfaceV6{
rangeStart: conf.RangeStart,
common: newNetInterface(name, l, conf.LeaseDuration),
raSLAACOnly: conf.RASLAACOnly,
raAllowSLAAC: conf.RAAllowSLAAC,
}
i.implicitOpts, i.explicitOpts = conf.options()
i.implicitOpts, i.explicitOpts = conf.options(ctx, l)
return i
}
// netInterfacesV4 is a slice of network interfaces of IPv4 address family.
type netInterfacesV6 []*netInterfaceV6
// dhcpInterfacesV6 is a slice of network interfaces of IPv6 address family.
type dhcpInterfacesV6 []*dhcpInterfaceV6
// find returns the first network interface within ifaces containing ip. It
// returns false if there is no such interface.
func (ifaces netInterfacesV6) find(ip netip.Addr) (iface6 *netInterface, ok bool) {
func (ifaces dhcpInterfacesV6) find(ip netip.Addr) (iface6 *netInterface, ok bool) {
// prefLen is the length of prefix to match ip against.
//
// TODO(e.burkov): DHCPv6 inherits the weird behavior of legacy
@@ -149,7 +130,7 @@ func (ifaces netInterfacesV6) find(ip netip.Addr) (iface6 *netInterface, ok bool
// be used instead.
const prefLen = netutil.IPv6BitLen - 8
i := slices.IndexFunc(ifaces, func(iface *netInterfaceV6) (contains bool) {
i := slices.IndexFunc(ifaces, func(iface *dhcpInterfaceV6) (contains bool) {
return !ip.Less(iface.rangeStart) &&
netip.PrefixFrom(iface.rangeStart, prefLen).Contains(ip)
})
@@ -157,5 +138,35 @@ func (ifaces netInterfacesV6) find(ip netip.Addr) (iface6 *netInterface, ok bool
return nil, false
}
return &ifaces[i].netInterface, true
return ifaces[i].common, true
}
// options returns the implicit and explicit options for the interface. The two
// lists are disjoint and the implicit options are initialized with default
// values.
//
// TODO(e.burkov): Add implicit options according to RFC.
func (c *IPv6Config) options(ctx context.Context, l *slog.Logger) (imp, exp layers.DHCPv6Options) {
// Set default values of host configuration parameters listed in RFC 8415.
imp = layers.DHCPv6Options{}
slices.SortFunc(imp, compareV6OptionCodes)
// Set values for explicitly configured options.
for _, e := range c.Options {
i, found := slices.BinarySearchFunc(imp, e, compareV6OptionCodes)
if found {
imp = slices.Delete(imp, i, i+1)
}
exp = append(exp, e)
}
l.DebugContext(ctx, "options", "implicit", imp, "explicit", exp)
return imp, exp
}
// compareV6OptionCodes compares option codes of a and b.
func compareV6OptionCodes(a, b layers.DHCPv6Option) (res int) {
return int(a.Code) - int(b.Code)
}

View File

@@ -8,6 +8,7 @@ import (
"testing"
"github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/testutil"
"github.com/quic-go/quic-go"
"github.com/stretchr/testify/assert"
@@ -217,7 +218,8 @@ func TestServer_clientIDFromDNSContext(t *testing.T) {
}
srv := &Server{
conf: ServerConfig{TLSConfig: tlsConf},
conf: ServerConfig{TLSConfig: tlsConf},
logger: slogutil.NewDiscardLogger(),
}
var (

View File

@@ -22,6 +22,7 @@ import (
"github.com/AdguardTeam/golibs/container"
"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/stringutil"
"github.com/AdguardTeam/golibs/timeutil"
@@ -301,6 +302,8 @@ type ServerConfig struct {
// UpstreamMode is a enumeration of upstream mode representations. See
// [proxy.UpstreamModeType].
//
// TODO(d.kolyshev): Consider using [proxy.UpstreamMode].
type UpstreamMode string
const (
@@ -339,6 +342,10 @@ func (s *Server) newProxyConfig() (conf *proxy.Config, err error) {
MessageConstructor: s,
}
if s.logger != nil {
conf.Logger = s.logger.With(slogutil.KeyPrefix, "dnsproxy")
}
if srvConf.EDNSClientSubnet.UseCustom {
// TODO(s.chzhen): Use netip.Addr instead of net.IP inside dnsproxy.
conf.EDNSAddr = net.IP(srvConf.EDNSClientSubnet.CustomIP.AsSlice())
@@ -690,7 +697,7 @@ func matchesDomainWildcard(host, pat string) (ok bool) {
// the DNS names and patterns from certificate. dnsNames must be sorted.
func anyNameMatches(dnsNames []string, sni string) (ok bool) {
// Check sni is either a valid hostname or a valid IP address.
if netutil.ValidateHostname(sni) != nil && net.ParseIP(sni) == nil {
if !netutil.IsValidHostname(sni) && !netutil.IsValidIPString(sni) {
return false
}

View File

@@ -10,6 +10,7 @@ import (
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/netutil"
)
// DialContext is an [aghnet.DialContextFunc] that uses s to resolve hostnames.
@@ -28,7 +29,7 @@ func (s *Server) DialContext(ctx context.Context, network, addr string) (conn ne
Timeout: time.Minute * 5,
}
if net.ParseIP(host) != nil {
if netutil.IsValidIPString(host) {
return dialer.DialContext(ctx, network, addr)
}

View File

@@ -6,6 +6,7 @@ import (
"context"
"fmt"
"io"
"log/slog"
"net"
"net/http"
"net/netip"
@@ -27,6 +28,7 @@ import (
"github.com/AdguardTeam/golibs/cache"
"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/sysresolv"
"github.com/AdguardTeam/golibs/stringutil"
@@ -121,6 +123,12 @@ type Server struct {
// access drops disallowed clients.
access *accessManager
// logger is used for logging during server routines.
//
// TODO(d.kolyshev): Make it never nil.
// TODO(d.kolyshev): Use this logger.
logger *slog.Logger
// localDomainSuffix is the suffix used to detect internal hosts. It
// must be a valid domain name plus dots on each side.
localDomainSuffix string
@@ -197,6 +205,10 @@ type DNSCreateParams struct {
PrivateNets netutil.SubnetSet
Anonymizer *aghnet.IPMut
EtcHosts *aghnet.HostsContainer
// Logger is used as a base logger. It must not be nil.
Logger *slog.Logger
LocalDomain string
}
@@ -233,6 +245,7 @@ func NewServer(p DNSCreateParams) (s *Server, err error) {
stats: p.Stats,
queryLog: p.QueryLog,
privateNets: p.PrivateNets,
logger: p.Logger.With(slogutil.KeyPrefix, "dnsforward"),
// TODO(e.burkov): Use some case-insensitive string comparison.
localDomainSuffix: strings.ToLower(localDomainSuffix),
etcHosts: etcHosts,
@@ -719,6 +732,10 @@ func (s *Server) prepareInternalProxy() (err error) {
MessageConstructor: s,
}
if s.logger != nil {
conf.Logger = s.logger.With(slogutil.KeyPrefix, "dnsproxy")
}
err = setProxyUpstreamMode(conf, srvConf.UpstreamMode, srvConf.FastestTimeout.Duration)
if err != nil {
return fmt.Errorf("invalid upstream mode: %w", err)

View File

@@ -28,6 +28,7 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/filtering/safesearch"
"github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/dnsproxy/upstream"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/testutil"
"github.com/AdguardTeam/golibs/timeutil"
@@ -99,6 +100,7 @@ func createTestServer(
DHCPServer: dhcp,
DNSFilter: f,
PrivateNets: netutil.SubnetSetFunc(netutil.IsLocallyServed),
Logger: slogutil.NewDiscardLogger(),
})
require.NoError(t, err)
@@ -339,7 +341,10 @@ func TestServer_timeout(t *testing.T) {
ServePlainDNS: true,
}
s, err := NewServer(DNSCreateParams{DNSFilter: createTestDNSFilter(t)})
s, err := NewServer(DNSCreateParams{
DNSFilter: createTestDNSFilter(t),
Logger: slogutil.NewDiscardLogger(),
})
require.NoError(t, err)
err = s.Prepare(srvConf)
@@ -349,7 +354,10 @@ func TestServer_timeout(t *testing.T) {
})
t.Run("default", func(t *testing.T) {
s, err := NewServer(DNSCreateParams{DNSFilter: createTestDNSFilter(t)})
s, err := NewServer(DNSCreateParams{
DNSFilter: createTestDNSFilter(t),
Logger: slogutil.NewDiscardLogger(),
})
require.NoError(t, err)
s.conf.Config.UpstreamMode = UpstreamModeLoadBalance
@@ -376,7 +384,9 @@ func TestServer_Prepare_fallbacks(t *testing.T) {
ServePlainDNS: true,
}
s, err := NewServer(DNSCreateParams{})
s, err := NewServer(DNSCreateParams{
Logger: slogutil.NewDiscardLogger(),
})
require.NoError(t, err)
err = s.Prepare(srvConf)
@@ -962,6 +972,7 @@ func TestBlockedCustomIP(t *testing.T) {
DHCPServer: dhcp,
DNSFilter: f,
PrivateNets: netutil.SubnetSetFunc(netutil.IsLocallyServed),
Logger: slogutil.NewDiscardLogger(),
})
require.NoError(t, err)
@@ -1127,6 +1138,7 @@ func TestRewrite(t *testing.T) {
DHCPServer: dhcp,
DNSFilter: f,
PrivateNets: netutil.SubnetSetFunc(netutil.IsLocallyServed),
Logger: slogutil.NewDiscardLogger(),
})
require.NoError(t, err)
@@ -1256,6 +1268,7 @@ func TestPTRResponseFromDHCPLeases(t *testing.T) {
},
},
PrivateNets: netutil.SubnetSetFunc(netutil.IsLocallyServed),
Logger: slogutil.NewDiscardLogger(),
LocalDomain: localDomain,
})
require.NoError(t, err)
@@ -1341,6 +1354,7 @@ func TestPTRResponseFromHosts(t *testing.T) {
DHCPServer: dhcp,
DNSFilter: flt,
PrivateNets: netutil.SubnetSetFunc(netutil.IsLocallyServed),
Logger: slogutil.NewDiscardLogger(),
})
require.NoError(t, err)
@@ -1392,24 +1406,29 @@ func TestNewServer(t *testing.T) {
in DNSCreateParams
wantErrMsg string
}{{
name: "success",
in: DNSCreateParams{},
name: "success",
in: DNSCreateParams{
Logger: slogutil.NewDiscardLogger(),
},
wantErrMsg: "",
}, {
name: "success_local_tld",
in: DNSCreateParams{
Logger: slogutil.NewDiscardLogger(),
LocalDomain: "mynet",
},
wantErrMsg: "",
}, {
name: "success_local_domain",
in: DNSCreateParams{
Logger: slogutil.NewDiscardLogger(),
LocalDomain: "my.local.net",
},
wantErrMsg: "",
}, {
name: "bad_local_domain",
in: DNSCreateParams{
Logger: slogutil.NewDiscardLogger(),
LocalDomain: "!!!",
},
wantErrMsg: `local domain: bad domain name "!!!": ` +

View File

@@ -9,6 +9,7 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/dnsproxy/upstream"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/netutil"
"github.com/miekg/dns"
"github.com/stretchr/testify/assert"
@@ -57,6 +58,7 @@ func TestHandleDNSRequest_handleDNSRequest(t *testing.T) {
},
DNSFilter: f,
PrivateNets: netutil.SubnetSetFunc(netutil.IsLocallyServed),
Logger: slogutil.NewDiscardLogger(),
})
require.NoError(t, err)
@@ -229,6 +231,7 @@ func TestHandleDNSRequest_filterDNSResponse(t *testing.T) {
DHCPServer: &testDHCP{},
DNSFilter: f,
PrivateNets: netutil.SubnetSetFunc(netutil.IsLocallyServed),
Logger: slogutil.NewDiscardLogger(),
})
require.NoError(t, err)

View File

@@ -6,7 +6,6 @@ import (
"os"
"strings"
"github.com/AdguardTeam/AdGuardHome/internal/aghos"
"github.com/AdguardTeam/AdGuardHome/internal/ipset"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
@@ -35,7 +34,7 @@ func (c *ipsetCtx) init(ipsetConf []string) (err error) {
log.Info("ipset: warning: cannot initialize: %s", err)
return nil
} else if unsupErr := (&aghos.UnsupportedError{}); errors.As(err, &unsupErr) {
} else if errors.Is(err, errors.ErrUnsupported) {
log.Info("ipset: warning: %s", err)
return nil

View File

@@ -159,7 +159,7 @@ func (s *Server) processInitial(dctx *dnsContext) (rc resultCode) {
q := pctx.Req.Question[0]
qt := q.Qtype
if s.conf.AAAADisabled && qt == dns.TypeAAAA {
_ = proxy.CheckDisabledAAAARequest(pctx, true)
pctx.Res = s.newMsgNODATA(pctx.Req)
return resultCodeFinish
}

View File

@@ -10,6 +10,7 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/dnsproxy/upstream"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/testutil"
"github.com/AdguardTeam/urlfilter/rules"
@@ -430,6 +431,7 @@ func TestServer_ProcessDHCPHosts_localRestriction(t *testing.T) {
dnsFilter: createTestDNSFilter(t),
dhcpServer: dhcp,
localDomainSuffix: localDomainSuffix,
logger: slogutil.NewDiscardLogger(),
}
req := &dns.Msg{
@@ -565,6 +567,7 @@ func TestServer_ProcessDHCPHosts(t *testing.T) {
dnsFilter: createTestDNSFilter(t),
dhcpServer: testDHCP,
localDomainSuffix: tc.suffix,
logger: slogutil.NewDiscardLogger(),
}
req := &dns.Msg{

View File

@@ -11,6 +11,7 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/stats"
"github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/dnsproxy/upstream"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/miekg/dns"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -202,6 +203,7 @@ func TestServer_ProcessQueryLogsAndStats(t *testing.T) {
ql := &testQueryLog{}
st := &testStats{}
srv := &Server{
logger: slogutil.NewDiscardLogger(),
queryLog: ql,
stats: st,
anonymizer: aghnet.NewIPMut(nil),

View File

@@ -150,12 +150,12 @@ func setProxyUpstreamMode(
) (err error) {
switch upstreamMode {
case UpstreamModeParallel:
conf.UpstreamMode = proxy.UModeParallel
conf.UpstreamMode = proxy.UpstreamModeParallel
case UpstreamModeFastestAddr:
conf.UpstreamMode = proxy.UModeFastestAddr
conf.UpstreamMode = proxy.UpstreamModeFastestAddr
conf.FastestPingTimeout = fastestTimeout
case UpstreamModeLoadBalance:
conf.UpstreamMode = proxy.UModeLoadBalance
conf.UpstreamMode = proxy.UpstreamModeLoadBalance
default:
return fmt.Errorf("unexpected value %q", upstreamMode)
}

View File

@@ -22,6 +22,7 @@ type SafeSearchConfig struct {
Bing bool `yaml:"bing" json:"bing"`
DuckDuckGo bool `yaml:"duckduckgo" json:"duckduckgo"`
Ecosia bool `yaml:"ecosia" json:"ecosia"`
Google bool `yaml:"google" json:"google"`
Pixabay bool `yaml:"pixabay" json:"pixabay"`
Yandex bool `yaml:"yandex" json:"yandex"`

View File

@@ -14,6 +14,9 @@ var pixabay string
//go:embed rules/duckduckgo.txt
var duckduckgo string
//go:embed rules/ecosia.txt
var ecosia string
//go:embed rules/yandex.txt
var yandex string
@@ -27,6 +30,7 @@ var youtube string
var safeSearchRules = map[Service]string{
Bing: bing,
DuckDuckGo: duckduckgo,
Ecosia: ecosia,
Google: google,
Pixabay: pixabay,
Yandex: yandex,

View File

@@ -1 +1,2 @@
|www.bing.com^$dnsrewrite=NOERROR;CNAME;strict.bing.com
|edgeservices.bing.com^$dnsrewrite=NOERROR;CNAME;strict.bing.com

View File

@@ -0,0 +1 @@
|www.ecosia.org^$dnsrewrite=NOERROR;CNAME;strict-safe-search.ecosia.org

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