Compare commits
54 Commits
fix-client
...
v0.106.0-b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
48e82f9ab5 | ||
|
|
e710ce1100 | ||
|
|
ffb503976b | ||
|
|
179b76da77 | ||
|
|
9631eff608 | ||
|
|
8c735d0dd5 | ||
|
|
27f4f05273 | ||
|
|
3764c1dfe4 | ||
|
|
88d924658e | ||
|
|
a7f9e0122b | ||
|
|
ba3fc242ab | ||
|
|
e10a3fa4b3 | ||
|
|
5d0d32b926 | ||
|
|
3b2f5d7842 | ||
|
|
eb9526cc92 | ||
|
|
ffa0afae27 | ||
|
|
5243399d9d | ||
|
|
3701c3f4bb | ||
|
|
67164f89f3 | ||
|
|
3a67cc2c45 | ||
|
|
9736123483 | ||
|
|
e6a8fe452c | ||
|
|
a818666294 | ||
|
|
75ac1a5346 | ||
|
|
313fd7107f | ||
|
|
d970b79f2b | ||
|
|
4c6bf68d4d | ||
|
|
7e64205d44 | ||
|
|
bfbf73f3cd | ||
|
|
968831c5b9 | ||
|
|
4cf44dd1d4 | ||
|
|
dfdbfee4fd | ||
|
|
2c9992e0cc | ||
|
|
94e21e69af | ||
|
|
c08bf86b71 | ||
|
|
55d4c7ee4f | ||
|
|
6de54b3b99 | ||
|
|
2d7042425e | ||
|
|
1789c96c7f | ||
|
|
8811c8817e | ||
|
|
5aa0ca9319 | ||
|
|
90054974bc | ||
|
|
9f774d776c | ||
|
|
8ead755b67 | ||
|
|
90ebc4d8c9 | ||
|
|
400b76d47b | ||
|
|
8aa8be2921 | ||
|
|
52575d0247 | ||
|
|
7e1b4ca6fe | ||
|
|
a234b63da1 | ||
|
|
94e783d572 | ||
|
|
91403d0b95 | ||
|
|
d6a059e395 | ||
|
|
f893df7e64 |
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
@@ -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':
|
||||
|
||||
67
CHANGELOG.md
67
CHANGELOG.md
@@ -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
|
||||
|
||||
151
HACKING.md
151
HACKING.md
@@ -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
|
||||
|
||||
23
Makefile
23
Makefile
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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 сървър",
|
||||
|
||||
@@ -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 på 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 på 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 opsætte 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 på 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>Få 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": "På 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 (så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 på 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 på <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": "Må 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 på 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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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設定",
|
||||
|
||||
@@ -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 설정",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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": "Открыть Панель управления",
|
||||
|
||||
@@ -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": "දැනුවත් ලුහුබැඳීමක්",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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ı",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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 设置",
|
||||
|
||||
@@ -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 覆寫刪除成功",
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
41
go.mod
@@ -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
142
go.sum
@@ -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=
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
@@ -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))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
@@ -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")
|
||||
}
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
// +build !linux,!darwin
|
||||
|
||||
package sysutil
|
||||
package aghnet
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
78
internal/aghnet/systemresolvers.go
Normal file
78
internal/aghnet/systemresolvers.go
Normal 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
|
||||
}
|
||||
96
internal/aghnet/systemresolvers_others.go
Normal file
96
internal/aghnet/systemresolvers_others.go
Normal 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
|
||||
}
|
||||
74
internal/aghnet/systemresolvers_others_test.go
Normal file
74
internal/aghnet/systemresolvers_others_test.go
Normal 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)
|
||||
})
|
||||
}
|
||||
}
|
||||
33
internal/aghnet/systemresolvers_test.go
Normal file
33
internal/aghnet/systemresolvers_test.go
Normal 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.
|
||||
158
internal/aghnet/systemresolvers_windows.go
Normal file
158
internal/aghnet/systemresolvers_windows.go
Normal 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
|
||||
}
|
||||
7
internal/aghnet/systemresolvers_windows_test.go
Normal file
7
internal/aghnet/systemresolvers_windows_test.go
Normal 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.
|
||||
10
internal/aghos/endian_big.go
Normal file
10
internal/aghos/endian_big.go
Normal 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
|
||||
10
internal/aghos/endian_little.go
Normal file
10
internal/aghos/endian_little.go
Normal 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
47
internal/aghos/os.go
Normal 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
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
// +build freebsd
|
||||
|
||||
package sysutil
|
||||
package aghos
|
||||
|
||||
import (
|
||||
"os"
|
||||
@@ -1,6 +1,6 @@
|
||||
// +build linux
|
||||
|
||||
package sysutil
|
||||
package aghos
|
||||
|
||||
import (
|
||||
"os"
|
||||
@@ -1,6 +1,6 @@
|
||||
// +build aix darwin dragonfly netbsd openbsd solaris
|
||||
|
||||
package sysutil
|
||||
package aghos
|
||||
|
||||
import (
|
||||
"os"
|
||||
@@ -1,6 +1,6 @@
|
||||
// +build windows
|
||||
|
||||
package sysutil
|
||||
package aghos
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -1,4 +1,4 @@
|
||||
package sysutil
|
||||
package aghos
|
||||
|
||||
// ConfigureSyslog reroutes standard logger output to syslog.
|
||||
func ConfigureSyslog(serviceName string) error {
|
||||
@@ -1,6 +1,6 @@
|
||||
// +build !windows,!plan9
|
||||
|
||||
package sysutil
|
||||
package aghos
|
||||
|
||||
import (
|
||||
"log/syslog"
|
||||
@@ -1,6 +1,6 @@
|
||||
// +build windows plan9
|
||||
|
||||
package sysutil
|
||||
package aghos
|
||||
|
||||
import (
|
||||
"strings"
|
||||
@@ -1,4 +1,4 @@
|
||||
package sysutil
|
||||
package aghos
|
||||
|
||||
import (
|
||||
"testing"
|
||||
@@ -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
|
||||
}
|
||||
@@ -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()
|
||||
|
||||
@@ -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
52
internal/dhcpd/bitset.go
Normal 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
|
||||
}
|
||||
90
internal/dhcpd/bitset_test.go
Normal file
90
internal/dhcpd/bitset_test.go
Normal 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)
|
||||
})
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
112
internal/dhcpd/iprange.go
Normal 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
|
||||
}
|
||||
154
internal/dhcpd/iprange_test.go
Normal file
154
internal/dhcpd/iprange_test.go
Normal 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)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
134
internal/dhcpd/options.go
Normal 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
|
||||
}
|
||||
109
internal/dhcpd/options_test.go
Normal file
109
internal/dhcpd/options_test.go
Normal 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)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
})
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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]))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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{}) {
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user