Compare commits
91 Commits
3389-query
...
v0.108.0-b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2c9dcc12ff | ||
|
|
6fea7099a2 | ||
|
|
8fb76701f4 | ||
|
|
2b9019313b | ||
|
|
4b04c620f2 | ||
|
|
28cfde9212 | ||
|
|
31f7aaecc3 | ||
|
|
3722c28464 | ||
|
|
2bfc9fcb12 | ||
|
|
cb6d4620c5 | ||
|
|
4b4036fa6a | ||
|
|
762e5be97a | ||
|
|
ff341bd7cf | ||
|
|
05262d7b6b | ||
|
|
887c48cee8 | ||
|
|
85e87b9c1d | ||
|
|
c54635e8a1 | ||
|
|
94cf50a53d | ||
|
|
111005b8d4 | ||
|
|
418c830e53 | ||
|
|
1e939703e5 | ||
|
|
c47509fabc | ||
|
|
54aee22720 | ||
|
|
93a0601f41 | ||
|
|
5eb3cd0f92 | ||
|
|
c0691cab6a | ||
|
|
c0c152885d | ||
|
|
a6c5cab218 | ||
|
|
fe0edc0065 | ||
|
|
2cbc5e5f9d | ||
|
|
5d900bdaa4 | ||
|
|
79306cb48a | ||
|
|
300821a7fb | ||
|
|
5f8fa006cf | ||
|
|
9f3af37eb3 | ||
|
|
698b963e11 | ||
|
|
996c6b3ee3 | ||
|
|
ed86af582a | ||
|
|
ac2ecaf4f5 | ||
|
|
f9daf72c7e | ||
|
|
84a2991ac2 | ||
|
|
5be0e84719 | ||
|
|
4e8d3d7628 | ||
|
|
685d982924 | ||
|
|
b0185201c6 | ||
|
|
33ce24abe4 | ||
|
|
dee7c0681d | ||
|
|
7bfad08dde | ||
|
|
dead10e033 | ||
|
|
0cd441f04f | ||
|
|
2adc8624c0 | ||
|
|
f22d893845 | ||
|
|
de63eeabfa | ||
|
|
1aaffd1b72 | ||
|
|
eb97e7dc01 | ||
|
|
55335c4061 | ||
|
|
a79deda665 | ||
|
|
40884624c2 | ||
|
|
0a1887a854 | ||
|
|
65b526b969 | ||
|
|
61ed743748 | ||
|
|
c02a14117d | ||
|
|
7b92d53b84 | ||
|
|
f7dd832517 | ||
|
|
c21f958eaf | ||
|
|
5a195b441c | ||
|
|
c46516475d | ||
|
|
97af062f7b | ||
|
|
8004b135b1 | ||
|
|
4b9264531b | ||
|
|
9a506d3755 | ||
|
|
e320eb29c2 | ||
|
|
282f11a7c2 | ||
|
|
91f3e29c08 | ||
|
|
1fd6cf1a2f | ||
|
|
efed23701a | ||
|
|
ad1bf5cf6e | ||
|
|
ee8eb1d8a6 | ||
|
|
39f5c50acd | ||
|
|
d4a4bda645 | ||
|
|
2069eddf98 | ||
|
|
c652653ea4 | ||
|
|
6889837785 | ||
|
|
cf7c12c97b | ||
|
|
d88181343c | ||
|
|
e7e638443f | ||
|
|
2f515e8d8f | ||
|
|
e54fc9b1e9 | ||
|
|
66345e855e | ||
|
|
37e046acc4 | ||
|
|
f40ef76c79 |
60
.github/ISSUE_TEMPLATE/bug.yml
vendored
60
.github/ISSUE_TEMPLATE/bug.yml
vendored
@@ -32,31 +32,33 @@
|
||||
- 'attributes':
|
||||
'description': 'On which Platform does the issue occur?'
|
||||
'label': 'Platform (OS and CPU architecture)'
|
||||
# NOTE: Keep the 386 at the bottom for each OS, because a lot of people
|
||||
# Seem to confuse them with AMD64, which is what they actually need.
|
||||
'options':
|
||||
- 'Darwin (aka macOS)/AMD64 (aka x86_64)'
|
||||
- 'Darwin (aka macOS)/ARM64'
|
||||
- 'FreeBSD/386'
|
||||
- 'FreeBSD/AMD64 (aka x86_64)'
|
||||
- 'FreeBSD/ARM64'
|
||||
- 'FreeBSD/ARMv5'
|
||||
- 'FreeBSD/ARMv6'
|
||||
- 'FreeBSD/ARMv7'
|
||||
- 'Linux/386'
|
||||
- 'Linux/AMD64 (aka x86_64)'
|
||||
- 'Linux/ARM64'
|
||||
- 'Linux/ARMv5'
|
||||
- 'Linux/ARMv6'
|
||||
- 'Linux/ARMv7'
|
||||
- 'Linux/MIPS LE'
|
||||
- 'Linux/MIPS'
|
||||
- 'Linux/MIPS64 LE'
|
||||
- 'Linux/MIPS64'
|
||||
- 'Linux/PPC64 LE'
|
||||
- 'OpenBSD/AMD64 (aka x86_64)'
|
||||
- 'OpenBSD/ARM64'
|
||||
- 'Windows/386'
|
||||
- 'Windows/AMD64 (aka x86_64)'
|
||||
- 'Windows/ARM64'
|
||||
- 'Darwin (aka macOS), AMD64 (aka x86_64)'
|
||||
- 'Darwin (aka macOS), ARM64'
|
||||
- 'FreeBSD, AMD64 (aka x86_64)'
|
||||
- 'FreeBSD, ARM64'
|
||||
- 'FreeBSD, ARMv5'
|
||||
- 'FreeBSD, ARMv6'
|
||||
- 'FreeBSD, ARMv7'
|
||||
- 'FreeBSD, 32-bit Intel (aka 386)'
|
||||
- 'Linux, AMD64 (aka x86_64)'
|
||||
- 'Linux, ARM64'
|
||||
- 'Linux, ARMv5'
|
||||
- 'Linux, ARMv6'
|
||||
- 'Linux, ARMv7'
|
||||
- 'Linux, MIPS LE'
|
||||
- 'Linux, MIPS'
|
||||
- 'Linux, MIPS64 LE'
|
||||
- 'Linux, MIPS64'
|
||||
- 'Linux, PPC64 LE'
|
||||
- 'Linux, 32-bit Intel (aka 386)'
|
||||
- 'OpenBSD, AMD64 (aka x86_64)'
|
||||
- 'OpenBSD, ARM64'
|
||||
- 'Windows, AMD64 (aka x86_64)'
|
||||
- 'Windows, ARM64'
|
||||
- 'Windows, 32-bit Intel (aka 386)'
|
||||
- 'Custom (please mention in the description)'
|
||||
'id': 'os'
|
||||
'type': 'dropdown'
|
||||
@@ -142,8 +144,10 @@
|
||||
'type': 'textarea'
|
||||
'validations':
|
||||
'required': false
|
||||
'description': >
|
||||
Open a bug report. Please do not open bug reports for questions or help
|
||||
with configuring clients. If you want to ask for help, use the Discussions
|
||||
section.
|
||||
# NOTE: GitHub limits the description length to 200 characters. Also, Markdown
|
||||
# doesn't work here.
|
||||
'description': |
|
||||
For help, use the Discussions section instead. Write the title in English
|
||||
to make it easier for other people to search for duplicates. (Any language
|
||||
is fine in the body.)
|
||||
'name': 'Bug'
|
||||
|
||||
6
.github/ISSUE_TEMPLATE/feature.yml
vendored
6
.github/ISSUE_TEMPLATE/feature.yml
vendored
@@ -48,7 +48,11 @@
|
||||
'type': 'textarea'
|
||||
'validations':
|
||||
'required': false
|
||||
'description': 'Suggest a feature or an enhancement for AdGuard Home'
|
||||
# NOTE: GitHub limits the description length to 200 characters. Also, Markdown
|
||||
# doesn't work here.
|
||||
'description': |
|
||||
Write the title in English to make it easier for other people to search for
|
||||
duplicates. (Any language is fine in the body.)
|
||||
'labels':
|
||||
- 'feature request'
|
||||
'name': 'Feature request or enhancement'
|
||||
|
||||
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@@ -1,7 +1,7 @@
|
||||
'name': 'build'
|
||||
|
||||
'env':
|
||||
'GO_VERSION': '1.19.10'
|
||||
'GO_VERSION': '1.20.7'
|
||||
'NODE_VERSION': '14'
|
||||
|
||||
'on':
|
||||
|
||||
2
.github/workflows/lint.yml
vendored
2
.github/workflows/lint.yml
vendored
@@ -1,7 +1,7 @@
|
||||
'name': 'lint'
|
||||
|
||||
'env':
|
||||
'GO_VERSION': '1.19.10'
|
||||
'GO_VERSION': '1.20.7'
|
||||
|
||||
'on':
|
||||
'push':
|
||||
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -9,6 +9,7 @@
|
||||
*.db
|
||||
*.log
|
||||
*.snap
|
||||
*.test
|
||||
/agh-backup/
|
||||
/bin/
|
||||
/build/*
|
||||
@@ -16,10 +17,13 @@
|
||||
/dist/
|
||||
/filtering/tests/filtering.TestLotsOfRules*.pprof
|
||||
/filtering/tests/top-1m.csv
|
||||
/internal/next/AdGuardHome.yaml
|
||||
/launchpad_credentials
|
||||
/querylog.json*
|
||||
/snapcraft_login
|
||||
AdGuardHome*
|
||||
AdGuardHome
|
||||
AdGuardHome.exe
|
||||
AdGuardHome.yaml*
|
||||
coverage.txt
|
||||
node_modules/
|
||||
|
||||
|
||||
@@ -835,7 +835,7 @@ Request:
|
||||
Response:
|
||||
|
||||
200 OK
|
||||
|
||||
|
||||
### API: Validate TLS configuration
|
||||
|
||||
Request:
|
||||
@@ -2008,7 +2008,7 @@ Request:
|
||||
Response:
|
||||
|
||||
200 OK
|
||||
|
||||
|
||||
DOH plist file
|
||||
|
||||
## API: Get DNS over TLS .mobileconfig
|
||||
|
||||
324
CHANGELOG.md
324
CHANGELOG.md
@@ -14,32 +14,298 @@ and this project adheres to
|
||||
<!--
|
||||
## [v0.108.0] - TBA
|
||||
|
||||
## [v0.107.33] - 2023-06-28 (APPROX.)
|
||||
## [v0.107.37] - 2023-08-16 (APPROX.)
|
||||
|
||||
See also the [v0.107.33 GitHub milestone][ms-v0.107.33].
|
||||
See also the [v0.107.37 GitHub milestone][ms-v0.107.37].
|
||||
|
||||
[ms-v0.107.33]: https://github.com/AdguardTeam/AdGuardHome/milestone/68?closed=1
|
||||
[ms-v0.107.37]: https://github.com/AdguardTeam/AdGuardHome/milestone/72?closed=1
|
||||
|
||||
NOTE: Add new changes BELOW THIS COMMENT.
|
||||
-->
|
||||
|
||||
### Added
|
||||
|
||||
- The new HTTP API, `GET /control/querylog/export`, which can be used to
|
||||
export query log items. See `openapi/openapi.yaml` for the full description
|
||||
([#3389]).
|
||||
- The ability to set inactivity periods for filtering blocked services in the
|
||||
configuration file ([#951]). The UI changes are coming in the upcoming
|
||||
releases.
|
||||
- IPv6 hints are now filtered in case IPv6 addresses resolving is disabled
|
||||
([#6122]).
|
||||
- The ability to set fallback DNS servers in the configuration file ([#3701]).
|
||||
- While adding or updating blocklists, the title can now be parsed from
|
||||
`! Title:` definition of the blocklist's source ([#6020]).
|
||||
- The ability to filter DNS HTTPS records including IPv4/v6 hints ([#6053]).
|
||||
- Two new metrics showing total number of responses from each upstream DNS
|
||||
server and their average processing time in the Web UI ([#1453]).
|
||||
- The ability to set the port for the `pprof` debug API, see configuration
|
||||
changes below.
|
||||
|
||||
### Changed
|
||||
|
||||
- `$dnsrewrite` rules containing IPv4-mapped IPv6 addresses are now working
|
||||
consistently with legacy DNS rewrites and match the `AAAA` requests.
|
||||
- For non-A and non-AAAA requests, which has been filtered, the NODATA response
|
||||
is returned if the blocking mode isn't set to `Null IP`. In previous versions
|
||||
it returned NXDOMAIN response in such cases.
|
||||
|
||||
#### Configuration Changes
|
||||
|
||||
In this release, the schema version has changed from 24 to 25.
|
||||
|
||||
- Property `debug_pprof` which used to setup profiling HTTP handler, is now
|
||||
moved to the new `pprof` object under `http` section. The new object contains
|
||||
properties `enabled` and `port`:
|
||||
|
||||
```yaml
|
||||
# BEFORE:
|
||||
'debug_pprof': true
|
||||
|
||||
# AFTER:
|
||||
'http':
|
||||
'pprof':
|
||||
'enabled': true
|
||||
'port': 6060
|
||||
```
|
||||
|
||||
Note that the new default `6060` is used as default. To rollback this change,
|
||||
remove the new object `pprof`, set back `debug_pprof`, and change the
|
||||
`schema_version` back to `24`.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Occasional DNS-over-QUIC and DNS-over-HTTP/3 errors ([#6133]).
|
||||
- Legacy DNS rewrites containing IPv4-mapped IPv6 addresses are now matching the
|
||||
`AAAA` requests, not `A` ([#6050]).
|
||||
- File log configuration, such as `max_size`, being ignored ([#6093]).
|
||||
- Panic on using a single-slash filtering rule.
|
||||
- Panic on shutting down while DNS requests are in process of filtering
|
||||
([#5948]).
|
||||
|
||||
[#1453]: https://github.com/AdguardTeam/AdGuardHome/issues/1453
|
||||
[#3701]: https://github.com/AdguardTeam/AdGuardHome/issues/3701
|
||||
[#5948]: https://github.com/AdguardTeam/AdGuardHome/issues/5948
|
||||
[#6020]: https://github.com/AdguardTeam/AdGuardHome/issues/6020
|
||||
[#6050]: https://github.com/AdguardTeam/AdGuardHome/issues/6050
|
||||
[#6053]: https://github.com/AdguardTeam/AdGuardHome/issues/6053
|
||||
[#6093]: https://github.com/AdguardTeam/AdGuardHome/issues/6093
|
||||
[#6122]: https://github.com/AdguardTeam/AdGuardHome/issues/6122
|
||||
[#6133]: https://github.com/AdguardTeam/AdGuardHome/issues/6133
|
||||
|
||||
<!--
|
||||
NOTE: Add new changes ABOVE THIS COMMENT.
|
||||
-->
|
||||
|
||||
|
||||
|
||||
## [v0.107.36] - 2023-08-02
|
||||
|
||||
See also the [v0.107.36 GitHub milestone][ms-v0.107.36].
|
||||
|
||||
### Security
|
||||
|
||||
- Go version has been updated to prevent the possibility of exploiting the
|
||||
CVE-2023-29409 Go vulnerability fixed in [Go 1.20.7][go-1.20.7].
|
||||
|
||||
### Deprecated
|
||||
|
||||
- Go 1.20 support. Future versions will require at least Go 1.21 to build.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Inability to block queries for the root domain, such as `NS .` queries, using
|
||||
the *Disallowed domains* feature on the *DNS settings* page ([#6049]). Users
|
||||
who want to block `.` queries should use the `|.^` AdBlock rule or a similar
|
||||
regular expression.
|
||||
- Client hostnames not resolving when upstream server responds with zero-TTL
|
||||
records ([#6046]).
|
||||
|
||||
[#6046]: https://github.com/AdguardTeam/AdGuardHome/issues/6046
|
||||
[#6049]: https://github.com/AdguardTeam/AdGuardHome/issues/6049
|
||||
|
||||
[go-1.20.7]: https://groups.google.com/g/golang-announce/c/X0b6CsSAaYI/m/Efv5DbZ9AwAJ
|
||||
[ms-v0.107.36]: https://github.com/AdguardTeam/AdGuardHome/milestone/71?closed=1
|
||||
|
||||
|
||||
|
||||
## [v0.107.35] - 2023-07-26
|
||||
|
||||
See also the [v0.107.35 GitHub milestone][ms-v0.107.35].
|
||||
|
||||
### Changed
|
||||
|
||||
- Improved reliability filtering-rule list updates on Unix systems.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Occasional client information lookup failures that could lead to the DNS
|
||||
server getting stuck ([#6006]).
|
||||
- `bufio.Scanner: token too long` and other errors when trying to add
|
||||
filtering-rule lists with lines over 1024 bytes long or containing cosmetic
|
||||
rules ([#6003]).
|
||||
|
||||
### Removed
|
||||
|
||||
- Default exposure of the non-standard ports 784 and 8853 for DNS-over-QUIC in
|
||||
the `Dockerfile`.
|
||||
|
||||
[#6003]: https://github.com/AdguardTeam/AdGuardHome/issues/6003
|
||||
[#6006]: https://github.com/AdguardTeam/AdGuardHome/issues/6006
|
||||
|
||||
[ms-v0.107.35]: https://github.com/AdguardTeam/AdGuardHome/milestone/70?closed=1
|
||||
|
||||
|
||||
|
||||
## [v0.107.34] - 2023-07-12
|
||||
|
||||
See also the [v0.107.34 GitHub milestone][ms-v0.107.34].
|
||||
|
||||
### Security
|
||||
|
||||
- Go version has been updated to prevent the possibility of exploiting the
|
||||
CVE-2023-29406 Go vulnerability fixed in [Go 1.19.11][go-1.19.11].
|
||||
|
||||
### Added
|
||||
|
||||
- Ability to ignore queries for the root domain, such as `NS .` queries
|
||||
([#5990]).
|
||||
|
||||
### Changed
|
||||
|
||||
- Improved CPU and RAM consumption during updates of filtering-rule lists.
|
||||
|
||||
#### Configuration Changes
|
||||
|
||||
In this release, the schema version has changed from 23 to 24.
|
||||
|
||||
- Properties starting with `log_`, and `verbose` property, which used to set up
|
||||
logging are now moved to the new object `log` containing new properties
|
||||
`file`, `max_backups`, `max_size`, `max_age`, `compress`, `local_time`, and
|
||||
`verbose`:
|
||||
|
||||
```yaml
|
||||
# BEFORE:
|
||||
'log_file': ""
|
||||
'log_max_backups': 0
|
||||
'log_max_size': 100
|
||||
'log_max_age': 3
|
||||
'log_compress': false
|
||||
'log_localtime': false
|
||||
'verbose': false
|
||||
|
||||
# AFTER:
|
||||
'log':
|
||||
'file': ""
|
||||
'max_backups': 0
|
||||
'max_size': 100
|
||||
'max_age': 3
|
||||
'compress': false
|
||||
'local_time': false
|
||||
'verbose': false
|
||||
```
|
||||
|
||||
To rollback this change, remove the new object `log`, set back `log_` and
|
||||
`verbose` properties and change the `schema_version` back to `23`.
|
||||
|
||||
### Deprecated
|
||||
|
||||
- Default exposure of the non-standard ports 784 and 8853 for DNS-over-QUIC in
|
||||
the `Dockerfile`.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Two unspecified IPs when a host is blocked in two filter lists ([#5972]).
|
||||
- Incorrect setting of Parental Control cache size.
|
||||
- Excessive RAM and CPU consumption by Safe Browsing and Parental Control
|
||||
filters ([#5896]).
|
||||
|
||||
### Removed
|
||||
|
||||
- The `HEALTHCHECK` section and the use of `tini` in the `ENTRYPOINT` section in
|
||||
`Dockerfile` ([#5939]). They caused a lot of issues, especially with tools
|
||||
like `docker-compose` and `podman`.
|
||||
|
||||
**NOTE:** Some Docker tools may cache `ENTRYPOINT` sections, so some users may
|
||||
be required to backup their configuration, stop the container, purge the old
|
||||
image, and reload it from scratch.
|
||||
|
||||
[#5896]: https://github.com/AdguardTeam/AdGuardHome/issues/5896
|
||||
[#5972]: https://github.com/AdguardTeam/AdGuardHome/issues/5972
|
||||
[#5990]: https://github.com/AdguardTeam/AdGuardHome/issues/5990
|
||||
|
||||
[go-1.19.11]: https://groups.google.com/g/golang-announce/c/2q13H6LEEx0/m/sduSepLLBwAJ
|
||||
[ms-v0.107.34]: https://github.com/AdguardTeam/AdGuardHome/milestone/69?closed=1
|
||||
|
||||
|
||||
|
||||
## [v0.107.33] - 2023-07-03
|
||||
|
||||
See also the [v0.107.33 GitHub milestone][ms-v0.107.33].
|
||||
|
||||
### Added
|
||||
|
||||
- The new command-line flag `--web-addr` is the address to serve the web UI on,
|
||||
in the host:port format.
|
||||
- The ability to set inactivity periods for filtering blocked services, both
|
||||
globally and per client, in the configuration file ([#951]). The UI changes
|
||||
are coming in the upcoming releases.
|
||||
- The ability to edit rewrite rules via `PUT /control/rewrite/update` HTTP API
|
||||
([#1577]).
|
||||
and the Web UI ([#1577]).
|
||||
|
||||
### Changed
|
||||
|
||||
#### Configuration Changes
|
||||
|
||||
In this release, the schema version has changed from 20 to 21.
|
||||
In this release, the schema version has changed from 20 to 23.
|
||||
|
||||
- Properties `bind_host`, `bind_port`, and `web_session_ttl` which used to setup
|
||||
web UI binding configuration, are now moved to a new object `http` containing
|
||||
new properties `address` and `session_ttl`:
|
||||
|
||||
```yaml
|
||||
# BEFORE:
|
||||
'bind_host': '1.2.3.4'
|
||||
'bind_port': 8080
|
||||
'web_session_ttl': 720
|
||||
|
||||
# AFTER:
|
||||
'http':
|
||||
'address': '1.2.3.4:8080'
|
||||
'session_ttl': '720h'
|
||||
```
|
||||
|
||||
Note that the new `http.session_ttl` property is now a duration string. To
|
||||
rollback this change, remove the new object `http`, set back `bind_host`,
|
||||
`bind_port`, `web_session_ttl`, and change the `schema_version` back to `22`.
|
||||
- Property `clients.persistent.blocked_services`, which in schema versions 21
|
||||
and earlier used to be a list containing ids of blocked services, is now an
|
||||
object containing ids and schedule for blocked services:
|
||||
|
||||
```yaml
|
||||
# BEFORE:
|
||||
'clients':
|
||||
'persistent':
|
||||
- 'name': 'client-name'
|
||||
'blocked_services':
|
||||
- id_1
|
||||
- id_2
|
||||
|
||||
# AFTER:
|
||||
'clients':
|
||||
'persistent':
|
||||
- 'name': client-name
|
||||
'blocked_services':
|
||||
'ids':
|
||||
- id_1
|
||||
- id_2
|
||||
'schedule':
|
||||
'time_zone': 'Local'
|
||||
'sun':
|
||||
'start': '0s'
|
||||
'end': '24h'
|
||||
'mon':
|
||||
'start': '1h'
|
||||
'end': '23h'
|
||||
```
|
||||
|
||||
To rollback this change, replace `clients.persistent.blocked_services` object
|
||||
with the list of ids of blocked services and change the `schema_version` back
|
||||
to `21`.
|
||||
- Property `dns.blocked_services`, which in schema versions 20 and earlier used
|
||||
to be a list containing ids of blocked services, is now an object containing
|
||||
ids and schedule for blocked services:
|
||||
@@ -83,8 +349,23 @@ In this release, the schema version has changed from 20 to 21.
|
||||
To rollback this change, replace `dns.blocked_services` object with the list
|
||||
of ids of blocked services and change the `schema_version` back to `20`.
|
||||
|
||||
### Deprecated
|
||||
|
||||
- The `HEALTHCHECK` section and the use of `tini` in the `ENTRYPOINT` section in
|
||||
`Dockerfile` ([#5939]). They cause a lot of issues, especially with tools
|
||||
like `docker-compose` and `podman`, and will be removed in a future release.
|
||||
- Flags `-h`, `--host`, `-p`, `--port` have been deprecated. The `-h` flag
|
||||
will work as an alias for `--help`, instead of the deprecated `--host` in the
|
||||
future releases.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Ignoring of `/etc/hosts` file when resolving the hostnames of upstream DNS
|
||||
servers ([#5902]).
|
||||
- Excessive error logging when using DNS-over-QUIC ([#5285]).
|
||||
- Inability to set `bind_host` in `AdGuardHome.yaml` in Docker ([#4231],
|
||||
[#4235]).
|
||||
- The blocklists can now be deleted properly ([#5700]).
|
||||
- Queries with the question-section target `.`, for example `NS .`, are now
|
||||
counted in the statistics and correctly shown in the query log ([#5910]).
|
||||
- Safe Search not working with `AAAA` queries for domains that don't have `AAAA`
|
||||
@@ -92,13 +373,16 @@ In this release, the schema version has changed from 20 to 21.
|
||||
|
||||
[#951]: https://github.com/AdguardTeam/AdGuardHome/issues/951
|
||||
[#1577]: https://github.com/AdguardTeam/AdGuardHome/issues/1577
|
||||
[#3389]: https://github.com/AdguardTeam/AdGuardHome/issues/3389
|
||||
[#4231]: https://github.com/AdguardTeam/AdGuardHome/issues/4231
|
||||
[#4235]: https://github.com/AdguardTeam/AdGuardHome/pull/4235
|
||||
[#5285]: https://github.com/AdguardTeam/AdGuardHome/issues/5285
|
||||
[#5700]: https://github.com/AdguardTeam/AdGuardHome/issues/5700
|
||||
[#5902]: https://github.com/AdguardTeam/AdGuardHome/issues/5902
|
||||
[#5910]: https://github.com/AdguardTeam/AdGuardHome/issues/5910
|
||||
[#5913]: https://github.com/AdguardTeam/AdGuardHome/issues/5913
|
||||
[#5939]: https://github.com/AdguardTeam/AdGuardHome/discussions/5939
|
||||
|
||||
<!--
|
||||
NOTE: Add new changes ABOVE THIS COMMENT.
|
||||
-->
|
||||
[ms-v0.107.33]: https://github.com/AdguardTeam/AdGuardHome/milestone/68?closed=1
|
||||
|
||||
|
||||
|
||||
@@ -2080,11 +2364,15 @@ See also the [v0.104.2 GitHub milestone][ms-v0.104.2].
|
||||
|
||||
|
||||
<!--
|
||||
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.33...HEAD
|
||||
[v0.107.33]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.32...v0.107.33
|
||||
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.37...HEAD
|
||||
[v0.107.37]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.36...v0.107.37
|
||||
-->
|
||||
|
||||
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.32...HEAD
|
||||
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.36...HEAD
|
||||
[v0.107.36]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.35...v0.107.36
|
||||
[v0.107.35]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.34...v0.107.35
|
||||
[v0.107.34]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.33...v0.107.34
|
||||
[v0.107.33]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.32...v0.107.33
|
||||
[v0.107.32]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.31...v0.107.32
|
||||
[v0.107.31]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.30...v0.107.31
|
||||
[v0.107.30]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.29...v0.107.30
|
||||
|
||||
11
Makefile
11
Makefile
@@ -78,7 +78,7 @@ build: deps quick-build
|
||||
|
||||
quick-build: js-build go-build
|
||||
|
||||
ci: deps test
|
||||
ci: deps test go-bench go-fuzz
|
||||
|
||||
deps: js-deps go-deps
|
||||
lint: js-lint go-lint
|
||||
@@ -104,8 +104,10 @@ js-deps:
|
||||
js-lint: ; $(NPM) $(NPM_FLAGS) run lint
|
||||
js-test: ; $(NPM) $(NPM_FLAGS) run test
|
||||
|
||||
go-bench: ; $(ENV) "$(SHELL)" ./scripts/make/go-bench.sh
|
||||
go-build: ; $(ENV) "$(SHELL)" ./scripts/make/go-build.sh
|
||||
go-deps: ; $(ENV) "$(SHELL)" ./scripts/make/go-deps.sh
|
||||
go-fuzz: ; $(ENV) "$(SHELL)" ./scripts/make/go-fuzz.sh
|
||||
go-lint: ; $(ENV) "$(SHELL)" ./scripts/make/go-lint.sh
|
||||
go-tools: ; $(ENV) "$(SHELL)" ./scripts/make/go-tools.sh
|
||||
|
||||
@@ -128,3 +130,10 @@ openapi-lint: ; cd ./openapi/ && $(YARN) test
|
||||
openapi-show: ; cd ./openapi/ && $(YARN) start
|
||||
|
||||
txt-lint: ; $(ENV) "$(SHELL)" ./scripts/make/txt-lint.sh
|
||||
|
||||
# TODO(a.garipov): Consider adding to scripts/ and the common project
|
||||
# structure.
|
||||
go-upd-tools:
|
||||
cd ./internal/tools/ &&\
|
||||
"$(GO.MACRO)" get -u &&\
|
||||
"$(GO.MACRO)" mod tidy
|
||||
|
||||
@@ -54,7 +54,7 @@ code.
|
||||
|
||||
|
||||
* [Getting Started](#getting-started)
|
||||
* [Automated install (Unix)](#automated-install-linux-and-mac)
|
||||
* [Automated install (Linux/Unix/MacOS/FreeBSD/OpenBSD)](#automated-install-linux-and-mac)
|
||||
* [Alternative methods](#alternative-methods)
|
||||
* [Guides](#guides)
|
||||
* [API](#api)
|
||||
@@ -79,7 +79,7 @@ code.
|
||||
|
||||
## <a href="#getting-started" id="getting-started" name="getting-started">Getting Started</a>
|
||||
|
||||
### <a href="#automated-install-linux-and-mac" id="automated-install-linux-and-mac" name="automated-install-linux-and-mac">Automated install (Unix)</a>
|
||||
### <a href="#automated-install-linux-and-mac" id="automated-install-linux-and-mac" name="automated-install-linux-and-mac">Automated install (Linux/Unix/MacOS/FreeBSD/OpenBSD)</a>
|
||||
|
||||
To install with `curl` run the following command:
|
||||
|
||||
@@ -261,7 +261,7 @@ Run `make init` to prepare the development environment.
|
||||
|
||||
You will need this to build AdGuard Home:
|
||||
|
||||
* [Go](https://golang.org/dl/) v1.19 or later;
|
||||
* [Go](https://golang.org/dl/) v1.20 or later;
|
||||
* [Node.js](https://nodejs.org/en/download/) v10.16.2 or later;
|
||||
* [npm](https://www.npmjs.com/) v6.14 or later;
|
||||
* [yarn](https://yarnpkg.com/) v1.22.5 or later.
|
||||
@@ -416,7 +416,8 @@ There are three options how you can install an unstable version:
|
||||
### <a href="#reporting-issues" id="reporting-issues" name="reporting-issues">Report issues</a>
|
||||
|
||||
If you run into any problem or have a suggestion, head to [this page][iss] and
|
||||
click on the “New issue” button.
|
||||
click on the “New issue” button. Please follow the instructions in the issue
|
||||
form carefully and don't forget to start by searching for duplicates.
|
||||
|
||||
[iss]: https://github.com/AdguardTeam/AdGuardHome/issues
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
# Make sure to sync any changes with the branch overrides below.
|
||||
'variables':
|
||||
'channel': 'edge'
|
||||
'dockerGo': 'adguard/golang-ubuntu:6.7'
|
||||
'dockerGo': 'adguard/golang-ubuntu:7.0'
|
||||
|
||||
'stages':
|
||||
- 'Build frontend':
|
||||
@@ -272,7 +272,7 @@
|
||||
# need to build a few of these.
|
||||
'variables':
|
||||
'channel': 'beta'
|
||||
'dockerGo': 'adguard/golang-ubuntu:6.7'
|
||||
'dockerGo': 'adguard/golang-ubuntu:7.0'
|
||||
# release-vX.Y.Z branches are the branches from which the actual final
|
||||
# release is built.
|
||||
- '^release-v[0-9]+\.[0-9]+\.[0-9]+':
|
||||
@@ -287,4 +287,4 @@
|
||||
# are the ones that actually get released.
|
||||
'variables':
|
||||
'channel': 'release'
|
||||
'dockerGo': 'adguard/golang-ubuntu:6.7'
|
||||
'dockerGo': 'adguard/golang-ubuntu:7.0'
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
# Make sure to sync any changes with the branch overrides below.
|
||||
'variables':
|
||||
'channel': 'edge'
|
||||
'dockerGo': 'adguard/golang-ubuntu:6.7'
|
||||
'dockerGo': 'adguard/golang-ubuntu:7.0'
|
||||
'snapcraftChannel': 'edge'
|
||||
|
||||
'stages':
|
||||
@@ -191,7 +191,7 @@
|
||||
# need to build a few of these.
|
||||
'variables':
|
||||
'channel': 'beta'
|
||||
'dockerGo': 'adguard/golang-ubuntu:6.7'
|
||||
'dockerGo': 'adguard/golang-ubuntu:7.0'
|
||||
'snapcraftChannel': 'beta'
|
||||
# release-vX.Y.Z branches are the branches from which the actual final
|
||||
# release is built.
|
||||
@@ -207,5 +207,5 @@
|
||||
# are the ones that actually get released.
|
||||
'variables':
|
||||
'channel': 'release'
|
||||
'dockerGo': 'adguard/golang-ubuntu:6.7'
|
||||
'dockerGo': 'adguard/golang-ubuntu:7.0'
|
||||
'snapcraftChannel': 'candidate'
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
'key': 'AHBRTSPECS'
|
||||
'name': 'AdGuard Home - Build and run tests'
|
||||
'variables':
|
||||
'dockerGo': 'adguard/golang-ubuntu:6.7'
|
||||
'dockerGo': 'adguard/golang-ubuntu:7.0'
|
||||
|
||||
'stages':
|
||||
- 'Tests':
|
||||
@@ -14,6 +14,12 @@
|
||||
'jobs':
|
||||
- 'Test'
|
||||
|
||||
- 'Artifact':
|
||||
manual: false
|
||||
final: false
|
||||
jobs:
|
||||
- 'Artifact'
|
||||
|
||||
'Test':
|
||||
'docker':
|
||||
'image': '${bamboo.dockerGo}'
|
||||
@@ -41,6 +47,53 @@
|
||||
'requirements':
|
||||
- 'adg-docker': 'true'
|
||||
|
||||
'Artifact':
|
||||
'docker':
|
||||
'image': '${bamboo.dockerGo}'
|
||||
'volumes':
|
||||
'${system.GO_CACHE_DIR}': '${bamboo.cacheGo}'
|
||||
'${system.GO_PKG_CACHE_DIR}': '${bamboo.cacheGoPkg}'
|
||||
'key': 'ART'
|
||||
'other':
|
||||
'clean-working-dir': true
|
||||
'tasks':
|
||||
- 'checkout':
|
||||
'force-clean-build': true
|
||||
- 'script':
|
||||
'interpreter': 'SHELL'
|
||||
'scripts':
|
||||
- |-
|
||||
#!/bin/sh
|
||||
|
||||
set -e -f -u -x
|
||||
|
||||
# Explicitly checkout the revision that we need.
|
||||
git checkout "${bamboo.repository.revision.number}"
|
||||
|
||||
make\
|
||||
ARCH="amd64"\
|
||||
OS="windows darwin linux"\
|
||||
CHANNEL="development"\
|
||||
SIGN=0\
|
||||
PARALLELISM=1\
|
||||
VERBOSE=2\
|
||||
build-release
|
||||
'artifacts':
|
||||
- 'name': 'AdGuardHome_windows_amd64'
|
||||
'pattern': 'dist/AdGuardHome_windows_amd64.zip'
|
||||
'shared': true
|
||||
'required': true
|
||||
- 'name': 'AdGuardHome_darwin_amd64'
|
||||
'pattern': 'dist/AdGuardHome_darwin_amd64.zip'
|
||||
'shared': true
|
||||
'required': true
|
||||
- 'name': 'AdGuardHome_linux_amd64'
|
||||
'pattern': 'dist/AdGuardHome_linux_amd64.tar.gz'
|
||||
'shared': true
|
||||
'required': true
|
||||
'requirements':
|
||||
- 'adg-docker': 'true'
|
||||
|
||||
'branches':
|
||||
'create': 'for-pull-request'
|
||||
'delete':
|
||||
|
||||
@@ -125,6 +125,8 @@
|
||||
"top_clients": "كبار العملاء",
|
||||
"no_clients_found": "لم يتم العثور على عملاء",
|
||||
"general_statistics": "الإحصاءات العامة",
|
||||
"top_upstreams": "أعلى الخوادم upstream",
|
||||
"no_upstreams_data_found": "لم يتم العثور على بيانات خوادم upstream",
|
||||
"number_of_dns_query_days": "عدد استعلامات DNS التي تمت معالجتها لآخر {{count}} يوم",
|
||||
"number_of_dns_query_days_plural": "عدد استعلامات DNS التي تمت معالجتها لآخر {{count}} أيام",
|
||||
"number_of_dns_query_24_hours": "عدد استعلامات DNS التي تمت معالجتها لآخر 24 ساعة",
|
||||
@@ -158,6 +160,7 @@
|
||||
"upstream_dns_configured_in_file": "تم اعداده في {{path}}",
|
||||
"test_upstream_btn": "اختبار upstream",
|
||||
"upstreams": "Upstreams",
|
||||
"upstream": "Upstream",
|
||||
"apply_btn": "تطبيق",
|
||||
"disabled_filtering_toast": "تم تعطيل الفلترة",
|
||||
"enabled_filtering_toast": "تم تمكين الفلترة",
|
||||
@@ -212,6 +215,7 @@
|
||||
"example_upstream_udp": "regular DNS (over UDP, hostname);",
|
||||
"example_upstream_dot": "مشفر<0>DNS-over-TLS</0>;",
|
||||
"example_upstream_doh": "مشفر <0>DNS-over-HTTPS</0>;",
|
||||
"example_upstream_doh3": "DNS-over-HTTPS المشفر مع فرض <0> HTTP / 3</0> ولا يوجد رجوع إلى HTTP / 2 أو أقل ؛",
|
||||
"example_upstream_doq": "encrypted <0>DNS-over-QUIC</0>;",
|
||||
"example_upstream_sdns": "<0>DNS Stamps</0> for <1>DNSCrypt</1> or <2>DNS-over-HTTPS</2> resolvers;",
|
||||
"example_upstream_tcp": "regular DNS (over TCP);",
|
||||
|
||||
@@ -125,6 +125,8 @@
|
||||
"top_clients": "Частыя кліенты",
|
||||
"no_clients_found": "Кліентаў не знойдзена",
|
||||
"general_statistics": "Агульная статыстыка",
|
||||
"top_upstreams": "Часта запытаныя upstream серверы",
|
||||
"no_upstreams_data_found": "Няма дадзеных аб upstream серверах",
|
||||
"number_of_dns_query_days": "Колькасць DNS-запытаў за апошні {{count}} дзень",
|
||||
"number_of_dns_query_days_plural": "Колькасць DNS запытаў, апрацаваных за апошнія {{count}} дзён",
|
||||
"number_of_dns_query_24_hours": "Колькасць DNS-запытаў за 24 гадзіны",
|
||||
@@ -158,6 +160,7 @@
|
||||
"upstream_dns_configured_in_file": "Наладжаны ў {{path}}",
|
||||
"test_upstream_btn": "Тэст upstream сервераў",
|
||||
"upstreams": "Upstreams",
|
||||
"upstream": "Upstream сервер",
|
||||
"apply_btn": "Ужыць",
|
||||
"disabled_filtering_toast": "Фільтрацыя выкл.",
|
||||
"enabled_filtering_toast": "Фільтрацыя ўкл.",
|
||||
@@ -475,7 +478,9 @@
|
||||
"setup_dns_notice": "Каб выкарыстоўваць <1>DNS-over-HTTPS</1> ці <1>DNS-over-TLS</1>, вам патрэбна <0>наладзіць шыфраванне</0> у наладах AdGuard Home.",
|
||||
"rewrite_added": "Правіла перанакіравання DNS для «{{key}}» паспяхова дададзена",
|
||||
"rewrite_deleted": "Правіла перанакіравання DNS для «{{key}}» паспяхова выдалена",
|
||||
"rewrite_updated": "Перазапіс DNS паспяхова абноўлены",
|
||||
"rewrite_add": "Дадаць правіла перанакіравання DNS",
|
||||
"rewrite_edit": "Рэдагаваць перазапіс DNS",
|
||||
"rewrite_not_found": "Не знойдзена правілаў перанакіравання DNS",
|
||||
"rewrite_confirm_delete": "Вы ўпэўнены, што хочаце выдаліць правіла перанакіравання DNS для «{{key}}»?",
|
||||
"rewrite_desc": "Дазваляе лёгка наладзіць карыстацкі DNS-адказ для пэўнага дамена.",
|
||||
|
||||
@@ -125,6 +125,8 @@
|
||||
"top_clients": "Nejčastější klienti",
|
||||
"no_clients_found": "Nenalezeny žádní klienti",
|
||||
"general_statistics": "Obecné statistiky",
|
||||
"top_upstreams": "Top odchozí připojení",
|
||||
"no_upstreams_data_found": "Nebyla nalezena žádná data odchozích připojení",
|
||||
"number_of_dns_query_days": "Počet DNS dotazů zpracovaných za posledních {{count}} den",
|
||||
"number_of_dns_query_days_plural": "Počet DNS dotazů zpracovaných za posledních {{count}} dní",
|
||||
"number_of_dns_query_24_hours": "Počet DNS dotazů zpracovaných za posledních 24 hodin",
|
||||
@@ -134,6 +136,7 @@
|
||||
"enforced_save_search": "Vynucené bezpečné vyhledávání",
|
||||
"number_of_dns_query_to_safe_search": "Počet požadavků DNS na vyhledávače, při kterých bylo vynucené bezpečné vyhledávání",
|
||||
"average_processing_time": "Průměrný čas zpracování",
|
||||
"processing_time": "Doba zpracování",
|
||||
"average_processing_time_hint": "Průměrný čas zpracování požadavků DNS v milisekundách",
|
||||
"block_domain_use_filters_and_hosts": "Blokovat domény pomocí filtrů a seznamů adres",
|
||||
"filters_block_toggle_hint": "Pravidla blokování můžete nastavit v nastavení <a>Filtry</a>.",
|
||||
@@ -158,6 +161,7 @@
|
||||
"upstream_dns_configured_in_file": "Konfigurováno v {{path}}",
|
||||
"test_upstream_btn": "Test upstreamů",
|
||||
"upstreams": "Odesláno",
|
||||
"upstream": "Odchozí připojení",
|
||||
"apply_btn": "Použít",
|
||||
"disabled_filtering_toast": "Vypnuté filtrování",
|
||||
"enabled_filtering_toast": "Zapnuté filtrování",
|
||||
@@ -444,7 +448,7 @@
|
||||
"client_confirm_delete": "Opravdu chcete odstranit klienta \"{{key}}\"?",
|
||||
"list_confirm_delete": "Opravdu chcete smazat tento seznam?",
|
||||
"auto_clients_title": "Spuštění klienti",
|
||||
"auto_clients_desc": "Zařízení, která nejsou na seznamu stálých klientů, a mohou nadále používat AdGuard Home",
|
||||
"auto_clients_desc": "Informace o IP adresách zařízení, která používají nebo mohou používat AdGuard Home. Tyto informace se získávají z několika zdrojů, včetně souborů hosts, reverzního DNS atd.",
|
||||
"access_title": "Nastavení přístupu",
|
||||
"access_desc": "Zde můžete konfigurovat pravidla přístupu pro server DNS AdGuard Home",
|
||||
"access_allowed_title": "Povolení klienti",
|
||||
@@ -478,7 +482,9 @@
|
||||
"setup_dns_notice": "Pro použití <1>DNS skrze HTTPS</1> nebo <1>DNS skrze TLS</1> potřebujete v nastaveních AdGuard Home <0>nakonfigurovat šifrování</0>.",
|
||||
"rewrite_added": "Přesměrování DNS pro „{{key}}“ úspěšně přidáno",
|
||||
"rewrite_deleted": "Přesměrování DNS pro „{{key}}“ úspěšně smazáno",
|
||||
"rewrite_updated": "Přesměrování DNS bylo úspěšně aktualizováno",
|
||||
"rewrite_add": "Přidat přesměrování DNS",
|
||||
"rewrite_edit": "Upravit přesměrování DNS",
|
||||
"rewrite_not_found": "Přesměrování DNS nenalezeny",
|
||||
"rewrite_confirm_delete": "Jste si jisti, že chcete smazat přesměrování DNS pro „{{key}}“?",
|
||||
"rewrite_desc": "Umožňuje snadno nakonfigurovat vlastní DNS odezvy pro konkrétní název domény.",
|
||||
|
||||
@@ -125,6 +125,8 @@
|
||||
"top_clients": "Hyppigste klienter",
|
||||
"no_clients_found": "Ingen klienter fundet",
|
||||
"general_statistics": "Generelle statistikker",
|
||||
"top_upstreams": "Top-upstreams",
|
||||
"no_upstreams_data_found": "Ingen upstreams-data fundet",
|
||||
"number_of_dns_query_days": "Antallet af DNS-forespørgsler behandlet den seneste {{count}} dag",
|
||||
"number_of_dns_query_days_plural": "Antallet af DNS-forespørgsler behandlet de seneste {{count}} dage",
|
||||
"number_of_dns_query_24_hours": "Antallet af DNS-forespørgsler behandlet de seneste 24 timer",
|
||||
@@ -134,6 +136,7 @@
|
||||
"enforced_save_search": "Håndhævet sikker søgning",
|
||||
"number_of_dns_query_to_safe_search": "Antallet af DNS-forespørgsler til søgemaskiner, hvor Sikker Søgning blev håndhævet",
|
||||
"average_processing_time": "Gennemsnitlig behandlingstid",
|
||||
"processing_time": "Behandlingstid",
|
||||
"average_processing_time_hint": "Gennemsnitlig behandlingstid i millisekunder af DNS-forespørgsel",
|
||||
"block_domain_use_filters_and_hosts": "Blokér domæner vha. filtre og værtsfiler",
|
||||
"filters_block_toggle_hint": "Du kan opsætte blokeringsregler i <a>Filterindstillingerne</a>.",
|
||||
@@ -158,6 +161,7 @@
|
||||
"upstream_dns_configured_in_file": "Opsat i {{path}}",
|
||||
"test_upstream_btn": "Test upstreams",
|
||||
"upstreams": "Upstreams",
|
||||
"upstream": "Upstream",
|
||||
"apply_btn": "Anvend",
|
||||
"disabled_filtering_toast": "Filtrering deaktiveret",
|
||||
"enabled_filtering_toast": "Filtrering aktiveret",
|
||||
@@ -444,7 +448,7 @@
|
||||
"client_confirm_delete": "Sikker på, at du vil slette klient \"{{key}}\"?",
|
||||
"list_confirm_delete": "Sikker på, at du vil slette denne liste?",
|
||||
"auto_clients_title": "Klienter (runtime)",
|
||||
"auto_clients_desc": "Enheder, som ikke er på listen over Permanente klienter, kan stadig bruge AdGuard Home",
|
||||
"auto_clients_desc": "Oplysninger om IP-adresser på enheder, som (måske) bruger AdGuard Home. Disse oplysninger indsamles fra flere kilder, herunder hosts-filer, reverse DNS mv.",
|
||||
"access_title": "Adgangsindstillinger",
|
||||
"access_desc": "Her kan adgangsregler for AdGuard Home DNS-serveren opsættes",
|
||||
"access_allowed_title": "Tilladte klienter",
|
||||
@@ -478,7 +482,9 @@
|
||||
"setup_dns_notice": "For at kunne bruge <1>DNS-over-HTTPS</1> eller <1>DNS-over-TLS</1>, skal du <0>opsætte Krypteringen</0> i AdGuard Homes indstillinger.",
|
||||
"rewrite_added": "DNS-omskrivning for \"{{key}}\" blev tilføjet",
|
||||
"rewrite_deleted": "DNS-omskrivning for \"{{key}}\" blev slettet",
|
||||
"rewrite_updated": "DNS-omskrivning hermed opdateret",
|
||||
"rewrite_add": "Tilføj DNS-omskrivning",
|
||||
"rewrite_edit": "Redigér DNS-omskrivning",
|
||||
"rewrite_not_found": "Ingen DNS-omskrivninger fundet",
|
||||
"rewrite_confirm_delete": "Sikker på, at du vil slette DNS-omskrivning for \"{{key}}\"?",
|
||||
"rewrite_desc": "Gør det nemt at opsætte det tilpassede DNS-svar for et specifikt domænenavn.",
|
||||
|
||||
@@ -125,6 +125,8 @@
|
||||
"top_clients": "Top Clients",
|
||||
"no_clients_found": "Keine Clients gefunden",
|
||||
"general_statistics": "Allgemeine Statistiken",
|
||||
"top_upstreams": "Top Upstreams",
|
||||
"no_upstreams_data_found": "Keine Upstream-Daten gefunden",
|
||||
"number_of_dns_query_days": "Anzahl der in den letzten {{count}} Tagen verarbeiteten DNS-Anfragen",
|
||||
"number_of_dns_query_days_plural": "Anzahl der DNS-Abfragen, die in den letzten {{count}} Tagen verarbeitet wurden",
|
||||
"number_of_dns_query_24_hours": "Anzahl der in den letzten 24 Stunden durchgeführten DNS-Anfragen",
|
||||
@@ -134,6 +136,7 @@
|
||||
"enforced_save_search": "Sichere Suche erzwungen",
|
||||
"number_of_dns_query_to_safe_search": "Anzahl der DNS-Anfragen bei denen Sichere Suche für Suchanfragen erzwungen wurde",
|
||||
"average_processing_time": "Durchschnittliche Bearbeitungsdauer",
|
||||
"processing_time": "Verarbeitungszeit",
|
||||
"average_processing_time_hint": "Durchschnittliche Zeit in Millisekunden zur Bearbeitung von DNS-Anfragen",
|
||||
"block_domain_use_filters_and_hosts": "Domains durch Filter und Host-Dateien sperren",
|
||||
"filters_block_toggle_hint": "Sie können Blockierregeln in den <a>Filter</a>einstellungen erstellen.",
|
||||
@@ -158,6 +161,7 @@
|
||||
"upstream_dns_configured_in_file": "Konfiguriert in {{path}}",
|
||||
"test_upstream_btn": "Upstreams testen",
|
||||
"upstreams": "Upstreams",
|
||||
"upstream": "Upstream",
|
||||
"apply_btn": "Anwenden",
|
||||
"disabled_filtering_toast": "Filtern deaktiviert",
|
||||
"enabled_filtering_toast": "Filtern aktiviert",
|
||||
@@ -444,7 +448,7 @@
|
||||
"client_confirm_delete": "Möchten Sie den Client „{{key}}“ wirklich löschen?",
|
||||
"list_confirm_delete": "Möchten Sie diese Liste wirklich löschen?",
|
||||
"auto_clients_title": "Laufzeit-Clients",
|
||||
"auto_clients_desc": "Geräte, die nicht auf der Liste der persistenten Clients stehen und trotzdem AdGuard Home verwenden dürfen",
|
||||
"auto_clients_desc": "Informationen über IP-Adressen der Geräten, die AdGuard Home nutzen oder nutzen könnten. Diese Informationen werden aus verschiedenen Quellen gesammelt, darunter Hosts-Dateien, Reverse-DNS usw.",
|
||||
"access_title": "Zugriffsrechte",
|
||||
"access_desc": "Hier können Sie die Zugriffsregeln für den DNS-Server von AdGuard Home konfigurieren",
|
||||
"access_allowed_title": "Zugelassene Clients",
|
||||
@@ -478,7 +482,9 @@
|
||||
"setup_dns_notice": "Um <1>DNS-over-HTTTPS</1> oder <1>DNS-over-TLS</1> verwenden zu können, müssen Sie in den AdGuard Home Einstellungen die <0>Verschlüsselung konfigurieren</0>.",
|
||||
"rewrite_added": "DNS-Umschreibung für „{{key}}“ erfolgreich hinzugefügt",
|
||||
"rewrite_deleted": "DNS-Umschreibung für „{{key}}“ erfolgreich entfernt",
|
||||
"rewrite_updated": "DNS-Rewrite erfolgreich aktualisiert",
|
||||
"rewrite_add": "DNS-Umschreibung hinzufügen",
|
||||
"rewrite_edit": "DNS-Rewrite bearbeiten",
|
||||
"rewrite_not_found": "Keine DNS-Umschreibungen gefunden",
|
||||
"rewrite_confirm_delete": "Möchten Sie die DNS-Umschreibung für „{{key}}“ wirklich entfernen?",
|
||||
"rewrite_desc": "Ermöglicht die einfache Konfiguration der benutzerdefinierten DNS-Antwort für einen bestimmten Domainnamen.",
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"load_balancing": "Load-balancing",
|
||||
"load_balancing_desc": "Query one upstream server at a time. AdGuard Home uses its weighted random algorithm to pick the server so that the fastest server is used more often.",
|
||||
"bootstrap_dns": "Bootstrap DNS servers",
|
||||
"bootstrap_dns_desc": "Bootstrap DNS servers are used to resolve IP addresses of the DoH/DoT resolvers you specify as upstreams.",
|
||||
"bootstrap_dns_desc": "IP addresses of DNS servers used to resolve IP addresses of the DoH/DoT resolvers you specify as upstreams. Comments are not permitted.",
|
||||
"local_ptr_title": "Private reverse DNS servers",
|
||||
"local_ptr_desc": "The DNS servers that AdGuard Home uses for local PTR queries. These servers are used to resolve PTR requests for addresses in private IP ranges, for example \"192.168.12.34\", using reverse DNS. If not set, AdGuard Home uses the addresses of the default DNS resolvers of your OS except for the addresses of AdGuard Home itself.",
|
||||
"local_ptr_default_resolver": "By default, AdGuard Home uses the following reverse DNS resolvers: {{ip}}.",
|
||||
@@ -125,6 +125,8 @@
|
||||
"top_clients": "Top clients",
|
||||
"no_clients_found": "No clients found",
|
||||
"general_statistics": "General statistics",
|
||||
"top_upstreams": "Top upstreams",
|
||||
"no_upstreams_data_found": "No upstreams data found",
|
||||
"number_of_dns_query_days": "The number of DNS queries processed for the last {{count}} day",
|
||||
"number_of_dns_query_days_plural": "The number of DNS queries processed for the last {{count}} days",
|
||||
"number_of_dns_query_24_hours": "The number of DNS queries processed for the last 24 hours",
|
||||
@@ -134,6 +136,7 @@
|
||||
"enforced_save_search": "Enforced safe search",
|
||||
"number_of_dns_query_to_safe_search": "The number of DNS requests to search engines for which Safe Search was enforced",
|
||||
"average_processing_time": "Average processing time",
|
||||
"processing_time": "Processing time",
|
||||
"average_processing_time_hint": "Average time in milliseconds on processing a DNS request",
|
||||
"block_domain_use_filters_and_hosts": "Block domains using filters and hosts files",
|
||||
"filters_block_toggle_hint": "You can setup blocking rules in the <a>Filters</a> settings.",
|
||||
@@ -158,6 +161,7 @@
|
||||
"upstream_dns_configured_in_file": "Configured in {{path}}",
|
||||
"test_upstream_btn": "Test upstreams",
|
||||
"upstreams": "Upstreams",
|
||||
"upstream": "Upstream",
|
||||
"apply_btn": "Apply",
|
||||
"disabled_filtering_toast": "Disabled filtering",
|
||||
"enabled_filtering_toast": "Enabled filtering",
|
||||
@@ -444,7 +448,7 @@
|
||||
"client_confirm_delete": "Are you sure you want to delete client \"{{key}}\"?",
|
||||
"list_confirm_delete": "Are you sure you want to delete this list?",
|
||||
"auto_clients_title": "Runtime clients",
|
||||
"auto_clients_desc": "Devices not on the list of Persistent clients that may still use AdGuard Home",
|
||||
"auto_clients_desc": "Information about IP addresses of devices that are using or may use AdGuard Home. This information is gathered from several sources, including hosts files, reverse DNS, etc.",
|
||||
"access_title": "Access settings",
|
||||
"access_desc": "Here you can configure access rules for the AdGuard Home DNS server",
|
||||
"access_allowed_title": "Allowed clients",
|
||||
@@ -478,7 +482,9 @@
|
||||
"setup_dns_notice": "In order to use <1>DNS-over-HTTPS</1> or <1>DNS-over-TLS</1>, you need to <0>configure Encryption</0> in AdGuard Home settings.",
|
||||
"rewrite_added": "DNS rewrite for \"{{key}}\" successfully added",
|
||||
"rewrite_deleted": "DNS rewrite for \"{{key}}\" successfully deleted",
|
||||
"rewrite_updated": "DNS rewrite successfully updated",
|
||||
"rewrite_add": "Add DNS rewrite",
|
||||
"rewrite_edit": "Edit DNS rewrite",
|
||||
"rewrite_not_found": "No DNS rewrites found",
|
||||
"rewrite_confirm_delete": "Are you sure you want to delete DNS rewrite for \"{{key}}\"?",
|
||||
"rewrite_desc": "Allows to easily configure custom DNS response for a specific domain name.",
|
||||
@@ -562,7 +568,7 @@
|
||||
"rewrite_A": "<0>A</0>: special value, keep <0>A</0> records from the upstream",
|
||||
"rewrite_AAAA": "<0>AAAA</0>: special value, keep <0>AAAA</0> records from the upstream",
|
||||
"disable_ipv6": "Disable resolving of IPv6 addresses",
|
||||
"disable_ipv6_desc": "Drop all DNS queries for IPv6 addresses (type AAAA).",
|
||||
"disable_ipv6_desc": "Drop all DNS queries for IPv6 addresses (type AAAA) and remove IPv6 hints from HTTPS responses.",
|
||||
"fastest_addr": "Fastest IP address",
|
||||
"fastest_addr_desc": "Query all DNS servers and return the fastest IP address among all responses. This slows down DNS queries as AdGuard Home has to wait for responses from all DNS servers, but improves the overall connectivity.",
|
||||
"autofix_warning_text": "If you click \"Fix\", AdGuard Home will configure your system to use AdGuard Home DNS server.",
|
||||
|
||||
@@ -125,6 +125,8 @@
|
||||
"top_clients": "Clientes más frecuentes",
|
||||
"no_clients_found": "No se han encontrado clientes",
|
||||
"general_statistics": "Estadísticas generales",
|
||||
"top_upstreams": "Mejores upstreams",
|
||||
"no_upstreams_data_found": "No se han encontrado datos de upstreams",
|
||||
"number_of_dns_query_days": "Número de consultas DNS procesadas durante el último {{count}} día",
|
||||
"number_of_dns_query_days_plural": "Número de consultas DNS procesadas durante los últimos {{count}} días",
|
||||
"number_of_dns_query_24_hours": "Número de consultas DNS procesadas durante las últimas 24 horas",
|
||||
@@ -134,6 +136,7 @@
|
||||
"enforced_save_search": "Búsquedas seguras forzadas",
|
||||
"number_of_dns_query_to_safe_search": "Número de peticiones DNS a los motores de búsqueda para los que se aplicó la búsqueda segura forzada",
|
||||
"average_processing_time": "Tiempo promedio de procesamiento",
|
||||
"processing_time": "Tiempo de procesamiento",
|
||||
"average_processing_time_hint": "Tiempo promedio en milisegundos al procesar una petición DNS",
|
||||
"block_domain_use_filters_and_hosts": "Bloquear dominios usando filtros y archivos hosts",
|
||||
"filters_block_toggle_hint": "Puedes configurar las reglas de bloqueo en la configuración de <a>filtros</a>.",
|
||||
@@ -158,6 +161,7 @@
|
||||
"upstream_dns_configured_in_file": "Configurado en {{path}}",
|
||||
"test_upstream_btn": "Probar DNS de subida",
|
||||
"upstreams": "DNS de subida",
|
||||
"upstream": "Upstream",
|
||||
"apply_btn": "Aplicar",
|
||||
"disabled_filtering_toast": "Filtrado deshabilitado",
|
||||
"enabled_filtering_toast": "Filtrado habilitado",
|
||||
@@ -444,7 +448,7 @@
|
||||
"client_confirm_delete": "¿Estás seguro de que deseas eliminar el cliente \"{{key}}\"?",
|
||||
"list_confirm_delete": "¿Estás seguro de que deseas eliminar esta lista?",
|
||||
"auto_clients_title": "Clientes activos",
|
||||
"auto_clients_desc": "Dispositivos que no están en la lista de clientes persistentes que aún pueden utilizar AdGuard Home",
|
||||
"auto_clients_desc": "Información sobre las direcciones IP de los dispositivos que usan o pueden usar AdGuard Home. Esta información se recopila de varias fuentes, incluidos ficheros de host, DNS inverso, etc.",
|
||||
"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",
|
||||
@@ -478,7 +482,9 @@
|
||||
"setup_dns_notice": "Para utilizar <1>DNS mediante HTTPS</1> o <1>DNS mediante TLS</1>, debes <0>configurar el cifrado</0> en la configuración de AdGuard Home.",
|
||||
"rewrite_added": "Reescritura DNS para \"{{key}}\" añadido correctamente",
|
||||
"rewrite_deleted": "Reescritura DNS para \"{{key}}\" eliminado correctamente",
|
||||
"rewrite_updated": "Reconfiguración de DNS actualizada correctamente",
|
||||
"rewrite_add": "Añadir reescritura DNS",
|
||||
"rewrite_edit": "Editar reconfiguración de DNS",
|
||||
"rewrite_not_found": "No se han encontrado reescrituras DNS",
|
||||
"rewrite_confirm_delete": "¿Estás seguro de que deseas eliminar la reescritura DNS para \"{{key}}\"?",
|
||||
"rewrite_desc": "Permite configurar fácilmente la respuesta DNS personalizada para un nombre de dominio específico.",
|
||||
|
||||
@@ -118,6 +118,8 @@
|
||||
"top_clients": "بالاترین کلاینت ها",
|
||||
"no_clients_found": "کلاینتی یافت نشد",
|
||||
"general_statistics": "آمار عمومی",
|
||||
"top_upstreams": "سرورهای بالادست بالا",
|
||||
"no_upstreams_data_found": "هیچ اطلاعاتی در مورد سرورهای بالادست یافت نشد",
|
||||
"number_of_dns_query_days": "تعداد جستار DNS پردازش شده در {{count}} روز آخر",
|
||||
"number_of_dns_query_days_plural": "تعداد جستار DNS پردازش شده در {{count}} روز گذشته",
|
||||
"number_of_dns_query_24_hours": "تعداد جستار DNS پردازش شده در 24 ساعت گذشته",
|
||||
@@ -149,6 +151,7 @@
|
||||
"upstream_dns": "سرورهای DNS جریان ارسالی",
|
||||
"test_upstream_btn": "تست جریان ارسالی",
|
||||
"upstreams": "جریان ارسالی",
|
||||
"upstream": "سرور مادر",
|
||||
"apply_btn": "اِعمال",
|
||||
"disabled_filtering_toast": "فیلترینگ غیرفعال شده است",
|
||||
"enabled_filtering_toast": "فیلترینگ فعال شده است",
|
||||
@@ -440,7 +443,9 @@
|
||||
"setup_dns_notice": "به منظور استفاده از <1>DNS-over-HTTPS</1> یا <1>DNS-over-TLS</1>، شما نیاز به <0>پیکربندی رمزگذاری</0> در تنظیمات AdGuard Home دارید.",
|
||||
"rewrite_added": "بازنویسی DNS برای \"{{key}}\" با موفقیت اضافه شد",
|
||||
"rewrite_deleted": "بازنویسی DNS برای \"{{key}}\" با موفقیت حذف شد",
|
||||
"rewrite_updated": "بازنویسی DNS با موفقیت به روز شد",
|
||||
"rewrite_add": "افزودن بازنویسی DNS",
|
||||
"rewrite_edit": "بازنویسی DNS را ویرایش کنید",
|
||||
"rewrite_not_found": "بازنویسی DNS یافت نشد",
|
||||
"rewrite_confirm_delete": "آیا واقعا میخواهید بازنویسی DNS برای \"{{key}}\" را حذف کنید؟",
|
||||
"rewrite_desc": "به آسانی اجازه پیکربندی پاسخ DNS دستی برای یک نام دامنه خاص را می دهد.",
|
||||
|
||||
@@ -2,21 +2,21 @@
|
||||
"client_settings": "Päätelaiteasetukset",
|
||||
"example_upstream_reserved": "ylävirta <0>tietyille verkkotunnuksille</0>;",
|
||||
"example_upstream_comment": "kommentti.",
|
||||
"upstream_parallel": "Käytä rinnakkaisia pyyntöjä ja nopeuta selvitystä käyttämällä kaikkia ylävirran palvelimia samanaikaisesti.",
|
||||
"upstream_parallel": "Käytä rinnakkaisia pyyntöjä ja nopeuta selvitystä käyttämällä kaikkia ylävirtapalvelimia samanaikaisesti.",
|
||||
"parallel_requests": "Rinnakkaiset pyynnöt",
|
||||
"load_balancing": "Kuormantasaus",
|
||||
"load_balancing_desc": "Lähetä pyyntö yhdelle ylävirran palvelimelle kerrallaan. AdGuard Home pyrkii valitsemaan nopeimman palvelimen painotetun satunnaisalgoritminsa avulla.",
|
||||
"load_balancing_desc": "Lähetä pyyntö yhdelle ylävirtapalvelimelle kerrallaan. AdGuard Home pyrkii valitsemaan nopeimman palvelimen painotetun satunnaisalgoritminsa avulla.",
|
||||
"bootstrap_dns": "Bootstrap DNS-palvelimet",
|
||||
"bootstrap_dns_desc": "Bootstrap DNS-palvelimia käytetään ylävirroiksi määritettyjen DoH/DoT-resolvereiden IP-osoitteiden selvitykseen.",
|
||||
"local_ptr_title": "Yksityiset käänteiset DNS-palvelimet",
|
||||
"local_ptr_desc": "DNS-palvelimet, joita AdGuard Home käyttää paikallisille PTR-pyynnöille. Näitä palvelimia käytetään yksityistä IP-osoitetta käyttävien PTR-pyyntöjen osoitteiden, kuten \"192.168.12.34\", selvitykseen käänteisen DNS:n avulla. Jos ei käytössä, AdGuard Home käyttää käyttöjärjestelmän oletusarvoisia DNS-resolvereita, poislukien AdGuard Homen omat osoitteet.",
|
||||
"local_ptr_default_resolver": "Oletusarvoisesti AdGuard Home käyttää seuraavia käänteisiä DNS-resolvereita: {{ip}}.",
|
||||
"local_ptr_no_default_resolver": "AdGuard Home ei voinut määrittää tälle järjestelmälle sopivaa yksityistä käänteistä DNS-resolveria.",
|
||||
"local_ptr_title": "Yksityiset käänteis-DNS-palvelimet",
|
||||
"local_ptr_desc": "DNS-palvelimet, joita AdGuard Home käyttää paikallisille PTR-pyynnöille. Näitä palvelimia käytetään yksityistä IP-osoitetta käyttävien PTR-pyyntöjen osoitteiden, kuten \"192.168.12.34\", selvitykseen käänteis-DNS:n avulla. Jos ei käytössä, AdGuard Home käyttää käyttöjärjestelmän oletusarvoisia DNS-resolvereita, poislukien AdGuard Homen omat osoitteet.",
|
||||
"local_ptr_default_resolver": "Oletusarvoisesti AdGuard Home käyttää seuraavia käänteis-DNS-resolvereita: {{ip}}.",
|
||||
"local_ptr_no_default_resolver": "AdGuard Home ei voinut määrittää tälle järjestelmälle sopivaa yksityistä käänteis-DNS-resolveria.",
|
||||
"local_ptr_placeholder": "Syötä yksi palvelimen osoite per rivi",
|
||||
"resolve_clients_title": "Käytä päätelaitteiden IP-osoitteille käänteistä selvitystä",
|
||||
"resolve_clients_desc": "Selvitä päätelaitteiden IP-osoitteiden isäntänimet käänteisesti lähettämällä PTR-pyynnöt sopiville resolvereille (yksityiset DNS-palvelimet paikallisille päätelaitteille, lähtevät palvelimet päätelaitteille, joilla on julkiset IP-osoitteet).",
|
||||
"use_private_ptr_resolvers_title": "Käytä yksityisiä käänteisiä DNS-resolvereita",
|
||||
"use_private_ptr_resolvers_desc": "Suorita käänteiset DNS-selvitykset paikallisesti tarjotuille osoitteille käyttäen näitä ylävirran palvelimia. Jos ei käytössä, vastaa AdGuard Home kaikkiin sen tyyppisiin PTR-pyyntöihin NXDOMAIN-arvolla, pois lukien DHCP, /etc/hosts, yms. -tiedoista tunnistettut päätelaitteet.",
|
||||
"resolve_clients_desc": "Selvitä päätelaitteiden IP-osoitteiden isäntänimet käänteisesti lähettämällä PTR-pyynnöt sopiville resolvereille (yksityiset DNS-palvelimet paikallisille päätelaitteille, ylävirtapalvelimet päätelaitteille, joilla on julkiset IP-osoitteet).",
|
||||
"use_private_ptr_resolvers_title": "Käytä yksityisiä käänteis-DNS-resolvereita",
|
||||
"use_private_ptr_resolvers_desc": "Suorita käänteis-DNS-selvitykset paikallisesti tarjotuille osoitteille käyttäen näitä ylävirtapalvelimia. Jos ei käytössä, vastaa AdGuard Home kaikkiin sen tyyppisiin PTR-pyyntöihin NXDOMAIN-arvolla, pois lukien DHCP, /etc/hosts, yms. -tiedoista tunnistettut päätelaitteet.",
|
||||
"check_dhcp_servers": "Etsi DHCP-palvelimia",
|
||||
"save_config": "Tallenna asetukset",
|
||||
"enabled_dhcp": "DHCP-palvelin otettiin käyttöön",
|
||||
@@ -125,6 +125,8 @@
|
||||
"top_clients": "Käytetyimmät päätelaitteet",
|
||||
"no_clients_found": "Päätelaitteita ei löytynyt",
|
||||
"general_statistics": "Yleiset tilastot",
|
||||
"top_upstreams": "Käytetyimmät ylävirrat",
|
||||
"no_upstreams_data_found": "Ylävirtatietoja ei löytynyt",
|
||||
"number_of_dns_query_days": "Käsiteltyjen DNS-pyyntöjen määrä viimeisten {{count}} päivän ajalta",
|
||||
"number_of_dns_query_days_plural": "Käsiteltyjen DNS-pyyntöjen määrä viimeisten {{count}} päivän ajalta",
|
||||
"number_of_dns_query_24_hours": "Käsiteltyjen DNS-pyyntöjen määrä viimeisten 24 tunnin ajalta",
|
||||
@@ -134,6 +136,7 @@
|
||||
"enforced_save_search": "Turvallinen haku pakotettiin",
|
||||
"number_of_dns_query_to_safe_search": "DNS-pyyntöjen määrä, joille turvallinen haku pakotettiin käyttöön",
|
||||
"average_processing_time": "Keskimääräinen käsittelyaika",
|
||||
"processing_time": "Käsittelyaika",
|
||||
"average_processing_time_hint": "Keskimääräinen DNS-pyynnön käsittelyyn kulutettu aika millisekunteina",
|
||||
"block_domain_use_filters_and_hosts": "Estä verkkotunnuksia suodattimilla ja hosts-tiedostoilla",
|
||||
"filters_block_toggle_hint": "Voit määrittää estosääntöjä <a>suodatinasetuksissa</a>.",
|
||||
@@ -158,6 +161,7 @@
|
||||
"upstream_dns_configured_in_file": "Määritetty tiedostossa {{path}}",
|
||||
"test_upstream_btn": "Testaa ylävirtoja",
|
||||
"upstreams": "Ylävirrat",
|
||||
"upstream": "Ylävirta",
|
||||
"apply_btn": "Käytä",
|
||||
"disabled_filtering_toast": "Suodatus poistettiin käytöstä",
|
||||
"enabled_filtering_toast": "Suodatus otettiin käyttöön",
|
||||
@@ -220,9 +224,9 @@
|
||||
"example_upstream_tcp_port": "tavallinen DNS (TCP, portti);",
|
||||
"example_upstream_tcp_hostname": "tavallinen DNS (TCP, isäntänimi);",
|
||||
"all_lists_up_to_date_toast": "Kaikki listat ovat ajan tasalla",
|
||||
"updated_upstream_dns_toast": "Ylävirtojen palvelimet tallennettiin",
|
||||
"updated_upstream_dns_toast": "Ylävirtapalvelimet tallennettiin",
|
||||
"dns_test_ok_toast": "Määritetyt DNS-palvelimet toimivat oikein",
|
||||
"dns_test_not_ok_toast": "Palvelin \"{{key}}\": ei voitu käyttää, tarkista sen oikeinkirjoitus",
|
||||
"dns_test_not_ok_toast": "Palvelin \"{{key}}\": Ei voitu käyttää, tarkista oikeinkirjoitus",
|
||||
"dns_test_warning_toast": "Datavuon \"{{key}}\" ei vastaa testipyyntöihin eikä välttämättä toimi kunnolla",
|
||||
"unblock": "Salli",
|
||||
"block": "Estä",
|
||||
@@ -444,7 +448,7 @@
|
||||
"client_confirm_delete": "Haluatko varmasti poistaa päätelaitteen \"{{key}}\"?",
|
||||
"list_confirm_delete": "Haluatko varmasti poistaa tämän listan?",
|
||||
"auto_clients_title": "Määrittämättömät päätelaitteet",
|
||||
"auto_clients_desc": "Päätelaitteet, joita ei ole määritetty pysyviksi ja jotka voivat silti käyttää AdGuard Homea.",
|
||||
"auto_clients_desc": "Päätelaitteet, joita ei ole määritetty pysyviksi ja jotka voivat silti käyttää AdGuard Homea. Näitä tietoja kertään useista lähteistä, mm. hosts-tiedostoista ja kääteis-DNS:llä.",
|
||||
"access_title": "Käytön asetukset",
|
||||
"access_desc": "Tässä voidaan määrittää AdGuard Homen DNS-palvelimen käyttöoikeussääntöjä.",
|
||||
"access_allowed_title": "Sallitut päätelaitteet",
|
||||
@@ -478,7 +482,9 @@
|
||||
"setup_dns_notice": "<1>DNS-over-HTTPS</1> tai <1>DNS-over-TLS</1> -toteutuksia varten, on AdGuard Homen <0>Salausasetukset</0> määritettävä.",
|
||||
"rewrite_added": "Kohteen \"{{key}}\" DNS-uudelleenohjaus lisättiin",
|
||||
"rewrite_deleted": "Kohteen \"{{key}}\" DNS-uudelleenohjaus poistettiin",
|
||||
"rewrite_updated": "DNS-uudelleenohjaukset päivitettiin",
|
||||
"rewrite_add": "Lisää DNS-uudelleenohjaus",
|
||||
"rewrite_edit": "Muokkaa DNS-uudelleenohjausta",
|
||||
"rewrite_not_found": "DNS-uudelleenohjauksia ei löytynyt",
|
||||
"rewrite_confirm_delete": "Haluatko varmasti poistaa DNS-uudelleenohjauksen kohteelle \"{{key}}\"?",
|
||||
"rewrite_desc": "Mahdollistaa oman DNS-vastauksen helpon määrityksen tietylle verkkotunnukselle.",
|
||||
@@ -621,7 +627,7 @@
|
||||
"enter_cache_size": "Syötä välimuistin koko (tavuina)",
|
||||
"enter_cache_ttl_min_override": "Syötä vähimmäis-TTL (sekunteina)",
|
||||
"enter_cache_ttl_max_override": "Syötä enimmäis-TTL (sekunteina)",
|
||||
"cache_ttl_min_override_desc": "Pidennä ylävirran palvelimelta vastaanotettuja, lyhyitä elinaika-arvoja (sekunteina) tallennettaessa DNS-vastauksia välimuistiin.",
|
||||
"cache_ttl_min_override_desc": "Pidennä ylävirtapalvelimelta vastaanotettuja, lyhyitä elinaika-arvoja (sekunteina) tallennettaessa DNS-vastauksia välimuistiin.",
|
||||
"cache_ttl_max_override_desc": "Määritä DNS-välimuistin kohteiden enimmäiselinaika (sekunteina).",
|
||||
"ttl_cache_validation": "Välimuistin vähimmäiselinajan on oltava pienempi tai sama kuin enimmäiselinajan",
|
||||
"cache_optimistic": "Optimistinen välimuisti",
|
||||
|
||||
@@ -125,6 +125,8 @@
|
||||
"top_clients": "Meilleurs clients",
|
||||
"no_clients_found": "Pas de clients trouvés",
|
||||
"general_statistics": "Statistiques générales",
|
||||
"top_upstreams": "Top amonts",
|
||||
"no_upstreams_data_found": "Aucune donnée en amont trouvée",
|
||||
"number_of_dns_query_days": "Le nombre de requêtes DNS traitées pour les {{count}} derniers jours",
|
||||
"number_of_dns_query_days_plural": "Le nombre de requêtes DNS traitées ces {{count}} derniers jours",
|
||||
"number_of_dns_query_24_hours": "Le nombre de requêtes DNS traitées au cours des 24 dernières heures",
|
||||
@@ -134,6 +136,7 @@
|
||||
"enforced_save_search": "Recherche sécurisée forcée",
|
||||
"number_of_dns_query_to_safe_search": "Le nombre de requêtes DNS faites avec la Recherche securisée",
|
||||
"average_processing_time": "Temps moyen de traitement",
|
||||
"processing_time": "Délai de traitement",
|
||||
"average_processing_time_hint": "Temps moyen (en millisecondes) de traitement d'une requête DNS",
|
||||
"block_domain_use_filters_and_hosts": "Bloquez les domaines à l'aide des filtres et fichiers hosts",
|
||||
"filters_block_toggle_hint": "Vous pouvez configurer les règles de filtrage dans les paramètres des <a>Filtres</a>.",
|
||||
@@ -158,6 +161,7 @@
|
||||
"upstream_dns_configured_in_file": "Configuré dans {{path}}",
|
||||
"test_upstream_btn": "Tester les upstreams",
|
||||
"upstreams": "En amont",
|
||||
"upstream": "Amont",
|
||||
"apply_btn": "Appliquer",
|
||||
"disabled_filtering_toast": "Filtrage désactivé",
|
||||
"enabled_filtering_toast": "Filtrage activé",
|
||||
@@ -444,7 +448,7 @@
|
||||
"client_confirm_delete": "Voulez-vous vraiment supprimer le client « {{key}} » ?",
|
||||
"list_confirm_delete": "Voulez-vous vraiment supprimer cette liste ?",
|
||||
"auto_clients_title": "Clients d'exécution",
|
||||
"auto_clients_desc": "Appareils ne figurant pas sur la liste des clients persistants qui peuvent encore utiliser AdGuard Home.",
|
||||
"auto_clients_desc": "Informations sur les adresses IP des appareils qui utilisent ou pourraient utiliser AdGuard Home. Ces informations sont recueillies à partir de plusieurs sources, notamment les fichiers hosts, le DNS inverse, etc.",
|
||||
"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",
|
||||
@@ -478,7 +482,9 @@
|
||||
"setup_dns_notice": "Pour utiliser le <1>DNS-over-HTTPS</1> ou le <1>DNS-over-TLS</1>, vous devez <0>configurer le Chiffrement</0> dans les paramètres de AdGuard Home.",
|
||||
"rewrite_added": "Réécriture DNS pour « {{key}} » ajoutée",
|
||||
"rewrite_deleted": "Réécriture DNS pour « {{key}} » supprimée",
|
||||
"rewrite_updated": "Réécriture DNS mise à jour",
|
||||
"rewrite_add": "Ajouter une réécriture DNS",
|
||||
"rewrite_edit": "Modifier la réécriture DNS",
|
||||
"rewrite_not_found": "Aucune réécriture DNS trouvée",
|
||||
"rewrite_confirm_delete": "Voulez-vous vraiment supprimer la réécriture DNS pour « {{key}} » ?",
|
||||
"rewrite_desc": "Permet de configurer facilement la réponse DNS personnalisée pour un nom de domaine spécifique.",
|
||||
|
||||
@@ -125,6 +125,8 @@
|
||||
"top_clients": "Top klijenti",
|
||||
"no_clients_found": "Nema pronađenih klijenata",
|
||||
"general_statistics": "Opća statistika",
|
||||
"top_upstreams": "Top upstream poslužitelji",
|
||||
"no_upstreams_data_found": "Nema podataka o upstream poslužiteljima",
|
||||
"number_of_dns_query_days": "Broj DNS upita obrađenih u posljednja {{count}} dan",
|
||||
"number_of_dns_query_days_plural": "Broj DNS upita obrađenih u posljednja {{count}} dana",
|
||||
"number_of_dns_query_24_hours": "Broj DNS upita obrađenih u posljednja 24 sata",
|
||||
@@ -158,6 +160,7 @@
|
||||
"upstream_dns_configured_in_file": "Postavljeno u {{path}}",
|
||||
"test_upstream_btn": "Testiraj upstream-ove",
|
||||
"upstreams": "Upstreams",
|
||||
"upstream": "Upstream poslužitelj",
|
||||
"apply_btn": "Primijeni",
|
||||
"disabled_filtering_toast": "Onemogućeno filtriranje",
|
||||
"enabled_filtering_toast": "Omogućeno filtriranje",
|
||||
@@ -444,7 +447,7 @@
|
||||
"client_confirm_delete": "Jeste li sigurni da želite ukloniti \"{{key}}\" klijenta?",
|
||||
"list_confirm_delete": "Jeste li sigurni da želite ukloniti ovaj popis?",
|
||||
"auto_clients_title": "Runtime klijenti",
|
||||
"auto_clients_desc": "Podaci na klijentu koji koriste AdGuard Home, ali se ne mijenjaju u postavkama",
|
||||
"auto_clients_desc": "Informacije o IP adresama uređaja koji koriste ili bi mogli koristiti AdGuard Home. Ove informacije prikupljaju se iz nekoliko izvora, uključujući datoteke hostova, obrnuti DNS itd.",
|
||||
"access_title": "Postavke pristupa",
|
||||
"access_desc": "Ovdje možete konfigurirati pravila pristupa za AdGuard Home DNS poslužitelj",
|
||||
"access_allowed_title": "Dopušteni klijenti",
|
||||
@@ -478,7 +481,9 @@
|
||||
"setup_dns_notice": "Da biste koristili <1>DNS-over-HTTPS</1> ili <1>DNS-over-TLS</1>, morate <0>postaviti šifriranje</0> u AdGuard Home postavkama.",
|
||||
"rewrite_added": "DNS prijepis za \"{{key}}\" je uspješno dodan",
|
||||
"rewrite_deleted": "DNS prijepis za \"{{key}}\" je uspješno uklonjen",
|
||||
"rewrite_updated": "Prepisivanje DNS-a uspješno ažurirano",
|
||||
"rewrite_add": "Dodaj DNS prijepis",
|
||||
"rewrite_edit": "Uredite prepisivanje DNS-a",
|
||||
"rewrite_not_found": "Nema DNS prijepisa",
|
||||
"rewrite_confirm_delete": "Jeste li sigurni da želite ukloniti DNS prijepis za \"{{key}}\" klijenta?",
|
||||
"rewrite_desc": "Omogućuje jednostavno postavljanje prilagođenog DNS odgovora za određenu domenu.",
|
||||
|
||||
@@ -125,6 +125,8 @@
|
||||
"top_clients": "Legaktívabb kliensek",
|
||||
"no_clients_found": "Nem található kliens",
|
||||
"general_statistics": "Általános statisztikák",
|
||||
"top_upstreams": "Top upstream szerverek",
|
||||
"no_upstreams_data_found": "Nem található upstream szerver adat",
|
||||
"number_of_dns_query_days": "Lekérdezések száma az utolsó {{count}} napban",
|
||||
"number_of_dns_query_days_plural": "Feldolgozott DNS lekérdezések száma az utolsó {{count}} napban",
|
||||
"number_of_dns_query_24_hours": "Az elmúlt 24 órában feldolgozott DNS lekérdezések száma",
|
||||
@@ -158,6 +160,7 @@
|
||||
"upstream_dns_configured_in_file": "Beállítva itt: {{path}}",
|
||||
"test_upstream_btn": "Upstreamek tesztelése",
|
||||
"upstreams": "Upstream-ek",
|
||||
"upstream": "Upstream szerver",
|
||||
"apply_btn": "Alkalmaz",
|
||||
"disabled_filtering_toast": "Szűrés letiltva",
|
||||
"enabled_filtering_toast": "Szűrés engedélyezve",
|
||||
@@ -167,6 +170,7 @@
|
||||
"enabled_parental_toast": "Szülői felügyelet engedélyezve",
|
||||
"disabled_safe_search_toast": "Biztonságos keresés letiltva",
|
||||
"enabled_save_search_toast": "Biztonságos keresés engedélyezve",
|
||||
"updated_save_search_toast": "A Biztonságos keresés beállításai frissítve",
|
||||
"enabled_table_header": "Engedélyezve",
|
||||
"name_table_header": "Név",
|
||||
"list_url_table_header": "Lista URL-je",
|
||||
@@ -290,6 +294,8 @@
|
||||
"rate_limit": "Kérések korlátozása",
|
||||
"edns_enable": "EDNS kliens alhálózat engedélyezése",
|
||||
"edns_cs_desc": "Adja hozzá az EDNS Client Subnet beállítást (ECS) a felfelé irányuló kérésekhez, és naplózza a kliensek által küldött értékeket a lekérdezési naplóban.",
|
||||
"edns_use_custom_ip": "Használjon egyéni IP-címet az EDNS-hez",
|
||||
"edns_use_custom_ip_desc": "Engedélyezze az egyéni IP-cím használatát az EDNS-hez",
|
||||
"rate_limit_desc": "Maximálisan hány kérést küldhet egy kliens másodpercenkén. Ha 0-ra állítja, akkor nincs korlátozás.",
|
||||
"blocking_ipv4_desc": "A blokkolt A kéréshez visszaadandó IP-cím",
|
||||
"blocking_ipv6_desc": "A blokkolt AAAA kéréshez visszaadandó IP-cím",
|
||||
@@ -441,7 +447,7 @@
|
||||
"client_confirm_delete": "Biztosan törölni szeretné a(z) \"{{key}}\" klienst?",
|
||||
"list_confirm_delete": "Biztosan törölni kívánja ezt a listát?",
|
||||
"auto_clients_title": "Futási idejű kliensek",
|
||||
"auto_clients_desc": "Ezek az eszközök nem szerepelnek a fenntartott kliensek listáján, de használják az AdGuard Home-ot",
|
||||
"auto_clients_desc": "Az AdGuard Home-ot használó vagy esetleg használó eszközök IP-címeire vonatkozó információk. Ezeket az információkat több forrásból gyűjtik, beleértve a hosts fájlokat, a fordított DNS-t stb.",
|
||||
"access_title": "Hozzáférési beállítások",
|
||||
"access_desc": "Itt konfigurálhatja az AdGuard Home DNS-kiszolgáló hozzáférési szabályait",
|
||||
"access_allowed_title": "Engedélyezett kliensek",
|
||||
@@ -475,7 +481,9 @@
|
||||
"setup_dns_notice": "Ahhoz, hogy a <1>DNS-over-HTTPS</1> vagy a <1>DNS-over-TLS</1> valamelyikét használja, muszáj <0>beállítania a titkosítást</0> az AdGuard Home beállításaiban.",
|
||||
"rewrite_added": "DNS átírás a(z) \"{{key}}\" kulcshoz sikeresen hozzáadva",
|
||||
"rewrite_deleted": "DNS átírás a(z) \"{{key}}\" kulcshoz sikeresen törölve",
|
||||
"rewrite_updated": "A DNS újraírása sikeresen frissítve",
|
||||
"rewrite_add": "DNS átírás hozzáadása",
|
||||
"rewrite_edit": "DNS újraírás szerkesztése",
|
||||
"rewrite_not_found": "Nem találhatók DNS átírások",
|
||||
"rewrite_confirm_delete": "Biztosan törölni szeretné a DNS átírást ehhez: \"{{key}}\"?",
|
||||
"rewrite_desc": "Lehetővé teszi, hogy egyszerűen beállítson egyéni DNS választ egy adott domain névhez.",
|
||||
@@ -523,6 +531,10 @@
|
||||
"statistics_retention_confirm": "Biztos benne, hogy megváltoztatja a statisztika megőrzési idejét? Ha csökkentette az értéket, a megadottnál korábbi adatok elvesznek",
|
||||
"statistics_cleared": "A statisztikák sikeresen vissza lettek állítva",
|
||||
"statistics_enable": "Statisztikák engedélyezése",
|
||||
"ignore_domains": "Figyelmen kívül hagyott domainek (újsorral elválasztva)",
|
||||
"ignore_domains_title": "Figyelmen kívül hagyott domainek",
|
||||
"ignore_domains_desc_stats": "Az ezekre a tartományokra vonatkozó lekérdezések nem kerülnek a statisztikákba",
|
||||
"ignore_domains_desc_query": "Az ezekhez a tartományokhoz tartozó lekérdezések nem kerülnek a lekérdezési naplóba",
|
||||
"interval_hours": "{{count}} óra",
|
||||
"interval_hours_plural": "{{count}} óra",
|
||||
"filters_configuration": "Szűrők beállításai",
|
||||
@@ -643,5 +655,29 @@
|
||||
"confirm_dns_cache_clear": "Biztos benne, hogy törölni szeretné a DNS-gyorsítótárat?",
|
||||
"cache_cleared": "A DNS gyorsítótár sikeresen törlődött",
|
||||
"clear_cache": "Gyorsítótár törlése",
|
||||
"protection_section_label": "Védelem"
|
||||
"make_static": "Statikussá tétel",
|
||||
"theme_auto_desc": "Automatikus (az eszköz színsémájától függően)",
|
||||
"theme_dark_desc": "Sötét téma",
|
||||
"theme_light_desc": "Világos téma",
|
||||
"disable_for_seconds": "{{count}} másodpercig",
|
||||
"disable_for_seconds_plural": "{{count}} másodpercig",
|
||||
"disable_for_minutes": "{{count}} percig",
|
||||
"disable_for_minutes_plural": "{{count}} percig",
|
||||
"disable_for_hours": "{{count}} óráig",
|
||||
"disable_for_hours_plural": "{{count}} óráig",
|
||||
"disable_until_tomorrow": "Holnapig",
|
||||
"disable_notify_for_seconds": "Kapcsolja ki a védelmet {{count}} másodpercre",
|
||||
"disable_notify_for_seconds_plural": "Kapcsolja ki a védelmet {{count}} másodpercre",
|
||||
"disable_notify_for_minutes": "Kapcsolja ki a védelmet {{count}} percre",
|
||||
"disable_notify_for_minutes_plural": "Kapcsolja ki a védelmet {{count}} percre",
|
||||
"disable_notify_for_hours": "Kapcsolja ki a védelmet {{count}} órára",
|
||||
"disable_notify_for_hours_plural": "Kapcsolja ki a védelmet {{count}} órára",
|
||||
"disable_notify_until_tomorrow": "Holnapig kapcsolja ki a védelmet",
|
||||
"enable_protection_timer": "A védelem {{time}}-kor aktiválódik",
|
||||
"custom_retention_input": "Adja meg a megőrzést órákban",
|
||||
"custom_rotation_input": "Írja be a forgatást órákban",
|
||||
"protection_section_label": "Védelem",
|
||||
"log_and_stats_section_label": "Lekérdezési napló és statisztikák",
|
||||
"ignore_query_log": "Figyelmen kívül hagyja ezt az ügyfelet a lekérdezési naplóban",
|
||||
"ignore_statistics": "Hagyja figyelmen kívül ezt az ügyfelet a statisztikákban"
|
||||
}
|
||||
|
||||
@@ -125,6 +125,8 @@
|
||||
"top_clients": "Klien teratas",
|
||||
"no_clients_found": "Tidak ditemukan klien",
|
||||
"general_statistics": "Statistik umum",
|
||||
"top_upstreams": "Top servers upstream",
|
||||
"no_upstreams_data_found": "Tidak ada data server upstream yang ditemukan",
|
||||
"number_of_dns_query_days": "Jumlah kueri DNS diproses selama {{value}} hari terakhir",
|
||||
"number_of_dns_query_days_plural": "Jumlah kueri DNS yang diproses selama {{count}} hari terakhir",
|
||||
"number_of_dns_query_24_hours": "Jumlah kueri DNS diproses selama 24 jam terakhir",
|
||||
@@ -158,6 +160,7 @@
|
||||
"upstream_dns_configured_in_file": "Diatur dalam {{path}}",
|
||||
"test_upstream_btn": "Uji hulu",
|
||||
"upstreams": "Upstream",
|
||||
"upstream": "Server upstream",
|
||||
"apply_btn": "Terapkan",
|
||||
"disabled_filtering_toast": "Penyaringan nonaktif",
|
||||
"enabled_filtering_toast": "Penyaringan aktif",
|
||||
@@ -474,7 +477,9 @@
|
||||
"setup_dns_notice": "Jikalau ingin menggunakan <1>DNS-over-HTTPS</1> atau <1>DNS-over-TLS</1>, Anda perlu <0>mengatur Enkripsi</0> pada pengaturan AdGuard Home.",
|
||||
"rewrite_added": "DNS rewrite untuk \"{{key}}\" berhasil ditambahkan",
|
||||
"rewrite_deleted": "DNS rewrite untuk \"{{key}}\" berhasil dihapus",
|
||||
"rewrite_updated": "Penulisan ulang DNS berhasil diperbarui",
|
||||
"rewrite_add": "Tambah DNS rewrite",
|
||||
"rewrite_edit": "Edit penulisan ulang DNS",
|
||||
"rewrite_not_found": "Tidak ada DNS rewrite ditemukan",
|
||||
"rewrite_confirm_delete": "Apakah anda yakin ingin menghapus DNS rewrite untuk \"{{key}}\"?",
|
||||
"rewrite_desc": "Memungkinkan untuk dengan mudah mengkonfigurasi respons DNS kustom untuk nama domain tertentu.",
|
||||
|
||||
@@ -125,6 +125,8 @@
|
||||
"top_clients": "Client più utilizzati",
|
||||
"no_clients_found": "Nessun client trovato",
|
||||
"general_statistics": "Statistiche generali",
|
||||
"top_upstreams": "Top upstream",
|
||||
"no_upstreams_data_found": "Nessun dato upstream trovato",
|
||||
"number_of_dns_query_days": "Numero di richieste DNS elaborate negli ultimi {{count}} giorni",
|
||||
"number_of_dns_query_days_plural": "Numero di richieste DNS elaborate negli ultimi {{count}} giorni",
|
||||
"number_of_dns_query_24_hours": "Numero di richieste DNS elaborate nelle ultime 24 ore",
|
||||
@@ -134,6 +136,7 @@
|
||||
"enforced_save_search": "Ricerca sicura forzata",
|
||||
"number_of_dns_query_to_safe_search": "Numero di richieste DNS dai motori di ricerca per i quali la Ricerca Sicura è stata forzata",
|
||||
"average_processing_time": "Tempo di elaborazione medio",
|
||||
"processing_time": "Tempo di elaborazione",
|
||||
"average_processing_time_hint": "Tempo medio in millisecondi per elaborare una richiesta DNS",
|
||||
"block_domain_use_filters_and_hosts": "Blocca domini utilizzando filtri e file hosts",
|
||||
"filters_block_toggle_hint": "Puoi impostare le regole di blocco nelle impostazioni dei <a>Filtri</a>.",
|
||||
@@ -158,6 +161,7 @@
|
||||
"upstream_dns_configured_in_file": "Configurato su {{path}}",
|
||||
"test_upstream_btn": "Testa gli upstream",
|
||||
"upstreams": "Upstream",
|
||||
"upstream": "Upstream",
|
||||
"apply_btn": "Applica",
|
||||
"disabled_filtering_toast": "Disattiva filtri",
|
||||
"enabled_filtering_toast": "Attiva filtri",
|
||||
@@ -444,7 +448,7 @@
|
||||
"client_confirm_delete": "Sei sicuro di voler eliminare il client \"{{key}}\"?",
|
||||
"list_confirm_delete": "Sei sicuro di voler eliminare questo elenco?",
|
||||
"auto_clients_title": "Client in tempo reale",
|
||||
"auto_clients_desc": "Dispositivi non presenti nell'elenco dei client Persistenti che possono ancora utilizzare AdGuard Home",
|
||||
"auto_clients_desc": "Informazioni sugli indirizzi IP dei dispositivi che utilizzano o potrebbero utilizzare AdGuard Home. Queste informazioni vengono raccolte da diverse fonti, inclusi file host, DNS inverso, ecc.",
|
||||
"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",
|
||||
@@ -478,7 +482,9 @@
|
||||
"setup_dns_notice": "Per utilizzare <1>DNS su HTTPS</1> o <1>DNS su TLS</1>, è necessario <0>configurare la crittografia</0> nelle impostazioni di AdGuard Home.",
|
||||
"rewrite_added": "Riscrittura DNS per \"{{key}}\" aggiunta correttamente",
|
||||
"rewrite_deleted": "La riscrittura DNS per \"{{key}}\" è stata eliminata correttamente",
|
||||
"rewrite_updated": "Riscrittura DNS aggiornata correttamente",
|
||||
"rewrite_add": "Aggiungi la riscrittura DNS",
|
||||
"rewrite_edit": "Modifica della riscrittura DNS",
|
||||
"rewrite_not_found": "Nessuna riscrittura DNS trovata",
|
||||
"rewrite_confirm_delete": "Sei sicuro di voler cancellare la riscrittura DNS per \"{{key}}\"?",
|
||||
"rewrite_desc": "Consente di configurare facilmente la risposta DNS personalizzata per un nome di dominio specifico.",
|
||||
|
||||
@@ -7,16 +7,16 @@
|
||||
"load_balancing": "ロードバランシング",
|
||||
"load_balancing_desc": "一度に1つのアップストリームサーバに処理要求します。 AdGuard Homeは、重み付きランダムアルゴリズム(weighted random algorithm)を使用してサーバを選択するため、最速のサーバがより頻繁に使用されます。",
|
||||
"bootstrap_dns": "ブートストラップDNSサーバ",
|
||||
"bootstrap_dns_desc": "ブートストラップDNSサーバは、上流として指定したDoH/DoTリゾルバのIPアドレスを解決するために使用されます。",
|
||||
"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リゾルバーのアドレス(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": "対応するリゾルバー(ローカルクライアントの場合はプライベートDNSサーバ、パブリックIPを持つクライアントの場合は上流サーバ)にPTRクエリを送信することにより、クライアントのIPアドレスをホストネームに逆解決します。",
|
||||
"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で応答します。",
|
||||
"use_private_ptr_resolvers_desc": "これらのアップストリームサーバーを使用して、ローカルで提供されるアドレスのリバースDNSルックアップを実行します。無効にすると、AdGuard Homeは、DHCP, /etc/hosts などから認識されるクライアントを除き、すべてのこのようなPTR要求にNXDOMAINで応答します。",
|
||||
"check_dhcp_servers": "DHCPサーバをチェックする",
|
||||
"save_config": "構成を保存する",
|
||||
"enabled_dhcp": "DHCPサーバを有効にしました",
|
||||
@@ -125,6 +125,8 @@
|
||||
"top_clients": "トップクライアント",
|
||||
"no_clients_found": "クライアント情報はありません",
|
||||
"general_statistics": "全般的な統計",
|
||||
"top_upstreams": "上位のアップストリーム",
|
||||
"no_upstreams_data_found": "アップストリームのデータが見つかりません",
|
||||
"number_of_dns_query_days": "過去{{count}}日間に処理されたDNSクエリの数",
|
||||
"number_of_dns_query_days_plural": "過去{{count}}日間に処理されたDNSクエリの数",
|
||||
"number_of_dns_query_24_hours": "過去24時間に処理されたDNSクエリの数",
|
||||
@@ -134,6 +136,7 @@
|
||||
"enforced_save_search": "強制されたセーフサーチ",
|
||||
"number_of_dns_query_to_safe_search": "セーフサーチが強制適用された検索エンジンへのDNSリクエストの数",
|
||||
"average_processing_time": "平均処理時間",
|
||||
"processing_time": "処理時間",
|
||||
"average_processing_time_hint": "DNSリクエストの処理にかかる平均時間(ミリ秒単位)",
|
||||
"block_domain_use_filters_and_hosts": "フィルタとhostsファイルを使用してドメインをブロックする",
|
||||
"filters_block_toggle_hint": "<a>フィルタ</a>の設定でブロックするルールを設定することができます。",
|
||||
@@ -153,11 +156,12 @@
|
||||
"custom_filtering_rules": "カスタム・フィルタリングルール",
|
||||
"encryption_settings": "暗号化設定",
|
||||
"dhcp_settings": "DHCP設定",
|
||||
"upstream_dns": "上流DNSサーバ",
|
||||
"upstream_dns_help": "サーバのアドレスは1行に1つずつ入力してください。上流DNSサーバの構成設定について詳しくは<a>こちら</a>でご確認いただけます。",
|
||||
"upstream_dns": "アップストリームDNSサーバー",
|
||||
"upstream_dns_help": "サーバのアドレスは1行に1つずつ入力してください。アップストリームDNSサーバーの構成設定について詳しくは<a>こちら</a>でご確認いただけます。",
|
||||
"upstream_dns_configured_in_file": "{{path}} にて設定されています",
|
||||
"test_upstream_btn": "上流サーバをテストする",
|
||||
"upstreams": "上流",
|
||||
"test_upstream_btn": "アップストリームをテストする",
|
||||
"upstreams": "アップストリーム",
|
||||
"upstream": "アップストリーム",
|
||||
"apply_btn": "適用する",
|
||||
"disabled_filtering_toast": "フィルタリングを無効にしました",
|
||||
"enabled_filtering_toast": "フィルタリングを有効にしました",
|
||||
@@ -220,7 +224,7 @@
|
||||
"example_upstream_tcp_port": "レギュラーDNS(over TCP、ポート付き);",
|
||||
"example_upstream_tcp_hostname": "通常のDNS(over TCP, ホスト名)。",
|
||||
"all_lists_up_to_date_toast": "すべてのリストは既に最新です",
|
||||
"updated_upstream_dns_toast": "上流DNSサーバを保存しました。",
|
||||
"updated_upstream_dns_toast": "アップストリームサーバーを保存しました。",
|
||||
"dns_test_ok_toast": "指定されたDNSサーバは正しく動作しています",
|
||||
"dns_test_not_ok_toast": "サーバ \"{{key}}\": 使用できませんでした。正しく入力されているかどうかを確認してください",
|
||||
"dns_test_warning_toast": "アップストリーム\"{{key}}\"はテストリクエストに応答せず、正しく動作しない可能性があります。",
|
||||
@@ -444,7 +448,7 @@
|
||||
"client_confirm_delete": "クライアント \"{{key}}\" を削除してもよろしいですか?",
|
||||
"list_confirm_delete": "このリストを削除してもよろしいですか?",
|
||||
"auto_clients_title": "ランタイムクライアント",
|
||||
"auto_clients_desc": "永続的クライアントのリストに未登録で、AdGuard Homeを使用する場合があるデバイスのリスト。",
|
||||
"auto_clients_desc": "AdGuard Home を使用している、または使用する可能性のあるデバイスの IP アドレスに関する情報です。この情報は、hosts ファイル、リバース DNS など、複数の情報源から収集されます。",
|
||||
"access_title": "アクセス設定",
|
||||
"access_desc": "こちらでは、AdGuard Home DNSサーバーのアクセスルールを設定できます。",
|
||||
"access_allowed_title": "許可されたクライアント",
|
||||
@@ -478,7 +482,9 @@
|
||||
"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_updated": "DNS rewrite を更新完了しました。",
|
||||
"rewrite_add": "DNS書き換え情報を追加する",
|
||||
"rewrite_edit": "DNS rewrite を編集する",
|
||||
"rewrite_not_found": "DNS書き換え情報はありません",
|
||||
"rewrite_confirm_delete": "\"{{key}}\" のDNS書き換え情報を削除してもよろしいですか?",
|
||||
"rewrite_desc": "特定のドメイン名に対するDNS応答を簡単にカスタマイズすることを可能にします。",
|
||||
@@ -621,7 +627,7 @@
|
||||
"enter_cache_size": "キャッシュサイズ(バイト単位)を入力してください",
|
||||
"enter_cache_ttl_min_override": "最小TTL(秒単位)を入力してください",
|
||||
"enter_cache_ttl_max_override": "最大TTL(秒単位)を入力してください",
|
||||
"cache_ttl_min_override_desc": "DNS応答をキャッシュするとき、上流サーバから受信した短いTTL(秒単位)を延長します。",
|
||||
"cache_ttl_min_override_desc": "DNS応答をキャッシュするとき、アップストリームサーバーから受信した短いTTL(秒単位)を延長します。",
|
||||
"cache_ttl_max_override_desc": "DNSキャッシュ内のエントリの最大TTL(秒単位)を設定します。",
|
||||
"ttl_cache_validation": "最小キャッシュTTL上書き値は最大値以下にする必要があります",
|
||||
"cache_optimistic": "Optimistic cashing (オプティミスティック・キャッシュ)",
|
||||
|
||||
@@ -125,6 +125,8 @@
|
||||
"top_clients": "클라이언트",
|
||||
"no_clients_found": "클라이언트가 없습니다",
|
||||
"general_statistics": "일반 통계",
|
||||
"top_upstreams": "상위 업스트림",
|
||||
"no_upstreams_data_found": "업스트림 데이터 없음",
|
||||
"number_of_dns_query_days": "최근 {{count}}일 동안 처리된 DNS 쿼리의 수",
|
||||
"number_of_dns_query_days_plural": "최근 {{count}}일 동안 처리된 DNS 쿼리의 수",
|
||||
"number_of_dns_query_24_hours": "최근 24시간 동안 처리된 DNS 쿼리의 수",
|
||||
@@ -134,6 +136,7 @@
|
||||
"enforced_save_search": "세이프서치 강제",
|
||||
"number_of_dns_query_to_safe_search": "세이프서치가 적용된 검색 엔진에 대해 DNS 요청 수",
|
||||
"average_processing_time": "평균처리 시간",
|
||||
"processing_time": "처리 시간",
|
||||
"average_processing_time_hint": "DNS 요청 처리시 평균 시간(밀리초)",
|
||||
"block_domain_use_filters_and_hosts": "필터 및 호스트 파일을 사용하여 도메인 차단",
|
||||
"filters_block_toggle_hint": "차단규칙<a>필터</a>을 설정할 수 있습니다.",
|
||||
@@ -158,6 +161,7 @@
|
||||
"upstream_dns_configured_in_file": "{{path}}에서 구성됨",
|
||||
"test_upstream_btn": "업스트림 테스트",
|
||||
"upstreams": "업스트림",
|
||||
"upstream": "업스트림",
|
||||
"apply_btn": "적용",
|
||||
"disabled_filtering_toast": "필터링 비활성화됨",
|
||||
"enabled_filtering_toast": "필터링 활성화됨",
|
||||
@@ -444,7 +448,7 @@
|
||||
"client_confirm_delete": "정말 클라이언트 '{{key}}'을(를) 삭제하시겠습니까?",
|
||||
"list_confirm_delete": "정말로 이 목록을 제거하시겠습니까?",
|
||||
"auto_clients_title": "런타임 클라이언트",
|
||||
"auto_clients_desc": "AdGuard Home을 계속 사용할 수 있는 영구 클라이언트 목록에 없는 디바이스입니다",
|
||||
"auto_clients_desc": "AdGuard Home을 사용 중이거나 사용할 수 있는 기기의 IP 주소에 대한 정보가 표시됩니다. 이 정보는 호스트 파일, 역방향 DNS 등 여러 소스에서 수집됩니다.",
|
||||
"access_title": "접근 설정",
|
||||
"access_desc": "여기에서 AdGuard Home DNS 서버에 대한 액세스 규칙을 설정할 수 있습니다",
|
||||
"access_allowed_title": "허용된 클라이언트",
|
||||
@@ -478,7 +482,9 @@
|
||||
"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_updated": "DNS 다시 쓰기 업데이트 완료",
|
||||
"rewrite_add": "DNS 변환 정보를 추가합니다",
|
||||
"rewrite_edit": "DNS 다시 쓰기 편집",
|
||||
"rewrite_not_found": "DNS 변경 정보를 찾을 수 없습니다",
|
||||
"rewrite_confirm_delete": "'{{key}}'에 대한 DNS 변경 정보를 삭제하시겠습니까?",
|
||||
"rewrite_desc": "특정 도메인 이름에 대한 사용자 지정 DNS 응답을 쉽게 구성할 수 있습니다.",
|
||||
|
||||
@@ -125,6 +125,8 @@
|
||||
"top_clients": "Top gebruikers",
|
||||
"no_clients_found": "Geen gebruikers gevonden",
|
||||
"general_statistics": "Algemene statistieken",
|
||||
"top_upstreams": "Top upstreams",
|
||||
"no_upstreams_data_found": "Geen upstreams-gegevens gevonden",
|
||||
"number_of_dns_query_days": "Aantal verwerkte DNS aanvragen van de laatste {{count}} dag",
|
||||
"number_of_dns_query_days_plural": "Aantal verwerkte DNS aanvragen van de laatste {{count}} dagen",
|
||||
"number_of_dns_query_24_hours": "Aantal verwerkte DNS aanvragen van de laatste 24 uur",
|
||||
@@ -134,6 +136,7 @@
|
||||
"enforced_save_search": "Geforceerd veilig zoeken",
|
||||
"number_of_dns_query_to_safe_search": "Aantal DNS aanvragen in zoekmachines dmv geforceerd veilig zoeken",
|
||||
"average_processing_time": "Gemiddelde procestijd",
|
||||
"processing_time": "Verwerkingstijd",
|
||||
"average_processing_time_hint": "Gemiddelde verwerkingstijd in milliseconden van een DNS aanvraag",
|
||||
"block_domain_use_filters_and_hosts": "Domeinen blokkeren d.m.v. filters en host-bestanden",
|
||||
"filters_block_toggle_hint": "Je kan blokkeringsregels toevoegen in de <a>Filters</a> instellingen.",
|
||||
@@ -158,6 +161,7 @@
|
||||
"upstream_dns_configured_in_file": "Geconfigureerd in {{path}}",
|
||||
"test_upstream_btn": "Test upstream",
|
||||
"upstreams": "Upstreams",
|
||||
"upstream": "Upstream",
|
||||
"apply_btn": "Toepassen",
|
||||
"disabled_filtering_toast": "Filters uitgeschakeld",
|
||||
"enabled_filtering_toast": "Filters ingeschakeld",
|
||||
@@ -186,7 +190,7 @@
|
||||
"cancel_btn": "Annuleren",
|
||||
"enter_name_hint": "Voeg naam toe",
|
||||
"enter_url_or_path_hint": "Voer een URL in of het pad van de lijst",
|
||||
"check_updates_btn": "Controleer op updates",
|
||||
"check_updates_btn": "Controleren op updates",
|
||||
"new_blocklist": "Nieuwe blokkeerlijst",
|
||||
"new_allowlist": "Nieuwe toelatingslijst",
|
||||
"edit_blocklist": "Blokkeerlijst beheren",
|
||||
@@ -444,7 +448,7 @@
|
||||
"client_confirm_delete": "Ben je zeker dat je deze gebruiker \"{{key}}\" wilt verwijderen?",
|
||||
"list_confirm_delete": "Ben je zeker om deze lijst te verwijderen?",
|
||||
"auto_clients_title": "Runtime-clients",
|
||||
"auto_clients_desc": "Apparaten die niet op de lijst van permanente clients staan die mogelijk nog steeds AdGuard Home gebruiken",
|
||||
"auto_clients_desc": "Informatie over IP-adressen van apparaten die AdGuard Home gebruiken of kunnen gebruiken. Deze informatie wordt verzameld uit verschillende bronnen, waaronder hosts-bestanden, reverse DNS, enz.",
|
||||
"access_title": "Toegangs instellingen",
|
||||
"access_desc": "Hier kan je toegangsregels voor de AdGuard Home DNS-server instellen",
|
||||
"access_allowed_title": "Toegestane gebruikers",
|
||||
@@ -456,7 +460,7 @@
|
||||
"access_settings_saved": "Toegangsinstellingen succesvol opgeslagen",
|
||||
"updates_checked": "Een nieuwe versie van AdGuard Home is beschikbaar\n",
|
||||
"updates_version_equal": "AdGuard Home is actueel",
|
||||
"check_updates_now": "Controleer op updates",
|
||||
"check_updates_now": "Nu controleren op updates",
|
||||
"version_request_error": "Updatecontrole mislukt. Controleer je internetverbinding.",
|
||||
"dns_privacy": "DNS Privacy",
|
||||
"setup_dns_privacy_1": "<0>DNS-via-TLS:</0> Gebruik <1>{{address}}</1> string.",
|
||||
@@ -478,7 +482,9 @@
|
||||
"setup_dns_notice": "Om <1>DNS-via-HTTPS</1> of <1>DNS-via-TLS</1> te gebruiken, moet je <0>Versleuteling configureren</0> in de AdGuard Home instellingen.",
|
||||
"rewrite_added": "DNS-herschrijving voor \"{{key}}\" met succes toegevoegd",
|
||||
"rewrite_deleted": "DNS-herschrijving voor \"{{key}}\" met succes verwijderd",
|
||||
"rewrite_updated": "DNS-herschrijven succesvol bijgewerkt",
|
||||
"rewrite_add": "DNS-herschrijving toevoegen",
|
||||
"rewrite_edit": "DNS-herschrijven bewerken",
|
||||
"rewrite_not_found": "Geen DNS-herschrijving gevonden",
|
||||
"rewrite_confirm_delete": "Bent u zeker dat u DNS-herschrijving \"{{key}}\" wilt verwijderen?",
|
||||
"rewrite_desc": "Hiermee kunt u eenvoudig aangepaste DNS-antwoorden configureren voor een specifieke domeinnaam.",
|
||||
@@ -571,7 +577,7 @@
|
||||
"tags_title": "Labels",
|
||||
"tags_desc": "Je kunt labels selecteren die overeenkomen met de client. Labels kunnen worden opgenomen in de filterregels om ze \n nauwkeuriger toe te passen. <0>Meer informatie</0>.",
|
||||
"form_select_tags": "Client tags selecteren",
|
||||
"check_title": "Controleer de filtering",
|
||||
"check_title": "De filtering controleren",
|
||||
"check_desc": "Controleren of een hostnaam wordt gefilterd.",
|
||||
"check": "Controleren",
|
||||
"form_enter_host": "Voer een hostnaam in",
|
||||
|
||||
@@ -114,6 +114,8 @@
|
||||
"top_clients": "Vanligste klienter",
|
||||
"no_clients_found": "Ingen klienter ble funnet",
|
||||
"general_statistics": "Generelle statistikker",
|
||||
"top_upstreams": "Topp oppstrøms servere",
|
||||
"no_upstreams_data_found": "Ingen oppstrøms servere data funnet",
|
||||
"number_of_dns_query_days": "Antall DNS-spørringer behandlet for de siste {{count}} dagene",
|
||||
"number_of_dns_query_days_plural": "Antall DNS-forespørsler som ble behandlet de siste {{count}} dagene",
|
||||
"number_of_dns_query_24_hours": "Antall DNS-forespørsler som ble behandlet de siste 24 timene",
|
||||
@@ -147,6 +149,7 @@
|
||||
"upstream_dns_configured_in_file": "Satt opp i {{path}}",
|
||||
"test_upstream_btn": "Test oppstrømstilkoblinger",
|
||||
"upstreams": "Oppstrømstjenere",
|
||||
"upstream": "Oppstrøms server",
|
||||
"apply_btn": "Benytt",
|
||||
"disabled_filtering_toast": "Skrudde av filtrering",
|
||||
"enabled_filtering_toast": "Skrudde på filtrering",
|
||||
@@ -457,7 +460,9 @@
|
||||
"setup_dns_notice": "For å benytte <1>DNS-over-HTTPS</1> eller <1>DNS-over-TLS</1>, må du <0>sette opp Kryptering</0> i AdGuard Home-innstillingene.",
|
||||
"rewrite_added": "DNS-omdirigeringen for «{{key}}» ble vellykket lagt til",
|
||||
"rewrite_deleted": "DNS-omdirigeringen for «{{key}}» ble vellykket slettet",
|
||||
"rewrite_updated": "DNS-omskriving ble oppdatert",
|
||||
"rewrite_add": "Legg til DNS-omdirigering",
|
||||
"rewrite_edit": "Rediger DNS-omskriving",
|
||||
"rewrite_not_found": "Ingen DNS-omdirigeringer ble funnet",
|
||||
"rewrite_confirm_delete": "Er du sikker på at du vil slette DNS-omdirigeringen for «{{key}}»?",
|
||||
"rewrite_desc": "Lar deg enkelt konfigurere selvvalgte DNS-tilbakemeldinger for et spesifikt domenenavn.",
|
||||
|
||||
@@ -125,6 +125,8 @@
|
||||
"top_clients": "Główni klienci",
|
||||
"no_clients_found": "Nie znaleziono klienta",
|
||||
"general_statistics": "Ogólne statystyki",
|
||||
"top_upstreams": "Często żądane serwery nadrzędne",
|
||||
"no_upstreams_data_found": "Brak danych dotyczących serwerów nadrzędnych",
|
||||
"number_of_dns_query_days": "Liczba przetworzonych zapytań DNS w ciągu ostatnich {{count}} dni",
|
||||
"number_of_dns_query_days_plural": "Liczba przetworzonych zapytań DNS w ciągu ostatnich {{count}} dni",
|
||||
"number_of_dns_query_24_hours": "Liczba zapytań DNS przetworzonych w ciągu ostatnich 24 godzin",
|
||||
@@ -158,6 +160,7 @@
|
||||
"upstream_dns_configured_in_file": "Skonfigurowano w {{path}}",
|
||||
"test_upstream_btn": "Test głównych serwerów DNS",
|
||||
"upstreams": "Główne serwery DNS",
|
||||
"upstream": "Serwer nadrzędny",
|
||||
"apply_btn": "Zastosuj",
|
||||
"disabled_filtering_toast": "Wyłączone filtrowanie",
|
||||
"enabled_filtering_toast": "Włączone filtrowanie",
|
||||
@@ -444,7 +447,7 @@
|
||||
"client_confirm_delete": "Czy na pewno chcesz usunąć klienta \"{{key}}\"?",
|
||||
"list_confirm_delete": "Czy na pewno chcesz usunąć tę listę?",
|
||||
"auto_clients_title": "Uruchomieni klienci",
|
||||
"auto_clients_desc": "Urządzenia, których nie ma na liście stałych klientów, które mogą nadal korzystać z AdGuard Home",
|
||||
"auto_clients_desc": "Informacje o adresach IP urządzeń korzystających lub mogących korzystać z AdGuard Home. Te informacje są gromadzone z wielu źródeł takich jak pliki hosta, odwrotna translacja DNS, itp.",
|
||||
"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",
|
||||
@@ -470,7 +473,7 @@
|
||||
"setup_dns_privacy_ios_2": "Aplikacja <0>AdGuard dla iOS</0> obsługuje <1>DNS-over-HTTPS</1> i <1>DNS-over-TLS</1>.",
|
||||
"setup_dns_privacy_other_title": "Inne implementacje",
|
||||
"setup_dns_privacy_other_1": "Sam AdGuard Home może być bezpiecznym klientem DNS na dowolnej platformie.",
|
||||
"setup_dns_privacy_other_2": "<0>dnsproxy</0> obsługuje wszystkie znane bezpieczne protokoły DNS.\n\n",
|
||||
"setup_dns_privacy_other_2": "<0>dnsproxy</0> obsługuje wszystkie znane bezpieczne protokoły DNS.",
|
||||
"setup_dns_privacy_other_3": "<0>dnscrypt-proxy</0> obsługuje <1>DNS-over-HTTPS</1>.",
|
||||
"setup_dns_privacy_other_4": "<0>Mozilla Firefox</0> obsługuje <1>DNS-over-HTTPS</1>.",
|
||||
"setup_dns_privacy_other_5": "Znajdziesz więcej implementacji <0>tutaj</0> i <1>tutaj</1>.",
|
||||
@@ -478,7 +481,9 @@
|
||||
"setup_dns_notice": "Aby skorzystać z <1>DNS-over-HTTPS</1> lub <1>DNS-over-TLS</1>, musisz w ustawieniach AdGuard Home <0>skonfigurować szyfrowanie</0>.",
|
||||
"rewrite_added": "Pomyślnie dodano przepisanie DNS dla „{{key}}”",
|
||||
"rewrite_deleted": "Przepisanie DNS dla „{{key}}” zostało pomyślnie usunięte",
|
||||
"rewrite_updated": "Pomyślnie zaktualizowano przepisywanie DNS",
|
||||
"rewrite_add": "Dodaj przepisywanie DNS",
|
||||
"rewrite_edit": "Edytuj przepisywanie DNS",
|
||||
"rewrite_not_found": "Nie znaleziono przepisywania DNS",
|
||||
"rewrite_confirm_delete": "Czy na pewno chcesz usunąć przepisywanie DNS dla „{{key}}”?",
|
||||
"rewrite_desc": "Pozwala łatwo skonfigurować niestandardową odpowiedź DNS dla określonej nazwy domeny.",
|
||||
|
||||
@@ -125,6 +125,8 @@
|
||||
"top_clients": "Principais clientes",
|
||||
"no_clients_found": "Nenhuma cliente encontrado",
|
||||
"general_statistics": "Estatísticas gerais",
|
||||
"top_upstreams": "Melhores servidores DNS primários",
|
||||
"no_upstreams_data_found": "Nenhum dado de servidor DNS primário encontrado",
|
||||
"number_of_dns_query_days": "O número de consultas DNS processadas nos últimos {{count}} dias",
|
||||
"number_of_dns_query_days_plural": "Número de consultas DNS processadas nos últimos {{count}} dias",
|
||||
"number_of_dns_query_24_hours": "O número de consultas DNS processadas nas últimas 24 horas",
|
||||
@@ -134,6 +136,7 @@
|
||||
"enforced_save_search": "Forçar pesquisa segura",
|
||||
"number_of_dns_query_to_safe_search": "O número de solicitações de DNS para mecanismos de pesquisa para os quais a pesquisa segura foi aplicada",
|
||||
"average_processing_time": "Tempo médio de processamento",
|
||||
"processing_time": "Tempo de processamento",
|
||||
"average_processing_time_hint": "Tempo médio em milissegundos no processamento de uma solicitação DNS",
|
||||
"block_domain_use_filters_and_hosts": "Bloquear domínios usando arquivos de filtros e hosts",
|
||||
"filters_block_toggle_hint": "Você pode configurar as regras de bloqueio nas configurações de <a>Filtros</a>.",
|
||||
@@ -158,6 +161,7 @@
|
||||
"upstream_dns_configured_in_file": "Configurado em {{path}}",
|
||||
"test_upstream_btn": "Testar DNS primário",
|
||||
"upstreams": "DNS primário",
|
||||
"upstream": "Servidor DNS primário",
|
||||
"apply_btn": "Aplicar",
|
||||
"disabled_filtering_toast": "Filtragem desativada",
|
||||
"enabled_filtering_toast": "Filtragem ativada",
|
||||
@@ -444,7 +448,7 @@
|
||||
"client_confirm_delete": "Você tem certeza de que deseja excluir o cliente \"{{key}}\"?",
|
||||
"list_confirm_delete": "Você tem certeza de que deseja excluir essa lista?",
|
||||
"auto_clients_title": "Clientes ativos",
|
||||
"auto_clients_desc": "Dispositivo não está na lista de dispositivos persistentes que podem ser utilizados no AdGuard Home",
|
||||
"auto_clients_desc": "Informações sobre endereços IP de dispositivos que usam ou podem usar o AdGuard Home. Essas informações são coletadas de várias fontes, incluindo arquivos de hosts, DNS reverso, etc.",
|
||||
"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",
|
||||
@@ -478,7 +482,9 @@
|
||||
"setup_dns_notice": "Para usar o <1>DNS-sobre-HTTPS</1> ou <1>DNS-sobre-TLS</1>, você precisa <0>configurar a criptografia</0> nas configurações do AdGuard Home.",
|
||||
"rewrite_added": "Reescrita de DNS para \"{{key}}\" adicionada com sucesso",
|
||||
"rewrite_deleted": "Reescrita de DNS para \"{{key}}\" excluída com sucesso",
|
||||
"rewrite_updated": "Reconfiguração de DNS atualizada com êxito",
|
||||
"rewrite_add": "Adicionar reescrita de DNS",
|
||||
"rewrite_edit": "Editar reconfiguração de DNS",
|
||||
"rewrite_not_found": "Nenhuma reescrita de DNS foi encontrada",
|
||||
"rewrite_confirm_delete": "Você tem certeza de que deseja excluir a reescrita de DNS para \"{{key}}\"?",
|
||||
"rewrite_desc": "Permite configurar uma resposta personalizada do DNS para um nome de domínio específico.",
|
||||
|
||||
@@ -125,6 +125,8 @@
|
||||
"top_clients": "Principais clientes",
|
||||
"no_clients_found": "Nenhum cliente foi encontrado",
|
||||
"general_statistics": "Estatísticas gerais",
|
||||
"top_upstreams": "Melhores servidores DNS primários",
|
||||
"no_upstreams_data_found": "Nenhum dado de servidor DNS primário encontrado",
|
||||
"number_of_dns_query_days": "Número de consultas DNS processadas durante los últimos {{count}} días",
|
||||
"number_of_dns_query_days_plural": "Número de consultas DNS processadas durante os últimos {{count}} dias",
|
||||
"number_of_dns_query_24_hours": "O número de consultas DNS processadas nas últimas 24 horas",
|
||||
@@ -134,6 +136,7 @@
|
||||
"enforced_save_search": "Forçar pesquisa segura",
|
||||
"number_of_dns_query_to_safe_search": "O número de solicitações de DNS para motores de busca para os quais a pesquisa segura foi aplicada",
|
||||
"average_processing_time": "Tempo médio de processamento",
|
||||
"processing_time": "Tempo de processamento",
|
||||
"average_processing_time_hint": "Tempo médio em milissegundos no processamento de uma solicitação DNS",
|
||||
"block_domain_use_filters_and_hosts": "Bloquear domínios usando ficheiros de filtros e hosts",
|
||||
"filters_block_toggle_hint": "Pode configurar as regras de bloqueio nas configurações de <a>Filtros</a>.",
|
||||
@@ -158,6 +161,7 @@
|
||||
"upstream_dns_configured_in_file": "Configurado em {{path}}",
|
||||
"test_upstream_btn": "Testar DNS primário",
|
||||
"upstreams": "DNS primário",
|
||||
"upstream": "Servidor DNS primário",
|
||||
"apply_btn": "Aplicar",
|
||||
"disabled_filtering_toast": "Filtragem desativada",
|
||||
"enabled_filtering_toast": "Filtragem ativada",
|
||||
@@ -444,7 +448,7 @@
|
||||
"client_confirm_delete": "Tem a certeza de que deseja excluir o cliente \"{{key}}\"?",
|
||||
"list_confirm_delete": "Você tem certeza de que deseja excluir essa lista?",
|
||||
"auto_clients_title": "Clientes ativos",
|
||||
"auto_clients_desc": "Dispositivo não está na lista de dispositivos persistentes que podem ser utilizados no AdGuard Home",
|
||||
"auto_clients_desc": "Informações sobre endereços IP de dispositivos que estão a utilizar ou podem utilizar o AdGuard Home. Estas informações são recolhidas a partir de várias fontes, incluindo ficheiros hosts, DNS reverso etc.",
|
||||
"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",
|
||||
@@ -478,7 +482,9 @@
|
||||
"setup_dns_notice": "Para usar o <1>DNS-sobre-HTTPS</1> ou <1>DNS-sobre-TLS</1>, precisa de <0>configurar a criptografia</0> nas configurações do AdGuard Home.",
|
||||
"rewrite_added": "Reescrita de DNS para \"{{key}}\" adicionada com sucesso",
|
||||
"rewrite_deleted": "Reescrita de DNS para \"{{key}}\" excluída com sucesso",
|
||||
"rewrite_updated": "Reedição de DNS atualizada com sucesso",
|
||||
"rewrite_add": "Adicionar reescrita de DNS",
|
||||
"rewrite_edit": "Editar reedição de DNS",
|
||||
"rewrite_not_found": "Nenhuma reescrita de DNS foi encontrada",
|
||||
"rewrite_confirm_delete": "Tem a certeza de que deseja excluir a reescrita de DNS para \"{{key}}\"?",
|
||||
"rewrite_desc": "Permite configurar uma resposta personalizada do DNS para um nome de domínio específico.",
|
||||
|
||||
@@ -125,6 +125,8 @@
|
||||
"top_clients": "Clienți de top",
|
||||
"no_clients_found": "Nu au fost găsiți clienți",
|
||||
"general_statistics": "Statistici generale",
|
||||
"top_upstreams": "Top servere în amonte",
|
||||
"no_upstreams_data_found": "Nu există date despre serverele din amonte",
|
||||
"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",
|
||||
@@ -158,6 +160,7 @@
|
||||
"upstream_dns_configured_in_file": "Configurat în {{path}}",
|
||||
"test_upstream_btn": "Testați upstreams",
|
||||
"upstreams": "Upstreams",
|
||||
"upstream": "Server în amonte",
|
||||
"apply_btn": "Aplică",
|
||||
"disabled_filtering_toast": "Filtrare dezactivată",
|
||||
"enabled_filtering_toast": "Filtrare activată",
|
||||
@@ -167,6 +170,7 @@
|
||||
"enabled_parental_toast": "Control Parental activat",
|
||||
"disabled_safe_search_toast": "Căutare protejată dezactivată",
|
||||
"enabled_save_search_toast": "Căutare protejată activată",
|
||||
"updated_save_search_toast": "Setări Căutare sigură actualizate",
|
||||
"enabled_table_header": "Activat",
|
||||
"name_table_header": "Nume",
|
||||
"list_url_table_header": "Lista URL",
|
||||
@@ -256,12 +260,12 @@
|
||||
"query_log_cleared": "Jurnalul de interogare a fost șters cu succes",
|
||||
"query_log_updated": "Jurnalul de solicitări a fost actualizat cu succes",
|
||||
"query_log_clear": "Curăță jurnalele",
|
||||
"query_log_retention": "Retenție jurnale interogare",
|
||||
"query_log_retention": "Interogarea jurnalelor de rotație",
|
||||
"query_log_enable": "Activați jurnal",
|
||||
"query_log_configuration": "Configurația jurnalelor",
|
||||
"query_log_disabled": "Jurnalul de interogare este dezactivat și poate fi configurat în <0>setări</0>",
|
||||
"query_log_strict_search": "Utilizați ghilimele duble pentru căutare strictă",
|
||||
"query_log_retention_confirm": "Sunteți sigur că doriți să schimbați retenția jurnalului de interogare? Reducând valoarea intervalului, unele date vor fi pierdute",
|
||||
"query_log_retention_confirm": "Sigur doriți să modificați rotația jurnalului de interogări? Dacă micșorați valoarea intervalului, unele date se vor pierde",
|
||||
"anonymize_client_ip": "Anonimizare client IP",
|
||||
"anonymize_client_ip_desc": "Nu salvați adresa IP completă a clientului în jurnale și statistici",
|
||||
"dns_config": "Configurația serverului DNS",
|
||||
@@ -290,6 +294,8 @@
|
||||
"rate_limit": "Limita ratei",
|
||||
"edns_enable": "Activați subrețeaua de clienți EDNS",
|
||||
"edns_cs_desc": "Adaugă opțiunea EDNS Client Subnet (ECS) la solicitările în amonte și înregistrează valorile trimise de clienți în jurnalul de interogare.",
|
||||
"edns_use_custom_ip": "Utilizați IP personalizat pentru EDNS",
|
||||
"edns_use_custom_ip_desc": "Permiteți utilizarea IP-ului personalizat pentru EDNS",
|
||||
"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",
|
||||
@@ -441,7 +447,7 @@
|
||||
"client_confirm_delete": "Sunteți sigur că doriți să ștergeți clientul \"{{key}}\"?",
|
||||
"list_confirm_delete": "Sigur doriți să ștergeți această listă?",
|
||||
"auto_clients_title": "Clienți runtime",
|
||||
"auto_clients_desc": "Dispozitivele care nu se află pe lista de clienți Persistent care pot utiliza în continuare AdGuard Home",
|
||||
"auto_clients_desc": "Informații despre adresele IP ale dispozitivelor care utilizează sau pot utiliza AdGuard Home. Aceste informații sunt colectate din mai multe surse, inclusiv din fișiere hosts, DNS inversat etc.",
|
||||
"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",
|
||||
@@ -475,7 +481,9 @@
|
||||
"setup_dns_notice": "Pentru a utiliza <1>DNS-over-HTTPS</1> sau <1>DNS-over-TLS</1>, trebuie să <0>configurați Criptarea</0> în setările AdGuard Home.",
|
||||
"rewrite_added": "Rescriere DNS pentru \"{{key}}\" adăugată cu succes",
|
||||
"rewrite_deleted": "Rescriere DNS pentru \"{{key}}\" ștearsă cu succes",
|
||||
"rewrite_updated": "DNS rescrie actualizat cu succes",
|
||||
"rewrite_add": "Adăugați rescriere DNS",
|
||||
"rewrite_edit": "Editați rescrierea DNS",
|
||||
"rewrite_not_found": "Nu s-au găsit rescrieri DNS",
|
||||
"rewrite_confirm_delete": "Sunteți sigur că doriți să ștergeți rescrierea DNS pentru \"{{key}}\"?",
|
||||
"rewrite_desc": "Permite configurarea cu ușurință a răspunsului personalizat DNS pentru un nume de domeniu specific.",
|
||||
@@ -523,6 +531,10 @@
|
||||
"statistics_retention_confirm": "Sunteți sigur că doriți să schimbați păstrarea statisticilor? Dacă reduceți valoarea intervalului, unele date vor fi pierdute",
|
||||
"statistics_cleared": "Statisticile au fost șterse cu succes",
|
||||
"statistics_enable": "Activați statisticile",
|
||||
"ignore_domains": "Domenii ignorate (separate prin linie nouă)",
|
||||
"ignore_domains_title": "Domenii ignorate",
|
||||
"ignore_domains_desc_stats": "Interogările pentru aceste domenii nu sunt scrise în statistici",
|
||||
"ignore_domains_desc_query": "Interogările pentru aceste domenii nu sunt scrise în jurnalul de interogări",
|
||||
"interval_hours": "{{count}} oră",
|
||||
"interval_hours_plural": "{{count}} ore",
|
||||
"filters_configuration": "Configurația filtrelor",
|
||||
@@ -643,5 +655,29 @@
|
||||
"confirm_dns_cache_clear": "Sunteți sigur că doriți să ștergeți memoria cache DNS?",
|
||||
"cache_cleared": "Cache-ul DNS a fost golit cu succes",
|
||||
"clear_cache": "Goliți memoria cache",
|
||||
"protection_section_label": "Protecție"
|
||||
"make_static": "Faceți static",
|
||||
"theme_auto_desc": "Auto (pe baza schemei de culori a dispozitivului dvs.)",
|
||||
"theme_dark_desc": "Temă întunecată",
|
||||
"theme_light_desc": "Temă luminoasă",
|
||||
"disable_for_seconds": "Timp de {{count}} secundă",
|
||||
"disable_for_seconds_plural": "Timp de {{count}} secunde",
|
||||
"disable_for_minutes": "Timp de {{count}} minut",
|
||||
"disable_for_minutes_plural": "Timp de {{count}} minute",
|
||||
"disable_for_hours": "Timp de {{count}} oră",
|
||||
"disable_for_hours_plural": "Timp de {{count}} ore",
|
||||
"disable_until_tomorrow": "Până mâine",
|
||||
"disable_notify_for_seconds": "Dezactivați protecția timp de {{count}} secundă",
|
||||
"disable_notify_for_seconds_plural": "Dezactivați protecția timp de {{count}} secunde",
|
||||
"disable_notify_for_minutes": "Dezactivați protecția timp de {{count}} minut",
|
||||
"disable_notify_for_minutes_plural": "Dezactivați protecția timp de {{count}} minute",
|
||||
"disable_notify_for_hours": "Dezactivează protecția timp de {{count}} oră",
|
||||
"disable_notify_for_hours_plural": "Dezactivați protecția timp de {{count}} ore",
|
||||
"disable_notify_until_tomorrow": "Dezactivează protecția până mâine",
|
||||
"enable_protection_timer": "Protecția va fi activată în {{time}}",
|
||||
"custom_retention_input": "Introduceți reținerea în ore",
|
||||
"custom_rotation_input": "Introduceți rotația în ore",
|
||||
"protection_section_label": "Protecție",
|
||||
"log_and_stats_section_label": "Jurnal de interogări și statistici",
|
||||
"ignore_query_log": "Ignorați acest client în jurnalul de interogări",
|
||||
"ignore_statistics": "Ignorați acest client în statistici"
|
||||
}
|
||||
|
||||
@@ -125,6 +125,8 @@
|
||||
"top_clients": "Частые клиенты",
|
||||
"no_clients_found": "Клиентов не найдено",
|
||||
"general_statistics": "Общая статистика",
|
||||
"top_upstreams": "Часто запрашиваемые upstream-серверы",
|
||||
"no_upstreams_data_found": "Нет данных об upstream-серверах",
|
||||
"number_of_dns_query_days": "Количество DNS-запросов за последний {{count}} день",
|
||||
"number_of_dns_query_days_plural": "Количество DNS запросов, обработанных за последние {{count}} дней",
|
||||
"number_of_dns_query_24_hours": "Количество DNS-запросов за последние 24 часа",
|
||||
@@ -134,8 +136,9 @@
|
||||
"enforced_save_search": "Применён безопасный поиск",
|
||||
"number_of_dns_query_to_safe_search": "Количество запросов DNS для поисковых систем, для которых был применён Безопасный поиск",
|
||||
"average_processing_time": "Среднее время обработки запроса",
|
||||
"processing_time": "Время обработки",
|
||||
"average_processing_time_hint": "Среднее время для обработки запроса DNS в миллисекундах",
|
||||
"block_domain_use_filters_and_hosts": "Блокировать домены с использованием фильтров и файлов хостов",
|
||||
"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.",
|
||||
@@ -158,6 +161,7 @@
|
||||
"upstream_dns_configured_in_file": "Настроен в {{path}}",
|
||||
"test_upstream_btn": "Тест upstream серверов",
|
||||
"upstreams": "Upstreams",
|
||||
"upstream": "Upstream-сервер",
|
||||
"apply_btn": "Применить",
|
||||
"disabled_filtering_toast": "Фильтрация выкл.",
|
||||
"enabled_filtering_toast": "Фильтрация вкл.",
|
||||
@@ -296,7 +300,7 @@
|
||||
"rate_limit_desc": "Ограничение на количество запросов в секунду для каждого клиента (0 — неограниченно).",
|
||||
"blocking_ipv4_desc": "IP-адрес, возвращаемый при блокировке A-запроса",
|
||||
"blocking_ipv6_desc": "IP-адрес, возвращаемый при блокировке AAAA-запроса",
|
||||
"blocking_mode_default": "Стандартный: Отвечает с нулевым IP-адресом, (0.0.0.0 для A; :: для AAAA) когда заблокировано правилом в стиле Adblock; отвечает с IP-адресом, указанным в правиле, когда заблокировано правилом в стиле /etc/hosts-style",
|
||||
"blocking_mode_default": "Стандартный: Отвечает с нулевым IP-адресом, (0.0.0.0 для A; :: для AAAA) когда заблокировано правилом в стиле Adblock; отвечает с IP-адресом, указанным в правиле, когда заблокировано правилом в стиле файлов hosts",
|
||||
"blocking_mode_refused": "REFUSED: Отвечает с кодом REFUSED",
|
||||
"blocking_mode_nxdomain": "NXDOMAIN: Отвечает с кодом NXDOMAIN\n",
|
||||
"blocking_mode_null_ip": "Нулевой IP: Отвечает с нулевым IP-адресом (0.0.0.0 для A; :: для AAAA)",
|
||||
@@ -444,7 +448,7 @@
|
||||
"client_confirm_delete": "Вы уверены, что хотите удалить клиента «{{key}}»?",
|
||||
"list_confirm_delete": "Вы уверены, что хотите удалить этот список?",
|
||||
"auto_clients_title": "Клиенты (runtime)",
|
||||
"auto_clients_desc": "Несохранённые клиенты, которые могут пользоваться AdGuard Home",
|
||||
"auto_clients_desc": "Информация об IP-адресах устройств, которые используют или могут использовать AdGuard Home. Эта информация собирается из нескольких источников, включая файлы hosts, обратный DNS и так далее.",
|
||||
"access_title": "Настройки доступа",
|
||||
"access_desc": "Здесь вы можете настроить правила доступа к DNS-серверу AdGuard Home",
|
||||
"access_allowed_title": "Разрешённые клиенты",
|
||||
@@ -478,7 +482,9 @@
|
||||
"setup_dns_notice": "Чтобы использовать <1>DNS-over-HTTPS</1> или <1>DNS-over-TLS</1>, вам нужно <0>настроить шифрование</0> в настройках AdGuard Home.",
|
||||
"rewrite_added": "Правило перезаписи DNS-запросов для «{{key}}» успешно добавлено",
|
||||
"rewrite_deleted": "Правило перезаписи DNS-запросов для «{{key}}» успешно удалено",
|
||||
"rewrite_updated": "Правило перезаписи DNS-запросов успешно обновлено",
|
||||
"rewrite_add": "Добавить правило перезаписи DNS-запросов",
|
||||
"rewrite_edit": "Редактировать правило перезаписи DNS-запросов",
|
||||
"rewrite_not_found": "Не найдено правил перезаписи DNS-запросов",
|
||||
"rewrite_confirm_delete": "Вы уверены, что хотите удалить правило перезаписи DNS-запросов для «{{key}}»?",
|
||||
"rewrite_desc": "Позволяет легко настроить пользовательский DNS-ответ для определеннного домена.",
|
||||
|
||||
@@ -153,6 +153,7 @@
|
||||
"enabled_parental_toast": "දෙමාපිය පාලනය සබල කෙරිණි",
|
||||
"disabled_safe_search_toast": "ආරක්ෂිත සෙවුම අබල කෙරිණි",
|
||||
"enabled_save_search_toast": "ආරක්ෂිත සෙවුම සබල කෙරිණි",
|
||||
"updated_save_search_toast": "ආරක්ෂිත සෙවුමේ සැකසුම් යාවත්කාල විය",
|
||||
"enabled_table_header": "සබලයි",
|
||||
"name_table_header": "නම",
|
||||
"list_url_table_header": "ඒ.ස.නි.(URL) ලැයිස්තුව",
|
||||
@@ -237,12 +238,12 @@
|
||||
"query_log_cleared": "විමසුම් සටහන සාර්ථකව හිස් කර ඇත",
|
||||
"query_log_updated": "විමසුම් සටහන සාර්ථකව යාවත්කාල කෙරිණි",
|
||||
"query_log_clear": "විමසුම් සටහන් හිස් කරන්න",
|
||||
"query_log_retention": "විමසුම් සටහන් රඳවා තබා ගැනීම",
|
||||
"query_log_retention": "විමසුම් සටහන් රැඳවීම",
|
||||
"query_log_enable": "සටහන සබල කරන්න",
|
||||
"query_log_configuration": "සටහන් වින්යාසය",
|
||||
"query_log_disabled": "විමසුම් සටහන අබල කර ඇති අතර එය <0>සැකසුම්</0> තුළ වින්යාසගත කළ හැකිය",
|
||||
"query_log_strict_search": "ඉතා නිවැරදිව සෙවීමට ද්විත්ව උද්ධෘතය භාවිතා කරන්න",
|
||||
"query_log_retention_confirm": "විමසුම් සටහන රඳවා තබා ගැනීම වෙනස් කිරීමට ඇවැසි බව ඔබට විශ්වාසද? ඔබ කාල පරතරයෙහි අගය අඩු කළහොත් සමහර දත්ත නැති වී යනු ඇත",
|
||||
"query_log_retention_confirm": "විමසුම් සටහන රඳවා තබා ගැනීම වෙනස් කිරීමට වුවමනා ද? ඔබ කාල පරතරයෙහි අගය අඩු කළහොත් සමහර දත්ත නැති වී යනු ඇත",
|
||||
"anonymize_client_ip": "අනුග්රාහකයෙහි අ.ජා.කෙ. (IP) නිර්නාමික කරන්න",
|
||||
"anonymize_client_ip_desc": "සටහන් සහ සංඛ්යාලේඛන තුළ අනුග්රාහකයේ පූර්ණ අ.ජා.කෙ. ලිපිනය සුරකින්න එපා",
|
||||
"dns_config": "ව.නා.ප. සේවාදායක වින්යාසය",
|
||||
@@ -270,6 +271,8 @@
|
||||
"form_enter_rate_limit": "අනුපාත සීමාව ඇතුල් කරන්න",
|
||||
"rate_limit": "අනුපාත සීමාව",
|
||||
"edns_enable": "EDNS අනුග්රාහක අනුජාලය සබල කරන්න",
|
||||
"edns_use_custom_ip": "EDNS සඳහා අභිරුචි අ.ජා.කෙ. යොදාගන්න",
|
||||
"edns_use_custom_ip_desc": "EDNS සඳහා අභිරුචි අ.ජා.කෙ. භාවිතයට ඉඩදෙන්න",
|
||||
"rate_limit_desc": "එක් අනුග්රාහකයකට ඉඩ දී ඇති තත්පරයට ඉල්ලීම් ගණන. එය 0 ලෙස සැකසීම යනුවෙන් අදහස් කරන්නේ සීමාවක් නැති බවයි.",
|
||||
"blocking_ipv4_desc": "අවහිර කළ A ඉල්ලීමක් සඳහා ආපසු එවිය යුතු අ.ජා.කෙ. (IP) ලිපිනය",
|
||||
"blocking_ipv6_desc": "අවහිර කළ AAAA ඉල්ලීමක් සඳහා ආපසු එවිය යුතු අ.ජා.කෙ. (IP) ලිපිනය",
|
||||
@@ -278,6 +281,9 @@
|
||||
"blocking_mode_nxdomain": "නොපවතින වසම: NXDOMAIN කේතය සමඟ ප්රතිචාර දක්වයි",
|
||||
"blocking_mode_null_ip": "අභිශූන්යය අ.ජා.කෙ.: ශුන්ය අ.ජා.කෙ. ලිපිනය සමඟ ප්රතිචාර දක්වයි (A සඳහා 0.0.0.0; AAAA සඳහා ::)",
|
||||
"blocking_mode_custom_ip": "අභිරුචි අන්තර්ජාල කෙටුම්පත: අතින් සැකසූ අ.ජා. කෙ. ලිපිනයක් සමඟ ප්රතිචාර දක්වයි",
|
||||
"theme_auto": "ස්වයං",
|
||||
"theme_light": "දීප්ත",
|
||||
"theme_dark": "අඳුරු",
|
||||
"upstream_dns_client_desc": "ඔබ මෙම ක්ෂේත්රය හිස්ව තබා ගන්නේ නම්, ඇඩ්ගාර්ඩ් හෝම් විසින් <0>ව.නා.ප. සැකසුම්</0> හි වින්යාසගත කර ඇති සේවාදායක භාවිතා කරනු ඇත.",
|
||||
"tracker_source": "ලුහුබැඳීම් මූලාශ්රය",
|
||||
"source_label": "මූලාශ්රය",
|
||||
@@ -370,6 +376,7 @@
|
||||
"encryption_issuer": "නිකුත් කරන්නා",
|
||||
"encryption_hostnames": "ධාරක නාම",
|
||||
"encryption_reset": "සංකේතාංකන සැකසුම් යළි පිහිටුවීමට අවශ්ය බව ඔබට විශ්වාස ද?",
|
||||
"encryption_warning": "අවවාදයයි",
|
||||
"topline_expiring_certificate": "ඔබගේ SSL සහතිකය කල් ඉකුත්වීමට ආසන්න වී ඇත. <0>සංකේතන සැකසුම්</0> යාවත්කාල කරන්න.",
|
||||
"topline_expired_certificate": "ඔබගේ SSL සහතිකය කල් ඉකුත් වී ඇත. <0>සංකේතන සැකසුම්</0> යාවත්කාල කරන්න.",
|
||||
"form_error_port_range": "80-65535 පරාසය හි තොටක අගයක් ඇතුල් කරන්න",
|
||||
@@ -428,6 +435,7 @@
|
||||
"updates_checked": "ඇඩ්ගාර්ඩ් හෝම් හි නව අනුවාදයක් තිබේ",
|
||||
"updates_version_equal": "ඇඩ්ගාර්ඩ් හෝම් යාවත්කාලීනයි",
|
||||
"check_updates_now": "දැන් යාවත්කාල පරීක්ෂා කරන්න",
|
||||
"version_request_error": "යාවත්කාලීන පරීක්ෂාවට අසමත් විය. ඔබගේ අන්තර්ජාල සම්බන්ධතාවය පරීක්ෂා කරන්න.",
|
||||
"dns_privacy": "ව.නා.ප. රහස්යතා",
|
||||
"setup_dns_privacy_1": "<0>TLS-මගින්-ව.නා.ප.</0> සඳහා <1>{{address}}</1>.",
|
||||
"setup_dns_privacy_2": "<0>HTTPS-මගින්-ව.නා.ප.</0> සඳහා <1>{{address}}</1>.",
|
||||
@@ -446,7 +454,9 @@
|
||||
"setup_dns_notice": "ඔබට <1>HTTPS-මගින්-ව.නා.ප.</1> හෝ <1>DNS-මගින්-ව.නා.ප.</1> භාවිතයට ඇඩ්ගාර්ඩ් හෝම් සැකසුම් තුළ <0>සංකේතනය වින්යාසගත</0> කළ යුතුය.",
|
||||
"rewrite_added": "\"{{key}}\" සඳහා ව.නා.ප. නැවත ලිවීම සාර්ථකව එකතු කෙරිණි",
|
||||
"rewrite_deleted": "\"{{key}}\" සඳහා ව.නා.ප. නැවත ලිවීම ඉවත් කෙරිණි",
|
||||
"rewrite_add": "ව.නා.ප. නැවත ලිවීමක් එකතු කරන්න",
|
||||
"rewrite_updated": "ව.නා.ප. නැවත ලිවීම සාර්ථකව යාවත්කාලීන කෙරිණි",
|
||||
"rewrite_add": "ව.නා.ප. නැවත ලිවීමක් යොදන්න",
|
||||
"rewrite_edit": "ව.නා.ප. නැවත ලිවීම සංස්කරණය",
|
||||
"rewrite_not_found": "ව.නා.ප. නැවත ලිවීම් හමු නොවිණි",
|
||||
"rewrite_confirm_delete": "\"{{key}}\" සඳහා ව.නා.ප. නැවත ලිවීම ඉවත් කිරීමට අවශ්ය බව ඔබට විශ්වාසද?",
|
||||
"rewrite_desc": "නිශ්චිත වසම් නාමයක් සඳහා අභිරුචි ව.නා.ප. ප්රතිචාර පහසුවෙන් වින්යාසගත කිරීමට ඉඩ දෙයි.",
|
||||
@@ -490,8 +500,10 @@
|
||||
"statistics_clear": "සංඛ්යාලේඛන හිස් කරන්න",
|
||||
"statistics_clear_confirm": "සංඛ්යාලේඛන ඉවත් කිරීමට වුවමනා ද?",
|
||||
"statistics_retention_confirm": "සංඛ්යාලේඛන රඳවා තබා ගැනීම වෙනස් කිරීමට අවශ්ය බව ඔබට විශ්වාසද? ඔබ කාල පරතරයෙහි අගය අඩු කළහොත් සමහර දත්ත නැති වී යනු ඇත",
|
||||
"statistics_cleared": "සංඛ්යාලේඛන සාර්ථකව ඉවත් කෙරිණි",
|
||||
"statistics_cleared": "සංඛ්යාලේඛන සාර්ථකව හිස් කෙරිණි",
|
||||
"statistics_enable": "සංඛ්යාලේඛන සබල කරන්න",
|
||||
"ignore_domains": "නොසලකන වසම් (පේළියකට එක බැගින්)",
|
||||
"ignore_domains_title": "නොසලකන වසම්",
|
||||
"interval_hours": "පැය {{count}}",
|
||||
"interval_hours_plural": "පැය {{count}}",
|
||||
"filters_configuration": "පෙරහන් වින්යාසය",
|
||||
@@ -601,5 +613,34 @@
|
||||
"parental_control": "දෙමාපිය පාලනය",
|
||||
"safe_browsing": "ආරක්ෂිත පිරික්සුම",
|
||||
"served_from_cache": "{{value}} <i>(නිහිතයෙන් ගැනිණි)</i>",
|
||||
"form_error_password_length": "මුරපදය අවම වශයෙන් අකුරු {{value}} ක් දිගු විය යුතුමයි"
|
||||
"form_error_password_length": "මුරපදය අවම වශයෙන් අකුරු {{value}} ක් දිගු විය යුතුමයි",
|
||||
"anonymizer_notification": "<0>සටහන:</0> අ.ජා.කෙ. නිර්නාමිකකරණය සබලයි. ඔබට එය <1>පොදු සැකසුම්</1> හරහා අබල කිරීමට හැකිය .",
|
||||
"confirm_dns_cache_clear": "ඔබට ව.නා.ප. නිහිතය හිස් කිරීමට වුවමනාද?",
|
||||
"cache_cleared": "ව.නා.ප. නිහිතය හිස් කෙරිණි",
|
||||
"clear_cache": "නිහිතය මකන්න",
|
||||
"make_static": "ස්ථිතික කරන්න",
|
||||
"theme_auto_desc": "ස්වයං (උපාංගයේ වර්ණ පරිපාටිය මත පදනම්ව)",
|
||||
"theme_dark_desc": "අඳුරු තේමාව",
|
||||
"theme_light_desc": "දීප්ත තේමාව",
|
||||
"disable_for_seconds": "තත්පර {{count}} ක්",
|
||||
"disable_for_seconds_plural": "තත්පර {{count}} ක්",
|
||||
"disable_for_minutes": "විනාඩි {{count}} ක්",
|
||||
"disable_for_minutes_plural": "විනාඩි {{count}} ක්",
|
||||
"disable_for_hours": "පැය {{count}} ක්",
|
||||
"disable_for_hours_plural": "පැය {{count}} ක්",
|
||||
"disable_until_tomorrow": "හෙට වනතුරු",
|
||||
"disable_notify_for_seconds": "තත්. {{count}} කට රැකවරණය අබල කරන්න",
|
||||
"disable_notify_for_seconds_plural": "තත්. {{count}} කට රැකවරණය අබල කරන්න",
|
||||
"disable_notify_for_minutes": "විනාඩි {{count}} කට රැකවරණය අබල කරන්න",
|
||||
"disable_notify_for_minutes_plural": "විනාඩි {{count}} කට රැකවරණය අබල කරන්න",
|
||||
"disable_notify_for_hours": "පැය {{count}} කට රැකවරණය අබල කරන්න",
|
||||
"disable_notify_for_hours_plural": "පැය {{count}} කට රැකවරණය අබල කරන්න",
|
||||
"disable_notify_until_tomorrow": "හෙට වනතුරු රැකවරණය අබල කරන්න",
|
||||
"enable_protection_timer": "{{time}} න් රැකවරණය සබල කෙරේ",
|
||||
"custom_retention_input": "රඳවා ගැනීම පැය වලින්",
|
||||
"custom_rotation_input": "රඳවා ගැනීම පැය වලින්",
|
||||
"protection_section_label": "රැකවරණය",
|
||||
"log_and_stats_section_label": "විමසුම් සටහන හා සංඛ්යාලේඛන",
|
||||
"ignore_query_log": "සටහනෙහි අනුග්රාහකය නොසලකන්න",
|
||||
"ignore_statistics": "සංඛ්යාලේඛනයට අනුග්රාහකය නොසලකන්න"
|
||||
}
|
||||
|
||||
@@ -125,6 +125,8 @@
|
||||
"top_clients": "Najčastejší klienti",
|
||||
"no_clients_found": "Neboli nájdení žiadni klienti",
|
||||
"general_statistics": "Všeobecná štatistika",
|
||||
"top_upstreams": "Často požadované upstream servery",
|
||||
"no_upstreams_data_found": "Nenašli sa žiadne údaje o upstream serveroch",
|
||||
"number_of_dns_query_days": "Počet DNS dopytov spracovaných za posledný {{count}} deň",
|
||||
"number_of_dns_query_days_plural": "Počet DNS dopytov spracovaných za posledných {{count}} dní",
|
||||
"number_of_dns_query_24_hours": "Počet DNS dopytov spracovaných za posledných 24 hodín",
|
||||
@@ -158,6 +160,7 @@
|
||||
"upstream_dns_configured_in_file": "Konfigurované v {{path}}",
|
||||
"test_upstream_btn": "Test upstreamov",
|
||||
"upstreams": "Upstreams",
|
||||
"upstream": "Upstream server",
|
||||
"apply_btn": "Použiť",
|
||||
"disabled_filtering_toast": "Vypnutá filtrácia",
|
||||
"enabled_filtering_toast": "Zapnutá filtrácia",
|
||||
@@ -387,7 +390,7 @@
|
||||
"encryption_key": "Súkromný kľúč",
|
||||
"encryption_key_input": "Skopírujte a prilepte sem svoj súkromný kľúč vo formáte PEM pre Váš certifikát.",
|
||||
"encryption_enable": "Zapnite šifrovanie (HTTPS, DNS-cez-HTTPS a DNS-cez-TLS)",
|
||||
"encryption_enable_desc": "Ak je šifrovanie zapnuté, AdGuard Home administrátorské rozhranie bude pracovať cez HTTPS a DNS server bude počúvať požiadavky cez DNS-cez-HTTPS a DNS-cez-TLS.",
|
||||
"encryption_enable_desc": "Ak je šifrovanie zapnuté, AdGuard Home administrátorské rozhranie bude pracovať cez HTTPS a DNS server bude počúvať dopyty cez DNS-cez-HTTPS a DNS-cez-TLS.",
|
||||
"encryption_chain_valid": "Certifikačný reťazec je platný",
|
||||
"encryption_chain_invalid": "Certifikačný reťazec je neplatný",
|
||||
"encryption_key_valid": "Toto je platný {{type}} súkromný kľúč",
|
||||
@@ -444,7 +447,7 @@
|
||||
"client_confirm_delete": "Naozaj chcete vymazať \"{{key}}\" klienta?",
|
||||
"list_confirm_delete": "Naozaj chcete vymazať tento zoznam?",
|
||||
"auto_clients_title": "Runtime klienti",
|
||||
"auto_clients_desc": "Zariadenia, ktoré nie sú na zozname trvalých klientov, ktorí môžu stále používať AdGuard Home",
|
||||
"auto_clients_desc": "Informácie o IP adresách zariadení, ktoré používajú alebo môžu používať AdGuard Home. Tieto informácie sa získavajú z viacerých zdrojov vrátane súborov hosts, reverzného DNS atď.",
|
||||
"access_title": "Nastavenia prístupu",
|
||||
"access_desc": "Tu môžete konfigurovať pravidlá prístupu pre server DNS AdGuard Home.",
|
||||
"access_allowed_title": "Povolení klienti",
|
||||
@@ -478,7 +481,9 @@
|
||||
"setup_dns_notice": "Pre použitie <1>DNS-over-HTTPS</1> alebo <1>DNS-over-TLS</1>, potrebujete v nastaveniach AdGuard Home <0>nakonfigurovať šifrovanie</0>.",
|
||||
"rewrite_added": "DNS prepísanie pre \"{{key}}\" bolo úspešne pridané",
|
||||
"rewrite_deleted": "DNS prepísanie pre \"{{key}}\" bolo úspešne vymazané",
|
||||
"rewrite_updated": "Prepísanie DNS bolo úspešne aktualizované",
|
||||
"rewrite_add": "Pridať DNS prepísanie",
|
||||
"rewrite_edit": "Upraviť prepísanie DNS",
|
||||
"rewrite_not_found": "Neboli nájdené žiadne DNS prepísania",
|
||||
"rewrite_confirm_delete": "Naozaj chcete odstrániť prepísanie DNS pre \"{{key}}\"?",
|
||||
"rewrite_desc": "Umožňuje ľahko nakonfigurovať vlastnú odpoveď DNS pre konkrétne meno domény.",
|
||||
@@ -495,7 +500,7 @@
|
||||
"blocked_services": "Blokované služby",
|
||||
"blocked_services_desc": "Umožňuje rýchlo blokovať populárne stránky a služby.",
|
||||
"blocked_services_saved": "Blokované služby boli úspešne uložené",
|
||||
"blocked_services_global": "Použite globálne blokované služby",
|
||||
"blocked_services_global": "Použiť globálne blokované služby",
|
||||
"blocked_service": "Blokované služby",
|
||||
"block_all": "Blokovať všetko",
|
||||
"unblock_all": "Odblokovať všetko",
|
||||
@@ -552,7 +557,7 @@
|
||||
"whois": "WHOIS",
|
||||
"filtering_rules_learn_more": "<0>Dozvedieť sa viac</0> o tvorbe vlastných zoznamov hostiteľov.",
|
||||
"blocked_by_response": "Blokované pomocou CNAME alebo IP v odpovedi",
|
||||
"blocked_by_cname_or_ip": "Zablokované na základe CNAME alebo IP",
|
||||
"blocked_by_cname_or_ip": "Blokované pomocou CNAME alebo IP",
|
||||
"try_again": "Skúste znova",
|
||||
"domain_desc": "Zadajte meno domény alebo zástupný znak, ktorý chcete prepísať.",
|
||||
"example_rewrite_domain": "prepísať odpovede iba pre toto meno domény.",
|
||||
@@ -569,7 +574,7 @@
|
||||
"autofix_warning_list": "Bude vykonávať tieto úlohy: <0>Deaktivovať systém DNSStubListener</0> <0>Nastaviť adresu servera DNS na 127.0.0.1</0> <0>Nahradiť cieľový symbolický odkaz /etc/resolv.conf na /run/systemd/resolve/resolv.conf</0> <0>Zastaviť službu DNSStubListener (znova načítať službu systemd-resolved)</0>",
|
||||
"autofix_warning_result": "Výsledkom bude, že všetky DNS dopyty z Vášho systému budú štandardne spracované službou AdGuard Home.",
|
||||
"tags_title": "Tagy",
|
||||
"tags_desc": "Môžete vybrať značky, ktoré zodpovedajú klientovi. Zahrňte značky do pravidiel filtrovania, aby ste ich použili presnejšie. <0>Viac informácií</0>.",
|
||||
"tags_desc": "Môžete vybrať značky, ktoré zodpovedajú klientovi. Zahrňte značky do pravidiel filtrácie, aby ste ich použili presnejšie. <0>Viac informácií</0>.",
|
||||
"form_select_tags": "Zvoľte tagy klienta",
|
||||
"check_title": "Skontrolujte filtráciu",
|
||||
"check_desc": "Skontrolujte, či je názov hostiteľa filtrovaný.",
|
||||
@@ -606,7 +611,7 @@
|
||||
"show_whitelisted_responses": "Obsiahnuté v bielej listine",
|
||||
"show_processed_responses": "Spracované",
|
||||
"blocked_safebrowsing": "Zablokované modulom Bezpečné prehliadanie",
|
||||
"blocked_adult_websites": "Zablokovaná stránka pre dospelých",
|
||||
"blocked_adult_websites": "Zablokované Rodičovskou kontrolou",
|
||||
"blocked_threats": "Zablokované hrozby",
|
||||
"allowed": "Povolené",
|
||||
"filtered": "Filtrované",
|
||||
|
||||
@@ -125,6 +125,8 @@
|
||||
"top_clients": "Najpogostejši odjemalci",
|
||||
"no_clients_found": "Ni najdenih odjemalcev",
|
||||
"general_statistics": "Splošna statistika",
|
||||
"top_upstreams": "Pogosto zahtevani gorvodni strežniki",
|
||||
"no_upstreams_data_found": "Ni podatkov o gorvodnih strežnikih",
|
||||
"number_of_dns_query_days": "Število obdelanih poizvedb DNS v zadnjem {{count}} dnevu",
|
||||
"number_of_dns_query_days_plural": "Število obdelanih poizvedb DNS v zadnjih {{count}} dneh",
|
||||
"number_of_dns_query_24_hours": "Število obdelanih poizvedb DNS v zadnjih 24 urah",
|
||||
@@ -134,6 +136,7 @@
|
||||
"enforced_save_search": "Prisilno varno iskanje",
|
||||
"number_of_dns_query_to_safe_search": "Število zahtev DNS za iskalnike, za katere je bilo uveljavljeno varno iskanje",
|
||||
"average_processing_time": "Povprečni čas obdelave",
|
||||
"processing_time": "Čas obdelave",
|
||||
"average_processing_time_hint": "Povprečni čas v milisekundah pri obdelavi zahteve DNS",
|
||||
"block_domain_use_filters_and_hosts": "Onemogoči domene s filtri in gostiteljskimi datotekami",
|
||||
"filters_block_toggle_hint": "Pravila zaviranja lahko nastavite v nastavitvah <a>Filtri</a>.",
|
||||
@@ -158,6 +161,7 @@
|
||||
"upstream_dns_configured_in_file": "Nastavljen v {{path}}",
|
||||
"test_upstream_btn": "Preizkusi upstreame",
|
||||
"upstreams": "Tokovi navzgor",
|
||||
"upstream": "Gorvodni strežnik",
|
||||
"apply_btn": "Uporabi",
|
||||
"disabled_filtering_toast": "Onemogočeno filtriranje",
|
||||
"enabled_filtering_toast": "Omogočeno filtriranje",
|
||||
@@ -444,7 +448,7 @@
|
||||
"client_confirm_delete": "Ali ste prepričani, da želite izbrisati odjemalca \"{{key}}\"?",
|
||||
"list_confirm_delete": "Ali ste prepričani, da želite izbrisati ta seznam?",
|
||||
"auto_clients_title": "Odjemalci izvajanja",
|
||||
"auto_clients_desc": "Naprave, ki niso na seznamu trajnih odjemalcev, ki morda še vedno uporabljajo AdGuard Home",
|
||||
"auto_clients_desc": "Informacije o naslovih IP naprav, ki uporabljajo ali bi lahko uporabljale AdGuard Home. Te informacije so zbrane iz več virov, vključno z datotekami gostiteljev, povratnim DNS-jem itd.",
|
||||
"access_title": "Nastavitve dostopa",
|
||||
"access_desc": "Tukaj lahko nastavite pravila dostopa strežnika DNS AdGuard Home",
|
||||
"access_allowed_title": "Dovoljeni odjemalci",
|
||||
@@ -478,7 +482,9 @@
|
||||
"setup_dns_notice": "Za uporabo <1>DNS-prek-HTTPS</1> ali <1>DNS-prek-TLS</1>, morate <0>konfigurirati šifriranje</0> v nastavitvah AdGuard Home.",
|
||||
"rewrite_added": "Uspešno je dodano DNS prepisovanje za \"{{key}}\"",
|
||||
"rewrite_deleted": "Uspešno je izbrisano DNS prepisovanje za \"{{key}}\"",
|
||||
"rewrite_updated": "DNS prepisovanje uspešno posodobljen",
|
||||
"rewrite_add": "Dodaj prepisovanje DNS",
|
||||
"rewrite_edit": "Urejanje prepisa DNS",
|
||||
"rewrite_not_found": "Ni bilo najdenih prepisovanj DNS",
|
||||
"rewrite_confirm_delete": "Ali ste prepričani, da želite izbrisati prepisovanje DNS za \"{{key}}\"?",
|
||||
"rewrite_desc": "Omogoča enostavno konfiguriranje odgovora DNS po meri za določeno ime domene.",
|
||||
|
||||
@@ -125,6 +125,8 @@
|
||||
"top_clients": "Najčešći klijenti",
|
||||
"no_clients_found": "Nema pronađenih klijenata",
|
||||
"general_statistics": "Opšte statistike",
|
||||
"top_upstreams": "Često traženi upstream serveri",
|
||||
"no_upstreams_data_found": "Nema podataka o upstream serverima",
|
||||
"number_of_dns_query_days": "Broj obrađenih DNS unosa u poslednjih {{count}} dan",
|
||||
"number_of_dns_query_days_plural": "Broj obrađenih DNS unosa u poslednjih {{count}} dana",
|
||||
"number_of_dns_query_24_hours": "Broj obrađenih DNS unosa u poslednja 24 časa",
|
||||
@@ -158,6 +160,7 @@
|
||||
"upstream_dns_configured_in_file": "Konfiguriši u {{path}}",
|
||||
"test_upstream_btn": "Testiraj upstreams",
|
||||
"upstreams": "Upstreams",
|
||||
"upstream": "Upstream-server",
|
||||
"apply_btn": "Primeni",
|
||||
"disabled_filtering_toast": "Isključeno filtriranje",
|
||||
"enabled_filtering_toast": "Uključeno filtriranje",
|
||||
@@ -167,6 +170,7 @@
|
||||
"enabled_parental_toast": "Uključena roditeljska kontrola",
|
||||
"disabled_safe_search_toast": "Isključena sigurna pretraga",
|
||||
"enabled_save_search_toast": "Uključeno sigurno pretraživanje",
|
||||
"updated_save_search_toast": "Ažurirane postavke bezbedne pretrage",
|
||||
"enabled_table_header": "Uključeno",
|
||||
"name_table_header": "Ime",
|
||||
"list_url_table_header": "URL do liste",
|
||||
@@ -256,12 +260,12 @@
|
||||
"query_log_cleared": "Dnevnik unosa je uspešno očišćen",
|
||||
"query_log_updated": "Dnevnik zapisa je uspešno ažuriran",
|
||||
"query_log_clear": "Očisti dnevnike unosa",
|
||||
"query_log_retention": "Zadržavanje dnevnika unosa",
|
||||
"query_log_retention": "Rotacija evidencija upita",
|
||||
"query_log_enable": "Uključi dnevnik",
|
||||
"query_log_configuration": "Konfiguracija dnevnika",
|
||||
"query_log_disabled": "Dnevnik unosa je isključen ali se može konfigurisati u <0>postavkama</0>",
|
||||
"query_log_strict_search": "Koristi duple navodnike za striktnu pretragu",
|
||||
"query_log_retention_confirm": "Jeste li sigurni da želite da promenite zadržavanje dnevnika unosa? Ako smanjite vrednost intervala, neki podaci će biti izgubljeni",
|
||||
"query_log_retention_confirm": "Želite li zaista da promenite rotaciju evidencije upita? Ako smanjite vrednost intervala, neki podaci će biti izgubljeni",
|
||||
"anonymize_client_ip": "Anonimizuj IP klijenta",
|
||||
"anonymize_client_ip_desc": "Ne čuvaj punu IP adresu klijenta u dnevnicima i statistikama",
|
||||
"dns_config": "Konfiguracija DNS servera",
|
||||
@@ -290,6 +294,8 @@
|
||||
"rate_limit": "Ograničenje brzine",
|
||||
"edns_enable": "Uključi EDNS Client Subnet",
|
||||
"edns_cs_desc": "Dodajte opciju podmreži EDNS klijenta (ECS) uzvodnim zahtevima i evidentirajte vrednosti koje klijenti šalju u evidenciji upita.",
|
||||
"edns_use_custom_ip": "Koristi prilagođeni IP za EDNS",
|
||||
"edns_use_custom_ip_desc": "Dozvoli korišćenje prilagođenog IP-a za EDNS",
|
||||
"rate_limit_desc": "Broj zahteva u sekundi dozvoljen po klijentu. Postavljanje na 0 znači da nema ograničenja.",
|
||||
"blocking_ipv4_desc": "IP adresa koja će biti vraćena za blokirane zahteve",
|
||||
"blocking_ipv6_desc": "IP adresa koja će biti vraćena za blokirane AAAA zahteve",
|
||||
@@ -441,7 +447,7 @@
|
||||
"client_confirm_delete": "Jeste li sigurni da želite da izbrišete klijenta \"{{key}}\"?",
|
||||
"list_confirm_delete": "Jeste li sigurni da želite da izbrišete ovu listu?",
|
||||
"auto_clients_title": "Klijenti (runtime)",
|
||||
"auto_clients_desc": "Uređaji koji nisu na listi upornih klijenata koji i dalje mogu da koriste AdGuard Home",
|
||||
"auto_clients_desc": "Podaci o klijentima koji koriste AdGuard Home, ali nisu sačuvani u konfiguraciji",
|
||||
"access_title": "Postavke pristupa",
|
||||
"access_desc": "Ovde možete konfigurisati pravila pristupa za AdGuard Home DNS server",
|
||||
"access_allowed_title": "Dozvoljeni klijenti",
|
||||
@@ -475,7 +481,9 @@
|
||||
"setup_dns_notice": "Kako biste koristili <1>DNS-over-HTTPS</1> ili <1>DNS-over-TLS</1>, potrebno je da <0>konfigurišete šifrovanje</0> u AdGuard Home postavkama.",
|
||||
"rewrite_added": "DNS prepisivanje za \"{{key}}\" je uspešno dodato",
|
||||
"rewrite_deleted": "DNS prepisivanje za \"{{key}}\" uspešno izbrisano",
|
||||
"rewrite_updated": "DNS ponovo napisao uspešno ažuriran",
|
||||
"rewrite_add": "Dodaj DNS prepisivanje",
|
||||
"rewrite_edit": "Uređivanje DNS prepravke",
|
||||
"rewrite_not_found": "DNS prepisivanja nisu pronađena",
|
||||
"rewrite_confirm_delete": "Jeste li sigurni da želite da izbrišete DNS prepisivanje za \"{{key}}\"?",
|
||||
"rewrite_desc": "Dozvoljava da jednostavno konfigurišete prilagođeni DNS odgovor za određeni domen.",
|
||||
@@ -523,6 +531,10 @@
|
||||
"statistics_retention_confirm": "Jeste li sigurni da želite da promenite zadržavanje statistike? Ako smanjite vrednost intervala, neki podaci će biti izgubljeni",
|
||||
"statistics_cleared": "Statistika je uspešno očišćena",
|
||||
"statistics_enable": "Uključi statistiku",
|
||||
"ignore_domains": "Zanemari domene (razdvojene novom linijom)",
|
||||
"ignore_domains_title": "Zanemareni domeni",
|
||||
"ignore_domains_desc_stats": "Upiti za ove domene nisu upisani u statistiku",
|
||||
"ignore_domains_desc_query": "Upiti za ove domene nisu upisani u evidenciju upita",
|
||||
"interval_hours": "{{count}} čas",
|
||||
"interval_hours_plural": "{{count}} časova",
|
||||
"filters_configuration": "Konfiguracija filtera",
|
||||
@@ -643,5 +655,29 @@
|
||||
"confirm_dns_cache_clear": "Želite li zaista da obrišite DNS keš?",
|
||||
"cache_cleared": "DNS keš je uspešno očišćen",
|
||||
"clear_cache": "Obriši keš memoriju",
|
||||
"protection_section_label": "Zaštita"
|
||||
"make_static": "Učini statičnim",
|
||||
"theme_auto_desc": "Automatski (na osnovu šeme boja uređaja)",
|
||||
"theme_dark_desc": "Tamna tema",
|
||||
"theme_light_desc": "Svetla tema",
|
||||
"disable_for_seconds": "Za {{count}} sekund",
|
||||
"disable_for_seconds_plural": "Za {{count}} sekundi",
|
||||
"disable_for_minutes": "Za {{count}} minut",
|
||||
"disable_for_minutes_plural": "Za {{count}} minuta",
|
||||
"disable_for_hours": "Za {{count}} sat",
|
||||
"disable_for_hours_plural": "Za {{count}} sati",
|
||||
"disable_until_tomorrow": "Do sutra",
|
||||
"disable_notify_for_seconds": "Isključi zaštitu na {{count}} sekund",
|
||||
"disable_notify_for_seconds_plural": "Isključi zaštitu na {{count}} sekundi",
|
||||
"disable_notify_for_minutes": "Isključi zaštitu na {{count}} minut",
|
||||
"disable_notify_for_minutes_plural": "Isključi zaštitu na {{count}} minuta",
|
||||
"disable_notify_for_hours": "Isključi zaštitu na {{count}} sat",
|
||||
"disable_notify_for_hours_plural": "Isključi zaštitu na {{count}} sati",
|
||||
"disable_notify_until_tomorrow": "Isključi zaštitu do sutra",
|
||||
"enable_protection_timer": "Zaštita će biti uključena u {{time}}",
|
||||
"custom_retention_input": "Unesite zadržavanje u časovima",
|
||||
"custom_rotation_input": "Unesite rotaciju u časovima",
|
||||
"protection_section_label": "Zaštita",
|
||||
"log_and_stats_section_label": "Evidencija upita i statistika",
|
||||
"ignore_query_log": "Zanemari ovog klijenta u evidenciji upita",
|
||||
"ignore_statistics": "Zanemari ovog klijenta u statističkim podacima"
|
||||
}
|
||||
|
||||
@@ -125,6 +125,8 @@
|
||||
"top_clients": "Toppklienter",
|
||||
"no_clients_found": "Inga klienter hittade",
|
||||
"general_statistics": "Allmän statistik",
|
||||
"top_upstreams": "Topp uppströmsservrar",
|
||||
"no_upstreams_data_found": "Inga uppströmsdata hittades",
|
||||
"number_of_dns_query_days": "Antalet DNS-förfrågningar som utfördes under senaste {{count}} dagen",
|
||||
"number_of_dns_query_days_plural": "Ett antal DNS förfrågningar utfördes under de senaste {{count}} dagarna",
|
||||
"number_of_dns_query_24_hours": "Antalet DNS-förfrågningar som utfördes under de senaste 24 timmarna",
|
||||
@@ -134,6 +136,7 @@
|
||||
"enforced_save_search": "Aktivering av Säker surf",
|
||||
"number_of_dns_query_to_safe_search": "Antalet DNS-förfrågningar mot sökmotorer där Säker surf tvingats",
|
||||
"average_processing_time": "Genomsnittlig processtid",
|
||||
"processing_time": "Bearbetningstid",
|
||||
"average_processing_time_hint": "Genomsnittlig processtid i millisekunder för DNS-förfrågning",
|
||||
"block_domain_use_filters_and_hosts": "Blockera domäner med filter- och värdfiler",
|
||||
"filters_block_toggle_hint": "Du kan ställa in egna blockerings regler i <a>Filterinställningar</a>.",
|
||||
@@ -158,6 +161,7 @@
|
||||
"upstream_dns_configured_in_file": "Konfigurerad i {{path}}",
|
||||
"test_upstream_btn": "Testa uppströmmar",
|
||||
"upstreams": "Uppströms",
|
||||
"upstream": "Uppströms server",
|
||||
"apply_btn": "Tillämpa",
|
||||
"disabled_filtering_toast": "Filtrering bortkopplad",
|
||||
"enabled_filtering_toast": "Filtrering inkopplad",
|
||||
@@ -475,7 +479,9 @@
|
||||
"setup_dns_notice": "För att kunna använda <1>DNS-över-HTTPS</1> eller <1>DNS-över-TLS</1>, behöver du <0>konfigurera Kryptering</0> i AdGuard Home-inställningar.",
|
||||
"rewrite_added": "DNS-omskrivning för \"{{key}}\" lyckad",
|
||||
"rewrite_deleted": "DNS-omskrivning för \"{{key}}\" har tagits bort",
|
||||
"rewrite_updated": "DNS-omskrivning har uppdaterats",
|
||||
"rewrite_add": "Lägg till DNS omskrivning",
|
||||
"rewrite_edit": "Redigera DNS-omskrivning",
|
||||
"rewrite_not_found": "Inga DNS omskrivningar hittades",
|
||||
"rewrite_confirm_delete": "Är du säker på att du vill ta bort DNS-omskrivningen för \"{{key}}\"?",
|
||||
"rewrite_desc": "Gör det enkelt att konfigurera anpassat DNS svar för ett specifikt domännamn.",
|
||||
|
||||
@@ -172,6 +172,7 @@
|
||||
"dnscrypt": "DNSCrypt",
|
||||
"dns_over_https": "DNS-over-HTTPS",
|
||||
"dns_over_tls": "DNS-over-TLS",
|
||||
"dns_over_quic": "DNS-over-QUIC",
|
||||
"form_enter_rate_limit": "ป้อนขีดจำกัดอัตรา",
|
||||
"rate_limit": "จำกัดอัตรา",
|
||||
"edns_enable": "เปิดใช้งานซับเน็ตไคลเอ็นต์ EDNS",
|
||||
@@ -392,6 +393,7 @@
|
||||
"show_processed_responses": "การประมวลผล",
|
||||
"blocked_adult_websites": "ถูกปิดกั้นโดยการควบคุมของผู้ปกครอง",
|
||||
"safe_search": "ค้นหาอย่างปลอดภัย",
|
||||
"blocklist": "บัญชีดำ",
|
||||
"filter_category_other": "อื่น ๆ",
|
||||
"parental_control": "ควบคุมโดยผู้ปกครอง"
|
||||
}
|
||||
|
||||
@@ -125,6 +125,8 @@
|
||||
"top_clients": "Başlıca istemciler",
|
||||
"no_clients_found": "İstemci bulunamadı",
|
||||
"general_statistics": "Genel istatistikler",
|
||||
"top_upstreams": "Başlıca üst kaynaklar",
|
||||
"no_upstreams_data_found": "Üst kaynak verisi bulunamadı",
|
||||
"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ı",
|
||||
@@ -134,6 +136,7 @@
|
||||
"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",
|
||||
"processing_time": "İşlem süresi",
|
||||
"average_processing_time_hint": "Bir DNS isteğinin milisaniye cinsinden ortalama işlem süresi",
|
||||
"block_domain_use_filters_and_hosts": "Filtre ve hosts dosyalarını kullanarak alan adlarını engelle",
|
||||
"filters_block_toggle_hint": "<a>Filtreler</a> ayarlarında engelleme kuralları oluşturabilirsiniz.",
|
||||
@@ -158,6 +161,7 @@
|
||||
"upstream_dns_configured_in_file": "{{path}} dosyasında yapılandırıldı",
|
||||
"test_upstream_btn": "Üst sunucuyu test et",
|
||||
"upstreams": "Üst kaynak",
|
||||
"upstream": "Üst kaynak",
|
||||
"apply_btn": "Uygula",
|
||||
"disabled_filtering_toast": "Filtreleme devre dışı",
|
||||
"enabled_filtering_toast": "Filtreleme etkin",
|
||||
@@ -444,7 +448,7 @@
|
||||
"client_confirm_delete": "\"{{key}}\" istemcisini silmek istediğinizden emin misiniz?",
|
||||
"list_confirm_delete": "Bu listeyi silmek istediğinizden emin misiniz?",
|
||||
"auto_clients_title": "Çalışma zamanı istemcileri",
|
||||
"auto_clients_desc": "Henüz AdGuard Home'u kullanabilecek Kalıcı istemciler listesinde olmayan cihazlar",
|
||||
"auto_clients_desc": "AdGuard Home'u kullanan veya kullanabilecek cihazların IP adresleri hakkında bilgiler. Bu bilgiler, hosts dosyaları, ters DNS, vb. dahil olmak üzere çeşitli kaynaklardan toplanır.",
|
||||
"access_title": "Erişim ayarları",
|
||||
"access_desc": "AdGuard Home DNS sunucusu için erişim kurallarını buradan yapılandırabilirsiniz",
|
||||
"access_allowed_title": "İzin verilen istemciler",
|
||||
@@ -478,7 +482,9 @@
|
||||
"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_updated": "DNS yeniden yazma başarıyla güncellendi",
|
||||
"rewrite_add": "DNS yeniden yazımı ekle",
|
||||
"rewrite_edit": "DNS yeniden yazmayı düzenle",
|
||||
"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 özel DNS yanıtını kolayca yapılandırmanızı sağlar.",
|
||||
|
||||
@@ -125,6 +125,8 @@
|
||||
"top_clients": "Найактивніші клієнти",
|
||||
"no_clients_found": "Клієнтів не знайдено",
|
||||
"general_statistics": "Загальна статистика",
|
||||
"top_upstreams": "Часто запитувані upstream-сервери",
|
||||
"no_upstreams_data_found": "Немає даних про upstream-сервери",
|
||||
"number_of_dns_query_days": "Кількість DNS-запитів, оброблених за останні {{count}} дні",
|
||||
"number_of_dns_query_days_plural": "Кількість DNS-запитів, оброблених за останні {{count}} днів",
|
||||
"number_of_dns_query_24_hours": "Кількість DNS-запитів, оброблених за останні 24 години",
|
||||
@@ -134,6 +136,7 @@
|
||||
"enforced_save_search": "Примусовий безпечний пошук",
|
||||
"number_of_dns_query_to_safe_search": "Кількість DNS-запитів до пошукових систем, для яких примусово застосований безпечний пошук",
|
||||
"average_processing_time": "Середній час обробки",
|
||||
"processing_time": "Час обробки",
|
||||
"average_processing_time_hint": "Середній час обробки DNS запиту в мілісекундах",
|
||||
"block_domain_use_filters_and_hosts": "Блокування доменів за допомогою фільтрів та hosts-файлів",
|
||||
"filters_block_toggle_hint": "Ви можете налаштувати правила блокування в розділі <a>Фільтри</a>.",
|
||||
@@ -158,6 +161,7 @@
|
||||
"upstream_dns_configured_in_file": "Налаштовано в {{path}}",
|
||||
"test_upstream_btn": "Перевірити сервери",
|
||||
"upstreams": "Upstreams",
|
||||
"upstream": "Upstream-сервер",
|
||||
"apply_btn": "Застосувати",
|
||||
"disabled_filtering_toast": "Фільтрування вимкнено",
|
||||
"enabled_filtering_toast": "Фільтрування увімкнено",
|
||||
@@ -444,7 +448,7 @@
|
||||
"client_confirm_delete": "Ви впевнені, що хочете видалити клієнта «{{key}}»?",
|
||||
"list_confirm_delete": "Ви впевнені, що хочете видалити цей список?",
|
||||
"auto_clients_title": "Runtime-клієнти",
|
||||
"auto_clients_desc": "Клієнти, які використовують AdGuard Home, незалежно від того, чи збережені вони в списку постійних",
|
||||
"auto_clients_desc": "Інформація про IP-адреси пристроїв, які використовують або можуть використовувати AdGuard Home. Ця інформація збирається з кількох джерел, зокрема з файлів hosts, зворотного DNS тощо.",
|
||||
"access_title": "Налаштування доступу",
|
||||
"access_desc": "Тут ви можете налаштувати правила доступу для DNS-сервера AdGuard Home",
|
||||
"access_allowed_title": "Дозволені клієнти",
|
||||
@@ -478,7 +482,9 @@
|
||||
"setup_dns_notice": "Для використання <1>DNS-over-HTTPS</1> або <1>DNS-over-TLS</1>, вам потрібно <0>налаштувати Шифрування</0> в налаштуваннях AdGuard Home.",
|
||||
"rewrite_added": "Перезапис DNS для «{{key}}» успішно додано",
|
||||
"rewrite_deleted": "Перезапис DNS для «{{key}}» успішно видалено",
|
||||
"rewrite_updated": "Перезапис DNS успішно оновлено",
|
||||
"rewrite_add": "Додати перезапис DNS",
|
||||
"rewrite_edit": "Редагувати перезапис DNS",
|
||||
"rewrite_not_found": "Перезаписів DNS не знайдено",
|
||||
"rewrite_confirm_delete": "Ви впевнені, що хочете видалити перезапис DNS для «{{key}}»?",
|
||||
"rewrite_desc": "Дозволяє легко налаштувати власну відповідь DNS для певного доменного імені.",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"client_settings": "Cài đặt máy khách",
|
||||
"client_settings": "Cài đặt thiết bị",
|
||||
"example_upstream_reserved": "ngược dòng <0>cho các miền cụ thể</0>;",
|
||||
"example_upstream_comment": "một lời bình luận.",
|
||||
"upstream_parallel": "Sử dụng truy vấn song song để tăng tốc độ giải quyết bằng cách truy vấn đồng thời tất cả các máy chủ ngược tuyến",
|
||||
@@ -125,6 +125,8 @@
|
||||
"top_clients": "Người dùng hàng đầu",
|
||||
"no_clients_found": "Không có người dùng",
|
||||
"general_statistics": "Thống kê chung",
|
||||
"top_upstreams": "Máy chủ thượng nguồn hàng đầu",
|
||||
"no_upstreams_data_found": "Không tìm thấy dữ liệu máy chủ ngược dòng",
|
||||
"number_of_dns_query_days": "Một số truy vấn DNS được xử lý trong {{count}} ngày qua",
|
||||
"number_of_dns_query_days_plural": "Một số truy vấn DNS được xử lý trong {{count}} ngày qua",
|
||||
"number_of_dns_query_24_hours": "Số yêu cầu DNS đã xử lý trong 24 giờ qua",
|
||||
@@ -134,6 +136,7 @@
|
||||
"enforced_save_search": "Bắt buộc tìm kiếm an toàn",
|
||||
"number_of_dns_query_to_safe_search": "Số yêu cầu DNS tới công cụ tìm kiếm đã chuyển thành tìm kiếm an toàn",
|
||||
"average_processing_time": "Thời gian xử lý trung bình",
|
||||
"processing_time": "Thời gian xử lý",
|
||||
"average_processing_time_hint": "Thời gian trung bình cho một yêu cầu DNS tính bằng mili giây",
|
||||
"block_domain_use_filters_and_hosts": "Chặn tên miền sử dụng các bộ lọc và file hosts",
|
||||
"filters_block_toggle_hint": "Bạn có thể thiết lập quy tắc chặn tại cài đặt <a>Bộ lọc</a>.",
|
||||
@@ -158,6 +161,7 @@
|
||||
"upstream_dns_configured_in_file": "Cấu hình tại {{path}}",
|
||||
"test_upstream_btn": "Kiểm tra",
|
||||
"upstreams": "Nguồn",
|
||||
"upstream": "Máy chủ thượng nguồn",
|
||||
"apply_btn": "Áp dụng",
|
||||
"disabled_filtering_toast": "Đã tắt chặn quảng cáo",
|
||||
"enabled_filtering_toast": "Đã bật chặn quảng cáo",
|
||||
@@ -167,6 +171,7 @@
|
||||
"enabled_parental_toast": "Đã bật quản lý của phụ huynh",
|
||||
"disabled_safe_search_toast": "Đã tắt tìm kiếm an toàn",
|
||||
"enabled_save_search_toast": "Đã bật tìm kiếm an toàn",
|
||||
"updated_save_search_toast": "Cài đặt Tìm kiếm an toàn đã được cập nhật",
|
||||
"enabled_table_header": "Kích hoạt",
|
||||
"name_table_header": "Tên",
|
||||
"list_url_table_header": "URL bộ lọc",
|
||||
@@ -256,12 +261,12 @@
|
||||
"query_log_cleared": "Nhật ký truy vấn đã được xóa thành công",
|
||||
"query_log_updated": "Cập nhật thành công nhật kí truy xuất",
|
||||
"query_log_clear": "Xóa nhật ký truy vấn",
|
||||
"query_log_retention": "Lưu giữ nhật ký truy vấn",
|
||||
"query_log_retention": "Xoay vòng nhật ký truy vấn",
|
||||
"query_log_enable": "Bật nhật ký",
|
||||
"query_log_configuration": "Cấu hình nhật ký",
|
||||
"query_log_disabled": "Nhật ký truy vấn bị vô hiệu hóa và có thể được định cấu hình trong <0>cài đặt</ 0>",
|
||||
"query_log_strict_search": "Sử dụng dấu ngoặc kép để tìm kiếm nghiêm ngặt",
|
||||
"query_log_retention_confirm": "Bạn có chắc chắn muốn thay đổi lưu giữ nhật ký truy vấn? Nếu bạn giảm giá trị khoảng, một số dữ liệu sẽ bị mất",
|
||||
"query_log_retention_confirm": "Bạn có chắc chắn muốn thay đổi xoay vòng nhật ký truy vấn không? Nếu bạn giảm giá trị khoảng thời gian, một số dữ liệu sẽ bị mất",
|
||||
"anonymize_client_ip": "Ẩn danh IP khách",
|
||||
"anonymize_client_ip_desc": "Không lưu địa chỉ IP đầy đủ của khách hàng trong nhật ký và thống kê",
|
||||
"dns_config": "Thiết lập máy chủ DNS",
|
||||
@@ -290,6 +295,8 @@
|
||||
"rate_limit": "Giới hạn yêu cầu",
|
||||
"edns_enable": "Bật mạng con EDNS Client",
|
||||
"edns_cs_desc": "Thêm tùy chọn EDNS Client Subnet (ECS) vào các yêu cầu ngược dòng và ghi lại các giá trị được gửi bởi các máy khách trong nhật ký truy vấn.",
|
||||
"edns_use_custom_ip": "Sử dụng địa chỉ IP tùy chỉnh cho EDNS",
|
||||
"edns_use_custom_ip_desc": "Cho phép sử dụng địa chỉ IP tùy chỉnh cho EDNS",
|
||||
"rate_limit_desc": "Số lượng yêu cầu mỗi giây mà một khách hàng được phép thực hiện (0: không giới hạn)",
|
||||
"blocking_ipv4_desc": "Địa chỉ IP được trả lại cho một yêu cầu A bị chặn",
|
||||
"blocking_ipv6_desc": "Địa chỉ IP được trả lại cho một yêu cầu AAA bị chặn",
|
||||
@@ -441,7 +448,7 @@
|
||||
"client_confirm_delete": "Bạn có chắc chắn muốn xóa máy khách \"{{key}}\" không?",
|
||||
"list_confirm_delete": "Bạn có muốn xóa bộ lọc này?",
|
||||
"auto_clients_title": "Máy khách (thời gian chạy)",
|
||||
"auto_clients_desc": "Các thiết bị không có trong danh sách khách hàng ổn định vẫn có thể sử dụng AdGuard Home",
|
||||
"auto_clients_desc": "Thông tin về địa chỉ IP của thiết bị đang sử dụng hoặc có thể sử dụng AdGuard Home. Thông tin này được thu thập từ nhiều nguồn, bao gồm tệp máy chủ, DNS ngược, v.v.",
|
||||
"access_title": "Cài đặt truy cập",
|
||||
"access_desc": "Tại đây bạn có thể định cấu hình quy tắc truy cập cho máy chủ AdGuard Home DNS",
|
||||
"access_allowed_title": "Máy chủ được phép",
|
||||
@@ -475,7 +482,9 @@
|
||||
"setup_dns_notice": "Để sử dụng <1>DNS-over-HTTPS</1> hoặc <1>DNS-over-TLS</1>, bạn cần <0>định cấu hình Mã hóa</0> trong cài đặt AdGuard Home.",
|
||||
"rewrite_added": "DNS viết lại cho \"{{key}}\" đã thêm thành công",
|
||||
"rewrite_deleted": "DNS viết lại cho \"{{key}}\" đã xóa thành công",
|
||||
"rewrite_updated": "Viết lại DNS được cập nhật thành công",
|
||||
"rewrite_add": "Thêm DNS viết lại",
|
||||
"rewrite_edit": "Chỉnh sửa viết lại DNS",
|
||||
"rewrite_not_found": "Không tìm thấy DNS viết lại",
|
||||
"rewrite_confirm_delete": "Bạn có chắc chắn muốn xóa DNS viết lại cho \"{{key}}\" không?",
|
||||
"rewrite_desc": "Cho phép dễ dàng định cấu hình tùy chỉnh DNS phản hồi cho một tên miền cụ thể.",
|
||||
@@ -523,6 +532,10 @@
|
||||
"statistics_retention_confirm": "Bạn có chắc chắn muốn thay đổi lưu giữ số liệu thống kê? Nếu bạn giảm giá trị khoảng, một số dữ liệu sẽ bị mất",
|
||||
"statistics_cleared": "Xoá thống kê thành công",
|
||||
"statistics_enable": "Bật thống kê",
|
||||
"ignore_domains": "Các miền bị bỏ qua (cách nhau bởi dòng mới)",
|
||||
"ignore_domains_title": "Các miền bị bỏ qua",
|
||||
"ignore_domains_desc_stats": "Các truy vấn cho các miền này sẽ không được ghi vào thống kê",
|
||||
"ignore_domains_desc_query": "Các truy vấn cho các miền này sẽ không được ghi vào nhật ký truy vấn",
|
||||
"interval_hours": "{{count}} giờ",
|
||||
"interval_hours_plural": "{{count}} giờ",
|
||||
"filters_configuration": "Cấu hình bộ lọc",
|
||||
@@ -643,5 +656,29 @@
|
||||
"confirm_dns_cache_clear": "Bạn có chắc chắn muốn xóa bộ đệm ẩn DNS không?",
|
||||
"cache_cleared": "Đã xóa thành công bộ đệm DNS",
|
||||
"clear_cache": "Xóa bộ nhớ cache",
|
||||
"protection_section_label": "Sự bảo vệ"
|
||||
"make_static": "Chuyển sang tĩnh",
|
||||
"theme_auto_desc": "Tự động (dựa trên chủ đề màu của thiết bị của bạn)",
|
||||
"theme_dark_desc": "Chủ đề tối",
|
||||
"theme_light_desc": "Chủ đề sáng",
|
||||
"disable_for_seconds": "Trong {{count}} giây",
|
||||
"disable_for_seconds_plural": "Trong {{count}} giây",
|
||||
"disable_for_minutes": "Trong {{count}} phút",
|
||||
"disable_for_minutes_plural": "Trong {{count}} phút",
|
||||
"disable_for_hours": "Trong {{count}} giờ",
|
||||
"disable_for_hours_plural": "Trong {{count}} giờ",
|
||||
"disable_until_tomorrow": "Cho đến ngày mai",
|
||||
"disable_notify_for_seconds": "Tắt bảo vệ trong {{count}} giây",
|
||||
"disable_notify_for_seconds_plural": "Tắt bảo vệ trong {{count}} giây",
|
||||
"disable_notify_for_minutes": "Tắt bảo vệ trong {{count}} phút",
|
||||
"disable_notify_for_minutes_plural": "Tắt bảo vệ trong {{count}} phút",
|
||||
"disable_notify_for_hours": "Tắt bảo vệ trong {{count}} giờ",
|
||||
"disable_notify_for_hours_plural": "Tắt bảo vệ trong {{count}} giờ",
|
||||
"disable_notify_until_tomorrow": "Vô hiệu hóa bảo vệ cho đến ngày mai",
|
||||
"enable_protection_timer": "Bảo vệ sẽ được bật trong {{time}}",
|
||||
"custom_retention_input": "Nhập thời gian giữ lại theo giờ",
|
||||
"custom_rotation_input": "Nhập chu kỳ theo giờ",
|
||||
"protection_section_label": "Sự bảo vệ",
|
||||
"log_and_stats_section_label": "Nhật ký truy vấn và thống kê",
|
||||
"ignore_query_log": "Bỏ qua máy khách này trong nhật ký truy vấn",
|
||||
"ignore_statistics": "Bỏ qua máy khách này trong thống kê"
|
||||
}
|
||||
|
||||
@@ -125,6 +125,8 @@
|
||||
"top_clients": "客户端排行",
|
||||
"no_clients_found": "未找到客户端",
|
||||
"general_statistics": "概况统计",
|
||||
"top_upstreams": "经常请求的上游服务器",
|
||||
"no_upstreams_data_found": "未找到上游服务器数据",
|
||||
"number_of_dns_query_days": "过去 {{count}} 天内处理的 DNS 查询总数",
|
||||
"number_of_dns_query_days_plural": "在过去的 {{count}} 天内处理了多少个 DNS 查询",
|
||||
"number_of_dns_query_24_hours": "过去 24 小时内处理的 DNS 请求总数",
|
||||
@@ -134,6 +136,7 @@
|
||||
"enforced_save_search": "强制安全搜索",
|
||||
"number_of_dns_query_to_safe_search": "启用强制安全搜索后对搜索引擎的 DNS 请求总数",
|
||||
"average_processing_time": "平均处理时间",
|
||||
"processing_time": "处理时间",
|
||||
"average_processing_time_hint": "处理 DNS 请求的平均时间(毫秒)",
|
||||
"block_domain_use_filters_and_hosts": "使用过滤器和 Hosts 文件以拦截指定域名",
|
||||
"filters_block_toggle_hint": "你可以在 <a>过滤器</a> 设置中添加过滤规则。",
|
||||
@@ -158,6 +161,7 @@
|
||||
"upstream_dns_configured_in_file": "配置路径{{path}}",
|
||||
"test_upstream_btn": "测试上游 DNS",
|
||||
"upstreams": "上游服务器",
|
||||
"upstream": "上游服务器",
|
||||
"apply_btn": "应用",
|
||||
"disabled_filtering_toast": "过滤器已禁用",
|
||||
"enabled_filtering_toast": "过滤器已启用",
|
||||
@@ -444,7 +448,7 @@
|
||||
"client_confirm_delete": "您确定要删除客户端 \"{{key}}\"?",
|
||||
"list_confirm_delete": "您确定要删除此列表吗?",
|
||||
"auto_clients_title": "客户端(运行时间)",
|
||||
"auto_clients_desc": "不在可继续使用 AdGuard Home 的持久客户端列表中的设备。",
|
||||
"auto_clients_desc": "有关正在使用或可能使用 AdGuard Home 的设备的 IP 地址的信息。此信息是从多个来源收集的,包括 hosts 文件、反向 DNS 等。",
|
||||
"access_title": "访问设置",
|
||||
"access_desc": "您可以在此处配置 AdGuard Home 的 DNS 服务器的访问规则",
|
||||
"access_allowed_title": "允许的客户端",
|
||||
@@ -478,7 +482,9 @@
|
||||
"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_updated": "DNS 重写已成功更新",
|
||||
"rewrite_add": "添加 DNS 重写",
|
||||
"rewrite_edit": "编辑 DNS 重写",
|
||||
"rewrite_not_found": "未找到 DNS 重写",
|
||||
"rewrite_confirm_delete": "您确定要删除 \"{{key}}\" 的 DNS 重写?",
|
||||
"rewrite_desc": "可以轻松地为特定域名配置自定义 DNS 响应。",
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
"out_of_range_error": "必須介於 \"{{start}}\" - \"{{end}}\" 範圍之外",
|
||||
"lower_range_start_error": "必須小於起始值",
|
||||
"greater_range_start_error": "必須大於起始值",
|
||||
"gateway_or_subnet_invalid": "無效子網路",
|
||||
"dhcp_form_gateway_input": "閘道 IP 位址",
|
||||
"dhcp_form_subnet_input": "子網路遮罩",
|
||||
"dhcp_form_range_title": "IP 位址範圍",
|
||||
@@ -163,7 +164,7 @@
|
||||
"disabled_parental_toast": "已停用家長監護",
|
||||
"enabled_parental_toast": "已啟用家長監護",
|
||||
"disabled_safe_search_toast": "已停用安全搜尋",
|
||||
"enabled_save_search_toast": "已啟用安全搜尋",
|
||||
"updated_save_search_toast": "已更新安全搜尋設定",
|
||||
"enabled_table_header": "啟用",
|
||||
"name_table_header": "名稱",
|
||||
"list_url_table_header": "清單 URL 網址",
|
||||
@@ -195,6 +196,7 @@
|
||||
"form_error_url_or_path_format": "列表中含有的 URL 網址或絕對路徑",
|
||||
"custom_filter_rules": "自訂過濾規則",
|
||||
"custom_filter_rules_hint": "一行一條規則。您可以使用「adblock」語法或「hosts檔案」的語法。",
|
||||
"system_host_files": "系統 hosts 檔案",
|
||||
"examples_title": "範例",
|
||||
"example_meaning_filter_block": "封鎖對 example.org 網域及其所有子網域的存取",
|
||||
"example_meaning_filter_whitelist": "解除對 example.org 網域及其所有子網域存取封鎖",
|
||||
@@ -209,6 +211,10 @@
|
||||
"example_upstream_doq": "加密 <0>DNS-over-QUIC</0>",
|
||||
"example_upstream_sdns": "您可以使透過 <0>DNS Stamps</0> 來解析 <1>DNSCrypt</1> 或 <2>DNS-over-HTTPS</2>",
|
||||
"example_upstream_tcp": "一般 DNS(透過 TCP)",
|
||||
"example_upstream_regular_port": "一般 DNS(透過 UDP,連接埠)",
|
||||
"example_upstream_udp": "一般 DNS(透過 UDP,主機名稱)",
|
||||
"example_upstream_tcp_port": "一般 DNS(透過 TCP,連接埠)",
|
||||
"example_upstream_tcp_hostname": "一般 DNS(透過 TCP,主機名稱)",
|
||||
"all_lists_up_to_date_toast": "所有清單已更新至最新",
|
||||
"dns_test_ok_toast": "設定中的 DNS 上游運作正常",
|
||||
"dns_test_not_ok_toast": "DNS 設定中的 \"{{key}}\" 出現錯誤,請確認是否正確輸入",
|
||||
@@ -279,6 +285,8 @@
|
||||
"rate_limit": "速率限制",
|
||||
"edns_enable": "啟用 EDNS Client Subnet",
|
||||
"edns_cs_desc": "傳送用戶端的子網路給 DNS 伺服器。",
|
||||
"edns_use_custom_ip": "使用自訂 EDNS IP",
|
||||
"edns_use_custom_ip_desc": "允許使用自訂 EDNS IP",
|
||||
"rate_limit_desc": "限制單一裝置每秒發出的查詢次數(設定為 0 即表示無限制)",
|
||||
"blocking_ipv4_desc": "回覆指定 IPv4 位址給被封鎖的網域的 A 紀錄查詢",
|
||||
"blocking_ipv6_desc": "回覆指定 IPv6 位址給被封鎖的網域的 AAAA 紀錄查詢",
|
||||
@@ -287,6 +295,9 @@
|
||||
"blocking_mode_nxdomain": "NXDOMAIN:回應 NXDOMAIN 狀態碼",
|
||||
"blocking_mode_null_ip": "Null IP:回應零值的 IP 位址(A 紀錄回應 0.0.0.0 ,AAAA 紀錄回應 ::)",
|
||||
"blocking_mode_custom_ip": "自訂 IP 位址:回應一個自訂的 IP 位址",
|
||||
"theme_auto": "自動",
|
||||
"theme_light": "明亮",
|
||||
"theme_dark": "深色",
|
||||
"upstream_dns_client_desc": "如果您將此欄位留白,AdGuard Home 將使用 <0>DNS 設定</0> 內的設定的 DNS 伺服器。",
|
||||
"tracker_source": "追蹤器來源",
|
||||
"source_label": "來源",
|
||||
@@ -397,6 +408,7 @@
|
||||
"dns_providers": "下列為常見的<0> DNS 伺服器</0>。",
|
||||
"update_now": "立即更新",
|
||||
"update_failed": "自動更新發生錯誤。請嘗試依照<a>以下步驟</a> 來手動更新。",
|
||||
"manual_update": "請嘗試依照<a>下列步驟</a>來手動更新。",
|
||||
"processing_update": "請稍候,AdGuard Home 正在更新",
|
||||
"clients_title": "用戶端",
|
||||
"clients_desc": "對已連接到 AdGuard Home 的裝置進行設定",
|
||||
@@ -460,6 +472,7 @@
|
||||
"rewrite_added": "「{{key}}」的 DNS 覆寫新增成功",
|
||||
"rewrite_deleted": "「{{key}}」的 DNS 覆寫刪除成功",
|
||||
"rewrite_add": "新增 DNS 覆寫",
|
||||
"rewrite_edit": "編輯 DNS 覆寫",
|
||||
"rewrite_not_found": "找不到 DNS 覆寫",
|
||||
"rewrite_confirm_delete": "您確定要刪除 \"{{key}}\" 的 DNS 覆寫?",
|
||||
"rewrite_desc": "提供簡單的方式對特定網域自訂 DNS 回應。",
|
||||
@@ -493,6 +506,7 @@
|
||||
"interval_days": "{{count}} 天",
|
||||
"interval_days_plural": "{{count}} 天",
|
||||
"domain": "網域",
|
||||
"ecs": "EDNS 子網",
|
||||
"punycode": "Punycode",
|
||||
"answer": "回應",
|
||||
"filter_added_successfully": "已成功新增清單",
|
||||
@@ -505,6 +519,9 @@
|
||||
"statistics_clear_confirm": "您確定要清除統計資料嗎?",
|
||||
"statistics_retention_confirm": "您確定要更改統計資料保存時間嗎?如果您縮短期限部分資料可能將會遺失",
|
||||
"statistics_cleared": "已清除統計資料",
|
||||
"statistics_enable": "啟用統計數據",
|
||||
"ignore_domains": "已忽略網域(每行一個)",
|
||||
"ignore_domains_title": "已忽略網域",
|
||||
"interval_hours": "{{count}} 小時",
|
||||
"interval_hours_plural": "{{count}} 小時",
|
||||
"filters_configuration": "過濾器設定",
|
||||
@@ -613,5 +630,23 @@
|
||||
"original_response": "原始回應",
|
||||
"click_to_view_queries": "按一下以檢視查詢結果",
|
||||
"port_53_faq_link": "連接埠 53 經常被「DNSStubListener」或「systemd-resolved」服務佔用。請閱讀下列有關解決<0>這個問題</0>的說明",
|
||||
"adg_will_drop_dns_queries": "AdGuard Home 將停止回應此用戶端的所有 DNS 查詢。"
|
||||
"adg_will_drop_dns_queries": "AdGuard Home 將停止回應此用戶端的所有 DNS 查詢。",
|
||||
"safe_browsing": "安全瀏覽",
|
||||
"served_from_cache": "{{value}} <i>(由快取回應)</i>",
|
||||
"form_error_password_length": "密碼必須至少 {{value}} 個字元長度",
|
||||
"make_static": "新增為靜態",
|
||||
"theme_dark_desc": "深色主題",
|
||||
"theme_light_desc": "淺色主題",
|
||||
"disable_for_seconds": "{{count}} 秒",
|
||||
"disable_for_seconds_plural": "{{count}} 秒",
|
||||
"disable_for_minutes": "{{count}} 分鐘",
|
||||
"disable_for_minutes_plural": "{{count}} 分鐘",
|
||||
"disable_for_hours": "{{count}} 小時",
|
||||
"disable_for_hours_plural": "{{count}} 小時",
|
||||
"disable_until_tomorrow": "直到明天",
|
||||
"disable_notify_for_seconds": "暫停防護 {{count}} 秒",
|
||||
"disable_notify_for_seconds_plural": "暫停防護 {{count}} 秒",
|
||||
"disable_notify_for_minutes": "暫停防護 {{count}} 分鐘",
|
||||
"disable_notify_for_minutes_plural": "暫停防護 {{count}} 分鐘",
|
||||
"disable_notify_for_hours": "暫停防護 {{count}} 小時"
|
||||
}
|
||||
|
||||
@@ -125,6 +125,8 @@
|
||||
"top_clients": "熱門用戶端",
|
||||
"no_clients_found": "無已發現之用戶端",
|
||||
"general_statistics": "一般的統計資料",
|
||||
"top_upstreams": "經常請求的上游伺服器",
|
||||
"no_upstreams_data_found": "找不到上游伺服器資料",
|
||||
"number_of_dns_query_days": "在最近的 {{count}} 日內已處理的 DNS 查詢之數量",
|
||||
"number_of_dns_query_days_plural": "在最近的 {{count}} 日內已處理的 DNS 查詢之數量",
|
||||
"number_of_dns_query_24_hours": "在最近的 24 小時內已處理的 DNS 查詢之數量",
|
||||
@@ -134,13 +136,14 @@
|
||||
"enforced_save_search": "已強制執行的安全搜尋",
|
||||
"number_of_dns_query_to_safe_search": "安全搜尋已被強制執行之屬於搜尋引擎的 DNS 請求之數量",
|
||||
"average_processing_time": "平均的處理時間",
|
||||
"processing_time": "處理時間",
|
||||
"average_processing_time_hint": "在處理一項 DNS 請求時以毫秒(ms)計的平均時間",
|
||||
"block_domain_use_filters_and_hosts": "透過過濾器和主機檔案封鎖網域",
|
||||
"filters_block_toggle_hint": "您可在<a>過濾器</a>設定中設置封鎖規則。",
|
||||
"use_adguard_browsing_sec": "使用 AdGuard 瀏覽安全網路服務",
|
||||
"use_adguard_browsing_sec_hint": "AdGuard Home 將檢查該網域是否被瀏覽安全網路服務封鎖。它將使用友好的隱私查找應用程式介面(API)以執行檢查:僅域名 SHA256 雜湊的短前綴被傳送到該伺服器。",
|
||||
"use_adguard_browsing_sec_hint": "AdGuard Home 將檢查該網域是否被瀏覽安全網路服務封鎖。它將使用對隱私友好的查找應用程式介面(API)以執行檢查:僅域名 SHA256 雜湊的短前綴被傳送到該伺服器。",
|
||||
"use_adguard_parental": "使用 AdGuard 家長控制之網路服務",
|
||||
"use_adguard_parental_hint": "AdGuard Home 將檢查網域是否包含成人資料。它使用如同瀏覽安全網路服務一樣之友好的隱私應用程式介面(API)。",
|
||||
"use_adguard_parental_hint": "AdGuard Home 將檢查網域是否包含成人資料。它使用如同瀏覽安全網路服務一樣之對隱私友好的應用程式介面(API)。",
|
||||
"enforce_safe_search": "使用安全搜尋",
|
||||
"enforce_save_search_hint": "AdGuard Home 將在下列的搜尋引擎:Google、YouTube、Bing、DuckDuckGo、Yandex 和 Pixabay 中強制執行安全搜尋。",
|
||||
"no_servers_specified": "無已明確指定的伺服器",
|
||||
@@ -158,6 +161,7 @@
|
||||
"upstream_dns_configured_in_file": "被配置在 {{path}}",
|
||||
"test_upstream_btn": "測試上行資料流",
|
||||
"upstreams": "上游",
|
||||
"upstream": "上游伺服器",
|
||||
"apply_btn": "套用",
|
||||
"disabled_filtering_toast": "已禁用過濾",
|
||||
"enabled_filtering_toast": "已啟用過濾",
|
||||
@@ -444,7 +448,7 @@
|
||||
"client_confirm_delete": "您確定您想要刪除用戶端 \"{{key}}\" 嗎?",
|
||||
"list_confirm_delete": "您確定您想要刪除該清單嗎?",
|
||||
"auto_clients_title": "執行時期用戶端",
|
||||
"auto_clients_desc": "未於可能仍然使用 AdGuard Home 的持續性用戶端之清單上的裝置",
|
||||
"auto_clients_desc": "AdGuard Home 使用或可能使用的裝置的 IP 地址資訊。這些資訊來自多個來源,包括主機檔案、反向 DNS 等。",
|
||||
"access_title": "存取設定",
|
||||
"access_desc": "於此您可配置用於 AdGuard Home DNS 伺服器之存取規則",
|
||||
"access_allowed_title": "已允許的用戶端",
|
||||
@@ -478,7 +482,9 @@
|
||||
"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_updated": "DNS 重寫已成功更新",
|
||||
"rewrite_add": "新增 DNS 改寫",
|
||||
"rewrite_edit": "編輯 DNS 重寫",
|
||||
"rewrite_not_found": "無已發現之 DNS 改寫",
|
||||
"rewrite_confirm_delete": "您確定您想要刪除對於 \"{{key}}\" 之 DNS 改寫嗎?",
|
||||
"rewrite_desc": "允許輕易地配置自訂的 DNS 回應供特定的域名。",
|
||||
|
||||
@@ -38,6 +38,29 @@ export const addRewrite = (config) => async (dispatch) => {
|
||||
}
|
||||
};
|
||||
|
||||
export const updateRewriteRequest = createAction('UPDATE_REWRITE_REQUEST');
|
||||
export const updateRewriteFailure = createAction('UPDATE_REWRITE_FAILURE');
|
||||
export const updateRewriteSuccess = createAction('UPDATE_REWRITE_SUCCESS');
|
||||
|
||||
/**
|
||||
* @param {Object} config
|
||||
* @param {string} config.target - current DNS rewrite value
|
||||
* @param {string} config.update - updated DNS rewrite value
|
||||
*/
|
||||
export const updateRewrite = (config) => async (dispatch) => {
|
||||
dispatch(updateRewriteRequest());
|
||||
try {
|
||||
await apiClient.updateRewrite(config);
|
||||
dispatch(updateRewriteSuccess());
|
||||
dispatch(toggleRewritesModal());
|
||||
dispatch(getRewritesList());
|
||||
dispatch(addSuccessToast(i18next.t('rewrite_updated', { key: config.domain })));
|
||||
} catch (error) {
|
||||
dispatch(addErrorToast({ error }));
|
||||
dispatch(updateRewriteFailure());
|
||||
}
|
||||
};
|
||||
|
||||
export const deleteRewriteRequest = createAction('DELETE_REWRITE_REQUEST');
|
||||
export const deleteRewriteFailure = createAction('DELETE_REWRITE_FAILURE');
|
||||
export const deleteRewriteSuccess = createAction('DELETE_REWRITE_SUCCESS');
|
||||
|
||||
@@ -56,6 +56,8 @@ export const getStats = () => async (dispatch) => {
|
||||
top_clients: topClientsWithInfo,
|
||||
top_queried_domains: normalizeTopStats(stats.top_queried_domains),
|
||||
avg_processing_time: secondsToMilliseconds(stats.avg_processing_time),
|
||||
top_upstreams_responses: normalizeTopStats(stats.top_upstreams_responses),
|
||||
top_upstrems_avg_time: normalizeTopStats(stats.top_upstreams_avg_time),
|
||||
};
|
||||
|
||||
dispatch(getStatsSuccess(normalizedStats));
|
||||
|
||||
@@ -455,6 +455,8 @@ class Api {
|
||||
|
||||
REWRITE_ADD = { path: 'rewrite/add', method: 'POST' };
|
||||
|
||||
REWRITE_UPDATE = { path: 'rewrite/update', method: 'PUT' };
|
||||
|
||||
REWRITE_DELETE = { path: 'rewrite/delete', method: 'POST' };
|
||||
|
||||
getRewritesList() {
|
||||
@@ -470,6 +472,14 @@ class Api {
|
||||
return this.makeRequest(path, method, parameters);
|
||||
}
|
||||
|
||||
updateRewrite(config) {
|
||||
const { path, method } = this.REWRITE_UPDATE;
|
||||
const parameters = {
|
||||
data: config,
|
||||
};
|
||||
return this.makeRequest(path, method, parameters);
|
||||
}
|
||||
|
||||
deleteRewrite(config) {
|
||||
const { path, method } = this.REWRITE_DELETE;
|
||||
const parameters = {
|
||||
|
||||
@@ -6,7 +6,7 @@ import { shallowEqual, useSelector } from 'react-redux';
|
||||
import Card from '../ui/Card';
|
||||
import { formatNumber } from '../../helpers/helpers';
|
||||
import LogsSearchLink from '../ui/LogsSearchLink';
|
||||
import { RESPONSE_FILTER } from '../../helpers/constants';
|
||||
import { RESPONSE_FILTER, DAY } from '../../helpers/constants';
|
||||
import Tooltip from '../ui/Tooltip';
|
||||
|
||||
const Row = ({
|
||||
@@ -54,12 +54,12 @@ const Counters = ({ refreshButton, subtitle }) => {
|
||||
avgProcessingTime,
|
||||
} = useSelector((state) => state.stats, shallowEqual);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const days = interval / DAY;
|
||||
const rows = [
|
||||
{
|
||||
label: 'dns_query',
|
||||
count: numDnsQueries,
|
||||
tooltipTitle: interval === 1 ? 'number_of_dns_query_24_hours' : t('number_of_dns_query_days', { count: interval }),
|
||||
tooltipTitle: days === 1 ? 'number_of_dns_query_24_hours' : t('number_of_dns_query_days', { count: days }),
|
||||
response_status: RESPONSE_FILTER.ALL.QUERY,
|
||||
},
|
||||
{
|
||||
|
||||
79
client/src/components/Dashboard/UpstreamAvgTime.js
Normal file
79
client/src/components/Dashboard/UpstreamAvgTime.js
Normal file
@@ -0,0 +1,79 @@
|
||||
import React from 'react';
|
||||
import ReactTable from 'react-table';
|
||||
import PropTypes from 'prop-types';
|
||||
import round from 'lodash/round';
|
||||
import { withTranslation, Trans } from 'react-i18next';
|
||||
|
||||
import Card from '../ui/Card';
|
||||
import DomainCell from './DomainCell';
|
||||
|
||||
const TimeCell = ({ value }) => {
|
||||
if (!value) {
|
||||
return '–';
|
||||
}
|
||||
|
||||
const valueInMilliseconds = round(value * 1000);
|
||||
|
||||
return (
|
||||
<div className="logs__row o-hidden">
|
||||
<span className="logs__text logs__text--full" title={valueInMilliseconds}>
|
||||
{valueInMilliseconds} ms
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
TimeCell.propTypes = {
|
||||
value: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.number,
|
||||
]),
|
||||
};
|
||||
|
||||
const UpstreamAvgTime = ({
|
||||
t,
|
||||
refreshButton,
|
||||
topUpstreamsAvgTime,
|
||||
subtitle,
|
||||
}) => (
|
||||
<Card
|
||||
title={t('average_processing_time')}
|
||||
subtitle={subtitle}
|
||||
bodyType="card-table"
|
||||
refresh={refreshButton}
|
||||
>
|
||||
<ReactTable
|
||||
data={topUpstreamsAvgTime.map(({ name: domain, count }) => ({
|
||||
domain,
|
||||
count,
|
||||
}))}
|
||||
columns={[
|
||||
{
|
||||
Header: <Trans>upstream</Trans>,
|
||||
accessor: 'domain',
|
||||
Cell: DomainCell,
|
||||
},
|
||||
{
|
||||
Header: <Trans>processing_time</Trans>,
|
||||
accessor: 'count',
|
||||
maxWidth: 190,
|
||||
Cell: TimeCell,
|
||||
},
|
||||
]}
|
||||
showPagination={false}
|
||||
noDataText={t('no_upstreams_data_found')}
|
||||
minRows={6}
|
||||
defaultPageSize={100}
|
||||
className="-highlight card-table-overflow--limited stats__table"
|
||||
/>
|
||||
</Card>
|
||||
);
|
||||
|
||||
UpstreamAvgTime.propTypes = {
|
||||
topUpstreamsAvgTime: PropTypes.array.isRequired,
|
||||
refreshButton: PropTypes.node.isRequired,
|
||||
subtitle: PropTypes.string.isRequired,
|
||||
t: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default withTranslation()(UpstreamAvgTime);
|
||||
81
client/src/components/Dashboard/UpstreamResponses.js
Normal file
81
client/src/components/Dashboard/UpstreamResponses.js
Normal file
@@ -0,0 +1,81 @@
|
||||
import React from 'react';
|
||||
import ReactTable from 'react-table';
|
||||
import PropTypes from 'prop-types';
|
||||
import { withTranslation, Trans } from 'react-i18next';
|
||||
|
||||
import Card from '../ui/Card';
|
||||
import Cell from '../ui/Cell';
|
||||
import DomainCell from './DomainCell';
|
||||
|
||||
import { getPercent } from '../../helpers/helpers';
|
||||
import { STATUS_COLORS } from '../../helpers/constants';
|
||||
|
||||
const CountCell = (totalBlocked) => (
|
||||
function cell(row) {
|
||||
const { value } = row;
|
||||
const percent = getPercent(totalBlocked, value);
|
||||
|
||||
return (
|
||||
<Cell
|
||||
value={value}
|
||||
percent={percent}
|
||||
color={STATUS_COLORS.green}
|
||||
/>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
const getTotalUpstreamRequests = (stats) => {
|
||||
let total = 0;
|
||||
stats.forEach(({ count }) => { total += count; });
|
||||
|
||||
return total;
|
||||
};
|
||||
|
||||
const UpstreamResponses = ({
|
||||
t,
|
||||
refreshButton,
|
||||
topUpstreamsResponses,
|
||||
subtitle,
|
||||
}) => (
|
||||
<Card
|
||||
title={t('top_upstreams')}
|
||||
subtitle={subtitle}
|
||||
bodyType="card-table"
|
||||
refresh={refreshButton}
|
||||
>
|
||||
<ReactTable
|
||||
data={topUpstreamsResponses.map(({ name: domain, count }) => ({
|
||||
domain,
|
||||
count,
|
||||
}))}
|
||||
columns={[
|
||||
{
|
||||
Header: <Trans>upstream</Trans>,
|
||||
accessor: 'domain',
|
||||
Cell: DomainCell,
|
||||
},
|
||||
{
|
||||
Header: <Trans>requests_count</Trans>,
|
||||
accessor: 'count',
|
||||
maxWidth: 190,
|
||||
Cell: CountCell(getTotalUpstreamRequests(topUpstreamsResponses)),
|
||||
},
|
||||
]}
|
||||
showPagination={false}
|
||||
noDataText={t('no_upstreams_data_found')}
|
||||
minRows={6}
|
||||
defaultPageSize={100}
|
||||
className="-highlight card-table-overflow--limited stats__table"
|
||||
/>
|
||||
</Card>
|
||||
);
|
||||
|
||||
UpstreamResponses.propTypes = {
|
||||
topUpstreamsResponses: PropTypes.array.isRequired,
|
||||
refreshButton: PropTypes.node.isRequired,
|
||||
subtitle: PropTypes.string.isRequired,
|
||||
t: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default withTranslation()(UpstreamResponses);
|
||||
@@ -21,6 +21,8 @@ import PageTitle from '../ui/PageTitle';
|
||||
import Loading from '../ui/Loading';
|
||||
import './Dashboard.css';
|
||||
import Dropdown from '../ui/Dropdown';
|
||||
import UpstreamResponses from './UpstreamResponses';
|
||||
import UpstreamAvgTime from './UpstreamAvgTime';
|
||||
|
||||
const Dashboard = ({
|
||||
getAccessList,
|
||||
@@ -136,12 +138,12 @@ const Dashboard = ({
|
||||
<PageTitle title={t('dashboard')} containerClass="page-title--dashboard">
|
||||
<div className="page-title__protection">
|
||||
<button
|
||||
type="button"
|
||||
className={buttonClass}
|
||||
onClick={() => {
|
||||
toggleProtection(protectionEnabled);
|
||||
}}
|
||||
disabled={processingProtection}
|
||||
type="button"
|
||||
className={buttonClass}
|
||||
onClick={() => {
|
||||
toggleProtection(protectionEnabled);
|
||||
}}
|
||||
disabled={processingProtection}
|
||||
>
|
||||
{protectionDisabledDuration
|
||||
? `${t('enable_protection_timer')} ${getRemaningTimeText(protectionDisabledDuration)}`
|
||||
@@ -160,9 +162,9 @@ const Dashboard = ({
|
||||
</Dropdown>}
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-outline-primary btn-sm"
|
||||
onClick={getAllStats}
|
||||
type="button"
|
||||
className="btn btn-outline-primary btn-sm"
|
||||
onClick={getAllStats}
|
||||
>
|
||||
<Trans>refresh_statics</Trans>
|
||||
</button>
|
||||
@@ -185,53 +187,67 @@ const Dashboard = ({
|
||||
</div>
|
||||
)}
|
||||
<Statistics
|
||||
interval={msToDays(stats.interval)}
|
||||
dnsQueries={stats.dnsQueries}
|
||||
blockedFiltering={stats.blockedFiltering}
|
||||
replacedSafebrowsing={stats.replacedSafebrowsing}
|
||||
replacedParental={stats.replacedParental}
|
||||
numDnsQueries={stats.numDnsQueries}
|
||||
numBlockedFiltering={stats.numBlockedFiltering}
|
||||
numReplacedSafebrowsing={stats.numReplacedSafebrowsing}
|
||||
numReplacedParental={stats.numReplacedParental}
|
||||
refreshButton={refreshButton}
|
||||
interval={msToDays(stats.interval)}
|
||||
dnsQueries={stats.dnsQueries}
|
||||
blockedFiltering={stats.blockedFiltering}
|
||||
replacedSafebrowsing={stats.replacedSafebrowsing}
|
||||
replacedParental={stats.replacedParental}
|
||||
numDnsQueries={stats.numDnsQueries}
|
||||
numBlockedFiltering={stats.numBlockedFiltering}
|
||||
numReplacedSafebrowsing={stats.numReplacedSafebrowsing}
|
||||
numReplacedParental={stats.numReplacedParental}
|
||||
refreshButton={refreshButton}
|
||||
/>
|
||||
</div>
|
||||
<div className="col-lg-6">
|
||||
<Counters
|
||||
subtitle={subtitle}
|
||||
refreshButton={refreshButton}
|
||||
subtitle={subtitle}
|
||||
refreshButton={refreshButton}
|
||||
/>
|
||||
</div>
|
||||
<div className="col-lg-6">
|
||||
<Clients
|
||||
subtitle={subtitle}
|
||||
dnsQueries={stats.numDnsQueries}
|
||||
topClients={stats.topClients}
|
||||
clients={dashboard.clients}
|
||||
autoClients={dashboard.autoClients}
|
||||
refreshButton={refreshButton}
|
||||
processingAccessSet={access.processingSet}
|
||||
disallowedClients={access.disallowed_clients}
|
||||
subtitle={subtitle}
|
||||
dnsQueries={stats.numDnsQueries}
|
||||
topClients={stats.topClients}
|
||||
clients={dashboard.clients}
|
||||
autoClients={dashboard.autoClients}
|
||||
refreshButton={refreshButton}
|
||||
processingAccessSet={access.processingSet}
|
||||
disallowedClients={access.disallowed_clients}
|
||||
/>
|
||||
</div>
|
||||
<div className="col-lg-6">
|
||||
<QueriedDomains
|
||||
subtitle={subtitle}
|
||||
dnsQueries={stats.numDnsQueries}
|
||||
topQueriedDomains={stats.topQueriedDomains}
|
||||
refreshButton={refreshButton}
|
||||
subtitle={subtitle}
|
||||
dnsQueries={stats.numDnsQueries}
|
||||
topQueriedDomains={stats.topQueriedDomains}
|
||||
refreshButton={refreshButton}
|
||||
/>
|
||||
</div>
|
||||
<div className="col-lg-6">
|
||||
<BlockedDomains
|
||||
subtitle={subtitle}
|
||||
topBlockedDomains={stats.topBlockedDomains}
|
||||
blockedFiltering={stats.numBlockedFiltering}
|
||||
replacedSafebrowsing={stats.numReplacedSafebrowsing}
|
||||
replacedSafesearch={stats.numReplacedSafesearch}
|
||||
replacedParental={stats.numReplacedParental}
|
||||
refreshButton={refreshButton}
|
||||
subtitle={subtitle}
|
||||
topBlockedDomains={stats.topBlockedDomains}
|
||||
blockedFiltering={stats.numBlockedFiltering}
|
||||
replacedSafebrowsing={stats.numReplacedSafebrowsing}
|
||||
replacedSafesearch={stats.numReplacedSafesearch}
|
||||
replacedParental={stats.numReplacedParental}
|
||||
refreshButton={refreshButton}
|
||||
/>
|
||||
</div>
|
||||
<div className="col-lg-6">
|
||||
<UpstreamResponses
|
||||
subtitle={subtitle}
|
||||
topUpstreamsResponses={stats.topUpstreamsResponses}
|
||||
refreshButton={refreshButton}
|
||||
/>
|
||||
</div>
|
||||
<div className="col-lg-6">
|
||||
<UpstreamAvgTime
|
||||
subtitle={subtitle}
|
||||
topUpstreamsAvgTime={stats.topUpstreamsAvgTime}
|
||||
refreshButton={refreshButton}
|
||||
/>
|
||||
</div>
|
||||
</div>}
|
||||
|
||||
@@ -134,7 +134,6 @@ const Form = (props) => {
|
||||
component={renderInputField}
|
||||
className="form-control"
|
||||
placeholder={t('enter_name_hint')}
|
||||
validate={[validateRequiredValue]}
|
||||
normalizeOnBlur={(data) => data.trim()}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -105,6 +105,7 @@ Form.propTypes = {
|
||||
submitting: PropTypes.bool.isRequired,
|
||||
processingAdd: PropTypes.bool.isRequired,
|
||||
t: PropTypes.func.isRequired,
|
||||
initialValues: PropTypes.object,
|
||||
};
|
||||
|
||||
export default flow([
|
||||
|
||||
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
|
||||
import { Trans, withTranslation } from 'react-i18next';
|
||||
import ReactModal from 'react-modal';
|
||||
|
||||
import { MODAL_TYPE } from '../../../helpers/constants';
|
||||
import Form from './Form';
|
||||
|
||||
const Modal = (props) => {
|
||||
@@ -12,6 +13,8 @@ const Modal = (props) => {
|
||||
toggleRewritesModal,
|
||||
processingAdd,
|
||||
processingDelete,
|
||||
modalType,
|
||||
currentRewrite,
|
||||
} = props;
|
||||
|
||||
return (
|
||||
@@ -24,13 +27,18 @@ const Modal = (props) => {
|
||||
<div className="modal-content">
|
||||
<div className="modal-header">
|
||||
<h4 className="modal-title">
|
||||
<Trans>rewrite_add</Trans>
|
||||
{modalType === MODAL_TYPE.EDIT_REWRITE ? (
|
||||
<Trans>rewrite_edit</Trans>
|
||||
) : (
|
||||
<Trans>rewrite_add</Trans>
|
||||
)}
|
||||
</h4>
|
||||
<button type="button" className="close" onClick={() => toggleRewritesModal()}>
|
||||
<span className="sr-only">Close</span>
|
||||
</button>
|
||||
</div>
|
||||
<Form
|
||||
initialValues={{ ...currentRewrite }}
|
||||
onSubmit={handleSubmit}
|
||||
toggleRewritesModal={toggleRewritesModal}
|
||||
processingAdd={processingAdd}
|
||||
@@ -47,6 +55,8 @@ Modal.propTypes = {
|
||||
toggleRewritesModal: PropTypes.func.isRequired,
|
||||
processingAdd: PropTypes.bool.isRequired,
|
||||
processingDelete: PropTypes.bool.isRequired,
|
||||
modalType: PropTypes.string.isRequired,
|
||||
currentRewrite: PropTypes.object,
|
||||
};
|
||||
|
||||
export default withTranslation()(Modal);
|
||||
|
||||
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
|
||||
import ReactTable from 'react-table';
|
||||
import { withTranslation } from 'react-i18next';
|
||||
import { sortIp } from '../../../helpers/helpers';
|
||||
import { MODAL_TYPE } from '../../../helpers/constants';
|
||||
|
||||
class Table extends Component {
|
||||
cellWrap = ({ value }) => (
|
||||
@@ -31,24 +32,44 @@ class Table extends Component {
|
||||
maxWidth: 100,
|
||||
sortable: false,
|
||||
resizable: false,
|
||||
Cell: (value) => (
|
||||
<div className="logs__row logs__row--center">
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-icon btn-icon--green btn-outline-secondary btn-sm"
|
||||
onClick={() => this.props.handleDelete({
|
||||
answer: value.row.answer,
|
||||
domain: value.row.domain,
|
||||
})
|
||||
}
|
||||
title={this.props.t('delete_table_action')}
|
||||
>
|
||||
<svg className="icons">
|
||||
<use xlinkHref="#delete" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
),
|
||||
Cell: (value) => {
|
||||
const currentRewrite = {
|
||||
answer: value.row.answer,
|
||||
domain: value.row.domain,
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="logs__row logs__row--center">
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-icon btn-outline-primary btn-sm mr-2"
|
||||
onClick={() => {
|
||||
this.props.toggleRewritesModal({
|
||||
type: MODAL_TYPE.EDIT_REWRITE,
|
||||
currentRewrite,
|
||||
});
|
||||
}}
|
||||
disabled={this.props.processingUpdate}
|
||||
title={this.props.t('edit_table_action')}
|
||||
>
|
||||
<svg className="icons icon12">
|
||||
<use xlinkHref="#edit" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-icon btn-outline-secondary btn-sm"
|
||||
onClick={() => this.props.handleDelete(currentRewrite)}
|
||||
title={this.props.t('delete_table_action')}
|
||||
>
|
||||
<svg className="icons">
|
||||
<use xlinkHref="#delete" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@@ -84,7 +105,9 @@ Table.propTypes = {
|
||||
processing: PropTypes.bool.isRequired,
|
||||
processingAdd: PropTypes.bool.isRequired,
|
||||
processingDelete: PropTypes.bool.isRequired,
|
||||
processingUpdate: PropTypes.bool.isRequired,
|
||||
handleDelete: PropTypes.func.isRequired,
|
||||
toggleRewritesModal: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default withTranslation()(Table);
|
||||
|
||||
@@ -6,16 +6,13 @@ import Table from './Table';
|
||||
import Modal from './Modal';
|
||||
import Card from '../../ui/Card';
|
||||
import PageTitle from '../../ui/PageTitle';
|
||||
import { MODAL_TYPE } from '../../../helpers/constants';
|
||||
|
||||
class Rewrites extends Component {
|
||||
componentDidMount() {
|
||||
this.props.getRewritesList();
|
||||
}
|
||||
|
||||
handleSubmit = (values) => {
|
||||
this.props.addRewrite(values);
|
||||
};
|
||||
|
||||
handleDelete = (values) => {
|
||||
// eslint-disable-next-line no-alert
|
||||
if (window.confirm(this.props.t('rewrite_confirm_delete', { key: values.domain }))) {
|
||||
@@ -23,6 +20,19 @@ class Rewrites extends Component {
|
||||
}
|
||||
};
|
||||
|
||||
handleSubmit = (values) => {
|
||||
const { modalType, currentRewrite } = this.props.rewrites;
|
||||
|
||||
if (modalType === MODAL_TYPE.EDIT_REWRITE && currentRewrite) {
|
||||
this.props.updateRewrite({
|
||||
target: currentRewrite,
|
||||
update: values,
|
||||
});
|
||||
} else {
|
||||
this.props.addRewrite(values);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
t,
|
||||
@@ -36,6 +46,9 @@ class Rewrites extends Component {
|
||||
processing,
|
||||
processingAdd,
|
||||
processingDelete,
|
||||
processingUpdate,
|
||||
modalType,
|
||||
currentRewrite,
|
||||
} = rewrites;
|
||||
|
||||
return (
|
||||
@@ -54,13 +67,15 @@ class Rewrites extends Component {
|
||||
processing={processing}
|
||||
processingAdd={processingAdd}
|
||||
processingDelete={processingDelete}
|
||||
processingUpdate={processingUpdate}
|
||||
handleDelete={this.handleDelete}
|
||||
toggleRewritesModal={toggleRewritesModal}
|
||||
/>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-success btn-standard mt-3"
|
||||
onClick={() => toggleRewritesModal()}
|
||||
onClick={() => toggleRewritesModal({ type: MODAL_TYPE.ADD_REWRITE })}
|
||||
disabled={processingAdd}
|
||||
>
|
||||
<Trans>rewrite_add</Trans>
|
||||
@@ -68,10 +83,13 @@ class Rewrites extends Component {
|
||||
|
||||
<Modal
|
||||
isModalOpen={isModalOpen}
|
||||
modalType={modalType}
|
||||
toggleRewritesModal={toggleRewritesModal}
|
||||
handleSubmit={this.handleSubmit}
|
||||
processingAdd={processingAdd}
|
||||
processingDelete={processingDelete}
|
||||
processingUpdate={processingUpdate}
|
||||
currentRewrite={currentRewrite}
|
||||
/>
|
||||
</Fragment>
|
||||
</Card>
|
||||
@@ -86,6 +104,7 @@ Rewrites.propTypes = {
|
||||
toggleRewritesModal: PropTypes.func.isRequired,
|
||||
addRewrite: PropTypes.func.isRequired,
|
||||
deleteRewrite: PropTypes.func.isRequired,
|
||||
updateRewrite: PropTypes.func.isRequired,
|
||||
rewrites: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
|
||||
@@ -48,6 +48,7 @@ class Table extends Component {
|
||||
Header: <Trans>list_url_table_header</Trans>,
|
||||
accessor: 'url',
|
||||
minWidth: 180,
|
||||
// eslint-disable-next-line react/prop-types
|
||||
Cell: ({ value }) => (
|
||||
<div className="logs__row">
|
||||
{isValidAbsolutePath(value) ? value
|
||||
|
||||
@@ -32,6 +32,8 @@ const ProtectionTimer = ({
|
||||
};
|
||||
|
||||
ProtectionTimer.propTypes = {
|
||||
protectionDisabledDuration: PropTypes.number,
|
||||
toggleProtectionSuccess: PropTypes.func.isRequired,
|
||||
setProtectionTimerTime: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ const ClientsTable = ({
|
||||
};
|
||||
|
||||
const handleSubmit = (values) => {
|
||||
const config = values;
|
||||
const config = { ...values };
|
||||
|
||||
if (values) {
|
||||
if (values.blocked_services) {
|
||||
|
||||
@@ -27,7 +27,6 @@ import {
|
||||
} from '../../../helpers/constants';
|
||||
import '../FormButton.css';
|
||||
|
||||
|
||||
const getIntervalTitle = (interval, t) => {
|
||||
switch (interval) {
|
||||
case RETENTION_CUSTOM:
|
||||
|
||||
@@ -7,7 +7,6 @@ import { Trans, withTranslation } from 'react-i18next';
|
||||
import flow from 'lodash/flow';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
|
||||
import {
|
||||
renderRadioField,
|
||||
toNumber,
|
||||
|
||||
@@ -1,25 +1,39 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import LogsSearchLink from './LogsSearchLink';
|
||||
import { formatNumber } from '../../helpers/helpers';
|
||||
|
||||
const Cell = ({
|
||||
value, percent, color, search,
|
||||
}) => <div className="stats__row">
|
||||
<div className="stats__row-value mb-1">
|
||||
<strong><LogsSearchLink search={search}>{formatNumber(value)}</LogsSearchLink></strong>
|
||||
<small className="ml-3 text-muted">{percent}%</small>
|
||||
value,
|
||||
percent,
|
||||
color,
|
||||
search,
|
||||
}) => (
|
||||
<div className="stats__row">
|
||||
<div className="stats__row-value mb-1">
|
||||
<strong>
|
||||
{search ? (
|
||||
<LogsSearchLink search={search}>
|
||||
{formatNumber(value)}
|
||||
</LogsSearchLink>
|
||||
) : (
|
||||
formatNumber(value)
|
||||
)}
|
||||
</strong>
|
||||
<small className="ml-3 text-muted">{percent}%</small>
|
||||
</div>
|
||||
<div className="progress progress-xs">
|
||||
<div
|
||||
className="progress-bar"
|
||||
style={{
|
||||
width: `${percent}%`,
|
||||
backgroundColor: color,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="progress progress-xs">
|
||||
<div
|
||||
className="progress-bar"
|
||||
style={{
|
||||
width: `${percent}%`,
|
||||
backgroundColor: color,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>;
|
||||
);
|
||||
|
||||
Cell.propTypes = {
|
||||
value: PropTypes.number.isRequired,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable react/no-unknown-property */
|
||||
import React from 'react';
|
||||
|
||||
import './Icons.css';
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
getRewritesList,
|
||||
addRewrite,
|
||||
deleteRewrite,
|
||||
updateRewrite,
|
||||
toggleRewritesModal,
|
||||
} from '../actions/rewrites';
|
||||
import Rewrites from '../components/Filters/Rewrites';
|
||||
@@ -17,6 +18,7 @@ const mapDispatchToProps = {
|
||||
getRewritesList,
|
||||
addRewrite,
|
||||
deleteRewrite,
|
||||
updateRewrite,
|
||||
toggleRewritesModal,
|
||||
};
|
||||
|
||||
|
||||
@@ -173,6 +173,8 @@ export const MODAL_TYPE = {
|
||||
ADD_FILTERS: 'ADD_FILTERS',
|
||||
EDIT_FILTERS: 'EDIT_FILTERS',
|
||||
CHOOSE_FILTERING_LIST: 'CHOOSE_FILTERING_LIST',
|
||||
ADD_REWRITE: 'ADD_REWRITE',
|
||||
EDIT_REWRITE: 'EDIT_REWRITE',
|
||||
};
|
||||
|
||||
export const CLIENT_ID = {
|
||||
|
||||
@@ -64,11 +64,11 @@ export default {
|
||||
"homepage": "https://github.com/MasterKia/PersianBlocker",
|
||||
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_19.txt"
|
||||
},
|
||||
"ITA_filtri_dns": {
|
||||
"name": "ITA: Filtri-DNS",
|
||||
"ISR_easyList_hebrew": {
|
||||
"name": "ISR: EasyList Hebrew",
|
||||
"categoryId": "regional",
|
||||
"homepage": "https://filtri-dns.ga/",
|
||||
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_18.txt"
|
||||
"homepage": "https://github.com/easylist/EasyListHebrew",
|
||||
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_43.txt"
|
||||
},
|
||||
"KOR_list_kr": {
|
||||
"name": "KOR: List-KR DNS",
|
||||
@@ -166,18 +166,48 @@ export default {
|
||||
"homepage": "https://github.com/DandelionSprout/adfilt",
|
||||
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_12.txt"
|
||||
},
|
||||
"dandelion_sprouts_anti_push_notifications": {
|
||||
"name": "Dandelion Sprout's Anti Push Notifications",
|
||||
"categoryId": "other",
|
||||
"homepage": "https://github.com/DandelionSprout/adfilt",
|
||||
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_39.txt"
|
||||
},
|
||||
"dandelion_sprouts_game_console_adblock_list": {
|
||||
"name": "Dandelion Sprout's Game Console Adblock List",
|
||||
"categoryId": "other",
|
||||
"homepage": "https://github.com/DandelionSprout/adfilt",
|
||||
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_6.txt"
|
||||
},
|
||||
"hagezi_personal": {
|
||||
"name": "HaGeZi Personal Black \u0026 White",
|
||||
"hagezi_allowlist_referral": {
|
||||
"name": "HaGeZi's Allowlist Referral",
|
||||
"categoryId": "other",
|
||||
"homepage": "https://github.com/hagezi/dns-blocklists#referral",
|
||||
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_45.txt"
|
||||
},
|
||||
"hagezi_antipiracy_blocklist": {
|
||||
"name": "HaGeZi's Anti-Piracy Blocklist",
|
||||
"categoryId": "other",
|
||||
"homepage": "https://github.com/hagezi/dns-blocklists#piracy",
|
||||
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_46.txt"
|
||||
},
|
||||
"hagezi_gambling_blocklist": {
|
||||
"name": "HaGeZi's Gambling Blocklist",
|
||||
"categoryId": "other",
|
||||
"homepage": "https://github.com/hagezi/dns-blocklists#gambling",
|
||||
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_47.txt"
|
||||
},
|
||||
"hagezi_multinormal": {
|
||||
"name": "HaGeZi Multi NORMAL",
|
||||
"categoryId": "general",
|
||||
"homepage": "https://github.com/hagezi/dns-blocklists",
|
||||
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_34.txt"
|
||||
},
|
||||
"hagezi_threat_intelligence_feeds": {
|
||||
"name": "HaGeZi's Threat Intelligence Feeds",
|
||||
"categoryId": "security",
|
||||
"homepage": "https://github.com/hagezi/dns-blocklists",
|
||||
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_44.txt"
|
||||
},
|
||||
"no_google": {
|
||||
"name": "No Google",
|
||||
"categoryId": "other",
|
||||
@@ -220,6 +250,12 @@ export default {
|
||||
"homepage": "https://pgl.yoyo.org/adservers/",
|
||||
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_3.txt"
|
||||
},
|
||||
"phishing_army": {
|
||||
"name": "Phishing Army",
|
||||
"categoryId": "security",
|
||||
"homepage": "https://gitlab.com/malware-filter/phishing-filter",
|
||||
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_18.txt"
|
||||
},
|
||||
"scam_blocklist_by_durablenapkin": {
|
||||
"name": "Scam Blocklist by DurableNapkin",
|
||||
"categoryId": "security",
|
||||
|
||||
@@ -845,7 +845,6 @@ export const sortIp = (a, b) => {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {number} filterId
|
||||
* @returns {string}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -30,7 +30,27 @@ const rewrites = handleActions(
|
||||
[actions.deleteRewriteFailure]: (state) => ({ ...state, processingDelete: false }),
|
||||
[actions.deleteRewriteSuccess]: (state) => ({ ...state, processingDelete: false }),
|
||||
|
||||
[actions.toggleRewritesModal]: (state) => {
|
||||
[actions.updateRewriteRequest]: (state) => ({ ...state, processingUpdate: true }),
|
||||
[actions.updateRewriteFailure]: (state) => ({ ...state, processingUpdate: false }),
|
||||
[actions.updateRewriteSuccess]: (state) => {
|
||||
const newState = {
|
||||
...state,
|
||||
processingUpdate: false,
|
||||
};
|
||||
return newState;
|
||||
},
|
||||
|
||||
[actions.toggleRewritesModal]: (state, { payload }) => {
|
||||
if (payload) {
|
||||
const newState = {
|
||||
...state,
|
||||
modalType: payload.type || '',
|
||||
isModalOpen: !state.isModalOpen,
|
||||
currentRewrite: payload.currentRewrite,
|
||||
};
|
||||
return newState;
|
||||
}
|
||||
|
||||
const newState = {
|
||||
...state,
|
||||
isModalOpen: !state.isModalOpen,
|
||||
@@ -42,7 +62,10 @@ const rewrites = handleActions(
|
||||
processing: true,
|
||||
processingAdd: false,
|
||||
processingDelete: false,
|
||||
processingUpdate: false,
|
||||
isModalOpen: false,
|
||||
modalType: '',
|
||||
currentRewrite: {},
|
||||
list: [],
|
||||
},
|
||||
);
|
||||
|
||||
@@ -58,6 +58,8 @@ const stats = handleActions(
|
||||
num_replaced_safebrowsing: numReplacedSafebrowsing,
|
||||
num_replaced_safesearch: numReplacedSafesearch,
|
||||
avg_processing_time: avgProcessingTime,
|
||||
top_upstreams_responses: topUpstreamsResponses,
|
||||
top_upstrems_avg_time: topUpstreamsAvgTime,
|
||||
} = payload;
|
||||
|
||||
const newState = {
|
||||
@@ -77,6 +79,8 @@ const stats = handleActions(
|
||||
numReplacedSafebrowsing,
|
||||
numReplacedSafesearch,
|
||||
avgProcessingTime,
|
||||
topUpstreamsResponses,
|
||||
topUpstreamsAvgTime,
|
||||
};
|
||||
|
||||
return newState;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# A docker file for scripts/make/build-docker.sh.
|
||||
|
||||
FROM alpine:3.17
|
||||
FROM alpine:3.18
|
||||
|
||||
ARG BUILD_DATE
|
||||
ARG VERSION
|
||||
@@ -25,8 +25,6 @@ RUN apk --no-cache add ca-certificates libcap tzdata && \
|
||||
mkdir -p /opt/adguardhome/conf /opt/adguardhome/work && \
|
||||
chown -R nobody: /opt/adguardhome
|
||||
|
||||
RUN apk --no-cache add tini
|
||||
|
||||
ARG DIST_DIR
|
||||
ARG TARGETARCH
|
||||
ARG TARGETOS
|
||||
@@ -43,45 +41,19 @@ RUN setcap 'cap_net_bind_service=+eip' /opt/adguardhome/AdGuardHome
|
||||
# 68 : UDP : DHCP (client)
|
||||
# 80 : TCP : HTTP (main)
|
||||
# 443 : TCP, UDP : HTTPS, DNS-over-HTTPS (incl. HTTP/3), DNSCrypt (main)
|
||||
# 784 : UDP : DNS-over-QUIC (experimental)
|
||||
# 853 : TCP, UDP : DNS-over-TLS, DNS-over-QUIC
|
||||
# 3000 : TCP, UDP : HTTP(S) (alt, incl. HTTP/3)
|
||||
# 3001 : TCP, UDP : HTTP(S) (beta, incl. HTTP/3)
|
||||
# 5443 : TCP, UDP : DNSCrypt (alt)
|
||||
# 6060 : TCP : HTTP (pprof)
|
||||
# 8853 : UDP : DNS-over-QUIC (experimental)
|
||||
#
|
||||
# TODO(a.garipov): Remove the old, non-standard 784 and 8853 ports for
|
||||
# DNS-over-QUIC in a future release.
|
||||
EXPOSE 53/tcp 53/udp 67/udp 68/udp 80/tcp 443/tcp 443/udp 784/udp\
|
||||
853/tcp 853/udp 3000/tcp 3000/udp 5443/tcp\
|
||||
5443/udp 6060/tcp 8853/udp
|
||||
EXPOSE 53/tcp 53/udp 67/udp 68/udp 80/tcp 443/tcp 443/udp 853/tcp\
|
||||
853/udp 3000/tcp 3000/udp 5443/tcp 5443/udp 6060/tcp
|
||||
|
||||
WORKDIR /opt/adguardhome/work
|
||||
|
||||
# Install helpers for healthcheck.
|
||||
COPY --chown=nobody:nogroup\
|
||||
./${DIST_DIR}/docker/scripts\
|
||||
/opt/adguardhome/scripts
|
||||
|
||||
HEALTHCHECK \
|
||||
--interval=30s \
|
||||
--timeout=10s \
|
||||
--retries=3 \
|
||||
CMD [ "/opt/adguardhome/scripts/healthcheck.sh" ]
|
||||
|
||||
# It seems that the healthckech script sometimes spawns zombie processes, so we
|
||||
# need a way to handle them, since AdGuard Home doesn't know how to keep track
|
||||
# of the processes delegated to it by the OS. Use tini as entry point because
|
||||
# it needs the PID=1 to be the default parent for orphaned processes.
|
||||
#
|
||||
# See https://github.com/adguardTeam/adGuardHome/issues/3290.
|
||||
ENTRYPOINT [ "/sbin/tini", "--" ]
|
||||
ENTRYPOINT ["/opt/adguardhome/AdGuardHome"]
|
||||
|
||||
CMD [ \
|
||||
"/opt/adguardhome/AdGuardHome", \
|
||||
"--no-check-update", \
|
||||
"-c", "/opt/adguardhome/conf/AdGuardHome.yaml", \
|
||||
"-h", "0.0.0.0", \
|
||||
"-w", "/opt/adguardhome/work" \
|
||||
]
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
/^[^[:space:]]/ { is_dns = /^dns:/ }
|
||||
|
||||
/^[[:space:]]+bind_hosts:/ { if (is_dns) prev_line = FNR }
|
||||
|
||||
/^[[:space:]]+- .+/ {
|
||||
if (FNR - prev_line == 1) {
|
||||
addrs[$2] = true
|
||||
prev_line = FNR
|
||||
|
||||
if ($2 == "0.0.0.0" || $2 == "'::'") {
|
||||
# Drop all the other addresses.
|
||||
delete addrs
|
||||
addrs[""] = true
|
||||
prev_line = -1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/^[[:space:]]+port:/ { if (is_dns) port = $2 }
|
||||
|
||||
END {
|
||||
for (addr in addrs) {
|
||||
if (match(addr, ":")) {
|
||||
print "[" addr "]:" port
|
||||
} else {
|
||||
print addr ":" port
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# AdGuard Home Docker healthcheck script
|
||||
|
||||
# Exit the script if a pipeline fails (-e), prevent accidental filename
|
||||
# expansion (-f), and consider undefined variables as errors (-u).
|
||||
set -e -f -u
|
||||
|
||||
# Function error_exit is an echo wrapper that writes to stderr and stops the
|
||||
# script execution with code 1.
|
||||
error_exit() {
|
||||
echo "$1" 1>&2
|
||||
|
||||
exit 1
|
||||
}
|
||||
|
||||
agh_dir="/opt/adguardhome"
|
||||
readonly agh_dir
|
||||
|
||||
filename="${agh_dir}/conf/AdGuardHome.yaml"
|
||||
readonly filename
|
||||
|
||||
if ! [ -f "$filename" ]
|
||||
then
|
||||
wget "http://127.0.0.1:3000" -O /dev/null -q || exit 1
|
||||
|
||||
exit 0
|
||||
fi
|
||||
|
||||
help_dir="${agh_dir}/scripts"
|
||||
readonly help_dir
|
||||
|
||||
# Parse web host
|
||||
|
||||
web_url="$( awk -f "${help_dir}/web-bind.awk" "$filename" )"
|
||||
readonly web_url
|
||||
|
||||
if [ "$web_url" = '' ]
|
||||
then
|
||||
error_exit "no web bindings could be retrieved from $filename"
|
||||
fi
|
||||
|
||||
# TODO(e.burkov): Deal with 0 port.
|
||||
case "$web_url"
|
||||
in
|
||||
(*':0')
|
||||
error_exit '0 in web port is not supported by healthcheck'
|
||||
;;
|
||||
(*)
|
||||
# Go on.
|
||||
;;
|
||||
esac
|
||||
|
||||
# Parse DNS hosts
|
||||
|
||||
dns_hosts="$( awk -f "${help_dir}/dns-bind.awk" "$filename" )"
|
||||
readonly dns_hosts
|
||||
|
||||
if [ "$dns_hosts" = '' ]
|
||||
then
|
||||
error_exit "no DNS bindings could be retrieved from $filename"
|
||||
fi
|
||||
|
||||
first_dns="$( echo "$dns_hosts" | head -n 1 )"
|
||||
readonly first_dns
|
||||
|
||||
# TODO(e.burkov): Deal with 0 port.
|
||||
case "$first_dns"
|
||||
in
|
||||
(*':0')
|
||||
error_exit '0 in DNS port is not supported by healthcheck'
|
||||
;;
|
||||
(*)
|
||||
# Go on.
|
||||
;;
|
||||
esac
|
||||
|
||||
# Check
|
||||
|
||||
# Skip SSL certificate validation since there is no guarantee the container
|
||||
# trusts the one used. It should be safe to drop the SSL validation since the
|
||||
# current script intended to be used from inside the container and only checks
|
||||
# the endpoint availability, ignoring the content of the response.
|
||||
#
|
||||
# See https://github.com/AdguardTeam/AdGuardHome/issues/5642.
|
||||
wget --no-check-certificate "$web_url" -O /dev/null -q || exit 1
|
||||
|
||||
test_fqdn="healthcheck.adguardhome.test."
|
||||
readonly test_fqdn
|
||||
|
||||
# The awk script currently returns only port prefixed with colon in case of
|
||||
# unspecified address.
|
||||
case "$first_dns"
|
||||
in
|
||||
(':'*)
|
||||
nslookup -type=a "$test_fqdn" "127.0.0.1${first_dns}" > /dev/null ||\
|
||||
nslookup -type=a "$test_fqdn" "[::1]${first_dns}" > /dev/null ||\
|
||||
error_exit "nslookup failed for $host"
|
||||
;;
|
||||
(*)
|
||||
echo "$dns_hosts" | while read -r host
|
||||
do
|
||||
nslookup -type=a "$test_fqdn" "$host" > /dev/null ||\
|
||||
error_exit "nslookup failed for $host"
|
||||
done
|
||||
;;
|
||||
esac
|
||||
@@ -1,13 +0,0 @@
|
||||
# Don't consider the HTTPS hostname since the enforced HTTPS redirection should
|
||||
# work if the SSL check skipped. See file docker/healthcheck.sh.
|
||||
/^bind_host:/ { host = $2 }
|
||||
|
||||
/^bind_port:/ { port = $2 }
|
||||
|
||||
END {
|
||||
if (match(host, ":")) {
|
||||
print "http://[" host "]:" port
|
||||
} else {
|
||||
print "http://" host ":" port
|
||||
}
|
||||
}
|
||||
43
go.mod
43
go.mod
@@ -1,11 +1,11 @@
|
||||
module github.com/AdguardTeam/AdGuardHome
|
||||
|
||||
go 1.19
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
github.com/AdguardTeam/dnsproxy v0.50.2
|
||||
github.com/AdguardTeam/golibs v0.13.3
|
||||
github.com/AdguardTeam/urlfilter v0.16.1
|
||||
github.com/AdguardTeam/dnsproxy v0.54.0
|
||||
github.com/AdguardTeam/golibs v0.15.0
|
||||
github.com/AdguardTeam/urlfilter v0.17.0
|
||||
github.com/NYTimes/gziphandler v1.1.1
|
||||
github.com/ameshkov/dnscrypt/v2 v2.2.7
|
||||
github.com/bluele/gcache v0.0.2
|
||||
@@ -15,9 +15,9 @@ require (
|
||||
github.com/go-ping/ping v1.1.0
|
||||
github.com/google/go-cmp v0.5.9
|
||||
github.com/google/gopacket v1.1.19
|
||||
github.com/google/renameio v1.0.1
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/insomniacslk/dhcp v0.0.0-20230516061539-49801966e6cb
|
||||
github.com/google/renameio/v2 v2.0.0
|
||||
github.com/google/uuid v1.3.1
|
||||
github.com/insomniacslk/dhcp v0.0.0-20230816195147-b3ca2534940d
|
||||
github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86
|
||||
github.com/kardianos/service v1.2.2
|
||||
github.com/mdlayher/ethernet v0.0.0-20220221185849-529eae5b6118
|
||||
@@ -26,15 +26,15 @@ require (
|
||||
// TODO(a.garipov): This package is deprecated; find a new one or use our
|
||||
// own code for that. Perhaps, use gopacket.
|
||||
github.com/mdlayher/raw v0.1.0
|
||||
github.com/miekg/dns v1.1.54
|
||||
github.com/quic-go/quic-go v0.35.1
|
||||
github.com/miekg/dns v1.1.55
|
||||
github.com/quic-go/quic-go v0.38.0
|
||||
github.com/stretchr/testify v1.8.4
|
||||
github.com/ti-mo/netfilter v0.5.0
|
||||
go.etcd.io/bbolt v1.3.7
|
||||
golang.org/x/crypto v0.10.0
|
||||
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1
|
||||
golang.org/x/net v0.11.0
|
||||
golang.org/x/sys v0.9.0
|
||||
golang.org/x/crypto v0.12.0
|
||||
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63
|
||||
golang.org/x/net v0.14.0
|
||||
golang.org/x/sys v0.11.0
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
howett.net/plist v1.0.0
|
||||
@@ -48,19 +48,18 @@ require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
|
||||
github.com/golang/mock v1.6.0 // indirect
|
||||
github.com/google/pprof v0.0.0-20230602150820-91b7bce49751 // indirect
|
||||
github.com/google/pprof v0.0.0-20230821062121-407c9e7a662f // indirect
|
||||
github.com/mdlayher/socket v0.4.1 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.10.0 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.12.0 // indirect
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.17 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.18 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/quic-go/qpack v0.4.0 // indirect
|
||||
github.com/quic-go/qtls-go1-19 v0.3.2 // indirect
|
||||
github.com/quic-go/qtls-go1-20 v0.2.2 // indirect
|
||||
github.com/quic-go/qtls-go1-20 v0.3.3 // indirect
|
||||
github.com/u-root/uio v0.0.0-20230305220412-3e8cd9d6bf63 // indirect
|
||||
golang.org/x/mod v0.10.0 // indirect
|
||||
golang.org/x/sync v0.2.0 // indirect
|
||||
golang.org/x/text v0.10.0 // indirect
|
||||
golang.org/x/tools v0.9.3 // indirect
|
||||
golang.org/x/mod v0.12.0 // indirect
|
||||
golang.org/x/sync v0.3.0 // indirect
|
||||
golang.org/x/text v0.12.0 // indirect
|
||||
golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846 // indirect
|
||||
)
|
||||
|
||||
126
go.sum
126
go.sum
@@ -1,16 +1,11 @@
|
||||
github.com/AdguardTeam/dnsproxy v0.50.2 h1:p1471SsMZ6SMo7T51Olw4aNluahvMwSLMorwxYV18ts=
|
||||
github.com/AdguardTeam/dnsproxy v0.50.2/go.mod h1:CQhZTkqC8X0ID6glrtyaxgqRRdiYfn1gJulC1cZ5Dn8=
|
||||
github.com/AdguardTeam/golibs v0.4.0/go.mod h1:skKsDKIBB7kkFflLJBpfGX+G8QFTx0WKUzB6TIgtUj4=
|
||||
github.com/AdguardTeam/golibs v0.10.4/go.mod h1:rSfQRGHIdgfxriDDNgNJ7HmE5zRoURq8R+VdR81Zuzw=
|
||||
github.com/AdguardTeam/golibs v0.13.3 h1:RT3QbzThtaLiFLkIUDS6/hlGEXrh0zYvdf4bd7UWpGo=
|
||||
github.com/AdguardTeam/golibs v0.13.3/go.mod h1:wkJ6EUsN4np/9Gp7+9QeooY9E2U2WCLJYAioLCzkHsI=
|
||||
github.com/AdguardTeam/gomitmproxy v0.2.0/go.mod h1:Qdv0Mktnzer5zpdpi5rAwixNJzW2FN91LjKJCkVbYGU=
|
||||
github.com/AdguardTeam/urlfilter v0.16.1 h1:ZPi0rjqo8cQf2FVdzo6cqumNoHZx2KPXj2yZa1A5BBw=
|
||||
github.com/AdguardTeam/urlfilter v0.16.1/go.mod h1:46YZDOV1+qtdRDuhZKVPSSp7JWWes0KayqHrKAFBdEI=
|
||||
github.com/AdguardTeam/dnsproxy v0.54.0 h1:OgSitM/EKrMMOi+guWZNwaU1cqRqJKWgR3l3fPWWayI=
|
||||
github.com/AdguardTeam/dnsproxy v0.54.0/go.mod h1:tG/treaQekcKnugYoKOfm8vt3JGi6CliWta0MkQr15U=
|
||||
github.com/AdguardTeam/golibs v0.15.0 h1:yOv/fdVkJIOWKr0NlUXAE9RA0DK9GKiBbiGzq47vY7o=
|
||||
github.com/AdguardTeam/golibs v0.15.0/go.mod h1:66ZLs8P7nk/3IfKroQ1rqtieLk+5eXYXMBKXlVL7KeI=
|
||||
github.com/AdguardTeam/urlfilter v0.17.0 h1:tUzhtR9wMx704GIP3cibsDQJrixlMHfwoQbYJfPdFow=
|
||||
github.com/AdguardTeam/urlfilter v0.17.0/go.mod h1:bbuZjPUzm/Ip+nz5qPPbwIP+9rZyQbQad8Lt/0fCulU=
|
||||
github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I=
|
||||
github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
|
||||
github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA=
|
||||
github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8=
|
||||
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY=
|
||||
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA=
|
||||
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 h1:52m0LGchQBBVqJRyYYufQuIbVqRawmubW3OFGqK1ekw=
|
||||
@@ -23,7 +18,6 @@ github.com/beefsack/go-rate v0.0.0-20220214233405-116f4ca011a0 h1:0b2vaepXIfMsG+
|
||||
github.com/beefsack/go-rate v0.0.0-20220214233405-116f4ca011a0/go.mod h1:6YNgTHLutezwnBvyneBbwvB8C82y3dcoOj5EQJIdGXA=
|
||||
github.com/bluele/gcache v0.0.2 h1:WcbfdXICg7G/DGBh1PFfcirkWOQV+v077yF1pSy3DGw=
|
||||
github.com/bluele/gcache v0.0.2/go.mod h1:m15KV+ECjptwSPxKhOhQoAFQVtUFjTVkc3H8o0t/fp0=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@@ -34,8 +28,7 @@ github.com/dimfeld/httptreemux/v5 v5.5.0/go.mod h1:QeEylH57C0v3VO0tkKraVz9oD3Uu9
|
||||
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
|
||||
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
||||
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
|
||||
github.com/go-ole/go-ole v1.2.5 h1:t4MGB5xEDZvXI+0rMjjsfBsD7yAgp/s9ZDkL1JndXwY=
|
||||
github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||
github.com/go-ping/ping v1.1.0 h1:3MCGhVX4fyEUuhsfwPrsEdQw6xspHkv5zHsiSoDFZYw=
|
||||
github.com/go-ping/ping v1.1.0/go.mod h1:xIFjORFzTxqIV/tDVGO4eDy/bLuSyawEeojSm3GfRGk=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
||||
@@ -50,16 +43,16 @@ github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
|
||||
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
|
||||
github.com/google/pprof v0.0.0-20230602150820-91b7bce49751 h1:hR7/MlvK23p6+lIw9SN1TigNLn9ZnF3W4SYRKq2gAHs=
|
||||
github.com/google/pprof v0.0.0-20230602150820-91b7bce49751/go.mod h1:Jh3hGz2jkYak8qXPD19ryItVnUgpgeqzdkY/D0EaeuA=
|
||||
github.com/google/renameio v1.0.1 h1:Lh/jXZmvZxb0BBeSY5VKEfidcbcbenKjZFzM/q0fSeU=
|
||||
github.com/google/renameio v1.0.1/go.mod h1:t/HQoYBZSsWSNK35C6CO/TpPLDVWvxOHboWUAweKUpk=
|
||||
github.com/google/pprof v0.0.0-20230821062121-407c9e7a662f h1:pDhu5sgp8yJlEF/g6osliIIpF9K4F5jvkULXa4daRDQ=
|
||||
github.com/google/pprof v0.0.0-20230821062121-407c9e7a662f/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
|
||||
github.com/google/renameio/v2 v2.0.0 h1:UifI23ZTGY8Tt29JbYFiuyIU3eX+RNFtUwefq9qAhxg=
|
||||
github.com/google/renameio/v2 v2.0.0/go.mod h1:BtmJXm5YlszgC+TD4HOEEUFgkJP3nLxehU6hfe7jRt4=
|
||||
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
|
||||
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714 h1:/jC7qQFrv8CrSJVmaolDVOxTfS9kc36uB6H40kdbQq8=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20230516061539-49801966e6cb h1:6fDKEAXwe3rsfS4khW3EZ8kEqmSiV9szhMPcDrD+Y7Q=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20230516061539-49801966e6cb/go.mod h1:7474bZ1YNCvarT6WFKie4kEET6J0KYRDC4XJqqXzQW4=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20230816195147-b3ca2534940d h1:Ka64cclWedOkGzm9M2/XYuwJUdmWRUozmsxW0PyKA3A=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20230816195147-b3ca2534940d/go.mod h1:7474bZ1YNCvarT6WFKie4kEET6J0KYRDC4XJqqXzQW4=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/josharian/native v1.0.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||
github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||
@@ -67,10 +60,8 @@ github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86 h1:elKwZS1OcdQ0
|
||||
github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86/go.mod h1:aFAMtuldEgx/4q7iSGazk22+IcgvtiC+HIimFO9XlS8=
|
||||
github.com/kardianos/service v1.2.2 h1:ZvePhAHfvo0A7Mftk/tEzqEZ7Q4lgnR8sGz4xu1YX60=
|
||||
github.com/kardianos/service v1.2.2/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM=
|
||||
github.com/kr/pty v1.1.1/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/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
|
||||
github.com/mdlayher/ethernet v0.0.0-20220221185849-529eae5b6118 h1:2oDp6OOhLxQ9JBoUuysVz9UZ9uI6oLUbvAZu0x8o+vE=
|
||||
github.com/mdlayher/ethernet v0.0.0-20220221185849-529eae5b6118/go.mod h1:ZFUnHIVchZ9lJoWoEGUg8Q3M4U8aNNWA3CVSUTkW4og=
|
||||
github.com/mdlayher/netlink v0.0.0-20190313131330-258ea9dff42c/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA=
|
||||
@@ -84,115 +75,99 @@ github.com/mdlayher/raw v0.1.0/go.mod h1:yXnxvs6c0XoF/aK52/H5PjsVHmWBCFfZUfoh/Y5
|
||||
github.com/mdlayher/socket v0.2.1/go.mod h1:QLlNPkFR88mRUNQIzRBMfXxwKal8H7u1h3bL1CV+f0E=
|
||||
github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U=
|
||||
github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA=
|
||||
github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
|
||||
github.com/miekg/dns v1.1.54 h1:5jon9mWcb0sFJGpnI99tOMhCPyJ+RPVz5b63MQG0VWI=
|
||||
github.com/miekg/dns v1.1.54/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
|
||||
github.com/miekg/dns v1.1.55 h1:GoQ4hpsj0nFLYe+bWiCToyrBEJXkQfOOIvFGFy0lEgo=
|
||||
github.com/miekg/dns v1.1.55/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/onsi/ginkgo/v2 v2.10.0 h1:sfUl4qgLdvkChZrWCYndY2EAu9BRIw1YphNAzy1VNWs=
|
||||
github.com/onsi/ginkgo/v2 v2.10.0/go.mod h1:UDQOh5wbQUlMnkLfVaIUMtQ1Vus92oM+P2JX1aulgcE=
|
||||
github.com/onsi/gomega v1.27.7 h1:fVih9JD6ogIiHUN6ePK7HJidyEDpWGVB5mzM7cWNXoU=
|
||||
github.com/onsi/ginkgo/v2 v2.12.0 h1:UIVDowFPwpg6yMUpPjGkYvf06K3RAiJXUhCxEwQVHRI=
|
||||
github.com/onsi/ginkgo/v2 v2.12.0/go.mod h1:ZNEzXISYlqpb8S36iN71ifqLi3vVD1rVJGvWRCJOUpQ=
|
||||
github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI=
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
||||
github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pierrec/lz4/v4 v4.1.17 h1:kV4Ip+/hUBC+8T6+2EgburRtkE9ef4nbY3f4dFhGjMc=
|
||||
github.com/pierrec/lz4/v4 v4.1.17/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pierrec/lz4/v4 v4.1.18 h1:xaKrnTkyoqfh1YItXl56+6KJNVYWlEEPuAQW9xsplYQ=
|
||||
github.com/pierrec/lz4/v4 v4.1.18/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
|
||||
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
|
||||
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
|
||||
github.com/quic-go/qtls-go1-19 v0.3.2 h1:tFxjCFcTQzK+oMxG6Zcvp4Dq8dx4yD3dDiIiyc86Z5U=
|
||||
github.com/quic-go/qtls-go1-19 v0.3.2/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI=
|
||||
github.com/quic-go/qtls-go1-20 v0.2.2 h1:WLOPx6OY/hxtTxKV1Zrq20FtXtDEkeY00CGQm8GEa3E=
|
||||
github.com/quic-go/qtls-go1-20 v0.2.2/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM=
|
||||
github.com/quic-go/quic-go v0.35.1 h1:b0kzj6b/cQAf05cT0CkQubHM31wiA+xH3IBkxP62poo=
|
||||
github.com/quic-go/quic-go v0.35.1/go.mod h1:+4CVgVppm0FNjpG3UcX8Joi/frKOH7/ciD5yGcwOO1g=
|
||||
github.com/shirou/gopsutil/v3 v3.21.8 h1:nKct+uP0TV8DjjNiHanKf8SAuub+GNsbrOtM9Nl9biA=
|
||||
github.com/shirou/gopsutil/v3 v3.21.8/go.mod h1:YWp/H8Qs5fVmf17v7JNZzA0mPJ+mS2e9JdiUF9LlKzQ=
|
||||
github.com/quic-go/qtls-go1-20 v0.3.3 h1:17/glZSLI9P9fDAeyCHBFSWSqJcwx1byhLwP5eUIDCM=
|
||||
github.com/quic-go/qtls-go1-20 v0.3.3/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
|
||||
github.com/quic-go/quic-go v0.38.0 h1:T45lASr5q/TrVwt+jrVccmqHhPL2XuSyoCLVCpfOSLc=
|
||||
github.com/quic-go/quic-go v0.38.0/go.mod h1:MPCuRq7KBK2hNcfKj/1iD1BGuN3eAYMeNxp3T42LRUg=
|
||||
github.com/shirou/gopsutil/v3 v3.23.7 h1:C+fHO8hfIppoJ1WdsVm1RoI0RwXoNdfTK7yWXV0wVj4=
|
||||
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/ti-mo/netfilter v0.2.0/go.mod h1:8GbBGsY/8fxtyIdfwy29JiluNcPK4K7wIT+x42ipqUU=
|
||||
github.com/ti-mo/netfilter v0.5.0 h1:MZmsUw5bFRecOb0AeyjOPxTHg4UxYzyEs0Ek/6Lxoy8=
|
||||
github.com/ti-mo/netfilter v0.5.0/go.mod h1:nt+8B9hx/QpqHr7Hazq+2qMCCA8u2OTkyc/7+U9ARz8=
|
||||
github.com/tklauser/go-sysconf v0.3.9 h1:JeUVdAOWhhxVcU6Eqr/ATFHgXk/mmiItdKeJPev3vTo=
|
||||
github.com/tklauser/go-sysconf v0.3.9/go.mod h1:11DU/5sG7UexIrp/O6g35hrWzu0JxlwQ3LSFUzyeuhs=
|
||||
github.com/tklauser/numcpus v0.3.0 h1:ILuRUQBtssgnxw0XXIjKUC56fgnOrFoQQ/4+DeU2biQ=
|
||||
github.com/tklauser/numcpus v0.3.0/go.mod h1:yFGUr7TUHQRAhyqBcEg0Ge34zDBAsIvJJcyE6boqnA8=
|
||||
github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM=
|
||||
github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms=
|
||||
github.com/u-root/uio v0.0.0-20230305220412-3e8cd9d6bf63 h1:YcojQL98T/OO+rybuzn2+5KrD5dBwXIvYBvQ2cD3Avg=
|
||||
github.com/u-root/uio v0.0.0-20230305220412-3e8cd9d6bf63/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw=
|
||||
go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ=
|
||||
go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM=
|
||||
golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=
|
||||
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc=
|
||||
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
|
||||
golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
|
||||
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
|
||||
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 h1:m64FZMko/V45gv0bNmrNYoDEq8U5YUhetc9cBWKS1TQ=
|
||||
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63/go.mod h1:0v4NqG35kSWCMzLaMeX+IQrlSnVE/bqGSyC2cz/9Le8=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk=
|
||||
golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210929193557-e81a3d93ecf6/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU=
|
||||
golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ=
|
||||
golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14=
|
||||
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI=
|
||||
golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190322080309-f49334f85ddc/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/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-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210909193231-528a39cd75f3/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.4.1-0.20230131160137-e7d7f63158de/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s=
|
||||
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
|
||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58=
|
||||
golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc=
|
||||
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.9.3 h1:Gn1I8+64MsuTb/HpH+LmQtNas23LhUVr3rYZ0eKuaMM=
|
||||
golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
|
||||
golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846 h1:Vve/L0v7CXXuxUmaMGIEK/dEeq7uiqb5qBgQrZzIE7E=
|
||||
golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM=
|
||||
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=
|
||||
@@ -200,12 +175,9 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T
|
||||
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
|
||||
gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
package aghio
|
||||
package aghio_test
|
||||
|
||||
import (
|
||||
"io"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghio"
|
||||
"github.com/AdguardTeam/golibs/testutil"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -31,7 +32,7 @@ func TestLimitReader(t *testing.T) {
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
_, err := LimitReader(nil, tc.n)
|
||||
_, err := aghio.LimitReader(nil, tc.n)
|
||||
testutil.AssertErrorMsg(t, tc.wantErrMsg, err)
|
||||
})
|
||||
}
|
||||
@@ -57,7 +58,7 @@ func TestLimitedReader_Read(t *testing.T) {
|
||||
limit: 3,
|
||||
want: 0,
|
||||
}, {
|
||||
err: &LimitReachedError{
|
||||
err: &aghio.LimitReachedError{
|
||||
Limit: 0,
|
||||
},
|
||||
name: "limit_reached",
|
||||
@@ -74,7 +75,7 @@ func TestLimitedReader_Read(t *testing.T) {
|
||||
|
||||
for _, tc := range testCases {
|
||||
readCloser := io.NopCloser(strings.NewReader(tc.rStr))
|
||||
lreader, err := LimitReader(readCloser, tc.limit)
|
||||
lreader, err := aghio.LimitReader(readCloser, tc.limit)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, lreader)
|
||||
|
||||
@@ -89,7 +90,7 @@ func TestLimitedReader_Read(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestLimitedReader_LimitReachedError(t *testing.T) {
|
||||
testutil.AssertErrorMsg(t, "attempted to read more than 0 bytes", &LimitReachedError{
|
||||
testutil.AssertErrorMsg(t, "attempted to read more than 0 bytes", &aghio.LimitReachedError{
|
||||
Limit: 0,
|
||||
})
|
||||
}
|
||||
|
||||
43
internal/aghnet/addr.go
Normal file
43
internal/aghnet/addr.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package aghnet
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/AdguardTeam/golibs/stringutil"
|
||||
)
|
||||
|
||||
// NormalizeDomain returns a lowercased version of host without the final dot,
|
||||
// unless host is ".", in which case it returns it unchanged. That is a special
|
||||
// case that to allow matching queries like:
|
||||
//
|
||||
// dig IN NS '.'
|
||||
func NormalizeDomain(host string) (norm string) {
|
||||
if host == "." {
|
||||
return host
|
||||
}
|
||||
|
||||
return strings.ToLower(strings.TrimSuffix(host, "."))
|
||||
}
|
||||
|
||||
// NewDomainNameSet returns nil and error, if list has duplicate or empty domain
|
||||
// name. Otherwise returns a set, which contains domain names normalized using
|
||||
// [NormalizeDomain].
|
||||
func NewDomainNameSet(list []string) (set *stringutil.Set, err error) {
|
||||
set = stringutil.NewSet()
|
||||
|
||||
for i, host := range list {
|
||||
if host == "" {
|
||||
return nil, fmt.Errorf("at index %d: hostname is empty", i)
|
||||
}
|
||||
|
||||
host = NormalizeDomain(host)
|
||||
if set.Has(host) {
|
||||
return nil, fmt.Errorf("duplicate hostname %q at index %d", host, i)
|
||||
}
|
||||
|
||||
set.Add(host)
|
||||
}
|
||||
|
||||
return set, nil
|
||||
}
|
||||
59
internal/aghnet/addr_test.go
Normal file
59
internal/aghnet/addr_test.go
Normal file
@@ -0,0 +1,59 @@
|
||||
package aghnet_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
||||
"github.com/AdguardTeam/golibs/testutil"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNewDomainNameSet(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
wantErrMsg string
|
||||
in []string
|
||||
}{{
|
||||
name: "nil",
|
||||
wantErrMsg: "",
|
||||
in: nil,
|
||||
}, {
|
||||
name: "success",
|
||||
wantErrMsg: "",
|
||||
in: []string{
|
||||
"Domain.Example",
|
||||
".",
|
||||
},
|
||||
}, {
|
||||
name: "dups",
|
||||
wantErrMsg: `duplicate hostname "domain.example" at index 1`,
|
||||
in: []string{
|
||||
"Domain.Example",
|
||||
"domain.example",
|
||||
},
|
||||
}, {
|
||||
name: "bad_domain",
|
||||
wantErrMsg: "at index 0: hostname is empty",
|
||||
in: []string{
|
||||
"",
|
||||
},
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
set, err := aghnet.NewDomainNameSet(tc.in)
|
||||
testutil.AssertErrorMsg(t, tc.wantErrMsg, err)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, host := range tc.in {
|
||||
assert.Truef(t, set.Has(aghnet.NormalizeDomain(host)), "%q not matched", host)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -60,7 +60,7 @@ func ifaceIPv4Subnet(iface *net.Interface) (subnet netip.Prefix, err error) {
|
||||
}
|
||||
|
||||
if ip = ip.To4(); ip != nil {
|
||||
return netip.PrefixFrom(netip.AddrFrom4(*(*[4]byte)(ip)), maskLen), nil
|
||||
return netip.PrefixFrom(netip.AddrFrom4([4]byte(ip)), maskLen), nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
package aghnet
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/netip"
|
||||
"strings"
|
||||
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/stringutil"
|
||||
)
|
||||
|
||||
// GenerateHostname generates the hostname from ip. In case of using IPv4 the
|
||||
@@ -29,32 +25,8 @@ func GenerateHostname(ip netip.Addr) (hostname string) {
|
||||
hostname = ip.StringExpanded()
|
||||
|
||||
if ip.Is4() {
|
||||
return strings.Replace(hostname, ".", "-", -1)
|
||||
return strings.ReplaceAll(hostname, ".", "-")
|
||||
}
|
||||
|
||||
return strings.Replace(hostname, ":", "-", -1)
|
||||
}
|
||||
|
||||
// NewDomainNameSet returns nil and error, if list has duplicate or empty
|
||||
// domain name. Otherwise returns a set, which contains non-FQDN domain names,
|
||||
// and nil error.
|
||||
func NewDomainNameSet(list []string) (set *stringutil.Set, err error) {
|
||||
set = stringutil.NewSet()
|
||||
|
||||
for i, v := range list {
|
||||
host := strings.ToLower(strings.TrimSuffix(v, "."))
|
||||
// TODO(a.garipov): Think about ignoring empty (".") names in the
|
||||
// future.
|
||||
if host == "" {
|
||||
return nil, errors.Error("host name is empty")
|
||||
}
|
||||
|
||||
if set.Has(host) {
|
||||
return nil, fmt.Errorf("duplicate host name %q at index %d", host, i)
|
||||
}
|
||||
|
||||
set.Add(host)
|
||||
}
|
||||
|
||||
return set, nil
|
||||
return strings.ReplaceAll(hostname, ":", "-")
|
||||
}
|
||||
|
||||
@@ -1,25 +1,19 @@
|
||||
package aghnet
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"net/netip"
|
||||
"path"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghos"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/hostsfile"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/AdguardTeam/golibs/netutil"
|
||||
"github.com/AdguardTeam/golibs/stringutil"
|
||||
"github.com/AdguardTeam/urlfilter"
|
||||
"github.com/AdguardTeam/urlfilter/filterlist"
|
||||
"github.com/AdguardTeam/urlfilter/rules"
|
||||
"github.com/miekg/dns"
|
||||
"golang.org/x/exp/maps"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
// DefaultHostsPaths returns the slice of paths default for the operating system
|
||||
@@ -29,88 +23,51 @@ func DefaultHostsPaths() (paths []string) {
|
||||
return defaultHostsPaths()
|
||||
}
|
||||
|
||||
// requestMatcher combines the logic for matching requests and translating the
|
||||
// appropriate rules.
|
||||
type requestMatcher struct {
|
||||
// stateLock protects all the fields of requestMatcher.
|
||||
stateLock *sync.RWMutex
|
||||
|
||||
// rulesStrg stores the rules obtained from the hosts' file.
|
||||
rulesStrg *filterlist.RuleStorage
|
||||
// engine serves rulesStrg.
|
||||
engine *urlfilter.DNSEngine
|
||||
|
||||
// translator maps generated $dnsrewrite rules into hosts-syntax rules.
|
||||
//
|
||||
// TODO(e.burkov): Store the filename from which the rule was parsed.
|
||||
translator map[string]string
|
||||
}
|
||||
|
||||
// MatchRequest processes the request rewriting hostnames and addresses read
|
||||
// from the operating system's hosts files. res is nil for any request having
|
||||
// not an A/AAAA or PTR type, see man 5 hosts.
|
||||
//
|
||||
// It's safe for concurrent use.
|
||||
func (rm *requestMatcher) MatchRequest(
|
||||
req *urlfilter.DNSRequest,
|
||||
) (res *urlfilter.DNSResult, ok bool) {
|
||||
switch req.DNSType {
|
||||
case dns.TypeA, dns.TypeAAAA, dns.TypePTR:
|
||||
log.Debug("%s: handling the request for %s", hostsContainerPrefix, req.Hostname)
|
||||
default:
|
||||
return nil, false
|
||||
// MatchAddr returns the records for the IP address.
|
||||
func (hc *HostsContainer) MatchAddr(ip netip.Addr) (recs []*hostsfile.Record) {
|
||||
cur := hc.current.Load()
|
||||
if cur == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
rm.stateLock.RLock()
|
||||
defer rm.stateLock.RUnlock()
|
||||
|
||||
return rm.engine.MatchRequest(req)
|
||||
return cur.addrs[ip]
|
||||
}
|
||||
|
||||
// Translate returns the source hosts-syntax rule for the generated dnsrewrite
|
||||
// rule or an empty string if the last doesn't exist. The returned rules are in
|
||||
// a processed format like:
|
||||
//
|
||||
// ip host1 host2 ...
|
||||
func (rm *requestMatcher) Translate(rule string) (hostRule string) {
|
||||
rm.stateLock.RLock()
|
||||
defer rm.stateLock.RUnlock()
|
||||
// MatchName returns the records for the hostname.
|
||||
func (hc *HostsContainer) MatchName(name string) (recs []*hostsfile.Record) {
|
||||
cur := hc.current.Load()
|
||||
if cur != nil {
|
||||
recs = cur.names[name]
|
||||
}
|
||||
|
||||
return rm.translator[rule]
|
||||
}
|
||||
|
||||
// resetEng updates container's engine and the translation map.
|
||||
func (rm *requestMatcher) resetEng(rulesStrg *filterlist.RuleStorage, tr map[string]string) {
|
||||
rm.stateLock.Lock()
|
||||
defer rm.stateLock.Unlock()
|
||||
|
||||
rm.rulesStrg = rulesStrg
|
||||
rm.engine = urlfilter.NewDNSEngine(rm.rulesStrg)
|
||||
|
||||
rm.translator = tr
|
||||
return recs
|
||||
}
|
||||
|
||||
// hostsContainerPrefix is a prefix for logging and wrapping errors in
|
||||
// HostsContainer's methods.
|
||||
const hostsContainerPrefix = "hosts container"
|
||||
|
||||
// Hosts is a map of IP addresses to the records, as it primarily stored in the
|
||||
// [HostsContainer]. It should not be accessed for writing since it may be read
|
||||
// concurrently, users should clone it before modifying.
|
||||
//
|
||||
// The order of records for each address is preserved from original files, but
|
||||
// the order of the addresses, being a map key, is not.
|
||||
//
|
||||
// TODO(e.burkov): Probably, this should be a sorted slice of records.
|
||||
type Hosts map[netip.Addr][]*hostsfile.Record
|
||||
|
||||
// HostsContainer stores the relevant hosts database provided by the OS and
|
||||
// processes both A/AAAA and PTR DNS requests for those.
|
||||
type HostsContainer struct {
|
||||
// requestMatcher matches the requests and translates the rules. It's
|
||||
// embedded to implement MatchRequest and Translate for *HostsContainer.
|
||||
//
|
||||
// TODO(a.garipov, e.burkov): Consider fully merging into HostsContainer.
|
||||
requestMatcher
|
||||
|
||||
// done is the channel to sign closing the container.
|
||||
done chan struct{}
|
||||
|
||||
// updates is the channel for receiving updated hosts.
|
||||
updates chan HostsRecords
|
||||
updates chan Hosts
|
||||
|
||||
// last is the set of hosts that was cached within last detected change.
|
||||
last HostsRecords
|
||||
// current is the last set of hosts parsed.
|
||||
current atomic.Pointer[hostsIndex]
|
||||
|
||||
// fsys is the working file system to read hosts files from.
|
||||
fsys fs.FS
|
||||
@@ -120,30 +77,6 @@ type HostsContainer struct {
|
||||
|
||||
// patterns stores specified paths in the fs.Glob-compatible form.
|
||||
patterns []string
|
||||
|
||||
// listID is the identifier for the list of generated rules.
|
||||
listID int
|
||||
}
|
||||
|
||||
// HostsRecords is a mapping of an IP address to its hosts data.
|
||||
type HostsRecords map[netip.Addr]*HostsRecord
|
||||
|
||||
// HostsRecord represents a single hosts file record.
|
||||
type HostsRecord struct {
|
||||
Aliases *stringutil.Set
|
||||
Canonical string
|
||||
}
|
||||
|
||||
// equal returns true if all fields of rec are equal to field in other or they
|
||||
// both are nil.
|
||||
func (rec *HostsRecord) equal(other *HostsRecord) (ok bool) {
|
||||
if rec == nil {
|
||||
return other == nil
|
||||
} else if other == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return rec.Canonical == other.Canonical && rec.Aliases.Equal(other.Aliases)
|
||||
}
|
||||
|
||||
// ErrNoHostsPaths is returned when there are no valid paths to watch passed to
|
||||
@@ -155,7 +88,6 @@ const ErrNoHostsPaths errors.Error = "no valid paths to hosts files provided"
|
||||
// shouldn't be empty and each of paths should locate either a file or a
|
||||
// directory in fsys. fsys and w must be non-nil.
|
||||
func NewHostsContainer(
|
||||
listID int,
|
||||
fsys fs.FS,
|
||||
w aghos.FSWatcher,
|
||||
paths ...string,
|
||||
@@ -175,12 +107,8 @@ func NewHostsContainer(
|
||||
}
|
||||
|
||||
hc = &HostsContainer{
|
||||
requestMatcher: requestMatcher{
|
||||
stateLock: &sync.RWMutex{},
|
||||
},
|
||||
listID: listID,
|
||||
done: make(chan struct{}, 1),
|
||||
updates: make(chan HostsRecords, 1),
|
||||
updates: make(chan Hosts, 1),
|
||||
fsys: fsys,
|
||||
watcher: w,
|
||||
patterns: patterns,
|
||||
@@ -226,7 +154,7 @@ func (hc *HostsContainer) Close() (err error) {
|
||||
}
|
||||
|
||||
// Upd returns the channel into which the updates are sent.
|
||||
func (hc *HostsContainer) Upd() (updates <-chan HostsRecords) {
|
||||
func (hc *HostsContainer) Upd() (updates <-chan Hosts) {
|
||||
return hc.updates
|
||||
}
|
||||
|
||||
@@ -273,7 +201,7 @@ func (hc *HostsContainer) handleEvents() {
|
||||
}
|
||||
|
||||
if err := hc.refresh(); err != nil {
|
||||
log.Error("%s: %s", hostsContainerPrefix, err)
|
||||
log.Error("%s: warning: refreshing: %s", hostsContainerPrefix, err)
|
||||
}
|
||||
case _, ok = <-hc.done:
|
||||
// Go on.
|
||||
@@ -281,198 +209,83 @@ func (hc *HostsContainer) handleEvents() {
|
||||
}
|
||||
}
|
||||
|
||||
// hostsParser is a helper type to parse rules from the operating system's hosts
|
||||
// file. It exists for only a single refreshing session.
|
||||
type hostsParser struct {
|
||||
// rulesBuilder builds the resulting rules list content.
|
||||
rulesBuilder *strings.Builder
|
||||
|
||||
// translations maps generated rules into actual hosts file lines.
|
||||
translations map[string]string
|
||||
|
||||
// table stores only the unique IP-hostname pairs. It's also sent to the
|
||||
// updates channel afterwards.
|
||||
table HostsRecords
|
||||
}
|
||||
|
||||
// newHostsParser creates a new *hostsParser with buffers of size taken from the
|
||||
// previous parse.
|
||||
func (hc *HostsContainer) newHostsParser() (hp *hostsParser) {
|
||||
return &hostsParser{
|
||||
rulesBuilder: &strings.Builder{},
|
||||
translations: map[string]string{},
|
||||
table: make(HostsRecords, len(hc.last)),
|
||||
}
|
||||
}
|
||||
|
||||
// parseFile is a aghos.FileWalker for parsing the files with hosts syntax. It
|
||||
// never signs to stop walking and never returns any additional patterns.
|
||||
//
|
||||
// See man hosts(5).
|
||||
func (hp *hostsParser) parseFile(r io.Reader) (patterns []string, cont bool, err error) {
|
||||
s := bufio.NewScanner(r)
|
||||
for s.Scan() {
|
||||
ip, hosts := hp.parseLine(s.Text())
|
||||
if ip == (netip.Addr{}) || len(hosts) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
hp.addRecord(ip, hosts)
|
||||
}
|
||||
|
||||
return nil, true, s.Err()
|
||||
}
|
||||
|
||||
// parseLine parses the line having the hosts syntax ignoring invalid ones.
|
||||
func (hp *hostsParser) parseLine(line string) (ip netip.Addr, hosts []string) {
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) < 2 {
|
||||
return netip.Addr{}, nil
|
||||
}
|
||||
|
||||
ip, err := netip.ParseAddr(fields[0])
|
||||
if err != nil {
|
||||
return netip.Addr{}, nil
|
||||
}
|
||||
|
||||
for _, f := range fields[1:] {
|
||||
hashIdx := strings.IndexByte(f, '#')
|
||||
if hashIdx == 0 {
|
||||
// The rest of the fields are a part of the comment so return.
|
||||
break
|
||||
} else if hashIdx > 0 {
|
||||
// Only a part of the field is a comment.
|
||||
f = f[:hashIdx]
|
||||
}
|
||||
|
||||
// Make sure that invalid hosts aren't turned into rules.
|
||||
//
|
||||
// See https://github.com/AdguardTeam/AdGuardHome/issues/3946.
|
||||
//
|
||||
// TODO(e.burkov): Investigate if hosts may contain DNS-SD domains.
|
||||
err = netutil.ValidateHostname(f)
|
||||
if err != nil {
|
||||
log.Error("%s: host %q is invalid, ignoring", hostsContainerPrefix, f)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
hosts = append(hosts, f)
|
||||
}
|
||||
|
||||
return ip, hosts
|
||||
}
|
||||
|
||||
// addRecord puts the record for the IP address to the rules builder if needed.
|
||||
// The first host is considered to be the canonical name for the IP address.
|
||||
// hosts must have at least one name.
|
||||
func (hp *hostsParser) addRecord(ip netip.Addr, hosts []string) {
|
||||
line := strings.Join(append([]string{ip.String()}, hosts...), " ")
|
||||
|
||||
rec, ok := hp.table[ip]
|
||||
if !ok {
|
||||
rec = &HostsRecord{
|
||||
Aliases: stringutil.NewSet(),
|
||||
}
|
||||
|
||||
rec.Canonical, hosts = hosts[0], hosts[1:]
|
||||
hp.addRules(ip, rec.Canonical, line)
|
||||
hp.table[ip] = rec
|
||||
}
|
||||
|
||||
for _, host := range hosts {
|
||||
if rec.Canonical == host || rec.Aliases.Has(host) {
|
||||
continue
|
||||
}
|
||||
|
||||
rec.Aliases.Add(host)
|
||||
|
||||
hp.addRules(ip, host, line)
|
||||
}
|
||||
}
|
||||
|
||||
// addRules adds rules and rule translations for the line.
|
||||
func (hp *hostsParser) addRules(ip netip.Addr, host, line string) {
|
||||
rule, rulePtr := hp.writeRules(host, ip)
|
||||
hp.translations[rule], hp.translations[rulePtr] = line, line
|
||||
|
||||
log.Debug("%s: added ip-host pair %q-%q", hostsContainerPrefix, ip, host)
|
||||
}
|
||||
|
||||
// writeRules writes the actual rule for the qtype and the PTR for the host-ip
|
||||
// pair into internal builders.
|
||||
func (hp *hostsParser) writeRules(host string, ip netip.Addr) (rule, rulePtr string) {
|
||||
// TODO(a.garipov): Add a netip.Addr version to netutil.
|
||||
arpa, err := netutil.IPToReversedAddr(ip.AsSlice())
|
||||
if err != nil {
|
||||
return "", ""
|
||||
}
|
||||
|
||||
const (
|
||||
nl = "\n"
|
||||
|
||||
rwSuccess = "^$dnsrewrite=NOERROR;"
|
||||
rwSuccessPTR = "^$dnsrewrite=NOERROR;PTR;"
|
||||
|
||||
modLen = len(rules.MaskPipe) + len(rwSuccess) + len(";")
|
||||
modLenPTR = len(rules.MaskPipe) + len(rwSuccessPTR)
|
||||
)
|
||||
|
||||
var qtype string
|
||||
// The validation of the IP address has been performed earlier so it is
|
||||
// guaranteed to be either an IPv4 or an IPv6.
|
||||
if ip.Is4() {
|
||||
qtype = "A"
|
||||
} else {
|
||||
qtype = "AAAA"
|
||||
}
|
||||
|
||||
ipStr := ip.String()
|
||||
fqdn := dns.Fqdn(host)
|
||||
|
||||
ruleBuilder := &strings.Builder{}
|
||||
ruleBuilder.Grow(modLen + len(host) + len(qtype) + len(ipStr))
|
||||
stringutil.WriteToBuilder(ruleBuilder, rules.MaskPipe, host, rwSuccess, qtype, ";", ipStr)
|
||||
rule = ruleBuilder.String()
|
||||
|
||||
ruleBuilder.Reset()
|
||||
|
||||
ruleBuilder.Grow(modLenPTR + len(arpa) + len(fqdn))
|
||||
stringutil.WriteToBuilder(ruleBuilder, rules.MaskPipe, arpa, rwSuccessPTR, fqdn)
|
||||
|
||||
rulePtr = ruleBuilder.String()
|
||||
|
||||
hp.rulesBuilder.Grow(len(rule) + len(rulePtr) + 2*len(nl))
|
||||
stringutil.WriteToBuilder(hp.rulesBuilder, rule, nl, rulePtr, nl)
|
||||
|
||||
return rule, rulePtr
|
||||
}
|
||||
|
||||
// sendUpd tries to send the parsed data to the ch.
|
||||
func (hp *hostsParser) sendUpd(ch chan HostsRecords) {
|
||||
func (hc *HostsContainer) sendUpd(recs Hosts) {
|
||||
log.Debug("%s: sending upd", hostsContainerPrefix)
|
||||
|
||||
upd := hp.table
|
||||
ch := hc.updates
|
||||
select {
|
||||
case ch <- upd:
|
||||
case ch <- recs:
|
||||
// Updates are delivered. Go on.
|
||||
case <-ch:
|
||||
ch <- upd
|
||||
ch <- recs
|
||||
log.Debug("%s: replaced the last update", hostsContainerPrefix)
|
||||
case ch <- upd:
|
||||
case ch <- recs:
|
||||
// The previous update was just read and the next one pushed. Go on.
|
||||
default:
|
||||
log.Error("%s: the updates channel is broken", hostsContainerPrefix)
|
||||
}
|
||||
}
|
||||
|
||||
// newStrg creates a new rules storage from parsed data.
|
||||
func (hp *hostsParser) newStrg(id int) (s *filterlist.RuleStorage, err error) {
|
||||
return filterlist.NewRuleStorage([]filterlist.RuleList{&filterlist.StringRuleList{
|
||||
ID: id,
|
||||
RulesText: hp.rulesBuilder.String(),
|
||||
IgnoreCosmetic: true,
|
||||
}})
|
||||
// hostsIndex is a [hostsfile.Set] to enumerate all the records.
|
||||
type hostsIndex struct {
|
||||
// addrs maps IP addresses to the records.
|
||||
addrs Hosts
|
||||
|
||||
// names maps hostnames to the records.
|
||||
names map[string][]*hostsfile.Record
|
||||
}
|
||||
|
||||
// walk is a file walking function for hostsIndex.
|
||||
func (idx *hostsIndex) walk(r io.Reader) (patterns []string, cont bool, err error) {
|
||||
return nil, true, hostsfile.Parse(idx, r, nil)
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ hostsfile.Set = (*hostsIndex)(nil)
|
||||
|
||||
// Add implements the [hostsfile.Set] interface for *hostsIndex.
|
||||
func (idx *hostsIndex) Add(rec *hostsfile.Record) {
|
||||
idx.addrs[rec.Addr] = append(idx.addrs[rec.Addr], rec)
|
||||
for _, name := range rec.Names {
|
||||
idx.names[name] = append(idx.names[name], rec)
|
||||
}
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ hostsfile.HandleSet = (*hostsIndex)(nil)
|
||||
|
||||
// HandleInvalid implements the [hostsfile.HandleSet] interface for *hostsIndex.
|
||||
func (idx *hostsIndex) HandleInvalid(src string, _ []byte, err error) {
|
||||
lineErr := &hostsfile.LineError{}
|
||||
if !errors.As(err, &lineErr) {
|
||||
// Must not happen if idx passed to [hostsfile.Parse].
|
||||
return
|
||||
} else if errors.Is(lineErr, hostsfile.ErrEmptyLine) {
|
||||
// Ignore empty lines.
|
||||
return
|
||||
}
|
||||
|
||||
log.Info("%s: warning: parsing %q: %s", hostsContainerPrefix, src, lineErr)
|
||||
}
|
||||
|
||||
// equalRecs is an equality function for [*hostsfile.Record].
|
||||
func equalRecs(a, b *hostsfile.Record) (ok bool) {
|
||||
return a.Addr == b.Addr && a.Source == b.Source && slices.Equal(a.Names, b.Names)
|
||||
}
|
||||
|
||||
// equalRecSlices is an equality function for slices of [*hostsfile.Record].
|
||||
func equalRecSlices(a, b []*hostsfile.Record) (ok bool) { return slices.EqualFunc(a, b, equalRecs) }
|
||||
|
||||
// Equal returns true if indexes are equal.
|
||||
func (idx *hostsIndex) Equal(other *hostsIndex) (ok bool) {
|
||||
if idx == nil {
|
||||
return other == nil
|
||||
} else if other == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return maps.EqualFunc(idx.addrs, other.addrs, equalRecSlices)
|
||||
}
|
||||
|
||||
// refresh gets the data from specified files and propagates the updates if
|
||||
@@ -482,27 +295,27 @@ func (hp *hostsParser) newStrg(id int) (s *filterlist.RuleStorage, err error) {
|
||||
func (hc *HostsContainer) refresh() (err error) {
|
||||
log.Debug("%s: refreshing", hostsContainerPrefix)
|
||||
|
||||
hp := hc.newHostsParser()
|
||||
if _, err = aghos.FileWalker(hp.parseFile).Walk(hc.fsys, hc.patterns...); err != nil {
|
||||
return fmt.Errorf("refreshing : %w", err)
|
||||
var addrLen, nameLen int
|
||||
last := hc.current.Load()
|
||||
if last != nil {
|
||||
addrLen, nameLen = len(last.addrs), len(last.names)
|
||||
}
|
||||
idx := &hostsIndex{
|
||||
addrs: make(Hosts, addrLen),
|
||||
names: make(map[string][]*hostsfile.Record, nameLen),
|
||||
}
|
||||
|
||||
// hc.last is nil on the first refresh, so let that one through.
|
||||
if hc.last != nil && maps.EqualFunc(hp.table, hc.last, (*HostsRecord).equal) {
|
||||
log.Debug("%s: no changes detected", hostsContainerPrefix)
|
||||
|
||||
return nil
|
||||
}
|
||||
defer hp.sendUpd(hc.updates)
|
||||
|
||||
hc.last = maps.Clone(hp.table)
|
||||
|
||||
var rulesStrg *filterlist.RuleStorage
|
||||
if rulesStrg, err = hp.newStrg(hc.listID); err != nil {
|
||||
return fmt.Errorf("initializing rules storage: %w", err)
|
||||
_, err = aghos.FileWalker(idx.walk).Walk(hc.fsys, hc.patterns...)
|
||||
if err != nil {
|
||||
// Don't wrap the error since it's informative enough as is.
|
||||
return err
|
||||
}
|
||||
|
||||
hc.resetEng(rulesStrg, hp.translations)
|
||||
// TODO(e.burkov): Serialize updates using time.
|
||||
if !last.Equal(idx) {
|
||||
hc.current.Store(idx)
|
||||
hc.sendUpd(idx.addrs)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
76
internal/aghnet/hostscontainer_internal_test.go
Normal file
76
internal/aghnet/hostscontainer_internal_test.go
Normal file
@@ -0,0 +1,76 @@
|
||||
package aghnet
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"path"
|
||||
"testing"
|
||||
"testing/fstest"
|
||||
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/testutil/fakefs"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
const nl = "\n"
|
||||
|
||||
func TestHostsContainer_PathsToPatterns(t *testing.T) {
|
||||
gsfs := fstest.MapFS{
|
||||
"dir_0/file_1": &fstest.MapFile{Data: []byte{1}},
|
||||
"dir_0/file_2": &fstest.MapFile{Data: []byte{2}},
|
||||
"dir_0/dir_1/file_3": &fstest.MapFile{Data: []byte{3}},
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
paths []string
|
||||
want []string
|
||||
}{{
|
||||
name: "no_paths",
|
||||
paths: nil,
|
||||
want: nil,
|
||||
}, {
|
||||
name: "single_file",
|
||||
paths: []string{"dir_0/file_1"},
|
||||
want: []string{"dir_0/file_1"},
|
||||
}, {
|
||||
name: "several_files",
|
||||
paths: []string{"dir_0/file_1", "dir_0/file_2"},
|
||||
want: []string{"dir_0/file_1", "dir_0/file_2"},
|
||||
}, {
|
||||
name: "whole_dir",
|
||||
paths: []string{"dir_0"},
|
||||
want: []string{"dir_0/*"},
|
||||
}, {
|
||||
name: "file_and_dir",
|
||||
paths: []string{"dir_0/file_1", "dir_0/dir_1"},
|
||||
want: []string{"dir_0/file_1", "dir_0/dir_1/*"},
|
||||
}, {
|
||||
name: "non-existing",
|
||||
paths: []string{path.Join("dir_0", "file_3")},
|
||||
want: nil,
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
patterns, err := pathsToPatterns(gsfs, tc.paths)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, tc.want, patterns)
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("bad_file", func(t *testing.T) {
|
||||
const errStat errors.Error = "bad file"
|
||||
|
||||
badFS := &fakefs.StatFS{
|
||||
OnOpen: func(_ string) (f fs.File, err error) { panic("not implemented") },
|
||||
OnStat: func(name string) (fi fs.FileInfo, err error) {
|
||||
return nil, errStat
|
||||
},
|
||||
}
|
||||
|
||||
_, err := pathsToPatterns(badFS, []string{""})
|
||||
assert.ErrorIs(t, err, errStat)
|
||||
})
|
||||
}
|
||||
@@ -1,32 +1,156 @@
|
||||
package aghnet
|
||||
package aghnet_test
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"net"
|
||||
"net/netip"
|
||||
"path"
|
||||
"strings"
|
||||
"path/filepath"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"testing/fstest"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghchan"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghtest"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/hostsfile"
|
||||
"github.com/AdguardTeam/golibs/netutil"
|
||||
"github.com/AdguardTeam/golibs/stringutil"
|
||||
"github.com/AdguardTeam/golibs/testutil"
|
||||
"github.com/AdguardTeam/urlfilter"
|
||||
"github.com/AdguardTeam/urlfilter/rules"
|
||||
"github.com/miekg/dns"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
const (
|
||||
nl = "\n"
|
||||
sp = " "
|
||||
// nl is a newline character.
|
||||
const nl = "\n"
|
||||
|
||||
// Variables mirroring the etc_hosts file from testdata.
|
||||
var (
|
||||
addr1000 = netip.MustParseAddr("1.0.0.0")
|
||||
addr1001 = netip.MustParseAddr("1.0.0.1")
|
||||
addr1002 = netip.MustParseAddr("1.0.0.2")
|
||||
addr1003 = netip.MustParseAddr("1.0.0.3")
|
||||
addr1004 = netip.MustParseAddr("1.0.0.4")
|
||||
addr1357 = netip.MustParseAddr("1.3.5.7")
|
||||
addr4216 = netip.MustParseAddr("4.2.1.6")
|
||||
addr7531 = netip.MustParseAddr("7.5.3.1")
|
||||
|
||||
addr0 = netip.MustParseAddr("::")
|
||||
addr1 = netip.MustParseAddr("::1")
|
||||
addr2 = netip.MustParseAddr("::2")
|
||||
addr3 = netip.MustParseAddr("::3")
|
||||
addr4 = netip.MustParseAddr("::4")
|
||||
addr42 = netip.MustParseAddr("::42")
|
||||
addr13 = netip.MustParseAddr("::13")
|
||||
addr31 = netip.MustParseAddr("::31")
|
||||
|
||||
hostsSrc = "./" + filepath.Join("./testdata", "etc_hosts")
|
||||
|
||||
testHosts = map[netip.Addr][]*hostsfile.Record{
|
||||
addr1000: {{
|
||||
Addr: addr1000,
|
||||
Source: hostsSrc,
|
||||
Names: []string{"hello", "hello.world"},
|
||||
}, {
|
||||
Addr: addr1000,
|
||||
Source: hostsSrc,
|
||||
Names: []string{"hello.world.again"},
|
||||
}, {
|
||||
Addr: addr1000,
|
||||
Source: hostsSrc,
|
||||
Names: []string{"hello.world"},
|
||||
}},
|
||||
addr1001: {{
|
||||
Addr: addr1001,
|
||||
Source: hostsSrc,
|
||||
Names: []string{"simplehost"},
|
||||
}, {
|
||||
Addr: addr1001,
|
||||
Source: hostsSrc,
|
||||
Names: []string{"simplehost"},
|
||||
}},
|
||||
addr1002: {{
|
||||
Addr: addr1002,
|
||||
Source: hostsSrc,
|
||||
Names: []string{"a.whole", "lot.of", "aliases", "for.testing"},
|
||||
}},
|
||||
addr1003: {{
|
||||
Addr: addr1003,
|
||||
Source: hostsSrc,
|
||||
Names: []string{"*"},
|
||||
}},
|
||||
addr1004: {{
|
||||
Addr: addr1004,
|
||||
Source: hostsSrc,
|
||||
Names: []string{"*.com"},
|
||||
}},
|
||||
addr1357: {{
|
||||
Addr: addr1357,
|
||||
Source: hostsSrc,
|
||||
Names: []string{"domain4", "domain4.alias"},
|
||||
}},
|
||||
addr7531: {{
|
||||
Addr: addr7531,
|
||||
Source: hostsSrc,
|
||||
Names: []string{"domain4.alias", "domain4"},
|
||||
}},
|
||||
addr4216: {{
|
||||
Addr: addr4216,
|
||||
Source: hostsSrc,
|
||||
Names: []string{"domain", "domain.alias"},
|
||||
}},
|
||||
addr0: {{
|
||||
Addr: addr0,
|
||||
Source: hostsSrc,
|
||||
Names: []string{"hello", "hello.world"},
|
||||
}, {
|
||||
Addr: addr0,
|
||||
Source: hostsSrc,
|
||||
Names: []string{"hello.world.again"},
|
||||
}, {
|
||||
Addr: addr0,
|
||||
Source: hostsSrc,
|
||||
Names: []string{"hello.world"},
|
||||
}},
|
||||
addr1: {{
|
||||
Addr: addr1,
|
||||
Source: hostsSrc,
|
||||
Names: []string{"simplehost"},
|
||||
}, {
|
||||
Addr: addr1,
|
||||
Source: hostsSrc,
|
||||
Names: []string{"simplehost"},
|
||||
}},
|
||||
addr2: {{
|
||||
Addr: addr2,
|
||||
Source: hostsSrc,
|
||||
Names: []string{"a.whole", "lot.of", "aliases", "for.testing"},
|
||||
}},
|
||||
addr3: {{
|
||||
Addr: addr3,
|
||||
Source: hostsSrc,
|
||||
Names: []string{"*"},
|
||||
}},
|
||||
addr4: {{
|
||||
Addr: addr4,
|
||||
Source: hostsSrc,
|
||||
Names: []string{"*.com"},
|
||||
}},
|
||||
addr42: {{
|
||||
Addr: addr42,
|
||||
Source: hostsSrc,
|
||||
Names: []string{"domain.alias", "domain"},
|
||||
}},
|
||||
addr13: {{
|
||||
Addr: addr13,
|
||||
Source: hostsSrc,
|
||||
Names: []string{"domain6", "domain6.alias"},
|
||||
}},
|
||||
addr31: {{
|
||||
Addr: addr31,
|
||||
Source: hostsSrc,
|
||||
Names: []string{"domain6.alias", "domain6"},
|
||||
}},
|
||||
}
|
||||
)
|
||||
|
||||
func TestNewHostsContainer(t *testing.T) {
|
||||
@@ -48,11 +172,11 @@ func TestNewHostsContainer(t *testing.T) {
|
||||
name: "one_file",
|
||||
paths: []string{p},
|
||||
}, {
|
||||
wantErr: ErrNoHostsPaths,
|
||||
wantErr: aghnet.ErrNoHostsPaths,
|
||||
name: "no_files",
|
||||
paths: []string{},
|
||||
}, {
|
||||
wantErr: ErrNoHostsPaths,
|
||||
wantErr: aghnet.ErrNoHostsPaths,
|
||||
name: "non-existent_file",
|
||||
paths: []string{path.Join(dirname, filename+"2")},
|
||||
}, {
|
||||
@@ -77,7 +201,7 @@ func TestNewHostsContainer(t *testing.T) {
|
||||
return eventsCh
|
||||
}
|
||||
|
||||
hc, err := NewHostsContainer(0, testFS, &aghtest.FSWatcher{
|
||||
hc, err := aghnet.NewHostsContainer(testFS, &aghtest.FSWatcher{
|
||||
OnEvents: onEvents,
|
||||
OnAdd: onAdd,
|
||||
OnClose: func() (err error) { return nil },
|
||||
@@ -103,7 +227,7 @@ func TestNewHostsContainer(t *testing.T) {
|
||||
|
||||
t.Run("nil_fs", func(t *testing.T) {
|
||||
require.Panics(t, func() {
|
||||
_, _ = NewHostsContainer(0, nil, &aghtest.FSWatcher{
|
||||
_, _ = aghnet.NewHostsContainer(nil, &aghtest.FSWatcher{
|
||||
// Those shouldn't panic.
|
||||
OnEvents: func() (e <-chan struct{}) { return nil },
|
||||
OnAdd: func(name string) (err error) { return nil },
|
||||
@@ -114,7 +238,7 @@ func TestNewHostsContainer(t *testing.T) {
|
||||
|
||||
t.Run("nil_watcher", func(t *testing.T) {
|
||||
require.Panics(t, func() {
|
||||
_, _ = NewHostsContainer(0, testFS, nil, p)
|
||||
_, _ = aghnet.NewHostsContainer(testFS, nil, p)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -127,7 +251,7 @@ func TestNewHostsContainer(t *testing.T) {
|
||||
OnClose: func() (err error) { return nil },
|
||||
}
|
||||
|
||||
hc, err := NewHostsContainer(0, testFS, errWatcher, p)
|
||||
hc, err := aghnet.NewHostsContainer(testFS, errWatcher, p)
|
||||
require.ErrorIs(t, err, errOnAdd)
|
||||
|
||||
assert.Nil(t, hc)
|
||||
@@ -140,6 +264,9 @@ func TestHostsContainer_refresh(t *testing.T) {
|
||||
ip := netutil.IPv4Localhost()
|
||||
ipStr := ip.String()
|
||||
|
||||
anotherIPStr := "1.2.3.4"
|
||||
anotherIP := netip.MustParseAddr(anotherIPStr)
|
||||
|
||||
testFS := fstest.MapFS{"dir/file1": &fstest.MapFile{Data: []byte(ipStr + ` hostname` + nl)}}
|
||||
|
||||
// event is a convenient alias for an empty struct{} to emit test events.
|
||||
@@ -158,40 +285,44 @@ func TestHostsContainer_refresh(t *testing.T) {
|
||||
OnClose: func() (err error) { return nil },
|
||||
}
|
||||
|
||||
hc, err := NewHostsContainer(0, testFS, w, "dir")
|
||||
hc, err := aghnet.NewHostsContainer(testFS, w, "dir")
|
||||
require.NoError(t, err)
|
||||
testutil.CleanupAndRequireSuccess(t, hc.Close)
|
||||
|
||||
checkRefresh := func(t *testing.T, want *HostsRecord) {
|
||||
checkRefresh := func(t *testing.T, want aghnet.Hosts) {
|
||||
t.Helper()
|
||||
|
||||
upd, ok := aghchan.MustReceive(hc.Upd(), 1*time.Second)
|
||||
require.True(t, ok)
|
||||
require.NotNil(t, upd)
|
||||
|
||||
assert.Len(t, upd, 1)
|
||||
|
||||
rec, ok := upd[ip]
|
||||
require.True(t, ok)
|
||||
require.NotNil(t, rec)
|
||||
|
||||
assert.Truef(t, rec.equal(want), "%+v != %+v", rec, want)
|
||||
assert.Equal(t, want, upd)
|
||||
}
|
||||
|
||||
t.Run("initial_refresh", func(t *testing.T) {
|
||||
checkRefresh(t, &HostsRecord{
|
||||
Aliases: stringutil.NewSet(),
|
||||
Canonical: "hostname",
|
||||
checkRefresh(t, aghnet.Hosts{
|
||||
ip: {{
|
||||
Addr: ip,
|
||||
Source: "file1",
|
||||
Names: []string{"hostname"},
|
||||
}},
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("second_refresh", func(t *testing.T) {
|
||||
testFS["dir/file2"] = &fstest.MapFile{Data: []byte(ipStr + ` alias` + nl)}
|
||||
testFS["dir/file2"] = &fstest.MapFile{Data: []byte(anotherIPStr + ` alias` + nl)}
|
||||
eventsCh <- event{}
|
||||
|
||||
checkRefresh(t, &HostsRecord{
|
||||
Aliases: stringutil.NewSet("alias"),
|
||||
Canonical: "hostname",
|
||||
checkRefresh(t, aghnet.Hosts{
|
||||
ip: {{
|
||||
Addr: ip,
|
||||
Source: "file1",
|
||||
Names: []string{"hostname"},
|
||||
}},
|
||||
anotherIP: {{
|
||||
Addr: anotherIP,
|
||||
Source: "file2",
|
||||
Names: []string{"alias"},
|
||||
}},
|
||||
})
|
||||
})
|
||||
|
||||
@@ -202,12 +333,9 @@ func TestHostsContainer_refresh(t *testing.T) {
|
||||
|
||||
// Require the changes are written.
|
||||
require.Eventually(t, func() bool {
|
||||
res, ok := hc.MatchRequest(&urlfilter.DNSRequest{
|
||||
Hostname: "hostname",
|
||||
DNSType: dns.TypeA,
|
||||
})
|
||||
ips := hc.MatchName("hostname")
|
||||
|
||||
return !ok && res.DNSRewrites() == nil
|
||||
return len(ips) == 0
|
||||
}, 5*time.Second, time.Second/2)
|
||||
|
||||
// Make a change again.
|
||||
@@ -216,411 +344,117 @@ func TestHostsContainer_refresh(t *testing.T) {
|
||||
|
||||
// Require the changes are written.
|
||||
require.Eventually(t, func() bool {
|
||||
res, ok := hc.MatchRequest(&urlfilter.DNSRequest{
|
||||
Hostname: "hostname",
|
||||
DNSType: dns.TypeA,
|
||||
})
|
||||
ips := hc.MatchName("hostname")
|
||||
|
||||
return !ok && res.DNSRewrites() != nil
|
||||
return len(ips) > 0
|
||||
}, 5*time.Second, time.Second/2)
|
||||
|
||||
assert.Len(t, hc.Upd(), 1)
|
||||
})
|
||||
}
|
||||
|
||||
func TestHostsContainer_PathsToPatterns(t *testing.T) {
|
||||
gsfs := fstest.MapFS{
|
||||
"dir_0/file_1": &fstest.MapFile{Data: []byte{1}},
|
||||
"dir_0/file_2": &fstest.MapFile{Data: []byte{2}},
|
||||
"dir_0/dir_1/file_3": &fstest.MapFile{Data: []byte{3}},
|
||||
}
|
||||
func TestHostsContainer_MatchName(t *testing.T) {
|
||||
require.NoError(t, fstest.TestFS(testdata, "etc_hosts"))
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
paths []string
|
||||
want []string
|
||||
}{{
|
||||
name: "no_paths",
|
||||
paths: nil,
|
||||
want: nil,
|
||||
}, {
|
||||
name: "single_file",
|
||||
paths: []string{"dir_0/file_1"},
|
||||
want: []string{"dir_0/file_1"},
|
||||
}, {
|
||||
name: "several_files",
|
||||
paths: []string{"dir_0/file_1", "dir_0/file_2"},
|
||||
want: []string{"dir_0/file_1", "dir_0/file_2"},
|
||||
}, {
|
||||
name: "whole_dir",
|
||||
paths: []string{"dir_0"},
|
||||
want: []string{"dir_0/*"},
|
||||
}, {
|
||||
name: "file_and_dir",
|
||||
paths: []string{"dir_0/file_1", "dir_0/dir_1"},
|
||||
want: []string{"dir_0/file_1", "dir_0/dir_1/*"},
|
||||
}, {
|
||||
name: "non-existing",
|
||||
paths: []string{path.Join("dir_0", "file_3")},
|
||||
want: nil,
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
patterns, err := pathsToPatterns(gsfs, tc.paths)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, tc.want, patterns)
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("bad_file", func(t *testing.T) {
|
||||
const errStat errors.Error = "bad file"
|
||||
|
||||
badFS := &aghtest.StatFS{
|
||||
OnStat: func(name string) (fs.FileInfo, error) {
|
||||
return nil, errStat
|
||||
},
|
||||
}
|
||||
|
||||
_, err := pathsToPatterns(badFS, []string{""})
|
||||
assert.ErrorIs(t, err, errStat)
|
||||
})
|
||||
}
|
||||
|
||||
func TestHostsContainer_Translate(t *testing.T) {
|
||||
stubWatcher := aghtest.FSWatcher{
|
||||
OnEvents: func() (e <-chan struct{}) { return nil },
|
||||
OnAdd: func(name string) (err error) { return nil },
|
||||
OnClose: func() (err error) { return nil },
|
||||
}
|
||||
|
||||
require.NoError(t, fstest.TestFS(testdata, "etc_hosts"))
|
||||
|
||||
hc, err := NewHostsContainer(0, testdata, &stubWatcher, "etc_hosts")
|
||||
require.NoError(t, err)
|
||||
testutil.CleanupAndRequireSuccess(t, hc.Close)
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
rule string
|
||||
wantTrans []string
|
||||
}{{
|
||||
name: "simplehost",
|
||||
rule: "|simplehost^$dnsrewrite=NOERROR;A;1.0.0.1",
|
||||
wantTrans: []string{"1.0.0.1", "simplehost"},
|
||||
}, {
|
||||
name: "hello",
|
||||
rule: "|hello^$dnsrewrite=NOERROR;A;1.0.0.0",
|
||||
wantTrans: []string{"1.0.0.0", "hello", "hello.world"},
|
||||
}, {
|
||||
name: "hello-alias",
|
||||
rule: "|hello.world.again^$dnsrewrite=NOERROR;A;1.0.0.0",
|
||||
wantTrans: []string{"1.0.0.0", "hello.world.again"},
|
||||
}, {
|
||||
name: "simplehost_v6",
|
||||
rule: "|simplehost^$dnsrewrite=NOERROR;AAAA;::1",
|
||||
wantTrans: []string{"::1", "simplehost"},
|
||||
}, {
|
||||
name: "hello_v6",
|
||||
rule: "|hello^$dnsrewrite=NOERROR;AAAA;::",
|
||||
wantTrans: []string{"::", "hello", "hello.world"},
|
||||
}, {
|
||||
name: "hello_v6-alias",
|
||||
rule: "|hello.world.again^$dnsrewrite=NOERROR;AAAA;::",
|
||||
wantTrans: []string{"::", "hello.world.again"},
|
||||
}, {
|
||||
name: "simplehost_ptr",
|
||||
rule: "|1.0.0.1.in-addr.arpa^$dnsrewrite=NOERROR;PTR;simplehost.",
|
||||
wantTrans: []string{"1.0.0.1", "simplehost"},
|
||||
}, {
|
||||
name: "hello_ptr",
|
||||
rule: "|0.0.0.1.in-addr.arpa^$dnsrewrite=NOERROR;PTR;hello.",
|
||||
wantTrans: []string{"1.0.0.0", "hello", "hello.world"},
|
||||
}, {
|
||||
name: "hello_ptr-alias",
|
||||
rule: "|0.0.0.1.in-addr.arpa^$dnsrewrite=NOERROR;PTR;hello.world.again.",
|
||||
wantTrans: []string{"1.0.0.0", "hello.world.again"},
|
||||
}, {
|
||||
name: "simplehost_ptr_v6",
|
||||
rule: "|1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa" +
|
||||
"^$dnsrewrite=NOERROR;PTR;simplehost.",
|
||||
wantTrans: []string{"::1", "simplehost"},
|
||||
}, {
|
||||
name: "hello_ptr_v6",
|
||||
rule: "|0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa" +
|
||||
"^$dnsrewrite=NOERROR;PTR;hello.",
|
||||
wantTrans: []string{"::", "hello", "hello.world"},
|
||||
}, {
|
||||
name: "hello_ptr_v6-alias",
|
||||
rule: "|0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa" +
|
||||
"^$dnsrewrite=NOERROR;PTR;hello.world.again.",
|
||||
wantTrans: []string{"::", "hello.world.again"},
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
got := stringutil.NewSet(strings.Fields(hc.Translate(tc.rule))...)
|
||||
assert.True(t, stringutil.NewSet(tc.wantTrans...).Equal(got))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHostsContainer(t *testing.T) {
|
||||
const listID = 1234
|
||||
|
||||
require.NoError(t, fstest.TestFS(testdata, "etc_hosts"))
|
||||
|
||||
testCases := []struct {
|
||||
req *urlfilter.DNSRequest
|
||||
req string
|
||||
name string
|
||||
want []*rules.DNSRewrite
|
||||
want []*hostsfile.Record
|
||||
}{{
|
||||
req: &urlfilter.DNSRequest{
|
||||
Hostname: "simplehost",
|
||||
DNSType: dns.TypeA,
|
||||
},
|
||||
req: "simplehost",
|
||||
name: "simple",
|
||||
want: []*rules.DNSRewrite{{
|
||||
RCode: dns.RcodeSuccess,
|
||||
Value: net.IPv4(1, 0, 0, 1),
|
||||
RRType: dns.TypeA,
|
||||
}, {
|
||||
RCode: dns.RcodeSuccess,
|
||||
Value: net.ParseIP("::1"),
|
||||
RRType: dns.TypeAAAA,
|
||||
}},
|
||||
want: append(testHosts[addr1001], testHosts[addr1]...),
|
||||
}, {
|
||||
req: &urlfilter.DNSRequest{
|
||||
Hostname: "hello.world",
|
||||
DNSType: dns.TypeA,
|
||||
},
|
||||
req: "hello.world",
|
||||
name: "hello_alias",
|
||||
want: []*rules.DNSRewrite{{
|
||||
RCode: dns.RcodeSuccess,
|
||||
Value: net.IPv4(1, 0, 0, 0),
|
||||
RRType: dns.TypeA,
|
||||
}, {
|
||||
RCode: dns.RcodeSuccess,
|
||||
Value: net.ParseIP("::"),
|
||||
RRType: dns.TypeAAAA,
|
||||
}},
|
||||
}, {
|
||||
req: &urlfilter.DNSRequest{
|
||||
Hostname: "hello.world.again",
|
||||
DNSType: dns.TypeA,
|
||||
want: []*hostsfile.Record{
|
||||
testHosts[addr1000][0],
|
||||
testHosts[addr1000][2],
|
||||
testHosts[addr0][0],
|
||||
testHosts[addr0][2],
|
||||
},
|
||||
}, {
|
||||
req: "hello.world.again",
|
||||
name: "other_line_alias",
|
||||
want: []*rules.DNSRewrite{{
|
||||
RCode: dns.RcodeSuccess,
|
||||
Value: net.IPv4(1, 0, 0, 0),
|
||||
RRType: dns.TypeA,
|
||||
}, {
|
||||
RCode: dns.RcodeSuccess,
|
||||
Value: net.ParseIP("::"),
|
||||
RRType: dns.TypeAAAA,
|
||||
}},
|
||||
}, {
|
||||
req: &urlfilter.DNSRequest{
|
||||
Hostname: "say.hello",
|
||||
DNSType: dns.TypeA,
|
||||
want: []*hostsfile.Record{
|
||||
testHosts[addr1000][1],
|
||||
testHosts[addr0][1],
|
||||
},
|
||||
}, {
|
||||
req: "say.hello",
|
||||
name: "hello_subdomain",
|
||||
want: []*rules.DNSRewrite{},
|
||||
}, {
|
||||
req: &urlfilter.DNSRequest{
|
||||
Hostname: "say.hello.world",
|
||||
DNSType: dns.TypeA,
|
||||
},
|
||||
name: "hello_alias_subdomain",
|
||||
want: []*rules.DNSRewrite{},
|
||||
}, {
|
||||
req: &urlfilter.DNSRequest{
|
||||
Hostname: "for.testing",
|
||||
DNSType: dns.TypeA,
|
||||
},
|
||||
name: "lots_of_aliases",
|
||||
want: []*rules.DNSRewrite{{
|
||||
RCode: dns.RcodeSuccess,
|
||||
RRType: dns.TypeA,
|
||||
Value: net.IPv4(1, 0, 0, 2),
|
||||
}, {
|
||||
RCode: dns.RcodeSuccess,
|
||||
RRType: dns.TypeAAAA,
|
||||
Value: net.ParseIP("::2"),
|
||||
}},
|
||||
}, {
|
||||
req: &urlfilter.DNSRequest{
|
||||
Hostname: "1.0.0.1.in-addr.arpa",
|
||||
DNSType: dns.TypePTR,
|
||||
},
|
||||
name: "reverse",
|
||||
want: []*rules.DNSRewrite{{
|
||||
RCode: dns.RcodeSuccess,
|
||||
RRType: dns.TypePTR,
|
||||
Value: "simplehost.",
|
||||
}},
|
||||
}, {
|
||||
req: &urlfilter.DNSRequest{
|
||||
Hostname: "nonexistent.example",
|
||||
DNSType: dns.TypeA,
|
||||
},
|
||||
name: "non-existing",
|
||||
want: []*rules.DNSRewrite{},
|
||||
}, {
|
||||
req: &urlfilter.DNSRequest{
|
||||
Hostname: "1.0.0.1.in-addr.arpa",
|
||||
DNSType: dns.TypeSRV,
|
||||
},
|
||||
name: "bad_type",
|
||||
want: nil,
|
||||
}, {
|
||||
req: &urlfilter.DNSRequest{
|
||||
Hostname: "domain",
|
||||
DNSType: dns.TypeA,
|
||||
},
|
||||
req: "say.hello.world",
|
||||
name: "hello_alias_subdomain",
|
||||
want: nil,
|
||||
}, {
|
||||
req: "for.testing",
|
||||
name: "lots_of_aliases",
|
||||
want: append(testHosts[addr1002], testHosts[addr2]...),
|
||||
}, {
|
||||
req: "nonexistent.example",
|
||||
name: "non-existing",
|
||||
want: nil,
|
||||
}, {
|
||||
req: "domain",
|
||||
name: "issue_4216_4_6",
|
||||
want: []*rules.DNSRewrite{{
|
||||
RCode: dns.RcodeSuccess,
|
||||
RRType: dns.TypeA,
|
||||
Value: net.IPv4(4, 2, 1, 6),
|
||||
}, {
|
||||
RCode: dns.RcodeSuccess,
|
||||
RRType: dns.TypeAAAA,
|
||||
Value: net.ParseIP("::42"),
|
||||
}},
|
||||
want: append(testHosts[addr4216], testHosts[addr42]...),
|
||||
}, {
|
||||
req: &urlfilter.DNSRequest{
|
||||
Hostname: "domain4",
|
||||
DNSType: dns.TypeA,
|
||||
},
|
||||
req: "domain4",
|
||||
name: "issue_4216_4",
|
||||
want: []*rules.DNSRewrite{{
|
||||
RCode: dns.RcodeSuccess,
|
||||
RRType: dns.TypeA,
|
||||
Value: net.IPv4(7, 5, 3, 1),
|
||||
}, {
|
||||
RCode: dns.RcodeSuccess,
|
||||
RRType: dns.TypeA,
|
||||
Value: net.IPv4(1, 3, 5, 7),
|
||||
}},
|
||||
want: append(testHosts[addr1357], testHosts[addr7531]...),
|
||||
}, {
|
||||
req: &urlfilter.DNSRequest{
|
||||
Hostname: "domain6",
|
||||
DNSType: dns.TypeAAAA,
|
||||
},
|
||||
req: "domain6",
|
||||
name: "issue_4216_6",
|
||||
want: []*rules.DNSRewrite{{
|
||||
RCode: dns.RcodeSuccess,
|
||||
RRType: dns.TypeAAAA,
|
||||
Value: net.ParseIP("::13"),
|
||||
}, {
|
||||
RCode: dns.RcodeSuccess,
|
||||
RRType: dns.TypeAAAA,
|
||||
Value: net.ParseIP("::31"),
|
||||
}},
|
||||
want: append(testHosts[addr13], testHosts[addr31]...),
|
||||
}}
|
||||
|
||||
hc, err := aghnet.NewHostsContainer(testdata, &stubWatcher, "etc_hosts")
|
||||
require.NoError(t, err)
|
||||
testutil.CleanupAndRequireSuccess(t, hc.Close)
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
recs := hc.MatchName(tc.req)
|
||||
assert.Equal(t, tc.want, recs)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHostsContainer_MatchAddr(t *testing.T) {
|
||||
require.NoError(t, fstest.TestFS(testdata, "etc_hosts"))
|
||||
|
||||
stubWatcher := aghtest.FSWatcher{
|
||||
OnEvents: func() (e <-chan struct{}) { return nil },
|
||||
OnAdd: func(name string) (err error) { return nil },
|
||||
OnClose: func() (err error) { return nil },
|
||||
}
|
||||
|
||||
hc, err := NewHostsContainer(listID, testdata, &stubWatcher, "etc_hosts")
|
||||
hc, err := aghnet.NewHostsContainer(testdata, &stubWatcher, "etc_hosts")
|
||||
require.NoError(t, err)
|
||||
testutil.CleanupAndRequireSuccess(t, hc.Close)
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
res, ok := hc.MatchRequest(tc.req)
|
||||
require.False(t, ok)
|
||||
|
||||
if tc.want == nil {
|
||||
assert.Nil(t, res)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
require.NotNil(t, res)
|
||||
|
||||
rewrites := res.DNSRewrites()
|
||||
require.Len(t, rewrites, len(tc.want))
|
||||
|
||||
for i, rewrite := range rewrites {
|
||||
require.Equal(t, listID, rewrite.FilterListID)
|
||||
|
||||
rw := rewrite.DNSRewrite
|
||||
require.NotNil(t, rw)
|
||||
|
||||
assert.Equal(t, tc.want[i], rw)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUniqueRules_ParseLine(t *testing.T) {
|
||||
ip := netutil.IPv4Localhost()
|
||||
ipStr := ip.String()
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
line string
|
||||
wantIP netip.Addr
|
||||
wantHosts []string
|
||||
req netip.Addr
|
||||
name string
|
||||
want []*hostsfile.Record
|
||||
}{{
|
||||
name: "simple",
|
||||
line: ipStr + ` hostname`,
|
||||
wantIP: ip,
|
||||
wantHosts: []string{"hostname"},
|
||||
}, {
|
||||
name: "aliases",
|
||||
line: ipStr + ` hostname alias`,
|
||||
wantIP: ip,
|
||||
wantHosts: []string{"hostname", "alias"},
|
||||
}, {
|
||||
name: "invalid_line",
|
||||
line: ipStr,
|
||||
wantIP: netip.Addr{},
|
||||
wantHosts: nil,
|
||||
}, {
|
||||
name: "invalid_line_hostname",
|
||||
line: ipStr + ` # hostname`,
|
||||
wantIP: ip,
|
||||
wantHosts: nil,
|
||||
}, {
|
||||
name: "commented_aliases",
|
||||
line: ipStr + ` hostname # alias`,
|
||||
wantIP: ip,
|
||||
wantHosts: []string{"hostname"},
|
||||
}, {
|
||||
name: "whole_comment",
|
||||
line: `# ` + ipStr + ` hostname`,
|
||||
wantIP: netip.Addr{},
|
||||
wantHosts: nil,
|
||||
}, {
|
||||
name: "partial_comment",
|
||||
line: ipStr + ` host#name`,
|
||||
wantIP: ip,
|
||||
wantHosts: []string{"host"},
|
||||
}, {
|
||||
name: "empty",
|
||||
line: ``,
|
||||
wantIP: netip.Addr{},
|
||||
wantHosts: nil,
|
||||
}, {
|
||||
name: "bad_hosts",
|
||||
line: ipStr + ` bad..host bad._tld empty.tld. ok.host`,
|
||||
wantIP: ip,
|
||||
wantHosts: []string{"ok.host"},
|
||||
req: netip.AddrFrom4([4]byte{1, 0, 0, 1}),
|
||||
name: "reverse",
|
||||
want: testHosts[addr1001],
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
hp := hostsParser{}
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
got, hosts := hp.parseLine(tc.line)
|
||||
assert.Equal(t, tc.wantIP, got)
|
||||
assert.Equal(t, tc.wantHosts, hosts)
|
||||
recs := hc.MatchAddr(tc.req)
|
||||
assert.Equal(t, tc.want, recs)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ package aghnet
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"syscall"
|
||||
@@ -24,20 +23,9 @@ func reuseAddrCtrl(_, _ string, c syscall.RawConn) (err error) {
|
||||
}
|
||||
})
|
||||
|
||||
const (
|
||||
errMsg = "setting control options"
|
||||
errMsgFmt = errMsg + ": %w"
|
||||
)
|
||||
err = errors.Join(err, cerr)
|
||||
|
||||
if err != nil && cerr != nil {
|
||||
err = errors.List(errMsg, err, cerr)
|
||||
} else if err != nil {
|
||||
err = fmt.Errorf(errMsgFmt, err)
|
||||
} else if cerr != nil {
|
||||
err = fmt.Errorf(errMsgFmt, cerr)
|
||||
}
|
||||
|
||||
return err
|
||||
return errors.Annotate(err, "setting control options: %w")
|
||||
}
|
||||
|
||||
// listenPacketReusable announces on the local network address additionally
|
||||
@@ -390,9 +390,5 @@ func (m *ipsetMgr) Close() (err error) {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
|
||||
if len(errs) != 0 {
|
||||
return errors.List("closing ipsets", errs...)
|
||||
}
|
||||
|
||||
return nil
|
||||
return errors.Annotate(errors.Join(errs...), "closing ipsets: %w")
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package aghnet
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -15,6 +16,10 @@ import (
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
)
|
||||
|
||||
// DialContextFunc is the semantic alias for dialing functions, such as
|
||||
// [http.Transport.DialContext].
|
||||
type DialContextFunc = func(ctx context.Context, network, addr string) (conn net.Conn, err error)
|
||||
|
||||
// Variables and functions to substitute in tests.
|
||||
var (
|
||||
// aghosRunCommand is the function to run shell commands.
|
||||
|
||||
@@ -5,9 +5,9 @@ import (
|
||||
"testing"
|
||||
"testing/fstest"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghtest"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/testutil"
|
||||
"github.com/AdguardTeam/golibs/testutil/fakefs"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -118,7 +118,7 @@ func TestIfaceSetStaticIP(t *testing.T) {
|
||||
Data: []byte(`nameserver 1.1.1.1`),
|
||||
},
|
||||
}
|
||||
panicFsys := &aghtest.FS{
|
||||
panicFsys := &fakefs.FS{
|
||||
OnOpen: func(name string) (fs.File, error) { panic("not implemented") },
|
||||
}
|
||||
|
||||
|
||||
330
internal/aghnet/net_internal_test.go
Normal file
330
internal/aghnet/net_internal_test.go
Normal file
@@ -0,0 +1,330 @@
|
||||
package aghnet
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"net"
|
||||
"net/netip"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/netutil"
|
||||
"github.com/AdguardTeam/golibs/testutil"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// substRootDirFS replaces the aghos.RootDirFS function used throughout the
|
||||
// package with fsys for tests ran under t.
|
||||
func substRootDirFS(t testing.TB, fsys fs.FS) {
|
||||
t.Helper()
|
||||
|
||||
prev := rootDirFS
|
||||
t.Cleanup(func() { rootDirFS = prev })
|
||||
rootDirFS = fsys
|
||||
}
|
||||
|
||||
// RunCmdFunc is the signature of aghos.RunCommand function.
|
||||
type RunCmdFunc func(cmd string, args ...string) (code int, out []byte, err error)
|
||||
|
||||
// substShell replaces the the aghos.RunCommand function used throughout the
|
||||
// package with rc for tests ran under t.
|
||||
func substShell(t testing.TB, rc RunCmdFunc) {
|
||||
t.Helper()
|
||||
|
||||
prev := aghosRunCommand
|
||||
t.Cleanup(func() { aghosRunCommand = prev })
|
||||
aghosRunCommand = rc
|
||||
}
|
||||
|
||||
// mapShell is a substitution of aghos.RunCommand that maps the command to it's
|
||||
// execution result. It's only needed to simplify testing.
|
||||
//
|
||||
// TODO(e.burkov): Perhaps put all the shell interactions behind an interface.
|
||||
type mapShell map[string]struct {
|
||||
err error
|
||||
out string
|
||||
code int
|
||||
}
|
||||
|
||||
// theOnlyCmd returns mapShell that only handles a single command and arguments
|
||||
// combination from cmd.
|
||||
func theOnlyCmd(cmd string, code int, out string, err error) (s mapShell) {
|
||||
return mapShell{cmd: {code: code, out: out, err: err}}
|
||||
}
|
||||
|
||||
// RunCmd is a RunCmdFunc handled by s.
|
||||
func (s mapShell) RunCmd(cmd string, args ...string) (code int, out []byte, err error) {
|
||||
key := strings.Join(append([]string{cmd}, args...), " ")
|
||||
ret, ok := s[key]
|
||||
if !ok {
|
||||
return 0, nil, fmt.Errorf("unexpected shell command %q", key)
|
||||
}
|
||||
|
||||
return ret.code, []byte(ret.out), ret.err
|
||||
}
|
||||
|
||||
// ifaceAddrsFunc is the signature of net.InterfaceAddrs function.
|
||||
type ifaceAddrsFunc func() (ifaces []net.Addr, err error)
|
||||
|
||||
// substNetInterfaceAddrs replaces the the net.InterfaceAddrs function used
|
||||
// throughout the package with f for tests ran under t.
|
||||
func substNetInterfaceAddrs(t *testing.T, f ifaceAddrsFunc) {
|
||||
t.Helper()
|
||||
|
||||
prev := netInterfaceAddrs
|
||||
t.Cleanup(func() { netInterfaceAddrs = prev })
|
||||
netInterfaceAddrs = f
|
||||
}
|
||||
|
||||
func TestGatewayIP(t *testing.T) {
|
||||
const ifaceName = "ifaceName"
|
||||
const cmd = "ip route show dev " + ifaceName
|
||||
|
||||
testCases := []struct {
|
||||
shell mapShell
|
||||
want netip.Addr
|
||||
name string
|
||||
}{{
|
||||
shell: theOnlyCmd(cmd, 0, `default via 1.2.3.4 onlink`, nil),
|
||||
want: netip.MustParseAddr("1.2.3.4"),
|
||||
name: "success_v4",
|
||||
}, {
|
||||
shell: theOnlyCmd(cmd, 0, `default via ::ffff onlink`, nil),
|
||||
want: netip.MustParseAddr("::ffff"),
|
||||
name: "success_v6",
|
||||
}, {
|
||||
shell: theOnlyCmd(cmd, 0, `non-default via 1.2.3.4 onlink`, nil),
|
||||
want: netip.Addr{},
|
||||
name: "bad_output",
|
||||
}, {
|
||||
shell: theOnlyCmd(cmd, 0, "", errors.Error("can't run command")),
|
||||
want: netip.Addr{},
|
||||
name: "err_runcmd",
|
||||
}, {
|
||||
shell: theOnlyCmd(cmd, 1, "", nil),
|
||||
want: netip.Addr{},
|
||||
name: "bad_code",
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
substShell(t, tc.shell.RunCmd)
|
||||
|
||||
assert.Equal(t, tc.want, GatewayIP(ifaceName))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestInterfaceByIP(t *testing.T) {
|
||||
ifaces, err := GetValidNetInterfacesForWeb()
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, ifaces)
|
||||
|
||||
for _, iface := range ifaces {
|
||||
t.Run(iface.Name, func(t *testing.T) {
|
||||
require.NotEmpty(t, iface.Addresses)
|
||||
|
||||
for _, ip := range iface.Addresses {
|
||||
ifaceName := InterfaceByIP(ip)
|
||||
require.Equal(t, iface.Name, ifaceName)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBroadcastFromIPNet(t *testing.T) {
|
||||
known4 := netip.MustParseAddr("192.168.0.1")
|
||||
fullBroadcast4 := netip.MustParseAddr("255.255.255.255")
|
||||
|
||||
known6 := netip.MustParseAddr("102:304:506:708:90a:b0c:d0e:f10")
|
||||
|
||||
testCases := []struct {
|
||||
pref netip.Prefix
|
||||
want netip.Addr
|
||||
name string
|
||||
}{{
|
||||
pref: netip.PrefixFrom(known4, 0),
|
||||
want: fullBroadcast4,
|
||||
name: "full",
|
||||
}, {
|
||||
pref: netip.PrefixFrom(known4, 20),
|
||||
want: netip.MustParseAddr("192.168.15.255"),
|
||||
name: "full",
|
||||
}, {
|
||||
pref: netip.PrefixFrom(known6, netutil.IPv6BitLen),
|
||||
want: known6,
|
||||
name: "ipv6_no_mask",
|
||||
}, {
|
||||
pref: netip.PrefixFrom(known4, netutil.IPv4BitLen),
|
||||
want: known4,
|
||||
name: "ipv4_no_mask",
|
||||
}, {
|
||||
pref: netip.PrefixFrom(netip.IPv4Unspecified(), 0),
|
||||
want: fullBroadcast4,
|
||||
name: "unspecified",
|
||||
}, {
|
||||
pref: netip.Prefix{},
|
||||
want: netip.Addr{},
|
||||
name: "invalid",
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
assert.Equal(t, tc.want, BroadcastFromPref(tc.pref))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckPort(t *testing.T) {
|
||||
laddr := netip.AddrPortFrom(netutil.IPv4Localhost(), 0)
|
||||
|
||||
t.Run("tcp_bound", func(t *testing.T) {
|
||||
l, err := net.Listen("tcp", laddr.String())
|
||||
require.NoError(t, err)
|
||||
testutil.CleanupAndRequireSuccess(t, l.Close)
|
||||
|
||||
ipp := testutil.RequireTypeAssert[*net.TCPAddr](t, l.Addr()).AddrPort()
|
||||
require.Equal(t, laddr.Addr(), ipp.Addr())
|
||||
require.NotZero(t, ipp.Port())
|
||||
|
||||
err = CheckPort("tcp", ipp)
|
||||
target := &net.OpError{}
|
||||
require.ErrorAs(t, err, &target)
|
||||
|
||||
assert.Equal(t, "listen", target.Op)
|
||||
})
|
||||
|
||||
t.Run("udp_bound", func(t *testing.T) {
|
||||
conn, err := net.ListenPacket("udp", laddr.String())
|
||||
require.NoError(t, err)
|
||||
testutil.CleanupAndRequireSuccess(t, conn.Close)
|
||||
|
||||
ipp := testutil.RequireTypeAssert[*net.UDPAddr](t, conn.LocalAddr()).AddrPort()
|
||||
require.Equal(t, laddr.Addr(), ipp.Addr())
|
||||
require.NotZero(t, ipp.Port())
|
||||
|
||||
err = CheckPort("udp", ipp)
|
||||
target := &net.OpError{}
|
||||
require.ErrorAs(t, err, &target)
|
||||
|
||||
assert.Equal(t, "listen", target.Op)
|
||||
})
|
||||
|
||||
t.Run("bad_network", func(t *testing.T) {
|
||||
err := CheckPort("bad_network", netip.AddrPortFrom(netip.Addr{}, 0))
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("can_bind", func(t *testing.T) {
|
||||
err := CheckPort("udp", netip.AddrPortFrom(netip.IPv4Unspecified(), 0))
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestCollectAllIfacesAddrs(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
wantErrMsg string
|
||||
addrs []net.Addr
|
||||
wantAddrs []string
|
||||
}{{
|
||||
name: "success",
|
||||
wantErrMsg: ``,
|
||||
addrs: []net.Addr{&net.IPNet{
|
||||
IP: net.IP{1, 2, 3, 4},
|
||||
Mask: net.CIDRMask(24, netutil.IPv4BitLen),
|
||||
}, &net.IPNet{
|
||||
IP: net.IP{4, 3, 2, 1},
|
||||
Mask: net.CIDRMask(16, netutil.IPv4BitLen),
|
||||
}},
|
||||
wantAddrs: []string{"1.2.3.4", "4.3.2.1"},
|
||||
}, {
|
||||
name: "not_cidr",
|
||||
wantErrMsg: `parsing cidr: invalid CIDR address: 1.2.3.4`,
|
||||
addrs: []net.Addr{&net.IPAddr{
|
||||
IP: net.IP{1, 2, 3, 4},
|
||||
}},
|
||||
wantAddrs: nil,
|
||||
}, {
|
||||
name: "empty",
|
||||
wantErrMsg: ``,
|
||||
addrs: []net.Addr{},
|
||||
wantAddrs: nil,
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
substNetInterfaceAddrs(t, func() ([]net.Addr, error) { return tc.addrs, nil })
|
||||
|
||||
addrs, err := CollectAllIfacesAddrs()
|
||||
testutil.AssertErrorMsg(t, tc.wantErrMsg, err)
|
||||
|
||||
assert.Equal(t, tc.wantAddrs, addrs)
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("internal_error", func(t *testing.T) {
|
||||
const errAddrs errors.Error = "can't get addresses"
|
||||
const wantErrMsg string = `getting interfaces addresses: ` + string(errAddrs)
|
||||
|
||||
substNetInterfaceAddrs(t, func() ([]net.Addr, error) { return nil, errAddrs })
|
||||
|
||||
_, err := CollectAllIfacesAddrs()
|
||||
testutil.AssertErrorMsg(t, wantErrMsg, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIsAddrInUse(t *testing.T) {
|
||||
t.Run("addr_in_use", func(t *testing.T) {
|
||||
l, err := net.Listen("tcp", "0.0.0.0:0")
|
||||
require.NoError(t, err)
|
||||
testutil.CleanupAndRequireSuccess(t, l.Close)
|
||||
|
||||
_, err = net.Listen(l.Addr().Network(), l.Addr().String())
|
||||
assert.True(t, IsAddrInUse(err))
|
||||
})
|
||||
|
||||
t.Run("another", func(t *testing.T) {
|
||||
const anotherErr errors.Error = "not addr in use"
|
||||
|
||||
assert.False(t, IsAddrInUse(anotherErr))
|
||||
})
|
||||
}
|
||||
|
||||
func TestNetInterface_MarshalJSON(t *testing.T) {
|
||||
const want = `{` +
|
||||
`"hardware_address":"aa:bb:cc:dd:ee:ff",` +
|
||||
`"flags":"up|multicast",` +
|
||||
`"ip_addresses":["1.2.3.4","aaaa::1"],` +
|
||||
`"name":"iface0",` +
|
||||
`"mtu":1500` +
|
||||
`}` + "\n"
|
||||
|
||||
ip4, ok := netip.AddrFromSlice([]byte{1, 2, 3, 4})
|
||||
require.True(t, ok)
|
||||
|
||||
ip6, ok := netip.AddrFromSlice([]byte{0xAA, 0xAA, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1})
|
||||
require.True(t, ok)
|
||||
|
||||
net4 := netip.PrefixFrom(ip4, 24)
|
||||
net6 := netip.PrefixFrom(ip6, 8)
|
||||
|
||||
iface := &NetInterface{
|
||||
Addresses: []netip.Addr{ip4, ip6},
|
||||
Subnets: []netip.Prefix{net4, net6},
|
||||
Name: "iface0",
|
||||
HardwareAddr: net.HardwareAddr{0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF},
|
||||
Flags: net.FlagUp | net.FlagMulticast,
|
||||
MTU: 1500,
|
||||
}
|
||||
|
||||
b := &bytes.Buffer{}
|
||||
err := json.NewEncoder(b).Encode(iface)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, want, b.String())
|
||||
}
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/AdguardTeam/golibs/stringutil"
|
||||
"github.com/google/renameio/maybe"
|
||||
"github.com/google/renameio/v2/maybe"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
|
||||
@@ -1,21 +1,11 @@
|
||||
package aghnet
|
||||
package aghnet_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"net"
|
||||
"net/netip"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/netutil"
|
||||
"github.com/AdguardTeam/golibs/testutil"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
@@ -24,315 +14,3 @@ func TestMain(m *testing.M) {
|
||||
|
||||
// testdata is the filesystem containing data for testing the package.
|
||||
var testdata fs.FS = os.DirFS("./testdata")
|
||||
|
||||
// substRootDirFS replaces the aghos.RootDirFS function used throughout the
|
||||
// package with fsys for tests ran under t.
|
||||
func substRootDirFS(t testing.TB, fsys fs.FS) {
|
||||
t.Helper()
|
||||
|
||||
prev := rootDirFS
|
||||
t.Cleanup(func() { rootDirFS = prev })
|
||||
rootDirFS = fsys
|
||||
}
|
||||
|
||||
// RunCmdFunc is the signature of aghos.RunCommand function.
|
||||
type RunCmdFunc func(cmd string, args ...string) (code int, out []byte, err error)
|
||||
|
||||
// substShell replaces the the aghos.RunCommand function used throughout the
|
||||
// package with rc for tests ran under t.
|
||||
func substShell(t testing.TB, rc RunCmdFunc) {
|
||||
t.Helper()
|
||||
|
||||
prev := aghosRunCommand
|
||||
t.Cleanup(func() { aghosRunCommand = prev })
|
||||
aghosRunCommand = rc
|
||||
}
|
||||
|
||||
// mapShell is a substitution of aghos.RunCommand that maps the command to it's
|
||||
// execution result. It's only needed to simplify testing.
|
||||
//
|
||||
// TODO(e.burkov): Perhaps put all the shell interactions behind an interface.
|
||||
type mapShell map[string]struct {
|
||||
err error
|
||||
out string
|
||||
code int
|
||||
}
|
||||
|
||||
// theOnlyCmd returns mapShell that only handles a single command and arguments
|
||||
// combination from cmd.
|
||||
func theOnlyCmd(cmd string, code int, out string, err error) (s mapShell) {
|
||||
return mapShell{cmd: {code: code, out: out, err: err}}
|
||||
}
|
||||
|
||||
// RunCmd is a RunCmdFunc handled by s.
|
||||
func (s mapShell) RunCmd(cmd string, args ...string) (code int, out []byte, err error) {
|
||||
key := strings.Join(append([]string{cmd}, args...), " ")
|
||||
ret, ok := s[key]
|
||||
if !ok {
|
||||
return 0, nil, fmt.Errorf("unexpected shell command %q", key)
|
||||
}
|
||||
|
||||
return ret.code, []byte(ret.out), ret.err
|
||||
}
|
||||
|
||||
// ifaceAddrsFunc is the signature of net.InterfaceAddrs function.
|
||||
type ifaceAddrsFunc func() (ifaces []net.Addr, err error)
|
||||
|
||||
// substNetInterfaceAddrs replaces the the net.InterfaceAddrs function used
|
||||
// throughout the package with f for tests ran under t.
|
||||
func substNetInterfaceAddrs(t *testing.T, f ifaceAddrsFunc) {
|
||||
t.Helper()
|
||||
|
||||
prev := netInterfaceAddrs
|
||||
t.Cleanup(func() { netInterfaceAddrs = prev })
|
||||
netInterfaceAddrs = f
|
||||
}
|
||||
|
||||
func TestGatewayIP(t *testing.T) {
|
||||
const ifaceName = "ifaceName"
|
||||
const cmd = "ip route show dev " + ifaceName
|
||||
|
||||
testCases := []struct {
|
||||
shell mapShell
|
||||
want netip.Addr
|
||||
name string
|
||||
}{{
|
||||
shell: theOnlyCmd(cmd, 0, `default via 1.2.3.4 onlink`, nil),
|
||||
want: netip.MustParseAddr("1.2.3.4"),
|
||||
name: "success_v4",
|
||||
}, {
|
||||
shell: theOnlyCmd(cmd, 0, `default via ::ffff onlink`, nil),
|
||||
want: netip.MustParseAddr("::ffff"),
|
||||
name: "success_v6",
|
||||
}, {
|
||||
shell: theOnlyCmd(cmd, 0, `non-default via 1.2.3.4 onlink`, nil),
|
||||
want: netip.Addr{},
|
||||
name: "bad_output",
|
||||
}, {
|
||||
shell: theOnlyCmd(cmd, 0, "", errors.Error("can't run command")),
|
||||
want: netip.Addr{},
|
||||
name: "err_runcmd",
|
||||
}, {
|
||||
shell: theOnlyCmd(cmd, 1, "", nil),
|
||||
want: netip.Addr{},
|
||||
name: "bad_code",
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
substShell(t, tc.shell.RunCmd)
|
||||
|
||||
assert.Equal(t, tc.want, GatewayIP(ifaceName))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestInterfaceByIP(t *testing.T) {
|
||||
ifaces, err := GetValidNetInterfacesForWeb()
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, ifaces)
|
||||
|
||||
for _, iface := range ifaces {
|
||||
t.Run(iface.Name, func(t *testing.T) {
|
||||
require.NotEmpty(t, iface.Addresses)
|
||||
|
||||
for _, ip := range iface.Addresses {
|
||||
ifaceName := InterfaceByIP(ip)
|
||||
require.Equal(t, iface.Name, ifaceName)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBroadcastFromIPNet(t *testing.T) {
|
||||
known4 := netip.MustParseAddr("192.168.0.1")
|
||||
fullBroadcast4 := netip.MustParseAddr("255.255.255.255")
|
||||
|
||||
known6 := netip.MustParseAddr("102:304:506:708:90a:b0c:d0e:f10")
|
||||
|
||||
testCases := []struct {
|
||||
pref netip.Prefix
|
||||
want netip.Addr
|
||||
name string
|
||||
}{{
|
||||
pref: netip.PrefixFrom(known4, 0),
|
||||
want: fullBroadcast4,
|
||||
name: "full",
|
||||
}, {
|
||||
pref: netip.PrefixFrom(known4, 20),
|
||||
want: netip.MustParseAddr("192.168.15.255"),
|
||||
name: "full",
|
||||
}, {
|
||||
pref: netip.PrefixFrom(known6, netutil.IPv6BitLen),
|
||||
want: known6,
|
||||
name: "ipv6_no_mask",
|
||||
}, {
|
||||
pref: netip.PrefixFrom(known4, netutil.IPv4BitLen),
|
||||
want: known4,
|
||||
name: "ipv4_no_mask",
|
||||
}, {
|
||||
pref: netip.PrefixFrom(netip.IPv4Unspecified(), 0),
|
||||
want: fullBroadcast4,
|
||||
name: "unspecified",
|
||||
}, {
|
||||
pref: netip.Prefix{},
|
||||
want: netip.Addr{},
|
||||
name: "invalid",
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
assert.Equal(t, tc.want, BroadcastFromPref(tc.pref))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckPort(t *testing.T) {
|
||||
laddr := netip.AddrPortFrom(netutil.IPv4Localhost(), 0)
|
||||
|
||||
t.Run("tcp_bound", func(t *testing.T) {
|
||||
l, err := net.Listen("tcp", laddr.String())
|
||||
require.NoError(t, err)
|
||||
testutil.CleanupAndRequireSuccess(t, l.Close)
|
||||
|
||||
ipp := testutil.RequireTypeAssert[*net.TCPAddr](t, l.Addr()).AddrPort()
|
||||
require.Equal(t, laddr.Addr(), ipp.Addr())
|
||||
require.NotZero(t, ipp.Port())
|
||||
|
||||
err = CheckPort("tcp", ipp)
|
||||
target := &net.OpError{}
|
||||
require.ErrorAs(t, err, &target)
|
||||
|
||||
assert.Equal(t, "listen", target.Op)
|
||||
})
|
||||
|
||||
t.Run("udp_bound", func(t *testing.T) {
|
||||
conn, err := net.ListenPacket("udp", laddr.String())
|
||||
require.NoError(t, err)
|
||||
testutil.CleanupAndRequireSuccess(t, conn.Close)
|
||||
|
||||
ipp := testutil.RequireTypeAssert[*net.UDPAddr](t, conn.LocalAddr()).AddrPort()
|
||||
require.Equal(t, laddr.Addr(), ipp.Addr())
|
||||
require.NotZero(t, ipp.Port())
|
||||
|
||||
err = CheckPort("udp", ipp)
|
||||
target := &net.OpError{}
|
||||
require.ErrorAs(t, err, &target)
|
||||
|
||||
assert.Equal(t, "listen", target.Op)
|
||||
})
|
||||
|
||||
t.Run("bad_network", func(t *testing.T) {
|
||||
err := CheckPort("bad_network", netip.AddrPortFrom(netip.Addr{}, 0))
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("can_bind", func(t *testing.T) {
|
||||
err := CheckPort("udp", netip.AddrPortFrom(netip.IPv4Unspecified(), 0))
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestCollectAllIfacesAddrs(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
wantErrMsg string
|
||||
addrs []net.Addr
|
||||
wantAddrs []string
|
||||
}{{
|
||||
name: "success",
|
||||
wantErrMsg: ``,
|
||||
addrs: []net.Addr{&net.IPNet{
|
||||
IP: net.IP{1, 2, 3, 4},
|
||||
Mask: net.CIDRMask(24, netutil.IPv4BitLen),
|
||||
}, &net.IPNet{
|
||||
IP: net.IP{4, 3, 2, 1},
|
||||
Mask: net.CIDRMask(16, netutil.IPv4BitLen),
|
||||
}},
|
||||
wantAddrs: []string{"1.2.3.4", "4.3.2.1"},
|
||||
}, {
|
||||
name: "not_cidr",
|
||||
wantErrMsg: `parsing cidr: invalid CIDR address: 1.2.3.4`,
|
||||
addrs: []net.Addr{&net.IPAddr{
|
||||
IP: net.IP{1, 2, 3, 4},
|
||||
}},
|
||||
wantAddrs: nil,
|
||||
}, {
|
||||
name: "empty",
|
||||
wantErrMsg: ``,
|
||||
addrs: []net.Addr{},
|
||||
wantAddrs: nil,
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
substNetInterfaceAddrs(t, func() ([]net.Addr, error) { return tc.addrs, nil })
|
||||
|
||||
addrs, err := CollectAllIfacesAddrs()
|
||||
testutil.AssertErrorMsg(t, tc.wantErrMsg, err)
|
||||
|
||||
assert.Equal(t, tc.wantAddrs, addrs)
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("internal_error", func(t *testing.T) {
|
||||
const errAddrs errors.Error = "can't get addresses"
|
||||
const wantErrMsg string = `getting interfaces addresses: ` + string(errAddrs)
|
||||
|
||||
substNetInterfaceAddrs(t, func() ([]net.Addr, error) { return nil, errAddrs })
|
||||
|
||||
_, err := CollectAllIfacesAddrs()
|
||||
testutil.AssertErrorMsg(t, wantErrMsg, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIsAddrInUse(t *testing.T) {
|
||||
t.Run("addr_in_use", func(t *testing.T) {
|
||||
l, err := net.Listen("tcp", "0.0.0.0:0")
|
||||
require.NoError(t, err)
|
||||
testutil.CleanupAndRequireSuccess(t, l.Close)
|
||||
|
||||
_, err = net.Listen(l.Addr().Network(), l.Addr().String())
|
||||
assert.True(t, IsAddrInUse(err))
|
||||
})
|
||||
|
||||
t.Run("another", func(t *testing.T) {
|
||||
const anotherErr errors.Error = "not addr in use"
|
||||
|
||||
assert.False(t, IsAddrInUse(anotherErr))
|
||||
})
|
||||
}
|
||||
|
||||
func TestNetInterface_MarshalJSON(t *testing.T) {
|
||||
const want = `{` +
|
||||
`"hardware_address":"aa:bb:cc:dd:ee:ff",` +
|
||||
`"flags":"up|multicast",` +
|
||||
`"ip_addresses":["1.2.3.4","aaaa::1"],` +
|
||||
`"name":"iface0",` +
|
||||
`"mtu":1500` +
|
||||
`}` + "\n"
|
||||
|
||||
ip4, ok := netip.AddrFromSlice([]byte{1, 2, 3, 4})
|
||||
require.True(t, ok)
|
||||
|
||||
ip6, ok := netip.AddrFromSlice([]byte{0xAA, 0xAA, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1})
|
||||
require.True(t, ok)
|
||||
|
||||
net4 := netip.PrefixFrom(ip4, 24)
|
||||
net6 := netip.PrefixFrom(ip6, 8)
|
||||
|
||||
iface := &NetInterface{
|
||||
Addresses: []netip.Addr{ip4, ip6},
|
||||
Subnets: []netip.Prefix{net4, net6},
|
||||
Name: "iface0",
|
||||
HardwareAddr: net.HardwareAddr{0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF},
|
||||
Flags: net.FlagUp | net.FlagMulticast,
|
||||
MTU: 1500,
|
||||
}
|
||||
|
||||
b := &bytes.Buffer{}
|
||||
err := json.NewEncoder(b).Encode(iface)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, want, b.String())
|
||||
}
|
||||
|
||||
52
internal/aghrenameio/renameio.go
Normal file
52
internal/aghrenameio/renameio.go
Normal file
@@ -0,0 +1,52 @@
|
||||
// Package aghrenameio is a wrapper around package github.com/google/renameio/v2
|
||||
// that provides a similar stream-based API for both Unix and Windows systems.
|
||||
// While the Windows API is not technically atomic, it still provides a
|
||||
// consistent stream-based interface, and atomic renames of files do not seem to
|
||||
// be possible in all cases anyway.
|
||||
//
|
||||
// See https://github.com/google/renameio/issues/1.
|
||||
//
|
||||
// TODO(a.garipov): Consider moving to golibs/renameioutil once tried and
|
||||
// tested.
|
||||
package aghrenameio
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
)
|
||||
|
||||
// PendingFile is the interface for pending temporary files.
|
||||
type PendingFile interface {
|
||||
// Cleanup closes the file, and removes it without performing the renaming.
|
||||
// To close and rename the file, use CloseReplace.
|
||||
Cleanup() (err error)
|
||||
|
||||
// CloseReplace closes the temporary file and replaces the destination file
|
||||
// with it, possibly atomically.
|
||||
//
|
||||
// This method is not safe for concurrent use by multiple goroutines.
|
||||
CloseReplace() (err error)
|
||||
|
||||
// Write writes len(b) bytes from b to the File. It returns the number of
|
||||
// bytes written and an error, if any. Write returns a non-nil error when n
|
||||
// != len(b).
|
||||
Write(b []byte) (n int, err error)
|
||||
}
|
||||
|
||||
// NewPendingFile is a wrapper around [renameio.NewPendingFile] on Unix systems
|
||||
// and [os.CreateTemp] on Windows.
|
||||
func NewPendingFile(filePath string, mode fs.FileMode) (f PendingFile, err error) {
|
||||
return newPendingFile(filePath, mode)
|
||||
}
|
||||
|
||||
// WithDeferredCleanup is a helper that performs the necessary cleanups and
|
||||
// finalizations of the temporary files based on the returned error.
|
||||
func WithDeferredCleanup(returned error, file PendingFile) (err error) {
|
||||
// Make sure that any error returned from here is marked as a deferred one.
|
||||
if returned != nil {
|
||||
return errors.WithDeferred(returned, file.Cleanup())
|
||||
}
|
||||
|
||||
return errors.WithDeferred(nil, file.CloseReplace())
|
||||
}
|
||||
101
internal/aghrenameio/renameio_test.go
Normal file
101
internal/aghrenameio/renameio_test.go
Normal file
@@ -0,0 +1,101 @@
|
||||
package aghrenameio_test
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghrenameio"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/testutil"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// testPerm is the common permission mode for tests.
|
||||
const testPerm fs.FileMode = 0o644
|
||||
|
||||
// Common file data for tests.
|
||||
var (
|
||||
initialData = []byte("initial data\n")
|
||||
newData = []byte("new data\n")
|
||||
)
|
||||
|
||||
func TestPendingFile(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
targetPath := newInitialFile(t)
|
||||
f, err := aghrenameio.NewPendingFile(targetPath, testPerm)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = f.Write(newData)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = f.CloseReplace()
|
||||
require.NoError(t, err)
|
||||
|
||||
gotData, err := os.ReadFile(targetPath)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, newData, gotData)
|
||||
}
|
||||
|
||||
// newInitialFile is a test helper that returns the path to the file containing
|
||||
// [initialData].
|
||||
func newInitialFile(t *testing.T) (targetPath string) {
|
||||
t.Helper()
|
||||
|
||||
dir := t.TempDir()
|
||||
targetPath = filepath.Join(dir, "target")
|
||||
|
||||
err := os.WriteFile(targetPath, initialData, 0o644)
|
||||
require.NoError(t, err)
|
||||
|
||||
return targetPath
|
||||
}
|
||||
|
||||
func TestWithDeferredCleanup(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const testError errors.Error = "test error"
|
||||
|
||||
testCases := []struct {
|
||||
error error
|
||||
name string
|
||||
wantErrMsg string
|
||||
wantData []byte
|
||||
}{{
|
||||
name: "success",
|
||||
error: nil,
|
||||
wantErrMsg: "",
|
||||
wantData: newData,
|
||||
}, {
|
||||
name: "error",
|
||||
error: testError,
|
||||
wantErrMsg: testError.Error(),
|
||||
wantData: initialData,
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
targetPath := newInitialFile(t)
|
||||
f, err := aghrenameio.NewPendingFile(targetPath, testPerm)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = f.Write(newData)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = aghrenameio.WithDeferredCleanup(tc.error, f)
|
||||
testutil.AssertErrorMsg(t, tc.wantErrMsg, err)
|
||||
|
||||
gotData, err := os.ReadFile(targetPath)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, tc.wantData, gotData)
|
||||
})
|
||||
}
|
||||
}
|
||||
48
internal/aghrenameio/renameio_unix.go
Normal file
48
internal/aghrenameio/renameio_unix.go
Normal file
@@ -0,0 +1,48 @@
|
||||
//go:build unix
|
||||
|
||||
package aghrenameio
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
|
||||
"github.com/google/renameio/v2"
|
||||
)
|
||||
|
||||
// pendingFile is a wrapper around [*renameio.PendingFile] making it an
|
||||
// [io.WriteCloser].
|
||||
type pendingFile struct {
|
||||
file *renameio.PendingFile
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ PendingFile = pendingFile{}
|
||||
|
||||
// Cleanup implements the [PendingFile] interface for pendingFile.
|
||||
func (f pendingFile) Cleanup() (err error) {
|
||||
return f.file.Cleanup()
|
||||
}
|
||||
|
||||
// CloseReplace implements the [PendingFile] interface for pendingFile.
|
||||
func (f pendingFile) CloseReplace() (err error) {
|
||||
return f.file.CloseAtomicallyReplace()
|
||||
}
|
||||
|
||||
// Write implements the [PendingFile] interface for pendingFile.
|
||||
func (f pendingFile) Write(b []byte) (n int, err error) {
|
||||
return f.file.Write(b)
|
||||
}
|
||||
|
||||
// NewPendingFile is a wrapper around [renameio.NewPendingFile].
|
||||
//
|
||||
// f.Close must be called to finish the renaming.
|
||||
func newPendingFile(filePath string, mode fs.FileMode) (f PendingFile, err error) {
|
||||
file, err := renameio.NewPendingFile(filePath, renameio.WithPermissions(mode))
|
||||
if err != nil {
|
||||
// Don't wrap the error since it's informative enough as is.
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return pendingFile{
|
||||
file: file,
|
||||
}, nil
|
||||
}
|
||||
74
internal/aghrenameio/renameio_windows.go
Normal file
74
internal/aghrenameio/renameio_windows.go
Normal file
@@ -0,0 +1,74 @@
|
||||
//go:build windows
|
||||
|
||||
package aghrenameio
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
)
|
||||
|
||||
// pendingFile is a wrapper around [*os.File] calling [os.Rename] in its Close
|
||||
// method.
|
||||
type pendingFile struct {
|
||||
file *os.File
|
||||
targetPath string
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ PendingFile = (*pendingFile)(nil)
|
||||
|
||||
// Cleanup implements the [PendingFile] interface for *pendingFile.
|
||||
func (f *pendingFile) Cleanup() (err error) {
|
||||
closeErr := f.file.Close()
|
||||
err = os.Remove(f.file.Name())
|
||||
|
||||
// Put closeErr into the deferred error because that's where it is usually
|
||||
// expected.
|
||||
return errors.WithDeferred(err, closeErr)
|
||||
}
|
||||
|
||||
// CloseReplace implements the [PendingFile] interface for *pendingFile.
|
||||
func (f *pendingFile) CloseReplace() (err error) {
|
||||
err = f.file.Close()
|
||||
if err != nil {
|
||||
return fmt.Errorf("closing: %w", err)
|
||||
}
|
||||
|
||||
err = os.Rename(f.file.Name(), f.targetPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("renaming: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Write implements the [PendingFile] interface for *pendingFile.
|
||||
func (f *pendingFile) Write(b []byte) (n int, err error) {
|
||||
return f.file.Write(b)
|
||||
}
|
||||
|
||||
// NewPendingFile is a wrapper around [os.CreateTemp].
|
||||
//
|
||||
// f.Close must be called to finish the renaming.
|
||||
func newPendingFile(filePath string, mode fs.FileMode) (f PendingFile, err error) {
|
||||
// Use the same directory as the file itself, because moves across
|
||||
// filesystems can be especially problematic.
|
||||
file, err := os.CreateTemp(filepath.Dir(filePath), "")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("opening pending file: %w", err)
|
||||
}
|
||||
|
||||
err = file.Chmod(mode)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("preparing pending file: %w", err)
|
||||
}
|
||||
|
||||
return &pendingFile{
|
||||
file: file,
|
||||
targetPath: filePath,
|
||||
}, nil
|
||||
}
|
||||
@@ -2,12 +2,22 @@
|
||||
package aghtest
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"io"
|
||||
"net/netip"
|
||||
"testing"
|
||||
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
)
|
||||
|
||||
const (
|
||||
// ReqHost is the common request host for filtering tests.
|
||||
ReqHost = "www.host.example"
|
||||
|
||||
// ReqFQDN is the common request FQDN for filtering tests.
|
||||
ReqFQDN = ReqHost + "."
|
||||
)
|
||||
|
||||
// ReplaceLogWriter moves logger output to w and uses Cleanup method of t to
|
||||
// revert changes.
|
||||
func ReplaceLogWriter(t testing.TB, w io.Writer) {
|
||||
@@ -34,3 +44,10 @@ func ReplaceLogLevel(t testing.TB, l log.Level) {
|
||||
t.Cleanup(func() { log.SetLevel(prev) })
|
||||
log.SetLevel(l)
|
||||
}
|
||||
|
||||
// HostToIPs is a helper that generates one IPv4 and one IPv6 address from host.
|
||||
func HostToIPs(host string) (ipv4, ipv6 netip.Addr) {
|
||||
hash := sha256.Sum256([]byte(host))
|
||||
|
||||
return netip.AddrFrom4([4]byte(hash[:4])), netip.AddrFrom16([16]byte(hash[4:20]))
|
||||
}
|
||||
|
||||
@@ -2,11 +2,15 @@ package aghtest
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io/fs"
|
||||
"net"
|
||||
"net/netip"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghos"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/client"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/next/agh"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/rdns"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/whois"
|
||||
"github.com/AdguardTeam/dnsproxy/upstream"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
@@ -15,94 +19,20 @@ import (
|
||||
//
|
||||
// Keep entities in this file in alphabetic order.
|
||||
|
||||
// Standard Library
|
||||
|
||||
// Package fs
|
||||
|
||||
// type check
|
||||
var _ fs.FS = &FS{}
|
||||
|
||||
// FS is a mock [fs.FS] implementation for tests.
|
||||
type FS struct {
|
||||
OnOpen func(name string) (fs.File, error)
|
||||
}
|
||||
|
||||
// Open implements the [fs.FS] interface for *FS.
|
||||
func (fsys *FS) Open(name string) (fs.File, error) {
|
||||
return fsys.OnOpen(name)
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ fs.GlobFS = &GlobFS{}
|
||||
|
||||
// GlobFS is a mock [fs.GlobFS] implementation for tests.
|
||||
type GlobFS struct {
|
||||
// FS is embedded here to avoid implementing all it's methods.
|
||||
FS
|
||||
OnGlob func(pattern string) ([]string, error)
|
||||
}
|
||||
|
||||
// Glob implements the [fs.GlobFS] interface for *GlobFS.
|
||||
func (fsys *GlobFS) Glob(pattern string) ([]string, error) {
|
||||
return fsys.OnGlob(pattern)
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ fs.StatFS = &StatFS{}
|
||||
|
||||
// StatFS is a mock [fs.StatFS] implementation for tests.
|
||||
type StatFS struct {
|
||||
// FS is embedded here to avoid implementing all it's methods.
|
||||
FS
|
||||
OnStat func(name string) (fs.FileInfo, error)
|
||||
}
|
||||
|
||||
// Stat implements the [fs.StatFS] interface for *StatFS.
|
||||
func (fsys *StatFS) Stat(name string) (fs.FileInfo, error) {
|
||||
return fsys.OnStat(name)
|
||||
}
|
||||
|
||||
// Package net
|
||||
|
||||
// type check
|
||||
var _ net.Listener = (*Listener)(nil)
|
||||
|
||||
// Listener is a mock [net.Listener] implementation for tests.
|
||||
type Listener struct {
|
||||
OnAccept func() (conn net.Conn, err error)
|
||||
OnAddr func() (addr net.Addr)
|
||||
OnClose func() (err error)
|
||||
}
|
||||
|
||||
// Accept implements the [net.Listener] interface for *Listener.
|
||||
func (l *Listener) Accept() (conn net.Conn, err error) {
|
||||
return l.OnAccept()
|
||||
}
|
||||
|
||||
// Addr implements the [net.Listener] interface for *Listener.
|
||||
func (l *Listener) Addr() (addr net.Addr) {
|
||||
return l.OnAddr()
|
||||
}
|
||||
|
||||
// Close implements the [net.Listener] interface for *Listener.
|
||||
func (l *Listener) Close() (err error) {
|
||||
return l.OnClose()
|
||||
}
|
||||
|
||||
// Module adguard-home
|
||||
|
||||
// Package aghos
|
||||
|
||||
// type check
|
||||
var _ aghos.FSWatcher = (*FSWatcher)(nil)
|
||||
|
||||
// FSWatcher is a mock [aghos.FSWatcher] implementation for tests.
|
||||
// FSWatcher is a fake [aghos.FSWatcher] implementation for tests.
|
||||
type FSWatcher struct {
|
||||
OnEvents func() (e <-chan struct{})
|
||||
OnAdd func(name string) (err error)
|
||||
OnClose func() (err error)
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ aghos.FSWatcher = (*FSWatcher)(nil)
|
||||
|
||||
// Events implements the [aghos.FSWatcher] interface for *FSWatcher.
|
||||
func (w *FSWatcher) Events() (e <-chan struct{}) {
|
||||
return w.OnEvents()
|
||||
@@ -120,16 +50,16 @@ func (w *FSWatcher) Close() (err error) {
|
||||
|
||||
// Package agh
|
||||
|
||||
// type check
|
||||
var _ agh.ServiceWithConfig[struct{}] = (*ServiceWithConfig[struct{}])(nil)
|
||||
|
||||
// ServiceWithConfig is a mock [agh.ServiceWithConfig] implementation for tests.
|
||||
// ServiceWithConfig is a fake [agh.ServiceWithConfig] implementation for tests.
|
||||
type ServiceWithConfig[ConfigType any] struct {
|
||||
OnStart func() (err error)
|
||||
OnShutdown func(ctx context.Context) (err error)
|
||||
OnConfig func() (c ConfigType)
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ agh.ServiceWithConfig[struct{}] = (*ServiceWithConfig[struct{}])(nil)
|
||||
|
||||
// Start implements the [agh.ServiceWithConfig] interface for
|
||||
// *ServiceWithConfig.
|
||||
func (s *ServiceWithConfig[_]) Start() (err error) {
|
||||
@@ -148,14 +78,76 @@ func (s *ServiceWithConfig[ConfigType]) Config() (c ConfigType) {
|
||||
return s.OnConfig()
|
||||
}
|
||||
|
||||
// Package client
|
||||
|
||||
// AddressProcessor is a fake [client.AddressProcessor] implementation for
|
||||
// tests.
|
||||
type AddressProcessor struct {
|
||||
OnProcess func(ip netip.Addr)
|
||||
OnClose func() (err error)
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ client.AddressProcessor = (*AddressProcessor)(nil)
|
||||
|
||||
// Process implements the [client.AddressProcessor] interface for
|
||||
// *AddressProcessor.
|
||||
func (p *AddressProcessor) Process(ip netip.Addr) {
|
||||
p.OnProcess(ip)
|
||||
}
|
||||
|
||||
// Close implements the [client.AddressProcessor] interface for
|
||||
// *AddressProcessor.
|
||||
func (p *AddressProcessor) Close() (err error) {
|
||||
return p.OnClose()
|
||||
}
|
||||
|
||||
// AddressUpdater is a fake [client.AddressUpdater] implementation for tests.
|
||||
type AddressUpdater struct {
|
||||
OnUpdateAddress func(ip netip.Addr, host string, info *whois.Info)
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ client.AddressUpdater = (*AddressUpdater)(nil)
|
||||
|
||||
// UpdateAddress implements the [client.AddressUpdater] interface for
|
||||
// *AddressUpdater.
|
||||
func (p *AddressUpdater) UpdateAddress(ip netip.Addr, host string, info *whois.Info) {
|
||||
p.OnUpdateAddress(ip, host, info)
|
||||
}
|
||||
|
||||
// Package filtering
|
||||
|
||||
// Resolver is a fake [filtering.Resolver] implementation for tests.
|
||||
type Resolver struct {
|
||||
OnLookupIP func(ctx context.Context, network, host string) (ips []net.IP, err error)
|
||||
}
|
||||
|
||||
// LookupIP implements the [filtering.Resolver] interface for *Resolver.
|
||||
func (r *Resolver) LookupIP(ctx context.Context, network, host string) (ips []net.IP, err error) {
|
||||
return r.OnLookupIP(ctx, network, host)
|
||||
}
|
||||
|
||||
// Package rdns
|
||||
|
||||
// Exchanger is a fake [rdns.Exchanger] implementation for tests.
|
||||
type Exchanger struct {
|
||||
OnExchange func(ip netip.Addr) (host string, ttl time.Duration, err error)
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ rdns.Exchanger = (*Exchanger)(nil)
|
||||
|
||||
// Exchange implements [rdns.Exchanger] interface for *Exchanger.
|
||||
func (e *Exchanger) Exchange(ip netip.Addr) (host string, ttl time.Duration, err error) {
|
||||
return e.OnExchange(ip)
|
||||
}
|
||||
|
||||
// Module dnsproxy
|
||||
|
||||
// Package upstream
|
||||
|
||||
// type check
|
||||
var _ upstream.Upstream = (*UpstreamMock)(nil)
|
||||
|
||||
// UpstreamMock is a mock [upstream.Upstream] implementation for tests.
|
||||
// UpstreamMock is a fake [upstream.Upstream] implementation for tests.
|
||||
//
|
||||
// TODO(a.garipov): Replace with all uses of Upstream with UpstreamMock and
|
||||
// rename it to just Upstream.
|
||||
@@ -165,6 +157,9 @@ type UpstreamMock struct {
|
||||
OnClose func() (err error)
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ upstream.Upstream = (*UpstreamMock)(nil)
|
||||
|
||||
// Address implements the [upstream.Upstream] interface for *UpstreamMock.
|
||||
func (u *UpstreamMock) Address() (addr string) {
|
||||
return u.OnAddress()
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user