Compare commits

...

54 Commits

Author SHA1 Message Date
Ainar Garipov
48e82f9ab5 Pull request: 2479 simpl
Updates #2479.

Squashed commit of the following:

commit 0fdb0d041d0bd0d9af64513cf82397456a30e2f2
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Mon Mar 29 19:22:56 2021 +0300

    dnsfilter: add a comment

commit d5d6538b8b5133d7c1e9b242a8ac802448d40893
Merge: 6a09acc2 e710ce11
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Mon Mar 29 19:19:39 2021 +0300

    Merge branch 'master' into 2479-simpl

commit 6a09acc262
Author: jvoisin <julien.voisin@dustri.org>
Date:   Tue Dec 22 16:43:47 2020 +0100

    Generalise a construct to simplify a function
2021-03-29 19:35:35 +03:00
Ainar Garipov
e710ce1100 Pull request: client: upd i18n
Updates #2643.

Squashed commit of the following:

commit 4f6098ce31fb3e9e81d66ca613e02c9573763e13
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Mon Mar 29 14:01:33 2021 +0300

    client: upd i18n
2021-03-29 15:39:08 +03:00
Ainar Garipov
ffb503976b Pull request: all: fix windows tempdir
Merge in DNS/adguard-home from try-fixing-windows-tests to master

Squashed commit of the following:

commit 25a43db5d53f24b98921efa21d8d2f231992e761
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Fri Mar 26 21:19:00 2021 +0300

    all: fix windows tempdir
2021-03-29 11:40:04 +03:00
Ainar Garipov
179b76da77 Pull request: dnsforward: imp code, decr cyclo
Updates #2646.

Squashed commit of the following:

commit b362aff81f6b91509b65bdb5e5efc55652449eaf
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Mar 25 21:13:30 2021 +0300

    dnsforward: imp code, decr cyclo
2021-03-26 13:29:33 +03:00
Ainar Garipov
9631eff608 Pull request: dnsfilter: imp code, decr cyclo
Updates #2646.

Squashed commit of the following:

commit c153f08bcf5ade4d0fb9b59d2a0e6a21598c4127
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Mar 25 21:03:51 2021 +0300

    dnsfilter: imp code, decr cyclo
2021-03-26 13:03:36 +03:00
Ainar Garipov
8c735d0dd5 Pull request: all: imp code, decr cyclo
Updates #2646.

Squashed commit of the following:

commit c83c230f3d2c542d7b1a4bc0e1c503d5bbc16cb8
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Mar 25 19:47:11 2021 +0300

    all: imp code, decr cyclo
2021-03-25 20:30:30 +03:00
Ainar Garipov
27f4f05273 Pull request: all: imp some tests
Merge in DNS/adguard-home from imp-tests to master

Squashed commit of the following:

commit 6171420068d66808f069f280a81e0c60e61e5ce3
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Mar 25 17:43:36 2021 +0300

    aghnet: imp test msgs

commit ed2880ff03ab0e41a77f07aad2caf13bf5f4bd9f
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Mar 25 17:39:44 2021 +0300

    all: imp some tests
2021-03-25 18:03:29 +03:00
Ainar Garipov
3764c1dfe4 Pull request: dnsforward: fix nxdomain for internal aaaa reqs
Updates #2393.

Squashed commit of the following:

commit 5ea4d3c2f217ed800da79c3d87184d8e0956e56c
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Mar 25 17:01:08 2021 +0300

    dnsforward: fix nxdomain for internal aaaa reqs
2021-03-25 17:21:00 +03:00
Ainar Garipov
88d924658e Pull request: all: imp HACKING
Merge in DNS/adguard-home from imp-hacking to master

Squashed commit of the following:

commit cce8b051a4324903da76680136b73af5689d3508
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Mar 25 16:47:26 2021 +0300

    all: imp HACKING
2021-03-25 16:55:54 +03:00
Ainar Garipov
a7f9e0122b Pull request: all: custom autohost tlds
Updates #2393.

Squashed commit of the following:

commit 87034134e240480938cdeec14d6b44294bf6442c
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Mar 25 15:48:46 2021 +0300

    dnsforward: fix

commit abf3a1ce8ed7a148d1cc631007fb0422f6da4ae6
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Mar 25 15:21:11 2021 +0300

    dnsforward: imp code, validation

commit fac389bdafc093ce17a7e0831166b89293b550be
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Mar 25 14:54:45 2021 +0300

    all: add validation, imp docs, tests

commit 21b4532afe59f3b89383cb330c9a7d49ec124b6e
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Mar 24 19:09:43 2021 +0300

    all: custom autohost tlds
2021-03-25 16:00:27 +03:00
Ainar Garipov
ba3fc242ab Pull request: home: fix migration, imp code
Updates #1401.
Updates #2646.

Squashed commit of the following:

commit 93b025a2184a72283e22748fecfc478fa549c922
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Mar 24 16:41:07 2021 +0300

    home: fix migration, imp code
2021-03-24 17:17:44 +03:00
Ainar Garipov
e10a3fa4b3 Pull request: home: fix dns address fallback
Closes #2868.

Squashed commit of the following:

commit 7497b0d80233fa0f0fbdc94a85007d39566eea73
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Mar 24 14:23:41 2021 +0300

    home: fix specified ip collecting

commit 7b1dfa69f4edeb3e07cd1067f77ff8519bcdbe1c
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Mar 24 14:01:25 2021 +0300

    home: fix dns address fallback
2021-03-24 14:52:37 +03:00
Ainar Garipov
5d0d32b926 Pull request: all: support multiple dns hosts
Updates #1401.

Squashed commit of the following:

commit a18c3f062a88ad7d7fbfacaedb893f1ca660b6dc
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Mon Mar 22 21:55:26 2021 +0300

    home: imp code

commit 2b4a28cbf379fbc5fb168af6d8d078cab2b8bd64
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Mon Mar 22 20:55:08 2021 +0300

    all: rm unused field

commit 5766a97dafff4acff6b909eb6303459f7991c81e
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Mon Mar 22 16:40:14 2021 +0300

    all: support multiple dns hosts
2021-03-23 12:32:07 +03:00
Eugene Burkov
3b2f5d7842 Pull request: 2704 local resolvers vol.1
Merge in DNS/adguard-home from 2704-local-addresses-vol.1 to master

Updates #2704.
Updates #2829.
Updates #2846.

Squashed commit of the following:

commit 9a49b3d27edcb30da7f16a065226907833b1dc81
Author: Eugene Burkov <e.burkov@adguard.com>
Date:   Mon Mar 22 15:39:17 2021 +0300

    aghnet: imp docs and logging

commit 74f95a29c55b9e732276601b0ecc63fb7c3a9f9e
Author: Eugene Burkov <e.burkov@adguard.com>
Date:   Fri Mar 19 20:56:51 2021 +0300

    all: fix friday evening mistakes

commit 0e2066bc5c16ed807fa601780b99e154502361a9
Author: Eugene Burkov <e.burkov@adguard.com>
Date:   Fri Mar 19 20:51:15 2021 +0300

    all: upd testify, imp code quality

commit 8237c50b670c58361ccf7adec3ff2452b1196677
Author: Eugene Burkov <e.burkov@adguard.com>
Date:   Fri Mar 19 20:19:29 2021 +0300

    aghnet: imp test naming

commit 14eb1e189339554c0a6d38e2ba7a93917774ebab
Author: Eugene Burkov <e.burkov@adguard.com>
Date:   Fri Mar 19 19:41:43 2021 +0300

    aghnet: isolate windows-specific functionality

commit d461ac8b18c187999da3e3aba116571b7ebe6785
Author: Eugene Burkov <e.burkov@adguard.com>
Date:   Fri Mar 19 14:50:05 2021 +0300

    aghnet: imp code quality

commit d0ee01cb1f8613de2085c0f2f2f396e46beb52a5
Author: Eugene Burkov <e.burkov@adguard.com>
Date:   Fri Mar 19 11:59:10 2021 +0300

    all: mv funcs to agherr, mk system resolvers getter
2021-03-22 16:46:36 +03:00
Ainar Garipov
eb9526cc92 Pull request: dhcpd: imp static lease validation
Closes #2838.
Updates #2834.

Squashed commit of the following:

commit 608dce28cf6bcbaf5a7f0bf499889ec25777e121
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Mar 18 16:49:20 2021 +0300

    dhcpd: fix windows; imp code

commit 5e56eebf6ab85ca5fd0a0278c312674d921a3077
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Mar 17 18:47:54 2021 +0300

    dhcpd: imp static lease validation
2021-03-18 17:07:13 +03:00
Ainar Garipov
ffa0afae27 Pull request: all: inc yarn timeout
Merge in DNS/adguard-home from yarn-timeout to master

Squashed commit of the following:

commit 219d8dfc9ae161491f66a9fafb7fb5dc4394c451
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Mar 17 15:29:29 2021 +0300

    all: inc yarn timeout
2021-03-17 15:51:31 +03:00
Ainar Garipov
5243399d9d Pull request: dhcpd: fix static leases
Updates #2541.
Updates #2834.

Squashed commit of the following:

commit d1580182db3b5866213e017405aa2cf8a6ee2f24
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Mar 17 14:51:43 2021 +0300

    all: doc changes; imp naming

commit f036b50a60ba030aa6866944fc7a7b8776ce01d4
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Mar 17 14:09:19 2021 +0300

    dhcpd: fix static leases
2021-03-17 15:02:17 +03:00
Eugene Burkov
3701c3f4bb Pull request: 2835 fix mobileconfig
Merge in DNS/adguard-home from 2835-fix-mobileconfig to master

Updates #2835.

Squashed commit of the following:

commit 83a6fd08d4429db9e532c2788689ca542d5ddb9b
Author: Eugene Burkov <e.burkov@adguard.com>
Date:   Wed Mar 17 13:37:14 2021 +0300

    all: imp changes' log

commit 3dabf0251b8b539509a097ac100d1b2948596668
Author: Eugene Burkov <e.burkov@adguard.com>
Date:   Wed Mar 17 13:31:36 2021 +0300

    all: imp naming, log changes

commit c87032fdc7c61618768843a06fcf7c3db1cea593
Author: Eugene Burkov <e.burkov@adguard.com>
Date:   Wed Mar 17 13:22:19 2021 +0300

    home: fix mobileconfig response
2021-03-17 13:51:26 +03:00
Ainar Garipov
67164f89f3 Pull request: all: add native endianness, imp Makefile
Merge in DNS/adguard-home from fix-some to master

Squashed commit of the following:

commit 190e9a88d9c0f2bfc597aa61b41dae8b8686158e
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Tue Mar 16 20:50:02 2021 +0300

    all: add native endianness, imp Makefile
2021-03-16 21:00:17 +03:00
Eugene Burkov
3a67cc2c45 Pull request: create aghnet package
Merge in DNS/adguard-home from mk-aghnet to master

Updates #2829.

Squashed commit of the following:

commit 519806c04b8d0517aacce9c31f2d06ab56631937
Merge: 92376c86 97361234
Author: Eugene Burkov <e.burkov@adguard.com>
Date:   Tue Mar 16 19:13:56 2021 +0300

    Merge branch 'master' into mk-aghnet

commit 92376c8665e529191aa482432f9d5e3e2e3afdc8
Author: Eugene Burkov <e.burkov@adguard.com>
Date:   Tue Mar 16 18:37:22 2021 +0300

    aghnet: fix linux

commit 7f36d19b0e650d4e4fc5cf9ea4b501a7f636abeb
Author: Eugene Burkov <e.burkov@adguard.com>
Date:   Tue Mar 16 18:08:30 2021 +0300

    aghnet: mv network utils from util

commit aa68c70c1146b8c32303c6e037953a41aa7d72f9
Author: Eugene Burkov <e.burkov@adguard.com>
Date:   Tue Mar 16 17:30:06 2021 +0300

    aghnet: mv ipDetector here

commit b033657f5c5ee91f869c36508a5eb15976a174a0
Author: Eugene Burkov <e.burkov@adguard.com>
Date:   Tue Mar 16 17:24:07 2021 +0300

    all: mk aghnet package, rename sysutil package
2021-03-16 19:42:15 +03:00
Ainar Garipov
9736123483 Pull request: dhcpd: fix ip ranges
Updates #2541.

Squashed commit of the following:

commit c81299991876f48836d24872d9145331a0bc9e6e
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Tue Mar 16 18:10:07 2021 +0300

    agherr: imp docs

commit f43a5f5cde0ea16dd38dd533e16e415a1d306cb2
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Tue Mar 16 17:35:59 2021 +0300

    all: imp err handling, fix code

commit ed26ad0ff53882725f7747264f8094e6fb9b0423
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Tue Mar 16 12:24:17 2021 +0300

    dhcpd: fix ip ranges
2021-03-16 19:11:32 +03:00
Ainar Garipov
e6a8fe452c Pull request: dhcpd: add ips and text option types
Updates #2385.

Squashed commit of the following:

commit ce8467f1c013c6b3fef59667084e2c6569a7213c
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Mon Mar 15 19:02:17 2021 +0300

    dhcpd: add ips and text option types
2021-03-15 19:24:26 +03:00
Ainar Garipov
a818666294 Pull request: client: s/Youtube/YouTube/
Merge in DNS/adguard-home from fix-youtube to master

Squashed commit of the following:

commit ec97400059c1ca9a39eb3622c3665e4c1f619fc7
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Mon Mar 15 17:19:07 2021 +0300

    client: s/Youtube/YouTube/
2021-03-15 17:38:30 +03:00
Ainar Garipov
75ac1a5346 Pull request: dnsforward: fix fqdn in some dns rewrites
Updates #2498.
Updates #2533.

Squashed commit of the following:

commit 9eec20ac3bee5a914f61f41a789f87dec63e910d
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Mon Mar 15 16:53:29 2021 +0300

    dnsforward: fix fqdn in some dns rewrites
2021-03-15 17:17:16 +03:00
Ainar Garipov
313fd7107f Pull request: all: imp code, err handling
Closes #2571.

Squashed commit of the following:

commit a5b50ee011a995f4ab3d93314acd6f0ca82d99cf
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Mon Mar 15 14:05:25 2021 +0300

    all: imp code

commit bc610f8f438549e8c6b04c8a213b5422dda2aff5
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Fri Mar 12 20:00:14 2021 +0300

    all: imp code, err handling
2021-03-15 14:19:04 +03:00
Ainar Garipov
d970b79f2b Pull request: all: add srv handling to dnsrewrite filters
Closes #2498.
Updates #2533.

Squashed commit of the following:

commit 452e0e7d281c1f10bef069bf7a73205266b8f1e0
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Fri Mar 12 19:33:18 2021 +0300

    all: add srv handling to dnsrewrite filters
2021-03-15 13:08:13 +03:00
Ainar Garipov
4c6bf68d4d Pull request: all: use better proxies
Squashed commit of the following:

commit 3d1d3d0b50433792265d8c3cb920f2d6635598d4
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Fri Mar 12 14:56:50 2021 +0300

    all: use better proxies
2021-03-12 15:05:16 +03:00
Ainar Garipov
7e64205d44 Pull request: home: rm var shadowing, vol. 4
Closes #2803.

Squashed commit of the following:

commit cb36cc8811160bb39a32fb8eddf962d0ebe9035a
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Fri Mar 12 14:21:46 2021 +0300

    home: imp more

commit 9ea7ccec8bb293881cf724d7ad57e6744243d8b9
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Fri Mar 12 13:58:10 2021 +0300

    all: imp naming, refactor http srv shutdown

commit f29221007c16fd3e7230bf2c1ac37b365f3e29aa
Merge: 2247c05b bfbf73f3
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Fri Mar 12 13:35:17 2021 +0300

    Merge branch 'master' into 2803-shadow-4

commit 2247c05b5521346aaf362d81ccdd64fee31f1e6d
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Fri Jan 29 20:53:21 2021 +0300

    home: rm var shadowing, vol. 4
2021-03-12 14:32:08 +03:00
Ainar Garipov
bfbf73f3cd Pull request: dhcpd: actually use hostname method
Updates #2582.

Squashed commit of the following:

commit 456bbafa5a7e84e30d5a5c0ee58f5bacebb2b003
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Fri Mar 12 13:23:32 2021 +0300

    dhcpd: imp more

commit 69258ed0d36e59f6be3c31250ea6fd37a1e4cb53
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Fri Mar 12 13:01:18 2021 +0300

    dhcpd: actually use hostname method
2021-03-12 13:34:43 +03:00
Ainar Garipov
968831c5b9 Pull request: all: rm var shadowing vol. 3
Updates #2803.

Squashed commit of the following:

commit ce711ae2e08c3714a4e0c6d177c02f7801c05fce
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Mar 11 20:58:40 2021 +0300

    all: imp code, docs

commit e91094e461893c27eda879b33b5ed2c7085510df
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Mar 11 20:42:14 2021 +0300

    all: rm var shadowing vol. 3
2021-03-11 21:30:52 +03:00
Ainar Garipov
4cf44dd1d4 Pull request: all: rm var shadowing, vol. 2
Updates #2803.

Squashed commit of the following:

commit bf7186378164f19ea9e21ec832526792efa2f9c3
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Mar 11 19:48:17 2021 +0300

    all: rm var shadowing, vol. 2
2021-03-11 20:36:54 +03:00
Eugene Burkov
dfdbfee4fd Pull request: 2639 use testify require vol.4
Merge in DNS/adguard-home from 2639-testify-require-4 to master

Closes #2639.

Squashed commit of the following:

commit 0bb9125f42ab6d2511c1b8e481112aa5edd581d9
Merge: 0e9e9ed1 2c9992e0
Author: Eugene Burkov <e.burkov@adguard.com>
Date:   Thu Mar 11 15:47:21 2021 +0300

    Merge branch 'master' into 2639-testify-require-4

commit 0e9e9ed16ae13ce648b5e1da6ffd123df911c2d7
Author: Eugene Burkov <e.burkov@adguard.com>
Date:   Wed Mar 10 12:43:15 2021 +0300

    home: rm deletion error check

commit 6bfbbcd2b7f9197a06856f9e6b959c2e1c4b8353
Merge: c8ebe541 8811c881
Author: Eugene Burkov <e.burkov@adguard.com>
Date:   Wed Mar 10 12:30:07 2021 +0300

    Merge branch 'master' into 2639-testify-require-4

commit c8ebe54142bba780226f76ddb72e33664ed28f30
Author: Eugene Burkov <e.burkov@adguard.com>
Date:   Wed Mar 10 12:28:43 2021 +0300

    home: imp tests

commit f0e1db456f02df5f5f56ca93e7bd40a48475b38c
Author: Eugene Burkov <e.burkov@adguard.com>
Date:   Fri Mar 5 14:06:41 2021 +0300

    dnsforward: imp tests

commit 4528246105ed06471a8778abbe8e5c30fc5483d5
Merge: 54b08d9c 90ebc4d8
Author: Eugene Burkov <e.burkov@adguard.com>
Date:   Thu Mar 4 18:17:52 2021 +0300

    Merge branch 'master' into 2639-testify-require-4

commit 54b08d9c980b8d69d019a1a1b3931aa048275691
Author: Eugene Burkov <e.burkov@adguard.com>
Date:   Thu Feb 11 13:17:05 2021 +0300

    dnsfilter: imp tests
2021-03-11 17:32:58 +03:00
Ainar Garipov
2c9992e0cc Pull request: all: rm var shadowing, vol. 1
Updates #2803.

Squashed commit of the following:

commit 15fa64ad4fb7561c4b0542245f99869685643bbd
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Mar 11 14:55:28 2021 +0300

    util: imp code

commit c3b0563a44ccc98a5901df19174acdca8a350d62
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Mar 11 14:38:54 2021 +0300

    all: rm var shadowing, vol. 1
2021-03-11 15:38:39 +03:00
Eugene Burkov
94e21e69af Pull request: 2582 invalid hostname vol.3
Merge in DNS/adguard-home from 2582-zero-byte to master

Updates #2582.

Squashed commit of the following:

commit 88db23f26cd6ee9978310c7030f6866b9ef58785
Author: Eugene Burkov <e.burkov@adguard.com>
Date:   Thu Mar 11 13:01:02 2021 +0300

    dhcpd: rm temporary workaround
2021-03-11 14:01:48 +03:00
Ainar Garipov
c08bf86b71 Pull request: all: update go and backend tools
Updates #2275.

Squashed commit of the following:

commit f24c26cd2b49fac00a581936da4ccb13ca341bc9
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Mar 10 21:33:15 2021 +0300

    aghtest: imp docs

commit 46f5b06f9743e800b489e8c30af07d24bfdcf989
Merge: bfb852cb 55d4c7ee
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Mar 10 21:32:32 2021 +0300

    Merge branch 'master' into 2275-upd

commit bfb852cbc74ec219a41e985f2dcb090d58299aee
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Mar 10 19:06:32 2021 +0300

    scripts: rem unsupported platform

commit c1645e247f18d384a980c60d3a94b9363f83f174
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Mar 10 18:47:57 2021 +0300

    all: rollback more

commit 989811b5e38498234dc11baf5dd153c76b9dada4
Merge: 976bdfbd 2d704242
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Mar 10 18:30:42 2021 +0300

    Merge branch 'master' into 2275-upd

commit 976bdfbdd44983f4cd657a486b94ff63f5885fd5
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Mar 10 18:28:23 2021 +0300

    aghtest: fix os_windows

commit 9e85080eefe882d72c939969f7008e3c46467c0c
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Mar 10 18:15:37 2021 +0300

    all: rewrite windows workaround, imp code, docs

commit 35a0b1d8656640a962fe9ae019c3d665f42707ce
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Jan 21 20:38:17 2021 +0300

    all: update go and backend tools
2021-03-11 12:17:54 +03:00
Ainar Garipov
55d4c7ee4f Pull request: scripts: fix readme
Merge in DNS/adguard-home from fix-scripts-readme to master

Squashed commit of the following:

commit dae6aa9f9f69b30d2c949c958b2af413f4ac63be
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Mar 10 20:18:49 2021 +0300

    scripts: fix readme
2021-03-10 20:26:35 +03:00
Ainar Garipov
6de54b3b99 Pull request: scripts: add more control to build-release, imp docs
Updates #2608.

Squashed commit of the following:

commit 59f68f2da22c111a73660a6b799e0429b79f743d
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Tue Feb 2 15:49:54 2021 +0300

    scripts: add more control to build-release, imp docs
2021-03-10 20:12:18 +03:00
Ainar Garipov
2d7042425e Pull request: scripts: imp version
Merge in DNS/adguard-home from 2412-versions to master

Squashed commit of the following:

commit 8c7f11cd2d8987c611a4e828482ba039a55ef677
Merge: 43cd0374 1789c96c
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Mar 10 17:05:58 2021 +0300

    Merge branch 'master' into 2412-versions

commit 43cd0374b7c200fdc45e153781e94ca23cf46b7d
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Fri Mar 5 15:27:13 2021 +0300

    scripts: imp version
2021-03-10 18:08:47 +03:00
Ainar Garipov
1789c96c7f Pull request: all: doc v0.105.2
Merge in DNS/adguard-home from changelog to master

Squashed commit of the following:

commit 38ec49c2947ed666c1495f944c8056087d9f6dd5
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Mar 10 13:27:42 2021 +0300

    all: doc v0.105.2
2021-03-10 13:35:08 +03:00
Eugene Burkov
8811c8817e Pull request: upd dnsproxy
Merge in DNS/adguard-home from upd-dnsproxy to master

Squashed commit of the following:

commit 2181c08de3c2be1723770a5ffff091346536cafa
Author: Eugene Burkov <e.burkov@adguard.com>
Date:   Tue Mar 9 18:55:32 2021 +0300

    all: upd dnsproxy
2021-03-09 19:09:29 +03:00
Eugene Burkov
5aa0ca9319 Pull request: 2582 invalid hostname vol.2
Merge in DNS/adguard-home from 2582-invalid-hostname-2 to master

Updates #2582.

Squashed commit of the following:

commit 9d3ceb289e3869b2c3d12e91ec104fb25d7931ee
Merge: 91c68e46 90054974
Author: Eugene Burkov <e.burkov@adguard.com>
Date:   Fri Mar 5 19:11:49 2021 +0300

    Merge branch 'master' into 2582-invalid-hostname-2

commit 91c68e468c5f5b12a2fb509ff391133483c9d915
Author: Eugene Burkov <e.burkov@adguard.com>
Date:   Fri Mar 5 18:28:14 2021 +0300

    all: mv trimming from home to dhcpd

commit f51faf35288577b6f610f172b26e7ac13aa24f72
Author: Eugene Burkov <e.burkov@adguard.com>
Date:   Fri Mar 5 16:28:00 2021 +0300

    home: add more host sanitizings
2021-03-05 19:20:36 +03:00
Artem Baskal
90054974bc 2694: Statistics of request count of top clients not always displayed correctly
Close #2694

* commit '9f774d776c524fd7c3f0d270ad27ee8d234bf081':
  2694: Statistics of request count of top clients not always displayed correctly
2021-03-05 18:04:49 +03:00
Artem Baskal
9f774d776c 2694: Statistics of request count of top clients not always displayed correctly 2021-03-05 17:33:58 +03:00
Ainar Garipov
8ead755b67 Pull request: scripts: zero patch version number in snapshot versions
Merge in DNS/adguard-home from fix-version to master

Squashed commit of the following:

commit 3cf64a13c881987dd1f2cd679f7ef8558cefee9a
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Mar 4 19:48:17 2021 +0300

    scripts: zero patch version number in snapshot versions
2021-03-04 19:56:46 +03:00
Eugene Burkov
90ebc4d8c9 Pull request: 2582 invalid hostname
Merge in DNS/adguard-home from 2582-invalid-hostname to master

Updates #2582.

Squashed commit of the following:

commit 909598dae00588792b092f89c272c4487ba55dd1
Author: Eugene Burkov <e.burkov@adguard.com>
Date:   Thu Mar 4 17:32:58 2021 +0300

    all: imp code quality, log changes

commit b3b970803709030c48cfe343b88d73524a043de3
Author: Eugene Burkov <e.burkov@adguard.com>
Date:   Thu Mar 4 16:56:28 2021 +0300

    home: add host processing logic
2021-03-04 17:49:34 +03:00
Eugene Burkov
400b76d47b Pull request: 2681 fix DNS-over-TLS bug
Merge in DNS/adguard-home from 2681-fix-dot-bug to master

Updates #2681

Squashed commit of the following:

commit 8de0f4c9767218cd6956a2ab97eda6c5028d1d07
Author: Eugene Burkov <e.burkov@adguard.com>
Date:   Wed Mar 3 19:25:34 2021 +0300

    all: upd dnsproxy
2021-03-03 19:55:37 +03:00
Ainar Garipov
8aa8be2921 Pull request: 2476 rwmutex
Updates #2476.

* commit '52575d0247d5411d26083d4c186d39d8098b916e':
  util: imp autohosts
  Use a RWMutex instead of a Mutex for authosts
2021-03-03 16:25:07 +03:00
Ainar Garipov
52575d0247 util: imp autohosts 2021-03-03 16:14:23 +03:00
Ainar Garipov
7e1b4ca6fe Merge branch 'master' into 2476-rwmutex 2021-03-03 16:10:29 +03:00
Eugene Burkov
a234b63da1 Pull request: 2600 inconsistent response
Merge in DNS/adguard-home from 2600-upd-dnsproxy to master

Closes #2600.

Squashed commit of the following:

commit 4b1515395841f14d86bc85b5c516d14919b5dc25
Author: Eugene Burkov <e.burkov@adguard.com>
Date:   Tue Mar 2 18:39:19 2021 +0300

    all: upd dnsproxy again, log changes

commit 8a866201f140655b0d2f58552a7ad9bcae91a173
Author: Eugene Burkov <e.burkov@adguard.com>
Date:   Tue Mar 2 14:53:48 2021 +0300

    all: cleanup

commit ae81234c79a6dbc61cccbae9c1b9d0144bb7f506
Author: Eugene Burkov <e.burkov@adguard.com>
Date:   Tue Mar 2 14:49:28 2021 +0300

    all: fix deps

commit 662384c366feaf553d9eba2b5e3be93774631ec5
Author: Eugene Burkov <e.burkov@adguard.com>
Date:   Tue Mar 2 13:58:26 2021 +0300

    all: upd dnsproxy version, fix functions' signatures
2021-03-03 15:27:25 +03:00
Eugene Burkov
94e783d572 Pull request: 2470 session token
Merge in DNS/adguard-home from 2470-session-token to master

Updates #2470.

Squashed commit of the following:

commit 02e874404808ee23000c49b4b2980b049dc4d0e6
Author: Eugene Burkov <e.burkov@adguard.com>
Date:   Mon Mar 1 20:11:35 2021 +0300

    home: imp time formating

commit 6f4a6c9b190b2672cecd3e3e31413b03d19f8771
Author: Eugene Burkov <e.burkov@adguard.com>
Date:   Mon Mar 1 18:48:15 2021 +0300

    home: rm user's data from session token
2021-03-01 20:37:28 +03:00
Eugene Burkov
91403d0b95 Pull request: 2757 fix OpenWRT detection
Merge in DNS/adguard-home from 2757-openwrt to master

Updates #2757.

Squashed commit of the following:

commit 8e94e6a67ae702bd1b281b306555a4ce9ecc6391
Author: Eugene Burkov <e.burkov@adguard.com>
Date:   Mon Mar 1 17:02:24 2021 +0300

    util: convert only once

commit f1c74f4d18898f286d70c58f93b2fa21de6b5780
Author: Eugene Burkov <e.burkov@adguard.com>
Date:   Mon Mar 1 16:22:51 2021 +0300

    util: log changes, imp docs

commit 0a4558d044602058255db71f825a730642cc9b07
Author: Eugene Burkov <e.burkov@adguard.com>
Date:   Mon Mar 1 15:53:26 2021 +0300

    util: imp os detection
2021-03-01 17:20:16 +03:00
Ainar Garipov
d6a059e395 Pull request: openapi: add missing property
Merge in DNS/adguard-home from fix-openapi to master

Squashed commit of the following:

commit 24efa5732b327d179f07cbf67fbe8df6ee1b9477
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Fri Feb 26 15:42:56 2021 +0300

    openapi: add missing property
2021-02-26 15:52:52 +03:00
jvoisin
f893df7e64 Use a RWMutex instead of a Mutex for authosts 2020-12-22 13:39:50 +01:00
171 changed files with 6566 additions and 3759 deletions

View File

@@ -1,7 +1,7 @@
'name': 'build'
'env':
'GO_VERSION': '1.14'
'GO_VERSION': '1.15'
'NODE_VERSION': '14'
'on':
@@ -17,7 +17,7 @@
'runs-on': '${{ matrix.os }}'
'env':
'GO111MODULE': 'on'
'GOPROXY': 'https://goproxy.io'
'GOPROXY': 'https://goproxy.cn'
'strategy':
'fail-fast': false
'matrix':

View File

@@ -10,22 +10,74 @@ and this project adheres to
## [Unreleased]
<!--
## [v0.106.0] - 2021-04-26
## [v0.106.0] - 2021-05-01
-->
<!--
## [v0.105.2] - 2021-02-24
-->
### Added
- The ability to set a custom TLD for known local-network hosts ([#2393]).
- The ability to serve DNS queries on multiple hosts and interfaces ([#1401]).
- `ips` and `text` DHCP server options ([#2385]).
- `SRV` records support in `$dnsrewrite` filters ([#2533]).
### Changed
- Stricter validation of the IP addresses of static leases in the DHCP server
with regards to the netmask ([#2838]).
- Stricter validation of `$dnsrewrite` filter modifier parameters ([#2498]).
- New, more correct versioning scheme ([#2412]).
### Deprecated
- Go 1.15 support. v0.107.0 will require at least Go 1.16 to build.
### Fixed
- Support for more than one `/24` subnet in DHCP ([#2541]).
- Invalid filenames in the `mobileconfig` API responses ([#2835]).
### Removed
- Go 1.14 support.
[#1401]: https://github.com/AdguardTeam/AdGuardHome/issues/1401
[#2385]: https://github.com/AdguardTeam/AdGuardHome/issues/2385
[#2393]: https://github.com/AdguardTeam/AdGuardHome/issues/2393
[#2412]: https://github.com/AdguardTeam/AdGuardHome/issues/2412
[#2498]: https://github.com/AdguardTeam/AdGuardHome/issues/2498
[#2533]: https://github.com/AdguardTeam/AdGuardHome/issues/2533
[#2541]: https://github.com/AdguardTeam/AdGuardHome/issues/2541
[#2835]: https://github.com/AdguardTeam/AdGuardHome/issues/2835
[#2838]: https://github.com/AdguardTeam/AdGuardHome/issues/2838
## [v0.105.2] - 2021-03-10
### Fixed
- Incomplete hostnames with trailing zero-bytes handling ([#2582]).
- Wrong DNS-over-TLS ALPN configuration ([#2681]).
- Inconsistent responses for messages with EDNS0 and AD when DNS caching is
enabled ([#2600]).
- Incomplete OpenWrt detection ([#2757]).
- DHCP lease's `expired` field incorrect time format ([#2692]).
- Incomplete DNS upstreams validation ([#2674]).
- Wrong parsing of DHCP options of the `ip` type ([#2688]).
[#2582]: https://github.com/AdguardTeam/AdGuardHome/issues/2582
[#2600]: https://github.com/AdguardTeam/AdGuardHome/issues/2600
[#2674]: https://github.com/AdguardTeam/AdGuardHome/issues/2674
[#2681]: https://github.com/AdguardTeam/AdGuardHome/issues/2681
[#2688]: https://github.com/AdguardTeam/AdGuardHome/issues/2688
[#2692]: https://github.com/AdguardTeam/AdGuardHome/issues/2692
[#2757]: https://github.com/AdguardTeam/AdGuardHome/issues/2757
### Security
- Session token doesn't contain user's information anymore ([#2470]).
[#2470]: https://github.com/AdguardTeam/AdGuardHome/issues/2470
@@ -210,11 +262,12 @@ and this project adheres to
<!--
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.105.2...HEAD
[v0.105.2]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.105.1...v0.105.2
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.106.0...HEAD
[v0.106.0]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.105.2...v0.106.0
-->
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.105.1...HEAD
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.105.2...HEAD
[v0.105.2]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.105.1...v0.105.2
[v0.105.1]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.105.0...v0.105.1
[v0.105.0]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.104.3...v0.105.0
[v0.104.3]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.104.2...v0.104.3

View File

@@ -1,19 +1,23 @@
# AdGuard Home Developer Guidelines
As of **February 2021**, this document is partially a work-in-progress, but
should still be followed. Some of the rules aren't enforced as thoroughly or
remain broken in old code, but this is still the place to find out about what we
**want** our code to look like.
As of **March 2021**, following this document is obligatory for all new code.
Some of the rules aren't enforced as thoroughly or remain broken in old code,
but this is still the place to find out about what we **want** our code to look
like and how to improve it.
The rules are mostly sorted in the alphabetical order.
## Contents
* [Git](#git)
* [Go](#go)
* [Code And Naming](#code-and-naming)
* [Code](#code)
* [Commenting](#commenting)
* [Formatting](#formatting)
* [Naming](#naming)
* [Testing](#testing)
* [Recommended Reading](#recommended-reading)
* [Markdown](#markdown)
* [Shell Scripting](#shell-scripting)
@@ -23,6 +27,8 @@ The rules are mostly sorted in the alphabetical order.
<!-- NOTE: Use the IDs that GitHub would generate in order for this to work both
on GitHub and most other Markdown renderers. -->
## <a id="git" href="#git">Git</a>
* Call your branches either `NNNN-fix-foo` (where `NNNN` is the ID of the
@@ -47,6 +53,8 @@ on GitHub and most other Markdown renderers. -->
The only exceptions are direct mentions of identifiers from the source code
and filenames like `HACKING.md`.
## <a id="go" href="#go">Go</a>
> Not Golang, not GO, not GOLANG, not GoLang. It is Go in natural language,
@@ -54,7 +62,13 @@ on GitHub and most other Markdown renderers. -->
— [@rakyll](https://twitter.com/rakyll/status/1229850223184269312)
### <a id="code-and-naming" href="#code-and-naming">Code And Naming</a>
### <a id="code" href="#code">Code</a>
* Always `recover` from panics in new goroutines. Preferably in the very
first statement. If all you want there is a log message, use
`agherr.LogPanic`.
* Avoid `errors.New`, use `aghnet.Error` instead.
* Avoid `goto`.
@@ -70,13 +84,48 @@ on GitHub and most other Markdown renderers. -->
}
```
Except when the check is done to then use the first character:
```go
if len(s) > 0 {
c := s[0]
}
```
* Constructors should validate their arguments and return meaningful errors.
As a corollary, avoid lazy initialization.
* Don't use naked `return`s.
* Don't mix horizontal and vertical placement of arguments in function and
method calls. That is, either this:
* Don't use underscores in file and package names, unless they're build tags
or for tests. This is to prevent accidental build errors with weird tags.
```go
err := f(a, b, c)
```
Or, when the arguments are too long, this:
```go
err := functionWithALongName(
firstArgumentWithALongName,
secondArgumentWithALongName,
thirdArgumentWithALongName,
)
```
But **never** this:
```go
err := functionWithALongName(firstArgumentWithALongName,
secondArgumentWithALongName,
thirdArgumentWithALongName,
)
```
* Don't use `fmt.Sprintf` where a more structured approach to string
conversion could be used. For example, `net.JoinHostPort` or
`url.(*URL).String`.
* Don't use naked `return`s.
* Don't write non-test code with more than four (**4**) levels of indentation.
Just like [Linus said], plus an additional level for an occasional error
@@ -88,25 +137,7 @@ on GitHub and most other Markdown renderers. -->
* Eschew external dependencies, including transitive, unless
absolutely necessary.
* Name benchmarks and tests using the same convention as examples. For
example:
```go
func TestFunction(t *testing.T) { /* … */ }
func TestFunction_suffix(t *testing.T) { /* … */ }
func TestType_Method(t *testing.T) { /* … */ }
func TestType_Method_suffix(t *testing.T) { /* … */ }
```
* Name parameters in interface definitions:
```go
type Frobulator interface {
Frobulate(f Foo, b Bar) (r Result, err error)
}
```
* Name the deferred errors (e.g. when closing something) `cerr`.
* Minimize scope of variables as much as possible.
* No shadowing, since it can often lead to subtle bugs, especially with
errors.
@@ -114,20 +145,12 @@ on GitHub and most other Markdown renderers. -->
* Prefer constants to variables where possible. Reduce global variables. Use
[constant errors] instead of `errors.New`.
* Prefer to use named functions for goroutines.
* Program code lines should not be longer than one hundred (**100**) columns.
For comments, see the text section below.
* Unused arguments in anonymous functions must be called `_`:
```go
v.onSuccess = func(_ int, msg string) {
// …
}
```
* Use linters.
* Use named returns to improve readability of function signatures.
* Use linters. `make go-lint`.
* Write logs and error messages in lowercase only to make it easier to `grep`
logs and error messages without using the `-i` flag.
@@ -185,6 +208,49 @@ on GitHub and most other Markdown renderers. -->
}}
```
### <a id="naming" href="#naming">Naming</a>
* Don't use underscores in file and package names, unless they're build tags
or for tests. This is to prevent accidental build errors with weird tags.
* Name benchmarks and tests using the same convention as examples. For
example:
```go
func TestFunction(t *testing.T) { /* … */ }
func TestFunction_suffix(t *testing.T) { /* … */ }
func TestType_Method(t *testing.T) { /* … */ }
func TestType_Method_suffix(t *testing.T) { /* … */ }
```
* Name parameters in interface definitions:
```go
type Frobulator interface {
Frobulate(f Foo, b Bar) (r Result, err error)
}
```
* Name the deferred errors (e.g. when closing something) `derr`.
* Unused arguments in anonymous functions must be called `_`:
```go
v.onSuccess = func(_ int, msg string) {
// …
}
```
* Use named returns to improve readability of function signatures.
### <a id="testing" href="#testing">Testing</a>
* Use `assert.NoError` and `require.NoError` instead of `assert.Nil` and
`require.Nil` on errors.
* Use functions like `require.Foo` instead of `assert.Foo` when the test
cannot continue if the condition is false.
### <a id="recommended-reading" href="#recommended-reading">Recommended Reading</a>
* <https://github.com/golang/go/wiki/CodeReviewComments>.
@@ -197,6 +263,8 @@ on GitHub and most other Markdown renderers. -->
[Linus said]: https://www.kernel.org/doc/html/v4.17/process/coding-style.html#indentation
[Text, Including Comments]: #text-including-comments
## <a id="markdown" href="#markdown">Markdown</a>
* **TODO(a.garipov):** Define more Markdown conventions.
@@ -208,6 +276,8 @@ on GitHub and most other Markdown renderers. -->
* Use either link references or link destinations only. Put all link
reference definitions at the end of the second-level block.
## <a id="shell-scripting" href="#shell-scripting">Shell Scripting</a>
* Avoid bashisms and GNUisms, prefer POSIX features only.
@@ -310,6 +380,7 @@ on GitHub and most other Markdown renderers. -->
escaping is required or there are single quotes inside the string (e.g. for
GitHub Actions).
* Use `>` for multiline strings, unless you need to keep the line breaks.
* Use `>` for multiline strings, unless you need to keep the line breaks. Use
`|` for multiline strings when you do.
[NO-rway Law]: https://news.ycombinator.com/item?id=17359376

View File

@@ -10,11 +10,7 @@ CLIENT_DIR = client
COMMIT = $$(git rev-parse --short HEAD)
DIST_DIR = dist
GO = go
# TODO(a.garipov): Add more default proxies using pipes after update to
# Go 1.15.
#
# GOPROXY = https://goproxy.io|https://goproxy.cn|direct
GOPROXY = https://goproxy.cn,https://goproxy.io,direct
GOPROXY = https://goproxy.cn|https://proxy.golang.org|direct
GPG_KEY = devteam@adguard.com
GPG_KEY_PASSPHRASE = not-a-real-password
NPM = npm
@@ -23,7 +19,7 @@ SIGN = 1
VERBOSE = 0
VERSION = v0.0.0
YARN = yarn
YARN_FLAGS = --cwd $(CLIENT_BETA_DIR)
YARN_FLAGS = --cwd $(CLIENT_BETA_DIR) --network-timeout 120000
ENV = env\
COMMIT='$(COMMIT)'\
@@ -86,18 +82,3 @@ go-check: go-tools go-lint go-test
openapi-lint: ; cd ./openapi/ && $(YARN) test
openapi-show: ; cd ./openapi/ && $(YARN) start
# TODO(a.garipov): Remove the legacy targets once the build
# infrastructure stops using them.
dependencies:
@ echo "use make deps instead"
@ $(MAKE) deps
docker-multi-arch:
@ echo "use make build-docker instead"
@ $(MAKE) build-docker
go-install-tools:
@ echo "use make go-tools instead"
@ $(MAKE) go-tools
release:
@ echo "use make build-release instead"
@ $(MAKE) build-release

View File

@@ -177,7 +177,7 @@ Run `make init` to prepare the development environment.
You will need this to build AdGuard Home:
* [go](https://golang.org/dl/) v1.14 or later.
* [go](https://golang.org/dl/) v1.15 or later.
* [node.js](https://nodejs.org/en/download/) v10.16.2 or later.
* [npm](https://www.npmjs.com/) v6.14 or later (temporary requirement, TODO: remove when redesign is finished).
* [yarn](https://yarnpkg.com/) v1.22.5 or later.

View File

@@ -122,7 +122,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",

View File

@@ -77,7 +77,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": "Общи настройки",
"upstream_dns": "Главен DNS сървър",

View File

@@ -1,68 +1,68 @@
{
"client_settings": "Klient-indstillinger",
"example_upstream_reserved": "Du kan specificere DNS upstream <0>for det(de) specifikke domæne(r)</0>",
"example_upstream_comment": "Du kan uddybe kommentaren",
"upstream_parallel": "Brug parallelle forespørgsler til at fremskynde behandlingen ved samtidig at spørge alle upstream servere",
"client_settings": "Klientindstillinger",
"example_upstream_reserved": "DNS-upstream kan agives <0>for et eller flere specifikke domæner</0>",
"example_upstream_comment": "Du kan angive en kommentaren",
"upstream_parallel": "Brug parallelle forespø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 en server ad gangen. AdGuard Home bruger den vægtede tilfældige algoritme til at vælge serveren, så den hurtigste server bliver brugt oftere.",
"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.",
"bootstrap_dns": "Bootstrap DNS-servere",
"bootstrap_dns_desc": "Bootstrap DNS-servere bliver brugt til at behandle IP-adresser af de DoH/DoT resolvere, som du angiver som upstream.",
"check_dhcp_servers": "Tjek for DHCP-servere",
"save_config": "Gem konfiguration",
"bootstrap_dns_desc": "Bootstrap DNS-servere bruges til at fortolke IP-adresser for de DoH-/DoT-resolvere, du angiver som upstream.",
"check_dhcp_servers": "Søg efter DHCP-servere",
"save_config": "Gem opsætning",
"enabled_dhcp": "DHCP-server aktiveret",
"disabled_dhcp": "DHCP-server deaktiveret",
"unavailable_dhcp": "DHCP er ikke tilgængelig",
"unavailable_dhcp_desc": "AdGuard Home kan ikke køre en DHCP-server dit OS",
"unavailable_dhcp": "DHCP utilgængelig",
"unavailable_dhcp_desc": "AdGuard Home kan ikke køre en DHCP-server i dit OS",
"dhcp_title": "DHCP-server (eksperimentel!)",
"dhcp_description": "Hvis din router ikke tilbyder DHCP-indstillinger, kan du bruge AdGuards egen indbyggede DHCP-server.",
"dhcp_enable": "Aktiver DHCP-server",
"dhcp_disable": "Deaktiver DHCP-server",
"dhcp_not_found": "Det er sikkert at aktivere den indbyggede DHCP-server - vi fandt ikke nogen aktive DHCP-servere på netværket. Alligevel opfordrer vi dig til at tjekke det manuelt, da vores automatiske test i øjeblikket ikke virker 100%.",
"dhcp_description": "Har din router ingen DHCP-indstillinger, kan du bruge AdGuards egen, indbyggede DHCP-server.",
"dhcp_enable": "Aktivere DHCP-server",
"dhcp_disable": "Deaktivere DHCP-server",
"dhcp_not_found": "Det er sikkert at aktivere den indbyggede DHCP-server - ingen aktive DHCP-servere blev fundet på netværket. Du opfordres dog til at tjekke dette manuelt, da den automatiske skanning pt. ikke giver en 100% garanti.",
"dhcp_found": "En aktiv DHCP-server er fundet på netværket. Det er ikke sikkert at aktivere den indbyggede DHCP-server.",
"dhcp_leases": "DHCP-leases",
"dhcp_static_leases": "DHCP static leases",
"dhcp_static_leases": "DHCP statiske leases",
"dhcp_leases_not_found": "Ingen DHCP-leases fundet",
"dhcp_config_saved": "DHCP-konfiguration gemt",
"dhcp_ipv4_settings": "DHCP IPv4 Indstillinger",
"dhcp_ipv6_settings": "DHCP IPv6 Indstillinger",
"dhcp_config_saved": "DHCP-opsætning gemt",
"dhcp_ipv4_settings": "DHCP IPv4-indstillinger",
"dhcp_ipv6_settings": "DHCP IPv6-indstillinger",
"form_error_required": "Obligatorisk felt",
"form_error_ip4_format": "Ugyldigt IPv4-format",
"form_error_ip6_format": "Ugyldigt IPv6-format",
"form_error_ip_format": "Ugyldigt IP-format",
"form_error_mac_format": "Ugyldigt MAC-format",
"form_error_client_id_format": "Ugyldigt klient-ID-format",
"form_error_client_id_format": "Ugyldigt klient-ID format",
"form_error_server_name": "Ugyldigt servernavn",
"form_error_positive": "Skal være større end 0",
"form_error_negative": "Skal være lig med 0 eller større",
"range_end_error": "Skal være større end starten af intervallet",
"range_end_error": "Skal være større end starten intervallet",
"dhcp_form_gateway_input": "Gateway IP",
"dhcp_form_subnet_input": "Subnet mask",
"dhcp_form_subnet_input": "Undernetmaske",
"dhcp_form_range_title": "Interval af IP-adresser",
"dhcp_form_range_start": "Start interval",
"dhcp_form_range_end": "Slut interval",
"dhcp_form_range_start": "Intervalstart",
"dhcp_form_range_end": "Intervalslut",
"dhcp_form_lease_title": "DHCP-lease tid (i sekunder)",
"dhcp_form_lease_input": "Lease varighed",
"dhcp_form_lease_input": "Lease-varighed",
"dhcp_interface_select": "Vælg DHCP-interface",
"dhcp_hardware_address": "Hardware-adresse",
"dhcp_ip_addresses": "IP-adresser",
"ip": "IP",
"dhcp_table_hostname": "Værtsnavn",
"dhcp_table_expires": "Udløber",
"dhcp_warning": "Hvis du vil aktivere DHCP-serveren alligevel, skal du sørge for, at der ikke er nogen anden aktiv DHCP-server på dit netværk. Ellers kan det ødelægge internettet for tilsluttede enheder!",
"dhcp_error": "Vi kunne ikke afgøre, om der er en anden DHCP-server på netværket.",
"dhcp_static_ip_error": "For at kunne bruge DHCP-serveren skal en statisk IP-adresse indstilles. Vi fejlede i at afgøre, om denne netværksgrænseflade er konfigureret ved hjælp af en statisk IP-adresse. Indstil venligst manuelt en statisk IP-adresse.",
"dhcp_dynamic_ip_found": "Dit system bruger en dynamisk IP-adresse konfiguration til grænsefladen <0>{{interfaceName}}</0>. For at kunne bruge DHCP-serveren skal en statisk IP-adresse indstilles. Din nuværende IP-adresse er <0>{{ipAddress}}</0>. Vi vil automatisk indstille denne IP-adresse som en statisk, hvis du trykker på Aktiver DHCP knappen.",
"dhcp_lease_added": "Static lease \"{{key}}\" succesfuldt tilføjet",
"dhcp_lease_deleted": "Static lease \"{{key}}\" succesfuldt slettet",
"dhcp_new_static_lease": "Ny static lease",
"dhcp_static_leases_not_found": "Ingen DHCP static leases fundet",
"dhcp_add_static_lease": "Tilføj static lease",
"dhcp_reset": "Er du sikker på, at du vil nulstille DHCP-konfigurationen?",
"dhcp_warning": "Vil du alligevel aktivere DHCP-serveren, så sørg for at der ikke er nogen anden aktiv DHCP-server på dit netværk, da dette kan ødelægge tilsluttede enheders Internetkonnektivitet!",
"dhcp_error": "Det kunne ikke afgøres, om der findes en anden DHCP-server på netværket.",
"dhcp_static_ip_error": "For at kunne bruge DHCP-serveren skal der opsættes en statisk IP-adresse. Da det ikke kunne afgøres, om denne netværksinterface er opsat vha. en statisk IP-adresse, bedes du opsætte en manuelt.",
"dhcp_dynamic_ip_found": "Dit system bruger en dynamisk IP-adresseopsætning til interface <0>{{interfaceName}}</0>. For at kunne bruge DHCP-serveren skal en statisk IP-adresse indstilles. Din aktuelle IP-adresse er <0>{{ipAddress}}</0>, og den vil automatisk blive indstillet som din statiske IP-adresse, hvis du trykker på knappen Aktivér DHCP.",
"dhcp_lease_added": "Statisk lease \"{{key}}\" tilføjet",
"dhcp_lease_deleted": "Statisk lease \"{{key}}\" slettet",
"dhcp_new_static_lease": "Ny statisk lease",
"dhcp_static_leases_not_found": "Intet DHCP statisk leases fundet",
"dhcp_add_static_lease": "Tilføj statisk lease",
"dhcp_reset": "Sikker på, at du vil nulstille DHCP-opsætningen?",
"country": "Land",
"city": "By",
"delete_confirm": "Er du sikker på, at du vil slette \"{{key}}\"?",
"form_enter_hostname": "Indtast værtsnavn",
"delete_confirm": "Sikker på, at du vil slette \"{{key}}\"?",
"form_enter_hostname": "Angiv værtsnavn",
"error_details": "Fejloplysninger",
"response_details": "Svardetaljer",
"request_details": "Anmod om detaljer",
@@ -80,62 +80,62 @@
"version": "Version",
"address": "Adresse",
"protocol": "Protokol",
"on": "TÆNDT",
"off": "SLUKKET",
"copyright": "Copyright",
"on": "TIL",
"off": "FRA",
"copyright": "Ophavsrettighed",
"homepage": "Hjemmeside",
"report_an_issue": "Rapporter et problem",
"privacy_policy": "Privatlivspolitik",
"enable_protection": "Aktiver beskyttelse",
"report_an_issue": "Anmeld et problem",
"privacy_policy": "Fortrolighedspolitik",
"enable_protection": "Aktivér beskyttelse",
"enabled_protection": "Beskyttelse aktiveret",
"disable_protection": "Deaktiver beskyttelse",
"disable_protection": "Deaktivér beskyttelse",
"disabled_protection": "Beskyttelse deaktiveret",
"refresh_statics": "Opdater statistikerne",
"dns_query": "DNS-Forespørgsler",
"refresh_statics": "Opdatér statistikerne",
"dns_query": "DNS-forespørgsler",
"blocked_by": "<0>Blokeret af Filtre</0>",
"stats_malware_phishing": "Blokeret malware/phishing",
"stats_adult": "Blokerede voksne websteder",
"stats_query_domain": "Mest eftertragtede domæner",
"for_last_24_hours": "i løbet af de sidste 24 timer",
"for_last_days": "for den sidste {{count}} dag",
"for_last_days_plural": "for de sidste {{count}} dage",
"stats_query_domain": "Mest forespurgte domæner",
"for_last_24_hours": "de seneste 24 timer",
"for_last_days": "den seneste {{count}} dag",
"for_last_days_plural": "de seneste {{count}} dage",
"no_domains_found": "Ingen domæner fundet",
"requests_count": "Antal anmodninger",
"top_blocked_domains": "De mest blokerede domæner",
"top_clients": "De bedste klienter",
"requests_count": "Antal forespørgsler",
"top_blocked_domains": "Hyppigst blokerede domæner",
"top_clients": "Hyppigste klienter",
"no_clients_found": "Ingen klienter fundet",
"general_statistics": "Generelle statistikker",
"number_of_dns_query_days": "Antallet af DNS-forespørgsler behandlet i løbet af den sidste {{count}} dag",
"number_of_dns_query_days_plural": "Antallet af DNS-forespørgsler behandlet i løbet af de sidste {{count}} dage",
"number_of_dns_query_24_hours": "Antallet af DNS-forespørgsler behandlet i løbet af de sidste 24 timer",
"number_of_dns_query_blocked_24_hours": "Antallet af DNS-forespørgsler blokeret af adblock filtre og værternes blokeringslister",
"number_of_dns_query_blocked_24_hours_by_sec": "Antallet af DNS-forespørgsler blokeret af AdGuards browsing sikkerhedsmodul",
"number_of_dns_query_blocked_24_hours_adult": "Antallet af voksensider blokeret",
"enforced_save_search": "Håndhæv sikker søgning",
"number_of_dns_query_days": "Antallet af DNS-forespørgsler behandlet den seneste {{count}} dag",
"number_of_dns_query_days_plural": "Antallet af DNS-forespørgsler behandlet de seneste {{count}} dage",
"number_of_dns_query_24_hours": "Antallet af DNS-forespørgsler behandlet de seneste 24 timer",
"number_of_dns_query_blocked_24_hours": "Antallet af DNS-forespørgsler blokeret af adblockfiltre og værtssortlister",
"number_of_dns_query_blocked_24_hours_by_sec": "Antallet af DNS-forespørgsler blokeret af AdGuards browsingsikkerhedsmodul",
"number_of_dns_query_blocked_24_hours_adult": "Antallet af blokerede voksenwebsteder",
"enforced_save_search": "Håndhævet sikker søgning",
"number_of_dns_query_to_safe_search": "Antallet af DNS-forespørgsler til søgemaskiner, hvor Sikker Søgning blev håndhævet",
"average_processing_time": "Gennemsnitlig behandlingstid",
"average_processing_time_hint": "Gennemsnitlig behandlingstid i millisekunder af DNS-forespørgsel",
"block_domain_use_filters_and_hosts": "Bloker domæner ved hjælp af filtre og værtsfiler",
"filters_block_toggle_hint": "Du kan oprette blokeringsregler i <a>Filterindstillingerne</a>.",
"use_adguard_browsing_sec": "Brug AdGuards browsing sikkerhedstjeneste",
"use_adguard_browsing_sec_hint": "AdGuard Home vil kontrollere om domænet er sortlistet af browsing sikkerhedstjenesten. Den vil bruge privatlivsvenlig lookup API til at udføre kontrollen: kun et kort præfiks af domænenavnet SHA256 hash bliver sendt til serveren.",
"use_adguard_parental": "Brug AdGuards forældrekontrol",
"use_adguard_parental_hint": "AdGuard Home vil kontrollere, om domænet indeholder voksenindhold. Den bruger den samme privatlivsvenlige API som browsing sikkerhedstjenesten.",
"block_domain_use_filters_and_hosts": "Blokér domæner vha. filtre og værtsfiler",
"filters_block_toggle_hint": "Du kan optte blokeringsregler i <a>Filterindstillingerne</a>.",
"use_adguard_browsing_sec": "Brug AdGuards webtjeneste til browsingsikkerhed",
"use_adguard_browsing_sec_hint": "AdGuard Homevil tjekke, om domænet er sortlistet af browsingsikkerhedswebtjenesten vha. en fortrolighedsvenlig lookup-API til at udføre tjekket. Kun et kort præfiks af domænenavns SHA256-hashen sendes til serveren.",
"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": "Håndhæv sikker søgning",
"enforce_save_search_hint": "AdGuard Home kan håndhæve sikker søgning i følgende søgemaskiner: Google, Youtube, Bing, DuckDuckGo, Yandex og Pixabay.",
"no_servers_specified": "Ingen servere specificeret",
"enforce_save_search_hint": "AdGuard Home kan håndhæve sikker søgning i flg. søgemaskiner: Google, YouTube, Bing, DuckDuckGo, Yandex og Pixabay.",
"no_servers_specified": "Ingen servere angivet",
"general_settings": "Generelle indstillinger",
"dns_settings": "DNS-indstillinger",
"dns_blocklists": "DNS-blokeringslister",
"dns_allowlists": "Lister over tilladte DNS",
"dns_blocklists_desc": "AdGuard Home vil blokere domæner, der matcher blokeringslisterne.",
"dns_allowlists_desc": "Domæner fra listerne over tilladte DNS vil være tilladt, selvom de er i nogen af blokeringslisterne.",
"custom_filtering_rules": "Brugerdefinerede filtreringsregler",
"dns_blocklists": "DNS-sortlister",
"dns_allowlists": "DNS-hvidlister",
"dns_blocklists_desc": "AdGuard Home blokerer domæner matchende sortlisterne.",
"dns_allowlists_desc": "Domæner fra DNS-hvidlisterne tillades, selv hvis de er nogle af sortlisterne.",
"custom_filtering_rules": "Tilpassede filtreringsregler",
"encryption_settings": "Krypteringsindstillinger",
"dhcp_settings": "DHCP-indstillinger",
"upstream_dns": "Upstream DNS-servere",
"upstream_dns_help": "Indtast serveradresserne en pr. linje. <a>Lær mere</a> om konfiguration af upstream DNS-servere.",
"upstream_dns_configured_in_file": "Konfigureret i {{path}}",
"upstream_dns_help": "Angiv serveradresserne, én pr. linje. <a> mere at vide</a> om upstream DNS-serveropsætning.",
"upstream_dns_configured_in_file": "Opsat i {{path}}",
"test_upstream_btn": "Test upstreams",
"upstreams": "Upstreams",
"apply_btn": "Anvend",
@@ -149,59 +149,59 @@
"enabled_save_search_toast": "Sikker søgning aktiveret",
"enabled_table_header": "Aktiveret",
"name_table_header": "Navn",
"list_url_table_header": "Listernes URL",
"list_url_table_header": "Liste-URL",
"rules_count_table_header": "Antal regler",
"last_time_updated_table_header": "Sidst opdateret",
"last_time_updated_table_header": "Senest opdateret",
"actions_table_header": "Handlinger",
"request_table_header": "Forespørgsel",
"edit_table_action": "Rediger",
"edit_table_action": "Redigér",
"delete_table_action": "Slet",
"elapsed": "Varighed",
"filters_and_hosts_hint": "AdGuard Home forstår de grundlæggende annonceblokeringsregler og værtsfilsyntaks.",
"no_blocklist_added": "Ingen blokeringslister tilføjet",
"no_whitelist_added": "Ingen lister over tilladte tilføjet",
"add_blocklist": "Tilføj blokeringsliste",
"add_allowlist": "Tilføj liste over tilladte",
"cancel_btn": "Annuller",
"enter_name_hint": "Indtast navn",
"enter_url_or_path_hint": "Indtast en URL eller en absolut sti på listen",
"filters_and_hosts_hint": "AdGuard Home forstår basis adblockingregler og værtsfilsyntaks.",
"no_blocklist_added": "Ingen sortlister tilføjet",
"no_whitelist_added": "Ingen hvidlister tilføjet",
"add_blocklist": "Tilføj sortliste",
"add_allowlist": "Tilføj hvidliste",
"cancel_btn": "Afbryd",
"enter_name_hint": "Angiv navn",
"enter_url_or_path_hint": "Angiv en URL eller en absolut listesti",
"check_updates_btn": "Søg efter opdateringer",
"new_blocklist": "Ny blokeringsliste",
"new_allowlist": "Ny liste over tilladte",
"edit_blocklist": "Rediger blokeringsliste",
"edit_allowlist": "Rediger liste over tilladte",
"choose_blocklist": "Vælg blokeringslister",
"choose_allowlist": "Vælg lister over tilladte",
"enter_valid_blocklist": "Indtast en gyldig URL til blokeringslisten.",
"enter_valid_allowlist": "Indtast en gyldig URL til listen over tilladte.",
"new_blocklist": "Ny sortliste",
"new_allowlist": "Ny hvidliste",
"edit_blocklist": "Sortlisteredigering",
"edit_allowlist": "Hvidlisteredigering",
"choose_blocklist": "Vælg sortlister",
"choose_allowlist": "Vælg hvidlister",
"enter_valid_blocklist": "Angiv en gyldig URL til sortlisten.",
"enter_valid_allowlist": "Angiv en gyldig URL til hvidlisten.",
"form_error_url_format": "Ugyldigt URL-format",
"form_error_url_or_path_format": "Ugyldig URL eller en absolut sti på listen",
"custom_filter_rules": "Brugerdefinerede filtreringsregler",
"custom_filter_rules_hint": "Indtast en regel per linje. Du kan enten bruge annonceblokeringsregler eller værtsfilsyntaks.",
"form_error_url_or_path_format": "Ugyldig URL eller absolut listesti",
"custom_filter_rules": "Tilpassede filtreringsregler",
"custom_filter_rules_hint": "Angiv én regel pr. linje. Du kan bruge enten adblockingregler eller værtsfilsyntaks.",
"examples_title": "Eksempler",
"example_meaning_filter_block": "bloker adgang til domænet example.org og alle dens subdomæner",
"example_meaning_filter_whitelist": "fjern blokering af adgangen til domænet example.ord og alle dens subdomæner",
"example_meaning_host_block": "AdGuard Home vil nu returnere adressen 127.0.0.1 til example.org domænet (men ikke dens underdomæner)",
"example_comment": "! Her er en kommentar",
"example_meaning_filter_block": "blokér adgang til example.org-domænet samt alle underdomæner",
"example_meaning_filter_whitelist": "afblokér adgang til example.ord-domænet samt alle underdomæner",
"example_meaning_host_block": "AdGuard Home returnerer nu adressen 127.0.0.1 for example.org-domænet (men ikke underdomænerne).",
"example_comment": "! Hér angives en evt. kommentar",
"example_comment_meaning": "bare en kommentar",
"example_comment_hash": "# Også en kommentar",
"example_regex_meaning": "bloker adgangen til de domæner, der matcher det angivne regulære udtryk",
"example_regex_meaning": "blokér adgang til domæner matchernde det angivne regulære udtryk",
"example_upstream_regular": "almindelig DNS (over UDP)",
"example_upstream_dot": "krypteret <0>DNS-over-TLS</0>",
"example_upstream_doh": "krypteret <0>DNS-over-HTTPS</0>",
"example_upstream_doq": "krypteret <0>DNS-over-QUIC</0>",
"example_upstream_sdns": "du kan bruge <0>DNS Stamps<0> til <1>DNSCrypt>/1> eller <2>DNS-over-HTTPS</2> resolvers",
"example_upstream_sdns": "du kan bruge <0>DNS Stamps</0> til <1>DNSCrypt</1> eller <2>DNS-over-HTTPS</2>-resolvers",
"example_upstream_tcp": "almindelig DNS (over TCP)",
"all_lists_up_to_date_toast": "Alle lister er allerede opdaterede",
"updated_upstream_dns_toast": "Opdaterede upstream DNS-servere",
"dns_test_ok_toast": "De angivne DNS-servere fungerer korrekt",
"dns_test_not_ok_toast": "Server \"{{key}}\": kunne ikke bruges, kontroller venligst at du har skrevet det korrekt",
"unblock": "Fjern blokering",
"block": "Bloker",
"updated_upstream_dns_toast": "Opdaterede upstream DNS-serverene",
"dns_test_ok_toast": "Angivne DNS-servere fungerer korrekt",
"dns_test_not_ok_toast": "Server \"{{key}}\": Kunne ikke bruges. Tjek, at du har angivet den korrekt",
"unblock": "Afblokering",
"block": "Blokering",
"disallow_this_client": "Afvis denne klient",
"allow_this_client": "Tillad denne klient",
"block_for_this_client_only": "Bloker kun for denne klient",
"unblock_for_this_client_only": "Fjern blokering kun for denne klient",
"block_for_this_client_only": "Blokér kun for denne klient",
"unblock_for_this_client_only": "Afblokér kun for denne klient",
"time_table_header": "Tid",
"date": "Dato",
"domain_name_table_header": "Domænenavn",
@@ -210,36 +210,36 @@
"response_table_header": "Svar",
"response_code": "Responskode",
"client_table_header": "Klient",
"empty_response_status": "Tom",
"empty_response_status": "Tomt",
"show_all_filter_type": "Vis alle",
"show_filtered_type": "Vis filtrerede",
"no_logs_found": "Ingen logfiler fundet",
"refresh_btn": "Opdater",
"previous_btn": "Forrige",
"no_logs_found": "Ingen logger fundet",
"refresh_btn": "Opdatér",
"previous_btn": "Foregående",
"next_btn": "Næste",
"loading_table_status": "Indlæser...",
"page_table_footer_text": "Side",
"rows_table_footer_text": "rækker",
"updated_custom_filtering_toast": "De brugerdefinerede filtreringsregler er blevet opdateret",
"rule_removed_from_custom_filtering_toast": "Regel fjernet fra de brugerdefinerede filtreringsregler: {{rule}}",
"rule_added_to_custom_filtering_toast": "Regel tilføjet til de brugerdefinerede filtreringsregler: {{rule}}",
"updated_custom_filtering_toast": "Tilpassede filtreringsregler er nu opdateret",
"rule_removed_from_custom_filtering_toast": "Regel fjernet fra de tilpassede filtreringsregler: {{rule}}",
"rule_added_to_custom_filtering_toast": "Regel føjet til de tilpassede filtreringsregler: {{rule}}",
"query_log_response_status": "Status: {{value}}",
"query_log_filtered": "Filtreret af {{filter}}",
"query_log_confirm_clear": "Er du sikker på, at du vil rydde hele forespørgselsloggen?",
"query_log_confirm_clear": "Sikker på, at du vil rydde hele forespørgselsloggen?",
"query_log_cleared": "Forespørgselsloggen er blevet ryddet",
"query_log_updated": "Forespørgselsloggen er blevet opdateret",
"query_log_clear": "Ryd forespørgselslogfiler",
"query_log_retention": "Opbevaring af forespørgselslogfiler",
"query_log_retention": "Opbevar forespørgselslogger i",
"query_log_enable": "Aktivér log",
"query_log_configuration": "Konfiguration af logfiler",
"query_log_disabled": "Forespørgselsloggen er deaktiveret og kan konfigureres i <0>indstillinger</0>",
"query_log_strict_search": "Brug dobbelt anførselstegn til streng søgning",
"query_log_retention_confirm": "Er du sikker på, at du vil ændre opbevaring af forespørgselsloggen? Hvis du mindsker intervalværdien, vil nogle data gå tabt",
"anonymize_client_ip": "Anonymiser klient-IP",
"query_log_configuration": "Opsætning af logger",
"query_log_disabled": "Forespørgselsloggen er deaktiveret og kan opsættes i <0>indstillingerne</0>",
"query_log_strict_search": "Brug dobbelt anførselstegn til stringent søgning",
"query_log_retention_confirm": "Sikker på, at du vil ændre forespørgselsloggens opbevaringperiode? Mindskes intervalværdien, mistes data",
"anonymize_client_ip": "Anonymisér klient-IP",
"anonymize_client_ip_desc": "Gem ikke klientens fulde IP-adresse i logfiler og statistikker",
"dns_config": "DNS-serverkonfiguration",
"dns_cache_config": "Konfiguration af DNS-cache",
"dns_cache_config_desc": "Her kan du konfigurere DNS-cache",
"dns_config": "DNS-serveropsætning",
"dns_cache_config": "DNS-cacheopsætning",
"dns_cache_config_desc": "Hér kan du opsætte DNS-cache",
"blocking_mode": "Blokeringstilstand",
"default": "Standard",
"nxdomain": "NXDOMAIN",
@@ -253,108 +253,108 @@
"dns_over_tls": "DNS-over-TLS",
"dns_over_quic": "DNS-over-Quic",
"client_id": "Klient-ID",
"client_id_placeholder": "Indtast klient-ID",
"client_id_desc": "Forskellige klienter kan identificeres ved hjælp af et specielt klient-ID. <a>Her</a> kan du lære mere om, hvordan du identificerer klienter.",
"client_id_placeholder": "Angiv klient-ID",
"client_id_desc": "Forskellige klienter kan identificeres via et specielt klient-ID. <a>Hér</a> finder du ud af mere om, hvordan klienter identificeres.",
"download_mobileconfig_doh": "Download .mobileconfig til DNS-over-HTTPS",
"download_mobileconfig_dot": "Download .mobileconfig til DNS-over-TLS",
"download_mobileconfig": "Download konfigurationsfil",
"download_mobileconfig": "Download opsætningsfil",
"plain_dns": "Almindelig DNS",
"form_enter_rate_limit": "Indtast hyppighedsgrænse",
"form_enter_rate_limit": "Angiv hyppighedsgrænse",
"rate_limit": "Hyppighedsgrænse",
"edns_enable": "Aktiver EDNS Client Subnet",
"edns_cs_desc": "Hvis det er aktiveret, vil AdGuard Home sende klienters subnets til DNS-serverne.",
"rate_limit_desc": "Antallet af anmodninger pr. sekund, som en enkelt klient får lov til at fremsætte (indstilles den til 0 betyder det ubegrænset)",
"blocking_ipv4_desc": "IP-adresse, der skal returneres for en blokeret A-anmodning",
"blocking_ipv6_desc": "IP-adresse, der skal returneres for en blokeret AAAA-anmodning",
"blocking_mode_default": "Standard: Svar med nul IP-adresse (0.0.0.0 for A; :: for AAAA), når den blokeres af Adblock-stil-reglen; svar med den IP-adresse, der er angivet i reglen, når den blokeres af /etc/hosts-style-reglen",
"blocking_mode_refused": "REFUSED: Svar med en REFUSED kode",
"edns_enable": "Aktivér EDNS-klientundernet",
"edns_cs_desc": "Hvis aktiveret, sender AdGuard Home klienters undernet til DNS-serverne.",
"rate_limit_desc": "Antallet af forespørgsler pr. sekund tilladt pr. klient (værdien 0 = ubegrænset)",
"blocking_ipv4_desc": "Returneret IP-adresse for en blokeret A-forespørgsel",
"blocking_ipv6_desc": "Returneret IP-adresse for en blokeret AAAA-forespørgsel",
"blocking_mode_default": "Standard: Svar med nul IP-adresse (0.0.0.0 for A; :: for AAAA), når blokeret af Adblock-lignende regel. Svar med IP-adressen angivet i reglen, når blokeret af /etc/hosts-lignende regel",
"blocking_mode_refused": "NÆGTET: Svar med en NÆGTET-kode",
"blocking_mode_nxdomain": "NXDOMAIN: Svar med NXDOMAIN-kode",
"blocking_mode_null_ip": "Null IP: Svar med nul IP-adresse (0.0.0.0 for A; :: for AAAA)",
"blocking_mode_custom_ip": "Brugerdefineret IP: Svar med en manuelt indstillet IP-adresse",
"upstream_dns_client_desc": "Hvis du lader dette felt være tomt, vil AdGuard Home bruge de servere, der er konfigureret i <0>DNS-indstillingerne</0>.",
"blocking_mode_custom_ip": "Tilpasset IP: Svar med en manuelt indstillet IP-adresse",
"upstream_dns_client_desc": "Holdes dette felt tomt, bruger AdGuard Home de i <0>DNS-indstillingerne</0> opsatte servere.",
"tracker_source": "Tracker-kilde",
"source_label": "Kilde",
"found_in_known_domain_db": "Fundet i databasen med kendte domæner.",
"category_label": "Kategori",
"rule_label": "Regel(regler)",
"rule_label": "Regel/Regler",
"list_label": "Liste",
"unknown_filter": "Ukendt filter {{filterId}}",
"known_tracker": "Kendt tracker",
"install_welcome_title": "Velkommen til AdGuard Home!",
"install_welcome_desc": "AdGuard Home er en netværksbaseret annonce-og-tracker blokerende DNS-server. Formålet er at lade dig kontrollere hele dit netværk og alle dine enheder, og det kræver ikke at man bruger klientsoftware.",
"install_settings_title": "Administrator Webgrænseflade",
"install_welcome_desc": "AdGuard Home er en netværksbaseret tracker- og adblocking DNS-server, hvis formål er at lade dig kontrollere hele dit netværk og alle dine enheder, og det kræver ikke brug af klientsoftware.",
"install_settings_title": "Admin Webgrænseflade",
"install_settings_listen": "Overvågningsgrænseflade",
"install_settings_port": "Port",
"install_settings_interface_link": "Din AdGuard Home administrator webgrænseflade vil være tilgængelig på følgende adresser:",
"form_error_port": "Indtast gyldig portværdi",
"install_settings_interface_link": "Din AdGuard Home admin webgrænseflade vil være tilgængelig på flg. adresser:",
"form_error_port": "Angiv gyldig portværdi",
"install_settings_dns": "DNS-server",
"install_settings_dns_desc": "Du skal konfigurere dine enheder eller router til at bruge DNS-serveren på følgende adresser:",
"install_settings_dns_desc": "Du skal opsætte dine enheder eller router til at bruge DNS-serveren på flg. adresser:",
"install_settings_all_interfaces": "Alle grænseflader",
"install_auth_title": "Autentificering",
"install_auth_desc": "Det anbefales stærkt at konfigurere adgangskodeautentificering til din AdGuard Home administrator webgrænseflade. Selvom det kun er tilgængeligt på dit lokale netværk, er det stadig vigtigt at få det beskyttet mod ubegrænset adgang.",
"install_auth_title": "Godkendelse",
"install_auth_desc": "Det anbefales stærkt at opsætte adgangskodegodkendelse på din AdGuard Home admin webgrænseflade. Selvom den kun er tilgængelig på dit lokalnetværk, er det stadig vigtigt at få den beskyttet mod ubegrænset adgang.",
"install_auth_username": "Brugernavn",
"install_auth_password": "Adgangskode",
"install_auth_confirm": "Bekræft adgangskode",
"install_auth_username_enter": "Indtast brugernavn",
"install_auth_password_enter": "Indtast adgangskode",
"install_auth_username_enter": "Angiv brugernavn",
"install_auth_password_enter": "Angiv adgangskode",
"install_step": "Trin",
"install_devices_title": "Konfigurer dine enheder",
"install_devices_desc": "For at kunne bruge AdGuard Home, skal du konfigurere dine enheder til at bruge den.",
"install_devices_title": "Opsæt dine enheder",
"install_devices_desc": "For brug af AdGuard Home, skal dine enheder opsættes til at bruge den.",
"install_submit_title": "Tillykke!",
"install_submit_desc": "Installationsproceduren er færdig, og du er klar til at starte med at bruge AdGuard Home.",
"install_submit_desc": "Opsætningsproceduren er færdig, og AdGuard Home er nu klar til brug.",
"install_devices_router": "Router",
"install_devices_router_desc": "Denne opsætning dækker automatisk alle enheder, der er tilsluttet din hjemmerouter, og du behøver ikke konfigurere hver af dem manuelt.",
"install_devices_address": "AdGuard Home DNS-server lytter på følgende adresser",
"install_devices_router_list_1": "Åbn præferencerne for din router. Normalt kan du få adgang til den fra din browser via en URL (som http://192.168.0.1/ eller http://192.168.1.1/). Du bliver muligvis bedt om at indtaste adgangskoden. Hvis du ikke kan huske den, kan du ofte nulstille adgangskoden ved at trykke på en knap på selve routeren. Nogle routere kræver et bestemt program, som i det tilfælde allerede skulle være installeret på din computer/telefon.",
"install_devices_router_list_2": "Find DHCP/DNS-indstillingerne. Kig efter DNS-bogstaverne ved siden af et felt, der tillader to eller tre sæt tal, hver opdelt i fire grupper med et til tre cifre.",
"install_devices_router_list_3": "Indtast dine AdGuard Home serveradresser der.",
"install_devices_router_list_4": "Du kan ikke opsætte en tilpasset DNS-server på nogle typer routere. I dette tilfælde kan det hjælpe, hvis du konfigurerer AdGuard Home som en <0>DHCP-server</0>. Du kan ellers søge efter manualen om, hvordan du tilpasser DNS-servere til din bestemte routermodel.",
"install_devices_windows_list_1": "Åbn Kontrolpanel gennem menuen Start eller Windows søgning.",
"install_devices_windows_list_2": "Gå til Netværk og internet kategorien og derefter til Netværks- og delingscenter.",
"install_devices_windows_list_3": " venstre side af skærmen finder du Skift adapterindstillinger og klik på den.",
"install_devices_router_desc": "Denne opsætning dækker automatisk alle enheder tilsluttet din hjemmerouter, og du behøver ikke at skulle opsætte hver af dem manuelt.",
"install_devices_address": "AdGuard Home DNS-server lytter på flg. adresser",
"install_devices_router_list_1": "Åbn præferencerne for din router. Normalt kan du tilgå disse fra din browser via en URL (som http://192.168.0.1/ eller http://192.168.1.1/). Du anmodes muligvis om at angive en adgangskoden. Kan du ikke huske den, kan du ofte nulstille adgangskoden ved hjælp af en knap på selve routeren, men vær opmærksom på, at vælges denne procedure, mister du sandsynligvis hele routerkonfigureringen. Visse routere kræver et bestemt program, der i så tilfælde allerede skulle være installeret på din computer/mobil.",
"install_devices_router_list_2": "Find DHCP-/DNS-indstillingerne. Kig efter DNS-bogstaverne ved siden af et felt, der tillader input af to eller tre sæt tal, hver opdelt i fire grupper med et til tre cifre.",
"install_devices_router_list_3": "Angiv dine AdGuard Home-serveradresser dér.",
"install_devices_router_list_4": "På visse routertyper kan en tilpasset DNS-server ikke opsættes. I så tilfælde kan det hjælpe, hvis du opsætter AdGuard Home som en <0>DHCP-server</0>. Ellers bør du tjekke i routermanualen, hvordan du tilpasser DNS-servere i din givne routermodel.",
"install_devices_windows_list_1": "Åbn Kontrolpanel via menuen Start eller Windows-søgning.",
"install_devices_windows_list_2": "Gå til kategorien Netværk og Internet og derefter til Netværks- og delingscenter.",
"install_devices_windows_list_3": "Til venstre finder du punktet Skift adapterindstillinger, klik på dette.",
"install_devices_windows_list_4": "Vælg din aktive forbindelse, højreklik på den og vælg Egenskaber.",
"install_devices_windows_list_5": "Find Internet Protocol Version 4 (TCP/IP) på listen, vælg den og klik derefter på Egenskaber igen.",
"install_devices_windows_list_6": "Vælg Brug følgende DNS-serveradresser og indtast dine AdGuard Home serveradresser.",
"install_devices_windows_list_6": "Vælg Brug følgende DNS-serveradresser og angiv dine AdGuard Home-serveradresser.",
"install_devices_macos_list_1": "Klik på Apple-ikonet og gå til Systemindstillinger.",
"install_devices_macos_list_2": "Klik på Netværk.",
"install_devices_macos_list_3": "Vælg den første forbindelse på din liste, og klik på Avanceret.",
"install_devices_macos_list_4": "Vælg fanen DNS og indtast dine AdGuard Home serveradresser.",
"install_devices_macos_list_4": "Vælg fanen DNS og angiv dine AdGuard Home-serveradresser.",
"install_devices_android_list_1": "Tryk på Indstillinger på Android-startskærmen.",
"install_devices_android_list_2": "Tryk på Wi-Fi i menuen. Alle tilgængelige netværk vil blive vist på skærmen (det er umuligt at angive brugerdefineret DNS til mobilforbindelse).",
"install_devices_android_list_3": "Tryk lang tid på det netværk, du har forbindelse til, og tryk på Rediger Netværk.",
"install_devices_android_list_4": "På nogle enheder skal du muligvis afkrydse afkrydsningsfeltet Avanceret for at se yderligere indstillinger. For at justere dine Android DNS-indstillinger skal du skifte IP-indstillingerne fra DHCP til Statisk.",
"install_devices_android_list_5": "Skift sæt DNS 1 og DNS 2 værdierne til dine AdGuard Home serveradresser.",
"install_devices_ios_list_1": "Tryk på Indstillinger på startskærmen.",
"install_devices_ios_list_2": "Vælg Wi-Fi i menuen til venstre (det er umuligt at konfigurere DNS til mobilnetværker).",
"install_devices_ios_list_3": "Tryk på navnet på det nuværende aktive netværk.",
"install_devices_ios_list_4": "Indtast dine AdGuard Home serveradresser i DNS-feltet.",
"get_started": "Kom I Gang",
"install_devices_android_list_2": "Tryk på Wi-Fi i menuen. Alle tilgængelige netværk vises på skærmen (det er umuligt at angive tilpasset DNS til mobilforbindelse).",
"install_devices_android_list_3": "Langt tryk på det netværk, du er forbundet til, og tryk på Redigér Netværk.",
"install_devices_android_list_4": "På visse enheder skal du muligvis afkrydse feltet Avanceret for at se yderligere indstillinger. For at ændre dine Android DNS-indstillinger skal du skifte IP-indstillingerne fra DHCP til Statisk.",
"install_devices_android_list_5": "Skift de aktuelle DNS 1- og DNS 2-værdier til dine AdGuard Home-serveradresser.",
"install_devices_ios_list_1": "Tryk på Indstillinger på Hjem-skærmen.",
"install_devices_ios_list_2": "Vælg Wi-Fi i menuen til venstre (det er umuligt at opsætte DNS for mobilnetværker).",
"install_devices_ios_list_3": "Tryk på navnet på det aktuelt aktive netværk.",
"install_devices_ios_list_4": "Angiv dine AdGuard Home-serveradresser i DNS-feltet.",
"get_started": "Komme I Gang",
"next": "Næste",
"open_dashboard": "Åbn Dashboard",
"install_saved": "Succesfuldt gemt",
"install_saved": "Gemt",
"encryption_title": "Kryptering",
"encryption_desc": "Kryptering (HTTPS/TLS) understøtter både DNS og admin webgrænseflade",
"encryption_config_saved": "Krypteringskonfiguration gemt",
"encryption_config_saved": "Krypteringsopsætning gemt",
"encryption_server": "Servernavn",
"encryption_server_enter": "Indtast dit domænenavn",
"encryption_server_desc": "For at kunne bruge HTTPS skal du indtaste det servernavn, der matcher dit SSL-certifikat eller wildcard-certifikat. Hvis feltet ikke er indstillet, accepterer det TLS-forbindelser til ethvert domæne.",
"encryption_redirect": "Omdiriger automatisk til HTTPS",
"encryption_redirect_desc": "Hvis afkrydset, vil AdGuard Home automatisk omdirigere dig fra HTTP til HTTPS-adresser.",
"encryption_server_enter": "Angiv dit domænenavn",
"encryption_server_desc": "For at kunne bruge HTTPS skal du angive det servernavn, der matcher dit SSL-certifikat eller wildcard-certifikat. Er feltet ikke er opsat, accepterer det TLS-forbindelser til ethvert domæne.",
"encryption_redirect": "Omdirigér automatisk til HTTPS",
"encryption_redirect_desc": "Hvis afkrydset, omdirigerer AdGuard Home dig automatisk fra HTTP- til HTTPS-adresser.",
"encryption_https": "HTTPS-port",
"encryption_https_desc": "Hvis HTTPS-porten er konfigureret, vil AdGuard Home admin grænsefladen være tilgængelig via HTTPS, og den vil give DNS-over-HTTPS på '/dns-query' placeringen.",
"encryption_https_desc": "Er HTTPS-porten opsat, vil AdGuard Home admin grænsefladen være tilgængelig via HTTPS, og den vil muliggøre DNS-over-HTTPS på '/dns-query' placeringen.",
"encryption_dot": "DNS-over-TLS port",
"encryption_dot_desc": "Hvis denne port er konfigureret, vil AdGuard Home køre en DNS-over-TLS server over denne port.",
"encryption_dot_desc": "Er denne port opsat, vil AdGuard Home køre en DNS-over-TLS server denne port.",
"encryption_doq": "DNS-over-QUIC port",
"encryption_doq_desc": "Hvis denne port er konfigureret, vil AdGuard Home køre en DNS-over-QUIC server på denne port. Den er eksperimentel og er måske ikke pålidelig. Der er heller ikke mange klienter, der understøtter den i øjeblikket.",
"encryption_doq_desc": "Er denne port opsat, vil AdGuard Home køre en DNS-over-QUIC server på denne port. Den er eksperimentel og er måske ikke pålidelig. Derudover understøttes den pt. heller ikke af ret mange klienter.",
"encryption_certificates": "Certifikater",
"encryption_certificates_desc": "For at kunne bruge kryptering skal du angive en gyldig SSL-certifikatkæde til dit domæne. Du kan få et gratis certifikat <0>{{link}}</ 0> eller du kan købe det fra en af de pålidelige Certifikatmyndigheder.",
"encryption_certificates_input": "Kopier/indsæt dine PEM-kodede certifikater her.",
"encryption_certificates_desc": "For at kunne bruge kryptering skal du angive en gyldig SSL-certifikatkæde til dit domæne. Du kan få et gratis certifikat via <0>{{link}}</ 0>, eller du kan købe det via en af de betroede Certifikatmyndigheder.",
"encryption_certificates_input": "Kopiér/indsæt dine PEM-kodede certifikater hér.",
"encryption_status": "Status",
"encryption_expire": "Udløber",
"encryption_key": "Privat nøgle",
"encryption_key_input": "Kopier/indsæt dine PEM-kodede private nøgle til dit certifikat her.",
"encryption_enable": "Aktiver Kryptering (HTTPS, DNS-over-HTTPS og DNS-over-TLS)",
"encryption_enable_desc": "Hvis kryptering er aktiveret, vil AdGuard Home admin grænseflade fungere over HTTPS og DNS-serveren vil lytte efter forespørgsler via DNS-over-HTTPS og DNS-over-TLS.",
"encryption_key_input": "Kopiér/indsæt dine PEM-kodede private nøgle til dit certifikat hér.",
"encryption_enable": "Aktivér Kryptering (HTTPS, DNS-over-HTTPS og DNS-over-TLS)",
"encryption_enable_desc": "Er kryptering aktiveret, fungerer AdGuard Home admin grænsefladen over HTTPS, og DNS-serveren lytter efter forespørgsler via DNS-over-HTTPS og DNS-over-TLS.",
"encryption_chain_valid": "Certifikatkæden er gyldig",
"encryption_chain_invalid": "Certifikatkæden er ugyldig",
"encryption_key_valid": "Dette er en gyldig {{type}} privat nøgle",
@@ -362,114 +362,114 @@
"encryption_subject": "Emne",
"encryption_issuer": "Udsteder",
"encryption_hostnames": "Værtsnavne",
"encryption_reset": "Er du sikker på, at du vil nulstille krypteringsindstillingerne?",
"topline_expiring_certificate": "Dit SSL-certifikat er ved at udløbe. Opdater <0>Krypteringsindstillinger</ 0>.",
"topline_expired_certificate": "Dit SSL-certifikat er udløbet. Opdater <0>Krypteringsindstillinger</ 0>.",
"form_error_port_range": "Indtast portværdi i intervallet 80-65535",
"encryption_reset": "Sikker på, at du vil nulstille krypteringsindstillingerne?",
"topline_expiring_certificate": "Dit SSL-certifikat er ved at udløbe. Opdatér <0>Krypteringsindstillinger</0>.",
"topline_expired_certificate": "Dit SSL-certifikat er udløbet. Opdatér <0>Krypteringsindstillinger</0>.",
"form_error_port_range": "Angiv portnummer i intervallet 80-65535",
"form_error_port_unsafe": "Dette er en usikker port",
"form_error_equal": "Burde ikke være lige",
"form_error_equal": " ikke være ens",
"form_error_password": "Adgangskoden matcher ikke",
"reset_settings": "Nulstil indstillinger",
"update_announcement": "AdGuard Home {{version}} er nu tilgængelig! <0>Kik her</0> for mere info.",
"update_announcement": "AdGuard Home {{version}} er nu tilgængelig! <0>Kik hér</0> for mere info.",
"setup_guide": "Installationsvejledning",
"dns_addresses": "DNS-adresser",
"dns_start": "DNS-server starter",
"dns_status_error": "Fejl ved at få DNS-serverstatus",
"dns_status_error": "Fejl ved tjek af DNS-serverstatus",
"down": "Ned",
"fix": "Reparer",
"fix": "Korrigér",
"dns_providers": "Her er en <0>liste over kendte DNS-udbydere</ 0> at vælge imellem.",
"update_now": "Opdater nu",
"update_failed": "Automatisk opdatering mislykkedes. Følg <a>disse trin</a> for at opdatere manuelt.",
"update_now": "Opdatér nu",
"update_failed": "Autoopdatering mislykkedes. Følg <a>disse trin</a> for at opdatere manuelt.",
"processing_update": "Vent venligst, AdGuard Home bliver opdateret",
"clients_title": "Klienter",
"clients_desc": "Konfigurer enheder, der er forbundet til AdGuard Home",
"clients_desc": "Opsæt enheder forbundet til AdGuard Home",
"settings_global": "Global",
"settings_custom": "Brugerdefineret",
"settings_custom": "Tilpasset",
"table_client": "Klient",
"table_name": "Navn",
"save_btn": "Gem",
"client_add": "Tilføj Klient",
"client_new": "Ny Klient",
"client_edit": "Rediger Klient",
"client_edit": "Redigér Klient",
"client_identifier": "Identifikator",
"ip_address": "IP-adresse",
"client_identifier_desc": "Klienter kan identificeres ud fra IP-adressen, CIDR eller MAC-adressen eller et specielt klient-ID (kan bruges til DoT/DoH/DoQ). <0>Her</0> kan du lære mere om, hvordan du identificerer klienter.",
"form_enter_ip": "Indtast IP",
"form_enter_mac": "Indtast MAC",
"form_enter_id": "Indtast identifikator",
"client_identifier_desc": "Klienter kan identificeres ud fra IP-adressen, CIDR eller MAC-adressen eller et specielt klient-ID (kan bruges til DoT/DoH/DoQ). <0>Hér</0> kan du læse mere om, hvordan klienter identificeres.",
"form_enter_ip": "Angiv IP",
"form_enter_mac": "Angiv MAC",
"form_enter_id": "Angiv identifikator",
"form_add_id": "Tilføj identifikator",
"form_client_name": "Indtast klientnavn",
"form_client_name": "Angiv klientnavn",
"name": "Navn",
"client_global_settings": "Brug globale indstillinger",
"client_deleted": "Klient \"{{key}}\" succesfuldt slettet",
"client_added": "Klient \"{{key}}\" succesfuldt tilføjet",
"client_updated": "Klient \"{{key}}\" succesfuldt opdateret",
"client_deleted": "Klient \"{{key}}\" slettet",
"client_added": "Klient \"{{key}}\" tilføjet",
"client_updated": "Klient \"{{key}}\" opdateret",
"clients_not_found": "Ingen klienter fundet",
"client_confirm_delete": "Er du sikker på, at du vil slette klient \"{{key}}\"?",
"list_confirm_delete": "Er du sikker på, at du vil slette denne liste?",
"client_confirm_delete": "Sikker på, at du vil slette klient \"{{key}}\"?",
"list_confirm_delete": "Sikker på, at du vil slette denne liste?",
"auto_clients_title": "Klienter (runtime)",
"auto_clients_desc": "Data om de klienter, der bruger AdGuard Home, men ikke gemt i konfigurationen",
"auto_clients_desc": "Data om de klienter, som bruger AdGuard Home, men ikke gemt i opsætningen",
"access_title": "Adgangsindstillinger",
"access_desc": "Her kan du konfigurere adgangsregler for AdGuard Home DNS-serveren.",
"access_desc": "Her kan du opsætte adgangsregler for AdGuard Home DNS-serveren.",
"access_allowed_title": "Tilladte klienter",
"access_allowed_desc": "En liste over CIDR- eller IP-adresser. Hvis den er konfigureret, vil AdGuard Home kun acceptere anmodninger fra disse IP-adresser.",
"access_allowed_desc": "En liste over CIDR- eller IP-adresser. Hvis opsat, accepterer AdGuard Home kun forespørgsler fra disse IP-adresser.",
"access_disallowed_title": "Ikke tilladte klienter",
"access_disallowed_desc": "En liste over CIDR- eller IP-adresser. Hvis den er konfigureret, vil AdGuard Home droppe anmodninger fra disse IP-adresser.",
"access_disallowed_desc": "En liste over CIDR- eller IP-adresser. Hvis opsat, dropper AdGuard Home forespørgsler fra disse IP-adresser.",
"access_blocked_title": "Ikke tilladte domæner",
"access_blocked_desc": "Forveksl det ikke med filtre. AdGuard Home vil droppe DNS-forespørgsler for disse domæner i forespørgselsspørgsmål. Her kan du specificere de nøjagtige domænenavne, wildcards og urlfilter-regler, f.eks. 'example.org', '*.example.org' eller '||example.org^'.",
"access_settings_saved": "Adgangsindstillinger succesfuldt gemt",
"updates_checked": "Søgt succesfuldt efter opdateringer",
"access_settings_saved": "Adgangsindstillinger gemt",
"updates_checked": "Opdateringstjek foretaget",
"updates_version_equal": "AdGuard Home er opdateret",
"check_updates_now": "Søg efter opdateringer nu",
"dns_privacy": "DNS Privatliv",
"dns_privacy": "DNS-fortrolighed",
"setup_dns_privacy_1": "<0>DNS-over-TLS:</0> Brug <1>{{address}}</1> streng.",
"setup_dns_privacy_2": "<0>DNS-over-HTTPS:</0> Brug <1>{{address}}</1> streng.",
"setup_dns_privacy_3": "<0>Her er en liste over software, du kan bruge.</0>",
"setup_dns_privacy_4": "På en iOS 14 eller macOS Big Sur-enhed kan du downloade en særlig '.mobileconfig' -fil, der tilføjer <highlight>DNS-over-HTTPS</highlight> eller <highlight>DNS-over-TLS</highlight> servere til DNS-indstillingerne.",
"setup_dns_privacy_android_1": "Android 9 understøtter den indbyggede DNS-over-TLS. For at konfigurere den, gå til Indstillinger → Netværk & internet → Avanceret → Privat DNS og indtast dit domænenavn.",
"setup_dns_privacy_4": "På en iOS 14- eller macOS Big Sur-enhed kan du downloade en særlig '.mobileconfig' -fil, der føjer <highlight>DNS-over-HTTPS</highlight> eller <highlight>DNS-over-TLS</highlight> servere til DNS-indstillingerne.",
"setup_dns_privacy_android_1": "Android 9 har indbygget understøttelse af DNS-over-TLS. For at opsætte den, gå til Indstillinger → Netværk og Internet → Avanceret → Privat DNS og angiv dit domænenavn.",
"setup_dns_privacy_android_2": "<0>AdGuard til Android</0> understøtter <1>DNS-over-HTTPS</1> og <1>DNS-over-TLS</1>.",
"setup_dns_privacy_android_3": "<0>Intra</0> tilføjer <1>DNS-over-HTTPS</1> understøttelse til Android.",
"setup_dns_privacy_ios_1": "<0>DNSCloak</0> understøtter <1>DNS-over-HTTPS</1>, men for at konfigurere den, så den bruger din egen server, skal du generere et <2>DNS Stamp</2> til den.",
"setup_dns_privacy_ios_2": "<0>AdGuard til iOS</0> understøtter <1>DNS-over-HTTPS</1> og <1>DNS-over-TLS</1> installation.",
"setup_dns_privacy_android_3": "<0>Intra</0> føjer <1>DNS-over-HTTPS</1> understøttelse til Android.",
"setup_dns_privacy_ios_1": "<0>DNSCloak</0> understøtter <1>DNS-over-HTTPS</1>, men for at opsætte den til brug af din egen server, skal du generere et <2>DNS Stamp</2> til den.",
"setup_dns_privacy_ios_2": "<0>AdGuard til iOS</0> understøtter <1>DNS-over-HTTPS</1> og <1>DNS-over-TLS</1> opsætning.",
"setup_dns_privacy_other_title": "Andre implementeringer",
"setup_dns_privacy_other_1": "AdGuard Home kan være en sikker DNS-klient på enhver platform.",
"setup_dns_privacy_other_2": "<0>dnsproxy</0> understøtter alle kendte sikre DNS-protokoller.",
"setup_dns_privacy_other_3": "<0>dnscrypt-proxy</0> understøtter <1>DNS-over-HTTPS</1>.",
"setup_dns_privacy_other_4": "<0>Mozilla Firefox</0> understøtter <1>DNS-over-HTTPS</1>.",
"setup_dns_privacy_other_5": "Du kan finde flere implementeringer <0>her</0> og <1>her</1>.",
"setup_dns_privacy_ioc_mac": "iOS- og macOS-konfiguration",
"setup_dns_notice": "For at kunne bruge <1>DNS-over-HTTPS</1> eller <1>DNS-over-TLS</1>, skal du <0>konfigurere Krypteringen</0> i indstillingerne i AdGuard Home.",
"setup_dns_privacy_other_5": "Du kan finde flere implementeringer <0>hér</0> og <1>hér</1>.",
"setup_dns_privacy_ioc_mac": "iOS- og macOS-opsætning",
"setup_dns_notice": "For at kunne bruge <1>DNS-over-HTTPS</1> eller <1>DNS-over-TLS</1>, skal du <0>opsætte Krypteringen</0> i AdGuard Homes indstillinger.",
"rewrite_added": "DNS-omskrivning for \"{{key}}\" blev tilføjet",
"rewrite_deleted": "DNS-omskrivning for \"{{key}}\" blev slettet",
"rewrite_add": "Tilføj DNS-omskrivning",
"rewrite_not_found": "Ingen DNS-omskrivninger fundet",
"rewrite_confirm_delete": "Er du sikker på, at du vil slette DNS-omskrivning for \"{{key}}\"?",
"rewrite_desc": "Gør det nemt at konfigurere det tilpassede DNS-svar for et specifikt domænenavn.",
"rewrite_applied": "Anvendt omskrivningsregel",
"rewrite_confirm_delete": "Sikker på, at du vil slette DNS-omskrivning for \"{{key}}\"?",
"rewrite_desc": "Gør det nemt at opsætte det tilpassede DNS-svar for et specifikt domænenavn.",
"rewrite_applied": "Omskrivningsregel effektueret",
"rewrite_hosts_applied": "Omskrevet af værtsfilreglen",
"dns_rewrites": "DNS-omskrivninger",
"form_domain": "Indtast domænenavn eller wildcard",
"form_answer": "Indtast IP-adresser eller domænenavne",
"form_domain": "Angiv domænenavn eller jokertegn",
"form_answer": "Angiv IP-adresse eller domænenavn",
"form_error_domain_format": "Ugyldigt domæneformat",
"form_error_answer_format": "Ugyldigt svarformat",
"configure": "Konfigurer",
"configure": "Opsæt",
"main_settings": "Hovedindstillinger",
"block_services": "Bloker specifikke tjenester",
"block_services": "Blokere specifikke tjenester",
"blocked_services": "Blokerede tjenester",
"blocked_services_desc": "Gør det muligt hurtigt at blokere populære websteder og tjenester.",
"blocked_services_saved": "Blokerede tjenester er gemt",
"blocked_services_global": "Brug globale blokerede tjenester",
"blocked_service": "Blokeret tjeneste",
"block_all": "Bloker alle",
"unblock_all": "Fjern blokering af alle",
"block_all": "Blokér alle",
"unblock_all": "Afblokér alle",
"encryption_certificate_path": "Certifikatsti",
"encryption_private_key_path": "Placering af den private nøgle",
"encryption_certificates_source_path": "Indstil en sti for certifikatfilen",
"encryption_private_key_path": "Private nøgle-sti",
"encryption_certificates_source_path": "Opsæt en certifikatfilsti",
"encryption_certificates_source_content": "Indsæt certifikatets indhold",
"encryption_key_source_path": "Indstil en fil for den private nøgle",
"encryption_key_source_path": "Opsæt en private nøgle-fil",
"encryption_key_source_content": "Indsæt indholdet af den private nøgle",
"stats_params": "Konfiguration af statistik",
"config_successfully_saved": "Konfiguration er gemt",
"stats_params": "Statistikopsætning",
"config_successfully_saved": "Opsætning er gemt",
"interval_24_hour": "24 timer",
"interval_days": "{{count}} dag",
"interval_days_plural": "{{count}} dage",
@@ -478,114 +478,114 @@
"filter_added_successfully": "Listen er tilføjet",
"filter_removed_successfully": "Listen er blevet fjernet",
"filter_updated": "Listen er blevet opdateret",
"statistics_configuration": "Konfiguration af statistik",
"statistics_retention": "Tilbageholdelse af statistikker",
"statistics_retention_desc": "Hvis du mindsker intervalværdien, vil nogle data gå tabt",
"statistics_configuration": "Statistikopsætning",
"statistics_retention": "Statistikbevarelse",
"statistics_retention_desc": "Mindskes intervalværdien, vil nogle data gå tabt",
"statistics_clear": " Ryd statistikker",
"statistics_clear_confirm": "Er du sikker på, at du vil slette statistikkerne?",
"statistics_retention_confirm": "Er du sikker på, at du vil ændre opbevaring af statistikker? Hvis du mindsker intervalværdien, vil nogle data gå tabt",
"statistics_cleared": "Statistikkerne blev slettet",
"statistics_clear_confirm": "Sikker på, at du vil slette statistikkerne?",
"statistics_retention_confirm": "Sikker på, at du vil ændre på statistikbevaring? Mindskes intervalværdien, vil nogle data gå tabt",
"statistics_cleared": "Statistikkerne er ryddet",
"interval_hours": "{{count}} time",
"interval_hours_plural": "{{count}} timer",
"filters_configuration": "Konfiguration af filtre",
"filters_configuration": "Filteropsætninger",
"filters_enable": "Aktivér filtre",
"filters_interval": "Filtrenes opdateringsinterval",
"disabled": "Deaktiveret",
"username_label": "Brugernavn",
"username_placeholder": "Indtast brugernavn",
"username_placeholder": "Angiv brugernavn",
"password_label": "Adgangskode",
"password_placeholder": "Indtast adgangskode",
"password_placeholder": "Angiv adgangskode",
"sign_in": "Log ind",
"sign_out": "Log ud",
"forgot_password": "Glemt adgangskode?",
"forgot_password_desc": "Følg <0>disse trin</0> for at oprette en ny adgangskode til din brugerkonto.",
"location": "Placering",
"orgname": "Organisationens navn",
"orgname": "Organisationsnavn",
"netname": "Netværksnavn",
"network": "Netværk",
"descr": "Beskrivelse",
"whois": "Whois",
"filtering_rules_learn_more": "<0>Lær mere</0> om at oprette dine egne værtslister.",
"blocked_by_response": "Blokeret af CNAME eller IP som svar",
"filtering_rules_learn_more": "<0>Læs mere</0> om at oprette dine egne værtslister.",
"blocked_by_response": "Blokeret af CNAME eller IP i svar",
"blocked_by_cname_or_ip": "Blokeret af CNAME eller IP",
"try_again": "Prøv igen",
"domain_desc": "Indtast det domænenavn eller wildcard, du ønsker skal omskrives.",
"domain_desc": "Angiv domænenavnet eller jokertegnene, du ønsker omskrevet.",
"example_rewrite_domain": "omskriv kun svar for dette domænenavn.",
"example_rewrite_wildcard": "omskriv svar for alle <0>example.org</0> subdomæner.",
"rewrite_ip_address": "IP-adresse: brug denne IP i et A- eller AAAA-svar",
"rewrite_domain_name": "Domænenavn: tilføj en CNAME-post",
"rewrite_A": "<0>A</0>: særlig værdi, hold <0>A</0> poster fra upstream",
"rewrite_AAAA": "<0>AAAA</0>: særlig værdi, hold <0>AAAA</0> poster fra upstream",
"disable_ipv6": "Deaktiver IPv6",
"disable_ipv6_desc": "Hvis denne funktion er aktiveret, slettes alle DNS-forespørgsler til IPv6-adresser (type AAAA).",
"example_rewrite_wildcard": "omskriv svar for alle <0>example.org</0> underdomæner.",
"rewrite_ip_address": "IP-adresse: Brug denne IP i et A- eller AAAA-svar",
"rewrite_domain_name": "Domænenavn: Tilføj en CNAME-post",
"rewrite_A": "<0>A</0>: Særlig værdi, hold <0>A</0> poster fra upstream",
"rewrite_AAAA": "<0>AAAA</0>: Særlig værdi, hold <0>AAAA</0> poster fra upstream",
"disable_ipv6": "Deaktivér IPv6",
"disable_ipv6_desc": "Er denne funktion aktiveret, slettes alle DNS-forespørgsler til IPv6-adresser (type AAAA).",
"fastest_addr": "Hurtigste IP-adresse",
"fastest_addr_desc": "Forespørg alle DNS-servere, og returner den hurtigste IP-adresse blandt alle svar. Dette vil gøre DNS-forespørgslerne langsommere, da vi er nødt til at vente svar fra alle DNS-servere, men forbedrer samlet set forbindelsen.",
"autofix_warning_text": "Hvis du klikker på \"Reparer\", vil AdGuardHome konfigurere dit system til at bruge AdGuardHome DNS-server.",
"autofix_warning_list": "Den vil udføre disse opgaver: <0>Deaktivering af DNSStubListener systemet</0> <0>Indstille DNS-serveradressen til 127.0.0.1</0> <0>Erstatte det symbolske linkmål for /etc/resolv.conf til /run/systemd/resolve/resolv.conf</0> <0>Stop DNSStubListener (genindlæs systemd-løst tjeneste)</0>",
"autofix_warning_result": "Som et resultat behandles alle DNS-anmodninger fra dit system som standard af AdGuard Home.",
"fastest_addr_desc": "Forespørg alle DNS-servere og returner den hurtigste IP-adresse blandt alle svar. Dette vil gøre DNS-forespørgslerne langsommere, da der skal afventes svar fra alle DNS-servere, men forbedrer samlet set forbindelsen.",
"autofix_warning_text": "Klikker du på \"Reparér\", opsætter AdGuard Home dit system til brug med AdGuard Home DNS-server.",
"autofix_warning_list": "Den vil udføre disse opgaver: <0>Deaktivere system DNSStubListener</0> <0>Opsætte DNS-serveradressen til 127.0.0.1</0> <0>Erstatte symbolsk linkmål for /etc/resolv.conf med /run/systemd/resolve/resolv.conf</0> <0>Stoppe DNSStubListener (genindlæs systemd-opløst tjeneste)</0>",
"autofix_warning_result": "Det betyder, at alle DNS-forespørgsler fra dit system som standard behandles af AdGuard Home.",
"tags_title": "Tags",
"tags_desc": "Du kan vælge de tags, der svarer til klienten. Tags kan inkluderes i filtreringsreglerne og giver dig mulighed for at anvende dem mere nøjagtigt. <0>Læs mere</0>",
"tags_desc": "Du kan vælge de tags, som svarer til klienten. Tags kan inkluderes i filtreringsreglerne, hvilket lader anvende dem mere præcist. <0>Læs mere</0>",
"form_select_tags": "Vælg klient tags",
"check_title": "Kontroller filtreringen",
"check_desc": "Kontroller, om værtsnavnet er filtreret",
"check": "Kontroller",
"form_enter_host": "Indtast et værtsnavn",
"filtered_custom_rules": "Filtreret af brugerdefinerede filtreringsregler",
"check_title": "Tjek filtreringen",
"check_desc": "Tjek, om værtsnavnet er filtreret",
"check": "Tjek",
"form_enter_host": "Angiv et værtsnavn",
"filtered_custom_rules": "Filtreret af tilpassede filtreringsregler",
"choose_from_list": "Vælg fra listen",
"add_custom_list": "Tilføj en tilpasset liste",
"host_whitelisted": "Værten er hvidlistet",
"check_ip": "IP-adresser: {{ip}}",
"check_cname": "CNAME: {{cname}}",
"check_reason": "Årsag: {{reason}}",
"check_service": "Servicenavn: {{service}}",
"service_name": "Navn på tjeneste",
"check_service": "Tjenestenavn: {{service}}",
"service_name": "Tjenestenavn",
"check_not_found": "Ikke fundet i dine filterlister",
"client_confirm_block": "Er du sikker på, at du vil blokere klienten \"{{ip}}\"?",
"client_confirm_unblock": "Er du sikker på, at du vil fjerne blokeringen af klienten \"{{ip}}\"?",
"client_confirm_block": "Sikker på, at du vil blokere klienten \"{{ip}}\"?",
"client_confirm_unblock": "Sikker på, at du vil afblokere klienten \"{{ip}}\"?",
"client_blocked": "Klient \"{{ip}}\" blev blokeret",
"client_unblocked": "Blokering af klient \"{{ip}}\" fjernet",
"client_unblocked": "Klient \"{{ip}}\" blev afblokeret",
"static_ip": "Statisk IP-adresse",
"static_ip_desc": "AdGuard Home er en server, så den har brug for en statisk IP-adresse for at fungere korrekt. Ellers på et tidspunkt vil din router muligvis tildele en anden IP-adresse til denne enhed.",
"set_static_ip": "Indstil en statisk IP-adresse",
"install_static_ok": "Gode nyheder! Den statiske IP-adresse er allerede konfigureret",
"install_static_error": "AdGuard Home kan ikke konfigurere det automatisk for denne netværksgrænseflade. Søg efter en instruktion om, hvordan man gør dette manuelt.",
"install_static_configure": "Vi har registreret, at der bruges en dynamisk IP-adresse <0>{{ip}}</0>. Vil du bruge den som din statiske adresse?",
"confirm_static_ip": "AdGuard Home vil konfigurere {{ip}} til at være din statiske IP-adresse. Vil du fortsætte?",
"static_ip_desc": "AdGuard Home er en server, så den behøver en statisk IP-adresse for at fungere korrekt, da din router ellers på et tidspunkt vil kunne tildele en anden IP-adresse til denne enhed.",
"set_static_ip": "Opsæt en statisk IP-adresse",
"install_static_ok": "Gode nyheder! Den statiske IP-adresse er allerede opsat",
"install_static_error": "AdGuard Home kan ikke opsætte den automatisk for denne netværksgrænseflade. Søg information om, hvordan dette gøres manuelt.",
"install_static_configure": "Det er registreret, at den dynamisk IP-adresse <0>{{ip}}</0> bruges. Opsæt denne som din statiske adresse?",
"confirm_static_ip": "AdGuard Home vil opsætte {{ip}} som din statiske IP-adresse. Fortsæt?",
"list_updated": "{{count}} liste opdateret",
"list_updated_plural": "{{count}} lister opdateret",
"dnssec_enable": "Aktivér DNSSEC",
"dnssec_enable_desc": "Sæt DNSSEC-flag i de udgående DNS-forespørgsler, og kontroller resultatet (DNSSEC-aktiveret resolver er krævet)",
"dnssec_enable_desc": "Sæt DNSSEC-flag i de udgående DNS-forespørgsler, og tjek resultatet (DNSSEC-aktiveret resolver er krævet)",
"validated_with_dnssec": "Valideret med DNSSEC",
"all_queries": "Alle forespørgsler",
"show_blocked_responses": "Blokeret",
"show_whitelisted_responses": "Hvidlistet",
"show_processed_responses": "Behandlet",
"blocked_safebrowsing": "Blokeret af Safebrowsing",
"blocked_adult_websites": "Blokerede Websteder for Voksne",
"blocked_adult_websites": "Blokerede Voksen Websteder",
"blocked_threats": "Blokerede Trusler",
"allowed": "Tilladt",
"filtered": "Filtreret",
"rewritten": "Omskrevet",
"safe_search": "Sikker søgning",
"blocklist": "Blokeringsliste",
"blocklist": "Sortliste",
"milliseconds_abbreviation": "ms",
"cache_size": "Cache-størrelse",
"cache_size_desc": "Størrelse på DNS-cache (i bytes)",
"cache_ttl_min_override": "Overskriv minimum TTL",
"cache_ttl_max_override": "Overskriv maksimal TTL",
"enter_cache_size": "Indtast cache-størrelse (bytes)",
"enter_cache_ttl_min_override": "Indtast minimum TTL (sekunder)",
"enter_cache_ttl_max_override": "Indtast maksimum TTL (sekunder)",
"cache_ttl_min_override_desc": "Udvid korte time-to-live værdier (sekunder) modtaget fra upstream-serveren, når DNS-svar cachelagres",
"cache_size_desc": "DNS-cache størrelse (i bytes)",
"cache_ttl_min_override": "Tilsidesæt minimum TTL",
"cache_ttl_max_override": "Tilsidesæt maksimal TTL",
"enter_cache_size": "Angiv cache-størrelse (bytes)",
"enter_cache_ttl_min_override": "Angiv minimum TTL (sekunder)",
"enter_cache_ttl_max_override": "Angiv maksimum TTL (sekunder)",
"cache_ttl_min_override_desc": "Forlæng korte time-to-live værdier (sekunder) modtaget fra upstream-serveren, når DNS-svar cachelagres",
"cache_ttl_max_override_desc": "Indstil en maksimal time-to-live (sekunder) for registreringer i DNS-cachen",
"ttl_cache_validation": "Minimum cache TTL-værdi skal være mindre end eller lig med den maksimale værdi",
"filter_category_general": "Generelt",
"filter_category_security": "Sikkerhed",
"filter_category_regional": "Regional",
"filter_category_other": "Andre",
"filter_category_general_desc": "Lister der blokerer for sporing og reklamer på de fleste enheder",
"filter_category_security_desc": "Lister, der er specialiserede i at blokere malware, phishing eller scam-domæner",
"filter_category_regional_desc": "Lister, der fokuserer på regionale annoncer og tracking-servere",
"filter_category_general_desc": "Lister, som blokerer sporing og reklamer på de fleste enheder",
"filter_category_security_desc": "Lister specialdesignet til at blokere malware-, phishing- eller svindel-domæner",
"filter_category_regional_desc": "Lister målrettet regionale annoncer og sporingsservere",
"filter_category_other_desc": "Andre blokeringslister",
"setup_config_to_enable_dhcp_server": "Opsætningskonfiguration for at aktivere DHCP-server",
"original_response": "Oprindeligt svar",

View File

@@ -122,7 +122,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": "SafeSearch erzwingen",
"enforce_save_search_hint": "AdGuard kann SafeSearch für folgende Suchmaschinen erzwingen: Google, Youtube, Bing und Yandex.",
"enforce_save_search_hint": "AdGuard kann SafeSearch für folgende Suchmaschinen erzwingen: Google, YouTube, Bing und Yandex.",
"no_servers_specified": "Keine Server festgelegt",
"general_settings": "Allgemeine Einstellungen",
"dns_settings": "DNS-Einstellungen",

View File

@@ -122,7 +122,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": "Enforce safe search",
"enforce_save_search_hint": "AdGuard Home can enforce safe search in the following search engines: Google, Youtube, Bing, DuckDuckGo, Yandex, Pixabay.",
"enforce_save_search_hint": "AdGuard Home can enforce safe search in the following search engines: Google, YouTube, Bing, DuckDuckGo, Yandex, Pixabay.",
"no_servers_specified": "No servers specified",
"general_settings": "General settings",
"dns_settings": "DNS settings",

View File

@@ -112,7 +112,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",

View File

@@ -122,7 +122,6 @@
"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": "Renforcer la recherche sécurisée",
"enforce_save_search_hint": "AdGuard Home peut renforcer la Recherche sécurisée dans les moteurs de recherche suivants : Google, Youtube, Bing, DuckDuckGo, Yandex, Pixabay.",
"no_servers_specified": "Pas de serveurs spécifiés",
"general_settings": "Paramètres généraux",
"dns_settings": "Paramètres DNS",

View File

@@ -122,7 +122,7 @@
"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": "Omogući sigurno pretraživanje",
"enforce_save_search_hint": "AdGuard Home može nametnuti sigurno pretraživanje na sljedećim tražilicama: Google, Youtube, Bing, DuckDuckGo i Yandex.",
"enforce_save_search_hint": "AdGuard Home može nametnuti sigurno pretraživanje na sljedećim tražilicama: Google, Youtube, Bing, DuckDuckGo, Yandex i Pixabay.",
"no_servers_specified": "Nije odabran nijedan poslužitelj",
"general_settings": "Opće postavke",
"dns_settings": "DNS postavke",
@@ -251,10 +251,10 @@
"dnscrypt": "DNSCrypt",
"dns_over_https": "DNS-over-HTTPS",
"dns_over_tls": "DNS-over-TLS",
"dns_over_quic": "DNS-over-Quic",
"dns_over_quic": "DNS-over-QUIC",
"client_id": "ID klijenta",
"client_id_placeholder": "Unesite ID klijenta",
"client_id_desc": "Razni klijenti mogu biti prepoznati po specijalnom identifikatoru. <a>Ovdje</a> možete saznati više kako možete identificirati klijente.",
"client_id_desc": "Različiti klijenti mogu se prepoznati pomoću posebnog ID-a klijenta. <a>Ovdje</a> možete saznati više o tome kako prepoznati klijente.",
"download_mobileconfig_doh": "Preuzmi .mobileconfig za DNS-over-HTTPS",
"download_mobileconfig_dot": "Preuzmi .mobileconfig za DNS-over-TLS",
"download_mobileconfig": "Preuzmite konfiguracijsku datoteku",
@@ -276,7 +276,7 @@
"source_label": "Izvor",
"found_in_known_domain_db": "Pronađeno u bazi poznatih domena.",
"category_label": "Kategorija",
"rule_label": "Pravilo",
"rule_label": "Pravilo/a",
"list_label": "Popis",
"unknown_filter": "Nepoznati filtar {{filterId}}",
"known_tracker": "Poznati pratitelj",
@@ -337,7 +337,7 @@
"encryption_config_saved": "Spremljene postavke šifriranja",
"encryption_server": "Naziv poslužitelja",
"encryption_server_enter": "Unesite naziv domene",
"encryption_server_desc": "Kako biste koristili HTTPS, morate unijeti naziv poslužitelja koji odgovara vašem SSL certifikatu.",
"encryption_server_desc": "Da biste koristili HTTPS, morate unijeti ime poslužitelja koje odgovara vašem SSL certifikatu ili wildcard certifikatu. Ako polje nije postavljeno, prihvatit će TLS veze za bilo koju domenu.",
"encryption_redirect": "Automatski preusmjeri na HTTPS",
"encryption_redirect_desc": "Ako je omogućeno, AdGuard Home će vas automatski preusmjeravati s HTTP na HTTPS adrese.",
"encryption_https": "HTTPS port",
@@ -393,7 +393,7 @@
"client_edit": "Uredi klijenta",
"client_identifier": "Identifikator",
"ip_address": "IP adresa",
"client_identifier_desc": "Klijenti se mogu prepoznati po IP adresi, CIDR-u ili MAC adresi. Imajte na umu da je upotreba MAC-a kao identifikatora, moguća samo ako je AdGuard Home također i <0>DHCP poslužitelj</0>",
"client_identifier_desc": "Klijenti se mogu identificirati prema IP adresi, CIDR-u, MAC adresi ili posebnom ID-u klijenta (može se koristiti za DoT/DoH/DoQ). <0>Ovdje</0> možete saznati više o tome kako prepoznati klijente.",
"form_enter_ip": "Unesite IP adresu",
"form_enter_mac": "Unesite MAC adresu",
"form_enter_id": "Unesi identifikator",
@@ -437,7 +437,7 @@
"setup_dns_privacy_other_3": "<0>dnscrypt-proxy</0> podržava <1>DNS-over-HTTPS</1>.",
"setup_dns_privacy_other_4": "<0>Mozilla Firefox</0> podržava <1>DNS-over-HTTPS</1>.",
"setup_dns_privacy_other_5": "Možete pronaći više implementacija <0>ovdje</0> i <1>ovdje</1>.",
"setup_dns_privacy_ioc_mac": "konfiguracija za iOS i macOS",
"setup_dns_privacy_ioc_mac": "iOS i macOS konfiguracija",
"setup_dns_notice": "Da biste koristili <1>DNS-over-HTTPS</1> ili <1>DNS-over-TLS</1>, morate <0>postaviti šifriranje</0> u AdGuard Home postavkama.",
"rewrite_added": "DNS prijepis za \"{{key}}\" je uspješno dodan",
"rewrite_deleted": "DNS prijepis za \"{{key}}\" je uspješno uklonjen",

View File

@@ -32,6 +32,7 @@
"form_error_ip_format": "Érvénytelen IP formátum",
"form_error_mac_format": "Érvénytelen MAC formátum",
"form_error_client_id_format": "Érvénytelen kliens ID formátum",
"form_error_server_name": "Érvénytelen szervernév",
"form_error_positive": "0-nál nagyobbnak kell lennie",
"form_error_negative": "Legalább 0-nak kell lennie",
"range_end_error": "Nagyobbnak kell lennie, mint a tartomány kezdete",
@@ -121,7 +122,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 kényszerítése",
"enforce_save_search_hint": "Az AdGuard Home a következő keresőmotorokban biztosíthatja a biztonságos keresést: Google, Youtube, Bing, DuckDuckGo és Yandex.",
"no_servers_specified": "Nincsenek megadott kiszolgálók",
"general_settings": "Általános beállítások",
"dns_settings": "DNS beállítások",
@@ -247,10 +247,16 @@
"custom_ip": "Egyedi IP",
"blocking_ipv4": "IPv4 blokkolása",
"blocking_ipv6": "IPv6 blokkolása",
"dnscrypt": "DNSCrypt",
"dns_over_https": "DNS-over-HTTPS",
"dns_over_tls": "DNS-over-TLS",
"dns_over_quic": "DNS-over-QUIC",
"client_id": "Kliens azonosító",
"client_id_placeholder": "Kliens azonosító megadása",
"client_id_desc": "A különböző klienseket egy speciális kliens azonosító segítségével lehet azonosítani. <a>Itt</a> többet is megtudhat arról, hogyan lehet a klienseket azonosítani.",
"download_mobileconfig_doh": ".mobileconfig letöltése DNS-over-HTTPS-hez",
"download_mobileconfig_dot": ".mobileconfig letöltése DNS-over-TLS-hez",
"download_mobileconfig": "Konfigurációs fájl letöltése",
"plain_dns": "Egyszerű DNS",
"form_enter_rate_limit": "Adja meg a kérések maximális számát",
"rate_limit": "Kérések korlátozása",
@@ -269,6 +275,7 @@
"source_label": "Forrás",
"found_in_known_domain_db": "Benne van az ismert domainek listájában.",
"category_label": "Kategória",
"rule_label": "Szabály(ok)",
"list_label": "Lista",
"unknown_filter": "Ismeretlen szűrő: {{filterId}}",
"known_tracker": "Ismert követő",
@@ -329,6 +336,7 @@
"encryption_config_saved": "Titkosítási beállítások mentve",
"encryption_server": "Szerver neve",
"encryption_server_enter": "Adja meg az Ön domain címét",
"encryption_server_desc": "A HTTPS használatához meg kell adnia a szerver nevét, amely megegyezik az SSL tanúsítvánnyal vagy a helyettesítő tanúsítvánnyal. Ha a mező nincs beállítva, akkor bármely tartományhoz elfogadja a TLS kapcsolatokat.",
"encryption_redirect": "Automatikus átirányítás HTTPS kapcsolatra",
"encryption_redirect_desc": "Ha be van jelölve, az AdGuard Home automatikusan átirányítja a HTTP kapcsolatokat a biztonságos HTTPS protokollra.",
"encryption_https": "HTTPS port",
@@ -384,6 +392,7 @@
"client_edit": "Kliens módosítása",
"client_identifier": "Azonosító",
"ip_address": "IP cím",
"client_identifier_desc": "A klienseket az IP-cím, a CIDR, a MAC-cím vagy egy speciális kliens azonosító alapján lehet azonosítani (ez használható DoT/DoH /DoQ esetén). <0>Itt</0> többet is megtudhat a kliensek azonosításáról.",
"form_enter_ip": "IP-cím megadása",
"form_enter_mac": "MAC-cím megadása",
"form_enter_id": "Azonosító megadása",
@@ -427,6 +436,7 @@
"setup_dns_privacy_other_3": "A <0>dnscrypt-proxy</0> támogatja a <1>DNS-over-HTTPS</1>-t.",
"setup_dns_privacy_other_4": "A <0>Mozilla Firefox</0> támogatja a <1>DNS-over-HTTPS</1>-t.",
"setup_dns_privacy_other_5": "További megvalósításokat találhat <0>ide</0> és <1>ide</1> kattintva.",
"setup_dns_privacy_ioc_mac": "iOS és macOS konfiguráció",
"setup_dns_notice": "Ahhoz, hogy a <1>DNS-over-HTTPS</1> vagy a <1>DNS-over-TLS</1> valamelyikét használja, muszáj <0>beállítania a titkosítást</0> az AdGuard Home beállításaiban.",
"rewrite_added": "DNS átírás a(z) \"{{key}}\" kulcshoz sikeresen hozzáadva",
"rewrite_deleted": "DNS átírás a(z) \"{{key}}\" kulcshoz sikeresen törölve",

View File

@@ -121,7 +121,6 @@
"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": "Paksa penelusuran aman",
"enforce_save_search_hint": "AdGuard Home dapat memaksa penelusuran aman pada mesin pencari berikut: Google, Youtube, Bing, dan Yandex.",
"no_servers_specified": "Sever tidak disebutkan",
"general_settings": "Pengaturan umum",
"dns_settings": "Pengaturan DNS",

View File

@@ -37,7 +37,7 @@
"form_error_negative": "Deve essere maggiore o uguale a 0 (zero)",
"range_end_error": "Deve essere maggiore dell'intervallo di inizio",
"dhcp_form_gateway_input": "IP Gateway",
"dhcp_form_subnet_input": "Subnet mask",
"dhcp_form_subnet_input": "Maschera di sottorete",
"dhcp_form_range_title": "Range indirizzi IP",
"dhcp_form_range_start": "Inizio range",
"dhcp_form_range_end": "Fine range",
@@ -49,7 +49,7 @@
"ip": "IP",
"dhcp_table_hostname": "Nome host",
"dhcp_table_expires": "Scaduto",
"dhcp_warning": "Se si desidera attivare il server DHCP integrato, assicurarsi che non vi siano altri server DHCP attivi. Altrimenti, possono sussistere problemi di rete per i dispositivi collegati!",
"dhcp_warning": "Se desideri attivare il server DHCP integrato, assicurati che non vi siano altri server DHCP attivi. In caso contrario, potrebbero sussistere problemi di rete per i dispositivi collegati!",
"dhcp_error": "Impossibile determinare se è presente un altro server DHCP nella rete.",
"dhcp_static_ip_error": "Pe rutilizzare un server DHCP, bisogna impostare un indirizzo IP statico. Non siamo riusciti a determinare se questa interfaccia di rete sia configurata per utilizzare un indirizzo IP statico. Per favore impostare un indirizzo IP statico manualmente.",
"dhcp_dynamic_ip_found": "Il tuo sistema utilizza indirizzi IP dinamici per questa interfaccia <0>{{interfaceName}}</0>. Per utilizzare un server DHCP bisogna impostare un indirizzo IP statico. Il tuo indirizzo attuale è <0>{{ipAddress}}</0>. Imposteremo automaticamente questo indirizzo come statico premendo il pulsante Attiva DHCP.",
@@ -83,7 +83,7 @@
"on": "ATTIVO",
"off": "DISATTIVATO",
"copyright": "Copyright",
"homepage": "Pagina iniziale",
"homepage": "Pagina principale",
"report_an_issue": "Segnala un problema",
"privacy_policy": "Politica sulla riservatezza",
"enable_protection": "Attiva protezione",
@@ -117,19 +117,19 @@
"average_processing_time_hint": "Tempo medio in millisecondi per elaborare una richiesta DNS",
"block_domain_use_filters_and_hosts": "Blocca domini utilizzando filtri e file hosts",
"filters_block_toggle_hint": "Puoi impostare le regole di blocco nelle impostazioni dei <a>Filtri</a>.",
"use_adguard_browsing_sec": "Utilizza il servizio web AdGuard \"sicurezza di navigazione\"",
"use_adguard_browsing_sec_hint": "AdGuard Home verificherà se il dominio è stato bloccato dal servizio web \"sicurezza di navigazione\". Per eseguire il controllo utilizzerà delle API privacy-friendly: verrà inviato al server solo un breve prefisso dell'hash SHA256 del nome del dominio.",
"use_adguard_browsing_sec": "Utilizza il servizio web AdGuard 'sicurezza di navigazione'",
"use_adguard_browsing_sec_hint": "AdGuard Home verificherà se il dominio è stato bloccato dal servizio web 'sicurezza di navigazione'. Per eseguire il controllo utilizzerà delle API privacy-friendly: verrà inviato al server solo un breve prefisso dell'hash SHA256 del nome del dominio.",
"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\".",
"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": "Forza ricerca sicura",
"enforce_save_search_hint": "AdGuard Home può forzare la ricerca sicura sui seguenti motori di ricerca: Google, YouTube, Bing e Yandex",
"enforce_save_search_hint": "AdGuard Home può forzare la ricerca sicura sui seguenti motori di ricerca: Google, Bing, DuckDuckGo, Yandex, Pixabay.",
"no_servers_specified": "Nessun server specificato",
"general_settings": "Impostazioni generali",
"dns_settings": "Impostazioni DNS",
"dns_blocklists": "Lista Nera DNS",
"dns_allowlists": "Lista Bianca DNS",
"dns_blocklists": "Lista nera DNS",
"dns_allowlists": "Liste bianche DNS",
"dns_blocklists_desc": "AdGuard Home bloccherà i domini che corrispondenti alla lista nera.",
"dns_allowlists_desc": "I domini DNS consentiti saranno consentiti anche se sono nella lista di blocco.",
"dns_allowlists_desc": "I domini DNS nelle liste bianche saranno consentiti anche fossero presenti in una delle liste nere.",
"custom_filtering_rules": "Regole filtri personalizzati",
"encryption_settings": "Impostazioni di crittografia",
"dhcp_settings": "Impostazioni DHCP",
@@ -145,8 +145,8 @@
"enabled_safe_browsing_toast": "Attiva navigazione sicura",
"disabled_parental_toast": "Disattiva il Controllo Parentale",
"enabled_parental_toast": "Attiva Controllo Parentale",
"disabled_safe_search_toast": "Disattiva Ricerca Sicura",
"enabled_save_search_toast": "Attiva Ricerca Sicura",
"disabled_safe_search_toast": "Ricerca sicura disattivata",
"enabled_save_search_toast": "Attiva ricerca sicura",
"enabled_table_header": "Attivo",
"name_table_header": "Nome",
"list_url_table_header": "Elenco URL",
@@ -157,30 +157,30 @@
"edit_table_action": "Modifica",
"delete_table_action": "Elimina",
"elapsed": "Trascorso",
"filters_and_hosts_hint": "AdGuard Home è in grado di comprendere la sintassi delle regole di blocco per annunci o quelle dei file hosts.",
"filters_and_hosts_hint": "AdGuard Home è in grado di comprendere la sintassi delle regole blocca-annunci o quelle dei file hosts.",
"no_blocklist_added": "Non è stata aggiunta alcuna lista di blocco",
"no_whitelist_added": "Non è stata aggiunta alcuna lista bianca",
"add_blocklist": "Aggiungi lista di blocco",
"add_allowlist": "Aggiungi lista dei consentiti",
"add_allowlist": "Aggiungi lista bianca",
"cancel_btn": "Annulla",
"enter_name_hint": "Inserisci nome",
"enter_url_or_path_hint": "Inmetti un URL o il percorso assoluto della lista",
"check_updates_btn": "Controlla aggiornamenti",
"check_updates_btn": "Ricerca aggiornamenti",
"new_blocklist": "Nuova lista di blocco",
"new_allowlist": "Nuova lista dei consentiti",
"new_allowlist": "Nuova lista bianca",
"edit_blocklist": "Modifica lista di blocco",
"edit_allowlist": "Modifica lista dei consentiti",
"edit_allowlist": "Modifica lista bianca",
"choose_blocklist": "Scegli liste di blocco",
"choose_allowlist": "Scegli liste di autorizzazione",
"choose_allowlist": "Scegli liste bianche",
"enter_valid_blocklist": "Inserisci un URL valido nella lista di blocco.",
"enter_valid_allowlist": "Inserisci un URL valido nella lista dei consentiti.",
"enter_valid_allowlist": "Inserisci un URL valido nella lista bianca.",
"form_error_url_format": "Formato url non valido",
"form_error_url_or_path_format": "URL o percorso assoluto della lista non valido",
"custom_filter_rules": "Regole filtri personalizzate",
"custom_filter_rules_hint": "Inserisci una regola per riga. Puoi utilizzare la sintassi delle regole di blocco per annunci o quelle dei file hosts.",
"custom_filter_rules_hint": "Inserisci una regola per riga. Puoi utilizzare la sintassi delle regole blocca-annunci o quelle dei file hosts.",
"examples_title": "Esempi",
"example_meaning_filter_block": "blocca accesso al dominio example.org e a tutti i suoi sottodomini",
"example_meaning_filter_whitelist": "permette l'accesso al dominio example.org e a tutti i suoi sottodimini",
"example_meaning_filter_whitelist": "consente l\\'accesso al dominio esempio.org e a tutti i relativi sottodomini",
"example_meaning_host_block": "AdGuard Home restituirà 127.0.0.1 come indirizzo per il dominio example.org (ma non per i suoi sottodomini)",
"example_comment": "! Qui va un commento",
"example_comment_meaning": "un commento",
@@ -227,7 +227,7 @@
"query_log_filtered": "Filtrato da {{filter}}",
"query_log_confirm_clear": "Sei sicuro di voler eliminare il registro richieste?",
"query_log_cleared": "Il registro richieste è stato correttamente cancellato",
"query_log_updated": "Il registro richieste è stato aggiornato con successo",
"query_log_updated": "Il registro richieste è stato correttamente aggiornato",
"query_log_clear": "Cancella registri richieste",
"query_log_retention": "Conservazione dei registri richieste",
"query_log_enable": "Attiva registro",
@@ -261,12 +261,12 @@
"plain_dns": "DNS semplice",
"form_enter_rate_limit": "Imposta limite delle richieste",
"rate_limit": "Limite delle richieste",
"edns_enable": "Attiva client di sottorete EDNS",
"edns_enable": "Attiva Client di Sottorete EDNS",
"edns_cs_desc": "Se attivato, AdGuard Home invierà le sottoreti dei client ai server DNS.",
"rate_limit_desc": "Il numero di richieste al secondo che un singolo client può fare (0: illimitato)",
"blocking_ipv4_desc": "Indirizzo IP per una richiesta DNS IPv4 bloccata",
"blocking_ipv6_desc": "Indirizzo IP restituito per una richiesta DNS IPv6 bloccata",
"blocking_mode_default": "Predefinito: Rispondi con IP address zero (0.0.0.0 per A; :: per AAAA) quando bloccato da una regola di blocco annunci; rispondi con l'indirizzo IP specificato nella regola quando bloccato dalla regola /etc/hosts-style",
"blocking_mode_default": "Risponde con un indirizzo IP pari a zero (0.0.0.0 per A; :: per AAAA) quando bloccato da una regola in stile Blocca-annunci; risponde con l\\'indirizzo IP specificato nella regola quando bloccato da una regola in stile /etc/hosts",
"blocking_mode_refused": "REFUSED: Risposta con codice di REFUSED",
"blocking_mode_nxdomain": "NXDOMAIN: Rispondi con il codice NXDOMAIN",
"blocking_mode_null_ip": "IP nullo: Rispondi con indirizzo IP zero (0.0.0.0 per A; :: per AAAA)",
@@ -331,7 +331,7 @@
"get_started": "Inizia",
"next": "Prossimo",
"open_dashboard": "Apri pannello di controllo",
"install_saved": "Salvataggio riuscito con successo",
"install_saved": "Salvataggio riuscito",
"encryption_title": "crittografia",
"encryption_desc": "Supporto di crittografia (HTTPS / TLS) per interfaccia web sia di DNS che di amministrazione",
"encryption_config_saved": "Configurazione della crittografia salvata",
@@ -377,9 +377,9 @@
"dns_status_error": "Errore nel recupero dello stato del server DNS",
"down": "Spenta",
"fix": "Risolvi",
"dns_providers": "Qui c'è una <0>list di provider DNS</0> da cui scegliere",
"dns_providers": "Qui c\\'è una <0>lista di provider DNS</0> da cui scegliere.",
"update_now": "Aggiorna ora",
"update_failed": "Aggiornamento automatico non riuscito. Si prega di <a>seguire questi passi</a>per aggiornare manualmente.",
"update_failed": "Aggiornamento automatico non riuscito. Si prega di <a>seguire questi passaggi</a>per aggiornare manualmente.",
"processing_update": "Perfavore aspetta, AdGuard Home si sta aggiornando",
"clients_title": "Client",
"clients_desc": "Configura i dispositivi connessi ad AdGuard Home",
@@ -418,9 +418,9 @@
"access_blocked_title": "Domini bloccati",
"access_blocked_desc": "Non confondere questi elementi con i filtri. AdGuard Home eliminerà le richieste DNS con questi domini in fase di elaborazione della richiesta.",
"access_settings_saved": "Impostazioni di accesso salvate correttamente",
"updates_checked": "Aggiornamenti controllati con successo",
"updates_checked": "Verifica aggiornamenti riuscita",
"updates_version_equal": "AdGuard Home è aggiornato",
"check_updates_now": "Controlla aggiornamenti adesso",
"check_updates_now": "Ricerca aggiornamenti ora",
"dns_privacy": "Privacy DNS",
"setup_dns_privacy_1": "<0>DNS su TLS:</0> Utilizza la stringa <1>{{address}}</1>.",
"setup_dns_privacy_2": "<0>DNS su HTTPS:</0> Utilizza la stringa <1>{{address}}</1>.",
@@ -498,7 +498,7 @@
"sign_in": "Accedi",
"sign_out": "Esci",
"forgot_password": "Hai perso la password?",
"forgot_password_desc": "Per favore segui <0>questi punti</0> per creare una nuova password per il tuo account.",
"forgot_password_desc": "Per favore segui <0>questi passaggi</0> per creare una nuova password per il tuo profilo.",
"location": "Locazione",
"orgname": "Nome dell'organizzazione",
"netname": "Nome Network",
@@ -519,7 +519,7 @@
"disable_ipv6": "Disattiva IPv6",
"disable_ipv6_desc": "Se questa funzionalità attiva, tutte le richieste DNS per gli indirizzi IPv6 (tipo AAAA) verranno eliminate.",
"fastest_addr": "Indirizzo IP più veloce",
"fastest_addr_desc": "Interroga tutti i server DNS ed ottieni l'indirizzo IP più veloce tra tutte le risposte",
"fastest_addr_desc": "Interroga tutti i server DNS e restituisci l\\'indirizzo IP più veloce tra tutte le risposte. Ciò rallenterà le query DNS poiché dobbiamo attendere risposte da tutti i server DNS, ma ciò migliorerà la connettività complessiva.",
"autofix_warning_text": "Se fai clic su \"Correggi\", AdGuardHome configurerà il tuo sistema per utilizzare il server DNS AdGuardHome.",
"autofix_warning_list": "Eseguirà queste attività: <0> Disattiva DNSStubListener di sistema </0> <0> Imposta l'indirizzo del server DNS su 127.0.0.1 </0> <0> Sostituisci la destinazione del collegamento simbolico di /etc/resolv.conf su / run / systemd /resolve/resolv.conf </0> <0> Arresta DNSStubListener (ricarica il servizio systemd-resolved) </0>",
"autofix_warning_result": "Di conseguenza, tutte le richieste DNS dal sistema verranno elaborate da AdGuardHome per impostazione predefinita.",
@@ -533,7 +533,7 @@
"filtered_custom_rules": "Filtrato dalle regole filtro personalizzate",
"choose_from_list": "Scegli dalla lista",
"add_custom_list": "Aggiungi lista personalizzata",
"host_whitelisted": "L'host è stato aggiunto alla lista bianca",
"host_whitelisted": "L\\'host è stato aggiunto alla lista bianca",
"check_ip": "Indirizzi IP: {{ip}}",
"check_cname": "CNAME: {{cname}}",
"check_reason": "Motivo: {{reason}}",
@@ -545,7 +545,7 @@
"client_blocked": "Client \"{{ip}}\" bloccato correttamente",
"client_unblocked": "Client \"{{ip}}\" sbloccato correttamente",
"static_ip": "Indirizzo IP statico",
"static_ip_desc": "AdGuard Home è un server quindi ha bisogno di un indirizzo IP statico per funzionare correttamente. Altrimenti, a un certo punto, il router potrebbe assegnare un indirizzo IP diverso a questo dispositivo.",
"static_ip_desc": "AdGuard Home è un server quindi ha bisogno di un indirizzo IP statico per funzionare correttamente. In caso contrario, ad un certo punto, il router potrebbe assegnare un indirizzo IP differente a questo dispositivo.",
"set_static_ip": "Imposta un indirizzo IP statico",
"install_static_ok": "Buone notizie! L'indirizzo IP statico è già configurato",
"install_static_error": "AdGuard Home non può configurarlo automaticamente per questa interfaccia di rete. Si prega di cercare un'istruzione su come farlo manualmente.",
@@ -558,7 +558,7 @@
"validated_with_dnssec": "Verificato con DNSSEC",
"all_queries": "Tutte le richieste",
"show_blocked_responses": "Bloccato",
"show_whitelisted_responses": "Nella Lista Bianca",
"show_whitelisted_responses": "Aggiunto alla Lista bianca",
"show_processed_responses": "Processato",
"blocked_safebrowsing": "Blocco Navigazione sicura",
"blocked_adult_websites": "Siti per adulti bloccati",
@@ -583,7 +583,7 @@
"filter_category_security": "Sicurezza",
"filter_category_regional": "Regionale",
"filter_category_other": "Altro",
"filter_category_general_desc": "Liste per blocco tracciamenti e annunci sulla maggioranza dei dispositivi",
"filter_category_general_desc": "Liste per il blocco dei traccianti e degli annunci sulla maggioranza dei dispositivi",
"filter_category_security_desc": "Liste specializzate sul blocco di malware, phishing o domini scam",
"filter_category_regional_desc": "Liste focalizzate su annunci regionali e server traccianti",
"filter_category_other_desc": "Altre liste di blocco",

View File

@@ -122,7 +122,6 @@
"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の検索エンジンでセーフサーチを強制できます。",
"no_servers_specified": "サーバが指定されていません",
"general_settings": "一般設定",
"dns_settings": "DNS設定",

View File

@@ -122,7 +122,6 @@
"use_adguard_parental": "AdGuard 자녀 보호 웹 서비스 사용",
"use_adguard_parental_hint": "AdGuard Home은 도메인에 성인 자료가 포함되어 있는지 확인합니다. 브라우징 보안 웹 서비스와 동일한 개인정보 보호 API를 사용함.",
"enforce_safe_search": "세이프서치 강제",
"enforce_save_search_hint": "AdGuard Home은 다음과 같은 검색엔진(구글, 유투브, 빙, 덕덕고, 얀덱스)에서 안전검색이 가능합니다.",
"no_servers_specified": "지정된 서버 없음",
"general_settings": "일반 설정",
"dns_settings": "DNS 설정",

View File

@@ -122,7 +122,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": "Forceer Veilig Zoeken",
"enforce_save_search_hint": "AdGuard Home kan veilig zoeken forceren voor de volgende zoekmachines: Google, Youtube, Bing, en Yandex.",
"enforce_save_search_hint": "AdGuard Home kan veilig zoeken forceren voor de volgende zoekmachines: Google, Youtube, Bing, en DuckDuckGo, Yandex, Pixabay.",
"no_servers_specified": "Geen servers gespecificeerd",
"general_settings": "Generieke instellingen",
"dns_settings": "DNS Instellingen",

View File

@@ -122,7 +122,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, og Yandex.",
"no_servers_specified": "Ingen tjenere er spesifisert",
"general_settings": "Generelle innstillinger",
"dns_settings": "DNS-innstillinger",

View File

@@ -122,7 +122,7 @@
"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": "Wymuszaj bezpieczne wyszukiwanie",
"enforce_save_search_hint": "AdGuard Home może wymusić bezpieczne wyszukiwanie w następujących wyszukiwarkach: Google, Youtube, Bing, DuckDuckGo, Yandex, Pixabay.",
"enforce_save_search_hint": "AdGuard Home może wymusić 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

@@ -190,7 +190,7 @@
"example_upstream_dot": "<0>DNS-sobre-TLS</0> criptografado",
"example_upstream_doh": "<0>DNS-sobre-HTTPS</0> criptografado",
"example_upstream_doq": "<0>DNS-sobre-QUIC</0> criptografado",
"example_upstream_sdns": "Você pode usar <0>DNS Stamps</0>para o <1>DNSCrypt</1>ou usar os resolvedores <2>DNS-sobre-HTTPS</2>",
"example_upstream_sdns": "você pode usar <0>DNS Stamps</0> para o <1>DNSCrypt</1> ou usar os resolvedores <2>DNS-sobre-HTTPS</2>",
"example_upstream_tcp": "DNS regular (através do TCP)",
"all_lists_up_to_date_toast": "Todas as listas já estão atualizadas",
"updated_upstream_dns_toast": "Atualizado os servidores DNS upstream",

View File

@@ -115,7 +115,7 @@
"number_of_dns_query_to_safe_search": "Várias solicitações de DNS para motores de busca para os quais a pesquisa segura foi aplicada",
"average_processing_time": "Tempo médio de processamento",
"average_processing_time_hint": "Tempo médio em milissegundos no processamento de uma solicitação DNS",
"block_domain_use_filters_and_hosts": "Bloquear domínios usando arquivos de filtros e hosts",
"block_domain_use_filters_and_hosts": "Bloquear domínios usando ficheiros de filtros e hosts",
"filters_block_toggle_hint": "Pode configurar as regras de bloqueio nas configurações de <a>Filtros</a>.",
"use_adguard_browsing_sec": "Usar o serviço de segurança da navegação do AdGuard",
"use_adguard_browsing_sec_hint": "O AdGuard Home irá verificar se o domínio está na lista negra do serviço de segurança da navegação. Usará a API de pesquisa de privacidade para executar a verificação: apenas um prefixo curto do hash do nome de domínio SHA256 é enviado para o servidor.",
@@ -157,7 +157,7 @@
"edit_table_action": "Editar",
"delete_table_action": "Apagar",
"elapsed": "Tempo decorrido",
"filters_and_hosts_hint": "O AdGuard Home entende regras básicas de bloqueio de anúncios e a sintaxe de arquivos de hosts.",
"filters_and_hosts_hint": "O AdGuard Home entende regras básicas de bloqueio de anúncios e a sintaxe de ficheiros de hosts.",
"no_blocklist_added": "Nenhuma lista de bloqueio foi adicionada",
"no_whitelist_added": "Nenhuma lista de permissões foi adicionada",
"add_blocklist": "Adicionar lista de bloqueio",
@@ -177,7 +177,7 @@
"form_error_url_format": "Formato da URL inválida",
"form_error_url_or_path_format": "URL ou local da lista inválida",
"custom_filter_rules": "Regras de filtragem personalizadas",
"custom_filter_rules_hint": "Insira uma regra por linha. Pode usar regras de bloqueio de anúncios ou a sintaxe de arquivos de hosts.",
"custom_filter_rules_hint": "Insira uma regra por linha. Pode usar regras de bloqueio de anúncios ou a sintaxe de ficheiros de hosts.",
"examples_title": "Exemplos",
"example_meaning_filter_block": "bloqueia o acesso ao domínio exemplo.org e a todos os seus subdomínios",
"example_meaning_filter_whitelist": "desbloqueia o acesso ao domínio exemplo.org e a todos os seus subdomínios",
@@ -190,7 +190,7 @@
"example_upstream_dot": "<0>DNS-sobre-TLS</0> criptografado",
"example_upstream_doh": "<0>DNS-sobre-HTTPS</0> criptografado",
"example_upstream_doq": "<0>DNS-sobre-QUIC</0> criptografado",
"example_upstream_sdns": "pode usar <0>DNS Stamps</0>para o <1>DNSCrypt</1>ou usar os resolvedores <2>DNS-sobre-HTTPS</2>",
"example_upstream_sdns": "pode usar <0>DNS Stamps</0> para o <1>DNSCrypt</1> ou usar os resolvedores <2>DNS-sobre-HTTPS</2>",
"example_upstream_tcp": "dNS regular (através do TCP)",
"all_lists_up_to_date_toast": "Todas as listas já estão atualizadas",
"updated_upstream_dns_toast": "A actualizar os servidores DNS upstream",
@@ -235,7 +235,7 @@
"query_log_disabled": "O registo de consulta está desactivado e pode ser configurado em <0>definições</0>",
"query_log_strict_search": "Usar aspas duplas para uma pesquisa rigorosa",
"query_log_retention_confirm": "Tem a certeza de que deseja alterar a retenção do registo de consulta? Se diminuir o valor do intervalo, alguns dados serão perdidos",
"anonymize_client_ip": "Tornar anônimo o IP do cliente",
"anonymize_client_ip": "Tornar anónimo o IP do cliente",
"anonymize_client_ip_desc": "Não salva o endereço de IP completo do cliente em registros e estatísticas",
"dns_config": "Definição do servidor DNS",
"dns_cache_config": "Definição de cache DNS",
@@ -257,7 +257,7 @@
"client_id_desc": "Diferentes clientes podem ser identificados por um ID de cliente especial. <a>Aqui</a> você pode aprender mais sobre como identificar clientes.",
"download_mobileconfig_doh": "Transferir .mobileconfig para DNS-sobre-HTTPS",
"download_mobileconfig_dot": "Transferir .mobileconfig para DNS-sobre-TLS",
"download_mobileconfig": "Transferir arquivo de configuração",
"download_mobileconfig": "Transferir ficheiro de configuração",
"plain_dns": "DNS simples",
"form_enter_rate_limit": "Insira o limite de taxa",
"rate_limit": "Limite de taxa",
@@ -446,7 +446,7 @@
"rewrite_confirm_delete": "Tem a certeza de que deseja excluir a reescrita de DNS para \"{{key}}\"?",
"rewrite_desc": "Permite configurar uma resposta personalizada do DNS para um nome de domínio específico.",
"rewrite_applied": "Regra de reescrita aplicada",
"rewrite_hosts_applied": "Reescrito pela regra do arquivo de hosts",
"rewrite_hosts_applied": "Reescrito pela regra do ficheiro de hosts",
"dns_rewrites": "Reescritas de DNS",
"form_domain": "Inserir domínio",
"form_answer": "Insira o endereço de IP ou nome de domínio",
@@ -464,9 +464,9 @@
"unblock_all": "Desbloquear todos",
"encryption_certificate_path": "Caminho do certificado",
"encryption_private_key_path": "Caminho da chave privada",
"encryption_certificates_source_path": "Definir um caminho do arquivo de certificados",
"encryption_certificates_source_path": "Definir um caminho do ficheiro de certificados",
"encryption_certificates_source_content": "Colar o conteúdo dos certificados",
"encryption_key_source_path": "Definir um arquivo de chave privada",
"encryption_key_source_path": "Definir um ficheiro de chave privada",
"encryption_key_source_content": "Colar o conteúdo da chave privada",
"stats_params": "Definição de estatísticas",
"config_successfully_saved": "Definição guardada com sucesso",
@@ -502,7 +502,7 @@
"location": "Localização",
"orgname": "Nome da organização",
"netname": "Nome da rede",
"network": "Network",
"network": "Rede",
"descr": "Descrição",
"whois": "Whois",
"filtering_rules_learn_more": "<0>Saiba mais</0>sobre como criar as suas próprias listas negras de servidores.",
@@ -524,7 +524,7 @@
"autofix_warning_list": "Ele irá realizar estas tarefas: <0>Desactivar sistema DNSStubListener</0> <0>Definir endereço do servidor DNS para 127.0.0.1</0> <0>Substituir o alvo simbólico do link /etc/resolv.conf para /run/systemd/resolv.conf</0> <0>Parar DNSStubListener (recarregar serviço resolvido pelo sistema)</0>",
"autofix_warning_result": "Como resultado, todos as solicitações DNS do seu sistema serão processadas pelo AdGuard Home por padrão.",
"tags_title": "Etiquetas",
"tags_desc": "Você pode selecionar as etiquetas que correspondem ao cliente. As tags podem ser incluídas nas regras de filtragem e permitir que você as aplique com mais precisão. <0>Saiba mais</0>",
"tags_desc": "Tu podes seleccionar as etiquetas que correspondem ao cliente. As etiquetas podem ser incluídas nas regras de filtragem e permitir que tu as aplique com mais precisão. <0>Saiba mais</0>",
"form_select_tags": "Seleccione as tags do cliente",
"check_title": "Verifique a filtragem",
"check_desc": "Verificar se o nome do host está sendo filtrado",

View File

@@ -1,7 +1,7 @@
{
"client_settings": "Setări client",
"example_upstream_reserved": "Puteți preciza un DNS în amonte <0>de domeniu(ii) specific(e)</0>",
"example_upstream_comment": "Puteți specifica comentariul",
"example_upstream_comment": "Puteți menționa comentariul",
"upstream_parallel": "Folosiți interogări paralele pentru rezolvări rapide interogând simultan toate serverele în amonte",
"parallel_requests": "Solicitări paralele",
"load_balancing": "Echilibrare-sarcini",
@@ -122,7 +122,7 @@
"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": "Căutare protejată întărită",
"enforce_save_search_hint": "AdGuard Home poate impune căutarea protejată în următoarele motoare de căutare: Google, Youtube, Bing, DuckDuckGo, Yandex, Pixabay.",
"enforce_save_search_hint": "AdGuard Home poate impune căutarea protejată î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",

View File

@@ -122,7 +122,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, Yandex и Pixabay.",
"no_servers_specified": "Нет указанных серверов",
"general_settings": "Основные настройки",
"dns_settings": "Настройки DNS",
@@ -189,7 +189,7 @@
"example_upstream_regular": "обычный DNS (поверх UDP)",
"example_upstream_dot": "зашифрованный <0>DNS-over-TLS</0>",
"example_upstream_doh": "зашифрованный <0>DNS-over-HTTPS</0>",
"example_upstream_doq": "зашифрован <0>DNS-over-QUIC</0>",
"example_upstream_doq": "зашифрованный <0>DNS-over-QUIC</0>",
"example_upstream_sdns": "вы можете использовать <0>DNS Stamps</0> для <1>DNSCrypt</1> или <2>DNS-over-HTTPS</2> резолверов",
"example_upstream_tcp": "обычный DNS (поверх TCP)",
"all_lists_up_to_date_toast": "Все списки уже обновлены",
@@ -315,19 +315,19 @@
"install_devices_windows_list_4": "Выделите ваше активное подключение, затем кликните по нему правой клавишей мыши и выберите \"Свойства\".",
"install_devices_windows_list_5": "Найдите в списке пункт \"IP версии 4 (TCP/IP)\", выделите его и затем снова нажмите \"Свойства\".",
"install_devices_windows_list_6": "Выберите \"Использовать следующие адреса DNS-серверов\" и введите адрес AdGuard Home.",
"install_devices_macos_list_1": "Кликните по иконке Apple и перейдите в «Системные настройки».",
"install_devices_macos_list_2": "Кликните по иконке «Сеть».",
"install_devices_macos_list_3": "Выберите первое подключение в списке и нажмите кнопку «Дополнительно».",
"install_devices_macos_list_4": "Выберите вкладку «DNS» и добавьте адреса AdGuard Home.",
"install_devices_android_list_1": "В меню управления нажмите иконку «Настройки».",
"install_devices_android_list_2": "Выберите пункт «Wi-Fi». Появится экран со списком доступных сетей (настройка DNS недоступна для мобильных сетей).",
"install_devices_android_list_3": "Долгим нажатием по текущей сети вызовите меню, в котором нажмите «Изменить сеть».",
"install_devices_android_list_4": "На некоторых устройствах может потребоваться нажать «Расширенные настройки». Чтобы получить возможность изменять настройки DNS, вам потребуется переключить «Настройки IP» на «Пользовательские».",
"install_devices_android_list_5": "Теперь можно изменить поля «DNS 1» и «DNS 2». Введите в них адреса AdGuard Home.",
"install_devices_macos_list_1": "Кликните по иконке Apple и перейдите в \"Системные настройки\".",
"install_devices_macos_list_2": "Кликните по иконке \"Сеть\".",
"install_devices_macos_list_3": "Выберите первое подключение в списке и нажмите кнопку \"Дополнительно\".",
"install_devices_macos_list_4": "Выберите вкладку \"DNS\" и добавьте адреса AdGuard Home.",
"install_devices_android_list_1": "В меню управления нажмите иконку \"Настройки\".",
"install_devices_android_list_2": "Выберите пункт \"Wi-Fi\". Появится экран со списком доступных сетей (настройка DNS недоступна для мобильных сетей).",
"install_devices_android_list_3": "Долгим нажатием по текущей сети вызовите меню, в котором нажмите \"Изменить сеть\".",
"install_devices_android_list_4": "На некоторых устройствах может потребоваться нажать \"Расширенные настройки\". Чтобы получить возможность изменять настройки DNS, вам потребуется переключить \"Настройки IP\" на \"Пользовательские\".",
"install_devices_android_list_5": "Теперь можно изменить поля \"DNS 1\" и \"DNS 2\". Введите в них адреса AdGuard Home.",
"install_devices_ios_list_1": "Войдите в меню настроек устройства.",
"install_devices_ios_list_2": "Выберите пункт «Wi-Fi» (для мобильных сетей ручная настройка DNS невозможна).",
"install_devices_ios_list_2": "Выберите пункт \"Wi-Fi\" (для мобильных сетей ручная настройка DNS невозможна).",
"install_devices_ios_list_3": "Нажмите на название сети, к которой устройство подключено в данный момент.",
"install_devices_ios_list_4": "В поле «DNS» введите введите адреса AdGuard Home.",
"install_devices_ios_list_4": "В поле \"DNS\" введите введите адреса AdGuard Home.",
"get_started": "Поехали",
"next": "Дальше",
"open_dashboard": "Открыть Панель управления",

View File

@@ -95,7 +95,6 @@
"use_adguard_parental": "ඇඩ්ගාර්ඩ් දෙමාපිය පාලන වියමන සේවාව භාවිතා කරන්න",
"use_adguard_parental_hint": "වසමේ වැඩිහිටියන්ට අදාල කරුණු අඩංගු දැයි ඇඩ්ගාර්ඩ් හෝම් විසින් පරීක්ෂා කරනු ඇත. එය පිරික්සුම් ආරක්ෂණ වියමන සේවාව මෙන් රහස්‍යතා හිතකාමී යෙ.ක්‍ර. අ.මු. (API) භාවිතා කරයි.",
"enforce_safe_search": "ආරක්ෂිත සෙවීම බලාත්මක කරන්න",
"enforce_save_search_hint": "ඇඩ්ගාර්ඩ් හෝම් හට පහත සෙවුම් යන්ත්‍ර තුළ ආරක්ෂිත සෙවීම බලාත්මක කළ හැකිය: ගූගල්, යූටියුබ්, බින්ග්, ඩක්ඩක්ගෝ, යාන්ඩෙක්ස් සහ පික්සාබේ.",
"no_servers_specified": "සේවාදායක කිසිවක් නිශ්චිතව දක්වා නැත",
"general_settings": "පොදු සැකසුම්",
"dns_settings": "ව.නා.ප. සැකසුම්",
@@ -227,6 +226,7 @@
"source_label": "මූලාශ්‍රය",
"found_in_known_domain_db": "දැනුවත් වසම් දත්ත ගබඩාවේ හමු විය.",
"category_label": "ප්‍රවර්ගය",
"rule_label": "නීති(ය)",
"list_label": "ලැයිස්තුව",
"unknown_filter": "{{filterId}} නොදන්නා පෙරහනකි",
"known_tracker": "දැනුවත් ලුහුබැඳීමක්",

View File

@@ -122,7 +122,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": "Vynútiť bezpečné vyhľadávanie",
"enforce_save_search_hint": "AdGuard Home môže vynútiť bezpečné vyhľadávanie v nasledujúcich vyhľadávačoch: Google, Youtube, Bing, DuckDuckGo a Yandex.",
"enforce_save_search_hint": "AdGuard Home môže vynútiť bezpečné vyhľadávanie v nasledujúcich vyhľadávačoch: Google, YouTube, Bing, DuckDuckGo, Yandex a Pixabay.",
"no_servers_specified": "Neboli špecifikované žiadne servery",
"general_settings": "Všeobecné nastavenia",
"dns_settings": "Nastavenia DNS",
@@ -276,6 +276,7 @@
"source_label": "Zdroj",
"found_in_known_domain_db": "Nájdené v databáze známych domén.",
"category_label": "Kategória",
"rule_label": "Pravidlo (pravidlá)",
"list_label": "Zoznam",
"unknown_filter": "Neznámy filter {{filterId}}",
"known_tracker": "Známy sledovač",
@@ -336,6 +337,7 @@
"encryption_config_saved": "Konfigurácia šifrovania uložená",
"encryption_server": "Meno servera",
"encryption_server_enter": "Zadajte meno Vašej domény",
"encryption_server_desc": "Ak chcete používať protokol HTTPS, musíte zadať názov servera, ktorý zodpovedá Vášmu certifikátu SSL alebo certifikátu so zástupnými znakmi. Ak pole nie je nastavené, bude akceptovať TLS pripojenia pre ľubovoľnú doménu.",
"encryption_redirect": "Automaticky presmerovať na HTTPS",
"encryption_redirect_desc": "Ak je táto možnosť začiarknutá, služba AdGuard Home Vás automaticky presmeruje z adresy HTTP na adresy HTTPS.",
"encryption_https": "HTTPS port",
@@ -391,6 +393,7 @@
"client_edit": "Upraviť klienta",
"client_identifier": "Identifikátor",
"ip_address": "IP adresa",
"client_identifier_desc": "Klientov je možné identifikovať podľa IP adresy, CIDR a MAC adresy alebo špeciálneho ID klienta (možno použiť pre DoT/DoH/DoQ). <0>Tu</0> sa dozviete viac o tom, ako identifikovať klientov.",
"form_enter_ip": "Zadajte IP adresu",
"form_enter_mac": "Zadajte MAC adresu",
"form_enter_id": "Zadajte identifikátor",

View File

@@ -122,7 +122,7 @@
"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": "Vsili varno iskanje",
"enforce_save_search_hint": "AdGuard Home lahko uveljavi varno iskanje v naslednjih iskalnikih: Google, Youtube, Bing, DuckDuckGo, Yandex, Pixabay.",
"enforce_save_search_hint": "AdGuard Home lahko prisili 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

@@ -121,7 +121,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",

View File

@@ -12,7 +12,7 @@
"dhcp_description": "Om din router inte har inställningar för DHCP kan du använda AdGuards inbyggda server.",
"dhcp_enable": "Aktivera DHCP.-server",
"dhcp_disable": "Avaktivera DHCP-server",
"dhcp_not_found": "Ingen aktiv DHCP-server hittades i nätverkat.",
"dhcp_not_found": "Ingen aktiv DHCP-server hittades i nätverket.",
"dhcp_found": "Några aktiva DHCP-servar upptäcktes. Det är inte säkert att aktivera inbyggda DHCP-servrar.",
"dhcp_leases": "DHCP-lease",
"dhcp_static_leases": "Statiska DHCP-leases",
@@ -36,7 +36,7 @@
"dhcp_table_expires": "Utgår",
"dhcp_warning": "Om du vill använda den inbyggda DHCP-servern ändå, se till att det inte finns några andra aktiva DHCP-servrar. Annars kan den störa internetanslutningen för anslutna enheter!",
"dhcp_error": "Vi kunde inte avgöra om det finns en till DHCP-server på nätverket.",
"dhcp_static_ip_error": "För att kunna använda en DHCP-server måste det finnas en statisk IP-adress. Vi kunde inte avgöra om nätverksgränssnittet är konfigurerat med en statisk IP-adress. Ställ in en statistik IP-adress manuellt.",
"dhcp_static_ip_error": "För att kunna använda en DHCP-server måste det finnas en statisk IP-adress. Vi kunde inte avgöra om nätverksgränssnittet är konfigurerat med en statisk IP-adress. Ställ in en statisk IP-adress manuellt.",
"dhcp_dynamic_ip_found": "Din enhet använder en dynamisk IP-adress för gränssnittet <0>{{interfaceName}}</0>. För att kunna använda DHCP-servern behövs en statisk IP-adress. Din nuvarande IP-adress är <0>{{ipAddress}}</0>. Vi kommer att göra denna IP-adress statisk automatiskt om du trycker på knappen \"Aktivera DHCP\".",
"dhcp_lease_added": "Statisk lease \"{{key}}\" har lagts till",
"dhcp_lease_deleted": "Statisk lease \"{{key}}\" har raderats",
@@ -67,17 +67,17 @@
"refresh_statics": "Uppdatera statistik",
"dns_query": "DNS-förfrågningar",
"blocked_by": "<0>Blockerat av filter</0>",
"stats_malware_phishing": "Blockerad skadekod/phising",
"stats_malware_phishing": "Blockerad skadekod/phishing",
"stats_adult": "Blockerade vuxensajter",
"stats_query_domain": "Mest eftersökta domäner",
"for_last_24_hours": "under de senaste 24 timamrna",
"for_last_24_hours": "under de senaste 24 timmarna",
"for_last_days": "för den senaste {{count}} dagen",
"for_last_days_plural": "för de senaste {{count}} dagarna",
"no_domains_found": "Inga domäner hittade",
"requests_count": "Förfrågningsantal",
"top_blocked_domains": "Flest blockerade domäner",
"top_clients": "Toppklienter",
"no_clients_found": "Inga hitatde klienter",
"no_clients_found": "Inga klienter hittade",
"general_statistics": "Allmän statistik",
"number_of_dns_query_days": "Ett antal DNS-förfrågningar utfördes under den senaste {{count}} dagen",
"number_of_dns_query_days_plural": "Ett antal DNS-förfrågningar utfördes under de senaste {{count}} dagarna",
@@ -91,12 +91,11 @@
"average_processing_time_hint": "Genomsnittlig processtid i millisekunder för DNS-förfrågning",
"block_domain_use_filters_and_hosts": "Blockera domäner med filter- och värdfiler",
"filters_block_toggle_hint": "Du kan ställa in egna blockerings regler i <a>Filterinställningar</a>.",
"use_adguard_browsing_sec": "Amvänd AdGuards webbservice för surfsäkerhet",
"use_adguard_browsing_sec": "Använd AdGuards webbservice för surfsäkerhet",
"use_adguard_browsing_sec_hint": "AdGuard Home kommer att kontrollera om en domän är svartlistad i webbservicens surfsäkerhet. Med en integritetsvänlig metod görs en API-lookup för att kontrollera : endast en kort prefix i domännamnet SHA256 hash skickas till servern.",
"use_adguard_parental": "Använda AdGuards webbservice för färäldrakontroll",
"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": "Tillämpa Säker surf",
"enforce_save_search_hint": "AdGuard Home kan framtvinga säker surf i följande sökmoterer: Google, Youtube, Bing, och Yandex.",
"no_servers_specified": "Inga servrar angivna",
"general_settings": "Allmänna inställningar",
"dns_settings": "DNS-inställningar",
@@ -197,7 +196,7 @@
"install_auth_password_enter": "Skriv in lösenord",
"install_step": "Steg",
"install_devices_title": "Ställ in dina enheter",
"install_devices_desc": "För att kunna använda AdGuard Home måste du sälla in dina enheter för att utnyttja den.",
"install_devices_desc": "För att kunna använda AdGuard Home måste du ställa in dina enheter för att utnyttja den.",
"install_submit_title": "Grattis!",
"install_submit_desc": "Inställningsproceduren är klar och du kan börja använda AdGuard Home.",
"install_devices_router": "Router",
@@ -337,7 +336,7 @@
"interval_hours_plural": "{{count}} timmar",
"filters_configuration": "Filterinställningar",
"filters_enable": "Aktivera filter",
"filters_interval": "Filterppdateringsintervall",
"filters_interval": "Filteruppdateringsintervall",
"disabled": "Avaktiverad",
"username_label": "Användarnamn",
"username_placeholder": "Skriv in användarnamn",

View File

@@ -101,7 +101,6 @@
"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",
"no_servers_specified": "ไม่ได้ระบุเซิร์ฟเวอร์",
"general_settings": "การตั้งค่าทั่วไป",
"dns_settings": "การตั้งค่า DNS",

View File

@@ -83,7 +83,7 @@
"on": "AÇIK",
"off": "KAPALI",
"copyright": "Telif hakkı",
"homepage": "Anasayfa",
"homepage": "Ana sayfa",
"report_an_issue": "Bir sorun bildir",
"privacy_policy": "Gizlilik politikası",
"enable_protection": "Korumayı etkinleştir",
@@ -108,7 +108,7 @@
"number_of_dns_query_days": "Son {{count}} gün boyunca işlenen DNS sorgularının sayısı",
"number_of_dns_query_days_plural": "Son {{count}} gün boyunca işlenen DNS sorgularının sayısı",
"number_of_dns_query_24_hours": "Son 24 saat içinde işlenen DNS sorgularının sayısı",
"number_of_dns_query_blocked_24_hours": "Reklam engelleme filtreleri ve ana bilgisayar engelleme listeleri tarafından engellenen DNS isteklerinin sayısı",
"number_of_dns_query_blocked_24_hours": "Reklam engelleme filtre ve ana bilgisayar engelleme listeleri tarafından engellenen DNS isteklerinin sayısı",
"number_of_dns_query_blocked_24_hours_by_sec": "AdGuard gezinti koruması modülü tarafından engellenmiş DNS isteklerinin sayısı",
"number_of_dns_query_blocked_24_hours_adult": "Engellenmiş yetişkin içerikli web sitelerinin sayısı",
"enforced_save_search": "Zorunlu kılınmış güvenli arama",
@@ -122,7 +122,7 @@
"use_adguard_parental": "AdGuard ebeveyn kontrolü web hizmetini kullan",
"use_adguard_parental_hint": "AdGuard Home, alan adının yetişkin içerik bulundurup bulundurmadığını kontrol edecek. Gezinti güvenliği web hizmeti ile kullandığımız aynı gizlilik dostu API'yi kullanıyoruz.",
"enforce_safe_search": "Güvenli aramayı zorunlu kıl",
"enforce_save_search_hint": "AdGuard Home şu arama motorlarında güvenli aramayı zorunlu kılabilir: Google, Youtube, Bing, DuckDuckGo, Yandex ve Pixabay.",
"enforce_save_search_hint": "AdGuard Home şu arama motorlarında güvenli aramayı zorunlu kılabilir: Google, YouTube, Bing, DuckDuckGo, Yandex ve Pixabay.",
"no_servers_specified": "Sunucu adresi girilmedi",
"general_settings": "Genel ayarlar",
"dns_settings": "DNS ayarları",

View File

@@ -122,7 +122,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",

View File

@@ -29,7 +29,7 @@
"form_error_required": "必填字段",
"form_error_ip4_format": "无效的 IPv4 格式",
"form_error_ip6_format": "无效的 IPv6 格式",
"form_error_ip_format": "无效的 IPv4 格式",
"form_error_ip_format": "无效的 IP 格式",
"form_error_mac_format": "无效的 MAC 格式",
"form_error_client_id_format": "无效的客户端 ID 格式",
"form_error_server_name": "无效的服务器名",
@@ -46,7 +46,7 @@
"dhcp_interface_select": "选择 DHCP 接口",
"dhcp_hardware_address": "硬件地址",
"dhcp_ip_addresses": "IP 地址",
"ip": "IP地址",
"ip": "IP 地址",
"dhcp_table_hostname": "主机名",
"dhcp_table_expires": "到期",
"dhcp_warning": "如果你想要启用内置的 DHCP 服务器,请确保在当前网络中没有其它起作用的 DHCP 服务器。否则,此操作可能会破坏已连接设备的网络连接!",
@@ -122,7 +122,6 @@
"use_adguard_parental": "使用 AdGuard 【家长控制】服务",
"use_adguard_parental_hint": "AdGuard Home 将使用与浏览安全服务相同的隐私性强的 API 来检查域名指向的网站是否包含成人内容。",
"enforce_safe_search": "强制安全搜索",
"enforce_save_search_hint": "AdGuard Home 将对以下搜索引擎强制启用安全搜索Google、YouTube、Bing 和 Yandex。",
"no_servers_specified": "未找到指定的服务器",
"general_settings": "常规设置",
"dns_settings": "DNS 设置",

View File

@@ -32,6 +32,7 @@
"form_error_ip_format": "無效的 IP 格式",
"form_error_mac_format": "無效的 「MAC 位址」格式",
"form_error_client_id_format": "無效的「客戶端 ID」格式",
"form_error_server_name": "無效伺服器名稱",
"form_error_positive": "數值必須大於 0",
"form_error_negative": "數值必須大於等於 0",
"range_end_error": "必須大於起始值",
@@ -121,7 +122,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。",
"no_servers_specified": "沒有指定的伺服器",
"general_settings": "一般設定",
"dns_settings": "DNS 設定",
@@ -247,10 +247,16 @@
"custom_ip": "自訂 IP 位址",
"blocking_ipv4": "封鎖 IPv4",
"blocking_ipv6": "封鎖 IPv6",
"dnscrypt": "DNSCrypt",
"dns_over_https": "DNS-over-HTTPS",
"dns_over_tls": "DNS-over-TLS",
"dns_over_quic": "DNS-over-QUIC",
"client_id": "用戶端 ID",
"client_id_placeholder": "輸入用戶端 ID",
"client_id_desc": "可通過建立不同用戶端 ID 來辨識不同裝置。您可以在<a>這裡</a>進一步了解如何辨識用戶端。",
"download_mobileconfig_doh": "下載適用於 DNS-over-HTTPS 的 .mobileconfig",
"download_mobileconfig_dot": "下載適用於 DNS-over-TLS 的 .mobileconfig",
"download_mobileconfig": "下載描述檔",
"plain_dns": "一般未加密 DNS",
"form_enter_rate_limit": "輸入速率限制",
"rate_limit": "速率限制",
@@ -290,7 +296,7 @@
"install_auth_username_enter": "輸入用戶名",
"install_auth_password_enter": "輸入密碼",
"install_step": "步驟",
"install_devices_title": "配置您的裝置",
"install_devices_title": "設定您的裝置",
"install_devices_desc": "要開始使用 AdGuard Home您需要設定好裝置才能使用。",
"install_submit_title": "恭喜!",
"install_submit_desc": "安裝步驟已完成,現在已經可以開始使用 AdGuard Home",
@@ -427,6 +433,7 @@
"setup_dns_privacy_other_3": "<0>dnscrypt-proxy</0> 支援 <1>DNS-over-HTTPS</1>。",
"setup_dns_privacy_other_4": "<0>Mozilla Firefox</0> 支援 <1>DNS-over-HTTPS</1>。",
"setup_dns_privacy_other_5": "您可以在<0>這裏</0>與<1>這裏</1>找到更多實作軟體。",
"setup_dns_privacy_ioc_mac": "iOS 與 macOS 描述檔",
"setup_dns_notice": "要使用 <1>DNS-over-HTTPS</1> 或 <1>DNS-over-TLS</1>,您必須先在 AdGuard Home 完成 <0>加密設定</0>。",
"rewrite_added": "「{{key}}」的 DNS 覆寫新增成功",
"rewrite_deleted": "「{{key}}」的 DNS 覆寫刪除成功",

View File

@@ -3,7 +3,7 @@ import React, {
useEffect,
useRef,
} from 'react';
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
import { useDispatch, useSelector } from 'react-redux';
import { useTranslation } from 'react-i18next';
import propTypes from 'prop-types';
import throttle from 'lodash/throttle';
@@ -25,19 +25,21 @@ const InfiniteTable = ({
const { t } = useTranslation();
const dispatch = useDispatch();
const loader = useRef(null);
const loadingRef = useRef(null);
const {
isEntireLog,
processingGetLogs,
} = useSelector((state) => state.queryLogs, shallowEqual);
const isEntireLog = useSelector((state) => state.queryLogs.isEntireLog);
const processingGetLogs = useSelector((state) => state.queryLogs.processingGetLogs);
const loading = isLoading || processingGetLogs;
const listener = useCallback(() => {
if (loader.current && isScrolledIntoView(loader.current)) {
if (!loadingRef.current && loader.current && isScrolledIntoView(loader.current)) {
dispatch(getLogs());
}
}, [loader.current, isScrolledIntoView, getLogs]);
}, []);
useEffect(() => {
loadingRef.current = processingGetLogs;
}, [processingGetLogs]);
useEffect(() => {
listener();

View File

@@ -4,7 +4,7 @@
"name": "adguard-home",
"version": "0.1.0",
"scripts": {
"build": "rm -rf ../build2 && yarn install && webpack --config ./scripts/webpack/webpack.config.prod.js",
"build": "rm -rf ../build2 && yarn install --network-timeout 120000 && webpack --config ./scripts/webpack/webpack.config.prod.js",
"start": "webpack serve --config ./scripts/webpack/webpack.config.dev.js",
"generate": "rm -rf ./src/lib/entities ./src/lib/apis && ts-node --compiler-options '{ \"module\": \"CommonJS\" }' ./scripts/generator/index.ts",
"translations:check": "ts-node --compiler-options '{ \"module\": \"CommonJS\" }' ./scripts/plugins/checkTranslations.ts",

41
go.mod
View File

@@ -1,42 +1,45 @@
module github.com/AdguardTeam/AdGuardHome
go 1.14
go 1.15
require (
github.com/AdguardTeam/dnsproxy v0.33.9
github.com/AdguardTeam/dnsproxy v0.35.5
github.com/AdguardTeam/golibs v0.4.4
github.com/AdguardTeam/urlfilter v0.14.3
github.com/AdguardTeam/urlfilter v0.14.4
github.com/NYTimes/gziphandler v1.1.1
github.com/ameshkov/dnscrypt/v2 v2.0.1
github.com/ameshkov/dnscrypt/v2 v2.0.3
github.com/digineo/go-ipset/v2 v2.2.1
github.com/fsnotify/fsnotify v1.4.9
github.com/go-ping/ping v0.0.0-20201115131931-3300c582a663
github.com/go-ping/ping v0.0.0-20210216210419-25d1413fb7bb
github.com/gobuffalo/envy v1.9.0 // indirect
github.com/gobuffalo/packr v1.30.1
github.com/gobuffalo/packr/v2 v2.8.1 // indirect
github.com/google/go-cmp v0.5.5 // indirect
github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714
github.com/insomniacslk/dhcp v0.0.0-20201112113307-4de412bc85d8
github.com/insomniacslk/dhcp v0.0.0-20210310193751-cfd4d47082c2
github.com/kardianos/service v1.2.0
github.com/karrick/godirwalk v1.16.1 // indirect
github.com/lucas-clemente/quic-go v0.19.3
github.com/marten-seemann/qtls-go1-15 v0.1.4 // indirect
github.com/mdlayher/ethernet v0.0.0-20190606142754-0394541c37b7
github.com/mdlayher/netlink v1.1.2-0.20201013204415-ded538f7f4be
github.com/mdlayher/netlink v1.4.0
github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065
github.com/miekg/dns v1.1.35
github.com/rogpeppe/go-internal v1.6.2 // indirect
github.com/miekg/dns v1.1.40
github.com/rogpeppe/go-internal v1.7.0 // indirect
github.com/satori/go.uuid v1.2.0
github.com/sirupsen/logrus v1.7.0 // indirect
github.com/spf13/cobra v1.1.1 // indirect
github.com/stretchr/testify v1.6.1
github.com/sirupsen/logrus v1.8.1 // indirect
github.com/spf13/cobra v1.1.3 // indirect
github.com/stretchr/testify v1.7.0
github.com/ti-mo/netfilter v0.4.0
github.com/u-root/u-root v7.0.0+incompatible
go.etcd.io/bbolt v1.3.5
golang.org/x/crypto v0.0.0-20201217014255-9d1352758620
golang.org/x/net v0.0.0-20201216054612-986b41b23924
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 // indirect
golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
golang.org/x/sys v0.0.0-20210309074719-68d13333faf2
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d // indirect
golang.org/x/text v0.3.5 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.0.0
gopkg.in/yaml.v2 v2.3.0
howett.net/plist v0.0.0-20201026045517-117a925f2150
gopkg.in/yaml.v2 v2.4.0
howett.net/plist v0.0.0-20201203080718-1454fab16a06
)

142
go.sum
View File

@@ -18,16 +18,15 @@ dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBr
dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4=
dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
github.com/AdguardTeam/dnsproxy v0.33.9 h1:HUwywkhUV/M73E7qWcBAF+SdsNq742s82Lvox4pr/tM=
github.com/AdguardTeam/dnsproxy v0.33.9/go.mod h1:dkI9VWh43XlOzF2XogDm1EmoVl7PANOR4isQV6X9LZs=
github.com/AdguardTeam/dnsproxy v0.35.5 h1:SsRF0eDzuLGaSUDKABIu9Mn1joi4v4kvEU1vju2DQPQ=
github.com/AdguardTeam/dnsproxy v0.35.5/go.mod h1:dkI9VWh43XlOzF2XogDm1EmoVl7PANOR4isQV6X9LZs=
github.com/AdguardTeam/golibs v0.4.0/go.mod h1:skKsDKIBB7kkFflLJBpfGX+G8QFTx0WKUzB6TIgtUj4=
github.com/AdguardTeam/golibs v0.4.2 h1:7M28oTZFoFwNmp8eGPb3ImmYbxGaJLyQXeIFVHjME0o=
github.com/AdguardTeam/golibs v0.4.2/go.mod h1:skKsDKIBB7kkFflLJBpfGX+G8QFTx0WKUzB6TIgtUj4=
github.com/AdguardTeam/golibs v0.4.4 h1:cM9UySQiYFW79zo5XRwnaIWVzfW4eNXmZktMrWbthpw=
github.com/AdguardTeam/golibs v0.4.4/go.mod h1:skKsDKIBB7kkFflLJBpfGX+G8QFTx0WKUzB6TIgtUj4=
github.com/AdguardTeam/gomitmproxy v0.2.0/go.mod h1:Qdv0Mktnzer5zpdpi5rAwixNJzW2FN91LjKJCkVbYGU=
github.com/AdguardTeam/urlfilter v0.14.3 h1:MBaLEXS0LRQNbHtLkDCYhHINDPtkevPrYWGiOUuLJU4=
github.com/AdguardTeam/urlfilter v0.14.3/go.mod h1:klx4JbOfc4EaNb5lWLqOwfg+pVcyRukmoJRvO55lL5U=
github.com/AdguardTeam/urlfilter v0.14.4 h1:lrS7lrfxVCFh4TFB6nwPp5UE4n1XNvv3zUetduD9mZw=
github.com/AdguardTeam/urlfilter v0.14.4/go.mod h1:klx4JbOfc4EaNb5lWLqOwfg+pVcyRukmoJRvO55lL5U=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
@@ -42,9 +41,9 @@ github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 h1:52m0LGchQBBVqJRyY
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635/go.mod h1:lmLxL+FV291OopO93Bwf9fQLQeLyt33VJRUg5VJ30us=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/ameshkov/dnscrypt/v2 v2.0.1 h1:igNVNM6NLBOqYUzHXaDUxn8i+wJXOsosY0/xEBirixA=
github.com/ameshkov/dnscrypt/v2 v2.0.1/go.mod h1:nbZnxJt4edIPx2Haa8n2XtC2g5AWcsdQiSuXkNH8eDI=
github.com/ameshkov/dnsstamps v1.0.1 h1:LhGvgWDzhNJh+kBQd/AfUlq1vfVe109huiXw4JhnPug=
github.com/ameshkov/dnscrypt/v2 v2.0.3 h1:PE6VVc8QUMYJv9dTwcDcX5cYXf58XPi1WVPHrLf8MDs=
github.com/ameshkov/dnscrypt/v2 v2.0.3/go.mod h1:nbZnxJt4edIPx2Haa8n2XtC2g5AWcsdQiSuXkNH8eDI=
github.com/ameshkov/dnsstamps v1.0.1/go.mod h1:Ii3eUu73dx4Vw5O4wjzmT5+lkCwovjzaEZZ4gKyIH5A=
github.com/ameshkov/dnsstamps v1.0.3 h1:Srzik+J9mivH1alRACTbys2xOxs0lRH9qnTA7Y1OYVo=
github.com/ameshkov/dnsstamps v1.0.3/go.mod h1:Ii3eUu73dx4Vw5O4wjzmT5+lkCwovjzaEZZ4gKyIH5A=
@@ -92,7 +91,6 @@ github.com/fanliao/go-promise v0.0.0-20141029170127-1890db352a72/go.mod h1:Pjfxu
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
@@ -105,26 +103,22 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI=
github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
github.com/go-ping/ping v0.0.0-20201115131931-3300c582a663 h1:jI2GiiRh+pPbey52EVmbU6kuLiXqwy4CXZ4gwUBj8Y0=
github.com/go-ping/ping v0.0.0-20201115131931-3300c582a663/go.mod h1:35JbSyV/BYqHwwRA6Zr1uVDm1637YlNOU61wI797NPI=
github.com/go-ping/ping v0.0.0-20210216210419-25d1413fb7bb h1:2opwLSXqxE0Za64PdpskXuvLYDj/XHQAD8tLcYpSlvY=
github.com/go-ping/ping v0.0.0-20210216210419-25d1413fb7bb/go.mod h1:35JbSyV/BYqHwwRA6Zr1uVDm1637YlNOU61wI797NPI=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-test/deep v1.0.5 h1:AKODKU3pDH1RzZzm6YZu77YWtEAq6uh1rLIAQlay2qc=
github.com/go-test/deep v1.0.5/go.mod h1:QV8Hv/iy04NyLBxAdO9njL0iVPN1S4d/A3NVv1V36o8=
github.com/gobuffalo/envy v1.7.0 h1:GlXgaiBkmrYMHco6t4j7SacKO4XUjvh5pwXh0f4uxXU=
github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
github.com/gobuffalo/envy v1.9.0 h1:eZR0DuEgVLfeIb1zIKt3bT4YovIMf9O9LXQeCZLXpqE=
github.com/gobuffalo/envy v1.9.0/go.mod h1:FurDp9+EDPE4aIUS3ZLyD+7/9fpx7YRt/ukY6jIHf0w=
github.com/gobuffalo/logger v1.0.0 h1:xw9Ko9EcC5iAFprrjJ6oZco9UpzS5MQ4jAwghsLHdy4=
github.com/gobuffalo/logger v1.0.0/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs=
github.com/gobuffalo/logger v1.0.3 h1:YaXOTHNPCvkqqA7w05A4v0k2tCdpr+sgFlgINbQ6gqc=
github.com/gobuffalo/logger v1.0.3/go.mod h1:SoeejUwldiS7ZsyCBphOGURmWdwUFXs0J7TCjEhjKxM=
github.com/gobuffalo/packd v0.3.0 h1:eMwymTkA1uXsqxS0Tpoop3Lc0u3kTfiMBE6nKtQU4g4=
github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q=
github.com/gobuffalo/packd v1.0.0 h1:6ERZvJHfe24rfFmA9OaoKBdC7+c9sydrytMg8SdFGBM=
github.com/gobuffalo/packd v1.0.0/go.mod h1:6VTc4htmJRFB7u1m/4LeMTWjFoYrUiBkU9Fdec9hrhI=
github.com/gobuffalo/packr v1.30.1 h1:hu1fuVR3fXEZR7rXNW3h8rqSML8EVAf6KNm0NKO/wKg=
github.com/gobuffalo/packr v1.30.1/go.mod h1:ljMyFO2EcrnzsHsN99cvbq055Y9OhRrIaviy289eRuk=
github.com/gobuffalo/packr/v2 v2.5.1 h1:TFOeY2VoGamPjQLiNDT3mn//ytzk236VMO2j7iHxJR4=
github.com/gobuffalo/packr/v2 v2.5.1/go.mod h1:8f9c96ITobJlPzI44jj+4tHnEKNt0xXWSVlXRN9X1Iw=
github.com/gobuffalo/packr/v2 v2.8.1 h1:tkQpju6i3EtMXJ9uoF5GT6kB+LMTimDWD8Xvbz6zDVA=
github.com/gobuffalo/packr/v2 v2.8.1/go.mod h1:c/PLlOuTU+p3SybaJATW3H6lX/iK7xEz5OeMf+NnJpg=
@@ -150,22 +144,20 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
@@ -210,30 +202,33 @@ github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Go
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/insomniacslk/dhcp v0.0.0-20201112113307-4de412bc85d8 h1:R1oP0/QEyvaL7dm+mBQouQ9V1X6gqQr5taZA1yaq5zQ=
github.com/insomniacslk/dhcp v0.0.0-20201112113307-4de412bc85d8/go.mod h1:TKl4jN3Voofo4UJIicyNhWGp/nlQqQkFxmwIFTvBkKI=
github.com/insomniacslk/dhcp v0.0.0-20210310193751-cfd4d47082c2 h1:NpTIlXznCStsY88jU+Gh1Dy5dt/jYV4z4uU8h2TUOt4=
github.com/insomniacslk/dhcp v0.0.0-20210310193751-cfd4d47082c2/go.mod h1:TKl4jN3Voofo4UJIicyNhWGp/nlQqQkFxmwIFTvBkKI=
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/joomcode/errorx v1.0.1 h1:CalpDWz14ZHd68fIqluJasJosAewpz2TFaJALrUxjrk=
github.com/joomcode/errorx v1.0.1/go.mod h1:kgco15ekB6cs+4Xjzo7SPeXzx38PbJzBwbnu9qfVNHQ=
github.com/joomcode/errorx v1.0.3 h1:3e1mi0u7/HTPNdg6d6DYyKGBhA5l9XpsfuVE29NxnWw=
github.com/joomcode/errorx v1.0.3/go.mod h1:eQzdtdlNyN7etw6YCS4W4+lu442waxZYw5yvz0ULrRo=
github.com/josharian/native v0.0.0-20200817173448-b6b71def0850 h1:uhL5Gw7BINiiPAo24A2sxkcDI0Jt/sqp1v5xQCniEFA=
github.com/josharian/native v0.0.0-20200817173448-b6b71def0850/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw=
github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ=
github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok=
github.com/jsimonetti/rtnetlink v0.0.0-20201110080708-d2c240429e6c h1:7cpGGTQO6+OuYQWkueqeXuErSjs1NZtpALpv1x7Mq4g=
github.com/jsimonetti/rtnetlink v0.0.0-20201110080708-d2c240429e6c/go.mod h1:huN4d1phzjhlOsNIjFsw2SVRbwIHj3fJDMEU2SDPTmg=
github.com/jsimonetti/rtnetlink v0.0.0-20201216134343-bde56ed16391/go.mod h1:cR77jAZG3Y3bsb8hF6fHJbFoyFukLFOkQ98S0pQz3xw=
github.com/jsimonetti/rtnetlink v0.0.0-20201220180245-69540ac93943/go.mod h1:z4c53zj6Eex712ROyh8WI0ihysb5j2ROyV42iNogmAs=
github.com/jsimonetti/rtnetlink v0.0.0-20210122163228-8d122574c736/go.mod h1:ZXpIyOK59ZnN7J0BV99cZUPmsqDRZ3eq5X+st7u/oSA=
github.com/jsimonetti/rtnetlink v0.0.0-20210212075122-66c871082f2b h1:c3NTyLNozICy8B4mlMXemD3z/gXgQzVXZS/HqT+i3do=
github.com/jsimonetti/rtnetlink v0.0.0-20210212075122-66c871082f2b/go.mod h1:8w9Rh8m+aHZIG69YPGGem1i5VzoyRC8nw2kA8B+ik5U=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kardianos/service v1.2.0 h1:bGuZ/epo3vrt8IPC7mnKQolqFeYJb7Cs8Rk4PSOBB/g=
github.com/kardianos/service v1.2.0/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM=
github.com/karrick/godirwalk v1.10.12 h1:BqUm+LuJcXjGv1d2mj3gBiQyrQ57a0rYoAmhvJQ7RDU=
github.com/karrick/godirwalk v1.10.12/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
github.com/karrick/godirwalk v1.15.8/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk=
github.com/karrick/godirwalk v1.16.1 h1:DynhcF+bztK8gooS0+NDJFrdNZjJ3gzVzC545UNA9iw=
@@ -241,7 +236,6 @@ github.com/karrick/godirwalk v1.16.1/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1q
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
@@ -265,30 +259,39 @@ github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kN
github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc=
github.com/marten-seemann/qtls v0.10.0 h1:ECsuYUKalRL240rRD4Ri33ISb7kAQ3qGDlrrl55b2pc=
github.com/marten-seemann/qtls v0.10.0/go.mod h1:UvMd1oaYDACI99/oZUYLzMCkBXQVT0aGm99sJhbT8hs=
github.com/marten-seemann/qtls-go1-15 v0.1.1 h1:LIH6K34bPVttyXnUWixk0bzH6/N07VxbSabxn5A5gZQ=
github.com/marten-seemann/qtls-go1-15 v0.1.1/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I=
github.com/marten-seemann/qtls-go1-15 v0.1.4 h1:RehYMOyRW8hPVEja1KBVsFVNSm35Jj9Mvs5yNoZZ28A=
github.com/marten-seemann/qtls-go1-15 v0.1.4/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mdlayher/ethernet v0.0.0-20190606142754-0394541c37b7 h1:lez6TS6aAau+8wXUP3G9I3TGlmPFEq2CTxBaRqY6AGE=
github.com/mdlayher/ethernet v0.0.0-20190606142754-0394541c37b7/go.mod h1:U6ZQobyTjI/tJyq2HG+i/dfSoFUt8/aZCM+GKtmFk/Y=
github.com/mdlayher/ethtool v0.0.0-20210210192532-2b88debcdd43 h1:WgyLFv10Ov49JAQI/ZLUkCZ7VJS3r74hwFIGXJsgZlY=
github.com/mdlayher/ethtool v0.0.0-20210210192532-2b88debcdd43/go.mod h1:+t7E0lkKfbBsebllff1xdTmyJt8lH37niI6kwFk9OTo=
github.com/mdlayher/genetlink v1.0.0 h1:OoHN1OdyEIkScEmRgxLEe2M9U8ClMytqA5niynLtfj0=
github.com/mdlayher/genetlink v1.0.0/go.mod h1:0rJ0h4itni50A86M2kHcgS85ttZazNt7a8H2a2cw0Gc=
github.com/mdlayher/netlink v0.0.0-20190313131330-258ea9dff42c/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA=
github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA=
github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M=
github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY=
github.com/mdlayher/netlink v1.1.1 h1:VqG+Voq9V4uZ+04vjIrcSCWDpf91B1xxbP4QBUmUJE8=
github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o=
github.com/mdlayher/netlink v1.1.2-0.20201013204415-ded538f7f4be h1:7JeFwhE5SIdgKRd0qnqjOYJxY8AML8x/j+/qvFZ8R+c=
github.com/mdlayher/netlink v1.1.2-0.20201013204415-ded538f7f4be/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o=
github.com/mdlayher/netlink v1.2.0/go.mod h1:kwVW1io0AZy9A1E2YYgaD4Cj+C+GPkU6klXCMzIJ9p8=
github.com/mdlayher/netlink v1.2.1/go.mod h1:bacnNlfhqHqqLo4WsYeXSqfyXkInQ9JneWI68v1KwSU=
github.com/mdlayher/netlink v1.2.2-0.20210123213345-5cc92139ae3e/go.mod h1:bacnNlfhqHqqLo4WsYeXSqfyXkInQ9JneWI68v1KwSU=
github.com/mdlayher/netlink v1.3.0/go.mod h1:xK/BssKuwcRXHrtN04UBkwQ6dY9VviGGuriDdoPSWys=
github.com/mdlayher/netlink v1.4.0 h1:n3ARR+Fm0dDv37dj5wSWZXDKcy+U0zwcXS3zKMnSiT0=
github.com/mdlayher/netlink v1.4.0/go.mod h1:dRJi5IABcZpBD2A3D0Mv/AiX8I9uDEu5oGkAVrekmf8=
github.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg=
github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065 h1:aFkJ6lx4FPip+S+Uw4aTegFMct9shDvP+79PsSxpm3w=
github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg=
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/miekg/dns v1.1.29 h1:xHBEhR+t5RzcFJjBLJlax2daXOrTYtr9z4WdKEfWFzg=
github.com/miekg/dns v1.1.29/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
github.com/miekg/dns v1.1.35 h1:oTfOaDH+mZkdcgdIjH6yBajRGtIwcwcaR+rt23ZSrJs=
github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
github.com/miekg/dns v1.1.40 h1:pyyPFfGMnciYUk/mXpKkVmeMQjfXqt3FAJ2hy7tPiLA=
github.com/miekg/dns v1.1.40/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
@@ -341,13 +344,11 @@ github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7z
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.3.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.5.2 h1:qLvObTrvO/XRCqmkKxUlOBc48bI3efyDuAZe25QiF0w=
github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.6.2 h1:aIihoIOHCiLZHxyoNQ+ABL4NKhFTgKLBdMLyEAh98m0=
github.com/rogpeppe/go-internal v1.6.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.7.0 h1:3qqXGV8nn7GJT65debw77Dzrx9sfWYgP0DDo7xcMFRk=
github.com/rogpeppe/go-internal v1.7.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
@@ -381,10 +382,9 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeV
github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4=
github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
@@ -393,13 +393,11 @@ github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
github.com/spf13/cobra v0.0.6/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
github.com/spf13/cobra v1.1.1 h1:KfztREH0tPxJJ+geloSLaAkaPkr4ki2Er5quFV1TDo4=
github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
github.com/spf13/cobra v1.1.3 h1:xghbfqPkxzxP3C/f3n5DdpAbdKLj4ZE4BWQI362l53M=
github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
@@ -412,10 +410,11 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
github.com/ti-mo/netfilter v0.2.0/go.mod h1:8GbBGsY/8fxtyIdfwy29JiluNcPK4K7wIT+x42ipqUU=
@@ -455,12 +454,10 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9 h1:sYNJzB4J8toYPQTM6pAkcmBRgw9SnQKP9oXCHfgy604=
golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20201217014255-9d1352758620 h1:3wPMTskHO3+O6jqTEXyFcsnuxMQOqYSaHsDxcbUXpqA=
golang.org/x/crypto v0.0.0-20201217014255-9d1352758620/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g=
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -472,7 +469,6 @@ golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTk
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3 h1:XQyxROzUlZH+WIQwySDgnISgOivlhjIEwaQaJEJrrN0=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
@@ -502,27 +498,25 @@ golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201016165138-7b1cca2348c0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11 h1:lwlPPsmjDKK0J6eG6xDWd5XPehI0R024zxjDnw3esPA=
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20201216054612-986b41b23924 h1:QsnDpLLOKwHBBDa8nDws4DYNc/ryVW2vCpxCs09d4PY=
golang.org/x/net v0.0.0-20201216054612-986b41b23924/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@@ -535,10 +529,9 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a h1:WXEvlFVvvGxCJLG6REjsT03iWnKLEWinaScsxF2Vm2o=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -572,36 +565,36 @@ golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d h1:nc5K6ox/4lTFbMVSL9WRR81ixkcwXThoiF6yf+R9scA=
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299 h1:DYfZAGf2WMFjMxbgTjaC+2HC7NkNAQs+6Q8b9WEB/F4=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201017003518-b09fb700fbb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201112073958-5cba982894dd h1:5CtCZbICpIOFdgO940moixOPjc0178IU44m4EjOO5IY=
golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
golang.org/x/sys v0.0.0-20201118182958-a01c418693c7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201214095126-aec9a390925b h1:tv7/y4pd+sR8bcNb2D6o7BNU6zjWm0VjQLac+w7fNNM=
golang.org/x/sys v0.0.0-20201214095126-aec9a390925b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e h1:AyodaIpKjppX+cBfTASF2E1US3H2JFBj920Ot3rtDjs=
golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221 h1:/ZHdbVpdR/jk3g30/d4yUL0JU9kksj8+F/bnQUVLGDM=
golang.org/x/sys v0.0.0-20201218084310-7d0127a74742/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210110051926-789bb1bd4061/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210123111255-9b0068b26619/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210216163648-f7da38b97c65/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210309074719-68d13333faf2 h1:46ULzRKLh1CwgRq2dC5SlBzEqqNCi8rreOZnNrbqcIY=
golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -627,12 +620,10 @@ golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425 h1:VvQyQJN0tSuecqgcIxMWnnfG5kSmgy9KZR9sW3W5QeA=
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200308013534-11ec41452d41/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -679,7 +670,6 @@ google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQ
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
@@ -702,10 +692,10 @@ gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bl
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
@@ -716,8 +706,8 @@ honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
howett.net/plist v0.0.0-20201026045517-117a925f2150 h1:s7O/9fwMNd6O1yXyQ8zv+U7dfl8k+zdiLWAY8h7XdVI=
howett.net/plist v0.0.0-20201026045517-117a925f2150/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0=
howett.net/plist v0.0.0-20201203080718-1454fab16a06 h1:QDxUo/w2COstK1wIBYpzQlHX/NqaQTcf9jyz347nI58=
howett.net/plist v0.0.0-20201203080718-1454fab16a06/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=

View File

@@ -1,10 +1,11 @@
// Package agherr contains the extended error type, and the function for
// wrapping several errors.
// Package agherr contains AdGuard Home's error handling helpers.
package agherr
import (
"fmt"
"strings"
"github.com/AdguardTeam/golibs/log"
)
// Error is the constant error type.
@@ -23,8 +24,10 @@ type manyError struct {
}
// Many wraps several errors and returns a single error.
func Many(message string, underlying ...error) error {
err := &manyError{
//
// TODO(a.garipov): Add formatting to message.
func Many(message string, underlying ...error) (err error) {
err = &manyError{
message: message,
underlying: underlying,
}
@@ -33,7 +36,7 @@ func Many(message string, underlying ...error) error {
}
// Error implements the error interface for *manyError.
func (e *manyError) Error() string {
func (e *manyError) Error() (msg string) {
switch len(e.underlying) {
case 0:
return e.message
@@ -58,7 +61,7 @@ func (e *manyError) Error() string {
}
// Unwrap implements the hidden errors.wrapper interface for *manyError.
func (e *manyError) Unwrap() error {
func (e *manyError) Unwrap() (err error) {
if len(e.underlying) == 0 {
return nil
}
@@ -71,3 +74,54 @@ func (e *manyError) Unwrap() error {
type wrapper interface {
Unwrap() error
}
// Annotate annotates the error with the message, unless the error is nil. This
// is a helper function to simplify code like this:
//
// func (f *foo) doStuff(s string) (err error) {
// defer func() {
// if err != nil {
// err = fmt.Errorf("bad foo string %q: %w", s, err)
// }
// }()
//
// // …
// }
//
// Instead, write:
//
// func (f *foo) doStuff(s string) (err error) {
// defer agherr.Annotate("bad foo string %q: %w", &err, s)
//
// // …
// }
//
// msg must contain the final ": %w" verb.
//
// TODO(a.garipov): Clearify the function usage.
func Annotate(msg string, errPtr *error, args ...interface{}) {
if errPtr == nil {
return
}
err := *errPtr
if err != nil {
args = append(args, err)
*errPtr = fmt.Errorf(msg, args...)
}
}
// LogPanic is a convinient helper function to log a panic in a goroutine. It
// should not be used where proper error handling is required.
func LogPanic(prefix string) {
if v := recover(); v != nil {
if prefix != "" {
log.Error("%s: recovered from panic: %v", prefix, v)
return
}
log.Error("recovered from panic: %v", v)
}
}

View File

@@ -1,35 +1,39 @@
package agherr
import (
"bytes"
"errors"
"fmt"
"testing"
"github.com/AdguardTeam/AdGuardHome/internal/aghtest"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestError_Error(t *testing.T) {
testCases := []struct {
err error
name string
want string
err error
}{{
err: Many("a"),
name: "simple",
want: "a",
err: Many("a"),
}, {
err: Many("a", errors.New("b")),
name: "wrapping",
want: "a: b",
err: Many("a", errors.New("b")),
}, {
err: Many("a", errors.New("b"), errors.New("c"), errors.New("d")),
name: "wrapping several",
want: "a: b (hidden: c, d)",
err: Many("a", errors.New("b"), errors.New("c"), errors.New("d")),
}, {
err: Many("a", Many("b", errors.New("c"), errors.New("d"))),
name: "wrapping wrapper",
want: "a: b: c (hidden: d)",
err: Many("a", Many("b", errors.New("c"), errors.New("d"))),
}}
for _, tc := range testCases {
assert.Equal(t, tc.want, tc.err.Error(), tc.name)
}
@@ -43,33 +47,114 @@ func TestError_Unwrap(t *testing.T) {
errWrapped
errNil
)
errs := []error{
errSimple: errors.New("a"),
errWrapped: fmt.Errorf("err: %w", errors.New("nested")),
errNil: nil,
}
testCases := []struct {
name string
want error
wrapped error
name string
}{{
name: "simple",
want: errs[errSimple],
wrapped: Many("a", errs[errSimple]),
name: "simple",
}, {
name: "nested",
want: errs[errWrapped],
wrapped: Many("b", errs[errWrapped]),
name: "nested",
}, {
name: "nil passed",
want: errs[errNil],
wrapped: Many("c", errs[errNil]),
name: "nil passed",
}, {
name: "nil not passed",
want: nil,
wrapped: Many("d"),
name: "nil not passed",
}}
for _, tc := range testCases {
assert.Equal(t, tc.want, errors.Unwrap(tc.wrapped), tc.name)
}
}
func TestAnnotate(t *testing.T) {
const s = "1234"
const wantMsg = `bad string "1234": test`
// Don't use const, because we can't take a pointer of a constant.
var errTest error = Error("test")
t.Run("nil", func(t *testing.T) {
var errPtr *error
assert.NotPanics(t, func() {
Annotate("bad string %q: %w", errPtr, s)
})
})
t.Run("non_nil", func(t *testing.T) {
errPtr := &errTest
assert.NotPanics(t, func() {
Annotate("bad string %q: %w", errPtr, s)
})
require.NotNil(t, errPtr)
err := *errPtr
require.Error(t, err)
assert.Equal(t, wantMsg, err.Error())
})
t.Run("defer", func(t *testing.T) {
f := func() (err error) {
defer Annotate("bad string %q: %w", &errTest, s)
return errTest
}
err := f()
require.Error(t, err)
assert.Equal(t, wantMsg, err.Error())
})
}
func TestLogPanic(t *testing.T) {
buf := &bytes.Buffer{}
aghtest.ReplaceLogWriter(t, buf)
t.Run("prefix", func(t *testing.T) {
const (
panicMsg = "spooky!"
prefix = "packagename"
errWithNoPrefix = "[error] recovered from panic: spooky!"
errWithPrefix = "[error] packagename: recovered from panic: spooky!"
)
panicFunc := func(prefix string) {
defer LogPanic(prefix)
panic(panicMsg)
}
panicFunc("")
assert.Contains(t, buf.String(), errWithNoPrefix)
buf.Reset()
panicFunc(prefix)
assert.Contains(t, buf.String(), errWithPrefix)
buf.Reset()
})
t.Run("don't_panic", func(t *testing.T) {
require.NotPanics(t, func() {
defer LogPanic("")
})
assert.Empty(t, buf.String())
})
}

View File

@@ -13,21 +13,21 @@ import (
func TestLimitReadCloser(t *testing.T) {
testCases := []struct {
want error
name string
n int64
want error
}{{
want: nil,
name: "positive",
n: 1,
want: nil,
}, {
want: nil,
name: "zero",
n: 0,
want: nil,
}, {
want: fmt.Errorf("aghio: invalid n in LimitReadCloser: -1"),
name: "negative",
n: -1,
want: fmt.Errorf("aghio: invalid n in LimitReadCloser: -1"),
}}
for _, tc := range testCases {
@@ -40,37 +40,37 @@ func TestLimitReadCloser(t *testing.T) {
func TestLimitedReadCloser_Read(t *testing.T) {
testCases := []struct {
name string
limit int64
rStr string
want int
err error
name string
rStr string
limit int64
want int
}{{
name: "perfectly_match",
limit: 3,
rStr: "abc",
want: 3,
err: nil,
}, {
name: "eof",
limit: 3,
rStr: "",
want: 0,
err: io.EOF,
}, {
name: "limit_reached",
limit: 0,
name: "perfectly_match",
rStr: "abc",
limit: 3,
want: 3,
}, {
err: io.EOF,
name: "eof",
rStr: "",
limit: 3,
want: 0,
}, {
err: &LimitReachedError{
Limit: 0,
},
}, {
name: "truncated",
limit: 2,
name: "limit_reached",
rStr: "abc",
want: 2,
limit: 0,
want: 0,
}, {
err: nil,
name: "truncated",
rStr: "abc",
limit: 2,
want: 2,
}}
for _, tc := range testCases {
@@ -79,7 +79,7 @@ func TestLimitedReadCloser_Read(t *testing.T) {
buf := make([]byte, tc.limit+1)
lreader, err := LimitReadCloser(readCloser, tc.limit)
require.Nil(t, err)
require.NoError(t, err)
n, err := lreader.Read(buf)
require.Equal(t, tc.err, err)
@@ -90,15 +90,15 @@ func TestLimitedReadCloser_Read(t *testing.T) {
func TestLimitedReadCloser_LimitReachedError(t *testing.T) {
testCases := []struct {
err error
name string
want string
err error
}{{
name: "simplest",
want: "attempted to read more than 0 bytes",
err: &LimitReachedError{
Limit: 0,
},
name: "simplest",
want: "attempted to read more than 0 bytes",
}}
for _, tc := range testCases {

View File

@@ -1,14 +1,14 @@
package home
package aghnet
import "net"
// ipDetector describes IP address properties.
type ipDetector struct {
// IPDetector describes IP address properties.
type IPDetector struct {
nets []*net.IPNet
}
// newIPDetector returns a new IP detector.
func newIPDetector() (ipd *ipDetector, err error) {
// NewIPDetector returns a new IP detector.
func NewIPDetector() (ipd *IPDetector, err error) {
specialNetworks := []string{
"0.0.0.0/8",
"10.0.0.0/8",
@@ -43,11 +43,12 @@ func newIPDetector() (ipd *ipDetector, err error) {
"fe80::/10",
}
ipd = &ipDetector{
ipd = &IPDetector{
nets: make([]*net.IPNet, len(specialNetworks)),
}
for i, ipnetStr := range specialNetworks {
_, ipnet, err := net.ParseCIDR(ipnetStr)
var ipnet *net.IPNet
_, ipnet, err = net.ParseCIDR(ipnetStr)
if err != nil {
return nil, err
}
@@ -58,10 +59,10 @@ func newIPDetector() (ipd *ipDetector, err error) {
return ipd, nil
}
// detectSpecialNetwork returns true if IP address is contained by any of
// DetectSpecialNetwork returns true if IP address is contained by any of
// special-purpose IP address registries according to RFC-6890
// (https://tools.ietf.org/html/rfc6890).
func (ipd *ipDetector) detectSpecialNetwork(ip net.IP) bool {
func (ipd *IPDetector) DetectSpecialNetwork(ip net.IP) bool {
for _, ipnet := range ipd.nets {
if ipnet.Contains(ip) {
return true

View File

@@ -1,4 +1,4 @@
package home
package aghnet
import (
"net"
@@ -9,11 +9,11 @@ import (
)
func TestIPDetector_detectSpecialNetwork(t *testing.T) {
var ipd *ipDetector
var ipd *IPDetector
var err error
ipd, err = newIPDetector()
require.Nil(t, err)
ipd, err = NewIPDetector()
require.NoError(t, err)
testCases := []struct {
name string
@@ -139,7 +139,7 @@ func TestIPDetector_detectSpecialNetwork(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
assert.Equal(t, tc.want, ipd.detectSpecialNetwork(tc.ip))
assert.Equal(t, tc.want, ipd.DetectSpecialNetwork(tc.ip))
})
}
}

View File

@@ -1,4 +1,5 @@
package util
// Package aghnet contains some utilities for networking.
package aghnet
import (
"encoding/json"
@@ -6,14 +7,70 @@ import (
"fmt"
"net"
"os"
"os/exec"
"runtime"
"strconv"
"strings"
"syscall"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/agherr"
"github.com/AdguardTeam/golibs/log"
)
// ErrNoStaticIPInfo is returned by IfaceHasStaticIP when no information about
// the IP being static is available.
const ErrNoStaticIPInfo agherr.Error = "no information about static ip"
// IfaceHasStaticIP checks if interface is configured to have static IP address.
// If it can't give a definitive answer, it returns false and an error for which
// errors.Is(err, ErrNoStaticIPInfo) is true.
func IfaceHasStaticIP(ifaceName string) (has bool, err error) {
return ifaceHasStaticIP(ifaceName)
}
// IfaceSetStaticIP sets static IP address for network interface.
func IfaceSetStaticIP(ifaceName string) (err error) {
return ifaceSetStaticIP(ifaceName)
}
// GatewayIP returns IP address of interface's gateway.
func GatewayIP(ifaceName string) net.IP {
cmd := exec.Command("ip", "route", "show", "dev", ifaceName)
log.Tracef("executing %s %v", cmd.Path, cmd.Args)
d, err := cmd.Output()
if err != nil || cmd.ProcessState.ExitCode() != 0 {
return nil
}
fields := strings.Fields(string(d))
// The meaningful "ip route" command output should contain the word
// "default" at first field and default gateway IP address at third
// field.
if len(fields) < 3 || fields[0] != "default" {
return nil
}
return net.ParseIP(fields[2])
}
// CanBindPort checks if we can bind to the given port.
func CanBindPort(port int) (can bool, err error) {
var addr *net.TCPAddr
addr, err = net.ResolveTCPAddr("tcp", fmt.Sprintf("127.0.0.1:%d", port))
if err != nil {
return false, err
}
var listener *net.TCPListener
listener, err = net.ListenTCP("tcp", addr)
if err != nil {
return false, err
}
_ = listener.Close()
return true, nil
}
// NetInterface represents an entry of network interfaces map.
type NetInterface struct {
MTU int `json:"mtu"`
@@ -69,7 +126,8 @@ func GetValidNetInterfacesForWeb() ([]*NetInterface, error) {
var netInterfaces []*NetInterface
for _, iface := range ifaces {
addrs, err := iface.Addrs()
var addrs []net.Addr
addrs, err = iface.Addrs()
if err != nil {
return nil, fmt.Errorf("failed to get addresses for interface %s: %w", iface.Name, err)
}
@@ -193,3 +251,25 @@ func ErrorIsAddrInUse(err error) bool {
return errErrno == syscall.EADDRINUSE
}
// SplitHost is a wrapper for net.SplitHostPort for the cases when the hostport
// does not necessarily contain a port.
func SplitHost(hostport string) (host string, err error) {
host, _, err = net.SplitHostPort(hostport)
if err != nil {
// Check for the missing port error. If it is that error, just
// use the host as is.
//
// See the source code for net.SplitHostPort.
const missingPort = "missing port in address"
addrErr := &net.AddrError{}
if !errors.As(err, &addrErr) || addrErr.Err != missingPort {
return "", err
}
host = hostport
}
return host, nil
}

View File

@@ -1,6 +1,6 @@
// +build darwin
package sysutil
package aghnet
import (
"errors"
@@ -9,7 +9,7 @@ import (
"regexp"
"strings"
"github.com/AdguardTeam/AdGuardHome/internal/util"
"github.com/AdguardTeam/AdGuardHome/internal/aghos"
)
// hardwarePortInfo - information obtained using MacOS networksetup
@@ -47,7 +47,7 @@ func getCurrentHardwarePortInfo(ifaceName string) (hardwarePortInfo, error) {
// it returns a map where the key is the interface name, and the value is the "hardware port"
// returns nil if it fails to parse the output
func getNetworkSetupHardwareReports() map[string]string {
_, out, err := util.RunCommand("networksetup", "-listallhardwareports")
_, out, err := aghos.RunCommand("networksetup", "-listallhardwareports")
if err != nil {
return nil
}
@@ -72,7 +72,7 @@ func getNetworkSetupHardwareReports() map[string]string {
func getHardwarePortInfo(hardwarePort string) (hardwarePortInfo, error) {
h := hardwarePortInfo{}
_, out, err := util.RunCommand("networksetup", "-getinfo", hardwarePort)
_, out, err := aghos.RunCommand("networksetup", "-getinfo", hardwarePort)
if err != nil {
return h, err
}
@@ -116,7 +116,7 @@ func ifaceSetStaticIP(ifaceName string) (err error) {
args = append(args, dnsAddrs...)
// Setting DNS servers is necessary when configuring a static IP
code, _, err := util.RunCommand("networksetup", args...)
code, _, err := aghos.RunCommand("networksetup", args...)
if err != nil {
return err
}
@@ -125,7 +125,7 @@ func ifaceSetStaticIP(ifaceName string) (err error) {
}
// Actually configures hardware port to have static IP
code, _, err = util.RunCommand("networksetup", "-setmanual",
code, _, err = aghos.RunCommand("networksetup", "-setmanual",
portInfo.name, portInfo.ip, portInfo.subnet, portInfo.gatewayIP)
if err != nil {
return err

View File

@@ -1,6 +1,6 @@
// +build linux
package sysutil
package aghnet
import (
"bufio"
@@ -13,7 +13,6 @@ import (
"strings"
"github.com/AdguardTeam/AdGuardHome/internal/aghio"
"github.com/AdguardTeam/AdGuardHome/internal/util"
"github.com/AdguardTeam/golibs/file"
)
@@ -132,7 +131,7 @@ func ifacesStaticConfig(r io.Reader, ifaceName string) (has bool, err error) {
}
func ifaceSetStaticIP(ifaceName string) (err error) {
ipNet := util.GetSubnet(ifaceName)
ipNet := GetSubnet(ifaceName)
if ipNet.IP == nil {
return errors.New("can't get IP address")
}

View File

@@ -1,6 +1,6 @@
// +build linux
package sysutil
package aghnet
import (
"bytes"
@@ -49,7 +49,8 @@ func TestDHCPCDStaticConfig(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
r := bytes.NewReader(tc.data)
has, err := dhcpcdStaticConfig(r, "wlan0")
require.Nil(t, err)
require.NoError(t, err)
assert.Equal(t, tc.want, has)
})
}
@@ -86,7 +87,8 @@ func TestIfacesStaticConfig(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
r := bytes.NewReader(tc.data)
has, err := ifacesStaticConfig(r, "enp0s3")
require.Nil(t, err)
require.NoError(t, err)
assert.Equal(t, tc.want, has)
})
}

View File

@@ -1,6 +1,6 @@
// +build !linux,!darwin
package sysutil
package aghnet
import (
"fmt"

View File

@@ -1,4 +1,4 @@
package util
package aghnet
import (
"testing"
@@ -8,9 +8,9 @@ import (
func TestGetValidNetInterfacesForWeb(t *testing.T) {
ifaces, err := GetValidNetInterfacesForWeb()
require.Nilf(t, err, "Cannot get net interfaces: %s", err)
require.NotEmpty(t, ifaces, "No net interfaces found")
require.NoErrorf(t, err, "cannot get net interfaces: %s", err)
require.NotEmpty(t, ifaces, "no net interfaces found")
for _, iface := range ifaces {
require.NotEmptyf(t, iface.Addresses, "No addresses found for %s", iface.Name)
require.NotEmptyf(t, iface.Addresses, "no addresses found for %s", iface.Name)
}
}

View File

@@ -0,0 +1,78 @@
package aghnet
import (
"time"
"github.com/AdguardTeam/AdGuardHome/internal/agherr"
"github.com/AdguardTeam/golibs/log"
)
// DefaultRefreshIvl is the default period of time between refreshing cached
// addresses.
// const DefaultRefreshIvl = 5 * time.Minute
// HostGenFunc is the signature for functions generating fake hostnames. The
// implementation must be safe for concurrent use.
type HostGenFunc func() (host string)
// unit is an alias for an existing map value.
type unit = struct{}
// SystemResolvers helps to work with local resolvers' addresses provided by OS.
type SystemResolvers interface {
// Get returns the slice of local resolvers' addresses.
// It should be safe for concurrent use.
Get() (rs []string)
// Refresh refreshes the local resolvers' addresses cache. It should be
// safe for concurrent use.
Refresh() (err error)
}
const (
// fakeDialErr is an error which dialFunc is expected to return.
fakeDialErr agherr.Error = "this error signals the successful dialFunc work"
// badAddrPassedErr is returned when dialFunc can't parse an IP address.
badAddrPassedErr agherr.Error = "the passed string is not a valid IP address"
)
// refreshWithTicker refreshes the cache of sr after each tick form tickCh.
func refreshWithTicker(sr SystemResolvers, tickCh <-chan time.Time) {
defer agherr.LogPanic("systemResolvers")
// TODO(e.burkov): Implement a functionality to stop ticker.
for range tickCh {
err := sr.Refresh()
if err != nil {
log.Error("systemResolvers: error in refreshing goroutine: %s", err)
continue
}
log.Debug("systemResolvers: local addresses cache is refreshed")
}
}
// NewSystemResolvers returns a SystemResolvers with the cache refresh rate
// defined by refreshIvl. It disables auto-resfreshing if refreshIvl is 0. If
// nil is passed for hostGenFunc, the default generator will be used.
func NewSystemResolvers(
refreshIvl time.Duration,
hostGenFunc HostGenFunc,
) (sr SystemResolvers, err error) {
sr = newSystemResolvers(refreshIvl, hostGenFunc)
// Fill cache.
err = sr.Refresh()
if err != nil {
return nil, err
}
if refreshIvl > 0 {
ticker := time.NewTicker(refreshIvl)
go refreshWithTicker(sr, ticker.C)
}
return sr, nil
}

View File

@@ -0,0 +1,96 @@
// +build !windows
package aghnet
import (
"context"
"errors"
"fmt"
"net"
"sync"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/agherr"
)
// defaultHostGen is the default method of generating host for Refresh.
func defaultHostGen() (host string) {
// TODO(e.burkov): Use strings.Builder.
return fmt.Sprintf("test%d.org", time.Now().UnixNano())
}
// systemResolvers is a default implementation of SystemResolvers interface.
type systemResolvers struct {
resolver *net.Resolver
hostGenFunc HostGenFunc
// addrs is the map that contains cached local resolvers' addresses.
addrs map[string]unit
addrsLock sync.RWMutex
}
func (sr *systemResolvers) Refresh() (err error) {
defer agherr.Annotate("systemResolvers: %w", &err)
_, err = sr.resolver.LookupHost(context.Background(), sr.hostGenFunc())
dnserr := &net.DNSError{}
if errors.As(err, &dnserr) && dnserr.Err == fakeDialErr.Error() {
return nil
}
return err
}
func newSystemResolvers(refreshIvl time.Duration, hostGenFunc HostGenFunc) (sr SystemResolvers) {
if hostGenFunc == nil {
hostGenFunc = defaultHostGen
}
s := &systemResolvers{
resolver: &net.Resolver{
PreferGo: true,
},
hostGenFunc: hostGenFunc,
addrs: make(map[string]unit),
}
s.resolver.Dial = s.dialFunc
return s
}
// dialFunc gets the resolver's address and puts it into internal cache.
func (sr *systemResolvers) dialFunc(_ context.Context, _, address string) (_ net.Conn, err error) {
// Just validate the passed address is a valid IP.
var host string
host, err = SplitHost(address)
if err != nil {
// TODO(e.burkov): Maybe use a structured badAddrPassedErr to
// allow unwrapping of the real error.
return nil, fmt.Errorf("%s: %w", err, badAddrPassedErr)
}
if net.ParseIP(host) == nil {
return nil, fmt.Errorf("parsing %q: %w", host, badAddrPassedErr)
}
sr.addrsLock.Lock()
defer sr.addrsLock.Unlock()
sr.addrs[address] = unit{}
return nil, fakeDialErr
}
func (sr *systemResolvers) Get() (rs []string) {
sr.addrsLock.RLock()
defer sr.addrsLock.RUnlock()
addrs := sr.addrs
rs = make([]string, len(addrs))
var i int
for addr := range addrs {
rs[i] = addr
i++
}
return rs
}

View File

@@ -0,0 +1,74 @@
// +build !windows
package aghnet
import (
"context"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func createTestSystemResolversImp(
t *testing.T,
refreshDur time.Duration,
hostGenFunc HostGenFunc,
) (imp *systemResolvers) {
t.Helper()
sr := createTestSystemResolvers(t, refreshDur, hostGenFunc)
var ok bool
imp, ok = sr.(*systemResolvers)
require.True(t, ok)
return imp
}
func TestSystemResolvers_Refresh(t *testing.T) {
t.Run("expected_error", func(t *testing.T) {
sr := createTestSystemResolvers(t, 0, nil)
assert.NoError(t, sr.Refresh())
})
t.Run("unexpected_error", func(t *testing.T) {
_, err := NewSystemResolvers(0, func() string {
return "127.0.0.1::123"
})
assert.Error(t, err)
})
}
func TestSystemResolvers_DialFunc(t *testing.T) {
imp := createTestSystemResolversImp(t, 0, nil)
testCases := []struct {
name string
address string
want error
}{{
name: "valid",
address: "127.0.0.1",
want: fakeDialErr,
}, {
name: "invalid_split_host",
address: "127.0.0.1::123",
want: badAddrPassedErr,
}, {
name: "invalid_parse_ip",
address: "not-ip",
want: badAddrPassedErr,
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
conn, err := imp.dialFunc(context.Background(), "", tc.address)
require.Nil(t, conn)
assert.ErrorIs(t, err, tc.want)
})
}
}

View File

@@ -0,0 +1,33 @@
package aghnet
import (
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func createTestSystemResolvers(
t *testing.T,
refreshDur time.Duration,
hostGenFunc HostGenFunc,
) (sr SystemResolvers) {
t.Helper()
var err error
sr, err = NewSystemResolvers(refreshDur, hostGenFunc)
require.NoError(t, err)
require.NotNil(t, sr)
return sr
}
func TestSystemResolvers_Get(t *testing.T) {
sr := createTestSystemResolvers(t, 0, nil)
assert.NotEmpty(t, sr.Get())
}
// TODO(e.burkov): Write tests for refreshWithTicker.
//
// See https://github.com/AdguardTeam/AdGuardHome/issues/2846.

View File

@@ -0,0 +1,158 @@
// +build windows
package aghnet
import (
"bufio"
"fmt"
"io"
"net"
"os/exec"
"strings"
"sync"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/agherr"
"github.com/AdguardTeam/AdGuardHome/internal/aghio"
"github.com/AdguardTeam/AdGuardHome/internal/aghos"
"github.com/AdguardTeam/golibs/log"
)
// systemResolvers implementation differs for Windows since Go's resolver
// doesn't work there.
//
// See https://github.com/golang/go/issues/33097.
type systemResolvers struct {
// addrs is the slice of cached local resolvers' addresses.
addrs []string
addrsLock sync.RWMutex
}
func newSystemResolvers(refreshIvl time.Duration, _ HostGenFunc) (sr SystemResolvers) {
return &systemResolvers{}
}
func (sr *systemResolvers) Get() (rs []string) {
sr.addrsLock.RLock()
defer sr.addrsLock.RUnlock()
addrs := sr.addrs
rs = make([]string, len(addrs))
copy(rs, addrs)
return rs
}
// getAddrs gets local resolvers' addresses from OS in a special Windows way.
//
// TODO(e.burkov): This whole function needs more detailed research on getting
// local resolvers addresses on Windows. We execute the external command for
// now that is not the most accurate way.
func (sr *systemResolvers) getAddrs() (addrs []string, err error) {
cmd := exec.Command("nslookup")
var stdin io.WriteCloser
stdin, err = cmd.StdinPipe()
if err != nil {
return nil, fmt.Errorf("getting the command's stdin pipe: %w", err)
}
var stdout io.ReadCloser
stdout, err = cmd.StdoutPipe()
if err != nil {
return nil, fmt.Errorf("getting the command's stdout pipe: %w", err)
}
var stdoutLimited io.ReadCloser
stdoutLimited, err = aghio.LimitReadCloser(stdout, aghos.MaxCmdOutputSize)
if err != nil {
return nil, fmt.Errorf("limiting stdout reader: %w", err)
}
go func() {
defer agherr.LogPanic("systemResolvers")
defer func() {
derr := stdin.Close()
if derr != nil {
log.Error("systemResolvers: closing stdin pipe: %s", derr)
}
}()
_, werr := io.WriteString(stdin, "exit")
if werr != nil {
log.Error("systemResolvers: writing to command pipe: %s", werr)
}
}()
err = cmd.Start()
if err != nil {
return nil, fmt.Errorf("start command executing: %w", err)
}
// The output of nslookup looks like this:
//
// Default Server: 192-168-1-1.qualified.domain.ru
// Address: 192.168.1.1
var possibleIPs []string
s := bufio.NewScanner(stdoutLimited)
for s.Scan() {
line := s.Text()
if len(line) == 0 {
continue
}
fields := strings.Fields(line)
if len(fields) != 2 || fields[0] != "Address:" {
continue
}
// If the address contains port then it is separated with '#'.
ipStrs := strings.Split(fields[1], "#")
if len(ipStrs) == 0 {
continue
}
possibleIPs = append(possibleIPs, ipStrs[0])
}
err = cmd.Wait()
if err != nil {
return nil, fmt.Errorf("executing the command: %w", err)
}
// Don't close StdoutPipe since Wait do it for us in ¿most? cases.
//
// See go doc os/exec.Cmd.StdoutPipe.
for _, addr := range possibleIPs {
if net.ParseIP(addr) == nil {
log.Debug("systemResolvers: %q is not a valid ip", addr)
continue
}
addrs = append(addrs, addr)
}
return addrs, nil
}
func (sr *systemResolvers) Refresh() (err error) {
defer agherr.Annotate("systemResolvers: %w", &err)
got, err := sr.getAddrs()
if err != nil {
return fmt.Errorf("can't get addresses: %w", err)
}
if len(got) == 0 {
return nil
}
sr.addrsLock.Lock()
defer sr.addrsLock.Unlock()
sr.addrs = got
return nil
}

View File

@@ -0,0 +1,7 @@
// +build windows
package aghnet
// TODO(e.burkov): Write tests for Windows implementation.
//
// See https://github.com/AdguardTeam/AdGuardHome/issues/2846.

View File

@@ -0,0 +1,10 @@
// +build mips mips64
// This file is an adapted version of github.com/josharian/native.
package aghos
import "encoding/binary"
// NativeEndian is the native endianness of this system.
var NativeEndian = binary.BigEndian

View File

@@ -0,0 +1,10 @@
// +build amd64 386 arm arm64 mipsle mips64le ppc64le
// This file is an adapted version of github.com/josharian/native.
package aghos
import "encoding/binary"
// NativeEndian is the native endianness of this system.
var NativeEndian = binary.LittleEndian

47
internal/aghos/os.go Normal file
View File

@@ -0,0 +1,47 @@
// Package aghos contains utilities for functions requiring system calls.
package aghos
import (
"fmt"
"os/exec"
"syscall"
)
// CanBindPrivilegedPorts checks if current process can bind to privileged
// ports.
func CanBindPrivilegedPorts() (can bool, err error) {
return canBindPrivilegedPorts()
}
// SetRlimit sets user-specified limit of how many fd's we can use
// https://github.com/AdguardTeam/AdGuardHome/internal/issues/659.
func SetRlimit(val uint) {
setRlimit(val)
}
// HaveAdminRights checks if the current user has root (administrator) rights.
func HaveAdminRights() (bool, error) {
return haveAdminRights()
}
// SendProcessSignal sends signal to a process.
func SendProcessSignal(pid int, sig syscall.Signal) error {
return sendProcessSignal(pid, sig)
}
// MaxCmdOutputSize is the maximum length of performed shell command output.
const MaxCmdOutputSize = 2 * 1024
// RunCommand runs shell command.
func RunCommand(command string, arguments ...string) (int, string, error) {
cmd := exec.Command(command, arguments...)
out, err := cmd.Output()
if len(out) > MaxCmdOutputSize {
out = out[:MaxCmdOutputSize]
}
if err != nil {
return 1, "", fmt.Errorf("exec.Command(%s) failed: %v: %s", command, err, string(out))
}
return cmd.ProcessState.ExitCode(), string(out), nil
}

View File

@@ -1,6 +1,6 @@
// +build freebsd
package sysutil
package aghos
import (
"os"

View File

@@ -1,6 +1,6 @@
// +build linux
package sysutil
package aghos
import (
"os"

View File

@@ -1,6 +1,6 @@
// +build aix darwin dragonfly netbsd openbsd solaris
package sysutil
package aghos
import (
"os"

View File

@@ -1,6 +1,6 @@
// +build windows
package sysutil
package aghos
import (
"fmt"

View File

@@ -1,4 +1,4 @@
package sysutil
package aghos
// ConfigureSyslog reroutes standard logger output to syslog.
func ConfigureSyslog(serviceName string) error {

View File

@@ -1,6 +1,6 @@
// +build !windows,!plan9
package sysutil
package aghos
import (
"log/syslog"

View File

@@ -1,6 +1,6 @@
// +build windows plan9
package sysutil
package aghos
import (
"strings"

View File

@@ -1,4 +1,4 @@
package sysutil
package aghos
import (
"testing"

View File

@@ -1,45 +0,0 @@
package aghtest
import (
"io/ioutil"
"os"
"runtime"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// PrepareTestDir returns the full path to temporary created directory and
// registers the appropriate cleanup for *t.
func PrepareTestDir(t *testing.T) (dir string) {
t.Helper()
wd, err := os.Getwd()
require.Nil(t, err)
dir, err = ioutil.TempDir(wd, "agh-test")
require.Nil(t, err)
require.NotEmpty(t, dir)
t.Cleanup(func() {
// TODO(e.burkov): Replace with t.TempDir methods after updating
// go version to 1.15.
start := time.Now()
for {
err := os.RemoveAll(dir)
if err == nil {
break
}
if runtime.GOOS != "windows" || time.Since(start) >= 500*time.Millisecond {
break
}
time.Sleep(5 * time.Millisecond)
}
assert.Nil(t, err)
})
return dir
}

View File

@@ -14,23 +14,17 @@ type TestResolver struct {
}
// HostToIPs generates IPv4 and IPv6 from host.
//
// TODO(e.burkov): Replace with LookupIP after upgrading go to v1.15.
func (r *TestResolver) HostToIPs(host string) (ipv4, ipv6 net.IP) {
hash := sha256.Sum256([]byte(host))
return net.IP(hash[:4]), net.IP(hash[4:20])
}
// LookupIPAddr implements Resolver interface for *testResolver. It returns the
// slice of net.IPAddr with IPv4 and IPv6 instances.
func (r *TestResolver) LookupIPAddr(_ context.Context, host string) (ips []net.IPAddr, err error) {
// LookupIP implements Resolver interface for *testResolver. It returns the
// slice of net.IP with IPv4 and IPv6 instances.
func (r *TestResolver) LookupIP(_ context.Context, _, host string) (ips []net.IP, err error) {
ipv4, ipv6 := r.HostToIPs(host)
addrs := []net.IPAddr{{
IP: ipv4,
}, {
IP: ipv6,
}}
addrs := []net.IP{ipv4, ipv6}
r.counterLock.Lock()
defer r.counterLock.Unlock()

View File

@@ -3,12 +3,12 @@ package aghtest
import (
"crypto/sha256"
"encoding/hex"
"errors"
"fmt"
"net"
"strings"
"sync"
"github.com/AdguardTeam/AdGuardHome/internal/agherr"
"github.com/miekg/dns"
)
@@ -166,7 +166,10 @@ type TestErrUpstream struct{}
// Exchange always returns nil Msg and non-nil error.
func (u *TestErrUpstream) Exchange(*dns.Msg) (*dns.Msg, error) {
return nil, agherr.Error("bad")
// We don't use an agherr.Error to avoid the import cycle since aghtests
// used to provide the utilities for testing which agherr (and any other
// testable package) should be able to use.
return nil, errors.New("bad")
}
// Address always returns an empty string.

52
internal/dhcpd/bitset.go Normal file
View File

@@ -0,0 +1,52 @@
package dhcpd
const bitsPerWord = 64
// bitSet is a sparse bitSet. A nil *bitSet is an empty bitSet.
type bitSet struct {
words map[uint64]uint64
}
// newBitSet returns a new bitset.
func newBitSet() (s *bitSet) {
return &bitSet{
words: map[uint64]uint64{},
}
}
// isSet returns true if the bit n is set.
func (s *bitSet) isSet(n uint64) (ok bool) {
if s == nil {
return false
}
wordIdx := n / bitsPerWord
bitIdx := n % bitsPerWord
var word uint64
word, ok = s.words[wordIdx]
if !ok {
return false
}
return word&(1<<bitIdx) != 0
}
// set sets or unsets a bit.
func (s *bitSet) set(n uint64, ok bool) {
if s == nil {
return
}
wordIdx := n / bitsPerWord
bitIdx := n % bitsPerWord
word := s.words[wordIdx]
if ok {
word |= 1 << bitIdx
} else {
word &^= 1 << bitIdx
}
s.words[wordIdx] = word
}

View File

@@ -0,0 +1,90 @@
package dhcpd
import (
"math"
"testing"
"testing/quick"
"github.com/stretchr/testify/assert"
)
func TestBitSet(t *testing.T) {
t.Run("nil", func(t *testing.T) {
var s *bitSet
ok := s.isSet(0)
assert.False(t, ok)
assert.NotPanics(t, func() {
s.set(0, true)
})
ok = s.isSet(0)
assert.False(t, ok)
assert.NotPanics(t, func() {
s.set(0, false)
})
ok = s.isSet(0)
assert.False(t, ok)
})
t.Run("non_nil", func(t *testing.T) {
s := newBitSet()
ok := s.isSet(0)
assert.False(t, ok)
s.set(0, true)
ok = s.isSet(0)
assert.True(t, ok)
s.set(0, false)
ok = s.isSet(0)
assert.False(t, ok)
})
t.Run("non_nil_long", func(t *testing.T) {
s := newBitSet()
s.set(0, true)
s.set(math.MaxUint64, true)
assert.Len(t, s.words, 2)
ok := s.isSet(0)
assert.True(t, ok)
ok = s.isSet(math.MaxUint64)
assert.True(t, ok)
})
t.Run("compare_to_map", func(t *testing.T) {
m := map[uint64]struct{}{}
s := newBitSet()
mapFunc := func(setNew, checkOld, delOld uint64) (ok bool) {
m[setNew] = struct{}{}
delete(m, delOld)
_, ok = m[checkOld]
return ok
}
setFunc := func(setNew, checkOld, delOld uint64) (ok bool) {
s.set(setNew, true)
s.set(delOld, false)
ok = s.isSet(checkOld)
return ok
}
err := quick.CheckEqual(mapFunc, setFunc, &quick.Config{
MaxCount: 10_000,
MaxCountScale: 10,
})
assert.NoError(t, err)
})
}

View File

@@ -20,7 +20,7 @@ import (
// CheckIfOtherDHCPServersPresentV4 sends a DHCP request to the specified network interface,
// and waits for a response for a period defined by defaultDiscoverTime
func CheckIfOtherDHCPServersPresentV4(ifaceName string) (bool, error) {
func CheckIfOtherDHCPServersPresentV4(ifaceName string) (ok bool, err error) {
iface, err := net.InterfaceByName(ifaceName)
if err != nil {
return false, fmt.Errorf("couldn't find interface by name %s: %w", ifaceName, err)
@@ -86,7 +86,8 @@ func CheckIfOtherDHCPServersPresentV4(ifaceName string) (bool, error) {
}
for {
ok, next, err := tryConn4(req, c, iface)
var next bool
ok, next, err = tryConn4(req, c, iface)
if next {
if err != nil {
log.Debug("dhcpv4: trying a connection: %s", err)
@@ -94,6 +95,7 @@ func CheckIfOtherDHCPServersPresentV4(ifaceName string) (bool, error) {
continue
}
if err != nil {
return false, err
}
@@ -155,7 +157,7 @@ func tryConn4(req *dhcpv4.DHCPv4, c net.PacketConn, iface *net.Interface) (ok, n
// CheckIfOtherDHCPServersPresentV6 sends a DHCP request to the specified network interface,
// and waits for a response for a period defined by defaultDiscoverTime
func CheckIfOtherDHCPServersPresentV6(ifaceName string) (bool, error) {
func CheckIfOtherDHCPServersPresentV6(ifaceName string) (ok bool, err error) {
iface, err := net.InterfaceByName(ifaceName)
if err != nil {
return false, fmt.Errorf("dhcpv6: net.InterfaceByName: %s: %w", ifaceName, err)
@@ -207,7 +209,8 @@ func CheckIfOtherDHCPServersPresentV6(ifaceName string) (bool, error) {
}
for {
ok, next, err := tryConn6(req, c)
var next bool
ok, next, err = tryConn6(req, c)
if next {
if err != nil {
log.Debug("dhcpv6: trying a connection: %s", err)
@@ -215,6 +218,7 @@ func CheckIfOtherDHCPServersPresentV6(ifaceName string) (bool, error) {
continue
}
if err != nil {
return false, err
}

View File

@@ -4,6 +4,7 @@ package dhcpd
import (
"encoding/json"
"errors"
"io/ioutil"
"net"
"os"
@@ -39,9 +40,10 @@ func (s *Server) dbLoad() {
data, err := ioutil.ReadFile(s.conf.DBFilePath)
if err != nil {
if !os.IsNotExist(err) {
log.Error("DHCP: can't read file %s: %v", s.conf.DBFilePath, err)
if !errors.Is(err, os.ErrNotExist) {
log.Error("dhcp: can't read file %q: %v", s.conf.DBFilePath, err)
}
return
}
@@ -126,7 +128,7 @@ func normalizeLeases(staticLeases, dynLeases []*Lease) []*Lease {
func (s *Server) dbStore() {
var leases []leaseJSON
leases4 := s.srv4.GetLeasesRef()
leases4 := s.srv4.getLeasesRef()
for _, l := range leases4 {
if l.Expiry.Unix() == 0 {
continue
@@ -141,7 +143,7 @@ func (s *Server) dbStore() {
}
if s.srv6 != nil {
leases6 := s.srv6.GetLeasesRef()
leases6 := s.srv6.getLeasesRef()
for _, l := range leases6 {
if l.Expiry.Unix() == 0 {
continue

View File

@@ -2,18 +2,14 @@
package dhcpd
import (
"encoding/hex"
"encoding/json"
"fmt"
"net"
"net/http"
"path/filepath"
"runtime"
"strconv"
"strings"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/util"
"github.com/AdguardTeam/golibs/log"
)
@@ -40,16 +36,23 @@ type Lease struct {
Expiry time.Time `json:"expires"`
}
// IsStatic returns true if the lease is static.
//
// TODO(a.garipov): Just make it a boolean field.
func (l *Lease) IsStatic() (ok bool) {
return l != nil && l.Expiry.Unix() == leaseExpireStatic
}
// MarshalJSON implements the json.Marshaler interface for *Lease.
func (l *Lease) MarshalJSON() ([]byte, error) {
var expiryStr string
if expiry := l.Expiry; expiry.Unix() != leaseExpireStatic {
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 = expiry.Format(time.RFC3339)
expiryStr = l.Expiry.Format(time.RFC3339)
}
type lease Lease
@@ -207,6 +210,7 @@ func Create(conf ServerConfig) *Server {
func (s *Server) onNotify(flags uint32) {
if flags == LeaseChangedDBStore {
s.dbStore()
return
}
@@ -222,6 +226,7 @@ func (s *Server) notify(flags int) {
if len(s.onLeaseChanged) == 0 {
return
}
for _, f := range s.onLeaseChanged {
f(flags)
}
@@ -283,48 +288,3 @@ func (s *Server) FindMACbyIP(ip net.IP) net.HardwareAddr {
func (s *Server) AddStaticLease(lease Lease) error {
return s.srv4.AddStaticLease(lease)
}
// Parse option string
// Format:
// CODE TYPE VALUE
func parseOptionString(s string) (uint8, []byte) {
s = strings.TrimSpace(s)
scode := util.SplitNext(&s, ' ')
t := util.SplitNext(&s, ' ')
sval := util.SplitNext(&s, ' ')
code, err := strconv.Atoi(scode)
if err != nil || code <= 0 || code > 255 {
return 0, nil
}
var val []byte
switch t {
case "hex":
val, err = hex.DecodeString(sval)
if err != nil {
return 0, nil
}
case "ip":
ip := net.ParseIP(sval)
if ip == nil {
return 0, nil
}
// Most DHCP options require IPv4, so do not put the 16-byte
// version if we can. Otherwise, the clients will receive weird
// data that looks like four IPv4 addresses.
//
// See https://github.com/AdguardTeam/AdGuardHome/issues/2688.
if ip4 := ip.To4(); ip4 != nil {
val = ip4
} else {
val = ip
}
default:
return 0, nil
}
return uint8(code), val
}

View File

@@ -54,7 +54,8 @@ func TestDB(t *testing.T) {
srv4, ok := s.srv4.(*v4Server)
require.True(t, ok)
srv4.addLease(&leases[0])
err = srv4.addLease(&leases[0])
require.Nil(t, err)
require.Nil(t, s.srv4.AddStaticLease(leases[1]))
s.dbStore()
@@ -69,7 +70,7 @@ func TestDB(t *testing.T) {
assert.Equal(t, leases[1].HWAddr, ll[0].HWAddr)
assert.Equal(t, leases[1].IP, ll[0].IP)
assert.EqualValues(t, leaseExpireStatic, ll[0].Expiry.Unix())
assert.True(t, ll[0].IsStatic())
assert.Equal(t, leases[0].HWAddr, ll[1].HWAddr)
assert.Equal(t, leases[0].IP, ll[1].IP)
@@ -124,67 +125,3 @@ func TestNormalizeLeases(t *testing.T) {
assert.Equal(t, leases[1].HWAddr, staticLeases[1].HWAddr)
assert.Equal(t, leases[2].HWAddr, dynLeases[1].HWAddr)
}
func TestOptions(t *testing.T) {
testCases := []struct {
name string
optStr string
wantVal []byte
wantCode uint8
}{{
name: "success_hex",
optStr: "12 hex abcdef",
wantVal: []byte{0xab, 0xcd, 0xef},
wantCode: 12,
}, {
name: "bad_hex",
optStr: "12 hex abcdefx",
wantVal: nil,
wantCode: 0,
}, {
name: "success_ip",
optStr: "123 ip 1.2.3.4",
wantVal: net.IP{1, 2, 3, 4},
wantCode: 123,
}, {
name: "success_ipv6",
optStr: "123 ip ::1234",
wantVal: net.IP{
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0x12, 0x34,
},
wantCode: 123,
}, {
name: "bad_code",
optStr: "256 ip 1.1.1.1",
wantVal: nil,
wantCode: 0,
}, {
name: "negative_code",
optStr: "-1 ip 1.1.1.1",
wantVal: nil,
wantCode: 0,
}, {
name: "bad_ip",
optStr: "12 ip 1.1.1.1x",
wantVal: nil,
wantCode: 0,
}, {
name: "bad_mode",
wantVal: nil,
optStr: "12 x 1.1.1.1",
wantCode: 0,
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
code, val := parseOptionString(tc.optStr)
require.Equal(t, tc.wantCode, code)
if tc.wantVal != nil {
assert.Equal(t, tc.wantVal, val)
}
})
}
}

View File

@@ -10,8 +10,7 @@ import (
"os"
"strings"
"github.com/AdguardTeam/AdGuardHome/internal/sysutil"
"github.com/AdguardTeam/AdGuardHome/internal/util"
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
"github.com/AdguardTeam/golibs/log"
)
@@ -93,7 +92,7 @@ func (s *Server) handleDHCPStatus(w http.ResponseWriter, r *http.Request) {
func (s *Server) enableDHCP(ifaceName string) (code int, err error) {
var hasStaticIP bool
hasStaticIP, err = sysutil.IfaceHasStaticIP(ifaceName)
hasStaticIP, err = aghnet.IfaceHasStaticIP(ifaceName)
if err != nil {
if errors.Is(err, os.ErrPermission) {
// ErrPermission may happen here on Linux systems where
@@ -110,7 +109,7 @@ func (s *Server) enableDHCP(ifaceName string) (code int, err error) {
log.Info("error while checking static ip: %s; "+
"assuming machine has static ip and going on", err)
hasStaticIP = true
} else if errors.Is(err, sysutil.ErrNoStaticIPInfo) {
} else if errors.Is(err, aghnet.ErrNoStaticIPInfo) {
// Couldn't obtain a definitive answer. Assume static
// IP an go on.
log.Info("can't check for static ip; " +
@@ -124,7 +123,7 @@ func (s *Server) enableDHCP(ifaceName string) (code int, err error) {
}
if !hasStaticIP {
err = sysutil.IfaceSetStaticIP(ifaceName)
err = aghnet.IfaceSetStaticIP(ifaceName)
if err != nil {
err = fmt.Errorf("setting static ip: %w", err)
@@ -267,7 +266,7 @@ type netInterfaceJSON struct {
func (s *Server) handleDHCPInterfaces(w http.ResponseWriter, r *http.Request) {
response := map[string]netInterfaceJSON{}
ifaces, err := util.GetValidNetInterfaces()
ifaces, err := aghnet.GetValidNetInterfaces()
if err != nil {
httpError(r, w, http.StatusInternalServerError, "Couldn't get interfaces: %s", err)
return
@@ -282,7 +281,9 @@ func (s *Server) handleDHCPInterfaces(w http.ResponseWriter, r *http.Request) {
// this interface doesn't support broadcast, skip it
continue
}
addrs, err := iface.Addrs()
var addrs []net.Addr
addrs, err = iface.Addrs()
if err != nil {
httpError(r, w, http.StatusInternalServerError, "Failed to get addresses for interface %s: %s", iface.Name, err)
return
@@ -315,7 +316,7 @@ func (s *Server) handleDHCPInterfaces(w http.ResponseWriter, r *http.Request) {
}
}
if len(jsonIface.Addrs4)+len(jsonIface.Addrs6) != 0 {
jsonIface.GatewayIP = sysutil.GatewayIP(iface.Name)
jsonIface.GatewayIP = aghnet.GatewayIP(iface.Name)
response[iface.Name] = jsonIface
}
}
@@ -395,13 +396,13 @@ func (s *Server) handleDHCPFindActiveServer(w http.ResponseWriter, r *http.Reque
found4, err4 := CheckIfOtherDHCPServersPresentV4(interfaceName)
isStaticIP, err := sysutil.IfaceHasStaticIP(interfaceName)
isStaticIP, err := aghnet.IfaceHasStaticIP(interfaceName)
if err != nil {
result.V4.StaticIP.Static = "error"
result.V4.StaticIP.Error = err.Error()
} else if !isStaticIP {
result.V4.StaticIP.Static = "no"
result.V4.StaticIP.IP = util.GetSubnet(interfaceName).String()
result.V4.StaticIP.IP = aghnet.GetSubnet(interfaceName).String()
}
if found4 {
@@ -506,8 +507,8 @@ func (s *Server) handleReset(w http.ResponseWriter, r *http.Request) {
s.Stop()
err := os.Remove(s.conf.DBFilePath)
if err != nil && !os.IsNotExist(err) {
log.Error("DHCP: os.Remove: %s: %s", s.conf.DBFilePath, err)
if err != nil && !errors.Is(err, os.ErrNotExist) {
log.Error("dhcp: removing %q: %s", s.conf.DBFilePath, err)
}
oldconf := s.conf

112
internal/dhcpd/iprange.go Normal file
View File

@@ -0,0 +1,112 @@
package dhcpd
import (
"fmt"
"math"
"math/big"
"net"
"github.com/AdguardTeam/AdGuardHome/internal/agherr"
)
// ipRange is an inclusive range of IP addresses. A nil range is a range that
// doesn't contain any IP addresses.
//
// It is safe for concurrent use.
//
// TODO(a.garipov): Perhaps create an optimised version with uint32 for
// IPv4 ranges? Or use one of uint128 packages?
type ipRange struct {
start *big.Int
end *big.Int
}
// maxRangeLen is the maximum IP range length. The bitsets used in servers only
// accept uints, which can have the size of 32 bit.
const maxRangeLen = math.MaxUint32
// newIPRange creates a new IP address range. start must be less than end. The
// resulting range must not be greater than maxRangeLen.
func newIPRange(start, end net.IP) (r *ipRange, err error) {
defer agherr.Annotate("invalid ip range: %w", &err)
// Make sure that both are 16 bytes long to simplify handling in
// methods.
start, end = start.To16(), end.To16()
startInt := (&big.Int{}).SetBytes(start)
endInt := (&big.Int{}).SetBytes(end)
diff := (&big.Int{}).Sub(endInt, startInt)
if diff.Sign() <= 0 {
return nil, fmt.Errorf("start is greater than or equal to end")
} else if !diff.IsUint64() || diff.Uint64() > maxRangeLen {
return nil, fmt.Errorf("range is too large")
}
r = &ipRange{
start: startInt,
end: endInt,
}
return r, nil
}
// contains returns true if r contains ip.
func (r *ipRange) contains(ip net.IP) (ok bool) {
if r == nil {
return false
}
ipInt := (&big.Int{}).SetBytes(ip.To16())
return r.containsInt(ipInt)
}
// containsInt returns true if r contains ipInt. For internal use only.
func (r *ipRange) containsInt(ipInt *big.Int) (ok bool) {
return ipInt.Cmp(r.start) >= 0 && ipInt.Cmp(r.end) <= 0
}
// ipPredicate is a function that is called on every IP address in
// (*ipRange).find. ip is given in the 16-byte form.
type ipPredicate func(ip net.IP) (ok bool)
// find finds the first IP address in r for which p returns true. ip is in the
// 16-byte form.
func (r *ipRange) find(p ipPredicate) (ip net.IP) {
if r == nil {
return nil
}
ip = make(net.IP, net.IPv6len)
_1 := big.NewInt(1)
for i := (&big.Int{}).Set(r.start); i.Cmp(r.end) <= 0; i.Add(i, _1) {
i.FillBytes(ip)
if p(ip) {
return ip
}
}
return nil
}
// offset returns the offset of ip from the beginning of r. It returns 0 and
// false if ip is not in r.
func (r *ipRange) offset(ip net.IP) (offset uint64, ok bool) {
if r == nil {
return 0, false
}
ip = ip.To16()
ipInt := (&big.Int{}).SetBytes(ip)
if !r.containsInt(ipInt) {
return 0, false
}
offsetInt := (&big.Int{}).Sub(ipInt, r.start)
// Assume that the range was checked against maxRangeLen during
// construction.
return offsetInt.Uint64(), true
}

View File

@@ -0,0 +1,154 @@
package dhcpd
import (
"net"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestNewIPRange(t *testing.T) {
start4 := net.IP{0, 0, 0, 1}
end4 := net.IP{0, 0, 0, 3}
start6 := net.IP{
0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01,
}
end6 := net.IP{
0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x03,
}
end6Large := net.IP{
0x02, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x03,
}
testCases := []struct {
name string
wantErrMsg string
start net.IP
end net.IP
}{{
name: "success_ipv4",
wantErrMsg: "",
start: start4,
end: end4,
}, {
name: "success_ipv6",
wantErrMsg: "",
start: start6,
end: end6,
}, {
name: "start_gt_end",
wantErrMsg: "invalid ip range: start is greater than or equal to end",
start: end4,
end: start4,
}, {
name: "start_eq_end",
wantErrMsg: "invalid ip range: start is greater than or equal to end",
start: start4,
end: start4,
}, {
name: "too_large",
wantErrMsg: "invalid ip range: range is too large",
start: start6,
end: end6Large,
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
r, err := newIPRange(tc.start, tc.end)
if tc.wantErrMsg == "" {
assert.NoError(t, err)
assert.NotNil(t, r)
} else {
require.Error(t, err)
assert.Equal(t, tc.wantErrMsg, err.Error())
}
})
}
}
func TestIPRange_Contains(t *testing.T) {
start, end := net.IP{0, 0, 0, 1}, net.IP{0, 0, 0, 3}
r, err := newIPRange(start, end)
require.NoError(t, err)
assert.True(t, r.contains(start))
assert.True(t, r.contains(net.IP{0, 0, 0, 2}))
assert.True(t, r.contains(end))
assert.False(t, r.contains(net.IP{0, 0, 0, 0}))
assert.False(t, r.contains(net.IP{0, 0, 0, 4}))
}
func TestIPRange_Find(t *testing.T) {
start, end := net.IP{0, 0, 0, 1}, net.IP{0, 0, 0, 5}
r, err := newIPRange(start, end)
require.NoError(t, err)
want := net.IPv4(0, 0, 0, 2)
got := r.find(func(ip net.IP) (ok bool) {
return ip[len(ip)-1]%2 == 0
})
assert.Equal(t, want, got)
got = r.find(func(ip net.IP) (ok bool) {
return ip[len(ip)-1]%10 == 0
})
assert.Nil(t, got)
}
func TestIPRange_Offset(t *testing.T) {
start, end := net.IP{0, 0, 0, 1}, net.IP{0, 0, 0, 5}
r, err := newIPRange(start, end)
require.NoError(t, err)
testCases := []struct {
name string
in net.IP
wantOffset uint64
wantOK bool
}{{
name: "in",
in: net.IP{0, 0, 0, 2},
wantOffset: 1,
wantOK: true,
}, {
name: "in_start",
in: start,
wantOffset: 0,
wantOK: true,
}, {
name: "in_end",
in: end,
wantOffset: 4,
wantOK: true,
}, {
name: "out_after",
in: net.IP{0, 0, 0, 6},
wantOffset: 0,
wantOK: false,
}, {
name: "out_before",
in: net.IP{0, 0, 0, 0},
wantOffset: 0,
wantOK: false,
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
offset, ok := r.offset(tc.in)
assert.Equal(t, tc.wantOffset, offset)
assert.Equal(t, tc.wantOK, ok)
})
}
}

View File

@@ -476,7 +476,7 @@ func (c *Client) send(dest *net.UDPAddr, msg *dhcpv4.DHCPv4) (resp <-chan *dhcpv
c.pendingMu.Unlock()
}
if _, err := c.conn.WriteTo(msg.ToBytes(), dest); err != nil {
if _, err = c.conn.WriteTo(msg.ToBytes(), dest); err != nil {
cancel()
return nil, nil, fmt.Errorf("error writing packet to connection: %w", err)
}

View File

@@ -206,7 +206,7 @@ func TestSendAndRead(t *testing.T) {
t.Error(err)
}
if err := ComparePacket(rcvd, tt.want); err != nil {
if err = ComparePacket(rcvd, tt.want); err != nil {
t.Errorf("got unexpected packets: %v", err)
}
})
@@ -290,7 +290,7 @@ func TestSimpleSendAndReadDiscardGarbage(t *testing.T) {
t.Errorf("SendAndRead(%v) = %v, want nil", pkt, err)
}
if err := ComparePacket(rcvd, responses); err != nil {
if err = ComparePacket(rcvd, responses); err != nil {
t.Errorf("got unexpected packets: %v", err)
}
}
@@ -337,7 +337,7 @@ func TestMultipleSendAndRead(t *testing.T) {
if wantErr := tt.wantErr[i]; err != wantErr {
t.Errorf("SendAndReadOne(%v): got %v, want %v", send, err, wantErr)
}
if err := pktsExpected([]*dhcpv4.DHCPv4{rcvd}, tt.server[i]); err != nil {
if err = pktsExpected([]*dhcpv4.DHCPv4{rcvd}, tt.server[i]); err != nil {
t.Errorf("got unexpected packets: %v", err)
}
}

134
internal/dhcpd/options.go Normal file
View File

@@ -0,0 +1,134 @@
package dhcpd
import (
"encoding/hex"
"fmt"
"net"
"strconv"
"strings"
"github.com/AdguardTeam/AdGuardHome/internal/agherr"
)
// hexDHCPOptionParserHandler parses a DHCP option as a hex-encoded string.
// For example:
//
// 252 hex 736f636b733a2f2f70726f78792e6578616d706c652e6f7267
//
func hexDHCPOptionParserHandler(s string) (data []byte, err error) {
data, err = hex.DecodeString(s)
if err != nil {
return nil, fmt.Errorf("decoding hex: %w", err)
}
return data, nil
}
// ipDHCPOptionParserHandler parses a DHCP option as a single IP address.
// For example:
//
// 6 ip 192.168.1.1
//
func ipDHCPOptionParserHandler(s string) (data []byte, err error) {
ip := net.ParseIP(s)
if ip == nil {
return nil, agherr.Error("invalid ip")
}
// Most DHCP options require IPv4, so do not put the 16-byte
// version if we can. Otherwise, the clients will receive weird
// data that looks like four IPv4 addresses.
//
// See https://github.com/AdguardTeam/AdGuardHome/issues/2688.
if ip4 := ip.To4(); ip4 != nil {
data = ip4
} else {
data = ip
}
return data, nil
}
// textDHCPOptionParserHandler parses a DHCP option as a simple UTF-8 encoded
// text. For example:
//
// 252 text http://192.168.1.1/wpad.dat
//
func ipsDHCPOptionParserHandler(s string) (data []byte, err error) {
ipStrs := strings.Split(s, ",")
for i, ipStr := range ipStrs {
var ipData []byte
ipData, err = ipDHCPOptionParserHandler(ipStr)
if err != nil {
return nil, fmt.Errorf("parsing ip at index %d: %w", i, err)
}
data = append(data, ipData...)
}
return data, nil
}
// ipsDHCPOptionParserHandler parses a DHCP option as a comma-separates list of
// IP addresses. For example:
//
// 6 ips 192.168.1.1,192.168.1.2
//
func textDHCPOptionParserHandler(s string) (data []byte, err error) {
return []byte(s), nil
}
// dhcpOptionParserHandler is a parser for a single dhcp option type.
type dhcpOptionParserHandler func(s string) (data []byte, err error)
// dhcpOptionParser parses DHCP options.
type dhcpOptionParser struct {
handlers map[string]dhcpOptionParserHandler
}
// newDHCPOptionParser returns a new dhcpOptionParser.
func newDHCPOptionParser() (p *dhcpOptionParser) {
return &dhcpOptionParser{
handlers: map[string]dhcpOptionParserHandler{
"hex": hexDHCPOptionParserHandler,
"ip": ipDHCPOptionParserHandler,
"ips": ipsDHCPOptionParserHandler,
"text": textDHCPOptionParserHandler,
},
}
}
// parse parses an option. See the handlers' documentation for more info.
func (p *dhcpOptionParser) parse(s string) (code uint8, data []byte, err error) {
defer agherr.Annotate("invalid option string %q: %w", &err, s)
s = strings.TrimSpace(s)
parts := strings.SplitN(s, " ", 3)
if len(parts) < 3 {
return 0, nil, agherr.Error("need at least three fields")
}
codeStr := parts[0]
typ := parts[1]
val := parts[2]
var code64 uint64
code64, err = strconv.ParseUint(codeStr, 10, 8)
if err != nil {
return 0, nil, fmt.Errorf("parsing option code: %w", err)
}
code = uint8(code64)
h, ok := p.handlers[typ]
if !ok {
return 0, nil, fmt.Errorf("unknown option type %q", typ)
}
data, err = h(val)
if err != nil {
return 0, nil, err
}
return uint8(code), data, nil
}

View File

@@ -0,0 +1,109 @@
package dhcpd
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestDHCPOptionParser(t *testing.T) {
testCases := []struct {
name string
in string
wantErrMsg string
wantData []byte
wantCode uint8
}{{
name: "hex_success",
in: "6 hex c0a80101c0a80102",
wantErrMsg: "",
wantData: []byte{0xC0, 0xA8, 0x01, 0x01, 0xC0, 0xA8, 0x01, 0x02},
wantCode: 6,
}, {
name: "ip_success",
in: "6 ip 1.2.3.4",
wantErrMsg: "",
wantData: []byte{0x01, 0x02, 0x03, 0x04},
wantCode: 6,
}, {
name: "ip_success_v6",
in: "6 ip ::1234",
wantErrMsg: "",
wantData: []byte{
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x12, 0x34,
},
wantCode: 6,
}, {
name: "ips_success",
in: "6 ips 192.168.1.1,192.168.1.2",
wantErrMsg: "",
wantData: []byte{0xC0, 0xA8, 0x01, 0x01, 0xC0, 0xA8, 0x01, 0x02},
wantCode: 6,
}, {
name: "text_success",
in: "252 text http://192.168.1.1/",
wantErrMsg: "",
wantData: []byte("http://192.168.1.1/"),
wantCode: 252,
}, {
name: "bad_parts",
in: "6 ip",
wantErrMsg: `invalid option string "6 ip": need at least three fields`,
wantCode: 0,
wantData: nil,
}, {
name: "bad_code",
in: "256 ip 1.1.1.1",
wantErrMsg: `invalid option string "256 ip 1.1.1.1": parsing option code: ` +
`strconv.ParseUint: parsing "256": value out of range`,
wantCode: 0,
wantData: nil,
}, {
name: "bad_type",
in: "6 bad 1.1.1.1",
wantErrMsg: `invalid option string "6 bad 1.1.1.1": unknown option type "bad"`,
wantCode: 0,
wantData: nil,
}, {
name: "hex_error",
in: "6 hex ZZZ",
wantErrMsg: `invalid option string "6 hex ZZZ": decoding hex: ` +
`encoding/hex: invalid byte: U+005A 'Z'`,
wantData: nil,
wantCode: 0,
}, {
name: "ip_error",
in: "6 ip 1.2.3.x",
wantErrMsg: `invalid option string "6 ip 1.2.3.x": invalid ip`,
wantData: nil,
wantCode: 0,
}, {
name: "ips_error",
in: "6 ips 192.168.1.1,192.168.1.x",
wantErrMsg: `invalid option string "6 ips 192.168.1.1,192.168.1.x": ` +
`parsing ip at index 1: invalid ip`,
wantData: nil,
wantCode: 0,
}}
p := newDHCPOptionParser()
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
code, data, err := p.parse(tc.in)
if tc.wantErrMsg == "" {
assert.Nil(t, err)
} else {
require.NotNil(t, err)
assert.Equal(t, tc.wantErrMsg, err.Error())
}
assert.Equal(t, tc.wantCode, code)
assert.Equal(t, tc.wantData, data)
})
}
}

View File

@@ -188,20 +188,20 @@ func (ra *raCtx) Init() error {
}
defer func() {
if !success {
cerr := ra.Close()
if cerr != nil {
log.Error("closing context: %s", cerr)
derr := ra.Close()
if derr != nil {
log.Error("closing context: %s", derr)
}
}
}()
con6 := ra.conn.IPv6PacketConn()
if err := con6.SetHopLimit(255); err != nil {
if err = con6.SetHopLimit(255); err != nil {
return fmt.Errorf("dhcpv6 ra: SetHopLimit: %w", err)
}
if err := con6.SetMulticastHopLimit(255); err != nil {
if err = con6.SetMulticastHopLimit(255); err != nil {
return fmt.Errorf("dhcpv6 ra: SetMulticastHopLimit: %w", err)
}

View File

@@ -11,8 +11,6 @@ type DHCPServer interface {
ResetLeases(leases []*Lease)
// GetLeases - get leases
GetLeases(flags int) []Lease
// GetLeasesRef - get reference to leases array
GetLeasesRef() []*Lease
// AddStaticLease - add a static lease
AddStaticLease(lease Lease) error
// RemoveStaticLease - remove a static lease
@@ -29,6 +27,8 @@ type DHCPServer interface {
Start() error
// Stop - stop server
Stop()
getLeasesRef() []*Lease
}
// V4ServerConf - server configuration
@@ -60,15 +60,20 @@ type V4ServerConf struct {
// DEC_CODE ip IP_ADDR
Options []string `yaml:"options" json:"-"`
ipStart net.IP // starting IP address for dynamic leases
ipEnd net.IP // ending IP address for dynamic leases
ipRange *ipRange
leaseTime time.Duration // the time during which a dynamic lease is considered valid
dnsIPAddrs []net.IP // IPv4 addresses to return to DHCP clients as DNS server addresses
routerIP net.IP // value for Option Router
subnetMask net.IPMask // value for Option SubnetMask
options []dhcpOption
// Server calls this function when leases data changes
// notify is a way to signal to other components that leases have
// change. notify must be called outside of locked sections, since the
// clients might want to get the new data.
//
// TODO(a.garipov): This is utter madness and must be refactored. It
// just begs for deadlock bugs and other nastiness.
notify func(uint32)
}
@@ -96,5 +101,5 @@ type V6ServerConf struct {
type dhcpOption struct {
code uint8
val []byte
data []byte
}

View File

@@ -4,12 +4,12 @@ package dhcpd
import (
"bytes"
"encoding/binary"
"fmt"
"net"
"sync"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/agherr"
"github.com/AdguardTeam/golibs/log"
"github.com/go-ping/ping"
"github.com/insomniacslk/dhcp/dhcpv4"
@@ -20,13 +20,18 @@ import (
//
// TODO(a.garipov): Think about unifying this and v6Server.
type v4Server struct {
srv *server4.Server
leasesLock sync.Mutex
leases []*Lease
// TODO(e.burkov): This field type should be a normal bitmap.
ipAddrs [256]byte
conf V4ServerConf
srv *server4.Server
// leasedOffsets contains offsets from conf.ipRange.start that have been
// leased.
leasedOffsets *bitSet
// leases contains all dynamic and static leases.
leases []*Lease
// leasesLock protects leases and leasedOffsets.
leasesLock sync.Mutex
}
// WriteDiskConfig4 - write configuration
@@ -38,67 +43,92 @@ func (s *v4Server) WriteDiskConfig4(c *V4ServerConf) {
func (s *v4Server) WriteDiskConfig6(c *V6ServerConf) {
}
// Return TRUE if IP address is within range [start..stop]
func ip4InRange(start, stop, ip net.IP) bool {
if len(start) != 4 || len(stop) != 4 {
return false
}
from := binary.BigEndian.Uint32(start)
to := binary.BigEndian.Uint32(stop)
check := binary.BigEndian.Uint32(ip)
return from <= check && check <= to
}
// ResetLeases - reset leases
func (s *v4Server) ResetLeases(leases []*Lease) {
s.leases = nil
r := s.conf.ipRange
for _, l := range leases {
if !l.IsStatic() && !r.contains(l.IP) {
log.Debug(
"dhcpv4: skipping lease %s (%s): not within current ip range",
l.IP,
l.HWAddr,
)
if l.Expiry.Unix() != leaseExpireStatic &&
!ip4InRange(s.conf.ipStart, s.conf.ipEnd, l.IP) {
log.Debug("dhcpv4: skipping a lease with IP %v: not within current IP range", l.IP)
continue
}
s.addLease(l)
err := s.addLease(l)
if err != nil {
// TODO(a.garipov): Better error handling.
log.Error("dhcpv4: adding a lease for %s (%s): %s", l.IP, l.HWAddr, err)
continue
}
}
}
// GetLeasesRef - get leases
func (s *v4Server) GetLeasesRef() []*Lease {
// getLeasesRef returns the actual leases slice. For internal use only.
func (s *v4Server) getLeasesRef() []*Lease {
return s.leases
}
// Return TRUE if this lease holds a blacklisted IP
func (s *v4Server) blacklisted(l *Lease) bool {
return l.HWAddr.String() == "00:00:00:00:00:00"
}
// isBlocklisted returns true if this lease holds a blocklisted IP.
//
// TODO(a.garipov): Make a method of *Lease?
func (s *v4Server) isBlocklisted(l *Lease) (ok bool) {
if len(l.HWAddr) == 0 {
return false
}
// GetLeases returns the list of current DHCP leases (thread-safe)
func (s *v4Server) GetLeases(flags int) []Lease {
// The function shouldn't return nil value because zero-length slice
// behaves differently in cases like marshalling. Our front-end also
// requires non-nil value in the response.
result := []Lease{}
now := time.Now().Unix()
ok = true
for _, b := range l.HWAddr {
if b != 0 {
ok = false
s.leasesLock.Lock()
for _, lease := range s.leases {
if ((flags&LeasesDynamic) != 0 && lease.Expiry.Unix() > now && !s.blacklisted(lease)) ||
((flags&LeasesStatic) != 0 && lease.Expiry.Unix() == leaseExpireStatic) {
result = append(result, *lease)
break
}
}
s.leasesLock.Unlock()
return result
return ok
}
// GetLeases returns the list of current DHCP leases. It is safe for concurrent
// use.
func (s *v4Server) GetLeases(flags int) (res []Lease) {
// The function shouldn't return nil, because zero-length slice behaves
// differently in cases like marshalling. Our front-end also requires
// a non-nil value in the response.
res = []Lease{}
// TODO(a.garipov): Remove the silly bit twiddling and make GetLeases
// accept booleans. Seriously, this doesn't even save stack space.
getDynamic := flags&LeasesDynamic != 0
getStatic := flags&LeasesStatic != 0
s.leasesLock.Lock()
defer s.leasesLock.Unlock()
now := time.Now()
for _, l := range s.leases {
if getDynamic && l.Expiry.After(now) && !s.isBlocklisted(l) {
res = append(res, *l)
continue
}
if getStatic && l.IsStatic() {
res = append(res, *l)
}
}
return res
}
// FindMACbyIP - find a MAC address by IP address in the currently active DHCP leases
func (s *v4Server) FindMACbyIP(ip net.IP) net.HardwareAddr {
now := time.Now().Unix()
now := time.Now()
s.leasesLock.Lock()
defer s.leasesLock.Unlock()
@@ -110,111 +140,193 @@ func (s *v4Server) FindMACbyIP(ip net.IP) net.HardwareAddr {
for _, l := range s.leases {
if l.IP.Equal(ip4) {
unix := l.Expiry.Unix()
if unix > now || unix == leaseExpireStatic {
if l.Expiry.After(now) || l.IsStatic() {
return l.HWAddr
}
}
}
return nil
}
// defaultHwAddrLen is the default length of a hardware (MAC) address.
const defaultHwAddrLen = 6
// Add the specified IP to the black list for a time period
func (s *v4Server) blacklistLease(lease *Lease) {
hw := make(net.HardwareAddr, 6)
lease.HWAddr = hw
lease.Hostname = ""
lease.Expiry = time.Now().Add(s.conf.leaseTime)
func (s *v4Server) blocklistLease(l *Lease) {
l.HWAddr = make(net.HardwareAddr, defaultHwAddrLen)
l.Hostname = ""
l.Expiry = time.Now().Add(s.conf.leaseTime)
}
// Remove (swap) lease by index
func (s *v4Server) leaseRemoveSwapByIndex(i int) {
s.ipAddrs[s.leases[i].IP[3]] = 0
log.Debug("dhcpv4: removed lease %s", s.leases[i].HWAddr)
// rmLeaseByIndex removes a lease by its index in the leases slice.
func (s *v4Server) rmLeaseByIndex(i int) {
n := len(s.leases)
if i != n-1 {
s.leases[i] = s.leases[n-1] // swap with the last element
if i >= n {
// TODO(a.garipov): Better error handling.
log.Debug("dhcpv4: can't remove lease at index %d: no such lease", i)
return
}
s.leases = s.leases[:n-1]
l := s.leases[i]
s.leases = append(s.leases[:i], s.leases[i+1:]...)
n = len(s.leases)
if n > 0 {
s.leases = s.leases[:n-1]
}
r := s.conf.ipRange
offset, ok := r.offset(l.IP)
if ok {
s.leasedOffsets.set(offset, false)
}
log.Debug("dhcpv4: removed lease %s (%s)", l.IP, l.HWAddr)
}
// Remove a dynamic lease with the same properties
// Return error if a static lease is found
func (s *v4Server) rmDynamicLease(lease Lease) error {
func (s *v4Server) rmDynamicLease(lease *Lease) (err error) {
for i := 0; i < len(s.leases); i++ {
l := s.leases[i]
if bytes.Equal(l.HWAddr, lease.HWAddr) {
if l.Expiry.Unix() == leaseExpireStatic {
if l.IsStatic() {
return fmt.Errorf("static lease already exists")
}
s.leaseRemoveSwapByIndex(i)
s.rmLeaseByIndex(i)
if i == len(s.leases) {
break
}
l = s.leases[i]
}
if net.IP.Equal(l.IP, lease.IP) {
if l.Expiry.Unix() == leaseExpireStatic {
if l.IsStatic() {
return fmt.Errorf("static lease already exists")
}
s.leaseRemoveSwapByIndex(i)
s.rmLeaseByIndex(i)
}
}
return nil
}
// Add a lease
func (s *v4Server) addLease(l *Lease) {
func (s *v4Server) addStaticLease(l *Lease) (err error) {
subnet := &net.IPNet{
IP: s.conf.routerIP,
Mask: s.conf.subnetMask,
}
if !subnet.Contains(l.IP) {
return fmt.Errorf("subnet %s does not contain the ip %q", subnet, l.IP)
}
s.leases = append(s.leases, l)
s.ipAddrs[l.IP[3]] = 1
log.Debug("dhcpv4: added lease %s <-> %s", l.IP, l.HWAddr)
r := s.conf.ipRange
offset, ok := r.offset(l.IP)
if ok {
s.leasedOffsets.set(offset, true)
}
return nil
}
func (s *v4Server) addDynamicLease(l *Lease) (err error) {
r := s.conf.ipRange
offset, ok := r.offset(l.IP)
if !ok {
return fmt.Errorf("lease %s (%s) out of range, not adding", l.IP, l.HWAddr)
}
s.leases = append(s.leases, l)
s.leasedOffsets.set(offset, true)
return nil
}
// addLease adds a dynamic or static lease.
func (s *v4Server) addLease(l *Lease) (err error) {
if l.IsStatic() {
return s.addStaticLease(l)
}
return s.addDynamicLease(l)
}
// Remove a lease with the same properties
func (s *v4Server) rmLease(lease Lease) error {
for i, l := range s.leases {
if net.IP.Equal(l.IP, lease.IP) {
if len(s.leases) == 0 {
return nil
}
if !bytes.Equal(l.HWAddr, lease.HWAddr) ||
l.Hostname != lease.Hostname {
return fmt.Errorf("lease not found")
for i, l := range s.leases {
if l.IP.Equal(lease.IP) {
if !bytes.Equal(l.HWAddr, lease.HWAddr) || l.Hostname != lease.Hostname {
return fmt.Errorf("lease for ip %s is different: %+v", lease.IP, l)
}
s.leaseRemoveSwapByIndex(i)
s.rmLeaseByIndex(i)
return nil
}
}
return fmt.Errorf("lease not found")
return agherr.Error("lease not found")
}
// AddStaticLease adds a static lease (thread-safe)
func (s *v4Server) AddStaticLease(lease Lease) error {
if len(lease.IP) != 4 {
return fmt.Errorf("invalid IP")
}
if len(lease.HWAddr) != 6 {
return fmt.Errorf("invalid MAC")
}
lease.Expiry = time.Unix(leaseExpireStatic, 0)
// AddStaticLease adds a static lease. It is safe for concurrent use.
func (s *v4Server) AddStaticLease(l Lease) (err error) {
defer agherr.Annotate("dhcpv4: %w", &err)
s.leasesLock.Lock()
err := s.rmDynamicLease(lease)
if ip4 := l.IP.To4(); ip4 == nil {
return fmt.Errorf("invalid ip %q, only ipv4 is supported", l.IP)
}
if len(l.HWAddr) != 6 {
return fmt.Errorf("invalid mac %q, only EUI-48 is supported", l.HWAddr)
}
l.Expiry = time.Unix(leaseExpireStatic, 0)
// Perform the following actions in an anonymous function to make sure
// that the lock gets unlocked before the notification step.
func() {
s.leasesLock.Lock()
defer s.leasesLock.Unlock()
err = s.rmDynamicLease(&l)
if err != nil {
err = fmt.Errorf(
"removing dynamic leases for %s (%s): %w",
l.IP,
l.HWAddr,
err,
)
return
}
err = s.addLease(&l)
if err != nil {
err = fmt.Errorf("adding static lease for %s (%s): %w", l.IP, l.HWAddr, err)
return
}
}()
if err != nil {
s.leasesLock.Unlock()
return err
}
s.addLease(&lease)
s.conf.notify(LeaseChangedDBStore)
s.leasesLock.Unlock()
s.conf.notify(LeaseChangedDBStore)
s.conf.notify(LeaseChangedAddedStatic)
return nil
}
@@ -231,12 +343,14 @@ func (s *v4Server) RemoveStaticLease(l Lease) error {
err := s.rmLease(l)
if err != nil {
s.leasesLock.Unlock()
return err
}
s.conf.notify(LeaseChangedDBStore)
s.leasesLock.Unlock()
s.conf.notify(LeaseChangedDBStore)
s.conf.notify(LeaseChangedRemovedStatic)
return nil
}
@@ -258,7 +372,7 @@ func (s *v4Server) addrAvailable(target net.IP) bool {
pinger.Timeout = time.Duration(s.conf.ICMPTimeout) * time.Millisecond
pinger.Count = 1
reply := false
pinger.OnRecv = func(pkt *ping.Packet) {
pinger.OnRecv = func(_ *ping.Packet) {
reply = true
}
log.Debug("dhcpv4: Sending ICMP Echo to %v", target)
@@ -278,62 +392,72 @@ func (s *v4Server) addrAvailable(target net.IP) bool {
return true
}
// Find lease by MAC
func (s *v4Server) findLease(mac net.HardwareAddr) *Lease {
for i := range s.leases {
if bytes.Equal(mac, s.leases[i].HWAddr) {
return s.leases[i]
// findLease finds a lease by its MAC-address.
func (s *v4Server) findLease(mac net.HardwareAddr) (l *Lease) {
for _, l = range s.leases {
if bytes.Equal(mac, l.HWAddr) {
return l
}
}
return nil
}
// Get next free IP
func (s *v4Server) findFreeIP() net.IP {
for i := s.conf.ipStart[3]; ; i++ {
if s.ipAddrs[i] == 0 {
ip := make([]byte, 4)
copy(ip, s.conf.ipStart)
ip[3] = i
return ip
// nextIP generates a new free IP.
func (s *v4Server) nextIP() (ip net.IP) {
r := s.conf.ipRange
ip = r.find(func(next net.IP) (ok bool) {
offset, ok := r.offset(next)
if !ok {
// Shouldn't happen.
return false
}
if i == s.conf.ipEnd[3] {
break
}
}
return nil
return !s.leasedOffsets.isSet(offset)
})
return ip.To4()
}
// Find an expired lease and return its index or -1
func (s *v4Server) findExpiredLease() int {
now := time.Now().Unix()
now := time.Now()
for i, lease := range s.leases {
if lease.Expiry.Unix() != leaseExpireStatic &&
lease.Expiry.Unix() <= now {
if !lease.IsStatic() && lease.Expiry.Before(now) {
return i
}
}
return -1
}
// Reserve lease for MAC
func (s *v4Server) reserveLease(mac net.HardwareAddr) *Lease {
l := Lease{}
l.HWAddr = make([]byte, 6)
// reserveLease reserves a lease for a client by its MAC-address. It returns
// nil if it couldn't allocate a new lease.
func (s *v4Server) reserveLease(mac net.HardwareAddr) (l *Lease, err error) {
l = &Lease{
HWAddr: make([]byte, len(mac)),
}
copy(l.HWAddr, mac)
l.IP = s.findFreeIP()
l.IP = s.nextIP()
if l.IP == nil {
i := s.findExpiredLease()
if i < 0 {
return nil
return nil, nil
}
copy(s.leases[i].HWAddr, mac)
return s.leases[i]
return s.leases[i], nil
}
s.addLease(&l)
return &l
err = s.addLease(l)
if err != nil {
return nil, err
}
return l, nil
}
func (s *v4Server) commitLease(l *Lease) {
@@ -347,47 +471,55 @@ func (s *v4Server) commitLease(l *Lease) {
}
// Process Discover request and return lease
func (s *v4Server) processDiscover(req, resp *dhcpv4.DHCPv4) *Lease {
func (s *v4Server) processDiscover(req, resp *dhcpv4.DHCPv4) (l *Lease, err error) {
mac := req.ClientHWAddr
s.leasesLock.Lock()
defer s.leasesLock.Unlock()
lease := s.findLease(mac)
if lease == nil {
// TODO(a.garipov): Refactor this mess.
l = s.findLease(mac)
if l == nil {
toStore := false
for lease == nil {
lease = s.reserveLease(mac)
if lease == nil {
log.Debug("dhcpv4: No more IP addresses")
for l == nil {
l, err = s.reserveLease(mac)
if err != nil {
return nil, fmt.Errorf("reserving a lease: %w", err)
}
if l == nil {
log.Debug("dhcpv4: no more ip addresses")
if toStore {
s.conf.notify(LeaseChangedDBStore)
}
return nil
// TODO(a.garipov): Return a special error?
return nil, nil
}
toStore = true
if !s.addrAvailable(lease.IP) {
s.blacklistLease(lease)
lease = nil
if !s.addrAvailable(l.IP) {
s.blocklistLease(l)
l = nil
continue
}
break
}
s.conf.notify(LeaseChangedDBStore)
} else {
reqIP := req.Options.Get(dhcpv4.OptionRequestedIPAddress)
if len(reqIP) != 0 &&
!bytes.Equal(reqIP, lease.IP) {
log.Debug("dhcpv4: different RequestedIP: %v != %v", reqIP, lease.IP)
reqIP := req.RequestedIPAddress()
if len(reqIP) != 0 && !reqIP.Equal(l.IP) {
log.Debug("dhcpv4: different RequestedIP: %s != %s", reqIP, l.IP)
}
}
resp.UpdateOption(dhcpv4.OptMessageType(dhcpv4.MessageTypeOffer))
return lease
return l, nil
}
type optFQDN struct {
@@ -424,46 +556,49 @@ func (o *optFQDN) ToBytes() []byte {
func (s *v4Server) processRequest(req, resp *dhcpv4.DHCPv4) (*Lease, bool) {
var lease *Lease
mac := req.ClientHWAddr
hostname := req.Options.Get(dhcpv4.OptionHostName)
reqIP := req.Options.Get(dhcpv4.OptionRequestedIPAddress)
reqIP := req.RequestedIPAddress()
if reqIP == nil {
reqIP = req.ClientIPAddr
}
sid := req.Options.Get(dhcpv4.OptionServerIdentifier)
if len(sid) != 0 &&
!bytes.Equal(sid, s.conf.dnsIPAddrs[0]) {
log.Debug("dhcpv4: Bad OptionServerIdentifier in Request message for %s", mac)
sid := req.ServerIdentifier()
if len(sid) != 0 && !sid.Equal(s.conf.dnsIPAddrs[0]) {
log.Debug("dhcpv4: bad OptionServerIdentifier in request message for %s", mac)
return nil, false
}
if len(reqIP) != 4 {
log.Debug("dhcpv4: Bad OptionRequestedIPAddress in Request message for %s", mac)
if ip4 := reqIP.To4(); ip4 == nil {
log.Debug("dhcpv4: bad OptionRequestedIPAddress in request message for %s", mac)
return nil, false
}
s.leasesLock.Lock()
for _, l := range s.leases {
if bytes.Equal(l.HWAddr, mac) {
if !bytes.Equal(l.IP, reqIP) {
if !l.IP.Equal(reqIP) {
s.leasesLock.Unlock()
log.Debug("dhcpv4: Mismatched OptionRequestedIPAddress in Request message for %s", mac)
log.Debug("dhcpv4: mismatched OptionRequestedIPAddress in request message for %s", mac)
return nil, true
}
lease = l
break
}
}
s.leasesLock.Unlock()
if lease == nil {
log.Debug("dhcpv4: No lease for %s", mac)
log.Debug("dhcpv4: no lease for %s", mac)
return nil, true
}
if lease.Expiry.Unix() != leaseExpireStatic {
lease.Hostname = string(hostname)
if !lease.IsStatic() {
lease.Hostname = req.HostName()
s.commitLease(lease)
} else if len(lease.Hostname) != 0 {
o := &optFQDN{
@@ -473,10 +608,12 @@ func (s *v4Server) processRequest(req, resp *dhcpv4.DHCPv4) (*Lease, bool) {
Code: dhcpv4.OptionFQDN,
Value: o,
}
resp.UpdateOption(fqdn)
}
resp.UpdateOption(dhcpv4.OptMessageType(dhcpv4.MessageTypeAck))
return lease, true
}
@@ -485,22 +622,27 @@ func (s *v4Server) processRequest(req, resp *dhcpv4.DHCPv4) (*Lease, bool) {
// Return 0: error; reply with Nak
// Return -1: error; don't reply
func (s *v4Server) process(req, resp *dhcpv4.DHCPv4) int {
var lease *Lease
var err error
resp.UpdateOption(dhcpv4.OptServerIdentifier(s.conf.dnsIPAddrs[0]))
var l *Lease
switch req.MessageType() {
case dhcpv4.MessageTypeDiscover:
lease = s.processDiscover(req, resp)
if lease == nil {
l, err = s.processDiscover(req, resp)
if err != nil {
log.Error("dhcpv4: processing discover: %s", err)
return 0
}
if l == nil {
return 0
}
case dhcpv4.MessageTypeRequest:
var toReply bool
lease, toReply = s.processRequest(req, resp)
if lease == nil {
l, toReply = s.processRequest(req, resp)
if l == nil {
if toReply {
return 0
}
@@ -509,7 +651,7 @@ func (s *v4Server) process(req, resp *dhcpv4.DHCPv4) int {
}
resp.YourIPAddr = make([]byte, 4)
copy(resp.YourIPAddr, lease.IP)
copy(resp.YourIPAddr, l.IP)
resp.UpdateOption(dhcpv4.OptIPAddressLeaseTime(s.conf.leaseTime))
resp.UpdateOption(dhcpv4.OptRouter(s.conf.routerIP))
@@ -517,8 +659,9 @@ func (s *v4Server) process(req, resp *dhcpv4.DHCPv4) int {
resp.UpdateOption(dhcpv4.OptDNS(s.conf.dnsIPAddrs...))
for _, opt := range s.conf.options {
resp.Options[opt.code] = opt.val
resp.Options[opt.code] = opt.data
}
return 1
}
@@ -627,15 +770,16 @@ func (s *v4Server) Stop() {
}
// Create DHCPv4 server
func v4Create(conf V4ServerConf) (DHCPServer, error) {
func v4Create(conf V4ServerConf) (srv DHCPServer, err error) {
s := &v4Server{}
s.conf = conf
// TODO(a.garipov): Don't use a disabled server in other places or just
// use an interface.
if !conf.Enabled {
return s, nil
}
var err error
s.conf.routerIP, err = tryTo4(s.conf.GatewayIP)
if err != nil {
return s, fmt.Errorf("dhcpv4: %w", err)
@@ -647,22 +791,12 @@ func v4Create(conf V4ServerConf) (DHCPServer, error) {
s.conf.subnetMask = make([]byte, 4)
copy(s.conf.subnetMask, s.conf.SubnetMask.To4())
s.conf.ipStart, err = tryTo4(conf.RangeStart)
if s.conf.ipStart == nil {
s.conf.ipRange, err = newIPRange(conf.RangeStart, conf.RangeEnd)
if err != nil {
return s, fmt.Errorf("dhcpv4: %w", err)
}
if s.conf.ipStart[0] == 0 {
return s, fmt.Errorf("dhcpv4: invalid range start IP")
}
s.conf.ipEnd, err = tryTo4(conf.RangeEnd)
if s.conf.ipEnd == nil {
return s, fmt.Errorf("dhcpv4: %w", err)
}
if !net.IP.Equal(s.conf.ipStart[:3], s.conf.ipEnd[:3]) ||
s.conf.ipStart[3] > s.conf.ipEnd[3] {
return s, fmt.Errorf("dhcpv4: range end IP should match range start IP")
}
s.leasedOffsets = newBitSet()
if conf.LeaseDuration == 0 {
s.conf.leaseTime = time.Hour * 24
@@ -671,17 +805,23 @@ func v4Create(conf V4ServerConf) (DHCPServer, error) {
s.conf.leaseTime = time.Second * time.Duration(conf.LeaseDuration)
}
for _, o := range conf.Options {
code, val := parseOptionString(o)
if code == 0 {
log.Debug("dhcpv4: bad option string: %s", o)
p := newDHCPOptionParser()
for i, o := range conf.Options {
var code uint8
var data []byte
code, data, err = p.parse(o)
if err != nil {
log.Error("dhcpv4: bad option string at index %d: %s", i, err)
continue
}
opt := dhcpOption{
code: code,
val: val,
data: data,
}
s.conf.options = append(s.conf.options, opt)
}

View File

@@ -10,7 +10,7 @@ type winServer struct{}
func (s *winServer) ResetLeases(leases []*Lease) {}
func (s *winServer) GetLeases(flags int) []Lease { return nil }
func (s *winServer) GetLeasesRef() []*Lease { return nil }
func (s *winServer) getLeasesRef() []*Lease { return nil }
func (s *winServer) AddStaticLease(lease Lease) error { return nil }
func (s *winServer) RemoveStaticLease(l Lease) error { return nil }
func (s *winServer) FindMACbyIP(ip net.IP) net.HardwareAddr { return nil }

View File

@@ -40,7 +40,7 @@ func TestV4_AddRemove_static(t *testing.T) {
require.Len(t, ls, 1)
assert.True(t, l.IP.Equal(ls[0].IP))
assert.Equal(t, l.HWAddr, ls[0].HWAddr)
assert.EqualValues(t, leaseExpireStatic, ls[0].Expiry.Unix())
assert.True(t, ls[0].IsStatic())
// Try to remove static lease.
assert.NotNil(t, s.RemoveStaticLease(Lease{
@@ -77,7 +77,8 @@ func TestV4_AddReplace(t *testing.T) {
}}
for i := range dynLeases {
s.addLease(&dynLeases[i])
err = s.addLease(&dynLeases[i])
require.Nil(t, err)
}
stLeases := []Lease{{
@@ -98,7 +99,7 @@ func TestV4_AddReplace(t *testing.T) {
for i, l := range ls {
assert.True(t, stLeases[i].IP.Equal(l.IP))
assert.Equal(t, stLeases[i].HWAddr, l.HWAddr)
assert.EqualValues(t, leaseExpireStatic, l.Expiry.Unix())
assert.True(t, l.IsStatic())
}
}
@@ -128,13 +129,13 @@ func TestV4StaticLease_Get(t *testing.T) {
mac := net.HardwareAddr{0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA}
t.Run("discover", func(t *testing.T) {
var err error
var terr error
req, err = dhcpv4.NewDiscovery(mac)
require.Nil(t, err)
req, terr = dhcpv4.NewDiscovery(mac)
require.Nil(t, terr)
resp, err = dhcpv4.NewReplyFromRequest(req)
require.Nil(t, err)
resp, terr = dhcpv4.NewReplyFromRequest(req)
require.Nil(t, terr)
assert.Equal(t, 1, s.process(req, resp))
})
require.Nil(t, err)
@@ -212,28 +213,36 @@ func TestV4DynamicLease_Get(t *testing.T) {
require.Nil(t, err)
assert.Equal(t, 1, s.process(req, resp))
})
// Don't continue if we got any errors in the previous subtest.
require.Nil(t, err)
t.Run("offer", func(t *testing.T) {
assert.Equal(t, dhcpv4.MessageTypeOffer, resp.MessageType())
assert.Equal(t, mac, resp.ClientHWAddr)
assert.True(t, s.conf.RangeStart.Equal(resp.YourIPAddr))
assert.True(t, s.conf.GatewayIP.Equal(resp.Router()[0]))
assert.True(t, s.conf.GatewayIP.Equal(resp.ServerIdentifier()))
assert.Equal(t, s.conf.RangeStart, resp.YourIPAddr)
assert.Equal(t, s.conf.GatewayIP, resp.ServerIdentifier())
router := resp.Router()
require.Len(t, router, 1)
assert.Equal(t, s.conf.GatewayIP, router[0])
assert.Equal(t, s.conf.subnetMask, resp.SubnetMask())
assert.Equal(t, s.conf.leaseTime.Seconds(), resp.IPAddressLeaseTime(-1).Seconds())
assert.Equal(t, []byte("012"), resp.Options[uint8(dhcpv4.OptionFQDN)])
assert.True(t, net.IP{1, 2, 3, 4}.Equal(net.IP(resp.Options[uint8(dhcpv4.OptionRelayAgentInformation)])))
assert.Equal(t, net.IP{1, 2, 3, 4}, net.IP(resp.RelayAgentInfo().ToBytes()))
})
t.Run("request", func(t *testing.T) {
var err error
var terr error
req, err = dhcpv4.NewRequestFromOffer(resp)
require.Nil(t, err)
req, terr = dhcpv4.NewRequestFromOffer(resp)
require.Nil(t, terr)
resp, err = dhcpv4.NewReplyFromRequest(req)
require.Nil(t, err)
resp, terr = dhcpv4.NewReplyFromRequest(req)
require.Nil(t, terr)
assert.Equal(t, 1, s.process(req, resp))
})
require.Nil(t, err)
@@ -260,31 +269,3 @@ func TestV4DynamicLease_Get(t *testing.T) {
assert.Equal(t, mac, ls[0].HWAddr)
})
}
func TestIP4InRange(t *testing.T) {
start := net.IP{192, 168, 10, 100}
stop := net.IP{192, 168, 10, 200}
testCases := []struct {
ip net.IP
want bool
}{{
ip: net.IP{192, 168, 10, 99},
want: false,
}, {
ip: net.IP{192, 168, 11, 100},
want: false,
}, {
ip: net.IP{192, 168, 11, 201},
want: false,
}, {
ip: start,
want: true,
}}
for _, tc := range testCases {
t.Run(tc.ip.String(), func(t *testing.T) {
assert.Equal(t, tc.want, ip4InRange(start, stop, tc.ip))
})
}
}

View File

@@ -46,7 +46,8 @@ func ip6InRange(start, ip net.IP) bool {
if len(start) != 16 {
return false
}
//lint:ignore SA1021 TODO(e.burkov): Ignore this for now, think about using masks.
//lint:ignore SA1021 TODO(e.burkov): Ignore this for now, think about
// using masks.
if !bytes.Equal(start[:15], ip[:15]) {
return false
}
@@ -91,8 +92,8 @@ func (s *v6Server) GetLeases(flags int) []Lease {
return result
}
// GetLeasesRef - get leases
func (s *v6Server) GetLeasesRef() []*Lease {
// getLeasesRef returns the actual leases slice. For internal use only.
func (s *v6Server) getLeasesRef() []*Lease {
return s.leases
}

View File

@@ -254,7 +254,7 @@ func BlockedSvcKnown(s string) bool {
}
// ApplyBlockedServices - set blocked services settings for this DNS request
func (d *DNSFilter) ApplyBlockedServices(setts *RequestFilteringSettings, list []string, global bool) {
func (d *DNSFilter) ApplyBlockedServices(setts *FilteringSettings, list []string, global bool) {
setts.ServicesRules = []ServiceEntry{}
if global {
d.confLock.RLock()

View File

@@ -29,32 +29,30 @@ type ServiceEntry struct {
Rules []*rules.NetworkRule
}
// RequestFilteringSettings is custom filtering settings
type RequestFilteringSettings struct {
FilteringEnabled bool
SafeSearchEnabled bool
SafeBrowsingEnabled bool
ParentalEnabled bool
// FilteringSettings are custom filtering settings for a client.
type FilteringSettings struct {
ClientName string
ClientIP net.IP
ClientTags []string
ServicesRules []ServiceEntry
FilteringEnabled bool
SafeSearchEnabled bool
SafeBrowsingEnabled bool
ParentalEnabled bool
}
// Resolver is the interface for net.Resolver to simplify testing.
type Resolver interface {
// TODO(e.burkov): Replace with LookupIP after upgrading go to v1.15.
LookupIPAddr(ctx context.Context, host string) (ips []net.IPAddr, err error)
LookupIP(ctx context.Context, network, host string) (ips []net.IP, err error)
}
// Config allows you to configure DNS filtering with New() or just change variables directly.
type Config struct {
ParentalEnabled bool `yaml:"parental_enabled"`
SafeSearchEnabled bool `yaml:"safesearch_enabled"`
SafeBrowsingEnabled bool `yaml:"safebrowsing_enabled"`
ResolverAddress string `yaml:"-"` // DNS server address
ParentalEnabled bool `yaml:"parental_enabled"`
SafeSearchEnabled bool `yaml:"safesearch_enabled"`
SafeBrowsingEnabled bool `yaml:"safebrowsing_enabled"`
SafeBrowsingCacheSize uint `yaml:"safebrowsing_cache_size"` // (in bytes)
SafeSearchCacheSize uint `yaml:"safesearch_cache_size"` // (in bytes)
@@ -101,6 +99,11 @@ type filtersInitializerParams struct {
blockFilters []Filter
}
type hostChecker struct {
check func(host string, qtype uint16, setts *FilteringSettings) (res Result, err error)
name string
}
// DNSFilter matches hostnames and DNS requests against filtering rules.
type DNSFilter struct {
rulesStorage *filterlist.RuleStorage
@@ -125,6 +128,8 @@ type DNSFilter struct {
//
// TODO(e.burkov): Use upstream that configured in dnsforward instead.
resolver Resolver
hostCheckers []hostChecker
}
// Filter represents a filter list
@@ -218,8 +223,8 @@ func (r Reason) In(reasons ...Reason) bool {
}
// GetConfig - get configuration
func (d *DNSFilter) GetConfig() RequestFilteringSettings {
c := RequestFilteringSettings{}
func (d *DNSFilter) GetConfig() FilteringSettings {
c := FilteringSettings{}
// d.confLock.RLock()
c.SafeSearchEnabled = d.Config.SafeSearchEnabled
c.SafeBrowsingEnabled = d.Config.SafeBrowsingEnabled
@@ -374,122 +379,85 @@ func (r Reason) Matched() bool {
}
// CheckHostRules tries to match the host against filtering rules only.
func (d *DNSFilter) CheckHostRules(host string, qtype uint16, setts *RequestFilteringSettings) (Result, error) {
func (d *DNSFilter) CheckHostRules(host string, qtype uint16, setts *FilteringSettings) (Result, error) {
if !setts.FilteringEnabled {
return Result{}, nil
}
return d.matchHost(host, qtype, *setts)
return d.matchHost(host, qtype, setts)
}
// CheckHost tries to match the host against filtering rules, then
// safebrowsing and parental control rules, if they are enabled.
func (d *DNSFilter) CheckHost(host string, qtype uint16, setts *RequestFilteringSettings) (Result, error) {
// sometimes DNS clients will try to resolve ".", which is a request to get root servers
// CheckHost tries to match the host against filtering rules, then safebrowsing
// and parental control rules, if they are enabled.
func (d *DNSFilter) CheckHost(
host string,
qtype uint16,
setts *FilteringSettings,
) (res Result, err error) {
// Sometimes clients try to resolve ".", which is a request to get root
// servers.
if host == "" {
return Result{Reason: NotFilteredNotFound}, nil
}
host = strings.ToLower(host)
var result Result
var err error
// first - check rewrites, they have the highest priority
result = d.processRewrites(host, qtype)
if result.Reason == Rewritten {
return result, nil
res = d.processRewrites(host, qtype)
if res.Reason == Rewritten {
return res, nil
}
// Now check the hosts file -- do we have any rules for it?
// just like DNS rewrites, it has higher priority than filtering rules.
if d.Config.AutoHosts != nil {
matched := d.checkAutoHosts(host, qtype, &result)
if matched {
return result, nil
}
}
if setts.FilteringEnabled {
result, err = d.matchHost(host, qtype, *setts)
for _, hc := range d.hostCheckers {
res, err = hc.check(host, qtype, setts)
if err != nil {
return result, err
}
if result.Reason.Matched() {
return result, nil
}
}
// are there any blocked services?
if len(setts.ServicesRules) != 0 {
result = matchBlockedServicesRules(host, setts.ServicesRules)
if result.Reason.Matched() {
return result, nil
}
}
// browsing security web service
if setts.SafeBrowsingEnabled {
result, err = d.checkSafeBrowsing(host)
if err != nil {
log.Info("SafeBrowsing: failed: %v", err)
return Result{}, nil
}
if result.Reason.Matched() {
return result, nil
}
}
// parental control web service
if setts.ParentalEnabled {
result, err = d.checkParental(host)
if err != nil {
log.Printf("Parental: failed: %v", err)
return Result{}, nil
}
if result.Reason.Matched() {
return result, nil
}
}
// apply safe search if needed
if setts.SafeSearchEnabled {
result, err = d.checkSafeSearch(host)
if err != nil {
log.Info("SafeSearch: failed: %v", err)
return Result{}, nil
return Result{}, fmt.Errorf("%s: %w", hc.name, err)
}
if result.Reason.Matched() {
return result, nil
if res.Reason.Matched() {
return res, nil
}
}
return Result{}, nil
}
func (d *DNSFilter) checkAutoHosts(host string, qtype uint16, result *Result) (matched bool) {
// checkAutoHosts compares the host against our autohosts table. The err is
// always nil, it is only there to make this a valid hostChecker function.
func (d *DNSFilter) checkAutoHosts(
host string,
qtype uint16,
_ *FilteringSettings,
) (res Result, err error) {
if d.Config.AutoHosts == nil {
return Result{}, nil
}
ips := d.Config.AutoHosts.Process(host, qtype)
if ips != nil {
result.Reason = RewrittenAutoHosts
result.IPList = ips
res = Result{
Reason: RewrittenAutoHosts,
IPList: ips,
}
return true
return res, nil
}
revHosts := d.Config.AutoHosts.ProcessReverse(host, qtype)
if len(revHosts) != 0 {
result.Reason = RewrittenAutoHosts
// TODO(a.garipov): Optimize this with a buffer.
result.ReverseHosts = make([]string, len(revHosts))
for i := range revHosts {
result.ReverseHosts[i] = revHosts[i] + "."
res = Result{
Reason: RewrittenAutoHosts,
}
return true
// TODO(a.garipov): Optimize this with a buffer.
res.ReverseHosts = make([]string, len(revHosts))
for i := range revHosts {
res.ReverseHosts[i] = revHosts[i] + "."
}
return res, nil
}
return false
return Result{}, nil
}
// Process rewrites table
@@ -547,10 +515,20 @@ func (d *DNSFilter) processRewrites(host string, qtype uint16) (res Result) {
return res
}
func matchBlockedServicesRules(host string, svcs []ServiceEntry) Result {
req := rules.NewRequestForHostname(host)
res := Result{}
// matchBlockedServicesRules checks the host against the blocked services rules
// in settings, if any. The err is always nil, it is only there to make this
// a valid hostChecker function.
func matchBlockedServicesRules(
host string,
_ uint16,
setts *FilteringSettings,
) (res Result, err error) {
svcs := setts.ServicesRules
if len(svcs) == 0 {
return Result{}, nil
}
req := rules.NewRequestForHostname(host)
for _, s := range svcs {
for _, rule := range s.Rules {
if rule.Match(req) {
@@ -567,11 +545,12 @@ func matchBlockedServicesRules(host string, svcs []ServiceEntry) Result {
log.Debug("blocked services: matched rule: %s host: %s service: %s",
ruleText, host, s.Name)
return res
return res, nil
}
}
}
return res
return res, nil
}
//
@@ -680,9 +659,66 @@ func (d *DNSFilter) matchHostProcessAllowList(host string, dnsres urlfilter.DNSR
return makeResult(rule, NotFilteredAllowList), nil
}
// matchHostProcessDNSResult processes the matched DNS filtering result.
func (d *DNSFilter) matchHostProcessDNSResult(
qtype uint16,
dnsres urlfilter.DNSResult,
) (res Result) {
if dnsres.NetworkRule != nil {
reason := FilteredBlockList
if dnsres.NetworkRule.Whitelist {
reason = NotFilteredAllowList
}
return makeResult(dnsres.NetworkRule, reason)
}
if qtype == dns.TypeA && dnsres.HostRulesV4 != nil {
rule := dnsres.HostRulesV4[0]
res = makeResult(rule, FilteredBlockList)
res.Rules[0].IP = rule.IP.To4()
return res
}
if qtype == dns.TypeAAAA && dnsres.HostRulesV6 != nil {
rule := dnsres.HostRulesV6[0]
res = makeResult(rule, FilteredBlockList)
res.Rules[0].IP = rule.IP.To16()
return res
}
if dnsres.HostRulesV4 != nil || dnsres.HostRulesV6 != nil {
// Question type doesn't match the host rules. Return the first
// matched host rule, but without an IP address.
var rule rules.Rule
if dnsres.HostRulesV4 != nil {
rule = dnsres.HostRulesV4[0]
} else if dnsres.HostRulesV6 != nil {
rule = dnsres.HostRulesV6[0]
}
res = makeResult(rule, FilteredBlockList)
res.Rules[0].IP = net.IP{}
return res
}
return Result{}
}
// matchHost is a low-level way to check only if hostname is filtered by rules,
// skipping expensive safebrowsing and parental lookups.
func (d *DNSFilter) matchHost(host string, qtype uint16, setts RequestFilteringSettings) (res Result, err error) {
func (d *DNSFilter) matchHost(
host string,
qtype uint16,
setts *FilteringSettings,
) (res Result, err error) {
if !setts.FilteringEnabled {
return Result{}, nil
}
d.engineLock.RLock()
// Keep in mind that this lock must be held no just when calling Match()
// but also while using the rules returned by it.
@@ -710,13 +746,12 @@ func (d *DNSFilter) matchHost(host string, qtype uint16, setts RequestFilteringS
dnsres, ok := d.filteringEngine.MatchRequest(ureq)
// Check DNS rewrites first, because the API there is a bit
// awkward.
// Check DNS rewrites first, because the API there is a bit awkward.
if dnsr := dnsres.DNSRewrites(); len(dnsr) > 0 {
res = d.processDNSRewrites(dnsr)
if res.Reason == RewrittenRule && res.CanonName == host {
// A rewrite of a host to itself. Go on and
// try matching other things.
// A rewrite of a host to itself. Go on and try
// matching other things.
} else {
return res, nil
}
@@ -724,55 +759,18 @@ func (d *DNSFilter) matchHost(host string, qtype uint16, setts RequestFilteringS
return Result{}, nil
}
if dnsres.NetworkRule != nil {
log.Debug("Filtering: found rule for host %q: %q list_id: %d",
host, dnsres.NetworkRule.Text(), dnsres.NetworkRule.GetFilterListID())
reason := FilteredBlockList
if dnsres.NetworkRule.Whitelist {
reason = NotFilteredAllowList
}
return makeResult(dnsres.NetworkRule, reason), nil
res = d.matchHostProcessDNSResult(qtype, dnsres)
if len(res.Rules) > 0 {
r := res.Rules[0]
log.Debug(
"filtering: found rule %q for host %q, filter list id: %d",
r.Text,
host,
r.FilterListID,
)
}
if qtype == dns.TypeA && dnsres.HostRulesV4 != nil {
rule := dnsres.HostRulesV4[0] // note that we process only 1 matched rule
log.Debug("Filtering: found rule for host %q: %q list_id: %d",
host, rule.Text(), rule.GetFilterListID())
res = makeResult(rule, FilteredBlockList)
res.Rules[0].IP = rule.IP.To4()
return res, nil
}
if qtype == dns.TypeAAAA && dnsres.HostRulesV6 != nil {
rule := dnsres.HostRulesV6[0] // note that we process only 1 matched rule
log.Debug("Filtering: found rule for host %q: %q list_id: %d",
host, rule.Text(), rule.GetFilterListID())
res = makeResult(rule, FilteredBlockList)
res.Rules[0].IP = rule.IP
return res, nil
}
if dnsres.HostRulesV4 != nil || dnsres.HostRulesV6 != nil {
// Question Type doesn't match the host rules
// Return the first matched host rule, but without an IP address
var rule rules.Rule
if dnsres.HostRulesV4 != nil {
rule = dnsres.HostRulesV4[0]
} else if dnsres.HostRulesV6 != nil {
rule = dnsres.HostRulesV6[0]
}
log.Debug("Filtering: found rule for host %q: %q list_id: %d",
host, rule.Text(), rule.GetFilterListID())
res = makeResult(rule, FilteredBlockList)
res.Rules[0].IP = net.IP{}
return res, nil
}
return Result{}, nil
return res, nil
}
// makeResult returns a properly constructed Result.
@@ -829,6 +827,26 @@ func New(c *Config, blockFilters []Filter) *DNSFilter {
resolver: resolver,
}
d.hostCheckers = []hostChecker{{
check: d.checkAutoHosts,
name: "autohosts",
}, {
check: d.matchHost,
name: "filtering",
}, {
check: matchBlockedServicesRules,
name: "blocked services",
}, {
check: d.checkSafeBrowsing,
name: "safe browsing",
}, {
check: d.checkParental,
name: "parental",
}, {
check: d.checkSafeSearch,
name: "safe search",
}}
err := d.initSecurityServices()
if err != nil {
log.Error("dnsfilter: initialize services: %s", err)
@@ -851,7 +869,7 @@ func New(c *Config, blockFilters []Filter) *DNSFilter {
d.BlockedServices = bsvcs
if blockFilters != nil {
err := d.initFiltering(nil, blockFilters)
err = d.initFiltering(nil, blockFilters)
if err != nil {
log.Error("Can't initialize filtering subsystem: %s", err)
d.Close()

View File

@@ -5,6 +5,7 @@ import (
"context"
"fmt"
"net"
"strings"
"testing"
"github.com/AdguardTeam/AdGuardHome/internal/aghtest"
@@ -13,13 +14,14 @@ import (
"github.com/AdguardTeam/urlfilter/rules"
"github.com/miekg/dns"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestMain(m *testing.M) {
aghtest.DiscardLogOutput(m)
}
var setts RequestFilteringSettings
var setts FilteringSettings
// Helpers.
@@ -36,7 +38,7 @@ func purgeCaches() {
}
func newForTest(c *Config, filters []Filter) *DNSFilter {
setts = RequestFilteringSettings{
setts = FilteringSettings{
FilteringEnabled: true,
}
setts.FilteringEnabled = true
@@ -58,7 +60,7 @@ func (d *DNSFilter) checkMatch(t *testing.T, hostname string) {
t.Helper()
res, err := d.CheckHost(hostname, dns.TypeA, &setts)
assert.Nilf(t, err, "Error while matching host %s: %s", hostname, err)
require.Nilf(t, err, "Error while matching host %s: %s", hostname, err)
assert.Truef(t, res.IsFiltered, "Expected hostname %s to match", hostname)
}
@@ -66,20 +68,20 @@ func (d *DNSFilter) checkMatchIP(t *testing.T, hostname, ip string, qtype uint16
t.Helper()
res, err := d.CheckHost(hostname, qtype, &setts)
assert.Nilf(t, err, "Error while matching host %s: %s", hostname, err)
require.Nilf(t, err, "Error while matching host %s: %s", hostname, err)
assert.Truef(t, res.IsFiltered, "Expected hostname %s to match", hostname)
if assert.NotEmpty(t, res.Rules, "Expected result to have rules") {
r := res.Rules[0]
assert.NotNilf(t, r.IP, "Expected ip %s to match, actual: %v", ip, r.IP)
assert.Equalf(t, ip, r.IP.String(), "Expected ip %s to match, actual: %v", ip, r.IP)
}
require.NotEmpty(t, res.Rules, "Expected result to have rules")
r := res.Rules[0]
require.NotNilf(t, r.IP, "Expected ip %s to match, actual: %v", ip, r.IP)
assert.Equalf(t, ip, r.IP.String(), "Expected ip %s to match, actual: %v", ip, r.IP)
}
func (d *DNSFilter) checkMatchEmpty(t *testing.T, hostname string) {
t.Helper()
res, err := d.CheckHost(hostname, dns.TypeA, &setts)
assert.Nilf(t, err, "Error while matching host %s: %s", hostname, err)
require.Nilf(t, err, "Error while matching host %s: %s", hostname, err)
assert.Falsef(t, res.IsFiltered, "Expected hostname %s to not match", hostname)
}
@@ -110,40 +112,40 @@ func TestEtcHostsMatching(t *testing.T) {
// Empty IPv6.
res, err := d.CheckHost("block.com", dns.TypeAAAA, &setts)
assert.Nil(t, err)
require.Nil(t, err)
assert.True(t, res.IsFiltered)
if assert.Len(t, res.Rules, 1) {
assert.Equal(t, "0.0.0.0 block.com", res.Rules[0].Text)
assert.Empty(t, res.Rules[0].IP)
}
require.Len(t, res.Rules, 1)
assert.Equal(t, "0.0.0.0 block.com", res.Rules[0].Text)
assert.Empty(t, res.Rules[0].IP)
// IPv6 match.
d.checkMatchIP(t, "ipv6.com", addr6, dns.TypeAAAA)
// Empty IPv4.
res, err = d.CheckHost("ipv6.com", dns.TypeA, &setts)
assert.Nil(t, err)
require.Nil(t, err)
assert.True(t, res.IsFiltered)
if assert.Len(t, res.Rules, 1) {
assert.Equal(t, "::1 ipv6.com", res.Rules[0].Text)
assert.Empty(t, res.Rules[0].IP)
}
require.Len(t, res.Rules, 1)
assert.Equal(t, "::1 ipv6.com", res.Rules[0].Text)
assert.Empty(t, res.Rules[0].IP)
// Two IPv4, the first one returned.
res, err = d.CheckHost("host2", dns.TypeA, &setts)
assert.Nil(t, err)
require.Nil(t, err)
assert.True(t, res.IsFiltered)
if assert.Len(t, res.Rules, 1) {
assert.Equal(t, res.Rules[0].IP, net.IP{0, 0, 0, 1})
}
require.Len(t, res.Rules, 1)
assert.Equal(t, res.Rules[0].IP, net.IP{0, 0, 0, 1})
// One IPv6 address.
res, err = d.CheckHost("host2", dns.TypeAAAA, &setts)
assert.Nil(t, err)
require.Nil(t, err)
assert.True(t, res.IsFiltered)
if assert.Len(t, res.Rules, 1) {
assert.Equal(t, res.Rules[0].IP, net.IPv6loopback)
}
require.Len(t, res.Rules, 1)
assert.Equal(t, res.Rules[0].IP, net.IPv6loopback)
}
// Safe Browsing.
@@ -155,14 +157,14 @@ func TestSafeBrowsing(t *testing.T) {
d := newForTest(&Config{SafeBrowsingEnabled: true}, nil)
t.Cleanup(d.Close)
matching := "wmconvirus.narod.ru"
const matching = "wmconvirus.narod.ru"
d.SetSafeBrowsingUpstream(&aghtest.TestBlockUpstream{
Hostname: matching,
Block: true,
})
d.checkMatch(t, matching)
assert.Contains(t, logOutput.String(), "SafeBrowsing lookup for "+matching)
require.Contains(t, logOutput.String(), "SafeBrowsing lookup for "+matching)
d.checkMatch(t, "test."+matching)
d.checkMatchEmpty(t, "yandex.ru")
@@ -178,7 +180,7 @@ func TestSafeBrowsing(t *testing.T) {
func TestParallelSB(t *testing.T) {
d := newForTest(&Config{SafeBrowsingEnabled: true}, nil)
t.Cleanup(d.Close)
matching := "wmconvirus.narod.ru"
const matching = "wmconvirus.narod.ru"
d.SetSafeBrowsingUpstream(&aghtest.TestBlockUpstream{
Hostname: matching,
Block: true,
@@ -203,7 +205,7 @@ func TestSafeSearch(t *testing.T) {
d := newForTest(&Config{SafeSearchEnabled: true}, nil)
t.Cleanup(d.Close)
val, ok := d.SafeSearchDomain("www.google.com")
assert.True(t, ok, "Expected safesearch to find result for www.google.com")
require.True(t, ok, "Expected safesearch to find result for www.google.com")
assert.Equal(t, "forcesafesearch.google.com", val, "Expected safesearch for google.com to be forcesafesearch.google.com")
}
@@ -211,6 +213,8 @@ func TestCheckHostSafeSearchYandex(t *testing.T) {
d := newForTest(&Config{SafeSearchEnabled: true}, nil)
t.Cleanup(d.Close)
yandexIP := net.IPv4(213, 180, 193, 56)
// Check host for each domain.
for _, host := range []string{
"yAndeX.ru",
@@ -220,22 +224,27 @@ func TestCheckHostSafeSearchYandex(t *testing.T) {
"yandex.kz",
"www.yandex.com",
} {
res, err := d.CheckHost(host, dns.TypeA, &setts)
assert.Nil(t, err)
assert.True(t, res.IsFiltered)
if assert.Len(t, res.Rules, 1) {
assert.Equal(t, res.Rules[0].IP, net.IPv4(213, 180, 193, 56))
}
t.Run(strings.ToLower(host), func(t *testing.T) {
res, err := d.CheckHost(host, dns.TypeA, &setts)
require.Nil(t, err)
assert.True(t, res.IsFiltered)
require.Len(t, res.Rules, 1)
assert.Equal(t, yandexIP, res.Rules[0].IP)
})
}
}
func TestCheckHostSafeSearchGoogle(t *testing.T) {
resolver := &aghtest.TestResolver{}
d := newForTest(&Config{
SafeSearchEnabled: true,
CustomResolver: &aghtest.TestResolver{},
CustomResolver: resolver,
}, nil)
t.Cleanup(d.Close)
ip, _ := resolver.HostToIPs("forcesafesearch.google.com")
// Check host for each domain.
for _, host := range []string{
"www.google.com",
@@ -248,9 +257,10 @@ func TestCheckHostSafeSearchGoogle(t *testing.T) {
} {
t.Run(host, func(t *testing.T) {
res, err := d.CheckHost(host, dns.TypeA, &setts)
assert.Nil(t, err)
require.Nil(t, err)
assert.True(t, res.IsFiltered)
assert.Len(t, res.Rules, 1)
require.Len(t, res.Rules, 1)
assert.Equal(t, ip, res.Rules[0].IP)
})
}
}
@@ -258,31 +268,31 @@ func TestCheckHostSafeSearchGoogle(t *testing.T) {
func TestSafeSearchCacheYandex(t *testing.T) {
d := newForTest(nil, nil)
t.Cleanup(d.Close)
domain := "yandex.ru"
const domain = "yandex.ru"
// Check host with disabled safesearch.
res, err := d.CheckHost(domain, dns.TypeA, &setts)
assert.Nil(t, err)
require.Nil(t, err)
assert.False(t, res.IsFiltered)
assert.Empty(t, res.Rules)
require.Empty(t, res.Rules)
yandexIP := net.IPv4(213, 180, 193, 56)
d = newForTest(&Config{SafeSearchEnabled: true}, nil)
t.Cleanup(d.Close)
res, err = d.CheckHost(domain, dns.TypeA, &setts)
assert.Nilf(t, err, "CheckHost for safesearh domain %s failed cause %s", domain, err)
require.Nilf(t, err, "CheckHost for safesearh domain %s failed cause %s", domain, err)
// For yandex we already know valid IP.
if assert.Len(t, res.Rules, 1) {
assert.Equal(t, res.Rules[0].IP, net.IPv4(213, 180, 193, 56))
}
require.Len(t, res.Rules, 1)
assert.Equal(t, res.Rules[0].IP, yandexIP)
// Check cache.
cachedValue, isFound := getCachedResult(gctx.safeSearchCache, domain)
assert.True(t, isFound)
if assert.Len(t, cachedValue.Rules, 1) {
assert.Equal(t, cachedValue.Rules[0].IP, net.IPv4(213, 180, 193, 56))
}
require.True(t, isFound)
require.Len(t, cachedValue.Rules, 1)
assert.Equal(t, cachedValue.Rules[0].IP, yandexIP)
}
func TestSafeSearchCacheGoogle(t *testing.T) {
@@ -292,11 +302,11 @@ func TestSafeSearchCacheGoogle(t *testing.T) {
}, nil)
t.Cleanup(d.Close)
domain := "www.google.ru"
const domain = "www.google.ru"
res, err := d.CheckHost(domain, dns.TypeA, &setts)
assert.Nil(t, err)
require.Nil(t, err)
assert.False(t, res.IsFiltered)
assert.Empty(t, res.Rules)
require.Empty(t, res.Rules)
d = newForTest(&Config{SafeSearchEnabled: true}, nil)
t.Cleanup(d.Close)
@@ -304,33 +314,30 @@ func TestSafeSearchCacheGoogle(t *testing.T) {
// Lookup for safesearch domain.
safeDomain, ok := d.SafeSearchDomain(domain)
assert.Truef(t, ok, "Failed to get safesearch domain for %s", domain)
require.Truef(t, ok, "Failed to get safesearch domain for %s", domain)
ipAddrs, err := resolver.LookupIPAddr(context.Background(), safeDomain)
if err != nil {
t.Fatalf("Failed to lookup for %s", safeDomain)
}
ips, err := resolver.LookupIP(context.Background(), "ip", safeDomain)
require.Nilf(t, err, "Failed to lookup for %s", safeDomain)
var ip net.IP
for _, foundIP := range ips {
if foundIP.To4() != nil {
ip = foundIP
ip := ipAddrs[0].IP
for _, ipAddr := range ipAddrs {
if ipAddr.IP.To4() != nil {
ip = ipAddr.IP
break
}
}
res, err = d.CheckHost(domain, dns.TypeA, &setts)
assert.Nil(t, err)
if assert.Len(t, res.Rules, 1) {
assert.True(t, res.Rules[0].IP.Equal(ip))
}
require.Nil(t, err)
require.Len(t, res.Rules, 1)
assert.True(t, res.Rules[0].IP.Equal(ip))
// Check cache.
cachedValue, isFound := getCachedResult(gctx.safeSearchCache, domain)
assert.True(t, isFound)
if assert.Len(t, cachedValue.Rules, 1) {
assert.True(t, cachedValue.Rules[0].IP.Equal(ip))
}
require.True(t, isFound)
require.Len(t, cachedValue.Rules, 1)
assert.True(t, cachedValue.Rules[0].IP.Equal(ip))
}
// Parental.
@@ -342,24 +349,23 @@ func TestParentalControl(t *testing.T) {
d := newForTest(&Config{ParentalEnabled: true}, nil)
t.Cleanup(d.Close)
matching := "pornhub.com"
const matching = "pornhub.com"
d.SetParentalUpstream(&aghtest.TestBlockUpstream{
Hostname: matching,
Block: true,
})
d.checkMatch(t, matching)
assert.Contains(t, logOutput.String(), "Parental lookup for "+matching)
require.Contains(t, logOutput.String(), "Parental lookup for "+matching)
d.checkMatch(t, "www."+matching)
d.checkMatchEmpty(t, "www.yandex.ru")
d.checkMatchEmpty(t, "yandex.ru")
d.checkMatchEmpty(t, "api.jquery.com")
// test cached result
// Test cached result.
d.parentalServer = "127.0.0.1"
d.checkMatch(t, matching)
d.checkMatchEmpty(t, "yandex.ru")
d.parentalServer = defaultParentalServer
}
// Filtering.
@@ -378,8 +384,8 @@ func TestMatching(t *testing.T) {
name string
rules string
host string
wantIsFiltered bool
wantReason Reason
wantIsFiltered bool
wantDNSType uint16
}{{
name: "sanity",
@@ -648,7 +654,7 @@ func TestMatching(t *testing.T) {
t.Cleanup(d.Close)
res, err := d.CheckHost(tc.host, tc.wantDNSType, &setts)
assert.Nilf(t, err, "Error while matching host %s: %s", tc.host, err)
require.Nilf(t, err, "Error while matching host %s: %s", tc.host, err)
assert.Equalf(t, tc.wantIsFiltered, res.IsFiltered, "Hostname %s has wrong result (%v must be %v)", tc.host, res.IsFiltered, tc.wantIsFiltered)
assert.Equalf(t, tc.wantReason, res.Reason, "Hostname %s has wrong reason (%v must be %v)", tc.host, res.Reason, tc.wantReason)
})
@@ -671,33 +677,29 @@ func TestWhitelist(t *testing.T) {
}}
d := newForTest(nil, filters)
err := d.SetFilters(filters, whiteFilters, false)
assert.Nil(t, err)
require.Nil(t, d.SetFilters(filters, whiteFilters, false))
t.Cleanup(d.Close)
// Matched by white filter.
res, err := d.CheckHost("host1", dns.TypeA, &setts)
assert.Nil(t, err)
require.Nil(t, err)
assert.False(t, res.IsFiltered)
assert.Equal(t, res.Reason, NotFilteredAllowList)
if assert.Len(t, res.Rules, 1) {
assert.Equal(t, "||host1^", res.Rules[0].Text)
}
require.Len(t, res.Rules, 1)
assert.Equal(t, "||host1^", res.Rules[0].Text)
// Not matched by white filter, but matched by block filter.
res, err = d.CheckHost("host2", dns.TypeA, &setts)
assert.Nil(t, err)
require.Nil(t, err)
assert.True(t, res.IsFiltered)
assert.Equal(t, res.Reason, FilteredBlockList)
if assert.Len(t, res.Rules, 1) {
assert.Equal(t, "||host2^", res.Rules[0].Text)
}
require.Len(t, res.Rules, 1)
assert.Equal(t, "||host2^", res.Rules[0].Text)
}
// Client Settings.
func applyClientSettings(setts *RequestFilteringSettings) {
func applyClientSettings(setts *FilteringSettings) {
setts.FilteringEnabled = false
setts.ParentalEnabled = false
setts.SafeBrowsingEnabled = true
@@ -794,7 +796,7 @@ func BenchmarkSafeBrowsing(b *testing.B) {
})
for n := 0; n < b.N; n++ {
res, err := d.CheckHost(blocked, dns.TypeA, &setts)
assert.Nilf(b, err, "Error while matching host %s: %s", blocked, err)
require.Nilf(b, err, "Error while matching host %s: %s", blocked, err)
assert.True(b, res.IsFiltered, "Expected hostname %s to match", blocked)
}
}
@@ -810,7 +812,7 @@ func BenchmarkSafeBrowsingParallel(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
res, err := d.CheckHost(blocked, dns.TypeA, &setts)
assert.Nilf(b, err, "Error while matching host %s: %s", blocked, err)
require.Nilf(b, err, "Error while matching host %s: %s", blocked, err)
assert.True(b, res.IsFiltered, "Expected hostname %s to match", blocked)
}
})
@@ -821,7 +823,7 @@ func BenchmarkSafeSearch(b *testing.B) {
b.Cleanup(d.Close)
for n := 0; n < b.N; n++ {
val, ok := d.SafeSearchDomain("www.google.com")
assert.True(b, ok, "Expected safesearch to find result for www.google.com")
require.True(b, ok, "Expected safesearch to find result for www.google.com")
assert.Equal(b, "forcesafesearch.google.com", val, "Expected safesearch for google.com to be forcesafesearch.google.com")
}
}
@@ -832,7 +834,7 @@ func BenchmarkSafeSearchParallel(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
val, ok := d.SafeSearchDomain("www.google.com")
assert.True(b, ok, "Expected safesearch to find result for www.google.com")
require.True(b, ok, "Expected safesearch to find result for www.google.com")
assert.Equal(b, "forcesafesearch.google.com", val, "Expected safesearch for google.com to be forcesafesearch.google.com")
}
})

View File

@@ -7,6 +7,7 @@ import (
"github.com/miekg/dns"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestDNSFilter_CheckHostRules_dnsrewrite(t *testing.T) {
@@ -46,7 +47,7 @@ func TestDNSFilter_CheckHostRules_dnsrewrite(t *testing.T) {
`
f := newForTest(nil, []Filter{{ID: 0, Data: []byte(text)}})
setts := &RequestFilteringSettings{
setts := &FilteringSettings{
FilteringEnabled: true,
}
@@ -55,138 +56,89 @@ func TestDNSFilter_CheckHostRules_dnsrewrite(t *testing.T) {
ipv6p1 := net.IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}
ipv6p2 := net.IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2}
testCasesA := []struct {
name string
dtyp uint16
rcode int
want []interface{}
}{{
name: "a-record",
dtyp: dns.TypeA,
rcode: dns.RcodeSuccess,
want: []interface{}{ipv4p1},
}, {
name: "aaaa-record",
dtyp: dns.TypeAAAA,
rcode: dns.RcodeSuccess,
want: []interface{}{ipv6p1},
}, {
name: "txt-record",
dtyp: dns.TypeTXT,
rcode: dns.RcodeSuccess,
want: []interface{}{"hello-world"},
}, {
name: "refused",
rcode: dns.RcodeRefused,
}, {
name: "a-records",
dtyp: dns.TypeA,
rcode: dns.RcodeSuccess,
want: []interface{}{ipv4p1, ipv4p2},
}, {
name: "aaaa-records",
dtyp: dns.TypeAAAA,
rcode: dns.RcodeSuccess,
want: []interface{}{ipv6p1, ipv6p2},
}, {
name: "disable-one",
dtyp: dns.TypeA,
rcode: dns.RcodeSuccess,
want: []interface{}{ipv4p2},
}, {
name: "disable-cname",
dtyp: dns.TypeA,
rcode: dns.RcodeSuccess,
want: []interface{}{ipv4p1},
}}
for _, tc := range testCasesA {
t.Run(tc.name, func(t *testing.T) {
host := path.Base(tc.name)
res, err := f.CheckHostRules(host, tc.dtyp, setts)
require.Nil(t, err)
dnsrr := res.DNSRewriteResult
require.NotNil(t, dnsrr)
assert.Equal(t, tc.rcode, dnsrr.RCode)
if tc.rcode == dns.RcodeRefused {
return
}
ipVals := dnsrr.Response[tc.dtyp]
require.Len(t, ipVals, len(tc.want))
for i, val := range tc.want {
require.Equal(t, val, ipVals[i])
}
})
}
t.Run("cname", func(t *testing.T) {
dtyp := dns.TypeA
host := path.Base(t.Name())
res, err := f.CheckHostRules(host, dtyp, setts)
assert.Nil(t, err)
require.Nil(t, err)
assert.Equal(t, "new-cname", res.CanonName)
})
t.Run("a-record", func(t *testing.T) {
dtyp := dns.TypeA
host := path.Base(t.Name())
res, err := f.CheckHostRules(host, dtyp, setts)
assert.Nil(t, err)
if dnsrr := res.DNSRewriteResult; assert.NotNil(t, dnsrr) {
assert.Equal(t, dns.RcodeSuccess, dnsrr.RCode)
if ipVals := dnsrr.Response[dtyp]; assert.Len(t, ipVals, 1) {
assert.Equal(t, ipv4p1, ipVals[0])
}
}
})
t.Run("aaaa-record", func(t *testing.T) {
dtyp := dns.TypeAAAA
host := path.Base(t.Name())
res, err := f.CheckHostRules(host, dtyp, setts)
assert.Nil(t, err)
if dnsrr := res.DNSRewriteResult; assert.NotNil(t, dnsrr) {
assert.Equal(t, dns.RcodeSuccess, dnsrr.RCode)
if ipVals := dnsrr.Response[dtyp]; assert.Len(t, ipVals, 1) {
assert.Equal(t, ipv6p1, ipVals[0])
}
}
})
t.Run("txt-record", func(t *testing.T) {
dtyp := dns.TypeTXT
host := path.Base(t.Name())
res, err := f.CheckHostRules(host, dtyp, setts)
assert.Nil(t, err)
if dnsrr := res.DNSRewriteResult; assert.NotNil(t, dnsrr) {
assert.Equal(t, dns.RcodeSuccess, dnsrr.RCode)
if strVals := dnsrr.Response[dtyp]; assert.Len(t, strVals, 1) {
assert.Equal(t, "hello-world", strVals[0])
}
}
})
t.Run("refused", func(t *testing.T) {
host := path.Base(t.Name())
res, err := f.CheckHostRules(host, dns.TypeA, setts)
assert.Nil(t, err)
if dnsrr := res.DNSRewriteResult; assert.NotNil(t, dnsrr) {
assert.Equal(t, dns.RcodeRefused, dnsrr.RCode)
}
})
t.Run("a-records", func(t *testing.T) {
dtyp := dns.TypeA
host := path.Base(t.Name())
res, err := f.CheckHostRules(host, dtyp, setts)
assert.Nil(t, err)
if dnsrr := res.DNSRewriteResult; assert.NotNil(t, dnsrr) {
assert.Equal(t, dns.RcodeSuccess, dnsrr.RCode)
if ipVals := dnsrr.Response[dtyp]; assert.Len(t, ipVals, 2) {
assert.Equal(t, ipv4p1, ipVals[0])
assert.Equal(t, ipv4p2, ipVals[1])
}
}
})
t.Run("aaaa-records", func(t *testing.T) {
dtyp := dns.TypeAAAA
host := path.Base(t.Name())
res, err := f.CheckHostRules(host, dtyp, setts)
assert.Nil(t, err)
if dnsrr := res.DNSRewriteResult; assert.NotNil(t, dnsrr) {
assert.Equal(t, dns.RcodeSuccess, dnsrr.RCode)
if ipVals := dnsrr.Response[dtyp]; assert.Len(t, ipVals, 2) {
assert.Equal(t, ipv6p1, ipVals[0])
assert.Equal(t, ipv6p2, ipVals[1])
}
}
})
t.Run("disable-one", func(t *testing.T) {
dtyp := dns.TypeA
host := path.Base(t.Name())
res, err := f.CheckHostRules(host, dtyp, setts)
assert.Nil(t, err)
if dnsrr := res.DNSRewriteResult; assert.NotNil(t, dnsrr) {
assert.Equal(t, dns.RcodeSuccess, dnsrr.RCode)
if ipVals := dnsrr.Response[dtyp]; assert.Len(t, ipVals, 1) {
assert.Equal(t, ipv4p2, ipVals[0])
}
}
})
t.Run("disable-cname", func(t *testing.T) {
dtyp := dns.TypeA
host := path.Base(t.Name())
res, err := f.CheckHostRules(host, dtyp, setts)
assert.Nil(t, err)
assert.Empty(t, res.CanonName)
if dnsrr := res.DNSRewriteResult; assert.NotNil(t, dnsrr) {
assert.Equal(t, dns.RcodeSuccess, dnsrr.RCode)
if ipVals := dnsrr.Response[dtyp]; assert.Len(t, ipVals, 1) {
assert.Equal(t, ipv4p1, ipVals[0])
}
}
})
t.Run("disable-cname-many", func(t *testing.T) {
dtyp := dns.TypeA
host := path.Base(t.Name())
res, err := f.CheckHostRules(host, dtyp, setts)
assert.Nil(t, err)
require.Nil(t, err)
assert.Equal(t, "new-cname-2", res.CanonName)
assert.Nil(t, res.DNSRewriteResult)
})
@@ -196,7 +148,7 @@ func TestDNSFilter_CheckHostRules_dnsrewrite(t *testing.T) {
host := path.Base(t.Name())
res, err := f.CheckHostRules(host, dtyp, setts)
assert.Nil(t, err)
require.Nil(t, err)
assert.Empty(t, res.CanonName)
assert.Empty(t, res.Rules)
})

View File

@@ -122,21 +122,27 @@ func findRewrites(a []RewriteEntry, host string) []RewriteEntry {
sort.Sort(rr)
isWC := isWildcard(rr[0].Domain)
if !isWC {
for i, r := range rr {
if isWildcard(r.Domain) {
rr = rr[:i]
break
}
for i, r := range rr {
if isWildcard(r.Domain) {
// Don't use rr[:0], because we need to return at least
// one item here.
rr = rr[:max(1, i)]
break
}
} else {
rr = rr[:1]
}
return rr
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
func rewriteArrayDup(a []RewriteEntry) []RewriteEntry {
a2 := make([]RewriteEntry, len(a))
copy(a2, a)

View File

@@ -6,215 +6,297 @@ import (
"github.com/miekg/dns"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// TODO(e.burkov): All the tests in this file may and should me merged together.
func TestRewrites(t *testing.T) {
d := newForTest(nil, nil)
t.Cleanup(d.Close)
// CNAME, A, AAAA
d.Rewrites = []RewriteEntry{
{"somecname", "somehost.com", 0, nil},
{"somehost.com", "0.0.0.0", 0, nil},
{"host.com", "1.2.3.4", 0, nil},
{"host.com", "1.2.3.5", 0, nil},
{"host.com", "1:2:3::4", 0, nil},
{"www.host.com", "host.com", 0, nil},
}
d.Rewrites = []RewriteEntry{{
// This one and below are about CNAME, A and AAAA.
Domain: "somecname",
Answer: "somehost.com",
}, {
Domain: "somehost.com",
Answer: "0.0.0.0",
}, {
Domain: "host.com",
Answer: "1.2.3.4",
}, {
Domain: "host.com",
Answer: "1.2.3.5",
}, {
Domain: "host.com",
Answer: "1:2:3::4",
}, {
Domain: "www.host.com",
Answer: "host.com",
}, {
// This one is a wildcard.
Domain: "*.host.com",
Answer: "1.2.3.5",
}, {
// This one and below are about wildcard overriding.
Domain: "a.host.com",
Answer: "1.2.3.4",
}, {
// This one is about CNAME and wildcard interacting.
Domain: "*.host2.com",
Answer: "host.com",
}, {
// This one and below are about 2 level CNAME.
Domain: "b.host.com",
Answer: "somecname",
}, {
// This one and below are about 2 level CNAME and wildcard.
Domain: "b.host3.com",
Answer: "a.host3.com",
}, {
Domain: "a.host3.com",
Answer: "x.host.com",
}}
d.prepareRewrites()
r := d.processRewrites("host2.com", dns.TypeA)
assert.Equal(t, NotFilteredNotFound, r.Reason)
r = d.processRewrites("www.host.com", dns.TypeA)
assert.Equal(t, Rewritten, r.Reason)
assert.Equal(t, "host.com", r.CanonName)
assert.Len(t, r.IPList, 2)
assert.True(t, r.IPList[0].Equal(net.IP{1, 2, 3, 4}))
assert.True(t, r.IPList[1].Equal(net.IP{1, 2, 3, 5}))
testCases := []struct {
name string
host string
dtyp uint16
wantCName string
wantVals []net.IP
}{{
name: "not_filtered_not_found",
host: "hoost.com",
dtyp: dns.TypeA,
}, {
name: "rewritten_a",
host: "www.host.com",
dtyp: dns.TypeA,
wantCName: "host.com",
wantVals: []net.IP{{1, 2, 3, 4}, {1, 2, 3, 5}},
}, {
name: "rewritten_aaaa",
host: "www.host.com",
dtyp: dns.TypeAAAA,
wantCName: "host.com",
wantVals: []net.IP{net.ParseIP("1:2:3::4")},
}, {
name: "wildcard_match",
host: "abc.host.com",
dtyp: dns.TypeA,
wantVals: []net.IP{{1, 2, 3, 5}},
}, {
name: "wildcard_override",
host: "a.host.com",
dtyp: dns.TypeA,
wantVals: []net.IP{{1, 2, 3, 4}},
}, {
name: "wildcard_cname_interaction",
host: "www.host2.com",
dtyp: dns.TypeA,
wantCName: "host.com",
wantVals: []net.IP{{1, 2, 3, 4}, {1, 2, 3, 5}},
}, {
name: "two_cnames",
host: "b.host.com",
dtyp: dns.TypeA,
wantCName: "somehost.com",
wantVals: []net.IP{{0, 0, 0, 0}},
}, {
name: "two_cnames_and_wildcard",
host: "b.host3.com",
dtyp: dns.TypeA,
wantCName: "x.host.com",
wantVals: []net.IP{{1, 2, 3, 5}},
}}
r = d.processRewrites("www.host.com", dns.TypeAAAA)
assert.Equal(t, Rewritten, r.Reason)
assert.Equal(t, "host.com", r.CanonName)
assert.Len(t, r.IPList, 1)
assert.True(t, r.IPList[0].Equal(net.ParseIP("1:2:3::4")))
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
valsNum := len(tc.wantVals)
// wildcard
d.Rewrites = []RewriteEntry{
{"host.com", "1.2.3.4", 0, nil},
{"*.host.com", "1.2.3.5", 0, nil},
r := d.processRewrites(tc.host, tc.dtyp)
if valsNum == 0 {
assert.Equal(t, NotFilteredNotFound, r.Reason)
return
}
require.Equal(t, Rewritten, r.Reason)
if tc.wantCName != "" {
assert.Equal(t, tc.wantCName, r.CanonName)
}
require.Len(t, r.IPList, valsNum)
for i, ip := range tc.wantVals {
assert.Equal(t, ip, r.IPList[i])
}
})
}
d.prepareRewrites()
r = d.processRewrites("host.com", dns.TypeA)
assert.Equal(t, Rewritten, r.Reason)
assert.True(t, r.IPList[0].Equal(net.IP{1, 2, 3, 4}))
r = d.processRewrites("www.host.com", dns.TypeA)
assert.Equal(t, Rewritten, r.Reason)
assert.True(t, r.IPList[0].Equal(net.IP{1, 2, 3, 5}))
r = d.processRewrites("www.host2.com", dns.TypeA)
assert.Equal(t, NotFilteredNotFound, r.Reason)
// override a wildcard
d.Rewrites = []RewriteEntry{
{"a.host.com", "1.2.3.4", 0, nil},
{"*.host.com", "1.2.3.5", 0, nil},
}
d.prepareRewrites()
r = d.processRewrites("a.host.com", dns.TypeA)
assert.Equal(t, Rewritten, r.Reason)
assert.Len(t, r.IPList, 1)
assert.True(t, r.IPList[0].Equal(net.IP{1, 2, 3, 4}))
// wildcard + CNAME
d.Rewrites = []RewriteEntry{
{"host.com", "1.2.3.4", 0, nil},
{"*.host.com", "host.com", 0, nil},
}
d.prepareRewrites()
r = d.processRewrites("www.host.com", dns.TypeA)
assert.Equal(t, Rewritten, r.Reason)
assert.Equal(t, "host.com", r.CanonName)
assert.True(t, r.IPList[0].Equal(net.IP{1, 2, 3, 4}))
// 2 CNAMEs
d.Rewrites = []RewriteEntry{
{"b.host.com", "a.host.com", 0, nil},
{"a.host.com", "host.com", 0, nil},
{"host.com", "1.2.3.4", 0, nil},
}
d.prepareRewrites()
r = d.processRewrites("b.host.com", dns.TypeA)
assert.Equal(t, Rewritten, r.Reason)
assert.Equal(t, "host.com", r.CanonName)
assert.Len(t, r.IPList, 1)
assert.True(t, r.IPList[0].Equal(net.IP{1, 2, 3, 4}))
// 2 CNAMEs + wildcard
d.Rewrites = []RewriteEntry{
{"b.host.com", "a.host.com", 0, nil},
{"a.host.com", "x.somehost.com", 0, nil},
{"*.somehost.com", "1.2.3.4", 0, nil},
}
d.prepareRewrites()
r = d.processRewrites("b.host.com", dns.TypeA)
assert.Equal(t, Rewritten, r.Reason)
assert.Equal(t, "x.somehost.com", r.CanonName)
assert.Len(t, r.IPList, 1)
assert.True(t, r.IPList[0].Equal(net.IP{1, 2, 3, 4}))
}
func TestRewritesLevels(t *testing.T) {
d := newForTest(nil, nil)
t.Cleanup(d.Close)
// exact host, wildcard L2, wildcard L3
d.Rewrites = []RewriteEntry{
{"host.com", "1.1.1.1", 0, nil},
{"*.host.com", "2.2.2.2", 0, nil},
{"*.sub.host.com", "3.3.3.3", 0, nil},
}
// Exact host, wildcard L2, wildcard L3.
d.Rewrites = []RewriteEntry{{
Domain: "host.com",
Answer: "1.1.1.1",
}, {
Domain: "*.host.com",
Answer: "2.2.2.2",
}, {
Domain: "*.sub.host.com",
Answer: "3.3.3.3",
}}
d.prepareRewrites()
// match exact
r := d.processRewrites("host.com", dns.TypeA)
assert.Equal(t, Rewritten, r.Reason)
assert.Len(t, r.IPList, 1)
assert.True(t, net.IP{1, 1, 1, 1}.Equal(r.IPList[0]))
testCases := []struct {
name string
host string
want net.IP
}{{
name: "exact_match",
host: "host.com",
want: net.IP{1, 1, 1, 1},
}, {
name: "l2_match",
host: "sub.host.com",
want: net.IP{2, 2, 2, 2},
}, {
name: "l3_match",
host: "my.sub.host.com",
want: net.IP{3, 3, 3, 3},
}}
// match L2
r = d.processRewrites("sub.host.com", dns.TypeA)
assert.Equal(t, Rewritten, r.Reason)
assert.Len(t, r.IPList, 1)
assert.True(t, net.IP{2, 2, 2, 2}.Equal(r.IPList[0]))
// match L3
r = d.processRewrites("my.sub.host.com", dns.TypeA)
assert.Equal(t, Rewritten, r.Reason)
assert.Len(t, r.IPList, 1)
assert.True(t, net.IP{3, 3, 3, 3}.Equal(r.IPList[0]))
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
r := d.processRewrites(tc.host, dns.TypeA)
assert.Equal(t, Rewritten, r.Reason)
require.Len(t, r.IPList, 1)
})
}
}
func TestRewritesExceptionCNAME(t *testing.T) {
d := newForTest(nil, nil)
t.Cleanup(d.Close)
// wildcard; exception for a sub-domain
d.Rewrites = []RewriteEntry{
{"*.host.com", "2.2.2.2", 0, nil},
{"sub.host.com", "sub.host.com", 0, nil},
}
// Wildcard and exception for a sub-domain.
d.Rewrites = []RewriteEntry{{
Domain: "*.host.com",
Answer: "2.2.2.2",
}, {
Domain: "sub.host.com",
Answer: "sub.host.com",
}, {
Domain: "*.sub.host.com",
Answer: "*.sub.host.com",
}}
d.prepareRewrites()
// match sub-domain
r := d.processRewrites("my.host.com", dns.TypeA)
assert.Equal(t, Rewritten, r.Reason)
assert.Len(t, r.IPList, 1)
assert.True(t, net.IP{2, 2, 2, 2}.Equal(r.IPList[0]))
testCases := []struct {
name string
host string
want net.IP
}{{
name: "match_sub-domain",
host: "my.host.com",
want: net.IP{2, 2, 2, 2},
}, {
name: "exception_cname",
host: "sub.host.com",
}, {
name: "exception_wildcard",
host: "my.sub.host.com",
}}
// match sub-domain, but handle exception
r = d.processRewrites("sub.host.com", dns.TypeA)
assert.Equal(t, NotFilteredNotFound, r.Reason)
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
r := d.processRewrites(tc.host, dns.TypeA)
if tc.want == nil {
assert.Equal(t, NotFilteredNotFound, r.Reason)
func TestRewritesExceptionWC(t *testing.T) {
d := newForTest(nil, nil)
t.Cleanup(d.Close)
// wildcard; exception for a sub-wildcard
d.Rewrites = []RewriteEntry{
{"*.host.com", "2.2.2.2", 0, nil},
{"*.sub.host.com", "*.sub.host.com", 0, nil},
return
}
assert.Equal(t, Rewritten, r.Reason)
require.Len(t, r.IPList, 1)
assert.True(t, tc.want.Equal(r.IPList[0]))
})
}
d.prepareRewrites()
// match sub-domain
r := d.processRewrites("my.host.com", dns.TypeA)
assert.Equal(t, Rewritten, r.Reason)
assert.Len(t, r.IPList, 1)
assert.True(t, net.IP{2, 2, 2, 2}.Equal(r.IPList[0]))
// match sub-domain, but handle exception
r = d.processRewrites("my.sub.host.com", dns.TypeA)
assert.Equal(t, NotFilteredNotFound, r.Reason)
}
func TestRewritesExceptionIP(t *testing.T) {
d := newForTest(nil, nil)
t.Cleanup(d.Close)
// exception for AAAA record
d.Rewrites = []RewriteEntry{
{"host.com", "1.2.3.4", 0, nil},
{"host.com", "AAAA", 0, nil},
{"host2.com", "::1", 0, nil},
{"host2.com", "A", 0, nil},
{"host3.com", "A", 0, nil},
}
// Exception for AAAA record.
d.Rewrites = []RewriteEntry{{
Domain: "host.com",
Answer: "1.2.3.4",
}, {
Domain: "host.com",
Answer: "AAAA",
}, {
Domain: "host2.com",
Answer: "::1",
}, {
Domain: "host2.com",
Answer: "A",
}, {
Domain: "host3.com",
Answer: "A",
}}
d.prepareRewrites()
// match domain
r := d.processRewrites("host.com", dns.TypeA)
assert.Equal(t, Rewritten, r.Reason)
assert.Len(t, r.IPList, 1)
assert.True(t, net.IP{1, 2, 3, 4}.Equal(r.IPList[0]))
testCases := []struct {
name string
host string
dtyp uint16
want []net.IP
}{{
name: "match_A",
host: "host.com",
dtyp: dns.TypeA,
want: []net.IP{{1, 2, 3, 4}},
}, {
name: "exception_AAAA_host.com",
host: "host.com",
dtyp: dns.TypeAAAA,
}, {
name: "exception_A_host2.com",
host: "host2.com",
dtyp: dns.TypeA,
}, {
name: "match_AAAA_host2.com",
host: "host2.com",
dtyp: dns.TypeAAAA,
want: []net.IP{net.ParseIP("::1")},
}, {
name: "exception_A_host3.com",
host: "host3.com",
dtyp: dns.TypeA,
}, {
name: "match_AAAA_host3.com",
host: "host3.com",
dtyp: dns.TypeAAAA,
want: []net.IP{},
}}
// match exception
r = d.processRewrites("host.com", dns.TypeAAAA)
assert.Equal(t, NotFilteredNotFound, r.Reason)
for _, tc := range testCases {
t.Run(tc.name+"_"+tc.host, func(t *testing.T) {
r := d.processRewrites(tc.host, tc.dtyp)
if tc.want == nil {
assert.Equal(t, NotFilteredNotFound, r.Reason)
// match exception
r = d.processRewrites("host2.com", dns.TypeA)
assert.Equal(t, NotFilteredNotFound, r.Reason)
return
}
// match domain
r = d.processRewrites("host2.com", dns.TypeAAAA)
assert.Equal(t, Rewritten, r.Reason)
assert.Len(t, r.IPList, 1)
assert.Equal(t, "::1", r.IPList[0].String())
// match exception
r = d.processRewrites("host3.com", dns.TypeA)
assert.Equal(t, NotFilteredNotFound, r.Reason)
// match domain
r = d.processRewrites("host3.com", dns.TypeAAAA)
assert.Equal(t, Rewritten, r.Reason)
assert.Empty(t, r.IPList)
assert.Equal(t, Rewritten, r.Reason)
require.Len(t, r.IPList, len(tc.want))
for _, ip := range tc.want {
assert.True(t, ip.Equal(r.IPList[0]))
}
})
}
}

View File

@@ -229,7 +229,9 @@ func (c *sbCtx) processTXT(resp *dns.Msg) (bool, [][]byte) {
if !matched {
var hash32 [32]byte
copy(hash32[:], hash)
hashHost, ok := c.hashToHost[hash32]
var hashHost string
hashHost, ok = c.hashToHost[hash32]
if ok {
log.Debug("%s: matched %s by %s/%s", c.svc, c.host, hashHost, t)
matched = true
@@ -302,46 +304,70 @@ func check(c *sbCtx, r Result, u upstream.Upstream) (Result, error) {
return Result{}, nil
}
func (d *DNSFilter) checkSafeBrowsing(host string) (Result, error) {
// TODO(a.garipov): Unify with checkParental.
func (d *DNSFilter) checkSafeBrowsing(
host string,
_ uint16,
setts *FilteringSettings,
) (res Result, err error) {
if !setts.SafeBrowsingEnabled {
return Result{}, nil
}
if log.GetLevel() >= log.DEBUG {
timer := log.StartTimer()
defer timer.LogElapsed("SafeBrowsing lookup for %s", host)
}
ctx := &sbCtx{
sctx := &sbCtx{
host: host,
svc: "SafeBrowsing",
cache: gctx.safebrowsingCache,
cacheTime: d.Config.CacheTime,
}
res := Result{
res = Result{
IsFiltered: true,
Reason: FilteredSafeBrowsing,
Rules: []*ResultRule{{
Text: "adguard-malware-shavar",
}},
}
return check(ctx, res, d.safeBrowsingUpstream)
return check(sctx, res, d.safeBrowsingUpstream)
}
func (d *DNSFilter) checkParental(host string) (Result, error) {
// TODO(a.garipov): Unify with checkSafeBrowsing.
func (d *DNSFilter) checkParental(
host string,
_ uint16,
setts *FilteringSettings,
) (res Result, err error) {
if !setts.ParentalEnabled {
return Result{}, nil
}
if log.GetLevel() >= log.DEBUG {
timer := log.StartTimer()
defer timer.LogElapsed("Parental lookup for %s", host)
}
ctx := &sbCtx{
sctx := &sbCtx{
host: host,
svc: "Parental",
cache: gctx.parentalCache,
cacheTime: d.Config.CacheTime,
}
res := Result{
res = Result{
IsFiltered: true,
Reason: FilteredParental,
Rules: []*ResultRule{{
Text: "parental CATEGORY_BLACKLISTED",
}},
}
return check(ctx, res, d.parentalUpstream)
return check(sctx, res, d.parentalUpstream)
}
func httpError(r *http.Request, w http.ResponseWriter, code int, format string, args ...interface{}) {

View File

@@ -7,7 +7,9 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/aghtest"
"github.com/AdguardTeam/golibs/cache"
"github.com/miekg/dns"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestSafeBrowsingHash(t *testing.T) {
@@ -114,11 +116,16 @@ func TestSBPC_checkErrorUpstream(t *testing.T) {
d.SetSafeBrowsingUpstream(ups)
d.SetParentalUpstream(ups)
_, err := d.checkSafeBrowsing("smthng.com")
assert.NotNil(t, err)
setts := &FilteringSettings{
SafeBrowsingEnabled: true,
ParentalEnabled: true,
}
_, err = d.checkParental("smthng.com")
assert.NotNil(t, err)
_, err := d.checkSafeBrowsing("smthng.com", dns.TypeA, setts)
assert.Error(t, err)
_, err = d.checkParental("smthng.com", dns.TypeA, setts)
assert.Error(t, err)
}
func TestSBPC(t *testing.T) {
@@ -127,10 +134,15 @@ func TestSBPC(t *testing.T) {
const hostname = "example.org"
setts := &FilteringSettings{
SafeBrowsingEnabled: true,
ParentalEnabled: true,
}
testCases := []struct {
name string
block bool
testFunc func(string) (Result, error)
testFunc func(host string, _ uint16, _ *FilteringSettings) (res Result, err error)
testCache cache.Cache
}{{
name: "sb_no_block",
@@ -155,25 +167,26 @@ func TestSBPC(t *testing.T) {
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
// Prepare the upstream.
ups := &aghtest.TestBlockUpstream{
Hostname: hostname,
Block: tc.block,
}
d.SetSafeBrowsingUpstream(ups)
d.SetParentalUpstream(ups)
// Prepare the upstream.
ups := &aghtest.TestBlockUpstream{
Hostname: hostname,
Block: tc.block,
}
d.SetSafeBrowsingUpstream(ups)
d.SetParentalUpstream(ups)
t.Run(tc.name, func(t *testing.T) {
// Firstly, check the request blocking.
hits := 0
res, err := tc.testFunc(hostname)
assert.Nil(t, err)
res, err := tc.testFunc(hostname, dns.TypeA, setts)
require.NoError(t, err)
if tc.block {
assert.True(t, res.IsFiltered)
assert.Len(t, res.Rules, 1)
require.Len(t, res.Rules, 1)
hits++
} else {
assert.False(t, res.IsFiltered)
require.False(t, res.IsFiltered)
}
// Check the cache state, check the response is now cached.
@@ -184,13 +197,14 @@ func TestSBPC(t *testing.T) {
assert.Equal(t, 1, ups.RequestsCount())
// Now make the same request to check the cache was used.
res, err = tc.testFunc(hostname)
assert.Nil(t, err)
res, err = tc.testFunc(hostname, dns.TypeA, setts)
require.NoError(t, err)
if tc.block {
assert.True(t, res.IsFiltered)
assert.Len(t, res.Rules, 1)
require.Len(t, res.Rules, 1)
} else {
assert.False(t, res.IsFiltered)
require.False(t, res.IsFiltered)
}
// Check the cache state, it should've been used.
@@ -199,8 +213,8 @@ func TestSBPC(t *testing.T) {
// Check that there were no additional requests.
assert.Equal(t, 1, ups.RequestsCount())
purgeCaches()
})
purgeCaches()
}
}

View File

@@ -69,7 +69,15 @@ func (d *DNSFilter) SafeSearchDomain(host string) (string, bool) {
return val, ok
}
func (d *DNSFilter) checkSafeSearch(host string) (Result, error) {
func (d *DNSFilter) checkSafeSearch(
host string,
_ uint16,
setts *FilteringSettings,
) (res Result, err error) {
if !setts.SafeSearchEnabled {
return Result{}, nil
}
if log.GetLevel() >= log.DEBUG {
timer := log.StartTimer()
defer timer.LogElapsed("SafeSearch: lookup for %s", host)
@@ -88,7 +96,7 @@ func (d *DNSFilter) checkSafeSearch(host string) (Result, error) {
return Result{}, nil
}
res := Result{
res = Result{
IsFiltered: true,
Reason: FilteredSafeSearch,
Rules: []*ResultRule{{}},
@@ -102,21 +110,23 @@ func (d *DNSFilter) checkSafeSearch(host string) (Result, error) {
return res, nil
}
ipAddrs, err := d.resolver.LookupIPAddr(context.Background(), safeHost)
ips, err := d.resolver.LookupIP(context.Background(), "ip", safeHost)
if err != nil {
log.Tracef("SafeSearchDomain for %s was found but failed to lookup for %s cause %s", host, safeHost, err)
return Result{}, err
}
for _, ipAddr := range ipAddrs {
if ipv4 := ipAddr.IP.To4(); ipv4 != nil {
res.Rules[0].IP = ipv4
l := d.setCacheResult(gctx.safeSearchCache, host, res)
log.Debug("SafeSearch: stored in cache: %s (%d bytes)", host, l)
return res, nil
for _, ip := range ips {
if ip = ip.To4(); ip == nil {
continue
}
res.Rules[0].IP = ip
l := d.setCacheResult(gctx.safeSearchCache, host, res)
log.Debug("SafeSearch: stored in cache: %s (%d bytes)", host, l)
return res, nil
}
return Result{}, fmt.Errorf("no ipv4 addresses in safe search response for %s", safeHost)

View File

@@ -5,71 +5,153 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestIsBlockedIPAllowed(t *testing.T) {
a := &accessCtx{}
assert.Nil(t, a.Init([]string{"1.1.1.1", "2.2.0.0/16"}, nil, nil))
func TestIsBlockedIP(t *testing.T) {
const (
ip int = iota
cidr
)
disallowed, disallowedRule := a.IsBlockedIP(net.IPv4(1, 1, 1, 1))
assert.False(t, disallowed)
assert.Empty(t, disallowedRule)
rules := []string{
ip: "1.1.1.1",
cidr: "2.2.0.0/16",
}
disallowed, disallowedRule = a.IsBlockedIP(net.IPv4(1, 1, 1, 2))
assert.True(t, disallowed)
assert.Empty(t, disallowedRule)
testCases := []struct {
name string
allowed bool
ip net.IP
wantDis bool
wantRule string
}{{
name: "allow_ip",
allowed: true,
ip: net.IPv4(1, 1, 1, 1),
wantDis: false,
wantRule: "",
}, {
name: "disallow_ip",
allowed: true,
ip: net.IPv4(1, 1, 1, 2),
wantDis: true,
wantRule: "",
}, {
name: "allow_cidr",
allowed: true,
ip: net.IPv4(2, 2, 1, 1),
wantDis: false,
wantRule: "",
}, {
name: "disallow_cidr",
allowed: true,
ip: net.IPv4(2, 3, 1, 1),
wantDis: true,
wantRule: "",
}, {
name: "allow_ip",
allowed: false,
ip: net.IPv4(1, 1, 1, 1),
wantDis: true,
wantRule: rules[ip],
}, {
name: "disallow_ip",
allowed: false,
ip: net.IPv4(1, 1, 1, 2),
wantDis: false,
wantRule: "",
}, {
name: "allow_cidr",
allowed: false,
ip: net.IPv4(2, 2, 1, 1),
wantDis: true,
wantRule: rules[cidr],
}, {
name: "disallow_cidr",
allowed: false,
ip: net.IPv4(2, 3, 1, 1),
wantDis: false,
wantRule: "",
}}
disallowed, disallowedRule = a.IsBlockedIP(net.IPv4(2, 2, 1, 1))
assert.False(t, disallowed)
assert.Empty(t, disallowedRule)
for _, tc := range testCases {
prefix := "allowed_"
if !tc.allowed {
prefix = "disallowed_"
}
disallowed, disallowedRule = a.IsBlockedIP(net.IPv4(2, 3, 1, 1))
assert.True(t, disallowed)
assert.Empty(t, disallowedRule)
t.Run(prefix+tc.name, func(t *testing.T) {
aCtx := &accessCtx{}
allowedRules := rules
var disallowedRules []string
if !tc.allowed {
allowedRules, disallowedRules = disallowedRules, allowedRules
}
require.Nil(t, aCtx.Init(allowedRules, disallowedRules, nil))
disallowed, rule := aCtx.IsBlockedIP(tc.ip)
assert.Equal(t, tc.wantDis, disallowed)
assert.Equal(t, tc.wantRule, rule)
})
}
}
func TestIsBlockedIPDisallowed(t *testing.T) {
a := &accessCtx{}
assert.Nil(t, a.Init(nil, []string{"1.1.1.1", "2.2.0.0/16"}, nil))
disallowed, disallowedRule := a.IsBlockedIP(net.IPv4(1, 1, 1, 1))
assert.True(t, disallowed)
assert.Equal(t, "1.1.1.1", disallowedRule)
disallowed, disallowedRule = a.IsBlockedIP(net.IPv4(1, 1, 1, 2))
assert.False(t, disallowed)
assert.Empty(t, disallowedRule)
disallowed, disallowedRule = a.IsBlockedIP(net.IPv4(2, 2, 1, 1))
assert.True(t, disallowed)
assert.Equal(t, "2.2.0.0/16", disallowedRule)
disallowed, disallowedRule = a.IsBlockedIP(net.IPv4(2, 3, 1, 1))
assert.False(t, disallowed)
assert.Empty(t, disallowedRule)
}
func TestIsBlockedIPBlockedDomain(t *testing.T) {
a := &accessCtx{}
assert.True(t, a.Init(nil, nil, []string{
func TestIsBlockedDomain(t *testing.T) {
aCtx := &accessCtx{}
require.Nil(t, aCtx.Init(nil, nil, []string{
"host1",
"host2",
"*.host.com",
"||host3.com^",
}) == nil)
}))
// match by "host2.com"
assert.True(t, a.IsBlockedDomain("host1"))
assert.True(t, a.IsBlockedDomain("host2"))
assert.False(t, a.IsBlockedDomain("host3"))
testCases := []struct {
name string
domain string
want bool
}{{
name: "plain_match",
domain: "host1",
want: true,
}, {
name: "plain_mismatch",
domain: "host2",
want: false,
}, {
name: "wildcard_type-1_match_short",
domain: "asdf.host.com",
want: true,
}, {
name: "wildcard_type-1_match_long",
domain: "qwer.asdf.host.com",
want: true,
}, {
name: "wildcard_type-1_mismatch_no-lead",
domain: "host.com",
want: false,
}, {
name: "wildcard_type-1_mismatch_bad-asterisk",
domain: "asdf.zhost.com",
want: false,
}, {
name: "wildcard_type-2_match_simple",
domain: "host3.com",
want: true,
}, {
name: "wildcard_type-2_match_complex",
domain: "asdf.host3.com",
want: true,
}, {
name: "wildcard_type-2_mismatch",
domain: ".host3.com",
want: false,
}}
// match by wildcard "*.host.com"
assert.False(t, a.IsBlockedDomain("host.com"))
assert.True(t, a.IsBlockedDomain("asdf.host.com"))
assert.True(t, a.IsBlockedDomain("qwer.asdf.host.com"))
assert.False(t, a.IsBlockedDomain("asdf.zhost.com"))
// match by wildcard "||host3.com^"
assert.True(t, a.IsBlockedDomain("host3.com"))
assert.True(t, a.IsBlockedDomain("asdf.host3.com"))
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
assert.Equal(t, tc.want, aCtx.IsBlockedDomain(tc.domain))
})
}
}

View File

@@ -10,20 +10,31 @@ import (
"github.com/lucas-clemente/quic-go"
)
const maxDomainPartLen = 64
// maxDomainLabelLen is the maximum allowed length of a domain name label
// according to RFC 1035.
const maxDomainLabelLen = 63
// validateDomainNameLabel returns an error if label is not a valid label of
// a domain name.
func validateDomainNameLabel(label string) (err error) {
if len(label) > maxDomainLabelLen {
return fmt.Errorf("%q is too long, max: %d", label, maxDomainLabelLen)
}
for i, r := range label {
if (r < 'a' || r > 'z') && (r < '0' || r > '9') && r != '-' {
return fmt.Errorf("invalid char %q at index %d in %q", r, i, label)
}
}
return nil
}
// ValidateClientID returns an error if clientID is not a valid client ID.
func ValidateClientID(clientID string) (err error) {
if len(clientID) > maxDomainPartLen {
return fmt.Errorf("client id %q is too long, max: %d", clientID, maxDomainPartLen)
}
for i, r := range clientID {
if (r >= 'a' && r <= 'z') || (r >= '0' && r <= '9') || r == '-' {
continue
}
return fmt.Errorf("invalid char %q at index %d in client id %q", r, i, clientID)
err = validateDomainNameLabel(clientID)
if err != nil {
return fmt.Errorf("invalid client id: %w", err)
}
return nil
@@ -49,7 +60,8 @@ func clientIDFromClientServerName(hostSrvName, cliSrvName string, strict bool) (
clientID = cliSrvName[:len(cliSrvName)-len(hostSrvName)-1]
err = ValidateClientID(clientID)
if err != nil {
return "", fmt.Errorf("invalid client id: %w", err)
// Don't wrap the error, because it's informative enough as is.
return "", err
}
return clientID, nil
@@ -93,7 +105,7 @@ func processClientIDHTTPS(ctx *dnsContext) (rc resultCode) {
err := ValidateClientID(clientID)
if err != nil {
ctx.err = fmt.Errorf("client id check: invalid client id: %w", err)
ctx.err = fmt.Errorf("client id check: %w", err)
return resultCodeError
}

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