Compare commits
43 Commits
v0.107.0-b
...
v0.107.0-b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d0772fda5f | ||
|
|
5c2ead5f60 | ||
|
|
c14bde280e | ||
|
|
fb6bc322c1 | ||
|
|
12db98230c | ||
|
|
6fa1167251 | ||
|
|
63ee95dfbe | ||
|
|
0030e31e33 | ||
|
|
fc063f580f | ||
|
|
f2046a5f06 | ||
|
|
2f661df88c | ||
|
|
8e8f14aefb | ||
|
|
0217c6ad11 | ||
|
|
5e2a59dbb8 | ||
|
|
5292c7c387 | ||
|
|
b9e85695db | ||
|
|
ebade2b6ce | ||
|
|
f4dde3f2c1 | ||
|
|
4684c8e4ed | ||
|
|
167447547a | ||
|
|
ff4905b2a2 | ||
|
|
194ea6ef95 | ||
|
|
f419896ec6 | ||
|
|
1a693f790b | ||
|
|
1571609e59 | ||
|
|
4b5a66ee61 | ||
|
|
e113b276e7 | ||
|
|
97073d0d9e | ||
|
|
116bedd727 | ||
|
|
232cd381ff | ||
|
|
77c701930e | ||
|
|
e08a64ebe4 | ||
|
|
16e5e09c2e | ||
|
|
9d1656b5c1 | ||
|
|
28f34ca399 | ||
|
|
e25a532987 | ||
|
|
90a853627e | ||
|
|
dab7b439d1 | ||
|
|
3ee0369cb9 | ||
|
|
dbe8b92dfc | ||
|
|
5104b79cf6 | ||
|
|
7547d3a422 | ||
|
|
84e71e912e |
2
.github/ISSUE_TEMPLATE/Bug_report.md
vendored
2
.github/ISSUE_TEMPLATE/Bug_report.md
vendored
@@ -20,7 +20,7 @@ Please answer the following questions for yourself before submitting an issue. *
|
||||
* **Version of AdGuard Home server:**
|
||||
* <!-- (e.g. v0.123.4) -->
|
||||
* **How did you install AdGuard Home:**
|
||||
* <!-- (e.g. Built from source, Snapcraft, Docker, Github releases, etc.) -->
|
||||
* <!-- (e.g. Built from source, Snapcraft, Docker, GitHub releases, etc.) -->
|
||||
* **How did you setup DNS configuration:**
|
||||
* <!-- (System/Router/IoT) -->
|
||||
* **If it's a router or IoT, please write device model:**
|
||||
|
||||
62
CHANGELOG.md
62
CHANGELOG.md
@@ -10,13 +10,23 @@ and this project adheres to
|
||||
## [Unreleased]
|
||||
|
||||
<!--
|
||||
## [v0.107.0] - 2021-06-28 (APPROX.)
|
||||
## [v0.107.0] - 2021-08-03 (APPROX.)
|
||||
-->
|
||||
|
||||
### Added
|
||||
|
||||
- The ability to set the timeout for querying the upstream servers ([#2280]).
|
||||
- The ability to change group and user ID on startup on Unix ([#2763]).
|
||||
- Static IP address detection on FreeBSD ([#3289]).
|
||||
- Optimistic cache ([#2145]).
|
||||
- New possible value of `6h` for `querylog_interval` setting ([#2504]).
|
||||
- Blocking access using client IDs ([#2624], [#3162]).
|
||||
- `source` directives support in `/etc/network/interfaces` on Linux ([#3257]).
|
||||
- RFC 9000 support in DNS-over-QUIC.
|
||||
- Completely disabling statistics by setting the statistics interval to zero
|
||||
([#2141]).
|
||||
- The ability to completely purge DHCP leases ([#1691]).
|
||||
- Settable timeouts for querying the upstream servers ([#2280]).
|
||||
- Configuration file parameters to change group and user ID on startup on Unix
|
||||
([#2763]).
|
||||
- Experimental OpenBSD support for AMD64 and 64-bit ARM CPUs ([#2439]).
|
||||
- Support for custom port in DNS-over-HTTPS profiles for Apple's devices
|
||||
([#3172]).
|
||||
@@ -28,26 +38,41 @@ and this project adheres to
|
||||
([#3185]).
|
||||
- The ability to completely disable reverse DNS resolving of IPs from
|
||||
locally-served networks ([#3184]).
|
||||
- New flag `--local-frontend` to serve dinamically changeable frontend files
|
||||
- New flag `--local-frontend` to serve dynamically changeable frontend files
|
||||
from disk as opposed to the ones that were compiled into the binary.
|
||||
|
||||
### Changed
|
||||
|
||||
- DNS-over-HTTPS queries that come from HTTP proxies in the `trusted_proxies`
|
||||
list now use the real IP address of the client instead of the address of the
|
||||
proxy ([#2799]).
|
||||
- Clients who are blocked by access settings now receive a `REFUSED` response
|
||||
when a protocol other than DNS-over-UDP and DNSCrypt is used.
|
||||
- `querylog_interval` setting is now formatted in hours.
|
||||
- Query log search now supports internationalized domains ([#3012]).
|
||||
- Internationalized domains are now shown decoded in the query log with the
|
||||
original encoded version shown in request details ([#3013]).
|
||||
- When /etc/hosts-type rules have several IPs for one host, all IPs are now
|
||||
returned instead of only the first one ([#1381]).
|
||||
- The setting `rlimit_nofile` is now in the `os` block of the configuration
|
||||
file, together with the new `group` and `user` settings ([#2763]).
|
||||
- Permissions on filter files are now `0o644` instead of `0o600` ([#3198]).
|
||||
|
||||
### Deprecated
|
||||
|
||||
<!--
|
||||
TODO(a.garipov): Remove the Go 1.16 deprecation message if Go 1.17 is not
|
||||
released by then.
|
||||
-->
|
||||
|
||||
- Go 1.16 support. v0.108.0 will require at least Go 1.17 to build.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Switching listening address to unspecified one when bound to a single
|
||||
specified IPv4 address on Darwin (macOS) ([#2807]).
|
||||
- Incomplete HTTP response for static IP address.
|
||||
- DNSCrypt queries weren't appearing in query log ([#3372]).
|
||||
- Wrong IP address for proxied DNS-over-HTTPS queries ([#2799]).
|
||||
- Domain name letter case mismatches in DNS rewrites ([#3351]).
|
||||
- Conflicts between IPv4 and IPv6 DNS rewrites ([#3343]).
|
||||
- Letter case mismatches in `CNAME` filtering ([#3335]).
|
||||
- Occasional breakages on network errors with DNS-over-HTTP upstreams ([#3217]).
|
||||
- Errors when setting static IP on Linux ([#3257]).
|
||||
- Treatment of domain names and FQDNs in custom rules with `$dnsrewrite` that
|
||||
use the `PTR` type ([#3256]).
|
||||
@@ -62,12 +87,23 @@ released by then.
|
||||
|
||||
- Go 1.15 support.
|
||||
|
||||
[#1381]: https://github.com/AdguardTeam/AdGuardHome/issues/1381
|
||||
[#1691]: https://github.com/AdguardTeam/AdGuardHome/issues/1691
|
||||
[#2141]: https://github.com/AdguardTeam/AdGuardHome/issues/2141
|
||||
[#2145]: https://github.com/AdguardTeam/AdGuardHome/issues/2145
|
||||
[#2280]: https://github.com/AdguardTeam/AdGuardHome/issues/2280
|
||||
[#2439]: https://github.com/AdguardTeam/AdGuardHome/issues/2439
|
||||
[#2441]: https://github.com/AdguardTeam/AdGuardHome/issues/2441
|
||||
[#2443]: https://github.com/AdguardTeam/AdGuardHome/issues/2443
|
||||
[#2504]: https://github.com/AdguardTeam/AdGuardHome/issues/2504
|
||||
[#2624]: https://github.com/AdguardTeam/AdGuardHome/issues/2624
|
||||
[#2763]: https://github.com/AdguardTeam/AdGuardHome/issues/2763
|
||||
[#2799]: https://github.com/AdguardTeam/AdGuardHome/issues/2799
|
||||
[#2807]: https://github.com/AdguardTeam/AdGuardHome/issues/2807
|
||||
[#3012]: https://github.com/AdguardTeam/AdGuardHome/issues/3012
|
||||
[#3013]: https://github.com/AdguardTeam/AdGuardHome/issues/3013
|
||||
[#3136]: https://github.com/AdguardTeam/AdGuardHome/issues/3136
|
||||
[#3162]: https://github.com/AdguardTeam/AdGuardHome/issues/3162
|
||||
[#3166]: https://github.com/AdguardTeam/AdGuardHome/issues/3166
|
||||
[#3172]: https://github.com/AdguardTeam/AdGuardHome/issues/3172
|
||||
[#3184]: https://github.com/AdguardTeam/AdGuardHome/issues/3184
|
||||
@@ -75,8 +111,14 @@ released by then.
|
||||
[#3186]: https://github.com/AdguardTeam/AdGuardHome/issues/3186
|
||||
[#3194]: https://github.com/AdguardTeam/AdGuardHome/issues/3194
|
||||
[#3198]: https://github.com/AdguardTeam/AdGuardHome/issues/3198
|
||||
[#3217]: https://github.com/AdguardTeam/AdGuardHome/issues/3217
|
||||
[#3256]: https://github.com/AdguardTeam/AdGuardHome/issues/3256
|
||||
[#3257]: https://github.com/AdguardTeam/AdGuardHome/issues/3257
|
||||
[#3289]: https://github.com/AdguardTeam/AdGuardHome/issues/3289
|
||||
[#3335]: https://github.com/AdguardTeam/AdGuardHome/issues/3335
|
||||
[#3343]: https://github.com/AdguardTeam/AdGuardHome/issues/3343
|
||||
[#3351]: https://github.com/AdguardTeam/AdGuardHome/issues/3351
|
||||
[#3372]: https://github.com/AdguardTeam/AdGuardHome/issues/3372
|
||||
|
||||
|
||||
|
||||
@@ -128,7 +170,7 @@ released by then.
|
||||
### Fixed
|
||||
|
||||
- Local domain name handling when the DHCP server is disabled ([#3028]).
|
||||
- Normalization of perviously-saved invalid static DHCP leases ([#3027]).
|
||||
- Normalization of previously-saved invalid static DHCP leases ([#3027]).
|
||||
- Validation of IPv6 addresses with zones in system resolvers ([#3022]).
|
||||
|
||||
[#3022]: https://github.com/AdguardTeam/AdGuardHome/issues/3022
|
||||
|
||||
513
HACKING.md
513
HACKING.md
@@ -1,504 +1,79 @@
|
||||
# AdGuard Home Developer Guidelines
|
||||
|
||||
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](#code)
|
||||
* [Commenting](#commenting)
|
||||
* [Formatting](#formatting)
|
||||
* [Naming](#naming)
|
||||
* [Testing](#testing)
|
||||
* [Recommended Reading](#recommended-reading)
|
||||
* [Markdown](#markdown)
|
||||
* [Shell Scripting](#shell-scripting)
|
||||
* [Shell Conditionals](#shell-conditionals)
|
||||
* [Text, Including Comments](#text-including-comments)
|
||||
* [YAML](#yaml)
|
||||
|
||||
<!-- NOTE: Use the IDs that GitHub would generate in order for this to work both
|
||||
on GitHub and most other Markdown renderers. Use both "id" and "name"
|
||||
attributes to make it work in Markdown renderers that strip "id". -->
|
||||
|
||||
|
||||
This document was moved to the [AdGuard Code Guidelines repository][repo]. All
|
||||
sections with IDs now only have links to the corresponding files and sections in
|
||||
that repository.
|
||||
|
||||
## <a href="#git" id="git" name="git">Git</a>
|
||||
|
||||
* Call your branches either `NNNN-fix-foo` (where `NNNN` is the ID of the
|
||||
GitHub issue you worked on in this branch) or just `fix-foo` if there was no
|
||||
GitHub issue.
|
||||
|
||||
* Follow the commit message header format:
|
||||
|
||||
```none
|
||||
pkg: fix the network error logging issue
|
||||
```
|
||||
|
||||
Where `pkg` is the directory or Go package (without the `internal/` part)
|
||||
where most changes took place. If there are several such packages, or the
|
||||
change is top-level only, write `all`.
|
||||
|
||||
* Keep your commit messages, including headers, to eighty (**80**) columns.
|
||||
|
||||
* Only use lowercase letters in your commit message headers. The rest of the
|
||||
message should follow the plain text conventions below.
|
||||
|
||||
The only exceptions are direct mentions of identifiers from the source code
|
||||
and filenames like `HACKING.md`.
|
||||
|
||||
|
||||
This section was moved to [its own document][git].
|
||||
|
||||
## <a href="#go" id="go" name="go">Go</a>
|
||||
|
||||
> Not Golang, not GO, not GOLANG, not GoLang. It is Go in natural language,
|
||||
> golang for others.
|
||||
This section was moved to [its own document][go].
|
||||
|
||||
— [@rakyll](https://twitter.com/rakyll/status/1229850223184269312)
|
||||
### <a href="#code" id="code" name="code">Code</a>
|
||||
|
||||
### <a href="#code" id="code" name="code">Code</a>
|
||||
This subsection was moved to the [corresponding section][code] of the Go
|
||||
guidelines document.
|
||||
|
||||
* Always `recover` from panics in new goroutines. Preferably in the very
|
||||
first statement. If all you want there is a log message, use `log.OnPanic`.
|
||||
### <a href="#commenting" id="commenting" name="commenting">Commenting</a>
|
||||
|
||||
* Avoid `fallthrough`. It makes it harder to rearrange `case`s, to reason
|
||||
about the code, and also to switch the code to a handler approach, if that
|
||||
becomes necessary later.
|
||||
This subsection was moved to the [corresponding section][cmnt] of the Go
|
||||
guidelines document.
|
||||
|
||||
* Avoid `goto`.
|
||||
### <a href="#formatting" id="formatting" name="formatting">Formatting</a>
|
||||
|
||||
* Avoid `init` and use explicit initialization functions instead.
|
||||
This subsection was moved to the [corresponding section][fmt] of the Go
|
||||
guidelines document.
|
||||
|
||||
* Avoid `new`, especially with structs.
|
||||
### <a href="#naming" id="naming" name="naming">Naming</a>
|
||||
|
||||
* Check against empty strings like this:
|
||||
This subsection was moved to the [corresponding section][name] of the Go
|
||||
guidelines document.
|
||||
|
||||
```go
|
||||
if s == "" {
|
||||
// …
|
||||
}
|
||||
```
|
||||
### <a href="#testing" id="testing" name="testing">Testing</a>
|
||||
|
||||
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.
|
||||
|
||||
* Define `MarshalFoo` methods on non-pointer receivers, as pointer receivers
|
||||
[can have surprising results][staticcheck-911].
|
||||
|
||||
* Don't mix horizontal and vertical placement of arguments in function and
|
||||
method calls. That is, either this:
|
||||
|
||||
```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 rely only on file names for build tags to work. Always add build tags
|
||||
as well.
|
||||
|
||||
* 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
|
||||
check or struct initialization.
|
||||
|
||||
The exception proving the rule is the table-driven test code, where an
|
||||
additional level of indentation is allowed.
|
||||
|
||||
* Eschew external dependencies, including transitive, unless absolutely
|
||||
necessary.
|
||||
|
||||
* Minimize scope of variables as much as possible.
|
||||
|
||||
* No shadowing, since it can often lead to subtle bugs, especially with
|
||||
errors.
|
||||
|
||||
* Prefer constants to variables where possible. Avoid 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.
|
||||
|
||||
* 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.
|
||||
|
||||
### <a href="#commenting" id="commenting" name="commenting">Commenting</a>
|
||||
|
||||
* See also the “[Text, Including Comments]” section below.
|
||||
|
||||
* Document everything, including unexported top-level identifiers, to build
|
||||
a habit of writing documentation.
|
||||
|
||||
* Don't put identifiers into any kind of quotes.
|
||||
|
||||
* Put comments above the documented entity, **not** to the side, to improve
|
||||
readability.
|
||||
|
||||
* When a method implements an interface, start the doc comment with the
|
||||
standard template:
|
||||
|
||||
```go
|
||||
// Foo implements the Fooer interface for *foo.
|
||||
func (f *foo) Foo() {
|
||||
// …
|
||||
}
|
||||
```
|
||||
|
||||
When the implemented interface is unexported:
|
||||
|
||||
```go
|
||||
// Unwrap implements the hidden wrapper interface for *fooError.
|
||||
func (err *fooError) Unwrap() (unwrapped error) {
|
||||
// …
|
||||
}
|
||||
```
|
||||
|
||||
### <a href="#formatting" id="formatting" name="formatting">Formatting</a>
|
||||
|
||||
* Decorate `break`, `continue`, `fallthrough`, `return`, and other terminating
|
||||
statements with empty lines unless it's the only statement in that block.
|
||||
|
||||
* Don't group type declarations together. Unlike with blocks of `const`s,
|
||||
where a `iota` may be used or where all constants belong to a certain type,
|
||||
there is no reason to group `type`s.
|
||||
|
||||
* Group `require.*` blocks together with the presceding related statements,
|
||||
but separate from the following `assert.*` and unrelated requirements.
|
||||
|
||||
```go
|
||||
val, ok := valMap[key]
|
||||
require.True(t, ok)
|
||||
require.NotNil(t, val)
|
||||
|
||||
assert.Equal(t, expected, val)
|
||||
```
|
||||
|
||||
* Use `gofumpt --extra -s`.
|
||||
|
||||
* Write slices of struct like this:
|
||||
|
||||
```go
|
||||
ts := []T{{
|
||||
Field: Value0,
|
||||
// …
|
||||
}, {
|
||||
Field: Value1,
|
||||
// …
|
||||
}, {
|
||||
Field: Value2,
|
||||
// …
|
||||
}}
|
||||
```
|
||||
|
||||
### <a href="#naming" id="naming" name="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.
|
||||
|
||||
* When naming a file which defines an entity, use singular nouns, unless the
|
||||
entity is some form of a container for other entities:
|
||||
|
||||
```go
|
||||
// File: client.go
|
||||
|
||||
package foo
|
||||
|
||||
type Client struct {
|
||||
// …
|
||||
}
|
||||
```
|
||||
|
||||
```go
|
||||
// File: clients.go
|
||||
|
||||
package foo
|
||||
|
||||
type Clients []*Client
|
||||
|
||||
// …
|
||||
|
||||
type ClientsWithCache struct {
|
||||
// …
|
||||
}
|
||||
```
|
||||
|
||||
### <a href="#testing" id="testing" name="testing">Testing</a>
|
||||
|
||||
* Use `assert.NoError` and `require.NoError` instead of `assert.Nil` and
|
||||
`require.Nil` on errors.
|
||||
|
||||
* Use formatted helpers, like `assert.Nilf` or `require.Nilf`, instead of
|
||||
simple helpers when a formatted message is required.
|
||||
|
||||
* Use functions like `require.Foo` instead of `assert.Foo` when the test
|
||||
cannot continue if the condition is false.
|
||||
|
||||
### <a href="#recommended-reading" id="recommended-reading" name="recommended-reading">Recommended Reading</a>
|
||||
|
||||
* <https://github.com/golang/go/wiki/CodeReviewComments>.
|
||||
|
||||
* <https://github.com/golang/go/wiki/TestComments>.
|
||||
|
||||
* <https://go-proverbs.github.io/>
|
||||
|
||||
[Linus said]: https://www.kernel.org/doc/html/v4.17/process/coding-style.html#indentation
|
||||
[Text, Including Comments]: #text-including-comments
|
||||
[constant errors]: https://dave.cheney.net/2016/04/07/constant-errors
|
||||
[staticcheck-911]: https://github.com/dominikh/go-tools/issues/911
|
||||
This subsection was moved to the [corresponding section][test] of the Go
|
||||
guidelines document.
|
||||
|
||||
### <a href="#recommended-reading" id="recommended-reading" name="recommended-reading">Recommended Reading</a>
|
||||
|
||||
This subsection was moved to the [corresponding section][read] of the Go
|
||||
guidelines document.
|
||||
|
||||
## <a href="#markdown" id="markdown" name="markdown">Markdown</a>
|
||||
|
||||
* **TODO(a.garipov):** Define more Markdown conventions.
|
||||
|
||||
* Prefer triple-backtick preformatted code blocks to indented code blocks.
|
||||
|
||||
* Use asterisks and not underscores for bold and italic.
|
||||
|
||||
* Use either link references or link destinations only. Put all link
|
||||
reference definitions at the end of the second-level block.
|
||||
|
||||
|
||||
This section was moved to [its own document][md].
|
||||
|
||||
## <a href="#shell-scripting" id="shell-scripting" name="shell-scripting">Shell Scripting</a>
|
||||
|
||||
* Avoid bashisms and GNUisms, prefer POSIX features only.
|
||||
|
||||
* Avoid spaces between patterns of the same `case` condition.
|
||||
|
||||
* `export` and `readonly` should be used separately from variable assignment,
|
||||
because otherwise failures in command substitutions won't stop the script.
|
||||
That is, do this:
|
||||
|
||||
```sh
|
||||
X="$( echo 42 )"
|
||||
export X
|
||||
```
|
||||
|
||||
And **not** this:
|
||||
|
||||
```sh
|
||||
# Bad!
|
||||
export X="$( echo 42 )"
|
||||
```
|
||||
|
||||
* If a binary value is needed, use `0` for `false`, and `1` for `true`.
|
||||
|
||||
* Mark every variable that shouldn't change later as `readonly`.
|
||||
|
||||
* Prefer `'raw strings'` to `"double quoted strings"` whenever possible.
|
||||
|
||||
* Put spaces within `$( cmd )`, `$(( expr ))`, and `{ cmd; }`.
|
||||
|
||||
* Put utility flags in the ASCII order and **don't** group them together. For
|
||||
example, `ls -1 -A -q`.
|
||||
|
||||
* Script code lines should not be longer than one hundred (**100**) columns.
|
||||
For comments, see the text section below.
|
||||
|
||||
* `snake_case`, not `camelCase` for variables. `kebab-case` for filenames.
|
||||
|
||||
* Start scripts with the following sections in the following order:
|
||||
|
||||
1. Shebang.
|
||||
1. Some initial documentation (optional).
|
||||
1. Verbosity level parsing (optional).
|
||||
1. `set` options.
|
||||
|
||||
* UPPERCASE names for external exported variables, lowercase for local,
|
||||
unexported ones.
|
||||
|
||||
* Use `set -e -f -u` and also `set -x` in verbose mode.
|
||||
|
||||
* Use the `"$var"` form instead of the `$var` form, unless word splitting is
|
||||
required.
|
||||
|
||||
* When concatenating, always use the form with curly braces to prevent
|
||||
accidental bad variable names. That is, `"${var}_tmp.txt"` and **not**
|
||||
`"$var_tmp.txt"`. The latter will try to lookup variable `var_tmp`.
|
||||
|
||||
* When concatenating, surround the whole string with quotes. That is, use
|
||||
this:
|
||||
|
||||
```sh
|
||||
dir="${TOP_DIR}/sub"
|
||||
```
|
||||
|
||||
And **not** this:
|
||||
|
||||
```sh
|
||||
# Bad!
|
||||
dir="${TOP_DIR}"/sub
|
||||
```
|
||||
|
||||
### <a href="#shell-conditionals" id="shell-conditionals" name="shell-conditionals">Shell Conditionals</a>
|
||||
|
||||
Guidelines and agreements for using command `test`, also known as `[`:
|
||||
|
||||
* For conditionals that check for equality against multiple values, prefer
|
||||
`case` instead of `test`.
|
||||
|
||||
* Prefer the `!= ''` form instead of using `-n` to check if string is empty.
|
||||
|
||||
* Spell compound conditions with `&&`, `||`, and `!` **outside** of `test`
|
||||
instead of `-a`, `-o`, and `!` **inside** of `test` correspondingly. The
|
||||
latter ones are pretty much deprecated in POSIX.
|
||||
|
||||
See also: “[Problems With the `test` Builtin: What Does `-a` Mean?][test]”.
|
||||
|
||||
* Use `=` for strings and `-eq` for numbers to catch typing errors.
|
||||
|
||||
[test]: https://www.oilshell.org/blog/2017/08/31.html
|
||||
This section was moved to [its own document][sh].
|
||||
|
||||
### <a href="#shell-conditionals" id="shell-conditionals" name="shell-conditionals">Shell Conditionals</a>
|
||||
|
||||
This subsection was moved to the [corresponding section][cond] of the Shell
|
||||
guidelines document.
|
||||
|
||||
## <a href="#text-including-comments" id="text-including-comments" name="text-including-comments">Text, Including Comments</a>
|
||||
|
||||
* End sentences with appropriate punctuation.
|
||||
|
||||
* Headers should be written with all initial letters capitalized, except for
|
||||
references to variable names that start with a lowercase letter.
|
||||
|
||||
* Mark temporary todos—that is, todos that must be resolved or removed before
|
||||
publishing a change—with two exclamation signs:
|
||||
|
||||
```go
|
||||
// TODO(usr1): !! Remove this debug before pushing!
|
||||
```
|
||||
|
||||
This makes it easier to find them both during development and during code
|
||||
review.
|
||||
|
||||
* Start sentences with a capital letter, unless the first word is a reference
|
||||
to a variable name that starts with a lowercase letter.
|
||||
|
||||
* Text should wrap at eighty (**80**) columns to be more readable, to use
|
||||
a common standard, and to allow editing or diffing side-by-side without
|
||||
wrapping.
|
||||
|
||||
The only exception are long hyperlinks.
|
||||
|
||||
* Use U.S. English, as it is the most widely used variety of English in the
|
||||
code right now as well as generally.
|
||||
|
||||
* Use double spacing between sentences to make sentence borders more clear.
|
||||
|
||||
* Use the serial comma (a.k.a. Oxford comma) to improve comprehension,
|
||||
decrease ambiguity, and use a common standard.
|
||||
|
||||
* Write todos like this:
|
||||
|
||||
```go
|
||||
// TODO(usr1): Fix the frobulation issue.
|
||||
```
|
||||
|
||||
Or, if several people need to look at the code:
|
||||
|
||||
```go
|
||||
// TODO(usr1, usr2): Fix the frobulation issue.
|
||||
```
|
||||
|
||||
|
||||
This section was moved to [its own document][txt].
|
||||
|
||||
## <a href="#yaml" id="yaml" name="yaml">YAML</a>
|
||||
|
||||
* **TODO(a.garipov):** Define naming conventions for schema names in our
|
||||
OpenAPI YAML file. And just generally OpenAPI conventions.
|
||||
This section was moved to [its own document][yaml].
|
||||
|
||||
* **TODO(a.garipov):** Find a YAML formatter or write our own.
|
||||
|
||||
* All strings, including keys, must be quoted. Reason: the “[NO-rway Law]”.
|
||||
|
||||
* Indent with two (**2**) spaces. YAML documents can get pretty
|
||||
deeply-nested.
|
||||
|
||||
* No extra indentation in multiline arrays:
|
||||
|
||||
```yaml
|
||||
'values':
|
||||
- 'value-1'
|
||||
- 'value-2'
|
||||
- 'value-3'
|
||||
```
|
||||
|
||||
* Prefer single quotes for strings to prevent accidental escaping, unless
|
||||
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 when you do.
|
||||
|
||||
[NO-rway Law]: https://news.ycombinator.com/item?id=17359376
|
||||
[cmnt]: https://github.com/AdguardTeam/CodeGuidelines/blob/master/Go/Go.md#commenting
|
||||
[code]: https://github.com/AdguardTeam/CodeGuidelines/blob/master/Go/Go.md#code
|
||||
[cond]: https://github.com/AdguardTeam/CodeGuidelines/blob/master/Go/Shell.md#shell-conditionals
|
||||
[fmt]: https://github.com/AdguardTeam/CodeGuidelines/blob/master/Go/Go.md#formatting
|
||||
[git]: https://github.com/AdguardTeam/CodeGuidelines/blob/master/Go/Git.md
|
||||
[go]: https://github.com/AdguardTeam/CodeGuidelines/blob/master/Go/Go.md
|
||||
[md]: https://github.com/AdguardTeam/CodeGuidelines/blob/master/Go/Markdown.md
|
||||
[name]: https://github.com/AdguardTeam/CodeGuidelines/blob/master/Go/Go.md#naming
|
||||
[read]: https://github.com/AdguardTeam/CodeGuidelines/blob/master/Go/Go.md#recommended-reading
|
||||
[repo]: https://github.com/AdguardTeam/CodeGuidelines
|
||||
[sh]: https://github.com/AdguardTeam/CodeGuidelines/blob/master/Go/Shell.md
|
||||
[test]: https://github.com/AdguardTeam/CodeGuidelines/blob/master/Go/Go.md#testing
|
||||
[txt]: https://github.com/AdguardTeam/CodeGuidelines/blob/master/Go/Text.md
|
||||
[yaml]: https://github.com/AdguardTeam/CodeGuidelines/blob/master/Go/YAML.md
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
# Make sure to sync any changes with the branch overrides below.
|
||||
'variables':
|
||||
'channel': 'edge'
|
||||
'dockerGo': 'adguard/golang-ubuntu:3.1'
|
||||
'dockerGo': 'adguard/golang-ubuntu:3.3'
|
||||
|
||||
'stages':
|
||||
- 'Make release':
|
||||
@@ -34,11 +34,11 @@
|
||||
'jobs':
|
||||
- 'Publish to Snapstore'
|
||||
|
||||
- 'Publish to Github Releases':
|
||||
- 'Publish to GitHub Releases':
|
||||
'manual': false
|
||||
'final': false
|
||||
'jobs':
|
||||
- 'Publish to Github Releases'
|
||||
- 'Publish to GitHub Releases'
|
||||
|
||||
'Make release':
|
||||
'docker':
|
||||
@@ -194,7 +194,7 @@
|
||||
'requirements':
|
||||
- 'adg-docker': 'true'
|
||||
|
||||
'Publish to Github Releases':
|
||||
'Publish to GitHub Releases':
|
||||
'key': 'PTGR'
|
||||
'other':
|
||||
'clean-working-dir': true
|
||||
@@ -215,7 +215,7 @@
|
||||
export CHANNEL="${bamboo.channel}"
|
||||
if [ "$CHANNEL" != 'release' ] && [ "${CHANNEL}" != 'beta' ]
|
||||
then
|
||||
echo "don't publish to Github Releases for this channel"
|
||||
echo "don't publish to GitHub Releases for this channel"
|
||||
|
||||
exit 0
|
||||
fi
|
||||
@@ -266,7 +266,7 @@
|
||||
# need to build a few of these.
|
||||
'variables':
|
||||
'channel': 'beta'
|
||||
'dockerGo': 'adguard/golang-ubuntu:3.1'
|
||||
'dockerGo': 'adguard/golang-ubuntu:3.3'
|
||||
# release-vX.Y.Z branches are the branches from which the actual final release
|
||||
# is built.
|
||||
- '^release-v[0-9]+\.[0-9]+\.[0-9]+':
|
||||
@@ -276,4 +276,4 @@
|
||||
# are the ones that actually get released.
|
||||
'variables':
|
||||
'channel': 'release'
|
||||
'dockerGo': 'adguard/golang-ubuntu:3.1'
|
||||
'dockerGo': 'adguard/golang-ubuntu:3.3'
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
'key': 'AHBRTSPECS'
|
||||
'name': 'AdGuard Home - Build and run tests'
|
||||
'variables':
|
||||
'dockerGo': 'adguard/golang-ubuntu:3.1'
|
||||
'dockerGo': 'adguard/golang-ubuntu:3.3'
|
||||
|
||||
'stages':
|
||||
- 'Tests':
|
||||
|
||||
@@ -68,6 +68,9 @@
|
||||
"dhcp_new_static_lease": "Новая статычная арэнда",
|
||||
"dhcp_static_leases_not_found": "Не знойдзена статычных арэнд DHCP",
|
||||
"dhcp_add_static_lease": "Дадаць статычную арэнду",
|
||||
"dhcp_reset_leases": "Скінуць усё арэнды",
|
||||
"dhcp_reset_leases_confirm": "Вы ўпэўнены, што хочаце выдаліць усё арэнды?",
|
||||
"dhcp_reset_leases_success": "Арэнды DHCP паспяхова выдалены",
|
||||
"dhcp_reset": "Вы ўпэўнены, што хочаце скінуць налады DHCP?",
|
||||
"country": "Краіна",
|
||||
"city": "Горад",
|
||||
@@ -481,10 +484,12 @@
|
||||
"encryption_key_source_content": "Уставіць змесціва зачыненага ключа",
|
||||
"stats_params": "Канфігурацыя статыстыкі",
|
||||
"config_successfully_saved": "Канфігурацыя паспяхова захавана",
|
||||
"interval_6_hour": "6 гадзін",
|
||||
"interval_24_hour": "24 гадзіны",
|
||||
"interval_days": "{{count}} дзень",
|
||||
"interval_days_plural": "{{count}} дзён",
|
||||
"domain": "Дамен",
|
||||
"punycode": "Punycode",
|
||||
"answer": "Адказ",
|
||||
"filter_added_successfully": "Спіс паспяхова дададзены",
|
||||
"filter_removed_successfully": "Спіс паспяхова выдалены",
|
||||
|
||||
@@ -68,6 +68,9 @@
|
||||
"dhcp_new_static_lease": "Nový statický pronájem",
|
||||
"dhcp_static_leases_not_found": "Nebyly nalezeny žádné statické pronájmy DHCP",
|
||||
"dhcp_add_static_lease": "Přidat statický pronájem",
|
||||
"dhcp_reset_leases": "Resetovat všechny pronájmy",
|
||||
"dhcp_reset_leases_confirm": "Opravdu chcete resetovat všechny pronájmy?",
|
||||
"dhcp_reset_leases_success": "Pronájmy DHCP byli úspěšně resetovány",
|
||||
"dhcp_reset": "Opravdu chcete resetovat konfiguraci DHCP?",
|
||||
"country": "Země",
|
||||
"city": "Město",
|
||||
@@ -109,6 +112,8 @@
|
||||
"for_last_24_hours": "za posledních 24 hodin",
|
||||
"for_last_days": "za posledních {{count}} dní",
|
||||
"for_last_days_plural": "za posledních {{count}} dní",
|
||||
"stats_disabled": "Statistiky byly vypnuty. Můžete je zapnout ze <0>stránky nastavení</0>.",
|
||||
"stats_disabled_short": "Statistiky byly vypnuty",
|
||||
"no_domains_found": "Nenalezeny žádné domény",
|
||||
"requests_count": "Počet požadavků",
|
||||
"top_blocked_domains": "Nejčastěji blokované domény",
|
||||
@@ -321,10 +326,10 @@
|
||||
"install_devices_router_list_4": "Na některých typech routerů nemůžete nastavit vlastní DNS server. V tomto případě může AdGuard Home pomoci, pokud jej nastavíte jako <0>DHCP server</0>. V ostatních případech byste si v manuálu k Vašemu routeru měli zjistit, jak přizpůsobit vlastní DNS servery.",
|
||||
"install_devices_windows_list_1": "Otevřete ovládací panel prostřednictvím nabídky Start nebo vyhledání v systému Windows.",
|
||||
"install_devices_windows_list_2": "Přejděte na kategorii Síť a Internet a poté na Centrum sítí a sdílení.",
|
||||
"install_devices_windows_list_3": "Na levé straně obrazovky najděte možnost Změnit nastavení adaptéru a klepněte na něj.",
|
||||
"install_devices_windows_list_3": "Na levé straně obrazovky najděte možnost \"Změnit nastavení adaptéru\" a klepněte na něj.",
|
||||
"install_devices_windows_list_4": "Vyberte své aktivní spojení, klikněte na něj pravým tlačítkem myši a zvolte Vlastnosti.",
|
||||
"install_devices_windows_list_5": "V seznamu najděte Internet Protocol Version 4 (TCP/IP), vyberte jej a znovu klikněte na Vlastnosti.",
|
||||
"install_devices_windows_list_6": "Zvolte Použít následující adresy serveru DNS a zadejte adresy AdGuard Home..",
|
||||
"install_devices_windows_list_5": "V seznamu najděte \"Internet Protocol Version 4 (TCP/IP)\", (nebo IPv6, \"Internet Protocol Version 6 (TCP/IPv6)\"), vyberte jej a znovu klikněte na Vlastnosti.",
|
||||
"install_devices_windows_list_6": "Zvolte \"Použít následující adresy serveru DNS\" a zadejte adresy serveru AdGuard Home.",
|
||||
"install_devices_macos_list_1": "Klikněte na ikonu Apple a přejděte na položku Systémové předvolby.",
|
||||
"install_devices_macos_list_2": "Klikněte na Síť.",
|
||||
"install_devices_macos_list_3": "Vyberte první připojení v seznamu a klepněte na tlačítko Pokročilé.",
|
||||
@@ -423,9 +428,9 @@
|
||||
"access_title": "Nastavení přístupu",
|
||||
"access_desc": "Zde můžete konfigurovat pravidla přístupu pro server DNS AdGuard Home.",
|
||||
"access_allowed_title": "Povolení klienti",
|
||||
"access_allowed_desc": "Seznam adres CIDR nebo IP. Pokud je nakonfigurován, AdGuard Home bude přijímat požadavky pouze z těchto IP adres.",
|
||||
"access_allowed_desc": "Seznam CIDR, IP adres nebo ID klientů. Pokud je nakonfigurován, AdGuard Home bude přijímat požadavky pouze od těchto klientů.",
|
||||
"access_disallowed_title": "Blokovaní klienti",
|
||||
"access_disallowed_desc": "Seznam adres CIDR nebo IP. Pokud je nakonfigurován, AdGuard Home bude odmítat požadavky pouze z těchto IP adres.",
|
||||
"access_disallowed_desc": "Seznam CIDR, IP adres nebo ID klientů. Pokud je nakonfigurován, AdGuard Home bude odmítat požadavky od těchto klientů. Pokud jsou povolení klienti nakonfigurováni, je toto pole ignorováno.",
|
||||
"access_blocked_title": "Blokované domény",
|
||||
"access_blocked_desc": "Nezaměňujte to s filtry. AdGuard Home zruší dotazy DNS odpovídající těmto doménám a tyto dotazy se neobjeví ani v protokolu dotazů. Zde můžete určit přesné názvy domén, zástupné znaky a pravidla filtrování URL adres, např. \"example.org\", \"*.example.org\" nebo \"||example.org^\".",
|
||||
"access_settings_saved": "Nastavení přístupu bylo úspěšně uloženo",
|
||||
@@ -481,10 +486,12 @@
|
||||
"encryption_key_source_content": "Vložte obsahy soukromého klíče",
|
||||
"stats_params": "Konfigurace statistik",
|
||||
"config_successfully_saved": "Konfigurace byla úspěšně uložena",
|
||||
"interval_6_hour": "6 hodin",
|
||||
"interval_24_hour": "24 hodin",
|
||||
"interval_days": "Dny: {{count}}",
|
||||
"interval_days_plural": "Dny: {{count}}",
|
||||
"domain": "Doména",
|
||||
"punycode": "Punycode",
|
||||
"answer": "Odpověď",
|
||||
"filter_added_successfully": "Seznam byl úspěšně přidán",
|
||||
"filter_removed_successfully": "Seznam byl úspěšně odstraněn",
|
||||
@@ -590,6 +597,8 @@
|
||||
"cache_ttl_min_override_desc": "Prodlužte nejkratší hodnotu TTL (v sekundách) obdrženou z odchozího serveru při ukládání DNS odpovědí do mezipaměti",
|
||||
"cache_ttl_max_override_desc": "Nastavte maximální hodnotu TTL (v sekundách) pro položky v mezipaměti DNS",
|
||||
"ttl_cache_validation": "Minimální hodnota TTL mezipaměti musí být menší nebo rovna maximální hodnotě",
|
||||
"cache_optimistic": "Optimistický režim",
|
||||
"cache_optimistic_desc": "Nechte AdGuard Home odpovědět z mezipaměti, i když už platnost položek skončila. Také se je pokuste obnovit.",
|
||||
"filter_category_general": "Obecné",
|
||||
"filter_category_security": "Bezpečnost",
|
||||
"filter_category_regional": "Regionální",
|
||||
|
||||
@@ -68,6 +68,9 @@
|
||||
"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_leases": "Nulstil alle gyldighedsperioder",
|
||||
"dhcp_reset_leases_confirm": "Sikker på, at du vil nulstille alle gyldighedsperioder?",
|
||||
"dhcp_reset_leases_success": "DHCP- gyldighedsperioder nulstillet",
|
||||
"dhcp_reset": "Sikker på, at du vil nulstille DHCP-opsætningen?",
|
||||
"country": "Land",
|
||||
"city": "By",
|
||||
@@ -109,6 +112,8 @@
|
||||
"for_last_24_hours": "de seneste 24 timer",
|
||||
"for_last_days": "den seneste {{count}} dag",
|
||||
"for_last_days_plural": "de seneste {{count}} dage",
|
||||
"stats_disabled": "Statistikker er deaktiveret. De kan aktiveres via <0>indstillingssiden</0>.",
|
||||
"stats_disabled_short": "Statistikker er deaktiveret",
|
||||
"no_domains_found": "Ingen domæner fundet",
|
||||
"requests_count": "Antal forespørgsler",
|
||||
"top_blocked_domains": "Hyppigst blokerede domæner",
|
||||
@@ -423,9 +428,9 @@
|
||||
"access_title": "Adgangsindstillinger",
|
||||
"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 opsat, accepterer AdGuard Home kun forespørgsler fra disse IP-adresser.",
|
||||
"access_allowed_desc": "En liste over CIDR-, IP-adresser eller klient-ID'er. Hvis opsat, accepterer AdGuard Home kun forespørgsler fra disse klienter.",
|
||||
"access_disallowed_title": "Ikke tilladte klienter",
|
||||
"access_disallowed_desc": "En liste over CIDR- eller IP-adresser. Hvis opsat, dropper AdGuard Home forespørgsler fra disse IP-adresser.",
|
||||
"access_disallowed_desc": "En liste over CIDR-, IP-adresser eller klient-ID'er. Hvis opsat, dropper AdGuard Home forespørgsler fra disse klienter. Opsættes tilladte klienter, ignoreres dette felt.",
|
||||
"access_blocked_title": "Ikke tilladte domæner",
|
||||
"access_blocked_desc": "Ikke at forveksle med filtre. AdGuard Home dropper DNS-forespørgsler matchende disse domæner, ej heller vil forespørgslerne optræde i forespørgselsloggen. Der kan angives præcise domænenavne, jokertegn eller URL-filterregler, f.eks. \"eksempel.org\", \"*.eksempel.org\", \"||eksempel.org^\" eller tilsvarende.",
|
||||
"access_settings_saved": "Adgangsindstillinger gemt",
|
||||
@@ -481,10 +486,12 @@
|
||||
"encryption_key_source_content": "Indsæt indholdet af den private nøgle",
|
||||
"stats_params": "Statistikopsætning",
|
||||
"config_successfully_saved": "Opsætning er gemt",
|
||||
"interval_6_hour": "6 timer",
|
||||
"interval_24_hour": "24 timer",
|
||||
"interval_days": "{{count}} dag",
|
||||
"interval_days_plural": "{{count}} dage",
|
||||
"domain": "Domæne",
|
||||
"punycode": "Punycode",
|
||||
"answer": "Svar",
|
||||
"filter_added_successfully": "Listen er tilføjet",
|
||||
"filter_removed_successfully": "Listen er blevet fjernet",
|
||||
@@ -590,6 +597,8 @@
|
||||
"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",
|
||||
"cache_optimistic": "Optimistisk",
|
||||
"cache_optimistic_desc": "Får AdGuard Home til at svare fra cachen, selv når posterne er udløbet, og prøver også at opdatere dem.",
|
||||
"filter_category_general": "Generelt",
|
||||
"filter_category_security": "Sikkerhed",
|
||||
"filter_category_regional": "Regional",
|
||||
|
||||
@@ -68,6 +68,9 @@
|
||||
"dhcp_new_static_lease": "Neuer statischer Lease",
|
||||
"dhcp_static_leases_not_found": "Keine statischen DHCP-Leases gefunden",
|
||||
"dhcp_add_static_lease": "Statischen Lease hinzufügen",
|
||||
"dhcp_reset_leases": "Setzen Sie alle Leases zurück",
|
||||
"dhcp_reset_leases_confirm": "Möchten Sie wirklich alle Leases zurücksetzen?",
|
||||
"dhcp_reset_leases_success": "DHCP-Leases erfolgreich zurückgesetzt",
|
||||
"dhcp_reset": "Möchten Sie die DHCP-Konfiguration wirklich zurücksetzen?",
|
||||
"country": "Land",
|
||||
"city": "Stadt",
|
||||
@@ -109,6 +112,8 @@
|
||||
"for_last_24_hours": "für die letzten 24 Stunden",
|
||||
"for_last_days": "am letzten {{count}} Tag",
|
||||
"for_last_days_plural": "in den letzten {{count}} Tage",
|
||||
"stats_disabled": "Die Statistik wurde deaktiviert. Sie können diese in den <0>Einstellungen</0> erneut aktivieren.",
|
||||
"stats_disabled_short": "Die Statistik wurde deaktiviert",
|
||||
"no_domains_found": "Keine Domains gefunden",
|
||||
"requests_count": "Anzahl der Anfragen",
|
||||
"top_blocked_domains": "Am häufigsten gesperrte Domains",
|
||||
@@ -323,7 +328,7 @@
|
||||
"install_devices_windows_list_2": "Öffnen Sie die Kategorie „Netzwerk und Internet” und dann „Netzwerk- und Freigabecenter”.",
|
||||
"install_devices_windows_list_3": "Suchen Sie auf der linken Seite des Bildschirms nach „Adaptereinstellungen ändern” und klicken Sie darauf.",
|
||||
"install_devices_windows_list_4": "Wählen Sie Ihre aktive Verbindung aus, klicken Sie mit der rechten Maustaste darauf und wählen Sie „Eigenschaften”.",
|
||||
"install_devices_windows_list_5": "Suchen Sie in der Liste nach „Internet Protokoll Version 4 (TCP/IP)”, markieren Sie diese und klicken Sie dann erneut auf „Eigenschaften”.",
|
||||
"install_devices_windows_list_5": "Suchen Sie in der Liste nach „Internet Protokoll Version 4 (TCP/IP)” (oder, für IPv6, „Internet Protocol Version 6 (TCP/IPv6)“), markieren Sie diese und klicken Sie dann erneut auf „Eigenschaften”.",
|
||||
"install_devices_windows_list_6": "Wählen Sie „Folgende DNS-Serveradressen verwenden” und geben Sie Ihre AdGuard Home-Serveradressen ein.",
|
||||
"install_devices_macos_list_1": "Klicken Sie auf das Apple-Symbol (oben links in der Menüzeile) und wählen den Eintrag „Systemeinstellungen”.",
|
||||
"install_devices_macos_list_2": "Klicken Sie dort auf „Netzwerk”",
|
||||
@@ -423,9 +428,9 @@
|
||||
"access_title": "Zugriffsrechte",
|
||||
"access_desc": "Hier können Sie die Zugriffsregeln für den AdGuard Home DNS-Server konfigurieren.",
|
||||
"access_allowed_title": "Zugelassene Clients",
|
||||
"access_allowed_desc": "Eine Liste von CIDR- oder IP-Adressen. Wenn konfiguriert, akzeptiert AdGuard Home nur Anfragen von diesen IP-Adressen.",
|
||||
"access_allowed_desc": "Eine Liste von CIDRs, IP-Adressen oder Client-IDs. Wenn konfiguriert, akzeptiert AdGuard Home Anfragen von diesen Clients.",
|
||||
"access_disallowed_title": "Nicht zugelassene Clients",
|
||||
"access_disallowed_desc": "Eine Liste von CIDR- oder IP-Adressen. Wenn konfiguriert, löscht AdGuard Home Anfragen von diesen IP-Adressen.",
|
||||
"access_disallowed_desc": "Eine Liste von CIDRs, IP-Adressen oder Client-IDs. Wenn konfiguriert, löscht AdGuard Home Anfragen von diesen Clients. Wenn erlaubte Clients konfiguriert sind, wird dieses Feld ignoriert.",
|
||||
"access_blocked_title": "Nicht zugelassene Domains",
|
||||
"access_blocked_desc": "Verwechseln Sie dies nicht mit Filtern. AdGuard Home verwirft DNS-Abfragen, die mit diesen Domänen übereinstimmen, und diese Abfragen erscheinen nicht einmal im Abfrageprotokoll. Hier können Sie die genauen Domain-Namen, Wildcards und URL-Filter-Regeln angeben, z.B. 'beispiel.org', '*.beispiel.org' oder '||beispiel.org^'.",
|
||||
"access_settings_saved": "Zugriffseinstellungen erfolgreich gespeichert",
|
||||
@@ -481,10 +486,12 @@
|
||||
"encryption_key_source_content": "Inhalt des privaten Schlüssels einfügen",
|
||||
"stats_params": "Statistikkonfiguration",
|
||||
"config_successfully_saved": "Konfiguration erfolgreich gespeichert",
|
||||
"interval_6_hour": "6 Stunden",
|
||||
"interval_24_hour": "24 Stunden",
|
||||
"interval_days": "{{count}} Tag",
|
||||
"interval_days_plural": "{{count}} Tage",
|
||||
"domain": "Domain",
|
||||
"punycode": "Punycode",
|
||||
"answer": "Antwort",
|
||||
"filter_added_successfully": "Der Filter wurde erfolgreich hinzugefügt",
|
||||
"filter_removed_successfully": "Der Filter wurde erfolgreich entfernt",
|
||||
@@ -590,6 +597,8 @@
|
||||
"cache_ttl_min_override_desc": "Überschreibt den TTL-Minimalwert, der vom vorgeschalteten Server empfangen wurde. Dieser Wert darf nicht mehr als 3600 (Sek.) (≙ 1 Stunde) betragen.",
|
||||
"cache_ttl_max_override_desc": "Überschreibt den TLL-Maximalwert, der vom vorgeschalteten Server empfangen wurde.",
|
||||
"ttl_cache_validation": "Der minimale Cache des TTL-Wertes muss kleiner oder gleich dem maximalen Wert sein",
|
||||
"cache_optimistic": "Optimistisch",
|
||||
"cache_optimistic_desc": "Sorgt dafür, dass AdGuard Home auch dann aus dem Zwischenspeicher antwortet, wenn die Einträge abgelaufen sind, und versucht zudem, diese zu aktualisieren.",
|
||||
"filter_category_general": "Allgemein",
|
||||
"filter_category_security": "Sicherheit",
|
||||
"filter_category_regional": "Regional",
|
||||
|
||||
@@ -68,6 +68,9 @@
|
||||
"dhcp_new_static_lease": "New static lease",
|
||||
"dhcp_static_leases_not_found": "No DHCP static leases found",
|
||||
"dhcp_add_static_lease": "Add static lease",
|
||||
"dhcp_reset_leases": "Reset all leases",
|
||||
"dhcp_reset_leases_confirm": "Are you sure you want to reset all leases?",
|
||||
"dhcp_reset_leases_success": "DHCP leases successfully reset",
|
||||
"dhcp_reset": "Are you sure you want to reset the DHCP configuration?",
|
||||
"country": "Country",
|
||||
"city": "City",
|
||||
@@ -109,6 +112,8 @@
|
||||
"for_last_24_hours": "for the last 24 hours",
|
||||
"for_last_days": "for the last {{count}} day",
|
||||
"for_last_days_plural": "for the last {{count}} days",
|
||||
"stats_disabled": "The statistics have been disabled. You can turn it on from the <0>settings page</0>.",
|
||||
"stats_disabled_short": "The statistics have been disabled",
|
||||
"no_domains_found": "No domains found",
|
||||
"requests_count": "Requests count",
|
||||
"top_blocked_domains": "Top blocked domains",
|
||||
@@ -321,10 +326,10 @@
|
||||
"install_devices_router_list_4": "On some router types, a custom DNS server cannot be set up. In that case, setting up AdGuard Home as a <0>DHCP server</0> may help. Otherwise, you should check the router manual on how to customize DNS servers on your specific router model.",
|
||||
"install_devices_windows_list_1": "Open Control Panel through Start menu or Windows search.",
|
||||
"install_devices_windows_list_2": "Go to Network and Internet category and then to Network and Sharing Center.",
|
||||
"install_devices_windows_list_3": "On the left side of the screen find Change adapter settings and click on it.",
|
||||
"install_devices_windows_list_3": "On the left side of the screen find \"Change adapter settings\" and click on it.",
|
||||
"install_devices_windows_list_4": "Select your active connection, right-click on it and choose Properties.",
|
||||
"install_devices_windows_list_5": "Find Internet Protocol Version 4 (TCP/IP) in the list, select it and then click on Properties again.",
|
||||
"install_devices_windows_list_6": "Choose Use the following DNS server addresses and enter your AdGuard Home server addresses.",
|
||||
"install_devices_windows_list_5": "Find \"Internet Protocol Version 4 (TCP/IPv4)\" (or, for IPv6, \"Internet Protocol Version 6 (TCP/IPv6)\") in the list, select it and then click on Properties again.",
|
||||
"install_devices_windows_list_6": "Choose \"Use the following DNS server addresses\" and enter your AdGuard Home server addresses.",
|
||||
"install_devices_macos_list_1": "Click on Apple icon and go to System Preferences.",
|
||||
"install_devices_macos_list_2": "Click on Network.",
|
||||
"install_devices_macos_list_3": "Select the first connection in your list and click Advanced.",
|
||||
@@ -423,9 +428,9 @@
|
||||
"access_title": "Access settings",
|
||||
"access_desc": "Here you can configure access rules for the AdGuard Home DNS server.",
|
||||
"access_allowed_title": "Allowed clients",
|
||||
"access_allowed_desc": "A list of CIDR or IP addresses. If configured, AdGuard Home will accept requests from these IP addresses only.",
|
||||
"access_allowed_desc": "A list of CIDRs, IP addresses, or client IDs. If configured, AdGuard Home will accept requests only from these clients.",
|
||||
"access_disallowed_title": "Disallowed clients",
|
||||
"access_disallowed_desc": "A list of CIDR or IP addresses. If configured, AdGuard Home will drop requests from these IP addresses.",
|
||||
"access_disallowed_desc": "A list of CIDRs, IP addresses, or client IDs. If configured, AdGuard Home will drop requests from these clients. If allowed clients are configured, this field is ignored.",
|
||||
"access_blocked_title": "Disallowed domains",
|
||||
"access_blocked_desc": "Not to be confused with filters. AdGuard Home drops DNS queries matching these domains, and these queries don't even appear in the query log. You can specify exact domain names, wildcards, or URL filter rules, e.g. \"example.org\", \"*.example.org\", or \"||example.org^\" correspondingly.",
|
||||
"access_settings_saved": "Access settings successfully saved",
|
||||
@@ -481,10 +486,12 @@
|
||||
"encryption_key_source_content": "Paste the private key contents",
|
||||
"stats_params": "Statistics configuration",
|
||||
"config_successfully_saved": "Configuration successfully saved",
|
||||
"interval_6_hour": "6 hours",
|
||||
"interval_24_hour": "24 hours",
|
||||
"interval_days": "{{count}} day",
|
||||
"interval_days_plural": "{{count}} days",
|
||||
"domain": "Domain",
|
||||
"punycode": "Punycode",
|
||||
"answer": "Answer",
|
||||
"filter_added_successfully": "The list has been successfully added",
|
||||
"filter_removed_successfully": "The list has been successfully removed",
|
||||
@@ -590,6 +597,8 @@
|
||||
"cache_ttl_min_override_desc": "Extend short time-to-live values (seconds) received from the upstream server when caching DNS responses",
|
||||
"cache_ttl_max_override_desc": "Set a maximum time-to-live value (seconds) for entries in the DNS cache",
|
||||
"ttl_cache_validation": "Minimum cache TTL value must be less than or equal to the maximum value",
|
||||
"cache_optimistic": "Optimistic",
|
||||
"cache_optimistic_desc": "Make AdGuard Home respond from the cache even when the entries are expired and also try to refresh them.",
|
||||
"filter_category_general": "General",
|
||||
"filter_category_security": "Security",
|
||||
"filter_category_regional": "Regional",
|
||||
|
||||
@@ -68,6 +68,9 @@
|
||||
"dhcp_new_static_lease": "Nueva asignación estática",
|
||||
"dhcp_static_leases_not_found": "No se han encontrado asignaciones DHCP estáticas",
|
||||
"dhcp_add_static_lease": "Añadir asignación estática",
|
||||
"dhcp_reset_leases": "Restablecer todas las asignaciones",
|
||||
"dhcp_reset_leases_confirm": "¿Estás seguro de que deseas restablecer todas las asignaciones?",
|
||||
"dhcp_reset_leases_success": "Asignaciones DHCP restablecidas correctamente",
|
||||
"dhcp_reset": "¿Estás seguro de que deseas restablecer la configuración DHCP?",
|
||||
"country": "País",
|
||||
"city": "Ciudad",
|
||||
@@ -109,6 +112,8 @@
|
||||
"for_last_24_hours": "en las últimas 24 horas",
|
||||
"for_last_days": "durante los últimos {{count}} días",
|
||||
"for_last_days_plural": "durante los últimos {{count}} días",
|
||||
"stats_disabled": "Las estadísticas se han deshabilitado. Puedes habilitarlas desde la <0>página de configuración</0>.",
|
||||
"stats_disabled_short": "Las estadísticas se han deshabilitado",
|
||||
"no_domains_found": "No se han encontrado dominios",
|
||||
"requests_count": "Número de peticiones",
|
||||
"top_blocked_domains": "Dominios más bloqueados",
|
||||
@@ -321,10 +326,10 @@
|
||||
"install_devices_router_list_4": "En algunos tipos de router, no se puede configurar un servidor DNS personalizado. En ese caso, configurar AdGuard Home como <0>servidor DHCP</0> puede ayudar. De lo contrario, debes consultar el manual del router para saber cómo personalizar los servidores DNS en tu modelo de router específico.",
|
||||
"install_devices_windows_list_1": "Abre el Panel de control a través del menú Inicio o en el buscador de Windows.",
|
||||
"install_devices_windows_list_2": "Ve a la categoría Redes e Internet, luego a Centro de redes y recursos compartidos.",
|
||||
"install_devices_windows_list_3": "En el lado izquierdo de la pantalla, busca Cambiar configuración del adaptador y luego haz clic en él.",
|
||||
"install_devices_windows_list_3": "En el lado izquierdo de la pantalla, busca \"Cambiar configuración del adaptador\" y luego haz clic en él.",
|
||||
"install_devices_windows_list_4": "Selecciona tu conexión activa, haz clic derecho sobre ella y elige Propiedades.",
|
||||
"install_devices_windows_list_5": "Busca en la lista el Protocolo de Internet versión 4 (TCP/IP), selecciónalo y vuelve a hacer clic en Propiedades.",
|
||||
"install_devices_windows_list_6": "Elige Usar las siguientes direcciones de servidor DNS e ingresa las direcciones de tu servidor AdGuard Home.",
|
||||
"install_devices_windows_list_5": "Busca en la lista el \"Protocolo de Internet versión 4 (TCP/IPv4)\" (o \"Protocolo de Internet versión 6 (TCP/IPv6)\"), selecciónalo y vuelve a hacer clic en Propiedades.",
|
||||
"install_devices_windows_list_6": "Elige \"Usar las siguientes direcciones de servidor DNS\" e ingresa las direcciones de tu servidor AdGuard Home.",
|
||||
"install_devices_macos_list_1": "Haz clic en el icono de Apple y ve a Preferencias del sistema.",
|
||||
"install_devices_macos_list_2": "Haz clic en Red.",
|
||||
"install_devices_macos_list_3": "Selecciona la primera conexión de la lista y haz clic en Avanzado.",
|
||||
@@ -423,9 +428,9 @@
|
||||
"access_title": "Configuración de acceso",
|
||||
"access_desc": "Aquí puedes configurar las reglas de acceso para el servidor DNS de AdGuard Home.",
|
||||
"access_allowed_title": "Clientes permitidos",
|
||||
"access_allowed_desc": "Lista de CIDR o direcciones IP. Si está configurado, AdGuard Home solo aceptará peticiones de estas direcciones IP.",
|
||||
"access_allowed_desc": "Lista de CIDR, direcciones IP o ID de clientes. Si está configurado, AdGuard Home aceptará peticiones solo de estos clientes.",
|
||||
"access_disallowed_title": "Clientes no permitidos",
|
||||
"access_disallowed_desc": "Lista de CIDR o direcciones IP. Si está configurado, AdGuard Home descartará las peticiones de estas direcciones IP.",
|
||||
"access_disallowed_desc": "Lista de CIDR, direcciones IP o ID de clientes. Si está configurado, AdGuard Home descartará las peticiones de estos clientes. Si se configuran clientes permitidos, este campo será ignorado.",
|
||||
"access_blocked_title": "Dominios no permitidos",
|
||||
"access_blocked_desc": "No debe confundirse con filtros. AdGuard Home descartará las consultas DNS que coincidan con estos dominios, y estas consultas ni siquiera aparecerán en el registro de consultas. Puedes especificar nombres de dominio exactos, comodines o reglas de filtrado de URL, por ejemplo: \"ejemplo.org\", \"*.ejemplo.org\" o \"||ejemplo.org^\" correspondientemente.",
|
||||
"access_settings_saved": "Configuración de acceso guardado correctamente",
|
||||
@@ -481,10 +486,12 @@
|
||||
"encryption_key_source_content": "Pegar el contenido de la clave privada",
|
||||
"stats_params": "Configuración de estadísticas",
|
||||
"config_successfully_saved": "Configuración guardada correctamente",
|
||||
"interval_6_hour": "6 horas",
|
||||
"interval_24_hour": "24 horas",
|
||||
"interval_days": "{{count}} día",
|
||||
"interval_days_plural": "{{count}} días",
|
||||
"domain": "Dominio",
|
||||
"punycode": "Punycode",
|
||||
"answer": "Respuesta",
|
||||
"filter_added_successfully": "La lista ha sido añadida correctamente",
|
||||
"filter_removed_successfully": "La lista ha sido eliminada correctamente",
|
||||
@@ -544,7 +551,7 @@
|
||||
"filtered_custom_rules": "Filtrado por reglas de filtrado personalizado",
|
||||
"choose_from_list": "Elegir de la lista",
|
||||
"add_custom_list": "Añadir lista personalizada",
|
||||
"host_whitelisted": "El host está en la lista blanca",
|
||||
"host_whitelisted": "El host está permitido",
|
||||
"check_ip": "Direcciones IP: {{ip}}",
|
||||
"check_cname": "CNAME: {{cname}}",
|
||||
"check_reason": "Razón: {{reason}}",
|
||||
@@ -569,7 +576,7 @@
|
||||
"validated_with_dnssec": "Validado con DNSSEC",
|
||||
"all_queries": "Todas las consultas",
|
||||
"show_blocked_responses": "Bloqueado",
|
||||
"show_whitelisted_responses": "En lista blanca",
|
||||
"show_whitelisted_responses": "Permitido",
|
||||
"show_processed_responses": "Procesado",
|
||||
"blocked_safebrowsing": "Bloqueado por navegación segura",
|
||||
"blocked_adult_websites": "Sitios web para adultos bloqueado",
|
||||
@@ -590,6 +597,8 @@
|
||||
"cache_ttl_min_override_desc": "Amplia el corto tiempo de vida de los valores recibidos del servidor DNS de subida al almacenar en caché las respuestas DNS",
|
||||
"cache_ttl_max_override_desc": "Establece un valor de tiempo de vida máximo para las entradas en la caché DNS",
|
||||
"ttl_cache_validation": "El valor TTL mínimo de la caché debe ser menor o igual al valor máximo",
|
||||
"cache_optimistic": "Optimista",
|
||||
"cache_optimistic_desc": "Haz que AdGuard Home responda desde la caché incluso cuando las entradas estén expiradas y también intente actualizarlas.",
|
||||
"filter_category_general": "General",
|
||||
"filter_category_security": "Seguridad",
|
||||
"filter_category_regional": "Regional",
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"bootstrap_dns": "Serveurs DNS d'amorçage",
|
||||
"bootstrap_dns_desc": "Les serveurs DNS d'amorçage sont utilisés pour résoudre les adresses IP des résolveurs DoH/DoT que vous spécifiez comme upstream.",
|
||||
"local_ptr_title": "Serveurs DNS privés inverses",
|
||||
"local_ptr_desc": "Les serveurs DNS que AdGuard Home utilise pour les requêtes PTR locales. Ces serveurs sont utilisés pour résoudre les noms d'hôte des clients avec des adresses IP privées, par exemple \"192.168.12.34\", en utilisant le DNS inversé. Si ce paramètre n'est pas défini, AdGuard Home utilise les adresses des résolveurs DNS par défaut de votre système d'exploitation, à l'exception des adresses d'AdGuard Home lui-même.",
|
||||
"local_ptr_desc": "Les serveurs DNS que AdGuard Home utilise pour les requêtes PTR locales. Ces serveurs sont utilisés pour résoudre les noms d'hôte des clients avec des adresses IP privées, par exemple « 192.168.12.34 », en utilisant le DNS inversé. Si ce paramètre n'est pas défini, AdGuard Home utilise les adresses des résolveurs DNS par défaut de votre système d'exploitation, à l'exception des adresses d'AdGuard Home lui-même.",
|
||||
"local_ptr_default_resolver": "Par défaut, AdGuard Home utilise les résolveurs DNS inversés suivants : {{ip}}.",
|
||||
"local_ptr_no_default_resolver": "AdGuard Home n'a pas pu déterminer de résolveurs DNS inversés privés appropriés pour ce système.",
|
||||
"local_ptr_placeholder": "Saisissez une adresse de serveur par ligne",
|
||||
@@ -23,7 +23,7 @@
|
||||
"disabled_dhcp": "Serveur DHCP désactivé",
|
||||
"unavailable_dhcp": "Le DHCP n’est pas disponible",
|
||||
"unavailable_dhcp_desc": "AdGuard Home ne peut pas exécuter un serveur DHCP sur votre système d’exploitation",
|
||||
"dhcp_title": "Serveur DHCP (experimental !)",
|
||||
"dhcp_title": "Serveur DHCP (expérimental !)",
|
||||
"dhcp_description": "Si votre routeur ne fonctionne pas avec les réglages DHCP, vous pouvez utiliser le serveur DHCP par défaut d'AdGuard.",
|
||||
"dhcp_enable": "Activer le serveur DHCP",
|
||||
"dhcp_disable": "Désactiver le serveur DHCP",
|
||||
@@ -57,7 +57,7 @@
|
||||
"dhcp_hardware_address": "Adresse de la machine",
|
||||
"dhcp_ip_addresses": "Adresses IP",
|
||||
"ip": "IP",
|
||||
"dhcp_table_hostname": "Nom de machine",
|
||||
"dhcp_table_hostname": "Nom d'hôte",
|
||||
"dhcp_table_expires": "Expire le",
|
||||
"dhcp_warning": "Si vous souhaitez tout de même activer le serveur DHCP, assurez-vous qu'il n'y a pas d'autre serveur DHCP actif sur votre réseau. Sinon, cela peut perturber la connexion Internet sur tous les appareils connectés au réseau !",
|
||||
"dhcp_error": "AdGuard Home ne peut pas déterminer s'il y a un autre serveur DHCP actif sur le réseau.",
|
||||
@@ -68,6 +68,9 @@
|
||||
"dhcp_new_static_lease": "Nouveau bail statique",
|
||||
"dhcp_static_leases_not_found": "Aucun bail statique DHCP trouvé",
|
||||
"dhcp_add_static_lease": "Ajoutez un bail statique",
|
||||
"dhcp_reset_leases": "Réinitialiser tous les baux",
|
||||
"dhcp_reset_leases_confirm": "Voulez-vous vraiment réinitialiser tous les baux DHCP?",
|
||||
"dhcp_reset_leases_success": "Les baux DHCP ont été réinitialisés avec succès",
|
||||
"dhcp_reset": "Voulez-vous vraiment réinitialiser votre configuration DHCP ?",
|
||||
"country": "Pays",
|
||||
"city": "Ville",
|
||||
@@ -91,7 +94,7 @@
|
||||
"address": "Addresse",
|
||||
"protocol": "Protocole",
|
||||
"on": "Activé",
|
||||
"off": "Éteint",
|
||||
"off": "Désactivé",
|
||||
"copyright": "Copyright",
|
||||
"homepage": "Page d'accueil",
|
||||
"report_an_issue": "Signaler un problème",
|
||||
@@ -100,7 +103,7 @@
|
||||
"enabled_protection": "Protection activée",
|
||||
"disable_protection": "Désactiver la protection",
|
||||
"disabled_protection": "Protection désactivée",
|
||||
"refresh_statics": "Renouveler les statistiques",
|
||||
"refresh_statics": "Actualiser les statistiques",
|
||||
"dns_query": "Requêtes DNS",
|
||||
"blocked_by": "<0>Bloqué par Filtres</0>",
|
||||
"stats_malware_phishing": "Tentative de malware/hameçonnage bloquée",
|
||||
@@ -109,6 +112,8 @@
|
||||
"for_last_24_hours": "pendant les dernières 24 heures",
|
||||
"for_last_days": "pour les {{count}} derniers jours",
|
||||
"for_last_days_plural": "pour les {{count}} derniers jours",
|
||||
"stats_disabled": "Les statistiques ont été désactivées. Vous pouvez l'activer à partir de la <0>page des paramètres</0>.",
|
||||
"stats_disabled_short": "Les statistiques ont été désactivées",
|
||||
"no_domains_found": "Pas de domaines trouvés",
|
||||
"requests_count": "Nombre de requêtes",
|
||||
"top_blocked_domains": "Les domaines les plus fréquemment bloqués",
|
||||
@@ -321,10 +326,10 @@
|
||||
"install_devices_router_list_4": "Vous ne pouvez pas définir un serveur DNS personnalisé sur certains types de routeurs. Dans ce cas, la configuration de AdGuard Home en tant que <0>serveur DHCP</0> peut aider. Sinon, vous devez rechercher le manuel sur la façon de personnaliser les serveurs DNS pour votre modèle de routeur particulier.",
|
||||
"install_devices_windows_list_1": "Ouvrez votre Panneau de configuration depuis le menu Démarrer ou la recherche Windows.",
|
||||
"install_devices_windows_list_2": "Allez dans la catégorie Réseau et Internet et ensuite dans le Centre Réseau et Partage.",
|
||||
"install_devices_windows_list_3": "Sur la partie gauche de l'écran, recherchez Modifier les paramètres de la carte et cliquez dessus.",
|
||||
"install_devices_windows_list_3": "Sur la partie gauche de l'écran, recherchez « Modifier les paramètres de l'adaptateur » et cliquez dessus.",
|
||||
"install_devices_windows_list_4": "Sélectionnez votre connexion active, clic droit dessus et sélectionnez Propriétés.",
|
||||
"install_devices_windows_list_5": "Recherchez la version du protocole Internet 4 (TCP/IP) dans la liste, sélectionnez-la puis cliquez à nouveau sur Propriétés.",
|
||||
"install_devices_windows_list_6": "Sélectionnez Utiliser l’adresse de serveur DNS suivante et saisissez votre adresse de seveur AdGuard Home.",
|
||||
"install_devices_windows_list_5": "Recherchez « Protocole Internet Version 4 (TCP/IPv4) » (soit, pour IPv6, « Protocole Internet Version 6 (TCP/IPv6) ») dans la liste, sélectionnez-la puis cliquez à nouveau sur Propriétés.",
|
||||
"install_devices_windows_list_6": "Sélectionnez « Utiliser l’adresse de serveur DNS suivante » et saisissez votre adresse de serveur AdGuard Home.",
|
||||
"install_devices_macos_list_1": "Cliquez sur l'icône Apple et allez dans les Préférences Système.",
|
||||
"install_devices_macos_list_2": "Cliquez sur Réseau.",
|
||||
"install_devices_macos_list_3": "Sélectionnez la première connexion dans votre liste et cliquez sur Avancés.",
|
||||
@@ -423,11 +428,11 @@
|
||||
"access_title": "Paramètres d'accès",
|
||||
"access_desc": "Ici vous pouvez configurer les règles d'accès au serveur DNS AdGuard Home.",
|
||||
"access_allowed_title": "Clients autorisés",
|
||||
"access_allowed_desc": "Une liste d'adresses IP ou CIDR. Si configuré, AdGuard Home acceptera uniquement les requêtes provenant de ces adresses IP.",
|
||||
"access_allowed_desc": "Une liste de CIDR, d'adresses IP ou d'ID client. S'il est configuré, AdGuard Home acceptera uniquement les demandes de ces clients.",
|
||||
"access_disallowed_title": "Clients non autorisés",
|
||||
"access_disallowed_desc": "Une liste d'adresses IP ou CIDR. Si configuré, AdGuard Home bloquera les requêtes provenant de ces adresses IP.",
|
||||
"access_disallowed_desc": "Une liste d'adresses IP ou CIDR. Si configuré, AdGuard Home bloquera les requêtes provenant de ces adresses IP. Si des clients sont configurés, ce champ sera ignoré.",
|
||||
"access_blocked_title": "Domaines interdits",
|
||||
"access_blocked_desc": "A ne pas confondre avec les filtres. AdGuard Home rejette les requêtes DNS correspondant à ces domaines, et ces requêtes n'apparaissent même pas dans le journal des requêtes. Vous pouvez spécifier des noms de domaine exacts, des caractères génériques ou des règles de filtrage d'URL, par exemple \"exemple.org\", \"*.exemple.org\" ou \"||example.org^\" de manière correspondante.",
|
||||
"access_blocked_desc": "A ne pas confondre avec les filtres. AdGuard Home rejette les requêtes DNS correspondant à ces domaines, et ces requêtes n'apparaissent même pas dans le journal des requêtes. Vous pouvez spécifier des noms de domaine exacts, des caractères génériques ou des règles de filtrage d'URL, par exemple « exemple.org », « *.exemple.org » ou « ||example.org^ » de manière correspondante.",
|
||||
"access_settings_saved": "Paramètres d'accès enregistrés avec succès",
|
||||
"updates_checked": "Mises à jour vérifiées",
|
||||
"updates_version_equal": "AdGuard Home est à jour",
|
||||
@@ -481,10 +486,12 @@
|
||||
"encryption_key_source_content": "Coller les contenus de la clef privée",
|
||||
"stats_params": "Configuration des statistiques",
|
||||
"config_successfully_saved": "Configuration sauvegardée",
|
||||
"interval_6_hour": "6 heures",
|
||||
"interval_24_hour": "24 heures",
|
||||
"interval_days": "{{count}} jour",
|
||||
"interval_days_plural": "{{count}} jours",
|
||||
"domain": "Domaine",
|
||||
"punycode": "Punycode",
|
||||
"answer": "Réponse",
|
||||
"filter_added_successfully": "Le filtre a été ajouté avec succès",
|
||||
"filter_removed_successfully": "La liste a été supprimée avec succès",
|
||||
@@ -590,6 +597,8 @@
|
||||
"cache_ttl_min_override_desc": "Prolonger les valeurs courtes de durée de vie (en secondes) reçues du serveur en amont lors de la mise en cache des réponses DNS",
|
||||
"cache_ttl_max_override_desc": "Établir la valeur de durée de vie TTL maximale (en secondes) pour les saisies dans le cache du DNS",
|
||||
"ttl_cache_validation": "La valeur TTL minimale du cache doit être inférieure ou égale à la valeur maximale",
|
||||
"cache_optimistic": "Optimiste",
|
||||
"cache_optimistic_desc": "Faites en sorte qu'AdGuard Home réponde à partir du cache même lorsque les entrées ont expiré et essayez également de les actualiser.",
|
||||
"filter_category_general": "Général",
|
||||
"filter_category_security": "Sécurité",
|
||||
"filter_category_regional": "Régional",
|
||||
|
||||
@@ -5,9 +5,18 @@
|
||||
"upstream_parallel": "Koristi paralelne upite kako bi ubrzali rješavanje istovremenim ispitavanjem svih upstream poslužitelja.",
|
||||
"parallel_requests": "Paralelni zahtjevi",
|
||||
"load_balancing": "Load-balancing",
|
||||
"load_balancing_desc": "Šaljite upite po jednom poslužitelju u isto vrijeme. AdGuard Home će koristiti ponderirani slučajni algoritam za odabir poslužitelja, tako da će se najbrži poslužitelj češće koristiti.",
|
||||
"load_balancing_desc": "Pitajte jedan po jedan uzvodni poslužitelj. AdGuard Home koristi svoj ponderirani slučajni algoritam za odabir poslužitelja tako da se najbrži poslužitelj koristi češće.",
|
||||
"bootstrap_dns": "Bootstrap DNS poslužitelji",
|
||||
"bootstrap_dns_desc": "Bootstrap DNS poslužitelji koriste se za rezolvanje IP adresa DoH/DoT rezolvera koje navedete kao upstreams.",
|
||||
"local_ptr_title": "Privatni obrnuti DNS poslužitelji",
|
||||
"local_ptr_desc": "DNS poslužitelji koje AdGuard Home koristi za lokalne PTR upite. Ti se poslužitelji koriste za razrješavanje naziva glavnog računala klijenata s privatnim IP adresama, na primjer \"192.168.12.34\", koristeći obrnuti DNS. Ako nije postavljeno, AdGuard Home koristi adrese zadanih DNS razrješivača vašeg OS-a, osim za adrese samog AdGuard Homea.",
|
||||
"local_ptr_default_resolver": "Prema zadanim postavkama AdGuard Home koristi sljedeće obrnute DNS razrješivače: {{ip}}.",
|
||||
"local_ptr_no_default_resolver": "AdGuard Home nije mogao odrediti prikladne privatne obrnute DNS razrješivače za ovaj sustav.",
|
||||
"local_ptr_placeholder": "Unesite jednu adresu poslužitelja po retku",
|
||||
"resolve_clients_title": "Omogući obrnuto rješavanje IP adresa klijenata",
|
||||
"resolve_clients_desc": "Obrnuto razriješite IP adrese klijenata u nazive glavnih računala slanjem PTR upita odgovarajućim razrješivačima (privatni DNS poslužitelji za lokalne klijente, uzvodni poslužitelji za klijente s javnim IP adresama).",
|
||||
"use_private_ptr_resolvers_title": "Koristi privatne reverzne DNS razrješivače",
|
||||
"use_private_ptr_resolvers_desc": "Izvršite obrnuta DNS traženja za lokalno poslužene adrese pomoću ovih uzlaznih poslužitelja. Ako je onemogućen, AdGuard Home odgovara S NXDOMAIN-om na sve takve PTR zahtjeve osim za klijente poznate iz DHCP-a, /etc/hosts i tako dalje.",
|
||||
"check_dhcp_servers": "Provjera DHCP poslužitelja",
|
||||
"save_config": "Spremi konfiguraciju",
|
||||
"enabled_dhcp": "DHCP poslužitelj je omogućen",
|
||||
@@ -33,6 +42,7 @@
|
||||
"form_error_mac_format": "Nevažeći MAC format",
|
||||
"form_error_client_id_format": "Nevažeći format ID-a klijenta",
|
||||
"form_error_server_name": "Nevažeće ime poslužitelja",
|
||||
"form_error_subnet": "Podmrežu \"{{cidr}}\" ne sadrži IP adresu \"{{ip}}\"",
|
||||
"form_error_positive": "Mora biti veće od 0",
|
||||
"form_error_negative": "Mora biti jednako ili veće od 0",
|
||||
"range_end_error": "Mora biti veće od početne vrijednosti raspona",
|
||||
@@ -58,6 +68,9 @@
|
||||
"dhcp_new_static_lease": "Novi static lease",
|
||||
"dhcp_static_leases_not_found": "Nisu pronađeni statični DHCP leases",
|
||||
"dhcp_add_static_lease": "Dodaj static lease",
|
||||
"dhcp_reset_leases": "Ponovno postavljanje svih najmova",
|
||||
"dhcp_reset_leases_confirm": "Jeste li sigurni da želite resetirati sve najmove?",
|
||||
"dhcp_reset_leases_success": "DHCP najmovi uspješno se resetiraju",
|
||||
"dhcp_reset": "Jeste li sigurni da želite poništiti DHCP postavke?",
|
||||
"country": "Država",
|
||||
"city": "Grad",
|
||||
@@ -99,6 +112,8 @@
|
||||
"for_last_24_hours": "u zadnja 24 sata",
|
||||
"for_last_days": "zadnjih {{count}} dana",
|
||||
"for_last_days_plural": "zadnjih {{count}} dana",
|
||||
"stats_disabled": "Statistika je onemogućena. Možete ga uključiti sa <0>stranice s postavkama</0>.",
|
||||
"stats_disabled_short": "Statistika je onemogućena",
|
||||
"no_domains_found": "Nije pronađena domena",
|
||||
"requests_count": "Broj zahtjeva",
|
||||
"top_blocked_domains": "Top blokirane domene",
|
||||
@@ -118,11 +133,11 @@
|
||||
"block_domain_use_filters_and_hosts": "Blokiraj domene koristeći filtre ili hosts datoteke",
|
||||
"filters_block_toggle_hint": "Pravila blokiranja možete postaviti u postavkama <a>filtara</a>.",
|
||||
"use_adguard_browsing_sec": "Koristi AdGuard uslugu zaštite pregledavanja",
|
||||
"use_adguard_browsing_sec_hint": "AdGuard Home će provjeriti nalazi li se domena na popisu nedopuštenih domena od usluge zaštite pregledavanja. Za provjeru će se koristiti API za provjeru koji poštuje vašu privatnost. Samo mali dio SHA256 hash-a od naziva domene se šalje poslužitelju.",
|
||||
"use_adguard_browsing_sec_hint": "AdGuard Home provjerit će je li domena blokirana sigurnosnim web servisom za pregledavanje. Za izvođenje provjere koristit će API za pretraživanje prilagođen privatnosti: poslužitelju se šalje samo kratki prefiks naziva domene SHA256 hash.",
|
||||
"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, Yandex i Pixabay.",
|
||||
"enforce_safe_search": "Koristi sigurno pretraživanje",
|
||||
"enforce_save_search_hint": "AdGuard Home provodit će sigurno pretraživanje u sljedećim tražilicama: Google, YouTube, Bing, DuckDuckGo, Yandex, Pixabay.",
|
||||
"no_servers_specified": "Nije odabran nijedan poslužitelj",
|
||||
"general_settings": "Opće postavke",
|
||||
"dns_settings": "DNS postavke",
|
||||
@@ -261,8 +276,8 @@
|
||||
"plain_dns": "Obični DNS",
|
||||
"form_enter_rate_limit": "Unesite ograničenje",
|
||||
"rate_limit": "Ograničenje",
|
||||
"edns_enable": "Omogući EDNS Client Subnet",
|
||||
"edns_cs_desc": "Ako je omogućeno, AdGuard Home će slati podmreže klijenata na DNS poslužitelje.",
|
||||
"edns_enable": "Omogući podmrežu klijenta EDNS-a",
|
||||
"edns_cs_desc": "Pošaljite podmreže klijenata DNS poslužiteljima.",
|
||||
"rate_limit_desc": "Broj zahtjeva u sekundi koji su dopušteni po jednom klijentu. Postavljanje na 0 znači neograničeno.",
|
||||
"blocking_ipv4_desc": "Povratna IP adresa za blokirane A zahtjeve",
|
||||
"blocking_ipv6_desc": "Povratna IP adresa za blokirane AAAA zahtjeve",
|
||||
@@ -301,9 +316,9 @@
|
||||
"install_devices_title": "Postavite vaše uređaje",
|
||||
"install_devices_desc": "Da biste započeli koristiti AdGuard Home, morate postaviti uređaje da ga koriste.",
|
||||
"install_submit_title": "Čestitamo!",
|
||||
"install_submit_desc": "Postavljanje je dovršeno i spremni ste koristiti AdGuard Home.",
|
||||
"install_submit_desc": "Postupak postavljanja je dovršen i sada ste spremni početi koristiti AdGuard Home.",
|
||||
"install_devices_router": "Usmjerivač (Router)",
|
||||
"install_devices_router_desc": "Ovo postavljanje će automatski pokriti sve uređaje povezane na vaš kućni router i nećete trebati ručno postavljati svaki od njih.",
|
||||
"install_devices_router_desc": "Ova postavka automatski pokriva sve uređaje povezane s kućnim usmjerivačem, nema potrebe za ručnom konfiguracijom svakog od njih.",
|
||||
"install_devices_address": "AdGuard Home DNS poslužitelj osluškuje sljedeće adrese",
|
||||
"install_devices_router_list_1": "Otvorite postavke za router. Obično mu možete pristupiti iz preglednika putem URL-a, kao što je http://192.168.0.1/ ili http://192.168.1.1/. Od vas će se možda tražiti da unesete lozinku. Ako je se ne sjećate, lozinku možete često poništiti pritiskom na dumge na samom routeru. Neki routeri trebaju određenu aplikaciju, koja bi u tom slučaju trebala biti već instalirana na vašem računalu/telefonu.",
|
||||
"install_devices_router_list_2": "Pronađite DHCP/DNS postavke. Potražite DNS slova pored polja koje dopušta dva ili tri skupa brojeva, svaki razdvojen u četiri skupine od jedne do tri znamenke.",
|
||||
@@ -311,10 +326,10 @@
|
||||
"install_devices_router_list_4": "Na nekim se vrstama usmjerivača ne može postaviti prilagođeni DNS poslužitelj. U ovom slučaju, može vam pomoći ako postavite AdGuard Home kao <0>DHCP poslužitelj</0>. U suprotnom, trebali biste potražiti priručnik o tome kako prilagoditi DNS poslužitelje za vaš određeni model routera.",
|
||||
"install_devices_windows_list_1": "Otvorite Upravljačku ploču putem Start izbornika ili Windows pretrage.",
|
||||
"install_devices_windows_list_2": "Idite na kategoriju Mreža i Internet i odaberite Centar za mreže i zajedničko korištenje.",
|
||||
"install_devices_windows_list_3": "Na lijevoj strani zaslona pronađite Promjeni postavke adaptera i pritisnite na to.",
|
||||
"install_devices_windows_list_3": "Na lijevoj strani zaslona pronađite \"Promjena postavki prilagodnika\" i kliknite na nju.",
|
||||
"install_devices_windows_list_4": "Odaberite aktivnu vezu, pritisnite desni klik na nju i odaberite Svojstva.",
|
||||
"install_devices_windows_list_5": "Pronađite Internet Protocol Version 4 (TCP/IP) na listi, odaberite ga i zatim pritisnite opet Postavke.",
|
||||
"install_devices_windows_list_6": "Odaberite Koristi sljedeće DNS adrese poslužitelja i unesite vaše adrese od AdGuard Home poslužitelja.",
|
||||
"install_devices_windows_list_5": "Na popisu pronađite \"Internet Protocol Version 4 (TCP/IPv4)\" (ili, za IPv6, \"Internet Protocol Version 6 (TCP/IPv6)\"), odaberite ga i zatim ponovno kliknite svojstva.",
|
||||
"install_devices_windows_list_6": "Odaberite \"Koristi sljedeće adrese DNS poslužitelja\" i unesite adrese AdGuard Home poslužitelja.",
|
||||
"install_devices_macos_list_1": "Pritisnite na Apple ikonu i idite u Postavke sustava.",
|
||||
"install_devices_macos_list_2": "Pritisnite na Mreža.",
|
||||
"install_devices_macos_list_3": "Odaberite prvu vezu s vašeg popisa i pritisnite Napredno.",
|
||||
@@ -323,7 +338,7 @@
|
||||
"install_devices_android_list_2": "Pritisnite Wi-Fi u izborniku. Prikazat će se zaslon s popisom svih dostupnih mreža (nemoguće je postaviti prilagođeni DNS za mobilnu vezu).",
|
||||
"install_devices_android_list_3": "Dugo pritisnite na mrežu na koju ste povezani i odaberite Uredi mrežu.",
|
||||
"install_devices_android_list_4": "Na nekim će uređajima možda trebati označiti Napredno za prikaz dodatnih postavki. Da biste prilagodili postavke Android DNS-a, morati će te prebaciti IP postavke s DHCP-a na Statičke.",
|
||||
"install_devices_android_list_5": "Promijenite DNS 1 i DNS 2 vrijednosti u one adrese AdGuard Home poslužitelja.",
|
||||
"install_devices_android_list_5": "Promijenite vrijednosti DNS-a 1 i DNS-a 2 u adrese AdGuard Home poslužitelja.",
|
||||
"install_devices_ios_list_1": "Na početnom zaslonu odaberite Postavke.",
|
||||
"install_devices_ios_list_2": "Odaberite Wi-Fi u lijevom izborniku (ne moguće je postaviti DNS za mobilne mreže).",
|
||||
"install_devices_ios_list_3": "Pritisnite na naziv vaše trenutne mreže.",
|
||||
@@ -393,8 +408,9 @@
|
||||
"client_edit": "Uredi klijenta",
|
||||
"client_identifier": "Identifikator",
|
||||
"ip_address": "IP adresa",
|
||||
"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.",
|
||||
"client_identifier_desc": "Klijenti se mogu identificirati putem IP adrese, CIDR-a, MAC adrese ili posebnog ID-a 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_subnet_ip": "Unesite IP adresu u podmrežu \"{{cidr}}\"",
|
||||
"form_enter_mac": "Unesite MAC adresu",
|
||||
"form_enter_id": "Unesi identifikator",
|
||||
"form_add_id": "Dodaj identifikator",
|
||||
@@ -412,11 +428,11 @@
|
||||
"access_title": "Postavke pristupa",
|
||||
"access_desc": "Postavite pravila pristupa za AdGuard Home DNS poslužitelj.",
|
||||
"access_allowed_title": "Dopušteni klijenti",
|
||||
"access_allowed_desc": "Popis CIDR-a ili IP adresa. Ukoliko je postavljeno, AdGuard Home će prihvatiti samo zahtjeve s ovih IP adresa.",
|
||||
"access_allowed_desc": "Popis CIDR-ova, IP adresa ili ID-ova klijenata. Ako je konfiguriran, AdGuard Home prihvatit će zahtjeve samo tih klijenata.",
|
||||
"access_disallowed_title": "Nedopušteni klijenti",
|
||||
"access_disallowed_desc": "Popis CIDR-a ili IP adresa. Ukoliko je postavljeno, AdGuard Home će zaustaviti zahtjeve s ovih IP adresa.",
|
||||
"access_disallowed_desc": "Popis CIDR-ova, IP adresa ili ID-ova klijenata. Ako je konfiguriran, AdGuard Home će odbaciti zahtjeve tih klijenata. Ako su konfigurirani dopušteni klijenti, ovo se polje zanemaruje.",
|
||||
"access_blocked_title": "Nedopuštene domene",
|
||||
"access_blocked_desc": "Ne miješajte ovo s filtrima. AdGuard Home će zaustaviti DNS upite s tim ovim domenama u podnesenim upitima. Ovdje možete definirati točne nazive domena, zamjenske znakove ili pravila URL filtriranja, npr. 'example.org', '*.example.org' or '||example.org^'.",
|
||||
"access_blocked_desc": "Ne smije se miješati s filterima. AdGuard Home ispušta DNS upite koji odgovaraju tim domenama, a ti se upiti čak i ne pojavljuju u zapisniku upita. Možete navesti točne nazive domena, zamjenske znakove ili pravila filtriranja URL-a, npr || example.org example.org. example.org^\" u skladu s tim.",
|
||||
"access_settings_saved": "Postavke pristupa su uspješno spremljene",
|
||||
"updates_checked": "Uspješna provjera ažuriranja",
|
||||
"updates_version_equal": "AdGuard Home je ažuriran",
|
||||
@@ -470,6 +486,7 @@
|
||||
"encryption_key_source_content": "Zalijepi sadržaj privatnog ključa",
|
||||
"stats_params": "Postavke statistike",
|
||||
"config_successfully_saved": "Postavke su uspješno spremljene",
|
||||
"interval_6_hour": "6 sati",
|
||||
"interval_24_hour": "24 sata",
|
||||
"interval_days": "{{count}} dan",
|
||||
"interval_days_plural": "{{count}} dana",
|
||||
@@ -516,8 +533,8 @@
|
||||
"rewrite_domain_name": "Naziv domene: Dodajte CNAME zapis",
|
||||
"rewrite_A": "<0>A</0>: posebna vrijednost, ukloni <0>A</0> zapis od upstreama",
|
||||
"rewrite_AAAA": "<0>AAAA</0>: posebna vrijednost, ukloni <0>AAAA</0> zapis od upstreama",
|
||||
"disable_ipv6": "Onemogući IPv6",
|
||||
"disable_ipv6_desc": "Ukoliko je ova značajka omogućena, svi DNS upiti za IPv6 adrese (AAAA tip) će biti odbačeni.",
|
||||
"disable_ipv6": "Onemogući razrješavanje IPv6 adresa",
|
||||
"disable_ipv6_desc": "Ispustite sve DNS upite za IPv6 adrese (upišite AAAA).",
|
||||
"fastest_addr": "Najbrža IP adresa",
|
||||
"fastest_addr_desc": "Ispitajte sve DNS poslužitelje i vratite najbržu IP adresu među svim odgovorima. To usporava DNS upite jer AdGuard Home mora čekati odgovore svih DNS poslužitelja, ali poboljšava ukupnu povezanost.",
|
||||
"autofix_warning_text": "Ako pritisnete \"Popravi\", AdGuard Home će postaviti vaš sustav da koristi AdGuardHome DNS poslužitelj.",
|
||||
@@ -554,7 +571,7 @@
|
||||
"list_updated": "{{count}} popis ažuriran",
|
||||
"list_updated_plural": "{{count}} popisa ažurirana",
|
||||
"dnssec_enable": "Omogući DNSSEC",
|
||||
"dnssec_enable_desc": "Omogućite DNSSEC u izlaznim DNS upitima i provjerite rezultat (potreban je resolver s omogućenim DNSSEC-om)",
|
||||
"dnssec_enable_desc": "Postavite ZASTAVICU DNSSEC-a u ulaznim DNS upitima i provjerite rezultat (potreban je DNSSEC-omogućen razrješivač).",
|
||||
"validated_with_dnssec": "Potvrđeno s DNSSEC-om",
|
||||
"all_queries": "Svi upiti",
|
||||
"show_blocked_responses": "Blokirano",
|
||||
@@ -579,12 +596,14 @@
|
||||
"cache_ttl_min_override_desc": "Povećajte kratke vrijednosti TTL-a (u sekundama) primljene od upstream poslužitelja prilikom predmemoriranja DNS odgovora",
|
||||
"cache_ttl_max_override_desc": "Postavite maksimalnu vrijednost TTL-a (u sekundama) za zapise u DNS predmemoriju",
|
||||
"ttl_cache_validation": "Minimalna vrijednost TTL predmemorije mora biti manja ili jednaka maksimalnoj vrijednosti",
|
||||
"cache_optimistic": "optimistički",
|
||||
"cache_optimistic_desc": "Učinite da AdGuard Home reagira iz predmemorije čak i kada su unosi istekli i pokušajte ih osvježiti.",
|
||||
"filter_category_general": "Općenito",
|
||||
"filter_category_security": "Sigurnost",
|
||||
"filter_category_regional": "Regionalno",
|
||||
"filter_category_other": "Ostalo",
|
||||
"filter_category_general_desc": "Popisi koji blokiraju pratitelje i oglase na većini uređaja",
|
||||
"filter_category_security_desc": "Popisi koju su specijalizirani za blokiranje zlonamjernih programa, krađe identiteta ili domena za obmanu",
|
||||
"filter_category_security_desc": "Popisi posebno dizajnirani za blokiranje zlonamjernih domena, domena za krađu identiteta i prijevare",
|
||||
"filter_category_regional_desc": "Popisi koji se fokusiraju na regionalne oglase i poslužitelje za praćenje",
|
||||
"filter_category_other_desc": "Ostali popisi nedopuštenih",
|
||||
"setup_config_to_enable_dhcp_server": "Postavite konfiguraciju za omogućavanje DHCP poslužitelja",
|
||||
|
||||
@@ -68,6 +68,9 @@
|
||||
"dhcp_new_static_lease": "Nuovo lease statico",
|
||||
"dhcp_static_leases_not_found": "Non è stato trovato nessun leases statico DHCP",
|
||||
"dhcp_add_static_lease": "Aggiungi lease statico",
|
||||
"dhcp_reset_leases": "Reimposta tutti i temporanei",
|
||||
"dhcp_reset_leases_confirm": "Sei sicuro di voler ripristinare tutti i temporanei?",
|
||||
"dhcp_reset_leases_success": "DHCP temporanei reimpostati correttamente",
|
||||
"dhcp_reset": "Sei sicuro di voler ripristinare la configurazione DHCP?",
|
||||
"country": "Regione",
|
||||
"city": "Città",
|
||||
@@ -109,6 +112,8 @@
|
||||
"for_last_24_hours": "nelle ultime 24 ore",
|
||||
"for_last_days": "per gli ultimi {{count}} giorni",
|
||||
"for_last_days_plural": "per gli ultimi {{count}} giorni",
|
||||
"stats_disabled": "Le statistiche sono state disattivate. Puoi attivarle dalla <0>pagina delle impostazioni</0>.",
|
||||
"stats_disabled_short": "Le statistiche sono state disattivate",
|
||||
"no_domains_found": "Nessun dominio trovato",
|
||||
"requests_count": "Numero richieste",
|
||||
"top_blocked_domains": "Domini maggiormente bloccati",
|
||||
@@ -321,10 +326,10 @@
|
||||
"install_devices_router_list_4": "Su alcuni tipi di router, non è possibile configurare un server DNS personalizzato. In tal caso, configurare AdGuard Home come un <0>server DHCP</0> potrebbe aiutare. In alternativa, dovresti leggere il manuale di istruzioni per capire come personalizzare i server DNS sul tuo specifico modello di router.",
|
||||
"install_devices_windows_list_1": "Apri il Pannello di controllo tramite il menu Start o la ricerca di Windows.",
|
||||
"install_devices_windows_list_2": "Vai a Rete e categoria Internet e poi a Centro connessioni di rete e condivisione.",
|
||||
"install_devices_windows_list_3": "Sul lato sinistro dello schermo, trova le impostazioni della scheda Cambia e fai clic su di esso.",
|
||||
"install_devices_windows_list_3": "Sul lato sinistro dello schermo, trova \"Cambia impostazioni adattatore\" e clicca su di esso.",
|
||||
"install_devices_windows_list_4": "Seleziona la tua connessione attiva, fai clic destro su di essa e scegli Proprietà.",
|
||||
"install_devices_windows_list_5": "Trova Protocollo Internet versione 4 (TCP / IP) nell'elenco, selezionalo e quindi fai nuovamente clic su Proprietà.",
|
||||
"install_devices_windows_list_6": "Scegli \"Utilizza i seguenti indirizzi del server DNS\" ed inserisci gli indirizzi del tuo server AdGuard Home.",
|
||||
"install_devices_windows_list_5": "Trova \"Protocollo Internet versione 4 (TCP/IPv4)\" (o, per IPv6, \"Protocollo Internet versione 6 (TCP/IPv6)\" nella lista, selezionalo e quindi clicca nuovamente su Proprietà.",
|
||||
"install_devices_windows_list_6": "Scegli \"Utilizza i seguenti indirizzi server DNS\" ed inserisci i tuoi indirizzi server AdGuard Home.",
|
||||
"install_devices_macos_list_1": "Fai clic sull'icona Apple e vai su Preferenze di Sistema.",
|
||||
"install_devices_macos_list_2": "Clicca sulla rete.",
|
||||
"install_devices_macos_list_3": "Seleziona la prima connessione nel tuo elenco e fai clic su Avanzate.",
|
||||
@@ -423,9 +428,9 @@
|
||||
"access_title": "Impostazioni di accesso",
|
||||
"access_desc": "Qui puoi configurare le regole d'accesso per il server DNS di AdGuard Home.",
|
||||
"access_allowed_title": "Client permessi",
|
||||
"access_allowed_desc": "Una lista in CIDR o indirizzi IP. Se configurata AdGuard Home accetterà richieste solo da questi indirizzi ip.",
|
||||
"access_allowed_desc": "Una lista di CIDR, indirizzi IP o ID client. Se configurata AdGuard Home accetterà richieste solo da questi client.",
|
||||
"access_disallowed_title": "Client non permessi",
|
||||
"access_disallowed_desc": "Una lista in CIDR o indirizzi IP. Se configurata AdGuard Home non accetterà richieste da questi indirizzi ip.",
|
||||
"access_disallowed_desc": "Una lista di CIDR, indirizzi IP o ID client. Se configurata, AdGuard Home rifiuterà richieste da questi client. Se i client consentiti risulteranno configurati, questo campo verrà ignorato.",
|
||||
"access_blocked_title": "Domini bloccati",
|
||||
"access_blocked_desc": "Da non confondere con i filtri. AdGuard Home eliminerà le richieste DNS corrispondenti a questi domini e queste richieste non verranno visualizzate nel relativo registro. Puoi specificare nomi di dominio esatti, caratteri jolly o regole di filtraggio URL, ad esempio \"esempio.org\", \"*.esempio.org\" o \"||esempio.org^\".",
|
||||
"access_settings_saved": "Impostazioni di accesso salvate correttamente",
|
||||
@@ -481,10 +486,12 @@
|
||||
"encryption_key_source_content": "Incolla i contenuti della chiave privata",
|
||||
"stats_params": "Configurazione delle statistiche",
|
||||
"config_successfully_saved": "Configurazione salvata correttamente",
|
||||
"interval_6_hour": "6 ore",
|
||||
"interval_24_hour": "24 ore",
|
||||
"interval_days": "{{count}} giorni",
|
||||
"interval_days_plural": "{{count}} giorni",
|
||||
"domain": "Dominio",
|
||||
"punycode": "Punycode",
|
||||
"answer": "Risposta",
|
||||
"filter_added_successfully": "Il filtro è stato aggiunto correttamente",
|
||||
"filter_removed_successfully": "La lista è stata correttamente rimossa",
|
||||
@@ -590,6 +597,8 @@
|
||||
"cache_ttl_min_override_desc": "Estende i valori brevi (in secondi) ricevuti dal server upstream durante la memorizzazione nella cache delle risposte DNS",
|
||||
"cache_ttl_max_override_desc": "Imposta un periodo massimo di attivazione (in secondi) per le voci nella cache DNS",
|
||||
"ttl_cache_validation": "Il valore minimo della cache TTL deve essere inferiore o uguale al valore massimo",
|
||||
"cache_optimistic": "Ottimistico",
|
||||
"cache_optimistic_desc": "Fai in modo che AdGuard Home risponda dalla cache anche quando le voci risultano scadute e prova anche ad aggiornarle.",
|
||||
"filter_category_general": "Generali",
|
||||
"filter_category_security": "Sicurezza",
|
||||
"filter_category_regional": "Regionale",
|
||||
|
||||
@@ -5,16 +5,18 @@
|
||||
"upstream_parallel": "並列リクエストを使用する(同時にすべてのアップストリームサーバーに処理要求することで解決スピードが向上)",
|
||||
"parallel_requests": "並列リクエスト",
|
||||
"load_balancing": "ロードバランシング",
|
||||
"load_balancing_desc": "一度に1つのサーバに処理要求します。 AdGuard Homeは、重み付きランダムアルゴリズム(weighted random algorithm)を使用してサーバを選択するため、最速のサーバがより頻繁に使用されます。",
|
||||
"load_balancing_desc": "一度に1つのアップストリームサーバに処理要求します。 AdGuard Homeは、重み付きランダムアルゴリズム(weighted random algorithm)を使用してサーバを選択するため、最速のサーバがより頻繁に使用されます。",
|
||||
"bootstrap_dns": "ブートストラップDNSサーバ",
|
||||
"bootstrap_dns_desc": "ブートストラップDNSサーバは、上流として指定したDoH/DoTリゾルバのIPアドレスを解決するために使用されます。",
|
||||
"local_ptr_title": "プライベートリバースDNSサーバー",
|
||||
"local_ptr_desc": "AdGuard HomeがローカルPTRクエリに使用するDNSサーバーです。これらのサーバーは、rDNSを使ってプライベートIPアドレス(例えば\"192.168.12.34\")を持つクライアントのホスト名を解決するために使用されます。設定されていない場合、AdGuard HomeはOSのデフォルトDNSリゾルバーを自動的に使用します。",
|
||||
"local_ptr_desc": "AdGuard HomeがローカルPTRクエリに使用するDNSサーバーです。これらのサーバーは、rDNSを使ってプライベートIPアドレス(例えば\"192.168.12.34\")を持つクライアントのホスト名を解決するために使用されます。設定されていない場合、AdGuard HomeはOSのデフォルトDNSリゾルバーのアドレス(AdGuard Home自体のアドレスを除く)を自動的に使用します。",
|
||||
"local_ptr_default_resolver": "デフォルトでは、AdGuard Homeは次のリバースDNSリゾルバを使用します: {{ip}}",
|
||||
"local_ptr_no_default_resolver": "AdGuard Homeは、このシステムに適したプライベートリバースDNSリゾルバを特定できませんでした。",
|
||||
"local_ptr_placeholder": "1行に1つのサーバを入力してください。",
|
||||
"resolve_clients_title": "クライアントのIPアドレスの逆解決を有効にする",
|
||||
"resolve_clients_desc": "有効にすると、AdGuard Homeは、対応するリゾルバー(ローカルクライアントの場合はプライベートDNSサーバ、パブリックIPを持つクライアントの場合は上流サーバ)にPTRクエリを送信することにより、クライアントのIPアドレスをホスト名に逆解決しようとします。",
|
||||
"use_private_ptr_resolvers_title": "プライベートrDNSリゾルバを使用",
|
||||
"use_private_ptr_resolvers_desc": "これらの上流サーバを使用して、ローカルで提供されるアドレスのリバースDNSルックアップを実行します。無効にすると、AdGuard Homeは、DHCP, /etc/hosts などから認識されるクライアントを除く、すべてのPTR要求にNXDOMAINで応答します。",
|
||||
"resolve_clients_desc": "対応するリゾルバー(ローカルクライアントの場合はプライベートDNSサーバ、パブリックIPを持つクライアントの場合は上流サーバ)にPTRクエリを送信することにより、クライアントのIPアドレスをホストネームに逆解決します。",
|
||||
"use_private_ptr_resolvers_title": "プライベートリバースDNSリゾルバを使用",
|
||||
"use_private_ptr_resolvers_desc": "これらの上流サーバを使用して、ローカルで提供されるアドレスのリバースDNSルックアップを実行します。無効にすると、AdGuard Homeは、DHCP, /etc/hosts などから認識されるクライアントを除き、すべてのこのようなPTR要求にNXDOMAINで応答します。",
|
||||
"check_dhcp_servers": "DHCPサーバをチェックする",
|
||||
"save_config": "構成を保存する",
|
||||
"enabled_dhcp": "DHCPサーバを有効にしました",
|
||||
@@ -66,6 +68,9 @@
|
||||
"dhcp_new_static_lease": "新規静的割り当て",
|
||||
"dhcp_static_leases_not_found": "DHCP静的割り当てはありません",
|
||||
"dhcp_add_static_lease": "静的割り当てを追加する",
|
||||
"dhcp_reset_leases": "すべてのリースをリセットする",
|
||||
"dhcp_reset_leases_confirm": "すべてのリース(割り当て)をリセットしてもよろしいですか?",
|
||||
"dhcp_reset_leases_success": "すべてのDHCPリース(割り当て)がリセット完了しました。",
|
||||
"dhcp_reset": "DHCP構成をリセットしてよろしいですか?",
|
||||
"country": "国",
|
||||
"city": "街",
|
||||
@@ -107,6 +112,8 @@
|
||||
"for_last_24_hours": "過去24時間以内",
|
||||
"for_last_days": "過去{{count}}日間以内",
|
||||
"for_last_days_plural": "過去{{count}}日間以内",
|
||||
"stats_disabled": "統計は無効化されています。<0>設定ページ</0>でオンにすることができます。",
|
||||
"stats_disabled_short": "統計は無効化されています",
|
||||
"no_domains_found": "ドメイン情報はありません",
|
||||
"requests_count": "リクエスト数",
|
||||
"top_blocked_domains": "最もブロックされたドメイン",
|
||||
@@ -126,11 +133,11 @@
|
||||
"block_domain_use_filters_and_hosts": "フィルタとhostsファイルを使用してドメインをブロックする",
|
||||
"filters_block_toggle_hint": "<a>フィルタ</a>の設定でブロックするルールを設定することができます。",
|
||||
"use_adguard_browsing_sec": "AdGuardブラウジングセキュリティ・ウェブサービスを使用する",
|
||||
"use_adguard_browsing_sec_hint": "AdGuard Homeは、ブラウジングセキュリティ・ウェブサービスによってドメインがブラックリストに登録されているかどうかを確認します。 これはプライバシーを考慮したAPIを使用してチェックを実行します。ドメイン名SHA256ハッシュの短いプレフィックスのみがサーバに送信されます。",
|
||||
"use_adguard_browsing_sec_hint": "AdGuard Homeは、ブラウジング・セキュリティ・ウェブサービスによってドメインがブロックされているかを確認します。 確認は、プライバシーに配慮したルックアップAPIを使用して行います(ドメイン名のSHA256ハッシュの短いプレフィックスのみがサーバーに送信されます)。",
|
||||
"use_adguard_parental": "AdGuardペアレンタルコントロール・ウェブサービスを使用する",
|
||||
"use_adguard_parental_hint": "AdGuard Homeは、ドメインにアダルトコンテンツが含まれているかどうかを確認します。 ブラウジングセキュリティ・ウェブサービスと同じプライバシーに優しいAPIを使用します。",
|
||||
"enforce_safe_search": "セーフサーチを強制する",
|
||||
"enforce_save_search_hint": "AdGuard Homeは、 Google, YouTube, Bing, DuckDuckGo, Yandex, Pixabay という検索エンジンでセーフサーチを強制適用できます。",
|
||||
"enforce_save_search_hint": "AdGuard Homeは、次の検索エンジンでセーフサーチを強制適用します: Google, YouTube, Bing, DuckDuckGo, Yandex, Pixabay",
|
||||
"no_servers_specified": "サーバが指定されていません",
|
||||
"general_settings": "一般設定",
|
||||
"dns_settings": "DNS設定",
|
||||
@@ -269,7 +276,7 @@
|
||||
"plain_dns": "通常のDNS",
|
||||
"form_enter_rate_limit": "頻度制限を入力してください",
|
||||
"rate_limit": "頻度制限",
|
||||
"edns_enable": "EDNSクライアント・サブネットを有効にする",
|
||||
"edns_enable": "EDNSクライアントサブネットを有効にする",
|
||||
"edns_cs_desc": "有効にすると、AdGuard HomeはクライアントのサブネットをDNSサーバへ送信します。",
|
||||
"rate_limit_desc": "一つのクライアントに対して許可される1秒あたりのリクエスト数(「0」に設定すると、制限なしになります)",
|
||||
"blocking_ipv4_desc": "ブロックされたAリクエストに対して応答されるIPアドレス",
|
||||
@@ -319,10 +326,10 @@
|
||||
"install_devices_router_list_4": "一部のルーターでは、カスタムDNSサーバーを設定できません。この場合、AdGuard Homeを<0>DHCPサーバ</0>として設定してみることがおすすめです。それ以外の場合は、特定のルータモデルにおいて、DNSサーバーをカスタマイズする方法に関するマニュアル等をご確認ください。",
|
||||
"install_devices_windows_list_1": "「スタート」メニューまたはWindowsの検索から「設定」を開きます。",
|
||||
"install_devices_windows_list_2": "「ネットワークとインターネット」カテゴリに移動し、さらに「ネットワークと共有センター」へ移動します。",
|
||||
"install_devices_windows_list_3": "画面の左側にある「アダプターの設定の変更」を見つけてクリックします。",
|
||||
"install_devices_windows_list_3": "画面の左側にある「アダプターの設定を変更」を見つけてクリックします。",
|
||||
"install_devices_windows_list_4": "動作中の接続を選択して右クリックし、「プロパティ」を選択します。",
|
||||
"install_devices_windows_list_5": "一覧から「インターネット プロトコル バージョン4(TCP/IPv4)」を見つけ、それを選択してから、もう一度プロパティをクリックします。",
|
||||
"install_devices_windows_list_6": "「次のDNSサーバのアドレスを使う」を選択して、AdGuard Homeサーバのアドレスを入力します。",
|
||||
"install_devices_windows_list_5": "一覧から「インターネット プロトコル バージョン4(TCP/IPv4)」(もしくはIPv6の場合「インターネット プロトコル バージョン6(TCP/IPv6)」)を見つけ、それを選択してから、もう一度「プロパティ」をクリックします。",
|
||||
"install_devices_windows_list_6": "「次のDNSサーバーアドレスを使う」を選択して、お使いのAdGuard Homeサーバーアドレスを入力します。",
|
||||
"install_devices_macos_list_1": "Apple アイコンをクリックして「システム環境設定」へ行きます。",
|
||||
"install_devices_macos_list_2": "「ネットワーク」をクリックします。",
|
||||
"install_devices_macos_list_3": "一覧の最初の接続を選択して「詳細...」をクリックします。",
|
||||
@@ -421,11 +428,11 @@
|
||||
"access_title": "アクセス設定",
|
||||
"access_desc": "ここで、AdGuard Home DNSサーバのアクセスルールを設定できます。",
|
||||
"access_allowed_title": "許可されたクライアント",
|
||||
"access_allowed_desc": "CIDRまたはIPアドレスのリスト。設定されると、AdGuard HomeはこれらのIPアドレスからのリクエストのみを許可します。",
|
||||
"access_allowed_desc": "CIDR、IPアドレス、またはクライアントIDのリスト。設定されている場合、AdGuard HomeはこれらのIPアドレスからのリクエストのみを受け入れます。",
|
||||
"access_disallowed_title": "拒否するクライアント",
|
||||
"access_disallowed_desc": "CIDRまたはIPアドレスのリスト。設定されると、AdGuard HomeはこれらのIPアドレスからのリクエストを破棄します。",
|
||||
"access_disallowed_desc": "CIDR、IPアドレス、またはクライアントIDのリスト。設定されている場合、AdGuard HomeはこれらのIPアドレスからのリクエストを破棄します。「許可されたクライアント」欄が設定されている場合、この欄は無視されます。",
|
||||
"access_blocked_title": "拒否するドメイン",
|
||||
"access_blocked_desc": "こちらをフィルタと混同しないでください。AdGuard Homeは、クエリクエスチョンにこれらのドメインを含むDNSクエリをドロップします。ここでは、「example.org」、「*.example.org」、「 ||example.org^ 」など、特定のドメイン名、ワイルドカード、URLフィルタルールを指定できます。",
|
||||
"access_blocked_desc": "こちらをフィルタと混同しないでください。AdGuard Homeは、ここで入力されたドメインに一致するDNSクエリをドロップし、そういったクエリはクエリログにも表示されません。ここでは、「example.org」、「*.example.org」、「 ||example.org^ 」など、特定のドメイン名、ワイルドカード、URLフィルタルールを入力できます。",
|
||||
"access_settings_saved": "アクセス設定の保存に成功しました",
|
||||
"updates_checked": "アップデートの確認に成功しました",
|
||||
"updates_version_equal": "AdGuard Homeは既に最新です",
|
||||
@@ -479,10 +486,12 @@
|
||||
"encryption_key_source_content": "秘密鍵の内容をペーストする",
|
||||
"stats_params": "統計設定",
|
||||
"config_successfully_saved": "設定の保存に成功しました",
|
||||
"interval_6_hour": "6時間",
|
||||
"interval_24_hour": "24時間",
|
||||
"interval_days": "{{count}}日",
|
||||
"interval_days_plural": "{{count}}日",
|
||||
"domain": "ドメイン",
|
||||
"punycode": "Punycode",
|
||||
"answer": "応答",
|
||||
"filter_added_successfully": "フィルタの追加に成功しました",
|
||||
"filter_removed_successfully": "リストの削除に成功しました。",
|
||||
@@ -525,8 +534,8 @@
|
||||
"rewrite_domain_name": "ドメイン名入力した場合:CNAME記録が追加されます。",
|
||||
"rewrite_A": "<0>A</0>:特別な値、アップストリームからの<0>A</0>記録を保持します。",
|
||||
"rewrite_AAAA": "<0>AAAA</0>:特別な値、アップストリームからの<0>AAAA</0>記録を保持します。",
|
||||
"disable_ipv6": "IPv6を無効にする",
|
||||
"disable_ipv6_desc": "チェックすると、IPv6アドレス(タイプAAAA)のすべてのDNSクエリは破棄されます。",
|
||||
"disable_ipv6": "IPv6アドレスの解決を無効にする",
|
||||
"disable_ipv6_desc": "IPv6アドレス(タイプAAAA)に対するすべてのDNSクエリをドロップします。",
|
||||
"fastest_addr": "最速のIPアドレス",
|
||||
"fastest_addr_desc": "すべてのDNSサーバーに処理要求し、全応答の中で最速のIPアドレスを返します。これにより、AdGuard HomeがすべてのDNSサーバーからの応答を待つ必要があるため、DNSクエリが遅くなりますが、全体的な接続性は向上します。",
|
||||
"autofix_warning_text": "\"改善\"をクリックすると、AdGuardHomeはAdGuardHome DNSサーバを使用するようにシステムを構成します。",
|
||||
@@ -563,7 +572,7 @@
|
||||
"list_updated": "{{count}}個のリストが更新されました",
|
||||
"list_updated_plural": "{{count}}個のリストが更新されました",
|
||||
"dnssec_enable": "DNSSECを有効にする",
|
||||
"dnssec_enable_desc": "DNSクエリの応答にDNSSECフラグを設定し、結果を確認します(DNSSEC対応のリゾルバが必要です)",
|
||||
"dnssec_enable_desc": "送信するDNSクエリにDNSSECフラグを設定し、結果を確認します(DNSSEC対応リゾルバが必要です)。",
|
||||
"validated_with_dnssec": "DNSSECにて検証済",
|
||||
"all_queries": "すべてのクエリ",
|
||||
"show_blocked_responses": "ブロック済",
|
||||
@@ -588,6 +597,8 @@
|
||||
"cache_ttl_min_override_desc": "DNS応答をキャッシュするとき、上流サーバから受信した短いTTL(秒単位)を延長します",
|
||||
"cache_ttl_max_override_desc": "DNSキャッシュ内のエントリの最大TTL(秒単位)を設定します",
|
||||
"ttl_cache_validation": "最小キャッシュTTL値は最大値以下にする必要があります",
|
||||
"cache_optimistic": "Optimistic (オプティミスティック)",
|
||||
"cache_optimistic_desc": "エントリの有効期限が切れた場合でも、AdGuard Homeがキャッシュから応答するようにし、エントリの更新も試みます。",
|
||||
"filter_category_general": "一般",
|
||||
"filter_category_security": "セキュリティ",
|
||||
"filter_category_regional": "地域別",
|
||||
|
||||
@@ -68,6 +68,9 @@
|
||||
"dhcp_new_static_lease": "새 고정 임대",
|
||||
"dhcp_static_leases_not_found": "DHCP 고정 임대를 찾을 수 없음",
|
||||
"dhcp_add_static_lease": "고정 임대 추가",
|
||||
"dhcp_reset_leases": "모든 임대 초기화",
|
||||
"dhcp_reset_leases_confirm": "정말로 모든 임대를 초기화할까요?",
|
||||
"dhcp_reset_leases_success": "DHCP 임대 성공적으로 초기화됨",
|
||||
"dhcp_reset": "정말로 DHCP 설정을 초기화할까요?",
|
||||
"country": "지역",
|
||||
"city": "도시",
|
||||
@@ -109,6 +112,8 @@
|
||||
"for_last_24_hours": "지난 24시간 동안",
|
||||
"for_last_days": "마지막 {{count}} 일",
|
||||
"for_last_days_plural": "마지막 {{count}} 일의 기록",
|
||||
"stats_disabled": "통계 기능이 꺼졌습니다. <0>설정 페이지</0>에서 켤 수 있습니다.",
|
||||
"stats_disabled_short": "통계 꺼짐",
|
||||
"no_domains_found": "도메인이 없습니다",
|
||||
"requests_count": "요청 수",
|
||||
"top_blocked_domains": "차단된 도메인",
|
||||
@@ -319,12 +324,12 @@
|
||||
"install_devices_router_list_2": "각각 1~3자리 숫자의 네 그룹으로 분할된 두 세트의 숫자를 허용하는 필드 옆에 있는 DNS 문자를 찾으세요.",
|
||||
"install_devices_router_list_3": "AdGuard Home 서버 주소를 입력하세요",
|
||||
"install_devices_router_list_4": "일부 라우터 유형에서는 사용자 정의 DNS 서버를 설정할 수 없습니다. 이 경우에는 AdGuard Home을 <0>DHCP 서버</0>로 설정할 수 있습니다. 그렇지 않으면 특정 라우터 모델에 맞게 DNS 서버를 설정하는 방법을 찾아야 합니다.",
|
||||
"install_devices_windows_list_1": "시작 메뉴 또는 윈도우 검색을 통해 제어판을 여세요",
|
||||
"install_devices_windows_list_2": "네트워크 및 인터넷 카테고리로 이동한 다음 네트워크 및 공유 센터로 이동하세요.",
|
||||
"install_devices_windows_list_3": "화면 왼쪽에서 어댑터 설정 변경을 찾아 클릭하세요.",
|
||||
"install_devices_windows_list_4": "활성 연결을 선택한 후 우클릭으로 속성을 선택하세요.",
|
||||
"install_devices_windows_list_5": "목록에서 인터넷 프로토콜 버전 4 (TCP/IP) 를 찾아 선택하고 속성을 다시 클릭하세요.",
|
||||
"install_devices_windows_list_6": "다음 DNS 서버 주소 사용을 선택하고 AdGuard Home 서버 주소 입력하세요.",
|
||||
"install_devices_windows_list_1": "시작 메뉴 또는 윈도우 검색을 통해 제어판을 엽니다.",
|
||||
"install_devices_windows_list_2": "네트워크 및 인터넷 카테고리로 이동한 다음 네트워크 및 공유 센터로 이동합니다.",
|
||||
"install_devices_windows_list_3": "화면 왼쪽에서 '어댑터 설정 변경'을 찾아 클릭합니다.",
|
||||
"install_devices_windows_list_4": "활성 연결을 선택한 후 우클릭으로 속성을 선택합니다.",
|
||||
"install_devices_windows_list_5": "목록에서 '인터넷 프로토콜 버전 4(TCP/IP)' (또는 IPv6의 경우 '인터넷 프로토콜 버전 6(TCP/IPv6)')를 찾아 선택하고 속성을 클릭합니다.",
|
||||
"install_devices_windows_list_6": "'DNS 서버 주소 사용'을 선택하고 AdGuard Home 서버 주소 입력합니다.",
|
||||
"install_devices_macos_list_1": "Apple 아이콘을 클릭하고 시스템 기본 설정으로 이동하세요.",
|
||||
"install_devices_macos_list_2": "네트워크를 클릭하세요.",
|
||||
"install_devices_macos_list_3": "목록에서 첫 번째 연결을 선택하고 고급을 클릭해주세요.",
|
||||
@@ -423,9 +428,9 @@
|
||||
"access_title": "접근 설정",
|
||||
"access_desc": "여기에서 AdGuard Home DNS 서버에 대한 액세스 규칙을 구성할 수 있습니다.",
|
||||
"access_allowed_title": "허용된 클라이언트",
|
||||
"access_allowed_desc": "CIDR 또는 IP 주소 목록입니다. 구성된 경우 AdGuard Home은 이러한 IP 주소의 요청만 수락할 수 있습니다.",
|
||||
"access_allowed_desc": "CIDR, IP 주소 또는 클라이언트 ID 목록입니다. 허용된 클라이언트가 구성된 경우, AdGuard Home은 이 클라이언트의 요청만 수락합니다.",
|
||||
"access_disallowed_title": "차단된 클라이언트",
|
||||
"access_disallowed_desc": "CIDR 또는 IP 주소 목록입니다. 구성된 경우 AdGuard Home은 이러한 IP 주소의 요청을 삭제합니다.",
|
||||
"access_disallowed_desc": "CIDR, IP 주소 또는 클라이언트 ID 목록입니다. 차단된 클라이언트가 구성된 경우, AdGuard Home은 이 클라이언트의 요청을 무시합니다. 허용된 클라이언트가 구성된 경우, 이 필드는 무시됩니다.",
|
||||
"access_blocked_title": "차단된 도메인",
|
||||
"access_blocked_desc": "이 기능을 필터와 혼동하지 마세요. AdGuard Home은 이 도메인에 대한 DNS 요청을 무시합니다. 여기에서는 'example.org' '*. example.org', '|| example.org ^'와 같은 특정 도메인 이름, 와일드 카드, URL 필터 규칙을 지정할 수 있습니다.",
|
||||
"access_settings_saved": "액세스 설정이 성공적으로 저장되었습니다.",
|
||||
@@ -481,10 +486,12 @@
|
||||
"encryption_key_source_content": "비밀키 내용 붙여넣기",
|
||||
"stats_params": "통계 구성",
|
||||
"config_successfully_saved": "설정이 성공적으로 저장되었습니다.",
|
||||
"interval_6_hour": "6시간",
|
||||
"interval_24_hour": "24시간",
|
||||
"interval_days": "{{count}} 일",
|
||||
"interval_days_plural": "{{count}} 일",
|
||||
"domain": "도메인",
|
||||
"punycode": "Punycode",
|
||||
"answer": "응답",
|
||||
"filter_added_successfully": "목록이 성공적으로 추가됨",
|
||||
"filter_removed_successfully": "목록이 성공적으로 제거되었습니다",
|
||||
@@ -590,6 +597,8 @@
|
||||
"cache_ttl_min_override_desc": "업스트림 서버에서 수신한 TTL 값(최소)을 무시합니다",
|
||||
"cache_ttl_max_override_desc": "업스트림 서버에서 수신한 TTL 값(최대)을 무시합니다",
|
||||
"ttl_cache_validation": "최소 캐시 TTL 값은 최대 값보다 이하여야 합니다",
|
||||
"cache_optimistic": "캐시 유지",
|
||||
"cache_optimistic_desc": "세션이 만료되었거나 새로고침을 시도하는 경우에도 AdGuard Home이 캐시를 기반으로 응답하도록 합니다.",
|
||||
"filter_category_general": "일반 목록",
|
||||
"filter_category_security": "보안 목록",
|
||||
"filter_category_regional": "지역 목록",
|
||||
|
||||
@@ -68,6 +68,9 @@
|
||||
"dhcp_new_static_lease": "Voeg static lease toe",
|
||||
"dhcp_static_leases_not_found": "Geen DHCP static lease gevonden",
|
||||
"dhcp_add_static_lease": "Voeg statische lease toe",
|
||||
"dhcp_reset_leases": "Alle leases resetten",
|
||||
"dhcp_reset_leases_confirm": "Weet je zeker dat je alle leases wilt resetten?",
|
||||
"dhcp_reset_leases_success": "DHCP-leases succesvol gereset",
|
||||
"dhcp_reset": "Weet je zeker dat je de DHCP configuratie wil resetten?",
|
||||
"country": "Land",
|
||||
"city": "Stad",
|
||||
@@ -109,6 +112,8 @@
|
||||
"for_last_24_hours": "van de laatste 24-uur",
|
||||
"for_last_days": "sinds de laatste {{count}} dagen",
|
||||
"for_last_days_plural": "sinds de laatste {{count}} dagen",
|
||||
"stats_disabled": "Statistieken zijn uitgeschakeld. Je kunt ze inschakelen op de <0>instellingen pagina</0>.",
|
||||
"stats_disabled_short": "Statistieken zijn uitgeschakeld",
|
||||
"no_domains_found": "Geen domeinen gevonden",
|
||||
"requests_count": "Verzoek teller",
|
||||
"top_blocked_domains": "Top geblokkeerde domeinen",
|
||||
@@ -321,10 +326,10 @@
|
||||
"install_devices_router_list_4": "Je kan een DNS-server niet instellen op sommige routers. In dat geval kan het een oplossing zijn om AdGuard Home te definiëren als een <0>DHCP-server</0>. Je kan ook in de handleiding van je router kijken hoe je een DNS-server aanpast.",
|
||||
"install_devices_windows_list_1": "Open het Configuratiescherm via het menu Start of Windows zoeken.",
|
||||
"install_devices_windows_list_2": "Ga naar de categorie Netwerk en Internet en vervolgens naar Netwerkcentrum.",
|
||||
"install_devices_windows_list_3": "Zoek aan de linkerkant van het scherm Adapter-instellingen wijzigen en klik erop.",
|
||||
"install_devices_windows_list_3": "Zoek aan de linkerkant van het scherm \"Adapter-instellingen wijzigen\" en klik erop.",
|
||||
"install_devices_windows_list_4": "Selecteer jouw actieve verbinding, klik er met de rechtermuisknop op en kies Eigenschappen.",
|
||||
"install_devices_windows_list_5": "Zoek Internet Protocol versie 4 (TCP / IP) in de lijst, selecteer het en klik vervolgens opnieuw op Eigenschappen.",
|
||||
"install_devices_windows_list_6": "Kies Gebruik de volgende DNS-server adressen en voer jouw AdGuard Home server adressen in.",
|
||||
"install_devices_windows_list_5": "Zoek \"Internet Protocol versie 4 (TCP/IPv4)\" (of, voor IPv6, \"Internet Protocol versie 6 (TCP/IPv6)\") in de lijst, selecteer het en klik vervolgens opnieuw op Eigenschappen.",
|
||||
"install_devices_windows_list_6": "Kies \"Gebruik de volgende DNS-serveradressen\" en voer jouw AdGuard Home serveradressen in.",
|
||||
"install_devices_macos_list_1": "Klik op het Apple-pictogram en ga naar Systeemvoorkeuren.",
|
||||
"install_devices_macos_list_2": "Klik op Netwerk.",
|
||||
"install_devices_macos_list_3": "Selecteer de eerste verbinding in jouw lijst en klik op Geavanceerd.",
|
||||
@@ -423,9 +428,9 @@
|
||||
"access_title": "Toegangs instellingen",
|
||||
"access_desc": "Hier kan je toegangsregels voor de AdGuard Home DNS-server instellen.",
|
||||
"access_allowed_title": "Toegestane gebruikers",
|
||||
"access_allowed_desc": "Een lijst van CIDR of IP adressen. Indien ingesteld, zal AdGuard Home alleen van deze IP adressen aanvragen accepteren.",
|
||||
"access_allowed_desc": "Een lijst met CIDR's, IP-adressen of client-ID's. Indien geconfigureerd, accepteert AdGuard Home alleen verzoeken van deze cliënts.",
|
||||
"access_disallowed_title": "Verworpen gebruikers",
|
||||
"access_disallowed_desc": "Een lijst van CIDR of IP adressen. Indien ingesteld, zal AdGuard Home aanvragen van deze IP adressen verwerpen.",
|
||||
"access_disallowed_desc": "Een lijst met CIDR's, IP-adressen of client-ID's. Indien geconfigureerd, zal AdGuard Home verzoeken van deze klanten verwerpen. Als toegestane cliënts zijn geconfigureerd, wordt dit veld genegeerd.",
|
||||
"access_blocked_title": "Niet toegelaten domeinen",
|
||||
"access_blocked_desc": "Verwar dit niet met filters. AdGuard Home zal deze DNS-zoekopdrachten niet uitvoeren die deze domeinen in de zoekopdracht bevatten. Hier kan je de exacte domeinnamen, wildcards en URL-filter-regels specifiëren, bijv. \"example.org\", \"*.example.org\" of \"||example.org^\".",
|
||||
"access_settings_saved": "Toegangsinstellingen succesvol opgeslagen",
|
||||
@@ -481,10 +486,12 @@
|
||||
"encryption_key_source_content": "Inhoud privé sleutel plakken",
|
||||
"stats_params": "Statistieken configuratie",
|
||||
"config_successfully_saved": "Configuratie succesvol opgeslagen",
|
||||
"interval_6_hour": "6 uren",
|
||||
"interval_24_hour": "24 uur",
|
||||
"interval_days": "{{count}} dagen",
|
||||
"interval_days_plural": "{{count}} dagen",
|
||||
"domain": "Domein",
|
||||
"punycode": "Punycode",
|
||||
"answer": "Antwoord",
|
||||
"filter_added_successfully": "De lijst is succesvol toegevoegd",
|
||||
"filter_removed_successfully": "De lijst is succesvol verwijderd",
|
||||
@@ -590,6 +597,8 @@
|
||||
"cache_ttl_min_override_desc": "Uitbreiden van korte Time-To-Live waardes (seconden) ontvangen van de upstream server bij het cachen van DNS antwoorden",
|
||||
"cache_ttl_max_override_desc": "Instellen van maximum time-to-live waarde (seconden) voor opslag in de DNS cache",
|
||||
"ttl_cache_validation": "Minimale waarde TTL-cache moet kleiner dan of gelijk zijn aan de maximale waarde",
|
||||
"cache_optimistic": "Optimistisch",
|
||||
"cache_optimistic_desc": "Laat AdGuard Home reageren vanuit de cache, zelfs als de vermeldingen zijn verlopen en probeer deze ook te vernieuwen.",
|
||||
"filter_category_general": "Algemeen",
|
||||
"filter_category_security": "Beveiliging",
|
||||
"filter_category_regional": "Regionaal",
|
||||
|
||||
@@ -233,6 +233,7 @@
|
||||
"dns_over_tls": "DNS-over-TLS",
|
||||
"dns_over_quic": "DNS-over-QUIC",
|
||||
"client_id": "Klient-ID",
|
||||
"client_id_placeholder": "Skriv inn klient-ID",
|
||||
"download_mobileconfig_doh": "Last ned .mobileconfig for DNS-over-HTTPS",
|
||||
"download_mobileconfig_dot": "Last ned .mobileconfig for DNS-over-TLS",
|
||||
"plain_dns": "Ordinær DNS",
|
||||
|
||||
@@ -68,6 +68,9 @@
|
||||
"dhcp_new_static_lease": "Nowa dzierżawa statyczna",
|
||||
"dhcp_static_leases_not_found": "Nie znaleziono statycznych dzierżaw DHCP",
|
||||
"dhcp_add_static_lease": "Dodaj dzierżawę statyczną",
|
||||
"dhcp_reset_leases": "Zresetuj wszystkie dzierżawy",
|
||||
"dhcp_reset_leases_confirm": "Czy na pewno chcesz zresetować wszystkie dzierżawy?",
|
||||
"dhcp_reset_leases_success": "Pomyślnie zresetowano dzierżawy DHCP",
|
||||
"dhcp_reset": "Czy na pewno chcesz zresetować konfigurację DHCP?",
|
||||
"country": "Kraj",
|
||||
"city": "Miasto",
|
||||
@@ -109,6 +112,8 @@
|
||||
"for_last_24_hours": "przez ostatnie 24 godziny",
|
||||
"for_last_days": "z ostatniego dnia",
|
||||
"for_last_days_plural": "z ostatnich {{count}} dni",
|
||||
"stats_disabled": "Statystyki zostały wyłączone. Można je włączyć na <0>stronie ustawień</0>.",
|
||||
"stats_disabled_short": "Statystyki zostały wyłączone",
|
||||
"no_domains_found": "Nie znaleziono domen",
|
||||
"requests_count": "Licznik żądań",
|
||||
"top_blocked_domains": "Najpopularniejsze zablokowane domeny",
|
||||
@@ -321,10 +326,10 @@
|
||||
"install_devices_router_list_4": "Na niektórych typach routerów nie można skonfigurować własnego serwera DNS. W takim przypadku pomocne może być skonfigurowanie AdGuard Home jako <0>serwera DHCP</0>. W przeciwnym razie należy sprawdzić w instrukcji obsługi routera, jak dostosować serwery DNS do konkretnego modelu routera.",
|
||||
"install_devices_windows_list_1": "Otwórz panel Ustawienia w menu Start lub w Windows.",
|
||||
"install_devices_windows_list_2": "Przejdź do kategorii Sieć i Internet, a następnie do Centrum sieci i udostępniania.",
|
||||
"install_devices_windows_list_3": "Po lewej stronie ekranu znajdź Zmień ustawienia adaptera i kliknij na niego.",
|
||||
"install_devices_windows_list_3": "Po lewej stronie ekranu znajdź \"Zmień ustawienia adaptera\" i kliknij na niego.",
|
||||
"install_devices_windows_list_4": "Wybierz aktywne połączenie, kliknij je prawym przyciskiem myszy i wybierz Właściwości.",
|
||||
"install_devices_windows_list_5": "Znajdź na liście protokół internetowy w wersji 4 (TCP/IP), zaznacz go, a następnie ponownie kliknij Właściwości.",
|
||||
"install_devices_windows_list_6": "Wybierz Użyj następujących adresów serwerów DNS i wpisz adresy serwerów AdGuard Home.",
|
||||
"install_devices_windows_list_5": "Znajdź na liście \"Protokół internetowy w wersji 4 (TCP/IPv4)\" (lub w przypadku IPv6 \"Protokół internetowy w wersji 6 (TCP/IPv6)\"), zaznacz go i ponownie kliknij na Właściwości.",
|
||||
"install_devices_windows_list_6": "Wybierz opcję \"Użyj następujących adresów serwerów DNS\" i wprowadź adresy serwerów AdGuard Home.",
|
||||
"install_devices_macos_list_1": "Kliknij ikonę Apple i przejdź do Preferencje systemowe.",
|
||||
"install_devices_macos_list_2": "Kliknij Sieć.",
|
||||
"install_devices_macos_list_3": "Wybierz pierwsze połączenie z listy i kliknij Zaawansowane.",
|
||||
@@ -423,9 +428,9 @@
|
||||
"access_title": "Ustawienia dostępu",
|
||||
"access_desc": "Tutaj możesz skonfigurować reguły dostępu dla serwera DNS AdGuard Home.",
|
||||
"access_allowed_title": "Dozwoleni klienci",
|
||||
"access_allowed_desc": "Lista adresów CIDR lub IP. Jeśli jest skonfigurowany, AdGuard Home akceptuje tylko żądania z tych adresów IP.",
|
||||
"access_allowed_desc": "Lista CIDR-ów, adresów IP lub identyfikatorów klientów. Jeśli zostanie skonfigurowana, AdGuard Home będzie przyjmował żądania tylko od tych klientów.",
|
||||
"access_disallowed_title": "Niedozwoleni klienci",
|
||||
"access_disallowed_desc": "Lista adresów CIDR lub IP. Po skonfigurowaniu AdGuard Home usunie żądania z tych adresów IP.",
|
||||
"access_disallowed_desc": "Lista CIDR-ów, adresów IP lub identyfikatorów klientów. Jeśli jest skonfigurowana, AdGuard Home będzie odrzucał żądania od tych klientów. Jeśli skonfigurowano dozwolonych klientów, pole to jest ignorowane.",
|
||||
"access_blocked_title": "Niedozwolone domeny",
|
||||
"access_blocked_desc": "Nie należy ich mylić z filtrami. AdGuard Home usuwa zapytania DNS pasujące do tych domen, a zapytania te nie pojawiają się nawet w dzienniku zapytań. Możesz określić dokładne nazwy domen, symbole wieloznaczne lub reguły filtrowania adresów URL, np. \"example.org\", \"*.example.org\" lub \"||example.org^\".",
|
||||
"access_settings_saved": "Ustawienia dostępu zostały pomyślnie zapisane",
|
||||
@@ -481,10 +486,12 @@
|
||||
"encryption_key_source_content": "Wklej zawartość klucza prywatnego",
|
||||
"stats_params": "Konfiguracja statystyk",
|
||||
"config_successfully_saved": "Konfiguracja została pomyślnie zapisana",
|
||||
"interval_6_hour": "6 godzin",
|
||||
"interval_24_hour": "24 godziny",
|
||||
"interval_days": "{{count}} dni",
|
||||
"interval_days_plural": "{{count}} dni",
|
||||
"domain": "Domena",
|
||||
"punycode": "Punycode",
|
||||
"answer": "Odpowiedź",
|
||||
"filter_added_successfully": "Lista została pomyślnie dodana",
|
||||
"filter_removed_successfully": "Lista została usunięta",
|
||||
@@ -590,6 +597,8 @@
|
||||
"cache_ttl_min_override_desc": "Zastąp wartość TTL (w sekundach) otrzymaną z serwera nadrzędnego podczas buforowania odpowiedzi DNS",
|
||||
"cache_ttl_max_override_desc": "Ustaw maksymalną wartość TTL (w sekundach) dla wpisów w pamięci podręcznej DNS",
|
||||
"ttl_cache_validation": "Minimalna pamięć podręczna wartości TTL musi być mniejsza lub równa maksymalnej wartości",
|
||||
"cache_optimistic": "Optymistyczny",
|
||||
"cache_optimistic_desc": "Spraw, aby AdGuard Home odpowiadał z pamięci podręcznej, nawet gdy wpisy wygasły, a także spróbuj je odświeżyć.",
|
||||
"filter_category_general": "Ogólne",
|
||||
"filter_category_security": "Bezpieczeństwo",
|
||||
"filter_category_regional": "Regionalne",
|
||||
|
||||
@@ -68,6 +68,9 @@
|
||||
"dhcp_new_static_lease": "Nova concessão estática",
|
||||
"dhcp_static_leases_not_found": "Nenhuma concessão DHCP estática foi encontrada",
|
||||
"dhcp_add_static_lease": "Adicionar nova concessão estática",
|
||||
"dhcp_reset_leases": "Redefinir todas as concessões",
|
||||
"dhcp_reset_leases_confirm": "Tem certeza de que deseja redefinir todas as concessões?",
|
||||
"dhcp_reset_leases_success": "Concessões de DHCP redefinidas com sucesso",
|
||||
"dhcp_reset": "Você tem certeza de que deseja redefinir a configuração DHCP?",
|
||||
"country": "País",
|
||||
"city": "Cidade",
|
||||
@@ -109,6 +112,8 @@
|
||||
"for_last_24_hours": "nas últimas 24 horas",
|
||||
"for_last_days": "nos últimos {{count}} dias",
|
||||
"for_last_days_plural": "nos últimos {{count}} dias",
|
||||
"stats_disabled": "As estatísticas foram desativadas. Você pode ligá-las através da <0>página de configurações</0>.",
|
||||
"stats_disabled_short": "As estatísticas foram desativadas",
|
||||
"no_domains_found": "Nenhum domínio encontrado",
|
||||
"requests_count": "Contagem de solicitações",
|
||||
"top_blocked_domains": "Principais domínios bloqueados",
|
||||
@@ -321,10 +326,10 @@
|
||||
"install_devices_router_list_4": "Em alguns tipos de roteador, um servidor DNS personalizado não pode ser configurado. Nesse caso, configurar o AdGuard Home como um <0>Servidor DHCP</0> pode ajudar. Caso contrário, você deve verificar o manual do roteador sobre como personalizar os servidores DNS em seu modelo de roteador específico.",
|
||||
"install_devices_windows_list_1": "Abra o Painel de Controle pelo Menu Iniciar ou pela Pesquisa do Windows.",
|
||||
"install_devices_windows_list_2": "Entre na categoria Rede e Internet e depois clique em Central de Rede e Compartilhamento.",
|
||||
"install_devices_windows_list_3": "No lado esquerdo da janela clique em Alterar as configurações do adaptador.",
|
||||
"install_devices_windows_list_3": "No lado esquerdo da janela clique em \"Alterar as configurações do adaptador\".",
|
||||
"install_devices_windows_list_4": "Selecione sua atual conexão, clique nela com o botão direito do mouse e depois clique em Propriedades.",
|
||||
"install_devices_windows_list_5": "Procure na lista por Internet Protocol Version 4 (TCP/IP), selecione e clique em Propriedades novamente.",
|
||||
"install_devices_windows_list_6": "Marque usar os seguintes endereços de servidor DNS e digite os endereços do servidores do AdGuard Home.",
|
||||
"install_devices_windows_list_5": "Procure na lista por \"Internet Protocol Version 4 (TCP/IP)\" (ou por IPv6, \"Internet Protocol Version 6 (TCP/IPv6)\"), selecione e clique em Propriedades novamente.",
|
||||
"install_devices_windows_list_6": "Marque \"usar os seguintes endereços de servidor DNS\" e digite os endereços do servidores do AdGuard Home.",
|
||||
"install_devices_macos_list_1": "Clique na ícone da Apple e depois em Preferências do Sistema.",
|
||||
"install_devices_macos_list_2": "Clique em Rede.",
|
||||
"install_devices_macos_list_3": "Selecione a primeira conexão da lista e clique em Avançado.",
|
||||
@@ -423,9 +428,9 @@
|
||||
"access_title": "Configurações de acessos",
|
||||
"access_desc": "Aqui você pode configurar as regras de acesso para o servidores de DNS do AdGuard Home.",
|
||||
"access_allowed_title": "Clientes permitidos",
|
||||
"access_allowed_desc": "Uma lista de endereços IP ou CIDR. Ao configurar, o AdGuard Home irá permitir solicitações apenas desses endereços de IP.",
|
||||
"access_allowed_desc": "Uma lista de CIDRs, endereços IP ou IDs de cliente. Se configurado, o AdGuard Home do aceitará solicitações apenas desses clientes.",
|
||||
"access_disallowed_title": "Clientes não permitidos",
|
||||
"access_disallowed_desc": "Uma lista de endereços IP ou CIDR. Ao configurar, o AdGuard Home irá descartar as solicitações desses endereços de IP.",
|
||||
"access_disallowed_desc": "Uma lista de CIDRs, endereços IP ou IDs de cliente. Se configurado, o AdGuard Home descartará as solicitações desses clientes. Se clientes permitidos estiverem configurados, este campo será ignorado.",
|
||||
"access_blocked_title": "Domínios bloqueados",
|
||||
"access_blocked_desc": "Não deve ser confundido com filtros. O AdGuard Home elimina as consultas DNS que correspondem a esses domínios, e essas consultas nem aparecem no registro de consultas. Você pode especificar nomes de domínio exatos, caracteres curinga ou regras de filtro de URL, por exemplo \"exemplo.org\", \"*.exemplo.org\", ou \"||exemplo.org^\" correspondentemente.",
|
||||
"access_settings_saved": "Configurações de acesso foram salvas com sucesso",
|
||||
@@ -481,10 +486,12 @@
|
||||
"encryption_key_source_content": "Colar o conteúdo da chave privada",
|
||||
"stats_params": "Configuração de estatísticas",
|
||||
"config_successfully_saved": "Configuração salva com sucesso",
|
||||
"interval_6_hour": "6 horas",
|
||||
"interval_24_hour": "24 horas",
|
||||
"interval_days": "{{count}} dias",
|
||||
"interval_days_plural": "{{count}} dias",
|
||||
"domain": "Domínio",
|
||||
"punycode": "Punycode",
|
||||
"answer": "Resposta",
|
||||
"filter_added_successfully": "O filtro foi adicionado com sucesso",
|
||||
"filter_removed_successfully": "A lista foi removida com sucesso",
|
||||
@@ -590,6 +597,8 @@
|
||||
"cache_ttl_min_override_desc": "Prolongue os valores de curta duração (segundos) recebidos do servidor primário ao armazenar em cache as respostas DNS",
|
||||
"cache_ttl_max_override_desc": "Defina um valor máximo de tempo de vida (segundos) para entradas no cache DNS",
|
||||
"ttl_cache_validation": "O valor TTL mínimo do cache deve ser menor ou igual ao valor máximo",
|
||||
"cache_optimistic": "Otimista",
|
||||
"cache_optimistic_desc": "Faz o AdGuard Home responder a partir do cache mesmo quando as entradas expirarem e também tenta atualizá-las.",
|
||||
"filter_category_general": "Geral",
|
||||
"filter_category_security": "Segurança",
|
||||
"filter_category_regional": "Regional",
|
||||
|
||||
@@ -68,6 +68,9 @@
|
||||
"dhcp_new_static_lease": "Nova concessão estática",
|
||||
"dhcp_static_leases_not_found": "Nenhuma concessão DHCP estática foi encontrada",
|
||||
"dhcp_add_static_lease": "Adicionar nova concessão estática",
|
||||
"dhcp_reset_leases": "Repor todas as concessões",
|
||||
"dhcp_reset_leases_confirm": "Tem certeza de que deseja repor todas as concessões?",
|
||||
"dhcp_reset_leases_success": "Concessões de DHCP repostas com sucesso",
|
||||
"dhcp_reset": "Tem a certeza de que deseja repor a definição de DHCP?",
|
||||
"country": "País",
|
||||
"city": "Cidade",
|
||||
@@ -104,11 +107,13 @@
|
||||
"dns_query": "Consultas de DNS",
|
||||
"blocked_by": "<0>Bloqueado por filtros</0>",
|
||||
"stats_malware_phishing": "Malware/phishing bloqueados",
|
||||
"stats_adult": "Sites adultos bloqueados",
|
||||
"stats_adult": "Sítios adultos bloqueados",
|
||||
"stats_query_domain": "Principais domínios consultados",
|
||||
"for_last_24_hours": "nas últimas 24 horas",
|
||||
"for_last_days": "nos últimos {{count}} dias",
|
||||
"for_last_days_plural": "nos últimos {{count}} dias",
|
||||
"stats_disabled": "As estatísticas foram desativadas. Você pode ligá-las através da <0>página de definições</0>.",
|
||||
"stats_disabled_short": "As estatísticas foram desativadas",
|
||||
"no_domains_found": "Não foram encontrados domínios",
|
||||
"requests_count": "Contagem de solicitações",
|
||||
"top_blocked_domains": "Principais domínios bloqueados",
|
||||
@@ -321,10 +326,10 @@
|
||||
"install_devices_router_list_4": "Em alguns tipos de roteador, um servidor DNS personalizado não pode ser configurado. Nesse caso, configurar o AdGuard Home como um <0>Servidor DHCP</0> pode ajudar. Caso contrário, tu deve verificar o manual do router sobre como personalizar os servidores DNS em seu modelo de router específico.",
|
||||
"install_devices_windows_list_1": "Abra o Painel de Controlo através do Menu Iniciar ou pela Pesquisa do Windows.",
|
||||
"install_devices_windows_list_2": "Entre na categoria Rede e Internet e depois clique em Central de Rede e Partilha.",
|
||||
"install_devices_windows_list_3": "No lado esquerdo da janela clique em Alterar as definições do adaptador.",
|
||||
"install_devices_windows_list_3": "No lado esquerdo da janela clique em \"Alterar as definições do adaptador\".",
|
||||
"install_devices_windows_list_4": "Selecione sua atual ligação, clique nela com o botão direito do rato e depois clique em Propriedades.",
|
||||
"install_devices_windows_list_5": "Procure na lista por Internet Protocol Version 4 (TCP/IP), selecione e clique novamente em Propriedades.",
|
||||
"install_devices_windows_list_6": "Marque Usar os seguintes endereços de servidor DNS e insira os endereços do servidores do AdGuard Home.",
|
||||
"install_devices_windows_list_5": "Procure na lista por \"Internet Protocol Version 4 (TCP/IP)\" (ou por IPv6, \"Internet Protocol Version 6 (TCP/IPv6)\"), selecione e clique em Propriedades novamente.",
|
||||
"install_devices_windows_list_6": "Marque \"Usar os seguintes endereços de servidor DNS\" e insira os endereços do servidores do AdGuard Home.",
|
||||
"install_devices_macos_list_1": "Clique na ícone da Apple e depois em Preferências do Sistema.",
|
||||
"install_devices_macos_list_2": "Clique em Rede.",
|
||||
"install_devices_macos_list_3": "Selecione a primeira ligação da lista e clique em Avançado.",
|
||||
@@ -423,9 +428,9 @@
|
||||
"access_title": "Definições de acesso",
|
||||
"access_desc": "Aqui pode configurar as regras de acesso para o servidores de DNS do AdGuard Home.",
|
||||
"access_allowed_title": "Clientes permitidos",
|
||||
"access_allowed_desc": "Uma lista de endereços IP ou CIDR. Ao configurar, o AdGuard Home irá permitir solicitações apenas desses endereços de IP.",
|
||||
"access_allowed_desc": "Uma lista de CIDRs, endereços IP ou IDs de cliente. Se configurado, o AdGuard Home do aceitará solicitações apenas desses clientes.",
|
||||
"access_disallowed_title": "Clientes não permitidos",
|
||||
"access_disallowed_desc": "Uma lista de endereços IP ou CIDR. Ao configurar, o AdGuard Home irá descartar as solicitações desses endereços de IP.",
|
||||
"access_disallowed_desc": "Uma lista de CIDRs, endereços IP ou IDs de cliente. Se configurado, o AdGuard Home descartará as solicitações desses clientes. Se clientes permitidos estiverem configurados, este campo será ignorado.",
|
||||
"access_blocked_title": "Domínios bloqueados",
|
||||
"access_blocked_desc": "Não deve ser confundido com filtros. O AdGuard Home elimina as consultas DNS que correspondem a esses domínios, e essas consultas nem aparecem no registro de consultas. Você pode especificar nomes de domínio exatos, caracteres curinga ou regras de filtro de URL, por exemplo \"exemplo.org\", \"*.exemplo.org\", ou \"||exemplo.org^\" correspondentemente.",
|
||||
"access_settings_saved": "Definições de acesso foram guardadas com sucesso",
|
||||
@@ -467,7 +472,7 @@
|
||||
"main_settings": "Definições principais",
|
||||
"block_services": "Bloquear serviços específicos",
|
||||
"blocked_services": "Serviços bloqueados",
|
||||
"blocked_services_desc": "Permite o bloqueio rápido de sites e serviços populares.",
|
||||
"blocked_services_desc": "Permite o bloqueio rápido de sítios e serviços populares.",
|
||||
"blocked_services_saved": "Serviços bloqueados guardados com sucesso",
|
||||
"blocked_services_global": "Usar serviços bloqueados globais",
|
||||
"blocked_service": "Serviço bloqueado",
|
||||
@@ -481,10 +486,12 @@
|
||||
"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",
|
||||
"interval_6_hour": "6 horas",
|
||||
"interval_24_hour": "24 horas",
|
||||
"interval_days": "{{count}} dias",
|
||||
"interval_days_plural": "{{count}} dias",
|
||||
"domain": "Domínio",
|
||||
"punycode": "Punycode",
|
||||
"answer": "Resposta",
|
||||
"filter_added_successfully": "O filtro foi adicionado com sucesso",
|
||||
"filter_removed_successfully": "A lista foi removida com sucesso",
|
||||
@@ -590,6 +597,8 @@
|
||||
"cache_ttl_min_override_desc": "Prolongue os valores de curta duração (segundos) recebidos do servidor primário ao armazenar em cache as respostas DNS",
|
||||
"cache_ttl_max_override_desc": "Defina um valor máximo de tempo de vida (segundos) para entradas no cache DNS",
|
||||
"ttl_cache_validation": "O valor TTL mínimo do cache deve ser menor ou igual ao valor máximo",
|
||||
"cache_optimistic": "Otimista",
|
||||
"cache_optimistic_desc": "Faz o AdGuard Home responder a partir do cache mesmo quando as entradas expirarem e também tenta atualizá-las.",
|
||||
"filter_category_general": "Geral",
|
||||
"filter_category_security": "Segurança",
|
||||
"filter_category_regional": "Regional",
|
||||
|
||||
@@ -1,10 +1,24 @@
|
||||
{
|
||||
"client_settings": "Setări client",
|
||||
"example_upstream_reserved": "Puteți specifica un DNS în amonte <0>pentru domeniul (domeniile) specific(e)</0>",
|
||||
"example_upstream_comment": "Puteți specifica un comentariu",
|
||||
"upstream_parallel": "Folosiți interogări paralele pentru a accelera rezolvarea, interogând simultan toate serverele în amonte.",
|
||||
"parallel_requests": "Solicitări paralele",
|
||||
"load_balancing": "Echilibrare-sarcini",
|
||||
"load_balancing_desc": "Interoghează câte un server în amonte la un moment dat. AdGuard Home utilizează un algoritm de randomizare ponderat pentru a alege serverul, astfel încât cel mai rapid server să fie utilizat mai des.",
|
||||
"bootstrap_dns": "Serverele DNS Bootstrap",
|
||||
"bootstrap_dns_desc": "Serverele DNS Bootstrap sunt folosite pentru a rezolva adresele IP ale resolverelor DoH/DoT indicate ca upstreams.",
|
||||
"local_ptr_title": "Servere DNS inverse private",
|
||||
"local_ptr_desc": "Servere DNS pe care AdGuard Home le utilizează pentru interogări PTR locale. Aceste servere sunt folosite pentru a rezolva numele gazdelor de clienți cu adrese IP private, cum ar fi \"192.168.12.34\", folosind DNS inversat. Dacă nu este setat, AdGuard Home utilizează adresele resolverelor DNS implicite ale SO al dvs., cu excepția adreselor AdGuard Home înseși.",
|
||||
"local_ptr_default_resolver": "În mod implicit, AdGuard Home utilizează următoarele resolvere DNS inverse: {{ip}}.",
|
||||
"local_ptr_no_default_resolver": "AdGuard Home nu a putut determina resolvere DNS private adecvate pentru acest sistem.",
|
||||
"local_ptr_placeholder": "Introduceți o adresă de server per linie",
|
||||
"resolve_clients_title": "Permiteți rezolvarea inversa a adreselor IP ale clienților",
|
||||
"resolve_clients_desc": "Rezolvă invers adresele IP ale clienților în numele lor de gazde prin trimiterea interogărilor PTR la resolverele corespunzătoare (servere DNS private pentru clienți locali, servere în amonte pentru clienți cu adrese IP publice).",
|
||||
"use_private_ptr_resolvers_title": "Utilizați resolvere DNS inverse private",
|
||||
"use_private_ptr_resolvers_desc": "Efectuează examinări DNS inverse pentru adresele deservite local folosind aceste servere în amonte. Dacă este dezactivată, AdGuard Home răspunde cu NXDOMAIN la toate aceste cereri PTR, cu excepția clienților cunoscuți din DHCP, /etc/hosts și așa mai departe.",
|
||||
"check_dhcp_servers": "Căutați servere DHCP",
|
||||
"save_config": "Salvare configurare",
|
||||
"enabled_dhcp": "Server DHCP activat",
|
||||
"disabled_dhcp": "Server DHCP dezactivat",
|
||||
"unavailable_dhcp": "DHCP este indisponibil",
|
||||
@@ -13,10 +27,12 @@
|
||||
"dhcp_description": "Dacă routerul dvs. nu furnizează setări DHCP, puteți utiliza serverul DHCP încorporat AdGuard.",
|
||||
"dhcp_enable": "Activați serverul DHCP",
|
||||
"dhcp_disable": "Dezactivați serverul DHCP",
|
||||
"dhcp_not_found": "Este sigur să activați serverul DHCP încorporat deoarece AdGuard Home nu a găsit niciun server DHCP activ în rețea. Cu toate acestea, ar trebui să verificați din nou manual, deoarece sondarea automată nu oferă în prezent o garanție de 100%.",
|
||||
"dhcp_found": "În rețea se găsește un server DHCP activ. Nu este sigur să activați serverul DHCP încorporat.",
|
||||
"dhcp_leases": "DHCP închiriate",
|
||||
"dhcp_static_leases": "DHCP statice închiriate",
|
||||
"dhcp_leases_not_found": "Nu s-au găsit DHCP închiriate",
|
||||
"dhcp_config_saved": "Configurare DHCP salvată cu succes",
|
||||
"dhcp_ipv4_settings": "Setări DHCP IPv4",
|
||||
"dhcp_ipv6_settings": "Setări DHCP IPv6",
|
||||
"form_error_required": "Câmp necesar",
|
||||
@@ -26,6 +42,7 @@
|
||||
"form_error_mac_format": "Format MAC invalid",
|
||||
"form_error_client_id_format": "Format ID de client invalid",
|
||||
"form_error_server_name": "Nume de server nevalid",
|
||||
"form_error_subnet": "Subrețeaua „{{cidr}}” nu conține adresa IP „{{ip}}”",
|
||||
"form_error_positive": "Trebuie să fie mai mare de 0",
|
||||
"form_error_negative": "Trebuie să fie egală cu 0 sau mai mare",
|
||||
"range_end_error": "Trebuie să fie mai mare decât începutul intervalului",
|
||||
@@ -42,11 +59,19 @@
|
||||
"ip": "IP",
|
||||
"dhcp_table_hostname": "Hostname",
|
||||
"dhcp_table_expires": "Expiră",
|
||||
"dhcp_warning": "Dacă doriți să activați serverul DHCP oricum, asigurați-vă că nu există nici un alt server DHCP activ în rețeaua dvs., deoarece acest lucru poate rupe conectivitatea la Internet a dispozitivelor din rețea!",
|
||||
"dhcp_error": "AdGuard Home nu a putut determina dacă există un alt server DHCP activ în rețea.",
|
||||
"dhcp_static_ip_error": "Pentru a utiliza serverul DHCP, trebuie setată o adresă IP statică. AdGuard Home nu a reușit să determine dacă această interfață de rețea este configurată utilizând o adresă IP statică. Setați manual o adresă IP statică.",
|
||||
"dhcp_dynamic_ip_found": "Sistemul dvs. folosește configurația dinamică a adreselor IP pentru interfața <0>{{interfaceName}}</0>. Pentru a utiliza serverul DHCP, trebuie setată o adresă IP statică. Adresa IP curentă este <0>{{ipAddress}}</0>. AdGuard Home o va configura automat ca adresă IP statică, dacă apăsați butonul \"Activați serverul DHCP\".",
|
||||
"dhcp_lease_added": "\"{{key}}\" statică închiriată adăugată cu succes",
|
||||
"dhcp_lease_deleted": "\"{{key}}\" statică închiriată eliminată cu succes",
|
||||
"dhcp_new_static_lease": "Închiriere statică nouă",
|
||||
"dhcp_static_leases_not_found": "Nu s-au găsit închirieri statice DHCP",
|
||||
"dhcp_add_static_lease": "Adăugați închiriere statică",
|
||||
"dhcp_reset_leases": "Resetați toate închirierile",
|
||||
"dhcp_reset_leases_confirm": "Sigur doriți să resetați toate închirierile?",
|
||||
"dhcp_reset_leases_success": "Închirierile DHCP au fost resetate cu succes",
|
||||
"dhcp_reset": "Sigur doriți să resetați configurația DHCP?",
|
||||
"country": "Țara",
|
||||
"city": "Oraș",
|
||||
"delete_confirm": "Sunteți sigur că doriți să ștergeți \"{{key}}\"?",
|
||||
@@ -93,17 +118,24 @@
|
||||
"top_clients": "Clienți de top",
|
||||
"no_clients_found": "Nu au fost găsiți clienți",
|
||||
"general_statistics": "Statistici generale",
|
||||
"number_of_dns_query_days": "Numărul de interogări DNS procesate în ultima {{count}} zi",
|
||||
"number_of_dns_query_days_plural": "Numărul de interogări DNS procesate în ultimele {{count}} zile",
|
||||
"number_of_dns_query_24_hours": "Numărul de interogări DNS procesate în ultimele 24 de ore",
|
||||
"number_of_dns_query_blocked_24_hours": "Numărul de interogări DNS blocate de filtrele adblock și lista de blocări din hosts",
|
||||
"number_of_dns_query_blocked_24_hours_by_sec": "Numărul de interogări DNS blocate de modulul de securitate de navigare AdGuard",
|
||||
"number_of_dns_query_blocked_24_hours_adult": "Numărul de site-uri pentru adulți blocate",
|
||||
"enforced_save_search": "Căutare protejată întărită",
|
||||
"number_of_dns_query_to_safe_search": "Numărul de interogări DNS pe motoarele de căutare pentru care a fost impusă Căutarea Sigură",
|
||||
"average_processing_time": "Timpul mediu de procesare",
|
||||
"average_processing_time_hint": "Timp mediu în milisecunde la procesarea unei cereri DNS",
|
||||
"block_domain_use_filters_and_hosts": "Blocați domenii folosind filtre și fișiere hosts",
|
||||
"filters_block_toggle_hint": "Puteți configura regulile de blocare în setările <a>Filtre</a>.",
|
||||
"use_adguard_browsing_sec": "Utilizați serviciul Navigarea în Securitate AdGuard",
|
||||
"use_adguard_browsing_sec_hint": "AdGuard Home va verifica dacă domeniul este în lista de blocări a serviciul web de securitate de navigare. Pentru acesta va utiliza un lookup API discret: un prefix scurt al numelui de domeniu SHA256 hash este trimis serverului.",
|
||||
"use_adguard_browsing_sec_hint": "AdGuard Home va verifica dacă domeniul este blocat de serviciul web de securitate de navigare. Pentru acesta, va utiliza un API de căutare discret: numai un prefix scurt al hash-ului SHA256 al numelui de domeniu este trimis la server.",
|
||||
"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_safe_search": "Folosiți Căutarea Sigură",
|
||||
"enforce_save_search_hint": "AdGuard Home va impune Căutarea Sigură în următoarele motoare de căutare: Google, YouTube, Bing, DuckDuckGo, Yandex, Pixabay.",
|
||||
"no_servers_specified": "Nu sunt specificate servere",
|
||||
"general_settings": "Setări Generale",
|
||||
"dns_settings": "Setări DNS",
|
||||
@@ -115,6 +147,7 @@
|
||||
"encryption_settings": "Setări de criptare",
|
||||
"dhcp_settings": "Setări DHCP",
|
||||
"upstream_dns": "Servere DNS în amonte",
|
||||
"upstream_dns_help": "Introduceți o adresă de server pe linie. <a>Aflați mai multe</a> despre configurarea serverelor DNS în amonte.",
|
||||
"upstream_dns_configured_in_file": "Configurat în {{path}}",
|
||||
"test_upstream_btn": "Testați upstreams",
|
||||
"upstreams": "Upstreams",
|
||||
@@ -241,8 +274,9 @@
|
||||
"plain_dns": "DNS simplu",
|
||||
"form_enter_rate_limit": "Introduceți limita ratei",
|
||||
"rate_limit": "Limita ratei",
|
||||
"edns_enable": "Activați clientul subnet EDNS",
|
||||
"edns_cs_desc": "Dacă este activat, AdGuard Home va trimite subnet-ul clienților către serverele DNS.",
|
||||
"edns_enable": "Activați subrețeaua de clienți EDNS",
|
||||
"edns_cs_desc": "Trimite subrețelele clienților la serverele DNS.",
|
||||
"rate_limit_desc": "Numărul de interogări pe secundă permise pe client. Setarea la 0 înseamnă că nu există limită.",
|
||||
"blocking_ipv4_desc": "Adresa IP de returnat pentru o cerere A de blocare",
|
||||
"blocking_ipv6_desc": "Adresa IP de returnat pentru o cerere AAAA de blocare",
|
||||
"blocking_mode_default": "Implicit: Răspunde cu adresa IP (0.0.0.0 for A; :: pentru AAAA) când sunt blocate de regulă tip Adblock; răspunde cu adresa IP specificată în regulă când sunt blocate de regula tip /etc/hosts",
|
||||
@@ -265,6 +299,7 @@
|
||||
"install_settings_listen": "Interfață de ascultare",
|
||||
"install_settings_port": "Port",
|
||||
"install_settings_interface_link": "Interfața dvs. de administrare AdGuard Home va fi disponibilă pe următoarele adrese:",
|
||||
"form_error_port": "Introduceți un număr de port valid",
|
||||
"install_settings_dns": "Server DNS",
|
||||
"install_settings_dns_desc": "Va trebui să configurați aparatele sau routerul pentru a utiliza serverul DNS pe următoarele adrese:",
|
||||
"install_settings_all_interfaces": "Toate interfețele",
|
||||
@@ -279,12 +314,14 @@
|
||||
"install_devices_title": "Configurați aparatele dvs",
|
||||
"install_devices_desc": "Pentru a începe să utilizați AdGuard Home, trebuie să configurați aparatele.",
|
||||
"install_submit_title": "Felicitări!",
|
||||
"install_submit_desc": "Etapa de instalare este terminată și sunteți gata să începeți utilizarea AdGuard Home.",
|
||||
"install_submit_desc": "Procedura de configurare este finalizată și acum sunteți gata să începeți să utilizați AdGuard Home.",
|
||||
"install_devices_router": "Router",
|
||||
"install_devices_router_desc": "Această configurație va acoperi automat toate aparatele conectate la routerul de acasă și nu va trebui să le configurați manual pe fiecare.",
|
||||
"install_devices_router_desc": "Această configurare acoperă automat toate dispozitivele conectate la routerul de acasă, nu este nevoie să le configurați manual.",
|
||||
"install_devices_address": "Serverul DNS AdGuard Home ascultă pe următoarele adrese",
|
||||
"install_devices_router_list_1": "Deschideți preferințele routerului dvs. De obicei, îl puteți accesa din browser printr-o adresă URL cum ar fi http://192.168.0.1/ sau http://192.168.1.1/. Vi se poate cere să introduceți o parolă. Dacă nu v-o amintiți, adesea puteți reseta parola apăsând un buton de pe routerul propriu-zis, dar fiți conștienți, că prin acest procedeu puteți pierde întreaga configurație a routerului. \nDacă routerul dvs. necesită o aplicație pentru configurare, instalați aplicația pe telefon sau pe PC și utilizați-o pentru a accesa setările routerului.",
|
||||
"install_devices_router_list_2": "Găsiți setările DHCP/DNS. Căutați literele DNS lângă un câmp care să permită două sau trei seturi de numere, fiecare împărțit în patru grupuri de una până la trei cifre.",
|
||||
"install_devices_router_list_3": "Introduceți adresele serverului dvs. AdGuard Home aici.",
|
||||
"install_devices_router_list_4": "Unele tipuri de routere, nu permit configurarea unui server DNS personalizat. În acest caz, configurarea AdGuard Home ca un <0>server DHCP</0>vă poate ajuta. Dacă nu, ar trebui verificat manualul routerului dvs. specific, ca să aflați cum se pot personaliza serverele DNS.",
|
||||
"install_devices_windows_list_1": "Deschideți panoul de control prin meniul Start sau căutare Windows.",
|
||||
"install_devices_windows_list_2": "Accesați categoria \"Rețea și Internet\", apoi la \"Centrul de Rețea și Partajare\".",
|
||||
"install_devices_windows_list_3": "În partea stângă a ecranului găsiți \"Schimbare setări adaptor\" și clicați pe el.",
|
||||
@@ -299,7 +336,7 @@
|
||||
"install_devices_android_list_2": "Tapați Wi-Fi din meniu. Ecranul cu toate rețelele disponibile va fi afișat (este imposibil să setați DNS personalizat pentru conexiunea mobilă).",
|
||||
"install_devices_android_list_3": "Apăsați lung pe rețeaua la care sunteți conectat și tapați Modificare Rețea.",
|
||||
"install_devices_android_list_4": "Pe unele aparate, poate fi necesar să bifați caseta Advanced pentru a vedea setările adiționale. Pentru a ajusta setările DNS Android, va trebui să comutați setările IP de la DHCP la Static.",
|
||||
"install_devices_android_list_5": "Schimbați valorile DNS 1 și DNS 2 la cele ale serverului dvs. AdGuard Home.",
|
||||
"install_devices_android_list_5": "Schimbați valorile DNS 1 și DNS 2 la adresele serverului dvs. AdGuard Home.",
|
||||
"install_devices_ios_list_1": "Din ecranul de start, tapați Setări.",
|
||||
"install_devices_ios_list_2": "Alegeți Wi-Fi în meniul din stânga (este imposibil să configurați DNS pentru rețelele mobile).",
|
||||
"install_devices_ios_list_3": "Tapați numele rețelei active curente.",
|
||||
@@ -310,6 +347,7 @@
|
||||
"install_saved": "Salvat cu succes",
|
||||
"encryption_title": "Criptare",
|
||||
"encryption_desc": "Suport de Criptare (HTTPS/TLS) pentru DNS și interfața web administrator",
|
||||
"encryption_config_saved": "Configurația de criptare salvată",
|
||||
"encryption_server": "Nume de server",
|
||||
"encryption_server_enter": "Introduceți numele domeniului",
|
||||
"encryption_server_desc": "Pentru a utiliza HTTPS, trebuie să introduceți numele serverului care se potrivește cu certificatul SSL sau certificatul wildcard al dvs. În cazul în care câmpul nu este setat, va accepta conexiuni TLS pentru orice domeniu.",
|
||||
@@ -340,10 +378,13 @@
|
||||
"encryption_reset": "Sunteți sigur că doriți să resetați setările de criptare?",
|
||||
"topline_expiring_certificate": "Certificatul dvs. SSL este pe cale să expire. Actualizați <0>Setările de criptare</0>.",
|
||||
"topline_expired_certificate": "Certificatul dvs. SSL a expirat. Actualizați <0>Setările de criptare</0>.",
|
||||
"form_error_port_range": "Introduceți valoarea portului între 80-65535",
|
||||
"form_error_port_unsafe": "Acesta este un port nesigur",
|
||||
"form_error_equal": "Nu trebuie să fie egale",
|
||||
"form_error_password": "Parolele nu corespund",
|
||||
"reset_settings": "Resetare setări",
|
||||
"update_announcement": "AdGuard Home {{version}} este disponibil! <0>Clicați aici</0> pentru mai multe informații.",
|
||||
"setup_guide": "Ghid de instalare",
|
||||
"dns_addresses": "Adrese DNS",
|
||||
"dns_start": "Serverul DNS demarează",
|
||||
"dns_status_error": "Eroare la verificare statut server DNS",
|
||||
@@ -365,8 +406,9 @@
|
||||
"client_edit": "Editare client",
|
||||
"client_identifier": "Identificator",
|
||||
"ip_address": "Adresa IP",
|
||||
"client_identifier_desc": "Clienții pot fi identificați prin adresa IP, CIDR, adresa MAC sau un ID special al clientului (poate fi folosit pentru DoT/DoH/DoQ). <0>Aici</0> puteți afla mai multe despre cum să identificați clienții.",
|
||||
"client_identifier_desc": "Clienții pot fi identificați prin adresa IP, CIDR, adresa MAC sau un ID client special (poate fi folosit pentru DoT/DoH/DoQ). <0>Aici</0> puteți afla mai multe despre cum să identificați clienții.",
|
||||
"form_enter_ip": "Introduceți IP",
|
||||
"form_enter_subnet_ip": "Introduceți o adresă IP în subrețeaua „{{cidr}}”",
|
||||
"form_enter_mac": "Introduceți MAC",
|
||||
"form_enter_id": "Introduceți identificator",
|
||||
"form_add_id": "Adăugați identificator",
|
||||
@@ -384,10 +426,11 @@
|
||||
"access_title": "Setări de acces",
|
||||
"access_desc": "Aici puteți configura regulile de acces pentru serverul DNS AdGuard Home.",
|
||||
"access_allowed_title": "Clienți autorizați",
|
||||
"access_allowed_desc": "O listă de adrese CIDR sau IP. Dacă este configurat, AdGuard Home va accepta doar cereri de la aceste adrese IP.",
|
||||
"access_allowed_desc": "O listă de adrese CIDR sau IP client. Dacă este configurat, AdGuard Home va accepta cereri numai de la acești clienți.",
|
||||
"access_disallowed_title": "Clienți neautorizați",
|
||||
"access_disallowed_desc": "O listă de adrese CIDR sau IP. Dacă este configurat, AdGuard Home va elimina cererile de la aceste adrese IP.",
|
||||
"access_disallowed_desc": "O listă de adrese CIDR sau IP. Dacă este configurat, AdGuard Home va elimina cereri de la acești clienți. Dacă sunt configurați clienți permiși, acest câmp este ignorat.",
|
||||
"access_blocked_title": "Domenii blocate",
|
||||
"access_blocked_desc": "A nu se confunda cu filtrele. AdGuard Home respinge cererile DNS pentru aceste domenii, iar aceste cereri nici măcar nu apar în jurnalul de solicitări. Puteți specifica nume exacte de domenii, metacaractere sau reguli de filtrare URL, cum ar fi \"example.org\", \"*.exemple.org\" sau \"||example.org^\" în mod corespunzător.",
|
||||
"access_settings_saved": "Setările de acces au fost salvate cu succes",
|
||||
"updates_checked": "Actualizările au fost verificate cu succes",
|
||||
"updates_version_equal": "AdGuard Home este la zi",
|
||||
@@ -441,10 +484,12 @@
|
||||
"encryption_key_source_content": "Lipiți conținutul cheii private",
|
||||
"stats_params": "Configurația statisticilor",
|
||||
"config_successfully_saved": "Configurarea a fost salvată cu succes",
|
||||
"interval_6_hour": "6 ore",
|
||||
"interval_24_hour": "24 ore",
|
||||
"interval_days": "{{count}} zi",
|
||||
"interval_days_plural": "{{count}} zile",
|
||||
"domain": "Domeniu",
|
||||
"punycode": "Punycode",
|
||||
"answer": "Răspuns",
|
||||
"filter_added_successfully": "Filtrul a fost adăugat cu succes",
|
||||
"filter_removed_successfully": "Lista a fost eliminată cu succes",
|
||||
@@ -487,9 +532,10 @@
|
||||
"rewrite_domain_name": "Nume de domeniu: adăugați o înregistrare CNAME",
|
||||
"rewrite_A": "<0>A</0>: valoare specială, păstrați <0>A</0> înregistrări din amonte",
|
||||
"rewrite_AAAA": "<0>AAAA</0>: valoare specială, păstrați <0>AAAA</0> înregistrări din amonte",
|
||||
"disable_ipv6": "Dezactivați IPv6",
|
||||
"disable_ipv6_desc": "Dacă această opțiune este activată, toate interogările DNS pentru adrese IPv6 (tip AAAA) vor fi anulate.",
|
||||
"disable_ipv6": "Dezactivați rezolvarea adreselor IPv6",
|
||||
"disable_ipv6_desc": "Anulați toate interogările DNS pentru adresele IPv6 (tip AAAA).",
|
||||
"fastest_addr": "Cea mai rapidă adresă IP",
|
||||
"fastest_addr_desc": "Interoghează toate serverele DNS și întoarce adresa IP cea mai rapidă din răspunsuri. Acest lucru încetinește interogările DNS, deoarece AdGuard Home trebuie să aștepte răspunsuri de la toate serverele DNS, dar îmbunătățește conectivitatea generală.",
|
||||
"autofix_warning_text": "Dacă clicați pe \"Fix\", AdGuardHome va configura sistemul dvs. pentru a utiliza serverul DNS AdGuardHome.",
|
||||
"autofix_warning_list": "Va efectua aceste sarcini: <0>Dezactivare sistem DNSStubListener</0> <0>Setare adresă server DNS la 127.0.0.1</0> <0>Înlocuire link simbolic țintă /etc/resolv.conf cu /run/systemd/resolve/resolv.conf</0> <0>Oprire DNSStubListener (reîncărcare servici rezolvat prin sistem)</0>",
|
||||
"autofix_warning_result": "Ca urmare, toate cererile DNS ale sistemul dvs. vor fi procesate în mod implicit de AdGuardHome.",
|
||||
@@ -519,11 +565,12 @@
|
||||
"set_static_ip": "Setați o adresă IP statică",
|
||||
"install_static_ok": "Vești bune! Adresa IP statică este deja configurată",
|
||||
"install_static_error": "AdGuard Home nu o poate configura automat pentru această interfață de rețea. Vă rugăm să căutați instrucțiuni despre cum să faceți acest lucru manual.",
|
||||
"install_static_configure": "AdGuard Home a detectat că se folosește adresa IP dinamică <0>{{ip}}</0>. Doriți ca aceasta să fie setată ca adresă statică?",
|
||||
"confirm_static_ip": "AdGuard Home va configura {{ip}} ca adresa dvs. IP statică. Doriți să continuați?",
|
||||
"list_updated": "{{count}} listă actualizată",
|
||||
"list_updated_plural": "{{count}} liste actualizate",
|
||||
"dnssec_enable": "Activați DNSSEC",
|
||||
"dnssec_enable_desc": "Setați steagul DNSSEC pe interogările DNS de ieșire și verificați rezultatul (este necesar un resolver DNSSEC activat)",
|
||||
"dnssec_enable_desc": "Activați semnalul DNSSEC în interogările DNS de ieșire și verificați rezultatul (este necesar un resolver compatibil DNSSEC).",
|
||||
"validated_with_dnssec": "Validat cu DNSSEC",
|
||||
"all_queries": "Toate interogările",
|
||||
"show_blocked_responses": "Blocat",
|
||||
@@ -553,9 +600,10 @@
|
||||
"filter_category_regional": "Regional",
|
||||
"filter_category_other": "Altele",
|
||||
"filter_category_general_desc": "Liste care blochează urmărirea și publicitatea pe majoritatea aparatelor",
|
||||
"filter_category_security_desc": "Liste specializate în blocarea domeniilor malware, phishing sau înșelătorie",
|
||||
"filter_category_security_desc": "Listele concepute special pentru a bloca domenii rău intenționate, phishing și înșelătorie",
|
||||
"filter_category_regional_desc": "Liste focalizate pe reclame regionale și servere de urmărire",
|
||||
"filter_category_other_desc": "Alte liste de blocări",
|
||||
"setup_config_to_enable_dhcp_server": "Setați configurația pentru a activa serverul DHCP",
|
||||
"original_response": "Răspuns original",
|
||||
"click_to_view_queries": "Clicați pentru a vizualiza interogări",
|
||||
"port_53_faq_link": "Portul 53 este adesea ocupat de serviciile \"DNSStubListener\" sau \"systemd-resolved\". Vă rugăm să citiți <0>această instrucțiune</0> despre cum să rezolvați aceasta.",
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"upstream_parallel": "Использовать параллельные запросы ко всем серверам одновременно для ускорения обработки запроса.",
|
||||
"parallel_requests": "Параллельные запросы",
|
||||
"load_balancing": "Распределение нагрузки\n",
|
||||
"load_balancing_desc": "Запрашивайте по одному серверу за раз. AdGuard Home использует случайный алгоритм для выбора сервера, так что самый быстрый сервер используется чаще.",
|
||||
"load_balancing_desc": "Запрашивать по одному серверу за раз. AdGuard Home использует алгоритм взвешенного случайного выбора сервера, так что самый быстрый сервер используется чаще.",
|
||||
"bootstrap_dns": "Bootstrap DNS-серверы",
|
||||
"bootstrap_dns_desc": "Bootstrap DNS-серверы используются для поиска IP-адресов DoH/DoT серверов, которые вы указали.",
|
||||
"local_ptr_title": "Приватные серверы для обратного DNS",
|
||||
@@ -68,6 +68,9 @@
|
||||
"dhcp_new_static_lease": "Новая статическая аренда",
|
||||
"dhcp_static_leases_not_found": "Не найдено статических аренд DHCP",
|
||||
"dhcp_add_static_lease": "Добавить статическую аренду",
|
||||
"dhcp_reset_leases": "Сбросить все аренды",
|
||||
"dhcp_reset_leases_confirm": "Вы уверены, что хотите удалить все аренды?",
|
||||
"dhcp_reset_leases_success": "Аренды DHCP успешно удалены",
|
||||
"dhcp_reset": "Вы уверены, что хотите сбросить настройки DHCP?",
|
||||
"country": "Страна",
|
||||
"city": "Город",
|
||||
@@ -109,6 +112,8 @@
|
||||
"for_last_24_hours": "за 24 часа",
|
||||
"for_last_days": "за последний {{count}} день",
|
||||
"for_last_days_plural": "за последние {{count}} дней",
|
||||
"stats_disabled": "Статистика отключена. Вы можете включить её на <0>странице настроек</0>.",
|
||||
"stats_disabled_short": "Статистика отключена",
|
||||
"no_domains_found": "Домены не найдены",
|
||||
"requests_count": "Количество запросов",
|
||||
"top_blocked_domains": "Часто блокируемые домены",
|
||||
@@ -248,8 +253,8 @@
|
||||
"anonymize_client_ip": "Анонимизировать IP-адрес клиента",
|
||||
"anonymize_client_ip_desc": "Не сохранять полный IP-адрес клиента в журналах и статистике",
|
||||
"dns_config": "Настройки DNS-сервера",
|
||||
"dns_cache_config": "Настройка кэша DNS",
|
||||
"dns_cache_config_desc": "Здесь можно настроить кэш DNS",
|
||||
"dns_cache_config": "Настройка кеша DNS",
|
||||
"dns_cache_config_desc": "Здесь можно настроить кеш DNS",
|
||||
"blocking_mode": "Режим блокировки",
|
||||
"default": "Стандартный",
|
||||
"nxdomain": "NXDOMAIN",
|
||||
@@ -323,8 +328,8 @@
|
||||
"install_devices_windows_list_2": "Перейдите в «Сеть и интернет», а затем в «Центр управления сетями и общим доступом»",
|
||||
"install_devices_windows_list_3": "В левой стороне экрана найдите «Изменение параметров адаптера» и кликните по нему.",
|
||||
"install_devices_windows_list_4": "Выделите ваше активное подключение, затем кликните по нему правой клавишей мыши и выберите «Свойства».",
|
||||
"install_devices_windows_list_5": "Найдите в списке пункт «IP версии 4 (TCP/IP)», выделите его и затем снова нажмите «Свойства».",
|
||||
"install_devices_windows_list_6": "Выберите «Использовать следующие адреса DNS-серверов» и введите адрес AdGuard Home.",
|
||||
"install_devices_windows_list_5": "Найдите в списке пункт «IP версии 4 (TCP/IPv4)» (или «IP версии 6 (TCP/IPv6)» для IPv6), выделите его и затем снова нажмите «Свойства».",
|
||||
"install_devices_windows_list_6": "Выберите «Использовать следующие адреса DNS-серверов» и введите адреса серверов AdGuard Home.",
|
||||
"install_devices_macos_list_1": "Кликните по иконке Apple и перейдите в «Системные настройки».",
|
||||
"install_devices_macos_list_2": "Кликните по иконке «Сеть».",
|
||||
"install_devices_macos_list_3": "Выберите первое подключение в списке и нажмите кнопку «Дополнительно».",
|
||||
@@ -423,9 +428,9 @@
|
||||
"access_title": "Настройки доступа",
|
||||
"access_desc": "Здесь вы можете настроить правила доступа к DNS-серверу AdGuard Home.",
|
||||
"access_allowed_title": "Разрешённые клиенты",
|
||||
"access_allowed_desc": "Список CIDR- или IP-адресов. Если он настроен, AdGuard Home будет принимать запросы только с этих IP-адресов.",
|
||||
"access_allowed_desc": "Список CIDR, IP-адресов или ID клиентов. Если он настроен, AdGuard Home будет принимать запросы только от этих клиентов.",
|
||||
"access_disallowed_title": "Запрещённые клиенты",
|
||||
"access_disallowed_desc": "Список CIDR- или IP-адресов. Если он настроен, AdGuard Home будет игнорировать запросы с этих IP-адресов.",
|
||||
"access_disallowed_desc": "Список CIDR, IP-адресов или ID клиентов. Если он настроен, AdGuard Home будет игнорировать запросы от этих клиентов. Если настроены разрешённые клиенты, это поле игнорируется.",
|
||||
"access_blocked_title": "Неразрешённые домены",
|
||||
"access_blocked_desc": "Не путать с фильтрами. AdGuard Home будет игнорировать DNS-запросы с этими доменами. Здесь вы можете уточнить точные имена доменов, шаблоны, правила URL-фильтрации, например, «example.org», «*.example.org» или «||example.org».",
|
||||
"access_settings_saved": "Настройки доступа успешно сохранены",
|
||||
@@ -481,10 +486,12 @@
|
||||
"encryption_key_source_content": "Вставить содержимое закрытого ключа",
|
||||
"stats_params": "Конфигурация статистики",
|
||||
"config_successfully_saved": "Конфигурация успешно сохранена",
|
||||
"interval_6_hour": "6 часов",
|
||||
"interval_24_hour": "24 часа",
|
||||
"interval_days": "{{count}} день",
|
||||
"interval_days_plural": "{{count}} дней",
|
||||
"domain": "Домен",
|
||||
"punycode": "Punycode",
|
||||
"answer": "Ответ",
|
||||
"filter_added_successfully": "Список успешно добавлен",
|
||||
"filter_removed_successfully": "Список успешно удалён",
|
||||
@@ -589,7 +596,9 @@
|
||||
"enter_cache_ttl_max_override": "Введите максимальный TTL (в секундах)",
|
||||
"cache_ttl_min_override_desc": "Расширить короткие TTL-значения (в секундах), полученные с upstream-сервера при кешировании DNS-ответов",
|
||||
"cache_ttl_max_override_desc": "Установить максимальное TTL-значение (в секундах) для записей в DNS-кэше",
|
||||
"ttl_cache_validation": "Минимальное значение TTL кеша должно быть меньше или равно максимальному значению",
|
||||
"ttl_cache_validation": "Минимальное значение TTL-кеша должно быть меньше или равно максимальному значению",
|
||||
"cache_optimistic": "Оптимистичный",
|
||||
"cache_optimistic_desc": "AdGuard Home будет отвечать из кеша, даже если ответы в нём неактуальны, и попытается обновить их.",
|
||||
"filter_category_general": "Общие",
|
||||
"filter_category_security": "Безопасность",
|
||||
"filter_category_regional": "Региональные",
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"example_upstream_comment": "ඔබට අදහසක් සඳහන් කළ හැකිය",
|
||||
"parallel_requests": "සමාන්තර ඉල්ලීම්",
|
||||
"load_balancing": "ධාරිතාව තුලනය",
|
||||
"local_ptr_title": "පෞද්ගලික ව.නා.ප. සේවාදායකයන්",
|
||||
"local_ptr_title": "පෞද්ගලික ප්රතිවර්ත ව.නා.ප. සේවාදායකයන්",
|
||||
"local_ptr_placeholder": "පේළියකට එක් සේවාදායක ලිපිනය බැගින් යොදන්න",
|
||||
"resolve_clients_title": "අනුග්රාහකවල අ.ජා. කෙ. ලිපින ප්රතිවර්ත විසඳීම සබල කරන්න",
|
||||
"check_dhcp_servers": "ග.ධා.වි.කෙ. සේවාදායකයන් සඳහා පරීක්ෂා කරන්න",
|
||||
@@ -102,8 +102,8 @@
|
||||
"use_adguard_browsing_sec": "ඇඩ්ගාර්ඩ් පිරික්සුම් ආරක්ෂණ වියමන සේවාව භාවිතා කරන්න",
|
||||
"use_adguard_parental": "ඇඩ්ගාර්ඩ් දෙමාපිය පාලන වියමන සේවාව භාවිතා කරන්න",
|
||||
"use_adguard_parental_hint": "වසමේ වැඩිහිටියන්ට අදාල කරුණු අඩංගු දැයි ඇඩ්ගාර්ඩ් හෝම් විසින් පරීක්ෂා කරනු ඇත. එය පිරික්සුම් ආරක්ෂණ වියමන සේවාව මෙන් රහස්යතා හිතකාමී යෙ.ක්ර. අ.මු. (API) භාවිතා කරයි.",
|
||||
"enforce_safe_search": "ආරක්ෂිත සෙවීම බලාත්මක කරන්න",
|
||||
"enforce_save_search_hint": "ඇඩ්ගාර්ඩ් හෝම් හට පහත සෙවුම් යන්ත්ර තුළ ආරක්ෂිත සෙවීම බලාත්මක කළ හැකිය: ගූගල්, යූටියුබ්, බින්ග්, ඩක්ඩක්ගෝ, යාන්ඩෙක්ස් සහ පික්සාබේ.",
|
||||
"enforce_safe_search": "ආරක්ෂිත සෙවීම භාවිතා කරන්න",
|
||||
"enforce_save_search_hint": "ඇඩ්ගාර්ඩ් හෝම් පහත සෙවුම් යන්ත්ර තුළ ආරක්ෂිත සෙවීම බලාත්මක කරනු ඇත: ගූගල්, යූටියුබ්, බින්ග්, ඩක්ඩක්ගෝ, යාන්ඩෙක්ස් සහ පික්සාබේ.",
|
||||
"no_servers_specified": "සේවාදායක කිසිවක් නිශ්චිතව දක්වා නැත",
|
||||
"general_settings": "පොදු සැකසුම්",
|
||||
"dns_settings": "ව.නා.ප. සැකසුම්",
|
||||
@@ -267,9 +267,9 @@
|
||||
"install_devices_title": "ඔබගේ උපාංග වින්යාසගත කරන්න",
|
||||
"install_devices_desc": "ඇඩ්ගාර්ඩ් හෝම් භාවිතා කිරීම ආරම්භයට, ඔබගේ උපාංග එය පරිශ්රීලනයට වින්යාසගත කිරීම අවශ්ය වේ.",
|
||||
"install_submit_title": "සුභ පැතුම්!",
|
||||
"install_submit_desc": "පිහිටුවීමේ ක්රියා පටිපාටිය අවසන් වී ඇති අතර ඔබ ඇඩ්ගාර්ඩ් හෝම් භාවිතය ආරම්භ කිරීමට සූදානම්ය.",
|
||||
"install_submit_desc": "පිහිටුවීමේ ක්රියා පටිපාටිය අවසන් වී ඇති අතර ඔබ දැන් ඇඩ්ගාර්ඩ් හෝම් භාවිතය ආරම්භ කිරීමට සූදානම්ය.",
|
||||
"install_devices_router": "මාර්ගකාරකය",
|
||||
"install_devices_router_desc": "මෙම පිහිටුම ඔබගේ නිවසේ මාර්ගකාරකයට සම්බන්ධ සියලුම උපාංග ස්වයංක්රීයව ආවරණය කරන අතර ඔබට ඒවා අතින් වින්යාසගත කිරීමට අවශ්ය නොවනු ඇත.",
|
||||
"install_devices_router_desc": "මෙම පිහිටුම ඔබගේ නිවසේ මාර්ගකාරකයට සම්බන්ධ සියලුම උපාංග ස්වයංක්රීයව ආවරණය කරන අතර ඔබට ඒ සෑම එකක්ම අතින් වින්යාසගත කිරීමට අවශ්ය නොවේ.",
|
||||
"install_devices_address": "ඇඩ්ගාර්ඩ් හෝම් ව.නා.ප. සේවාදායකය පහත ලිපිනයන්ට සවන් දෙමින් පවතී",
|
||||
"install_devices_router_list_2": "ග.ධා.වි.කෙ. (DHCP)/ ව.නා.ප. (DNS) සැකසුම් සොයා ගන්න. ඉලක්කම් කට්ටල දෙකකට හෝ තුනකට ඉඩ දෙන ක්ෂේත්රයක් අසල ඇති ව.නා.ප. අක්ෂර සොයන්න, සෑම එකක්ම ඉලක්කම් එකේ සිට තුන දක්වා කාණ්ඩ හතරකට බෙදා ඇත.",
|
||||
"install_devices_router_list_3": "ඔබගේ ඇඩ්ගාර්ඩ් හෝම් සේවාදායක ලිපින එහි ඇතුළත් කරන්න.",
|
||||
@@ -420,6 +420,7 @@
|
||||
"encryption_key_source_content": "පුද්ගලික යතුරෙහි අන්තර්ගත අලවන්න",
|
||||
"stats_params": "සංඛ්යාලේඛන වින්යාසය",
|
||||
"config_successfully_saved": "වින්යාසය සාර්ථකව සුරකින ලදි",
|
||||
"interval_6_hour": "පැය 6",
|
||||
"interval_24_hour": "පැය 24",
|
||||
"interval_days": "{{count}} දිනය",
|
||||
"interval_days_plural": "දින {{count}}",
|
||||
|
||||
@@ -68,6 +68,9 @@
|
||||
"dhcp_new_static_lease": "Nový statický prenájom",
|
||||
"dhcp_static_leases_not_found": "Nebol nájdený žiadny statický DHCP prenájom",
|
||||
"dhcp_add_static_lease": "Pridať statický prenájom",
|
||||
"dhcp_reset_leases": "Resetovať všetky prenájmy",
|
||||
"dhcp_reset_leases_confirm": "Naozaj chcete resetovať všetky prenájmy?",
|
||||
"dhcp_reset_leases_success": "DHCP prenájmy boli úspešne resetované",
|
||||
"dhcp_reset": "Naozaj chcete vymazať DHCP konfiguráciu?",
|
||||
"country": "Krajina",
|
||||
"city": "Mesto",
|
||||
@@ -109,6 +112,8 @@
|
||||
"for_last_24_hours": "za posledných 24 hodín",
|
||||
"for_last_days": "za posledný {{count}} deň",
|
||||
"for_last_days_plural": "za posledných {{count}} dní",
|
||||
"stats_disabled": "Štatistiky boli vypnuté. Môžete ich zapnúť na <0>stránke nastavení</0>.",
|
||||
"stats_disabled_short": "Štatistiky boli vypnuté",
|
||||
"no_domains_found": "Žiadna doména nebola nájdená",
|
||||
"requests_count": "Počet dopytov",
|
||||
"top_blocked_domains": "Najčastejšie zablokované domény",
|
||||
@@ -423,9 +428,9 @@
|
||||
"access_title": "Nastavenia prístupu",
|
||||
"access_desc": "Tu môžete konfigurovať pravidlá prístupu pre server DNS AdGuard Home.",
|
||||
"access_allowed_title": "Povolení klienti",
|
||||
"access_allowed_desc": "Zoznam CIDR alebo IP adries. Ak je nakonfigurovaný, AdGuard Home akceptuje len dopyty z týchto IP adries",
|
||||
"access_allowed_desc": "Zoznam CIDR, IP adries alebo ID klientov. Ak je nakonfigurovaný, AdGuard Home akceptuje len dopyty od týchto klientov.",
|
||||
"access_disallowed_title": "Nepovolení klienti",
|
||||
"access_disallowed_desc": "Zoznam CIDR alebo IP adries. Ak je nakonfigurovaný, AdGuard Home bude ignorovať dopyty z týchto IP adries",
|
||||
"access_disallowed_desc": "Zoznam CIDR, IP adries alebo ID klientov. Ak je nakonfigurovaný, AdGuard Home bude ignorovať dopyty od týchto klientov.",
|
||||
"access_blocked_title": "Nepovolené domény",
|
||||
"access_blocked_desc": "Nesmie byť zamieňaná s filtrami. AdGuard Home zruší DNS dopyty, ktoré sa zhodujú s týmito doménami, a tieto dopyty sa nezobrazia ani v denníku dopytov. Môžete určiť presné názvy domén, zástupné znaky alebo pravidlá filtrovania URL adries, napr. \"example.org\", \"*.example.org\" alebo ||example.org^\" zodpovedajúcim spôsobom.",
|
||||
"access_settings_saved": "Nastavenia prístupu úspešne uložené",
|
||||
@@ -481,10 +486,12 @@
|
||||
"encryption_key_source_content": "Vložte obsah privátneho kľúča",
|
||||
"stats_params": "Konfigurácia štatistiky",
|
||||
"config_successfully_saved": "Konfigurácia bola úspešne uložená",
|
||||
"interval_6_hour": "6 hodín",
|
||||
"interval_24_hour": "24 hodín",
|
||||
"interval_days": "{{count}} deň",
|
||||
"interval_days_plural": "{{count}} dní",
|
||||
"domain": "Doména",
|
||||
"punycode": "Punycode",
|
||||
"answer": "Odpoveď",
|
||||
"filter_added_successfully": "Filter bol úspešne pridaný",
|
||||
"filter_removed_successfully": "Zoznam bol úspešne odstránený",
|
||||
@@ -590,6 +597,8 @@
|
||||
"cache_ttl_min_override_desc": "Predĺži krátke hodnoty TTL (v sekundách) prijaté od servera typu upstream pri ukladaní odpovedí DNS do cache pamäte",
|
||||
"cache_ttl_max_override_desc": "Nastaví maximálnu hodnotu TTL (v sekundách) pre záznamy v DNS cache pamäti",
|
||||
"ttl_cache_validation": "Minimálna hodnota TTL cache musí byť menšia alebo rovná maximálnej hodnote",
|
||||
"cache_optimistic": "Optimistické nastavenie",
|
||||
"cache_optimistic_desc": "Nechajte AdGuard Home odpovedať z vyrovnávacej pamäte, aj keď už platnosť položiek skončila, a tiež sa pokúste ich obnoviť.",
|
||||
"filter_category_general": "Všeobecné",
|
||||
"filter_category_security": "Bezpečnosť",
|
||||
"filter_category_regional": "Regionálne",
|
||||
|
||||
@@ -68,6 +68,9 @@
|
||||
"dhcp_new_static_lease": "Nov statični najem",
|
||||
"dhcp_static_leases_not_found": "Ni najdenih statičnih najemov DHCP",
|
||||
"dhcp_add_static_lease": "Dodaj statičen najem",
|
||||
"dhcp_reset_leases": "Ponastavi vse najeme",
|
||||
"dhcp_reset_leases_confirm": "Ali ste prepričani, da želite ponastaviti vse najeme?",
|
||||
"dhcp_reset_leases_success": "Najemi DHCP so bili uspešno ponastavljeni",
|
||||
"dhcp_reset": "Ali ste prepričani, da želite ponastaviti nastavitve DHCP?",
|
||||
"country": "Dežela",
|
||||
"city": "Mesto",
|
||||
@@ -109,6 +112,8 @@
|
||||
"for_last_24_hours": "v zadnjih 24 urah",
|
||||
"for_last_days": "zadnjega {{count}} dne",
|
||||
"for_last_days_plural": "zadnjih {{count}} dni",
|
||||
"stats_disabled": "Statistika je onemogočena. Vklopite ga lahko na <0>strani z nastavitvami</0>.",
|
||||
"stats_disabled_short": "Statistika je bila onemogočena",
|
||||
"no_domains_found": "Ni najdenih domen",
|
||||
"requests_count": "Štavilo zahtev",
|
||||
"top_blocked_domains": "Najbolj zavirane domene",
|
||||
@@ -323,7 +328,7 @@
|
||||
"install_devices_windows_list_2": "Pojdite v 'Omrežje' in 'Kategorija interneta' in nato v 'Omrežje' in 'Središče za skupno rabo'.",
|
||||
"install_devices_windows_list_3": "Na levi strani zaslona poiščite 'Spremeni nastavitve kartice' in kliknite nanjo.",
|
||||
"install_devices_windows_list_4": "Izberite aktivno povezavo, kliknite na njo z desno miškino tipko in izberite 'Lastnosti'.",
|
||||
"install_devices_windows_list_5": "Na seznamu poiščite različico 4 (TCP/IP) internetnega protokola, jo izberite in znova kliknite na 'Lastnosti'.",
|
||||
"install_devices_windows_list_5": "Na seznamu poiščite 'Internet protokol različica 4 (TCP/IPv4)' (ali, za IPv6, 'Internet protokol različica 6 (TCP/IPv6)'), jo izberite in nato še enkrat kliknite 'Lastnosti'.",
|
||||
"install_devices_windows_list_6": "Izberite 'Uporabi naslednje naslove DNS strežnikov' in vnesite vaše naslove strežnika AdGuard Home.",
|
||||
"install_devices_macos_list_1": "Kliknite ikono Apple in pojdite na 'Nastavitve sistema'.",
|
||||
"install_devices_macos_list_2": "Kliknite na 'Omrežje'",
|
||||
@@ -423,9 +428,9 @@
|
||||
"access_title": "Nastavitve dostopa",
|
||||
"access_desc": "Tukaj lahko nastavite pravila dostopa strežnika DNS AdGuard Home.",
|
||||
"access_allowed_title": "Dovoljeni odjemalci",
|
||||
"access_allowed_desc": "Seznam naslovov CIDR ali IP. Če je konfiguriran, bo AdGuard Home sprejel zahteve samo od teh teh IP naslovov.",
|
||||
"access_allowed_desc": "Seznam naslovov CIDR ali IP. Če je nastavljen, bo AdGuard Home sprejel zahteve samo od teh teh naslovov IP.",
|
||||
"access_disallowed_title": "Zavrnjeni odjemalci",
|
||||
"access_disallowed_desc": "Seznam naslovov CIDR ali IP. Če je konfiguriran, bo AdGuard Home spustil zahteve iz teh IP naslovov.",
|
||||
"access_disallowed_desc": "Seznam naslovov CIDR ali IP. Če je nastavljen, bo AdGuard Home spustil zahteve iz teh naslovov IP.",
|
||||
"access_blocked_title": "Prepovedane domene",
|
||||
"access_blocked_desc": "Ne gre zamenjati s filtri. AdGuard Home spusti poizvedbe DNS, ki se ujemajo s temi domenami, in te poizvedbe se niti ne pojavijo v dnevniku poizvedb. Določite lahko natančna imena domen, nadomestne znake ali pravila filtriranja URL-jev, npr. ustrezno \"example.org\", \"*.example.org\" ali \"|| example.org ^\".",
|
||||
"access_settings_saved": "Nastavitve dostopa so uspešno shranjene",
|
||||
@@ -481,10 +486,12 @@
|
||||
"encryption_key_source_content": "Prilepi vsebino zasebnega ključa",
|
||||
"stats_params": "Nastavitve statistike",
|
||||
"config_successfully_saved": "Nastavitve so uspešno shranjene",
|
||||
"interval_6_hour": "6 ur",
|
||||
"interval_24_hour": "24 ur",
|
||||
"interval_days": "{{count}} dan",
|
||||
"interval_days_plural": "{{count}} dni",
|
||||
"domain": "Domena",
|
||||
"punycode": "Slaba koda",
|
||||
"answer": "Odgovor",
|
||||
"filter_added_successfully": "Seznam je bil uspešno dodan",
|
||||
"filter_removed_successfully": "Seznam je bil uspešno odstranjen",
|
||||
@@ -590,6 +597,8 @@
|
||||
"cache_ttl_min_override_desc": "Razširite kratke vrednosti časa v živo (v sekundah), ki jih prejme strežnik za predpomnjenje, ko predpomni odzive DNS",
|
||||
"cache_ttl_max_override_desc": "Nastavi največjo vrednost časa v živo (v sekundah) za vnose v predpomnilnik DNS",
|
||||
"ttl_cache_validation": "Najmanjša vrednost predpomnilnika TTL mora biti manjša ali enaka največji vrednosti",
|
||||
"cache_optimistic": "Optimistično",
|
||||
"cache_optimistic_desc": "Poskrbi, da se AdGuard Home odzove iz predpomnilnika, tudi ko vnosi potečejo, in jih tudi poskusi osvežiti.",
|
||||
"filter_category_general": "Splošno",
|
||||
"filter_category_security": "Varnost",
|
||||
"filter_category_regional": "Področno",
|
||||
|
||||
@@ -5,18 +5,18 @@
|
||||
"upstream_parallel": "Tüm üst sunucuları eş zamanlı sorgulayarak çözümlemeyi hızlandırmak için paralel sorgular kullanın.",
|
||||
"parallel_requests": "Paralel istekler",
|
||||
"load_balancing": "Yük dengeleme",
|
||||
"load_balancing_desc": "Bir seferde bir üst sunucusunu sorgulayın. AdGuard Home, sunucuyu seçmek için ağırlıklı rastgele algoritmasını kullanır, böylece en hızlı sunucu daha sık kullanılır.",
|
||||
"load_balancing_desc": "Her seferde bir üst sunucuyu sorgulayın. AdGuard Home, sunucuyu seçmek için ağırlıklı rastgele algoritmasını kullanır, böylece en hızlı sunucu daha sık kullanılır.",
|
||||
"bootstrap_dns": "DNS Önyükleme sunucuları",
|
||||
"bootstrap_dns_desc": "DNS Önyükleme sunucuları, seçtiğiniz üst sunucuların DoH/DoT çözücülerine ait ip adreslerinin çözülmesi için kullanılır.",
|
||||
"bootstrap_dns_desc": "DNS Önyükleme sunucuları, belirttiğiniz üst sunucuların DoH/DoT çözücülerine ait IP adreslerinin çözülmesi için kullanılır.",
|
||||
"local_ptr_title": "Özel ters DNS sunucuları",
|
||||
"local_ptr_desc": "AdGuard Home'un yerel PTR sorguları için kullandığı DNS sunucuları. Bu sunucular, rDNS kullanarak \"192.168.12.34\" gibi özel IP adreslerine sahip istemcilerin ana bilgisayar adlarını çözmek için kullanılır. Ayarlanmazsa, AdGuard Home işletim sisteminizin varsayılan DNS çözümlerini kullanır.",
|
||||
"local_ptr_desc": "AdGuard Home'un yerel PTR sorguları için kullandığı DNS sunucuları. Bu sunucular, rDNS kullanarak \"192.168.12.34\" gibi özel IP adreslerine sahip istemcilerin ana bilgisayar adlarını çözmek için kullanılır. Ayarlanmadığı durumda AdGuard Home, işletim sisteminizin varsayılan DNS çözümleme adreslerini kullanır.",
|
||||
"local_ptr_default_resolver": "AdGuard Home, varsayılan olarak aşağıdaki ters DNS çözümleyicilerini kullanır: {{ip}}.",
|
||||
"local_ptr_no_default_resolver": "AdGuard Home, bu sistem için uygun özel ters DNS çözümleyicilerini belirleyemedi.",
|
||||
"local_ptr_no_default_resolver": "AdGuard Home, bu sistem için uygun olan özel ters DNS çözümleyicilerini belirleyemedi.",
|
||||
"local_ptr_placeholder": "Her satıra bir sunucu adresi girin",
|
||||
"resolve_clients_title": "İstemcilerin IP adreslerinin ters çözümlenmesini etkinleştir",
|
||||
"resolve_clients_desc": "Etkinleştirilirse, AdGuard Home ilgili çözümleyicilere (yerel istemciler için özel DNS sunucuları, genel IP adreslerine sahip istemciler için üst sunucusu) PTR sorguları göndererek istemcilerin IP adreslerini ana bilgisayar adlarına ters olarak çözümlemeye çalışır.",
|
||||
"use_private_ptr_resolvers_title": "Özel DNS çözümleyicileri kullan",
|
||||
"use_private_ptr_resolvers_desc": "Bu üst kaynak sunucularını kullanarak yerel olarak sunulan adresler için ters DNS aramaları gerçekleştirin. Devre dışı bırakılırsa, AdGuard Home, DHCP, /etc/hosts, vb. tarafından bilinen istemciler dışında bu tür tüm PTR isteklerine NXDOMAIN ile yanıt verir.",
|
||||
"resolve_clients_desc": "Karşılık gelen çözümleyicilere (yerel istemciler için özel DNS sunucuları, genel IP adresleri olan istemciler için üst sunucuları) PTR sorguları göndererek istemcilerin IP adreslerini ana bilgisayar adlarının tersine çözün.",
|
||||
"use_private_ptr_resolvers_title": "Özel ters DNS çözümleyicileri kullan",
|
||||
"use_private_ptr_resolvers_desc": "Bu üst kaynak sunucularını kullanarak yerel olarak sunulan adresler için ters DNS aramaları yapın. Devre dışı bırakılırsa, AdGuard Home, DHCP, /etc/hosts, vb. tarafından bilinen istemciler dışında bu tür tüm PTR isteklerine NXDOMAIN ile yanıt verir.",
|
||||
"check_dhcp_servers": "DHCP sunucularını denetle",
|
||||
"save_config": "Yapılandırmayı kaydet",
|
||||
"enabled_dhcp": "DHCP sunucusu etkinleştirildi",
|
||||
@@ -24,11 +24,11 @@
|
||||
"unavailable_dhcp": "DHCP kullanılamıyor",
|
||||
"unavailable_dhcp_desc": "AdGuard Home, işletim sisteminizde DHCP sunucusu çalıştıramıyor",
|
||||
"dhcp_title": "DHCP sunucusu (deneysel!)",
|
||||
"dhcp_description": "Yönlendiriciniz DHCP ayarlarını sağlamıyorsa, AdGuard'ın kendi yerleşik DHCP sunucusunu kullanabilirsiniz.",
|
||||
"dhcp_description": "Yönlendiriciniz DHCP ayarlarını sağlamıyorsa, AdGuard'ın yerleşik DHCP sunucusunu kullanabilirsiniz.",
|
||||
"dhcp_enable": "DHCP sunucusunu etkinleştir",
|
||||
"dhcp_disable": "DHCP sunucusunu devre dışı bırak",
|
||||
"dhcp_not_found": "Yerleşik DHCP sunucusunu etkinleştirmek güvenlidir, çünkü AdGuard Home ağ üzerinde herhangi bir etkin DHCP sunucusu bulmamıştır. Ancak, otomatik deneme şu anda %100 garanti sağlamadığından, bunu elle yeniden kontrol etmelisiniz.",
|
||||
"dhcp_found": "Ağ üzerinde bazı aktif DHCP sunucuları bulundu. Yerleşik DHCP sunucusunu aktif etmek sağlıklı olmayacaktır.",
|
||||
"dhcp_not_found": "AdGuard Home, ağda herhangi bir aktif DHCP sunucusu bulamadığı için yerleşik DHCP sunucusunu etkinleştirmek güvenlidir. Ancak, otomatik ayarlama şu anda %100 garanti sağlamadığından bunu elle yeniden kontrol etmelisiniz.",
|
||||
"dhcp_found": "Ağ üzerinde aktif bir DHCP sunucusu bulundu. Yerleşik DHCP sunucusunu etkinleştirmek güvenli olmayacaktır.",
|
||||
"dhcp_leases": "DHCP kiralamaları",
|
||||
"dhcp_static_leases": "Sabit DHCP kiralamaları",
|
||||
"dhcp_leases_not_found": "DHCP kiralaması bulunamadı",
|
||||
@@ -43,12 +43,12 @@
|
||||
"form_error_client_id_format": "Geçersiz istemci kimliği biçimi",
|
||||
"form_error_server_name": "Geçersiz sunucu adı",
|
||||
"form_error_subnet": "\"{{cidr}}\" alt ağı, \"{{ip}}\" IP adresini içermiyor",
|
||||
"form_error_positive": "0'dan büyük olmalı",
|
||||
"form_error_positive": "0'dan büyük olmalıdır",
|
||||
"form_error_negative": "0 veya daha büyük olmalıdır",
|
||||
"range_end_error": "Başlangıç aralığından daha büyük olmalı",
|
||||
"dhcp_form_gateway_input": "Ağ Geçidi IP'si",
|
||||
"dhcp_form_subnet_input": "Alt Ağ Maskesi",
|
||||
"dhcp_form_range_title": "IP adres aralığı",
|
||||
"dhcp_form_subnet_input": "Alt ağ maskesi",
|
||||
"dhcp_form_range_title": "IP adresi aralığı",
|
||||
"dhcp_form_range_start": "Aralık başlangıcı",
|
||||
"dhcp_form_range_end": "Aralık sonu",
|
||||
"dhcp_form_lease_title": "DHCP kira süresi (saniye olarak)",
|
||||
@@ -57,34 +57,37 @@
|
||||
"dhcp_hardware_address": "Donanım adresi",
|
||||
"dhcp_ip_addresses": "IP adresleri",
|
||||
"ip": "IP",
|
||||
"dhcp_table_hostname": "Ana bilgisayar Adı",
|
||||
"dhcp_table_hostname": "Bilgisayar Adı",
|
||||
"dhcp_table_expires": "Geçerlilik Tarihi",
|
||||
"dhcp_warning": "DHCP sunucusunu yine de etkinleştirmek istiyorsanız, ağınızda başka aktif DHCP sunucusu olmadığından emin olun çünkü bu, ağdaki cihazların İnternet bağlantısını kesebilir!",
|
||||
"dhcp_warning": "DHCP sunucusunu yine de etkinleştirmek istiyorsanız, ağınızda başka aktif DHCP sunucusu olmadığından emin olun, aksi takdirde ağa bağlı cihazların İnternet bağlantısı kesilebilir!",
|
||||
"dhcp_error": "AdGuard Home, ağda başka bir etkin DHCP sunucusu olup olmadığını belirleyemedi.",
|
||||
"dhcp_static_ip_error": "DHCP sunucusunu kullanmak için statik bir IP adresi ayarlanmalıdır. AdGuard Home, bu ağ arayüzünün statik bir IP adresi kullanılarak yapılandırılıp yapılandırılmadığını belirleyemedi. Lütfen elle statik bir IP adresi ayarlayın.",
|
||||
"dhcp_dynamic_ip_found": "Sisteminiz <0>{{interfaceName}}</0> arayüzü için dinamik IP adresi yapılandırması kullanıyor. DHCP sunucusunu kullanmak için statik bir IP adresi ayarlanmalı. Geçerli IP adresiniz <0>{{ipAddress}}</0>. \"DHCP sunucusunu etkinleştir\" düğmesine basarsanız, AdGuard Home bu IP adresini otomatikman statik olarak ayarlayacak.",
|
||||
"dhcp_static_ip_error": "DHCP sunucusunu kullanmak için sabit bir IP adresi ayarlanmalıdır. AdGuard Home, bu ağ arayüzünün sabit bir IP adresi kullanılarak yapılandırılıp yapılandırılmadığını belirleyemedi. Lütfen sabit IP adresini elle ayarlayın.",
|
||||
"dhcp_dynamic_ip_found": "Sisteminiz, <0>{{interfaceName}}</0> arayüzü için dinamik IP adresi yapılandırması kullanıyor. DHCP sunucusunu kullanmak için sabit bir IP adresi ayarlanmalıdır. Geçerli olan IP adresiniz <0>{{ipAddress}}</0>. \"DHCP sunucusunu etkinleştir\" düğmesine basarsanız, AdGuard Home bu IP adresini otomatik bir şekilde sabit olarak ayarlayacaktır.",
|
||||
"dhcp_lease_added": "Sabit kiralama \"{{key}}\" başarıyla eklendi",
|
||||
"dhcp_lease_deleted": "Sabit kiralama \"{{key}}\" başarıyla silindi",
|
||||
"dhcp_new_static_lease": "Yeni sabit kiralama",
|
||||
"dhcp_static_leases_not_found": "Sabit DHCP kiralaması bulunamadı",
|
||||
"dhcp_add_static_lease": "Sabit kiralama ekle",
|
||||
"dhcp_reset_leases": "Tüm kiralamaları sıfırla",
|
||||
"dhcp_reset_leases_confirm": "Tüm kiralamaları sıfırlamak istediğinizden emin misiniz?",
|
||||
"dhcp_reset_leases_success": "DHCP kiralamaları başarıyla sıfırlandı",
|
||||
"dhcp_reset": "DHCP yapılandırmasını sıfırlamak istediğinizden emin misiniz?",
|
||||
"country": "Ülke",
|
||||
"city": "Şehir",
|
||||
"delete_confirm": "\"{{key}}\" silmek istediğinizden emin misiniz?",
|
||||
"delete_confirm": "\"{{key}}\" öğesini silmek istediğinizden emin misiniz?",
|
||||
"form_enter_hostname": "Ana bilgisayar adı girin",
|
||||
"error_details": "Hata detayları",
|
||||
"error_details": "Hata ayrıntıları",
|
||||
"response_details": "Yanıt ayrıntıları",
|
||||
"request_details": "İstek ayrıntıları",
|
||||
"client_details": "İstemci detayları",
|
||||
"details": "Detaylar",
|
||||
"client_details": "İstemci ayrıntıları",
|
||||
"details": "Ayrıntılar",
|
||||
"back": "Geri",
|
||||
"dashboard": "Pano",
|
||||
"dashboard": "Ana Sayfa",
|
||||
"settings": "Ayarlar",
|
||||
"filters": "Filtreler",
|
||||
"filter": "Filtre",
|
||||
"query_log": "Sorgu Günlüğü",
|
||||
"compact": "Kompakt",
|
||||
"compact": "Yoğun",
|
||||
"nothing_found": "Hiçbir şey bulunamadı",
|
||||
"faq": "SSS",
|
||||
"version": "Sürüm",
|
||||
@@ -95,20 +98,22 @@
|
||||
"copyright": "Telif hakkı",
|
||||
"homepage": "Ana sayfa",
|
||||
"report_an_issue": "Bir sorun bildir",
|
||||
"privacy_policy": "Gizlilik politikası",
|
||||
"privacy_policy": "Gizlilik ilkesi",
|
||||
"enable_protection": "Korumayı etkinleştir",
|
||||
"enabled_protection": "Koruma etkileştirildi",
|
||||
"disable_protection": "Korumayı durdur",
|
||||
"disabled_protection": "Koruma devre dışı",
|
||||
"disabled_protection": "Koruma durduruldu",
|
||||
"refresh_statics": "İstatistikleri yenile",
|
||||
"dns_query": "DNS Sorguları",
|
||||
"blocked_by": "<0>Filtreler tarafından engellendi</0>",
|
||||
"stats_malware_phishing": "Kötü amaçlı yazılım/kimlik avı engellendi",
|
||||
"stats_malware_phishing": "Kötü amaçlı yazılım ve kimlik avı engellendi",
|
||||
"stats_adult": "Yetişkin içerikli site engellendi",
|
||||
"stats_query_domain": "En fazla sorgulanan alan adları",
|
||||
"for_last_24_hours": "son 24 saat içindekiler",
|
||||
"for_last_days": "son {{count}} gün için",
|
||||
"for_last_days": "son {{count}} gün boyunca",
|
||||
"for_last_days_plural": "son {{count}} gün boyunca",
|
||||
"stats_disabled": "İstatistikler devre dışı bırakıldı. Bunu, <0>ayarlar sayfasından</0> etkinleştirebilirsiniz.",
|
||||
"stats_disabled_short": "İstatistikler devre dışı bırakıldı",
|
||||
"no_domains_found": "Alan adı bulunamadı",
|
||||
"requests_count": "İstek sayısı",
|
||||
"top_blocked_domains": "En fazla engellenen alan adları",
|
||||
@@ -118,28 +123,28 @@
|
||||
"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": "Adblock filtreleri 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": "Engellenen yetişkin sitelerinin sayısı",
|
||||
"enforced_save_search": "Zorunlu kılınmış güvenli arama",
|
||||
"number_of_dns_query_to_safe_search": "Güvenli Aramanın zorunlu kıldığı arama motorlarına gönderilen DNS isteklerinin sayısı",
|
||||
"number_of_dns_query_blocked_24_hours": "Reklam engelleme filtreleri ve 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 engellenen DNS isteklerinin sayısı",
|
||||
"number_of_dns_query_blocked_24_hours_adult": "Engellenen yetişkin içerikli sitelerin sayısı",
|
||||
"enforced_save_search": "Uygulanan güvenli arama",
|
||||
"number_of_dns_query_to_safe_search": "Güvenli Aramanın uygulandığı arama motorlarına gönderilen DNS isteklerinin sayısı",
|
||||
"average_processing_time": "Ortalama işlem süresi",
|
||||
"average_processing_time_hint": "Bir DNS isteğinin mili saniye cinsinden ortalama işlem süresi",
|
||||
"block_domain_use_filters_and_hosts": "Filtre ve ana bilgisayar listelerini kullanarak alan adlarını engelle",
|
||||
"filters_block_toggle_hint": "<a>Filtreler</a> sayfasından engelleme kurallarını ayarlayabilirsiniz.",
|
||||
"use_adguard_browsing_sec": "AdGuard gezinti koruması web hizmetini kullan",
|
||||
"use_adguard_browsing_sec_hint": "AdGuard Home, alan adının gezinti koruması web hizmetinde kara listede olup olmadığını kontrol edecek. Kontrol işlemi gizlilik dostu API kullanılarak yapılacak: yalnızca alan adının kısa bir ön eki SHA256 ile şifrelenip sunucuya gönderilecek.",
|
||||
"use_adguard_browsing_sec_hint": "AdGuard Home, alan adının gezinti koruması web hizmeti tarafından engellenip engellenmediğini kontrol eder. Kontrolü gerçekleştirmek için gizlilik dostu arama API'sini kullanır: sunucuya yalnızca SHA256 karma alan adının kısa bir ön eki gönderilir.",
|
||||
"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.",
|
||||
"use_adguard_parental_hint": "AdGuard Home, alan adının yetişkin içerik bulundurup bulundurmadığını kontrol eder. Gezinti koruması web hizmeti ile kullandığımız aynı gizlilik dostu API'yi kullanır.",
|
||||
"enforce_safe_search": "Güvenli aramayı kullan",
|
||||
"enforce_save_search_hint": "AdGuard Home şu arama motorlarında güvenli aramayı zorunlu kılabilir: Google, YouTube, Bing, DuckDuckGo, Yandex ve Pixabay.",
|
||||
"no_servers_specified": "Sunucu adresi girilmedi",
|
||||
"enforce_save_search_hint": "AdGuard Home, şu arama motorlarında güvenli aramayı uygular: Google, YouTube, Bing, DuckDuckGo, Yandex ve Pixabay.",
|
||||
"no_servers_specified": "Sunucu belirtilmedi",
|
||||
"general_settings": "Genel ayarlar",
|
||||
"dns_settings": "DNS ayarları",
|
||||
"dns_blocklists": "DNS engelleme listeleri",
|
||||
"dns_allowlists": "DNS izin verilen listeleri",
|
||||
"dns_blocklists_desc": "AdGuard Home, engelleme listeleriyle eşleşen alanları engeller.",
|
||||
"dns_allowlists_desc": "DNS izin verilen listelerindeki alanlara, engelleme listelerinden birinde olsalar bile izin verilir.",
|
||||
"dns_allowlists": "DNS izin listeleri",
|
||||
"dns_blocklists_desc": "AdGuard Home, engelleme listeleriyle eşleşen alan adlarını engeller.",
|
||||
"dns_allowlists_desc": "DNS izin listesindeki alan adlarına, engelleme listesinde olsa bile izin verilir.",
|
||||
"custom_filtering_rules": "Özel filtreleme kuralları",
|
||||
"encryption_settings": "Şifreleme ayarları",
|
||||
"dhcp_settings": "DHCP ayarları",
|
||||
@@ -150,13 +155,13 @@
|
||||
"upstreams": "Üst kaynak",
|
||||
"apply_btn": "Uygula",
|
||||
"disabled_filtering_toast": "Filtreleme devre dışı",
|
||||
"enabled_filtering_toast": "Filtreleme çalışıyor",
|
||||
"enabled_filtering_toast": "Filtreleme etkin",
|
||||
"disabled_safe_browsing_toast": "Güvenli gezinti devre dışı",
|
||||
"enabled_safe_browsing_toast": "Güvenli gezinti çalışıyor",
|
||||
"enabled_safe_browsing_toast": "Güvenli gezinti etkin",
|
||||
"disabled_parental_toast": "Ebeveyn kontrolü devre dışı",
|
||||
"enabled_parental_toast": "Ebeveyn kontrolü çalışıyor",
|
||||
"enabled_parental_toast": "Ebeveyn kontrolü etkin",
|
||||
"disabled_safe_search_toast": "Güvenli arama devre dışı",
|
||||
"enabled_save_search_toast": "Güvenli arama çalışıyor",
|
||||
"enabled_save_search_toast": "Güvenli arama etkin",
|
||||
"enabled_table_header": "Etkin",
|
||||
"name_table_header": "İsim",
|
||||
"list_url_table_header": "Liste URL'si",
|
||||
@@ -167,31 +172,31 @@
|
||||
"edit_table_action": "Düzenle",
|
||||
"delete_table_action": "Sil",
|
||||
"elapsed": "Geçen zaman",
|
||||
"filters_and_hosts_hint": "AdGuard Home temel reklam engelleme kuralları ve ana bilgisayar dosyalarının sözdizim kurallarını anlamaktadır.",
|
||||
"filters_and_hosts_hint": "AdGuard Home, temel reklam engelleme kurallarını ve ana bilgisayar engelleme dosyalarının söz dizimini anlar.",
|
||||
"no_blocklist_added": "Engelleme listesi eklenmedi",
|
||||
"no_whitelist_added": "İzin verilen listesi eklenmedi",
|
||||
"no_whitelist_added": "İzin listesi eklenmedi",
|
||||
"add_blocklist": "Engelleme listesi ekle",
|
||||
"add_allowlist": "İzin verilen listesine ekle",
|
||||
"add_allowlist": "İzin listesi ekle",
|
||||
"cancel_btn": "İptal",
|
||||
"enter_name_hint": "İsim girin",
|
||||
"enter_url_or_path_hint": "Bir URL ya da listenin tam yolunu girin",
|
||||
"enter_url_or_path_hint": "Listenin URL adresini veya dosya konumunu girin",
|
||||
"check_updates_btn": "Güncellemeleri denetle",
|
||||
"new_blocklist": "Yeni engelleme listesi",
|
||||
"new_allowlist": "Yeni izin verilen listesi",
|
||||
"new_allowlist": "Yeni izin listesi",
|
||||
"edit_blocklist": "Engelleme listesini düzenle",
|
||||
"edit_allowlist": "İzin verilen listesini düzenle",
|
||||
"edit_allowlist": "İzin listesini düzenle",
|
||||
"choose_blocklist": "Engelleme listelerini seçin",
|
||||
"choose_allowlist": "İzin verilen listelerini seç",
|
||||
"choose_allowlist": "İzin listelerini seçin",
|
||||
"enter_valid_blocklist": "Engelleme listesine geçerli bir URL girin.",
|
||||
"enter_valid_allowlist": "İzin verilen listesine geçerli bir URL girin.",
|
||||
"enter_valid_allowlist": "İzin listesine geçerli bir URL girin.",
|
||||
"form_error_url_format": "Geçersiz URL biçimi",
|
||||
"form_error_url_or_path_format": "Geçersiz URL ya da listenin tam yolu",
|
||||
"form_error_url_or_path_format": "Listenin URL adresi veya dosya konumu geçersiz",
|
||||
"custom_filter_rules": "Özel filtreleme kuralları",
|
||||
"custom_filter_rules_hint": "Her satıra bir kural girin. Reklama engelleme kuralı veya ana bilgisayar dosyası sözdizimi kullanabilirsiniz.",
|
||||
"custom_filter_rules_hint": "Her satıra bir kural girin. Reklam engelleme kuralı veya ana bilgisayar dosyası söz dizimi kullanabilirsiniz.",
|
||||
"examples_title": "Örnekler",
|
||||
"example_meaning_filter_block": "example.org alan adına ve tüm alt alan adlarına olan erişimi engeller",
|
||||
"example_meaning_filter_whitelist": "example.org alan adına ve tüm alt alan adlarına olan erişim engelini kaldırır",
|
||||
"example_meaning_host_block": "AdGuard Home bu example.org adresi için 127.0.0.1 adresine yönlendirme yapacaktır (alt alan adları için geçerli değildir)",
|
||||
"example_meaning_host_block": "AdGuard Home, example.org adresi için 127.0.0.1 adresine yönlendirme yapacaktır (alt alan adları için geçerli değildir)",
|
||||
"example_comment": "! Buraya bir yorum ekledim",
|
||||
"example_comment_meaning": "sadece bir yorum",
|
||||
"example_comment_hash": "# Bir yorum daha ekledim",
|
||||
@@ -200,18 +205,18 @@
|
||||
"example_upstream_dot": "şifrelenmiş <0>DNS-over-TLS</0>",
|
||||
"example_upstream_doh": "şifrelenmiş <0>DNS-over-HTTPS</0>",
|
||||
"example_upstream_doq": "şifrelenmiş <0>DNS-over-QUIC</0>",
|
||||
"example_upstream_sdns": "<1>DNSCrypt</1> veya <2>DNS-over-HTTPS</2> çözücüleri için <0>DNS Damgaları</0> kullanabilirsiniz.",
|
||||
"example_upstream_sdns": "<1>DNSCrypt</1> veya <2>DNS-over-HTTPS</2> çözümleyicileri için <0>DNS Damgaları</0> kullanabilirsiniz",
|
||||
"example_upstream_tcp": "normal DNS (TCP üzerinden)",
|
||||
"all_lists_up_to_date_toast": "Tüm listeler zaten güncel",
|
||||
"all_lists_up_to_date_toast": "Tüm listeler güncel durumda",
|
||||
"updated_upstream_dns_toast": "Üst DNS sunucuları güncellendi",
|
||||
"dns_test_ok_toast": "Belirtilmiş DNS sunucuları düzgün çalışıyor",
|
||||
"dns_test_ok_toast": "Belirtilen DNS sunucuları düzgün çalışıyor",
|
||||
"dns_test_not_ok_toast": "Sunucu \"{{key}}\": kullanılamıyor, lütfen doğru yazdığınızdan emin olun",
|
||||
"unblock": "Engeli kaldır",
|
||||
"block": "Engelle",
|
||||
"disallow_this_client": "Bu istemciye izin verme",
|
||||
"allow_this_client": "Bu istemciye izin ver",
|
||||
"block_for_this_client_only": "Yalnızca bu istemci için engelle",
|
||||
"unblock_for_this_client_only": "Yalnızca bu müşteri için engellemeyi kaldır",
|
||||
"unblock_for_this_client_only": "Yalnızca bu istemci için engellemeyi kaldır",
|
||||
"time_table_header": "Saat",
|
||||
"date": "Tarih",
|
||||
"domain_name_table_header": "Alan adı",
|
||||
@@ -222,7 +227,7 @@
|
||||
"client_table_header": "İstemci",
|
||||
"empty_response_status": "Boş",
|
||||
"show_all_filter_type": "Tümünü göster",
|
||||
"show_filtered_type": "Filtrelenmişleri göster",
|
||||
"show_filtered_type": "Filtrelenenleri göster",
|
||||
"no_logs_found": "Günlük kaydı bulunamadı",
|
||||
"refresh_btn": "Yenile",
|
||||
"previous_btn": "Önceki",
|
||||
@@ -231,24 +236,24 @@
|
||||
"page_table_footer_text": "Sayfa",
|
||||
"rows_table_footer_text": "satır",
|
||||
"updated_custom_filtering_toast": "Özel filtreleme kuralları güncellendi",
|
||||
"rule_removed_from_custom_filtering_toast": "Özel filtreleme kurallarından kural kaldırıldı: {{rule}}",
|
||||
"rule_added_to_custom_filtering_toast": "Özel filtreleme kurallarına kural eklendi: {{rule}}",
|
||||
"rule_removed_from_custom_filtering_toast": "Özel filtreleme kurallarından kaldırıldı: {{rule}}",
|
||||
"rule_added_to_custom_filtering_toast": "Özel filtreleme kurallarına eklendi: {{rule}}",
|
||||
"query_log_response_status": "Durum: {{value}}",
|
||||
"query_log_filtered": "{{filter}} tarafından filtrelendi",
|
||||
"query_log_confirm_clear": "Tüm sorgu günlüğünü temizlemek istediğinizden emin misiniz?",
|
||||
"query_log_cleared": "Sorgu günlüğü başarıyla temizlendi",
|
||||
"query_log_updated": "Sorgu günlüğü başarıyla güncellendi",
|
||||
"query_log_clear": "Sorgu günlüklerini temizle",
|
||||
"query_log_retention": "Sorgu günlüklerinin saklanması",
|
||||
"query_log_retention": "Sorgu günlüklerini sakla",
|
||||
"query_log_enable": "Günlüğü etkinleştir",
|
||||
"query_log_configuration": "Günlük yapılandırması",
|
||||
"query_log_disabled": "Sorgu günlüğü devre dışı bırakıldı ve <0>ayarlar</0>da yapılandırılabilir",
|
||||
"query_log_strict_search": "Katı arama için çift tırnak işareti kullanın",
|
||||
"query_log_disabled": "Sorgu günlüğü devre dışı bırakıldı, bunu <0>ayarlar</0> kısmından yapılandırılabilirsiniz",
|
||||
"query_log_strict_search": "Tam arama için çift tırnak işareti kullanın",
|
||||
"query_log_retention_confirm": "Sorgu günlüğü saklama süresini değiştirmek istediğinize emin misiniz? Aralık değerini azaltırsanız, bazı veriler kaybolacaktır",
|
||||
"anonymize_client_ip": "İstemci IP'sini anonimize et",
|
||||
"anonymize_client_ip_desc": "İstemcinin tam IP adresini günlüklere ve istatistiklere kaydetmeyin",
|
||||
"dns_config": "DNS sunucusu yapılandırması",
|
||||
"dns_cache_config": "DNS önbelleği yapılandırması",
|
||||
"anonymize_client_ip": "İstemcinin IP adresini gizle",
|
||||
"anonymize_client_ip_desc": "İstemcinin IP adresini günlüklere ve istatistiklere kaydetmeyin",
|
||||
"dns_config": "DNS sunucu yapılandırması",
|
||||
"dns_cache_config": "DNS önbellek yapılandırması",
|
||||
"dns_cache_config_desc": "Burada DNS önbelleğini yapılandırabilirsiniz",
|
||||
"blocking_mode": "Engelleme modu",
|
||||
"default": "Varsayılan",
|
||||
@@ -263,134 +268,134 @@
|
||||
"dns_over_tls": "DNS-over-TLS",
|
||||
"dns_over_quic": "DNS-over-QUIC",
|
||||
"client_id": "İstemci Kimliği",
|
||||
"client_id_placeholder": "İstemci Kimliği girin",
|
||||
"client_id_desc": "Farklı istemciler, özel bir istemci kimliği ile tanımlanabilir. <a>Burada</a> istemcileri nasıl belirleyeceğiniz hakkında daha fazla bilgi edinebilirsiniz.",
|
||||
"client_id_placeholder": "İstemci kimliği girin",
|
||||
"client_id_desc": "Farklı istemciler, özel bir istemci kimliği ile tanımlanabilir. <a>Burada</a> istemcileri nasıl tanımlayacağınız hakkında daha fazla bilgi edinebilirsiniz.",
|
||||
"download_mobileconfig_doh": "DNS-over-HTTPS için .mobileconfig dosyasını indir",
|
||||
"download_mobileconfig_dot": "DNS-over-TLS için .mobileconfig dosyasını indir",
|
||||
"download_mobileconfig": "Yapılandırma dosyasını indir",
|
||||
"plain_dns": "Sade DNS",
|
||||
"plain_dns": "Düz DNS",
|
||||
"form_enter_rate_limit": "Sıklık limitini girin",
|
||||
"rate_limit": "Sıklık limiti",
|
||||
"edns_enable": "EDNS istemci alt ağını etkinleştir",
|
||||
"edns_cs_desc": "İstemcilerin alt ağlarını DNS sunucularına gönderin.",
|
||||
"rate_limit_desc": "Tek bir istemcinin yapmasına izin verilen saniye başına istek sayısı 0 olarak ayarlamak, sınır olmadığı anlamına gelir.",
|
||||
"rate_limit_desc": "İstemci başına izin verilen saniyedeki istek sayısı. 0 olarak ayarlamak, sınır olmadığı anlamına gelir.",
|
||||
"blocking_ipv4_desc": "Engellenen bir A isteği için geri döndürülecek IP adresi",
|
||||
"blocking_ipv6_desc": "Engellenen bir AAAA isteği için geri döndürülecek IP adresi",
|
||||
"blocking_mode_default": "Varsayılan: Reklam engelleme stili kuralı tarafından engellendiğinde sıfır IP adresiyle (A için 0.0.0.0; AAAA için) yanıt verin; /etc/hosts-style kuralı tarafından engellendiğinde, kuralda belirtilen IP adresiyle yanıt verin",
|
||||
"blocking_mode_refused": "REFUSED: REFUSED kod ile yanıt verin",
|
||||
"blocking_mode_nxdomain": "NXDOMAIN: NXDOMAIN koduyla yanıt",
|
||||
"blocking_mode_default": "Varsayılan: Reklam engelleme tarzı kural tarafından engellendiğinde sıfır IP adresiyle (A için 0.0.0.0; :: AAAA için) yanıt verin; /etc/hosts-tarzı kural tarafından engellendiğinde, kuralda belirtilen IP adresiyle yanıt verin",
|
||||
"blocking_mode_refused": "REFUSED: REFUSED koduyla yanıt verin",
|
||||
"blocking_mode_nxdomain": "NXDOMAIN: NXDOMAIN koduyla yanıt verin",
|
||||
"blocking_mode_null_ip": "Boş IP: Sıfır IP adresiyle yanıt verin (A için 0.0.0.0; :: AAAA için)",
|
||||
"blocking_mode_custom_ip": "Özel IP: El ile ayarlanmış bir IP adresi ile yanıt verin",
|
||||
"upstream_dns_client_desc": "Bu alanı boş tutarsanız, AdGuard Home, <0>DNS ayarlarında</0> yapılandırılmış sunucuları kullanır.",
|
||||
"blocking_mode_custom_ip": "Özel IP: El ile ayarlanmış bir IP adresiyle yanıt verin",
|
||||
"upstream_dns_client_desc": "Bu alanı boş bırakırsanız, AdGuard Home, <0>DNS ayarlarında</0> yapılandırılan sunucuları kullanır.",
|
||||
"tracker_source": "İzleyici kaynağı",
|
||||
"source_label": "Kaynak",
|
||||
"found_in_known_domain_db": "Bilinen alan adları veritabanı içinde bulundu.",
|
||||
"found_in_known_domain_db": "Alan adlarının bilindiği veri tabanında bulundu.",
|
||||
"category_label": "Kategori",
|
||||
"rule_label": "Kural",
|
||||
"list_label": "Liste",
|
||||
"unknown_filter": "Bilinmeyen filtre {{filterId}}",
|
||||
"known_tracker": "Bilinen izleyici",
|
||||
"install_welcome_title": "AdGuard Home'a hoş geldiniz!",
|
||||
"install_welcome_desc": "AdGuard Home, ağ genelinde reklam ve izleyicileri engelleyen bir DNS sunucusudur. Tüm ağınızı ve tüm cihazlarınızı kontrol etmenize yarayan bir araçtır, istemci tarafında bir program kullanmanıza gerek duymaz.",
|
||||
"install_welcome_desc": "AdGuard Home, ağ genelinde reklamları ve izleyicileri engelleyen bir DNS sunucusudur. Tüm ağınızı ve tüm cihazlarınızı kontrol etmenizi sağlar, istemci tarafında herhangi bir program kullanmanıza gerek duymaz.",
|
||||
"install_settings_title": "Yönetici Web Arayüzü",
|
||||
"install_settings_listen": "Dinleme arayüzü",
|
||||
"install_settings_port": "Bağlantı noktası",
|
||||
"install_settings_interface_link": "AdGuard Home yönetici web arayüzü sayfanız şu adresten erişilebilir olacaktır:",
|
||||
"form_error_port": "Geçerli bağlantı noktası numarası girin",
|
||||
"form_error_port": "Geçerli bir bağlantı noktası değeri girin",
|
||||
"install_settings_dns": "DNS sunucusu",
|
||||
"install_settings_dns_desc": "Cihazlarınızı veya yönlendiricinizi şu adresteki DNS sunucusunu kullanması için ayarlamanız gerekecek:",
|
||||
"install_settings_all_interfaces": "Tüm arayüzler",
|
||||
"install_auth_title": "Kimlik Doğrulama",
|
||||
"install_auth_desc": "AdAdGuard Home yönetici web arayüzüne erişim için kullanıcı adı ve şifresi oluşturmanız şiddetle tavsiye edilir. Sadece yerel ağınız erişilebilir olsa bile izinsiz giriş yapılmasını engellemek için şifrenizin olması önemlidir.",
|
||||
"install_auth_desc": "AdGuard Home yönetici web arayüzüne erişim için kullanıcı adı ve şifre yapılandırmanız önemle tavsiye edilir. Yalnızca yerel ağınızdan erişilebilir olsa bile izinsiz erişimden korumak yine de önemlidir.",
|
||||
"install_auth_username": "Kullanıcı adı",
|
||||
"install_auth_password": "Parola",
|
||||
"install_auth_confirm": "Parolayı onayla",
|
||||
"install_auth_username_enter": "Kullanıcı adı girin",
|
||||
"install_auth_password_enter": "Şifre girin",
|
||||
"install_auth_password_enter": "Parola girin",
|
||||
"install_step": "Adım",
|
||||
"install_devices_title": "Cihazlarınızı ayarlayın",
|
||||
"install_devices_desc": "AdGuard Home'un çalışması için cihazlarınızı onu kullanacak şekilde ayarlamalısınız.",
|
||||
"install_devices_title": "Cihazlarınızı yapılandırın",
|
||||
"install_devices_desc": "AdGuard Home'u kullanmaya başlamak için, cihazlarınızı onu kullanacak şekilde yapılandırmanız gerekir.",
|
||||
"install_submit_title": "Tebrikler!",
|
||||
"install_submit_desc": "Kurulum prosedürü tamamlandı ve artık AdGuard Home'u kullanmaya hazırsınız.",
|
||||
"install_submit_desc": "Kurulum işlemi tamamlandı ve artık AdGuard Home'u kullanmaya hazırsınız.",
|
||||
"install_devices_router": "Yönlendirici",
|
||||
"install_devices_router_desc": "Bu kurulum evdeki yönlendiricinize bağlı tüm cihazlarınızı otomatik olarak kapsar ve her birini elle ayarlamanız gerekmez.",
|
||||
"install_devices_router_desc": "Bu kurulum, ev yönlendiricinize bağlı tüm cihazları otomatik olarak kapsar ve her birini elle yapılandırmanıza gerek yoktur.",
|
||||
"install_devices_address": "AdGuard Home DNS sunucusu şu adresi dinleyecektir",
|
||||
"install_devices_router_list_1": "Yönlendiricinizin ayarlarına girin. Genellikle tarayıcınızdan http://192.168.0.1/ veya http://192.168.1.1/ gibi bir URL aracılığıyla erişebilirsiniz. Bir parola girmeniz istenebilir. Hatırlamıyorsanız, genellikle yönlendiricinin üzerindeki bir düğmeye basarak parolayı sıfırlayabilirsiniz, ancak bu yöntemin seçilmesi durumunda muhtemelen tüm yönlendirici yapılandırmasını kaybedeceğinizi unutmayın. Yönlendiricinizin kurulması için bir uygulama gerekiyorsa, lütfen uygulamayı telefonunuza veya PC'nize yükleyin ve yönlendiricinin ayarlarına erişmek için kullanın.",
|
||||
"install_devices_router_list_1": "Yönlendiricinizin ayarlarına gidin. Genellikle tarayıcınızdan http://192.168.0.1/ veya http://192.168.1.1/ gibi bir URL aracılığıyla erişebilirsiniz. Bir parola girmeniz istenebilir. Hatırlamıyorsanız, genellikle yönlendiricinin üzerindeki bir düğmeye basarak parolayı sıfırlayabilirsiniz, ancak bu işlemin seçilmesi durumunda yüksek ihtimalle tüm yönlendirici yapılandırmasını kaybedeceğinizi unutmayın. Yönlendiricinizin kurulumu için bir uygulama gerekiyorsa, lütfen uygulamayı telefonunuza veya PC'nize yükleyin ve yönlendiricinin ayarlarına erişmek için kullanın.",
|
||||
"install_devices_router_list_2": "DHCP/DNS ayarlarını bulun. DNS satırlarını arayın, genelde iki veya üç tanedir, üç rakam girilebilen dört ayrı grup içeren satırdır.",
|
||||
"install_devices_router_list_3": "AdGuard Home sunucusunun adresini o kısma yazın.",
|
||||
"install_devices_router_list_4": "Bazı yönlendirici türlerinde özel bir DNS sunucusu kurulamaz. Bu durumda, AdGuard Home'u bir <0>DHCP sunucusu</0> olarak kurmak yardımcı olabilir. Aksi takdirde, belirli yönlendirici modelinizdeki DNS sunucularını nasıl özelleştireceğiniz konusunda yönlendirici kılavuzunu kontrol etmelisiniz.",
|
||||
"install_devices_windows_list_1": "Başlat menüsünden veya Windows aramasıyla Denetim Masası'na girin.",
|
||||
"install_devices_windows_list_2": "Ağ ve Internet kategorisine girin, sonra Ağ ve Paylaşım Merkezi'ne girin.",
|
||||
"install_devices_windows_list_3": "Sol taraftaki Bağdaştırıcı ayarlarını değiştir ayarını bulun ve ona tıklayın.",
|
||||
"install_devices_windows_list_4": "Aktif bağlantınızı seçin, sağ tıklayın ve Özellikler'e tıklayın.",
|
||||
"install_devices_windows_list_5": "Listeden İnternet Protokolü Sürüm 4 (TCP/IP)'ye tıklayın ve Özellikler'e tıklayın.",
|
||||
"install_devices_windows_list_6": "Aşağıdaki DNS sunucu adreslerini kullanın'ı seçin ve Tercih edilen DNS sunucusu alanına AdGuard Home sunucusunun adreslerini girin.",
|
||||
"install_devices_macos_list_1": "Apple simgesine tıklayın ve Sistem Tercihleri'ne tıklayın.",
|
||||
"install_devices_router_list_4": "Bazı yönlendirici türlerinde özel bir DNS sunucusu ayarlanamaz. Bu durumda, AdGuard Home'u <0>DHCP sunucusu</0> olarak ayarlamak yardımcı olabilir. Aksi takdirde, yönlendirici modeliniz için DNS sunucularını nasıl ayarlayacağınız konusunda yönlendirici kılavuzuna bakmalısınız.",
|
||||
"install_devices_windows_list_1": "Başlat menüsünden veya Windows araması aracılığıyla Denetim Masası'nı açın.",
|
||||
"install_devices_windows_list_2": "Ağ ve İnternet kategorisine girin ve ardından Ağ ve Paylaşım Merkezi'ne girin.",
|
||||
"install_devices_windows_list_3": "Ekranın sol tarafında bulunan \"Adaptör ayarlarını değiştir\" öğesini bulun ve tıklayın.",
|
||||
"install_devices_windows_list_4": "Kullandığınız aktif bağlantının üzerine sağ tıklayın ve Özellikler öğesine tıklayın.",
|
||||
"install_devices_windows_list_5": "Listede \"İnternet Protokolü Sürüm 4 (TCP/IPv4)\" (veya IPv6 için \"İnternet Protokolü Sürüm 6 (TCP/IPv6)\") öğesini bulun, seçin ve ardından tekrar Özellikler'e tıklayın.",
|
||||
"install_devices_windows_list_6": "\"Aşağıdaki DNS sunucu adreslerini kullan\"ı seçin ve AdGuard Home sunucu adreslerinizi girin.",
|
||||
"install_devices_macos_list_1": "Apple simgesinde bulunan Sistem Tercihleri'ne tıklayın.",
|
||||
"install_devices_macos_list_2": "Ağ seçeneğine tıklayın.",
|
||||
"install_devices_macos_list_3": "Listenizdeki ilk bağlantıyı seçin ve Gelişmiş'e tıklayın.",
|
||||
"install_devices_macos_list_4": "DNS sekmesine tıklayın ve AdGuard Home sunucunuzun adreslerini girin.",
|
||||
"install_devices_macos_list_3": "Listedeki ilk bağlantıyı seçin ve Gelişmiş öğesine tıklayın.",
|
||||
"install_devices_macos_list_4": "DNS sekmesini seçin ve AdGuard Home sunucunuzun adreslerini girin.",
|
||||
"install_devices_android_list_1": "Android cihazınızda Ayarlar simgesine dokunun.",
|
||||
"install_devices_android_list_2": "Wi-Fi menüsüne dokunun. Mevcut tüm ağlar listelenecektir (mobil bağlantı için isteğe bağlı DNS sunucusu ayarlanamaz).",
|
||||
"install_devices_android_list_3": "Bağlı olduğunuz ağa uzun basın ve Ağı Değiştir'e dokunun.",
|
||||
"install_devices_android_list_4": "Bazı cihazlarda, diğer ayarları görmek için Gelişmiş kutucuğu işaretlemeniz gerekebilir. Android DNS ayarlarınızı yapmak için IP ayarlarını DHCP'den Statik'e geçirmeniz gerekecektir.",
|
||||
"install_devices_android_list_5": "DNS 1 ve DNS 2 değerlerini AdGuard Home sunucu adreslerinizle değiştirin.",
|
||||
"install_devices_android_list_2": "Menüde bulunan Wi-Fi seçeneğine dokunun. Mevcut tüm ağlar listelenecektir (mobil ağlar için özel DNS sunucusu ayarlanamaz).",
|
||||
"install_devices_android_list_3": "Bağlı olduğunuz ağın üzerine basılı tutun ve Ağı Değiştir'e dokunun.",
|
||||
"install_devices_android_list_4": "Bazı cihazlarda, diğer ayarları görmek için \"Gelişmiş\" seçeneğini seçmeniz gerekebilir. Android DNS ayarlarınızı yapmak için IP ayarlarını DHCP modundan Statik moda almanız gerekecektir.",
|
||||
"install_devices_android_list_5": "DNS 1 ve DNS 2 değerlerini AdGuard Home sunucunuzun adresleriyle değiştirin.",
|
||||
"install_devices_ios_list_1": "Ana ekrandaki Ayarlar simgesine dokunun.",
|
||||
"install_devices_ios_list_2": "Sol menüdeki Wi-Fi bölümüne girin (mobil bağlantı için isteğe bağlı DNS sunucusu ayarlanamaz).",
|
||||
"install_devices_ios_list_2": "Sol menüde bulunan Wi-Fi bölümüne girin (mobil ağlar için özel DNS sunucusu ayarlanamaz).",
|
||||
"install_devices_ios_list_3": "Bağlı olduğunuz ağın ismine dokunun.",
|
||||
"install_devices_ios_list_4": "DNS alanına AdGuard Home sunucunuzun adreslerini girin.",
|
||||
"get_started": "Başlarken",
|
||||
"get_started": "Başlayın",
|
||||
"next": "Sonraki",
|
||||
"open_dashboard": "Panoyu Aç",
|
||||
"open_dashboard": "Ana Sayfayı Aç",
|
||||
"install_saved": "Başarıyla kaydedildi",
|
||||
"encryption_title": "Şifreleme",
|
||||
"encryption_desc": "Hem DNS ve hem de yönetici web arayüzü için şifreleme (HTTPS/TLS) desteği",
|
||||
"encryption_desc": "DNS ve yönetici web arayüzü için şifreleme (HTTPS/TLS) desteği",
|
||||
"encryption_config_saved": "Şifreleme yapılandırması kaydedildi",
|
||||
"encryption_server": "Sunucu adı",
|
||||
"encryption_server_enter": "Alan adınızı girin",
|
||||
"encryption_server_desc": "HTTPS kullanmak için, SSL sertifikanız veya joker karakter sertifikanızla eşleşen sunucu adını girmeniz gerekir. Alan ayarlanmazsa, herhangi bir alan adı için TKG bağlantılarını kabul eder.",
|
||||
"encryption_server_desc": "HTTPS kullanmak için SSL sertifikanızla veya Willcard sertifikanızla eşleşen sunucu adını girmeniz gerekir. Bu alan ayarlanmazsa, herhangi bir alan adının TLS bağlantılarını kabul eder.",
|
||||
"encryption_redirect": "Otomatik olarak HTTPS'e yönlendir",
|
||||
"encryption_redirect_desc": "Etkinleştirirseniz AdGuard Home sizi HTTP yerine HTTPS adreslerine yönlendirir.",
|
||||
"encryption_redirect_desc": "Etkinleştirirseniz, AdGuard Home sizi HTTP adresi yerine HTTPS adresine yönlendirir.",
|
||||
"encryption_https": "HTTPS bağlantı noktası",
|
||||
"encryption_https_desc": "Eğer HTTPS portu yapılandırılmışsa, AdGuard Home yönetici arayüzü HTTPS ile ulaşılabilir olacak, ayrıca '/dns-query' üzerinden DNS-over-HTTPS hizmeti de çalışacak.",
|
||||
"encryption_https_desc": "HTTPS bağlantı noktası yapılandırılırsa, AdGuard Home yönetici arayüzüne HTTPS aracılığıyla erişilebilir olacak ve ayrıca '/dns-query' üzerinden DNS-over-HTTPS bağlantısı sağlanır.",
|
||||
"encryption_dot": "DNS-over-TLS bağlantı noktası",
|
||||
"encryption_dot_desc": "Eğer bu portu yapılandırırsanız AdGuard Home bu port üzerinden DNS-over-TLS sunucusu çalıştıracak.",
|
||||
"encryption_dot_desc": "Bu bağlantı noktası yapılandırılırsa, AdGuard Home, DNS-over-TLS sunucusunu bu bağlantı noktası üzerinden çalıştıracaktır.",
|
||||
"encryption_doq": "DNS-over-QUIC bağlantı noktası",
|
||||
"encryption_doq_desc": "Bu bağlantı noktası yapılandırılırsa, AdGuard Home bu bağlantı noktasında DNS-over-QUIC sunucusu çalıştıracaktır. Deneysel ve güvenilir olmayabilir. Ayrıca, şu anda bunu destekleyen çok fazla istemci yok.",
|
||||
"encryption_doq_desc": "Bu bağlantı noktası yapılandırılırsa, AdGuard Home, DNS-over-QUIC sunucusunu bu bağlantı noktası üzerinden çalıştıracaktır. Bu özellik deneme aşamasındadır ve güvenilir olmayabilir. Ayrıca, şu anda bu özelliği destekleyen çok fazla istemci yok.",
|
||||
"encryption_certificates": "Sertifikalar",
|
||||
"encryption_certificates_desc": "Şifrelemeyi kullanmak için alan adınız için geçerli bir SSL sertifika zinciri temin etmeniz gerekir. <0>{{link}}</0> adresinden ücretsiz temin edebilirsiniz veya güvenilir Sertifika Otoritelerinden satın alabilirsiniz.",
|
||||
"encryption_certificates_input": "PEM biçimindeki sertifikalarınızı buraya yapıştırın.",
|
||||
"encryption_certificates_desc": "Şifrelemeyi kullanmak için alan adınız için geçerli bir SSL sertifika zinciri sağlamanız gerekir. <0>{{link}}</0> adresinden ücretsiz bir sertifika alabilir veya güvenilir Sertifika Yetkililerinden satın alabilirsiniz.",
|
||||
"encryption_certificates_input": "PEM biçimindeki sertifikalarınızı kopyalayıp buraya yapıştırın.",
|
||||
"encryption_status": "Durum",
|
||||
"encryption_expire": "Bitiş tarihi",
|
||||
"encryption_key": "Özel anahtar",
|
||||
"encryption_key_input": "Sertifikanızın PEM biçimi özel anahtarını buraya yapıştırın.",
|
||||
"encryption_key_input": "Sertifikanızın PEM biçimli özel anahtarını kopyalayıp buraya yapıştırın.",
|
||||
"encryption_enable": "Şifrelemeyi etkinleştir (HTTPS, DNS-over-HTTPS ve DNS-over-TLS)",
|
||||
"encryption_enable_desc": "Şifrelemeyi etkinleştirirseniz, AdGuard Home yönetici arayüzü HTTPS üzerinden çalışır ve DNS sunucusu DNS-over-HTTPS ve DNS-over-TLS üzerinden gelen istekleri dinler.",
|
||||
"encryption_enable_desc": "Şifrelemeyi etkinleştirirseniz, AdGuard Home yönetici arayüzü HTTPS üzerinden çalışır ve DNS sunucusu, DNS-over-HTTPS ve DNS-over-TLS üzerinden gelen istekleri dinler.",
|
||||
"encryption_chain_valid": "Sertifika zinciri geçerli",
|
||||
"encryption_chain_invalid": "Sertifika zinciri geçersiz",
|
||||
"encryption_key_valid": "Bu geçerli bir {{type}} özel anahtar",
|
||||
"encryption_key_invalid": "Bu geçersiz bir {{type}} özel anahtar",
|
||||
"encryption_subject": "Konu",
|
||||
"encryption_issuer": "Sertifikayı veren",
|
||||
"encryption_issuer": "Sağlayan",
|
||||
"encryption_hostnames": "Ana bilgisayar adları",
|
||||
"encryption_reset": "Şifreleme ayarlarını sıfırlamak istediğinize emin misiniz?",
|
||||
"encryption_reset": "Şifreleme ayarlarını sıfırlamak istediğinizden emin misiniz?",
|
||||
"topline_expiring_certificate": "SSL sertifikanızın süresi dolmak üzere. <0>Şifreleme ayarlarını</0> güncelleyin.",
|
||||
"topline_expired_certificate": "SSL sertifikanızın süresi dolmuş. <0>Şifreleme ayarlarını</0> güncelleyin.",
|
||||
"topline_expired_certificate": "SSL sertifikanızın süresi doldu. <0>Şifreleme ayarlarını</0> güncelleyin.",
|
||||
"form_error_port_range": "80-65535 aralığında geçerli bir bağlantı noktası değeri girin",
|
||||
"form_error_port_unsafe": "Bu güvenli olmayan bir bağlantı noktası",
|
||||
"form_error_equal": "Eşit olmamalı",
|
||||
"form_error_port_unsafe": "Bu bağlantı noktası güvenli değil",
|
||||
"form_error_equal": "Aynı olmamalı",
|
||||
"form_error_password": "Şifreler uyuşmuyor",
|
||||
"reset_settings": "Ayarları sıfırla",
|
||||
"update_announcement": "AdGuard Home {{version}} şu an yüklenmeye hazır! Daha fazla bilgi için <0>buraya tıklayın.</0>",
|
||||
"setup_guide": "Kurulum rehberi",
|
||||
"update_announcement": "AdGuard Home {{version}} sürümü mevcut! Daha fazla bilgi için <0>buraya tıklayın.</0>",
|
||||
"setup_guide": "Kurulum Rehberi",
|
||||
"dns_addresses": "DNS adresleri",
|
||||
"dns_start": "DNS sunucusu başlatılıyor",
|
||||
"dns_status_error": "DNS sunucusu durumunu alma hatası",
|
||||
"down": "kapalı",
|
||||
"dns_status_error": "DNS sunucusunun durumu denetlenirken bir hata oluştu",
|
||||
"down": "Kapalı",
|
||||
"fix": "Düzelt",
|
||||
"dns_providers": "Aralarından seçim yapabileceğiniz bilinen <0>DNS sağlayıcıların listesi</0>.",
|
||||
"update_now": "Şimdi güncelleyin",
|
||||
"update_failed": "Otomatik güncelleme başarısız oldu. Lütfen elle güncellemek için <a>talimatları uygulayın</a>.",
|
||||
"processing_update": "Lütfen bekleyin. AdGuard Home güncelleniyor",
|
||||
"dns_providers": "Aralarından seçim yapabileceğiniz, bilinen <0>DNS sağlayıcıların listesi</0>.",
|
||||
"update_now": "Şimdi güncelle",
|
||||
"update_failed": "Otomatik güncelleme başarısız oldu. Elle güncellemek için lütfen <a>bu adımları uygulayın</a>.",
|
||||
"processing_update": "Lütfen bekleyin, AdGuard Home güncelleniyor",
|
||||
"clients_title": "İstemciler",
|
||||
"clients_desc": "AdGuard Home'a bağlı cihazları yapılandırın",
|
||||
"settings_global": "Genel",
|
||||
@@ -398,15 +403,15 @@
|
||||
"table_client": "İstemci",
|
||||
"table_name": "İsim",
|
||||
"save_btn": "Kaydet",
|
||||
"client_add": "İstemci ekle",
|
||||
"client_add": "İstemci Ekle",
|
||||
"client_new": "Yeni İstemci",
|
||||
"client_edit": "İstemciyi düzenle",
|
||||
"client_edit": "İstemciyi Düzenle",
|
||||
"client_identifier": "Tanımlayıcı",
|
||||
"ip_address": "IP adresi",
|
||||
"client_identifier_desc": "İstemciler IP adresi, CIDR, MAC adresi veya özel bir istemci kimliği ile tanımlanabilir (DoT/DoH/DoQ için kullanılabilir). <0>Burada</0> istemcileri nasıl belirleyeceğiniz hakkında daha fazla bilgi edinebilirsiniz.",
|
||||
"form_enter_ip": "IP Girin",
|
||||
"client_identifier_desc": "İstemciler IP adresi, CIDR, MAC adresi veya özel bir istemci kimliği ile tanımlanabilir (DoT/DoH/DoQ için kullanılabilir). İstemcileri nasıl tanımlayacağınız hakkında daha fazla bilgiyi <0>burada</0> bulabilirsiniz.",
|
||||
"form_enter_ip": "IP adresi girin",
|
||||
"form_enter_subnet_ip": "\"{{cidr}}\" alt ağına bir IP adresi girin",
|
||||
"form_enter_mac": "MAC Girin",
|
||||
"form_enter_mac": "MAC adresi girin",
|
||||
"form_enter_id": "Tanımlayıcı girin",
|
||||
"form_add_id": "Tanımlayıcı ekle",
|
||||
"form_client_name": "İstemci ismi girin",
|
||||
@@ -419,82 +424,84 @@
|
||||
"client_confirm_delete": "\"{{key}}\" istemcisini silmek istediğinizden emin misiniz?",
|
||||
"list_confirm_delete": "Bu listeyi silmek istediğinizden emin misiniz?",
|
||||
"auto_clients_title": "İstemciler (çalışma zamanı)",
|
||||
"auto_clients_desc": "AdGuard Home'u kullanan, ancak yapılandırmada saklanmayan istemcilerdeki veriler",
|
||||
"auto_clients_desc": "AdGuard Home'u kullanan ancak yapılandırmada depolanmayan istemcilerle ilgili veriler",
|
||||
"access_title": "Erişim ayarları",
|
||||
"access_desc": "Burada, AdGuard Home DNS sunucusu için erişim kurallarını yapılandırabilirsiniz.",
|
||||
"access_desc": "AdGuard Home DNS sunucusu için erişim kurallarını buradan yapılandırabilirsiniz.",
|
||||
"access_allowed_title": "İzin verilen istemciler",
|
||||
"access_allowed_desc": "CIDR veya IP adreslerinin listesi. Yapılandırılırsa, AdGuard Home yalnızca bu IP adreslerinden gelen istekleri kabul edecektir.",
|
||||
"access_allowed_desc": "CIDR'lerin, IP adreslerinin veya istemci kimliklerinin listesi. Yapılandırılırsa, AdGuard Home yalnızca bu istemcilerden gelen istekleri kabul eder.",
|
||||
"access_disallowed_title": "İzin verilmeyen istemciler",
|
||||
"access_disallowed_desc": "CIDR veya IP adreslerinin listesi. Yapılandırılırsa, AdGuard Home yalnızca bu IP adreslerinden gelen istekleri cevapsız bırakacaktır.",
|
||||
"access_blocked_title": "Engellenmiş alan adları",
|
||||
"access_blocked_desc": "Bunu filtrelerle karıştırmayın. AdGuard Home, sorguların sorularına bu alan adlarıyla DNS sorgularını bırakacaktır. Burada tam alan adlarını, joker karakterleri ve URL filtre kurallarını belirtebilirsiniz, örn. \"example.org\", \"*.example.org\" veya \"|| example.org^\".",
|
||||
"access_disallowed_desc": "CIDR'lerin, IP adreslerinin veya istemci kimliklerinin listesi. Yapılandırılırsa, AdGuard Home bu istemcilerden gelen istekleri keser. İzin verilen istemciler yapılandırılırsa, bu alan yok sayılır.",
|
||||
"access_blocked_title": "Engellenen alan adları",
|
||||
"access_blocked_desc": "Bu işlem filtrelerle ilgili değildir. AdGuard Home, bu alan adlarından gelen DNS sorgularını yanıtsız bırakır ve bu sorgular sorgu günlüğünde görünmez. Tam alan adlarını, joker karakterleri veya URL filtre kurallarını belirtebilirsiniz, ör. \"example.org\", \"*.example.org\" veya \"||example.org^\".",
|
||||
"access_settings_saved": "Erişim ayarları başarıyla kaydedildi!",
|
||||
"updates_checked": "Güncelleme kontrolü başarılı",
|
||||
"updates_version_equal": "AdGuard Home yazılımı günceldir",
|
||||
"updates_version_equal": "AdGuard Home yazılımı güncel durumda",
|
||||
"check_updates_now": "Güncellemeleri şimdi denetle",
|
||||
"dns_privacy": "DNS Gizliliği",
|
||||
"setup_dns_privacy_1": "<0>DNS-over-TLS:</0> <1>{{address}}</1> dizesini kullan.",
|
||||
"setup_dns_privacy_2": "<0>DNS-over-HTTPS:</0> <1>{{address}}</1> dizesini kullan.",
|
||||
"setup_dns_privacy_3": "<0>İşte kullanabileceğiniz yazılımların bir listesi.</0>",
|
||||
"setup_dns_privacy_3": "<0>İşte, kullanabileceğiniz yazılımların bir listesi.</0>",
|
||||
"setup_dns_privacy_4": "Bir iOS 14 veya macOS Big Sur cihazında, DNS ayarlarına <highlight>DNS-over-HTTPS</highlight> veya <highlight>DNS-over-TLS</highlight> sunucuları ekleyen özel '.mobileconfig' dosyasını indirebilirsiniz.",
|
||||
"setup_dns_privacy_android_1": "Android 9 aslen DNS-over-TLS desteklemektedir. Yapılandırmak için, Ayarlar → Ağ ve internet → Gelişmiş → Özel DNS seçeneğine gidin ve alan adınızı buraya girin.",
|
||||
"setup_dns_privacy_android_2": "<0>AdGuard for Android</0>, <1>DNS-over-HTTPS</1> ve <1>DNS-over-TLS</1> desteklemektedir.",
|
||||
"setup_dns_privacy_android_1": "Android 9, yerel olarak DNS-over-TLS protokolünü destekler. Yapılandırmak için Ayarlar → Ağ ve İnternet → Gelişmiş → Özel DNS seçeneğine gidin ve alan adınızı girin.",
|
||||
"setup_dns_privacy_android_2": "<0>Android için AdGuard</0>, <1>DNS-over-HTTPS</1> ve <1>DNS-over-TLS</1> protokolünü destekler.",
|
||||
"setup_dns_privacy_android_3": "<0>Intra</0> Android'e <1>DNS-over-HTTPS</1> desteğini ekler.",
|
||||
"setup_dns_privacy_ios_1": "<0>DNSCloak</0>, <1>DNS-over-HTTPS</1> destekler, ancak kendi sunucunuzu kullanacak şekilde yapılandırmak için bir <2>DNS Damgası</2> oluşturmanız gerekir.",
|
||||
"setup_dns_privacy_ios_2": "<0>AdGuard for iOS</0>, <1>DNS-over-HTTPS</1> ve <1>DNS-over-TLS</1> destekler.",
|
||||
"setup_dns_privacy_other_title": "Diğer uygulamalar",
|
||||
"setup_dns_privacy_other_1": "AdGuard Home'un kendisi herhangi bir platformda güvenli bir DNS istemcisi olabilir.",
|
||||
"setup_dns_privacy_ios_1": "<0>DNSCloak</0>, <1>DNS-over-HTTPS</1> protokolünü destekler, ancak kendi sunucunuzu kullanacak şekilde yapılandırmak için bir <2>DNS Damgası</2> oluşturmanız gerekir.",
|
||||
"setup_dns_privacy_ios_2": "<0>iOS için AdGuard</0>, <1>DNS-over-HTTPS</1> ve <1>DNS-over-TLS</1> protokolünü destekler.",
|
||||
"setup_dns_privacy_other_title": "Diğer kullanım alanları",
|
||||
"setup_dns_privacy_other_1": "AdGuard Home, herhangi bir platformda güvenli bir DNS istemcisi olabilir.",
|
||||
"setup_dns_privacy_other_2": "<0>dnsproxy</0>, bilinen tüm güvenli DNS protokollerini destekler.",
|
||||
"setup_dns_privacy_other_3": "<0>dnscrypt-proxy</0>, <1>DNS-over-HTTPS</1> destekler.",
|
||||
"setup_dns_privacy_other_4": "<0>Mozilla Firefox</0>, <1>DNS-over-HTTPS</1> desteklemektedir.",
|
||||
"setup_dns_privacy_other_5": "<0>Burada</0> ve <1>burada</1> daha fazla uygulama bulacaksınız.",
|
||||
"setup_dns_privacy_other_3": "<0>dnscrypt-proxy</0>, <1>DNS-over-HTTPS</1> protokolünü destekler.",
|
||||
"setup_dns_privacy_other_4": "<0>Mozilla Firefox</0>, <1>DNS-over-HTTPS</1> protokolünü destekler.",
|
||||
"setup_dns_privacy_other_5": "<0>Burada</0> ve <1>burada</1> daha fazla kullanım alanı bulacaksınız.",
|
||||
"setup_dns_privacy_ioc_mac": "iOS ve macOS yapılandırması",
|
||||
"setup_dns_notice": "<1>DNS-over-HTTPS</1> veya <1>DNS-over-TLS</1> kullanmak için, AdGuard Home ayarlarında <0>Şifreleme yapılandırmasını</0> yapmanız gerekir.",
|
||||
"setup_dns_notice": "<1>DNS-over-HTTPS</1> veya <1>DNS-over-TLS</1> protokolünü kullanmak için AdGuard Home üzerinde <0>Şifreleme ayarları</0> bölümünden ayarları yapmanız gerekir.",
|
||||
"rewrite_added": "\"{{key}}\" için DNS yeniden yazımı başarıyla eklendi",
|
||||
"rewrite_deleted": "\"{{key}}\" için DNS yeniden yazımı başarıyla silindi",
|
||||
"rewrite_add": "DNS yeniden yazımı ekle",
|
||||
"rewrite_not_found": "DNS yeniden yazımı bulunamadı",
|
||||
"rewrite_confirm_delete": "\"{{key}}\" için DNS yeniden yazımını silmek istediğinize emin misiniz?",
|
||||
"rewrite_desc": "Belirli bir alan adı için kolayca özel DNS yanıtı yapılandırmanıza olanak tanır.",
|
||||
"rewrite_applied": "Uygulanan Yeniden Yazım kuralı",
|
||||
"rewrite_desc": "Belirli bir alan adı için özel DNS yanıtını kolayca yapılandırmanızı sağlar.",
|
||||
"rewrite_applied": "Yeniden yazım kuralı uygulandı",
|
||||
"rewrite_hosts_applied": "Ana bilgisayar dosyası kuralı tarafından yeniden yazıldı",
|
||||
"dns_rewrites": "DNS yeniden yazımları",
|
||||
"form_domain": "Alan adı girin",
|
||||
"form_answer": "IP adresini veya alan adı girin",
|
||||
"form_domain": "Alan adı veya joker karakter girin",
|
||||
"form_answer": "IP adresi veya alan adı girin",
|
||||
"form_error_domain_format": "Geçersiz alan adı biçimi",
|
||||
"form_error_answer_format": "Geçersiz cevap biçimi",
|
||||
"form_error_answer_format": "Geçersiz yanıt biçimi",
|
||||
"configure": "Yapılandır",
|
||||
"main_settings": "Ana ayarlar",
|
||||
"block_services": "Belirli hizmetleri engelle",
|
||||
"blocked_services": "Engellenen hizmetler",
|
||||
"blocked_services_desc": "Popüler siteleri ve hizmetleri hızlı bir şekilde engellemenizi sağlar.",
|
||||
"blocked_services_saved": "Engellenen servisler başarıyla kaydedildi",
|
||||
"blocked_services_global": "Genel engellenen hizmetleri kullanın",
|
||||
"blocked_services_saved": "Engellenen hizmetler başarıyla kaydedildi",
|
||||
"blocked_services_global": "Genel olarak engellenen hizmetleri kullan",
|
||||
"blocked_service": "Engellenen hizmet",
|
||||
"block_all": "Hepsini engelle",
|
||||
"unblock_all": "Tüm engellemeyi kaldır",
|
||||
"encryption_certificate_path": "Sertifika yolu",
|
||||
"encryption_private_key_path": "Özel anahtar yolu",
|
||||
"encryption_certificates_source_path": "Sertifika dosyalarının yolunu belirleyin",
|
||||
"encryption_certificates_source_content": "Sertifikaların içeriklerini yapıştırın",
|
||||
"encryption_certificates_source_content": "Sertifika içeriğini yapıştır",
|
||||
"encryption_key_source_path": "Özel bir anahtar dosyası belirleyin",
|
||||
"encryption_key_source_content": "Özel anahtar içeriklerini yapıştırın",
|
||||
"encryption_key_source_content": "Özel anahtar içeriğini yapıştır",
|
||||
"stats_params": "İstatistik yapılandırması",
|
||||
"config_successfully_saved": "Yapılandırma başarıyla kaydedildi",
|
||||
"interval_6_hour": "6 saat",
|
||||
"interval_24_hour": "24 saat",
|
||||
"interval_days": "{{count}} gün",
|
||||
"interval_days_plural": "{{count}} gün",
|
||||
"domain": "Alan adı",
|
||||
"answer": "Cevap",
|
||||
"filter_added_successfully": "Filtre başarıyla eklendi",
|
||||
"filter_removed_successfully": "Filtre başarıyla kaldırıldı",
|
||||
"filter_updated": "Filtre başarıyla güncellendi",
|
||||
"punycode": "Punycode",
|
||||
"answer": "Yanıt",
|
||||
"filter_added_successfully": "Liste başarıyla eklendi",
|
||||
"filter_removed_successfully": "Liste başarıyla kaldırıldı",
|
||||
"filter_updated": "Liste başarıyla güncellendi",
|
||||
"statistics_configuration": "İstatistik yapılandırması",
|
||||
"statistics_retention": "İstatistikleri depolama",
|
||||
"statistics_retention_desc": "Zaman değerini azaltırsanız bazı veriler kaybolacaktır",
|
||||
"statistics_retention": "İstatistikleri sakla",
|
||||
"statistics_retention_desc": "Zaman değerini azaltırsanız, bazı veriler kaybolacaktır",
|
||||
"statistics_clear": " İstatistikleri temizle",
|
||||
"statistics_clear_confirm": "İstatistikleri temizlemeyi istediğinizden emin misiniz?",
|
||||
"statistics_retention_confirm": "İstatistik saklama süresini değiştirmek istediğinize emin misiniz? Aralık değerini azaltırsanız, bazı veriler kaybolacaktır",
|
||||
"statistics_clear_confirm": "İstatistikleri temizlemek istediğinizden emin misiniz?",
|
||||
"statistics_retention_confirm": "İstatistik saklama süresini değiştirmek istediğinizden emin misiniz? Zaman değerini azaltırsanız, bazı veriler kaybolacaktır",
|
||||
"statistics_cleared": "İstatistikler başarıyla temizlendi",
|
||||
"interval_hours": "{{count}} saat",
|
||||
"interval_hours_plural": "{{count}} saat",
|
||||
@@ -509,87 +516,89 @@
|
||||
"sign_in": "Giriş yap",
|
||||
"sign_out": "Oturumu kapat",
|
||||
"forgot_password": "Parolanızı mı unuttunuz?",
|
||||
"forgot_password_desc": "Kullanıcı hesabınız için yeni bir parola oluşturmak için lütfen <0>bu adımları</0> takip edin.",
|
||||
"forgot_password_desc": "Kullanıcı hesabınız için yeni bir parola oluşturmak istiyorsanız lütfen <0>bu adımları</0> uygulayın.",
|
||||
"location": "Konum",
|
||||
"orgname": "Organizasyon adı",
|
||||
"orgname": "Kuruluş adı",
|
||||
"netname": "Ağ adı",
|
||||
"network": "Ağ",
|
||||
"descr": "Açıklama",
|
||||
"whois": "Whois",
|
||||
"filtering_rules_learn_more": "Kendi ana bilgisayar listelerinizi oluşturma hakkında <0>daha fazla bilgi edinin</0></0>.",
|
||||
"blocked_by_response": "Cevap olarak CNAME veya IP tarafından engellendi",
|
||||
"filtering_rules_learn_more": "Kendi ana bilgisayar listelerinizi oluşturma hakkında <0>daha fazla bilgi edinin</0>.",
|
||||
"blocked_by_response": "Yanıt olarak CNAME veya IP tarafından engellendi",
|
||||
"blocked_by_cname_or_ip": "CNAME veya IP tarafından engellendi",
|
||||
"try_again": "Tekrar deneyin",
|
||||
"try_again": "Tekrar dene",
|
||||
"domain_desc": "Yeniden yazılmasını istediğiniz alan adını veya joker karakteri girin.",
|
||||
"example_rewrite_domain": "cevapları yalnızca bu alan adı için yeniden yaz.",
|
||||
"example_rewrite_wildcard": "tüm <0>example.org</0> alt alanları için cevapları yeniden yaz.",
|
||||
"example_rewrite_domain": "yanıtları yalnızca bu alan adı için yeniden yaz.",
|
||||
"example_rewrite_wildcard": "tüm <0>example.org</0> alt alanları için yanıtları yeniden yaz.",
|
||||
"rewrite_ip_address": "IP adresi: bu IP'yi A veya AAAA yanıtında kullanın",
|
||||
"rewrite_domain_name": "Alan adı: bir CNAME kaydı ekleyin",
|
||||
"rewrite_A": "<0>A</0>: özel değer, üst sunucudan gelen <0>A</0> kayıtlarını tut",
|
||||
"rewrite_AAAA": "<0>AAA</0>: özel değer, üst sunucudan gelen <0>AAA</0> kayıtlarını tut",
|
||||
"rewrite_A": "<0>A</0>: özel değer, üst sunucudan gelen <0>A</0> kayıtlarını tutun",
|
||||
"rewrite_AAAA": "<0>AAA</0>: özel değer, üst sunucudan gelen <0>AAA</0> kayıtlarını tutun",
|
||||
"disable_ipv6": "IPv6 adreslerinin çözümlenmesini devre dışı bırak",
|
||||
"disable_ipv6_desc": "IPv6 adresleri için tüm DNS sorgularını bırakın (AAAA yazın).",
|
||||
"disable_ipv6_desc": "IPv6 adresleri için tüm DNS sorgularını yanıtsız bırakır (AAAA yazar).",
|
||||
"fastest_addr": "En hızlı IP adresi",
|
||||
"fastest_addr_desc": "Tüm DNS sunucularını sorgulayın ve tüm yanıtlar arasında en hızlı IP adresini döndürün. AdGuard Home'un tüm DNS sunucularından yanıt beklemesi gerektiğinden, bu DNS sorgularını yavaşlatır ancak genel bağlantıyı iyileştirir.",
|
||||
"autofix_warning_text": "\"Düzelt\" i tıklatırsanız, AdGuardHome sisteminizi AdGuardHome DNS sunucusunu kullanacak şekilde yapılandırır.",
|
||||
"fastest_addr_desc": "Tüm DNS sunucularını sorgulayın ve tüm yanıtlar arasından en hızlı olan IP adresini döndürün. AdGuard Home'un tüm DNS sunucularından yanıt beklemesi gerektiği için DNS sorgularını yavaşlatır, ancak genel bağlantıyı iyileştirir.",
|
||||
"autofix_warning_text": "\"Düzelt\" seçeneğine tıklarsanız, AdGuard Home, sisteminizi AdGuard Home DNS sunucusunu kullanacak şekilde yapılandırır.",
|
||||
"autofix_warning_list": "Bu görevleri gerçekleştirecek: <0>Sistem DNSStubListener'ı devre dışı bırakın</0> <0>DNS sunucusu adresini 127.0.0.1 olarak ayarlayın</0> <0>/etc/resolv.conf'un sembolik bağlantı hedefini /run/systemd/resolve/resolv.conf ile değiştirin<0> <0>DNSStubListener'ı durdurun (systemd çözümlenmiş hizmeti yeniden yükleyin)</0>",
|
||||
"autofix_warning_result": "Sonuç olarak, sisteminizden gelen tüm DNS istekleri varsayılan olarak AdGuard Home tarafından işlenecektir.",
|
||||
"tags_title": "Etiketler",
|
||||
"tags_desc": "Müşteriye karşılık gelen etiketleri seçebilirsiniz. Etiketler filtreleme kurallarına dahil edilebilir ve bunları daha doğru uygulamanıza olanak tanır. <0>Daha fazla bilgi edinin</0>",
|
||||
"tags_desc": "İstemciyi tanımlayan etiketleri seçebilirsiniz. Etiketler, filtreleme kurallarına dahil edilebilir ve bunları daha doğru bir şekilde uygulamanıza olanak tanır. <0>Daha fazla bilgi edinin</0>",
|
||||
"form_select_tags": "İstemci etiketlerini seçin",
|
||||
"check_title": "Filtrelemeyi denetleyin",
|
||||
"check_desc": "Ana bilgisayar adının filtrelenip filtrelenmediğini kontrol edin",
|
||||
"check_desc": "Ana bilgisayar adının filtreleme durumunu kontrol edin",
|
||||
"check": "Denetle",
|
||||
"form_enter_host": "Ana bilgisayar adı girin",
|
||||
"filtered_custom_rules": "Özel filtreleme kurallarına göre filtrelendi",
|
||||
"choose_from_list": "Listeden seç",
|
||||
"add_custom_list": "Özel bir liste ekle",
|
||||
"host_whitelisted": "Ana bilgisayar beyaz listeye eklendi",
|
||||
"add_custom_list": "Özel liste ekle",
|
||||
"host_whitelisted": "Ana bilgisayara izin verildi",
|
||||
"check_ip": "IP adresleri: {{ip}}",
|
||||
"check_cname": "CNAME: {{cname}}",
|
||||
"check_reason": "Sebep: {{reason}}",
|
||||
"check_service": "Hizmet adı: {{service}}",
|
||||
"service_name": "Servis adı",
|
||||
"service_name": "Hizmet adı",
|
||||
"check_not_found": "Filtre listelerinizde bulunamadı",
|
||||
"client_confirm_block": "\"{{ip}}\" istemcisini engellemek istediğinizden emin misiniz?",
|
||||
"client_confirm_unblock": "\"{{ip}}\" istemcisinin engellemesini kaldırmak istediğinizden emin misiniz?",
|
||||
"client_blocked": "\"{{ip}}\" istemcisi başarıyla engellendi",
|
||||
"client_unblocked": "\"{{ip}}\" istemcinin engellemesi başarıyla kaldırıldı",
|
||||
"static_ip": "Statik IP adres",
|
||||
"static_ip_desc": "AdGuard Home bir sunucudur, bu nedenle düzgün çalışması için statik bir IP adresine ihtiyaç duyar. Aksi takdirde, bir noktada yönlendiriciniz bu cihaza farklı bir IP adresi atayabilir.",
|
||||
"set_static_ip": "Statik IP adresi ayarlama",
|
||||
"install_static_ok": "İyi haberler! Statik IP adresi zaten yapılandırılmış",
|
||||
"install_static_error": "AdGuard Home, bu ağ arayüzü için otomatik olarak yapılandıramaz. Lütfen bunu manuel olarak nasıl yapacağınıza ilişkin bir talimat arayın.",
|
||||
"install_static_configure": "AdGuard Home, <0>{{ip}}</0> dinamik IP adresinin kullanıldığını tespit etti. Statik adresiniz olarak ayarlanmasını ister misiniz?",
|
||||
"confirm_static_ip": "AdGuard Home, {{ip}} adresini statik IP adresiniz olacak şekilde yapılandıracak. Devam etmek istiyor musunuz?",
|
||||
"static_ip": "Sabit IP adresi",
|
||||
"static_ip_desc": "AdGuard Home bir sunucudur, bu nedenle düzgün çalışması için sabit bir IP adresine ihtiyacı vardır. Aksi takdirde, yönlendiriciniz bir zaman sonra bu cihaza farklı bir IP adresi atayabilir.",
|
||||
"set_static_ip": "Sabit IP adresi ayarla",
|
||||
"install_static_ok": "Güzel haber! Sabit IP adresi zaten yapılandırılmış",
|
||||
"install_static_error": "AdGuard Home, bu ağ arayüzü için otomatik olarak yapılandıramıyor. Lütfen bunu elle nasıl yapacağınızla ilgili talimatlara bakın.",
|
||||
"install_static_configure": "AdGuard Home, <0>{{ip}}</0> dinamik IP adresinin kullanıldığını tespit etti. Sabit adresiniz olarak ayarlanmasını ister misiniz?",
|
||||
"confirm_static_ip": "AdGuard Home, {{ip}} adresini sabit IP adresiniz olacak şekilde yapılandıracaktır. Devam etmek istiyor musunuz?",
|
||||
"list_updated": "{{count}} liste güncellendi",
|
||||
"list_updated_plural": "{{count}} liste güncellendi",
|
||||
"dnssec_enable": "DNSSEC'i etkinleştir",
|
||||
"dnssec_enable_desc": "Gelen DNS sorgularında DNSSEC bayrağını ayarlayın ve sonucu kontrol edin (DNSSEC etkin çözümleyici gereklidir).",
|
||||
"dnssec_enable_desc": "Giden DNS sorguları için DNSSEC özelliğini etkinleştir ve sonucu kontrol et (DNSSEC özellikli çözümleyici gerekli).",
|
||||
"validated_with_dnssec": "DNSSEC ile doğrulandı",
|
||||
"all_queries": "Tüm sorgular",
|
||||
"show_blocked_responses": "Engellendi",
|
||||
"show_whitelisted_responses": "Beyaz listeye eklendi",
|
||||
"show_whitelisted_responses": "İzin verildi",
|
||||
"show_processed_responses": "İşlendi",
|
||||
"blocked_safebrowsing": "Güvenli gezinti tarafından engellendi",
|
||||
"blocked_adult_websites": "Yetişkin içerikli site engellendi",
|
||||
"blocked_threats": "Engellenen Tehditler",
|
||||
"allowed": "İzin verildi",
|
||||
"filtered": "Filtrelenmiş",
|
||||
"allowed": "İzin verilen",
|
||||
"filtered": "Filtrelenen",
|
||||
"rewritten": "Yeniden yazılan",
|
||||
"safe_search": "Güvenli arama",
|
||||
"blocklist": "Engelleme listesi",
|
||||
"milliseconds_abbreviation": "ms",
|
||||
"cache_size": "Önbellek boyutu",
|
||||
"cache_size_desc": "DNS önbelleği boyutu (byte cinsinden)",
|
||||
"cache_size_desc": "DNS önbellek boyutu (bayt cinsinden)",
|
||||
"cache_ttl_min_override": "Minimum TTL'yi değiştir",
|
||||
"cache_ttl_max_override": "Maksimum TTL'yi değiştir",
|
||||
"enter_cache_size": "Önbellek boyutunu girin (byte)",
|
||||
"enter_cache_size": "Önbellek boyutunu girin (bayt)",
|
||||
"enter_cache_ttl_min_override": "Minimum TTL değerini girin (saniye)",
|
||||
"enter_cache_ttl_max_override": "Maksimum TTL değerini girin (saniye)",
|
||||
"cache_ttl_min_override_desc": "DNS yanıtlarını önbelleğe alırken üst sunucusundan alınan kullanım süresi değerini (saniye) uzatın",
|
||||
"cache_ttl_max_override_desc": "DNS önbelleğindeki girişler için maksimum kullanım süresi değerini (saniye) ayarlayın",
|
||||
"cache_ttl_min_override_desc": "DNS yanıtlarını önbelleğe alırken üst sunucudan alınan kullanım süresi değerini uzatın (saniye olarak)",
|
||||
"cache_ttl_max_override_desc": "DNS önbelleğindeki girişler için maksimum kullanım süresi değerini ayarlayın (saniye olarak)",
|
||||
"ttl_cache_validation": "Minimum önbellek TTL değeri, maksimum değerden küçük veya bu değere eşit olmalıdır",
|
||||
"cache_optimistic": "İyimser",
|
||||
"cache_optimistic_desc": "Girişlerin süresi dolduğunda bile AdGuard Home'un önbellekten yanıt vermesini sağlayın ve bunları yenilemeye çalışın.",
|
||||
"filter_category_general": "Genel",
|
||||
"filter_category_security": "Güvenlik",
|
||||
"filter_category_regional": "Bölgesel",
|
||||
@@ -599,10 +608,10 @@
|
||||
"filter_category_regional_desc": "Bölgesel reklamlara ve izleme sunucularına odaklanan listeler",
|
||||
"filter_category_other_desc": "Diğer engelleme listeleri",
|
||||
"setup_config_to_enable_dhcp_server": "DHCP sunucusunu etkinleştirmek için kurulum yapılandırması",
|
||||
"original_response": "Esas yanıt",
|
||||
"original_response": "Gerçek yanıt",
|
||||
"click_to_view_queries": "Sorguları görmek için tıklayın",
|
||||
"port_53_faq_link": "Port 53 genellikle \"DNSStubListener\" veya \"sistemd-resolved\" hizmetler tarafından kullanılır. Lütfen problemin nasıl çözüleceğine ilişkin <0>bu talimatı</0> okuyun.",
|
||||
"adg_will_drop_dns_queries": "AdGuard Home, bu istemciden gelen tüm DNS sorgularını iptal eder.",
|
||||
"client_not_in_allowed_clients": "İstemciye \"İzin verilen istemciler\" listesinde olmadığı için izin verilmiyor.",
|
||||
"port_53_faq_link": "53 numaralı bağlantı noktası genellikle \"DNSStubListener\" veya \"systemd-resolved\" hizmetleri tarafından kullanılır. Lütfen bu sorunun nasıl çözüleceğine ilişkin <0>bu talimatı</0> okuyun.",
|
||||
"adg_will_drop_dns_queries": "AdGuard Home, bu istemciden gelen tüm DNS sorgularını yok sayar.",
|
||||
"client_not_in_allowed_clients": "\"İzin verilen istemciler\" listesinde olmadığı için istemciye izin verilmiyor.",
|
||||
"experimental": "Deneysel"
|
||||
}
|
||||
|
||||
@@ -68,6 +68,9 @@
|
||||
"dhcp_new_static_lease": "新建静态租约",
|
||||
"dhcp_static_leases_not_found": "未找到 DHCP 静态租约",
|
||||
"dhcp_add_static_lease": "添加静态租约",
|
||||
"dhcp_reset_leases": "重置所有租约",
|
||||
"dhcp_reset_leases_confirm": "您确定您想重置所有租约吗?",
|
||||
"dhcp_reset_leases_success": "成功重置了 DHCP 租约",
|
||||
"dhcp_reset": "您确定要重置 DHCP 设定吗?",
|
||||
"country": "国家",
|
||||
"city": "城市",
|
||||
@@ -109,6 +112,8 @@
|
||||
"for_last_24_hours": "在过去 24 小时",
|
||||
"for_last_days": "最近 {{count}} 天",
|
||||
"for_last_days_plural": "最近 {{count}} 天",
|
||||
"stats_disabled": "已禁用统计数据。您可以从<0>设置页面</0>打开它。",
|
||||
"stats_disabled_short": "已禁用统计数据",
|
||||
"no_domains_found": "未找到域名",
|
||||
"requests_count": "请求数",
|
||||
"top_blocked_domains": "被拦截域名排行",
|
||||
@@ -324,7 +329,7 @@
|
||||
"install_devices_windows_list_3": "在窗口的左侧找到 ”更改适配器设置“ 并点击进入。",
|
||||
"install_devices_windows_list_4": "选择您正在连接的网络设备,右击它并选择 ”属性“ 。",
|
||||
"install_devices_windows_list_5": "在列表中找到 ”Internet 协议版本 4 (TCP/IPv4)“ ,选择并再次点击 ”属性“ 。",
|
||||
"install_devices_windows_list_6": "选择 ”使用下面的 DNS 服务器地址“ ,并输入您的 AdGuard Home 服务器地址。",
|
||||
"install_devices_windows_list_6": "选择“使用下面的 DNS 服务器地址”,并输入您的 AdGuard Home 服务器地址。",
|
||||
"install_devices_macos_list_1": "点击苹果图标,进入 ”系统首选项“。",
|
||||
"install_devices_macos_list_2": "点击 ”网络“ 。",
|
||||
"install_devices_macos_list_3": "选择在列表中的第一个连接,并点击 ”高级“ 。",
|
||||
@@ -423,9 +428,9 @@
|
||||
"access_title": "访问设置",
|
||||
"access_desc": "您可在此处配置 AdGuard Home DNS 服务器的访问规则。",
|
||||
"access_allowed_title": "允许的客户端",
|
||||
"access_allowed_desc": "CIDR 或 IP 地址列表。如配置,则 AdGuard Home 仅会接受源自这些 IP 地址的请求。",
|
||||
"access_allowed_desc": "CIDR、IP 地址或客户端 ID 的列表。如已配置,则 AdGuard Home 将仅接受来自这些客户端的请求。",
|
||||
"access_disallowed_title": "不允许的客户端",
|
||||
"access_disallowed_desc": "CIDR 或 IP 地址列表。如配置,则 AdGuard Home 会放弃源自这些 IP 地址的请求。",
|
||||
"access_disallowed_desc": "CIDR、IP 地址或客户端 ID 的列表。如果已配置,则 AdGuard Home 将丢弃来自这些 IP 地址的请求。如果允许的客户端已配置,此字段将会被忽略。",
|
||||
"access_blocked_title": "不允许的域名",
|
||||
"access_blocked_desc": "不要将此功能与过滤器混淆。AdGuard Home 将排除匹配这些网域的 DNS 查询,并且这些查询将不会在查询日志中显示。在此可以明确指定域名、通配符(wildcard)和网址过滤的规则,例如 \"example.org\"、\"*.example.org\" 或 \"||example.org^\"。",
|
||||
"access_settings_saved": "访问设置保存成功",
|
||||
@@ -481,10 +486,12 @@
|
||||
"encryption_key_source_content": "粘贴私钥内容",
|
||||
"stats_params": "统计配置",
|
||||
"config_successfully_saved": "配置保存成功",
|
||||
"interval_6_hour": "6 小时",
|
||||
"interval_24_hour": "24 小时",
|
||||
"interval_days": "{{count}} 天",
|
||||
"interval_days_plural": "{{count}} 天",
|
||||
"domain": "域名",
|
||||
"punycode": "Punycode",
|
||||
"answer": "应答",
|
||||
"filter_added_successfully": "已成功添加过滤器",
|
||||
"filter_removed_successfully": "已成功删除该列表",
|
||||
@@ -590,6 +597,8 @@
|
||||
"cache_ttl_min_override_desc": "缓存 DNS 响应时,延长从上游服务器接收到的 TTL 值 (秒)",
|
||||
"cache_ttl_max_override_desc": "设定 DNS 缓存条目的最大 TTL 值(秒)",
|
||||
"ttl_cache_validation": "最小缓存TTL值必须小于或等于最大值",
|
||||
"cache_optimistic": "优化的",
|
||||
"cache_optimistic_desc": "即使条目已过期,也让 AdGuard Home 从缓存中响应,并尝试刷新它们。",
|
||||
"filter_category_general": "常规",
|
||||
"filter_category_security": "安全",
|
||||
"filter_category_regional": "区域",
|
||||
|
||||
@@ -9,10 +9,14 @@
|
||||
"bootstrap_dns": "引導(Boostrap) DNS 伺服器",
|
||||
"bootstrap_dns_desc": "Bootstrap DNS 伺服器用於解析您所設定的上游 DoH/DoT 解析器的 IP 地址",
|
||||
"local_ptr_title": "私人 DNS 伺服器",
|
||||
"local_ptr_desc": "AdGuard Home 用於區域 PTR 查詢的 DNS 伺服器。該伺服器將被用於解析具有私人 IP 位址的用戶端的主機名稱,比如 \"192.168.12.34\",使用 rDNS。如果沒有設定,AdGuard Home 將自動使用您的預設 DNS 解析器。",
|
||||
"local_ptr_desc": "AdGuard Home 用於區域 PTR 查詢的 DNS 伺服器。這些伺服器將用於解析具有私人 IP 位址的用戶端的主機名稱,例如 \"192.168.12.34\",使用 rDNS。如果沒有設定,AdGuard Home 將自動使用您的系統預設 DNS 解析。",
|
||||
"local_ptr_default_resolver": "AdGuard Home 預設使用以下作為 DNS 反解器:{{ip}}",
|
||||
"local_ptr_no_default_resolver": "AdGuard Home 無法為此系統確定適合私有反解析器。",
|
||||
"local_ptr_placeholder": "每行輸入一個伺服器位址",
|
||||
"resolve_clients_title": "啟用用戶端的 IP 位址的反向解析",
|
||||
"resolve_clients_desc": "如果啟用,AdGuard Home 將嘗試透過傳送 PTR 查詢到對應的解析器 (本機用戶端的私人 DNS 伺服器,公有 IP 用戶端的上遊伺服器) ,將 IP 位址反向解析成其用戶端的主機名稱。",
|
||||
"resolve_clients_desc": "透過相應的伺服器傳送 PTR 查詢(本機用戶端使用私人 DNS 伺服器,公共 IP 使用上游伺服器),將客戶端的 IP 反解為主機名稱。",
|
||||
"use_private_ptr_resolvers_title": "使用私人 DNS 反解器",
|
||||
"use_private_ptr_resolvers_desc": "使用這些上游為本機提供反解 DNS 查詢。如果禁用 AdGuard Home 將會對相關 PTR 請求回應 NXDOMAIN,但 DHCP、/etc/hosts 等已知的用戶端除外。",
|
||||
"check_dhcp_servers": "檢查 DHCP 伺服器",
|
||||
"save_config": "儲存設定",
|
||||
"enabled_dhcp": "DHCP 伺服器已啟動",
|
||||
@@ -64,6 +68,9 @@
|
||||
"dhcp_new_static_lease": "新增靜態租用",
|
||||
"dhcp_static_leases_not_found": "找不到 DHCP 靜態租用",
|
||||
"dhcp_add_static_lease": "新增靜態租用",
|
||||
"dhcp_reset_leases": "重置所有 DHCP 租約",
|
||||
"dhcp_reset_leases_confirm": "您確定要重設所有 DHCP 租約嗎?",
|
||||
"dhcp_reset_leases_success": "重置 DHCP 租約成功",
|
||||
"dhcp_reset": "您確定要重設 DHCP 設定嗎?",
|
||||
"country": "國家",
|
||||
"city": "城市",
|
||||
@@ -105,6 +112,8 @@
|
||||
"for_last_24_hours": "過去 24 小時",
|
||||
"for_last_days": "最近 {{count}} 天內",
|
||||
"for_last_days_plural": "最近 {{count}} 天內",
|
||||
"stats_disabled": "已禁用統計資料。您可以從<0>設定頁面</0>打開它。",
|
||||
"stats_disabled_short": "已禁用統計資料",
|
||||
"no_domains_found": "找不到網域",
|
||||
"requests_count": "查詢次數",
|
||||
"top_blocked_domains": "熱門封鎖網域排行",
|
||||
@@ -128,7 +137,7 @@
|
||||
"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。",
|
||||
"enforce_save_search_hint": "AdGuard Home 可在下列搜尋引擎使用強制安全搜尋:Google、YouTube、Bing、DuckDuckGo, Yandex 和 Pixabay。",
|
||||
"no_servers_specified": "沒有指定的伺服器",
|
||||
"general_settings": "一般設定",
|
||||
"dns_settings": "DNS 設定",
|
||||
@@ -268,7 +277,7 @@
|
||||
"form_enter_rate_limit": "輸入速率限制",
|
||||
"rate_limit": "速率限制",
|
||||
"edns_enable": "啟用 EDNS Client Subnet",
|
||||
"edns_cs_desc": "開啟後 AdGuard Home 將會傳送用戶端的子網路給 DNS 伺服器。",
|
||||
"edns_cs_desc": "傳送用戶端的子網路給 DNS 伺服器。",
|
||||
"rate_limit_desc": "限制單一裝置每秒發出的查詢次數(設定為 0 即表示無限制)",
|
||||
"blocking_ipv4_desc": "回覆指定 IPv4 位址給被封鎖的網域的 A 紀錄查詢",
|
||||
"blocking_ipv6_desc": "回覆指定 IPv6 位址給被封鎖的網域的 AAAA 紀錄查詢",
|
||||
@@ -307,7 +316,7 @@
|
||||
"install_devices_title": "設定您的裝置",
|
||||
"install_devices_desc": "要開始使用 AdGuard Home,您需要設定好裝置才能使用。",
|
||||
"install_submit_title": "恭喜!",
|
||||
"install_submit_desc": "安裝步驟已完成,現在已經可以開始使用 AdGuard Home",
|
||||
"install_submit_desc": "安裝步驟已完成,現在您已經可以開始使用 AdGuard Home",
|
||||
"install_devices_router": "路由器",
|
||||
"install_devices_router_desc": "使用此設定後,所有連接家中路由器的裝置都會自動套用,無須在每台裝置上個別設定。",
|
||||
"install_devices_address": "AdGuard Home DNS 伺服器正在監聽以下位址",
|
||||
@@ -399,7 +408,7 @@
|
||||
"client_edit": "編輯用戶端",
|
||||
"client_identifier": "識別碼",
|
||||
"ip_address": "IP 位址",
|
||||
"client_identifier_desc": "可通過 IP 地址、CIDR、MAC 地址來辨識使用者裝置。注意:必須使用 AdGuard Home 內建 <0>DHCP 伺服器</0> 才能偵測 MAC 地址。",
|
||||
"client_identifier_desc": "可通過 IP 地址、CIDR、MAC 地址來辨識使用者裝置,或使用個別客戶端 ID (可用於 DoT/DoH/DoQ)。<0>點擊這裡</0>進一步了解關於辨識使用者裝置。",
|
||||
"form_enter_ip": "輸入 IP",
|
||||
"form_enter_subnet_ip": "在子網路 \"{{cidr}}\" 中輸入一個 IP 位址",
|
||||
"form_enter_mac": "輸入 MAC 地址",
|
||||
@@ -477,10 +486,12 @@
|
||||
"encryption_key_source_content": "貼上私鑰內容",
|
||||
"stats_params": "統計資料設定",
|
||||
"config_successfully_saved": "已儲存設定",
|
||||
"interval_6_hour": "6 小時",
|
||||
"interval_24_hour": "24 小時",
|
||||
"interval_days": "{{count}} 天",
|
||||
"interval_days_plural": "{{count}} 天",
|
||||
"domain": "網域",
|
||||
"punycode": "Punycode",
|
||||
"answer": "回應",
|
||||
"filter_added_successfully": "已成功新增清單",
|
||||
"filter_removed_successfully": "已成功移除清單",
|
||||
@@ -523,7 +534,7 @@
|
||||
"rewrite_domain_name": "網域名稱:新增一筆 CNAME 紀錄",
|
||||
"rewrite_A": "<0>A</0>: 特殊值,將上游查詢結果覆寫 <0>A</0> 紀錄",
|
||||
"rewrite_AAAA": "<0>AAAA</0>: 特殊值,將上游查詢結果覆寫 <0>AAAA</0> 紀錄",
|
||||
"disable_ipv6": "停用 IPv6",
|
||||
"disable_ipv6": "停止解析 IPv6 位址",
|
||||
"disable_ipv6_desc": "開啟此功能後,將捨棄所有對 IPv6 位址(AAAA)的查詢。",
|
||||
"fastest_addr": "Fastest IP 位址",
|
||||
"fastest_addr_desc": "從所有 DNS 伺服器查詢中回應最快的 IP 位址。但這操作會等待所有 DNS 查詢結果後才能回應,導致速度有所降低,不過同時卻也改善了整體連線品質。",
|
||||
@@ -586,6 +597,8 @@
|
||||
"cache_ttl_min_override_desc": "快取 DNS 回應時,延長從上游伺服器收到的 TTL 值 (秒)",
|
||||
"cache_ttl_max_override_desc": "設定 DNS 快取條目的最大 TTL 值(秒)",
|
||||
"ttl_cache_validation": "最小快取 TTL 值必須小於或等於最大值",
|
||||
"cache_optimistic": "優化的",
|
||||
"cache_optimistic_desc": "即使條目已過期,也讓 AdGuard Home 從快取中回應,並嘗試刷新它們。",
|
||||
"filter_category_general": "一般",
|
||||
"filter_category_security": "安全性",
|
||||
"filter_category_regional": "區域性",
|
||||
|
||||
@@ -67,7 +67,10 @@
|
||||
"dhcp_lease_deleted": "靜態租約 \"{{key}}\" 被成功地刪除",
|
||||
"dhcp_new_static_lease": "新的靜態租約",
|
||||
"dhcp_static_leases_not_found": "無已發現之動態主機設定協定(DHCP)靜態租約",
|
||||
"dhcp_add_static_lease": "增加靜態租約",
|
||||
"dhcp_add_static_lease": "新增靜態租約",
|
||||
"dhcp_reset_leases": "重置所有的租約",
|
||||
"dhcp_reset_leases_confirm": "您確定您想要重置所有的租約嗎?",
|
||||
"dhcp_reset_leases_success": "動態主機設定協定(DHCP)租約被成功地重置",
|
||||
"dhcp_reset": "您確定您想要重置動態主機設定協定(DHCP)配置嗎?",
|
||||
"country": "國家",
|
||||
"city": "城市",
|
||||
@@ -109,6 +112,8 @@
|
||||
"for_last_24_hours": "在最近的 24 小時內",
|
||||
"for_last_days": "在最近的 {{count}} 日內",
|
||||
"for_last_days_plural": "在最近的 {{count}} 日內",
|
||||
"stats_disabled": "該統計資料已被禁用。您可從<0>設定頁面</0>中打開它。",
|
||||
"stats_disabled_short": "該統計資料已被禁用",
|
||||
"no_domains_found": "無已發現之網域",
|
||||
"requests_count": "請求總數",
|
||||
"top_blocked_domains": "熱門已封鎖的網域",
|
||||
@@ -170,8 +175,8 @@
|
||||
"filters_and_hosts_hint": "AdGuard Home 懂得基本的廣告封鎖規則和主機檔案語法。",
|
||||
"no_blocklist_added": "無已加入的封鎖清單",
|
||||
"no_whitelist_added": "無已加入的允許清單",
|
||||
"add_blocklist": "增加封鎖清單",
|
||||
"add_allowlist": "增加允許清單",
|
||||
"add_blocklist": "新增封鎖清單",
|
||||
"add_allowlist": "新增允許清單",
|
||||
"cancel_btn": "取消",
|
||||
"enter_name_hint": "輸入名稱",
|
||||
"enter_url_or_path_hint": "輸入一個該清單之網址或絕對的路徑",
|
||||
@@ -321,10 +326,10 @@
|
||||
"install_devices_router_list_4": "於某些路由器機型上,自訂的 DNS 伺服器無法被設置。在這種情況下,設置 AdGuard Home 作為 <0>DHCP</0> 伺服器可能有所幫助。否則,您應查明有關如何對您的特定路由器型號自訂 DNS 伺服器之路由器用法說明。",
|
||||
"install_devices_windows_list_1": "通過開始功能表或 Windows 搜尋,開啟控制台。",
|
||||
"install_devices_windows_list_2": "去網路和網際網路類別,然後去網路和共用中心。",
|
||||
"install_devices_windows_list_3": "於畫面之左側上找到變更介面卡設定並向它點擊。",
|
||||
"install_devices_windows_list_3": "於畫面之左側上找到\"變更介面卡設定\"並向它點擊。",
|
||||
"install_devices_windows_list_4": "選擇您現行的連線,向它點擊滑鼠右鍵,然後選擇內容。",
|
||||
"install_devices_windows_list_5": "在清單中找到網際網路通訊協定第 4 版(TCP/IPv4),選擇它,然後再次向內容點擊。",
|
||||
"install_devices_windows_list_6": "選擇使用下列的 DNS 伺服器位址,然後輸入您的 AdGuard Home 伺服器位址。",
|
||||
"install_devices_windows_list_5": "在清單中找到\"網際網路通訊協定第 4 版(TCP/IPv4)\"[或用於 IPv6,\"網際網路通訊協定第 6 版(TCP/IPv6)\"],選擇它,然後再次向內容點擊。",
|
||||
"install_devices_windows_list_6": "選擇\"使用下列的 DNS 伺服器位址\",然後輸入您的 AdGuard Home 伺服器位址。",
|
||||
"install_devices_macos_list_1": "向 Apple 圖像點擊,然後去系統偏好設定。",
|
||||
"install_devices_macos_list_2": "向網路點擊。",
|
||||
"install_devices_macos_list_3": "選擇在您的清單中之首要的連線,然後點擊進階的。",
|
||||
@@ -398,7 +403,7 @@
|
||||
"table_client": "用戶端",
|
||||
"table_name": "名稱",
|
||||
"save_btn": "儲存",
|
||||
"client_add": "增加用戶端",
|
||||
"client_add": "新增用戶端",
|
||||
"client_new": "新的用戶端",
|
||||
"client_edit": "編輯用戶端",
|
||||
"client_identifier": "識別碼",
|
||||
@@ -408,7 +413,7 @@
|
||||
"form_enter_subnet_ip": "在子網路 \"{{cidr}}\" 中輸入一組 IP 位址",
|
||||
"form_enter_mac": "輸入媒體存取控制(MAC)",
|
||||
"form_enter_id": "輸入識別碼",
|
||||
"form_add_id": "增加識別碼",
|
||||
"form_add_id": "新增識別碼",
|
||||
"form_client_name": "輸入用戶端名稱",
|
||||
"name": "名稱",
|
||||
"client_global_settings": "使用全域的設定",
|
||||
@@ -423,9 +428,9 @@
|
||||
"access_title": "存取設定",
|
||||
"access_desc": "於此您可配置用於 AdGuard Home DNS 伺服器之存取規則。",
|
||||
"access_allowed_title": "已允許的用戶端",
|
||||
"access_allowed_desc": "無類別網域間路由(CIDR)或 IP 位址之清單。如果被配置,AdGuard Home 將接受僅來自這些 IP 位址的請求。",
|
||||
"access_allowed_desc": "無類別網域間路由(CIDRs)、IP 位址或用戶端 ID 之清單。如果被配置,AdGuard Home 將接受僅來自這些用戶端的請求。",
|
||||
"access_disallowed_title": "未被允許的用戶端",
|
||||
"access_disallowed_desc": "無類別網域間路由(CIDR)或 IP 位址之清單。如果被配置,AdGuard Home 將排除來自這些 IP 位址的請求。",
|
||||
"access_disallowed_desc": "無類別網域間路由(CIDRs)、IP 位址或用戶端 IDs 之清單。如果被配置,AdGuard Home 將排除來自這些用戶端的請求。如果已允許的用戶端被配置,此欄位被忽略。",
|
||||
"access_blocked_title": "未被允許的網域",
|
||||
"access_blocked_desc": "不要把這個和過濾器混淆。AdGuard Home 排除與這些網域相符的 DNS 查詢,且這些查詢甚至不會出現在查詢記錄中。您可相應地明確指定確切的域名、萬用字元(wildcard)或網址過濾器的規則,例如,\"example.org\"、\"*.example.org\" 或 \"||example.org^\"。",
|
||||
"access_settings_saved": "存取設定被成功地儲存",
|
||||
@@ -436,10 +441,10 @@
|
||||
"setup_dns_privacy_1": "<0>DNS-over-TLS:</0>使用 <1>{{address}}</1> 字串。",
|
||||
"setup_dns_privacy_2": "<0>DNS-over-HTTPS:</0>使用 <1>{{address}}</1> 字串。",
|
||||
"setup_dns_privacy_3": "<0>這裡是您可使用的軟體之清單。</0>",
|
||||
"setup_dns_privacy_4": "於 iOS 14 或 macOS Big Sur 裝置上,您可下載增加 <highlight>DNS-over-HTTPS</highlight> 或 <highlight>DNS-over-TLS</highlight> 伺服器到 DNS 設定之特殊的 '.mobileconfig' 檔案。",
|
||||
"setup_dns_privacy_4": "於 iOS 14 或 macOS Big Sur 裝置上,您可下載新增 <highlight>DNS-over-HTTPS</highlight> 或 <highlight>DNS-over-TLS</highlight> 伺服器到 DNS 設定之特殊的 '.mobileconfig' 檔案。",
|
||||
"setup_dns_privacy_android_1": "Android 9 原生地支援 DNS-over-TLS。為了配置它,去設定 → 網路 & 網際網路 → 進階 → 私人 DNS 並在那輸入您的域名。",
|
||||
"setup_dns_privacy_android_2": "<0>AdGuard for Android</0> 支援 <1>DNS-over-HTTPS</1> 和 <1>DNS-over-TLS</1>。",
|
||||
"setup_dns_privacy_android_3": "<0>Intra</0> 對 Android 增加 <1>DNS-over-HTTPS</1> 支援。",
|
||||
"setup_dns_privacy_android_3": "<0>Intra</0> 對 Android 新增 <1>DNS-over-HTTPS</1> 支援。",
|
||||
"setup_dns_privacy_ios_1": "<0>DNSCloak</0> 支援 <1>DNS-over-HTTPS</1>,但為了配置它以使用您自己的伺服器,您將需要為它產生一個 <2>DNS 戳記</2>。",
|
||||
"setup_dns_privacy_ios_2": "<0>AdGuard for iOS</0> 支援 <1>DNS-over-HTTPS</1> 和 <1>DNS-over-TLS</1> 設置。",
|
||||
"setup_dns_privacy_other_title": "其它的執行",
|
||||
@@ -452,7 +457,7 @@
|
||||
"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 改寫被成功地刪除",
|
||||
"rewrite_add": "增加 DNS 改寫",
|
||||
"rewrite_add": "新增 DNS 改寫",
|
||||
"rewrite_not_found": "無已發現之 DNS 改寫",
|
||||
"rewrite_confirm_delete": "您確定您想要刪除對於 \"{{key}}\" 之 DNS 改寫嗎?",
|
||||
"rewrite_desc": "允許輕易地配置自訂的 DNS 回應供特定的域名。",
|
||||
@@ -481,10 +486,12 @@
|
||||
"encryption_key_source_content": "貼上該私密金鑰內容",
|
||||
"stats_params": "統計資料配置",
|
||||
"config_successfully_saved": "配置被成功地儲存",
|
||||
"interval_6_hour": "6 小時",
|
||||
"interval_24_hour": "24 小時",
|
||||
"interval_days": "{{count}} 日",
|
||||
"interval_days_plural": "{{count}} 日",
|
||||
"domain": "網域",
|
||||
"punycode": "國際化域名代碼(Punycode)",
|
||||
"answer": "回應",
|
||||
"filter_added_successfully": "該清單已被成功地加入",
|
||||
"filter_removed_successfully": "該清單已被成功地移除",
|
||||
@@ -524,7 +531,7 @@
|
||||
"example_rewrite_domain": "僅對於此域名改寫回應。",
|
||||
"example_rewrite_wildcard": "對於所有的 <0>example.org</0> 子網域改寫回應。",
|
||||
"rewrite_ip_address": "IP 位址:在一個 A 或 AAAA 回應中使用此 IP",
|
||||
"rewrite_domain_name": "域名:增加一筆正規名稱(CNAME)記錄",
|
||||
"rewrite_domain_name": "域名:新增一筆正規名稱(CNAME)記錄",
|
||||
"rewrite_A": "<0>A</0>:特殊的數值,阻止 <0>A</0> 記錄免於該上游",
|
||||
"rewrite_AAAA": "<0>AAAA</0>:特殊的數值,阻止 <0>AAAA</0> 記錄免於該上游",
|
||||
"disable_ipv6": "禁用 IPv6 位址之解析",
|
||||
@@ -543,7 +550,7 @@
|
||||
"form_enter_host": "輸入主機名稱",
|
||||
"filtered_custom_rules": "被自訂的過濾規則過濾",
|
||||
"choose_from_list": "從該清單中選擇",
|
||||
"add_custom_list": "增加一個自訂的清單",
|
||||
"add_custom_list": "新增一個自訂的清單",
|
||||
"host_whitelisted": "該主機被允許",
|
||||
"check_ip": "IP 位址:{{ip}}",
|
||||
"check_cname": "正規名稱(CNAME):{{cname}}",
|
||||
@@ -590,12 +597,14 @@
|
||||
"cache_ttl_min_override_desc": "當快取 DNS 回應時,延長從上游的伺服器收到的短存活時間數值(秒)",
|
||||
"cache_ttl_max_override_desc": "設定最大的存活時間數值(秒)供在 DNS 快取中的項目",
|
||||
"ttl_cache_validation": "最小的快取存活時間(TTL)數值必須小於或等於最大的數值",
|
||||
"cache_optimistic": "樂觀快取模式",
|
||||
"cache_optimistic_desc": "即使當項目為已到期的,從快取使 AdGuard Home 回覆,並還嘗試重新整理它們。",
|
||||
"filter_category_general": "一般的",
|
||||
"filter_category_security": "安全性",
|
||||
"filter_category_regional": "區域性的",
|
||||
"filter_category_other": "其它的",
|
||||
"filter_category_general_desc": "封鎖大多數朝向裝置的追蹤和廣告之清單",
|
||||
"filter_category_security_desc": "特別地封鎖惡意、網路釣魚和詐騙的網域之清單",
|
||||
"filter_category_security_desc": "專門地封鎖惡意、網路釣魚和詐騙的網域之清單",
|
||||
"filter_category_regional_desc": "專注於區域性的廣告和追蹤伺服器之清單",
|
||||
"filter_category_other_desc": "其它的封鎖清單",
|
||||
"setup_config_to_enable_dhcp_server": "設置配置以啟用 DHCP 伺服器",
|
||||
|
||||
@@ -547,6 +547,22 @@ export const resetDhcp = () => async (dispatch) => {
|
||||
}
|
||||
};
|
||||
|
||||
export const resetDhcpLeasesRequest = createAction('RESET_DHCP_LEASES_REQUEST');
|
||||
export const resetDhcpLeasesSuccess = createAction('RESET_DHCP_LEASES_SUCCESS');
|
||||
export const resetDhcpLeasesFailure = createAction('RESET_DHCP_LEASES_FAILURE');
|
||||
|
||||
export const resetDhcpLeases = () => async (dispatch) => {
|
||||
dispatch(resetDhcpLeasesRequest());
|
||||
try {
|
||||
const status = await apiClient.resetDhcpLeases();
|
||||
dispatch(resetDhcpLeasesSuccess(status));
|
||||
dispatch(addSuccessToast('dhcp_reset_leases_success'));
|
||||
} catch (error) {
|
||||
dispatch(addErrorToast({ error }));
|
||||
dispatch(resetDhcpLeasesFailure());
|
||||
}
|
||||
};
|
||||
|
||||
export const toggleLeaseModal = createAction('TOGGLE_LEASE_MODAL');
|
||||
|
||||
export const addStaticLeaseRequest = createAction('ADD_STATIC_LEASE_REQUEST');
|
||||
|
||||
@@ -264,6 +264,8 @@ class Api {
|
||||
|
||||
DHCP_RESET = { path: 'dhcp/reset', method: 'POST' };
|
||||
|
||||
DHCP_LEASES_RESET = { path: 'dhcp/reset_leases', method: 'POST' };
|
||||
|
||||
getDhcpStatus() {
|
||||
const { path, method } = this.DHCP_STATUS;
|
||||
return this.makeRequest(path, method);
|
||||
@@ -315,6 +317,11 @@ class Api {
|
||||
return this.makeRequest(path, method);
|
||||
}
|
||||
|
||||
resetDhcpLeases() {
|
||||
const { path, method } = this.DHCP_LEASES_RESET;
|
||||
return this.makeRequest(path, method);
|
||||
}
|
||||
|
||||
// Installation
|
||||
INSTALL_GET_ADDRESSES = { path: 'install/get_addresses', method: 'GET' };
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import Card from '../ui/Card';
|
||||
import Cell from '../ui/Cell';
|
||||
|
||||
import { getPercent, sortIp } from '../../helpers/helpers';
|
||||
import { BLOCK_ACTIONS, R_CLIENT_ID, STATUS_COLORS } from '../../helpers/constants';
|
||||
import { BLOCK_ACTIONS, STATUS_COLORS } from '../../helpers/constants';
|
||||
import { toggleClientBlock } from '../../actions/access';
|
||||
import { renderFormattedClientCell } from '../../helpers/renderFormattedClientCell';
|
||||
import { getStats } from '../../actions/stats';
|
||||
@@ -35,10 +35,6 @@ const CountCell = (row) => {
|
||||
};
|
||||
|
||||
const renderBlockingButton = (ip, disallowed, disallowed_rule) => {
|
||||
if (R_CLIENT_ID.test(ip)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const dispatch = useDispatch();
|
||||
const { t } = useTranslation();
|
||||
const processingSet = useSelector((state) => state.access.processingSet);
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { withTranslation, Trans } from 'react-i18next';
|
||||
|
||||
import StatsCard from './StatsCard';
|
||||
import { getPercent, normalizeHistory } from '../../helpers/helpers';
|
||||
import { RESPONSE_FILTER } from '../../helpers/constants';
|
||||
|
||||
const getNormalizedHistory = (data, interval, id) => [
|
||||
{ data: normalizeHistory(data, interval), id },
|
||||
@@ -25,7 +27,7 @@ const Statistics = ({
|
||||
<StatsCard
|
||||
total={numDnsQueries}
|
||||
lineData={getNormalizedHistory(dnsQueries, interval, 'dnsQuery')}
|
||||
title={<Trans>dns_query</Trans>}
|
||||
title={<Link to="logs"><Trans>dns_query</Trans></Link>}
|
||||
color="blue"
|
||||
/>
|
||||
</div>
|
||||
@@ -34,7 +36,7 @@ const Statistics = ({
|
||||
total={numBlockedFiltering}
|
||||
lineData={getNormalizedHistory(blockedFiltering, interval, 'blockedFiltering')}
|
||||
percent={getPercent(numDnsQueries, numBlockedFiltering)}
|
||||
title={<Trans components={[<a href="#filters" key="0">link</a>]}>blocked_by</Trans>}
|
||||
title={<Trans components={[<Link to={`logs?response_status=${RESPONSE_FILTER.BLOCKED.QUERY}`} key="0">link</Link>]}>blocked_by</Trans>}
|
||||
color="red"
|
||||
/>
|
||||
</div>
|
||||
@@ -47,7 +49,7 @@ const Statistics = ({
|
||||
'replacedSafebrowsing',
|
||||
)}
|
||||
percent={getPercent(numDnsQueries, numReplacedSafebrowsing)}
|
||||
title={<Trans>stats_malware_phishing</Trans>}
|
||||
title={<Link to={`logs?response_status=${RESPONSE_FILTER.BLOCKED_THREATS.QUERY}`}><Trans>stats_malware_phishing</Trans></Link>}
|
||||
color="green"
|
||||
/>
|
||||
</div>
|
||||
@@ -56,7 +58,7 @@ const Statistics = ({
|
||||
total={numReplacedParental}
|
||||
lineData={getNormalizedHistory(replacedParental, interval, 'replacedParental')}
|
||||
percent={getPercent(numDnsQueries, numReplacedParental)}
|
||||
title={<Trans>stats_adult</Trans>}
|
||||
title={<Link to={`logs?response_status=${RESPONSE_FILTER.BLOCKED_ADULT_WEBSITES.QUERY}`}><Trans>stats_adult</Trans></Link>}
|
||||
color="yellow"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { HashLink as Link } from 'react-router-hash-link';
|
||||
import { Trans, useTranslation } from 'react-i18next';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import Statistics from './Statistics';
|
||||
import Counters from './Counters';
|
||||
import Clients from './Clients';
|
||||
import QueriedDomains from './QueriedDomains';
|
||||
import BlockedDomains from './BlockedDomains';
|
||||
import { SETTINGS_URLS } from '../../helpers/constants';
|
||||
|
||||
import PageTitle from '../ui/PageTitle';
|
||||
import Loading from '../ui/Loading';
|
||||
@@ -34,6 +37,16 @@ const Dashboard = ({
|
||||
getAllStats();
|
||||
}, []);
|
||||
|
||||
const getSubtitle = () => {
|
||||
if (stats.interval === 0) {
|
||||
return t('stats_disabled_short');
|
||||
}
|
||||
|
||||
return stats.interval === 1
|
||||
? t('for_last_24_hours')
|
||||
: t('for_last_days', { count: stats.interval });
|
||||
};
|
||||
|
||||
const buttonText = protectionEnabled ? 'disable_protection' : 'enable_protection';
|
||||
|
||||
const buttonClass = classNames('btn btn-sm dashboard-title__button', {
|
||||
@@ -52,14 +65,12 @@ const Dashboard = ({
|
||||
</svg>
|
||||
</button>;
|
||||
|
||||
const subtitle = stats.interval === 1
|
||||
? t('for_last_24_hours')
|
||||
: t('for_last_days', { count: stats.interval });
|
||||
|
||||
const statsProcessing = stats.processingStats
|
||||
|| stats.processingGetConfig
|
||||
|| access.processing;
|
||||
|
||||
const subtitle = getSubtitle();
|
||||
|
||||
return <>
|
||||
<PageTitle title={t('dashboard')} containerClass="page-title--dashboard">
|
||||
<button
|
||||
@@ -81,6 +92,20 @@ const Dashboard = ({
|
||||
{statsProcessing && <Loading />}
|
||||
{!statsProcessing && <div className="row row-cards dashboard">
|
||||
<div className="col-lg-12">
|
||||
{stats.interval === 0 && (
|
||||
<div className="alert alert-warning" role="alert">
|
||||
<Trans components={[
|
||||
<Link
|
||||
to={`${SETTINGS_URLS.settings}#stats-config`}
|
||||
key="0"
|
||||
>
|
||||
link
|
||||
</Link>,
|
||||
]}>
|
||||
stats_disabled
|
||||
</Trans>
|
||||
</div>
|
||||
)}
|
||||
<Statistics
|
||||
interval={stats.interval}
|
||||
dnsQueries={stats.dnsQueries}
|
||||
|
||||
@@ -3,7 +3,9 @@ import { shallowEqual, useDispatch, useSelector } from 'react-redux';
|
||||
import { nanoid } from 'nanoid';
|
||||
import classNames from 'classnames';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Link } from 'react-router-dom';
|
||||
import propTypes from 'prop-types';
|
||||
|
||||
import { checkFiltered, getBlockingClientName } from '../../../helpers/helpers';
|
||||
import { BLOCK_ACTIONS } from '../../../helpers/constants';
|
||||
import { toggleBlocking, toggleBlockingForClient } from '../../../actions';
|
||||
@@ -192,12 +194,13 @@ const ClientCell = ({
|
||||
{renderFormattedClientCell(client, clientInfo, isDetailed, true)}
|
||||
</div>
|
||||
{isDetailed && clientName && !whoisAvailable && (
|
||||
<div
|
||||
className="detailed-info d-none d-sm-block logs__text"
|
||||
<Link
|
||||
className="detailed-info d-none d-sm-block logs__text logs__text--link"
|
||||
to={`logs?search=${encodeURIComponent(clientName)}`}
|
||||
title={clientName}
|
||||
>
|
||||
{clientName}
|
||||
</div>
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
{renderBlockingButton(isFiltered, domain)}
|
||||
|
||||
@@ -16,6 +16,7 @@ const DomainCell = ({
|
||||
answer_dnssec,
|
||||
client_proto,
|
||||
domain,
|
||||
unicodeName,
|
||||
time,
|
||||
tracker,
|
||||
type,
|
||||
@@ -41,10 +42,22 @@ const DomainCell = ({
|
||||
const protocol = t(SCHEME_TO_PROTOCOL_MAP[client_proto]) || '';
|
||||
const ip = type ? `${t('type_table_header')}: ${type}` : '';
|
||||
|
||||
const requestDetailsObj = {
|
||||
let requestDetailsObj = {
|
||||
time_table_header: formatTime(time, LONG_TIME_FORMAT),
|
||||
date: formatDateTime(time, DEFAULT_SHORT_DATE_FORMAT_OPTIONS),
|
||||
domain,
|
||||
};
|
||||
|
||||
if (domain && unicodeName) {
|
||||
requestDetailsObj = {
|
||||
...requestDetailsObj,
|
||||
domain: unicodeName,
|
||||
punycode: domain,
|
||||
};
|
||||
}
|
||||
|
||||
requestDetailsObj = {
|
||||
...requestDetailsObj,
|
||||
type_table_header: type,
|
||||
protocol,
|
||||
};
|
||||
@@ -54,23 +67,40 @@ const DomainCell = ({
|
||||
const knownTrackerDataObj = {
|
||||
name_table_header: tracker?.name,
|
||||
category_label: hasTracker && captitalizeWords(tracker.category),
|
||||
source_label: sourceData
|
||||
&& <a href={sourceData.url} target="_blank" rel="noopener noreferrer"
|
||||
className="link--green">{sourceData.name}</a>,
|
||||
source_label: sourceData && (
|
||||
<a
|
||||
href={sourceData.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="link--green"
|
||||
>
|
||||
{sourceData.name}
|
||||
</a>
|
||||
),
|
||||
};
|
||||
|
||||
const renderGrid = (content, idx) => {
|
||||
const preparedContent = typeof content === 'string' ? t(content) : content;
|
||||
const className = classNames('text-truncate o-hidden', {
|
||||
'overflow-break': preparedContent.length > 100,
|
||||
});
|
||||
return <div key={idx} className={className}>{preparedContent}</div>;
|
||||
|
||||
const className = classNames(
|
||||
'text-truncate o-hidden',
|
||||
{ 'overflow-break': preparedContent?.length > 100 },
|
||||
);
|
||||
|
||||
return (
|
||||
<div key={idx} className={className}>
|
||||
{preparedContent}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const getGrid = (contentObj, title, className) => [
|
||||
<div key={title} className={classNames('pb-2 grid--title', className)}>{t(title)}</div>,
|
||||
<div key={`${title}-1`}
|
||||
className="grid grid--limited">{React.Children.map(Object.entries(contentObj), renderGrid)}</div>,
|
||||
<div key={title} className={classNames('pb-2 grid--title', className)}>
|
||||
{t(title)}
|
||||
</div>,
|
||||
<div key={`${title}-1`} className="grid grid--limited">
|
||||
{React.Children.map(Object.entries(contentObj), renderGrid)}
|
||||
</div>,
|
||||
];
|
||||
|
||||
const requestDetails = getGrid(requestDetailsObj, 'request_details');
|
||||
@@ -81,35 +111,60 @@ const DomainCell = ({
|
||||
'px-2 d-flex justify-content-center flex-column': isDetailed,
|
||||
});
|
||||
|
||||
const details = [ip, protocol].filter(Boolean)
|
||||
.join(', ');
|
||||
const details = [ip, protocol].filter(Boolean).join(', ');
|
||||
|
||||
return <div className="d-flex o-hidden logs__cell logs__cell logs__cell--domain" role="gridcell">
|
||||
{dnssec_enabled && <IconTooltip
|
||||
className={lockIconClass}
|
||||
tooltipClass='py-4 px-5 pb-45'
|
||||
canShowTooltip={!!answer_dnssec}
|
||||
xlinkHref='lock'
|
||||
columnClass='w-100'
|
||||
content='validated_with_dnssec'
|
||||
placement='bottom'
|
||||
/>}
|
||||
<IconTooltip className={privacyIconClass} tooltipClass='pt-4 pb-5 px-5 mw-75'
|
||||
xlinkHref='privacy' contentItemClass='key-colon' renderContent={renderContent}
|
||||
place='bottom' />
|
||||
<div className={valueClass}>
|
||||
<div className="text-truncate" title={domain}>{domain}</div>
|
||||
{details && isDetailed
|
||||
&& <div className="detailed-info d-none d-sm-block text-truncate"
|
||||
title={details}>{details}</div>}
|
||||
return (
|
||||
<div
|
||||
className="d-flex o-hidden logs__cell logs__cell logs__cell--domain"
|
||||
role="gridcell"
|
||||
>
|
||||
{dnssec_enabled && (
|
||||
<IconTooltip
|
||||
className={lockIconClass}
|
||||
tooltipClass="py-4 px-5 pb-45"
|
||||
canShowTooltip={!!answer_dnssec}
|
||||
xlinkHref="lock"
|
||||
columnClass="w-100"
|
||||
content="validated_with_dnssec"
|
||||
placement="bottom"
|
||||
/>
|
||||
)}
|
||||
<IconTooltip
|
||||
className={privacyIconClass}
|
||||
tooltipClass="pt-4 pb-5 px-5 mw-75"
|
||||
xlinkHref="privacy"
|
||||
contentItemClass="key-colon"
|
||||
renderContent={renderContent}
|
||||
place="bottom"
|
||||
/>
|
||||
<div className={valueClass}>
|
||||
{unicodeName ? (
|
||||
<div className="text-truncate" title={unicodeName}>
|
||||
{unicodeName}
|
||||
</div>
|
||||
) : (
|
||||
<div className="text-truncate" title={domain}>
|
||||
{domain}
|
||||
</div>
|
||||
)}
|
||||
{details && isDetailed && (
|
||||
<div
|
||||
className="detailed-info d-none d-sm-block text-truncate"
|
||||
title={details}
|
||||
>
|
||||
{details}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>;
|
||||
);
|
||||
};
|
||||
|
||||
DomainCell.propTypes = {
|
||||
answer_dnssec: propTypes.bool.isRequired,
|
||||
client_proto: propTypes.string.isRequired,
|
||||
domain: propTypes.string.isRequired,
|
||||
unicodeName: propTypes.string,
|
||||
time: propTypes.string.isRequired,
|
||||
type: propTypes.string.isRequired,
|
||||
tracker: propTypes.object,
|
||||
|
||||
@@ -197,4 +197,5 @@ Form.propTypes = {
|
||||
|
||||
export default reduxForm({
|
||||
form: FORM_NAME.LOGS_FILTER,
|
||||
enableReinitialize: true,
|
||||
})(Form);
|
||||
|
||||
@@ -51,6 +51,15 @@
|
||||
color: #888888;
|
||||
}
|
||||
|
||||
.logs__text--link {
|
||||
color: #467fcf;
|
||||
}
|
||||
|
||||
.logs__text--link:hover,
|
||||
.logs__text--link:focus {
|
||||
color: #295a9f;
|
||||
}
|
||||
|
||||
.icon--selected {
|
||||
background-color: var(--gray-f3);
|
||||
border: solid 1px var(--gray-d8);
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
getDhcpStatus,
|
||||
resetDhcp,
|
||||
setDhcpConfig,
|
||||
resetDhcpLeases,
|
||||
toggleDhcp,
|
||||
toggleLeaseModal,
|
||||
} from '../../../actions';
|
||||
@@ -111,6 +112,12 @@ const Dhcp = () => {
|
||||
}));
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
if (window.confirm(t('dhcp_reset_leases_confirm'))) {
|
||||
dispatch(resetDhcpLeases());
|
||||
}
|
||||
};
|
||||
|
||||
const enteredSomeV4Value = Object.values(v4)
|
||||
.some(Boolean);
|
||||
const enteredSomeV6Value = Object.values(v6)
|
||||
@@ -188,18 +195,18 @@ const Dhcp = () => {
|
||||
<PageTitle title={t('dhcp_settings')} subtitle={t('dhcp_description')} containerClass="page-title--dhcp">
|
||||
{toggleDhcpButton}
|
||||
<button
|
||||
type="button"
|
||||
className={statusButtonClass}
|
||||
onClick={onClick}
|
||||
disabled={enabled || !interface_name || processingConfig}
|
||||
type="button"
|
||||
className={statusButtonClass}
|
||||
onClick={onClick}
|
||||
disabled={enabled || !interface_name || processingConfig}
|
||||
>
|
||||
<Trans>check_dhcp_servers</Trans>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className='btn btn-sm btn-outline-secondary'
|
||||
disabled={!enteredSomeValue || processingConfig}
|
||||
onClick={clear}
|
||||
type="button"
|
||||
className='btn btn-sm btn-outline-secondary'
|
||||
disabled={!enteredSomeValue || processingConfig}
|
||||
onClick={clear}
|
||||
>
|
||||
<Trans>reset_settings</Trans>
|
||||
</button>
|
||||
@@ -269,16 +276,23 @@ const Dhcp = () => {
|
||||
processingDeleting={processingDeleting}
|
||||
cidr={cidr}
|
||||
/>
|
||||
</div>
|
||||
<div className="col-12">
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-success btn-standard mt-3"
|
||||
onClick={toggleModal}
|
||||
disabled={disabledLeasesButton}
|
||||
>
|
||||
<Trans>dhcp_add_static_lease</Trans>
|
||||
</button>
|
||||
<div className="btn-list mt-2">
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-success btn-standard mt-3"
|
||||
onClick={toggleModal}
|
||||
disabled={disabledLeasesButton}
|
||||
>
|
||||
<Trans>dhcp_add_static_lease</Trans>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-secondary btn-standard mt-3"
|
||||
onClick={handleReset}
|
||||
>
|
||||
<Trans>dhcp_reset_leases</Trans>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
|
||||
import { Field, reduxForm } from 'redux-form';
|
||||
import { Trans, useTranslation } from 'react-i18next';
|
||||
import { shallowEqual, useSelector } from 'react-redux';
|
||||
import { renderInputField, toNumber } from '../../../../helpers/form';
|
||||
import { renderInputField, toNumber, CheckboxField } from '../../../../helpers/form';
|
||||
import { CACHE_CONFIG_FIELDS, FORM_NAME, UINT32_RANGE } from '../../../../helpers/constants';
|
||||
import { replaceZeroWithEmptyString } from '../../../../helpers/helpers';
|
||||
|
||||
@@ -47,27 +47,48 @@ const Form = ({
|
||||
}) => <div className="col-12" key={name}>
|
||||
<div className="col-12 col-md-7 p-0">
|
||||
<div className="form__group form__group--settings">
|
||||
<label htmlFor={name}
|
||||
className="form__label form__label--with-desc">{t(title)}</label>
|
||||
<label
|
||||
htmlFor={name}
|
||||
className="form__label form__label--with-desc"
|
||||
>
|
||||
{t(title)}
|
||||
</label>
|
||||
<div className="form__desc form__desc--top">{t(description)}</div>
|
||||
<Field
|
||||
name={name}
|
||||
type="number"
|
||||
component={renderInputField}
|
||||
placeholder={t(placeholder)}
|
||||
disabled={processingSetConfig}
|
||||
className="form-control"
|
||||
validate={validate}
|
||||
normalizeOnBlur={replaceZeroWithEmptyString}
|
||||
normalize={toNumber}
|
||||
min={min}
|
||||
max={max}
|
||||
name={name}
|
||||
type="number"
|
||||
component={renderInputField}
|
||||
placeholder={t(placeholder)}
|
||||
disabled={processingSetConfig}
|
||||
className="form-control"
|
||||
validate={validate}
|
||||
normalizeOnBlur={replaceZeroWithEmptyString}
|
||||
normalize={toNumber}
|
||||
min={min}
|
||||
max={max}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>)}
|
||||
{minExceedsMax
|
||||
&& <span className="text-danger pl-3 pb-3">{t('ttl_cache_validation')}</span>}
|
||||
{minExceedsMax && (
|
||||
<span className="text-danger pl-3 pb-3">
|
||||
{t('ttl_cache_validation')}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="row">
|
||||
<div className="col-12 col-md-7">
|
||||
<div className="form__group form__group--settings">
|
||||
<Field
|
||||
name="cache_optimistic"
|
||||
type="checkbox"
|
||||
component={CheckboxField}
|
||||
placeholder={t('cache_optimistic')}
|
||||
disabled={processingSetConfig}
|
||||
subtitle={t('cache_optimistic_desc')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
type="submit"
|
||||
|
||||
@@ -10,7 +10,7 @@ const CacheConfig = () => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useDispatch();
|
||||
const {
|
||||
cache_size, cache_ttl_max, cache_ttl_min,
|
||||
cache_size, cache_ttl_max, cache_ttl_min, cache_optimistic,
|
||||
} = useSelector((state) => state.dnsConfig, shallowEqual);
|
||||
|
||||
const handleFormSubmit = (values) => {
|
||||
@@ -31,6 +31,7 @@ const CacheConfig = () => {
|
||||
cache_size: replaceZeroWithEmptyString(cache_size),
|
||||
cache_ttl_max: replaceZeroWithEmptyString(cache_ttl_max),
|
||||
cache_ttl_min: replaceZeroWithEmptyString(cache_ttl_min),
|
||||
cache_optimistic,
|
||||
}}
|
||||
onSubmit={handleFormSubmit}
|
||||
/>
|
||||
|
||||
@@ -4,26 +4,33 @@ import { Field, reduxForm } from 'redux-form';
|
||||
import { Trans, withTranslation } from 'react-i18next';
|
||||
import flow from 'lodash/flow';
|
||||
|
||||
import { CheckboxField, renderRadioField, toNumber } from '../../../helpers/form';
|
||||
import { CheckboxField, renderRadioField, toFloatNumber } from '../../../helpers/form';
|
||||
import { FORM_NAME, QUERY_LOG_INTERVALS_DAYS } from '../../../helpers/constants';
|
||||
import '../FormButton.css';
|
||||
|
||||
const getIntervalFields = (processing, t, toNumber) => QUERY_LOG_INTERVALS_DAYS.map((interval) => {
|
||||
const title = interval === 1 ? t('interval_24_hour') : t('interval_days', { count: interval });
|
||||
const getIntervalTitle = (interval, t) => {
|
||||
switch (interval) {
|
||||
case 0.25:
|
||||
return t('interval_6_hour');
|
||||
case 1:
|
||||
return t('interval_24_hour');
|
||||
default:
|
||||
return t('interval_days', { count: interval });
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Field
|
||||
key={interval}
|
||||
name="interval"
|
||||
type="radio"
|
||||
component={renderRadioField}
|
||||
value={interval}
|
||||
placeholder={title}
|
||||
normalize={toNumber}
|
||||
disabled={processing}
|
||||
/>
|
||||
);
|
||||
});
|
||||
const getIntervalFields = (processing, t, toNumber) => QUERY_LOG_INTERVALS_DAYS.map((interval) => (
|
||||
<Field
|
||||
key={interval}
|
||||
name="interval"
|
||||
type="radio"
|
||||
component={renderRadioField}
|
||||
value={interval}
|
||||
placeholder={getIntervalTitle(interval, t)}
|
||||
normalize={toNumber}
|
||||
disabled={processing}
|
||||
/>
|
||||
));
|
||||
|
||||
const Form = (props) => {
|
||||
const {
|
||||
@@ -56,7 +63,7 @@ const Form = (props) => {
|
||||
</label>
|
||||
<div className="form__group form__group--settings">
|
||||
<div className="custom-controls-stacked">
|
||||
{getIntervalFields(processing, t, toNumber)}
|
||||
{getIntervalFields(processing, t, toFloatNumber)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-5">
|
||||
|
||||
@@ -8,22 +8,27 @@ import { renderRadioField, toNumber } from '../../../helpers/form';
|
||||
import { FORM_NAME, STATS_INTERVALS_DAYS } from '../../../helpers/constants';
|
||||
import '../FormButton.css';
|
||||
|
||||
const getIntervalFields = (processing, t, toNumber) => STATS_INTERVALS_DAYS.map((interval) => {
|
||||
const title = interval === 1 ? t('interval_24_hour') : t('interval_days', { count: interval });
|
||||
const getIntervalTitle = (interval, t) => {
|
||||
switch (interval) {
|
||||
case 0:
|
||||
return t('disabled');
|
||||
case 1:
|
||||
return t('interval_24_hour');
|
||||
default:
|
||||
return t('interval_days', { count: interval });
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Field
|
||||
key={interval}
|
||||
name="interval"
|
||||
type="radio"
|
||||
component={renderRadioField}
|
||||
value={interval}
|
||||
placeholder={title}
|
||||
normalize={toNumber}
|
||||
disabled={processing}
|
||||
/>
|
||||
);
|
||||
});
|
||||
const getIntervalFields = (processing, t, toNumber) => STATS_INTERVALS_DAYS.map((interval) => <Field
|
||||
key={interval}
|
||||
name="interval"
|
||||
type="radio"
|
||||
component={renderRadioField}
|
||||
value={interval}
|
||||
placeholder={getIntervalTitle(interval, t)}
|
||||
normalize={toNumber}
|
||||
disabled={processing}
|
||||
/>);
|
||||
|
||||
const Form = (props) => {
|
||||
const {
|
||||
|
||||
@@ -7,9 +7,13 @@ import Form from './Form';
|
||||
|
||||
class StatsConfig extends Component {
|
||||
handleFormSubmit = (values) => {
|
||||
const { t } = this.props;
|
||||
// eslint-disable-next-line no-alert
|
||||
if (window.confirm(t('statistics_retention_confirm'))) {
|
||||
const { t, interval: prevInterval } = this.props;
|
||||
|
||||
if (values.interval < prevInterval) {
|
||||
if (window.confirm(t('statistics_retention_confirm'))) {
|
||||
this.props.setStatsConfig(values);
|
||||
}
|
||||
} else {
|
||||
this.props.setStatsConfig(values);
|
||||
}
|
||||
};
|
||||
@@ -28,7 +32,11 @@ class StatsConfig extends Component {
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<Card title={t('statistics_configuration')} bodyType="card-body box-body--settings">
|
||||
<Card
|
||||
title={t('statistics_configuration')}
|
||||
bodyType="card-body box-body--settings"
|
||||
id="stats-config"
|
||||
>
|
||||
<div className="form">
|
||||
<Form
|
||||
initialValues={{ interval }}
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
.stats__link {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.stats__link:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
@@ -2,7 +2,6 @@ import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import './LogsSearchLink.css';
|
||||
import { getLogsUrlParams } from '../../helpers/helpers';
|
||||
import { MENU_URLS } from '../../helpers/constants';
|
||||
|
||||
@@ -13,11 +12,16 @@ const LogsSearchLink = ({
|
||||
|
||||
const to = link === MENU_URLS.logs ? `${MENU_URLS.logs}${getLogsUrlParams(search && `"${search}"`, response_status)}` : link;
|
||||
|
||||
return <Link to={to}
|
||||
className={'stats__link'}
|
||||
tabIndex={0}
|
||||
title={t('click_to_view_queries')}
|
||||
aria-label={t('click_to_view_queries')}>{children}</Link>;
|
||||
return (
|
||||
<Link
|
||||
to={to}
|
||||
tabIndex={0}
|
||||
title={t('click_to_view_queries')}
|
||||
aria-label={t('click_to_view_queries')}
|
||||
>
|
||||
{children}
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
LogsSearchLink.propTypes = {
|
||||
|
||||
@@ -355,9 +355,9 @@ export const ENCRYPTION_SOURCE = {
|
||||
export const FILTERED = 'Filtered';
|
||||
export const NOT_FILTERED = 'NotFiltered';
|
||||
|
||||
export const STATS_INTERVALS_DAYS = [1, 7, 30, 90];
|
||||
export const STATS_INTERVALS_DAYS = [0, 1, 7, 30, 90];
|
||||
|
||||
export const QUERY_LOG_INTERVALS_DAYS = [1, 7, 30, 90];
|
||||
export const QUERY_LOG_INTERVALS_DAYS = [0.25, 1, 7, 30, 90];
|
||||
|
||||
export const FILTERS_INTERVALS_HOURS = [0, 1, 12, 24, 72, 168];
|
||||
|
||||
|
||||
@@ -276,6 +276,12 @@ export const ip4ToInt = (ip) => {
|
||||
*/
|
||||
export const toNumber = (value) => value && parseInt(value, 10);
|
||||
|
||||
/**
|
||||
* @param value {string}
|
||||
* @returns {*|number}
|
||||
*/
|
||||
export const toFloatNumber = (value) => value && parseFloat(value, 10);
|
||||
|
||||
/**
|
||||
* @param value {string}
|
||||
* @returns {boolean}
|
||||
|
||||
@@ -77,7 +77,7 @@ export const normalizeLogs = (logs) => logs.map((log) => {
|
||||
upstream,
|
||||
} = log;
|
||||
|
||||
const { host: domain, type } = question;
|
||||
const { name: domain, unicode_name: unicodeName, type } = question;
|
||||
|
||||
const processResponse = (data) => (data ? data.map((response) => {
|
||||
const { value, type, ttl } = response;
|
||||
@@ -96,6 +96,7 @@ export const normalizeLogs = (logs) => logs.map((log) => {
|
||||
return {
|
||||
time,
|
||||
domain,
|
||||
unicodeName,
|
||||
type,
|
||||
response: processResponse(answer),
|
||||
reason,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { normalizeWhois } from './helpers';
|
||||
import { WHOIS_ICONS } from './constants';
|
||||
|
||||
@@ -42,7 +43,7 @@ export const renderFormattedClientCell = (value, info, isDetailed = false, isLog
|
||||
const whoisAvailable = whois_info && Object.keys(whois_info).length > 0;
|
||||
|
||||
if (name) {
|
||||
const nameValue = <div className="logs__text logs__text--nowrap" title={`${name} (${value})`}>
|
||||
const nameValue = <div className="logs__text logs__text--link logs__text--nowrap" title={`${name} (${value})`}>
|
||||
{name} <small>{`(${value})`}</small>
|
||||
</div>;
|
||||
|
||||
@@ -63,7 +64,7 @@ export const renderFormattedClientCell = (value, info, isDetailed = false, isLog
|
||||
}
|
||||
|
||||
return <div className="logs__text mw-100" title={value}>
|
||||
{nameContainer}
|
||||
<Link to={`logs?search=${encodeURIComponent(value)}`}>{nameContainer}</Link>
|
||||
{whoisContainer}
|
||||
</div>;
|
||||
};
|
||||
|
||||
@@ -118,6 +118,11 @@ const dhcp = handleActions(
|
||||
v6: {},
|
||||
interface_name: '',
|
||||
}),
|
||||
[actions.resetDhcpLeasesSuccess]: (state) => ({
|
||||
...state,
|
||||
leases: [],
|
||||
staticLeases: [],
|
||||
}),
|
||||
|
||||
[actions.toggleLeaseModal]: (state) => {
|
||||
const newState = {
|
||||
|
||||
10
go.mod
10
go.mod
@@ -3,19 +3,19 @@ module github.com/AdguardTeam/AdGuardHome
|
||||
go 1.16
|
||||
|
||||
require (
|
||||
github.com/AdguardTeam/dnsproxy v0.37.6
|
||||
github.com/AdguardTeam/golibs v0.8.0
|
||||
github.com/AdguardTeam/dnsproxy v0.39.2
|
||||
github.com/AdguardTeam/golibs v0.8.4
|
||||
github.com/AdguardTeam/urlfilter v0.14.6
|
||||
github.com/NYTimes/gziphandler v1.1.1
|
||||
github.com/ameshkov/dnscrypt/v2 v2.1.3
|
||||
github.com/ameshkov/dnscrypt/v2 v2.2.1
|
||||
github.com/digineo/go-ipset/v2 v2.2.1
|
||||
github.com/fsnotify/fsnotify v1.4.9
|
||||
github.com/go-ping/ping v0.0.0-20210506233800-ff8be3320020
|
||||
github.com/google/go-cmp v0.5.5 // indirect
|
||||
github.com/google/go-cmp v0.5.5
|
||||
github.com/google/renameio v1.0.1
|
||||
github.com/insomniacslk/dhcp v0.0.0-20210310193751-cfd4d47082c2
|
||||
github.com/kardianos/service v1.2.0
|
||||
github.com/lucas-clemente/quic-go v0.20.1
|
||||
github.com/lucas-clemente/quic-go v0.21.1
|
||||
github.com/mdlayher/netlink v1.4.0
|
||||
github.com/mdlayher/raw v0.0.0-20210412142147-51b895745faf // indirect
|
||||
github.com/miekg/dns v1.1.42
|
||||
|
||||
51
go.sum
51
go.sum
@@ -9,13 +9,13 @@ dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D
|
||||
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
|
||||
github.com/AdguardTeam/dhcp v0.0.0-20210519141215-51808c73c0bf h1:gc042VRSIRSUzZ+Px6xQCRWNJZTaPkomisDfUZmoFNk=
|
||||
github.com/AdguardTeam/dhcp v0.0.0-20210519141215-51808c73c0bf/go.mod h1:TKl4jN3Voofo4UJIicyNhWGp/nlQqQkFxmwIFTvBkKI=
|
||||
github.com/AdguardTeam/dnsproxy v0.37.6 h1:qN9KX/fxLIR5TZKtpPHmMx/AUJLAdeXbQ5sjdqHXWW0=
|
||||
github.com/AdguardTeam/dnsproxy v0.37.6/go.mod h1:xkJWEuTr550gPDmB9azsciKZzSXjf9wMn+Ji54PQ4gE=
|
||||
github.com/AdguardTeam/dnsproxy v0.39.2 h1:GqsR1S4fFfVsVCSrdrfa0RfsQ2u+MeNUMqDkxdTD3gU=
|
||||
github.com/AdguardTeam/dnsproxy v0.39.2/go.mod h1:aNXKNdTyKfgAG2OS712SYSaGIM9AasZsZxfiY4YiR/0=
|
||||
github.com/AdguardTeam/golibs v0.4.0/go.mod h1:skKsDKIBB7kkFflLJBpfGX+G8QFTx0WKUzB6TIgtUj4=
|
||||
github.com/AdguardTeam/golibs v0.4.2/go.mod h1:skKsDKIBB7kkFflLJBpfGX+G8QFTx0WKUzB6TIgtUj4=
|
||||
github.com/AdguardTeam/golibs v0.4.4/go.mod h1:skKsDKIBB7kkFflLJBpfGX+G8QFTx0WKUzB6TIgtUj4=
|
||||
github.com/AdguardTeam/golibs v0.8.0 h1:rHo+yIgT2fivFG0yW2Cwk/DPc2+t/Aw6QvzPpiIFre0=
|
||||
github.com/AdguardTeam/golibs v0.8.0/go.mod h1:skKsDKIBB7kkFflLJBpfGX+G8QFTx0WKUzB6TIgtUj4=
|
||||
github.com/AdguardTeam/golibs v0.8.4 h1:jd6GwvQQtfSLOKn30qisDVujvas3q7Agjm3BOEqRWpQ=
|
||||
github.com/AdguardTeam/golibs v0.8.4/go.mod h1:skKsDKIBB7kkFflLJBpfGX+G8QFTx0WKUzB6TIgtUj4=
|
||||
github.com/AdguardTeam/gomitmproxy v0.2.0/go.mod h1:Qdv0Mktnzer5zpdpi5rAwixNJzW2FN91LjKJCkVbYGU=
|
||||
github.com/AdguardTeam/urlfilter v0.14.6 h1:emqoKZElooHACYehRBYENeKVN1a/rspxiqTIMYLuoIo=
|
||||
github.com/AdguardTeam/urlfilter v0.14.6/go.mod h1:klx4JbOfc4EaNb5lWLqOwfg+pVcyRukmoJRvO55lL5U=
|
||||
@@ -29,8 +29,9 @@ github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmH
|
||||
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA=
|
||||
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 h1:52m0LGchQBBVqJRyYYufQuIbVqRawmubW3OFGqK1ekw=
|
||||
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635/go.mod h1:lmLxL+FV291OopO93Bwf9fQLQeLyt33VJRUg5VJ30us=
|
||||
github.com/ameshkov/dnscrypt/v2 v2.1.3 h1:DG4Uf7LSDg6XDj9sp3maxh3Ur26jeGQaP5MeYosn6v0=
|
||||
github.com/ameshkov/dnscrypt/v2 v2.1.3/go.mod h1:+8SbPbVXpxxcUsgGi8eodkqWPo1MyNHxKYC8hDpqLSo=
|
||||
github.com/ameshkov/dnscrypt/v2 v2.2.1 h1:+cApRxzeBZqjUNsN26TTz7r5A8U+buON3kJgIYE3QWQ=
|
||||
github.com/ameshkov/dnscrypt/v2 v2.2.1/go.mod h1:+8SbPbVXpxxcUsgGi8eodkqWPo1MyNHxKYC8hDpqLSo=
|
||||
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=
|
||||
@@ -40,7 +41,6 @@ github.com/beefsack/go-rate v0.0.0-20200827232406-6cde80facd47/go.mod h1:6YNgTHL
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
|
||||
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE=
|
||||
github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
@@ -52,8 +52,6 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
||||
github.com/digineo/go-ipset/v2 v2.2.1 h1:k6skY+0fMqeUjjeWO/m5OuWPSZUAn7AucHMnQ1MX77g=
|
||||
github.com/digineo/go-ipset/v2 v2.2.1/go.mod h1:wBsNzJlZlABHUITkesrggFnZQtgW5wkqw1uo8Qxe0VU=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/fanliao/go-promise v0.0.0-20141029170127-1890db352a72/go.mod h1:PjfxuH4FZdUyfMdtBio2lsRr1AKEaVPwelzuHuh8Lqc=
|
||||
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=
|
||||
@@ -75,25 +73,21 @@ github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200j
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||
github.com/golang/mock v1.5.0 h1:jlYHihg//f7RRwuPfptm04yp4s7O6Kw8EZiVYIGcH0g=
|
||||
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
|
||||
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
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/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
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/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
@@ -140,8 +134,8 @@ github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/lucas-clemente/quic-go v0.20.1 h1:hb5m76V8QS/8Nw/suHvXqo3BMHAozvIkcnzpJdpanSk=
|
||||
github.com/lucas-clemente/quic-go v0.20.1/go.mod h1:fZq/HUDIM+mW6X6wtzORjC0E/WDBMKe5Hf9bgjISwLk=
|
||||
github.com/lucas-clemente/quic-go v0.21.1 h1:uuhCcu885TE9u/piPYMChI/yqA1lXfaLUEx8uCMxf8w=
|
||||
github.com/lucas-clemente/quic-go v0.21.1/go.mod h1:U9kFi5LKbNIlU30dkuM9vxmTxWq4Bvzee/MjBI+07UA=
|
||||
github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
|
||||
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc=
|
||||
@@ -149,6 +143,8 @@ github.com/marten-seemann/qtls-go1-15 v0.1.4 h1:RehYMOyRW8hPVEja1KBVsFVNSm35Jj9M
|
||||
github.com/marten-seemann/qtls-go1-15 v0.1.4/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I=
|
||||
github.com/marten-seemann/qtls-go1-16 v0.1.3 h1:XEZ1xGorVy9u+lJq+WXNE+hiqRYLNvJGYmwfwKQN2gU=
|
||||
github.com/marten-seemann/qtls-go1-16 v0.1.3/go.mod h1:gNpI2Ol+lRS3WwSOtIUUtRwZEQMXjYK+dQSBFbethAk=
|
||||
github.com/marten-seemann/qtls-go1-17 v0.1.0-beta.1.2 h1:SficYjyOthSrliKI+EaFuXS6HqSsX3dkY9AqxAAjBjw=
|
||||
github.com/marten-seemann/qtls-go1-17 v0.1.0-beta.1.2/go.mod h1:fz4HIxByo+LlWcreM4CZOYNuz3taBQ8rN2X6FqvaWo8=
|
||||
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=
|
||||
@@ -202,7 +198,6 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
@@ -254,6 +249,7 @@ github.com/u-root/u-root v7.0.0+incompatible h1:u+KSS04pSxJGI5E7WE4Bs9+Zd75QjFv+
|
||||
github.com/u-root/u-root v7.0.0+incompatible/go.mod h1:RYkpo8pTHrNjW08opNd/U6p/RJE7K0D8fXO0d47+3YY=
|
||||
github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=
|
||||
github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0=
|
||||
go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
|
||||
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
|
||||
@@ -273,9 +269,8 @@ golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL
|
||||
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
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-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -300,11 +295,11 @@ golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81R
|
||||
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/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
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/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210510120150-4163338589ed h1:p9UgmWI9wKpfYmgaV/IZKGdXc5qEK45tDwwwDyjS26I=
|
||||
golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
@@ -317,8 +312,6 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
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/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-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -353,13 +346,14 @@ golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
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-20201218084310-7d0127a74742/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201231184435-2d18734c6014/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-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015 h1:hZR0X1kPW+nwyJ9xRxqZk1vx5RUObAPBdKVvXPDUH/E=
|
||||
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
@@ -378,12 +372,11 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm
|
||||
golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@@ -401,23 +394,16 @@ google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoA
|
||||
google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
|
||||
google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
|
||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
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/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/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||
@@ -442,7 +428,6 @@ grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJd
|
||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
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=
|
||||
sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck=
|
||||
|
||||
@@ -10,6 +10,24 @@ import (
|
||||
"golang.org/x/net/idna"
|
||||
)
|
||||
|
||||
// CloneIP returns a clone of an IP address.
|
||||
func CloneIP(ip net.IP) (clone net.IP) {
|
||||
if ip != nil && len(ip) == 0 {
|
||||
return net.IP{}
|
||||
}
|
||||
|
||||
return append(clone, ip...)
|
||||
}
|
||||
|
||||
// CloneMAC returns a clone of a MAC address.
|
||||
func CloneMAC(mac net.HardwareAddr) (clone net.HardwareAddr) {
|
||||
if mac != nil && len(mac) == 0 {
|
||||
return net.HardwareAddr{}
|
||||
}
|
||||
|
||||
return append(clone, mac...)
|
||||
}
|
||||
|
||||
// IPFromAddr returns an IP address from addr. If addr is neither
|
||||
// a *net.TCPAddr nor a *net.UDPAddr, it returns nil.
|
||||
func IPFromAddr(addr net.Addr) (ip net.IP) {
|
||||
@@ -31,6 +49,12 @@ func IsValidHostOuterRune(r rune) (ok bool) {
|
||||
(r >= '0' && r <= '9')
|
||||
}
|
||||
|
||||
// JoinHostPort is a convinient wrapper for net.JoinHostPort with port of type
|
||||
// int.
|
||||
func JoinHostPort(host string, port int) (hostport string) {
|
||||
return net.JoinHostPort(host, strconv.Itoa(port))
|
||||
}
|
||||
|
||||
// isValidHostRune returns true if r is a valid rune for a hostname label.
|
||||
func isValidHostRune(r rune) (ok bool) {
|
||||
return r == '-' || IsValidHostOuterRune(r)
|
||||
|
||||
@@ -9,6 +9,26 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestCloneIP(t *testing.T) {
|
||||
assert.Equal(t, net.IP(nil), CloneIP(nil))
|
||||
assert.Equal(t, net.IP{}, CloneIP(net.IP{}))
|
||||
|
||||
ip := net.IP{1, 2, 3, 4}
|
||||
clone := CloneIP(ip)
|
||||
assert.Equal(t, ip, clone)
|
||||
assert.NotSame(t, &ip[0], &clone[0])
|
||||
}
|
||||
|
||||
func TestCloneMAC(t *testing.T) {
|
||||
assert.Equal(t, net.HardwareAddr(nil), CloneMAC(nil))
|
||||
assert.Equal(t, net.HardwareAddr{}, CloneMAC(net.HardwareAddr{}))
|
||||
|
||||
mac := net.HardwareAddr{0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC}
|
||||
clone := CloneMAC(mac)
|
||||
assert.Equal(t, mac, clone)
|
||||
assert.NotSame(t, &mac[0], &clone[0])
|
||||
}
|
||||
|
||||
func TestIPFromAddr(t *testing.T) {
|
||||
ip := net.IP{1, 2, 3, 4}
|
||||
assert.Equal(t, net.IP(nil), IPFromAddr(nil))
|
||||
@@ -66,6 +86,14 @@ func TestValidateHardwareAddress(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestJoinHostPort(t *testing.T) {
|
||||
assert.Equal(t, ":0", JoinHostPort("", 0))
|
||||
assert.Equal(t, "host:12345", JoinHostPort("host", 12345))
|
||||
assert.Equal(t, "1.2.3.4:12345", JoinHostPort("1.2.3.4", 12345))
|
||||
assert.Equal(t, "[1234::5678]:12345", JoinHostPort("1234::5678", 12345))
|
||||
assert.Equal(t, "[1234::5678%lo]:12345", JoinHostPort("1234::5678%lo", 12345))
|
||||
}
|
||||
|
||||
func repeatStr(b *strings.Builder, s string, n int) {
|
||||
for i := 0; i < n; i++ {
|
||||
_, _ = b.WriteString(s)
|
||||
|
||||
@@ -27,10 +27,9 @@ type EtcHostsContainer struct {
|
||||
lock sync.RWMutex
|
||||
// table is the host-to-IPs map.
|
||||
table map[string][]net.IP
|
||||
// tableReverse is the IP-to-hosts map.
|
||||
//
|
||||
// TODO(a.garipov): Make better use of newtypes. Perhaps a custom map.
|
||||
tableReverse map[string][]string
|
||||
// tableReverse is the IP-to-hosts map. The type of the values in the
|
||||
// map is []string.
|
||||
tableReverse *IPMap
|
||||
|
||||
hostsFn string // path to the main hosts-file
|
||||
hostsDirs []string // paths to OS-specific directories with hosts-files
|
||||
@@ -80,7 +79,7 @@ func (ehc *EtcHostsContainer) Init(hostsFn string) {
|
||||
var err error
|
||||
ehc.watcher, err = fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
log.Error("etchostscontainer: %s", err)
|
||||
log.Error("etchosts: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,7 +140,7 @@ func (ehc *EtcHostsContainer) Process(host string, qtype uint16) []net.IP {
|
||||
copy(ipsCopy, ips)
|
||||
}
|
||||
|
||||
log.Debug("etchostscontainer: answer: %s -> %v", host, ipsCopy)
|
||||
log.Debug("etchosts: answer: %s -> %v", host, ipsCopy)
|
||||
return ipsCopy
|
||||
}
|
||||
|
||||
@@ -151,38 +150,40 @@ func (ehc *EtcHostsContainer) ProcessReverse(addr string, qtype uint16) (hosts [
|
||||
return nil
|
||||
}
|
||||
|
||||
ipReal := UnreverseAddr(addr)
|
||||
if ipReal == nil {
|
||||
ip := UnreverseAddr(addr)
|
||||
if ip == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
ipStr := ipReal.String()
|
||||
|
||||
ehc.lock.RLock()
|
||||
defer ehc.lock.RUnlock()
|
||||
|
||||
hosts = ehc.tableReverse[ipStr]
|
||||
|
||||
if len(hosts) == 0 {
|
||||
return nil // not found
|
||||
v, ok := ehc.tableReverse.Get(ip)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Debug("etchostscontainer: reverse-lookup: %s -> %s", addr, hosts)
|
||||
hosts, ok = v.([]string)
|
||||
if !ok {
|
||||
log.Error("etchosts: bad type %T in tableReverse for %s", v, ip)
|
||||
|
||||
return nil
|
||||
} else if len(hosts) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Debug("etchosts: reverse-lookup: %s -> %s", addr, hosts)
|
||||
|
||||
return hosts
|
||||
}
|
||||
|
||||
// List returns an IP-to-hostnames table. It is safe for concurrent use.
|
||||
func (ehc *EtcHostsContainer) List() (ipToHosts map[string][]string) {
|
||||
// List returns an IP-to-hostnames table. The type of the values in the map is
|
||||
// []string. It is safe for concurrent use.
|
||||
func (ehc *EtcHostsContainer) List() (ipToHosts *IPMap) {
|
||||
ehc.lock.RLock()
|
||||
defer ehc.lock.RUnlock()
|
||||
|
||||
ipToHosts = make(map[string][]string, len(ehc.tableReverse))
|
||||
for k, v := range ehc.tableReverse {
|
||||
ipToHosts[k] = v
|
||||
}
|
||||
|
||||
return ipToHosts
|
||||
return ehc.tableReverse.ShallowClone()
|
||||
}
|
||||
|
||||
// update table
|
||||
@@ -205,29 +206,31 @@ func (ehc *EtcHostsContainer) updateTable(table map[string][]net.IP, host string
|
||||
ok = true
|
||||
}
|
||||
if ok {
|
||||
log.Debug("etchostscontainer: added %s -> %s", ipAddr, host)
|
||||
log.Debug("etchosts: added %s -> %s", ipAddr, host)
|
||||
}
|
||||
}
|
||||
|
||||
// updateTableRev updates the reverse address table.
|
||||
func (ehc *EtcHostsContainer) updateTableRev(tableRev map[string][]string, newHost string, ipAddr net.IP) {
|
||||
ipStr := ipAddr.String()
|
||||
hosts, ok := tableRev[ipStr]
|
||||
func (ehc *EtcHostsContainer) updateTableRev(tableRev *IPMap, newHost string, ip net.IP) {
|
||||
v, ok := tableRev.Get(ip)
|
||||
if !ok {
|
||||
tableRev[ipStr] = []string{newHost}
|
||||
log.Debug("etchostscontainer: added reverse-address %s -> %s", ipStr, newHost)
|
||||
tableRev.Set(ip, []string{newHost})
|
||||
log.Debug("etchosts: added reverse-address %s -> %s", ip, newHost)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
hosts, _ := v.([]string)
|
||||
for _, host := range hosts {
|
||||
if host == newHost {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
tableRev[ipStr] = append(tableRev[ipStr], newHost)
|
||||
log.Debug("etchostscontainer: added reverse-address %s -> %s", ipStr, newHost)
|
||||
hosts = append(hosts, newHost)
|
||||
tableRev.Set(ip, hosts)
|
||||
|
||||
log.Debug("etchosts: added reverse-address %s -> %s", ip, newHost)
|
||||
}
|
||||
|
||||
// parseHostsLine parses hosts from the fields.
|
||||
@@ -255,12 +258,12 @@ func parseHostsLine(fields []string) (hosts []string) {
|
||||
// line for one IP are supported.
|
||||
func (ehc *EtcHostsContainer) load(
|
||||
table map[string][]net.IP,
|
||||
tableRev map[string][]string,
|
||||
tableRev *IPMap,
|
||||
fn string,
|
||||
) {
|
||||
f, err := os.Open(fn)
|
||||
if err != nil {
|
||||
log.Error("etchostscontainer: %s", err)
|
||||
log.Error("etchosts: %s", err)
|
||||
|
||||
return
|
||||
}
|
||||
@@ -268,11 +271,11 @@ func (ehc *EtcHostsContainer) load(
|
||||
defer func() {
|
||||
derr := f.Close()
|
||||
if derr != nil {
|
||||
log.Error("etchostscontainer: closing file: %s", err)
|
||||
log.Error("etchosts: closing file: %s", err)
|
||||
}
|
||||
}()
|
||||
|
||||
log.Debug("etchostscontainer: loading hosts from file %s", fn)
|
||||
log.Debug("etchosts: loading hosts from file %s", fn)
|
||||
|
||||
s := bufio.NewScanner(f)
|
||||
for s.Scan() {
|
||||
@@ -296,7 +299,7 @@ func (ehc *EtcHostsContainer) load(
|
||||
|
||||
err = s.Err()
|
||||
if err != nil {
|
||||
log.Error("etchostscontainer: %s", err)
|
||||
log.Error("etchosts: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -334,7 +337,7 @@ func (ehc *EtcHostsContainer) watcherLoop() {
|
||||
}
|
||||
|
||||
if event.Op&fsnotify.Write == fsnotify.Write {
|
||||
log.Debug("etchostscontainer: modified: %s", event.Name)
|
||||
log.Debug("etchosts: modified: %s", event.Name)
|
||||
ehc.updateHosts()
|
||||
}
|
||||
|
||||
@@ -342,7 +345,7 @@ func (ehc *EtcHostsContainer) watcherLoop() {
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
log.Error("etchostscontainer: %s", err)
|
||||
log.Error("etchosts: %s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -350,7 +353,7 @@ func (ehc *EtcHostsContainer) watcherLoop() {
|
||||
// updateHosts - loads system hosts
|
||||
func (ehc *EtcHostsContainer) updateHosts() {
|
||||
table := make(map[string][]net.IP)
|
||||
tableRev := make(map[string][]string)
|
||||
tableRev := NewIPMap(0)
|
||||
|
||||
ehc.load(table, tableRev, ehc.hostsFn)
|
||||
|
||||
@@ -358,7 +361,7 @@ func (ehc *EtcHostsContainer) updateHosts() {
|
||||
des, err := os.ReadDir(dir)
|
||||
if err != nil {
|
||||
if !errors.Is(err, os.ErrNotExist) {
|
||||
log.Error("etchostscontainer: Opening directory: %q: %s", dir, err)
|
||||
log.Error("etchosts: Opening directory: %q: %s", dir, err)
|
||||
}
|
||||
|
||||
continue
|
||||
|
||||
@@ -70,7 +70,7 @@ func TestEtcHostsContainerResolution(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("hosts_file", func(t *testing.T) {
|
||||
names, ok := ehc.List()["127.0.0.1"]
|
||||
names, ok := ehc.List().Get(net.IP{127, 0, 0, 1})
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, []string{"host", "localhost"}, names)
|
||||
})
|
||||
|
||||
117
internal/aghnet/ipmap.go
Normal file
117
internal/aghnet/ipmap.go
Normal file
@@ -0,0 +1,117 @@
|
||||
package aghnet
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
)
|
||||
|
||||
// ipArr is a representation of an IP address as an array of bytes.
|
||||
type ipArr [16]byte
|
||||
|
||||
// String implements the fmt.Stringer interface for ipArr.
|
||||
func (a ipArr) String() (s string) {
|
||||
return net.IP(a[:]).String()
|
||||
}
|
||||
|
||||
// IPMap is a map of IP addresses.
|
||||
type IPMap struct {
|
||||
m map[ipArr]interface{}
|
||||
}
|
||||
|
||||
// NewIPMap returns a new empty IP map using hint as a size hint for the
|
||||
// underlying map.
|
||||
func NewIPMap(hint int) (m *IPMap) {
|
||||
return &IPMap{
|
||||
m: make(map[ipArr]interface{}, hint),
|
||||
}
|
||||
}
|
||||
|
||||
// ipToArr converts a net.IP into an ipArr.
|
||||
//
|
||||
// TODO(a.garipov): Use the slice-to-array conversion in Go 1.17.
|
||||
func ipToArr(ip net.IP) (a ipArr) {
|
||||
copy(a[:], ip.To16())
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
// Del deletes ip from the map. Calling Del on a nil *IPMap has no effect, just
|
||||
// like delete on an empty map doesn't.
|
||||
func (m *IPMap) Del(ip net.IP) {
|
||||
if m != nil {
|
||||
delete(m.m, ipToArr(ip))
|
||||
}
|
||||
}
|
||||
|
||||
// Get returns the value from the map. Calling Get on a nil *IPMap returns nil
|
||||
// and false, just like indexing on an empty map does.
|
||||
func (m *IPMap) Get(ip net.IP) (v interface{}, ok bool) {
|
||||
if m != nil {
|
||||
v, ok = m.m[ipToArr(ip)]
|
||||
|
||||
return v, ok
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// Len returns the length of the map. A nil *IPMap has a length of zero, just
|
||||
// like an empty map.
|
||||
func (m *IPMap) Len() (n int) {
|
||||
if m == nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
return len(m.m)
|
||||
}
|
||||
|
||||
// Range calls f for each key and value present in the map in an undefined
|
||||
// order. If cont is false, range stops the iteration. Calling Range on a nil
|
||||
// *IPMap has no effect, just like ranging over a nil map.
|
||||
func (m *IPMap) Range(f func(ip net.IP, v interface{}) (cont bool)) {
|
||||
if m == nil {
|
||||
return
|
||||
}
|
||||
|
||||
for k, v := range m.m {
|
||||
// Array slicing produces a pointer, so copy the array here.
|
||||
//
|
||||
// See https://github.com/AdguardTeam/AdGuardHome/issues/3346
|
||||
// as well as https://github.com/kyoh86/looppointer/issues/9.
|
||||
k := k
|
||||
if !f(net.IP(k[:]), v) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set sets the value. Set panics if the m is a nil *IPMap, just like a nil map
|
||||
// does.
|
||||
func (m *IPMap) Set(ip net.IP, v interface{}) {
|
||||
m.m[ipToArr(ip)] = v
|
||||
}
|
||||
|
||||
// ShallowClone returns a shallow clone of the map.
|
||||
func (m *IPMap) ShallowClone() (sclone *IPMap) {
|
||||
if m == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
sclone = NewIPMap(m.Len())
|
||||
m.Range(func(ip net.IP, v interface{}) (cont bool) {
|
||||
sclone.Set(ip, v)
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
return sclone
|
||||
}
|
||||
|
||||
// String implements the fmt.Stringer interface for *IPMap.
|
||||
func (m *IPMap) String() (s string) {
|
||||
if m == nil {
|
||||
return "<nil>"
|
||||
}
|
||||
|
||||
return fmt.Sprint(m.m)
|
||||
}
|
||||
142
internal/aghnet/ipmap_test.go
Normal file
142
internal/aghnet/ipmap_test.go
Normal file
@@ -0,0 +1,142 @@
|
||||
package aghnet
|
||||
|
||||
import (
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestIPMap_allocs(t *testing.T) {
|
||||
ip4 := net.IP{1, 2, 3, 4}
|
||||
m := NewIPMap(0)
|
||||
m.Set(ip4, 42)
|
||||
|
||||
t.Run("get", func(t *testing.T) {
|
||||
var v interface{}
|
||||
var ok bool
|
||||
allocs := testing.AllocsPerRun(100, func() {
|
||||
v, ok = m.Get(ip4)
|
||||
})
|
||||
|
||||
require.True(t, ok)
|
||||
require.Equal(t, 42, v)
|
||||
|
||||
assert.Equal(t, float64(0), allocs)
|
||||
})
|
||||
|
||||
t.Run("len", func(t *testing.T) {
|
||||
var n int
|
||||
allocs := testing.AllocsPerRun(100, func() {
|
||||
n = m.Len()
|
||||
})
|
||||
|
||||
require.Equal(t, 1, n)
|
||||
|
||||
assert.Equal(t, float64(0), allocs)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIPMap(t *testing.T) {
|
||||
ip4 := net.IP{1, 2, 3, 4}
|
||||
ip6 := net.IP{
|
||||
0x12, 0x34, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x56, 0x78,
|
||||
}
|
||||
|
||||
val := 42
|
||||
|
||||
t.Run("nil", func(t *testing.T) {
|
||||
var m *IPMap
|
||||
|
||||
assert.NotPanics(t, func() {
|
||||
m.Del(ip4)
|
||||
m.Del(ip6)
|
||||
})
|
||||
|
||||
assert.NotPanics(t, func() {
|
||||
v, ok := m.Get(ip4)
|
||||
assert.Nil(t, v)
|
||||
assert.False(t, ok)
|
||||
|
||||
v, ok = m.Get(ip6)
|
||||
assert.Nil(t, v)
|
||||
assert.False(t, ok)
|
||||
})
|
||||
|
||||
assert.NotPanics(t, func() {
|
||||
assert.Equal(t, 0, m.Len())
|
||||
})
|
||||
|
||||
assert.NotPanics(t, func() {
|
||||
n := 0
|
||||
m.Range(func(_ net.IP, _ interface{}) (cont bool) {
|
||||
n++
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
assert.Equal(t, 0, n)
|
||||
})
|
||||
|
||||
assert.Panics(t, func() {
|
||||
m.Set(ip4, val)
|
||||
})
|
||||
|
||||
assert.Panics(t, func() {
|
||||
m.Set(ip6, val)
|
||||
})
|
||||
|
||||
assert.NotPanics(t, func() {
|
||||
sclone := m.ShallowClone()
|
||||
assert.Nil(t, sclone)
|
||||
})
|
||||
})
|
||||
|
||||
testIPMap := func(t *testing.T, ip net.IP, s string) {
|
||||
m := NewIPMap(0)
|
||||
assert.Equal(t, 0, m.Len())
|
||||
|
||||
v, ok := m.Get(ip)
|
||||
assert.Nil(t, v)
|
||||
assert.False(t, ok)
|
||||
|
||||
m.Set(ip, val)
|
||||
v, ok = m.Get(ip)
|
||||
assert.Equal(t, val, v)
|
||||
assert.True(t, ok)
|
||||
|
||||
n := 0
|
||||
m.Range(func(ipKey net.IP, v interface{}) (cont bool) {
|
||||
assert.Equal(t, ip.To16(), ipKey)
|
||||
assert.Equal(t, val, v)
|
||||
|
||||
n++
|
||||
|
||||
return false
|
||||
})
|
||||
assert.Equal(t, 1, n)
|
||||
|
||||
sclone := m.ShallowClone()
|
||||
assert.Equal(t, m, sclone)
|
||||
|
||||
assert.Equal(t, s, m.String())
|
||||
|
||||
m.Del(ip)
|
||||
v, ok = m.Get(ip)
|
||||
assert.Nil(t, v)
|
||||
assert.False(t, ok)
|
||||
assert.Equal(t, 0, m.Len())
|
||||
}
|
||||
|
||||
t.Run("ipv4", func(t *testing.T) {
|
||||
testIPMap(t, ip4, "map[1.2.3.4:42]")
|
||||
})
|
||||
|
||||
t.Run("ipv6", func(t *testing.T) {
|
||||
testIPMap(t, ip6, "map[1234::5678:42]")
|
||||
})
|
||||
}
|
||||
26
internal/aghnet/ipset.go
Normal file
26
internal/aghnet/ipset.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package aghnet
|
||||
|
||||
import (
|
||||
"net"
|
||||
)
|
||||
|
||||
// IpsetManager is the ipset manager interface.
|
||||
//
|
||||
// TODO(a.garipov): Perhaps generalize this into some kind of a NetFilter type,
|
||||
// since ipset is exclusive to Linux?
|
||||
type IpsetManager interface {
|
||||
Add(host string, ip4s, ip6s []net.IP) (n int, err error)
|
||||
Close() (err error)
|
||||
}
|
||||
|
||||
// NewIpsetManager returns a new ipset. IPv4 addresses are added to an ipset
|
||||
// with an ipv4 family; IPv6 addresses, to an ipv6 ipset. ipset must exist.
|
||||
//
|
||||
// The syntax of the ipsetConf is:
|
||||
//
|
||||
// DOMAIN[,DOMAIN].../IPSET_NAME[,IPSET_NAME]...
|
||||
//
|
||||
// The error is of type *aghos.UnsupportedError if the OS is not supported.
|
||||
func NewIpsetManager(ipsetConf []string) (mgr IpsetManager, err error) {
|
||||
return newIpsetMgr(ipsetConf)
|
||||
}
|
||||
376
internal/aghnet/ipset_linux.go
Normal file
376
internal/aghnet/ipset_linux.go
Normal file
@@ -0,0 +1,376 @@
|
||||
//go:build linux
|
||||
// +build linux
|
||||
|
||||
package aghnet
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/digineo/go-ipset/v2"
|
||||
"github.com/mdlayher/netlink"
|
||||
"github.com/ti-mo/netfilter"
|
||||
)
|
||||
|
||||
// How to test on a real Linux machine:
|
||||
//
|
||||
// 1. Run:
|
||||
//
|
||||
// sudo ipset create example_set hash:ip family ipv4
|
||||
//
|
||||
// 2. Run:
|
||||
//
|
||||
// sudo ipset list example_set
|
||||
//
|
||||
// The Members field should be empty.
|
||||
//
|
||||
// 3. Add the line "example.com/example_set" to your AdGuardHome.yaml.
|
||||
//
|
||||
// 4. Start AdGuardHome.
|
||||
//
|
||||
// 5. Make requests to example.com and its subdomains.
|
||||
//
|
||||
// 6. Run:
|
||||
//
|
||||
// sudo ipset list example_set
|
||||
//
|
||||
// The Members field should contain the resolved IP addresses.
|
||||
|
||||
// newIpsetMgr returns a new Linux ipset manager.
|
||||
func newIpsetMgr(ipsetConf []string) (set IpsetManager, err error) {
|
||||
dial := func(pf netfilter.ProtoFamily, conf *netlink.Config) (conn ipsetConn, err error) {
|
||||
return ipset.Dial(pf, conf)
|
||||
}
|
||||
|
||||
return newIpsetMgrWithDialer(ipsetConf, dial)
|
||||
}
|
||||
|
||||
// ipsetConn is the ipset conn interface.
|
||||
type ipsetConn interface {
|
||||
Add(name string, entries ...*ipset.Entry) (err error)
|
||||
Close() (err error)
|
||||
Header(name string) (p *ipset.HeaderPolicy, err error)
|
||||
}
|
||||
|
||||
// ipsetDialer creates an ipsetConn.
|
||||
type ipsetDialer func(pf netfilter.ProtoFamily, conf *netlink.Config) (conn ipsetConn, err error)
|
||||
|
||||
// ipsetProps contains one Linux Netfilter ipset properties.
|
||||
type ipsetProps struct {
|
||||
name string
|
||||
family netfilter.ProtoFamily
|
||||
}
|
||||
|
||||
// unit is a convenient alias for struct{}.
|
||||
type unit = struct{}
|
||||
|
||||
// ipsetMgr is the Linux Netfilter ipset manager.
|
||||
type ipsetMgr struct {
|
||||
nameToIpset map[string]ipsetProps
|
||||
domainToIpsets map[string][]ipsetProps
|
||||
|
||||
dial ipsetDialer
|
||||
|
||||
// mu protects all properties below.
|
||||
mu *sync.Mutex
|
||||
|
||||
// TODO(a.garipov): Currently, the ipset list is static, and we don't
|
||||
// read the IPs already in sets, so we can assume that all incoming IPs
|
||||
// are either added to all corresponding ipsets or not. When that stops
|
||||
// being the case, for example if we add dynamic reconfiguration of
|
||||
// ipsets, this map will need to become a per-ipset-name one.
|
||||
addedIPs map[[16]byte]unit
|
||||
|
||||
ipv4Conn ipsetConn
|
||||
ipv6Conn ipsetConn
|
||||
}
|
||||
|
||||
// dialNetfilter establishes connections to Linux's netfilter module.
|
||||
func (m *ipsetMgr) dialNetfilter(conf *netlink.Config) (err error) {
|
||||
// The kernel API does not actually require two sockets but package
|
||||
// github.com/digineo/go-ipset does.
|
||||
//
|
||||
// TODO(a.garipov): Perhaps we can ditch package ipset altogether and
|
||||
// just use packages netfilter and netlink.
|
||||
m.ipv4Conn, err = m.dial(netfilter.ProtoIPv4, conf)
|
||||
if err != nil {
|
||||
return fmt.Errorf("dialing v4: %w", err)
|
||||
}
|
||||
|
||||
m.ipv6Conn, err = m.dial(netfilter.ProtoIPv6, conf)
|
||||
if err != nil {
|
||||
return fmt.Errorf("dialing v6: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// parseIpsetConfig parses one ipset configuration string.
|
||||
func parseIpsetConfig(confStr string) (hosts, ipsetNames []string, err error) {
|
||||
confStr = strings.TrimSpace(confStr)
|
||||
hostsAndNames := strings.Split(confStr, "/")
|
||||
if len(hostsAndNames) != 2 {
|
||||
return nil, nil, fmt.Errorf("invalid value %q: expected one slash", confStr)
|
||||
}
|
||||
|
||||
hosts = strings.Split(hostsAndNames[0], ",")
|
||||
ipsetNames = strings.Split(hostsAndNames[1], ",")
|
||||
|
||||
if len(ipsetNames) == 0 {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
for i := range ipsetNames {
|
||||
ipsetNames[i] = strings.TrimSpace(ipsetNames[i])
|
||||
if len(ipsetNames[i]) == 0 {
|
||||
return nil, nil, fmt.Errorf("invalid value %q: empty ipset name", confStr)
|
||||
}
|
||||
}
|
||||
|
||||
for i := range hosts {
|
||||
hosts[i] = strings.ToLower(strings.TrimSpace(hosts[i]))
|
||||
}
|
||||
|
||||
return hosts, ipsetNames, nil
|
||||
}
|
||||
|
||||
// ipsetProps returns the properties of an ipset with the given name.
|
||||
func (m *ipsetMgr) ipsetProps(name string) (set ipsetProps, err error) {
|
||||
// The family doesn't seem to matter when we use a header query, so
|
||||
// query only the IPv4 one.
|
||||
//
|
||||
// TODO(a.garipov): Find out if this is a bug or a feature.
|
||||
var res *ipset.HeaderPolicy
|
||||
res, err = m.ipv4Conn.Header(name)
|
||||
if err != nil {
|
||||
return set, err
|
||||
}
|
||||
|
||||
if res == nil || res.Family == nil {
|
||||
return set, errors.Error("empty response or no family data")
|
||||
}
|
||||
|
||||
family := netfilter.ProtoFamily(res.Family.Value)
|
||||
if family != netfilter.ProtoIPv4 && family != netfilter.ProtoIPv6 {
|
||||
return set, fmt.Errorf("unexpected ipset family %d", family)
|
||||
}
|
||||
|
||||
return ipsetProps{
|
||||
name: name,
|
||||
family: family,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ipsets returns currently known ipsets.
|
||||
func (m *ipsetMgr) ipsets(names []string) (sets []ipsetProps, err error) {
|
||||
for _, name := range names {
|
||||
set, ok := m.nameToIpset[name]
|
||||
if ok {
|
||||
sets = append(sets, set)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
set, err = m.ipsetProps(name)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("querying ipset %q: %w", name, err)
|
||||
}
|
||||
|
||||
m.nameToIpset[name] = set
|
||||
sets = append(sets, set)
|
||||
}
|
||||
|
||||
return sets, nil
|
||||
}
|
||||
|
||||
// newIpsetMgrWithDialer returns a new Linux ipset manager using the provided
|
||||
// dialer.
|
||||
func newIpsetMgrWithDialer(ipsetConf []string, dial ipsetDialer) (mgr IpsetManager, err error) {
|
||||
defer func() { err = errors.Annotate(err, "ipset: %w") }()
|
||||
|
||||
m := &ipsetMgr{
|
||||
mu: &sync.Mutex{},
|
||||
|
||||
nameToIpset: make(map[string]ipsetProps),
|
||||
domainToIpsets: make(map[string][]ipsetProps),
|
||||
|
||||
dial: dial,
|
||||
|
||||
addedIPs: make(map[[16]byte]unit),
|
||||
}
|
||||
|
||||
err = m.dialNetfilter(&netlink.Config{})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("dialing netfilter: %w", err)
|
||||
}
|
||||
|
||||
for i, confStr := range ipsetConf {
|
||||
var hosts, ipsetNames []string
|
||||
hosts, ipsetNames, err = parseIpsetConfig(confStr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("config line at idx %d: %w", i, err)
|
||||
}
|
||||
|
||||
var ipsets []ipsetProps
|
||||
ipsets, err = m.ipsets(ipsetNames)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(
|
||||
"getting ipsets from config line at idx %d: %w",
|
||||
i,
|
||||
err,
|
||||
)
|
||||
}
|
||||
|
||||
for _, host := range hosts {
|
||||
m.domainToIpsets[host] = append(m.domainToIpsets[host], ipsets...)
|
||||
}
|
||||
}
|
||||
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// lookupHost find the ipsets for the host, taking subdomain wildcards into
|
||||
// account.
|
||||
func (m *ipsetMgr) lookupHost(host string) (sets []ipsetProps) {
|
||||
// Search for matching ipset hosts starting with most specific domain.
|
||||
// We could use a trie here but the simple, inefficient solution isn't
|
||||
// that expensive: ~10 ns for TLD + SLD vs. ~140 ns for 10 subdomains on
|
||||
// an AMD Ryzen 7 PRO 4750U CPU; ~120 ns vs. ~ 1500 ns on a Raspberry
|
||||
// Pi's ARMv7 rev 4 CPU.
|
||||
for i := 0; ; i++ {
|
||||
host = host[i:]
|
||||
sets = m.domainToIpsets[host]
|
||||
if sets != nil {
|
||||
return sets
|
||||
}
|
||||
|
||||
i = strings.Index(host, ".")
|
||||
if i == -1 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Check the root catch-all one.
|
||||
return m.domainToIpsets[""]
|
||||
}
|
||||
|
||||
// addIPs adds the IP addresses for the host to the ipset. set must be same
|
||||
// family as set's family.
|
||||
func (m *ipsetMgr) addIPs(host string, set ipsetProps, ips []net.IP) (n int, err error) {
|
||||
if len(ips) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
var entries []*ipset.Entry
|
||||
var newAddedIPs [][16]byte
|
||||
for _, ip := range ips {
|
||||
var iparr [16]byte
|
||||
copy(iparr[:], ip.To16())
|
||||
if _, added := m.addedIPs[iparr]; added {
|
||||
continue
|
||||
}
|
||||
|
||||
entries = append(entries, ipset.NewEntry(ipset.EntryIP(ip)))
|
||||
newAddedIPs = append(newAddedIPs, iparr)
|
||||
}
|
||||
|
||||
n = len(entries)
|
||||
if n == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
var conn ipsetConn
|
||||
switch set.family {
|
||||
case netfilter.ProtoIPv4:
|
||||
conn = m.ipv4Conn
|
||||
case netfilter.ProtoIPv6:
|
||||
conn = m.ipv6Conn
|
||||
default:
|
||||
return 0, fmt.Errorf("unexpected family %s for ipset %q", set.family, set.name)
|
||||
}
|
||||
|
||||
err = conn.Add(set.name, entries...)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("adding %q%s to ipset %q: %w", host, ips, set.name, err)
|
||||
}
|
||||
|
||||
// Only add these to the cache once we're sure that all of them were
|
||||
// actually sent to the ipset.
|
||||
for _, iparr := range newAddedIPs {
|
||||
m.addedIPs[iparr] = unit{}
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// addToSets adds the IP addresses to the corresponding ipset.
|
||||
func (m *ipsetMgr) addToSets(
|
||||
host string,
|
||||
ip4s []net.IP,
|
||||
ip6s []net.IP,
|
||||
sets []ipsetProps,
|
||||
) (n int, err error) {
|
||||
for _, set := range sets {
|
||||
var nn int
|
||||
switch set.family {
|
||||
case netfilter.ProtoIPv4:
|
||||
nn, err = m.addIPs(host, set, ip4s)
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
case netfilter.ProtoIPv6:
|
||||
nn, err = m.addIPs(host, set, ip6s)
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
default:
|
||||
return n, fmt.Errorf("unexpected family %s for ipset %q", set.family, set.name)
|
||||
}
|
||||
|
||||
n += nn
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// Add implements the IpsetManager interface for *ipsetMgr
|
||||
func (m *ipsetMgr) Add(host string, ip4s, ip6s []net.IP) (n int, err error) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
sets := m.lookupHost(host)
|
||||
if len(sets) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
return m.addToSets(host, ip4s, ip6s, sets)
|
||||
}
|
||||
|
||||
// Close implements the IpsetManager interface for *ipsetMgr.
|
||||
func (m *ipsetMgr) Close() (err error) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
var errs []error
|
||||
|
||||
// Close both and collect errors so that the errors from closing one
|
||||
// don't interfere with closing the other.
|
||||
err = m.ipv4Conn.Close()
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
|
||||
err = m.ipv6Conn.Close()
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
|
||||
if len(errs) != 0 {
|
||||
return errors.List("closing ipsets", errs...)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
155
internal/aghnet/ipset_linux_test.go
Normal file
155
internal/aghnet/ipset_linux_test.go
Normal file
@@ -0,0 +1,155 @@
|
||||
//go:build linux
|
||||
// +build linux
|
||||
|
||||
package aghnet
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/digineo/go-ipset/v2"
|
||||
"github.com/mdlayher/netlink"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/ti-mo/netfilter"
|
||||
)
|
||||
|
||||
// fakeIpsetConn is a fake ipsetConn for tests.
|
||||
type fakeIpsetConn struct {
|
||||
ipv4Header *ipset.HeaderPolicy
|
||||
ipv4Entries *[]*ipset.Entry
|
||||
ipv6Header *ipset.HeaderPolicy
|
||||
ipv6Entries *[]*ipset.Entry
|
||||
}
|
||||
|
||||
// Add implements the ipsetConn interface for *fakeIpsetConn.
|
||||
func (c *fakeIpsetConn) Add(name string, entries ...*ipset.Entry) (err error) {
|
||||
if strings.Contains(name, "ipv4") {
|
||||
*c.ipv4Entries = append(*c.ipv4Entries, entries...)
|
||||
|
||||
return nil
|
||||
} else if strings.Contains(name, "ipv6") {
|
||||
*c.ipv6Entries = append(*c.ipv6Entries, entries...)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
return errors.Error("test: ipset not found")
|
||||
}
|
||||
|
||||
// Close implements the ipsetConn interface for *fakeIpsetConn.
|
||||
func (c *fakeIpsetConn) Close() (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Header implements the ipsetConn interface for *fakeIpsetConn.
|
||||
func (c *fakeIpsetConn) Header(name string) (p *ipset.HeaderPolicy, err error) {
|
||||
if strings.Contains(name, "ipv4") {
|
||||
return c.ipv4Header, nil
|
||||
} else if strings.Contains(name, "ipv6") {
|
||||
return c.ipv6Header, nil
|
||||
}
|
||||
|
||||
return nil, errors.Error("test: ipset not found")
|
||||
}
|
||||
|
||||
func TestIpsetMgr_Add(t *testing.T) {
|
||||
ipsetConf := []string{
|
||||
"example.com,example.net/ipv4set",
|
||||
"example.org,example.biz/ipv6set",
|
||||
}
|
||||
|
||||
var ipv4Entries []*ipset.Entry
|
||||
var ipv6Entries []*ipset.Entry
|
||||
|
||||
fakeDial := func(
|
||||
pf netfilter.ProtoFamily,
|
||||
conf *netlink.Config,
|
||||
) (conn ipsetConn, err error) {
|
||||
return &fakeIpsetConn{
|
||||
ipv4Header: &ipset.HeaderPolicy{
|
||||
Family: ipset.NewUInt8Box(uint8(netfilter.ProtoIPv4)),
|
||||
},
|
||||
ipv4Entries: &ipv4Entries,
|
||||
ipv6Header: &ipset.HeaderPolicy{
|
||||
Family: ipset.NewUInt8Box(uint8(netfilter.ProtoIPv6)),
|
||||
},
|
||||
ipv6Entries: &ipv6Entries,
|
||||
}, nil
|
||||
}
|
||||
|
||||
m, err := newIpsetMgrWithDialer(ipsetConf, fakeDial)
|
||||
require.NoError(t, err)
|
||||
|
||||
ip4 := net.IP{1, 2, 3, 4}
|
||||
ip6 := net.IP{
|
||||
0x12, 0x34, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x56, 0x78,
|
||||
}
|
||||
|
||||
n, err := m.Add("example.net", []net.IP{ip4}, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, 1, n)
|
||||
|
||||
require.Len(t, ipv4Entries, 1)
|
||||
|
||||
gotIP4 := ipv4Entries[0].IP.Value
|
||||
assert.Equal(t, ip4, gotIP4)
|
||||
|
||||
n, err = m.Add("example.biz", nil, []net.IP{ip6})
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, 1, n)
|
||||
|
||||
require.Len(t, ipv6Entries, 1)
|
||||
|
||||
gotIP6 := ipv6Entries[0].IP.Value
|
||||
assert.Equal(t, ip6, gotIP6)
|
||||
|
||||
err = m.Close()
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
var ipsetPropsSink []ipsetProps
|
||||
|
||||
func BenchmarkIpsetMgr_lookupHost(b *testing.B) {
|
||||
propsLong := []ipsetProps{{
|
||||
name: "example.com",
|
||||
family: netfilter.ProtoIPv4,
|
||||
}}
|
||||
|
||||
propsShort := []ipsetProps{{
|
||||
name: "example.net",
|
||||
family: netfilter.ProtoIPv4,
|
||||
}}
|
||||
|
||||
m := &ipsetMgr{
|
||||
domainToIpsets: map[string][]ipsetProps{
|
||||
"": propsLong,
|
||||
"example.net": propsShort,
|
||||
},
|
||||
}
|
||||
|
||||
b.Run("long", func(b *testing.B) {
|
||||
const name = "a.very.long.domain.name.inside.the.domain.example.com"
|
||||
for i := 0; i < b.N; i++ {
|
||||
ipsetPropsSink = m.lookupHost(name)
|
||||
}
|
||||
|
||||
require.Equal(b, propsLong, ipsetPropsSink)
|
||||
})
|
||||
|
||||
b.Run("short", func(b *testing.B) {
|
||||
const name = "example.net"
|
||||
for i := 0; i < b.N; i++ {
|
||||
ipsetPropsSink = m.lookupHost(name)
|
||||
}
|
||||
|
||||
require.Equal(b, propsShort, ipsetPropsSink)
|
||||
})
|
||||
}
|
||||
12
internal/aghnet/ipset_others.go
Normal file
12
internal/aghnet/ipset_others.go
Normal file
@@ -0,0 +1,12 @@
|
||||
//go:build !linux
|
||||
// +build !linux
|
||||
|
||||
package aghnet
|
||||
|
||||
import (
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghos"
|
||||
)
|
||||
|
||||
func newIpsetMgr(_ []string) (mgr IpsetManager, err error) {
|
||||
return nil, aghos.Unsupported("ipset")
|
||||
}
|
||||
@@ -13,9 +13,9 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghstrings"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/AdguardTeam/golibs/stringutil"
|
||||
)
|
||||
|
||||
// ErrNoStaticIPInfo is returned by IfaceHasStaticIP when no information about
|
||||
@@ -71,6 +71,12 @@ func CanBindPort(port int) (can bool, err error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// CanBindPrivilegedPorts checks if current process can bind to privileged
|
||||
// ports.
|
||||
func CanBindPrivilegedPorts() (can bool, err error) {
|
||||
return canBindPrivilegedPorts()
|
||||
}
|
||||
|
||||
// NetInterface represents an entry of network interfaces map.
|
||||
type NetInterface struct {
|
||||
MTU int `json:"mtu"`
|
||||
@@ -365,14 +371,14 @@ func ReverseAddr(ip net.IP) (arpa string) {
|
||||
strLen, suffix = arpaV4MaxLen, arpaV4Suffix[1:]
|
||||
ip = ip4
|
||||
writeByte = func(val byte) {
|
||||
aghstrings.WriteToBuilder(b, strconv.Itoa(int(val)), dot)
|
||||
stringutil.WriteToBuilder(b, strconv.Itoa(int(val)), dot)
|
||||
}
|
||||
|
||||
} else if ip6 := ip.To16(); ip6 != nil {
|
||||
strLen, suffix = arpaV6MaxLen, arpaV6Suffix[1:]
|
||||
ip = ip6
|
||||
writeByte = func(val byte) {
|
||||
aghstrings.WriteToBuilder(
|
||||
stringutil.WriteToBuilder(
|
||||
b,
|
||||
strconv.FormatUint(uint64(val&0xF), 16),
|
||||
dot,
|
||||
@@ -389,7 +395,7 @@ func ReverseAddr(ip net.IP) (arpa string) {
|
||||
for i := len(ip) - 1; i >= 0; i-- {
|
||||
writeByte(ip[i])
|
||||
}
|
||||
aghstrings.WriteToBuilder(b, suffix)
|
||||
stringutil.WriteToBuilder(b, suffix)
|
||||
|
||||
return b.String()
|
||||
}
|
||||
@@ -424,9 +430,3 @@ func CollectAllIfacesAddrs() (addrs []string, err error) {
|
||||
|
||||
return addrs, nil
|
||||
}
|
||||
|
||||
// JoinHostPort is a convinient wrapper for net.JoinHostPort with port of type
|
||||
// int.
|
||||
func JoinHostPort(host string, port int) (hostport string) {
|
||||
return net.JoinHostPort(host, strconv.Itoa(port))
|
||||
}
|
||||
|
||||
@@ -13,8 +13,8 @@ import (
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
)
|
||||
|
||||
// hardwarePortInfo - information obtained using MacOS networksetup
|
||||
// about the current state of the internet connection
|
||||
// hardwarePortInfo contains information about the current state of the internet
|
||||
// connection obtained from macOS networksetup.
|
||||
type hardwarePortInfo struct {
|
||||
name string
|
||||
ip string
|
||||
@@ -23,6 +23,10 @@ type hardwarePortInfo struct {
|
||||
static bool
|
||||
}
|
||||
|
||||
func canBindPrivilegedPorts() (can bool, err error) {
|
||||
return aghos.HaveAdminRights()
|
||||
}
|
||||
|
||||
func ifaceHasStaticIP(ifaceName string) (bool, error) {
|
||||
portInfo, err := getCurrentHardwarePortInfo(ifaceName)
|
||||
if err != nil {
|
||||
|
||||
78
internal/aghnet/net_freebsd.go
Normal file
78
internal/aghnet/net_freebsd.go
Normal file
@@ -0,0 +1,78 @@
|
||||
//go:build freebsd
|
||||
// +build freebsd
|
||||
|
||||
package aghnet
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghio"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghos"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
)
|
||||
|
||||
func canBindPrivilegedPorts() (can bool, err error) {
|
||||
return aghos.HaveAdminRights()
|
||||
}
|
||||
|
||||
// maxCheckedFileSize is the maximum acceptable length of the /etc/rc.conf file.
|
||||
const maxCheckedFileSize = 1024 * 1024
|
||||
|
||||
func ifaceHasStaticIP(ifaceName string) (ok bool, err error) {
|
||||
const filename = "/etc/rc.conf"
|
||||
|
||||
var f *os.File
|
||||
f, err = os.Open(filename)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer func() { err = errors.WithDeferred(err, f.Close()) }()
|
||||
|
||||
var r io.Reader
|
||||
r, err = aghio.LimitReader(f, maxCheckedFileSize)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return rcConfStaticConfig(r, ifaceName)
|
||||
}
|
||||
|
||||
// rcConfStaticConfig checks if the interface is configured by /etc/rc.conf to
|
||||
// have a static IP.
|
||||
func rcConfStaticConfig(r io.Reader, ifaceName string) (has bool, err error) {
|
||||
s := bufio.NewScanner(r)
|
||||
for ifaceLinePref := fmt.Sprintf("ifconfig_%s", ifaceName); s.Scan(); {
|
||||
line := strings.TrimSpace(s.Text())
|
||||
if !strings.HasPrefix(line, ifaceLinePref) {
|
||||
continue
|
||||
}
|
||||
|
||||
eqIdx := len(ifaceLinePref)
|
||||
if line[eqIdx] != '=' {
|
||||
continue
|
||||
}
|
||||
|
||||
fieldsStart, fieldsEnd := eqIdx+2, len(line)-1
|
||||
if fieldsStart >= fieldsEnd {
|
||||
continue
|
||||
}
|
||||
|
||||
fields := strings.Fields(line[fieldsStart:fieldsEnd])
|
||||
if len(fields) >= 2 &&
|
||||
strings.ToLower(fields[0]) == "inet" &&
|
||||
net.ParseIP(fields[1]) != nil {
|
||||
return true, s.Err()
|
||||
}
|
||||
}
|
||||
|
||||
return false, s.Err()
|
||||
}
|
||||
|
||||
func ifaceSetStaticIP(string) (err error) {
|
||||
return aghos.Unsupported("setting static ip")
|
||||
}
|
||||
60
internal/aghnet/net_freebsd_test.go
Normal file
60
internal/aghnet/net_freebsd_test.go
Normal file
@@ -0,0 +1,60 @@
|
||||
//go:build freebsd
|
||||
// +build freebsd
|
||||
|
||||
package aghnet
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestRcConfStaticConfig(t *testing.T) {
|
||||
const ifaceName = `em0`
|
||||
const nl = "\n"
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
rcconfData string
|
||||
wantHas bool
|
||||
}{{
|
||||
name: "simple",
|
||||
rcconfData: `ifconfig_em0="inet 127.0.0.253 netmask 0xffffffff"` + nl,
|
||||
wantHas: true,
|
||||
}, {
|
||||
name: "case_insensitiveness",
|
||||
rcconfData: `ifconfig_em0="InEt 127.0.0.253 NeTmAsK 0xffffffff"` + nl,
|
||||
wantHas: true,
|
||||
}, {
|
||||
name: "comments_and_trash",
|
||||
rcconfData: `# comment 1` + nl +
|
||||
`` + nl +
|
||||
`# comment 2` + nl +
|
||||
`ifconfig_em0="inet 127.0.0.253 netmask 0xffffffff"` + nl,
|
||||
wantHas: true,
|
||||
}, {
|
||||
name: "aliases",
|
||||
rcconfData: `ifconfig_em0_alias="inet 127.0.0.1/24"` + nl +
|
||||
`ifconfig_em0="inet 127.0.0.253 netmask 0xffffffff"` + nl,
|
||||
wantHas: true,
|
||||
}, {
|
||||
name: "incorrect_config",
|
||||
rcconfData: `ifconfig_em0="inet6 127.0.0.253 netmask 0xffffffff"` + nl +
|
||||
`ifconfig_em0="inet 127.0.0.253 net-mask 0xffffffff"` + nl +
|
||||
`ifconfig_em0="inet 256.256.256.256 netmask 0xffffffff"` + nl +
|
||||
`ifconfig_em0=""` + nl,
|
||||
wantHas: false,
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
r := strings.NewReader(tc.rcconfData)
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
has, err := rcConfStaticConfig(r, ifaceName)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, tc.wantHas, has)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -9,16 +9,132 @@ import (
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghio"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghos"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/stringutil"
|
||||
"github.com/google/renameio/maybe"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// maxConfigFileSize is the maximum assumed length of the interface
|
||||
// configuration file.
|
||||
const maxConfigFileSize = 1024 * 1024
|
||||
// recurrentChecker is used to check all the files which may include references
|
||||
// for other ones.
|
||||
type recurrentChecker struct {
|
||||
// checker is the function to check if r's stream contains the desired
|
||||
// attribute. It must return all the patterns for files which should
|
||||
// also be checked and each of them should be valid for filepath.Glob
|
||||
// function.
|
||||
checker func(r io.Reader, desired string) (patterns []string, has bool, err error)
|
||||
// initPath is the path of the first member in the sequence of checked
|
||||
// files.
|
||||
initPath string
|
||||
}
|
||||
|
||||
// maxCheckedFileSize is the maximum length of the file that recurrentChecker
|
||||
// may check.
|
||||
const maxCheckedFileSize = 1024 * 1024
|
||||
|
||||
// checkFile tries to open and to check single file located on the sourcePath.
|
||||
func (rc *recurrentChecker) checkFile(sourcePath, desired string) (
|
||||
subsources []string,
|
||||
has bool,
|
||||
err error,
|
||||
) {
|
||||
var f *os.File
|
||||
f, err = os.Open(sourcePath)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
defer func() { err = errors.WithDeferred(err, f.Close()) }()
|
||||
|
||||
var r io.Reader
|
||||
r, err = aghio.LimitReader(f, maxCheckedFileSize)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
subsources, has, err = rc.checker(r, desired)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
if has {
|
||||
return nil, true, nil
|
||||
}
|
||||
|
||||
return subsources, has, nil
|
||||
}
|
||||
|
||||
// handlePatterns parses the patterns and takes care of duplicates.
|
||||
func (rc *recurrentChecker) handlePatterns(sourcesSet *stringutil.Set, patterns []string) (
|
||||
subsources []string,
|
||||
err error,
|
||||
) {
|
||||
subsources = make([]string, 0, len(patterns))
|
||||
for _, p := range patterns {
|
||||
var matches []string
|
||||
matches, err = filepath.Glob(p)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid pattern %q: %w", p, err)
|
||||
}
|
||||
|
||||
for _, m := range matches {
|
||||
if sourcesSet.Has(m) {
|
||||
continue
|
||||
}
|
||||
|
||||
sourcesSet.Add(m)
|
||||
subsources = append(subsources, m)
|
||||
}
|
||||
}
|
||||
|
||||
return subsources, nil
|
||||
}
|
||||
|
||||
// check walks through all the files searching for the desired attribute.
|
||||
func (rc *recurrentChecker) check(desired string) (has bool, err error) {
|
||||
var i int
|
||||
sources := []string{rc.initPath}
|
||||
|
||||
defer func() {
|
||||
if i >= len(sources) {
|
||||
return
|
||||
}
|
||||
|
||||
err = errors.Annotate(err, "checking %q: %w", sources[i])
|
||||
}()
|
||||
|
||||
var patterns, subsources []string
|
||||
// The slice of sources is separate from the set of sources to keep the
|
||||
// order in which the files are walked.
|
||||
for sourcesSet := stringutil.NewSet(rc.initPath); i < len(sources); i++ {
|
||||
patterns, has, err = rc.checkFile(sources[i], desired)
|
||||
if err != nil {
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
continue
|
||||
}
|
||||
|
||||
return false, err
|
||||
}
|
||||
|
||||
if has {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
subsources, err = rc.handlePatterns(sourcesSet, patterns)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
sources = append(sources, subsources...)
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func ifaceHasStaticIP(ifaceName string) (has bool, err error) {
|
||||
// TODO(a.garipov): Currently, this function returns the first
|
||||
@@ -26,48 +142,34 @@ func ifaceHasStaticIP(ifaceName string) (has bool, err error) {
|
||||
// /etc/network/interfaces doesn't, it will return true. Perhaps this
|
||||
// is not the most desirable behavior.
|
||||
|
||||
for _, check := range []struct {
|
||||
checker func(io.Reader, string) (bool, error)
|
||||
filePath string
|
||||
}{{
|
||||
for _, rc := range []*recurrentChecker{{
|
||||
checker: dhcpcdStaticConfig,
|
||||
filePath: "/etc/dhcpcd.conf",
|
||||
initPath: "/etc/dhcpcd.conf",
|
||||
}, {
|
||||
checker: ifacesStaticConfig,
|
||||
filePath: "/etc/network/interfaces",
|
||||
initPath: "/etc/network/interfaces",
|
||||
}} {
|
||||
var f *os.File
|
||||
f, err = os.Open(check.filePath)
|
||||
if err != nil {
|
||||
// ErrNotExist can happen here if there is no such file.
|
||||
// This is normal, as not every system uses those files.
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
err = nil
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
return false, err
|
||||
}
|
||||
defer func() { err = errors.WithDeferred(err, f.Close()) }()
|
||||
|
||||
var fileReader io.Reader
|
||||
fileReader, err = aghio.LimitReader(f, maxConfigFileSize)
|
||||
has, err = rc.check(ifaceName)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
has, err = check.checker(fileReader, ifaceName)
|
||||
if err != nil {
|
||||
return false, err
|
||||
if has {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return has, nil
|
||||
}
|
||||
|
||||
return false, ErrNoStaticIPInfo
|
||||
}
|
||||
|
||||
func canBindPrivilegedPorts() (can bool, err error) {
|
||||
cnbs, err := unix.PrctlRetInt(unix.PR_CAP_AMBIENT, unix.PR_CAP_AMBIENT_IS_SET, unix.CAP_NET_BIND_SERVICE, 0, 0)
|
||||
// Don't check the error because it's always nil on Linux.
|
||||
adm, _ := aghos.HaveAdminRights()
|
||||
|
||||
return cnbs == 1 || adm, err
|
||||
}
|
||||
|
||||
// findIfaceLine scans s until it finds the line that declares an interface with
|
||||
// the given name. If findIfaceLine can't find the line, it returns false.
|
||||
func findIfaceLine(s *bufio.Scanner, name string) (ok bool) {
|
||||
@@ -84,11 +186,11 @@ func findIfaceLine(s *bufio.Scanner, name string) (ok bool) {
|
||||
|
||||
// dhcpcdStaticConfig checks if interface is configured by /etc/dhcpcd.conf to
|
||||
// have a static IP.
|
||||
func dhcpcdStaticConfig(r io.Reader, ifaceName string) (has bool, err error) {
|
||||
func dhcpcdStaticConfig(r io.Reader, ifaceName string) (subsources []string, has bool, err error) {
|
||||
s := bufio.NewScanner(r)
|
||||
ifaceFound := findIfaceLine(s, ifaceName)
|
||||
if !ifaceFound {
|
||||
return false, s.Err()
|
||||
return nil, false, s.Err()
|
||||
}
|
||||
|
||||
for s.Scan() {
|
||||
@@ -97,7 +199,7 @@ func dhcpcdStaticConfig(r io.Reader, ifaceName string) (has bool, err error) {
|
||||
if len(fields) >= 2 &&
|
||||
fields[0] == "static" &&
|
||||
strings.HasPrefix(fields[1], "ip_address=") {
|
||||
return true, s.Err()
|
||||
return nil, true, s.Err()
|
||||
}
|
||||
|
||||
if len(fields) > 0 && fields[0] == "interface" {
|
||||
@@ -106,29 +208,41 @@ func dhcpcdStaticConfig(r io.Reader, ifaceName string) (has bool, err error) {
|
||||
}
|
||||
}
|
||||
|
||||
return false, s.Err()
|
||||
return nil, false, s.Err()
|
||||
}
|
||||
|
||||
// ifacesStaticConfig checks if interface is configured by
|
||||
// /etc/network/interfaces to have a static IP.
|
||||
func ifacesStaticConfig(r io.Reader, ifaceName string) (has bool, err error) {
|
||||
// ifacesStaticConfig checks if the interface is configured by any file of
|
||||
// /etc/network/interfaces format to have a static IP.
|
||||
func ifacesStaticConfig(r io.Reader, ifaceName string) (subsources []string, has bool, err error) {
|
||||
s := bufio.NewScanner(r)
|
||||
for s.Scan() {
|
||||
line := strings.TrimSpace(s.Text())
|
||||
|
||||
if len(line) == 0 || line[0] == '#' {
|
||||
continue
|
||||
}
|
||||
|
||||
// TODO(e.burkov): As man page interfaces(5) says, a line may be
|
||||
// extended across multiple lines by making the last character a
|
||||
// backslash. Provide extended lines and "source-directory"
|
||||
// stanzas support.
|
||||
|
||||
fields := strings.Fields(line)
|
||||
fieldsNum := len(fields)
|
||||
|
||||
// Man page interfaces(5) declares that interface definition
|
||||
// should consist of the key word "iface" followed by interface
|
||||
// name, and method at fourth field.
|
||||
if len(fields) >= 4 && fields[0] == "iface" && fields[1] == ifaceName && fields[3] == "static" {
|
||||
return true, nil
|
||||
if fieldsNum >= 4 &&
|
||||
fields[0] == "iface" && fields[1] == ifaceName && fields[3] == "static" {
|
||||
return nil, true, nil
|
||||
}
|
||||
|
||||
if fieldsNum >= 2 && fields[0] == "source" {
|
||||
subsources = append(subsources, fields[1])
|
||||
}
|
||||
}
|
||||
return false, s.Err()
|
||||
|
||||
return subsources, false, s.Err()
|
||||
}
|
||||
|
||||
// ifaceSetStaticIP configures the system to retain its current IP on the
|
||||
|
||||
@@ -12,6 +12,21 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestRecurrentChecker(t *testing.T) {
|
||||
c := &recurrentChecker{
|
||||
checker: ifacesStaticConfig,
|
||||
initPath: "./testdata/include-subsources",
|
||||
}
|
||||
|
||||
has, err := c.check("sample_name")
|
||||
require.NoError(t, err)
|
||||
assert.True(t, has)
|
||||
|
||||
has, err = c.check("another_name")
|
||||
require.NoError(t, err)
|
||||
assert.False(t, has)
|
||||
}
|
||||
|
||||
const nl = "\n"
|
||||
|
||||
func TestDHCPCDStaticConfig(t *testing.T) {
|
||||
@@ -49,7 +64,7 @@ func TestDHCPCDStaticConfig(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
r := bytes.NewReader(tc.data)
|
||||
has, err := dhcpcdStaticConfig(r, "wlan0")
|
||||
_, has, err := dhcpcdStaticConfig(r, "wlan0")
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, tc.want, has)
|
||||
@@ -59,9 +74,10 @@ func TestDHCPCDStaticConfig(t *testing.T) {
|
||||
|
||||
func TestIfacesStaticConfig(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
data []byte
|
||||
want bool
|
||||
name string
|
||||
data []byte
|
||||
want bool
|
||||
wantPatterns []string
|
||||
}{{
|
||||
name: "has_not",
|
||||
data: []byte(`allow-hotplug enp0s3` + nl +
|
||||
@@ -71,7 +87,8 @@ func TestIfacesStaticConfig(t *testing.T) {
|
||||
`# gateway 192.168.0.1` + nl +
|
||||
`iface enp0s3 inet dhcp` + nl,
|
||||
),
|
||||
want: false,
|
||||
want: false,
|
||||
wantPatterns: []string{},
|
||||
}, {
|
||||
name: "has",
|
||||
data: []byte(`allow-hotplug enp0s3` + nl +
|
||||
@@ -81,16 +98,36 @@ func TestIfacesStaticConfig(t *testing.T) {
|
||||
` gateway 192.168.0.1` + nl +
|
||||
`#iface enp0s3 inet dhcp` + nl,
|
||||
),
|
||||
want: true,
|
||||
want: true,
|
||||
wantPatterns: []string{},
|
||||
}, {
|
||||
name: "return_patterns",
|
||||
data: []byte(`source hello` + nl +
|
||||
`source world` + nl +
|
||||
`#iface enp0s3 inet static` + nl,
|
||||
),
|
||||
want: false,
|
||||
wantPatterns: []string{"hello", "world"},
|
||||
}, {
|
||||
// This one tests if the first found valid interface prevents
|
||||
// checking files under the `source` directive.
|
||||
name: "ignore_patterns",
|
||||
data: []byte(`source hello` + nl +
|
||||
`source world` + nl +
|
||||
`iface enp0s3 inet static` + nl,
|
||||
),
|
||||
want: true,
|
||||
wantPatterns: []string{},
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
r := bytes.NewReader(tc.data)
|
||||
has, err := ifacesStaticConfig(r, "enp0s3")
|
||||
patterns, has, err := ifacesStaticConfig(r, "enp0s3")
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, tc.want, has)
|
||||
assert.ElementsMatch(t, tc.wantPatterns, patterns)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
//go:build !(linux || darwin)
|
||||
// +build !linux,!darwin
|
||||
//go:build !(linux || darwin || freebsd)
|
||||
// +build !linux,!darwin,!freebsd
|
||||
|
||||
package aghnet
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghos"
|
||||
)
|
||||
|
||||
func ifaceHasStaticIP(string) (bool, error) {
|
||||
return false, fmt.Errorf("cannot check if IP is static: not supported on %s", runtime.GOOS)
|
||||
func canBindPrivilegedPorts() (can bool, err error) {
|
||||
return aghos.HaveAdminRights()
|
||||
}
|
||||
|
||||
func ifaceSetStaticIP(string) error {
|
||||
return fmt.Errorf("cannot set static IP on %s", runtime.GOOS)
|
||||
func ifaceHasStaticIP(string) (ok bool, err error) {
|
||||
return false, aghos.Unsupported("checking static ip")
|
||||
}
|
||||
|
||||
func ifaceSetStaticIP(string) (err error) {
|
||||
return aghos.Unsupported("setting static ip")
|
||||
}
|
||||
|
||||
@@ -11,8 +11,8 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghstrings"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/stringutil"
|
||||
)
|
||||
|
||||
// defaultHostGen is the default method of generating host for Refresh.
|
||||
@@ -27,7 +27,7 @@ type systemResolvers struct {
|
||||
hostGenFunc HostGenFunc
|
||||
|
||||
// addrs is the set that contains cached local resolvers' addresses.
|
||||
addrs *aghstrings.Set
|
||||
addrs *stringutil.Set
|
||||
addrsLock sync.RWMutex
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ func newSystemResolvers(refreshIvl time.Duration, hostGenFunc HostGenFunc) (sr S
|
||||
PreferGo: true,
|
||||
},
|
||||
hostGenFunc: hostGenFunc,
|
||||
addrs: aghstrings.NewSet(),
|
||||
addrs: stringutil.NewSet(),
|
||||
}
|
||||
s.resolver.Dial = s.dialFunc
|
||||
|
||||
|
||||
1
internal/aghnet/testdata/ifaces
vendored
Normal file
1
internal/aghnet/testdata/ifaces
vendored
Normal file
@@ -0,0 +1 @@
|
||||
iface sample_name inet static
|
||||
5
internal/aghnet/testdata/include-subsources
vendored
Normal file
5
internal/aghnet/testdata/include-subsources
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
# The "testdata" part is added here because the test is actually run from the
|
||||
# parent directory. Real interface files usually contain only absolute paths.
|
||||
|
||||
source ./testdata/ifaces
|
||||
source ./testdata/*
|
||||
@@ -1,4 +1,6 @@
|
||||
// Package aghos contains utilities for functions requiring system calls.
|
||||
// Package aghos contains utilities for functions requiring system calls and
|
||||
// other OS-specific APIs. OS-specific network handling should go to aghnet
|
||||
// instead.
|
||||
package aghos
|
||||
|
||||
import (
|
||||
@@ -29,12 +31,6 @@ func Unsupported(op string) (err error) {
|
||||
}
|
||||
}
|
||||
|
||||
// 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.
|
||||
//
|
||||
// See https://github.com/AdguardTeam/AdGuardHome/internal/issues/659.
|
||||
|
||||
@@ -8,10 +8,6 @@ import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func canBindPrivilegedPorts() (can bool, err error) {
|
||||
return HaveAdminRights()
|
||||
}
|
||||
|
||||
func setRlimit(val uint64) (err error) {
|
||||
var rlim syscall.Rlimit
|
||||
rlim.Max = val
|
||||
|
||||
@@ -8,10 +8,6 @@ import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func canBindPrivilegedPorts() (can bool, err error) {
|
||||
return HaveAdminRights()
|
||||
}
|
||||
|
||||
func setRlimit(val uint64) (err error) {
|
||||
var rlim syscall.Rlimit
|
||||
rlim.Max = int64(val)
|
||||
|
||||
@@ -9,18 +9,8 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func canBindPrivilegedPorts() (can bool, err error) {
|
||||
cnbs, err := unix.PrctlRetInt(unix.PR_CAP_AMBIENT, unix.PR_CAP_AMBIENT_IS_SET, unix.CAP_NET_BIND_SERVICE, 0, 0)
|
||||
// Don't check the error because it's always nil on Linux.
|
||||
adm, _ := haveAdminRights()
|
||||
|
||||
return cnbs == 1 || adm, err
|
||||
}
|
||||
|
||||
func setRlimit(val uint64) (err error) {
|
||||
var rlim syscall.Rlimit
|
||||
rlim.Max = val
|
||||
|
||||
@@ -9,10 +9,6 @@ import (
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
func canBindPrivilegedPorts() (can bool, err error) {
|
||||
return HaveAdminRights()
|
||||
}
|
||||
|
||||
func setRlimit(val uint64) (err error) {
|
||||
return Unsupported("setrlimit")
|
||||
}
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
package aghstrings
|
||||
|
||||
// unit is a convenient alias for struct{}
|
||||
type unit = struct{}
|
||||
|
||||
// Set is a set of strings.
|
||||
type Set struct {
|
||||
m map[string]unit
|
||||
}
|
||||
|
||||
// NewSet returns a new string set containing strs.
|
||||
func NewSet(strs ...string) (set *Set) {
|
||||
set = &Set{
|
||||
m: make(map[string]unit, len(strs)),
|
||||
}
|
||||
|
||||
for _, s := range strs {
|
||||
set.Add(s)
|
||||
}
|
||||
|
||||
return set
|
||||
}
|
||||
|
||||
// Add adds s to the set. Add panics if the set is a nil set, just like a nil
|
||||
// map does.
|
||||
func (set *Set) Add(s string) {
|
||||
set.m[s] = unit{}
|
||||
}
|
||||
|
||||
// Del deletes s from the set. Calling Del on a nil set has no effect, just
|
||||
// like delete on an empty map doesn't.
|
||||
func (set *Set) Del(s string) {
|
||||
if set != nil {
|
||||
delete(set.m, s)
|
||||
}
|
||||
}
|
||||
|
||||
// Has returns true if s is in the set. Calling Has on a nil set returns false,
|
||||
// just like indexing on an empty map does.
|
||||
func (set *Set) Has(s string) (ok bool) {
|
||||
if set != nil {
|
||||
_, ok = set.m[s]
|
||||
}
|
||||
|
||||
return ok
|
||||
}
|
||||
|
||||
// Len returns the length of the set. A nil set has a length of zero, just like
|
||||
// an empty map.
|
||||
func (set *Set) Len() (n int) {
|
||||
if set == nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
return len(set.m)
|
||||
}
|
||||
|
||||
// Values returns all values in the set. The order of the values is undefined.
|
||||
// Values returns nil if the set is nil.
|
||||
func (set *Set) Values() (strs []string) {
|
||||
if set == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
strs = make([]string, 0, len(set.m))
|
||||
for s := range set.m {
|
||||
strs = append(strs, s)
|
||||
}
|
||||
|
||||
return strs
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
package aghstrings
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestSet(t *testing.T) {
|
||||
const s = "a"
|
||||
|
||||
t.Run("nil", func(t *testing.T) {
|
||||
var set *Set
|
||||
|
||||
assert.NotPanics(t, func() {
|
||||
set.Del(s)
|
||||
})
|
||||
|
||||
assert.NotPanics(t, func() {
|
||||
assert.False(t, set.Has(s))
|
||||
})
|
||||
|
||||
assert.NotPanics(t, func() {
|
||||
assert.Equal(t, 0, set.Len())
|
||||
})
|
||||
|
||||
assert.NotPanics(t, func() {
|
||||
assert.Nil(t, set.Values())
|
||||
})
|
||||
|
||||
assert.Panics(t, func() {
|
||||
set.Add(s)
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("non_nil", func(t *testing.T) {
|
||||
set := NewSet()
|
||||
assert.Equal(t, 0, set.Len())
|
||||
|
||||
ok := set.Has(s)
|
||||
assert.False(t, ok)
|
||||
|
||||
set.Add(s)
|
||||
ok = set.Has(s)
|
||||
assert.True(t, ok)
|
||||
|
||||
assert.Equal(t, []string{s}, set.Values())
|
||||
|
||||
set.Del(s)
|
||||
ok = set.Has(s)
|
||||
assert.False(t, ok)
|
||||
|
||||
set = NewSet(s)
|
||||
assert.Equal(t, 1, set.Len())
|
||||
})
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
// Package aghstrings contains utilities dealing with strings.
|
||||
package aghstrings
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// CloneSliceOrEmpty returns the copy of a or empty strings slice if a is nil.
|
||||
func CloneSliceOrEmpty(a []string) (b []string) {
|
||||
return append([]string{}, a...)
|
||||
}
|
||||
|
||||
// CloneSlice returns the exact copy of a.
|
||||
func CloneSlice(a []string) (b []string) {
|
||||
if a == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return CloneSliceOrEmpty(a)
|
||||
}
|
||||
|
||||
// Coalesce returns the first non-empty string. It is named after the function
|
||||
// COALESCE in SQL except that since strings in Go are non-nullable, it uses an
|
||||
// empty string as a NULL value. If strs or all it's elements are empty, it
|
||||
// returns an empty string.
|
||||
func Coalesce(strs ...string) (res string) {
|
||||
for _, s := range strs {
|
||||
if s != "" {
|
||||
return s
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// FilterOut returns a copy of strs with all strings for which f returned true
|
||||
// removed.
|
||||
func FilterOut(strs []string, f func(s string) (ok bool)) (filtered []string) {
|
||||
for _, s := range strs {
|
||||
if !f(s) {
|
||||
filtered = append(filtered, s)
|
||||
}
|
||||
}
|
||||
|
||||
return filtered
|
||||
}
|
||||
|
||||
// InSlice checks if string is in the slice of strings.
|
||||
func InSlice(strs []string, str string) (ok bool) {
|
||||
for _, s := range strs {
|
||||
if s == str {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// IsCommentOrEmpty returns true of the string starts with a "#" character or is
|
||||
// an empty string.
|
||||
func IsCommentOrEmpty(s string) (ok bool) {
|
||||
return len(s) == 0 || s[0] == '#'
|
||||
}
|
||||
|
||||
// SplitNext splits string by a byte and returns the first chunk skipping empty
|
||||
// ones. Whitespaces are trimmed.
|
||||
func SplitNext(s *string, sep rune) (chunk string) {
|
||||
if s == nil {
|
||||
return chunk
|
||||
}
|
||||
|
||||
i := strings.IndexByte(*s, byte(sep))
|
||||
if i == -1 {
|
||||
chunk = *s
|
||||
*s = ""
|
||||
|
||||
return strings.TrimSpace(chunk)
|
||||
}
|
||||
|
||||
chunk = (*s)[:i]
|
||||
*s = (*s)[i+1:]
|
||||
var j int
|
||||
var r rune
|
||||
for j, r = range *s {
|
||||
if r != sep {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
*s = (*s)[j:]
|
||||
|
||||
return strings.TrimSpace(chunk)
|
||||
}
|
||||
|
||||
// WriteToBuilder is a convenient wrapper for strings.(*Builder).WriteString
|
||||
// that deals with multiple strings and ignores errors that are guaranteed to be
|
||||
// nil.
|
||||
func WriteToBuilder(b *strings.Builder, strs ...string) {
|
||||
// TODO(e.burkov): Recover from panic?
|
||||
for _, s := range strs {
|
||||
_, _ = b.WriteString(s)
|
||||
}
|
||||
}
|
||||
@@ -1,137 +0,0 @@
|
||||
package aghstrings
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestCloneSlice_family(t *testing.T) {
|
||||
a := []string{"1", "2", "3"}
|
||||
|
||||
t.Run("cloneslice_simple", func(t *testing.T) {
|
||||
assert.Equal(t, a, CloneSlice(a))
|
||||
})
|
||||
|
||||
t.Run("cloneslice_nil", func(t *testing.T) {
|
||||
assert.Nil(t, CloneSlice(nil))
|
||||
})
|
||||
|
||||
t.Run("cloneslice_empty", func(t *testing.T) {
|
||||
assert.Equal(t, []string{}, CloneSlice([]string{}))
|
||||
})
|
||||
|
||||
t.Run("clonesliceorempty_nil", func(t *testing.T) {
|
||||
assert.Equal(t, []string{}, CloneSliceOrEmpty(nil))
|
||||
})
|
||||
|
||||
t.Run("clonesliceorempty_empty", func(t *testing.T) {
|
||||
assert.Equal(t, []string{}, CloneSliceOrEmpty([]string{}))
|
||||
})
|
||||
|
||||
t.Run("clonesliceorempty_sameness", func(t *testing.T) {
|
||||
assert.Equal(t, CloneSlice(a), CloneSliceOrEmpty(a))
|
||||
})
|
||||
}
|
||||
|
||||
func TestCoalesce(t *testing.T) {
|
||||
assert.Equal(t, "", Coalesce())
|
||||
assert.Equal(t, "a", Coalesce("a"))
|
||||
assert.Equal(t, "a", Coalesce("", "a"))
|
||||
assert.Equal(t, "a", Coalesce("a", ""))
|
||||
assert.Equal(t, "a", Coalesce("a", "b"))
|
||||
}
|
||||
|
||||
func TestFilterOut(t *testing.T) {
|
||||
strs := []string{
|
||||
"1.2.3.4",
|
||||
"",
|
||||
"# 5.6.7.8",
|
||||
}
|
||||
|
||||
want := []string{
|
||||
"1.2.3.4",
|
||||
}
|
||||
|
||||
got := FilterOut(strs, IsCommentOrEmpty)
|
||||
assert.Equal(t, want, got)
|
||||
}
|
||||
|
||||
func TestInSlice(t *testing.T) {
|
||||
simpleStrs := []string{"1", "2", "3"}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
str string
|
||||
strs []string
|
||||
want bool
|
||||
}{{
|
||||
name: "yes",
|
||||
str: "2",
|
||||
strs: simpleStrs,
|
||||
want: true,
|
||||
}, {
|
||||
name: "no",
|
||||
str: "4",
|
||||
strs: simpleStrs,
|
||||
want: false,
|
||||
}, {
|
||||
name: "nil",
|
||||
str: "any",
|
||||
strs: nil,
|
||||
want: false,
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
assert.Equal(t, tc.want, InSlice(tc.strs, tc.str))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSplitNext(t *testing.T) {
|
||||
t.Run("ordinary", func(t *testing.T) {
|
||||
s := " a,b , c "
|
||||
require.Equal(t, "a", SplitNext(&s, ','))
|
||||
require.Equal(t, "b", SplitNext(&s, ','))
|
||||
require.Equal(t, "c", SplitNext(&s, ','))
|
||||
|
||||
assert.Empty(t, s)
|
||||
})
|
||||
|
||||
t.Run("nil_source", func(t *testing.T) {
|
||||
assert.Equal(t, "", SplitNext(nil, 's'))
|
||||
})
|
||||
}
|
||||
|
||||
func TestWriteToBuilder(t *testing.T) {
|
||||
b := &strings.Builder{}
|
||||
|
||||
t.Run("single", func(t *testing.T) {
|
||||
assert.NotPanics(t, func() { WriteToBuilder(b, t.Name()) })
|
||||
assert.Equal(t, t.Name(), b.String())
|
||||
})
|
||||
|
||||
b.Reset()
|
||||
t.Run("several", func(t *testing.T) {
|
||||
const (
|
||||
_1 = "one"
|
||||
_2 = "two"
|
||||
_123 = _1 + _2
|
||||
)
|
||||
assert.NotPanics(t, func() { WriteToBuilder(b, _1, _2) })
|
||||
assert.Equal(t, _123, b.String())
|
||||
})
|
||||
|
||||
b.Reset()
|
||||
t.Run("nothing", func(t *testing.T) {
|
||||
assert.NotPanics(t, func() { WriteToBuilder(b) })
|
||||
assert.Equal(t, "", b.String())
|
||||
})
|
||||
|
||||
t.Run("nil_builder", func(t *testing.T) {
|
||||
assert.Panics(t, func() { WriteToBuilder(nil, "a") })
|
||||
})
|
||||
}
|
||||
@@ -4,6 +4,7 @@ package dhcpd
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"time"
|
||||
@@ -31,7 +32,7 @@ func normalizeIP(ip net.IP) net.IP {
|
||||
}
|
||||
|
||||
// Load lease table from DB
|
||||
func (s *Server) dbLoad() {
|
||||
func (s *Server) dbLoad() (err error) {
|
||||
dynLeases := []*Lease{}
|
||||
staticLeases := []*Lease{}
|
||||
v6StaticLeases := []*Lease{}
|
||||
@@ -40,17 +41,16 @@ func (s *Server) dbLoad() {
|
||||
data, err := os.ReadFile(s.conf.DBFilePath)
|
||||
if err != nil {
|
||||
if !errors.Is(err, os.ErrNotExist) {
|
||||
log.Error("dhcp: can't read file %q: %v", s.conf.DBFilePath, err)
|
||||
return fmt.Errorf("reading db: %w", err)
|
||||
}
|
||||
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
||||
obj := []leaseJSON{}
|
||||
err = json.Unmarshal(data, &obj)
|
||||
if err != nil {
|
||||
log.Error("dhcp: invalid DB: %v", err)
|
||||
return
|
||||
return fmt.Errorf("decoding db: %w", err)
|
||||
}
|
||||
|
||||
numLeases := len(obj)
|
||||
@@ -85,15 +85,23 @@ func (s *Server) dbLoad() {
|
||||
}
|
||||
|
||||
leases4 := normalizeLeases(staticLeases, dynLeases)
|
||||
s.srv4.ResetLeases(leases4)
|
||||
err = s.srv4.ResetLeases(leases4)
|
||||
if err != nil {
|
||||
return fmt.Errorf("resetting dhcpv4 leases: %w", err)
|
||||
}
|
||||
|
||||
leases6 := normalizeLeases(v6StaticLeases, v6DynLeases)
|
||||
if s.srv6 != nil {
|
||||
s.srv6.ResetLeases(leases6)
|
||||
err = s.srv6.ResetLeases(leases6)
|
||||
if err != nil {
|
||||
return fmt.Errorf("resetting dhcpv6 leases: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
log.Info("dhcp: loaded leases v4:%d v6:%d total-read:%d from DB",
|
||||
len(leases4), len(leases6), numLeases)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Skip duplicate leases
|
||||
@@ -124,20 +132,24 @@ func normalizeLeases(staticLeases, dynLeases []*Lease) []*Lease {
|
||||
}
|
||||
|
||||
// Store lease table in DB
|
||||
func (s *Server) dbStore() {
|
||||
var leases []leaseJSON
|
||||
func (s *Server) dbStore() (err error) {
|
||||
// Use an empty slice here as opposed to nil so that it doesn't write
|
||||
// "null" into the database file if leases are empty.
|
||||
leases := []leaseJSON{}
|
||||
|
||||
leases4 := s.srv4.getLeasesRef()
|
||||
for _, l := range leases4 {
|
||||
if l.Expiry.Unix() == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
lease := leaseJSON{
|
||||
HWAddr: l.HWAddr,
|
||||
IP: l.IP,
|
||||
Hostname: l.Hostname,
|
||||
Expiry: l.Expiry.Unix(),
|
||||
}
|
||||
|
||||
leases = append(leases, lease)
|
||||
}
|
||||
|
||||
@@ -147,29 +159,30 @@ func (s *Server) dbStore() {
|
||||
if l.Expiry.Unix() == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
lease := leaseJSON{
|
||||
HWAddr: l.HWAddr,
|
||||
IP: l.IP,
|
||||
Hostname: l.Hostname,
|
||||
Expiry: l.Expiry.Unix(),
|
||||
}
|
||||
|
||||
leases = append(leases, lease)
|
||||
}
|
||||
}
|
||||
|
||||
data, err := json.Marshal(leases)
|
||||
var data []byte
|
||||
data, err = json.Marshal(leases)
|
||||
if err != nil {
|
||||
log.Error("json.Marshal: %v", err)
|
||||
return
|
||||
return fmt.Errorf("encoding db: %w", err)
|
||||
}
|
||||
|
||||
err = maybe.WriteFile(s.conf.DBFilePath, data, 0o644)
|
||||
if err != nil {
|
||||
log.Error("dhcp: can't store lease table on disk: %v filename: %s",
|
||||
err, s.conf.DBFilePath)
|
||||
|
||||
return
|
||||
return fmt.Errorf("writing db: %w", err)
|
||||
}
|
||||
|
||||
log.Info("dhcp: stored %d leases in DB", len(leases))
|
||||
log.Info("dhcp: stored %d leases in db", len(leases))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
)
|
||||
|
||||
@@ -36,6 +37,37 @@ type Lease struct {
|
||||
IP net.IP `json:"ip"`
|
||||
}
|
||||
|
||||
// Clone returns a deep copy of l.
|
||||
func (l *Lease) Clone() (clone *Lease) {
|
||||
if l == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &Lease{
|
||||
Expiry: l.Expiry,
|
||||
Hostname: l.Hostname,
|
||||
HWAddr: aghnet.CloneMAC(l.HWAddr),
|
||||
IP: aghnet.CloneIP(l.IP),
|
||||
}
|
||||
}
|
||||
|
||||
// IsBlocklisted returns true if the lease is blocklisted.
|
||||
//
|
||||
// TODO(a.garipov): Just make it a boolean field.
|
||||
func (l *Lease) IsBlocklisted() (ok bool) {
|
||||
if len(l.HWAddr) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, b := range l.HWAddr {
|
||||
if b != 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// IsStatic returns true if the lease is static.
|
||||
//
|
||||
// TODO(a.garipov): Just make it a boolean field.
|
||||
@@ -131,16 +163,27 @@ type Server struct {
|
||||
onLeaseChanged []OnLeaseChangedT
|
||||
}
|
||||
|
||||
// GetLeasesFlags are the flags for GetLeases.
|
||||
type GetLeasesFlags uint8
|
||||
|
||||
// GetLeasesFlags values
|
||||
const (
|
||||
LeasesDynamic GetLeasesFlags = 0b0001
|
||||
LeasesStatic GetLeasesFlags = 0b0010
|
||||
|
||||
LeasesAll = LeasesDynamic | LeasesStatic
|
||||
)
|
||||
|
||||
// ServerInterface is an interface for servers.
|
||||
type ServerInterface interface {
|
||||
Enabled() (ok bool)
|
||||
Leases(flags int) []Lease
|
||||
Leases(flags GetLeasesFlags) (leases []*Lease)
|
||||
SetOnLeaseChanged(onLeaseChanged OnLeaseChangedT)
|
||||
}
|
||||
|
||||
// Create - create object
|
||||
func Create(conf ServerConfig) *Server {
|
||||
s := &Server{}
|
||||
func Create(conf ServerConfig) (s *Server, err error) {
|
||||
s = &Server{}
|
||||
|
||||
s.conf.Enabled = conf.Enabled
|
||||
s.conf.InterfaceName = conf.InterfaceName
|
||||
@@ -166,15 +209,18 @@ func Create(conf ServerConfig) *Server {
|
||||
webHandlersRegistered = true
|
||||
}
|
||||
|
||||
var err4, err6 error
|
||||
v4conf := conf.Conf4
|
||||
v4conf.Enabled = s.conf.Enabled
|
||||
if len(v4conf.RangeStart) == 0 {
|
||||
v4conf.Enabled = false
|
||||
}
|
||||
|
||||
v4conf.InterfaceName = s.conf.InterfaceName
|
||||
v4conf.notify = s.onNotify
|
||||
s.srv4, err4 = v4Create(v4conf)
|
||||
s.srv4, err = v4Create(v4conf)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating dhcpv4 srv: %w", err)
|
||||
}
|
||||
|
||||
v6conf := conf.Conf6
|
||||
v6conf.Enabled = s.conf.Enabled
|
||||
@@ -183,29 +229,26 @@ func Create(conf ServerConfig) *Server {
|
||||
}
|
||||
v6conf.InterfaceName = s.conf.InterfaceName
|
||||
v6conf.notify = s.onNotify
|
||||
s.srv6, err6 = v6Create(v6conf)
|
||||
|
||||
if err4 != nil {
|
||||
log.Error("%s", err4)
|
||||
return nil
|
||||
}
|
||||
if err6 != nil {
|
||||
log.Error("%s", err6)
|
||||
return nil
|
||||
s.srv6, err = v6Create(v6conf)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating dhcpv6 srv: %w", err)
|
||||
}
|
||||
|
||||
s.conf.Conf4 = conf.Conf4
|
||||
s.conf.Conf6 = conf.Conf6
|
||||
|
||||
if s.conf.Enabled && !v4conf.Enabled && !v6conf.Enabled {
|
||||
log.Error("Can't enable DHCP server because neither DHCPv4 nor DHCPv6 servers are configured")
|
||||
return nil
|
||||
return nil, fmt.Errorf("neither dhcpv4 nor dhcpv6 srv is configured")
|
||||
}
|
||||
|
||||
// we can't delay database loading until DHCP server is started,
|
||||
// because we need static leases functionality available beforehand
|
||||
s.dbLoad()
|
||||
return s
|
||||
// Don't delay database loading until the DHCP server is started,
|
||||
// because we need static leases functionality available beforehand.
|
||||
err = s.dbLoad()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("loading db: %w", err)
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// Enabled returns true when the server is enabled.
|
||||
@@ -213,10 +256,30 @@ func (s *Server) Enabled() (ok bool) {
|
||||
return s.conf.Enabled
|
||||
}
|
||||
|
||||
// resetLeases resets all leases in the lease database.
|
||||
func (s *Server) resetLeases() (err error) {
|
||||
err = s.srv4.ResetLeases(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if s.srv6 != nil {
|
||||
err = s.srv6.ResetLeases(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return s.dbStore()
|
||||
}
|
||||
|
||||
// server calls this function after DB is updated
|
||||
func (s *Server) onNotify(flags uint32) {
|
||||
if flags == LeaseChangedDBStore {
|
||||
s.dbStore()
|
||||
err := s.dbStore()
|
||||
if err != nil {
|
||||
log.Error("updating db: %s", err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
@@ -263,21 +326,23 @@ func (s *Server) Start() (err error) {
|
||||
}
|
||||
|
||||
// Stop closes the listening UDP socket
|
||||
func (s *Server) Stop() {
|
||||
s.srv4.Stop()
|
||||
s.srv6.Stop()
|
||||
}
|
||||
func (s *Server) Stop() (err error) {
|
||||
err = s.srv4.Stop()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// flags for Leases() function
|
||||
const (
|
||||
LeasesDynamic = 1
|
||||
LeasesStatic = 2
|
||||
LeasesAll = LeasesDynamic | LeasesStatic
|
||||
)
|
||||
err = s.srv6.Stop()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Leases returns the list of active IPv4 and IPv6 DHCP leases. It's safe for
|
||||
// concurrent use.
|
||||
func (s *Server) Leases(flags int) (leases []Lease) {
|
||||
func (s *Server) Leases(flags GetLeasesFlags) (leases []*Lease) {
|
||||
return append(s.srv4.GetLeases(flags), s.srv6.GetLeases(flags)...)
|
||||
}
|
||||
|
||||
@@ -290,6 +355,6 @@ func (s *Server) FindMACbyIP(ip net.IP) net.HardwareAddr {
|
||||
}
|
||||
|
||||
// AddStaticLease - add static v4 lease
|
||||
func (s *Server) AddStaticLease(lease Lease) error {
|
||||
return s.srv4.AddStaticLease(lease)
|
||||
func (s *Server) AddStaticLease(l *Lease) error {
|
||||
return s.srv4.AddStaticLease(l)
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ func TestDB(t *testing.T) {
|
||||
s.srv6, err = v6Create(V6ServerConf{})
|
||||
require.NoError(t, err)
|
||||
|
||||
leases := []Lease{{
|
||||
leases := []*Lease{{
|
||||
Expiry: time.Now().Add(time.Hour),
|
||||
Hostname: "static-1.local",
|
||||
HWAddr: net.HardwareAddr{0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA},
|
||||
@@ -57,18 +57,24 @@ func TestDB(t *testing.T) {
|
||||
srv4, ok := s.srv4.(*v4Server)
|
||||
require.True(t, ok)
|
||||
|
||||
err = srv4.addLease(&leases[0])
|
||||
err = srv4.addLease(leases[0])
|
||||
require.NoError(t, err)
|
||||
|
||||
err = s.srv4.AddStaticLease(leases[1])
|
||||
require.NoError(t, err)
|
||||
|
||||
s.dbStore()
|
||||
err = s.dbStore()
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Cleanup(func() {
|
||||
assert.NoError(t, os.Remove(dbFilename))
|
||||
})
|
||||
s.srv4.ResetLeases(nil)
|
||||
s.dbLoad()
|
||||
|
||||
err = s.srv4.ResetLeases(nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = s.dbLoad()
|
||||
require.NoError(t, err)
|
||||
|
||||
ll := s.srv4.GetLeases(LeasesAll)
|
||||
require.Len(t, ll, len(leases))
|
||||
|
||||
@@ -60,12 +60,12 @@ func v6JSONToServerConf(j *v6ServerConfJSON) V6ServerConf {
|
||||
|
||||
// dhcpStatusResponse is the response for /control/dhcp/status endpoint.
|
||||
type dhcpStatusResponse struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
IfaceName string `json:"interface_name"`
|
||||
V4 V4ServerConf `json:"v4"`
|
||||
V6 V6ServerConf `json:"v6"`
|
||||
Leases []Lease `json:"leases"`
|
||||
StaticLeases []Lease `json:"static_leases"`
|
||||
Leases []*Lease `json:"leases"`
|
||||
StaticLeases []*Lease `json:"static_leases"`
|
||||
Enabled bool `json:"enabled"`
|
||||
}
|
||||
|
||||
func (s *Server) handleDHCPStatus(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -146,12 +146,68 @@ type dhcpServerConfigJSON struct {
|
||||
Enabled nullBool `json:"enabled"`
|
||||
}
|
||||
|
||||
func (s *Server) handleDHCPSetConfigV4(
|
||||
conf *dhcpServerConfigJSON,
|
||||
) (srv4 DHCPServer, enabled bool, err error) {
|
||||
if conf.V4 == nil {
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
v4Conf := v4JSONToServerConf(conf.V4)
|
||||
v4Conf.Enabled = conf.Enabled == nbTrue
|
||||
if len(v4Conf.RangeStart) == 0 {
|
||||
v4Conf.Enabled = false
|
||||
}
|
||||
|
||||
enabled = v4Conf.Enabled
|
||||
v4Conf.InterfaceName = conf.InterfaceName
|
||||
|
||||
c4 := V4ServerConf{}
|
||||
s.srv4.WriteDiskConfig4(&c4)
|
||||
v4Conf.notify = c4.notify
|
||||
v4Conf.ICMPTimeout = c4.ICMPTimeout
|
||||
v4Conf.Options = c4.Options
|
||||
|
||||
srv4, err = v4Create(v4Conf)
|
||||
|
||||
return srv4, enabled, err
|
||||
}
|
||||
|
||||
func (s *Server) handleDHCPSetConfigV6(
|
||||
conf *dhcpServerConfigJSON,
|
||||
) (srv6 DHCPServer, enabled bool, err error) {
|
||||
if conf.V6 == nil {
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
v6Conf := v6JSONToServerConf(conf.V6)
|
||||
v6Conf.Enabled = conf.Enabled == nbTrue
|
||||
if len(v6Conf.RangeStart) == 0 {
|
||||
v6Conf.Enabled = false
|
||||
}
|
||||
|
||||
// Don't overwrite the RA/SLAAC settings from the config file.
|
||||
//
|
||||
// TODO(a.garipov): Perhaps include them into the request to allow
|
||||
// changing them from the HTTP API?
|
||||
v6Conf.RASLAACOnly = s.conf.Conf6.RASLAACOnly
|
||||
v6Conf.RAAllowSLAAC = s.conf.Conf6.RAAllowSLAAC
|
||||
|
||||
enabled = v6Conf.Enabled
|
||||
v6Conf.InterfaceName = conf.InterfaceName
|
||||
v6Conf.notify = s.onNotify
|
||||
|
||||
srv6, err = v6Create(v6Conf)
|
||||
|
||||
return srv6, enabled, err
|
||||
}
|
||||
|
||||
func (s *Server) handleDHCPSetConfig(w http.ResponseWriter, r *http.Request) {
|
||||
conf := dhcpServerConfigJSON{}
|
||||
conf := &dhcpServerConfigJSON{}
|
||||
conf.Enabled = boolToNullBool(s.conf.Enabled)
|
||||
conf.InterfaceName = s.conf.InterfaceName
|
||||
|
||||
err := json.NewDecoder(r.Body).Decode(&conf)
|
||||
err := json.NewDecoder(r.Body).Decode(conf)
|
||||
if err != nil {
|
||||
httpError(r, w, http.StatusBadRequest,
|
||||
"failed to parse new dhcp config json: %s", err)
|
||||
@@ -159,61 +215,18 @@ func (s *Server) handleDHCPSetConfig(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
var s4 DHCPServer
|
||||
var s6 DHCPServer
|
||||
v4Enabled := false
|
||||
v6Enabled := false
|
||||
srv4, v4Enabled, err := s.handleDHCPSetConfigV4(conf)
|
||||
if err != nil {
|
||||
httpError(r, w, http.StatusBadRequest, "bad dhcpv4 configuration: %s", err)
|
||||
|
||||
if conf.V4 != nil {
|
||||
v4Conf := v4JSONToServerConf(conf.V4)
|
||||
v4Conf.Enabled = conf.Enabled == nbTrue
|
||||
if len(v4Conf.RangeStart) == 0 {
|
||||
v4Conf.Enabled = false
|
||||
}
|
||||
|
||||
v4Enabled = v4Conf.Enabled
|
||||
v4Conf.InterfaceName = conf.InterfaceName
|
||||
|
||||
c4 := V4ServerConf{}
|
||||
s.srv4.WriteDiskConfig4(&c4)
|
||||
v4Conf.notify = c4.notify
|
||||
v4Conf.ICMPTimeout = c4.ICMPTimeout
|
||||
v4Conf.Options = c4.Options
|
||||
|
||||
s4, err = v4Create(v4Conf)
|
||||
if err != nil {
|
||||
httpError(r, w, http.StatusBadRequest,
|
||||
"invalid dhcpv4 configuration: %s", err)
|
||||
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if conf.V6 != nil {
|
||||
v6Conf := v6JSONToServerConf(conf.V6)
|
||||
v6Conf.Enabled = conf.Enabled == nbTrue
|
||||
if len(v6Conf.RangeStart) == 0 {
|
||||
v6Conf.Enabled = false
|
||||
}
|
||||
srv6, v6Enabled, err := s.handleDHCPSetConfigV6(conf)
|
||||
if err != nil {
|
||||
httpError(r, w, http.StatusBadRequest, "bad dhcpv6 configuration: %s", err)
|
||||
|
||||
// Don't overwrite the RA/SLAAC settings from the config file.
|
||||
//
|
||||
// TODO(a.garipov): Perhaps include them into the request to
|
||||
// allow changing them from the HTTP API?
|
||||
v6Conf.RASLAACOnly = s.conf.Conf6.RASLAACOnly
|
||||
v6Conf.RAAllowSLAAC = s.conf.Conf6.RAAllowSLAAC
|
||||
|
||||
v6Enabled = v6Conf.Enabled
|
||||
v6Conf.InterfaceName = conf.InterfaceName
|
||||
v6Conf.notify = s.onNotify
|
||||
|
||||
s6, err = v6Create(v6Conf)
|
||||
if err != nil {
|
||||
httpError(r, w, http.StatusBadRequest,
|
||||
"invalid dhcpv6 configuration: %s", err)
|
||||
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if conf.Enabled == nbTrue && !v4Enabled && !v6Enabled {
|
||||
@@ -223,7 +236,12 @@ func (s *Server) handleDHCPSetConfig(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
s.Stop()
|
||||
err = s.Stop()
|
||||
if err != nil {
|
||||
httpError(r, w, http.StatusInternalServerError, "stopping dhcp: %s", err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if conf.Enabled != nbNull {
|
||||
s.conf.Enabled = conf.Enabled == nbTrue
|
||||
@@ -233,16 +251,22 @@ func (s *Server) handleDHCPSetConfig(w http.ResponseWriter, r *http.Request) {
|
||||
s.conf.InterfaceName = conf.InterfaceName
|
||||
}
|
||||
|
||||
if s4 != nil {
|
||||
s.srv4 = s4
|
||||
if srv4 != nil {
|
||||
s.srv4 = srv4
|
||||
}
|
||||
|
||||
if s6 != nil {
|
||||
s.srv6 = s6
|
||||
if srv6 != nil {
|
||||
s.srv6 = srv6
|
||||
}
|
||||
|
||||
s.conf.ConfigModified()
|
||||
s.dbLoad()
|
||||
|
||||
err = s.dbLoad()
|
||||
if err != nil {
|
||||
httpError(r, w, http.StatusInternalServerError, "loading leases db: %s", err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if s.conf.Enabled {
|
||||
var code int
|
||||
@@ -388,7 +412,9 @@ func (s *Server) handleDHCPFindActiveServer(w http.ResponseWriter, r *http.Reque
|
||||
result := dhcpSearchResult{
|
||||
V4: dhcpSearchV4Result{
|
||||
OtherServer: dhcpSearchOtherResult{},
|
||||
StaticIP: dhcpStaticIPStatus{},
|
||||
StaticIP: dhcpStaticIPStatus{
|
||||
Static: "yes",
|
||||
},
|
||||
},
|
||||
V6: dhcpSearchV6Result{
|
||||
OtherServer: dhcpSearchOtherResult{},
|
||||
@@ -431,26 +457,26 @@ func (s *Server) handleDHCPFindActiveServer(w http.ResponseWriter, r *http.Reque
|
||||
}
|
||||
|
||||
func (s *Server) handleDHCPAddStaticLease(w http.ResponseWriter, r *http.Request) {
|
||||
lj := Lease{}
|
||||
err := json.NewDecoder(r.Body).Decode(&lj)
|
||||
l := &Lease{}
|
||||
err := json.NewDecoder(r.Body).Decode(l)
|
||||
if err != nil {
|
||||
httpError(r, w, http.StatusBadRequest, "json.Decode: %s", err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if lj.IP == nil {
|
||||
if l.IP == nil {
|
||||
httpError(r, w, http.StatusBadRequest, "invalid IP")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
ip4 := lj.IP.To4()
|
||||
ip4 := l.IP.To4()
|
||||
|
||||
if ip4 == nil {
|
||||
lj.IP = lj.IP.To16()
|
||||
l.IP = l.IP.To16()
|
||||
|
||||
err = s.srv6.AddStaticLease(lj)
|
||||
err = s.srv6.AddStaticLease(l)
|
||||
if err != nil {
|
||||
httpError(r, w, http.StatusBadRequest, "%s", err)
|
||||
}
|
||||
@@ -458,8 +484,8 @@ func (s *Server) handleDHCPAddStaticLease(w http.ResponseWriter, r *http.Request
|
||||
return
|
||||
}
|
||||
|
||||
lj.IP = ip4
|
||||
err = s.srv4.AddStaticLease(lj)
|
||||
l.IP = ip4
|
||||
err = s.srv4.AddStaticLease(l)
|
||||
if err != nil {
|
||||
httpError(r, w, http.StatusBadRequest, "%s", err)
|
||||
|
||||
@@ -468,26 +494,26 @@ func (s *Server) handleDHCPAddStaticLease(w http.ResponseWriter, r *http.Request
|
||||
}
|
||||
|
||||
func (s *Server) handleDHCPRemoveStaticLease(w http.ResponseWriter, r *http.Request) {
|
||||
lj := Lease{}
|
||||
err := json.NewDecoder(r.Body).Decode(&lj)
|
||||
l := &Lease{}
|
||||
err := json.NewDecoder(r.Body).Decode(l)
|
||||
if err != nil {
|
||||
httpError(r, w, http.StatusBadRequest, "json.Decode: %s", err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if lj.IP == nil {
|
||||
if l.IP == nil {
|
||||
httpError(r, w, http.StatusBadRequest, "invalid IP")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
ip4 := lj.IP.To4()
|
||||
ip4 := l.IP.To4()
|
||||
|
||||
if ip4 == nil {
|
||||
lj.IP = lj.IP.To16()
|
||||
l.IP = l.IP.To16()
|
||||
|
||||
err = s.srv6.RemoveStaticLease(lj)
|
||||
err = s.srv6.RemoveStaticLease(l)
|
||||
if err != nil {
|
||||
httpError(r, w, http.StatusBadRequest, "%s", err)
|
||||
}
|
||||
@@ -495,8 +521,8 @@ func (s *Server) handleDHCPRemoveStaticLease(w http.ResponseWriter, r *http.Requ
|
||||
return
|
||||
}
|
||||
|
||||
lj.IP = ip4
|
||||
err = s.srv4.RemoveStaticLease(lj)
|
||||
l.IP = ip4
|
||||
err = s.srv4.RemoveStaticLease(l)
|
||||
if err != nil {
|
||||
httpError(r, w, http.StatusBadRequest, "%s", err)
|
||||
|
||||
@@ -505,11 +531,16 @@ func (s *Server) handleDHCPRemoveStaticLease(w http.ResponseWriter, r *http.Requ
|
||||
}
|
||||
|
||||
func (s *Server) handleReset(w http.ResponseWriter, r *http.Request) {
|
||||
s.Stop()
|
||||
err := s.Stop()
|
||||
if err != nil {
|
||||
httpError(r, w, http.StatusInternalServerError, "stopping dhcp: %s", err)
|
||||
|
||||
err := os.Remove(s.conf.DBFilePath)
|
||||
return
|
||||
}
|
||||
|
||||
err = os.Remove(s.conf.DBFilePath)
|
||||
if err != nil && !errors.Is(err, os.ErrNotExist) {
|
||||
log.Error("dhcp: removing %q: %s", s.conf.DBFilePath, err)
|
||||
log.Error("dhcp: removing db: %s", err)
|
||||
}
|
||||
|
||||
oldconf := s.conf
|
||||
@@ -531,6 +562,16 @@ func (s *Server) handleReset(w http.ResponseWriter, r *http.Request) {
|
||||
s.conf.ConfigModified()
|
||||
}
|
||||
|
||||
func (s *Server) handleResetLeases(w http.ResponseWriter, r *http.Request) {
|
||||
err := s.resetLeases()
|
||||
if err != nil {
|
||||
msg := "resetting leases: %s"
|
||||
httpError(r, w, http.StatusInternalServerError, msg, err)
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) registerHandlers() {
|
||||
s.conf.HTTPRegister(http.MethodGet, "/control/dhcp/status", s.handleDHCPStatus)
|
||||
s.conf.HTTPRegister(http.MethodGet, "/control/dhcp/interfaces", s.handleDHCPInterfaces)
|
||||
@@ -539,6 +580,7 @@ func (s *Server) registerHandlers() {
|
||||
s.conf.HTTPRegister(http.MethodPost, "/control/dhcp/add_static_lease", s.handleDHCPAddStaticLease)
|
||||
s.conf.HTTPRegister(http.MethodPost, "/control/dhcp/remove_static_lease", s.handleDHCPRemoveStaticLease)
|
||||
s.conf.HTTPRegister(http.MethodPost, "/control/dhcp/reset", s.handleReset)
|
||||
s.conf.HTTPRegister(http.MethodPost, "/control/dhcp/reset_leases", s.handleResetLeases)
|
||||
}
|
||||
|
||||
// jsonError is a generic JSON error response.
|
||||
@@ -579,4 +621,5 @@ func (s *Server) registerNotImplementedHandlers() {
|
||||
s.conf.HTTPRegister(http.MethodPost, "/control/dhcp/add_static_lease", h)
|
||||
s.conf.HTTPRegister(http.MethodPost, "/control/dhcp/remove_static_lease", h)
|
||||
s.conf.HTTPRegister(http.MethodPost, "/control/dhcp/reset", h)
|
||||
s.conf.HTTPRegister(http.MethodPost, "/control/dhcp/reset_leases", h)
|
||||
}
|
||||
|
||||
@@ -7,14 +7,14 @@ import (
|
||||
|
||||
// DHCPServer - DHCP server interface
|
||||
type DHCPServer interface {
|
||||
// ResetLeases - reset leases
|
||||
ResetLeases(leases []*Lease)
|
||||
// GetLeases - get leases
|
||||
GetLeases(flags int) []Lease
|
||||
// ResetLeases resets leases.
|
||||
ResetLeases(leases []*Lease) (err error)
|
||||
// GetLeases returns deep clones of the current leases.
|
||||
GetLeases(flags GetLeasesFlags) (leases []*Lease)
|
||||
// AddStaticLease - add a static lease
|
||||
AddStaticLease(lease Lease) error
|
||||
AddStaticLease(l *Lease) (err error)
|
||||
// RemoveStaticLease - remove a static lease
|
||||
RemoveStaticLease(l Lease) error
|
||||
RemoveStaticLease(l *Lease) (err error)
|
||||
// FindMACbyIP - find a MAC address by IP address in the currently active DHCP leases
|
||||
FindMACbyIP(ip net.IP) net.HardwareAddr
|
||||
|
||||
@@ -24,9 +24,9 @@ type DHCPServer interface {
|
||||
WriteDiskConfig6(c *V6ServerConf)
|
||||
|
||||
// Start - start server
|
||||
Start() error
|
||||
Start() (err error)
|
||||
// Stop - stop server
|
||||
Stop()
|
||||
Stop() (err error)
|
||||
|
||||
getLeasesRef() []*Lease
|
||||
}
|
||||
@@ -38,6 +38,9 @@ type V4ServerConf struct {
|
||||
|
||||
GatewayIP net.IP `yaml:"gateway_ip" json:"gateway_ip"`
|
||||
SubnetMask net.IP `yaml:"subnet_mask" json:"subnet_mask"`
|
||||
// broadcastIP is the broadcasting address pre-calculated from the
|
||||
// configured gateway IP and subnet mask.
|
||||
broadcastIP net.IP
|
||||
|
||||
// The first & the last IP address for dynamic leases
|
||||
// Bytes [0..2] of the last allowed IP address must match the first IP
|
||||
|
||||
@@ -12,9 +12,9 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghstrings"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/AdguardTeam/golibs/stringutil"
|
||||
"github.com/go-ping/ping"
|
||||
"github.com/insomniacslk/dhcp/dhcpv4"
|
||||
"github.com/insomniacslk/dhcp/dhcpv4/server4"
|
||||
@@ -32,7 +32,7 @@ type v4Server struct {
|
||||
leasedOffsets *bitSet
|
||||
|
||||
// leaseHosts is the set of all hostnames of all known DHCP clients.
|
||||
leaseHosts *aghstrings.Set
|
||||
leaseHosts *stringutil.Set
|
||||
|
||||
// leases contains all dynamic and static leases.
|
||||
leases []*Lease
|
||||
@@ -96,16 +96,16 @@ func (s *v4Server) validHostnameForClient(cliHostname string, ip net.IP) (hostna
|
||||
return hostname
|
||||
}
|
||||
|
||||
// ResetLeases - reset leases
|
||||
func (s *v4Server) ResetLeases(leases []*Lease) {
|
||||
var err error
|
||||
// ResetLeases resets leases.
|
||||
func (s *v4Server) ResetLeases(leases []*Lease) (err error) {
|
||||
defer func() { err = errors.Annotate(err, "dhcpv4: %w") }()
|
||||
|
||||
if !s.conf.Enabled {
|
||||
return
|
||||
}
|
||||
|
||||
s.leasedOffsets = newBitSet()
|
||||
s.leaseHosts = aghstrings.NewSet()
|
||||
s.leaseHosts = stringutil.NewSet()
|
||||
s.leases = nil
|
||||
|
||||
for _, l := range leases {
|
||||
@@ -125,6 +125,8 @@ func (s *v4Server) ResetLeases(leases []*Lease) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getLeasesRef returns the actual leases slice. For internal use only.
|
||||
@@ -154,14 +156,12 @@ func (s *v4Server) isBlocklisted(l *Lease) (ok bool) {
|
||||
|
||||
// GetLeases returns the list of current DHCP leases. It is safe for concurrent
|
||||
// use.
|
||||
func (s *v4Server) GetLeases(flags int) (res []Lease) {
|
||||
func (s *v4Server) GetLeases(flags GetLeasesFlags) (leases []*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{}
|
||||
leases = []*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
|
||||
|
||||
@@ -171,17 +171,17 @@ func (s *v4Server) GetLeases(flags int) (res []Lease) {
|
||||
now := time.Now()
|
||||
for _, l := range s.leases {
|
||||
if getDynamic && l.Expiry.After(now) && !s.isBlocklisted(l) {
|
||||
res = append(res, *l)
|
||||
leases = append(leases, l.Clone())
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if getStatic && l.IsStatic() {
|
||||
res = append(res, *l)
|
||||
leases = append(leases, l.Clone())
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
return leases
|
||||
}
|
||||
|
||||
// FindMACbyIP - find a MAC address by IP address in the currently active DHCP leases
|
||||
@@ -305,7 +305,7 @@ func (s *v4Server) addLease(l *Lease) (err error) {
|
||||
}
|
||||
|
||||
// rmLease removes a lease with the same properties.
|
||||
func (s *v4Server) rmLease(lease Lease) (err error) {
|
||||
func (s *v4Server) rmLease(lease *Lease) (err error) {
|
||||
if len(s.leases) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -326,7 +326,7 @@ func (s *v4Server) rmLease(lease Lease) (err error) {
|
||||
}
|
||||
|
||||
// AddStaticLease adds a static lease. It is safe for concurrent use.
|
||||
func (s *v4Server) AddStaticLease(l Lease) (err error) {
|
||||
func (s *v4Server) AddStaticLease(l *Lease) (err error) {
|
||||
defer func() { err = errors.Annotate(err, "dhcpv4: adding static lease: %w") }()
|
||||
|
||||
if ip4 := l.IP.To4(); ip4 == nil {
|
||||
@@ -365,7 +365,7 @@ func (s *v4Server) AddStaticLease(l Lease) (err error) {
|
||||
s.leasesLock.Lock()
|
||||
defer s.leasesLock.Unlock()
|
||||
|
||||
err = s.rmDynamicLease(&l)
|
||||
err = s.rmDynamicLease(l)
|
||||
if err != nil {
|
||||
err = fmt.Errorf(
|
||||
"removing dynamic leases for %s (%s): %w",
|
||||
@@ -377,7 +377,7 @@ func (s *v4Server) AddStaticLease(l Lease) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
err = s.addLease(&l)
|
||||
err = s.addLease(l)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("adding static lease for %s (%s): %w", l.IP, l.HWAddr, err)
|
||||
|
||||
@@ -395,7 +395,7 @@ func (s *v4Server) AddStaticLease(l Lease) (err error) {
|
||||
}
|
||||
|
||||
// RemoveStaticLease removes a static lease. It is safe for concurrent use.
|
||||
func (s *v4Server) RemoveStaticLease(l Lease) (err error) {
|
||||
func (s *v4Server) RemoveStaticLease(l *Lease) (err error) {
|
||||
defer func() { err = errors.Annotate(err, "dhcpv4: %w") }()
|
||||
|
||||
if len(l.IP) != 4 {
|
||||
@@ -927,12 +927,30 @@ func (s *v4Server) packetHandler(conn net.PacketConn, peer net.Addr, req *dhcpv4
|
||||
resp.Options.Update(dhcpv4.OptMessageType(dhcpv4.MessageTypeNak))
|
||||
}
|
||||
|
||||
// peer is expected to be of type *net.UDPConn as the server4.NewServer
|
||||
// initializes it.
|
||||
udpPeer, ok := peer.(*net.UDPAddr)
|
||||
if !ok {
|
||||
log.Error("dhcpv4: peer is of unexpected type %T", peer)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Despite the fact that server4.NewIPv4UDPConn explicitly sets socket
|
||||
// options to allow broadcasting, it also binds the connection to a
|
||||
// specific interface. On FreeBSD conn.WriteTo causes errors while
|
||||
// writing to the addresses that belong to another interface. So, use
|
||||
// the broadcast address specific for the binded interface in case
|
||||
// server4.Server.Serve sets it to net.IPv4Bcast.
|
||||
if udpPeer.IP.Equal(net.IPv4bcast) {
|
||||
udpPeer.IP = s.conf.broadcastIP
|
||||
}
|
||||
|
||||
log.Debug("dhcpv4: sending: %s", resp.Summary())
|
||||
|
||||
_, err = conn.WriteTo(resp.ToBytes(), peer)
|
||||
if err != nil {
|
||||
log.Error("dhcpv4: conn.Write to %s failed: %s", peer, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -993,15 +1011,15 @@ func (s *v4Server) Start() (err error) {
|
||||
}
|
||||
|
||||
// Stop - stop server
|
||||
func (s *v4Server) Stop() {
|
||||
func (s *v4Server) Stop() (err error) {
|
||||
if s.srv == nil {
|
||||
return
|
||||
}
|
||||
|
||||
log.Debug("dhcpv4: stopping")
|
||||
err := s.srv.Close()
|
||||
err = s.srv.Close()
|
||||
if err != nil {
|
||||
log.Error("dhcpv4: srv.Close: %s", err)
|
||||
return fmt.Errorf("closing dhcpv4 srv: %w", err)
|
||||
}
|
||||
|
||||
// Signal to the clients containers in packages home and dnsforward that
|
||||
@@ -1009,13 +1027,15 @@ func (s *v4Server) Stop() {
|
||||
s.conf.notify(LeaseChangedRemovedAll)
|
||||
|
||||
s.srv = nil
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create DHCPv4 server
|
||||
func v4Create(conf V4ServerConf) (srv DHCPServer, err error) {
|
||||
s := &v4Server{}
|
||||
s.conf = conf
|
||||
s.leaseHosts = aghstrings.NewSet()
|
||||
s.leaseHosts = stringutil.NewSet()
|
||||
|
||||
// TODO(a.garipov): Don't use a disabled server in other places or just
|
||||
// use an interface.
|
||||
@@ -1041,6 +1061,12 @@ func v4Create(conf V4ServerConf) (srv DHCPServer, err error) {
|
||||
Mask: subnetMask,
|
||||
}
|
||||
|
||||
bcastIP := aghnet.CloneIP(routerIP)
|
||||
for i, b := range subnetMask {
|
||||
bcastIP[i] |= ^b
|
||||
}
|
||||
s.conf.broadcastIP = bcastIP
|
||||
|
||||
s.conf.ipRange, err = newIPRange(conf.RangeStart, conf.RangeEnd)
|
||||
if err != nil {
|
||||
return s, fmt.Errorf("dhcpv4: %w", err)
|
||||
|
||||
@@ -9,16 +9,15 @@ import "net"
|
||||
|
||||
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) 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 }
|
||||
func (s *winServer) WriteDiskConfig4(c *V4ServerConf) {}
|
||||
func (s *winServer) WriteDiskConfig6(c *V6ServerConf) {}
|
||||
func (s *winServer) Start() error { return nil }
|
||||
func (s *winServer) Stop() {}
|
||||
func (s *winServer) Reset() {}
|
||||
func v4Create(conf V4ServerConf) (DHCPServer, error) { return &winServer{}, nil }
|
||||
func v6Create(conf V6ServerConf) (DHCPServer, error) { return &winServer{}, nil }
|
||||
func (s *winServer) ResetLeases(_ []*Lease) (err error) { return nil }
|
||||
func (s *winServer) GetLeases(_ GetLeasesFlags) (leases []*Lease) { return nil }
|
||||
func (s *winServer) getLeasesRef() []*Lease { return nil }
|
||||
func (s *winServer) AddStaticLease(_ *Lease) (err error) { return nil }
|
||||
func (s *winServer) RemoveStaticLease(_ *Lease) (err error) { return nil }
|
||||
func (s *winServer) FindMACbyIP(ip net.IP) (mac net.HardwareAddr) { return nil }
|
||||
func (s *winServer) WriteDiskConfig4(c *V4ServerConf) {}
|
||||
func (s *winServer) WriteDiskConfig6(c *V6ServerConf) {}
|
||||
func (s *winServer) Start() (err error) { return nil }
|
||||
func (s *winServer) Stop() (err error) { return nil }
|
||||
func v4Create(conf V4ServerConf) (DHCPServer, error) { return &winServer{}, nil }
|
||||
func v6Create(conf V6ServerConf) (DHCPServer, error) { return &winServer{}, nil }
|
||||
|
||||
@@ -30,7 +30,7 @@ func TestV4_AddRemove_static(t *testing.T) {
|
||||
assert.Empty(t, ls)
|
||||
|
||||
// Add static lease.
|
||||
l := Lease{
|
||||
l := &Lease{
|
||||
Hostname: "static-1.local",
|
||||
HWAddr: net.HardwareAddr{0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA},
|
||||
IP: net.IP{192, 168, 10, 150},
|
||||
@@ -50,7 +50,7 @@ func TestV4_AddRemove_static(t *testing.T) {
|
||||
assert.True(t, ls[0].IsStatic())
|
||||
|
||||
// Try to remove static lease.
|
||||
err = s.RemoveStaticLease(Lease{
|
||||
err = s.RemoveStaticLease(&Lease{
|
||||
IP: net.IP{192, 168, 10, 110},
|
||||
HWAddr: net.HardwareAddr{0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA},
|
||||
})
|
||||
@@ -92,7 +92,7 @@ func TestV4_AddReplace(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
stLeases := []Lease{{
|
||||
stLeases := []*Lease{{
|
||||
Hostname: "static-1.local",
|
||||
HWAddr: net.HardwareAddr{0x33, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA},
|
||||
IP: net.IP{192, 168, 10, 150},
|
||||
@@ -134,7 +134,7 @@ func TestV4StaticLease_Get(t *testing.T) {
|
||||
|
||||
s.conf.dnsIPAddrs = []net.IP{{192, 168, 10, 1}}
|
||||
|
||||
l := Lease{
|
||||
l := &Lease{
|
||||
Hostname: "static-1.local",
|
||||
HWAddr: net.HardwareAddr{0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA},
|
||||
IP: net.IP{192, 168, 10, 150},
|
||||
|
||||
@@ -57,10 +57,12 @@ func ip6InRange(start, ip net.IP) bool {
|
||||
return start[15] <= ip[15]
|
||||
}
|
||||
|
||||
// ResetLeases - reset leases
|
||||
func (s *v6Server) ResetLeases(ll []*Lease) {
|
||||
// ResetLeases resets leases.
|
||||
func (s *v6Server) ResetLeases(leases []*Lease) (err error) {
|
||||
defer func() { err = errors.Annotate(err, "dhcpv4: %w") }()
|
||||
|
||||
s.leases = nil
|
||||
for _, l := range ll {
|
||||
for _, l := range leases {
|
||||
|
||||
if l.Expiry.Unix() != leaseExpireStatic &&
|
||||
!ip6InRange(s.conf.ipStart, l.IP) {
|
||||
@@ -72,28 +74,31 @@ func (s *v6Server) ResetLeases(ll []*Lease) {
|
||||
|
||||
s.addLease(l)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetLeases - get current leases
|
||||
func (s *v6Server) GetLeases(flags int) []Lease {
|
||||
// GetLeases returns the list of current DHCP leases. It is safe for concurrent
|
||||
// use.
|
||||
func (s *v6Server) GetLeases(flags GetLeasesFlags) (leases []*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{}
|
||||
leases = []*Lease{}
|
||||
s.leasesLock.Lock()
|
||||
for _, lease := range s.leases {
|
||||
if lease.Expiry.Unix() == leaseExpireStatic {
|
||||
for _, l := range s.leases {
|
||||
if l.Expiry.Unix() == leaseExpireStatic {
|
||||
if (flags & LeasesStatic) != 0 {
|
||||
result = append(result, *lease)
|
||||
leases = append(leases, l.Clone())
|
||||
}
|
||||
} else {
|
||||
if (flags & LeasesDynamic) != 0 {
|
||||
result = append(result, *lease)
|
||||
leases = append(leases, l.Clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
s.leasesLock.Unlock()
|
||||
return result
|
||||
return leases
|
||||
}
|
||||
|
||||
// getLeasesRef returns the actual leases slice. For internal use only.
|
||||
@@ -133,12 +138,11 @@ func (s *v6Server) leaseRemoveSwapByIndex(i int) {
|
||||
|
||||
// Remove a dynamic lease with the same properties
|
||||
// Return error if a static lease is found
|
||||
func (s *v6Server) rmDynamicLease(lease Lease) error {
|
||||
func (s *v6Server) 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 {
|
||||
return fmt.Errorf("static lease already exists")
|
||||
}
|
||||
@@ -147,11 +151,11 @@ func (s *v6Server) rmDynamicLease(lease Lease) error {
|
||||
if i == len(s.leases) {
|
||||
break
|
||||
}
|
||||
|
||||
l = s.leases[i]
|
||||
}
|
||||
|
||||
if net.IP.Equal(l.IP, lease.IP) {
|
||||
|
||||
if l.Expiry.Unix() == leaseExpireStatic {
|
||||
return fmt.Errorf("static lease already exists")
|
||||
}
|
||||
@@ -159,11 +163,12 @@ func (s *v6Server) rmDynamicLease(lease Lease) error {
|
||||
s.leaseRemoveSwapByIndex(i)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddStaticLease adds a static lease. It is safe for concurrent use.
|
||||
func (s *v6Server) AddStaticLease(l Lease) (err error) {
|
||||
func (s *v6Server) AddStaticLease(l *Lease) (err error) {
|
||||
defer func() { err = errors.Annotate(err, "dhcpv6: %w") }()
|
||||
|
||||
if len(l.IP) != 16 {
|
||||
@@ -181,18 +186,21 @@ func (s *v6Server) AddStaticLease(l Lease) (err error) {
|
||||
err = s.rmDynamicLease(l)
|
||||
if err != nil {
|
||||
s.leasesLock.Unlock()
|
||||
|
||||
return err
|
||||
}
|
||||
s.addLease(&l)
|
||||
|
||||
s.addLease(l)
|
||||
s.conf.notify(LeaseChangedDBStore)
|
||||
s.leasesLock.Unlock()
|
||||
|
||||
s.conf.notify(LeaseChangedAddedStatic)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveStaticLease removes a static lease. It is safe for concurrent use.
|
||||
func (s *v6Server) RemoveStaticLease(l Lease) (err error) {
|
||||
func (s *v6Server) RemoveStaticLease(l *Lease) (err error) {
|
||||
defer func() { err = errors.Annotate(err, "dhcpv6: %w") }()
|
||||
|
||||
if len(l.IP) != 16 {
|
||||
@@ -224,19 +232,20 @@ func (s *v6Server) addLease(l *Lease) {
|
||||
}
|
||||
|
||||
// Remove a lease with the same properties
|
||||
func (s *v6Server) rmLease(lease Lease) error {
|
||||
func (s *v6Server) rmLease(lease *Lease) (err error) {
|
||||
for i, l := range s.leases {
|
||||
if net.IP.Equal(l.IP, lease.IP) {
|
||||
|
||||
if !bytes.Equal(l.HWAddr, lease.HWAddr) ||
|
||||
l.Hostname != lease.Hostname {
|
||||
return fmt.Errorf("lease not found")
|
||||
}
|
||||
|
||||
s.leaseRemoveSwapByIndex(i)
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("lease not found")
|
||||
}
|
||||
|
||||
@@ -654,10 +663,10 @@ func (s *v6Server) Start() (err error) {
|
||||
}
|
||||
|
||||
// Stop - stop server
|
||||
func (s *v6Server) Stop() {
|
||||
err := s.ra.Close()
|
||||
func (s *v6Server) Stop() (err error) {
|
||||
err = s.ra.Close()
|
||||
if err != nil {
|
||||
log.Error("dhcpv6: s.ra.Close: %s", err)
|
||||
return fmt.Errorf("closing ra ctx: %w", err)
|
||||
}
|
||||
|
||||
// DHCPv6 server may not be initialized if ra_slaac_only=true
|
||||
@@ -668,11 +677,13 @@ func (s *v6Server) Stop() {
|
||||
log.Debug("dhcpv6: stopping")
|
||||
err = s.srv.Close()
|
||||
if err != nil {
|
||||
log.Error("dhcpv6: srv.Close: %s", err)
|
||||
return fmt.Errorf("closing dhcpv6 srv: %w", err)
|
||||
}
|
||||
|
||||
// now server.Serve() will return
|
||||
s.srv = nil
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create DHCPv6 server
|
||||
|
||||
@@ -22,34 +22,39 @@ func TestV6_AddRemove_static(t *testing.T) {
|
||||
RangeStart: net.ParseIP("2001::1"),
|
||||
notify: notify6,
|
||||
})
|
||||
require.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Empty(t, s.GetLeases(LeasesStatic))
|
||||
|
||||
// Add static lease.
|
||||
l := Lease{
|
||||
l := &Lease{
|
||||
IP: net.ParseIP("2001::1"),
|
||||
HWAddr: net.HardwareAddr{0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA},
|
||||
}
|
||||
require.Nil(t, s.AddStaticLease(l))
|
||||
err = s.AddStaticLease(l)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Try to add the same static lease.
|
||||
require.NotNil(t, s.AddStaticLease(l))
|
||||
err = s.AddStaticLease(l)
|
||||
require.Error(t, err)
|
||||
|
||||
ls := s.GetLeases(LeasesStatic)
|
||||
require.Len(t, ls, 1)
|
||||
|
||||
assert.Equal(t, l.IP, ls[0].IP)
|
||||
assert.Equal(t, l.HWAddr, ls[0].HWAddr)
|
||||
assert.EqualValues(t, leaseExpireStatic, ls[0].Expiry.Unix())
|
||||
|
||||
// Try to remove non-existent static lease.
|
||||
require.NotNil(t, s.RemoveStaticLease(Lease{
|
||||
err = s.RemoveStaticLease(&Lease{
|
||||
IP: net.ParseIP("2001::2"),
|
||||
HWAddr: l.HWAddr,
|
||||
}))
|
||||
})
|
||||
require.Error(t, err)
|
||||
|
||||
// Remove static lease.
|
||||
require.Nil(t, s.RemoveStaticLease(l))
|
||||
err = s.RemoveStaticLease(l)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Empty(t, s.GetLeases(LeasesStatic))
|
||||
}
|
||||
@@ -60,7 +65,8 @@ func TestV6_AddReplace(t *testing.T) {
|
||||
RangeStart: net.ParseIP("2001::1"),
|
||||
notify: notify6,
|
||||
})
|
||||
require.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
s, ok := sIface.(*v6Server)
|
||||
require.True(t, ok)
|
||||
|
||||
@@ -77,7 +83,7 @@ func TestV6_AddReplace(t *testing.T) {
|
||||
s.addLease(l)
|
||||
}
|
||||
|
||||
stLeases := []Lease{{
|
||||
stLeases := []*Lease{{
|
||||
IP: net.ParseIP("2001::1"),
|
||||
HWAddr: net.HardwareAddr{0x33, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA},
|
||||
}, {
|
||||
@@ -86,7 +92,8 @@ func TestV6_AddReplace(t *testing.T) {
|
||||
}}
|
||||
|
||||
for _, l := range stLeases {
|
||||
require.Nil(t, s.AddStaticLease(l))
|
||||
err = s.AddStaticLease(l)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
ls := s.GetLeases(LeasesStatic)
|
||||
@@ -106,8 +113,9 @@ func TestV6GetLease(t *testing.T) {
|
||||
RangeStart: net.ParseIP("2001::1"),
|
||||
notify: notify6,
|
||||
})
|
||||
require.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
s, ok := sIface.(*v6Server)
|
||||
|
||||
require.True(t, ok)
|
||||
|
||||
dnsAddr := net.ParseIP("2000::1")
|
||||
@@ -118,33 +126,36 @@ func TestV6GetLease(t *testing.T) {
|
||||
LinkLayerAddr: net.HardwareAddr{0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA},
|
||||
}
|
||||
|
||||
l := Lease{
|
||||
l := &Lease{
|
||||
IP: net.ParseIP("2001::1"),
|
||||
HWAddr: net.HardwareAddr{0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA},
|
||||
}
|
||||
require.Nil(t, s.AddStaticLease(l))
|
||||
err = s.AddStaticLease(l)
|
||||
require.NoError(t, err)
|
||||
|
||||
var req, resp, msg *dhcpv6.Message
|
||||
mac := net.HardwareAddr{0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA}
|
||||
t.Run("solicit", func(t *testing.T) {
|
||||
req, err = dhcpv6.NewSolicit(mac)
|
||||
require.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
msg, err = req.GetInnerMessage()
|
||||
require.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
resp, err = dhcpv6.NewAdvertiseFromSolicit(msg)
|
||||
require.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.True(t, s.process(msg, req, resp))
|
||||
})
|
||||
require.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
resp.AddOption(dhcpv6.OptServerID(s.sid))
|
||||
|
||||
var oia *dhcpv6.OptIANA
|
||||
var oiaAddr *dhcpv6.OptIAAddress
|
||||
t.Run("advertise", func(t *testing.T) {
|
||||
require.Equal(t, dhcpv6.MessageTypeAdvertise, resp.Type())
|
||||
|
||||
oia = resp.Options.OneIANA()
|
||||
oiaAddr = oia.Options.OneAddress()
|
||||
|
||||
@@ -154,20 +165,21 @@ func TestV6GetLease(t *testing.T) {
|
||||
|
||||
t.Run("request", func(t *testing.T) {
|
||||
req, err = dhcpv6.NewRequestFromAdvertise(resp)
|
||||
require.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
msg, err = req.GetInnerMessage()
|
||||
require.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
resp, err = dhcpv6.NewReplyFromMessage(msg)
|
||||
require.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.True(t, s.process(msg, req, resp))
|
||||
})
|
||||
require.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Run("reply", func(t *testing.T) {
|
||||
require.Equal(t, dhcpv6.MessageTypeReply, resp.Type())
|
||||
|
||||
oia = resp.Options.OneIANA()
|
||||
oiaAddr = oia.Options.OneAddress()
|
||||
|
||||
@@ -182,6 +194,7 @@ func TestV6GetLease(t *testing.T) {
|
||||
t.Run("lease", func(t *testing.T) {
|
||||
ls := s.GetLeases(LeasesStatic)
|
||||
require.Len(t, ls, 1)
|
||||
|
||||
assert.Equal(t, l.IP, ls[0].IP)
|
||||
assert.Equal(t, l.HWAddr, ls[0].HWAddr)
|
||||
})
|
||||
@@ -193,7 +206,8 @@ func TestV6GetDynamicLease(t *testing.T) {
|
||||
RangeStart: net.ParseIP("2001::2"),
|
||||
notify: notify6,
|
||||
})
|
||||
require.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
s, ok := sIface.(*v6Server)
|
||||
require.True(t, ok)
|
||||
|
||||
@@ -209,23 +223,25 @@ func TestV6GetDynamicLease(t *testing.T) {
|
||||
mac := net.HardwareAddr{0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA}
|
||||
t.Run("solicit", func(t *testing.T) {
|
||||
req, err = dhcpv6.NewSolicit(mac)
|
||||
require.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
msg, err = req.GetInnerMessage()
|
||||
require.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
resp, err = dhcpv6.NewAdvertiseFromSolicit(msg)
|
||||
require.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.True(t, s.process(msg, req, resp))
|
||||
})
|
||||
require.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
resp.AddOption(dhcpv6.OptServerID(s.sid))
|
||||
|
||||
var oia *dhcpv6.OptIANA
|
||||
var oiaAddr *dhcpv6.OptIAAddress
|
||||
t.Run("advertise", func(t *testing.T) {
|
||||
require.Equal(t, dhcpv6.MessageTypeAdvertise, resp.Type())
|
||||
|
||||
oia = resp.Options.OneIANA()
|
||||
oiaAddr = oia.Options.OneAddress()
|
||||
assert.Equal(t, "2001::2", oiaAddr.IPv6Addr.String())
|
||||
@@ -233,20 +249,21 @@ func TestV6GetDynamicLease(t *testing.T) {
|
||||
|
||||
t.Run("request", func(t *testing.T) {
|
||||
req, err = dhcpv6.NewRequestFromAdvertise(resp)
|
||||
require.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
msg, err = req.GetInnerMessage()
|
||||
require.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
resp, err = dhcpv6.NewReplyFromMessage(msg)
|
||||
require.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.True(t, s.process(msg, req, resp))
|
||||
})
|
||||
require.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Run("reply", func(t *testing.T) {
|
||||
require.Equal(t, dhcpv6.MessageTypeReply, resp.Type())
|
||||
|
||||
oia = resp.Options.OneIANA()
|
||||
oiaAddr = oia.Options.OneAddress()
|
||||
assert.Equal(t, "2001::2", oiaAddr.IPv6Addr.String())
|
||||
@@ -254,11 +271,13 @@ func TestV6GetDynamicLease(t *testing.T) {
|
||||
|
||||
dnsAddrs := resp.Options.DNS()
|
||||
require.Len(t, dnsAddrs, 1)
|
||||
|
||||
assert.Equal(t, dnsAddr, dnsAddrs[0])
|
||||
|
||||
t.Run("lease", func(t *testing.T) {
|
||||
ls := s.GetLeases(LeasesDynamic)
|
||||
require.Len(t, ls, 1)
|
||||
|
||||
assert.Equal(t, "2001::2", ls[0].IP.String())
|
||||
assert.Equal(t, mac, ls[0].HWAddr)
|
||||
})
|
||||
|
||||
@@ -6,138 +6,163 @@ import (
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghstrings"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/AdguardTeam/golibs/stringutil"
|
||||
"github.com/AdguardTeam/urlfilter"
|
||||
"github.com/AdguardTeam/urlfilter/filterlist"
|
||||
)
|
||||
|
||||
// accessCtx controls IP and client blocking that takes place before all other
|
||||
// processing. An accessCtx is safe for concurrent use.
|
||||
type accessCtx struct {
|
||||
lock sync.Mutex
|
||||
allowedIPs *aghnet.IPMap
|
||||
blockedIPs *aghnet.IPMap
|
||||
|
||||
// allowedClients are the IP addresses of clients in the allowlist.
|
||||
allowedClients *aghstrings.Set
|
||||
allowedClientIDs *stringutil.Set
|
||||
blockedClientIDs *stringutil.Set
|
||||
|
||||
// disallowedClients are the IP addresses of clients in the blocklist.
|
||||
disallowedClients *aghstrings.Set
|
||||
blockedHostsEng *urlfilter.DNSEngine
|
||||
|
||||
allowedClientsIPNet []net.IPNet // CIDRs of whitelist clients
|
||||
disallowedClientsIPNet []net.IPNet // CIDRs of clients that should be blocked
|
||||
|
||||
blockedHostsEngine *urlfilter.DNSEngine // finds hosts that should be blocked
|
||||
// TODO(a.garipov): Create a type for a set of IP networks.
|
||||
// aghnet.IPNetSet?
|
||||
allowedNets []*net.IPNet
|
||||
blockedNets []*net.IPNet
|
||||
}
|
||||
|
||||
func newAccessCtx(allowedClients, disallowedClients, blockedHosts []string) (a *accessCtx, err error) {
|
||||
a = &accessCtx{
|
||||
allowedClients: aghstrings.NewSet(),
|
||||
disallowedClients: aghstrings.NewSet(),
|
||||
}
|
||||
// unit is a convenient alias for struct{}
|
||||
type unit = struct{}
|
||||
|
||||
err = processIPCIDRArray(a.allowedClients, &a.allowedClientsIPNet, allowedClients)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("processing allowed clients: %w", err)
|
||||
}
|
||||
// processAccessClients is a helper for processing a list of client strings,
|
||||
// which may be an IP address, a CIDR, or a ClientID.
|
||||
func processAccessClients(
|
||||
clientStrs []string,
|
||||
ips *aghnet.IPMap,
|
||||
nets *[]*net.IPNet,
|
||||
clientIDs *stringutil.Set,
|
||||
) (err error) {
|
||||
for i, s := range clientStrs {
|
||||
if ip := net.ParseIP(s); ip != nil {
|
||||
ips.Set(ip, unit{})
|
||||
} else if cidrIP, ipnet, cidrErr := net.ParseCIDR(s); cidrErr == nil {
|
||||
ipnet.IP = cidrIP
|
||||
*nets = append(*nets, ipnet)
|
||||
} else {
|
||||
idErr := ValidateClientID(s)
|
||||
if idErr != nil {
|
||||
return fmt.Errorf(
|
||||
"value %q at index %d: bad ip, cidr, or clientid",
|
||||
s,
|
||||
i,
|
||||
)
|
||||
}
|
||||
|
||||
err = processIPCIDRArray(a.disallowedClients, &a.disallowedClientsIPNet, disallowedClients)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("processing disallowed clients: %w", err)
|
||||
}
|
||||
|
||||
b := &strings.Builder{}
|
||||
for _, s := range blockedHosts {
|
||||
aghstrings.WriteToBuilder(b, strings.ToLower(s), "\n")
|
||||
}
|
||||
|
||||
listArray := []filterlist.RuleList{}
|
||||
list := &filterlist.StringRuleList{
|
||||
ID: int(0),
|
||||
RulesText: b.String(),
|
||||
IgnoreCosmetic: true,
|
||||
}
|
||||
listArray = append(listArray, list)
|
||||
rulesStorage, err := filterlist.NewRuleStorage(listArray)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("filterlist.NewRuleStorage(): %w", err)
|
||||
}
|
||||
a.blockedHostsEngine = urlfilter.NewDNSEngine(rulesStorage)
|
||||
|
||||
return a, nil
|
||||
}
|
||||
|
||||
// Split array of IP or CIDR into 2 containers for fast search
|
||||
func processIPCIDRArray(dst *aghstrings.Set, dstIPNet *[]net.IPNet, src []string) error {
|
||||
for _, s := range src {
|
||||
ip := net.ParseIP(s)
|
||||
if ip != nil {
|
||||
dst.Add(s)
|
||||
|
||||
continue
|
||||
clientIDs.Add(s)
|
||||
}
|
||||
|
||||
_, ipnet, err := net.ParseCIDR(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*dstIPNet = append(*dstIPNet, *ipnet)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsBlockedIP - return TRUE if this client should be blocked
|
||||
// Returns the item from the "disallowedClients" list that lead to blocking IP.
|
||||
// If it returns TRUE and an empty string, it means that the "allowedClients" is not empty,
|
||||
// but the ip does not belong to it.
|
||||
func (a *accessCtx) IsBlockedIP(ip net.IP) (bool, string) {
|
||||
ipStr := ip.String()
|
||||
// newAccessCtx creates a new accessCtx.
|
||||
func newAccessCtx(allowed, blocked, blockedHosts []string) (a *accessCtx, err error) {
|
||||
a = &accessCtx{
|
||||
allowedIPs: aghnet.NewIPMap(0),
|
||||
blockedIPs: aghnet.NewIPMap(0),
|
||||
|
||||
a.lock.Lock()
|
||||
defer a.lock.Unlock()
|
||||
|
||||
if a.allowedClients.Len() != 0 || len(a.allowedClientsIPNet) != 0 {
|
||||
if a.allowedClients.Has(ipStr) {
|
||||
return false, ""
|
||||
}
|
||||
|
||||
if len(a.allowedClientsIPNet) != 0 {
|
||||
for _, ipnet := range a.allowedClientsIPNet {
|
||||
if ipnet.Contains(ip) {
|
||||
return false, ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true, ""
|
||||
allowedClientIDs: stringutil.NewSet(),
|
||||
blockedClientIDs: stringutil.NewSet(),
|
||||
}
|
||||
|
||||
if a.disallowedClients.Has(ipStr) {
|
||||
return true, ipStr
|
||||
err = processAccessClients(allowed, a.allowedIPs, &a.allowedNets, a.allowedClientIDs)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("adding allowed: %w", err)
|
||||
}
|
||||
|
||||
if len(a.disallowedClientsIPNet) != 0 {
|
||||
for _, ipnet := range a.disallowedClientsIPNet {
|
||||
if ipnet.Contains(ip) {
|
||||
return true, ipnet.String()
|
||||
}
|
||||
}
|
||||
err = processAccessClients(blocked, a.blockedIPs, &a.blockedNets, a.blockedClientIDs)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("adding blocked: %w", err)
|
||||
}
|
||||
|
||||
return false, ""
|
||||
b := &strings.Builder{}
|
||||
for _, h := range blockedHosts {
|
||||
stringutil.WriteToBuilder(b, strings.ToLower(h), "\n")
|
||||
}
|
||||
|
||||
lists := []filterlist.RuleList{
|
||||
&filterlist.StringRuleList{
|
||||
ID: int(0),
|
||||
RulesText: b.String(),
|
||||
IgnoreCosmetic: true,
|
||||
},
|
||||
}
|
||||
|
||||
rulesStrg, err := filterlist.NewRuleStorage(lists)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("adding blocked hosts: %w", err)
|
||||
}
|
||||
|
||||
a.blockedHostsEng = urlfilter.NewDNSEngine(rulesStrg)
|
||||
|
||||
return a, nil
|
||||
}
|
||||
|
||||
// IsBlockedDomain - return TRUE if this domain should be blocked
|
||||
func (a *accessCtx) IsBlockedDomain(host string) (ok bool) {
|
||||
a.lock.Lock()
|
||||
defer a.lock.Unlock()
|
||||
// allowlistMode returns true if this *accessCtx is in the allowlist mode.
|
||||
func (a *accessCtx) allowlistMode() (ok bool) {
|
||||
return a.allowedIPs.Len() != 0 || a.allowedClientIDs.Len() != 0 || len(a.allowedNets) != 0
|
||||
}
|
||||
|
||||
_, ok = a.blockedHostsEngine.Match(strings.ToLower(host))
|
||||
// isBlockedClientID returns true if the ClientID should be blocked.
|
||||
func (a *accessCtx) isBlockedClientID(id string) (ok bool) {
|
||||
allowlistMode := a.allowlistMode()
|
||||
if id == "" {
|
||||
// In allowlist mode, consider requests without client IDs
|
||||
// blocked by default.
|
||||
return allowlistMode
|
||||
}
|
||||
|
||||
if allowlistMode {
|
||||
return !a.allowedClientIDs.Has(id)
|
||||
}
|
||||
|
||||
return a.blockedClientIDs.Has(id)
|
||||
}
|
||||
|
||||
// isBlockedHost returns true if host should be blocked.
|
||||
func (a *accessCtx) isBlockedHost(host string) (ok bool) {
|
||||
_, ok = a.blockedHostsEng.Match(strings.ToLower(host))
|
||||
|
||||
return ok
|
||||
}
|
||||
|
||||
// isBlockedIP returns the status of the IP address blocking as well as the rule
|
||||
// that blocked it.
|
||||
func (a *accessCtx) isBlockedIP(ip net.IP) (blocked bool, rule string) {
|
||||
blocked = true
|
||||
ips := a.blockedIPs
|
||||
ipnets := a.blockedNets
|
||||
|
||||
if a.allowlistMode() {
|
||||
// Enable allowlist mode and use the allowlist sets.
|
||||
blocked = false
|
||||
ips = a.allowedIPs
|
||||
ipnets = a.allowedNets
|
||||
}
|
||||
|
||||
if _, ok := ips.Get(ip); ok {
|
||||
return blocked, ip.String()
|
||||
}
|
||||
|
||||
for _, ipnet := range ipnets {
|
||||
if ipnet.Contains(ip) {
|
||||
return blocked, ipnet.String()
|
||||
}
|
||||
}
|
||||
|
||||
return !blocked, ""
|
||||
}
|
||||
|
||||
type accessListJSON struct {
|
||||
AllowedClients []string `json:"allowed_clients"`
|
||||
DisallowedClients []string `json:"disallowed_clients"`
|
||||
@@ -149,9 +174,9 @@ func (s *Server) accessListJSON() (j accessListJSON) {
|
||||
defer s.serverLock.RUnlock()
|
||||
|
||||
return accessListJSON{
|
||||
AllowedClients: aghstrings.CloneSlice(s.conf.AllowedClients),
|
||||
DisallowedClients: aghstrings.CloneSlice(s.conf.DisallowedClients),
|
||||
BlockedHosts: aghstrings.CloneSlice(s.conf.BlockedHosts),
|
||||
AllowedClients: stringutil.CloneSlice(s.conf.AllowedClients),
|
||||
DisallowedClients: stringutil.CloneSlice(s.conf.DisallowedClients),
|
||||
BlockedHosts: stringutil.CloneSlice(s.conf.BlockedHosts),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,62 +186,43 @@ func (s *Server) handleAccessList(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
err := json.NewEncoder(w).Encode(j)
|
||||
if err != nil {
|
||||
httpError(r, w, http.StatusInternalServerError, "json.Encode: %s", err)
|
||||
httpError(r, w, http.StatusInternalServerError, "encoding response: %s", err)
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func checkIPCIDRArray(src []string) error {
|
||||
for _, s := range src {
|
||||
ip := net.ParseIP(s)
|
||||
if ip != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
_, _, err := net.ParseCIDR(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) handleAccessSet(w http.ResponseWriter, r *http.Request) {
|
||||
j := accessListJSON{}
|
||||
err := json.NewDecoder(r.Body).Decode(&j)
|
||||
list := accessListJSON{}
|
||||
err := json.NewDecoder(r.Body).Decode(&list)
|
||||
if err != nil {
|
||||
httpError(r, w, http.StatusBadRequest, "json.Decode: %s", err)
|
||||
return
|
||||
}
|
||||
httpError(r, w, http.StatusBadRequest, "decoding request: %s", err)
|
||||
|
||||
err = checkIPCIDRArray(j.AllowedClients)
|
||||
if err == nil {
|
||||
err = checkIPCIDRArray(j.DisallowedClients)
|
||||
}
|
||||
if err != nil {
|
||||
httpError(r, w, http.StatusBadRequest, "%s", err)
|
||||
return
|
||||
}
|
||||
|
||||
var a *accessCtx
|
||||
a, err = newAccessCtx(j.AllowedClients, j.DisallowedClients, j.BlockedHosts)
|
||||
a, err = newAccessCtx(list.AllowedClients, list.DisallowedClients, list.BlockedHosts)
|
||||
if err != nil {
|
||||
httpError(r, w, http.StatusBadRequest, "creating access ctx: %s", err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
defer log.Debug("Access: updated lists: %d, %d, %d",
|
||||
len(j.AllowedClients), len(j.DisallowedClients), len(j.BlockedHosts))
|
||||
defer log.Debug(
|
||||
"access: updated lists: %d, %d, %d",
|
||||
len(list.AllowedClients),
|
||||
len(list.DisallowedClients),
|
||||
len(list.BlockedHosts),
|
||||
)
|
||||
|
||||
defer s.conf.ConfigModified()
|
||||
|
||||
s.serverLock.Lock()
|
||||
defer s.serverLock.Unlock()
|
||||
|
||||
s.conf.AllowedClients = j.AllowedClients
|
||||
s.conf.DisallowedClients = j.DisallowedClients
|
||||
s.conf.BlockedHosts = j.BlockedHosts
|
||||
s.conf.AllowedClients = list.AllowedClients
|
||||
s.conf.DisallowedClients = list.DisallowedClients
|
||||
s.conf.BlockedHosts = list.BlockedHosts
|
||||
s.access = a
|
||||
}
|
||||
|
||||
@@ -8,99 +8,23 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestIsBlockedIP(t *testing.T) {
|
||||
const (
|
||||
ip int = iota
|
||||
cidr
|
||||
)
|
||||
func TestIsBlockedClientID(t *testing.T) {
|
||||
clientID := "client-1"
|
||||
clients := []string{clientID}
|
||||
|
||||
rules := []string{
|
||||
ip: "1.1.1.1",
|
||||
cidr: "2.2.0.0/16",
|
||||
}
|
||||
a, err := newAccessCtx(clients, nil, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
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: "",
|
||||
}}
|
||||
assert.False(t, a.isBlockedClientID(clientID))
|
||||
|
||||
for _, tc := range testCases {
|
||||
prefix := "allowed_"
|
||||
if !tc.allowed {
|
||||
prefix = "disallowed_"
|
||||
}
|
||||
a, err = newAccessCtx(nil, clients, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Run(prefix+tc.name, func(t *testing.T) {
|
||||
allowedRules := rules
|
||||
var disallowedRules []string
|
||||
|
||||
if !tc.allowed {
|
||||
allowedRules, disallowedRules = disallowedRules, allowedRules
|
||||
}
|
||||
|
||||
aCtx, err := newAccessCtx(allowedRules, disallowedRules, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
disallowed, rule := aCtx.IsBlockedIP(tc.ip)
|
||||
assert.Equal(t, tc.wantDis, disallowed)
|
||||
assert.Equal(t, tc.wantRule, rule)
|
||||
})
|
||||
}
|
||||
assert.True(t, a.isBlockedClientID(clientID))
|
||||
}
|
||||
|
||||
func TestIsBlockedDomain(t *testing.T) {
|
||||
aCtx, err := newAccessCtx(nil, nil, []string{
|
||||
func TestIsBlockedHost(t *testing.T) {
|
||||
a, err := newAccessCtx(nil, nil, []string{
|
||||
"host1",
|
||||
"*.host.com",
|
||||
"||host3.com^",
|
||||
@@ -108,50 +32,106 @@ func TestIsBlockedDomain(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
domain string
|
||||
want bool
|
||||
name string
|
||||
host string
|
||||
want bool
|
||||
}{{
|
||||
name: "plain_match",
|
||||
domain: "host1",
|
||||
want: true,
|
||||
name: "plain_match",
|
||||
host: "host1",
|
||||
want: true,
|
||||
}, {
|
||||
name: "plain_mismatch",
|
||||
domain: "host2",
|
||||
want: false,
|
||||
name: "plain_mismatch",
|
||||
host: "host2",
|
||||
want: false,
|
||||
}, {
|
||||
name: "wildcard_type-1_match_short",
|
||||
domain: "asdf.host.com",
|
||||
want: true,
|
||||
name: "subdomain_match_short",
|
||||
host: "asdf.host.com",
|
||||
want: true,
|
||||
}, {
|
||||
name: "wildcard_type-1_match_long",
|
||||
domain: "qwer.asdf.host.com",
|
||||
want: true,
|
||||
name: "subdomain_match_long",
|
||||
host: "qwer.asdf.host.com",
|
||||
want: true,
|
||||
}, {
|
||||
name: "wildcard_type-1_mismatch_no-lead",
|
||||
domain: "host.com",
|
||||
want: false,
|
||||
name: "subdomain_mismatch_no_lead",
|
||||
host: "host.com",
|
||||
want: false,
|
||||
}, {
|
||||
name: "wildcard_type-1_mismatch_bad-asterisk",
|
||||
domain: "asdf.zhost.com",
|
||||
want: false,
|
||||
name: "subdomain_mismatch_bad_asterisk",
|
||||
host: "asdf.zhost.com",
|
||||
want: false,
|
||||
}, {
|
||||
name: "wildcard_type-2_match_simple",
|
||||
domain: "host3.com",
|
||||
want: true,
|
||||
name: "rule_match_simple",
|
||||
host: "host3.com",
|
||||
want: true,
|
||||
}, {
|
||||
name: "wildcard_type-2_match_complex",
|
||||
domain: "asdf.host3.com",
|
||||
want: true,
|
||||
name: "rule_match_complex",
|
||||
host: "asdf.host3.com",
|
||||
want: true,
|
||||
}, {
|
||||
name: "wildcard_type-2_mismatch",
|
||||
domain: ".host3.com",
|
||||
want: false,
|
||||
name: "rule_mismatch",
|
||||
host: ".host3.com",
|
||||
want: false,
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
assert.Equal(t, tc.want, aCtx.IsBlockedDomain(tc.domain))
|
||||
assert.Equal(t, tc.want, a.isBlockedHost(tc.host))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsBlockedIP(t *testing.T) {
|
||||
clients := []string{
|
||||
"1.2.3.4",
|
||||
"5.6.7.8/24",
|
||||
}
|
||||
|
||||
allowCtx, err := newAccessCtx(clients, nil, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
blockCtx, err := newAccessCtx(nil, clients, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
wantRule string
|
||||
ip net.IP
|
||||
wantBlocked bool
|
||||
}{{
|
||||
name: "match_ip",
|
||||
wantRule: "1.2.3.4",
|
||||
ip: net.IP{1, 2, 3, 4},
|
||||
wantBlocked: true,
|
||||
}, {
|
||||
name: "match_cidr",
|
||||
wantRule: "5.6.7.8/24",
|
||||
ip: net.IP{5, 6, 7, 100},
|
||||
wantBlocked: true,
|
||||
}, {
|
||||
name: "no_match_ip",
|
||||
wantRule: "",
|
||||
ip: net.IP{9, 2, 3, 4},
|
||||
wantBlocked: false,
|
||||
}, {
|
||||
name: "no_match_cidr",
|
||||
wantRule: "",
|
||||
ip: net.IP{9, 6, 7, 100},
|
||||
wantBlocked: false,
|
||||
}}
|
||||
|
||||
t.Run("allow", func(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
blocked, rule := allowCtx.isBlockedIP(tc.ip)
|
||||
assert.Equal(t, !tc.wantBlocked, blocked)
|
||||
assert.Equal(t, tc.wantRule, rule)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("block", func(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
blocked, rule := blockCtx.isBlockedIP(tc.ip)
|
||||
assert.Equal(t, tc.wantBlocked, blocked)
|
||||
assert.Equal(t, tc.wantRule, rule)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package dnsforward
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"path"
|
||||
"strings"
|
||||
@@ -50,15 +51,15 @@ func clientIDFromClientServerName(hostSrvName, cliSrvName string, strict bool) (
|
||||
return clientID, nil
|
||||
}
|
||||
|
||||
// processClientIDHTTPS extracts the client's ID from the path of the
|
||||
// clientIDFromDNSContextHTTPS extracts the client's ID from the path of the
|
||||
// client's DNS-over-HTTPS request.
|
||||
func processClientIDHTTPS(ctx *dnsContext) (rc resultCode) {
|
||||
pctx := ctx.proxyCtx
|
||||
func clientIDFromDNSContextHTTPS(pctx *proxy.DNSContext) (clientID string, err error) {
|
||||
r := pctx.HTTPRequest
|
||||
if r == nil {
|
||||
ctx.err = fmt.Errorf("proxy ctx http request of proto %s is nil", pctx.Proto)
|
||||
|
||||
return resultCodeError
|
||||
return "", fmt.Errorf(
|
||||
"proxy ctx http request of proto %s is nil",
|
||||
pctx.Proto,
|
||||
)
|
||||
}
|
||||
|
||||
origPath := r.URL.Path
|
||||
@@ -68,34 +69,25 @@ func processClientIDHTTPS(ctx *dnsContext) (rc resultCode) {
|
||||
}
|
||||
|
||||
if len(parts) == 0 || parts[0] != "dns-query" {
|
||||
ctx.err = fmt.Errorf("client id check: invalid path %q", origPath)
|
||||
|
||||
return resultCodeError
|
||||
return "", fmt.Errorf("client id check: invalid path %q", origPath)
|
||||
}
|
||||
|
||||
clientID := ""
|
||||
switch len(parts) {
|
||||
case 1:
|
||||
// Just /dns-query, no client ID.
|
||||
return resultCodeSuccess
|
||||
return "", nil
|
||||
case 2:
|
||||
clientID = parts[1]
|
||||
default:
|
||||
ctx.err = fmt.Errorf("client id check: invalid path %q: extra parts", origPath)
|
||||
|
||||
return resultCodeError
|
||||
return "", fmt.Errorf("client id check: invalid path %q: extra parts", origPath)
|
||||
}
|
||||
|
||||
err := ValidateClientID(clientID)
|
||||
err = ValidateClientID(clientID)
|
||||
if err != nil {
|
||||
ctx.err = fmt.Errorf("client id check: %w", err)
|
||||
|
||||
return resultCodeError
|
||||
return "", fmt.Errorf("client id check: %w", err)
|
||||
}
|
||||
|
||||
ctx.clientID = clientID
|
||||
|
||||
return resultCodeSuccess
|
||||
return clientID, nil
|
||||
}
|
||||
|
||||
// tlsConn is a narrow interface for *tls.Conn to simplify testing.
|
||||
@@ -108,53 +100,73 @@ type quicSession interface {
|
||||
ConnectionState() (cs quic.ConnectionState)
|
||||
}
|
||||
|
||||
// processClientID extracts the client's ID from the server name of the client's
|
||||
// DOT or DOQ request or the path of the client's DOH.
|
||||
func processClientID(dctx *dnsContext) (rc resultCode) {
|
||||
pctx := dctx.proxyCtx
|
||||
// clientIDFromDNSContext extracts the client's ID from the server name of the
|
||||
// client's DoT or DoQ request or the path of the client's DoH. If the protocol
|
||||
// is not one of these, clientID is an empty string and err is nil.
|
||||
func (s *Server) clientIDFromDNSContext(pctx *proxy.DNSContext) (clientID string, err error) {
|
||||
proto := pctx.Proto
|
||||
if proto == proxy.ProtoHTTPS {
|
||||
return processClientIDHTTPS(dctx)
|
||||
return clientIDFromDNSContextHTTPS(pctx)
|
||||
} else if proto != proxy.ProtoTLS && proto != proxy.ProtoQUIC {
|
||||
return resultCodeSuccess
|
||||
return "", nil
|
||||
}
|
||||
|
||||
srvConf := dctx.srv.conf
|
||||
hostSrvName := srvConf.TLSConfig.ServerName
|
||||
hostSrvName := s.conf.ServerName
|
||||
if hostSrvName == "" {
|
||||
return resultCodeSuccess
|
||||
return "", nil
|
||||
}
|
||||
|
||||
cliSrvName := ""
|
||||
if proto == proxy.ProtoTLS {
|
||||
switch proto {
|
||||
case proxy.ProtoTLS:
|
||||
conn := pctx.Conn
|
||||
tc, ok := conn.(tlsConn)
|
||||
if !ok {
|
||||
dctx.err = fmt.Errorf("proxy ctx conn of proto %s is %T, want *tls.Conn", proto, conn)
|
||||
|
||||
return resultCodeError
|
||||
return "", fmt.Errorf(
|
||||
"proxy ctx conn of proto %s is %T, want *tls.Conn",
|
||||
proto,
|
||||
conn,
|
||||
)
|
||||
}
|
||||
|
||||
cliSrvName = tc.ConnectionState().ServerName
|
||||
} else if proto == proxy.ProtoQUIC {
|
||||
case proxy.ProtoQUIC:
|
||||
qs, ok := pctx.QUICSession.(quicSession)
|
||||
if !ok {
|
||||
dctx.err = fmt.Errorf("proxy ctx quic session of proto %s is %T, want quic.Session", proto, pctx.QUICSession)
|
||||
|
||||
return resultCodeError
|
||||
return "", fmt.Errorf(
|
||||
"proxy ctx quic session of proto %s is %T, want quic.Session",
|
||||
proto,
|
||||
pctx.QUICSession,
|
||||
)
|
||||
}
|
||||
|
||||
cliSrvName = qs.ConnectionState().TLS.ServerName
|
||||
}
|
||||
|
||||
clientID, err := clientIDFromClientServerName(hostSrvName, cliSrvName, srvConf.StrictSNICheck)
|
||||
clientID, err = clientIDFromClientServerName(
|
||||
hostSrvName,
|
||||
cliSrvName,
|
||||
s.conf.StrictSNICheck,
|
||||
)
|
||||
if err != nil {
|
||||
dctx.err = fmt.Errorf("client id check: %w", err)
|
||||
|
||||
return resultCodeError
|
||||
return "", fmt.Errorf("client id check: %w", err)
|
||||
}
|
||||
|
||||
dctx.clientID = clientID
|
||||
return clientID, nil
|
||||
}
|
||||
|
||||
// processClientID puts the clientID into the DNS context, if there is one.
|
||||
func (s *Server) processClientID(dctx *dnsContext) (rc resultCode) {
|
||||
pctx := dctx.proxyCtx
|
||||
|
||||
var key [8]byte
|
||||
binary.BigEndian.PutUint64(key[:], pctx.RequestID)
|
||||
clientIDData := s.clientIDCache.Get(key[:])
|
||||
if clientIDData == nil {
|
||||
return resultCodeSuccess
|
||||
}
|
||||
|
||||
dctx.clientID = string(clientIDData)
|
||||
|
||||
return resultCodeSuccess
|
||||
}
|
||||
|
||||
@@ -45,15 +45,14 @@ func (c testQUICSession) ConnectionState() (cs quic.ConnectionState) {
|
||||
return cs
|
||||
}
|
||||
|
||||
func TestProcessClientID(t *testing.T) {
|
||||
func TestServer_clientIDFromDNSContext(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
proto string
|
||||
proto proxy.Proto
|
||||
hostSrvName string
|
||||
cliSrvName string
|
||||
wantClientID string
|
||||
wantErrMsg string
|
||||
wantRes resultCode
|
||||
strictSNI bool
|
||||
}{{
|
||||
name: "udp",
|
||||
@@ -62,7 +61,6 @@ func TestProcessClientID(t *testing.T) {
|
||||
cliSrvName: "",
|
||||
wantClientID: "",
|
||||
wantErrMsg: "",
|
||||
wantRes: resultCodeSuccess,
|
||||
strictSNI: false,
|
||||
}, {
|
||||
name: "tls_no_client_id",
|
||||
@@ -71,7 +69,6 @@ func TestProcessClientID(t *testing.T) {
|
||||
cliSrvName: "example.com",
|
||||
wantClientID: "",
|
||||
wantErrMsg: "",
|
||||
wantRes: resultCodeSuccess,
|
||||
strictSNI: true,
|
||||
}, {
|
||||
name: "tls_no_client_server_name",
|
||||
@@ -81,7 +78,6 @@ func TestProcessClientID(t *testing.T) {
|
||||
wantClientID: "",
|
||||
wantErrMsg: `client id check: client server name "" ` +
|
||||
`doesn't match host server name "example.com"`,
|
||||
wantRes: resultCodeError,
|
||||
strictSNI: true,
|
||||
}, {
|
||||
name: "tls_no_client_server_name_no_strict",
|
||||
@@ -90,7 +86,6 @@ func TestProcessClientID(t *testing.T) {
|
||||
cliSrvName: "",
|
||||
wantClientID: "",
|
||||
wantErrMsg: "",
|
||||
wantRes: resultCodeSuccess,
|
||||
strictSNI: false,
|
||||
}, {
|
||||
name: "tls_client_id",
|
||||
@@ -99,7 +94,6 @@ func TestProcessClientID(t *testing.T) {
|
||||
cliSrvName: "cli.example.com",
|
||||
wantClientID: "cli",
|
||||
wantErrMsg: "",
|
||||
wantRes: resultCodeSuccess,
|
||||
strictSNI: true,
|
||||
}, {
|
||||
name: "tls_client_id_hostname_error",
|
||||
@@ -109,7 +103,6 @@ func TestProcessClientID(t *testing.T) {
|
||||
wantClientID: "",
|
||||
wantErrMsg: `client id check: client server name "cli.example.net" ` +
|
||||
`doesn't match host server name "example.com"`,
|
||||
wantRes: resultCodeError,
|
||||
strictSNI: true,
|
||||
}, {
|
||||
name: "tls_invalid_client_id",
|
||||
@@ -119,7 +112,6 @@ func TestProcessClientID(t *testing.T) {
|
||||
wantClientID: "",
|
||||
wantErrMsg: `client id check: invalid client id "!!!": ` +
|
||||
`invalid char '!' at index 0`,
|
||||
wantRes: resultCodeError,
|
||||
strictSNI: true,
|
||||
}, {
|
||||
name: "tls_client_id_too_long",
|
||||
@@ -131,7 +123,6 @@ func TestProcessClientID(t *testing.T) {
|
||||
wantErrMsg: `client id check: invalid client id "abcdefghijklmno` +
|
||||
`pqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789": ` +
|
||||
`label is too long, max: 63`,
|
||||
wantRes: resultCodeError,
|
||||
strictSNI: true,
|
||||
}, {
|
||||
name: "quic_client_id",
|
||||
@@ -140,7 +131,6 @@ func TestProcessClientID(t *testing.T) {
|
||||
cliSrvName: "cli.example.com",
|
||||
wantClientID: "cli",
|
||||
wantErrMsg: "",
|
||||
wantRes: resultCodeSuccess,
|
||||
strictSNI: true,
|
||||
}}
|
||||
|
||||
@@ -150,6 +140,7 @@ func TestProcessClientID(t *testing.T) {
|
||||
ServerName: tc.hostSrvName,
|
||||
StrictSNICheck: tc.strictSNI,
|
||||
}
|
||||
|
||||
srv := &Server{
|
||||
conf: ServerConfig{TLSConfig: tlsConf},
|
||||
}
|
||||
@@ -168,79 +159,68 @@ func TestProcessClientID(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
dctx := &dnsContext{
|
||||
srv: srv,
|
||||
proxyCtx: &proxy.DNSContext{
|
||||
Proto: tc.proto,
|
||||
Conn: conn,
|
||||
QUICSession: qs,
|
||||
},
|
||||
pctx := &proxy.DNSContext{
|
||||
Proto: tc.proto,
|
||||
Conn: conn,
|
||||
QUICSession: qs,
|
||||
}
|
||||
|
||||
res := processClientID(dctx)
|
||||
assert.Equal(t, tc.wantRes, res)
|
||||
assert.Equal(t, tc.wantClientID, dctx.clientID)
|
||||
clientID, err := srv.clientIDFromDNSContext(pctx)
|
||||
assert.Equal(t, tc.wantClientID, clientID)
|
||||
|
||||
if tc.wantErrMsg == "" {
|
||||
assert.NoError(t, dctx.err)
|
||||
assert.NoError(t, err)
|
||||
} else {
|
||||
require.Error(t, dctx.err)
|
||||
assert.Equal(t, tc.wantErrMsg, dctx.err.Error())
|
||||
require.Error(t, err)
|
||||
|
||||
assert.Equal(t, tc.wantErrMsg, err.Error())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestProcessClientID_https(t *testing.T) {
|
||||
func TestClientIDFromDNSContextHTTPS(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
path string
|
||||
wantClientID string
|
||||
wantErrMsg string
|
||||
wantRes resultCode
|
||||
}{{
|
||||
name: "no_client_id",
|
||||
path: "/dns-query",
|
||||
wantClientID: "",
|
||||
wantErrMsg: "",
|
||||
wantRes: resultCodeSuccess,
|
||||
}, {
|
||||
name: "no_client_id_slash",
|
||||
path: "/dns-query/",
|
||||
wantClientID: "",
|
||||
wantErrMsg: "",
|
||||
wantRes: resultCodeSuccess,
|
||||
}, {
|
||||
name: "client_id",
|
||||
path: "/dns-query/cli",
|
||||
wantClientID: "cli",
|
||||
wantErrMsg: "",
|
||||
wantRes: resultCodeSuccess,
|
||||
}, {
|
||||
name: "client_id_slash",
|
||||
path: "/dns-query/cli/",
|
||||
wantClientID: "cli",
|
||||
wantErrMsg: "",
|
||||
wantRes: resultCodeSuccess,
|
||||
}, {
|
||||
name: "bad_url",
|
||||
path: "/foo",
|
||||
wantClientID: "",
|
||||
wantErrMsg: `client id check: invalid path "/foo"`,
|
||||
wantRes: resultCodeError,
|
||||
}, {
|
||||
name: "extra",
|
||||
path: "/dns-query/cli/foo",
|
||||
wantClientID: "",
|
||||
wantErrMsg: `client id check: invalid path "/dns-query/cli/foo": extra parts`,
|
||||
wantRes: resultCodeError,
|
||||
}, {
|
||||
name: "invalid_client_id",
|
||||
path: "/dns-query/!!!",
|
||||
wantClientID: "",
|
||||
wantErrMsg: `client id check: invalid client id "!!!": ` +
|
||||
`invalid char '!' at index 0`,
|
||||
wantRes: resultCodeError,
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
@@ -251,23 +231,20 @@ func TestProcessClientID_https(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
dctx := &dnsContext{
|
||||
proxyCtx: &proxy.DNSContext{
|
||||
Proto: proxy.ProtoHTTPS,
|
||||
HTTPRequest: r,
|
||||
},
|
||||
pctx := &proxy.DNSContext{
|
||||
Proto: proxy.ProtoHTTPS,
|
||||
HTTPRequest: r,
|
||||
}
|
||||
|
||||
res := processClientID(dctx)
|
||||
assert.Equal(t, tc.wantRes, res)
|
||||
assert.Equal(t, tc.wantClientID, dctx.clientID)
|
||||
clientID, err := clientIDFromDNSContextHTTPS(pctx)
|
||||
assert.Equal(t, tc.wantClientID, clientID)
|
||||
|
||||
if tc.wantErrMsg == "" {
|
||||
assert.NoError(t, dctx.err)
|
||||
assert.NoError(t, err)
|
||||
} else {
|
||||
require.Error(t, dctx.err)
|
||||
require.Error(t, err)
|
||||
|
||||
assert.Equal(t, tc.wantErrMsg, dctx.err.Error())
|
||||
assert.Equal(t, tc.wantErrMsg, err.Error())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -12,15 +12,39 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghstrings"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
||||
"github.com/AdguardTeam/dnsproxy/proxy"
|
||||
"github.com/AdguardTeam/dnsproxy/upstream"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/AdguardTeam/golibs/stringutil"
|
||||
"github.com/ameshkov/dnscrypt/v2"
|
||||
)
|
||||
|
||||
// BlockingMode is an enum of all allowed blocking modes.
|
||||
type BlockingMode string
|
||||
|
||||
// Allowed blocking modes.
|
||||
const (
|
||||
// BlockingModeCustomIP means respond with a custom IP address.
|
||||
BlockingModeCustomIP BlockingMode = "custom_ip"
|
||||
|
||||
// BlockingModeDefault is the same as BlockingModeNullIP for
|
||||
// Adblock-style rules, but responds with the IP address specified in
|
||||
// the rule when blocked by an `/etc/hosts`-style rule.
|
||||
BlockingModeDefault BlockingMode = "default"
|
||||
|
||||
// BlockingModeNullIP means respond with a zero IP address: "0.0.0.0"
|
||||
// for A requests and "::" for AAAA ones.
|
||||
BlockingModeNullIP BlockingMode = "null_ip"
|
||||
|
||||
// BlockingModeNXDOMAIN means respond with the NXDOMAIN code.
|
||||
BlockingModeNXDOMAIN BlockingMode = "nxdomain"
|
||||
|
||||
// BlockingModeREFUSED means respond with the REFUSED code.
|
||||
BlockingModeREFUSED BlockingMode = "refused"
|
||||
)
|
||||
|
||||
// FilteringConfig represents the DNS filtering configuration of AdGuard Home
|
||||
// The zero FilteringConfig is empty and ready for use.
|
||||
type FilteringConfig struct {
|
||||
@@ -38,11 +62,11 @@ type FilteringConfig struct {
|
||||
// Protection configuration
|
||||
// --
|
||||
|
||||
ProtectionEnabled bool `yaml:"protection_enabled"` // whether or not use any of filtering features
|
||||
BlockingMode string `yaml:"blocking_mode"` // mode how to answer filtered requests
|
||||
BlockingIPv4 net.IP `yaml:"blocking_ipv4"` // IP address to be returned for a blocked A request
|
||||
BlockingIPv6 net.IP `yaml:"blocking_ipv6"` // IP address to be returned for a blocked AAAA request
|
||||
BlockedResponseTTL uint32 `yaml:"blocked_response_ttl"` // if 0, then default is used (3600)
|
||||
ProtectionEnabled bool `yaml:"protection_enabled"` // whether or not use any of filtering features
|
||||
BlockingMode BlockingMode `yaml:"blocking_mode"` // mode how to answer filtered requests
|
||||
BlockingIPv4 net.IP `yaml:"blocking_ipv4"` // IP address to be returned for a blocked A request
|
||||
BlockingIPv6 net.IP `yaml:"blocking_ipv6"` // IP address to be returned for a blocked AAAA request
|
||||
BlockedResponseTTL uint32 `yaml:"blocked_response_ttl"` // if 0, then default is used (3600)
|
||||
|
||||
// IP (or domain name) which is used to respond to DNS requests blocked by parental control or safe-browsing
|
||||
ParentalBlockHost string `yaml:"parental_block_host"`
|
||||
@@ -70,6 +94,11 @@ type FilteringConfig struct {
|
||||
AllowedClients []string `yaml:"allowed_clients"` // IP addresses of whitelist clients
|
||||
DisallowedClients []string `yaml:"disallowed_clients"` // IP addresses of clients that should be blocked
|
||||
BlockedHosts []string `yaml:"blocked_hosts"` // hosts that should be blocked
|
||||
// TrustedProxies is the list of IP addresses and CIDR networks to
|
||||
// detect proxy servers addresses the DoH requests from which should be
|
||||
// handled. The value of nil or an empty slice for this field makes
|
||||
// Proxy not trust any address.
|
||||
TrustedProxies []string `yaml:"trusted_proxies"`
|
||||
|
||||
// DNS cache settings
|
||||
// --
|
||||
@@ -77,6 +106,8 @@ type FilteringConfig struct {
|
||||
CacheSize uint32 `yaml:"cache_size"` // DNS cache size (in bytes)
|
||||
CacheMinTTL uint32 `yaml:"cache_ttl_min"` // override TTL value (minimum) received from upstream server
|
||||
CacheMaxTTL uint32 `yaml:"cache_ttl_max"` // override TTL value (maximum) received from upstream server
|
||||
// CacheOptimistic defines if optimistic cache mechanism should be used.
|
||||
CacheOptimistic bool `yaml:"cache_optimistic"`
|
||||
|
||||
// Other settings
|
||||
// --
|
||||
@@ -87,10 +118,12 @@ type FilteringConfig struct {
|
||||
EnableEDNSClientSubnet bool `yaml:"edns_client_subnet"` // Enable EDNS Client Subnet option
|
||||
MaxGoroutines uint32 `yaml:"max_goroutines"` // Max. number of parallel goroutines for processing incoming requests
|
||||
|
||||
// IPSET configuration - add IP addresses of the specified domain names to an ipset list
|
||||
// Syntax:
|
||||
// "DOMAIN[,DOMAIN].../IPSET_NAME"
|
||||
IPSETList []string `yaml:"ipset"`
|
||||
// IpsetList is the ipset configuration that allows AdGuard Home to add
|
||||
// IP addresses of the specified domain names to an ipset list. Syntax:
|
||||
//
|
||||
// DOMAIN[,DOMAIN].../IPSET_NAME
|
||||
//
|
||||
IpsetList []string `yaml:"ipset"`
|
||||
}
|
||||
|
||||
// TLSConfig is the TLS configuration for HTTPS, DNS-over-HTTPS, and DNS-over-TLS
|
||||
@@ -141,7 +174,7 @@ type ServerConfig struct {
|
||||
FilteringConfig
|
||||
TLSConfig
|
||||
DNSCryptConfig
|
||||
TLSAllowUnencryptedDOH bool
|
||||
TLSAllowUnencryptedDoH bool
|
||||
|
||||
// UpstreamTimeout is the timeout for querying upstream servers.
|
||||
UpstreamTimeout time.Duration
|
||||
@@ -182,8 +215,10 @@ func (s *Server) createProxyConfig() (proxy.Config, error) {
|
||||
Ratelimit: int(s.conf.Ratelimit),
|
||||
RatelimitWhitelist: s.conf.RatelimitWhitelist,
|
||||
RefuseAny: s.conf.RefuseAny,
|
||||
TrustedProxies: s.conf.TrustedProxies,
|
||||
CacheMinTTL: s.conf.CacheMinTTL,
|
||||
CacheMaxTTL: s.conf.CacheMaxTTL,
|
||||
CacheOptimistic: s.conf.CacheOptimistic,
|
||||
UpstreamConfig: s.conf.UpstreamConfig,
|
||||
BeforeRequestHandler: s.beforeRequestHandler,
|
||||
RequestHandler: s.handleDNSRequest,
|
||||
@@ -292,20 +327,18 @@ func (s *Server) prepareUpstreamSettings() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d := string(data)
|
||||
for len(d) != 0 {
|
||||
s := aghstrings.SplitNext(&d, '\n')
|
||||
upstreams = append(upstreams, s)
|
||||
}
|
||||
|
||||
upstreams = stringutil.SplitTrimmed(string(data), "\n")
|
||||
|
||||
log.Debug("dns: using %d upstream servers from file %s", len(upstreams), s.conf.UpstreamDNSFileName)
|
||||
} else {
|
||||
upstreams = s.conf.UpstreamDNS
|
||||
}
|
||||
|
||||
upstreams = aghstrings.FilterOut(upstreams, aghstrings.IsCommentOrEmpty)
|
||||
upstreams = stringutil.FilterOut(upstreams, IsCommentOrEmpty)
|
||||
upstreamConfig, err := proxy.ParseUpstreamsConfig(
|
||||
upstreams,
|
||||
upstream.Options{
|
||||
&upstream.Options{
|
||||
Bootstrap: s.conf.BootstrapDNS,
|
||||
Timeout: s.conf.UpstreamTimeout,
|
||||
},
|
||||
@@ -316,10 +349,10 @@ func (s *Server) prepareUpstreamSettings() error {
|
||||
|
||||
if len(upstreamConfig.Upstreams) == 0 {
|
||||
log.Info("warning: no default upstream servers specified, using %v", defaultDNS)
|
||||
var uc proxy.UpstreamConfig
|
||||
var uc *proxy.UpstreamConfig
|
||||
uc, err = proxy.ParseUpstreamsConfig(
|
||||
defaultDNS,
|
||||
upstream.Options{
|
||||
&upstream.Options{
|
||||
Bootstrap: s.conf.BootstrapDNS,
|
||||
Timeout: s.conf.UpstreamTimeout,
|
||||
},
|
||||
@@ -330,7 +363,8 @@ func (s *Server) prepareUpstreamSettings() error {
|
||||
upstreamConfig.Upstreams = uc.Upstreams
|
||||
}
|
||||
|
||||
s.conf.UpstreamConfig = &upstreamConfig
|
||||
s.conf.UpstreamConfig = upstreamConfig
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -6,11 +6,11 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghstrings"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/dhcpd"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
||||
"github.com/AdguardTeam/dnsproxy/proxy"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/AdguardTeam/golibs/stringutil"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
@@ -32,7 +32,7 @@ type dnsContext struct {
|
||||
unreversedReqIP net.IP
|
||||
// err is the error returned from a processing function.
|
||||
err error
|
||||
// clientID is the clientID from DOH, DOQ, or DOT, if provided.
|
||||
// clientID is the clientID from DoH, DoQ, or DoT, if provided.
|
||||
clientID string
|
||||
// origQuestion is the question received from the client. It is set
|
||||
// when the request is modified by rewrites.
|
||||
@@ -89,7 +89,7 @@ func (s *Server) handleDNSRequest(_ *proxy.Proxy, d *proxy.DNSContext) error {
|
||||
s.processInternalHosts,
|
||||
s.processRestrictLocal,
|
||||
s.processInternalIPAddrs,
|
||||
processClientID,
|
||||
s.processClientID,
|
||||
processFilteringBeforeRequest,
|
||||
s.processLocalPTR,
|
||||
s.processUpstream,
|
||||
@@ -165,7 +165,7 @@ func (s *Server) setTableHostToIP(t hostToIPTable) {
|
||||
s.tableHostToIP = t
|
||||
}
|
||||
|
||||
func (s *Server) setTableIPToHost(t ipToHostTable) {
|
||||
func (s *Server) setTableIPToHost(t *aghnet.IPMap) {
|
||||
s.tableIPToHostLock.Lock()
|
||||
defer s.tableIPToHostLock.Unlock()
|
||||
|
||||
@@ -188,13 +188,13 @@ func (s *Server) onDHCPLeaseChanged(flags int) {
|
||||
}
|
||||
|
||||
var hostToIP hostToIPTable
|
||||
var ipToHost ipToHostTable
|
||||
var ipToHost *aghnet.IPMap
|
||||
if add {
|
||||
hostToIP = make(hostToIPTable)
|
||||
ipToHost = make(ipToHostTable)
|
||||
|
||||
ll := s.dhcpServer.Leases(dhcpd.LeasesAll)
|
||||
|
||||
hostToIP = make(hostToIPTable, len(ll))
|
||||
ipToHost = aghnet.NewIPMap(len(ll))
|
||||
|
||||
for _, l := range ll {
|
||||
// TODO(a.garipov): Remove this after we're finished
|
||||
// with the client hostname validations in the DHCP
|
||||
@@ -210,14 +210,14 @@ func (s *Server) onDHCPLeaseChanged(flags int) {
|
||||
|
||||
lowhost := strings.ToLower(l.Hostname)
|
||||
|
||||
ipToHost[l.IP.String()] = lowhost
|
||||
ipToHost.Set(l.IP, lowhost)
|
||||
|
||||
ip := make(net.IP, 4)
|
||||
copy(ip, l.IP.To4())
|
||||
hostToIP[lowhost] = ip
|
||||
}
|
||||
|
||||
log.Debug("dns: added %d A/PTR entries from DHCP", len(ipToHost))
|
||||
log.Debug("dns: added %d A/PTR entries from DHCP", ipToHost.Len())
|
||||
}
|
||||
|
||||
s.setTableHostToIP(hostToIP)
|
||||
@@ -377,9 +377,19 @@ func (s *Server) ipToHost(ip net.IP) (host string, ok bool) {
|
||||
return "", false
|
||||
}
|
||||
|
||||
host, ok = s.tableIPToHost[ip.String()]
|
||||
var v interface{}
|
||||
v, ok = s.tableIPToHost.Get(ip)
|
||||
if !ok {
|
||||
return "", false
|
||||
}
|
||||
|
||||
return host, ok
|
||||
if host, ok = v.(string); !ok {
|
||||
log.Error("dns: bad type %T in tableIPToHost for %s", v, ip)
|
||||
|
||||
return "", false
|
||||
}
|
||||
|
||||
return host, true
|
||||
}
|
||||
|
||||
// Respond to PTR requests if the target IP is leased by our DHCP server and the
|
||||
@@ -508,7 +518,7 @@ func (s *Server) processUpstream(ctx *dnsContext) (rc resultCode) {
|
||||
|
||||
if d.Addr != nil && s.conf.GetCustomUpstreamByClient != nil {
|
||||
// Use the clientID first, since it has a higher priority.
|
||||
id := aghstrings.Coalesce(ctx.clientID, ipStringFromAddr(d.Addr))
|
||||
id := stringutil.Coalesce(ctx.clientID, ipStringFromAddr(d.Addr))
|
||||
upsConf, err := s.conf.GetCustomUpstreamByClient(id)
|
||||
if err != nil {
|
||||
log.Error("dns: getting custom upstreams for client %s: %s", id, err)
|
||||
|
||||
@@ -5,28 +5,33 @@ import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghstrings"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/dhcpd"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/querylog"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/stats"
|
||||
"github.com/AdguardTeam/dnsproxy/proxy"
|
||||
"github.com/AdguardTeam/dnsproxy/upstream"
|
||||
"github.com/AdguardTeam/golibs/cache"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/AdguardTeam/golibs/stringutil"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
// DefaultTimeout is the default upstream timeout
|
||||
const DefaultTimeout = 10 * time.Second
|
||||
|
||||
// defaultClientIDCacheCount is the default count of items in the LRU client ID
|
||||
// cache. The assumption here is that there won't be more than this many
|
||||
// requests between the BeforeRequestHandler stage and the actual processing.
|
||||
const defaultClientIDCacheCount = 1024
|
||||
|
||||
const (
|
||||
safeBrowsingBlockHost = "standard-block.dns.adguard.com"
|
||||
parentalBlockHost = "family-block.dns.adguard.com"
|
||||
@@ -45,12 +50,6 @@ var webRegistered bool
|
||||
// hostToIPTable is an alias for the type of Server.tableHostToIP.
|
||||
type hostToIPTable = map[string]net.IP
|
||||
|
||||
// ipToHostTable is an alias for the type of Server.tableIPToHost.
|
||||
//
|
||||
// TODO(a.garipov): Define an IPMap type in aghnet and use here and in other
|
||||
// places?
|
||||
type ipToHostTable = map[string]string
|
||||
|
||||
// Server is the main way to start a DNS server.
|
||||
//
|
||||
// Example:
|
||||
@@ -82,9 +81,13 @@ type Server struct {
|
||||
tableHostToIP hostToIPTable
|
||||
tableHostToIPLock sync.Mutex
|
||||
|
||||
tableIPToHost ipToHostTable
|
||||
tableIPToHost *aghnet.IPMap
|
||||
tableIPToHostLock sync.Mutex
|
||||
|
||||
// clientIDCache is a temporary storage for clientIDs that were
|
||||
// extracted during the BeforeRequestHandler stage.
|
||||
clientIDCache cache.Cache
|
||||
|
||||
// DNS proxy instance for internal usage
|
||||
// We don't Start() it and so no listen port is required.
|
||||
internalProxy *proxy.Proxy
|
||||
@@ -153,6 +156,10 @@ func NewServer(p DNSCreateParams) (s *Server, err error) {
|
||||
subnetDetector: p.SubnetDetector,
|
||||
localDomainSuffix: localDomainSuffix,
|
||||
recDetector: newRecursionDetector(recursionTTL, cachedRecurrentReqNum),
|
||||
clientIDCache: cache.New(cache.Config{
|
||||
EnableLRU: true,
|
||||
MaxCount: defaultClientIDCacheCount,
|
||||
}),
|
||||
}
|
||||
|
||||
// TODO(e.burkov): Enable the refresher after the actual implementation
|
||||
@@ -203,7 +210,7 @@ func (s *Server) Close() {
|
||||
s.queryLog = nil
|
||||
s.dnsProxy = nil
|
||||
|
||||
if err := s.ipset.Close(); err != nil {
|
||||
if err := s.ipset.close(); err != nil {
|
||||
log.Error("closing ipset: %s", err)
|
||||
}
|
||||
}
|
||||
@@ -215,12 +222,13 @@ func (s *Server) WriteDiskConfig(c *FilteringConfig) {
|
||||
|
||||
sc := s.conf.FilteringConfig
|
||||
*c = sc
|
||||
c.RatelimitWhitelist = aghstrings.CloneSlice(sc.RatelimitWhitelist)
|
||||
c.BootstrapDNS = aghstrings.CloneSlice(sc.BootstrapDNS)
|
||||
c.AllowedClients = aghstrings.CloneSlice(sc.AllowedClients)
|
||||
c.DisallowedClients = aghstrings.CloneSlice(sc.DisallowedClients)
|
||||
c.BlockedHosts = aghstrings.CloneSlice(sc.BlockedHosts)
|
||||
c.UpstreamDNS = aghstrings.CloneSlice(sc.UpstreamDNS)
|
||||
c.RatelimitWhitelist = stringutil.CloneSlice(sc.RatelimitWhitelist)
|
||||
c.BootstrapDNS = stringutil.CloneSlice(sc.BootstrapDNS)
|
||||
c.AllowedClients = stringutil.CloneSlice(sc.AllowedClients)
|
||||
c.DisallowedClients = stringutil.CloneSlice(sc.DisallowedClients)
|
||||
c.BlockedHosts = stringutil.CloneSlice(sc.BlockedHosts)
|
||||
c.TrustedProxies = stringutil.CloneSlice(sc.TrustedProxies)
|
||||
c.UpstreamDNS = stringutil.CloneSlice(sc.UpstreamDNS)
|
||||
}
|
||||
|
||||
// RDNSSettings returns the copy of actual RDNS configuration.
|
||||
@@ -228,7 +236,7 @@ func (s *Server) RDNSSettings() (localPTRResolvers []string, resolveClients, res
|
||||
s.serverLock.RLock()
|
||||
defer s.serverLock.RUnlock()
|
||||
|
||||
return aghstrings.CloneSlice(s.conf.LocalPTRResolvers),
|
||||
return stringutil.CloneSlice(s.conf.LocalPTRResolvers),
|
||||
s.conf.ResolveClients,
|
||||
s.conf.UsePrivateRDNS
|
||||
}
|
||||
@@ -390,13 +398,13 @@ func (s *Server) filterOurDNSAddrs(addrs []string) (filtered []string, err error
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ourAddrsSet := aghstrings.NewSet(ourAddrs...)
|
||||
ourAddrsSet := stringutil.NewSet(ourAddrs...)
|
||||
|
||||
// TODO(e.burkov): The approach of subtracting sets of strings is not
|
||||
// really applicable here since in case of listening on all network
|
||||
// interfaces we should check the whole interface's network to cut off
|
||||
// all the loopback addresses as well.
|
||||
return aghstrings.FilterOut(addrs, ourAddrsSet.Has), nil
|
||||
return stringutil.FilterOut(addrs, ourAddrsSet.Has), nil
|
||||
}
|
||||
|
||||
// setupResolvers initializes the resolvers for local addresses. For internal
|
||||
@@ -415,19 +423,22 @@ func (s *Server) setupResolvers(localAddrs []string) (err error) {
|
||||
|
||||
log.Debug("upstreams to resolve PTR for local addresses: %v", localAddrs)
|
||||
|
||||
var upsConfig proxy.UpstreamConfig
|
||||
upsConfig, err = proxy.ParseUpstreamsConfig(localAddrs, upstream.Options{
|
||||
Bootstrap: bootstraps,
|
||||
Timeout: defaultLocalTimeout,
|
||||
// TODO(e.burkov): Should we verify server's ceritificates?
|
||||
})
|
||||
var upsConfig *proxy.UpstreamConfig
|
||||
upsConfig, err = proxy.ParseUpstreamsConfig(
|
||||
localAddrs,
|
||||
&upstream.Options{
|
||||
Bootstrap: bootstraps,
|
||||
Timeout: defaultLocalTimeout,
|
||||
// TODO(e.burkov): Should we verify server's ceritificates?
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("parsing upstreams: %w", err)
|
||||
}
|
||||
|
||||
s.localResolvers = &proxy.Proxy{
|
||||
Config: proxy.Config{
|
||||
UpstreamConfig: &upsConfig,
|
||||
UpstreamConfig: upsConfig,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -451,26 +462,15 @@ func (s *Server) Prepare(config *ServerConfig) error {
|
||||
// --
|
||||
s.initDefaultSettings()
|
||||
|
||||
// Initialize IPSET configuration
|
||||
// Initialize ipset configuration
|
||||
// --
|
||||
err := s.ipset.init(s.conf.IPSETList)
|
||||
err := s.ipset.init(s.conf.IpsetList)
|
||||
if err != nil {
|
||||
if !errors.Is(err, os.ErrInvalid) && !errors.Is(err, os.ErrPermission) {
|
||||
return fmt.Errorf("cannot initialize ipset: %w", err)
|
||||
}
|
||||
|
||||
// ipset cannot currently be initialized if the server was
|
||||
// installed from Snap or when the user or the binary doesn't
|
||||
// have the required permissions, or when the kernel doesn't
|
||||
// support netfilter.
|
||||
//
|
||||
// Log and go on.
|
||||
//
|
||||
// TODO(a.garipov): The Snap problem can probably be solved if
|
||||
// we add the netlink-connector interface plug.
|
||||
log.Info("warning: cannot initialize ipset: %s", err)
|
||||
return err
|
||||
}
|
||||
|
||||
log.Debug("inited ipset")
|
||||
|
||||
// Prepare DNS servers settings
|
||||
// --
|
||||
err = s.prepareUpstreamSettings()
|
||||
@@ -589,11 +589,33 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
// IsBlockedIP - return TRUE if this client should be blocked
|
||||
func (s *Server) IsBlockedIP(ip net.IP) (bool, string) {
|
||||
if ip == nil {
|
||||
return false, ""
|
||||
// IsBlockedClient returns true if the client is blocked by the current access
|
||||
// settings.
|
||||
func (s *Server) IsBlockedClient(ip net.IP, clientID string) (blocked bool, rule string) {
|
||||
s.serverLock.RLock()
|
||||
defer s.serverLock.RUnlock()
|
||||
|
||||
allowlistMode := s.access.allowlistMode()
|
||||
blockedByIP, rule := s.access.isBlockedIP(ip)
|
||||
blockedByClientID := s.access.isBlockedClientID(clientID)
|
||||
|
||||
// Allow if at least one of the checks allows in allowlist mode, but
|
||||
// block if at least one of the checks blocks in blocklist mode.
|
||||
if allowlistMode && blockedByIP && blockedByClientID {
|
||||
log.Debug("client %s (id %q) is not in access allowlist", ip, clientID)
|
||||
|
||||
// Return now without substituting the empty rule for the
|
||||
// clientID because the rule can't be empty here.
|
||||
return true, rule
|
||||
} else if !allowlistMode && (blockedByIP || blockedByClientID) {
|
||||
log.Debug("client %s (id %q) is in access blocklist", ip, clientID)
|
||||
|
||||
blocked = true
|
||||
}
|
||||
|
||||
return s.access.IsBlockedIP(ip)
|
||||
if rule == "" {
|
||||
rule = clientID
|
||||
}
|
||||
|
||||
return blocked, rule
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user