Compare commits
59 Commits
v0.104.0-b
...
v0.104.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b1c71a1284 | ||
|
|
4690229d81 | ||
|
|
de257b73aa | ||
|
|
40614d9a7b | ||
|
|
f2eda6d192 | ||
|
|
8a9c6e8a02 | ||
|
|
bf4c256c72 | ||
|
|
6358240e9b | ||
|
|
a19523b258 | ||
|
|
394fc5a9d8 | ||
|
|
09196118e9 | ||
|
|
e802e6645e | ||
|
|
13f708b483 | ||
|
|
979874c92b | ||
|
|
3cc5bf210d | ||
|
|
1e583315a8 | ||
|
|
ee8a033472 | ||
|
|
b85c0b3bf7 | ||
|
|
859ed209a0 | ||
|
|
c6a6f05b1b | ||
|
|
5bf330bf14 | ||
|
|
eefa553100 | ||
|
|
298f74ba81 | ||
|
|
3e1f922252 | ||
|
|
7e16fda57b | ||
|
|
11e794f554 | ||
|
|
2baa33fb1f | ||
|
|
02d16a0b40 | ||
|
|
84ca2f58a8 | ||
|
|
bc20917840 | ||
|
|
62cc334f46 | ||
|
|
d398e4b01c | ||
|
|
3d19f91bed | ||
|
|
df34ee5c09 | ||
|
|
64c1a68fb9 | ||
|
|
d747185b45 | ||
|
|
8afdc049ef | ||
|
|
812b43a4b3 | ||
|
|
ae8de95d89 | ||
|
|
df3fa595a2 | ||
|
|
98f6843aab | ||
|
|
9761f13399 | ||
|
|
a376bf915a | ||
|
|
da5c1ebbbf | ||
|
|
40ebccaab4 | ||
|
|
d1e55c31af | ||
|
|
6b64bfac00 | ||
|
|
e37ccdbb70 | ||
|
|
0b43bf4cd9 | ||
|
|
fe87bdc9cd | ||
|
|
f75a0f8719 | ||
|
|
2ba85ba8e1 | ||
|
|
052a9ae196 | ||
|
|
32b24ce093 | ||
|
|
3acfaa162f | ||
|
|
345a97c4af | ||
|
|
a7cf717116 | ||
|
|
8315b46f30 | ||
|
|
e0ddb1c0ea |
14
.github/workflows/build.yml
vendored
14
.github/workflows/build.yml
vendored
@@ -134,23 +134,21 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
needs: test
|
||||
steps:
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: crazy-max/ghaction-docker-buildx@v1
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
-
|
||||
name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
-
|
||||
name: Docker Buildx (build)
|
||||
run: |
|
||||
make docker-multi-arch
|
||||
-
|
||||
name: Clear
|
||||
if: always() && startsWith(github.ref, 'refs/tags/v')
|
||||
run: |
|
||||
rm -f ${HOME}/.docker/config.json
|
||||
|
||||
notify:
|
||||
needs: [app, docker]
|
||||
|
||||
4
.github/workflows/lint.yml
vendored
4
.github/workflows/lint.yml
vendored
@@ -12,10 +12,10 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v1
|
||||
uses: golangci/golangci-lint-action@v2.3.0
|
||||
with:
|
||||
# Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.
|
||||
version: v1.27
|
||||
version: v1.32
|
||||
|
||||
eslint:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -90,7 +90,13 @@ snapcrafts:
|
||||
apps:
|
||||
adguard-home:
|
||||
command: AdGuardHome -w $SNAP_DATA --no-check-update
|
||||
plugs: [ network-bind ]
|
||||
plugs:
|
||||
# Add the "netrwork-bind" plug to bind to interfaces.
|
||||
- network-bind
|
||||
# Add the "netrwork-control" plug to be able to bind to ports below
|
||||
# 1024 (cap_net_bind_service) and also to bind to a particular
|
||||
# interface using SO_BINDTODEVICE (cap_net_raw).
|
||||
- network-control
|
||||
daemon: simple
|
||||
adguard-home-web:
|
||||
command: adguard-home-web.sh
|
||||
|
||||
@@ -1587,7 +1587,7 @@ Response:
|
||||
"upstream":"...", // Upstream URL starting with tcp://, tls://, https://, or with an IP address
|
||||
"answer_dnssec": true,
|
||||
"client":"127.0.0.1",
|
||||
"client_proto": "" (plain) | "doh" | "dot",
|
||||
"client_proto": "" (plain) | "doh" | "dot" | "doq",
|
||||
"elapsedMs":"0.098403",
|
||||
"filterId":1,
|
||||
"question":{
|
||||
|
||||
32
CHANGELOG.md
Normal file
32
CHANGELOG.md
Normal file
@@ -0,0 +1,32 @@
|
||||
# AdGuard Home Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on
|
||||
[*Keep a Changelog*](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to
|
||||
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [v0.104.2] - 2020-11-19
|
||||
|
||||
### Added
|
||||
|
||||
- This changelog :-) (#2294).
|
||||
- `HACKING.md`, a guide for developers.
|
||||
|
||||
### Changed
|
||||
|
||||
- Improved tests output (#2273).
|
||||
|
||||
### Fixed
|
||||
|
||||
- Query logs from file not loading after the ones buffered in memory (#2325).
|
||||
- Unnecessary errors in query logs when switching between log files (#2324).
|
||||
- `404 Not Found` errors on the DHCP settings page on *Windows*. The page now
|
||||
correctly shows that DHCP is not currently available on that OS (#2295).
|
||||
- Infinite loop in `/dhcp/find_active_dhcp` (#2301).
|
||||
|
||||
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.104.2...HEAD
|
||||
[v0.104.2]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.104.1...v0.104.2
|
||||
@@ -59,7 +59,6 @@ RUN apk --update --no-cache add \
|
||||
ca-certificates \
|
||||
libcap \
|
||||
libressl \
|
||||
tzdata \
|
||||
&& rm -rf /tmp/* /var/cache/apk/*
|
||||
|
||||
COPY --from=builder --chown=nobody:nogroup /app/AdGuardHome /opt/adguardhome/AdGuardHome
|
||||
|
||||
153
HACKING.md
Normal file
153
HACKING.md
Normal file
@@ -0,0 +1,153 @@
|
||||
# AdGuardHome Developer Guidelines
|
||||
|
||||
As of **2020-11-12**, this document is still a work-in-progress. Some of the
|
||||
rules aren't enforced, and others might change. Still, this is a good place to
|
||||
find out about how we **want** our code to look like.
|
||||
|
||||
## Git
|
||||
|
||||
* Follow the commit message header format:
|
||||
|
||||
```none
|
||||
pkg: fix the network error logging issue
|
||||
```
|
||||
|
||||
Where `pkg` is the package where most changes took place. If there are
|
||||
several such packages, just write `all`.
|
||||
|
||||
* Keep your commit messages to be no wider than eighty (**80**) columns.
|
||||
|
||||
* Only use lowercase letters in your commit message headers.
|
||||
|
||||
## Go
|
||||
|
||||
* <https://github.com/golang/go/wiki/CodeReviewComments>.
|
||||
|
||||
* <https://github.com/golang/go/wiki/TestComments>.
|
||||
|
||||
* <https://go-proverbs.github.io/>
|
||||
|
||||
* Avoid `init` and use explicit initialization functions instead.
|
||||
|
||||
* Avoid `new`, especially with structs.
|
||||
|
||||
* Document everything, including unexported top-level identifiers, to build
|
||||
a habit of writing documentation.
|
||||
|
||||
* Don't put variable names into any kind of quotes.
|
||||
|
||||
* Don't use naked `return`s.
|
||||
|
||||
* Don't use underscores in file and package names, unless they're build tags
|
||||
or for tests. This is to prevent accidental build errors with weird tags.
|
||||
|
||||
* Don't write code with more than four (**4**) levels of indentation. Just
|
||||
like [Linus said], plus an additional level for an occasional error check or
|
||||
struct initialization.
|
||||
|
||||
* Eschew external dependencies, including transitive, unless
|
||||
absolutely necessary.
|
||||
|
||||
* No `goto`.
|
||||
|
||||
* No shadowing, since it can often lead to subtle bugs, especially with
|
||||
errors.
|
||||
|
||||
* Prefer constants to variables where possible. Reduce global variables. Use
|
||||
[constant errors] instead of `errors.New`.
|
||||
|
||||
* Put comments above the documented entity, **not** to the side, to improve
|
||||
readability.
|
||||
|
||||
* Use `gofumpt --extra -s`.
|
||||
|
||||
**TODO(a.garipov):** Add to the linters.
|
||||
|
||||
* Use linters.
|
||||
|
||||
* Use named returns to improve readability of function signatures.
|
||||
|
||||
* When a method implements an interface, start the doc comment with the
|
||||
standard template:
|
||||
|
||||
```go
|
||||
// Foo implements the Fooer interface for *foo.
|
||||
func (f *foo) Foo() {
|
||||
// …
|
||||
}
|
||||
```
|
||||
|
||||
* Write logs and error messages in lowercase only to make it easier to `grep`
|
||||
logs and error messages without using the `-i` flag.
|
||||
|
||||
* Write slices of struct like this:
|
||||
|
||||
```go
|
||||
ts := []T{{
|
||||
Field: Value0,
|
||||
// …
|
||||
}, {
|
||||
Field: Value1,
|
||||
// …
|
||||
}, {
|
||||
Field: Value2,
|
||||
// …
|
||||
}}
|
||||
```
|
||||
|
||||
[constant errors]: https://dave.cheney.net/2016/04/07/constant-errors
|
||||
[Linus said]: https://www.kernel.org/doc/html/v4.17/process/coding-style.html#indentation
|
||||
|
||||
## Text, Including Comments
|
||||
|
||||
* Text should wrap at eighty (**80**) columns to be more readable, to use
|
||||
a common standard, and to allow editing or diffing side-by-side without
|
||||
wrapping.
|
||||
|
||||
The only exception are long hyperlinks.
|
||||
|
||||
* Use U.S. English, as it is the most widely used variety of English in the
|
||||
code right now as well as generally.
|
||||
|
||||
* Use double spacing between sentences to make sentence borders more clear.
|
||||
|
||||
* Use the serial comma (a.k.a. Oxford comma) to improve comprehension,
|
||||
decrease ambiguity, and use a common standard.
|
||||
|
||||
* Write todos like this:
|
||||
|
||||
```go
|
||||
// TODO(usr1): Fix the frobulation issue.
|
||||
```
|
||||
|
||||
Or, if several people need to look at the code:
|
||||
|
||||
```go
|
||||
// TODO(usr1, usr2): Fix the frobulation issue.
|
||||
```
|
||||
|
||||
## Markdown
|
||||
|
||||
* **TODO(a.garipov):** Define our Markdown conventions.
|
||||
|
||||
## YAML
|
||||
|
||||
* **TODO(a.garipov):** Find a YAML formatter or write our own.
|
||||
|
||||
* All strings, including keys, must be quoted. Reason: the [NO-rway Law].
|
||||
|
||||
* Indent with two (**2**) spaces.
|
||||
|
||||
* No extra indentation in multiline arrays:
|
||||
|
||||
```yaml
|
||||
'values':
|
||||
- 'value-1'
|
||||
- 'value-2'
|
||||
- 'value-3'
|
||||
```
|
||||
|
||||
* Prefer single quotes for string to prevent accidental escaping, unless
|
||||
escaping is required.
|
||||
|
||||
[NO-rway Law]: https://news.ycombinator.com/item?id=17359376
|
||||
12
Makefile
12
Makefile
@@ -109,7 +109,7 @@ $(error DOCKER_IMAGE_NAME value is not set)
|
||||
endif
|
||||
|
||||
# OS-specific flags
|
||||
TEST_FLAGS := -race
|
||||
TEST_FLAGS := --race -v
|
||||
ifeq ($(OS),Windows_NT)
|
||||
TEST_FLAGS :=
|
||||
endif
|
||||
@@ -160,11 +160,13 @@ lint-go:
|
||||
@echo Running go linter
|
||||
golangci-lint run
|
||||
|
||||
test:
|
||||
@echo Running JS unit-tests
|
||||
test: test-js test-go
|
||||
|
||||
test-js:
|
||||
npm run test --prefix client
|
||||
@echo Running Go unit-tests
|
||||
go test $(TEST_FLAGS) -v -coverprofile=coverage.txt -covermode=atomic ./...
|
||||
|
||||
test-go:
|
||||
go test $(TEST_FLAGS) --coverprofile coverage.txt ./...
|
||||
|
||||
ci: client_with_deps
|
||||
go mod download
|
||||
|
||||
@@ -60,6 +60,7 @@ It operates as a DNS server that re-routes tracking domains to a "black hole," t
|
||||
* [Other](#help-other)
|
||||
* [Projects that use AdGuardHome](#uses)
|
||||
* [Acknowledgments](#acknowledgments)
|
||||
* [Privacy](#privacy)
|
||||
|
||||
<a id="getting-started"></a>
|
||||
## Getting Started
|
||||
@@ -292,7 +293,7 @@ Here is a link to AdGuard Home project: https://crowdin.com/project/adguard-appl
|
||||
Here's what you can also do to contribute:
|
||||
|
||||
1. [Look for issues](https://github.com/AdguardTeam/AdGuardHome/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22+) marked as "help wanted".
|
||||
2. Actualize the list of *Blocked services*. It it can be found in [dnsfilter/blocked_services.go](https://github.com/AdguardTeam/AdGuardHome/blob/master/dnsfilter/blocked_services.go).
|
||||
2. Actualize the list of *Blocked services*. It it can be found in [dnsfilter/blocked_services.go](https://github.com/AdguardTeam/AdGuardHome/blob/master/internal/dnsfilter/blocked_services.go).
|
||||
3. Actualize the list of known *trackers*. It it can be found in [client/src/helpers/trackers/adguard.json](https://github.com/AdguardTeam/AdGuardHome/blob/master/client/src/helpers/trackers/adguard.json).
|
||||
4. Actualize the list of vetted *blocklists*. It it can be found in [client/src/helpers/filters/filters.json](https://github.com/AdguardTeam/AdGuardHome/blob/master/client/src/helpers/filters/filters.json).
|
||||
|
||||
@@ -326,3 +327,8 @@ This software wouldn't have been possible without:
|
||||
You might have seen that [CoreDNS](https://coredns.io) was mentioned here before — we've stopped using it in AdGuardHome. While we still use it on our servers for [AdGuard DNS](https://adguard.com/adguard-dns/overview.html) service, it seemed like an overkill for Home as it impeded with Home features that we plan to implement.
|
||||
|
||||
For a full list of all node.js packages in use, please take a look at [client/package.json](https://github.com/AdguardTeam/AdGuardHome/blob/master/client/package.json) file.
|
||||
|
||||
<a id="privacy"></a>
|
||||
## Privacy
|
||||
|
||||
Our main idea is that you are the one, who should be in control of your data. So it is only natural, that AdGuard Home does not collect any usage statistics, and does not use any web services unless you configure it to do so. Full policy with every bit that _could in theory be_ sent by AdGuard Home is available [here](https://adguard.com/en/privacy/home.html).
|
||||
2
client/constants.js
vendored
2
client/constants.js
vendored
@@ -3,7 +3,7 @@ const BUILD_ENVS = {
|
||||
prod: 'production',
|
||||
};
|
||||
|
||||
const BASE_URL = '/control';
|
||||
const BASE_URL = 'control';
|
||||
|
||||
module.exports = {
|
||||
BUILD_ENVS,
|
||||
|
||||
337
client/package-lock.json
generated
vendored
337
client/package-lock.json
generated
vendored
@@ -2014,72 +2014,168 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"@nivo/axes": {
|
||||
"version": "0.49.1",
|
||||
"resolved": "https://registry.npmjs.org/@nivo/axes/-/axes-0.49.1.tgz",
|
||||
"integrity": "sha512-2ZqpKtnZ9HE30H+r565VCrypKRQzAoMbAg1hsht88dlNQRtghBSxbAS0Y4IUW/wgN/AzvOIBJHvxH7bgaB8Oow==",
|
||||
"@nivo/annotations": {
|
||||
"version": "0.64.0",
|
||||
"resolved": "https://registry.npmjs.org/@nivo/annotations/-/annotations-0.64.0.tgz",
|
||||
"integrity": "sha512-b9VAVuAn2HztOZckU2GcBwptjCobYV5VgX/jwZTSFeZFLtjZza+QinNL2AbQ2cnmeU3nVCw1dTbJMMZ9fTCCNQ==",
|
||||
"requires": {
|
||||
"@nivo/core": "0.49.0",
|
||||
"d3-format": "^1.3.2",
|
||||
"@nivo/colors": "0.64.0",
|
||||
"lodash": "^4.17.11",
|
||||
"react-spring": "^8.0.27"
|
||||
}
|
||||
},
|
||||
"@nivo/axes": {
|
||||
"version": "0.64.0",
|
||||
"resolved": "https://registry.npmjs.org/@nivo/axes/-/axes-0.64.0.tgz",
|
||||
"integrity": "sha512-Pm+Y3C67OuBb3JqHpyFKWAoPAnNojb1s5/LFQYVYN1QpKyjeqilGAoLCjHK6ckgfzreiGf7NG+oBgpH8Ncz2fQ==",
|
||||
"requires": {
|
||||
"@nivo/scales": "0.64.0",
|
||||
"d3-format": "^1.4.4",
|
||||
"d3-time": "^1.0.11",
|
||||
"d3-time-format": "^2.1.3",
|
||||
"lodash": "^4.17.4",
|
||||
"react-motion": "^0.5.2",
|
||||
"recompose": "^0.26.0"
|
||||
"react-spring": "^8.0.27"
|
||||
},
|
||||
"dependencies": {
|
||||
"d3-format": {
|
||||
"version": "1.4.5",
|
||||
"resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.4.5.tgz",
|
||||
"integrity": "sha512-J0piedu6Z8iB6TbIGfZgDzfXxUFN3qQRMofy2oPdXzQibYGqPB/9iMcxr/TGalU+2RsyDO+U4f33id8tbnSRMQ=="
|
||||
},
|
||||
"d3-time": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-time/-/d3-time-1.1.0.tgz",
|
||||
"integrity": "sha512-Xh0isrZ5rPYYdqhAVk8VLnMEidhz5aP7htAADH6MfzgmmicPkTo8LhkLxci61/lCB7n7UmE3bN0leRt+qvkLxA=="
|
||||
},
|
||||
"d3-time-format": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.3.0.tgz",
|
||||
"integrity": "sha512-guv6b2H37s2Uq/GefleCDtbe0XZAuy7Wa49VGkPVPMfLL9qObgBST3lEHJBMUp8S7NdLQAGIvr2KXk8Hc98iKQ==",
|
||||
"requires": {
|
||||
"d3-time": "1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@nivo/colors": {
|
||||
"version": "0.64.0",
|
||||
"resolved": "https://registry.npmjs.org/@nivo/colors/-/colors-0.64.0.tgz",
|
||||
"integrity": "sha512-3CKIkSjKgwSgBsiJoTlZpFUUpaGTl60pF6rFsIiFT30os9jMxP/J4ikQGQ/vMLPTXskZYoxByaMHGKJy5wypqg==",
|
||||
"requires": {
|
||||
"d3-color": "^1.2.3",
|
||||
"d3-scale": "^3.0.0",
|
||||
"d3-scale-chromatic": "^1.3.3",
|
||||
"lodash.get": "^4.4.2",
|
||||
"lodash.isplainobject": "^4.0.6",
|
||||
"react-motion": "^0.5.2"
|
||||
}
|
||||
},
|
||||
"@nivo/core": {
|
||||
"version": "0.49.0",
|
||||
"resolved": "https://registry.npmjs.org/@nivo/core/-/core-0.49.0.tgz",
|
||||
"integrity": "sha512-TCPMUO2aJ7fI+ZB6t3d3EBQtNxJnTzaxLJsrVyn/3AQIjUwccAeo2aIy81wLBGWGtlGNUDNdAbnFzXiJosH0yg==",
|
||||
"version": "0.64.0",
|
||||
"resolved": "https://registry.npmjs.org/@nivo/core/-/core-0.64.0.tgz",
|
||||
"integrity": "sha512-tupETbvxgv4B9y3pcXy/lErMwY2aZht+FKSyah1dPFd88LnMD/DOL+to6ociBHmpLQNUMA7wid6R7BlXRY/bmg==",
|
||||
"requires": {
|
||||
"d3-color": "^1.0.3",
|
||||
"d3-format": "^1.3.2",
|
||||
"d3-color": "^1.2.3",
|
||||
"d3-format": "^1.4.4",
|
||||
"d3-hierarchy": "^1.1.8",
|
||||
"d3-interpolate": "^1.3.2",
|
||||
"d3-scale": "^2.1.2",
|
||||
"d3-interpolate": "^2.0.1",
|
||||
"d3-scale": "^3.0.0",
|
||||
"d3-scale-chromatic": "^1.3.3",
|
||||
"d3-shape": "^1.2.2",
|
||||
"d3-shape": "^1.3.5",
|
||||
"d3-time-format": "^2.1.3",
|
||||
"lodash": "^4.17.4",
|
||||
"react-measure": "^2.0.2",
|
||||
"react-motion": "^0.5.2",
|
||||
"recompose": "^0.26.0"
|
||||
"lodash": "^4.17.11",
|
||||
"react-spring": "^8.0.27",
|
||||
"recompose": "^0.30.0",
|
||||
"resize-observer-polyfill": "^1.5.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"d3-format": {
|
||||
"version": "1.4.5",
|
||||
"resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.4.5.tgz",
|
||||
"integrity": "sha512-J0piedu6Z8iB6TbIGfZgDzfXxUFN3qQRMofy2oPdXzQibYGqPB/9iMcxr/TGalU+2RsyDO+U4f33id8tbnSRMQ=="
|
||||
},
|
||||
"d3-time": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-time/-/d3-time-1.1.0.tgz",
|
||||
"integrity": "sha512-Xh0isrZ5rPYYdqhAVk8VLnMEidhz5aP7htAADH6MfzgmmicPkTo8LhkLxci61/lCB7n7UmE3bN0leRt+qvkLxA=="
|
||||
},
|
||||
"d3-time-format": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.3.0.tgz",
|
||||
"integrity": "sha512-guv6b2H37s2Uq/GefleCDtbe0XZAuy7Wa49VGkPVPMfLL9qObgBST3lEHJBMUp8S7NdLQAGIvr2KXk8Hc98iKQ==",
|
||||
"requires": {
|
||||
"d3-time": "1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@nivo/legends": {
|
||||
"version": "0.49.0",
|
||||
"resolved": "https://registry.npmjs.org/@nivo/legends/-/legends-0.49.0.tgz",
|
||||
"integrity": "sha512-8KbUFYozqwD+/rj4in0mnF9b9CuyNFjVgXqm2KW3ODVlWIgYgjTVlEhlg9VZIArFPlIyyAjEYC88YSRcALHugg==",
|
||||
"version": "0.64.0",
|
||||
"resolved": "https://registry.npmjs.org/@nivo/legends/-/legends-0.64.0.tgz",
|
||||
"integrity": "sha512-L7Mp/of/jY4qE7ef6PXJ8/e3aASBTfsf5BTOh3imSXZT6I4hXa5ppmGAgZ0gOSpcPXuMEjBc0aSIEJoeoytQ/g==",
|
||||
"requires": {
|
||||
"lodash": "^4.17.4",
|
||||
"recompose": "^0.26.0"
|
||||
"@nivo/core": "0.64.0",
|
||||
"lodash": "^4.17.11",
|
||||
"recompose": "^0.30.0"
|
||||
}
|
||||
},
|
||||
"@nivo/line": {
|
||||
"version": "0.49.1",
|
||||
"resolved": "https://registry.npmjs.org/@nivo/line/-/line-0.49.1.tgz",
|
||||
"integrity": "sha512-wKkOmpnwK2psmZbJReDq+Eh/WV9r1JA8V4Vl4eIRuf971CW0KUT9nCAoc/FcKio0qsiq5wyFt3J5LuAhfzlV/w==",
|
||||
"version": "0.64.0",
|
||||
"resolved": "https://registry.npmjs.org/@nivo/line/-/line-0.64.0.tgz",
|
||||
"integrity": "sha512-WkQU28ZL9Mxq42AdmybWe+2qFh/TiUXu+7e6nj41e/8DO95Guxg1XQ+i5zQKuw/UZlqXZs6WOsMW8EMNE4GzXw==",
|
||||
"requires": {
|
||||
"@nivo/axes": "0.49.1",
|
||||
"@nivo/core": "0.49.0",
|
||||
"@nivo/legends": "0.49.0",
|
||||
"@nivo/scales": "0.49.0",
|
||||
"d3-format": "^1.3.2",
|
||||
"d3-scale": "^2.1.2",
|
||||
"d3-shape": "^1.2.2",
|
||||
"lodash": "^4.17.4",
|
||||
"react-motion": "^0.5.2",
|
||||
"recompose": "^0.26.0"
|
||||
"@nivo/annotations": "0.64.0",
|
||||
"@nivo/axes": "0.64.0",
|
||||
"@nivo/colors": "0.64.0",
|
||||
"@nivo/legends": "0.64.0",
|
||||
"@nivo/scales": "0.64.0",
|
||||
"@nivo/tooltip": "0.64.0",
|
||||
"@nivo/voronoi": "0.64.0",
|
||||
"d3-shape": "^1.3.5",
|
||||
"react-spring": "^8.0.27"
|
||||
}
|
||||
},
|
||||
"@nivo/scales": {
|
||||
"version": "0.49.0",
|
||||
"resolved": "https://registry.npmjs.org/@nivo/scales/-/scales-0.49.0.tgz",
|
||||
"integrity": "sha512-+5Leu4zX6mDSAunf4ZJHeqVlT+ZsqiKXLB6hT/u7r3GjxZP9A+n3rHePhIzikBiBRMlLjyiBlylLzhKBAYbGWQ==",
|
||||
"version": "0.64.0",
|
||||
"resolved": "https://registry.npmjs.org/@nivo/scales/-/scales-0.64.0.tgz",
|
||||
"integrity": "sha512-Jbr1rlfe/gLCippndPaCM7doJzzx1K9oXPxS4JiyvrIUkQoMWiozymZQdEp8kgigI6uwWu5xvPwCOFXalCIhKg==",
|
||||
"requires": {
|
||||
"d3-scale": "^2.1.2",
|
||||
"d3-scale": "^3.0.0",
|
||||
"d3-time-format": "^2.1.3",
|
||||
"lodash": "^4.17.4"
|
||||
"lodash": "^4.17.11"
|
||||
},
|
||||
"dependencies": {
|
||||
"d3-time": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-time/-/d3-time-1.1.0.tgz",
|
||||
"integrity": "sha512-Xh0isrZ5rPYYdqhAVk8VLnMEidhz5aP7htAADH6MfzgmmicPkTo8LhkLxci61/lCB7n7UmE3bN0leRt+qvkLxA=="
|
||||
},
|
||||
"d3-time-format": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.3.0.tgz",
|
||||
"integrity": "sha512-guv6b2H37s2Uq/GefleCDtbe0XZAuy7Wa49VGkPVPMfLL9qObgBST3lEHJBMUp8S7NdLQAGIvr2KXk8Hc98iKQ==",
|
||||
"requires": {
|
||||
"d3-time": "1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@nivo/tooltip": {
|
||||
"version": "0.64.0",
|
||||
"resolved": "https://registry.npmjs.org/@nivo/tooltip/-/tooltip-0.64.0.tgz",
|
||||
"integrity": "sha512-iGsuCi42uw/8F7OVvPyWdQgxJXVOPTEdtl2WK2FlSJIH7bfnEsZ+R/lTdElY2JAvGHuNW6hQwpNUZdC/2rOatg==",
|
||||
"requires": {
|
||||
"react-spring": "^8.0.27"
|
||||
}
|
||||
},
|
||||
"@nivo/voronoi": {
|
||||
"version": "0.64.0",
|
||||
"resolved": "https://registry.npmjs.org/@nivo/voronoi/-/voronoi-0.64.0.tgz",
|
||||
"integrity": "sha512-YdNRzD2rFc1NcAZe9D8gxos+IT2CRPOV/7fUfBCG9SoNw1TtSwSKtEs4xsxmUFmLT1FadWcyKeKuhgJUQnof/A==",
|
||||
"requires": {
|
||||
"@nivo/core": "0.64.0",
|
||||
"d3-delaunay": "^5.1.1",
|
||||
"d3-scale": "^3.0.0",
|
||||
"recompose": "^0.30.0"
|
||||
}
|
||||
},
|
||||
"@nodelib/fs.scandir": {
|
||||
@@ -4681,24 +4777,27 @@
|
||||
"dev": true
|
||||
},
|
||||
"d3-array": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz",
|
||||
"integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw=="
|
||||
},
|
||||
"d3-collection": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/d3-collection/-/d3-collection-1.0.7.tgz",
|
||||
"integrity": "sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A=="
|
||||
"version": "2.8.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.8.0.tgz",
|
||||
"integrity": "sha512-6V272gsOeg7+9pTW1jSYOR1QE37g95I3my1hBmY+vOUNHRrk9yt4OTz/gK7PMkVAVDrYYq4mq3grTiZ8iJdNIw=="
|
||||
},
|
||||
"d3-color": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.4.1.tgz",
|
||||
"integrity": "sha512-p2sTHSLCJI2QKunbGb7ocOh7DgTAn8IrLx21QRc/BSnodXM4sv6aLQlnfpvehFMLZEfBc6g9pH9SWQccFYfJ9Q=="
|
||||
},
|
||||
"d3-delaunay": {
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-5.3.0.tgz",
|
||||
"integrity": "sha512-amALSrOllWVLaHTnDLHwMIiz0d1bBu9gZXd1FiLfXf8sHcX9jrcj81TVZOqD4UX7MgBZZ07c8GxzEgBpJqc74w==",
|
||||
"requires": {
|
||||
"delaunator": "4"
|
||||
}
|
||||
},
|
||||
"d3-format": {
|
||||
"version": "1.4.4",
|
||||
"resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.4.4.tgz",
|
||||
"integrity": "sha512-TWks25e7t8/cqctxCmxpUuzZN11QxIA7YrMbram94zMQ0PXjE4LVIMe/f6a4+xxL8HQ3OsAFULOINQi1pE62Aw=="
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-format/-/d3-format-2.0.0.tgz",
|
||||
"integrity": "sha512-Ab3S6XuE/Q+flY96HXT0jOXcM4EAClYFnRGY5zsjRGNy6qCYrQsMffs7cV5Q9xejb35zxW5hf/guKw34kvIKsA=="
|
||||
},
|
||||
"d3-hierarchy": {
|
||||
"version": "1.1.9",
|
||||
@@ -4706,11 +4805,11 @@
|
||||
"integrity": "sha512-j8tPxlqh1srJHAtxfvOUwKNYJkQuBFdM1+JAUfq6xqH5eAqf93L7oG1NVqDa4CpFZNvnNKtCYEUC8KY9yEn9lQ=="
|
||||
},
|
||||
"d3-interpolate": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.4.0.tgz",
|
||||
"integrity": "sha512-V9znK0zc3jOPV4VD2zZn0sDhZU3WAE2bmlxdIwwQPPzPjvyLkd8B3JUVdS1IDUFDkWZ72c9qnv1GK2ZagTZ8EA==",
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-2.0.1.tgz",
|
||||
"integrity": "sha512-c5UhwwTs/yybcmTpAVqwSFl6vrQ8JZJoT5F7xNFK9pymv5C0Ymcc9/LIJHtYIggg/yS9YHw8i8O8tgb9pupjeQ==",
|
||||
"requires": {
|
||||
"d3-color": "1"
|
||||
"d3-color": "1 - 2"
|
||||
}
|
||||
},
|
||||
"d3-path": {
|
||||
@@ -4719,16 +4818,15 @@
|
||||
"integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg=="
|
||||
},
|
||||
"d3-scale": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-2.2.2.tgz",
|
||||
"integrity": "sha512-LbeEvGgIb8UMcAa0EATLNX0lelKWGYDQiPdHj+gLblGVhGLyNbaCn3EvrJf0A3Y/uOOU5aD6MTh5ZFCdEwGiCw==",
|
||||
"version": "3.2.3",
|
||||
"resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-3.2.3.tgz",
|
||||
"integrity": "sha512-8E37oWEmEzj57bHcnjPVOBS3n4jqakOeuv1EDdQSiSrYnMCBdMd3nc4HtKk7uia8DUHcY/CGuJ42xxgtEYrX0g==",
|
||||
"requires": {
|
||||
"d3-array": "^1.2.0",
|
||||
"d3-collection": "1",
|
||||
"d3-format": "1",
|
||||
"d3-interpolate": "1",
|
||||
"d3-time": "1",
|
||||
"d3-time-format": "2"
|
||||
"d3-array": "^2.3.0",
|
||||
"d3-format": "1 - 2",
|
||||
"d3-interpolate": "1.2.0 - 2",
|
||||
"d3-time": "1 - 2",
|
||||
"d3-time-format": "2 - 3"
|
||||
}
|
||||
},
|
||||
"d3-scale-chromatic": {
|
||||
@@ -4738,6 +4836,16 @@
|
||||
"requires": {
|
||||
"d3-color": "1",
|
||||
"d3-interpolate": "1"
|
||||
},
|
||||
"dependencies": {
|
||||
"d3-interpolate": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.4.0.tgz",
|
||||
"integrity": "sha512-V9znK0zc3jOPV4VD2zZn0sDhZU3WAE2bmlxdIwwQPPzPjvyLkd8B3JUVdS1IDUFDkWZ72c9qnv1GK2ZagTZ8EA==",
|
||||
"requires": {
|
||||
"d3-color": "1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"d3-shape": {
|
||||
@@ -4749,16 +4857,16 @@
|
||||
}
|
||||
},
|
||||
"d3-time": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-time/-/d3-time-1.1.0.tgz",
|
||||
"integrity": "sha512-Xh0isrZ5rPYYdqhAVk8VLnMEidhz5aP7htAADH6MfzgmmicPkTo8LhkLxci61/lCB7n7UmE3bN0leRt+qvkLxA=="
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-time/-/d3-time-2.0.0.tgz",
|
||||
"integrity": "sha512-2mvhstTFcMvwStWd9Tj3e6CEqtOivtD8AUiHT8ido/xmzrI9ijrUUihZ6nHuf/vsScRBonagOdj0Vv+SEL5G3Q=="
|
||||
},
|
||||
"d3-time-format": {
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.2.3.tgz",
|
||||
"integrity": "sha512-RAHNnD8+XvC4Zc4d2A56Uw0yJoM7bsvOlJR33bclxq399Rak/b9bhvu/InjxdWhPtkgU53JJcleJTGkNRnN6IA==",
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-3.0.0.tgz",
|
||||
"integrity": "sha512-UXJh6EKsHBTjopVqZBhFysQcoXSv/5yLONZvkQ5Kk3qbwiUYkdX17Xa1PT6U1ZWXGGfB1ey5L8dKMlFq2DO0Ag==",
|
||||
"requires": {
|
||||
"d3-time": "1"
|
||||
"d3-time": "1 - 2"
|
||||
}
|
||||
},
|
||||
"damerau-levenshtein": {
|
||||
@@ -4963,6 +5071,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"delaunator": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/delaunator/-/delaunator-4.0.1.tgz",
|
||||
"integrity": "sha512-WNPWi1IRKZfCt/qIDMfERkDp93+iZEmOxN2yy4Jg+Xhv8SLk2UTqqbe1sfiipn0and9QrE914/ihdx82Y/Giag=="
|
||||
},
|
||||
"delayed-stream": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||
@@ -5253,11 +5366,21 @@
|
||||
"dev": true
|
||||
},
|
||||
"encoding": {
|
||||
"version": "0.1.12",
|
||||
"resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz",
|
||||
"integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=",
|
||||
"version": "0.1.13",
|
||||
"resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz",
|
||||
"integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==",
|
||||
"requires": {
|
||||
"iconv-lite": "~0.4.13"
|
||||
"iconv-lite": "^0.6.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"iconv-lite": {
|
||||
"version": "0.6.2",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz",
|
||||
"integrity": "sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ==",
|
||||
"requires": {
|
||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"end-of-stream": {
|
||||
@@ -6766,11 +6889,6 @@
|
||||
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
|
||||
"dev": true
|
||||
},
|
||||
"get-node-dimensions": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/get-node-dimensions/-/get-node-dimensions-1.2.1.tgz",
|
||||
"integrity": "sha512-2MSPMu7S1iOTL+BOa6K1S62hB2zUAYNF/lV0gSVlOaacd087lc6nR1H1r0e3B1CerTo+RceOmi1iJW+vp21xcQ=="
|
||||
},
|
||||
"get-package-type": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz",
|
||||
@@ -7430,6 +7548,7 @@
|
||||
"version": "0.4.24",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"safer-buffer": ">= 2.1.2 < 3"
|
||||
}
|
||||
@@ -10194,6 +10313,16 @@
|
||||
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.15.tgz",
|
||||
"integrity": "sha512-rlrc3yU3+JNOpZ9zj5pQtxnx2THmvRykwL4Xlxoa8I9lHBlVbbyPhgyPMioxVZ4NqyxaVVtaJnzsyOidQIhyyQ=="
|
||||
},
|
||||
"lodash.get": {
|
||||
"version": "4.4.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
|
||||
"integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk="
|
||||
},
|
||||
"lodash.isplainobject": {
|
||||
"version": "4.0.6",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
|
||||
"integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs="
|
||||
},
|
||||
"lodash.sortby": {
|
||||
"version": "4.7.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
|
||||
@@ -12403,17 +12532,6 @@
|
||||
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
|
||||
"integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
|
||||
},
|
||||
"react-measure": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/react-measure/-/react-measure-2.3.0.tgz",
|
||||
"integrity": "sha512-dwAvmiOeblj5Dvpnk8Jm7Q8B4THF/f1l1HtKVi0XDecsG6LXwGvzV5R1H32kq3TW6RW64OAf5aoQxpIgLa4z8A==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.2.0",
|
||||
"get-node-dimensions": "^1.2.1",
|
||||
"prop-types": "^15.6.2",
|
||||
"resize-observer-polyfill": "^1.5.0"
|
||||
}
|
||||
},
|
||||
"react-modal": {
|
||||
"version": "3.11.2",
|
||||
"resolved": "https://registry.npmjs.org/react-modal/-/react-modal-3.11.2.tgz",
|
||||
@@ -12576,6 +12694,15 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"react-spring": {
|
||||
"version": "8.0.27",
|
||||
"resolved": "https://registry.npmjs.org/react-spring/-/react-spring-8.0.27.tgz",
|
||||
"integrity": "sha512-nDpWBe3ZVezukNRandTeLSPcwwTMjNVu1IDq9qA/AMiUqHuRN4BeSWvKr3eIxxg1vtiYiOLy4FqdfCP5IoP77g==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.3.1",
|
||||
"prop-types": "^15.5.8"
|
||||
}
|
||||
},
|
||||
"react-table": {
|
||||
"version": "6.11.4",
|
||||
"resolved": "https://registry.npmjs.org/react-table/-/react-table-6.11.4.tgz",
|
||||
@@ -12663,13 +12790,15 @@
|
||||
}
|
||||
},
|
||||
"recompose": {
|
||||
"version": "0.26.0",
|
||||
"resolved": "https://registry.npmjs.org/recompose/-/recompose-0.26.0.tgz",
|
||||
"integrity": "sha512-KwOu6ztO0mN5vy3+zDcc45lgnaUoaQse/a5yLVqtzTK13czSWnFGmXbQVmnoMgDkI5POd1EwIKSbjU1V7xdZog==",
|
||||
"version": "0.30.0",
|
||||
"resolved": "https://registry.npmjs.org/recompose/-/recompose-0.30.0.tgz",
|
||||
"integrity": "sha512-ZTrzzUDa9AqUIhRk4KmVFihH0rapdCSMFXjhHbNrjAWxBuUD/guYlyysMnuHjlZC/KRiOKRtB4jf96yYSkKE8w==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.0.0",
|
||||
"change-emitter": "^0.1.2",
|
||||
"fbjs": "^0.8.1",
|
||||
"hoist-non-react-statics": "^2.3.1",
|
||||
"react-lifecycles-compat": "^3.0.2",
|
||||
"symbol-observable": "^1.0.4"
|
||||
}
|
||||
},
|
||||
@@ -15067,9 +15196,9 @@
|
||||
}
|
||||
},
|
||||
"ua-parser-js": {
|
||||
"version": "0.7.21",
|
||||
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.21.tgz",
|
||||
"integrity": "sha512-+O8/qh/Qj8CgC6eYBVBykMrNtp5Gebn4dlGD/kKXVkJNDwyrAwSIqwz8CDf+tsAIWVycKcku6gIXJ0qwx/ZXaQ=="
|
||||
"version": "0.7.22",
|
||||
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.22.tgz",
|
||||
"integrity": "sha512-YUxzMjJ5T71w6a8WWVcMGM6YWOTX27rCoIQgLXiWaxqXSx9D7DNjiGWn1aJIRSQ5qr0xuhra77bSIh6voR/46Q=="
|
||||
},
|
||||
"unherit": {
|
||||
"version": "1.1.3",
|
||||
@@ -16086,9 +16215,9 @@
|
||||
}
|
||||
},
|
||||
"whatwg-fetch": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz",
|
||||
"integrity": "sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q=="
|
||||
"version": "3.5.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.5.0.tgz",
|
||||
"integrity": "sha512-jXkLtsR42xhXg7akoDKvKWE40eJeI+2KZqcp2h3NsOrRnDvtWX36KcKl30dy+hxECivdk2BVUHVNrPtoMBUx6A=="
|
||||
},
|
||||
"whatwg-mimetype": {
|
||||
"version": "2.3.0",
|
||||
|
||||
2
client/package.json
vendored
2
client/package.json
vendored
@@ -13,7 +13,7 @@
|
||||
"test:watch": "jest --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nivo/line": "^0.49.1",
|
||||
"@nivo/line": "^0.64.0",
|
||||
"axios": "^0.19.2",
|
||||
"classnames": "^2.2.6",
|
||||
"date-fns": "^1.29.0",
|
||||
|
||||
@@ -249,6 +249,7 @@
|
||||
"blocking_ipv6": "Blocking IPv6",
|
||||
"dns_over_https": "DNS-over-HTTPS",
|
||||
"dns_over_tls": "DNS-over-TLS",
|
||||
"dns_over_quic": "DNS-over-QUIC",
|
||||
"download_mobileconfig_doh": "Download .mobileconfig for DNS-over-HTTPS",
|
||||
"download_mobileconfig_dot": "Download .mobileconfig for DNS-over-TLS",
|
||||
"plain_dns": "Plain DNS",
|
||||
@@ -587,4 +588,4 @@
|
||||
"adg_will_drop_dns_queries": "AdGuard Home will be dropping all DNS queries from this client.",
|
||||
"client_not_in_allowed_clients": "The client is not allowed because it is not in the \"Allowed clients\" list.",
|
||||
"experimental": "Experimental"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"client_settings": "Configuración de clientes",
|
||||
"example_upstream_reserved": "puedes especificar el DNS de subida <0>para un dominio específico</0>",
|
||||
"example_upstream_comment": "Puedes especificar el comentario",
|
||||
"example_upstream_comment": "puedes especificar el comentario",
|
||||
"upstream_parallel": "Usar peticiones paralelas para acelerar la resolución al consultar simultáneamente a todos los servidores de subida",
|
||||
"parallel_requests": "Peticiones paralelas",
|
||||
"load_balancing": "Balanceo de carga",
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
{
|
||||
"client_settings": "Kliens beállítások",
|
||||
"example_upstream_reserved": "Megadhat egy DNS kiszolgálót <0>egy adott domainhez vagy domainekhez</0>",
|
||||
"example_upstream_comment": "Megadhat egy megjegyzést",
|
||||
"upstream_parallel": "Használjon párhuzamos lekéréseket a domainek feloldásának felgyorsításához az összes upstream kiszolgálóra való egyidejű lekérdezéssel",
|
||||
"parallel_requests": "Párhuzamos lekérések",
|
||||
"load_balancing": "Terheléselosztás",
|
||||
"load_balancing_desc": "Egyszerre csak egy szerverről történjen lekérdezés. Az AdGuard Home egy súlyozott, véletlenszerű algoritmust fog használni a megfelelő szerver kiválasztására, így a leggyorsabb szervert gyakrabban fogja használni.",
|
||||
"bootstrap_dns": "Bootstrap DNS kiszolgálók",
|
||||
"bootstrap_dns_desc": "Bootstrap DNS szerverek feloldják a megadott DoH/DoT feloldók IP-címeit.",
|
||||
"bootstrap_dns_desc": "A Bootstrap DNS szerverek a DoH/DoT feloldók IP-címeinek feloldására szolgálnak.",
|
||||
"check_dhcp_servers": "DHCP szerverek keresése",
|
||||
"save_config": "Konfiguráció mentése",
|
||||
"enabled_dhcp": "DHCP szerver engedélyezve",
|
||||
@@ -89,10 +90,10 @@
|
||||
"disable_protection": "Védelem letiltása",
|
||||
"disabled_protection": "Letiltott védelem",
|
||||
"refresh_statics": "Statisztikák frissítése",
|
||||
"dns_query": "DNS lekérdezések",
|
||||
"dns_query": "DNS lekérdezés",
|
||||
"blocked_by": "<0>Szűrők által blokkolt</0>",
|
||||
"stats_malware_phishing": "Blokkolt kártevő/adathalászat",
|
||||
"stats_adult": "Blokkolva a felnőtt tartalmak által",
|
||||
"stats_adult": "Blokkolt felnőtt tartalom",
|
||||
"stats_query_domain": "Leglátogatottabb domainek",
|
||||
"for_last_24_hours": "az utóbbi 24 órában",
|
||||
"for_last_days": "az utóbbi {{count}} napban",
|
||||
@@ -132,6 +133,8 @@
|
||||
"encryption_settings": "Titkosítási beállítások",
|
||||
"dhcp_settings": "DHCP beállítások",
|
||||
"upstream_dns": "Upstream DNS-kiszolgálók",
|
||||
"upstream_dns_help": "Adja meg a szerverek címeit soronként. <a>Tudjon meg többet</a> a DNS szerverek bekonfigurálásáról.",
|
||||
"upstream_dns_configured_in_file": "Beállítva itt: {{path}}",
|
||||
"test_upstream_btn": "Upstreamek tesztelése",
|
||||
"upstreams": "Upstream-ek",
|
||||
"apply_btn": "Alkalmaz",
|
||||
@@ -185,6 +188,7 @@
|
||||
"example_upstream_regular": "hagyományos DNS (UDP felett)",
|
||||
"example_upstream_dot": "titkosított <0>DNS-over-TLS</0>",
|
||||
"example_upstream_doh": "titkosított <0>DNS-over-HTTPS</0>",
|
||||
"example_upstream_doq": "titkosított <0>DNS-over-QUIC</0>",
|
||||
"example_upstream_sdns": "használhatja a <0> DNS Stamps</0>-ot a <1>DNSCrypt</1> vagy a <2>DNS-over-HTTPS</2> feloldások érdekében",
|
||||
"example_upstream_tcp": "hagyományos DNS (TCP felett)",
|
||||
"all_lists_up_to_date_toast": "Már minden lista naprakész",
|
||||
@@ -193,6 +197,10 @@
|
||||
"dns_test_not_ok_toast": "Szerver \"{{key}}\": nem használható, ellenőrizze, hogy helyesen írta-e be",
|
||||
"unblock": "Feloldás",
|
||||
"block": "Blokkolás",
|
||||
"disallow_this_client": "Tiltás ennek a kliensnek",
|
||||
"allow_this_client": "Engedélyezés ennek a kliensnek",
|
||||
"block_for_this_client_only": "Tiltás csak ennek a kliensnek",
|
||||
"unblock_for_this_client_only": "Feloldás csak ennek a kliensnek",
|
||||
"time_table_header": "Idő",
|
||||
"date": "Dátum",
|
||||
"domain_name_table_header": "Domain név",
|
||||
@@ -234,19 +242,25 @@
|
||||
"blocking_mode": "Blokkolás módja",
|
||||
"default": "Alapértelmezett",
|
||||
"nxdomain": "NXDOMAIN",
|
||||
"refused": "REFUSED",
|
||||
"null_ip": "Null IP-cím",
|
||||
"custom_ip": "Egyedi IP",
|
||||
"blocking_ipv4": "IPv4 blokkolása",
|
||||
"blocking_ipv6": "IPv6 blokkolása",
|
||||
"dns_over_https": "DNS-over-HTTPS",
|
||||
"dns_over_tls": "DNS-over-TLS",
|
||||
"download_mobileconfig_doh": ".mobileconfig letöltése DNS-over-HTTPS-hez",
|
||||
"download_mobileconfig_dot": ".mobileconfig letöltése DNS-over-TLS-hez",
|
||||
"plain_dns": "Egyszerű DNS",
|
||||
"form_enter_rate_limit": "Adja meg a kérések maximális számát",
|
||||
"rate_limit": "Kérések korlátozása",
|
||||
"edns_enable": "EDNS kliens alhálózat engedélyezése",
|
||||
"edns_cs_desc": "Ha engedélyezve van, az AdGuard Home a kliensek alhálózatait küldi el a DNS-kiszolgálóknak.",
|
||||
"rate_limit_desc": "Maximálisan hány kérést küldhet egy kliens másodpercenként (0: korlátlan)",
|
||||
"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",
|
||||
"blocking_mode_default": "Alapértelmezés: Válaszoljon nulla IP-címmel (vagyis 0.0.0.0 az A-hoz, :: pedig az AAAA-hoz), amikor a blokkolás egy adblock-stílusú szabállyal történik; illetve válaszoljon egy, a szabály által meghatározott IP címmel, amikor a blokkolás egy /etc/hosts stílusú szabállyal történik",
|
||||
"blocking_mode_refused": "REFUSED: Válaszoljon REFUSED kóddal",
|
||||
"blocking_mode_nxdomain": "NXDOMAIN: Az NXDOMAIN kóddal fog válaszolni",
|
||||
"blocking_mode_null_ip": "Null IP: Nullákból álló IP-címmel válaszol (0.0.0.0 for A; :: for AAAA)",
|
||||
"blocking_mode_custom_ip": "Egyedi IP: Válasz egy kézzel beállított IP címmel",
|
||||
@@ -260,7 +274,7 @@
|
||||
"unknown_filter": "Ismeretlen szűrő: {{filterId}}",
|
||||
"known_tracker": "Ismert követő",
|
||||
"install_welcome_title": "Üdvözli az AdGuard Home!",
|
||||
"install_welcome_desc": "Az AdGuard Home egy, a teljes hálózatot lefedő hirdetés és követő blokkoló DNS szerver. Az a célja, hogy lehetővé tegye a teljes hálózat és az összes eszköz vezérlését, és nem igényel kliensoldali programot.",
|
||||
"install_welcome_desc": "Az AdGuard Home egy, a teljes hálózatot lefedő DNS szerver, amely blokkolja a hirdetéseket és a nyomkövető rendszereket. Az a célja, hogy lehetővé tegye a teljes hálózat és az összes eszköz felügyeletét, emellett pedig nem igényel kliensoldali programot.",
|
||||
"install_settings_title": "Webes admin felület",
|
||||
"install_settings_listen": "Figyelő felület",
|
||||
"install_settings_port": "Port",
|
||||
@@ -323,6 +337,8 @@
|
||||
"encryption_https_desc": "Ha a HTTPS port konfigurálva van, akkor az AdGuard Home admin felülete elérhető lesz a HTTPS-en keresztül, és ezenkívül DNS-over-HTTPS-t is biztosít a '/dns-query' helyen.",
|
||||
"encryption_dot": "DNS-over-TLS port",
|
||||
"encryption_dot_desc": "Ha ez a port be van állítva, az AdGuard Home DNS-over-TLS szerverként tud futni ezen a porton.",
|
||||
"encryption_doq": "DNS-over-TLS port",
|
||||
"encryption_doq_desc": "Ha ez a port be van állítva, akkor az AdGuard Home egy DNS-over-QUIC szerverként fog futni ezen a porton. Ez egy kísérleti funkció és nem biztos, hogy megbízható. Emellett nincs sok olyan kliens, ami támogatná ezt jelenleg.",
|
||||
"encryption_certificates": "Tanúsítványok",
|
||||
"encryption_certificates_desc": "A titkosítás használatához érvényes SSL tanúsítványláncot kell megadnia a domainjéhez. Ingyenes tanúsítványt kaphat a <0>{{link}}</0> webhelyen, vagy megvásárolhatja az egyik megbízható tanúsítványkibocsátó hatóságtól.",
|
||||
"encryption_certificates_input": "Másolja be ide a PEM-kódolt tanúsítványt.",
|
||||
@@ -401,6 +417,8 @@
|
||||
"dns_privacy": "DNS Adatvédelem",
|
||||
"setup_dns_privacy_1": "<0>DNS-over-TLS:</0> Használja a(z) <1>{{address}}</1> szöveget.",
|
||||
"setup_dns_privacy_2": "<0>DNS-over-HTTPS:</0> Használja a(z) <1>{{address}}</1> szöveget.",
|
||||
"setup_dns_privacy_3": "<0>Azon szoftverek listája, amelyeket használhat.</0>",
|
||||
"setup_dns_privacy_4": "Az iOS14-en vagy a macOS Big Sur eszközökön le tud tölteni egy speciális, '.mobileconfig' nevű fájlt, ami hozzáadja a <highlight>DNS-over-HTTPS</highlight> vagy a <highlight>DNS-over-TLS</highlight> szervereket a DNS beállításokhoz.",
|
||||
"setup_dns_privacy_android_1": "Az Android 9 natív módon támogatja a DNS-over-TLS-t. A beállításához menjen a Beállítások → Hálózat & internet → Speciális → Privát DNS menübe, és adja meg itt a domaint.",
|
||||
"setup_dns_privacy_android_2": "Az <0>AdGuard for Android</0> támogatja a <1>DNS-over-HTTPS</1>-t és a <1>DNS-over-TLS</1>-t.",
|
||||
"setup_dns_privacy_android_3": "Az <0>Intra</0> hozzáadja a <1>DNS-over-HTTPS</1> támogatást az Androidhoz.",
|
||||
@@ -513,6 +531,7 @@
|
||||
"check_reason": "Indok: {{reason}}",
|
||||
"check_rule": "Szabály: {{rule}}",
|
||||
"check_service": "Szolgáltatás neve: {{service}}",
|
||||
"service_name": "Szolgáltatás neve",
|
||||
"check_not_found": "Nem található az Ön szűrőlistái között",
|
||||
"client_confirm_block": "Biztosan blokkolni szeretné a(z) \"{{ip}}\" klienst?",
|
||||
"client_confirm_unblock": "Biztosan fel szeretné oldani a(z) \"{{ip}}\" kliens blokkolását?",
|
||||
@@ -545,6 +564,14 @@
|
||||
"milliseconds_abbreviation": "ms",
|
||||
"cache_size": "Gyorsítótár mérete",
|
||||
"cache_size_desc": "DNS gyorsítótár mérete (bájtokban)",
|
||||
"cache_ttl_min_override": "A minimális TTL felülírása",
|
||||
"cache_ttl_max_override": "A maximális TTL felülírása",
|
||||
"enter_cache_size": "Adja meg a gyorsítótár méretét",
|
||||
"enter_cache_ttl_min_override": "Adja meg a minimális TTL-t (másodpercben)",
|
||||
"enter_cache_ttl_max_override": "Adja meg a maximális TTL-t (másodpercben)",
|
||||
"cache_ttl_min_override_desc": "Megnöveli a DNS kiszolgálótól kapott rövid TTL értékeket (másodpercben), ha gyorsítótárazza a DNS kéréseket",
|
||||
"cache_ttl_max_override_desc": "Állítson be egy maximális TTL értéket (másodpercben) a DNS gyorsítótár bejegyzéseihez",
|
||||
"ttl_cache_validation": "A minimális gyorsítótár TTL értéknek kisebbnek vagy egyenlőnek kell lennie a maximum értékkel",
|
||||
"filter_category_general": "Általános",
|
||||
"filter_category_security": "Biztonság",
|
||||
"filter_category_regional": "Regionális",
|
||||
@@ -556,5 +583,8 @@
|
||||
"setup_config_to_enable_dhcp_server": "Konfiguráció beállítása a DHCP-kiszolgáló engedélyezéséhez",
|
||||
"original_response": "Eredeti válasz",
|
||||
"click_to_view_queries": "Kattintson a lekérésekért",
|
||||
"port_53_faq_link": "Az 53-as portot gyakran a \"DNSStubListener\" vagy a \"systemd-resolved\" (rendszer által feloldott) szolgáltatások használják. Kérjük, olvassa el <0>ezt az útmutatót</0> a probléma megoldásához."
|
||||
"port_53_faq_link": "Az 53-as portot gyakran a \"DNSStubListener\" vagy a \"systemd-resolved\" (rendszer által feloldott) szolgáltatások használják. Kérjük, olvassa el <0>ezt az útmutatót</0> a probléma megoldásához.",
|
||||
"adg_will_drop_dns_queries": "Az AdGuard Home eldobja az összes DNS kérést erről a kliensről.",
|
||||
"client_not_in_allowed_clients": "Ez a kliens nincs engedélyezve, mivel nincs rajta az \"Engedélyezett kliensek\" listáján.",
|
||||
"experimental": "Kísérleti"
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"client_settings": "Klientinnstillinger",
|
||||
"example_upstream_reserved": "du kan bestemme en oppstrøms-DNS <0>for et spesifikt domene(r)</0>",
|
||||
"example_upstream_comment": "Du kan spesifisere kommentaren",
|
||||
"upstream_parallel": "Bruk parallele forespørsler for å få oppfarten på behandlinger, ved å forespørre til alle oppstrømstjenerne samtidig",
|
||||
"parallel_requests": "Parallelle forespørsler",
|
||||
"load_balancing": "Pågangstrykk-utjevning",
|
||||
@@ -248,6 +249,8 @@
|
||||
"blocking_ipv6": "IPv6-blokkering",
|
||||
"dns_over_https": "DNS-over-HTTPS",
|
||||
"dns_over_tls": "DNS-over-TLS",
|
||||
"download_mobileconfig_doh": "Last ned .mobileconfig for DNS-over-HTTPS",
|
||||
"download_mobileconfig_dot": "Last ned .mobileconfig for DNS-over-TLS",
|
||||
"plain_dns": "Ordinær DNS",
|
||||
"form_enter_rate_limit": "Skriv inn forespørselsfrekvensgrense",
|
||||
"rate_limit": "Forespørselsfrekvensgrense",
|
||||
@@ -525,6 +528,7 @@
|
||||
"check_reason": "Årsak: {{reason}}",
|
||||
"check_rule": "Oppføring: {{rule}}",
|
||||
"check_service": "Tjenestenavn: {{service}}",
|
||||
"service_name": "Tjenestenavn",
|
||||
"check_not_found": "Ikke funnet i filterlistene dine",
|
||||
"client_confirm_block": "Er du sikker på at du vil blokkere klienten «{{ip}}»?",
|
||||
"client_confirm_unblock": "Er du sikker på at du vil oppheve blokkeringen av klienten «{{ip}}»?",
|
||||
@@ -578,5 +582,6 @@
|
||||
"click_to_view_queries": "Klikk for å vise forespørsler",
|
||||
"port_53_faq_link": "Port 53 er ofte opptatt av «DNSStubListener»- eller «systemd-resolved»-tjenestene. Vennligst les <0>denne instruksjonen</0> om hvordan man løser dette.",
|
||||
"adg_will_drop_dns_queries": "AdGuard Home vil droppe alle DNS-forespørsler fra denne klienten.",
|
||||
"client_not_in_allowed_clients": "Klienten er ikke tillatt, fordi den ikke er i «Tillatte klienter»-listen.",
|
||||
"experimental": "Eksperimentell"
|
||||
}
|
||||
@@ -9,7 +9,7 @@
|
||||
"bootstrap_dns": "Servidores DNS de inicialização",
|
||||
"bootstrap_dns_desc": "Servidores DNS de inicialização são usados para resolver endereços IP dos resolvedores DoH/DoT que especifica como upstreams.",
|
||||
"check_dhcp_servers": "Verificar por servidores DHCP",
|
||||
"save_config": "Guardar configuração",
|
||||
"save_config": "Guardar definição",
|
||||
"enabled_dhcp": "Servidor DHCP activado",
|
||||
"disabled_dhcp": "Servidor DHCP desactivado",
|
||||
"unavailable_dhcp": "DHCP não está disponível",
|
||||
@@ -57,7 +57,7 @@
|
||||
"dhcp_new_static_lease": "Nova concessão estática",
|
||||
"dhcp_static_leases_not_found": "Nenhuma concessão DHCP estática foi encontrada",
|
||||
"dhcp_add_static_lease": "Adicionar nova concessão estática",
|
||||
"dhcp_reset": "Tem a certeza de que deseja redefinir a configuração DHCP?",
|
||||
"dhcp_reset": "Tem a certeza de que deseja repor a definição de DHCP?",
|
||||
"country": "País",
|
||||
"city": "Cidade",
|
||||
"delete_confirm": "Tem a certeza de que deseja excluir \"{{key}}\"?",
|
||||
@@ -133,7 +133,7 @@
|
||||
"encryption_settings": "Configurações de criptografia",
|
||||
"dhcp_settings": "Configurações de DHCP",
|
||||
"upstream_dns": "Servidores DNS upstream",
|
||||
"upstream_dns_help": "Insira os endereços dos servidores, um por linha. <a>Saber mais</a> sobre a configuração de servidores DNS primários.",
|
||||
"upstream_dns_help": "Insira os endereços dos servidores, um por linha. <a>Saber mais</a> sobre a definição de servidores DNS primários.",
|
||||
"upstream_dns_configured_in_file": "Configurado em {{path}}",
|
||||
"test_upstream_btn": "Testar upstreams",
|
||||
"upstreams": "Upstreams",
|
||||
@@ -236,8 +236,8 @@
|
||||
"query_log_retention_confirm": "Tem a certeza de que deseja alterar a retenção do registo de consulta? Se diminuir o valor do intervalo, alguns dados serão perdidos",
|
||||
"anonymize_client_ip": "Tornar anônimo o IP do cliente",
|
||||
"anonymize_client_ip_desc": "Não salva o endereço de IP completo do cliente em registros e estatísticas",
|
||||
"dns_config": "Configuração do servidor DNS",
|
||||
"dns_cache_config": "Configuração de cache DNS",
|
||||
"dns_config": "Definição do servidor DNS",
|
||||
"dns_cache_config": "Definição de cache DNS",
|
||||
"dns_cache_config_desc": "Aqui você pode configurar o cache do DNS",
|
||||
"blocking_mode": "Modo de bloqueio",
|
||||
"default": "Padrão",
|
||||
@@ -294,11 +294,11 @@
|
||||
"install_devices_title": "Configure os seus dispositivos",
|
||||
"install_devices_desc": "Para que o AdGuard Home comece a funcionar, precisa de configurar os seus dispositivos para o poder usar.",
|
||||
"install_submit_title": "Parabéns!",
|
||||
"install_submit_desc": "O procedimento de configuração está concluído e está pronto para começar a usar o AdGuard Home.",
|
||||
"install_submit_desc": "O procedimento de definição está concluído e está pronto para começar a usar o AdGuard Home.",
|
||||
"install_devices_router": "Router",
|
||||
"install_devices_router_desc": "Esta configuração cobrirá automaticamente todos os dispositivos ligados ao seu router doméstico e não irá precisar de configurar cada um deles manualmente.",
|
||||
"install_devices_router_desc": "Esta definição cobrirá automaticamente todos os dispositivos ligados ao seu router doméstico e não irá precisar de configurar cada um deles manualmente.",
|
||||
"install_devices_address": "O servidor de DNS do AdGuard Home está a capturar os seguintes endereços",
|
||||
"install_devices_router_list_1": "Abra as configurações do seu router. No navegador insira o IP do router, o padrão é (http://192.168.0.1/ ou http://192.168.1.1/), e o login e a palavra-passe é admin/admin; Se não se lembra da palavra-passe, pode redefinir a palavra-passe rapidamente pressionando um botão no próprio router. Alguns routers têm um aplicação específica que já deve estar instalada no seu computador/telefone.",
|
||||
"install_devices_router_list_1": "Abra as configurações do seu router. No navegador insira o IP do router, o padrão é (http://192.168.0.1/ ou http://192.168.1.1/), e o login e a palavra-passe é admin/admin; Se não se lembra da palavra-passe, pode repor a palavra-passe rapidamente pressionando um botão no próprio router. Alguns routers têm um aplicação específica que já deve estar instalada no seu computador/telefone.",
|
||||
"install_devices_router_list_2": "Encontre as configurações de DNS. Procure as letras DNS ao lado de um campo que permite dois ou três conjuntos de números, cada um dividido em quatro grupos de um a três números.",
|
||||
"install_devices_router_list_3": "Insira aqui seu servidor do AdGuard Home.",
|
||||
"install_devices_router_list_4": "Você não pode definir um servidor DNS personalizado em alguns tipos de roteadores. Nesse caso, pode ajudar se você configurar o AdGuard Home como um <0>servidor DHCP</0>. Caso contrário, você deve procurar o manual sobre como personalizar os servidores DNS para o seu modelo de roteador específico.",
|
||||
@@ -327,7 +327,7 @@
|
||||
"install_saved": "Guardado com sucesso",
|
||||
"encryption_title": "Encriptação",
|
||||
"encryption_desc": "Suporta a criptografia (HTTPS/TLS) para DNS e interface de administração web",
|
||||
"encryption_config_saved": "Configuração de criptografia guardada",
|
||||
"encryption_config_saved": "Definição de criptografia guardada",
|
||||
"encryption_server": "Nome do servidor",
|
||||
"encryption_server_enter": "Insira o seu nome de domínio",
|
||||
"encryption_server_desc": "Para usar o protocolo HTTPS, precisa de inserir o nome do servidor que corresponde ao seu certificado SSL.",
|
||||
@@ -355,14 +355,14 @@
|
||||
"encryption_subject": "Assunto",
|
||||
"encryption_issuer": "Emissor",
|
||||
"encryption_hostnames": "Nomes dos servidores",
|
||||
"encryption_reset": "Tem a certeza de que deseja redefinir a configuração de criptografia?",
|
||||
"encryption_reset": "Tem a certeza de que deseja repor a definição de criptografia?",
|
||||
"topline_expiring_certificate": "O seu certificado SSL está prestes a expirar. Actualize as suas <0>definições de criptografia</0>.",
|
||||
"topline_expired_certificate": "O seu certificado SSL está expirado. Actualize as suas <0>definições de criptografia</0>.",
|
||||
"form_error_port_range": "Digite um porta entre 80 e 65535",
|
||||
"form_error_port_unsafe": "Esta porta não é segura",
|
||||
"form_error_equal": "Não deve ser igual",
|
||||
"form_error_password": "As palavras-passe não coincidem",
|
||||
"reset_settings": "Redefinir configurações",
|
||||
"reset_settings": "Repor configurações",
|
||||
"update_announcement": "AdGuard Home {{version}} está disponível!<0>Clique aqui</0> para mais informações.",
|
||||
"setup_guide": "Guia de instalação",
|
||||
"dns_addresses": "Endereços DNS",
|
||||
@@ -401,7 +401,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 (tempo de execução)",
|
||||
"auto_clients_desc": "Dados dos clientes que usam o AdGuard Home, que não são armazenados na configuração",
|
||||
"auto_clients_desc": "Dados dos clientes que usam o AdGuard Home, que não são armazenados na definição",
|
||||
"access_title": "Configuraçõ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",
|
||||
@@ -423,7 +423,7 @@
|
||||
"setup_dns_privacy_android_2": "O <0>AdGuard para Android</0> suporta <1>DNS-sobre-HTTPS</1> e <1>DNS-sobre-TLS</1>.",
|
||||
"setup_dns_privacy_android_3": "<0>Intra</0> adiciona o suporte <1>DNS-sobre-HTTPS</1> para o Android.",
|
||||
"setup_dns_privacy_ios_1": "<0>DNSCloak</0> suporta <1>DNS-sobre-HTTPS</1>, mas para o configurar para usar o seu próprio servidor, precisará de gerar um <2>DNS Stamp</2>.",
|
||||
"setup_dns_privacy_ios_2": "O <0>AdGuard para iOS</0> suporta a configuração do <1>DNS-sobre-HTTPS</1> e <1>DNS-sobre-TLS</1>.",
|
||||
"setup_dns_privacy_ios_2": "O <0>AdGuard para iOS</0> suporta a definição do <1>DNS-sobre-HTTPS</1> e <1>DNS-sobre-TLS</1>.",
|
||||
"setup_dns_privacy_other_title": "Outras implementações",
|
||||
"setup_dns_privacy_other_1": "O próprio AdGuard Home pode ser usado como um cliente DNS seguro em qualquer plataforma.",
|
||||
"setup_dns_privacy_other_2": "<0>dnsproxy</0> suporta todos os protocolos de DNS seguros conhecidos.",
|
||||
@@ -460,8 +460,8 @@
|
||||
"encryption_certificates_source_content": "Colar o conteúdo dos certificados",
|
||||
"encryption_key_source_path": "Definir um arquivo de chave privada",
|
||||
"encryption_key_source_content": "Colar o conteúdo da chave privada",
|
||||
"stats_params": "Configuração de estatísticas",
|
||||
"config_successfully_saved": "Configuração guardada com sucesso",
|
||||
"stats_params": "Definição de estatísticas",
|
||||
"config_successfully_saved": "Definição guardada com sucesso",
|
||||
"interval_24_hour": "24 horas",
|
||||
"interval_days": "{{count}} dias",
|
||||
"interval_days_plural": "{{count}} dias",
|
||||
@@ -470,7 +470,7 @@
|
||||
"filter_added_successfully": "O filtro foi adicionado com sucesso",
|
||||
"filter_removed_successfully": "A lista foi removida com sucesso",
|
||||
"filter_updated": "O filtro atualizado com sucesso",
|
||||
"statistics_configuration": "Configuração das estatísticas",
|
||||
"statistics_configuration": "Definição das estatísticas",
|
||||
"statistics_retention": "Retenção de estatísticas",
|
||||
"statistics_retention_desc": "Se diminuir o valor do intervalo, alguns dados serão perdidos",
|
||||
"statistics_clear": " Limpar estatísticas",
|
||||
@@ -479,7 +479,7 @@
|
||||
"statistics_cleared": "As estatísticas foram apagadas com sucesso",
|
||||
"interval_hours": "{{count}} hora",
|
||||
"interval_hours_plural": "{{count}} horas",
|
||||
"filters_configuration": "Configuração dos filtros",
|
||||
"filters_configuration": "Definição dos filtros",
|
||||
"filters_enable": "Activar filtros",
|
||||
"filters_interval": "Intervalo de actualização de filtros",
|
||||
"disabled": "Desactivado",
|
||||
@@ -576,11 +576,11 @@
|
||||
"filter_category_security": "Segurança",
|
||||
"filter_category_regional": "Regional",
|
||||
"filter_category_other": "Outro",
|
||||
"filter_category_general_desc": "Listas que bloqueiam o rastreamento e a publicidade na maioria dos dispositivos",
|
||||
"filter_category_general_desc": "Listas que bloqueiam o monitorização e a publicidade na maioria dos dispositivos",
|
||||
"filter_category_security_desc": "Listas especializadas em bloquear domínios de malware, phishing ou fraude",
|
||||
"filter_category_regional_desc": "Listas focadas em anúncios regionais e servidores de rastreamento",
|
||||
"filter_category_regional_desc": "Listas focadas em anúncios regionais e servidores de monitorização",
|
||||
"filter_category_other_desc": "Outras listas negras",
|
||||
"setup_config_to_enable_dhcp_server": "Configure a configuração para habilitar o servidor DHCP",
|
||||
"setup_config_to_enable_dhcp_server": "Defina a definição para habilitar o servidor DHCP",
|
||||
"original_response": "Resposta original",
|
||||
"click_to_view_queries": "Clique para ver as consultas",
|
||||
"port_53_faq_link": "A porta 53 é frequentemente ocupada por serviços \"DNSStubListener\" ou \"systemd-resolved\". Por favor leia <0>essa instrução</0> para resolver isso.",
|
||||
|
||||
@@ -105,8 +105,8 @@
|
||||
"custom_filtering_rules": "අභිරුචි පෙරීමේ නීති",
|
||||
"encryption_settings": "සංකේතාංකන සැකසුම්",
|
||||
"dhcp_settings": "DHCP සැකසුම්",
|
||||
"upstream_dns": "Upstream ව.නා.ප. (DNS) සේවාදායකයන්",
|
||||
"upstream_dns_help": "පේළියකට එක් සේවාදායක ලිපිනය බැගින් ඇතුළත් කරන්න. upstream ව.නා.ප. සේවාදායකයන් වින්යාසගත කිරීම ගැන <a>තව දැනගන්න</a>.",
|
||||
"upstream_dns": "Upstream ව.නා.ප. සේවාදායකයන්",
|
||||
"upstream_dns_help": "පේළියකට එක් සේවාදායක ලිපිනය බැගින් ඇතුළත් කරන්න. upstream ව.නා.ප. (DNS) \n සේවාදායකයන් වින්යාසගත කිරීම ගැන <a>තව දැනගන්න</a>.",
|
||||
"apply_btn": "යොදන්න",
|
||||
"disabled_filtering_toast": "පෙරීම අබල කර ඇත",
|
||||
"enabled_filtering_toast": "පෙරීම සබල කර ඇත",
|
||||
|
||||
@@ -87,7 +87,7 @@
|
||||
"refresh_statics": "İstatistikleri yenile",
|
||||
"dns_query": "DNS Sorguları",
|
||||
"blocked_by": "<0>Filtreler tarafından engellendi</0>",
|
||||
"stats_malware_phishing": "Zararlı yazılım/kimlik hırsızlığı engellendi",
|
||||
"stats_malware_phishing": "Kötü amaçlı yazılım/kimlik avı engellendi",
|
||||
"stats_adult": "Yetişkin içerikli site engellendi",
|
||||
"stats_query_domain": "En fazla sorgulanan alan adları",
|
||||
"for_last_24_hours": "son 24 saat içindekiler",
|
||||
|
||||
@@ -89,7 +89,7 @@
|
||||
"enabled_protection": "保护已启用",
|
||||
"disable_protection": "禁用保护",
|
||||
"disabled_protection": "保护已禁用",
|
||||
"refresh_statics": "刷新状态",
|
||||
"refresh_statics": "刷新统计数据",
|
||||
"dns_query": "DNS查询",
|
||||
"blocked_by": "<0>已被过滤器拦截</0>",
|
||||
"stats_malware_phishing": "被拦截的恶意/钓鱼网站",
|
||||
@@ -199,7 +199,7 @@
|
||||
"block": "拦截",
|
||||
"disallow_this_client": "不允许这个客户端",
|
||||
"allow_this_client": "允许这个客户端",
|
||||
"block_for_this_client_only": "仅拦截这个客户端",
|
||||
"block_for_this_client_only": "仅对此客户端拦截",
|
||||
"unblock_for_this_client_only": "仅解除对此客户端的拦截",
|
||||
"time_table_header": "时间",
|
||||
"date": "日期",
|
||||
@@ -316,7 +316,7 @@
|
||||
"install_devices_android_list_2": "点击菜单上的 ”无线局域网“ 选项。在屏幕上将列出所有可用的网络(蜂窝移动网络不支持修改 DNS )。",
|
||||
"install_devices_android_list_3": "长按当前已连接的网络,然后点击 ”修改网络设置“ 。",
|
||||
"install_devices_android_list_4": "在某些设备上,您可能需要选中 ”高级“ 复选框以查看进一步的设置。您可能需要调整您安卓设备的 DNS 设置,或是需要将 IP 设置从 DHCP 切换到静态。",
|
||||
"install_devices_android_list_5": "将 \"DNS 1 / 主 DNS\" 和 ”DNS 2 / 副 DNS“ 的值改为您的 AdGuard Home 服务器地址。",
|
||||
"install_devices_android_list_5": "将 DNS 1 和 DNS 2 的值改为您的 AdGuard Home 服务器地址。",
|
||||
"install_devices_ios_list_1": "从主屏幕中点击 ”设置“ 。",
|
||||
"install_devices_ios_list_2": "从左侧目录中选择 ”无线局域网“(移动数据网络环境下不支持修改 DNS )。",
|
||||
"install_devices_ios_list_3": "点击当前已连接网络的名称。",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"client_settings": "用戶端設定",
|
||||
"example_upstream_reserved": "您可以<0>指定網域</0>使用特定 DNS 查詢",
|
||||
"example_upstream_comment": "您可以指定註解",
|
||||
"upstream_parallel": "使用平行查詢,同時查詢所有上游伺服器來來加速解析結果",
|
||||
"parallel_requests": "平行處理",
|
||||
"load_balancing": "負載平衝",
|
||||
@@ -132,6 +133,8 @@
|
||||
"encryption_settings": "加密設定",
|
||||
"dhcp_settings": "DHCP 設定",
|
||||
"upstream_dns": "上游 DNS 伺服器",
|
||||
"upstream_dns_help": "每行輸入一個伺服器位址。<a>了解更多</a>有關配置上遊 DNS 伺服器的內容",
|
||||
"upstream_dns_configured_in_file": "被配置在 {{path}}",
|
||||
"test_upstream_btn": "測試上游 DNS",
|
||||
"upstreams": "上游",
|
||||
"apply_btn": "套用",
|
||||
@@ -194,6 +197,10 @@
|
||||
"dns_test_not_ok_toast": "設定中的 \"{{key}}\" DNS 出現錯誤,請檢察拼字",
|
||||
"unblock": "解除封鎖",
|
||||
"block": "封鎖",
|
||||
"disallow_this_client": "不允許此用戶端",
|
||||
"allow_this_client": "允許此用戶端",
|
||||
"block_for_this_client_only": "僅為此用戶端封鎖",
|
||||
"unblock_for_this_client_only": "僅為此用戶端解除封鎖",
|
||||
"time_table_header": "時間",
|
||||
"date": "日期",
|
||||
"domain_name_table_header": "域名",
|
||||
@@ -235,12 +242,15 @@
|
||||
"blocking_mode": "封鎖模式",
|
||||
"default": "預設",
|
||||
"nxdomain": "NXDOMAIN",
|
||||
"refused": "REFUSED",
|
||||
"null_ip": "Null IP",
|
||||
"custom_ip": "自訂 IP 位址",
|
||||
"blocking_ipv4": "封鎖 IPv4",
|
||||
"blocking_ipv6": "封鎖 IPv6",
|
||||
"dns_over_https": "DNS-over-HTTPS",
|
||||
"dns_over_tls": "DNS-over-TLS",
|
||||
"download_mobileconfig_doh": "下載適用於 DNS-over-HTTPS 的 .mobileconfig",
|
||||
"download_mobileconfig_dot": "下載適用於 DNS-over-TLS 的 .mobileconfig",
|
||||
"plain_dns": "一般未加密 DNS",
|
||||
"form_enter_rate_limit": "輸入速率限制",
|
||||
"rate_limit": "速率限制",
|
||||
@@ -249,6 +259,8 @@
|
||||
"rate_limit_desc": "限制單一裝置每秒發出的查詢次數(設定為 0 即表示無限制)",
|
||||
"blocking_ipv4_desc": "回覆指定 IPv4 位址給被封鎖的網域的 A 紀錄查詢",
|
||||
"blocking_ipv6_desc": "回覆指定 IPv6 位址給被封鎖的網域的 AAAA 紀錄查詢",
|
||||
"blocking_mode_default": "預設:被 Adblock 規則封鎖時回應零值的 IP 位址(A 紀錄回應 0.0.0.0 ,AAAA 紀錄回應 ::);被 /etc/hosts 規則封鎖時回應規則中指定 IP 位址",
|
||||
"blocking_mode_refused": "REFUSED:以 REFUSED 碼回應",
|
||||
"blocking_mode_nxdomain": "NXDOMAIN:回應 NXDOMAIN 狀態碼",
|
||||
"blocking_mode_null_ip": "Null IP:回應零值的 IP 位址(A 紀錄回應 0.0.0.0 ,AAAA 紀錄回應 ::)",
|
||||
"blocking_mode_custom_ip": "自訂 IP 位址:回應一個自訂的 IP 位址",
|
||||
@@ -405,6 +417,8 @@
|
||||
"dns_privacy": "DNS 隱私",
|
||||
"setup_dns_privacy_1": "<0>DNS-over-TLS:</0>使用 <1>{{address}}</1>。",
|
||||
"setup_dns_privacy_2": "<0>DNS-over-HTTPS:</0>使用 <1>{{address}}</1>。",
|
||||
"setup_dns_privacy_3": "<0>以下是您可以使用軟體的列表</0>",
|
||||
"setup_dns_privacy_4": "在 iOS 14 或 macOS Big Sur 裝置上,您可以下載特定的 '.mobileconfig' 檔案。此檔案將<highlight>DNS-over-HTTPS</highlight> 或 <highlight>DNS-over-TLS</highlight> 伺服器添加至 DNS 設定。",
|
||||
"setup_dns_privacy_android_1": "Android 9 原生支援 DNS-over-TLS。前網「設定」→「網路 & 網際網路」→「進階」→「私人 DNS」設定。",
|
||||
"setup_dns_privacy_android_2": "<0>AdGuard for Android</0> 支援 <1>DNS-over-HTTPS</1> 與 <1>DNS-over-TLS</1>。",
|
||||
"setup_dns_privacy_android_3": "<0>Intra</0> 對 Android 新增支援 <1>DNS-over-HTTPS</1>。",
|
||||
@@ -517,6 +531,7 @@
|
||||
"check_reason": "原因:{{reason}}",
|
||||
"check_rule": "規則:{{rule}}",
|
||||
"check_service": "服務名稱:{{service}}",
|
||||
"service_name": "服務名稱",
|
||||
"check_not_found": "未在您的過濾清單中找到",
|
||||
"client_confirm_block": "您確定要封鎖「{{ip}}」用戶端?",
|
||||
"client_confirm_unblock": "您確定要將「{{ip}}」解除封鎖嗎?",
|
||||
@@ -551,6 +566,12 @@
|
||||
"cache_size_desc": "DNS 快取大小(bytes)",
|
||||
"cache_ttl_min_override": "覆寫最小 TTL 值",
|
||||
"cache_ttl_max_override": "覆寫最大 TTL 值",
|
||||
"enter_cache_size": "輸入快取大小(bytes)",
|
||||
"enter_cache_ttl_min_override": "輸入最小 TTL 值(秒)",
|
||||
"enter_cache_ttl_max_override": "輸入最大 TTL 值(秒)",
|
||||
"cache_ttl_min_override_desc": "快取 DNS 回應時,延長從上遊伺服器收到的 TTL 值 (秒)",
|
||||
"cache_ttl_max_override_desc": "設定 DNS 快取條目的最大 TTL 值(秒)",
|
||||
"ttl_cache_validation": "最小快取 TTL 值必須小於或等於最大值",
|
||||
"filter_category_general": "一般",
|
||||
"filter_category_security": "安全性",
|
||||
"filter_category_regional": "區域性",
|
||||
@@ -563,5 +584,7 @@
|
||||
"original_response": "原始回應",
|
||||
"click_to_view_queries": "按一下以檢視查詢結果",
|
||||
"port_53_faq_link": "連接埠 53 經常被「DNSStubListener」或「systemd-resolved」服務佔用。請閱讀下列有關解決<0>這個問題</0>的說明",
|
||||
"adg_will_drop_dns_queries": "AdGuard Home 將要終止所有來自此用戶端的 DNS 查詢。",
|
||||
"client_not_in_allowed_clients": "此用戶端不被允許,它不在\"允許的用戶端\"列表中。",
|
||||
"experimental": "實驗性"
|
||||
}
|
||||
@@ -264,7 +264,7 @@
|
||||
"blocking_mode_nxdomain": "不存在的網域(NXDOMAIN):以 NXDOMAIN 碼回覆",
|
||||
"blocking_mode_null_ip": "無效的 IP:以零值 IP 位址(0.0.0.0 供 A;:: 供 AAAA)回覆",
|
||||
"blocking_mode_custom_ip": "自訂的 IP:以一組手動地被設定的 IP 位址回覆",
|
||||
"upstream_dns_client_desc": "如果您將該欄位留空,AdGuard Home 將使用在 <0>DNS 設定</0>中被配置的伺服器。",
|
||||
"upstream_dns_client_desc": "如果您將此欄位留空,AdGuard Home 將使用在 <0>DNS 設定</0>中被配置的伺服器。",
|
||||
"tracker_source": "追蹤器來源",
|
||||
"source_label": "來源",
|
||||
"found_in_known_domain_db": "在已知的域名資料庫中被發現。",
|
||||
@@ -338,7 +338,7 @@
|
||||
"encryption_dot": "DNS-over-TLS 連接埠",
|
||||
"encryption_dot_desc": "如果該連接埠被配置,AdGuard Home 將於此連接埠上運行 DNS-over-TLS 伺服器。",
|
||||
"encryption_doq": "DNS-over-QUIC 連接埠",
|
||||
"encryption_doq_desc": "如果此連接埠被配置,AdGuard Home 將於此連接埠上執行 DNS-over-QUIC 伺服器。它是實驗性的並可能為不可靠的。再者,此刻沒有太多支援它的用戶端。",
|
||||
"encryption_doq_desc": "如果此連接埠被配置,AdGuard Home 將於此連接埠上運行 DNS-over-QUIC 伺服器。它是實驗性的並可能為不可靠的。再者,此刻沒有太多支援它的用戶端。",
|
||||
"encryption_certificates": "憑證",
|
||||
"encryption_certificates_desc": "為了使用加密,您需要提供有效的安全通訊端層(SSL)憑證鏈結供您的網域。於 <0>{{link}}</0> 上您可取得免費的憑證或您可從受信任的憑證授權單位之一購買它。",
|
||||
"encryption_certificates_input": "於此複製/貼上您的隱私增強郵件編碼之(PEM-encoded)憑證。",
|
||||
|
||||
@@ -51,9 +51,10 @@ export const toggleClientBlockSuccess = createAction('TOGGLE_CLIENT_BLOCK_SUCCES
|
||||
export const toggleClientBlock = (ip, disallowed, disallowed_rule) => async (dispatch) => {
|
||||
dispatch(toggleClientBlockRequest());
|
||||
try {
|
||||
const {
|
||||
allowed_clients, blocked_hosts, disallowed_clients = [],
|
||||
} = await apiClient.getAccessList();
|
||||
const accessList = await apiClient.getAccessList();
|
||||
const allowed_clients = accessList.allowed_clients ?? [];
|
||||
const blocked_hosts = accessList.blocked_hosts ?? [];
|
||||
const disallowed_clients = accessList.disallowed_clients ?? [];
|
||||
|
||||
const updatedDisallowedClients = disallowed
|
||||
? disallowed_clients.filter((client) => client !== disallowed_rule)
|
||||
|
||||
@@ -373,10 +373,14 @@ export const getDhcpStatusFailure = createAction('GET_DHCP_STATUS_FAILURE');
|
||||
export const getDhcpStatus = () => async (dispatch) => {
|
||||
dispatch(getDhcpStatusRequest());
|
||||
try {
|
||||
const status = await apiClient.getDhcpStatus();
|
||||
const globalStatus = await apiClient.getGlobalStatus();
|
||||
status.dhcp_available = globalStatus.dhcp_available;
|
||||
dispatch(getDhcpStatusSuccess(status));
|
||||
if (globalStatus.dhcp_available) {
|
||||
const status = await apiClient.getDhcpStatus();
|
||||
status.dhcp_available = globalStatus.dhcp_available;
|
||||
dispatch(getDhcpStatusSuccess(status));
|
||||
} else {
|
||||
dispatch(getDhcpStatusFailure());
|
||||
}
|
||||
} catch (error) {
|
||||
dispatch(addErrorToast({ error }));
|
||||
dispatch(getDhcpStatusFailure());
|
||||
|
||||
@@ -22,11 +22,6 @@
|
||||
border-bottom: 6px solid #585965;
|
||||
}
|
||||
|
||||
.card-chart-bg {
|
||||
left: -20px;
|
||||
width: calc(100% + 20px);
|
||||
}
|
||||
|
||||
@media (max-width: 1279.98px) {
|
||||
.table__action {
|
||||
position: absolute;
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
--gray-4d: #4D4D4D;
|
||||
--gray-f3: #F3F3F3;
|
||||
--gray-8: #888;
|
||||
--gray-3: #333;
|
||||
--danger: #DF3812;
|
||||
--white80: rgba(255, 255, 255, 0.8);
|
||||
|
||||
|
||||
@@ -72,30 +72,30 @@ const Interfaces = () => {
|
||||
(store) => store.form[FORM_NAME.DHCP_INTERFACES]?.values?.interface_name,
|
||||
);
|
||||
|
||||
if (processingInterfaces || !interfaces) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const interfaceValue = interface_name && interfaces[interface_name];
|
||||
|
||||
return !processingInterfaces
|
||||
&& interfaces
|
||||
&& <>
|
||||
<div className="row dhcp__interfaces">
|
||||
<div className="col col__dhcp">
|
||||
<Field
|
||||
name="interface_name"
|
||||
component={renderSelectField}
|
||||
className="form-control custom-select pl-4 col-md"
|
||||
validate={[validateRequiredValue]}
|
||||
label='dhcp_interface_select'
|
||||
>
|
||||
<option value='' disabled={enabled}>
|
||||
{t('dhcp_interface_select')}
|
||||
</option>
|
||||
{renderInterfaces(interfaces)}
|
||||
</Field>
|
||||
</div>
|
||||
{interfaceValue
|
||||
&& renderInterfaceValues(interfaceValue)}
|
||||
</div>
|
||||
</>;
|
||||
return <div className="row dhcp__interfaces">
|
||||
<div className="col col__dhcp">
|
||||
<Field
|
||||
name="interface_name"
|
||||
component={renderSelectField}
|
||||
className="form-control custom-select pl-4 col-md"
|
||||
validate={[validateRequiredValue]}
|
||||
label='dhcp_interface_select'
|
||||
>
|
||||
<option value='' disabled={enabled}>
|
||||
{t('dhcp_interface_select')}
|
||||
</option>
|
||||
{renderInterfaces(interfaces)}
|
||||
</Field>
|
||||
</div>
|
||||
{interfaceValue
|
||||
&& renderInterfaceValues(interfaceValue)}
|
||||
</div>;
|
||||
};
|
||||
|
||||
renderInterfaceValues.propTypes = {
|
||||
|
||||
@@ -65,9 +65,14 @@ const Dhcp = () => {
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(getDhcpStatus());
|
||||
dispatch(getDhcpInterfaces());
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (dhcp_available) {
|
||||
dispatch(getDhcpInterfaces());
|
||||
}
|
||||
}, [dhcp_available]);
|
||||
|
||||
useEffect(() => {
|
||||
const [ipv4] = interfaces?.[interface_name]?.ipv4_addresses ?? [];
|
||||
const [ipv6] = interfaces?.[interface_name]?.ipv6_addresses ?? [];
|
||||
|
||||
@@ -8,10 +8,10 @@ const Examples = (props) => (
|
||||
<Trans>examples_title</Trans>:
|
||||
<ol className="leading-loose">
|
||||
<li>
|
||||
<code>9.9.9.9</code> - {props.t('example_upstream_regular')}
|
||||
<code>94.140.14.140</code> - {props.t('example_upstream_regular')}
|
||||
</li>
|
||||
<li>
|
||||
<code>tls://dns.quad9.net</code> –
|
||||
<code>tls://dns-unfiltered.adguard.com</code> –
|
||||
<span>
|
||||
<Trans
|
||||
components={[
|
||||
@@ -30,7 +30,7 @@ const Examples = (props) => (
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<code>https://dns.quad9.net/dns-query</code> –
|
||||
<code>https://dns-unfiltered.adguard.com/dns-query</code> –
|
||||
<span>
|
||||
<Trans
|
||||
components={[
|
||||
@@ -70,7 +70,7 @@ const Examples = (props) => (
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<code>tcp://9.9.9.9</code> – <Trans>example_upstream_tcp</Trans>
|
||||
<code>tcp://94.140.14.140</code> – <Trans>example_upstream_tcp</Trans>
|
||||
</li>
|
||||
<li>
|
||||
<code>sdns://...</code> –
|
||||
@@ -108,7 +108,7 @@ const Examples = (props) => (
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<code>[/example.local/]9.9.9.9</code> –
|
||||
<code>[/example.local/]94.140.14.140</code> –
|
||||
<span>
|
||||
<Trans
|
||||
components={[
|
||||
|
||||
@@ -80,7 +80,6 @@
|
||||
.card-body-stats {
|
||||
position: relative;
|
||||
flex: 1 1 auto;
|
||||
height: calc(100% - 3rem);
|
||||
margin: 0;
|
||||
padding: 1rem 1.5rem;
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@ const dnsPrivacyList = [{
|
||||
components: [
|
||||
{
|
||||
key: 0,
|
||||
href: 'https://github.com/jedisct1/dnscrypt-proxy',
|
||||
href: 'https://support.mozilla.org/kb/firefox-dns-over-https',
|
||||
},
|
||||
<code key="1">text</code>,
|
||||
],
|
||||
|
||||
@@ -245,6 +245,16 @@ const Icons = () => (
|
||||
d="M41 4H9C6.243 4 4 6.243 4 9v32c0 2.757 2.243 5 5 5h32c2.757 0 5-2.243 5-5V9c0-2.757-2.243-5-5-5zm-3.994 18.323a7.482 7.482 0 0 1-.69.035 7.492 7.492 0 0 1-6.269-3.388v11.537a8.527 8.527 0 1 1-8.527-8.527c.178 0 .352.016.527.027v4.202c-.175-.021-.347-.053-.527-.053a4.351 4.351 0 1 0 0 8.704c2.404 0 4.527-1.894 4.527-4.298l.042-19.594h4.016a7.488 7.488 0 0 0 6.901 6.685v4.67z" />
|
||||
</symbol>
|
||||
|
||||
<symbol id="service_qq" viewBox="0 0 32 32" >
|
||||
<g fill="none" fillRule="evenodd">
|
||||
<path d="M0 0h32v32H0z" />
|
||||
<g fill="currentColor" fillRule="nonzero">
|
||||
<path d="M11.25 32C8.342 32 6 30.74 6 29.242c0-1.497 2.342-2.757 5.25-2.757s5.25 1.26 5.25 2.757S14.158 32 11.25 32zM27 29.242c0-1.497-2.342-2.757-5.25-2.757s-5.25 1.26-5.25 2.757S18.842 32 21.75 32 27 30.74 27 29.242zM14.885 7.182c0 .63-.323 1.182-.808 1.182-.485 0-.808-.552-.808-1.182 0-.63.323-1.182.808-1.182.485 0 .808.552.808 1.182zM18.923 6c-.485 0-.808.552-.808 1.182 0 .63.323-.394.808-.394.485 0 .808 1.024.808.394S19.408 6 18.923 6z" />
|
||||
<path d="M6.653 12.638s4.685 2.465 9.926 2.465c5.242 0 9.927-2.465 9.927-2.465.112-.09.217-.161.316-.212-.002-1.088-.078-2.026-.078-2.808C26.744 4.292 22.138 0 16.5 0S6.176 4.292 6.176 9.618v2.78c.146.042.3.113.477.24zm12.626-8.664c1.112 0 1.986 1.272 1.986 2.782s-.874 2.782-1.986 2.782c-1.111 0-1.985-1.271-1.985-2.782 0-1.51.874-2.782 1.985-2.782zm-5.558 0c1.111 0 1.985 1.272 1.985 2.782s-.874 2.782-1.985 2.782c-1.112 0-1.986-1.271-1.986-2.782 0-1.51.874-2.782 1.986-2.782zm2.779 6.624c2.912 0 5.294.464 5.294.994s-2.382 1.656-5.294 1.656c-2.912 0-5.294-1.126-5.294-1.656s2.382-.994 5.294-.994zm11.374 5.182c-.058.038-.108.076-.177.117-.159.08-5.241 3.18-11.038 3.18-1.43 0-2.7-.239-3.97-.477-.239 1.67-.239 3.259-.239 3.974 0 1.272-1.032 1.193-2.303 1.272-1.27 0-2.223.16-2.303-1.033 0-.16-.08-2.782.397-5.564-1.588-.716-2.62-1.272-2.7-1.352a3.293 3.293 0 01-.335-.216C4.012 17.55 3 19.598 3 21.223c0 3.815 1.112 3.418 1.112 3.418.476 0 1.27-.795 1.985-1.67C7.765 27.662 11.735 31 16.5 31c4.765 0 8.735-3.338 10.403-8.028.715.874 1.509 1.669 1.985 1.669 0 0 1.112.397 1.112-3.418 0-1.588-.968-3.631-2.126-5.443z" />
|
||||
</g>
|
||||
</g>
|
||||
</symbol>
|
||||
|
||||
<symbol id="question" width="20px" height="20px">
|
||||
<g transform="translate(-982.000000, -454.000000) translate(416.000000, 440.000000) translate(564.000000, 12.000000)"
|
||||
fill="none" fillRule="evenodd">
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
.line__tooltip {
|
||||
padding: 2px 10px 7px;
|
||||
line-height: 1.1;
|
||||
color: #fff;
|
||||
color: var(--white);
|
||||
background-color: var(--gray-3);
|
||||
border-radius: 4px;
|
||||
opacity: 90%;
|
||||
}
|
||||
|
||||
.line__tooltip-text {
|
||||
font-size: 0.7rem;
|
||||
}
|
||||
|
||||
.card-chart-bg path[d^="M0,32"] {
|
||||
transform: translateY(32px);
|
||||
}
|
||||
|
||||
@@ -1,56 +1,75 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { ResponsiveLine } from '@nivo/line';
|
||||
|
||||
import addDays from 'date-fns/add_days';
|
||||
import addHours from 'date-fns/add_hours';
|
||||
import subDays from 'date-fns/sub_days';
|
||||
import subHours from 'date-fns/sub_hours';
|
||||
import dateFormat from 'date-fns/format';
|
||||
import round from 'lodash/round';
|
||||
import { useSelector } from 'react-redux';
|
||||
import PropTypes from 'prop-types';
|
||||
import './Line.css';
|
||||
|
||||
const Line = ({ data, color }) => data
|
||||
&& <ResponsiveLine
|
||||
const Line = ({
|
||||
data, color = 'black',
|
||||
}) => {
|
||||
const interval = useSelector((state) => state.stats.interval);
|
||||
|
||||
return <ResponsiveLine
|
||||
enableArea
|
||||
animate
|
||||
enableSlices="x"
|
||||
curve="linear"
|
||||
colors={[color]}
|
||||
data={data}
|
||||
margin={{
|
||||
top: data[0].data.every(({ y }) => y === 0) ? 62 : 15,
|
||||
right: 0,
|
||||
bottom: 1,
|
||||
left: 20,
|
||||
}}
|
||||
minY="auto"
|
||||
stacked={false}
|
||||
curve='linear'
|
||||
axisBottom={null}
|
||||
axisLeft={null}
|
||||
enableGridX={false}
|
||||
enableGridY={false}
|
||||
enableDots={false}
|
||||
enableArea={true}
|
||||
animate={false}
|
||||
colorBy={() => (color)}
|
||||
tooltip={(slice) => (
|
||||
<div>
|
||||
{slice.data.map((d) => (
|
||||
<div key={d.serie.id} className="line__tooltip">
|
||||
<span className="line__tooltip-text">
|
||||
<strong>{d.data.y}</strong>
|
||||
<br />
|
||||
<small>{d.data.x}</small>
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
theme={{
|
||||
tooltip: {
|
||||
container: {
|
||||
padding: '0',
|
||||
background: '#333',
|
||||
borderRadius: '4px',
|
||||
crosshair: {
|
||||
line: {
|
||||
stroke: 'black',
|
||||
strokeWidth: 1,
|
||||
strokeOpacity: 0.35,
|
||||
},
|
||||
},
|
||||
}}
|
||||
xScale={{
|
||||
type: 'linear',
|
||||
min: 0,
|
||||
max: 'auto',
|
||||
}}
|
||||
crosshairType="x"
|
||||
axisLeft={false}
|
||||
axisBottom={false}
|
||||
enableGridX={false}
|
||||
enableGridY={false}
|
||||
enablePoints={false}
|
||||
xFormat={(x) => {
|
||||
if (interval === 1 || interval === 7) {
|
||||
const hoursAgo = subHours(Date.now(), 24 * interval);
|
||||
return dateFormat(addHours(hoursAgo, x), 'D MMM HH:00');
|
||||
}
|
||||
|
||||
const daysAgo = subDays(Date.now(), interval - 1);
|
||||
return dateFormat(addDays(daysAgo, x), 'D MMM YYYY');
|
||||
}}
|
||||
yFormat={(y) => round(y, 2)}
|
||||
sliceTooltip={(slice) => {
|
||||
const { xFormatted, yFormatted } = slice.slice.points[0].data;
|
||||
return <div className="line__tooltip">
|
||||
<span className="line__tooltip-text">
|
||||
<strong>{yFormatted}</strong>
|
||||
<br />
|
||||
<small>{xFormatted}</small>
|
||||
</span>
|
||||
</div>;
|
||||
}}
|
||||
/>;
|
||||
};
|
||||
|
||||
Line.propTypes = {
|
||||
data: PropTypes.array.isRequired,
|
||||
color: PropTypes.string.isRequired,
|
||||
color: PropTypes.string,
|
||||
width: PropTypes.number,
|
||||
height: PropTypes.number,
|
||||
};
|
||||
|
||||
export default Line;
|
||||
|
||||
@@ -13452,10 +13452,8 @@ a.icon:hover {
|
||||
|
||||
.card-chart-bg {
|
||||
height: 4rem;
|
||||
margin-top: -1rem;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.card-options {
|
||||
|
||||
@@ -279,6 +279,10 @@ export const SERVICES = [
|
||||
id: 'tiktok',
|
||||
name: 'TikTok',
|
||||
},
|
||||
{
|
||||
id: 'qq',
|
||||
name: 'QQ',
|
||||
},
|
||||
];
|
||||
|
||||
export const SERVICES_ID_NAME_MAP = SERVICES.reduce((acc, { id, name }) => {
|
||||
@@ -471,6 +475,7 @@ export const BLOCK_ACTIONS = {
|
||||
export const SCHEME_TO_PROTOCOL_MAP = {
|
||||
doh: 'dns_over_https',
|
||||
dot: 'dns_over_tls',
|
||||
doq: 'dns_over_quic',
|
||||
'': 'plain_dns',
|
||||
};
|
||||
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
import 'url-polyfill';
|
||||
import dateParse from 'date-fns/parse';
|
||||
import dateFormat from 'date-fns/format';
|
||||
import subHours from 'date-fns/sub_hours';
|
||||
import addHours from 'date-fns/add_hours';
|
||||
import addDays from 'date-fns/add_days';
|
||||
import subDays from 'date-fns/sub_days';
|
||||
import round from 'lodash/round';
|
||||
import axios from 'axios';
|
||||
import i18n from 'i18next';
|
||||
@@ -105,21 +101,10 @@ export const normalizeLogs = (logs) => logs.map((log) => {
|
||||
};
|
||||
});
|
||||
|
||||
export const normalizeHistory = (history, interval) => {
|
||||
if (interval === 1 || interval === 7) {
|
||||
const hoursAgo = subHours(Date.now(), 24 * interval);
|
||||
return history.map((item, index) => ({
|
||||
x: dateFormat(addHours(hoursAgo, index), 'D MMM HH:00'),
|
||||
y: round(item, 2),
|
||||
}));
|
||||
}
|
||||
|
||||
const daysAgo = subDays(Date.now(), interval - 1);
|
||||
return history.map((item, index) => ({
|
||||
x: dateFormat(addDays(daysAgo, index), 'D MMM YYYY'),
|
||||
y: round(item, 2),
|
||||
}));
|
||||
};
|
||||
export const normalizeHistory = (history) => history.map((item, idx) => ({
|
||||
x: idx,
|
||||
y: item,
|
||||
}));
|
||||
|
||||
export const normalizeTopStats = (stats) => (
|
||||
stats.map((item) => ({
|
||||
|
||||
@@ -17,7 +17,7 @@ export const getTextareaCommentsHighlight = (
|
||||
) => {
|
||||
const renderLine = (line, idx) => renderHighlightedLine(line, idx, commentLineTokens);
|
||||
|
||||
return <code className={classnames('text-output', className)} ref={ref}>{lines?.split('\n').map(renderLine)}</code>;
|
||||
return <code className={classnames('text-output font-monospace', className)} ref={ref}>{lines?.split('\n').map(renderLine)}</code>;
|
||||
};
|
||||
|
||||
export const syncScroll = (e, ref) => {
|
||||
|
||||
1
client/webpack.dev.js
vendored
1
client/webpack.dev.js
vendored
@@ -44,7 +44,6 @@ const getDevServerConfig = (proxyUrl = BASE_URL) => {
|
||||
proxy: {
|
||||
[proxyUrl]: `http://${devServerHost}:${port}`,
|
||||
},
|
||||
open: true,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -1,215 +0,0 @@
|
||||
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
|
||||
|
||||
package dhcpd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/dhcpd/nclient4"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/insomniacslk/dhcp/dhcpv4"
|
||||
"github.com/insomniacslk/dhcp/dhcpv6"
|
||||
"github.com/insomniacslk/dhcp/dhcpv6/nclient6"
|
||||
"github.com/insomniacslk/dhcp/iana"
|
||||
)
|
||||
|
||||
// CheckIfOtherDHCPServersPresentV4 sends a DHCP request to the specified network interface,
|
||||
// and waits for a response for a period defined by defaultDiscoverTime
|
||||
func CheckIfOtherDHCPServersPresentV4(ifaceName string) (bool, error) {
|
||||
iface, err := net.InterfaceByName(ifaceName)
|
||||
if err != nil {
|
||||
return false, wrapErrPrint(err, "Couldn't find interface by name %s", ifaceName)
|
||||
}
|
||||
|
||||
// get ipv4 address of an interface
|
||||
ifaceIPNet := getIfaceIPv4(*iface)
|
||||
if len(ifaceIPNet) == 0 {
|
||||
return false, fmt.Errorf("couldn't find IPv4 address of interface %s %+v", ifaceName, iface)
|
||||
}
|
||||
|
||||
if runtime.GOOS == "darwin" {
|
||||
return false, fmt.Errorf("can't find DHCP server: not supported on macOS")
|
||||
}
|
||||
|
||||
srcIP := ifaceIPNet[0]
|
||||
src := net.JoinHostPort(srcIP.String(), "68")
|
||||
dst := "255.255.255.255:67"
|
||||
|
||||
hostname, _ := os.Hostname()
|
||||
|
||||
req, err := dhcpv4.NewDiscovery(iface.HardwareAddr)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("dhcpv4.NewDiscovery: %s", err)
|
||||
}
|
||||
req.Options.Update(dhcpv4.OptClientIdentifier(iface.HardwareAddr))
|
||||
req.Options.Update(dhcpv4.OptHostName(hostname))
|
||||
|
||||
// resolve 0.0.0.0:68
|
||||
udpAddr, err := net.ResolveUDPAddr("udp4", src)
|
||||
if err != nil {
|
||||
return false, wrapErrPrint(err, "Couldn't resolve UDP address %s", src)
|
||||
}
|
||||
|
||||
if !udpAddr.IP.To4().Equal(srcIP) {
|
||||
return false, wrapErrPrint(err, "Resolved UDP address is not %s", src)
|
||||
}
|
||||
|
||||
// resolve 255.255.255.255:67
|
||||
dstAddr, err := net.ResolveUDPAddr("udp4", dst)
|
||||
if err != nil {
|
||||
return false, wrapErrPrint(err, "Couldn't resolve UDP address %s", dst)
|
||||
}
|
||||
|
||||
// bind to 0.0.0.0:68
|
||||
log.Tracef("Listening to udp4 %+v", udpAddr)
|
||||
c, err := nclient4.NewRawUDPConn(ifaceName, 68)
|
||||
if err != nil {
|
||||
return false, wrapErrPrint(err, "Couldn't listen on :68")
|
||||
}
|
||||
if c != nil {
|
||||
defer c.Close()
|
||||
}
|
||||
|
||||
// send to 255.255.255.255:67
|
||||
_, err = c.WriteTo(req.ToBytes(), dstAddr)
|
||||
if err != nil {
|
||||
return false, wrapErrPrint(err, "Couldn't send a packet to %s", dst)
|
||||
}
|
||||
|
||||
for {
|
||||
// wait for answer
|
||||
log.Tracef("Waiting %v for an answer", defaultDiscoverTime)
|
||||
// TODO: replicate dhclient's behaviour of retrying several times with progressively bigger timeouts
|
||||
b := make([]byte, 1500)
|
||||
_ = c.SetReadDeadline(time.Now().Add(defaultDiscoverTime))
|
||||
n, _, err := c.ReadFrom(b)
|
||||
if isTimeout(err) {
|
||||
// timed out -- no DHCP servers
|
||||
log.Debug("DHCPv4: didn't receive DHCP response")
|
||||
return false, nil
|
||||
}
|
||||
if err != nil {
|
||||
return false, wrapErrPrint(err, "Couldn't receive packet")
|
||||
}
|
||||
|
||||
log.Tracef("Received packet (%v bytes)", n)
|
||||
|
||||
response, err := dhcpv4.FromBytes(b[:n])
|
||||
if err != nil {
|
||||
log.Debug("DHCPv4: dhcpv4.FromBytes: %s", err)
|
||||
continue
|
||||
}
|
||||
|
||||
log.Debug("DHCPv4: received message from server: %s", response.Summary())
|
||||
|
||||
if !(response.OpCode == dhcpv4.OpcodeBootReply &&
|
||||
response.HWType == iana.HWTypeEthernet &&
|
||||
bytes.Equal(response.ClientHWAddr, iface.HardwareAddr) &&
|
||||
bytes.Equal(response.TransactionID[:], req.TransactionID[:]) &&
|
||||
response.Options.Has(dhcpv4.OptionDHCPMessageType)) {
|
||||
log.Debug("DHCPv4: received message from server doesn't match our request")
|
||||
continue
|
||||
}
|
||||
|
||||
log.Tracef("The packet is from an active DHCP server")
|
||||
// that's a DHCP server there
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
// CheckIfOtherDHCPServersPresentV6 sends a DHCP request to the specified network interface,
|
||||
// and waits for a response for a period defined by defaultDiscoverTime
|
||||
func CheckIfOtherDHCPServersPresentV6(ifaceName string) (bool, error) {
|
||||
iface, err := net.InterfaceByName(ifaceName)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("DHCPv6: net.InterfaceByName: %s: %s", ifaceName, err)
|
||||
}
|
||||
|
||||
ifaceIPNet := getIfaceIPv6(*iface)
|
||||
if len(ifaceIPNet) == 0 {
|
||||
return false, fmt.Errorf("DHCPv6: couldn't find IPv6 address of interface %s %+v", ifaceName, iface)
|
||||
}
|
||||
|
||||
srcIP := ifaceIPNet[0]
|
||||
src := net.JoinHostPort(srcIP.String(), "546")
|
||||
dst := "[ff02::1:2]:547"
|
||||
|
||||
req, err := dhcpv6.NewSolicit(iface.HardwareAddr)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("DHCPv6: dhcpv6.NewSolicit: %s", err)
|
||||
}
|
||||
|
||||
udpAddr, err := net.ResolveUDPAddr("udp6", src)
|
||||
if err != nil {
|
||||
return false, wrapErrPrint(err, "DHCPv6: Couldn't resolve UDP address %s", src)
|
||||
}
|
||||
|
||||
if !udpAddr.IP.To16().Equal(srcIP) {
|
||||
return false, wrapErrPrint(err, "DHCPv6: Resolved UDP address is not %s", src)
|
||||
}
|
||||
|
||||
dstAddr, err := net.ResolveUDPAddr("udp6", dst)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("DHCPv6: Couldn't resolve UDP address %s: %s", dst, err)
|
||||
}
|
||||
|
||||
log.Debug("DHCPv6: Listening to udp6 %+v", udpAddr)
|
||||
c, err := nclient6.NewIPv6UDPConn(ifaceName, dhcpv6.DefaultClientPort)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("DHCPv6: Couldn't listen on :546: %s", err)
|
||||
}
|
||||
if c != nil {
|
||||
defer c.Close()
|
||||
}
|
||||
|
||||
_, err = c.WriteTo(req.ToBytes(), dstAddr)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("DHCPv6: Couldn't send a packet to %s: %s", dst, err)
|
||||
}
|
||||
|
||||
for {
|
||||
log.Debug("DHCPv6: Waiting %v for an answer", defaultDiscoverTime)
|
||||
b := make([]byte, 4096)
|
||||
_ = c.SetReadDeadline(time.Now().Add(defaultDiscoverTime))
|
||||
n, _, err := c.ReadFrom(b)
|
||||
if isTimeout(err) {
|
||||
log.Debug("DHCPv6: didn't receive DHCP response")
|
||||
return false, nil
|
||||
}
|
||||
if err != nil {
|
||||
return false, wrapErrPrint(err, "Couldn't receive packet")
|
||||
}
|
||||
|
||||
log.Debug("DHCPv6: Received packet (%v bytes)", n)
|
||||
|
||||
resp, err := dhcpv6.FromBytes(b[:n])
|
||||
if err != nil {
|
||||
log.Debug("DHCPv6: dhcpv6.FromBytes: %s", err)
|
||||
continue
|
||||
}
|
||||
|
||||
log.Debug("DHCPv6: received message from server: %s", resp.Summary())
|
||||
|
||||
cid := req.Options.ClientID()
|
||||
msg, err := resp.GetInnerMessage()
|
||||
if err != nil {
|
||||
log.Debug("DHCPv6: resp.GetInnerMessage: %s", err)
|
||||
continue
|
||||
}
|
||||
rcid := msg.Options.ClientID()
|
||||
if resp.Type() == dhcpv6.MessageTypeAdvertise &&
|
||||
msg.TransactionID == req.TransactionID &&
|
||||
rcid != nil &&
|
||||
cid.Equal(*rcid) {
|
||||
log.Debug("DHCPv6: The packet is from an active DHCP server")
|
||||
return true, nil
|
||||
}
|
||||
|
||||
log.Debug("DHCPv6: received message from server doesn't match our request")
|
||||
}
|
||||
}
|
||||
27
go.mod
27
go.mod
@@ -3,34 +3,35 @@ module github.com/AdguardTeam/AdGuardHome
|
||||
go 1.14
|
||||
|
||||
require (
|
||||
github.com/AdguardTeam/dnsproxy v0.32.6
|
||||
github.com/AdguardTeam/golibs v0.4.2
|
||||
github.com/AdguardTeam/urlfilter v0.12.2
|
||||
github.com/AdguardTeam/dnsproxy v0.33.2
|
||||
github.com/AdguardTeam/golibs v0.4.3
|
||||
github.com/AdguardTeam/urlfilter v0.12.3
|
||||
github.com/NYTimes/gziphandler v1.1.1
|
||||
github.com/beefsack/go-rate v0.0.0-20200827232406-6cde80facd47 // indirect
|
||||
github.com/fsnotify/fsnotify v1.4.9
|
||||
github.com/gobuffalo/packr v1.30.1
|
||||
github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714
|
||||
github.com/insomniacslk/dhcp v0.0.0-20200621044212-d74cd86ad5b8
|
||||
github.com/joomcode/errorx v1.0.3
|
||||
github.com/joomcode/errorx v1.0.3 // indirect
|
||||
github.com/kardianos/service v1.1.0
|
||||
github.com/marten-seemann/qtls-go1-15 v0.1.1 // indirect
|
||||
github.com/mdlayher/ethernet v0.0.0-20190606142754-0394541c37b7
|
||||
github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065
|
||||
github.com/miekg/dns v1.1.31
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/miekg/dns v1.1.35
|
||||
github.com/rogpeppe/go-internal v1.5.2 // indirect
|
||||
github.com/satori/go.uuid v1.2.0
|
||||
github.com/sirupsen/logrus v1.6.0 // indirect
|
||||
github.com/sparrc/go-ping v0.0.0-20190613174326-4e5b6552494c
|
||||
github.com/stretchr/testify v1.5.1
|
||||
github.com/stretchr/testify v1.6.1
|
||||
github.com/u-root/u-root v6.0.0+incompatible
|
||||
go.etcd.io/bbolt v1.3.4
|
||||
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a
|
||||
golang.org/x/net v0.0.0-20200904194848-62affa334b73
|
||||
golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20
|
||||
golang.org/x/text v0.3.3 // indirect
|
||||
google.golang.org/protobuf v1.25.0 // indirect
|
||||
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 // indirect
|
||||
golang.org/x/sys v0.0.0-20201109165425-215b40eba54c
|
||||
golang.org/x/text v0.3.4 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0
|
||||
gopkg.in/yaml.v2 v2.3.0
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 // indirect
|
||||
howett.net/plist v0.0.0-20200419221736-3b63eb3a43b5
|
||||
)
|
||||
|
||||
58
go.sum
58
go.sum
@@ -7,14 +7,16 @@ dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBr
|
||||
dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4=
|
||||
dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
|
||||
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
|
||||
github.com/AdguardTeam/dnsproxy v0.32.6 h1:HCKQFUvyvIP3lrJxP8t8+mE/DJwLk2HDmzI/af5PUu4=
|
||||
github.com/AdguardTeam/dnsproxy v0.32.6/go.mod h1:ZLDrKIypYxBDz2N9FQHgeehuHrwTbuhZXdGwNySshbw=
|
||||
github.com/AdguardTeam/dnsproxy v0.33.2 h1:k5aMcsw3TA/G2DR8EjIkwutDPuuRkKh8xij4cFWC6Fk=
|
||||
github.com/AdguardTeam/dnsproxy v0.33.2/go.mod h1:kLi6lMpErnZThy5haiRSis4q0KTB8uPWO4JQsU1EDJA=
|
||||
github.com/AdguardTeam/golibs v0.4.0/go.mod h1:skKsDKIBB7kkFflLJBpfGX+G8QFTx0WKUzB6TIgtUj4=
|
||||
github.com/AdguardTeam/golibs v0.4.2 h1:7M28oTZFoFwNmp8eGPb3ImmYbxGaJLyQXeIFVHjME0o=
|
||||
github.com/AdguardTeam/golibs v0.4.2/go.mod h1:skKsDKIBB7kkFflLJBpfGX+G8QFTx0WKUzB6TIgtUj4=
|
||||
github.com/AdguardTeam/golibs v0.4.3 h1:nXTLLLlIyU4BSRF0An5azS0uimSK/YpIMOBAO0/v1RY=
|
||||
github.com/AdguardTeam/golibs v0.4.3/go.mod h1:skKsDKIBB7kkFflLJBpfGX+G8QFTx0WKUzB6TIgtUj4=
|
||||
github.com/AdguardTeam/gomitmproxy v0.2.0/go.mod h1:Qdv0Mktnzer5zpdpi5rAwixNJzW2FN91LjKJCkVbYGU=
|
||||
github.com/AdguardTeam/urlfilter v0.12.2 h1:5ZkH/+AWNBK8cCfbcgOL1MIrpPJ2NJXDs00KjYJr2WE=
|
||||
github.com/AdguardTeam/urlfilter v0.12.2/go.mod h1:1fcCQx5TGJANrQN6sHNNM9KPBl7qx7BJml45ko6vru0=
|
||||
github.com/AdguardTeam/urlfilter v0.12.3 h1:FMjQG0eTgrr8xA3z2zaLVcCgGdpzoECPGWwgPjtwPNs=
|
||||
github.com/AdguardTeam/urlfilter v0.12.3/go.mod h1:1fcCQx5TGJANrQN6sHNNM9KPBl7qx7BJml45ko6vru0=
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I=
|
||||
@@ -25,10 +27,12 @@ github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmH
|
||||
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA=
|
||||
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 h1:52m0LGchQBBVqJRyYYufQuIbVqRawmubW3OFGqK1ekw=
|
||||
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635/go.mod h1:lmLxL+FV291OopO93Bwf9fQLQeLyt33VJRUg5VJ30us=
|
||||
github.com/ameshkov/dnscrypt v1.1.0 h1:2vAt5dD6ZmqlAxEAfzRcLBnkvdf8NI46Kn9InSwQbSI=
|
||||
github.com/ameshkov/dnscrypt v1.1.0/go.mod h1:ikduAxNLCTEfd1AaCgpIA5TgroIVQ8JY3Vb095fiFJg=
|
||||
github.com/ameshkov/dnscrypt/v2 v2.0.0 h1:i83G8MeGLrAFgUL8GSu98TVhtFDEifF7SIS7Qi/RZ3U=
|
||||
github.com/ameshkov/dnscrypt/v2 v2.0.0/go.mod h1:nbZnxJt4edIPx2Haa8n2XtC2g5AWcsdQiSuXkNH8eDI=
|
||||
github.com/ameshkov/dnsstamps v1.0.1 h1:LhGvgWDzhNJh+kBQd/AfUlq1vfVe109huiXw4JhnPug=
|
||||
github.com/ameshkov/dnsstamps v1.0.1/go.mod h1:Ii3eUu73dx4Vw5O4wjzmT5+lkCwovjzaEZZ4gKyIH5A=
|
||||
github.com/ameshkov/dnsstamps v1.0.3 h1:Srzik+J9mivH1alRACTbys2xOxs0lRH9qnTA7Y1OYVo=
|
||||
github.com/ameshkov/dnsstamps v1.0.3/go.mod h1:Ii3eUu73dx4Vw5O4wjzmT5+lkCwovjzaEZZ4gKyIH5A=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/beefsack/go-rate v0.0.0-20180408011153-efa7637bb9b6 h1:KXlsf+qt/X5ttPGEjR0tPH1xaWWoKBEg9Q1THAj2h3I=
|
||||
@@ -98,6 +102,8 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
|
||||
@@ -152,8 +158,8 @@ github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/lucas-clemente/quic-go v0.18.0 h1:JhQDdqxdwdmGdKsKgXi1+coHRoGhvU6z0rNzOJqZ/4o=
|
||||
github.com/lucas-clemente/quic-go v0.18.0/go.mod h1:yXttHsSNxQi8AWijC/vLP+OJczXqzHSOcJrM5ITUlCg=
|
||||
github.com/lucas-clemente/quic-go v0.18.1 h1:DMR7guC0NtVS8zNZR3IO7NARZvZygkSC56GGtC6cyys=
|
||||
github.com/lucas-clemente/quic-go v0.18.1/go.mod h1:yXttHsSNxQi8AWijC/vLP+OJczXqzHSOcJrM5ITUlCg=
|
||||
github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
@@ -173,8 +179,9 @@ github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZ
|
||||
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
|
||||
github.com/miekg/dns v1.1.29 h1:xHBEhR+t5RzcFJjBLJlax2daXOrTYtr9z4WdKEfWFzg=
|
||||
github.com/miekg/dns v1.1.29/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
||||
github.com/miekg/dns v1.1.31 h1:sJFOl9BgwbYAWOGEwr61FU28pqsBNdpRBnhGXtO06Oo=
|
||||
github.com/miekg/dns v1.1.31/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
||||
github.com/miekg/dns v1.1.34/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
||||
github.com/miekg/dns v1.1.35 h1:oTfOaDH+mZkdcgdIjH6yBajRGtIwcwcaR+rt23ZSrJs=
|
||||
github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
@@ -209,6 +216,8 @@ github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R
|
||||
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.5.2 h1:qLvObTrvO/XRCqmkKxUlOBc48bI3efyDuAZe25QiF0w=
|
||||
github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
@@ -261,6 +270,8 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
|
||||
github.com/u-root/u-root v6.0.0+incompatible h1:YqPGmRoRyYmeg17KIWFRSyVq6LX5T6GSzawyA6wG6EE=
|
||||
github.com/u-root/u-root v6.0.0+incompatible/go.mod h1:RYkpo8pTHrNjW08opNd/U6p/RJE7K0D8fXO0d47+3YY=
|
||||
@@ -284,12 +295,13 @@ golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPh
|
||||
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a h1:vclmkQCjlDX5OydZ9wv8rBCcS0QyQY66Mpf/7BZbInM=
|
||||
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E=
|
||||
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3 h1:XQyxROzUlZH+WIQwySDgnISgOivlhjIEwaQaJEJrrN0=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -312,8 +324,9 @@ golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU=
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200904194848-62affa334b73 h1:MXfv8rhZWmFeqX3GNZRsd6vOLoaCHjYEX3qkRo3YBUA=
|
||||
golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201016165138-7b1cca2348c0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME=
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
@@ -327,6 +340,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a h1:WXEvlFVvvGxCJLG6REjsT03iWnKLEWinaScsxF2Vm2o=
|
||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -351,8 +366,10 @@ golang.org/x/sys v0.0.0-20200331124033-c3d80250170d h1:nc5K6ox/4lTFbMVSL9WRR81ix
|
||||
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299 h1:DYfZAGf2WMFjMxbgTjaC+2HC7NkNAQs+6Q8b9WEB/F4=
|
||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20 h1:4X356008q5SA3YXu8PiRap39KFmy4Lf6sGlceJKZQsU=
|
||||
golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201109165425-215b40eba54c h1:+B+zPA6081G5cEb2triOIJpcvSW4AYzmIyWAqMn2JAc=
|
||||
golang.org/x/sys v0.0.0-20201109165425-215b40eba54c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -360,6 +377,8 @@ golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
@@ -376,6 +395,8 @@ golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapK
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||
google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||
google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=
|
||||
@@ -427,6 +448,11 @@ gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c h1:grhR+C34yXImVGp7EzNk+DTIk+323eIUWOmEevy6bDo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
|
||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
||||
67
internal/agherr/agherr.go
Normal file
67
internal/agherr/agherr.go
Normal file
@@ -0,0 +1,67 @@
|
||||
// Package agherr contains the extended error type, and the function for
|
||||
// wrapping several errors.
|
||||
package agherr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Error is the constant error type.
|
||||
type Error string
|
||||
|
||||
// Error implements the error interface for Error.
|
||||
func (err Error) Error() (msg string) {
|
||||
return string(err)
|
||||
}
|
||||
|
||||
// manyError is an error containing several wrapped errors. It is created to be
|
||||
// a simpler version of the API provided by github.com/joomcode/errorx.
|
||||
type manyError struct {
|
||||
message string
|
||||
underlying []error
|
||||
}
|
||||
|
||||
// Many wraps several errors and returns a single error.
|
||||
func Many(message string, underlying ...error) error {
|
||||
err := &manyError{
|
||||
message: message,
|
||||
underlying: underlying,
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Error implements the error interface for *manyError.
|
||||
func (e *manyError) Error() string {
|
||||
switch len(e.underlying) {
|
||||
case 0:
|
||||
return e.message
|
||||
case 1:
|
||||
return fmt.Sprintf("%s: %s", e.message, e.underlying[0])
|
||||
default:
|
||||
b := &strings.Builder{}
|
||||
|
||||
// Ignore errors, since strings.(*Buffer).Write never returns
|
||||
// errors.
|
||||
_, _ = fmt.Fprintf(b, "%s: %s (hidden: %s", e.message, e.underlying[0], e.underlying[1])
|
||||
for _, u := range e.underlying[2:] {
|
||||
// See comment above.
|
||||
_, _ = fmt.Fprintf(b, ", %s", u)
|
||||
}
|
||||
|
||||
// See comment above.
|
||||
_, _ = b.WriteString(")")
|
||||
|
||||
return b.String()
|
||||
}
|
||||
}
|
||||
|
||||
// Unwrap implements the hidden errors.wrapper interface for *manyError.
|
||||
func (e *manyError) Unwrap() error {
|
||||
if len(e.underlying) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return e.underlying[0]
|
||||
}
|
||||
78
internal/agherr/agherr_test.go
Normal file
78
internal/agherr/agherr_test.go
Normal file
@@ -0,0 +1,78 @@
|
||||
package agherr
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/testutil"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
testutil.DiscardLogOutput(m)
|
||||
}
|
||||
|
||||
func TestError_Error(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
want string
|
||||
err error
|
||||
}{{
|
||||
name: "simple",
|
||||
want: "a",
|
||||
err: Many("a"),
|
||||
}, {
|
||||
name: "wrapping",
|
||||
want: "a: b",
|
||||
err: Many("a", errors.New("b")),
|
||||
}, {
|
||||
name: "wrapping several",
|
||||
want: "a: b (hidden: c, d)",
|
||||
err: Many("a", errors.New("b"), errors.New("c"), errors.New("d")),
|
||||
}, {
|
||||
name: "wrapping wrapper",
|
||||
want: "a: b: c (hidden: d)",
|
||||
err: Many("a", Many("b", errors.New("c"), errors.New("d"))),
|
||||
}}
|
||||
for _, tc := range testCases {
|
||||
assert.Equal(t, tc.want, tc.err.Error(), tc.name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestError_Unwrap(t *testing.T) {
|
||||
const (
|
||||
errSimple = iota
|
||||
errWrapped
|
||||
errNil
|
||||
)
|
||||
errs := []error{
|
||||
errSimple: errors.New("a"),
|
||||
errWrapped: fmt.Errorf("err: %w", errors.New("nested")),
|
||||
errNil: nil,
|
||||
}
|
||||
testCases := []struct {
|
||||
name string
|
||||
want error
|
||||
wrapped error
|
||||
}{{
|
||||
name: "simple",
|
||||
want: errs[errSimple],
|
||||
wrapped: Many("a", errs[errSimple]),
|
||||
}, {
|
||||
name: "nested",
|
||||
want: errs[errWrapped],
|
||||
wrapped: Many("b", errs[errWrapped]),
|
||||
}, {
|
||||
name: "nil passed",
|
||||
want: errs[errNil],
|
||||
wrapped: Many("c", errs[errNil]),
|
||||
}, {
|
||||
name: "nil not passed",
|
||||
want: nil,
|
||||
wrapped: Many("d"),
|
||||
}}
|
||||
for _, tc := range testCases {
|
||||
assert.Equal(t, tc.want, errors.Unwrap(tc.wrapped), tc.name)
|
||||
}
|
||||
}
|
||||
284
internal/dhcpd/check_other_dhcp.go
Normal file
284
internal/dhcpd/check_other_dhcp.go
Normal file
@@ -0,0 +1,284 @@
|
||||
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
|
||||
|
||||
package dhcpd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/dhcpd/nclient4"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/insomniacslk/dhcp/dhcpv4"
|
||||
"github.com/insomniacslk/dhcp/dhcpv6"
|
||||
"github.com/insomniacslk/dhcp/dhcpv6/nclient6"
|
||||
"github.com/insomniacslk/dhcp/iana"
|
||||
)
|
||||
|
||||
// CheckIfOtherDHCPServersPresentV4 sends a DHCP request to the specified network interface,
|
||||
// and waits for a response for a period defined by defaultDiscoverTime
|
||||
func CheckIfOtherDHCPServersPresentV4(ifaceName string) (bool, error) {
|
||||
iface, err := net.InterfaceByName(ifaceName)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("couldn't find interface by name %s: %w", ifaceName, err)
|
||||
}
|
||||
|
||||
ifaceIPNet, err := ifaceIPv4Addrs(iface)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("getting ipv4 addrs for iface %s: %w", ifaceName, err)
|
||||
}
|
||||
if len(ifaceIPNet) == 0 {
|
||||
return false, fmt.Errorf("interface %s has no ipv4 addresses", ifaceName)
|
||||
}
|
||||
|
||||
// TODO(a.garipov): Find out what this is about. Perhaps this
|
||||
// information is outdated or at least incomplete.
|
||||
if runtime.GOOS == "darwin" {
|
||||
return false, fmt.Errorf("can't find DHCP server: not supported on macOS")
|
||||
}
|
||||
|
||||
srcIP := ifaceIPNet[0]
|
||||
src := net.JoinHostPort(srcIP.String(), "68")
|
||||
dst := "255.255.255.255:67"
|
||||
|
||||
hostname, _ := os.Hostname()
|
||||
|
||||
req, err := dhcpv4.NewDiscovery(iface.HardwareAddr)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("dhcpv4.NewDiscovery: %w", err)
|
||||
}
|
||||
req.Options.Update(dhcpv4.OptClientIdentifier(iface.HardwareAddr))
|
||||
req.Options.Update(dhcpv4.OptHostName(hostname))
|
||||
|
||||
// resolve 0.0.0.0:68
|
||||
udpAddr, err := net.ResolveUDPAddr("udp4", src)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("couldn't resolve UDP address %s: %w", src, err)
|
||||
}
|
||||
|
||||
if !udpAddr.IP.To4().Equal(srcIP) {
|
||||
return false, fmt.Errorf("resolved UDP address is not %s: %w", src, err)
|
||||
}
|
||||
|
||||
// resolve 255.255.255.255:67
|
||||
dstAddr, err := net.ResolveUDPAddr("udp4", dst)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("couldn't resolve UDP address %s: %w", dst, err)
|
||||
}
|
||||
|
||||
// bind to 0.0.0.0:68
|
||||
log.Tracef("Listening to udp4 %+v", udpAddr)
|
||||
c, err := nclient4.NewRawUDPConn(ifaceName, 68)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("couldn't listen on :68: %w", err)
|
||||
}
|
||||
if c != nil {
|
||||
defer c.Close()
|
||||
}
|
||||
|
||||
// send to 255.255.255.255:67
|
||||
_, err = c.WriteTo(req.ToBytes(), dstAddr)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("couldn't send a packet to %s: %w", dst, err)
|
||||
}
|
||||
|
||||
for {
|
||||
ok, next, err := tryConn4(req, c, iface)
|
||||
if next {
|
||||
if err != nil {
|
||||
log.Debug("dhcpv4: trying a connection: %s", err)
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
if ok {
|
||||
return true, nil
|
||||
}
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(a.garipov): Refactor further. Inspect error handling, remove parameter
|
||||
// next, address the TODO, merge with tryConn6, etc.
|
||||
func tryConn4(req *dhcpv4.DHCPv4, c net.PacketConn, iface *net.Interface) (ok, next bool, err error) {
|
||||
// TODO: replicate dhclient's behavior of retrying several times with
|
||||
// progressively longer timeouts.
|
||||
log.Tracef("dhcpv4: waiting %v for an answer", defaultDiscoverTime)
|
||||
|
||||
b := make([]byte, 1500)
|
||||
err = c.SetDeadline(time.Now().Add(defaultDiscoverTime))
|
||||
if err != nil {
|
||||
return false, false, fmt.Errorf("setting deadline: %w", err)
|
||||
}
|
||||
|
||||
n, _, err := c.ReadFrom(b)
|
||||
if err != nil {
|
||||
if isTimeout(err) {
|
||||
log.Debug("dhcpv4: didn't receive dhcp response")
|
||||
|
||||
return false, false, nil
|
||||
}
|
||||
|
||||
return false, false, fmt.Errorf("receiving packet: %w", err)
|
||||
}
|
||||
|
||||
log.Tracef("dhcpv4: received packet, %d bytes", n)
|
||||
|
||||
response, err := dhcpv4.FromBytes(b[:n])
|
||||
if err != nil {
|
||||
log.Debug("dhcpv4: encoding: %s", err)
|
||||
|
||||
return false, true, err
|
||||
}
|
||||
|
||||
log.Debug("dhcpv4: received message from server: %s", response.Summary())
|
||||
|
||||
if !(response.OpCode == dhcpv4.OpcodeBootReply &&
|
||||
response.HWType == iana.HWTypeEthernet &&
|
||||
bytes.Equal(response.ClientHWAddr, iface.HardwareAddr) &&
|
||||
bytes.Equal(response.TransactionID[:], req.TransactionID[:]) &&
|
||||
response.Options.Has(dhcpv4.OptionDHCPMessageType)) {
|
||||
|
||||
log.Debug("dhcpv4: received message from server doesn't match our request")
|
||||
|
||||
return false, true, nil
|
||||
}
|
||||
|
||||
log.Tracef("dhcpv4: the packet is from an active dhcp server")
|
||||
|
||||
return true, false, nil
|
||||
}
|
||||
|
||||
// CheckIfOtherDHCPServersPresentV6 sends a DHCP request to the specified network interface,
|
||||
// and waits for a response for a period defined by defaultDiscoverTime
|
||||
func CheckIfOtherDHCPServersPresentV6(ifaceName string) (bool, error) {
|
||||
iface, err := net.InterfaceByName(ifaceName)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("dhcpv6: net.InterfaceByName: %s: %w", ifaceName, err)
|
||||
}
|
||||
|
||||
ifaceIPNet, err := ifaceIPv6Addrs(iface)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("getting ipv6 addrs for iface %s: %w", ifaceName, err)
|
||||
}
|
||||
if len(ifaceIPNet) == 0 {
|
||||
return false, fmt.Errorf("interface %s has no ipv6 addresses", ifaceName)
|
||||
}
|
||||
|
||||
srcIP := ifaceIPNet[0]
|
||||
src := net.JoinHostPort(srcIP.String(), "546")
|
||||
dst := "[ff02::1:2]:547"
|
||||
|
||||
req, err := dhcpv6.NewSolicit(iface.HardwareAddr)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("dhcpv6: dhcpv6.NewSolicit: %w", err)
|
||||
}
|
||||
|
||||
udpAddr, err := net.ResolveUDPAddr("udp6", src)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("dhcpv6: Couldn't resolve UDP address %s: %w", src, err)
|
||||
}
|
||||
|
||||
if !udpAddr.IP.To16().Equal(srcIP) {
|
||||
return false, fmt.Errorf("dhcpv6: Resolved UDP address is not %s: %w", src, err)
|
||||
}
|
||||
|
||||
dstAddr, err := net.ResolveUDPAddr("udp6", dst)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("dhcpv6: Couldn't resolve UDP address %s: %w", dst, err)
|
||||
}
|
||||
|
||||
log.Debug("DHCPv6: Listening to udp6 %+v", udpAddr)
|
||||
c, err := nclient6.NewIPv6UDPConn(ifaceName, dhcpv6.DefaultClientPort)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("dhcpv6: Couldn't listen on :546: %w", err)
|
||||
}
|
||||
if c != nil {
|
||||
defer c.Close()
|
||||
}
|
||||
|
||||
_, err = c.WriteTo(req.ToBytes(), dstAddr)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("dhcpv6: Couldn't send a packet to %s: %w", dst, err)
|
||||
}
|
||||
|
||||
for {
|
||||
ok, next, err := tryConn6(req, c)
|
||||
if next {
|
||||
if err != nil {
|
||||
log.Debug("dhcpv6: trying a connection: %s", err)
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
if ok {
|
||||
return true, nil
|
||||
}
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(a.garipov): See the comment on tryConn4. Sigh…
|
||||
func tryConn6(req *dhcpv6.Message, c net.PacketConn) (ok, next bool, err error) {
|
||||
// TODO: replicate dhclient's behavior of retrying several times with
|
||||
// progressively longer timeouts.
|
||||
log.Tracef("dhcpv6: waiting %v for an answer", defaultDiscoverTime)
|
||||
|
||||
b := make([]byte, 4096)
|
||||
err = c.SetDeadline(time.Now().Add(defaultDiscoverTime))
|
||||
if err != nil {
|
||||
return false, false, fmt.Errorf("setting deadline: %w", err)
|
||||
}
|
||||
|
||||
n, _, err := c.ReadFrom(b)
|
||||
if err != nil {
|
||||
if isTimeout(err) {
|
||||
log.Debug("dhcpv6: didn't receive dhcp response")
|
||||
|
||||
return false, false, nil
|
||||
}
|
||||
|
||||
return false, false, fmt.Errorf("receiving packet: %w", err)
|
||||
}
|
||||
|
||||
log.Tracef("dhcpv6: received packet, %d bytes", n)
|
||||
|
||||
response, err := dhcpv6.FromBytes(b[:n])
|
||||
if err != nil {
|
||||
log.Debug("dhcpv6: encoding: %s", err)
|
||||
|
||||
return false, true, err
|
||||
}
|
||||
|
||||
log.Debug("dhcpv6: received message from server: %s", response.Summary())
|
||||
|
||||
cid := req.Options.ClientID()
|
||||
msg, err := response.GetInnerMessage()
|
||||
if err != nil {
|
||||
log.Debug("dhcpv6: resp.GetInnerMessage(): %s", err)
|
||||
|
||||
return false, true, err
|
||||
}
|
||||
|
||||
rcid := msg.Options.ClientID()
|
||||
if !(response.Type() == dhcpv6.MessageTypeAdvertise &&
|
||||
msg.TransactionID == req.TransactionID &&
|
||||
rcid != nil &&
|
||||
cid.Equal(*rcid)) {
|
||||
|
||||
log.Debug("dhcpv6: received message from server doesn't match our request")
|
||||
|
||||
return false, true, nil
|
||||
}
|
||||
|
||||
log.Tracef("dhcpv6: the packet is from an active dhcp server")
|
||||
|
||||
return true, false, nil
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
// Package dhcpd provides a DHCP server.
|
||||
package dhcpd
|
||||
|
||||
import (
|
||||
@@ -5,16 +6,19 @@ import (
|
||||
"net"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/util"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/util"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
)
|
||||
|
||||
const defaultDiscoverTime = time.Second * 3
|
||||
const leaseExpireStatic = 1
|
||||
const (
|
||||
defaultDiscoverTime = time.Second * 3
|
||||
leaseExpireStatic = 1
|
||||
)
|
||||
|
||||
var webHandlersRegistered = false
|
||||
|
||||
@@ -82,7 +86,8 @@ func (s *Server) CheckConfig(config ServerConfig) error {
|
||||
|
||||
// Create - create object
|
||||
func Create(config ServerConfig) *Server {
|
||||
s := Server{}
|
||||
s := &Server{}
|
||||
|
||||
s.conf.Enabled = config.Enabled
|
||||
s.conf.InterfaceName = config.InterfaceName
|
||||
s.conf.HTTPRegister = config.HTTPRegister
|
||||
@@ -90,8 +95,21 @@ func Create(config ServerConfig) *Server {
|
||||
s.conf.DBFilePath = filepath.Join(config.WorkDir, dbFilename)
|
||||
|
||||
if !webHandlersRegistered && s.conf.HTTPRegister != nil {
|
||||
if runtime.GOOS == "windows" {
|
||||
// Our DHCP server doesn't work on Windows yet, so
|
||||
// signal that to the front with an HTTP 501.
|
||||
//
|
||||
// TODO(a.garipov): This needs refactoring. We
|
||||
// shouldn't even try and initialize a DHCP server on
|
||||
// Windows, but there are currently too many
|
||||
// interconnected parts--such as HTTP handlers and
|
||||
// frontend--to make that work properly.
|
||||
s.registerNotImplementedHandlers()
|
||||
} else {
|
||||
s.registerHandlers()
|
||||
}
|
||||
|
||||
webHandlersRegistered = true
|
||||
s.registerHandlers()
|
||||
}
|
||||
|
||||
var err4, err6 error
|
||||
@@ -130,7 +148,7 @@ func Create(config ServerConfig) *Server {
|
||||
// we can't delay database loading until DHCP server is started,
|
||||
// because we need static leases functionality available beforehand
|
||||
s.dbLoad()
|
||||
return &s
|
||||
return s
|
||||
}
|
||||
|
||||
// server calls this function after DB is updated
|
||||
@@ -9,13 +9,12 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/testutil"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func check(t *testing.T, result bool, msg string) {
|
||||
if !result {
|
||||
t.Fatal(msg)
|
||||
}
|
||||
func TestMain(m *testing.M) {
|
||||
testutil.DiscardLogOutput(m)
|
||||
}
|
||||
|
||||
func testNotify(flags uint32) {
|
||||
@@ -83,7 +82,6 @@ func TestIsValidSubnetMask(t *testing.T) {
|
||||
func TestNormalizeLeases(t *testing.T) {
|
||||
dynLeases := []*Lease{}
|
||||
staticLeases := []*Lease{}
|
||||
leases := []*Lease{}
|
||||
|
||||
lease := &Lease{}
|
||||
lease.HWAddr = []byte{1, 2, 3, 4}
|
||||
@@ -100,7 +98,7 @@ func TestNormalizeLeases(t *testing.T) {
|
||||
lease.HWAddr = []byte{2, 2, 3, 4}
|
||||
staticLeases = append(staticLeases, lease)
|
||||
|
||||
leases = normalizeLeases(staticLeases, dynLeases)
|
||||
leases := normalizeLeases(staticLeases, dynLeases)
|
||||
|
||||
assert.True(t, len(leases) == 3)
|
||||
assert.True(t, bytes.Equal(leases[0].HWAddr, []byte{1, 2, 3, 4}))
|
||||
@@ -10,8 +10,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/util"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/util"
|
||||
"github.com/AdguardTeam/golibs/jsonutil"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
)
|
||||
@@ -175,7 +174,7 @@ func (s *Server) handleDHCPSetConfig(w http.ResponseWriter, r *http.Request) {
|
||||
v6conf.InterfaceName = newconfig.InterfaceName
|
||||
v6conf.notify = s.onNotify
|
||||
s6, err = v6Create(v6conf)
|
||||
if s6 == nil {
|
||||
if err != nil {
|
||||
httpError(r, w, http.StatusBadRequest, "Invalid DHCPv6 configuration: %s", err)
|
||||
return
|
||||
}
|
||||
@@ -302,17 +301,17 @@ func (s *Server) handleDHCPInterfaces(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *Server) handleDHCPFindActiveServer(w http.ResponseWriter, r *http.Request) {
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
errorText := fmt.Sprintf("failed to read request body: %s", err)
|
||||
log.Error(errorText)
|
||||
http.Error(w, errorText, http.StatusBadRequest)
|
||||
msg := fmt.Sprintf("failed to read request body: %s", err)
|
||||
log.Error(msg)
|
||||
http.Error(w, msg, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
interfaceName := strings.TrimSpace(string(body))
|
||||
if interfaceName == "" {
|
||||
errorText := fmt.Sprintf("empty interface name specified")
|
||||
log.Error(errorText)
|
||||
http.Error(w, errorText, http.StatusBadRequest)
|
||||
msg := "empty interface name specified"
|
||||
log.Error(msg)
|
||||
http.Error(w, msg, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -335,7 +334,7 @@ func (s *Server) handleDHCPFindActiveServer(w http.ResponseWriter, r *http.Reque
|
||||
foundVal := "no"
|
||||
if found4 {
|
||||
foundVal = "yes"
|
||||
} else if err != nil {
|
||||
} else if err4 != nil {
|
||||
foundVal = "error"
|
||||
othSrv["error"] = err4.Error()
|
||||
}
|
||||
@@ -370,7 +369,6 @@ func (s *Server) handleDHCPFindActiveServer(w http.ResponseWriter, r *http.Reque
|
||||
}
|
||||
|
||||
func (s *Server) handleDHCPAddStaticLease(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
lj := staticLeaseJSON{}
|
||||
err := json.NewDecoder(r.Body).Decode(&lj)
|
||||
if err != nil {
|
||||
@@ -424,7 +422,6 @@ func (s *Server) handleDHCPAddStaticLease(w http.ResponseWriter, r *http.Request
|
||||
}
|
||||
|
||||
func (s *Server) handleDHCPRemoveStaticLease(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
lj := staticLeaseJSON{}
|
||||
err := json.NewDecoder(r.Body).Decode(&lj)
|
||||
if err != nil {
|
||||
@@ -501,11 +498,48 @@ func (s *Server) handleReset(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func (s *Server) registerHandlers() {
|
||||
s.conf.HTTPRegister("GET", "/control/dhcp/status", s.handleDHCPStatus)
|
||||
s.conf.HTTPRegister("GET", "/control/dhcp/interfaces", s.handleDHCPInterfaces)
|
||||
s.conf.HTTPRegister("POST", "/control/dhcp/set_config", s.handleDHCPSetConfig)
|
||||
s.conf.HTTPRegister("POST", "/control/dhcp/find_active_dhcp", s.handleDHCPFindActiveServer)
|
||||
s.conf.HTTPRegister("POST", "/control/dhcp/add_static_lease", s.handleDHCPAddStaticLease)
|
||||
s.conf.HTTPRegister("POST", "/control/dhcp/remove_static_lease", s.handleDHCPRemoveStaticLease)
|
||||
s.conf.HTTPRegister("POST", "/control/dhcp/reset", s.handleReset)
|
||||
s.conf.HTTPRegister(http.MethodGet, "/control/dhcp/status", s.handleDHCPStatus)
|
||||
s.conf.HTTPRegister(http.MethodGet, "/control/dhcp/interfaces", s.handleDHCPInterfaces)
|
||||
s.conf.HTTPRegister(http.MethodPost, "/control/dhcp/set_config", s.handleDHCPSetConfig)
|
||||
s.conf.HTTPRegister(http.MethodPost, "/control/dhcp/find_active_dhcp", s.handleDHCPFindActiveServer)
|
||||
s.conf.HTTPRegister(http.MethodPost, "/control/dhcp/add_static_lease", s.handleDHCPAddStaticLease)
|
||||
s.conf.HTTPRegister(http.MethodPost, "/control/dhcp/remove_static_lease", s.handleDHCPRemoveStaticLease)
|
||||
s.conf.HTTPRegister(http.MethodPost, "/control/dhcp/reset", s.handleReset)
|
||||
}
|
||||
|
||||
// jsonError is a generic JSON error response.
|
||||
type jsonError struct {
|
||||
// Message is the error message, an opaque string.
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// notImplemented returns a handler that replies to any request with an HTTP 501
|
||||
// Not Implemented status and a JSON error with the provided message msg.
|
||||
//
|
||||
// TODO(a.garipov): Either take the logger from the server after we've
|
||||
// refactored logging or make this not a method of *Server.
|
||||
func (s *Server) notImplemented(msg string) (f func(http.ResponseWriter, *http.Request)) {
|
||||
return func(w http.ResponseWriter, _ *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusNotImplemented)
|
||||
|
||||
err := json.NewEncoder(w).Encode(&jsonError{
|
||||
Message: msg,
|
||||
})
|
||||
if err != nil {
|
||||
log.Debug("writing 501 json response: %s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) registerNotImplementedHandlers() {
|
||||
h := s.notImplemented("dhcp is not supported on windows")
|
||||
|
||||
s.conf.HTTPRegister(http.MethodGet, "/control/dhcp/status", h)
|
||||
s.conf.HTTPRegister(http.MethodGet, "/control/dhcp/interfaces", h)
|
||||
s.conf.HTTPRegister(http.MethodPost, "/control/dhcp/set_config", h)
|
||||
s.conf.HTTPRegister(http.MethodPost, "/control/dhcp/find_active_dhcp", h)
|
||||
s.conf.HTTPRegister(http.MethodPost, "/control/dhcp/add_static_lease", h)
|
||||
s.conf.HTTPRegister(http.MethodPost, "/control/dhcp/remove_static_lease", h)
|
||||
s.conf.HTTPRegister(http.MethodPost, "/control/dhcp/reset", h)
|
||||
}
|
||||
22
internal/dhcpd/dhcphttp_test.go
Normal file
22
internal/dhcpd/dhcphttp_test.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package dhcpd
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestServer_notImplemented(t *testing.T) {
|
||||
s := &Server{}
|
||||
h := s.notImplemented("never!")
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
r, err := http.NewRequest(http.MethodGet, "/unsupported", nil)
|
||||
assert.Nil(t, err)
|
||||
|
||||
h(w, r)
|
||||
assert.Equal(t, http.StatusNotImplemented, w.Code)
|
||||
assert.Equal(t, `{"message":"never!"}`+"\n", w.Body.String())
|
||||
}
|
||||
@@ -4,9 +4,6 @@ import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/joomcode/errorx"
|
||||
)
|
||||
|
||||
func isTimeout(err error) bool {
|
||||
@@ -17,37 +14,6 @@ func isTimeout(err error) bool {
|
||||
return operr.Timeout()
|
||||
}
|
||||
|
||||
// Get IPv4 address list
|
||||
func getIfaceIPv4(iface net.Interface) []net.IP {
|
||||
addrs, err := iface.Addrs()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var res []net.IP
|
||||
for _, a := range addrs {
|
||||
ipnet, ok := a.(*net.IPNet)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if ipnet.IP.To4() != nil {
|
||||
res = append(res, ipnet.IP.To4())
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func wrapErrPrint(err error, message string, args ...interface{}) error {
|
||||
var errx error
|
||||
if err == nil {
|
||||
errx = fmt.Errorf(message, args...)
|
||||
} else {
|
||||
errx = errorx.Decorate(err, message, args...)
|
||||
}
|
||||
log.Println(errx.Error())
|
||||
return errx
|
||||
}
|
||||
|
||||
func parseIPv4(text string) (net.IP, error) {
|
||||
result := net.ParseIP(text)
|
||||
if result == nil {
|
||||
@@ -48,14 +48,12 @@ const (
|
||||
ServerPort = 67
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultServers is the address of all link-local DHCP servers and
|
||||
// relay agents.
|
||||
DefaultServers = &net.UDPAddr{
|
||||
IP: net.IPv4bcast,
|
||||
Port: ServerPort,
|
||||
}
|
||||
)
|
||||
// DefaultServers is the address of all link-local DHCP servers and
|
||||
// relay agents.
|
||||
var DefaultServers = &net.UDPAddr{
|
||||
IP: net.IPv4bcast,
|
||||
Port: ServerPort,
|
||||
}
|
||||
|
||||
var (
|
||||
// ErrNoResponse is returned when no response packet is received.
|
||||
@@ -375,14 +373,6 @@ func WithHWAddr(hwAddr net.HardwareAddr) ClientOpt {
|
||||
}
|
||||
}
|
||||
|
||||
// nolint
|
||||
func withBufferCap(n int) ClientOpt {
|
||||
return func(c *Client) (err error) {
|
||||
c.bufferCap = n
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// WithRetry configures the number of retransmissions to attempt.
|
||||
//
|
||||
// Default is 3.
|
||||
@@ -17,11 +17,16 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/testutil"
|
||||
"github.com/hugelgupf/socketpair"
|
||||
"github.com/insomniacslk/dhcp/dhcpv4"
|
||||
"github.com/insomniacslk/dhcp/dhcpv4/server4"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
testutil.DiscardLogOutput(m)
|
||||
}
|
||||
|
||||
type handler struct {
|
||||
mu sync.Mutex
|
||||
received []*dhcpv4.DHCPv4
|
||||
@@ -122,6 +127,13 @@ func newPacket(op dhcpv4.OpcodeType, xid dhcpv4.TransactionID) *dhcpv4.DHCPv4 {
|
||||
return p
|
||||
}
|
||||
|
||||
func withBufferCap(n int) ClientOpt {
|
||||
return func(c *Client) (err error) {
|
||||
c.bufferCap = n
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func TestSendAndRead(t *testing.T) {
|
||||
for _, tt := range []struct {
|
||||
desc string
|
||||
@@ -10,14 +10,15 @@ import (
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/util"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/util"
|
||||
|
||||
"github.com/AdguardTeam/golibs/file"
|
||||
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
)
|
||||
|
||||
// Check if network interface has a static IP configured
|
||||
// HasStaticIP check if the network interface has a static IP configured
|
||||
//
|
||||
// Supports: Raspbian.
|
||||
func HasStaticIP(ifaceName string) (bool, error) {
|
||||
if runtime.GOOS == "linux" {
|
||||
@@ -36,7 +37,7 @@ func HasStaticIP(ifaceName string) (bool, error) {
|
||||
return false, fmt.Errorf("cannot check if IP is static: not supported on %s", runtime.GOOS)
|
||||
}
|
||||
|
||||
// Set a static IP for the specified network interface
|
||||
// SetStaticIP sets a static IP for the network interface.
|
||||
func SetStaticIP(ifaceName string) error {
|
||||
if runtime.GOOS == "linux" {
|
||||
return setStaticIPDhcpdConf(ifaceName)
|
||||
@@ -73,7 +74,6 @@ func hasStaticIPDhcpcdConf(dhcpConf, ifaceName string) bool {
|
||||
// we found our interface
|
||||
withinInterfaceCtx = true
|
||||
}
|
||||
|
||||
} else {
|
||||
if strings.HasPrefix(line, "interface ") {
|
||||
// we found another interface - reset our state
|
||||
@@ -240,7 +240,7 @@ func getNetworkSetupHardwareReports() map[string]string {
|
||||
return nil
|
||||
}
|
||||
|
||||
m := make(map[string]string, 0)
|
||||
m := make(map[string]string)
|
||||
|
||||
matches := re.FindAllStringSubmatch(out, -1)
|
||||
for i := range matches {
|
||||
@@ -163,7 +163,7 @@ func (ra *raCtx) Init() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Debug("DHCPv6 RA: source IP address: %s DNS IP address: %s",
|
||||
log.Debug("dhcpv6 ra: source IP address: %s DNS IP address: %s",
|
||||
ra.ipAddr, ra.dnsIPAddr)
|
||||
|
||||
params := icmpv6RA{
|
||||
@@ -183,7 +183,7 @@ func (ra *raCtx) Init() error {
|
||||
ipAndScope := ra.ipAddr.String() + "%" + ra.ifaceName
|
||||
ra.conn, err = icmp.ListenPacket("ip6:ipv6-icmp", ipAndScope)
|
||||
if err != nil {
|
||||
return fmt.Errorf("DHCPv6 RA: icmp.ListenPacket: %s", err)
|
||||
return fmt.Errorf("dhcpv6 ra: icmp.ListenPacket: %w", err)
|
||||
}
|
||||
success := false
|
||||
defer func() {
|
||||
@@ -195,11 +195,11 @@ func (ra *raCtx) Init() error {
|
||||
con6 := ra.conn.IPv6PacketConn()
|
||||
|
||||
if err := con6.SetHopLimit(255); err != nil {
|
||||
return fmt.Errorf("DHCPv6 RA: SetHopLimit: %s", err)
|
||||
return fmt.Errorf("dhcpv6 ra: SetHopLimit: %w", err)
|
||||
}
|
||||
|
||||
if err := con6.SetMulticastHopLimit(255); err != nil {
|
||||
return fmt.Errorf("DHCPv6 RA: SetMulticastHopLimit: %s", err)
|
||||
return fmt.Errorf("dhcpv6 ra: SetMulticastHopLimit: %w", err)
|
||||
}
|
||||
|
||||
msg := &ipv6.ControlMessage{
|
||||
@@ -212,15 +212,15 @@ func (ra *raCtx) Init() error {
|
||||
}
|
||||
|
||||
go func() {
|
||||
log.Debug("DHCPv6 RA: starting to send periodic RouterAdvertisement packets")
|
||||
log.Debug("dhcpv6 ra: starting to send periodic RouterAdvertisement packets")
|
||||
for ra.stop.Load() == 0 {
|
||||
_, err = con6.WriteTo(data, msg, addr)
|
||||
if err != nil {
|
||||
log.Error("DHCPv6 RA: WriteTo: %s", err)
|
||||
log.Error("dhcpv6 ra: WriteTo: %s", err)
|
||||
}
|
||||
time.Sleep(ra.packetSendPeriod)
|
||||
}
|
||||
log.Debug("DHCPv6 RA: loop exit")
|
||||
log.Debug("dhcpv6 ra: loop exit")
|
||||
}()
|
||||
|
||||
success = true
|
||||
@@ -229,7 +229,7 @@ func (ra *raCtx) Init() error {
|
||||
|
||||
// Close - close module
|
||||
func (ra *raCtx) Close() {
|
||||
log.Debug("DHCPv6 RA: closing")
|
||||
log.Debug("dhcpv6 ra: closing")
|
||||
|
||||
ra.stop.Store(1)
|
||||
|
||||
@@ -36,7 +36,7 @@ func (s *v4Server) WriteDiskConfig6(c *V6ServerConf) {
|
||||
}
|
||||
|
||||
// Return TRUE if IP address is within range [start..stop]
|
||||
func ip4InRange(start net.IP, stop net.IP, ip net.IP) bool {
|
||||
func ip4InRange(start, stop, ip net.IP) bool {
|
||||
if len(start) != 4 || len(stop) != 4 {
|
||||
return false
|
||||
}
|
||||
@@ -55,7 +55,7 @@ func (s *v4Server) ResetLeases(leases []*Lease) {
|
||||
if l.Expiry.Unix() != leaseExpireStatic &&
|
||||
!ip4InRange(s.conf.ipStart, s.conf.ipEnd, l.IP) {
|
||||
|
||||
log.Debug("DHCPv4: skipping a lease with IP %v: not within current IP range", l.IP)
|
||||
log.Debug("dhcpv4: skipping a lease with IP %v: not within current IP range", l.IP)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ func (s *v4Server) blacklistLease(lease *Lease) {
|
||||
// Remove (swap) lease by index
|
||||
func (s *v4Server) leaseRemoveSwapByIndex(i int) {
|
||||
s.ipAddrs[s.leases[i].IP[3]] = 0
|
||||
log.Debug("DHCPv4: removed lease %s", s.leases[i].HWAddr)
|
||||
log.Debug("dhcpv4: removed lease %s", s.leases[i].HWAddr)
|
||||
|
||||
n := len(s.leases)
|
||||
if i != n-1 {
|
||||
@@ -168,7 +168,7 @@ func (s *v4Server) rmDynamicLease(lease Lease) error {
|
||||
func (s *v4Server) addLease(l *Lease) {
|
||||
s.leases = append(s.leases, l)
|
||||
s.ipAddrs[l.IP[3]] = 1
|
||||
log.Debug("DHCPv4: added lease %s <-> %s", l.IP, l.HWAddr)
|
||||
log.Debug("dhcpv4: added lease %s <-> %s", l.IP, l.HWAddr)
|
||||
}
|
||||
|
||||
// Remove a lease with the same properties
|
||||
@@ -178,8 +178,7 @@ func (s *v4Server) rmLease(lease Lease) error {
|
||||
|
||||
if !bytes.Equal(l.HWAddr, lease.HWAddr) ||
|
||||
l.Hostname != lease.Hostname {
|
||||
|
||||
return fmt.Errorf("Lease not found")
|
||||
return fmt.Errorf("lease not found")
|
||||
}
|
||||
|
||||
s.leaseRemoveSwapByIndex(i)
|
||||
@@ -238,7 +237,6 @@ func (s *v4Server) RemoveStaticLease(l Lease) error {
|
||||
// Send ICMP to the specified machine
|
||||
// Return TRUE if it doesn't reply, which probably means that the IP is available
|
||||
func (s *v4Server) addrAvailable(target net.IP) bool {
|
||||
|
||||
if s.conf.ICMPTimeout == 0 {
|
||||
return true
|
||||
}
|
||||
@@ -256,15 +254,15 @@ func (s *v4Server) addrAvailable(target net.IP) bool {
|
||||
pinger.OnRecv = func(pkt *ping.Packet) {
|
||||
reply = true
|
||||
}
|
||||
log.Debug("DHCPv4: Sending ICMP Echo to %v", target)
|
||||
log.Debug("dhcpv4: Sending ICMP Echo to %v", target)
|
||||
pinger.Run()
|
||||
|
||||
if reply {
|
||||
log.Info("DHCPv4: IP conflict: %v is already used by another device", target)
|
||||
log.Info("dhcpv4: IP conflict: %v is already used by another device", target)
|
||||
return false
|
||||
}
|
||||
|
||||
log.Debug("DHCPv4: ICMP procedure is complete: %v", target)
|
||||
log.Debug("dhcpv4: ICMP procedure is complete: %v", target)
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -337,7 +335,7 @@ func (s *v4Server) commitLease(l *Lease) {
|
||||
}
|
||||
|
||||
// Process Discover request and return lease
|
||||
func (s *v4Server) processDiscover(req *dhcpv4.DHCPv4, resp *dhcpv4.DHCPv4) *Lease {
|
||||
func (s *v4Server) processDiscover(req, resp *dhcpv4.DHCPv4) *Lease {
|
||||
mac := req.ClientHWAddr
|
||||
|
||||
s.leasesLock.Lock()
|
||||
@@ -349,7 +347,7 @@ func (s *v4Server) processDiscover(req *dhcpv4.DHCPv4, resp *dhcpv4.DHCPv4) *Lea
|
||||
for lease == nil {
|
||||
lease = s.reserveLease(mac)
|
||||
if lease == nil {
|
||||
log.Debug("DHCPv4: No more IP addresses")
|
||||
log.Debug("dhcpv4: No more IP addresses")
|
||||
if toStore {
|
||||
s.conf.notify(LeaseChangedDBStore)
|
||||
}
|
||||
@@ -372,7 +370,7 @@ func (s *v4Server) processDiscover(req *dhcpv4.DHCPv4, resp *dhcpv4.DHCPv4) *Lea
|
||||
reqIP := req.Options.Get(dhcpv4.OptionRequestedIPAddress)
|
||||
if len(reqIP) != 0 &&
|
||||
!bytes.Equal(reqIP, lease.IP) {
|
||||
log.Debug("DHCPv4: different RequestedIP: %v != %v", reqIP, lease.IP)
|
||||
log.Debug("dhcpv4: different RequestedIP: %v != %v", reqIP, lease.IP)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -407,12 +405,11 @@ func (o *optFQDN) ToBytes() []byte {
|
||||
|
||||
copy(b[i:], []byte(o.name))
|
||||
return b
|
||||
|
||||
}
|
||||
|
||||
// Process Request request and return lease
|
||||
// Return false if we don't need to reply
|
||||
func (s *v4Server) processRequest(req *dhcpv4.DHCPv4, resp *dhcpv4.DHCPv4) (*Lease, bool) {
|
||||
func (s *v4Server) processRequest(req, resp *dhcpv4.DHCPv4) (*Lease, bool) {
|
||||
var lease *Lease
|
||||
mac := req.ClientHWAddr
|
||||
hostname := req.Options.Get(dhcpv4.OptionHostName)
|
||||
@@ -424,12 +421,12 @@ func (s *v4Server) processRequest(req *dhcpv4.DHCPv4, resp *dhcpv4.DHCPv4) (*Lea
|
||||
sid := req.Options.Get(dhcpv4.OptionServerIdentifier)
|
||||
if len(sid) != 0 &&
|
||||
!bytes.Equal(sid, s.conf.dnsIPAddrs[0]) {
|
||||
log.Debug("DHCPv4: Bad OptionServerIdentifier in Request message for %s", mac)
|
||||
log.Debug("dhcpv4: Bad OptionServerIdentifier in Request message for %s", mac)
|
||||
return nil, false
|
||||
}
|
||||
|
||||
if len(reqIP) != 4 {
|
||||
log.Debug("DHCPv4: Bad OptionRequestedIPAddress in Request message for %s", mac)
|
||||
log.Debug("dhcpv4: Bad OptionRequestedIPAddress in Request message for %s", mac)
|
||||
return nil, false
|
||||
}
|
||||
|
||||
@@ -438,7 +435,7 @@ func (s *v4Server) processRequest(req *dhcpv4.DHCPv4, resp *dhcpv4.DHCPv4) (*Lea
|
||||
if bytes.Equal(l.HWAddr, mac) {
|
||||
if !bytes.Equal(l.IP, reqIP) {
|
||||
s.leasesLock.Unlock()
|
||||
log.Debug("DHCPv4: Mismatched OptionRequestedIPAddress in Request message for %s", mac)
|
||||
log.Debug("dhcpv4: Mismatched OptionRequestedIPAddress in Request message for %s", mac)
|
||||
return nil, true
|
||||
}
|
||||
|
||||
@@ -449,7 +446,7 @@ func (s *v4Server) processRequest(req *dhcpv4.DHCPv4, resp *dhcpv4.DHCPv4) (*Lea
|
||||
s.leasesLock.Unlock()
|
||||
|
||||
if lease == nil {
|
||||
log.Debug("DHCPv4: No lease for %s", mac)
|
||||
log.Debug("dhcpv4: No lease for %s", mac)
|
||||
return nil, true
|
||||
}
|
||||
|
||||
@@ -475,8 +472,7 @@ func (s *v4Server) processRequest(req *dhcpv4.DHCPv4, resp *dhcpv4.DHCPv4) (*Lea
|
||||
// Return 1: OK
|
||||
// Return 0: error; reply with Nak
|
||||
// Return -1: error; don't reply
|
||||
func (s *v4Server) process(req *dhcpv4.DHCPv4, resp *dhcpv4.DHCPv4) int {
|
||||
|
||||
func (s *v4Server) process(req, resp *dhcpv4.DHCPv4) int {
|
||||
var lease *Lease
|
||||
|
||||
resp.UpdateOption(dhcpv4.OptServerIdentifier(s.conf.dnsIPAddrs[0]))
|
||||
@@ -519,7 +515,7 @@ func (s *v4Server) process(req *dhcpv4.DHCPv4, resp *dhcpv4.DHCPv4) int {
|
||||
// client(0.0.0.0:68) -> (Request:ClientMAC,Type=Request,ClientID,ReqIP||ClientIP,HostName,ServerID,ParamReqList) -> server(255.255.255.255:67)
|
||||
// client(255.255.255.255:68) <- (Reply:YourIP,ClientMAC,Type=ACK,ServerID,SubnetMask,LeaseTime) <- server(<IP>:67)
|
||||
func (s *v4Server) packetHandler(conn net.PacketConn, peer net.Addr, req *dhcpv4.DHCPv4) {
|
||||
log.Debug("DHCPv4: received message: %s", req.Summary())
|
||||
log.Debug("dhcpv4: received message: %s", req.Summary())
|
||||
|
||||
switch req.MessageType() {
|
||||
case dhcpv4.MessageTypeDiscover,
|
||||
@@ -527,18 +523,18 @@ func (s *v4Server) packetHandler(conn net.PacketConn, peer net.Addr, req *dhcpv4
|
||||
//
|
||||
|
||||
default:
|
||||
log.Debug("DHCPv4: unsupported message type %d", req.MessageType())
|
||||
log.Debug("dhcpv4: unsupported message type %d", req.MessageType())
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := dhcpv4.NewReplyFromRequest(req)
|
||||
if err != nil {
|
||||
log.Debug("DHCPv4: dhcpv4.New: %s", err)
|
||||
log.Debug("dhcpv4: dhcpv4.New: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
if len(req.ClientHWAddr) != 6 {
|
||||
log.Debug("DHCPv4: Invalid ClientHWAddr")
|
||||
log.Debug("dhcpv4: Invalid ClientHWAddr")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -549,33 +545,74 @@ func (s *v4Server) packetHandler(conn net.PacketConn, peer net.Addr, req *dhcpv4
|
||||
resp.Options.Update(dhcpv4.OptMessageType(dhcpv4.MessageTypeNak))
|
||||
}
|
||||
|
||||
log.Debug("DHCPv4: sending: %s", resp.Summary())
|
||||
log.Debug("dhcpv4: sending: %s", resp.Summary())
|
||||
|
||||
_, err = conn.WriteTo(resp.ToBytes(), peer)
|
||||
if err != nil {
|
||||
log.Error("DHCPv4: conn.Write to %s failed: %s", peer, err)
|
||||
log.Error("dhcpv4: conn.Write to %s failed: %s", peer, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Start - start server
|
||||
// ifaceIPv4Addrs returns the interface's IPv4 addresses.
|
||||
func ifaceIPv4Addrs(iface *net.Interface) (ips []net.IP, err error) {
|
||||
addrs, err := iface.Addrs()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, a := range addrs {
|
||||
ipnet, ok := a.(*net.IPNet)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if ip := ipnet.IP.To4(); ip != nil {
|
||||
ips = append(ips, ip)
|
||||
}
|
||||
}
|
||||
|
||||
return ips, nil
|
||||
}
|
||||
|
||||
// Start starts the IPv4 DHCP server.
|
||||
func (s *v4Server) Start() error {
|
||||
if !s.conf.Enabled {
|
||||
return nil
|
||||
}
|
||||
|
||||
iface, err := net.InterfaceByName(s.conf.InterfaceName)
|
||||
ifaceName := s.conf.InterfaceName
|
||||
iface, err := net.InterfaceByName(ifaceName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("DHCPv4: Couldn't find interface by name %s: %s", s.conf.InterfaceName, err)
|
||||
return fmt.Errorf("dhcpv4: finding interface %s by name: %w", ifaceName, err)
|
||||
}
|
||||
|
||||
log.Debug("DHCPv4: starting...")
|
||||
s.conf.dnsIPAddrs = getIfaceIPv4(*iface)
|
||||
if len(s.conf.dnsIPAddrs) == 0 {
|
||||
log.Debug("DHCPv4: no IPv6 address for interface %s", iface.Name)
|
||||
return nil
|
||||
log.Debug("dhcpv4: starting...")
|
||||
|
||||
dnsIPAddrs, err := ifaceIPv4Addrs(iface)
|
||||
if err != nil {
|
||||
return fmt.Errorf("dhcpv4: getting ipv4 addrs for iface %s: %w", ifaceName, err)
|
||||
}
|
||||
|
||||
switch len(dnsIPAddrs) {
|
||||
case 0:
|
||||
log.Debug("dhcpv4: no ipv4 address for interface %s", iface.Name)
|
||||
|
||||
return nil
|
||||
case 1:
|
||||
// Some Android devices use 8.8.8.8 if there is no secondary DNS
|
||||
// server. Fix that by setting the secondary DNS address to our
|
||||
// address as well.
|
||||
//
|
||||
// See https://github.com/AdguardTeam/AdGuardHome/issues/1708.
|
||||
log.Debug("dhcpv4: setting secondary dns ip to iself for interface %s", iface.Name)
|
||||
dnsIPAddrs = append(dnsIPAddrs, dnsIPAddrs[0])
|
||||
default:
|
||||
// Go on.
|
||||
}
|
||||
|
||||
s.conf.dnsIPAddrs = dnsIPAddrs
|
||||
|
||||
laddr := &net.UDPAddr{
|
||||
IP: net.ParseIP("0.0.0.0"),
|
||||
Port: dhcpv4.ServerPort,
|
||||
@@ -585,12 +622,13 @@ func (s *v4Server) Start() error {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Info("DHCPv4: listening")
|
||||
log.Info("dhcpv4: listening")
|
||||
|
||||
go func() {
|
||||
err = s.srv.Serve()
|
||||
log.Debug("DHCPv4: srv.Serve: %s", err)
|
||||
log.Debug("dhcpv4: srv.Serve: %s", err)
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -600,10 +638,10 @@ func (s *v4Server) Stop() {
|
||||
return
|
||||
}
|
||||
|
||||
log.Debug("DHCPv4: stopping")
|
||||
log.Debug("dhcpv4: stopping")
|
||||
err := s.srv.Close()
|
||||
if err != nil {
|
||||
log.Error("DHCPv4: srv.Close: %s", err)
|
||||
log.Error("dhcpv4: srv.Close: %s", err)
|
||||
}
|
||||
// now s.srv.Serve() will return
|
||||
s.srv = nil
|
||||
@@ -621,31 +659,31 @@ func v4Create(conf V4ServerConf) (DHCPServer, error) {
|
||||
var err error
|
||||
s.conf.routerIP, err = parseIPv4(s.conf.GatewayIP)
|
||||
if err != nil {
|
||||
return s, fmt.Errorf("DHCPv4: %s", err)
|
||||
return s, fmt.Errorf("dhcpv4: %w", err)
|
||||
}
|
||||
|
||||
subnet, err := parseIPv4(s.conf.SubnetMask)
|
||||
if err != nil || !isValidSubnetMask(subnet) {
|
||||
return s, fmt.Errorf("DHCPv4: invalid subnet mask: %s", s.conf.SubnetMask)
|
||||
return s, fmt.Errorf("dhcpv4: invalid subnet mask: %s", s.conf.SubnetMask)
|
||||
}
|
||||
s.conf.subnetMask = make([]byte, 4)
|
||||
copy(s.conf.subnetMask, subnet)
|
||||
|
||||
s.conf.ipStart, err = parseIPv4(conf.RangeStart)
|
||||
if s.conf.ipStart == nil {
|
||||
return s, fmt.Errorf("DHCPv4: %s", err)
|
||||
return s, fmt.Errorf("dhcpv4: %w", err)
|
||||
}
|
||||
if s.conf.ipStart[0] == 0 {
|
||||
return s, fmt.Errorf("DHCPv4: invalid range start IP")
|
||||
return s, fmt.Errorf("dhcpv4: invalid range start IP")
|
||||
}
|
||||
|
||||
s.conf.ipEnd, err = parseIPv4(conf.RangeEnd)
|
||||
if s.conf.ipEnd == nil {
|
||||
return s, fmt.Errorf("DHCPv4: %s", err)
|
||||
return s, fmt.Errorf("dhcpv4: %w", err)
|
||||
}
|
||||
if !net.IP.Equal(s.conf.ipStart[:3], s.conf.ipEnd[:3]) ||
|
||||
s.conf.ipStart[3] > s.conf.ipEnd[3] {
|
||||
return s, fmt.Errorf("DHCPv4: range end IP should match range start IP")
|
||||
return s, fmt.Errorf("dhcpv4: range end IP should match range start IP")
|
||||
}
|
||||
|
||||
if conf.LeaseDuration == 0 {
|
||||
@@ -658,7 +696,7 @@ func v4Create(conf V4ServerConf) (DHCPServer, error) {
|
||||
for _, o := range conf.Options {
|
||||
code, val := parseOptionString(o)
|
||||
if code == 0 {
|
||||
log.Debug("DHCPv4: bad option string: %s", o)
|
||||
log.Debug("dhcpv4: bad option string: %s", o)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -41,10 +41,11 @@ func (s *v6Server) WriteDiskConfig6(c *V6ServerConf) {
|
||||
|
||||
// Return TRUE if IP address is within range [start..0xff]
|
||||
// nolint(staticcheck)
|
||||
func ip6InRange(start net.IP, ip net.IP) bool {
|
||||
func ip6InRange(start, ip net.IP) bool {
|
||||
if len(start) != 16 {
|
||||
return false
|
||||
}
|
||||
//lint:ignore SA1021 TODO(e.burkov): Ignore this for now, think about using masks.
|
||||
if !bytes.Equal(start[:15], ip[:15]) {
|
||||
return false
|
||||
}
|
||||
@@ -72,12 +73,10 @@ func (s *v6Server) GetLeases(flags int) []Lease {
|
||||
var result []Lease
|
||||
s.leasesLock.Lock()
|
||||
for _, lease := range s.leases {
|
||||
|
||||
if lease.Expiry.Unix() == leaseExpireStatic {
|
||||
if (flags & LeasesStatic) != 0 {
|
||||
result = append(result, *lease)
|
||||
}
|
||||
|
||||
} else {
|
||||
if (flags & LeasesDynamic) != 0 {
|
||||
result = append(result, *lease)
|
||||
@@ -214,8 +213,7 @@ func (s *v6Server) rmLease(lease Lease) error {
|
||||
|
||||
if !bytes.Equal(l.HWAddr, lease.HWAddr) ||
|
||||
l.Hostname != lease.Hostname {
|
||||
|
||||
return fmt.Errorf("Lease not found")
|
||||
return fmt.Errorf("lease not found")
|
||||
}
|
||||
|
||||
s.leaseRemoveSwapByIndex(i)
|
||||
@@ -302,7 +300,7 @@ func (s *v6Server) commitDynamicLease(l *Lease) {
|
||||
// Check Client ID
|
||||
func (s *v6Server) checkCID(msg *dhcpv6.Message) error {
|
||||
if msg.Options.ClientID() == nil {
|
||||
return fmt.Errorf("DHCPv6: no ClientID option in request")
|
||||
return fmt.Errorf("dhcpv6: no ClientID option in request")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -317,7 +315,7 @@ func (s *v6Server) checkSID(msg *dhcpv6.Message) error {
|
||||
dhcpv6.MessageTypeRebind:
|
||||
|
||||
if sid != nil {
|
||||
return fmt.Errorf("DHCPv6: drop packet: ServerID option in message %s", msg.Type().String())
|
||||
return fmt.Errorf("dhcpv6: drop packet: ServerID option in message %s", msg.Type().String())
|
||||
}
|
||||
|
||||
case dhcpv6.MessageTypeRequest,
|
||||
@@ -326,10 +324,10 @@ func (s *v6Server) checkSID(msg *dhcpv6.Message) error {
|
||||
dhcpv6.MessageTypeDecline:
|
||||
|
||||
if sid == nil {
|
||||
return fmt.Errorf("DHCPv6: drop packet: no ServerID option in message %s", msg.Type().String())
|
||||
return fmt.Errorf("dhcpv6: drop packet: no ServerID option in message %s", msg.Type().String())
|
||||
}
|
||||
if !sid.Equal(s.sid) {
|
||||
return fmt.Errorf("DHCPv6: drop packet: mismatched ServerID option in message %s: %s",
|
||||
return fmt.Errorf("dhcpv6: drop packet: mismatched ServerID option in message %s: %s",
|
||||
msg.Type().String(), sid.String())
|
||||
}
|
||||
}
|
||||
@@ -371,7 +369,7 @@ func (s *v6Server) commitLease(msg *dhcpv6.Message, lease *Lease) time.Duration
|
||||
//
|
||||
|
||||
case dhcpv6.MessageTypeConfirm:
|
||||
lifetime = lease.Expiry.Sub(time.Now())
|
||||
lifetime = time.Until(lease.Expiry)
|
||||
|
||||
case dhcpv6.MessageTypeRequest,
|
||||
dhcpv6.MessageTypeRenew,
|
||||
@@ -385,7 +383,7 @@ func (s *v6Server) commitLease(msg *dhcpv6.Message, lease *Lease) time.Duration
|
||||
}
|
||||
|
||||
// Find a lease associated with MAC and prepare response
|
||||
func (s *v6Server) process(msg *dhcpv6.Message, req dhcpv6.DHCPv6, resp dhcpv6.DHCPv6) bool {
|
||||
func (s *v6Server) process(msg *dhcpv6.Message, req, resp dhcpv6.DHCPv6) bool {
|
||||
switch msg.Type() {
|
||||
case dhcpv6.MessageTypeSolicit,
|
||||
dhcpv6.MessageTypeRequest,
|
||||
@@ -539,24 +537,25 @@ func (s *v6Server) packetHandler(conn net.PacketConn, peer net.Addr, req dhcpv6.
|
||||
}
|
||||
}
|
||||
|
||||
// Get IPv6 address list
|
||||
func getIfaceIPv6(iface net.Interface) []net.IP {
|
||||
// ifaceIPv6Addrs returns the interface's IPv6 addresses.
|
||||
func ifaceIPv6Addrs(iface *net.Interface) (ips []net.IP, err error) {
|
||||
addrs, err := iface.Addrs()
|
||||
if err != nil {
|
||||
return nil
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var res []net.IP
|
||||
for _, a := range addrs {
|
||||
ipnet, ok := a.(*net.IPNet)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if ipnet.IP.To4() == nil {
|
||||
res = append(res, ipnet.IP)
|
||||
|
||||
if ip := ipnet.IP.To16(); ip != nil {
|
||||
ips = append(ips, ip)
|
||||
}
|
||||
}
|
||||
return res
|
||||
|
||||
return ips, nil
|
||||
}
|
||||
|
||||
// initialize RA module
|
||||
@@ -580,23 +579,40 @@ func (s *v6Server) initRA(iface *net.Interface) error {
|
||||
return s.ra.Init()
|
||||
}
|
||||
|
||||
// Start - start server
|
||||
// Start starts the IPv6 DHCP server.
|
||||
func (s *v6Server) Start() error {
|
||||
if !s.conf.Enabled {
|
||||
return nil
|
||||
}
|
||||
|
||||
iface, err := net.InterfaceByName(s.conf.InterfaceName)
|
||||
ifaceName := s.conf.InterfaceName
|
||||
iface, err := net.InterfaceByName(ifaceName)
|
||||
if err != nil {
|
||||
return wrapErrPrint(err, "Couldn't find interface by name %s", s.conf.InterfaceName)
|
||||
return fmt.Errorf("dhcpv6: finding interface %s by name: %w", ifaceName, err)
|
||||
}
|
||||
|
||||
s.conf.dnsIPAddrs = getIfaceIPv6(*iface)
|
||||
if len(s.conf.dnsIPAddrs) == 0 {
|
||||
log.Debug("DHCPv6: no IPv6 address for interface %s", iface.Name)
|
||||
return nil
|
||||
log.Debug("dhcpv4: starting...")
|
||||
|
||||
dnsIPAddrs, err := ifaceIPv6Addrs(iface)
|
||||
if err != nil {
|
||||
return fmt.Errorf("dhcpv6: getting ipv6 addrs for iface %s: %w", ifaceName, err)
|
||||
}
|
||||
|
||||
switch len(dnsIPAddrs) {
|
||||
case 0:
|
||||
log.Debug("dhcpv6: no ipv6 address for interface %s", iface.Name)
|
||||
|
||||
return nil
|
||||
case 1:
|
||||
// See the comment in (*v4Server).Start.
|
||||
log.Debug("dhcpv6: setting secondary dns ip to iself for interface %s", iface.Name)
|
||||
dnsIPAddrs = append(dnsIPAddrs, dnsIPAddrs[0])
|
||||
default:
|
||||
// Go on.
|
||||
}
|
||||
|
||||
s.conf.dnsIPAddrs = dnsIPAddrs
|
||||
|
||||
err = s.initRA(iface)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -611,7 +627,7 @@ func (s *v6Server) Start() error {
|
||||
log.Debug("DHCPv6: starting...")
|
||||
|
||||
if len(iface.HardwareAddr) != 6 {
|
||||
return fmt.Errorf("DHCPv6: invalid MAC %s", iface.HardwareAddr)
|
||||
return fmt.Errorf("dhcpv6: invalid MAC %s", iface.HardwareAddr)
|
||||
}
|
||||
s.sid = dhcpv6.Duid{
|
||||
Type: dhcpv6.DUID_LLT,
|
||||
@@ -666,7 +682,7 @@ func v6Create(conf V6ServerConf) (DHCPServer, error) {
|
||||
|
||||
s.conf.ipStart = net.ParseIP(conf.RangeStart)
|
||||
if s.conf.ipStart == nil || s.conf.ipStart.To16() == nil {
|
||||
return s, fmt.Errorf("DHCPv6: invalid range-start IP: %s", conf.RangeStart)
|
||||
return s, fmt.Errorf("dhcpv6: invalid range-start IP: %s", conf.RangeStart)
|
||||
}
|
||||
|
||||
if conf.LeaseDuration == 0 {
|
||||
@@ -22,12 +22,12 @@ func main() {
|
||||
res, err := filter.CheckHost(host)
|
||||
if err != nil {
|
||||
// temporary failure
|
||||
log.Fatalf("Failed to check host '%s': %s", host, err)
|
||||
log.Fatalf("Failed to check host %q: %s", host, err)
|
||||
}
|
||||
if res.IsFiltered {
|
||||
log.Printf("Host %s is filtered, reason - '%s', matched rule: '%s'", host, res.Reason, res.Rule)
|
||||
log.Printf("Host %s is filtered, reason - %q, matched rule: %q", host, res.Reason, res.Rule)
|
||||
} else {
|
||||
log.Printf("Host %s is not filtered, reason - '%s'", host, res.Reason)
|
||||
log.Printf("Host %s is not filtered, reason - %q", host, res.Reason)
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -59,12 +59,12 @@ func main() {
|
||||
res, err := filter.CheckHost(host)
|
||||
if err != nil {
|
||||
// temporary failure
|
||||
log.Fatalf("Failed to check host '%s': %s", host, err)
|
||||
log.Fatalf("Failed to check host %q: %s", host, err)
|
||||
}
|
||||
if res.IsFiltered {
|
||||
log.Printf("Host %s is filtered, reason - '%s', matched rule: '%s'", host, res.Reason, res.Rule)
|
||||
log.Printf("Host %s is filtered, reason - %q, matched rule: %q", host, res.Reason, res.Rule)
|
||||
} else {
|
||||
log.Printf("Host %s is not filtered, reason - '%s'", host, res.Reason)
|
||||
log.Printf("Host %s is not filtered, reason - %q", host, res.Reason)
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -43,7 +43,7 @@ var serviceRulesArray = []svc{
|
||||
"||youtube",
|
||||
}},
|
||||
{"twitch", []string{"||twitch.tv^", "||ttvnw.net^", "||jtvnw.net^", "||twitchcdn.net^"}},
|
||||
{"netflix", []string{"||nflxext.com^", "||netflix.com^", "||nflximg.net^", "||nflxvideo.net^"}},
|
||||
{"netflix", []string{"||nflxext.com^", "||netflix.com^", "||nflximg.net^", "||nflxvideo.net^", "||nflxso.net^"}},
|
||||
{"instagram", []string{"||instagram.com^", "||cdninstagram.com^"}},
|
||||
{"snapchat", []string{
|
||||
"||snapchat.com^",
|
||||
@@ -161,6 +161,7 @@ var serviceRulesArray = []svc{
|
||||
"||douyin.com^",
|
||||
"||tiktokv.com^",
|
||||
}},
|
||||
{"qq", []string{"||qq.com^", "||qqzaixian.com^"}},
|
||||
}
|
||||
|
||||
// convert array to map
|
||||
@@ -1,3 +1,4 @@
|
||||
// Package dnsfilter implements a DNS filter.
|
||||
package dnsfilter
|
||||
|
||||
import (
|
||||
@@ -11,7 +12,7 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/util"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/util"
|
||||
"github.com/AdguardTeam/dnsproxy/upstream"
|
||||
"github.com/AdguardTeam/golibs/cache"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
@@ -199,7 +200,7 @@ func (d *Dnsfilter) WriteDiskConfig(c *Config) {
|
||||
// SetFilters - set new filters (synchronously or asynchronously)
|
||||
// When filters are set asynchronously, the old filters continue working until the new filters are ready.
|
||||
// In this case the caller must ensure that the old filter files are intact.
|
||||
func (d *Dnsfilter) SetFilters(blockFilters []Filter, allowFilters []Filter, async bool) error {
|
||||
func (d *Dnsfilter) SetFilters(blockFilters, allowFilters []Filter, async bool) error {
|
||||
if async {
|
||||
params := filtersInitializerParams{
|
||||
allowFilters: allowFilters,
|
||||
@@ -281,7 +282,7 @@ type Result struct {
|
||||
CanonName string `json:",omitempty"` // CNAME value
|
||||
|
||||
// for RewriteEtcHosts:
|
||||
ReverseHost string `json:",omitempty"`
|
||||
ReverseHosts []string `json:",omitempty"`
|
||||
|
||||
// for ReasonRewrite & RewriteEtcHosts:
|
||||
IPList []net.IP `json:",omitempty"` // list of IP addresses
|
||||
@@ -325,18 +326,9 @@ func (d *Dnsfilter) CheckHost(host string, qtype uint16, setts *RequestFiltering
|
||||
// Now check the hosts file -- do we have any rules for it?
|
||||
// just like DNS rewrites, it has higher priority than filtering rules.
|
||||
if d.Config.AutoHosts != nil {
|
||||
ips := d.Config.AutoHosts.Process(host, qtype)
|
||||
if ips != nil {
|
||||
result.Reason = RewriteEtcHosts
|
||||
result.IPList = ips
|
||||
return result, nil
|
||||
}
|
||||
|
||||
revHost := d.Config.AutoHosts.ProcessReverse(host, qtype)
|
||||
if len(revHost) != 0 {
|
||||
result.Reason = RewriteEtcHosts
|
||||
result.ReverseHost = revHost + "."
|
||||
return result, nil
|
||||
matched, err := d.checkAutoHosts(host, qtype, &result)
|
||||
if matched {
|
||||
return result, err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -401,6 +393,31 @@ func (d *Dnsfilter) CheckHost(host string, qtype uint16, setts *RequestFiltering
|
||||
return Result{}, nil
|
||||
}
|
||||
|
||||
func (d *Dnsfilter) checkAutoHosts(host string, qtype uint16, result *Result) (matched bool, err error) {
|
||||
ips := d.Config.AutoHosts.Process(host, qtype)
|
||||
if ips != nil {
|
||||
result.Reason = RewriteEtcHosts
|
||||
result.IPList = ips
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
revHosts := d.Config.AutoHosts.ProcessReverse(host, qtype)
|
||||
if len(revHosts) != 0 {
|
||||
result.Reason = RewriteEtcHosts
|
||||
|
||||
// TODO(a.garipov): Optimize this with a buffer.
|
||||
result.ReverseHosts = make([]string, len(revHosts))
|
||||
for i := range revHosts {
|
||||
result.ReverseHosts[i] = revHosts[i] + "."
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// Process rewrites table
|
||||
// . Find CNAME for a domain name (exact match or by wildcard)
|
||||
// . if found and CNAME equals to domain name - this is an exception; exit
|
||||
@@ -481,13 +498,10 @@ func matchBlockedServicesRules(host string, svcs []ServiceEntry) Result {
|
||||
// Adding rule and matching against the rules
|
||||
//
|
||||
|
||||
// Return TRUE if file exists
|
||||
// fileExists returns true if file exists.
|
||||
func fileExists(fn string) bool {
|
||||
_, err := os.Stat(fn)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func createFilteringEngine(filters []Filter) (*filterlist.RuleStorage, *urlfilter.DNSEngine, error) {
|
||||
@@ -501,19 +515,18 @@ func createFilteringEngine(filters []Filter) (*filterlist.RuleStorage, *urlfilte
|
||||
RulesText: string(f.Data),
|
||||
IgnoreCosmetic: true,
|
||||
}
|
||||
|
||||
} else if !fileExists(f.FilePath) {
|
||||
list = &filterlist.StringRuleList{
|
||||
ID: int(f.ID),
|
||||
IgnoreCosmetic: true,
|
||||
}
|
||||
|
||||
} else if runtime.GOOS == "windows" {
|
||||
// On Windows we don't pass a file to urlfilter because
|
||||
// it's difficult to update this file while it's being used.
|
||||
// it's difficult to update this file while it's being
|
||||
// used.
|
||||
data, err := ioutil.ReadFile(f.FilePath)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("ioutil.ReadFile(): %s: %s", f.FilePath, err)
|
||||
return nil, nil, fmt.Errorf("ioutil.ReadFile(): %s: %w", f.FilePath, err)
|
||||
}
|
||||
list = &filterlist.StringRuleList{
|
||||
ID: int(f.ID),
|
||||
@@ -525,7 +538,7 @@ func createFilteringEngine(filters []Filter) (*filterlist.RuleStorage, *urlfilte
|
||||
var err error
|
||||
list, err = filterlist.NewFileRuleList(int(f.ID), f.FilePath, true)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("filterlist.NewFileRuleList(): %s: %s", f.FilePath, err)
|
||||
return nil, nil, fmt.Errorf("filterlist.NewFileRuleList(): %s: %w", f.FilePath, err)
|
||||
}
|
||||
}
|
||||
listArray = append(listArray, list)
|
||||
@@ -533,13 +546,13 @@ func createFilteringEngine(filters []Filter) (*filterlist.RuleStorage, *urlfilte
|
||||
|
||||
rulesStorage, err := filterlist.NewRuleStorage(listArray)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("filterlist.NewRuleStorage(): %s", err)
|
||||
return nil, nil, fmt.Errorf("filterlist.NewRuleStorage(): %w", err)
|
||||
}
|
||||
filteringEngine := urlfilter.NewDNSEngine(rulesStorage)
|
||||
return rulesStorage, filteringEngine, nil
|
||||
}
|
||||
|
||||
// Initialize urlfilter objects
|
||||
// Initialize urlfilter objects.
|
||||
func (d *Dnsfilter) initFiltering(allowFilters, blockFilters []Filter) error {
|
||||
rulesStorage, filteringEngine, err := createFilteringEngine(blockFilters)
|
||||
if err != nil {
|
||||
@@ -565,7 +578,8 @@ func (d *Dnsfilter) initFiltering(allowFilters, blockFilters []Filter) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// matchHost is a low-level way to check only if hostname is filtered by rules, skipping expensive safebrowsing and parental lookups
|
||||
// matchHost is a low-level way to check only if hostname is filtered by rules,
|
||||
// skipping expensive safebrowsing and parental lookups.
|
||||
func (d *Dnsfilter) matchHost(host string, qtype uint16, setts RequestFilteringSettings) (Result, error) {
|
||||
d.engineLock.RLock()
|
||||
// Keep in mind that this lock must be held no just when calling Match()
|
||||
@@ -590,7 +604,7 @@ func (d *Dnsfilter) matchHost(host string, qtype uint16, setts RequestFilteringS
|
||||
rule = rr.HostRulesV6[0]
|
||||
}
|
||||
|
||||
log.Debug("Filtering: found whitelist rule for host '%s': '%s' list_id: %d",
|
||||
log.Debug("Filtering: found whitelist rule for host %q: %q list_id: %d",
|
||||
host, rule.Text(), rule.GetFilterListID())
|
||||
res := makeResult(rule, NotFilteredWhiteList)
|
||||
return res, nil
|
||||
@@ -607,7 +621,7 @@ func (d *Dnsfilter) matchHost(host string, qtype uint16, setts RequestFilteringS
|
||||
}
|
||||
|
||||
if rr.NetworkRule != nil {
|
||||
log.Debug("Filtering: found rule for host '%s': '%s' list_id: %d",
|
||||
log.Debug("Filtering: found rule for host %q: %q list_id: %d",
|
||||
host, rr.NetworkRule.Text(), rr.NetworkRule.GetFilterListID())
|
||||
reason := FilteredBlackList
|
||||
if rr.NetworkRule.Whitelist {
|
||||
@@ -619,7 +633,7 @@ func (d *Dnsfilter) matchHost(host string, qtype uint16, setts RequestFilteringS
|
||||
|
||||
if qtype == dns.TypeA && rr.HostRulesV4 != nil {
|
||||
rule := rr.HostRulesV4[0] // note that we process only 1 matched rule
|
||||
log.Debug("Filtering: found rule for host '%s': '%s' list_id: %d",
|
||||
log.Debug("Filtering: found rule for host %q: %q list_id: %d",
|
||||
host, rule.Text(), rule.GetFilterListID())
|
||||
res := makeResult(rule, FilteredBlackList)
|
||||
res.IP = rule.IP.To4()
|
||||
@@ -628,7 +642,7 @@ func (d *Dnsfilter) matchHost(host string, qtype uint16, setts RequestFilteringS
|
||||
|
||||
if qtype == dns.TypeAAAA && rr.HostRulesV6 != nil {
|
||||
rule := rr.HostRulesV6[0] // note that we process only 1 matched rule
|
||||
log.Debug("Filtering: found rule for host '%s': '%s' list_id: %d",
|
||||
log.Debug("Filtering: found rule for host %q: %q list_id: %d",
|
||||
host, rule.Text(), rule.GetFilterListID())
|
||||
res := makeResult(rule, FilteredBlackList)
|
||||
res.IP = rule.IP
|
||||
@@ -644,7 +658,7 @@ func (d *Dnsfilter) matchHost(host string, qtype uint16, setts RequestFilteringS
|
||||
} else if rr.HostRulesV6 != nil {
|
||||
rule = rr.HostRulesV6[0]
|
||||
}
|
||||
log.Debug("Filtering: found rule for host '%s': '%s' list_id: %d",
|
||||
log.Debug("Filtering: found rule for host %q: %q list_id: %d",
|
||||
host, rule.Text(), rule.GetFilterListID())
|
||||
res := makeResult(rule, FilteredBlackList)
|
||||
res.IP = net.IP{}
|
||||
@@ -666,21 +680,18 @@ func makeResult(rule rules.Rule, reason Reason) Result {
|
||||
return res
|
||||
}
|
||||
|
||||
// InitModule() - manually initialize blocked services map
|
||||
// InitModule manually initializes blocked services map.
|
||||
func InitModule() {
|
||||
initBlockedServices()
|
||||
}
|
||||
|
||||
// New creates properly initialized DNS Filter that is ready to be used
|
||||
// New creates properly initialized DNS Filter that is ready to be used.
|
||||
func New(c *Config, blockFilters []Filter) *Dnsfilter {
|
||||
|
||||
if c != nil {
|
||||
cacheConf := cache.Config{
|
||||
EnableLRU: true,
|
||||
}
|
||||
|
||||
// initialize objects only once
|
||||
|
||||
if gctx.safebrowsingCache == nil {
|
||||
cacheConf.MaxSize = c.SafeBrowsingCacheSize
|
||||
gctx.safebrowsingCache = cache.New(cacheConf)
|
||||
@@ -713,7 +724,7 @@ func New(c *Config, blockFilters []Filter) *Dnsfilter {
|
||||
bsvcs := []string{}
|
||||
for _, s := range d.BlockedServices {
|
||||
if !BlockedSvcKnown(s) {
|
||||
log.Debug("skipping unknown blocked-service '%s'", s)
|
||||
log.Debug("skipping unknown blocked-service %q", s)
|
||||
continue
|
||||
}
|
||||
bsvcs = append(bsvcs, s)
|
||||
@@ -750,7 +761,7 @@ func (d *Dnsfilter) Start() {
|
||||
// stats
|
||||
//
|
||||
|
||||
// GetStats return dns filtering stats since startup
|
||||
// GetStats return dns filtering stats since startup.
|
||||
func (d *Dnsfilter) GetStats() Stats {
|
||||
return gctx.stats
|
||||
}
|
||||
@@ -1,18 +1,23 @@
|
||||
package dnsfilter
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"path"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/testutil"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/AdguardTeam/urlfilter/rules"
|
||||
"github.com/miekg/dns"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
testutil.DiscardLogOutput(m)
|
||||
}
|
||||
|
||||
var setts RequestFilteringSettings
|
||||
|
||||
// HELPERS
|
||||
@@ -36,13 +41,6 @@ func purgeCaches() {
|
||||
}
|
||||
}
|
||||
|
||||
func _Func() string {
|
||||
pc := make([]uintptr, 10) // at least 1 entry needed
|
||||
runtime.Callers(2, pc)
|
||||
f := runtime.FuncForPC(pc[0])
|
||||
return path.Base(f.Name())
|
||||
}
|
||||
|
||||
func NewForTest(c *Config, filters []Filter) *Dnsfilter {
|
||||
setts = RequestFilteringSettings{}
|
||||
setts.FilteringEnabled = true
|
||||
@@ -71,7 +69,7 @@ func (d *Dnsfilter) checkMatch(t *testing.T, hostname string) {
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Dnsfilter) checkMatchIP(t *testing.T, hostname string, ip string, qtype uint16) {
|
||||
func (d *Dnsfilter) checkMatchIP(t *testing.T, hostname, ip string, qtype uint16) {
|
||||
t.Helper()
|
||||
ret, err := d.CheckHost(hostname, qtype, &setts)
|
||||
if err != nil {
|
||||
@@ -107,7 +105,7 @@ func TestEtcHostsMatching(t *testing.T) {
|
||||
::1 host2
|
||||
`,
|
||||
addr, addr6)
|
||||
filters := []Filter{Filter{
|
||||
filters := []Filter{{
|
||||
ID: 0, Data: []byte(text),
|
||||
}}
|
||||
d := NewForTest(nil, filters)
|
||||
@@ -147,10 +145,17 @@ func TestEtcHostsMatching(t *testing.T) {
|
||||
// SAFE BROWSING
|
||||
|
||||
func TestSafeBrowsing(t *testing.T) {
|
||||
logOutput := &bytes.Buffer{}
|
||||
testutil.ReplaceLogWriter(t, logOutput)
|
||||
testutil.ReplaceLogLevel(t, log.DEBUG)
|
||||
|
||||
d := NewForTest(&Config{SafeBrowsingEnabled: true}, nil)
|
||||
defer d.Close()
|
||||
gctx.stats.Safebrowsing.Requests = 0
|
||||
d.checkMatch(t, "wmconvirus.narod.ru")
|
||||
|
||||
assert.True(t, strings.Contains(logOutput.String(), "SafeBrowsing lookup for wmconvirus.narod.ru"))
|
||||
|
||||
d.checkMatch(t, "test.wmconvirus.narod.ru")
|
||||
d.checkMatchEmpty(t, "yandex.ru")
|
||||
d.checkMatchEmpty(t, "pornhub.com")
|
||||
@@ -300,7 +305,6 @@ func TestSafeSearchCacheGoogle(t *testing.T) {
|
||||
t.Fatalf("Failed to lookup for %s", safeDomain)
|
||||
}
|
||||
|
||||
t.Logf("IP addresses: %v", ips)
|
||||
ip := ips[0]
|
||||
for _, i := range ips {
|
||||
if i.To4() != nil {
|
||||
@@ -334,9 +338,14 @@ func TestSafeSearchCacheGoogle(t *testing.T) {
|
||||
// PARENTAL
|
||||
|
||||
func TestParentalControl(t *testing.T) {
|
||||
logOutput := &bytes.Buffer{}
|
||||
testutil.ReplaceLogWriter(t, logOutput)
|
||||
testutil.ReplaceLogLevel(t, log.DEBUG)
|
||||
|
||||
d := NewForTest(&Config{ParentalEnabled: true}, nil)
|
||||
defer d.Close()
|
||||
d.checkMatch(t, "pornhub.com")
|
||||
assert.True(t, strings.Contains(logOutput.String(), "Parental lookup for pornhub.com"))
|
||||
d.checkMatch(t, "www.pornhub.com")
|
||||
d.checkMatchEmpty(t, "www.yandex.ru")
|
||||
d.checkMatchEmpty(t, "yandex.ru")
|
||||
@@ -351,11 +360,15 @@ func TestParentalControl(t *testing.T) {
|
||||
|
||||
// FILTERING
|
||||
|
||||
var blockingRules = "||example.org^\n"
|
||||
var whitelistRules = "||example.org^\n@@||test.example.org\n"
|
||||
var importantRules = "@@||example.org^\n||test.example.org^$important\n"
|
||||
var regexRules = "/example\\.org/\n@@||test.example.org^\n"
|
||||
var maskRules = "test*.example.org^\nexam*.com\n"
|
||||
const nl = "\n"
|
||||
|
||||
const (
|
||||
blockingRules = `||example.org^` + nl
|
||||
whitelistRules = `||example.org^` + nl + `@@||test.example.org` + nl
|
||||
importantRules = `@@||example.org^` + nl + `||test.example.org^$important` + nl
|
||||
regexRules = `/example\.org/` + nl + `@@||test.example.org^` + nl
|
||||
maskRules = `test*.example.org^` + nl + `exam*.com` + nl
|
||||
)
|
||||
|
||||
var tests = []struct {
|
||||
testname string
|
||||
@@ -406,7 +419,7 @@ var tests = []struct {
|
||||
func TestMatching(t *testing.T) {
|
||||
for _, test := range tests {
|
||||
t.Run(fmt.Sprintf("%s-%s", test.testname, test.hostname), func(t *testing.T) {
|
||||
filters := []Filter{Filter{
|
||||
filters := []Filter{{
|
||||
ID: 0, Data: []byte(test.rules),
|
||||
}}
|
||||
d := NewForTest(nil, filters)
|
||||
@@ -430,14 +443,14 @@ func TestWhitelist(t *testing.T) {
|
||||
rules := `||host1^
|
||||
||host2^
|
||||
`
|
||||
filters := []Filter{Filter{
|
||||
filters := []Filter{{
|
||||
ID: 0, Data: []byte(rules),
|
||||
}}
|
||||
|
||||
whiteRules := `||host1^
|
||||
||host3^
|
||||
`
|
||||
whiteFilters := []Filter{Filter{
|
||||
whiteFilters := []Filter{{
|
||||
ID: 0, Data: []byte(whiteRules),
|
||||
}}
|
||||
d := NewForTest(nil, filters)
|
||||
@@ -455,7 +468,6 @@ func TestWhitelist(t *testing.T) {
|
||||
assert.True(t, err == nil)
|
||||
assert.True(t, ret.IsFiltered && ret.Reason == FilteredBlackList)
|
||||
assert.True(t, ret.Rule == "||host2^")
|
||||
|
||||
}
|
||||
|
||||
// CLIENT SETTINGS
|
||||
@@ -476,7 +488,7 @@ func applyClientSettings(setts *RequestFilteringSettings) {
|
||||
// then apply per-client settings and check behaviour once again
|
||||
func TestClientSettings(t *testing.T) {
|
||||
var r Result
|
||||
filters := []Filter{Filter{
|
||||
filters := []Filter{{
|
||||
ID: 0, Data: []byte("||example.org^\n"),
|
||||
}}
|
||||
d := NewForTest(&Config{ParentalEnabled: true, SafeBrowsingEnabled: false}, filters)
|
||||
@@ -532,13 +544,6 @@ func TestClientSettings(t *testing.T) {
|
||||
assert.True(t, r.IsFiltered && r.Reason == FilteredBlockedService)
|
||||
}
|
||||
|
||||
func prepareTestDir() string {
|
||||
const dir = "./agh-test"
|
||||
_ = os.RemoveAll(dir)
|
||||
_ = os.MkdirAll(dir, 0755)
|
||||
return dir
|
||||
}
|
||||
|
||||
// BENCHMARKS
|
||||
|
||||
func BenchmarkSafeBrowsing(b *testing.B) {
|
||||
@@ -22,8 +22,7 @@ func (d *Dnsfilter) setCacheResult(cache cache.Cache, host string, res Result) i
|
||||
var buf bytes.Buffer
|
||||
|
||||
expire := uint(time.Now().Unix()) + d.Config.CacheTime*60
|
||||
var exp []byte
|
||||
exp = make([]byte, 4)
|
||||
exp := make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(exp, uint32(expire))
|
||||
_, _ = buf.Write(exp)
|
||||
|
||||
@@ -22,11 +22,13 @@ import (
|
||||
"golang.org/x/net/publicsuffix"
|
||||
)
|
||||
|
||||
const dnsTimeout = 3 * time.Second
|
||||
const defaultSafebrowsingServer = "https://dns-family.adguard.com/dns-query"
|
||||
const defaultParentalServer = "https://dns-family.adguard.com/dns-query"
|
||||
const sbTXTSuffix = "sb.dns.adguard.com."
|
||||
const pcTXTSuffix = "pc.dns.adguard.com."
|
||||
const (
|
||||
dnsTimeout = 3 * time.Second
|
||||
defaultSafebrowsingServer = `https://dns-family.adguard.com/dns-query`
|
||||
defaultParentalServer = `https://dns-family.adguard.com/dns-query`
|
||||
sbTXTSuffix = `sb.dns.adguard.com.`
|
||||
pcTXTSuffix = `pc.dns.adguard.com.`
|
||||
)
|
||||
|
||||
func (d *Dnsfilter) initSecurityServices() error {
|
||||
var err error
|
||||
@@ -60,7 +62,7 @@ expire byte[4]
|
||||
hash byte[32]
|
||||
...
|
||||
*/
|
||||
func (c *sbCtx) setCache(prefix []byte, hashes []byte) {
|
||||
func (c *sbCtx) setCache(prefix, hashes []byte) {
|
||||
d := make([]byte, 4+len(hashes))
|
||||
expire := uint(time.Now().Unix()) + c.cacheTime*60
|
||||
binary.BigEndian.PutUint32(d[:4], uint32(expire))
|
||||
@@ -158,16 +160,28 @@ func hostnameToHashes(host string) map[[32]byte]string {
|
||||
|
||||
// convert hash array to string
|
||||
func (c *sbCtx) getQuestion() string {
|
||||
q := ""
|
||||
b := &strings.Builder{}
|
||||
encoder := hex.NewEncoder(b)
|
||||
|
||||
for hash := range c.hashToHost {
|
||||
q += fmt.Sprintf("%s.", hex.EncodeToString(hash[0:2]))
|
||||
// Ignore errors, since strings.(*Buffer).Write never returns
|
||||
// errors.
|
||||
//
|
||||
// TODO(e.burkov, a.garipov): Find out and document why exactly
|
||||
// this slice.
|
||||
_, _ = encoder.Write(hash[0:2])
|
||||
_, _ = b.WriteRune('.')
|
||||
}
|
||||
|
||||
if c.svc == "SafeBrowsing" {
|
||||
q += sbTXTSuffix
|
||||
} else {
|
||||
q += pcTXTSuffix
|
||||
// See comment above.
|
||||
_, _ = b.WriteString(sbTXTSuffix)
|
||||
return b.String()
|
||||
}
|
||||
return q
|
||||
|
||||
// See comment above.
|
||||
_, _ = b.WriteString(pcTXTSuffix)
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// Find the target hash in TXT response
|
||||
@@ -5,7 +5,9 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/agherr"
|
||||
"github.com/AdguardTeam/golibs/cache"
|
||||
"github.com/miekg/dns"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -23,16 +25,16 @@ func TestSafeBrowsingHash(t *testing.T) {
|
||||
assert.False(t, ok)
|
||||
|
||||
c := &sbCtx{
|
||||
svc: "SafeBrowsing",
|
||||
svc: "SafeBrowsing",
|
||||
hashToHost: hashes,
|
||||
}
|
||||
|
||||
// test getQuestion()
|
||||
c.hashToHost = hashes
|
||||
q := c.getQuestion()
|
||||
assert.True(t, strings.Index(q, "7a1b.") >= 0)
|
||||
assert.True(t, strings.Index(q, "af5a.") >= 0)
|
||||
assert.True(t, strings.Index(q, "eb11.") >= 0)
|
||||
assert.True(t, strings.Index(q, "sb.dns.adguard.com.") > 0)
|
||||
|
||||
assert.True(t, strings.Contains(q, "7a1b."))
|
||||
assert.True(t, strings.Contains(q, "af5a."))
|
||||
assert.True(t, strings.Contains(q, "eb11."))
|
||||
assert.True(t, strings.HasSuffix(q, "sb.dns.adguard.com."))
|
||||
}
|
||||
|
||||
func TestSafeBrowsingCache(t *testing.T) {
|
||||
@@ -88,4 +90,47 @@ func TestSafeBrowsingCache(t *testing.T) {
|
||||
hash = sha256.Sum256([]byte("nonexisting.com"))
|
||||
_, ok = c.hashToHost[hash]
|
||||
assert.True(t, ok)
|
||||
|
||||
c = &sbCtx{
|
||||
svc: "SafeBrowsing",
|
||||
cacheTime: 100,
|
||||
}
|
||||
conf = cache.Config{}
|
||||
c.cache = cache.New(conf)
|
||||
|
||||
hash = sha256.Sum256([]byte("sub.host.com"))
|
||||
c.hashToHost = make(map[[32]byte]string)
|
||||
c.hashToHost[hash] = "sub.host.com"
|
||||
|
||||
c.cache.Set(hash[0:2], make([]byte, 32))
|
||||
assert.Equal(t, 0, c.getCached())
|
||||
}
|
||||
|
||||
// testErrUpstream implements upstream.Upstream interface for replacing real
|
||||
// upstream in tests.
|
||||
type testErrUpstream struct{}
|
||||
|
||||
// Exchange always returns nil Msg and non-nil error.
|
||||
func (teu *testErrUpstream) Exchange(*dns.Msg) (*dns.Msg, error) {
|
||||
return nil, agherr.Error("bad")
|
||||
}
|
||||
|
||||
func (teu *testErrUpstream) Address() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func TestSBPC_checkErrorUpstream(t *testing.T) {
|
||||
d := NewForTest(&Config{SafeBrowsingEnabled: true}, nil)
|
||||
defer d.Close()
|
||||
|
||||
ups := &testErrUpstream{}
|
||||
|
||||
d.safeBrowsingUpstream = ups
|
||||
d.parentalUpstream = ups
|
||||
|
||||
_, err := d.checkSafeBrowsing("smthng.com")
|
||||
assert.NotNil(t, err)
|
||||
|
||||
_, err = d.checkParental("smthng.com")
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
@@ -51,7 +51,7 @@ func (a *accessCtx) Init(allowedClients, disallowedClients, blockedHosts []strin
|
||||
listArray = append(listArray, list)
|
||||
rulesStorage, err := filterlist.NewRuleStorage(listArray)
|
||||
if err != nil {
|
||||
return fmt.Errorf("filterlist.NewRuleStorage(): %s", err)
|
||||
return fmt.Errorf("filterlist.NewRuleStorage(): %w", err)
|
||||
}
|
||||
a.blockedHostsEngine = urlfilter.NewDNSEngine(rulesStorage)
|
||||
|
||||
@@ -10,12 +10,11 @@ import (
|
||||
"net/http"
|
||||
"sort"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/dnsfilter"
|
||||
"github.com/AdguardTeam/AdGuardHome/util"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/dnsfilter"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/util"
|
||||
"github.com/AdguardTeam/dnsproxy/proxy"
|
||||
"github.com/AdguardTeam/dnsproxy/upstream"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/joomcode/errorx"
|
||||
)
|
||||
|
||||
// FilteringConfig represents the DNS filtering configuration of AdGuard Home
|
||||
@@ -252,14 +251,14 @@ func (s *Server) prepareUpstreamSettings() error {
|
||||
upstreams = filterOutComments(upstreams)
|
||||
upstreamConfig, err := proxy.ParseUpstreamsConfig(upstreams, s.conf.BootstrapDNS, DefaultTimeout)
|
||||
if err != nil {
|
||||
return fmt.Errorf("DNS: proxy.ParseUpstreamsConfig: %s", err)
|
||||
return fmt.Errorf("dns: proxy.ParseUpstreamsConfig: %w", err)
|
||||
}
|
||||
|
||||
if len(upstreamConfig.Upstreams) == 0 {
|
||||
log.Info("Warning: no default upstream servers specified, using %v", defaultDNS)
|
||||
uc, err := proxy.ParseUpstreamsConfig(defaultDNS, s.conf.BootstrapDNS, DefaultTimeout)
|
||||
if err != nil {
|
||||
return fmt.Errorf("DNS: failed to parse default upstreams: %v", err)
|
||||
return fmt.Errorf("dns: failed to parse default upstreams: %v", err)
|
||||
}
|
||||
upstreamConfig.Upstreams = uc.Upstreams
|
||||
}
|
||||
@@ -300,13 +299,13 @@ func (s *Server) prepareTLS(proxyConfig *proxy.Config) error {
|
||||
var err error
|
||||
s.conf.cert, err = tls.X509KeyPair(s.conf.CertificateChainData, s.conf.PrivateKeyData)
|
||||
if err != nil {
|
||||
return errorx.Decorate(err, "Failed to parse TLS keypair")
|
||||
return fmt.Errorf("failed to parse TLS keypair: %w", err)
|
||||
}
|
||||
|
||||
if s.conf.StrictSNICheck {
|
||||
x, err := x509.ParseCertificate(s.conf.cert.Certificate[0])
|
||||
if err != nil {
|
||||
return errorx.Decorate(err, "x509.ParseCertificate(): %s", err)
|
||||
return fmt.Errorf("x509.ParseCertificate(): %w", err)
|
||||
}
|
||||
if len(x.DNSNames) != 0 {
|
||||
s.conf.dnsNames = x.DNSNames
|
||||
@@ -1,3 +1,4 @@
|
||||
// Package dnsforward contains a DNS forwarding server.
|
||||
package dnsforward
|
||||
|
||||
import (
|
||||
@@ -8,13 +9,12 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/dhcpd"
|
||||
"github.com/AdguardTeam/AdGuardHome/dnsfilter"
|
||||
"github.com/AdguardTeam/AdGuardHome/querylog"
|
||||
"github.com/AdguardTeam/AdGuardHome/stats"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/dhcpd"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/dnsfilter"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/querylog"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/stats"
|
||||
"github.com/AdguardTeam/dnsproxy/proxy"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/joomcode/errorx"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
@@ -181,12 +181,9 @@ func (s *Server) Prepare(config *ServerConfig) error {
|
||||
s.conf.BlockingIPAddrv4 = net.ParseIP(s.conf.BlockingIPv4)
|
||||
s.conf.BlockingIPAddrv6 = net.ParseIP(s.conf.BlockingIPv6)
|
||||
if s.conf.BlockingIPAddrv4 == nil || s.conf.BlockingIPAddrv6 == nil {
|
||||
return fmt.Errorf("DNS: invalid custom blocking IP address specified")
|
||||
return fmt.Errorf("dns: invalid custom blocking IP address specified")
|
||||
}
|
||||
}
|
||||
if s.conf.MaxGoroutines == 0 {
|
||||
s.conf.MaxGoroutines = 50
|
||||
}
|
||||
}
|
||||
|
||||
// Set default values in the case if nothing is configured
|
||||
@@ -249,7 +246,7 @@ func (s *Server) stopInternal() error {
|
||||
if s.dnsProxy != nil {
|
||||
err := s.dnsProxy.Stop()
|
||||
if err != nil {
|
||||
return errorx.Decorate(err, "could not stop the DNS server properly")
|
||||
return fmt.Errorf("could not stop the DNS server properly: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -272,7 +269,7 @@ func (s *Server) Reconfigure(config *ServerConfig) error {
|
||||
log.Print("Start reconfiguring the server")
|
||||
err := s.stopInternal()
|
||||
if err != nil {
|
||||
return errorx.Decorate(err, "could not reconfigure the server")
|
||||
return fmt.Errorf("could not reconfigure the server: %w", err)
|
||||
}
|
||||
|
||||
// It seems that net.Listener.Close() doesn't close file descriptors right away.
|
||||
@@ -281,12 +278,12 @@ func (s *Server) Reconfigure(config *ServerConfig) error {
|
||||
|
||||
err = s.Prepare(config)
|
||||
if err != nil {
|
||||
return errorx.Decorate(err, "could not reconfigure the server")
|
||||
return fmt.Errorf("could not reconfigure the server: %w", err)
|
||||
}
|
||||
|
||||
err = s.startInternal()
|
||||
if err != nil {
|
||||
return errorx.Decorate(err, "could not reconfigure the server")
|
||||
return fmt.Errorf("could not reconfigure the server: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
|
||||
func httpError(r *http.Request, w http.ResponseWriter, code int, format string, args ...interface{}) {
|
||||
text := fmt.Sprintf(format, args...)
|
||||
log.Info("DNS: %s %s: %s", r.Method, r.URL, text)
|
||||
log.Info("dns: %s %s: %s", r.Method, r.URL, text)
|
||||
http.Error(w, text, code)
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ func checkBootstrap(addr string) error {
|
||||
}
|
||||
_, err := upstream.NewResolver(addr, 0)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid bootstrap server address: %s", err)
|
||||
return fmt.Errorf("invalid bootstrap server address: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -322,7 +322,7 @@ func separateUpstream(upstream string) (string, bool, error) {
|
||||
// split domains and upstream string
|
||||
domainsAndUpstream := strings.Split(strings.TrimPrefix(upstream, "[/"), "/]")
|
||||
if len(domainsAndUpstream) != 2 {
|
||||
return "", defaultUpstream, fmt.Errorf("wrong DNS upstream per domain specification: %s", upstream)
|
||||
return "", defaultUpstream, fmt.Errorf("wrong dns upstream per domain specification: %s", upstream)
|
||||
}
|
||||
|
||||
// split domains list and validate each one
|
||||
@@ -357,7 +357,7 @@ func checkPlainDNS(upstream string) error {
|
||||
|
||||
_, err = strconv.ParseInt(port, 0, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s is not a valid port: %s", port, err)
|
||||
return fmt.Errorf("%s is not a valid port: %w", port, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -371,11 +371,6 @@ func (s *Server) handleTestUpstreamDNS(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if len(req.Upstreams) == 0 {
|
||||
httpError(r, w, http.StatusBadRequest, "No servers specified")
|
||||
return
|
||||
}
|
||||
|
||||
result := map[string]string{}
|
||||
|
||||
for _, host := range req.Upstreams {
|
||||
@@ -410,7 +405,7 @@ func checkDNS(input string, bootstrap []string) error {
|
||||
// separate upstream from domains list
|
||||
input, defaultUpstream, err := separateUpstream(input)
|
||||
if err != nil {
|
||||
return fmt.Errorf("wrong upstream format: %s", err)
|
||||
return fmt.Errorf("wrong upstream format: %w", err)
|
||||
}
|
||||
|
||||
// No need to check this DNS server
|
||||
@@ -419,17 +414,17 @@ func checkDNS(input string, bootstrap []string) error {
|
||||
}
|
||||
|
||||
if _, err := validateUpstream(input); err != nil {
|
||||
return fmt.Errorf("wrong upstream format: %s", err)
|
||||
return fmt.Errorf("wrong upstream format: %w", err)
|
||||
}
|
||||
|
||||
if len(bootstrap) == 0 {
|
||||
bootstrap = defaultBootstrap
|
||||
}
|
||||
|
||||
log.Debug("Checking if DNS %s works...", input)
|
||||
log.Debug("checking if dns %s works...", input)
|
||||
u, err := upstream.AddressToUpstream(input, upstream.Options{Bootstrap: bootstrap, Timeout: DefaultTimeout})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to choose upstream for %s: %s", input, err)
|
||||
return fmt.Errorf("failed to choose upstream for %s: %w", input, err)
|
||||
}
|
||||
|
||||
req := dns.Msg{}
|
||||
@@ -440,18 +435,18 @@ func checkDNS(input string, bootstrap []string) error {
|
||||
}
|
||||
reply, err := u.Exchange(&req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("couldn't communicate with DNS server %s: %s", input, err)
|
||||
return fmt.Errorf("couldn't communicate with dns server %s: %w", input, err)
|
||||
}
|
||||
if len(reply.Answer) != 1 {
|
||||
return fmt.Errorf("DNS server %s returned wrong answer", input)
|
||||
return fmt.Errorf("dns server %s returned wrong answer", input)
|
||||
}
|
||||
if t, ok := reply.Answer[0].(*dns.A); ok {
|
||||
if !net.IPv4(8, 8, 8, 8).Equal(t.A) {
|
||||
return fmt.Errorf("DNS server %s returned wrong answer: %v", input, t.A)
|
||||
return fmt.Errorf("dns server %s returned wrong answer: %v", input, t.A)
|
||||
}
|
||||
}
|
||||
|
||||
log.Debug("DNS %s works OK", input)
|
||||
log.Debug("dns %s works OK", input)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -467,7 +462,7 @@ func (s *Server) handleDOH(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
if !s.IsRunning() {
|
||||
httpError(r, w, http.StatusInternalServerError, "DNS server is not running")
|
||||
httpError(r, w, http.StatusInternalServerError, "dns server is not running")
|
||||
return
|
||||
}
|
||||
|
||||
170
internal/dnsforward/dnsforward_http_test.go
Normal file
170
internal/dnsforward/dnsforward_http_test.go
Normal file
@@ -0,0 +1,170 @@
|
||||
package dnsforward
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestDNSForwardHTTTP_handleGetConfig(t *testing.T) {
|
||||
s := createTestServer(t)
|
||||
err := s.Start()
|
||||
assert.Nil(t, err)
|
||||
defer assert.Nil(t, s.Stop())
|
||||
|
||||
defaultConf := s.conf
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
conf func() ServerConfig
|
||||
want string
|
||||
}{{
|
||||
name: "all_right",
|
||||
conf: func() ServerConfig {
|
||||
return defaultConf
|
||||
},
|
||||
want: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0}",
|
||||
}, {
|
||||
name: "fastest_addr",
|
||||
conf: func() ServerConfig {
|
||||
conf := defaultConf
|
||||
conf.FastestAddr = true
|
||||
return conf
|
||||
},
|
||||
want: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"fastest_addr\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0}",
|
||||
}, {
|
||||
name: "parallel",
|
||||
conf: func() ServerConfig {
|
||||
conf := defaultConf
|
||||
conf.AllServers = true
|
||||
return conf
|
||||
},
|
||||
want: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"parallel\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0}",
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
s.conf = tc.conf()
|
||||
s.handleGetConfig(w, nil)
|
||||
assert.Equal(t, tc.want, w.Body.String())
|
||||
|
||||
assert.Equal(t, "application/json", w.Header().Get("Content-Type"))
|
||||
})
|
||||
w.Body.Reset()
|
||||
}
|
||||
}
|
||||
|
||||
func TestDNSForwardHTTTP_handleSetConfig(t *testing.T) {
|
||||
s := createTestServer(t)
|
||||
|
||||
defaultConf := s.conf
|
||||
|
||||
err := s.Start()
|
||||
assert.Nil(t, err)
|
||||
defer func() {
|
||||
assert.Nil(t, s.Stop())
|
||||
}()
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
const defaultConfJSON = "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0}"
|
||||
testCases := []struct {
|
||||
name string
|
||||
req string
|
||||
wantSet string
|
||||
wantGet string
|
||||
}{{
|
||||
name: "upstream_dns",
|
||||
req: "{\"upstream_dns\":[\"8.8.8.8:77\",\"8.8.4.4:77\"]}",
|
||||
wantSet: "",
|
||||
wantGet: "{\"upstream_dns\":[\"8.8.8.8:77\",\"8.8.4.4:77\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0}",
|
||||
}, {
|
||||
name: "bootstraps",
|
||||
req: "{\"bootstrap_dns\":[\"9.9.9.10\"]}",
|
||||
wantSet: "",
|
||||
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0}",
|
||||
}, {
|
||||
name: "blocking_mode_good",
|
||||
req: "{\"blocking_mode\":\"refused\"}",
|
||||
wantSet: "",
|
||||
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"refused\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0}",
|
||||
}, {
|
||||
name: "blocking_mode_bad",
|
||||
req: "{\"blocking_mode\":\"custom_ip\"}",
|
||||
wantSet: "blocking_mode: incorrect value\n",
|
||||
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0}",
|
||||
}, {
|
||||
name: "ratelimit",
|
||||
req: "{\"ratelimit\":6}",
|
||||
wantSet: "",
|
||||
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":6,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0}",
|
||||
}, {
|
||||
name: "edns_cs_enabled",
|
||||
req: "{\"edns_cs_enabled\":true}",
|
||||
wantSet: "",
|
||||
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":true,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0}",
|
||||
}, {
|
||||
name: "dnssec_enabled",
|
||||
req: "{\"dnssec_enabled\":true}",
|
||||
wantSet: "",
|
||||
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":true,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0}",
|
||||
}, {
|
||||
name: "cache_size",
|
||||
req: "{\"cache_size\":1024}",
|
||||
wantSet: "",
|
||||
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":1024,\"cache_ttl_min\":0,\"cache_ttl_max\":0}",
|
||||
}, {
|
||||
name: "upstream_mode_parallel",
|
||||
req: "{\"upstream_mode\":\"parallel\"}",
|
||||
wantSet: "",
|
||||
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"parallel\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0}",
|
||||
}, {
|
||||
name: "upstream_mode_fastest_addr",
|
||||
req: "{\"upstream_mode\":\"fastest_addr\"}",
|
||||
wantSet: "",
|
||||
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"fastest_addr\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0}",
|
||||
}, {
|
||||
name: "upstream_dns_bad",
|
||||
req: "{\"upstream_dns\":[\"\"]}",
|
||||
wantSet: "wrong upstreams specification: missing port in address\n",
|
||||
wantGet: defaultConfJSON,
|
||||
}, {
|
||||
name: "bootstraps_bad",
|
||||
req: "{\"bootstrap_dns\":[\"a\"]}",
|
||||
wantSet: "a can not be used as bootstrap dns cause: invalid bootstrap server address: Resolver a is not eligible to be a bootstrap DNS server\n",
|
||||
wantGet: defaultConfJSON,
|
||||
}, {
|
||||
name: "cache_bad_ttl",
|
||||
req: "{\"cache_ttl_min\":1024,\"cache_ttl_max\":512}",
|
||||
wantSet: "cache_ttl_min must be less or equal than cache_ttl_max\n",
|
||||
wantGet: defaultConfJSON,
|
||||
}, {
|
||||
name: "upstream_mode_bad",
|
||||
req: "{\"upstream_mode\":\"somethingelse\"}",
|
||||
wantSet: "upstream_mode: incorrect value\n",
|
||||
wantGet: defaultConfJSON,
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
rBody := ioutil.NopCloser(strings.NewReader(tc.req))
|
||||
r, err := http.NewRequest(http.MethodPost, "http://example.com", rBody)
|
||||
assert.Nil(t, err)
|
||||
|
||||
s.handleSetConfig(w, r)
|
||||
assert.Equal(t, tc.wantSet, w.Body.String())
|
||||
w.Body.Reset()
|
||||
|
||||
s.handleGetConfig(w, nil)
|
||||
assert.Equal(t, tc.wantGet, w.Body.String())
|
||||
w.Body.Reset()
|
||||
})
|
||||
s.conf = defaultConf
|
||||
}
|
||||
}
|
||||
@@ -18,16 +18,21 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/util"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/testutil"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/util"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/dhcpd"
|
||||
"github.com/AdguardTeam/AdGuardHome/dnsfilter"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/dhcpd"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/dnsfilter"
|
||||
"github.com/AdguardTeam/dnsproxy/proxy"
|
||||
"github.com/AdguardTeam/dnsproxy/upstream"
|
||||
"github.com/miekg/dns"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
testutil.DiscardLogOutput(m)
|
||||
}
|
||||
|
||||
const (
|
||||
tlsServerName = "testdns.adguard.com"
|
||||
testMessagesCount = 10
|
||||
@@ -751,11 +756,14 @@ func createTestServer(t *testing.T) *Server {
|
||||
c.CacheTime = 30
|
||||
|
||||
f := dnsfilter.New(&c, filters)
|
||||
|
||||
s := NewServer(DNSCreateParams{DNSFilter: f})
|
||||
s.conf.UDPListenAddr = &net.UDPAddr{Port: 0}
|
||||
s.conf.TCPListenAddr = &net.TCPAddr{Port: 0}
|
||||
s.conf.UpstreamDNS = []string{"8.8.8.8:53", "8.8.4.4:53"}
|
||||
s.conf.FilteringConfig.ProtectionEnabled = true
|
||||
s.conf.ConfigModified = func() {}
|
||||
|
||||
err := s.Prepare(nil)
|
||||
assert.True(t, err == nil)
|
||||
return s
|
||||
@@ -815,12 +823,12 @@ func sendTestMessageAsync(t *testing.T, conn *dns.Conn, g *sync.WaitGroup) {
|
||||
req := createGoogleATestMessage()
|
||||
err := conn.WriteMsg(req)
|
||||
if err != nil {
|
||||
t.Fatalf("cannot write message: %s", err)
|
||||
panic(fmt.Sprintf("cannot write message: %s", err))
|
||||
}
|
||||
|
||||
res, err := conn.ReadMsg()
|
||||
if err != nil {
|
||||
t.Fatalf("cannot read response to message: %s", err)
|
||||
panic(fmt.Sprintf("cannot read response to message: %s", err))
|
||||
}
|
||||
assertGoogleAResponse(t, res)
|
||||
}
|
||||
@@ -916,20 +924,23 @@ func publicKey(priv interface{}) interface{} {
|
||||
}
|
||||
|
||||
func TestValidateUpstream(t *testing.T) {
|
||||
invalidUpstreams := []string{"1.2.3.4.5",
|
||||
invalidUpstreams := []string{
|
||||
"1.2.3.4.5",
|
||||
"123.3.7m",
|
||||
"htttps://google.com/dns-query",
|
||||
"[/host.com]tls://dns.adguard.com",
|
||||
"[host.ru]#",
|
||||
}
|
||||
|
||||
validDefaultUpstreams := []string{"1.1.1.1",
|
||||
validDefaultUpstreams := []string{
|
||||
"1.1.1.1",
|
||||
"tls://1.1.1.1",
|
||||
"https://dns.adguard.com/dns-query",
|
||||
"sdns://AQMAAAAAAAAAFDE3Ni4xMDMuMTMwLjEzMDo1NDQzINErR_JS3PLCu_iZEIbq95zkSV2LFsigxDIuUso_OQhzIjIuZG5zY3J5cHQuZGVmYXVsdC5uczEuYWRndWFyZC5jb20",
|
||||
}
|
||||
|
||||
validUpstreams := []string{"[/host.com/]1.1.1.1",
|
||||
validUpstreams := []string{
|
||||
"[/host.com/]1.1.1.1",
|
||||
"[//]tls://1.1.1.1",
|
||||
"[/www.host.com/]#",
|
||||
"[/host.com/google.com/]8.8.8.8",
|
||||
@@ -975,7 +986,8 @@ func TestValidateUpstreamsSet(t *testing.T) {
|
||||
assert.Nil(t, err, "comments should not be validated")
|
||||
|
||||
// Set of valid upstreams. There is no default upstream specified
|
||||
upstreamsSet = []string{"[/host.com/]1.1.1.1",
|
||||
upstreamsSet = []string{
|
||||
"[/host.com/]1.1.1.1",
|
||||
"[//]tls://1.1.1.1",
|
||||
"[/www.host.com/]#",
|
||||
"[/host.com/google.com/]8.8.8.8",
|
||||
@@ -1029,9 +1041,7 @@ func (d *testDHCP) Leases(flags int) []dhcpd.Lease {
|
||||
l.Hostname = "localhost"
|
||||
return []dhcpd.Lease{l}
|
||||
}
|
||||
func (d *testDHCP) SetOnLeaseChanged(onLeaseChanged dhcpd.OnLeaseChangedT) {
|
||||
return
|
||||
}
|
||||
func (d *testDHCP) SetOnLeaseChanged(onLeaseChanged dhcpd.OnLeaseChangedT) {}
|
||||
|
||||
func TestPTRResponseFromDHCPLeases(t *testing.T) {
|
||||
dhcp := &testDHCP{}
|
||||
@@ -1,12 +1,13 @@
|
||||
package dnsforward
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/dnsfilter"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/dnsfilter"
|
||||
"github.com/AdguardTeam/dnsproxy/proxy"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/joomcode/errorx"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
@@ -49,29 +50,32 @@ func (s *Server) filterDNSRequest(ctx *dnsContext) (*dnsfilter.Result, error) {
|
||||
res, err := s.dnsFilter.CheckHost(host, d.Req.Question[0].Qtype, ctx.setts)
|
||||
if err != nil {
|
||||
// Return immediately if there's an error
|
||||
return nil, errorx.Decorate(err, "dnsfilter failed to check host '%s'", host)
|
||||
|
||||
return nil, fmt.Errorf("dnsfilter failed to check host %q: %w", host, err)
|
||||
} else if res.IsFiltered {
|
||||
log.Tracef("Host %s is filtered, reason - '%s', matched rule: '%s'", host, res.Reason, res.Rule)
|
||||
log.Tracef("Host %s is filtered, reason - %q, matched rule: %q", host, res.Reason, res.Rule)
|
||||
d.Res = s.genDNSFilterMessage(d, &res)
|
||||
|
||||
} else if res.Reason == dnsfilter.ReasonRewrite && len(res.CanonName) != 0 && len(res.IPList) == 0 {
|
||||
ctx.origQuestion = d.Req.Question[0]
|
||||
// resolve canonical name, not the original host name
|
||||
d.Req.Question[0].Name = dns.Fqdn(res.CanonName)
|
||||
|
||||
} else if res.Reason == dnsfilter.RewriteEtcHosts && len(res.ReverseHost) != 0 {
|
||||
|
||||
} else if res.Reason == dnsfilter.RewriteEtcHosts && len(res.ReverseHosts) != 0 {
|
||||
resp := s.makeResponse(req)
|
||||
ptr := &dns.PTR{}
|
||||
ptr.Hdr = dns.RR_Header{
|
||||
Name: req.Question[0].Name,
|
||||
Rrtype: dns.TypePTR,
|
||||
Ttl: s.conf.BlockedResponseTTL,
|
||||
Class: dns.ClassINET,
|
||||
for _, h := range res.ReverseHosts {
|
||||
hdr := dns.RR_Header{
|
||||
Name: req.Question[0].Name,
|
||||
Rrtype: dns.TypePTR,
|
||||
Ttl: s.conf.BlockedResponseTTL,
|
||||
Class: dns.ClassINET,
|
||||
}
|
||||
|
||||
ptr := &dns.PTR{
|
||||
Hdr: hdr,
|
||||
Ptr: h,
|
||||
}
|
||||
|
||||
resp.Answer = append(resp.Answer, ptr)
|
||||
}
|
||||
ptr.Ptr = res.ReverseHost
|
||||
resp.Answer = append(resp.Answer, ptr)
|
||||
|
||||
d.Res = resp
|
||||
} else if res.Reason == dnsfilter.ReasonRewrite || res.Reason == dnsfilter.RewriteEtcHosts {
|
||||
resp := s.makeResponse(req)
|
||||
@@ -136,7 +140,6 @@ func (s *Server) filterDNSResponse(ctx *dnsContext) (*dnsfilter.Result, error) {
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
} else if res.IsFiltered {
|
||||
d.Res = s.genDNSFilterMessage(d, &res)
|
||||
log.Debug("DNSFwd: Matched %s by response: %s", d.Req.Question[0].Name, host)
|
||||
@@ -5,9 +5,9 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/dhcpd"
|
||||
"github.com/AdguardTeam/AdGuardHome/dnsfilter"
|
||||
"github.com/AdguardTeam/AdGuardHome/util"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/dhcpd"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/dnsfilter"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/util"
|
||||
"github.com/AdguardTeam/dnsproxy/proxy"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/miekg/dns"
|
||||
@@ -3,8 +3,9 @@ package dnsforward
|
||||
import (
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/util"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/util"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
@@ -12,7 +13,9 @@ import (
|
||||
type ipsetCtx struct {
|
||||
ipsetList map[string][]string // domain -> []ipset_name
|
||||
ipsetCache map[[4]byte]bool // cache for IP[] to prevent duplicate calls to ipset program
|
||||
ipset6Cache map[[16]byte]bool // cache for IP[] to prevent duplicate calls to ipset program
|
||||
ipsetMutex *sync.Mutex
|
||||
ipset6Cache map[[16]byte]bool // cache for IP[] to prevent duplicate calls to ipset program
|
||||
ipset6Mutex *sync.Mutex
|
||||
}
|
||||
|
||||
// Convert configuration settings to an internal map
|
||||
@@ -20,19 +23,21 @@ type ipsetCtx struct {
|
||||
func (c *ipsetCtx) init(ipsetConfig []string) {
|
||||
c.ipsetList = make(map[string][]string)
|
||||
c.ipsetCache = make(map[[4]byte]bool)
|
||||
c.ipsetMutex = &sync.Mutex{}
|
||||
c.ipset6Cache = make(map[[16]byte]bool)
|
||||
c.ipset6Mutex = &sync.Mutex{}
|
||||
|
||||
for _, it := range ipsetConfig {
|
||||
it = strings.TrimSpace(it)
|
||||
hostsAndNames := strings.Split(it, "/")
|
||||
if len(hostsAndNames) != 2 {
|
||||
log.Debug("IPSET: invalid value '%s'", it)
|
||||
log.Debug("IPSET: invalid value %q", it)
|
||||
continue
|
||||
}
|
||||
|
||||
ipsetNames := strings.Split(hostsAndNames[1], ",")
|
||||
if len(ipsetNames) == 0 {
|
||||
log.Debug("IPSET: invalid value '%s'", it)
|
||||
log.Debug("IPSET: invalid value %q", it)
|
||||
continue
|
||||
}
|
||||
bad := false
|
||||
@@ -44,7 +49,7 @@ func (c *ipsetCtx) init(ipsetConfig []string) {
|
||||
}
|
||||
}
|
||||
if bad {
|
||||
log.Debug("IPSET: invalid value '%s'", it)
|
||||
log.Debug("IPSET: invalid value %q", it)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -53,7 +58,7 @@ func (c *ipsetCtx) init(ipsetConfig []string) {
|
||||
host = strings.TrimSpace(host)
|
||||
host = strings.ToLower(host)
|
||||
if len(host) == 0 {
|
||||
log.Debug("IPSET: invalid value '%s'", it)
|
||||
log.Debug("IPSET: invalid value %q", it)
|
||||
continue
|
||||
}
|
||||
c.ipsetList[host] = ipsetNames
|
||||
@@ -67,6 +72,8 @@ func (c *ipsetCtx) getIP(rr dns.RR) net.IP {
|
||||
case *dns.A:
|
||||
var ip4 [4]byte
|
||||
copy(ip4[:], a.A.To4())
|
||||
c.ipsetMutex.Lock()
|
||||
defer c.ipsetMutex.Unlock()
|
||||
_, found := c.ipsetCache[ip4]
|
||||
if found {
|
||||
return nil // this IP was added before
|
||||
@@ -77,6 +84,8 @@ func (c *ipsetCtx) getIP(rr dns.RR) net.IP {
|
||||
case *dns.AAAA:
|
||||
var ip6 [16]byte
|
||||
copy(ip6[:], a.AAAA)
|
||||
c.ipset6Mutex.Lock()
|
||||
defer c.ipset6Mutex.Unlock()
|
||||
_, found := c.ipset6Cache[ip6]
|
||||
if found {
|
||||
return nil // this IP was added before
|
||||
@@ -122,7 +131,7 @@ func (c *ipsetCtx) process(ctx *dnsContext) int {
|
||||
continue
|
||||
}
|
||||
if code != 0 {
|
||||
log.Info("IPSET: ipset add: code:%d output:'%s'", code, out)
|
||||
log.Info("IPSET: ipset add: code:%d output:%q", code, out)
|
||||
continue
|
||||
}
|
||||
log.Debug("IPSET: added %s(%s) -> %s", host, ipStr, name)
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/dnsfilter"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/dnsfilter"
|
||||
"github.com/AdguardTeam/dnsproxy/proxy"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
@@ -46,7 +46,6 @@ func (s *Server) genDNSFilterMessage(d *proxy.DNSContext, result *dnsfilter.Resu
|
||||
if s.conf.BlockingMode == "null_ip" {
|
||||
// it means that we should return 0.0.0.0 or :: for any blocked request
|
||||
return s.makeResponseNullIP(m)
|
||||
|
||||
} else if s.conf.BlockingMode == "custom_ip" {
|
||||
// means that we should return custom IP for any blocked request
|
||||
|
||||
@@ -56,12 +55,10 @@ func (s *Server) genDNSFilterMessage(d *proxy.DNSContext, result *dnsfilter.Resu
|
||||
case dns.TypeAAAA:
|
||||
return s.genAAAARecord(m, s.conf.BlockingIPAddrv6)
|
||||
}
|
||||
|
||||
} else if s.conf.BlockingMode == "nxdomain" {
|
||||
// means that we should return NXDOMAIN for any blocked request
|
||||
|
||||
return s.genNXDomain(m)
|
||||
|
||||
} else if s.conf.BlockingMode == "refused" {
|
||||
// means that we should return NXDOMAIN for any blocked request
|
||||
|
||||
@@ -148,7 +145,6 @@ func (s *Server) makeResponseNullIP(req *dns.Msg) *dns.Msg {
|
||||
}
|
||||
|
||||
func (s *Server) genBlockedHost(request *dns.Msg, newAddr string, d *proxy.DNSContext) *dns.Msg {
|
||||
|
||||
ip := net.ParseIP(newAddr)
|
||||
if ip != nil {
|
||||
return s.genResponseWithIP(request, ip)
|
||||
@@ -168,7 +164,7 @@ func (s *Server) genBlockedHost(request *dns.Msg, newAddr string, d *proxy.DNSCo
|
||||
|
||||
err := s.dnsProxy.Resolve(newContext)
|
||||
if err != nil {
|
||||
log.Printf("Couldn't look up replacement host '%s': %s", newAddr, err)
|
||||
log.Printf("Couldn't look up replacement host %q: %s", newAddr, err)
|
||||
return s.genServerFailure(request)
|
||||
}
|
||||
|
||||
@@ -5,12 +5,11 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/querylog"
|
||||
"github.com/miekg/dns"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/dnsfilter"
|
||||
"github.com/AdguardTeam/AdGuardHome/stats"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/dnsfilter"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/querylog"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/stats"
|
||||
"github.com/AdguardTeam/dnsproxy/proxy"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
// Write Stats data and logs
|
||||
@@ -40,10 +39,16 @@ func processQueryLogsAndStats(ctx *dnsContext) int {
|
||||
ClientIP: getIP(d.Addr),
|
||||
}
|
||||
|
||||
if d.Proto == "https" {
|
||||
p.ClientProto = "doh"
|
||||
} else if d.Proto == "tls" {
|
||||
p.ClientProto = "dot"
|
||||
switch d.Proto {
|
||||
case proxy.ProtoHTTPS:
|
||||
p.ClientProto = querylog.ClientProtoDOH
|
||||
case proxy.ProtoQUIC:
|
||||
p.ClientProto = querylog.ClientProtoDOQ
|
||||
case proxy.ProtoTLS:
|
||||
p.ClientProto = querylog.ClientProtoDOT
|
||||
default:
|
||||
// Consider this a plain DNS-over-UDP or DNS-over-TCL
|
||||
// request.
|
||||
}
|
||||
|
||||
if d.Upstream != nil {
|
||||
@@ -27,7 +27,7 @@ func stringArrayDup(a []string) []string {
|
||||
|
||||
// Get IP address from net.Addr object
|
||||
// Note: we can't use net.SplitHostPort(a.String()) because of IPv6 zone:
|
||||
// https://github.com/AdguardTeam/AdGuardHome/issues/1261
|
||||
// https://github.com/AdguardTeam/AdGuardHome/internal/issues/1261
|
||||
func ipFromAddr(a net.Addr) string {
|
||||
switch addr := a.(type) {
|
||||
case *net.UDPAddr:
|
||||
@@ -17,22 +17,22 @@ import (
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
const cookieTTL = 365 * 24 // in hours
|
||||
const sessionCookieName = "agh_session"
|
||||
const (
|
||||
cookieTTL = 365 * 24 // in hours
|
||||
sessionCookieName = "agh_session"
|
||||
)
|
||||
|
||||
type session struct {
|
||||
userName string
|
||||
expire uint32 // expiration time (in seconds)
|
||||
}
|
||||
|
||||
/*
|
||||
expire byte[4]
|
||||
name_len byte[2]
|
||||
name byte[]
|
||||
*/
|
||||
func (s *session) serialize() []byte {
|
||||
var data []byte
|
||||
data = make([]byte, 4+2+len(s.userName))
|
||||
const (
|
||||
expireLen = 4
|
||||
nameLen = 2
|
||||
)
|
||||
data := make([]byte, expireLen+nameLen+len(s.userName))
|
||||
binary.BigEndian.PutUint32(data[0:4], s.expire)
|
||||
binary.BigEndian.PutUint16(data[4:6], uint16(len(s.userName)))
|
||||
copy(data[6:], []byte(s.userName))
|
||||
@@ -78,11 +78,11 @@ func InitAuth(dbFilename string, users []User, sessionTTL uint32) *Auth {
|
||||
a.sessions = make(map[string]*session)
|
||||
rand.Seed(time.Now().UTC().Unix())
|
||||
var err error
|
||||
a.db, err = bbolt.Open(dbFilename, 0644, nil)
|
||||
a.db, err = bbolt.Open(dbFilename, 0o644, nil)
|
||||
if err != nil {
|
||||
log.Error("Auth: open DB: %s: %s", dbFilename, err)
|
||||
if err.Error() == "invalid argument" {
|
||||
log.Error("AdGuard Home cannot be initialized due to an incompatible file system.\nPlease read the explanation here: https://github.com/AdguardTeam/AdGuardHome/wiki/Getting-Started#limitations")
|
||||
log.Error("AdGuard Home cannot be initialized due to an incompatible file system.\nPlease read the explanation here: https://github.com/AdguardTeam/AdGuardHome/internal/wiki/Getting-Started#limitations")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -318,7 +318,7 @@ func handleLogin(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
cookie := Context.auth.httpCookie(req)
|
||||
if len(cookie) == 0 {
|
||||
log.Info("Auth: invalid user name or password: name='%s'", req.Name)
|
||||
log.Info("Auth: invalid user name or password: name=%q", req.Name)
|
||||
time.Sleep(1 * time.Second)
|
||||
http.Error(w, "invalid user name or password", http.StatusBadRequest)
|
||||
return
|
||||
@@ -372,7 +372,6 @@ func parseCookie(cookie string) string {
|
||||
// nolint(gocyclo)
|
||||
func optionalAuth(handler func(http.ResponseWriter, *http.Request)) func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
if r.URL.Path == "/login.html" {
|
||||
// redirect to dashboard if already authenticated
|
||||
authRequired := Context.auth != nil && Context.auth.AuthRequired()
|
||||
@@ -424,7 +423,6 @@ func optionalAuth(handler func(http.ResponseWriter, *http.Request)) func(http.Re
|
||||
if r.URL.Path == "/" || r.URL.Path == "/index.html" {
|
||||
if glProcessRedirect(w, r) {
|
||||
log.Debug("Auth: redirected to login page by GL-Inet submodule")
|
||||
|
||||
} else {
|
||||
w.Header().Set("Location", "/login.html")
|
||||
w.WriteHeader(http.StatusFound)
|
||||
@@ -474,7 +472,7 @@ func (a *Auth) UserAdd(u *User, password string) {
|
||||
}
|
||||
|
||||
// UserFind - find a user
|
||||
func (a *Auth) UserFind(login string, password string) User {
|
||||
func (a *Auth) UserFind(login, password string) User {
|
||||
a.lock.Lock()
|
||||
defer a.lock.Unlock()
|
||||
for _, u := range a.users {
|
||||
@@ -9,9 +9,14 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/testutil"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
testutil.DiscardLogOutput(m)
|
||||
}
|
||||
|
||||
func prepareTestDir() string {
|
||||
const dir = "./agh-test"
|
||||
_ = os.RemoveAll(dir)
|
||||
@@ -85,9 +90,11 @@ type testResponseWriter struct {
|
||||
func (w *testResponseWriter) Header() http.Header {
|
||||
return w.hdr
|
||||
}
|
||||
|
||||
func (w *testResponseWriter) Write([]byte) (int, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func (w *testResponseWriter) WriteHeader(statusCode int) {
|
||||
w.statusCode = statusCode
|
||||
}
|
||||
@@ -13,10 +13,10 @@ import (
|
||||
|
||||
"github.com/AdguardTeam/dnsproxy/proxy"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/dhcpd"
|
||||
"github.com/AdguardTeam/AdGuardHome/dnsfilter"
|
||||
"github.com/AdguardTeam/AdGuardHome/dnsforward"
|
||||
"github.com/AdguardTeam/AdGuardHome/util"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/dhcpd"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/dnsfilter"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/dnsforward"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/util"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/AdguardTeam/golibs/utils"
|
||||
)
|
||||
@@ -126,7 +126,6 @@ func (clients *clientsContainer) Start() {
|
||||
}
|
||||
go clients.periodicUpdate()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Reload - reload auto-clients
|
||||
@@ -173,7 +172,7 @@ func (clients *clientsContainer) addFromConfig(objects []clientObject) {
|
||||
|
||||
for _, s := range cy.BlockedServices {
|
||||
if !dnsfilter.BlockedSvcKnown(s) {
|
||||
log.Debug("Clients: skipping unknown blocked-service '%s'", s)
|
||||
log.Debug("Clients: skipping unknown blocked-service %q", s)
|
||||
continue
|
||||
}
|
||||
cli.BlockedServices = append(cli.BlockedServices, s)
|
||||
@@ -181,7 +180,7 @@ func (clients *clientsContainer) addFromConfig(objects []clientObject) {
|
||||
|
||||
for _, t := range cy.Tags {
|
||||
if !clients.tagKnown(t) {
|
||||
log.Debug("Clients: skipping unknown tag '%s'", t)
|
||||
log.Debug("Clients: skipping unknown tag %q", t)
|
||||
continue
|
||||
}
|
||||
cli.Tags = append(cli.Tags, t)
|
||||
@@ -377,7 +376,7 @@ func (clients *clientsContainer) check(c *Client) error {
|
||||
}
|
||||
|
||||
if len(c.IDs) == 0 {
|
||||
return fmt.Errorf("ID required")
|
||||
return fmt.Errorf("id required")
|
||||
}
|
||||
|
||||
for i, id := range c.IDs {
|
||||
@@ -409,7 +408,7 @@ func (clients *clientsContainer) check(c *Client) error {
|
||||
|
||||
err := dnsforward.ValidateUpstreams(c.Upstreams)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid upstream servers: %s", err)
|
||||
return fmt.Errorf("invalid upstream servers: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -448,7 +447,7 @@ func (clients *clientsContainer) Add(c Client) (bool, error) {
|
||||
clients.idIndex[id] = &c
|
||||
}
|
||||
|
||||
log.Debug("Clients: added '%s': ID:%v [%d]", c.Name, c.IDs, len(clients.list))
|
||||
log.Debug("Clients: added %q: ID:%v [%d]", c.Name, c.IDs, len(clients.list))
|
||||
return true, nil
|
||||
}
|
||||
|
||||
@@ -590,7 +589,7 @@ func (clients *clientsContainer) addHost(ip, host string, source clientSource) (
|
||||
}
|
||||
clients.ipHost[ip] = ch
|
||||
}
|
||||
log.Debug("Clients: added '%s' -> '%s' [%d]", ip, host, len(clients.ipHost))
|
||||
log.Debug("Clients: added %q -> %q [%d]", ip, host, len(clients.ipHost))
|
||||
return true, nil
|
||||
}
|
||||
|
||||
@@ -607,22 +606,25 @@ func (clients *clientsContainer) rmHosts(source clientSource) int {
|
||||
return n
|
||||
}
|
||||
|
||||
// Fill clients array from system hosts-file
|
||||
// addFromHostsFile fills the clients hosts list from the system's hosts files.
|
||||
func (clients *clientsContainer) addFromHostsFile() {
|
||||
hosts := clients.autoHosts.List()
|
||||
|
||||
clients.lock.Lock()
|
||||
defer clients.lock.Unlock()
|
||||
|
||||
_ = clients.rmHosts(ClientSourceHostsFile)
|
||||
|
||||
n := 0
|
||||
for ip, name := range hosts {
|
||||
ok, err := clients.addHost(ip, name, ClientSourceHostsFile)
|
||||
if err != nil {
|
||||
log.Debug("Clients: %s", err)
|
||||
}
|
||||
if ok {
|
||||
n++
|
||||
for ip, names := range hosts {
|
||||
for _, name := range names {
|
||||
ok, err := clients.addHost(ip, name, ClientSourceHostsFile)
|
||||
if err != nil {
|
||||
log.Debug("Clients: %s", err)
|
||||
}
|
||||
if ok {
|
||||
n++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user