Compare commits
1 Commits
v0.105.0-b
...
1383-devic
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
503974c210 |
@@ -1,13 +1,18 @@
|
|||||||
#!/bin/sh
|
#!/bin/bash
|
||||||
|
set -e;
|
||||||
|
|
||||||
set -e -f -u
|
found=0
|
||||||
|
git diff --cached --name-only | grep -q '.js$' && found=1
|
||||||
if [ "$(git diff --cached --name-only -- '*.js')" ]
|
if [ $found == 1 ]; then
|
||||||
then
|
npm --prefix client run lint || exit 1
|
||||||
make js-lint js-test
|
npm run test --prefix client || exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$(git diff --cached --name-only -- '*.go' 'go.mod')" ]
|
found=0
|
||||||
then
|
git diff --cached --name-only | grep -q '.go$' && found=1
|
||||||
make go-lint go-test
|
if [ $found == 1 ]; then
|
||||||
|
make go-lint || exit 1
|
||||||
|
go test ./... || exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
exit 0;
|
||||||
|
|||||||
@@ -1833,22 +1833,16 @@ Response:
|
|||||||
200 OK
|
200 OK
|
||||||
|
|
||||||
{
|
{
|
||||||
"reason":"FilteredBlackList",
|
"reason":"FilteredBlackList",
|
||||||
"rules":{
|
"filter_id":1,
|
||||||
"filter_list_id":42,
|
"rule":"||doubleclick.net^",
|
||||||
"text":"||doubleclick.net^",
|
"service_name": "...", // set if reason=FilteredBlockedService
|
||||||
},
|
|
||||||
// If we have "reason":"FilteredBlockedService".
|
// if reason=ReasonRewrite:
|
||||||
"service_name": "...",
|
"cname": "...",
|
||||||
// If we have "reason":"Rewrite".
|
"ip_addrs": ["1.2.3.4", ...],
|
||||||
"cname": "...",
|
|
||||||
"ip_addrs": ["1.2.3.4", ...]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
There are also deprecated properties `filter_id` and `rule` on the top level of
|
|
||||||
the response object. Their usaga should be replaced with
|
|
||||||
`rules[*].filter_list_id` and `rules[*].text` correspondingly. See the
|
|
||||||
_OpenAPI_ documentation and the `./openapi/CHANGELOG.md` file.
|
|
||||||
|
|
||||||
## Log-in page
|
## Log-in page
|
||||||
|
|
||||||
|
|||||||
18
CHANGELOG.md
18
CHANGELOG.md
@@ -15,19 +15,15 @@ and this project adheres to
|
|||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- `$dnsrewrite` modifier for filters ([#2102]).
|
- Detecting of network interface configurated to have static IP address via
|
||||||
- The host checking API and the query logs API can now return multiple matched
|
|
||||||
rules ([#2102]).
|
|
||||||
- Detecting of network interface configured to have static IP address via
|
|
||||||
`/etc/network/interfaces` ([#2302]).
|
`/etc/network/interfaces` ([#2302]).
|
||||||
- DNSCrypt protocol support ([#1361]).
|
- DNSCrypt protocol support [#1361].
|
||||||
- A 5 second wait period until a DHCP server's network interface gets an IP
|
- A 5 second wait period until a DHCP server's network interface gets an IP
|
||||||
address ([#2304]).
|
address ([#2304]).
|
||||||
- `$dnstype` modifier for filters ([#2337]).
|
- `$dnstype` modifier for filters ([#2337]).
|
||||||
- HTTP API request body size limit ([#2305]).
|
- HTTP API request body size limit ([#2305]).
|
||||||
|
|
||||||
[#1361]: https://github.com/AdguardTeam/AdGuardHome/issues/1361
|
[#1361]: https://github.com/AdguardTeam/AdGuardHome/issues/1361
|
||||||
[#2102]: https://github.com/AdguardTeam/AdGuardHome/issues/2102
|
|
||||||
[#2302]: https://github.com/AdguardTeam/AdGuardHome/issues/2302
|
[#2302]: https://github.com/AdguardTeam/AdGuardHome/issues/2302
|
||||||
[#2304]: https://github.com/AdguardTeam/AdGuardHome/issues/2304
|
[#2304]: https://github.com/AdguardTeam/AdGuardHome/issues/2304
|
||||||
[#2305]: https://github.com/AdguardTeam/AdGuardHome/issues/2305
|
[#2305]: https://github.com/AdguardTeam/AdGuardHome/issues/2305
|
||||||
@@ -35,9 +31,6 @@ and this project adheres to
|
|||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- When `dns.bogus_nxdomain` option is used, the server will now transform
|
|
||||||
responses if there is at least one bogus address instead of all of them
|
|
||||||
([#2394]). The new behavior is the same as in `dnsmasq`.
|
|
||||||
- Post-updating relaunch possibility is now determined OS-dependently ([#2231],
|
- Post-updating relaunch possibility is now determined OS-dependently ([#2231],
|
||||||
[#2391]).
|
[#2391]).
|
||||||
- Made the mobileconfig HTTP API more robust and predictable, add parameters and
|
- Made the mobileconfig HTTP API more robust and predictable, add parameters and
|
||||||
@@ -54,27 +47,20 @@ and this project adheres to
|
|||||||
[#2343]: https://github.com/AdguardTeam/AdGuardHome/issues/2343
|
[#2343]: https://github.com/AdguardTeam/AdGuardHome/issues/2343
|
||||||
[#2358]: https://github.com/AdguardTeam/AdGuardHome/issues/2358
|
[#2358]: https://github.com/AdguardTeam/AdGuardHome/issues/2358
|
||||||
[#2391]: https://github.com/AdguardTeam/AdGuardHome/issues/2391
|
[#2391]: https://github.com/AdguardTeam/AdGuardHome/issues/2391
|
||||||
[#2394]: https://github.com/AdguardTeam/AdGuardHome/issues/2394
|
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Inability to set DNS cache TTL limits ([#2459]).
|
|
||||||
- Possible freezes on slower machines ([#2225]).
|
|
||||||
- A mitigation against records being shown in the wrong order on the query log
|
- A mitigation against records being shown in the wrong order on the query log
|
||||||
page ([#2293]).
|
page ([#2293]).
|
||||||
- A JSON parsing error in query log ([#2345]).
|
- A JSON parsing error in query log ([#2345]).
|
||||||
- Incorrect detection of the IPv6 address of an interface as well as another
|
- Incorrect detection of the IPv6 address of an interface as well as another
|
||||||
infinite loop in the `/dhcp/find_active_dhcp` HTTP API ([#2355]).
|
infinite loop in the `/dhcp/find_active_dhcp` HTTP API ([#2355]).
|
||||||
|
|
||||||
[#2225]: https://github.com/AdguardTeam/AdGuardHome/issues/2225
|
|
||||||
[#2293]: https://github.com/AdguardTeam/AdGuardHome/issues/2293
|
[#2293]: https://github.com/AdguardTeam/AdGuardHome/issues/2293
|
||||||
[#2345]: https://github.com/AdguardTeam/AdGuardHome/issues/2345
|
[#2345]: https://github.com/AdguardTeam/AdGuardHome/issues/2345
|
||||||
[#2355]: https://github.com/AdguardTeam/AdGuardHome/issues/2355
|
[#2355]: https://github.com/AdguardTeam/AdGuardHome/issues/2355
|
||||||
[#2459]: https://github.com/AdguardTeam/AdGuardHome/issues/2459
|
|
||||||
|
|
||||||
### Removed
|
|
||||||
|
|
||||||
- Support for pre-v0.99.3 format of query logs ([#2102]).
|
|
||||||
|
|
||||||
## [v0.104.3] - 2020-11-19
|
## [v0.104.3] - 2020-11-19
|
||||||
|
|
||||||
|
|||||||
42
HACKING.md
42
HACKING.md
@@ -78,14 +78,6 @@ The rules are mostly sorted in the alphabetical order.
|
|||||||
* Prefer constants to variables where possible. Reduce global variables. Use
|
* Prefer constants to variables where possible. Reduce global variables. Use
|
||||||
[constant errors] instead of `errors.New`.
|
[constant errors] instead of `errors.New`.
|
||||||
|
|
||||||
* Unused arguments in anonymous functions must be called `_`:
|
|
||||||
|
|
||||||
```go
|
|
||||||
v.onSuccess = func(_ int, msg string) {
|
|
||||||
// …
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
* Use linters.
|
* Use linters.
|
||||||
|
|
||||||
* Use named returns to improve readability of function signatures.
|
* Use named returns to improve readability of function signatures.
|
||||||
@@ -114,16 +106,7 @@ The rules are mostly sorted in the alphabetical order.
|
|||||||
```go
|
```go
|
||||||
// Foo implements the Fooer interface for *foo.
|
// Foo implements the Fooer interface for *foo.
|
||||||
func (f *foo) Foo() {
|
func (f *foo) Foo() {
|
||||||
// …
|
// …
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
When the implemented interface is unexported:
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Unwrap implements the hidden wrapper interface for *fooError.
|
|
||||||
func (err *fooError) Unwrap() (unwrapped error) {
|
|
||||||
// …
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -163,15 +146,12 @@ The rules are mostly sorted in the alphabetical order.
|
|||||||
|
|
||||||
## Shell Scripting
|
## Shell Scripting
|
||||||
|
|
||||||
* Avoid bashisms and GNUisms, prefer *POSIX* features only.
|
* Avoid bashisms, prefer *POSIX* features only.
|
||||||
|
|
||||||
* Prefer `'raw strings'` to `"double quoted strings"` whenever possible.
|
* Prefer `'raw strings'` to `"double quoted strings"` whenever possible.
|
||||||
|
|
||||||
* Put spaces within `$( cmd )`, `$(( expr ))`, and `{ cmd; }`.
|
* Put spaces within `$( cmd )`, `$(( expr ))`, and `{ cmd; }`.
|
||||||
|
|
||||||
* Put utility flags in the ASCII order and **don't** group them together. For
|
|
||||||
example, `ls -1 -A -q`.
|
|
||||||
|
|
||||||
* `snake_case`, not `camelCase`.
|
* `snake_case`, not `camelCase`.
|
||||||
|
|
||||||
* Use `set -e -f -u` and also `set -x` in verbose mode.
|
* Use `set -e -f -u` and also `set -x` in verbose mode.
|
||||||
@@ -179,24 +159,6 @@ The rules are mostly sorted in the alphabetical order.
|
|||||||
* Use the `"$var"` form instead of the `$var` form, unless word splitting is
|
* Use the `"$var"` form instead of the `$var` form, unless word splitting is
|
||||||
required.
|
required.
|
||||||
|
|
||||||
* When concatenating, always use the form with curly braces to prevent
|
|
||||||
accidental bad variable names. That is, `"${var}_tmp.txt"` and **not**
|
|
||||||
`"$var_tmp.txt"`. The latter will try to lookup variable `var_tmp`.
|
|
||||||
|
|
||||||
* When concatenating, surround the whole string with quotes. That is, use
|
|
||||||
this:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
dir="${TOP_DIR}/sub"
|
|
||||||
```
|
|
||||||
|
|
||||||
And **not** this:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
# Bad!
|
|
||||||
dir="${TOP_DIR}"/sub
|
|
||||||
```
|
|
||||||
|
|
||||||
## Text, Including Comments
|
## Text, Including Comments
|
||||||
|
|
||||||
* End sentences with appropriate punctuation.
|
* End sentences with appropriate punctuation.
|
||||||
|
|||||||
4
Makefile
4
Makefile
@@ -35,7 +35,6 @@ GPG_KEY := devteam@adguard.com
|
|||||||
GPG_KEY_PASSPHRASE :=
|
GPG_KEY_PASSPHRASE :=
|
||||||
GPG_CMD := gpg --detach-sig --default-key $(GPG_KEY) --pinentry-mode loopback --passphrase $(GPG_KEY_PASSPHRASE)
|
GPG_CMD := gpg --detach-sig --default-key $(GPG_KEY) --pinentry-mode loopback --passphrase $(GPG_KEY_PASSPHRASE)
|
||||||
VERBOSE := -v
|
VERBOSE := -v
|
||||||
REBUILD_CLIENT = 1
|
|
||||||
|
|
||||||
# See release target
|
# See release target
|
||||||
DIST_DIR=dist
|
DIST_DIR=dist
|
||||||
@@ -125,8 +124,7 @@ all: build
|
|||||||
init:
|
init:
|
||||||
git config core.hooksPath .githooks
|
git config core.hooksPath .githooks
|
||||||
|
|
||||||
build:
|
build: client_with_deps
|
||||||
test '$(REBUILD_CLIENT)' = '1' && $(MAKE) client_with_deps || exit 0
|
|
||||||
$(GO) mod download
|
$(GO) mod download
|
||||||
PATH=$(GOPATH)/bin:$(PATH) $(GO) generate ./...
|
PATH=$(GOPATH)/bin:$(PATH) $(GO) generate ./...
|
||||||
CGO_ENABLED=0 $(GO) build -ldflags="-s -w -X main.version=$(VERSION) -X main.channel=$(CHANNEL) -X main.goarm=$(GOARM)"
|
CGO_ENABLED=0 $(GO) build -ldflags="-s -w -X main.version=$(VERSION) -X main.channel=$(CHANNEL) -X main.goarm=$(GOARM)"
|
||||||
|
|||||||
@@ -255,7 +255,7 @@ curl -sSL https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/master/scrip
|
|||||||
|
|
||||||
* Beta channel builds
|
* Beta channel builds
|
||||||
* Linux: [64-bit](https://static.adguard.com/adguardhome/beta/AdGuardHome_linux_amd64.tar.gz), [32-bit](https://static.adguard.com/adguardhome/beta/AdGuardHome_linux_386.tar.gz)
|
* Linux: [64-bit](https://static.adguard.com/adguardhome/beta/AdGuardHome_linux_amd64.tar.gz), [32-bit](https://static.adguard.com/adguardhome/beta/AdGuardHome_linux_386.tar.gz)
|
||||||
* Linux ARM: [32-bit ARMv6](https://static.adguard.com/adguardhome/beta/AdGuardHome_linux_armv6.tar.gz) (recommended for Raspberry Pi), [64-bit](https://static.adguard.com/adguardhome/beta/AdGuardHome_linux_arm64.tar.gz), [32-bit ARMv5](https://static.adguard.com/adguardhome/beta/AdGuardHome_linux_armv5.tar.gz), [32-bit ARMv7](https://static.adguard.com/adguardhome/beta/AdGuardHome_linux_armv7.tar.gz)
|
* Linux ARM: [32-bit ARMv6](https://static.adguard.com/adguardhome/beta/AdGuardHome_linux_armv6.tar.gz) (recommended for Rapsberry Pi), [64-bit](https://static.adguard.com/adguardhome/beta/AdGuardHome_linux_arm64.tar.gz), [32-bit ARMv5](https://static.adguard.com/adguardhome/beta/AdGuardHome_linux_armv5.tar.gz), [32-bit ARMv7](https://static.adguard.com/adguardhome/beta/AdGuardHome_linux_armv7.tar.gz)
|
||||||
* Linux MIPS: [32-bit MIPS](https://static.adguard.com/adguardhome/beta/AdGuardHome_linux_mips_softfloat.tar.gz), [32-bit MIPSLE](https://static.adguard.com/adguardhome/beta/AdGuardHome_linux_mipsle_softfloat.tar.gz), [64-bit MIPS](https://static.adguard.com/adguardhome/beta/AdGuardHome_linux_mips64_softfloat.tar.gz), [64-bit MIPSLE](https://static.adguard.com/adguardhome/beta/AdGuardHome_linux_mips64le_softfloat.tar.gz)
|
* Linux MIPS: [32-bit MIPS](https://static.adguard.com/adguardhome/beta/AdGuardHome_linux_mips_softfloat.tar.gz), [32-bit MIPSLE](https://static.adguard.com/adguardhome/beta/AdGuardHome_linux_mipsle_softfloat.tar.gz), [64-bit MIPS](https://static.adguard.com/adguardhome/beta/AdGuardHome_linux_mips64_softfloat.tar.gz), [64-bit MIPSLE](https://static.adguard.com/adguardhome/beta/AdGuardHome_linux_mips64le_softfloat.tar.gz)
|
||||||
* Windows: [64-bit](https://static.adguard.com/adguardhome/beta/AdGuardHome_windows_amd64.zip), [32-bit](https://static.adguard.com/adguardhome/beta/AdGuardHome_windows_386.zip)
|
* Windows: [64-bit](https://static.adguard.com/adguardhome/beta/AdGuardHome_windows_amd64.zip), [32-bit](https://static.adguard.com/adguardhome/beta/AdGuardHome_windows_386.zip)
|
||||||
* MacOS: [64-bit](https://static.adguard.com/adguardhome/beta/AdGuardHome_darwin_amd64.zip), [32-bit](https://static.adguard.com/adguardhome/beta/AdGuardHome_darwin_386.zip)
|
* MacOS: [64-bit](https://static.adguard.com/adguardhome/beta/AdGuardHome_darwin_amd64.zip), [32-bit](https://static.adguard.com/adguardhome/beta/AdGuardHome_darwin_386.zip)
|
||||||
@@ -264,7 +264,7 @@ curl -sSL https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/master/scrip
|
|||||||
|
|
||||||
* Edge channel builds
|
* Edge channel builds
|
||||||
* Linux: [64-bit](https://static.adguard.com/adguardhome/edge/AdGuardHome_linux_amd64.tar.gz), [32-bit](https://static.adguard.com/adguardhome/edge/AdGuardHome_linux_386.tar.gz)
|
* Linux: [64-bit](https://static.adguard.com/adguardhome/edge/AdGuardHome_linux_amd64.tar.gz), [32-bit](https://static.adguard.com/adguardhome/edge/AdGuardHome_linux_386.tar.gz)
|
||||||
* Linux ARM: [32-bit ARMv6](https://static.adguard.com/adguardhome/edge/AdGuardHome_linux_armv6.tar.gz) (recommended for Raspberry Pi), [64-bit](https://static.adguard.com/adguardhome/edge/AdGuardHome_linux_arm64.tar.gz), [32-bit ARMv5](https://static.adguard.com/adguardhome/edge/AdGuardHome_linux_armv5.tar.gz), [32-bit ARMv7](https://static.adguard.com/adguardhome/edge/AdGuardHome_linux_armv7.tar.gz)
|
* Linux ARM: [32-bit ARMv6](https://static.adguard.com/adguardhome/edge/AdGuardHome_linux_armv6.tar.gz) (recommended for Rapsberry Pi), [64-bit](https://static.adguard.com/adguardhome/edge/AdGuardHome_linux_arm64.tar.gz), [32-bit ARMv5](https://static.adguard.com/adguardhome/edge/AdGuardHome_linux_armv5.tar.gz), [32-bit ARMv7](https://static.adguard.com/adguardhome/edge/AdGuardHome_linux_armv7.tar.gz)
|
||||||
* Linux MIPS: [32-bit MIPS](https://static.adguard.com/adguardhome/edge/AdGuardHome_linux_mips_softfloat.tar.gz), [32-bit MIPSLE](https://static.adguard.com/adguardhome/edge/AdGuardHome_linux_mipsle_softfloat.tar.gz), [64-bit MIPS](https://static.adguard.com/adguardhome/edge/AdGuardHome_linux_mips64_softfloat.tar.gz), [64-bit MIPSLE](https://static.adguard.com/adguardhome/edge/AdGuardHome_linux_mips64le_softfloat.tar.gz)
|
* Linux MIPS: [32-bit MIPS](https://static.adguard.com/adguardhome/edge/AdGuardHome_linux_mips_softfloat.tar.gz), [32-bit MIPSLE](https://static.adguard.com/adguardhome/edge/AdGuardHome_linux_mipsle_softfloat.tar.gz), [64-bit MIPS](https://static.adguard.com/adguardhome/edge/AdGuardHome_linux_mips64_softfloat.tar.gz), [64-bit MIPSLE](https://static.adguard.com/adguardhome/edge/AdGuardHome_linux_mips64le_softfloat.tar.gz)
|
||||||
* Windows: [64-bit](https://static.adguard.com/adguardhome/edge/AdGuardHome_windows_amd64.zip), [32-bit](https://static.adguard.com/adguardhome/edge/AdGuardHome_windows_386.zip)
|
* Windows: [64-bit](https://static.adguard.com/adguardhome/edge/AdGuardHome_windows_amd64.zip), [32-bit](https://static.adguard.com/adguardhome/edge/AdGuardHome_windows_386.zip)
|
||||||
* MacOS: [64-bit](https://static.adguard.com/adguardhome/edge/AdGuardHome_darwin_amd64.zip), [32-bit](https://static.adguard.com/adguardhome/edge/AdGuardHome_darwin_386.zip)
|
* MacOS: [64-bit](https://static.adguard.com/adguardhome/edge/AdGuardHome_darwin_amd64.zip), [32-bit](https://static.adguard.com/adguardhome/edge/AdGuardHome_darwin_386.zip)
|
||||||
|
|||||||
12
client/package-lock.json
generated
vendored
12
client/package-lock.json
generated
vendored
@@ -3066,12 +3066,6 @@
|
|||||||
"pkg-up": "^2.0.0"
|
"pkg-up": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"caniuse-lite": {
|
|
||||||
"version": "1.0.30001062",
|
|
||||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001062.tgz",
|
|
||||||
"integrity": "sha512-ei9ZqeOnN7edDrb24QfJ0OZicpEbsWxv7WusOiQGz/f2SfvBgHHbOEwBJ8HKGVSyx8Z6ndPjxzR6m0NQq+0bfw==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"postcss": {
|
"postcss": {
|
||||||
"version": "7.0.30",
|
"version": "7.0.30",
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.30.tgz",
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.30.tgz",
|
||||||
@@ -3928,9 +3922,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"caniuse-lite": {
|
"caniuse-lite": {
|
||||||
"version": "1.0.30001059",
|
"version": "1.0.30001165",
|
||||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001059.tgz",
|
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001165.tgz",
|
||||||
"integrity": "sha512-oOrc+jPJWooKIA0IrNZ5sYlsXc7NP7KLhNWrSGEJhnfSzDvDJ0zd3i6HXsslExY9bbu+x0FQ5C61LcqmPt7bOQ==",
|
"integrity": "sha512-8cEsSMwXfx7lWSUMA2s08z9dIgsnR5NAqjXP23stdsU3AUWkCr/rr4s4OFtHXn5XXr6+7kam3QFVoYyXNPdJPA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"capture-exit": {
|
"capture-exit": {
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
"form_error_ip_format": "Invalid IP format",
|
"form_error_ip_format": "Invalid IP format",
|
||||||
"form_error_mac_format": "Invalid MAC format",
|
"form_error_mac_format": "Invalid MAC format",
|
||||||
"form_error_client_id_format": "Invalid client ID format",
|
"form_error_client_id_format": "Invalid client ID format",
|
||||||
|
"form_error_server_name": "Invalid server name or wildcard certificate",
|
||||||
"form_error_positive": "Must be greater than 0",
|
"form_error_positive": "Must be greater than 0",
|
||||||
"form_error_negative": "Must be equal to 0 or greater",
|
"form_error_negative": "Must be equal to 0 or greater",
|
||||||
"range_end_error": "Must be greater than range start",
|
"range_end_error": "Must be greater than range start",
|
||||||
@@ -331,7 +332,7 @@
|
|||||||
"encryption_config_saved": "Encryption config saved",
|
"encryption_config_saved": "Encryption config saved",
|
||||||
"encryption_server": "Server name",
|
"encryption_server": "Server name",
|
||||||
"encryption_server_enter": "Enter your domain name",
|
"encryption_server_enter": "Enter your domain name",
|
||||||
"encryption_server_desc": "In order to use HTTPS, you need to enter the server name that matches your SSL certificate.",
|
"encryption_server_desc": "In order to use HTTPS, you need to enter the server name that matches your SSL certificate or wildcard certificate. If the field is not set, it will accept TLS connections for any domain.",
|
||||||
"encryption_redirect": "Redirect to HTTPS automatically",
|
"encryption_redirect": "Redirect to HTTPS automatically",
|
||||||
"encryption_redirect_desc": "If checked, AdGuard Home will automatically redirect you from HTTP to HTTPS addresses.",
|
"encryption_redirect_desc": "If checked, AdGuard Home will automatically redirect you from HTTP to HTTPS addresses.",
|
||||||
"encryption_https": "HTTPS port",
|
"encryption_https": "HTTPS port",
|
||||||
@@ -387,7 +388,7 @@
|
|||||||
"client_edit": "Edit Client",
|
"client_edit": "Edit Client",
|
||||||
"client_identifier": "Identifier",
|
"client_identifier": "Identifier",
|
||||||
"ip_address": "IP address",
|
"ip_address": "IP address",
|
||||||
"client_identifier_desc": "Clients can be identified by the IP address, CIDR, MAC address. Please note that using MAC as identifier is possible only if AdGuard Home is also a <0>DHCP server</0>",
|
"client_identifier_desc": "Clients can be identified by the IP address, CIDR, MAC address or domain. Please note that using MAC as identifier is possible only if AdGuard Home is also a <0>DHCP server</0>",
|
||||||
"form_enter_ip": "Enter IP",
|
"form_enter_ip": "Enter IP",
|
||||||
"form_enter_mac": "Enter MAC",
|
"form_enter_mac": "Enter MAC",
|
||||||
"form_enter_id": "Enter identifier",
|
"form_enter_id": "Enter identifier",
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ const CertificateStatus = ({
|
|||||||
{dnsNames && (
|
{dnsNames && (
|
||||||
<li>
|
<li>
|
||||||
<Trans>encryption_hostnames</Trans>:
|
<Trans>encryption_hostnames</Trans>:
|
||||||
{dnsNames}
|
{dnsNames.join(', ')}
|
||||||
</li>
|
</li>
|
||||||
)}
|
)}
|
||||||
</Fragment>
|
</Fragment>
|
||||||
@@ -65,7 +65,7 @@ CertificateStatus.propTypes = {
|
|||||||
subject: PropTypes.string,
|
subject: PropTypes.string,
|
||||||
issuer: PropTypes.string,
|
issuer: PropTypes.string,
|
||||||
notAfter: PropTypes.string,
|
notAfter: PropTypes.string,
|
||||||
dnsNames: PropTypes.string,
|
dnsNames: PropTypes.arrayOf(PropTypes.string),
|
||||||
};
|
};
|
||||||
|
|
||||||
export default withTranslation()(CertificateStatus);
|
export default withTranslation()(CertificateStatus);
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import {
|
|||||||
toNumber,
|
toNumber,
|
||||||
} from '../../../helpers/form';
|
} from '../../../helpers/form';
|
||||||
import {
|
import {
|
||||||
validateIsSafePort, validatePort, validatePortQuic, validatePortTLS,
|
validateServerName, validateIsSafePort, validatePort, validatePortQuic, validatePortTLS,
|
||||||
} from '../../../helpers/validators';
|
} from '../../../helpers/validators';
|
||||||
import i18n from '../../../i18n';
|
import i18n from '../../../i18n';
|
||||||
import KeyStatus from './KeyStatus';
|
import KeyStatus from './KeyStatus';
|
||||||
@@ -127,6 +127,7 @@ let Form = (props) => {
|
|||||||
placeholder={t('encryption_server_enter')}
|
placeholder={t('encryption_server_enter')}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
disabled={!isEnabled}
|
disabled={!isEnabled}
|
||||||
|
validate={validateServerName}
|
||||||
/>
|
/>
|
||||||
<div className="form__desc">
|
<div className="form__desc">
|
||||||
<Trans>encryption_server_desc</Trans>
|
<Trans>encryption_server_desc</Trans>
|
||||||
@@ -413,7 +414,7 @@ Form.propTypes = {
|
|||||||
valid_key: PropTypes.bool,
|
valid_key: PropTypes.bool,
|
||||||
valid_cert: PropTypes.bool,
|
valid_cert: PropTypes.bool,
|
||||||
valid_pair: PropTypes.bool,
|
valid_pair: PropTypes.bool,
|
||||||
dns_names: PropTypes.string,
|
dns_names: PropTypes.arrayOf(PropTypes.string),
|
||||||
key_type: PropTypes.string,
|
key_type: PropTypes.string,
|
||||||
issuer: PropTypes.string,
|
issuer: PropTypes.string,
|
||||||
subject: PropTypes.string,
|
subject: PropTypes.string,
|
||||||
|
|||||||
@@ -11,15 +11,17 @@ const MOBILE_CONFIG_LINKS = {
|
|||||||
DOT: '/apple/dot.mobileconfig',
|
DOT: '/apple/dot.mobileconfig',
|
||||||
DOH: '/apple/doh.mobileconfig',
|
DOH: '/apple/doh.mobileconfig',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* FIXME: find out `client_id` */
|
||||||
const renderMobileconfigInfo = ({ label, components, server_name }) => <li key={label}>
|
const renderMobileconfigInfo = ({ label, components, server_name }) => <li key={label}>
|
||||||
<Trans components={components}>{label}</Trans>
|
<Trans components={components}>{label}</Trans>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<a href={getPathWithQueryString(MOBILE_CONFIG_LINKS.DOT, { host: server_name })}
|
<a href={getPathWithQueryString(MOBILE_CONFIG_LINKS.DOT, { host: server_name, client_id: 'client_id' })}
|
||||||
download>{i18next.t('download_mobileconfig_dot')}</a>
|
download>{i18next.t('download_mobileconfig_dot')}</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href={getPathWithQueryString(MOBILE_CONFIG_LINKS.DOH, { host: server_name })}
|
<a href={getPathWithQueryString(MOBILE_CONFIG_LINKS.DOH, { host: server_name, client_id: 'client_id' })}
|
||||||
download>{i18next.t('download_mobileconfig_doh')}</a>
|
download>{i18next.t('download_mobileconfig_doh')}</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@@ -13,6 +13,10 @@ export const R_MAC = /^((([a-fA-F0-9][a-fA-F0-9]+[-]){5}|([a-fA-F0-9][a-fA-F0-9]
|
|||||||
|
|
||||||
export const R_CIDR_IPV6 = /^s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:)))(%.+)?s*(\/(12[0-8]|1[0-1][0-9]|[1-9][0-9]|[0-9]))$/;
|
export const R_CIDR_IPV6 = /^s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:)))(%.+)?s*(\/(12[0-8]|1[0-1][0-9]|[1-9][0-9]|[0-9]))$/;
|
||||||
|
|
||||||
|
export const R_DOMAIN = /^[a-zA-Z0-9][a-zA-Z0-9-]{1,61}[a-zA-Z0-9]\.[a-zA-Z]{2,}$/;
|
||||||
|
|
||||||
|
export const R_SERVER_NAME = /^(\*\.)?[a-zA-Z0-9][a-zA-Z0-9-]{1,61}[a-zA-Z0-9]\.[a-zA-Z]{2,}$/;
|
||||||
|
|
||||||
export const R_PATH_LAST_PART = /\/[^/]*$/;
|
export const R_PATH_LAST_PART = /\/[^/]*$/;
|
||||||
|
|
||||||
// eslint-disable-next-line no-control-regex
|
// eslint-disable-next-line no-control-regex
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ import {
|
|||||||
R_URL_REQUIRES_PROTOCOL,
|
R_URL_REQUIRES_PROTOCOL,
|
||||||
STANDARD_WEB_PORT,
|
STANDARD_WEB_PORT,
|
||||||
UNSAFE_PORTS,
|
UNSAFE_PORTS,
|
||||||
|
R_DOMAIN,
|
||||||
|
R_SERVER_NAME,
|
||||||
} from './constants';
|
} from './constants';
|
||||||
import { getLastIpv4Octet, isValidAbsolutePath } from './form';
|
import { getLastIpv4Octet, isValidAbsolutePath } from './form';
|
||||||
|
|
||||||
@@ -64,19 +66,35 @@ export const validateClientId = (value) => {
|
|||||||
if (!value) {
|
if (!value) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
const formattedValue = value.trim();
|
const formattedValue = value ? value.trim() : value;
|
||||||
if (formattedValue && !(
|
if (formattedValue && !(
|
||||||
R_IPV4.test(formattedValue)
|
R_IPV4.test(formattedValue)
|
||||||
|| R_IPV6.test(formattedValue)
|
|| R_IPV6.test(formattedValue)
|
||||||
|| R_MAC.test(formattedValue)
|
|| R_MAC.test(formattedValue)
|
||||||
|| R_CIDR.test(formattedValue)
|
|| R_CIDR.test(formattedValue)
|
||||||
|| R_CIDR_IPV6.test(formattedValue)
|
|| R_CIDR_IPV6.test(formattedValue)
|
||||||
|
|| R_DOMAIN.test(formattedValue)
|
||||||
)) {
|
)) {
|
||||||
return 'form_error_client_id_format';
|
return 'form_error_client_id_format';
|
||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param value {string}
|
||||||
|
* @returns {undefined|string}
|
||||||
|
*/
|
||||||
|
export const validateServerName = (value) => {
|
||||||
|
if (!value) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const formattedValue = value ? value.trim() : value;
|
||||||
|
if (formattedValue && !R_SERVER_NAME.test(formattedValue)) {
|
||||||
|
return 'form_error_server_name';
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param value {string}
|
* @param value {string}
|
||||||
* @returns {undefined|string}
|
* @returns {undefined|string}
|
||||||
|
|||||||
17
go.mod
17
go.mod
@@ -3,11 +3,12 @@ module github.com/AdguardTeam/AdGuardHome
|
|||||||
go 1.14
|
go 1.14
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/AdguardTeam/dnsproxy v0.33.7
|
github.com/AdguardTeam/dnsproxy v0.33.2
|
||||||
github.com/AdguardTeam/golibs v0.4.4
|
github.com/AdguardTeam/golibs v0.4.4
|
||||||
github.com/AdguardTeam/urlfilter v0.14.1
|
github.com/AdguardTeam/urlfilter v0.13.0
|
||||||
github.com/NYTimes/gziphandler v1.1.1
|
github.com/NYTimes/gziphandler v1.1.1
|
||||||
github.com/ameshkov/dnscrypt/v2 v2.0.1
|
github.com/ameshkov/dnscrypt/v2 v2.0.0
|
||||||
|
github.com/beefsack/go-rate v0.0.0-20200827232406-6cde80facd47 // indirect
|
||||||
github.com/fsnotify/fsnotify v1.4.9
|
github.com/fsnotify/fsnotify v1.4.9
|
||||||
github.com/go-ping/ping v0.0.0-20201115131931-3300c582a663
|
github.com/go-ping/ping v0.0.0-20201115131931-3300c582a663
|
||||||
github.com/gobuffalo/envy v1.9.0 // indirect
|
github.com/gobuffalo/envy v1.9.0 // indirect
|
||||||
@@ -15,8 +16,10 @@ require (
|
|||||||
github.com/gobuffalo/packr/v2 v2.8.1 // indirect
|
github.com/gobuffalo/packr/v2 v2.8.1 // indirect
|
||||||
github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714
|
github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714
|
||||||
github.com/insomniacslk/dhcp v0.0.0-20201112113307-4de412bc85d8
|
github.com/insomniacslk/dhcp v0.0.0-20201112113307-4de412bc85d8
|
||||||
|
github.com/joomcode/errorx v1.0.3 // indirect
|
||||||
github.com/kardianos/service v1.2.0
|
github.com/kardianos/service v1.2.0
|
||||||
github.com/karrick/godirwalk v1.16.1 // indirect
|
github.com/karrick/godirwalk v1.16.1 // indirect
|
||||||
|
github.com/lucas-clemente/quic-go v0.19.1 // indirect
|
||||||
github.com/mdlayher/ethernet v0.0.0-20190606142754-0394541c37b7
|
github.com/mdlayher/ethernet v0.0.0-20190606142754-0394541c37b7
|
||||||
github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065
|
github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065
|
||||||
github.com/miekg/dns v1.1.35
|
github.com/miekg/dns v1.1.35
|
||||||
@@ -27,12 +30,14 @@ require (
|
|||||||
github.com/stretchr/testify v1.6.1
|
github.com/stretchr/testify v1.6.1
|
||||||
github.com/u-root/u-root v7.0.0+incompatible
|
github.com/u-root/u-root v7.0.0+incompatible
|
||||||
go.etcd.io/bbolt v1.3.5
|
go.etcd.io/bbolt v1.3.5
|
||||||
golang.org/x/crypto v0.0.0-20201217014255-9d1352758620
|
golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9
|
||||||
golang.org/x/net v0.0.0-20201216054612-986b41b23924
|
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 // indirect
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 // indirect
|
||||||
golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68
|
||||||
|
golang.org/x/text v0.3.4 // indirect
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0
|
gopkg.in/natefinch/lumberjack.v2 v2.0.0
|
||||||
gopkg.in/yaml.v2 v2.3.0
|
gopkg.in/yaml.v2 v2.3.0
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 // indirect
|
||||||
howett.net/plist v0.0.0-20201026045517-117a925f2150
|
howett.net/plist v0.0.0-20201026045517-117a925f2150
|
||||||
)
|
)
|
||||||
|
|||||||
48
go.sum
48
go.sum
@@ -18,16 +18,18 @@ 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/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=
|
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=
|
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
|
||||||
github.com/AdguardTeam/dnsproxy v0.33.7 h1:DXsLTJoBSUejB2ZqVHyMG0/kXD8PzuVPbLCsGKBdaDc=
|
github.com/AdguardTeam/dnsproxy v0.33.2 h1:k5aMcsw3TA/G2DR8EjIkwutDPuuRkKh8xij4cFWC6Fk=
|
||||||
github.com/AdguardTeam/dnsproxy v0.33.7/go.mod h1:dkI9VWh43XlOzF2XogDm1EmoVl7PANOR4isQV6X9LZs=
|
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.0/go.mod h1:skKsDKIBB7kkFflLJBpfGX+G8QFTx0WKUzB6TIgtUj4=
|
||||||
github.com/AdguardTeam/golibs v0.4.2 h1:7M28oTZFoFwNmp8eGPb3ImmYbxGaJLyQXeIFVHjME0o=
|
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.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/golibs v0.4.4 h1:cM9UySQiYFW79zo5XRwnaIWVzfW4eNXmZktMrWbthpw=
|
github.com/AdguardTeam/golibs v0.4.4 h1:cM9UySQiYFW79zo5XRwnaIWVzfW4eNXmZktMrWbthpw=
|
||||||
github.com/AdguardTeam/golibs v0.4.4/go.mod h1:skKsDKIBB7kkFflLJBpfGX+G8QFTx0WKUzB6TIgtUj4=
|
github.com/AdguardTeam/golibs v0.4.4/go.mod h1:skKsDKIBB7kkFflLJBpfGX+G8QFTx0WKUzB6TIgtUj4=
|
||||||
github.com/AdguardTeam/gomitmproxy v0.2.0/go.mod h1:Qdv0Mktnzer5zpdpi5rAwixNJzW2FN91LjKJCkVbYGU=
|
github.com/AdguardTeam/gomitmproxy v0.2.0/go.mod h1:Qdv0Mktnzer5zpdpi5rAwixNJzW2FN91LjKJCkVbYGU=
|
||||||
github.com/AdguardTeam/urlfilter v0.14.1 h1:imYls0fit9ojA6pP1hWFUEIjyoXbDF85ZM+G67bI48c=
|
github.com/AdguardTeam/urlfilter v0.13.0 h1:MfO46K81JVTkhgP6gRu/buKl5wAOSfusjiDwjT1JN1c=
|
||||||
github.com/AdguardTeam/urlfilter v0.14.1/go.mod h1:klx4JbOfc4EaNb5lWLqOwfg+pVcyRukmoJRvO55lL5U=
|
github.com/AdguardTeam/urlfilter v0.13.0/go.mod h1:klx4JbOfc4EaNb5lWLqOwfg+pVcyRukmoJRvO55lL5U=
|
||||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||||
@@ -42,8 +44,8 @@ github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 h1:52m0LGchQBBVqJRyY
|
|||||||
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635/go.mod h1:lmLxL+FV291OopO93Bwf9fQLQeLyt33VJRUg5VJ30us=
|
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635/go.mod h1:lmLxL+FV291OopO93Bwf9fQLQeLyt33VJRUg5VJ30us=
|
||||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
github.com/ameshkov/dnscrypt/v2 v2.0.1 h1:igNVNM6NLBOqYUzHXaDUxn8i+wJXOsosY0/xEBirixA=
|
github.com/ameshkov/dnscrypt/v2 v2.0.0 h1:i83G8MeGLrAFgUL8GSu98TVhtFDEifF7SIS7Qi/RZ3U=
|
||||||
github.com/ameshkov/dnscrypt/v2 v2.0.1/go.mod h1:nbZnxJt4edIPx2Haa8n2XtC2g5AWcsdQiSuXkNH8eDI=
|
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 h1:LhGvgWDzhNJh+kBQd/AfUlq1vfVe109huiXw4JhnPug=
|
||||||
github.com/ameshkov/dnsstamps v1.0.1/go.mod h1:Ii3eUu73dx4Vw5O4wjzmT5+lkCwovjzaEZZ4gKyIH5A=
|
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 h1:Srzik+J9mivH1alRACTbys2xOxs0lRH9qnTA7Y1OYVo=
|
||||||
@@ -53,6 +55,8 @@ github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hC
|
|||||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||||
|
github.com/beefsack/go-rate v0.0.0-20180408011153-efa7637bb9b6 h1:KXlsf+qt/X5ttPGEjR0tPH1xaWWoKBEg9Q1THAj2h3I=
|
||||||
|
github.com/beefsack/go-rate v0.0.0-20180408011153-efa7637bb9b6/go.mod h1:6YNgTHLutezwnBvyneBbwvB8C82y3dcoOj5EQJIdGXA=
|
||||||
github.com/beefsack/go-rate v0.0.0-20200827232406-6cde80facd47 h1:M57m0xQqZIhx7CEJgeLSvRFKEK1RjzRuIXiA3HfYU7g=
|
github.com/beefsack/go-rate v0.0.0-20200827232406-6cde80facd47 h1:M57m0xQqZIhx7CEJgeLSvRFKEK1RjzRuIXiA3HfYU7g=
|
||||||
github.com/beefsack/go-rate v0.0.0-20200827232406-6cde80facd47/go.mod h1:6YNgTHLutezwnBvyneBbwvB8C82y3dcoOj5EQJIdGXA=
|
github.com/beefsack/go-rate v0.0.0-20200827232406-6cde80facd47/go.mod h1:6YNgTHLutezwnBvyneBbwvB8C82y3dcoOj5EQJIdGXA=
|
||||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||||
@@ -247,8 +251,10 @@ 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.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 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/lucas-clemente/quic-go v0.19.3 h1:eCDQqvGBB+kCTkA0XrAFtNe81FMa0/fn4QSoeAbmiF4=
|
github.com/lucas-clemente/quic-go v0.18.1 h1:DMR7guC0NtVS8zNZR3IO7NARZvZygkSC56GGtC6cyys=
|
||||||
github.com/lucas-clemente/quic-go v0.19.3/go.mod h1:ADXpNbTQjq1hIzCpB+y/k5iz4n4z4IwqoLb94Kh5Hu8=
|
github.com/lucas-clemente/quic-go v0.18.1/go.mod h1:yXttHsSNxQi8AWijC/vLP+OJczXqzHSOcJrM5ITUlCg=
|
||||||
|
github.com/lucas-clemente/quic-go v0.19.1 h1:J9TkQJGJVOR3UmGhd4zdVYwKSA0EoXbLRf15uQJ6gT4=
|
||||||
|
github.com/lucas-clemente/quic-go v0.19.1/go.mod h1:ZUygOqIoai0ASXXLJ92LTnKdbqh9MHCLTX6Nr1jUrK0=
|
||||||
github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
|
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/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||||
@@ -259,9 +265,12 @@ github.com/markbates/oncer v1.0.0 h1:E83IaVAHygyndzPimgUYJjbshhDTALZyXxvk9FOlQRY
|
|||||||
github.com/markbates/oncer v1.0.0/go.mod h1:Z59JA581E9GP6w96jai+TGqafHPW+cPfRxz2aSZ0mcI=
|
github.com/markbates/oncer v1.0.0/go.mod h1:Z59JA581E9GP6w96jai+TGqafHPW+cPfRxz2aSZ0mcI=
|
||||||
github.com/markbates/safe v1.0.1 h1:yjZkbvRM6IzKj9tlu/zMJLS0n/V351OZWRnF3QfaUxI=
|
github.com/markbates/safe v1.0.1 h1:yjZkbvRM6IzKj9tlu/zMJLS0n/V351OZWRnF3QfaUxI=
|
||||||
github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
|
github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
|
||||||
|
github.com/marten-seemann/qpack v0.2.0/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc=
|
||||||
github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc=
|
github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc=
|
||||||
github.com/marten-seemann/qtls v0.10.0 h1:ECsuYUKalRL240rRD4Ri33ISb7kAQ3qGDlrrl55b2pc=
|
github.com/marten-seemann/qtls v0.10.0 h1:ECsuYUKalRL240rRD4Ri33ISb7kAQ3qGDlrrl55b2pc=
|
||||||
github.com/marten-seemann/qtls v0.10.0/go.mod h1:UvMd1oaYDACI99/oZUYLzMCkBXQVT0aGm99sJhbT8hs=
|
github.com/marten-seemann/qtls v0.10.0/go.mod h1:UvMd1oaYDACI99/oZUYLzMCkBXQVT0aGm99sJhbT8hs=
|
||||||
|
github.com/marten-seemann/qtls-go1-15 v0.1.0 h1:i/YPXVxz8q9umso/5y474CNcHmTpA+5DH+mFPjx6PZg=
|
||||||
|
github.com/marten-seemann/qtls-go1-15 v0.1.0/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I=
|
||||||
github.com/marten-seemann/qtls-go1-15 v0.1.1 h1:LIH6K34bPVttyXnUWixk0bzH6/N07VxbSabxn5A5gZQ=
|
github.com/marten-seemann/qtls-go1-15 v0.1.1 h1:LIH6K34bPVttyXnUWixk0bzH6/N07VxbSabxn5A5gZQ=
|
||||||
github.com/marten-seemann/qtls-go1-15 v0.1.1/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I=
|
github.com/marten-seemann/qtls-go1-15 v0.1.1/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I=
|
||||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||||
@@ -280,6 +289,7 @@ github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00v
|
|||||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||||
github.com/miekg/dns v1.1.29 h1:xHBEhR+t5RzcFJjBLJlax2daXOrTYtr9z4WdKEfWFzg=
|
github.com/miekg/dns v1.1.29 h1:xHBEhR+t5RzcFJjBLJlax2daXOrTYtr9z4WdKEfWFzg=
|
||||||
github.com/miekg/dns v1.1.29/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
github.com/miekg/dns v1.1.29/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 h1:oTfOaDH+mZkdcgdIjH6yBajRGtIwcwcaR+rt23ZSrJs=
|
||||||
github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
||||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||||
@@ -447,10 +457,10 @@ 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-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 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9 h1:sYNJzB4J8toYPQTM6pAkcmBRgw9SnQKP9oXCHfgy604=
|
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E=
|
||||||
golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20201217014255-9d1352758620 h1:3wPMTskHO3+O6jqTEXyFcsnuxMQOqYSaHsDxcbUXpqA=
|
golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9 h1:phUcVbl53swtrUN8kQEXFhUxPlIlWyBfKmidCu7P95o=
|
||||||
golang.org/x/crypto v0.0.0-20201217014255-9d1352758620/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||||
@@ -506,12 +516,9 @@ golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgN
|
|||||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
|
golang.org/x/net v0.0.0-20201016165138-7b1cca2348c0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME=
|
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME=
|
||||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11 h1:lwlPPsmjDKK0J6eG6xDWd5XPehI0R024zxjDnw3esPA=
|
|
||||||
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
|
||||||
golang.org/x/net v0.0.0-20201216054612-986b41b23924 h1:QsnDpLLOKwHBBDa8nDws4DYNc/ryVW2vCpxCs09d4PY=
|
|
||||||
golang.org/x/net v0.0.0-20201216054612-986b41b23924/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
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-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
@@ -567,19 +574,14 @@ golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201112073958-5cba982894dd h1:5CtCZbICpIOFdgO940moixOPjc0178IU44m4EjOO5IY=
|
golang.org/x/sys v0.0.0-20201112073958-5cba982894dd h1:5CtCZbICpIOFdgO940moixOPjc0178IU44m4EjOO5IY=
|
||||||
golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201214095126-aec9a390925b h1:tv7/y4pd+sR8bcNb2D6o7BNU6zjWm0VjQLac+w7fNNM=
|
|
||||||
golang.org/x/sys v0.0.0-20201214095126-aec9a390925b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e h1:AyodaIpKjppX+cBfTASF2E1US3H2JFBj920Ot3rtDjs=
|
|
||||||
golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221 h1:/ZHdbVpdR/jk3g30/d4yUL0JU9kksj8+F/bnQUVLGDM=
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221 h1:/ZHdbVpdR/jk3g30/d4yUL0JU9kksj8+F/bnQUVLGDM=
|
||||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
|
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
|
||||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
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.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
@@ -694,6 +696,8 @@ 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 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
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-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 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
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=
|
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
|
||||||
|
|||||||
@@ -188,7 +188,7 @@ func BlockedSvcKnown(s string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ApplyBlockedServices - set blocked services settings for this DNS request
|
// ApplyBlockedServices - set blocked services settings for this DNS request
|
||||||
func (d *DNSFilter) ApplyBlockedServices(setts *RequestFilteringSettings, list []string, global bool) {
|
func (d *Dnsfilter) ApplyBlockedServices(setts *RequestFilteringSettings, list []string, global bool) {
|
||||||
setts.ServicesRules = []ServiceEntry{}
|
setts.ServicesRules = []ServiceEntry{}
|
||||||
if global {
|
if global {
|
||||||
d.confLock.RLock()
|
d.confLock.RLock()
|
||||||
@@ -210,7 +210,7 @@ func (d *DNSFilter) ApplyBlockedServices(setts *RequestFilteringSettings, list [
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DNSFilter) handleBlockedServicesList(w http.ResponseWriter, r *http.Request) {
|
func (d *Dnsfilter) handleBlockedServicesList(w http.ResponseWriter, r *http.Request) {
|
||||||
d.confLock.RLock()
|
d.confLock.RLock()
|
||||||
list := d.Config.BlockedServices
|
list := d.Config.BlockedServices
|
||||||
d.confLock.RUnlock()
|
d.confLock.RUnlock()
|
||||||
@@ -223,7 +223,7 @@ func (d *DNSFilter) handleBlockedServicesList(w http.ResponseWriter, r *http.Req
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DNSFilter) handleBlockedServicesSet(w http.ResponseWriter, r *http.Request) {
|
func (d *Dnsfilter) handleBlockedServicesSet(w http.ResponseWriter, r *http.Request) {
|
||||||
list := []string{}
|
list := []string{}
|
||||||
err := json.NewDecoder(r.Body).Decode(&list)
|
err := json.NewDecoder(r.Body).Decode(&list)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -241,7 +241,7 @@ func (d *DNSFilter) handleBlockedServicesSet(w http.ResponseWriter, r *http.Requ
|
|||||||
}
|
}
|
||||||
|
|
||||||
// registerBlockedServicesHandlers - register HTTP handlers
|
// registerBlockedServicesHandlers - register HTTP handlers
|
||||||
func (d *DNSFilter) registerBlockedServicesHandlers() {
|
func (d *Dnsfilter) registerBlockedServicesHandlers() {
|
||||||
d.Config.HTTPRegister("GET", "/control/blocked_services/list", d.handleBlockedServicesList)
|
d.Config.HTTPRegister("GET", "/control/blocked_services/list", d.handleBlockedServicesList)
|
||||||
d.Config.HTTPRegister("POST", "/control/blocked_services/set", d.handleBlockedServicesSet)
|
d.Config.HTTPRegister("POST", "/control/blocked_services/set", d.handleBlockedServicesSet)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Package dnsfilter implements a DNS request and response filter.
|
// Package dnsfilter implements a DNS filter.
|
||||||
package dnsfilter
|
package dnsfilter
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -91,12 +91,12 @@ type filtersInitializerParams struct {
|
|||||||
blockFilters []Filter
|
blockFilters []Filter
|
||||||
}
|
}
|
||||||
|
|
||||||
// DNSFilter matches hostnames and DNS requests against filtering rules.
|
// Dnsfilter holds added rules and performs hostname matches against the rules
|
||||||
type DNSFilter struct {
|
type Dnsfilter struct {
|
||||||
rulesStorage *filterlist.RuleStorage
|
rulesStorage *filterlist.RuleStorage
|
||||||
filteringEngine *urlfilter.DNSEngine
|
filteringEngine *urlfilter.DNSEngine
|
||||||
rulesStorageAllow *filterlist.RuleStorage
|
rulesStorageWhite *filterlist.RuleStorage
|
||||||
filteringEngineAllow *urlfilter.DNSEngine
|
filteringEngineWhite *urlfilter.DNSEngine
|
||||||
engineLock sync.RWMutex
|
engineLock sync.RWMutex
|
||||||
|
|
||||||
parentalServer string // access via methods
|
parentalServer string // access via methods
|
||||||
@@ -127,16 +127,13 @@ const (
|
|||||||
|
|
||||||
// NotFilteredNotFound - host was not find in any checks, default value for result
|
// NotFilteredNotFound - host was not find in any checks, default value for result
|
||||||
NotFilteredNotFound Reason = iota
|
NotFilteredNotFound Reason = iota
|
||||||
// NotFilteredAllowList - the host is explicitly allowed
|
// NotFilteredWhiteList - the host is explicitly whitelisted
|
||||||
NotFilteredAllowList
|
NotFilteredWhiteList
|
||||||
// NotFilteredError is returned when there was an error during
|
|
||||||
// checking. Reserved, currently unused.
|
|
||||||
NotFilteredError
|
|
||||||
|
|
||||||
// reasons for filtering
|
// reasons for filtering
|
||||||
|
|
||||||
// FilteredBlockList - the host was matched to be advertising host
|
// FilteredBlackList - the host was matched to be advertising host
|
||||||
FilteredBlockList
|
FilteredBlackList
|
||||||
// FilteredSafeBrowsing - the host was matched to be malicious/phishing
|
// FilteredSafeBrowsing - the host was matched to be malicious/phishing
|
||||||
FilteredSafeBrowsing
|
FilteredSafeBrowsing
|
||||||
// FilteredParental - the host was matched to be outside of parental control settings
|
// FilteredParental - the host was matched to be outside of parental control settings
|
||||||
@@ -148,45 +145,33 @@ const (
|
|||||||
// FilteredBlockedService - the host is blocked by "blocked services" settings
|
// FilteredBlockedService - the host is blocked by "blocked services" settings
|
||||||
FilteredBlockedService
|
FilteredBlockedService
|
||||||
|
|
||||||
// ReasonRewrite is returned when there was a rewrite by
|
// ReasonRewrite - rewrite rule was applied
|
||||||
// a legacy DNS Rewrite rule.
|
|
||||||
ReasonRewrite
|
ReasonRewrite
|
||||||
|
|
||||||
// RewriteAutoHosts is returned when there was a rewrite by
|
// RewriteEtcHosts - rewrite by /etc/hosts rule
|
||||||
// autohosts rules (/etc/hosts and so on).
|
RewriteEtcHosts
|
||||||
RewriteAutoHosts
|
|
||||||
|
|
||||||
// DNSRewriteRule is returned when a $dnsrewrite filter rule was
|
|
||||||
// applied.
|
|
||||||
DNSRewriteRule
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO(a.garipov): Resync with actual code names or replace completely
|
|
||||||
// in HTTP API v1.
|
|
||||||
var reasonNames = []string{
|
var reasonNames = []string{
|
||||||
NotFilteredNotFound: "NotFilteredNotFound",
|
"NotFilteredNotFound",
|
||||||
NotFilteredAllowList: "NotFilteredWhiteList",
|
"NotFilteredWhiteList",
|
||||||
NotFilteredError: "NotFilteredError",
|
"NotFilteredError",
|
||||||
|
|
||||||
FilteredBlockList: "FilteredBlackList",
|
"FilteredBlackList",
|
||||||
FilteredSafeBrowsing: "FilteredSafeBrowsing",
|
"FilteredSafeBrowsing",
|
||||||
FilteredParental: "FilteredParental",
|
"FilteredParental",
|
||||||
FilteredInvalid: "FilteredInvalid",
|
"FilteredInvalid",
|
||||||
FilteredSafeSearch: "FilteredSafeSearch",
|
"FilteredSafeSearch",
|
||||||
FilteredBlockedService: "FilteredBlockedService",
|
"FilteredBlockedService",
|
||||||
|
|
||||||
ReasonRewrite: "Rewrite",
|
"Rewrite",
|
||||||
|
"RewriteEtcHosts",
|
||||||
RewriteAutoHosts: "RewriteEtcHosts",
|
|
||||||
|
|
||||||
DNSRewriteRule: "DNSRewriteRule",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r Reason) String() string {
|
func (r Reason) String() string {
|
||||||
if r < 0 || int(r) >= len(reasonNames) {
|
if uint(r) >= uint(len(reasonNames)) {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
return reasonNames[r]
|
return reasonNames[r]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,7 +186,7 @@ func (r Reason) In(reasons ...Reason) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetConfig - get configuration
|
// GetConfig - get configuration
|
||||||
func (d *DNSFilter) GetConfig() RequestFilteringSettings {
|
func (d *Dnsfilter) GetConfig() RequestFilteringSettings {
|
||||||
c := RequestFilteringSettings{}
|
c := RequestFilteringSettings{}
|
||||||
// d.confLock.RLock()
|
// d.confLock.RLock()
|
||||||
c.SafeSearchEnabled = d.Config.SafeSearchEnabled
|
c.SafeSearchEnabled = d.Config.SafeSearchEnabled
|
||||||
@@ -212,7 +197,7 @@ func (d *DNSFilter) GetConfig() RequestFilteringSettings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// WriteDiskConfig - write configuration
|
// WriteDiskConfig - write configuration
|
||||||
func (d *DNSFilter) WriteDiskConfig(c *Config) {
|
func (d *Dnsfilter) WriteDiskConfig(c *Config) {
|
||||||
d.confLock.Lock()
|
d.confLock.Lock()
|
||||||
*c = d.Config
|
*c = d.Config
|
||||||
c.Rewrites = rewriteArrayDup(d.Config.Rewrites)
|
c.Rewrites = rewriteArrayDup(d.Config.Rewrites)
|
||||||
@@ -223,7 +208,7 @@ func (d *DNSFilter) WriteDiskConfig(c *Config) {
|
|||||||
// SetFilters - set new filters (synchronously or asynchronously)
|
// SetFilters - set new filters (synchronously or asynchronously)
|
||||||
// When filters are set asynchronously, the old filters continue working until the new filters are ready.
|
// 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.
|
// In this case the caller must ensure that the old filter files are intact.
|
||||||
func (d *DNSFilter) SetFilters(blockFilters, allowFilters []Filter, async bool) error {
|
func (d *Dnsfilter) SetFilters(blockFilters, allowFilters []Filter, async bool) error {
|
||||||
if async {
|
if async {
|
||||||
params := filtersInitializerParams{
|
params := filtersInitializerParams{
|
||||||
allowFilters: allowFilters,
|
allowFilters: allowFilters,
|
||||||
@@ -257,7 +242,7 @@ func (d *DNSFilter) SetFilters(blockFilters, allowFilters []Filter, async bool)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Starts initializing new filters by signal from channel
|
// Starts initializing new filters by signal from channel
|
||||||
func (d *DNSFilter) filtersInitializer() {
|
func (d *Dnsfilter) filtersInitializer() {
|
||||||
for {
|
for {
|
||||||
params := <-d.filtersInitializerChan
|
params := <-d.filtersInitializerChan
|
||||||
err := d.initFiltering(params.allowFilters, params.blockFilters)
|
err := d.initFiltering(params.allowFilters, params.blockFilters)
|
||||||
@@ -269,13 +254,13 @@ func (d *DNSFilter) filtersInitializer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Close - close the object
|
// Close - close the object
|
||||||
func (d *DNSFilter) Close() {
|
func (d *Dnsfilter) Close() {
|
||||||
d.engineLock.Lock()
|
d.engineLock.Lock()
|
||||||
defer d.engineLock.Unlock()
|
defer d.engineLock.Unlock()
|
||||||
d.reset()
|
d.reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DNSFilter) reset() {
|
func (d *Dnsfilter) reset() {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
if d.rulesStorage != nil {
|
if d.rulesStorage != nil {
|
||||||
@@ -285,15 +270,16 @@ func (d *DNSFilter) reset() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if d.rulesStorageAllow != nil {
|
if d.rulesStorageWhite != nil {
|
||||||
err = d.rulesStorageAllow.Close()
|
err = d.rulesStorageWhite.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("dnsfilter: rulesStorageAllow.Close: %s", err)
|
log.Error("dnsfilter: rulesStorageWhite.Close: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type dnsFilterContext struct {
|
type dnsFilterContext struct {
|
||||||
|
stats Stats
|
||||||
safebrowsingCache cache.Cache
|
safebrowsingCache cache.Cache
|
||||||
parentalCache cache.Cache
|
parentalCache cache.Cache
|
||||||
safeSearchCache cache.Cache
|
safeSearchCache cache.Cache
|
||||||
@@ -301,63 +287,34 @@ type dnsFilterContext struct {
|
|||||||
|
|
||||||
var gctx dnsFilterContext // global dnsfilter context
|
var gctx dnsFilterContext // global dnsfilter context
|
||||||
|
|
||||||
// ResultRule contains information about applied rules.
|
// Result holds state of hostname check
|
||||||
type ResultRule struct {
|
|
||||||
// FilterListID is the ID of the rule's filter list.
|
|
||||||
FilterListID int64 `json:",omitempty"`
|
|
||||||
// Text is the text of the rule.
|
|
||||||
Text string `json:",omitempty"`
|
|
||||||
// IP is the host IP. It is nil unless the rule uses the
|
|
||||||
// /etc/hosts syntax or the reason is FilteredSafeSearch.
|
|
||||||
IP net.IP `json:",omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Result contains the result of a request check.
|
|
||||||
//
|
|
||||||
// All fields transitively have omitempty tags so that the query log
|
|
||||||
// doesn't become too large.
|
|
||||||
//
|
|
||||||
// TODO(a.garipov): Clarify relationships between fields. Perhaps
|
|
||||||
// replace with a sum type or an interface?
|
|
||||||
type Result struct {
|
type Result struct {
|
||||||
// IsFiltered is true if the request is filtered.
|
IsFiltered bool `json:",omitempty"` // True if the host name is filtered
|
||||||
IsFiltered bool `json:",omitempty"`
|
Reason Reason `json:",omitempty"` // Reason for blocking / unblocking
|
||||||
|
Rule string `json:",omitempty"` // Original rule text
|
||||||
|
IP net.IP `json:",omitempty"` // Not nil only in the case of a hosts file syntax
|
||||||
|
FilterID int64 `json:",omitempty"` // Filter ID the rule belongs to
|
||||||
|
|
||||||
// Reason is the reason for blocking or unblocking the request.
|
// for ReasonRewrite:
|
||||||
Reason Reason `json:",omitempty"`
|
CanonName string `json:",omitempty"` // CNAME value
|
||||||
|
|
||||||
// Rules are applied rules. If Rules are not empty, each rule
|
// for RewriteEtcHosts:
|
||||||
// is not nil.
|
|
||||||
Rules []*ResultRule `json:",omitempty"`
|
|
||||||
|
|
||||||
// ReverseHosts is the reverse lookup rewrite result. It is
|
|
||||||
// empty unless Reason is set to RewriteAutoHosts.
|
|
||||||
ReverseHosts []string `json:",omitempty"`
|
ReverseHosts []string `json:",omitempty"`
|
||||||
|
|
||||||
// IPList is the lookup rewrite result. It is empty unless
|
// for ReasonRewrite & RewriteEtcHosts:
|
||||||
// Reason is set to RewriteAutoHosts or ReasonRewrite.
|
IPList []net.IP `json:",omitempty"` // list of IP addresses
|
||||||
IPList []net.IP `json:",omitempty"`
|
|
||||||
|
|
||||||
// CanonName is the CNAME value from the lookup rewrite result.
|
// for FilteredBlockedService:
|
||||||
// It is empty unless Reason is set to ReasonRewrite.
|
ServiceName string `json:",omitempty"` // Name of the blocked service
|
||||||
CanonName string `json:",omitempty"`
|
|
||||||
|
|
||||||
// ServiceName is the name of the blocked service. It is empty
|
|
||||||
// unless Reason is set to FilteredBlockedService.
|
|
||||||
ServiceName string `json:",omitempty"`
|
|
||||||
|
|
||||||
// DNSRewriteResult is the $dnsrewrite filter rule result.
|
|
||||||
DNSRewriteResult *DNSRewriteResult `json:",omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Matched returns true if any match at all was found regardless of
|
// Matched can be used to see if any match at all was found, no matter filtered or not
|
||||||
// whether it was filtered or not.
|
|
||||||
func (r Reason) Matched() bool {
|
func (r Reason) Matched() bool {
|
||||||
return r != NotFilteredNotFound
|
return r != NotFilteredNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckHostRules tries to match the host against filtering rules only.
|
// CheckHostRules tries to match the host against filtering rules only
|
||||||
func (d *DNSFilter) CheckHostRules(host string, qtype uint16, setts *RequestFilteringSettings) (Result, error) {
|
func (d *Dnsfilter) CheckHostRules(host string, qtype uint16, setts *RequestFilteringSettings) (Result, error) {
|
||||||
if !setts.FilteringEnabled {
|
if !setts.FilteringEnabled {
|
||||||
return Result{}, nil
|
return Result{}, nil
|
||||||
}
|
}
|
||||||
@@ -365,9 +322,9 @@ func (d *DNSFilter) CheckHostRules(host string, qtype uint16, setts *RequestFilt
|
|||||||
return d.matchHost(host, qtype, *setts)
|
return d.matchHost(host, qtype, *setts)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckHost tries to match the host against filtering rules, then
|
// CheckHost tries to match the host against filtering rules,
|
||||||
// safebrowsing and parental control rules, if they are enabled.
|
// then safebrowsing and parental if they are enabled
|
||||||
func (d *DNSFilter) CheckHost(host string, qtype uint16, setts *RequestFilteringSettings) (Result, error) {
|
func (d *Dnsfilter) CheckHost(host string, qtype uint16, setts *RequestFilteringSettings) (Result, error) {
|
||||||
// sometimes DNS clients will try to resolve ".", which is a request to get root servers
|
// sometimes DNS clients will try to resolve ".", which is a request to get root servers
|
||||||
if host == "" {
|
if host == "" {
|
||||||
return Result{Reason: NotFilteredNotFound}, nil
|
return Result{Reason: NotFilteredNotFound}, nil
|
||||||
@@ -392,6 +349,9 @@ func (d *DNSFilter) CheckHost(host string, qtype uint16, setts *RequestFiltering
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Then check the filter lists.
|
||||||
|
// if request is blocked -- it should be blocked.
|
||||||
|
// if it is whitelisted -- we should do nothing with it anymore.
|
||||||
if setts.FilteringEnabled {
|
if setts.FilteringEnabled {
|
||||||
result, err = d.matchHost(host, qtype, *setts)
|
result, err = d.matchHost(host, qtype, *setts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -450,10 +410,10 @@ func (d *DNSFilter) CheckHost(host string, qtype uint16, setts *RequestFiltering
|
|||||||
return Result{}, nil
|
return Result{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DNSFilter) checkAutoHosts(host string, qtype uint16, result *Result) (matched bool) {
|
func (d *Dnsfilter) checkAutoHosts(host string, qtype uint16, result *Result) (matched bool) {
|
||||||
ips := d.Config.AutoHosts.Process(host, qtype)
|
ips := d.Config.AutoHosts.Process(host, qtype)
|
||||||
if ips != nil {
|
if ips != nil {
|
||||||
result.Reason = RewriteAutoHosts
|
result.Reason = RewriteEtcHosts
|
||||||
result.IPList = ips
|
result.IPList = ips
|
||||||
|
|
||||||
return true
|
return true
|
||||||
@@ -461,7 +421,7 @@ func (d *DNSFilter) checkAutoHosts(host string, qtype uint16, result *Result) (m
|
|||||||
|
|
||||||
revHosts := d.Config.AutoHosts.ProcessReverse(host, qtype)
|
revHosts := d.Config.AutoHosts.ProcessReverse(host, qtype)
|
||||||
if len(revHosts) != 0 {
|
if len(revHosts) != 0 {
|
||||||
result.Reason = RewriteAutoHosts
|
result.Reason = RewriteEtcHosts
|
||||||
|
|
||||||
// TODO(a.garipov): Optimize this with a buffer.
|
// TODO(a.garipov): Optimize this with a buffer.
|
||||||
result.ReverseHosts = make([]string, len(revHosts))
|
result.ReverseHosts = make([]string, len(revHosts))
|
||||||
@@ -482,7 +442,9 @@ func (d *DNSFilter) checkAutoHosts(host string, qtype uint16, result *Result) (m
|
|||||||
// . repeat for the new domain name (Note: we return only the last CNAME)
|
// . repeat for the new domain name (Note: we return only the last CNAME)
|
||||||
// . Find A or AAAA record for a domain name (exact match or by wildcard)
|
// . Find A or AAAA record for a domain name (exact match or by wildcard)
|
||||||
// . if found, set IP addresses (IPv4 or IPv6 depending on qtype) in Result.IPList array
|
// . if found, set IP addresses (IPv4 or IPv6 depending on qtype) in Result.IPList array
|
||||||
func (d *DNSFilter) processRewrites(host string, qtype uint16) (res Result) {
|
func (d *Dnsfilter) processRewrites(host string, qtype uint16) Result {
|
||||||
|
var res Result
|
||||||
|
|
||||||
d.confLock.RLock()
|
d.confLock.RLock()
|
||||||
defer d.confLock.RUnlock()
|
defer d.confLock.RUnlock()
|
||||||
|
|
||||||
@@ -497,8 +459,7 @@ func (d *DNSFilter) processRewrites(host string, qtype uint16) (res Result) {
|
|||||||
log.Debug("Rewrite: CNAME for %s is %s", host, rr[0].Answer)
|
log.Debug("Rewrite: CNAME for %s is %s", host, rr[0].Answer)
|
||||||
|
|
||||||
if host == rr[0].Answer { // "host == CNAME" is an exception
|
if host == rr[0].Answer { // "host == CNAME" is an exception
|
||||||
res.Reason = NotFilteredNotFound
|
res.Reason = 0
|
||||||
|
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -540,16 +501,9 @@ func matchBlockedServicesRules(host string, svcs []ServiceEntry) Result {
|
|||||||
res.Reason = FilteredBlockedService
|
res.Reason = FilteredBlockedService
|
||||||
res.IsFiltered = true
|
res.IsFiltered = true
|
||||||
res.ServiceName = s.Name
|
res.ServiceName = s.Name
|
||||||
|
res.Rule = rule.Text()
|
||||||
ruleText := rule.Text()
|
log.Debug("Blocked Services: matched rule: %s host: %s service: %s",
|
||||||
res.Rules = []*ResultRule{{
|
res.Rule, host, s.Name)
|
||||||
FilterListID: int64(rule.GetFilterListID()),
|
|
||||||
Text: ruleText,
|
|
||||||
}}
|
|
||||||
|
|
||||||
log.Debug("blocked services: matched rule: %s host: %s service: %s",
|
|
||||||
ruleText, host, s.Name)
|
|
||||||
|
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -616,12 +570,12 @@ func createFilteringEngine(filters []Filter) (*filterlist.RuleStorage, *urlfilte
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Initialize urlfilter objects.
|
// Initialize urlfilter objects.
|
||||||
func (d *DNSFilter) initFiltering(allowFilters, blockFilters []Filter) error {
|
func (d *Dnsfilter) initFiltering(allowFilters, blockFilters []Filter) error {
|
||||||
rulesStorage, filteringEngine, err := createFilteringEngine(blockFilters)
|
rulesStorage, filteringEngine, err := createFilteringEngine(blockFilters)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
rulesStorageAllow, filteringEngineAllow, err := createFilteringEngine(allowFilters)
|
rulesStorageWhite, filteringEngineWhite, err := createFilteringEngine(allowFilters)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -630,8 +584,8 @@ func (d *DNSFilter) initFiltering(allowFilters, blockFilters []Filter) error {
|
|||||||
d.reset()
|
d.reset()
|
||||||
d.rulesStorage = rulesStorage
|
d.rulesStorage = rulesStorage
|
||||||
d.filteringEngine = filteringEngine
|
d.filteringEngine = filteringEngine
|
||||||
d.rulesStorageAllow = rulesStorageAllow
|
d.rulesStorageWhite = rulesStorageWhite
|
||||||
d.filteringEngineAllow = filteringEngineAllow
|
d.filteringEngineWhite = filteringEngineWhite
|
||||||
d.engineLock.Unlock()
|
d.engineLock.Unlock()
|
||||||
|
|
||||||
// Make sure that the OS reclaims memory as soon as possible
|
// Make sure that the OS reclaims memory as soon as possible
|
||||||
@@ -641,31 +595,9 @@ func (d *DNSFilter) initFiltering(allowFilters, blockFilters []Filter) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// matchHostProcessAllowList processes the allowlist logic of host
|
|
||||||
// matching.
|
|
||||||
func (d *DNSFilter) matchHostProcessAllowList(host string, dnsres urlfilter.DNSResult) (res Result, err error) {
|
|
||||||
var rule rules.Rule
|
|
||||||
if dnsres.NetworkRule != nil {
|
|
||||||
rule = dnsres.NetworkRule
|
|
||||||
} else if len(dnsres.HostRulesV4) > 0 {
|
|
||||||
rule = dnsres.HostRulesV4[0]
|
|
||||||
} else if len(dnsres.HostRulesV6) > 0 {
|
|
||||||
rule = dnsres.HostRulesV6[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
if rule == nil {
|
|
||||||
return Result{}, fmt.Errorf("invalid dns result: rules are empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Debug("Filtering: found allowlist rule for host %q: %q list_id: %d",
|
|
||||||
host, rule.Text(), rule.GetFilterListID())
|
|
||||||
|
|
||||||
return makeResult(rule, NotFilteredAllowList), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// matchHost is a low-level way to check only if hostname is filtered by rules,
|
// matchHost is a low-level way to check only if hostname is filtered by rules,
|
||||||
// skipping expensive safebrowsing and parental lookups.
|
// skipping expensive safebrowsing and parental lookups.
|
||||||
func (d *DNSFilter) matchHost(host string, qtype uint16, setts RequestFilteringSettings) (res Result, err error) {
|
func (d *Dnsfilter) matchHost(host string, qtype uint16, setts RequestFilteringSettings) (Result, error) {
|
||||||
d.engineLock.RLock()
|
d.engineLock.RLock()
|
||||||
// Keep in mind that this lock must be held no just when calling Match()
|
// Keep in mind that this lock must be held no just when calling Match()
|
||||||
// but also while using the rules returned by it.
|
// but also while using the rules returned by it.
|
||||||
@@ -679,10 +611,22 @@ func (d *DNSFilter) matchHost(host string, qtype uint16, setts RequestFilteringS
|
|||||||
DNSType: qtype,
|
DNSType: qtype,
|
||||||
}
|
}
|
||||||
|
|
||||||
if d.filteringEngineAllow != nil {
|
if d.filteringEngineWhite != nil {
|
||||||
dnsres, ok := d.filteringEngineAllow.MatchRequest(ureq)
|
rr, ok := d.filteringEngineWhite.MatchRequest(ureq)
|
||||||
if ok {
|
if ok {
|
||||||
return d.matchHostProcessAllowList(host, dnsres)
|
var rule rules.Rule
|
||||||
|
if rr.NetworkRule != nil {
|
||||||
|
rule = rr.NetworkRule
|
||||||
|
} else if rr.HostRulesV4 != nil {
|
||||||
|
rule = rr.HostRulesV4[0]
|
||||||
|
} else if rr.HostRulesV6 != nil {
|
||||||
|
rule = rr.HostRulesV6[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -690,87 +634,68 @@ func (d *DNSFilter) matchHost(host string, qtype uint16, setts RequestFilteringS
|
|||||||
return Result{}, nil
|
return Result{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
dnsres, ok := d.filteringEngine.MatchRequest(ureq)
|
rr, ok := d.filteringEngine.MatchRequest(ureq)
|
||||||
|
if !ok {
|
||||||
// Check DNS rewrites first, because the API there is a bit
|
|
||||||
// awkward.
|
|
||||||
if dnsr := dnsres.DNSRewrites(); len(dnsr) > 0 {
|
|
||||||
res = d.processDNSRewrites(dnsr)
|
|
||||||
if res.Reason == DNSRewriteRule && res.CanonName == host {
|
|
||||||
// A rewrite of a host to itself. Go on and
|
|
||||||
// try matching other things.
|
|
||||||
} else {
|
|
||||||
return res, nil
|
|
||||||
}
|
|
||||||
} else if !ok {
|
|
||||||
return Result{}, nil
|
return Result{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if dnsres.NetworkRule != nil {
|
if rr.NetworkRule != nil {
|
||||||
log.Debug("Filtering: found rule for host %q: %q list_id: %d",
|
log.Debug("Filtering: found rule for host %q: %q list_id: %d",
|
||||||
host, dnsres.NetworkRule.Text(), dnsres.NetworkRule.GetFilterListID())
|
host, rr.NetworkRule.Text(), rr.NetworkRule.GetFilterListID())
|
||||||
reason := FilteredBlockList
|
reason := FilteredBlackList
|
||||||
if dnsres.NetworkRule.Whitelist {
|
if rr.NetworkRule.Whitelist {
|
||||||
reason = NotFilteredAllowList
|
reason = NotFilteredWhiteList
|
||||||
}
|
}
|
||||||
|
res := makeResult(rr.NetworkRule, reason)
|
||||||
return makeResult(dnsres.NetworkRule, reason), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if qtype == dns.TypeA && dnsres.HostRulesV4 != nil {
|
|
||||||
rule := dnsres.HostRulesV4[0] // note that we process only 1 matched rule
|
|
||||||
log.Debug("Filtering: found rule for host %q: %q list_id: %d",
|
|
||||||
host, rule.Text(), rule.GetFilterListID())
|
|
||||||
res = makeResult(rule, FilteredBlockList)
|
|
||||||
res.Rules[0].IP = rule.IP.To4()
|
|
||||||
|
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if qtype == dns.TypeAAAA && dnsres.HostRulesV6 != nil {
|
if qtype == dns.TypeA && rr.HostRulesV4 != nil {
|
||||||
rule := dnsres.HostRulesV6[0] // note that we process only 1 matched rule
|
rule := rr.HostRulesV4[0] // note that we process only 1 matched rule
|
||||||
log.Debug("Filtering: found rule for host %q: %q list_id: %d",
|
log.Debug("Filtering: found rule for host %q: %q list_id: %d",
|
||||||
host, rule.Text(), rule.GetFilterListID())
|
host, rule.Text(), rule.GetFilterListID())
|
||||||
res = makeResult(rule, FilteredBlockList)
|
res := makeResult(rule, FilteredBlackList)
|
||||||
res.Rules[0].IP = rule.IP
|
res.IP = rule.IP.To4()
|
||||||
|
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if dnsres.HostRulesV4 != nil || dnsres.HostRulesV6 != nil {
|
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 %q: %q list_id: %d",
|
||||||
|
host, rule.Text(), rule.GetFilterListID())
|
||||||
|
res := makeResult(rule, FilteredBlackList)
|
||||||
|
res.IP = rule.IP
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if rr.HostRulesV4 != nil || rr.HostRulesV6 != nil {
|
||||||
// Question Type doesn't match the host rules
|
// Question Type doesn't match the host rules
|
||||||
// Return the first matched host rule, but without an IP address
|
// Return the first matched host rule, but without an IP address
|
||||||
var rule rules.Rule
|
var rule rules.Rule
|
||||||
if dnsres.HostRulesV4 != nil {
|
if rr.HostRulesV4 != nil {
|
||||||
rule = dnsres.HostRulesV4[0]
|
rule = rr.HostRulesV4[0]
|
||||||
} else if dnsres.HostRulesV6 != nil {
|
} else if rr.HostRulesV6 != nil {
|
||||||
rule = dnsres.HostRulesV6[0]
|
rule = rr.HostRulesV6[0]
|
||||||
}
|
}
|
||||||
log.Debug("Filtering: found rule for host %q: %q list_id: %d",
|
log.Debug("Filtering: found rule for host %q: %q list_id: %d",
|
||||||
host, rule.Text(), rule.GetFilterListID())
|
host, rule.Text(), rule.GetFilterListID())
|
||||||
res = makeResult(rule, FilteredBlockList)
|
res := makeResult(rule, FilteredBlackList)
|
||||||
res.Rules[0].IP = net.IP{}
|
res.IP = net.IP{}
|
||||||
|
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return Result{}, nil
|
return Result{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// makeResult returns a properly constructed Result.
|
// Construct Result object
|
||||||
func makeResult(rule rules.Rule, reason Reason) Result {
|
func makeResult(rule rules.Rule, reason Reason) Result {
|
||||||
res := Result{
|
res := Result{}
|
||||||
Reason: reason,
|
res.FilterID = int64(rule.GetFilterListID())
|
||||||
Rules: []*ResultRule{{
|
res.Rule = rule.Text()
|
||||||
FilterListID: int64(rule.GetFilterListID()),
|
res.Reason = reason
|
||||||
Text: rule.Text(),
|
if reason == FilteredBlackList {
|
||||||
}},
|
|
||||||
}
|
|
||||||
|
|
||||||
if reason == FilteredBlockList {
|
|
||||||
res.IsFiltered = true
|
res.IsFiltered = true
|
||||||
}
|
}
|
||||||
|
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -780,7 +705,7 @@ func InitModule() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 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 {
|
func New(c *Config, blockFilters []Filter) *Dnsfilter {
|
||||||
if c != nil {
|
if c != nil {
|
||||||
cacheConf := cache.Config{
|
cacheConf := cache.Config{
|
||||||
EnableLRU: true,
|
EnableLRU: true,
|
||||||
@@ -802,7 +727,7 @@ func New(c *Config, blockFilters []Filter) *DNSFilter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
d := new(DNSFilter)
|
d := new(Dnsfilter)
|
||||||
|
|
||||||
err := d.initSecurityServices()
|
err := d.initSecurityServices()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -840,7 +765,7 @@ func New(c *Config, blockFilters []Filter) *DNSFilter {
|
|||||||
// Start - start the module:
|
// Start - start the module:
|
||||||
// . start async filtering initializer goroutine
|
// . start async filtering initializer goroutine
|
||||||
// . register web handlers
|
// . register web handlers
|
||||||
func (d *DNSFilter) Start() {
|
func (d *Dnsfilter) Start() {
|
||||||
d.filtersInitializerChan = make(chan filtersInitializerParams, 1)
|
d.filtersInitializerChan = make(chan filtersInitializerParams, 1)
|
||||||
go d.filtersInitializer()
|
go d.filtersInitializer()
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ func purgeCaches() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewForTest(c *Config, filters []Filter) *DNSFilter {
|
func NewForTest(c *Config, filters []Filter) *Dnsfilter {
|
||||||
setts = RequestFilteringSettings{}
|
setts = RequestFilteringSettings{}
|
||||||
setts.FilteringEnabled = true
|
setts.FilteringEnabled = true
|
||||||
if c != nil {
|
if c != nil {
|
||||||
@@ -58,48 +58,38 @@ func NewForTest(c *Config, filters []Filter) *DNSFilter {
|
|||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DNSFilter) checkMatch(t *testing.T, hostname string) {
|
func (d *Dnsfilter) checkMatch(t *testing.T, hostname string) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
res, err := d.CheckHost(hostname, dns.TypeA, &setts)
|
ret, err := d.CheckHost(hostname, dns.TypeA, &setts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error while matching host %s: %s", hostname, err)
|
t.Errorf("Error while matching host %s: %s", hostname, err)
|
||||||
}
|
}
|
||||||
if !res.IsFiltered {
|
if !ret.IsFiltered {
|
||||||
t.Errorf("Expected hostname %s to match", hostname)
|
t.Errorf("Expected hostname %s to match", hostname)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DNSFilter) checkMatchIP(t *testing.T, hostname, ip string, qtype uint16) {
|
func (d *Dnsfilter) checkMatchIP(t *testing.T, hostname, ip string, qtype uint16) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
ret, err := d.CheckHost(hostname, qtype, &setts)
|
||||||
res, err := d.CheckHost(hostname, qtype, &setts)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error while matching host %s: %s", hostname, err)
|
t.Errorf("Error while matching host %s: %s", hostname, err)
|
||||||
}
|
}
|
||||||
|
if !ret.IsFiltered {
|
||||||
if !res.IsFiltered {
|
|
||||||
t.Errorf("Expected hostname %s to match", hostname)
|
t.Errorf("Expected hostname %s to match", hostname)
|
||||||
}
|
}
|
||||||
|
if ret.IP == nil || ret.IP.String() != ip {
|
||||||
if len(res.Rules) == 0 {
|
t.Errorf("Expected ip %s to match, actual: %v", ip, ret.IP)
|
||||||
t.Errorf("Expected result to have rules")
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
r := res.Rules[0]
|
|
||||||
if r.IP == nil || r.IP.String() != ip {
|
|
||||||
t.Errorf("Expected ip %s to match, actual: %v", ip, r.IP)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DNSFilter) checkMatchEmpty(t *testing.T, hostname string) {
|
func (d *Dnsfilter) checkMatchEmpty(t *testing.T, hostname string) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
res, err := d.CheckHost(hostname, dns.TypeA, &setts)
|
ret, err := d.CheckHost(hostname, dns.TypeA, &setts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error while matching host %s: %s", hostname, err)
|
t.Errorf("Error while matching host %s: %s", hostname, err)
|
||||||
}
|
}
|
||||||
if res.IsFiltered {
|
if ret.IsFiltered {
|
||||||
t.Errorf("Expected hostname %s to not match", hostname)
|
t.Errorf("Expected hostname %s to not match", hostname)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -130,43 +120,26 @@ func TestEtcHostsMatching(t *testing.T) {
|
|||||||
d.checkMatchIP(t, "block.com", "0.0.0.0", dns.TypeA)
|
d.checkMatchIP(t, "block.com", "0.0.0.0", dns.TypeA)
|
||||||
|
|
||||||
// ...but empty IPv6
|
// ...but empty IPv6
|
||||||
res, err := d.CheckHost("block.com", dns.TypeAAAA, &setts)
|
ret, err := d.CheckHost("block.com", dns.TypeAAAA, &setts)
|
||||||
assert.Nil(t, err)
|
assert.True(t, err == nil && ret.IsFiltered && ret.IP != nil && len(ret.IP) == 0)
|
||||||
assert.True(t, res.IsFiltered)
|
assert.True(t, ret.Rule == "0.0.0.0 block.com")
|
||||||
if assert.Len(t, res.Rules, 1) {
|
|
||||||
assert.Equal(t, "0.0.0.0 block.com", res.Rules[0].Text)
|
|
||||||
assert.Len(t, res.Rules[0].IP, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IPv6
|
// IPv6
|
||||||
d.checkMatchIP(t, "ipv6.com", addr6, dns.TypeAAAA)
|
d.checkMatchIP(t, "ipv6.com", addr6, dns.TypeAAAA)
|
||||||
|
|
||||||
// ...but empty IPv4
|
// ...but empty IPv4
|
||||||
res, err = d.CheckHost("ipv6.com", dns.TypeA, &setts)
|
ret, err = d.CheckHost("ipv6.com", dns.TypeA, &setts)
|
||||||
assert.Nil(t, err)
|
assert.True(t, err == nil && ret.IsFiltered && ret.IP != nil && len(ret.IP) == 0)
|
||||||
assert.True(t, res.IsFiltered)
|
|
||||||
if assert.Len(t, res.Rules, 1) {
|
|
||||||
assert.Equal(t, "::1 ipv6.com", res.Rules[0].Text)
|
|
||||||
assert.Len(t, res.Rules[0].IP, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2 IPv4 (return only the first one)
|
// 2 IPv4 (return only the first one)
|
||||||
res, err = d.CheckHost("host2", dns.TypeA, &setts)
|
ret, err = d.CheckHost("host2", dns.TypeA, &setts)
|
||||||
assert.Nil(t, err)
|
assert.True(t, err == nil && ret.IsFiltered)
|
||||||
assert.True(t, res.IsFiltered)
|
assert.True(t, ret.IP != nil && ret.IP.Equal(net.ParseIP("0.0.0.1")))
|
||||||
if assert.Len(t, res.Rules, 1) {
|
|
||||||
loopback4 := net.IP{0, 0, 0, 1}
|
|
||||||
assert.Equal(t, res.Rules[0].IP, loopback4)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ...and 1 IPv6 address
|
// ...and 1 IPv6 address
|
||||||
res, err = d.CheckHost("host2", dns.TypeAAAA, &setts)
|
ret, err = d.CheckHost("host2", dns.TypeAAAA, &setts)
|
||||||
assert.Nil(t, err)
|
assert.True(t, err == nil && ret.IsFiltered)
|
||||||
assert.True(t, res.IsFiltered)
|
assert.True(t, ret.IP != nil && ret.IP.Equal(net.ParseIP("::1")))
|
||||||
if assert.Len(t, res.Rules, 1) {
|
|
||||||
loopback6 := net.IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}
|
|
||||||
assert.Equal(t, res.Rules[0].IP, loopback6)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFE BROWSING
|
// SAFE BROWSING
|
||||||
@@ -178,6 +151,7 @@ func TestSafeBrowsing(t *testing.T) {
|
|||||||
|
|
||||||
d := NewForTest(&Config{SafeBrowsingEnabled: true}, nil)
|
d := NewForTest(&Config{SafeBrowsingEnabled: true}, nil)
|
||||||
defer d.Close()
|
defer d.Close()
|
||||||
|
gctx.stats.Safebrowsing.Requests = 0
|
||||||
d.checkMatch(t, "wmconvirus.narod.ru")
|
d.checkMatch(t, "wmconvirus.narod.ru")
|
||||||
|
|
||||||
assert.True(t, strings.Contains(logOutput.String(), "SafeBrowsing lookup for wmconvirus.narod.ru"))
|
assert.True(t, strings.Contains(logOutput.String(), "SafeBrowsing lookup for wmconvirus.narod.ru"))
|
||||||
@@ -232,11 +206,13 @@ func TestCheckHostSafeSearchYandex(t *testing.T) {
|
|||||||
|
|
||||||
// Check host for each domain
|
// Check host for each domain
|
||||||
for _, host := range yandex {
|
for _, host := range yandex {
|
||||||
res, err := d.CheckHost(host, dns.TypeA, &setts)
|
result, err := d.CheckHost(host, dns.TypeA, &setts)
|
||||||
assert.Nil(t, err)
|
if err != nil {
|
||||||
assert.True(t, res.IsFiltered)
|
t.Errorf("SafeSearch doesn't work for yandex domain `%s` cause %s", host, err)
|
||||||
if assert.Len(t, res.Rules, 1) {
|
}
|
||||||
assert.Equal(t, res.Rules[0].IP.String(), "213.180.193.56")
|
|
||||||
|
if result.IP.String() != "213.180.193.56" {
|
||||||
|
t.Errorf("SafeSearch doesn't work for yandex domain `%s`", host)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -250,11 +226,13 @@ func TestCheckHostSafeSearchGoogle(t *testing.T) {
|
|||||||
|
|
||||||
// Check host for each domain
|
// Check host for each domain
|
||||||
for _, host := range googleDomains {
|
for _, host := range googleDomains {
|
||||||
res, err := d.CheckHost(host, dns.TypeA, &setts)
|
result, err := d.CheckHost(host, dns.TypeA, &setts)
|
||||||
assert.Nil(t, err)
|
if err != nil {
|
||||||
assert.True(t, res.IsFiltered)
|
t.Errorf("SafeSearch doesn't work for %s cause %s", host, err)
|
||||||
if assert.Len(t, res.Rules, 1) {
|
}
|
||||||
assert.NotEqual(t, res.Rules[0].IP.String(), "0.0.0.0")
|
|
||||||
|
if result.IP == nil {
|
||||||
|
t.Errorf("SafeSearch doesn't work for %s", host)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -264,30 +242,40 @@ func TestSafeSearchCacheYandex(t *testing.T) {
|
|||||||
defer d.Close()
|
defer d.Close()
|
||||||
domain := "yandex.ru"
|
domain := "yandex.ru"
|
||||||
|
|
||||||
// Check host with disabled safesearch.
|
var result Result
|
||||||
res, err := d.CheckHost(domain, dns.TypeA, &setts)
|
var err error
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.False(t, res.IsFiltered)
|
// Check host with disabled safesearch
|
||||||
assert.Len(t, res.Rules, 0)
|
result, err = d.CheckHost(domain, dns.TypeA, &setts)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot check host due to %s", err)
|
||||||
|
}
|
||||||
|
if result.IP != nil {
|
||||||
|
t.Fatalf("SafeSearch is not enabled but there is an answer for `%s` !", domain)
|
||||||
|
}
|
||||||
|
|
||||||
d = NewForTest(&Config{SafeSearchEnabled: true}, nil)
|
d = NewForTest(&Config{SafeSearchEnabled: true}, nil)
|
||||||
defer d.Close()
|
defer d.Close()
|
||||||
|
|
||||||
res, err = d.CheckHost(domain, dns.TypeA, &setts)
|
result, err = d.CheckHost(domain, dns.TypeA, &setts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("CheckHost for safesearh domain %s failed cause %s", domain, err)
|
t.Fatalf("CheckHost for safesearh domain %s failed cause %s", domain, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// For yandex we already know valid ip.
|
// Fir yandex we already know valid ip
|
||||||
if assert.Len(t, res.Rules, 1) {
|
if result.IP.String() != "213.180.193.56" {
|
||||||
assert.Equal(t, res.Rules[0].IP.String(), "213.180.193.56")
|
t.Fatalf("Wrong IP for %s safesearch: %s", domain, result.IP.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check cache.
|
// Check cache
|
||||||
cachedValue, isFound := getCachedResult(gctx.safeSearchCache, domain)
|
cachedValue, isFound := getCachedResult(gctx.safeSearchCache, domain)
|
||||||
assert.True(t, isFound)
|
|
||||||
if assert.Len(t, cachedValue.Rules, 1) {
|
if !isFound {
|
||||||
assert.Equal(t, cachedValue.Rules[0].IP.String(), "213.180.193.56")
|
t.Fatalf("Safesearch cache doesn't work for %s!", domain)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cachedValue.IP.String() != "213.180.193.56" {
|
||||||
|
t.Fatalf("Wrong IP in cache for %s safesearch: %s", domain, cachedValue.IP.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -295,10 +283,13 @@ func TestSafeSearchCacheGoogle(t *testing.T) {
|
|||||||
d := NewForTest(nil, nil)
|
d := NewForTest(nil, nil)
|
||||||
defer d.Close()
|
defer d.Close()
|
||||||
domain := "www.google.ru"
|
domain := "www.google.ru"
|
||||||
res, err := d.CheckHost(domain, dns.TypeA, &setts)
|
result, err := d.CheckHost(domain, dns.TypeA, &setts)
|
||||||
assert.Nil(t, err)
|
if err != nil {
|
||||||
assert.False(t, res.IsFiltered)
|
t.Fatalf("Cannot check host due to %s", err)
|
||||||
assert.Len(t, res.Rules, 0)
|
}
|
||||||
|
if result.IP != nil {
|
||||||
|
t.Fatalf("SafeSearch is not enabled but there is an answer!")
|
||||||
|
}
|
||||||
|
|
||||||
d = NewForTest(&Config{SafeSearchEnabled: true}, nil)
|
d = NewForTest(&Config{SafeSearchEnabled: true}, nil)
|
||||||
defer d.Close()
|
defer d.Close()
|
||||||
@@ -322,17 +313,25 @@ func TestSafeSearchCacheGoogle(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err = d.CheckHost(domain, dns.TypeA, &setts)
|
result, err = d.CheckHost(domain, dns.TypeA, &setts)
|
||||||
assert.Nil(t, err)
|
if err != nil {
|
||||||
if assert.Len(t, res.Rules, 1) {
|
t.Fatalf("CheckHost for safesearh domain %s failed cause %s", domain, err)
|
||||||
assert.True(t, res.Rules[0].IP.Equal(ip))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check cache.
|
if result.IP.String() != ip.String() {
|
||||||
|
t.Fatalf("Wrong IP for %s safesearch: %s. Should be: %s",
|
||||||
|
domain, result.IP.String(), ip)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check cache
|
||||||
cachedValue, isFound := getCachedResult(gctx.safeSearchCache, domain)
|
cachedValue, isFound := getCachedResult(gctx.safeSearchCache, domain)
|
||||||
assert.True(t, isFound)
|
|
||||||
if assert.Len(t, cachedValue.Rules, 1) {
|
if !isFound {
|
||||||
assert.True(t, cachedValue.Rules[0].IP.Equal(ip))
|
t.Fatalf("Safesearch cache doesn't work for %s!", domain)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cachedValue.IP.String() != ip.String() {
|
||||||
|
t.Fatalf("Wrong IP in cache for %s safesearch: %s", domain, cachedValue.IP.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -365,7 +364,7 @@ const nl = "\n"
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
blockingRules = `||example.org^` + nl
|
blockingRules = `||example.org^` + nl
|
||||||
allowlistRules = `||example.org^` + nl + `@@||test.example.org` + nl
|
whitelistRules = `||example.org^` + nl + `@@||test.example.org` + nl
|
||||||
importantRules = `@@||example.org^` + nl + `||test.example.org^$important` + nl
|
importantRules = `@@||example.org^` + nl + `||test.example.org^$important` + nl
|
||||||
regexRules = `/example\.org/` + nl + `@@||test.example.org^` + nl
|
regexRules = `/example\.org/` + nl + `@@||test.example.org^` + nl
|
||||||
maskRules = `test*.example.org^` + nl + `exam*.com` + nl
|
maskRules = `test*.example.org^` + nl + `exam*.com` + nl
|
||||||
@@ -380,49 +379,49 @@ var tests = []struct {
|
|||||||
reason Reason
|
reason Reason
|
||||||
dnsType uint16
|
dnsType uint16
|
||||||
}{
|
}{
|
||||||
{"sanity", "||doubleclick.net^", "www.doubleclick.net", true, FilteredBlockList, dns.TypeA},
|
{"sanity", "||doubleclick.net^", "www.doubleclick.net", true, FilteredBlackList, dns.TypeA},
|
||||||
{"sanity", "||doubleclick.net^", "nodoubleclick.net", false, NotFilteredNotFound, dns.TypeA},
|
{"sanity", "||doubleclick.net^", "nodoubleclick.net", false, NotFilteredNotFound, dns.TypeA},
|
||||||
{"sanity", "||doubleclick.net^", "doubleclick.net.ru", false, NotFilteredNotFound, dns.TypeA},
|
{"sanity", "||doubleclick.net^", "doubleclick.net.ru", false, NotFilteredNotFound, dns.TypeA},
|
||||||
{"sanity", "||doubleclick.net^", "wmconvirus.narod.ru", false, NotFilteredNotFound, dns.TypeA},
|
{"sanity", "||doubleclick.net^", "wmconvirus.narod.ru", false, NotFilteredNotFound, dns.TypeA},
|
||||||
|
|
||||||
{"blocking", blockingRules, "example.org", true, FilteredBlockList, dns.TypeA},
|
{"blocking", blockingRules, "example.org", true, FilteredBlackList, dns.TypeA},
|
||||||
{"blocking", blockingRules, "test.example.org", true, FilteredBlockList, dns.TypeA},
|
{"blocking", blockingRules, "test.example.org", true, FilteredBlackList, dns.TypeA},
|
||||||
{"blocking", blockingRules, "test.test.example.org", true, FilteredBlockList, dns.TypeA},
|
{"blocking", blockingRules, "test.test.example.org", true, FilteredBlackList, dns.TypeA},
|
||||||
{"blocking", blockingRules, "testexample.org", false, NotFilteredNotFound, dns.TypeA},
|
{"blocking", blockingRules, "testexample.org", false, NotFilteredNotFound, dns.TypeA},
|
||||||
{"blocking", blockingRules, "onemoreexample.org", false, NotFilteredNotFound, dns.TypeA},
|
{"blocking", blockingRules, "onemoreexample.org", false, NotFilteredNotFound, dns.TypeA},
|
||||||
|
|
||||||
{"allowlist", allowlistRules, "example.org", true, FilteredBlockList, dns.TypeA},
|
{"whitelist", whitelistRules, "example.org", true, FilteredBlackList, dns.TypeA},
|
||||||
{"allowlist", allowlistRules, "test.example.org", false, NotFilteredAllowList, dns.TypeA},
|
{"whitelist", whitelistRules, "test.example.org", false, NotFilteredWhiteList, dns.TypeA},
|
||||||
{"allowlist", allowlistRules, "test.test.example.org", false, NotFilteredAllowList, dns.TypeA},
|
{"whitelist", whitelistRules, "test.test.example.org", false, NotFilteredWhiteList, dns.TypeA},
|
||||||
{"allowlist", allowlistRules, "testexample.org", false, NotFilteredNotFound, dns.TypeA},
|
{"whitelist", whitelistRules, "testexample.org", false, NotFilteredNotFound, dns.TypeA},
|
||||||
{"allowlist", allowlistRules, "onemoreexample.org", false, NotFilteredNotFound, dns.TypeA},
|
{"whitelist", whitelistRules, "onemoreexample.org", false, NotFilteredNotFound, dns.TypeA},
|
||||||
|
|
||||||
{"important", importantRules, "example.org", false, NotFilteredAllowList, dns.TypeA},
|
{"important", importantRules, "example.org", false, NotFilteredWhiteList, dns.TypeA},
|
||||||
{"important", importantRules, "test.example.org", true, FilteredBlockList, dns.TypeA},
|
{"important", importantRules, "test.example.org", true, FilteredBlackList, dns.TypeA},
|
||||||
{"important", importantRules, "test.test.example.org", true, FilteredBlockList, dns.TypeA},
|
{"important", importantRules, "test.test.example.org", true, FilteredBlackList, dns.TypeA},
|
||||||
{"important", importantRules, "testexample.org", false, NotFilteredNotFound, dns.TypeA},
|
{"important", importantRules, "testexample.org", false, NotFilteredNotFound, dns.TypeA},
|
||||||
{"important", importantRules, "onemoreexample.org", false, NotFilteredNotFound, dns.TypeA},
|
{"important", importantRules, "onemoreexample.org", false, NotFilteredNotFound, dns.TypeA},
|
||||||
|
|
||||||
{"regex", regexRules, "example.org", true, FilteredBlockList, dns.TypeA},
|
{"regex", regexRules, "example.org", true, FilteredBlackList, dns.TypeA},
|
||||||
{"regex", regexRules, "test.example.org", false, NotFilteredAllowList, dns.TypeA},
|
{"regex", regexRules, "test.example.org", false, NotFilteredWhiteList, dns.TypeA},
|
||||||
{"regex", regexRules, "test.test.example.org", false, NotFilteredAllowList, dns.TypeA},
|
{"regex", regexRules, "test.test.example.org", false, NotFilteredWhiteList, dns.TypeA},
|
||||||
{"regex", regexRules, "testexample.org", true, FilteredBlockList, dns.TypeA},
|
{"regex", regexRules, "testexample.org", true, FilteredBlackList, dns.TypeA},
|
||||||
{"regex", regexRules, "onemoreexample.org", true, FilteredBlockList, dns.TypeA},
|
{"regex", regexRules, "onemoreexample.org", true, FilteredBlackList, dns.TypeA},
|
||||||
|
|
||||||
{"mask", maskRules, "test.example.org", true, FilteredBlockList, dns.TypeA},
|
{"mask", maskRules, "test.example.org", true, FilteredBlackList, dns.TypeA},
|
||||||
{"mask", maskRules, "test2.example.org", true, FilteredBlockList, dns.TypeA},
|
{"mask", maskRules, "test2.example.org", true, FilteredBlackList, dns.TypeA},
|
||||||
{"mask", maskRules, "example.com", true, FilteredBlockList, dns.TypeA},
|
{"mask", maskRules, "example.com", true, FilteredBlackList, dns.TypeA},
|
||||||
{"mask", maskRules, "exampleeee.com", true, FilteredBlockList, dns.TypeA},
|
{"mask", maskRules, "exampleeee.com", true, FilteredBlackList, dns.TypeA},
|
||||||
{"mask", maskRules, "onemoreexamsite.com", true, FilteredBlockList, dns.TypeA},
|
{"mask", maskRules, "onemoreexamsite.com", true, FilteredBlackList, dns.TypeA},
|
||||||
{"mask", maskRules, "example.org", false, NotFilteredNotFound, dns.TypeA},
|
{"mask", maskRules, "example.org", false, NotFilteredNotFound, dns.TypeA},
|
||||||
{"mask", maskRules, "testexample.org", false, NotFilteredNotFound, dns.TypeA},
|
{"mask", maskRules, "testexample.org", false, NotFilteredNotFound, dns.TypeA},
|
||||||
{"mask", maskRules, "example.co.uk", false, NotFilteredNotFound, dns.TypeA},
|
{"mask", maskRules, "example.co.uk", false, NotFilteredNotFound, dns.TypeA},
|
||||||
|
|
||||||
{"dnstype", dnstypeRules, "onemoreexample.org", false, NotFilteredNotFound, dns.TypeA},
|
{"dnstype", dnstypeRules, "onemoreexample.org", false, NotFilteredNotFound, dns.TypeA},
|
||||||
{"dnstype", dnstypeRules, "example.org", false, NotFilteredNotFound, dns.TypeA},
|
{"dnstype", dnstypeRules, "example.org", false, NotFilteredNotFound, dns.TypeA},
|
||||||
{"dnstype", dnstypeRules, "example.org", true, FilteredBlockList, dns.TypeAAAA},
|
{"dnstype", dnstypeRules, "example.org", true, FilteredBlackList, dns.TypeAAAA},
|
||||||
{"dnstype", dnstypeRules, "test.example.org", false, NotFilteredAllowList, dns.TypeA},
|
{"dnstype", dnstypeRules, "test.example.org", false, NotFilteredWhiteList, dns.TypeA},
|
||||||
{"dnstype", dnstypeRules, "test.example.org", false, NotFilteredAllowList, dns.TypeAAAA},
|
{"dnstype", dnstypeRules, "test.example.org", false, NotFilteredWhiteList, dns.TypeAAAA},
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMatching(t *testing.T) {
|
func TestMatching(t *testing.T) {
|
||||||
@@ -434,15 +433,15 @@ func TestMatching(t *testing.T) {
|
|||||||
d := NewForTest(nil, filters)
|
d := NewForTest(nil, filters)
|
||||||
defer d.Close()
|
defer d.Close()
|
||||||
|
|
||||||
res, err := d.CheckHost(test.hostname, test.dnsType, &setts)
|
ret, err := d.CheckHost(test.hostname, test.dnsType, &setts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error while matching host %s: %s", test.hostname, err)
|
t.Errorf("Error while matching host %s: %s", test.hostname, err)
|
||||||
}
|
}
|
||||||
if res.IsFiltered != test.isFiltered {
|
if ret.IsFiltered != test.isFiltered {
|
||||||
t.Errorf("Hostname %s has wrong result (%v must be %v)", test.hostname, res.IsFiltered, test.isFiltered)
|
t.Errorf("Hostname %s has wrong result (%v must be %v)", test.hostname, ret.IsFiltered, test.isFiltered)
|
||||||
}
|
}
|
||||||
if res.Reason != test.reason {
|
if ret.Reason != test.reason {
|
||||||
t.Errorf("Hostname %s has wrong reason (%v must be %v)", test.hostname, res.Reason.String(), test.reason.String())
|
t.Errorf("Hostname %s has wrong reason (%v must be %v)", test.hostname, ret.Reason.String(), test.reason.String())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -467,20 +466,16 @@ func TestWhitelist(t *testing.T) {
|
|||||||
defer d.Close()
|
defer d.Close()
|
||||||
|
|
||||||
// matched by white filter
|
// matched by white filter
|
||||||
res, err := d.CheckHost("host1", dns.TypeA, &setts)
|
ret, err := d.CheckHost("host1", dns.TypeA, &setts)
|
||||||
assert.True(t, err == nil)
|
assert.True(t, err == nil)
|
||||||
assert.True(t, !res.IsFiltered && res.Reason == NotFilteredAllowList)
|
assert.True(t, !ret.IsFiltered && ret.Reason == NotFilteredWhiteList)
|
||||||
if assert.Len(t, res.Rules, 1) {
|
assert.True(t, ret.Rule == "||host1^")
|
||||||
assert.True(t, res.Rules[0].Text == "||host1^")
|
|
||||||
}
|
|
||||||
|
|
||||||
// not matched by white filter, but matched by block filter
|
// not matched by white filter, but matched by block filter
|
||||||
res, err = d.CheckHost("host2", dns.TypeA, &setts)
|
ret, err = d.CheckHost("host2", dns.TypeA, &setts)
|
||||||
assert.True(t, err == nil)
|
assert.True(t, err == nil)
|
||||||
assert.True(t, res.IsFiltered && res.Reason == FilteredBlockList)
|
assert.True(t, ret.IsFiltered && ret.Reason == FilteredBlackList)
|
||||||
if assert.Len(t, res.Rules, 1) {
|
assert.True(t, ret.Rule == "||host2^")
|
||||||
assert.True(t, res.Rules[0].Text == "||host2^")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CLIENT SETTINGS
|
// CLIENT SETTINGS
|
||||||
@@ -511,8 +506,8 @@ func TestClientSettings(t *testing.T) {
|
|||||||
|
|
||||||
// blocked by filters
|
// blocked by filters
|
||||||
r, _ = d.CheckHost("example.org", dns.TypeA, &setts)
|
r, _ = d.CheckHost("example.org", dns.TypeA, &setts)
|
||||||
if !r.IsFiltered || r.Reason != FilteredBlockList {
|
if !r.IsFiltered || r.Reason != FilteredBlackList {
|
||||||
t.Fatalf("CheckHost FilteredBlockList")
|
t.Fatalf("CheckHost FilteredBlackList")
|
||||||
}
|
}
|
||||||
|
|
||||||
// blocked by parental
|
// blocked by parental
|
||||||
@@ -564,11 +559,11 @@ func BenchmarkSafeBrowsing(b *testing.B) {
|
|||||||
defer d.Close()
|
defer d.Close()
|
||||||
for n := 0; n < b.N; n++ {
|
for n := 0; n < b.N; n++ {
|
||||||
hostname := "wmconvirus.narod.ru"
|
hostname := "wmconvirus.narod.ru"
|
||||||
res, err := d.CheckHost(hostname, dns.TypeA, &setts)
|
ret, err := d.CheckHost(hostname, dns.TypeA, &setts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Errorf("Error while matching host %s: %s", hostname, err)
|
b.Errorf("Error while matching host %s: %s", hostname, err)
|
||||||
}
|
}
|
||||||
if !res.IsFiltered {
|
if !ret.IsFiltered {
|
||||||
b.Errorf("Expected hostname %s to match", hostname)
|
b.Errorf("Expected hostname %s to match", hostname)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -580,11 +575,11 @@ func BenchmarkSafeBrowsingParallel(b *testing.B) {
|
|||||||
b.RunParallel(func(pb *testing.PB) {
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
for pb.Next() {
|
for pb.Next() {
|
||||||
hostname := "wmconvirus.narod.ru"
|
hostname := "wmconvirus.narod.ru"
|
||||||
res, err := d.CheckHost(hostname, dns.TypeA, &setts)
|
ret, err := d.CheckHost(hostname, dns.TypeA, &setts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Errorf("Error while matching host %s: %s", hostname, err)
|
b.Errorf("Error while matching host %s: %s", hostname, err)
|
||||||
}
|
}
|
||||||
if !res.IsFiltered {
|
if !ret.IsFiltered {
|
||||||
b.Errorf("Expected hostname %s to match", hostname)
|
b.Errorf("Expected hostname %s to match", hostname)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,80 +0,0 @@
|
|||||||
package dnsfilter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/AdguardTeam/urlfilter/rules"
|
|
||||||
"github.com/miekg/dns"
|
|
||||||
)
|
|
||||||
|
|
||||||
// DNSRewriteResult is the result of application of $dnsrewrite rules.
|
|
||||||
type DNSRewriteResult struct {
|
|
||||||
Response DNSRewriteResultResponse `json:",omitempty"`
|
|
||||||
RCode rules.RCode `json:",omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// DNSRewriteResultResponse is the collection of DNS response records
|
|
||||||
// the server returns.
|
|
||||||
type DNSRewriteResultResponse map[rules.RRType][]rules.RRValue
|
|
||||||
|
|
||||||
// processDNSRewrites processes DNS rewrite rules in dnsr. It returns
|
|
||||||
// an empty result if dnsr is empty. Otherwise, the result will have
|
|
||||||
// either CanonName or DNSRewriteResult set.
|
|
||||||
func (d *DNSFilter) processDNSRewrites(dnsr []*rules.NetworkRule) (res Result) {
|
|
||||||
if len(dnsr) == 0 {
|
|
||||||
return Result{}
|
|
||||||
}
|
|
||||||
|
|
||||||
var rules []*ResultRule
|
|
||||||
dnsrr := &DNSRewriteResult{
|
|
||||||
Response: DNSRewriteResultResponse{},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, nr := range dnsr {
|
|
||||||
dr := nr.DNSRewrite
|
|
||||||
if dr.NewCNAME != "" {
|
|
||||||
// NewCNAME rules have a higher priority than
|
|
||||||
// the other rules.
|
|
||||||
rules := []*ResultRule{{
|
|
||||||
FilterListID: int64(nr.GetFilterListID()),
|
|
||||||
Text: nr.RuleText,
|
|
||||||
}}
|
|
||||||
|
|
||||||
return Result{
|
|
||||||
Reason: DNSRewriteRule,
|
|
||||||
Rules: rules,
|
|
||||||
CanonName: dr.NewCNAME,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch dr.RCode {
|
|
||||||
case dns.RcodeSuccess:
|
|
||||||
dnsrr.RCode = dr.RCode
|
|
||||||
dnsrr.Response[dr.RRType] = append(dnsrr.Response[dr.RRType], dr.Value)
|
|
||||||
rules = append(rules, &ResultRule{
|
|
||||||
FilterListID: int64(nr.GetFilterListID()),
|
|
||||||
Text: nr.RuleText,
|
|
||||||
})
|
|
||||||
default:
|
|
||||||
// RcodeRefused and other such codes have higher
|
|
||||||
// priority. Return immediately.
|
|
||||||
rules := []*ResultRule{{
|
|
||||||
FilterListID: int64(nr.GetFilterListID()),
|
|
||||||
Text: nr.RuleText,
|
|
||||||
}}
|
|
||||||
dnsrr = &DNSRewriteResult{
|
|
||||||
RCode: dr.RCode,
|
|
||||||
}
|
|
||||||
|
|
||||||
return Result{
|
|
||||||
Reason: DNSRewriteRule,
|
|
||||||
Rules: rules,
|
|
||||||
DNSRewriteResult: dnsrr,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Result{
|
|
||||||
Reason: DNSRewriteRule,
|
|
||||||
Rules: rules,
|
|
||||||
DNSRewriteResult: dnsrr,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,202 +0,0 @@
|
|||||||
package dnsfilter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
"path"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/miekg/dns"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestDNSFilter_CheckHostRules_dnsrewrite(t *testing.T) {
|
|
||||||
const text = `
|
|
||||||
|cname^$dnsrewrite=new_cname
|
|
||||||
|
|
||||||
|a_record^$dnsrewrite=127.0.0.1
|
|
||||||
|
|
||||||
|aaaa_record^$dnsrewrite=::1
|
|
||||||
|
|
||||||
|txt_record^$dnsrewrite=NOERROR;TXT;hello_world
|
|
||||||
|
|
||||||
|refused^$dnsrewrite=REFUSED
|
|
||||||
|
|
||||||
|a_records^$dnsrewrite=127.0.0.1
|
|
||||||
|a_records^$dnsrewrite=127.0.0.2
|
|
||||||
|
|
||||||
|aaaa_records^$dnsrewrite=::1
|
|
||||||
|aaaa_records^$dnsrewrite=::2
|
|
||||||
|
|
||||||
|disable_one^$dnsrewrite=127.0.0.1
|
|
||||||
|disable_one^$dnsrewrite=127.0.0.2
|
|
||||||
@@||disable_one^$dnsrewrite=127.0.0.1
|
|
||||||
|
|
||||||
|disable_cname^$dnsrewrite=127.0.0.1
|
|
||||||
|disable_cname^$dnsrewrite=new_cname
|
|
||||||
@@||disable_cname^$dnsrewrite=new_cname
|
|
||||||
|
|
||||||
|disable_cname_many^$dnsrewrite=127.0.0.1
|
|
||||||
|disable_cname_many^$dnsrewrite=new_cname_1
|
|
||||||
|disable_cname_many^$dnsrewrite=new_cname_2
|
|
||||||
@@||disable_cname_many^$dnsrewrite=new_cname_1
|
|
||||||
|
|
||||||
|disable_all^$dnsrewrite=127.0.0.1
|
|
||||||
|disable_all^$dnsrewrite=127.0.0.2
|
|
||||||
@@||disable_all^$dnsrewrite
|
|
||||||
`
|
|
||||||
f := NewForTest(nil, []Filter{{ID: 0, Data: []byte(text)}})
|
|
||||||
setts := &RequestFilteringSettings{
|
|
||||||
FilteringEnabled: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
ipv4p1 := net.IPv4(127, 0, 0, 1)
|
|
||||||
ipv4p2 := net.IPv4(127, 0, 0, 2)
|
|
||||||
ipv6p1 := net.IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}
|
|
||||||
ipv6p2 := net.IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2}
|
|
||||||
|
|
||||||
t.Run("cname", func(t *testing.T) {
|
|
||||||
dtyp := dns.TypeA
|
|
||||||
host := path.Base(t.Name())
|
|
||||||
|
|
||||||
res, err := f.CheckHostRules(host, dtyp, setts)
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.Equal(t, "new_cname", res.CanonName)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("a_record", func(t *testing.T) {
|
|
||||||
dtyp := dns.TypeA
|
|
||||||
host := path.Base(t.Name())
|
|
||||||
|
|
||||||
res, err := f.CheckHostRules(host, dtyp, setts)
|
|
||||||
assert.Nil(t, err)
|
|
||||||
|
|
||||||
if dnsrr := res.DNSRewriteResult; assert.NotNil(t, dnsrr) {
|
|
||||||
assert.Equal(t, dns.RcodeSuccess, dnsrr.RCode)
|
|
||||||
if ipVals := dnsrr.Response[dtyp]; assert.Len(t, ipVals, 1) {
|
|
||||||
assert.Equal(t, ipv4p1, ipVals[0])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("aaaa_record", func(t *testing.T) {
|
|
||||||
dtyp := dns.TypeAAAA
|
|
||||||
host := path.Base(t.Name())
|
|
||||||
|
|
||||||
res, err := f.CheckHostRules(host, dtyp, setts)
|
|
||||||
assert.Nil(t, err)
|
|
||||||
|
|
||||||
if dnsrr := res.DNSRewriteResult; assert.NotNil(t, dnsrr) {
|
|
||||||
assert.Equal(t, dns.RcodeSuccess, dnsrr.RCode)
|
|
||||||
if ipVals := dnsrr.Response[dtyp]; assert.Len(t, ipVals, 1) {
|
|
||||||
assert.Equal(t, ipv6p1, ipVals[0])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("txt_record", func(t *testing.T) {
|
|
||||||
dtyp := dns.TypeTXT
|
|
||||||
host := path.Base(t.Name())
|
|
||||||
res, err := f.CheckHostRules(host, dtyp, setts)
|
|
||||||
assert.Nil(t, err)
|
|
||||||
|
|
||||||
if dnsrr := res.DNSRewriteResult; assert.NotNil(t, dnsrr) {
|
|
||||||
assert.Equal(t, dns.RcodeSuccess, dnsrr.RCode)
|
|
||||||
if strVals := dnsrr.Response[dtyp]; assert.Len(t, strVals, 1) {
|
|
||||||
assert.Equal(t, "hello_world", strVals[0])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("refused", func(t *testing.T) {
|
|
||||||
host := path.Base(t.Name())
|
|
||||||
res, err := f.CheckHostRules(host, dns.TypeA, setts)
|
|
||||||
assert.Nil(t, err)
|
|
||||||
|
|
||||||
if dnsrr := res.DNSRewriteResult; assert.NotNil(t, dnsrr) {
|
|
||||||
assert.Equal(t, dns.RcodeRefused, dnsrr.RCode)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("a_records", func(t *testing.T) {
|
|
||||||
dtyp := dns.TypeA
|
|
||||||
host := path.Base(t.Name())
|
|
||||||
|
|
||||||
res, err := f.CheckHostRules(host, dtyp, setts)
|
|
||||||
assert.Nil(t, err)
|
|
||||||
|
|
||||||
if dnsrr := res.DNSRewriteResult; assert.NotNil(t, dnsrr) {
|
|
||||||
assert.Equal(t, dns.RcodeSuccess, dnsrr.RCode)
|
|
||||||
if ipVals := dnsrr.Response[dtyp]; assert.Len(t, ipVals, 2) {
|
|
||||||
assert.Equal(t, ipv4p1, ipVals[0])
|
|
||||||
assert.Equal(t, ipv4p2, ipVals[1])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("aaaa_records", func(t *testing.T) {
|
|
||||||
dtyp := dns.TypeAAAA
|
|
||||||
host := path.Base(t.Name())
|
|
||||||
|
|
||||||
res, err := f.CheckHostRules(host, dtyp, setts)
|
|
||||||
assert.Nil(t, err)
|
|
||||||
|
|
||||||
if dnsrr := res.DNSRewriteResult; assert.NotNil(t, dnsrr) {
|
|
||||||
assert.Equal(t, dns.RcodeSuccess, dnsrr.RCode)
|
|
||||||
if ipVals := dnsrr.Response[dtyp]; assert.Len(t, ipVals, 2) {
|
|
||||||
assert.Equal(t, ipv6p1, ipVals[0])
|
|
||||||
assert.Equal(t, ipv6p2, ipVals[1])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("disable_one", func(t *testing.T) {
|
|
||||||
dtyp := dns.TypeA
|
|
||||||
host := path.Base(t.Name())
|
|
||||||
|
|
||||||
res, err := f.CheckHostRules(host, dtyp, setts)
|
|
||||||
assert.Nil(t, err)
|
|
||||||
|
|
||||||
if dnsrr := res.DNSRewriteResult; assert.NotNil(t, dnsrr) {
|
|
||||||
assert.Equal(t, dns.RcodeSuccess, dnsrr.RCode)
|
|
||||||
if ipVals := dnsrr.Response[dtyp]; assert.Len(t, ipVals, 1) {
|
|
||||||
assert.Equal(t, ipv4p2, ipVals[0])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("disable_cname", func(t *testing.T) {
|
|
||||||
dtyp := dns.TypeA
|
|
||||||
host := path.Base(t.Name())
|
|
||||||
|
|
||||||
res, err := f.CheckHostRules(host, dtyp, setts)
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.Equal(t, "", res.CanonName)
|
|
||||||
|
|
||||||
if dnsrr := res.DNSRewriteResult; assert.NotNil(t, dnsrr) {
|
|
||||||
assert.Equal(t, dns.RcodeSuccess, dnsrr.RCode)
|
|
||||||
if ipVals := dnsrr.Response[dtyp]; assert.Len(t, ipVals, 1) {
|
|
||||||
assert.Equal(t, ipv4p1, ipVals[0])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("disable_cname_many", func(t *testing.T) {
|
|
||||||
dtyp := dns.TypeA
|
|
||||||
host := path.Base(t.Name())
|
|
||||||
|
|
||||||
res, err := f.CheckHostRules(host, dtyp, setts)
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.Equal(t, "new_cname_2", res.CanonName)
|
|
||||||
assert.Nil(t, res.DNSRewriteResult)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("disable_all", func(t *testing.T) {
|
|
||||||
dtyp := dns.TypeA
|
|
||||||
host := path.Base(t.Name())
|
|
||||||
|
|
||||||
res, err := f.CheckHostRules(host, dtyp, setts)
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.Equal(t, "", res.CanonName)
|
|
||||||
assert.Len(t, res.Rules, 0)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -95,7 +95,7 @@ func (r *RewriteEntry) prepare() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DNSFilter) prepareRewrites() {
|
func (d *Dnsfilter) prepareRewrites() {
|
||||||
for i := range d.Rewrites {
|
for i := range d.Rewrites {
|
||||||
d.Rewrites[i].prepare()
|
d.Rewrites[i].prepare()
|
||||||
}
|
}
|
||||||
@@ -148,7 +148,7 @@ type rewriteEntryJSON struct {
|
|||||||
Answer string `json:"answer"`
|
Answer string `json:"answer"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DNSFilter) handleRewriteList(w http.ResponseWriter, r *http.Request) {
|
func (d *Dnsfilter) handleRewriteList(w http.ResponseWriter, r *http.Request) {
|
||||||
arr := []*rewriteEntryJSON{}
|
arr := []*rewriteEntryJSON{}
|
||||||
|
|
||||||
d.confLock.Lock()
|
d.confLock.Lock()
|
||||||
@@ -169,7 +169,7 @@ func (d *DNSFilter) handleRewriteList(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DNSFilter) handleRewriteAdd(w http.ResponseWriter, r *http.Request) {
|
func (d *Dnsfilter) handleRewriteAdd(w http.ResponseWriter, r *http.Request) {
|
||||||
jsent := rewriteEntryJSON{}
|
jsent := rewriteEntryJSON{}
|
||||||
err := json.NewDecoder(r.Body).Decode(&jsent)
|
err := json.NewDecoder(r.Body).Decode(&jsent)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -191,7 +191,7 @@ func (d *DNSFilter) handleRewriteAdd(w http.ResponseWriter, r *http.Request) {
|
|||||||
d.Config.ConfigModified()
|
d.Config.ConfigModified()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DNSFilter) handleRewriteDelete(w http.ResponseWriter, r *http.Request) {
|
func (d *Dnsfilter) handleRewriteDelete(w http.ResponseWriter, r *http.Request) {
|
||||||
jsent := rewriteEntryJSON{}
|
jsent := rewriteEntryJSON{}
|
||||||
err := json.NewDecoder(r.Body).Decode(&jsent)
|
err := json.NewDecoder(r.Body).Decode(&jsent)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -218,7 +218,7 @@ func (d *DNSFilter) handleRewriteDelete(w http.ResponseWriter, r *http.Request)
|
|||||||
d.Config.ConfigModified()
|
d.Config.ConfigModified()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DNSFilter) registerRewritesHandlers() {
|
func (d *Dnsfilter) registerRewritesHandlers() {
|
||||||
d.Config.HTTPRegister("GET", "/control/rewrite/list", d.handleRewriteList)
|
d.Config.HTTPRegister("GET", "/control/rewrite/list", d.handleRewriteList)
|
||||||
d.Config.HTTPRegister("POST", "/control/rewrite/add", d.handleRewriteAdd)
|
d.Config.HTTPRegister("POST", "/control/rewrite/add", d.handleRewriteAdd)
|
||||||
d.Config.HTTPRegister("POST", "/control/rewrite/delete", d.handleRewriteDelete)
|
d.Config.HTTPRegister("POST", "/control/rewrite/delete", d.handleRewriteDelete)
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestRewrites(t *testing.T) {
|
func TestRewrites(t *testing.T) {
|
||||||
d := DNSFilter{}
|
d := Dnsfilter{}
|
||||||
// CNAME, A, AAAA
|
// CNAME, A, AAAA
|
||||||
d.Rewrites = []RewriteEntry{
|
d.Rewrites = []RewriteEntry{
|
||||||
{"somecname", "somehost.com", 0, nil},
|
{"somecname", "somehost.com", 0, nil},
|
||||||
@@ -104,7 +104,7 @@ func TestRewrites(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestRewritesLevels(t *testing.T) {
|
func TestRewritesLevels(t *testing.T) {
|
||||||
d := DNSFilter{}
|
d := Dnsfilter{}
|
||||||
// exact host, wildcard L2, wildcard L3
|
// exact host, wildcard L2, wildcard L3
|
||||||
d.Rewrites = []RewriteEntry{
|
d.Rewrites = []RewriteEntry{
|
||||||
{"host.com", "1.1.1.1", 0, nil},
|
{"host.com", "1.1.1.1", 0, nil},
|
||||||
@@ -133,7 +133,7 @@ func TestRewritesLevels(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestRewritesExceptionCNAME(t *testing.T) {
|
func TestRewritesExceptionCNAME(t *testing.T) {
|
||||||
d := DNSFilter{}
|
d := Dnsfilter{}
|
||||||
// wildcard; exception for a sub-domain
|
// wildcard; exception for a sub-domain
|
||||||
d.Rewrites = []RewriteEntry{
|
d.Rewrites = []RewriteEntry{
|
||||||
{"*.host.com", "2.2.2.2", 0, nil},
|
{"*.host.com", "2.2.2.2", 0, nil},
|
||||||
@@ -153,7 +153,7 @@ func TestRewritesExceptionCNAME(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestRewritesExceptionWC(t *testing.T) {
|
func TestRewritesExceptionWC(t *testing.T) {
|
||||||
d := DNSFilter{}
|
d := Dnsfilter{}
|
||||||
// wildcard; exception for a sub-wildcard
|
// wildcard; exception for a sub-wildcard
|
||||||
d.Rewrites = []RewriteEntry{
|
d.Rewrites = []RewriteEntry{
|
||||||
{"*.host.com", "2.2.2.2", 0, nil},
|
{"*.host.com", "2.2.2.2", 0, nil},
|
||||||
@@ -173,7 +173,7 @@ func TestRewritesExceptionWC(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestRewritesExceptionIP(t *testing.T) {
|
func TestRewritesExceptionIP(t *testing.T) {
|
||||||
d := DNSFilter{}
|
d := Dnsfilter{}
|
||||||
// exception for AAAA record
|
// exception for AAAA record
|
||||||
d.Rewrites = []RewriteEntry{
|
d.Rewrites = []RewriteEntry{
|
||||||
{"host.com", "1.2.3.4", 0, nil},
|
{"host.com", "1.2.3.4", 0, nil},
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import (
|
|||||||
expire byte[4]
|
expire byte[4]
|
||||||
res Result
|
res Result
|
||||||
*/
|
*/
|
||||||
func (d *DNSFilter) setCacheResult(cache cache.Cache, host string, res Result) int {
|
func (d *Dnsfilter) setCacheResult(cache cache.Cache, host string, res Result) int {
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
|
|
||||||
expire := uint(time.Now().Unix()) + d.Config.CacheTime*60
|
expire := uint(time.Now().Unix()) + d.Config.CacheTime*60
|
||||||
@@ -63,12 +63,12 @@ func getCachedResult(cache cache.Cache, host string) (Result, bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SafeSearchDomain returns replacement address for search engine
|
// SafeSearchDomain returns replacement address for search engine
|
||||||
func (d *DNSFilter) SafeSearchDomain(host string) (string, bool) {
|
func (d *Dnsfilter) SafeSearchDomain(host string) (string, bool) {
|
||||||
val, ok := safeSearchDomains[host]
|
val, ok := safeSearchDomains[host]
|
||||||
return val, ok
|
return val, ok
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DNSFilter) checkSafeSearch(host string) (Result, error) {
|
func (d *Dnsfilter) checkSafeSearch(host string) (Result, error) {
|
||||||
if log.GetLevel() >= log.DEBUG {
|
if log.GetLevel() >= log.DEBUG {
|
||||||
timer := log.StartTimer()
|
timer := log.StartTimer()
|
||||||
defer timer.LogElapsed("SafeSearch: lookup for %s", host)
|
defer timer.LogElapsed("SafeSearch: lookup for %s", host)
|
||||||
@@ -87,52 +87,49 @@ func (d *DNSFilter) checkSafeSearch(host string) (Result, error) {
|
|||||||
return Result{}, nil
|
return Result{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
res := Result{
|
res := Result{IsFiltered: true, Reason: FilteredSafeSearch}
|
||||||
IsFiltered: true,
|
|
||||||
Reason: FilteredSafeSearch,
|
|
||||||
Rules: []*ResultRule{{}},
|
|
||||||
}
|
|
||||||
|
|
||||||
if ip := net.ParseIP(safeHost); ip != nil {
|
if ip := net.ParseIP(safeHost); ip != nil {
|
||||||
res.Rules[0].IP = ip
|
res.IP = ip
|
||||||
valLen := d.setCacheResult(gctx.safeSearchCache, host, res)
|
valLen := d.setCacheResult(gctx.safeSearchCache, host, res)
|
||||||
log.Debug("SafeSearch: stored in cache: %s (%d bytes)", host, valLen)
|
log.Debug("SafeSearch: stored in cache: %s (%d bytes)", host, valLen)
|
||||||
|
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO this address should be resolved with upstream that was configured in dnsforward
|
// TODO this address should be resolved with upstream that was configured in dnsforward
|
||||||
ips, err := net.LookupIP(safeHost)
|
addrs, err := net.LookupIP(safeHost)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Tracef("SafeSearchDomain for %s was found but failed to lookup for %s cause %s", host, safeHost, err)
|
log.Tracef("SafeSearchDomain for %s was found but failed to lookup for %s cause %s", host, safeHost, err)
|
||||||
return Result{}, err
|
return Result{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, ip := range ips {
|
for _, i := range addrs {
|
||||||
if ipv4 := ip.To4(); ipv4 != nil {
|
if ipv4 := i.To4(); ipv4 != nil {
|
||||||
res.Rules[0].IP = ipv4
|
res.IP = ipv4
|
||||||
|
break
|
||||||
l := d.setCacheResult(gctx.safeSearchCache, host, res)
|
|
||||||
log.Debug("SafeSearch: stored in cache: %s (%d bytes)", host, l)
|
|
||||||
|
|
||||||
return res, nil
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Result{}, fmt.Errorf("no ipv4 addresses in safe search response for %s", safeHost)
|
if len(res.IP) == 0 {
|
||||||
|
return Result{}, fmt.Errorf("no ipv4 addresses in safe search response for %s", safeHost)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cache result
|
||||||
|
valLen := d.setCacheResult(gctx.safeSearchCache, host, res)
|
||||||
|
log.Debug("SafeSearch: stored in cache: %s (%d bytes)", host, valLen)
|
||||||
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DNSFilter) handleSafeSearchEnable(w http.ResponseWriter, r *http.Request) {
|
func (d *Dnsfilter) handleSafeSearchEnable(w http.ResponseWriter, r *http.Request) {
|
||||||
d.Config.SafeSearchEnabled = true
|
d.Config.SafeSearchEnabled = true
|
||||||
d.Config.ConfigModified()
|
d.Config.ConfigModified()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DNSFilter) handleSafeSearchDisable(w http.ResponseWriter, r *http.Request) {
|
func (d *Dnsfilter) handleSafeSearchDisable(w http.ResponseWriter, r *http.Request) {
|
||||||
d.Config.SafeSearchEnabled = false
|
d.Config.SafeSearchEnabled = false
|
||||||
d.Config.ConfigModified()
|
d.Config.ConfigModified()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DNSFilter) handleSafeSearchStatus(w http.ResponseWriter, r *http.Request) {
|
func (d *Dnsfilter) handleSafeSearchStatus(w http.ResponseWriter, r *http.Request) {
|
||||||
data := map[string]interface{}{
|
data := map[string]interface{}{
|
||||||
"enabled": d.Config.SafeSearchEnabled,
|
"enabled": d.Config.SafeSearchEnabled,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
// Safe Browsing, Parental Control
|
||||||
|
|
||||||
package dnsfilter
|
package dnsfilter
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -20,8 +22,6 @@ import (
|
|||||||
"golang.org/x/net/publicsuffix"
|
"golang.org/x/net/publicsuffix"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Safe browsing and parental control methods.
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
dnsTimeout = 3 * time.Second
|
dnsTimeout = 3 * time.Second
|
||||||
defaultSafebrowsingServer = `https://dns-family.adguard.com/dns-query`
|
defaultSafebrowsingServer = `https://dns-family.adguard.com/dns-query`
|
||||||
@@ -30,7 +30,7 @@ const (
|
|||||||
pcTXTSuffix = `pc.dns.adguard.com.`
|
pcTXTSuffix = `pc.dns.adguard.com.`
|
||||||
)
|
)
|
||||||
|
|
||||||
func (d *DNSFilter) initSecurityServices() error {
|
func (d *Dnsfilter) initSecurityServices() error {
|
||||||
var err error
|
var err error
|
||||||
d.safeBrowsingServer = defaultSafebrowsingServer
|
d.safeBrowsingServer = defaultSafebrowsingServer
|
||||||
d.parentalServer = defaultParentalServer
|
d.parentalServer = defaultParentalServer
|
||||||
@@ -287,7 +287,7 @@ func check(c *sbCtx, r Result, u upstream.Upstream) (Result, error) {
|
|||||||
return Result{}, nil
|
return Result{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DNSFilter) checkSafeBrowsing(host string) (Result, error) {
|
func (d *Dnsfilter) checkSafeBrowsing(host string) (Result, error) {
|
||||||
if log.GetLevel() >= log.DEBUG {
|
if log.GetLevel() >= log.DEBUG {
|
||||||
timer := log.StartTimer()
|
timer := log.StartTimer()
|
||||||
defer timer.LogElapsed("SafeBrowsing lookup for %s", host)
|
defer timer.LogElapsed("SafeBrowsing lookup for %s", host)
|
||||||
@@ -301,14 +301,12 @@ func (d *DNSFilter) checkSafeBrowsing(host string) (Result, error) {
|
|||||||
res := Result{
|
res := Result{
|
||||||
IsFiltered: true,
|
IsFiltered: true,
|
||||||
Reason: FilteredSafeBrowsing,
|
Reason: FilteredSafeBrowsing,
|
||||||
Rules: []*ResultRule{{
|
Rule: "adguard-malware-shavar",
|
||||||
Text: "adguard-malware-shavar",
|
|
||||||
}},
|
|
||||||
}
|
}
|
||||||
return check(ctx, res, d.safeBrowsingUpstream)
|
return check(ctx, res, d.safeBrowsingUpstream)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DNSFilter) checkParental(host string) (Result, error) {
|
func (d *Dnsfilter) checkParental(host string) (Result, error) {
|
||||||
if log.GetLevel() >= log.DEBUG {
|
if log.GetLevel() >= log.DEBUG {
|
||||||
timer := log.StartTimer()
|
timer := log.StartTimer()
|
||||||
defer timer.LogElapsed("Parental lookup for %s", host)
|
defer timer.LogElapsed("Parental lookup for %s", host)
|
||||||
@@ -322,9 +320,7 @@ func (d *DNSFilter) checkParental(host string) (Result, error) {
|
|||||||
res := Result{
|
res := Result{
|
||||||
IsFiltered: true,
|
IsFiltered: true,
|
||||||
Reason: FilteredParental,
|
Reason: FilteredParental,
|
||||||
Rules: []*ResultRule{{
|
Rule: "parental CATEGORY_BLACKLISTED",
|
||||||
Text: "parental CATEGORY_BLACKLISTED",
|
|
||||||
}},
|
|
||||||
}
|
}
|
||||||
return check(ctx, res, d.parentalUpstream)
|
return check(ctx, res, d.parentalUpstream)
|
||||||
}
|
}
|
||||||
@@ -335,17 +331,17 @@ func httpError(r *http.Request, w http.ResponseWriter, code int, format string,
|
|||||||
http.Error(w, text, code)
|
http.Error(w, text, code)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DNSFilter) handleSafeBrowsingEnable(w http.ResponseWriter, r *http.Request) {
|
func (d *Dnsfilter) handleSafeBrowsingEnable(w http.ResponseWriter, r *http.Request) {
|
||||||
d.Config.SafeBrowsingEnabled = true
|
d.Config.SafeBrowsingEnabled = true
|
||||||
d.Config.ConfigModified()
|
d.Config.ConfigModified()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DNSFilter) handleSafeBrowsingDisable(w http.ResponseWriter, r *http.Request) {
|
func (d *Dnsfilter) handleSafeBrowsingDisable(w http.ResponseWriter, r *http.Request) {
|
||||||
d.Config.SafeBrowsingEnabled = false
|
d.Config.SafeBrowsingEnabled = false
|
||||||
d.Config.ConfigModified()
|
d.Config.ConfigModified()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DNSFilter) handleSafeBrowsingStatus(w http.ResponseWriter, r *http.Request) {
|
func (d *Dnsfilter) handleSafeBrowsingStatus(w http.ResponseWriter, r *http.Request) {
|
||||||
data := map[string]interface{}{
|
data := map[string]interface{}{
|
||||||
"enabled": d.Config.SafeBrowsingEnabled,
|
"enabled": d.Config.SafeBrowsingEnabled,
|
||||||
}
|
}
|
||||||
@@ -362,17 +358,17 @@ func (d *DNSFilter) handleSafeBrowsingStatus(w http.ResponseWriter, r *http.Requ
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DNSFilter) handleParentalEnable(w http.ResponseWriter, r *http.Request) {
|
func (d *Dnsfilter) handleParentalEnable(w http.ResponseWriter, r *http.Request) {
|
||||||
d.Config.ParentalEnabled = true
|
d.Config.ParentalEnabled = true
|
||||||
d.Config.ConfigModified()
|
d.Config.ConfigModified()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DNSFilter) handleParentalDisable(w http.ResponseWriter, r *http.Request) {
|
func (d *Dnsfilter) handleParentalDisable(w http.ResponseWriter, r *http.Request) {
|
||||||
d.Config.ParentalEnabled = false
|
d.Config.ParentalEnabled = false
|
||||||
d.Config.ConfigModified()
|
d.Config.ConfigModified()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DNSFilter) handleParentalStatus(w http.ResponseWriter, r *http.Request) {
|
func (d *Dnsfilter) handleParentalStatus(w http.ResponseWriter, r *http.Request) {
|
||||||
data := map[string]interface{}{
|
data := map[string]interface{}{
|
||||||
"enabled": d.Config.ParentalEnabled,
|
"enabled": d.Config.ParentalEnabled,
|
||||||
}
|
}
|
||||||
@@ -390,7 +386,7 @@ func (d *DNSFilter) handleParentalStatus(w http.ResponseWriter, r *http.Request)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DNSFilter) registerSecurityHandlers() {
|
func (d *Dnsfilter) registerSecurityHandlers() {
|
||||||
d.Config.HTTPRegister("POST", "/control/safebrowsing/enable", d.handleSafeBrowsingEnable)
|
d.Config.HTTPRegister("POST", "/control/safebrowsing/enable", d.handleSafeBrowsingEnable)
|
||||||
d.Config.HTTPRegister("POST", "/control/safebrowsing/disable", d.handleSafeBrowsingDisable)
|
d.Config.HTTPRegister("POST", "/control/safebrowsing/disable", d.handleSafeBrowsingDisable)
|
||||||
d.Config.HTTPRegister("GET", "/control/safebrowsing/status", d.handleSafeBrowsingStatus)
|
d.Config.HTTPRegister("GET", "/control/safebrowsing/status", d.handleSafeBrowsingStatus)
|
||||||
@@ -366,9 +366,7 @@ func processFilteringAfterResponse(ctx *dnsContext) int {
|
|||||||
var err error
|
var err error
|
||||||
|
|
||||||
switch res.Reason {
|
switch res.Reason {
|
||||||
case dnsfilter.ReasonRewrite,
|
case dnsfilter.ReasonRewrite:
|
||||||
dnsfilter.DNSRewriteRule:
|
|
||||||
|
|
||||||
if len(ctx.origQuestion.Name) == 0 {
|
if len(ctx.origQuestion.Name) == 0 {
|
||||||
// origQuestion is set in case we get only CNAME without IP from rewrites table
|
// origQuestion is set in case we get only CNAME without IP from rewrites table
|
||||||
break
|
break
|
||||||
@@ -379,12 +377,12 @@ func processFilteringAfterResponse(ctx *dnsContext) int {
|
|||||||
|
|
||||||
if len(d.Res.Answer) != 0 {
|
if len(d.Res.Answer) != 0 {
|
||||||
answer := []dns.RR{}
|
answer := []dns.RR{}
|
||||||
answer = append(answer, s.genAnswerCNAME(d.Req, res.CanonName))
|
answer = append(answer, s.genCNAMEAnswer(d.Req, res.CanonName))
|
||||||
answer = append(answer, d.Res.Answer...)
|
answer = append(answer, d.Res.Answer...) // host -> IP
|
||||||
d.Res.Answer = answer
|
d.Res.Answer = answer
|
||||||
}
|
}
|
||||||
|
|
||||||
case dnsfilter.NotFilteredAllowList:
|
case dnsfilter.NotFilteredWhiteList:
|
||||||
// nothing
|
// nothing
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ var webRegistered bool
|
|||||||
// The zero Server is empty and ready for use.
|
// The zero Server is empty and ready for use.
|
||||||
type Server struct {
|
type Server struct {
|
||||||
dnsProxy *proxy.Proxy // DNS proxy instance
|
dnsProxy *proxy.Proxy // DNS proxy instance
|
||||||
dnsFilter *dnsfilter.DNSFilter // DNS filter instance
|
dnsFilter *dnsfilter.Dnsfilter // DNS filter instance
|
||||||
dhcpServer dhcpd.ServerInterface // DHCP server instance (optional)
|
dhcpServer dhcpd.ServerInterface // DHCP server instance (optional)
|
||||||
queryLog querylog.QueryLog // Query log instance
|
queryLog querylog.QueryLog // Query log instance
|
||||||
stats stats.Stats
|
stats stats.Stats
|
||||||
@@ -74,7 +74,7 @@ type Server struct {
|
|||||||
|
|
||||||
// DNSCreateParams - parameters for NewServer()
|
// DNSCreateParams - parameters for NewServer()
|
||||||
type DNSCreateParams struct {
|
type DNSCreateParams struct {
|
||||||
DNSFilter *dnsfilter.DNSFilter
|
DNSFilter *dnsfilter.Dnsfilter
|
||||||
Stats stats.Stats
|
Stats stats.Stats
|
||||||
QueryLog querylog.QueryLog
|
QueryLog querylog.QueryLog
|
||||||
DHCPServer dhcpd.ServerInterface
|
DHCPServer dhcpd.ServerInterface
|
||||||
|
|||||||
@@ -296,7 +296,7 @@ func TestBlockedRequest(t *testing.T) {
|
|||||||
|
|
||||||
func TestServerCustomClientUpstream(t *testing.T) {
|
func TestServerCustomClientUpstream(t *testing.T) {
|
||||||
s := createTestServer(t)
|
s := createTestServer(t)
|
||||||
s.conf.GetCustomUpstreamByClient = func(_ string) *proxy.UpstreamConfig {
|
s.conf.GetCustomUpstreamByClient = func(clientAddr string) *proxy.UpstreamConfig {
|
||||||
uc := &proxy.UpstreamConfig{}
|
uc := &proxy.UpstreamConfig{}
|
||||||
u := &testUpstream{}
|
u := &testUpstream{}
|
||||||
u.ipv4 = map[string][]net.IP{}
|
u.ipv4 = map[string][]net.IP{}
|
||||||
@@ -473,7 +473,7 @@ func TestBlockCNAME(t *testing.T) {
|
|||||||
func TestClientRulesForCNAMEMatching(t *testing.T) {
|
func TestClientRulesForCNAMEMatching(t *testing.T) {
|
||||||
s := createTestServer(t)
|
s := createTestServer(t)
|
||||||
testUpstm := &testUpstream{testCNAMEs, testIPv4, nil}
|
testUpstm := &testUpstream{testCNAMEs, testIPv4, nil}
|
||||||
s.conf.FilterHandler = func(_ string, settings *dnsfilter.RequestFilteringSettings) {
|
s.conf.FilterHandler = func(clientAddr string, settings *dnsfilter.RequestFilteringSettings) {
|
||||||
settings.FilteringEnabled = false
|
settings.FilteringEnabled = false
|
||||||
}
|
}
|
||||||
err := s.startWithUpstream(testUpstm)
|
err := s.startWithUpstream(testUpstm)
|
||||||
@@ -863,8 +863,6 @@ func sendTestMessages(t *testing.T, conn *dns.Conn) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func exchangeAndAssertResponse(t *testing.T, client *dns.Client, addr net.Addr, host, ip string) {
|
func exchangeAndAssertResponse(t *testing.T, client *dns.Client, addr net.Addr, host, ip string) {
|
||||||
t.Helper()
|
|
||||||
|
|
||||||
req := createTestMessage(host)
|
req := createTestMessage(host)
|
||||||
reply, _, err := client.Exchange(req, addr.String())
|
reply, _, err := client.Exchange(req, addr.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -902,8 +900,6 @@ func assertGoogleAResponse(t *testing.T, reply *dns.Msg) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func assertResponse(t *testing.T, reply *dns.Msg, ip string) {
|
func assertResponse(t *testing.T, reply *dns.Msg, ip string) {
|
||||||
t.Helper()
|
|
||||||
|
|
||||||
if len(reply.Answer) != 1 {
|
if len(reply.Answer) != 1 {
|
||||||
t.Fatalf("DNS server returned reply with wrong number of answers - %d", len(reply.Answer))
|
t.Fatalf("DNS server returned reply with wrong number of answers - %d", len(reply.Answer))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,107 +0,0 @@
|
|||||||
package dnsforward
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
|
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/agherr"
|
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/dnsfilter"
|
|
||||||
"github.com/AdguardTeam/dnsproxy/proxy"
|
|
||||||
"github.com/AdguardTeam/golibs/log"
|
|
||||||
"github.com/AdguardTeam/urlfilter/rules"
|
|
||||||
"github.com/miekg/dns"
|
|
||||||
)
|
|
||||||
|
|
||||||
// filterDNSRewriteResponse handles a single DNS rewrite response entry.
|
|
||||||
// It returns the properly constructed answer resource record.
|
|
||||||
func (s *Server) filterDNSRewriteResponse(req *dns.Msg, rr rules.RRType, v rules.RRValue) (ans dns.RR, err error) {
|
|
||||||
// TODO(a.garipov): As more types are added, we will probably want to
|
|
||||||
// use a handler-oriented approach here. So, think of a way to decouple
|
|
||||||
// the answer generation logic from the Server.
|
|
||||||
|
|
||||||
switch rr {
|
|
||||||
case dns.TypeA, dns.TypeAAAA:
|
|
||||||
ip, ok := v.(net.IP)
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("value for rr type %d has type %T, not net.IP", rr, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
if rr == dns.TypeA {
|
|
||||||
return s.genAnswerA(req, ip.To4()), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return s.genAnswerAAAA(req, ip), nil
|
|
||||||
case dns.TypePTR,
|
|
||||||
dns.TypeTXT:
|
|
||||||
str, ok := v.(string)
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("value for rr type %d has type %T, not string", rr, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
if rr == dns.TypeTXT {
|
|
||||||
return s.genAnswerTXT(req, []string{str}), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return s.genAnswerPTR(req, str), nil
|
|
||||||
case dns.TypeMX:
|
|
||||||
mx, ok := v.(*rules.DNSMX)
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("value for rr type %d has type %T, not *rules.DNSMX", rr, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
return s.genAnswerMX(req, mx), nil
|
|
||||||
case dns.TypeHTTPS,
|
|
||||||
dns.TypeSVCB:
|
|
||||||
svcb, ok := v.(*rules.DNSSVCB)
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("value for rr type %d has type %T, not *rules.DNSSVCB", rr, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
if rr == dns.TypeHTTPS {
|
|
||||||
return s.genAnswerHTTPS(req, svcb), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return s.genAnswerSVCB(req, svcb), nil
|
|
||||||
default:
|
|
||||||
log.Debug("don't know how to handle dns rr type %d, skipping", rr)
|
|
||||||
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// filterDNSRewrite handles dnsrewrite filters. It constructs a DNS
|
|
||||||
// response and sets it into d.Res.
|
|
||||||
func (s *Server) filterDNSRewrite(req *dns.Msg, res dnsfilter.Result, d *proxy.DNSContext) (err error) {
|
|
||||||
resp := s.makeResponse(req)
|
|
||||||
dnsrr := res.DNSRewriteResult
|
|
||||||
if dnsrr == nil {
|
|
||||||
return agherr.Error("no dns rewrite rule content")
|
|
||||||
}
|
|
||||||
|
|
||||||
resp.Rcode = dnsrr.RCode
|
|
||||||
if resp.Rcode != dns.RcodeSuccess {
|
|
||||||
d.Res = resp
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if dnsrr.Response == nil {
|
|
||||||
return agherr.Error("no dns rewrite rule responses")
|
|
||||||
}
|
|
||||||
|
|
||||||
rr := req.Question[0].Qtype
|
|
||||||
values := dnsrr.Response[rr]
|
|
||||||
for i, v := range values {
|
|
||||||
var ans dns.RR
|
|
||||||
ans, err = s.filterDNSRewriteResponse(req, rr, v)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("dns rewrite response for %d[%d]: %w", rr, i, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
resp.Answer = append(resp.Answer, ans)
|
|
||||||
}
|
|
||||||
|
|
||||||
d.Res = resp
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -42,8 +42,7 @@ func (s *Server) getClientRequestFilteringSettings(d *proxy.DNSContext) *dnsfilt
|
|||||||
return &setts
|
return &setts
|
||||||
}
|
}
|
||||||
|
|
||||||
// filterDNSRequest applies the dnsFilter and sets d.Res if the request
|
// filterDNSRequest applies the dnsFilter and sets d.Res if the request was filtered
|
||||||
// was filtered.
|
|
||||||
func (s *Server) filterDNSRequest(ctx *dnsContext) (*dnsfilter.Result, error) {
|
func (s *Server) filterDNSRequest(ctx *dnsContext) (*dnsfilter.Result, error) {
|
||||||
d := ctx.proxyCtx
|
d := ctx.proxyCtx
|
||||||
req := d.Req
|
req := d.Req
|
||||||
@@ -53,17 +52,13 @@ func (s *Server) filterDNSRequest(ctx *dnsContext) (*dnsfilter.Result, error) {
|
|||||||
// Return immediately if there's an error
|
// Return immediately if there's an error
|
||||||
return nil, fmt.Errorf("dnsfilter failed to check host %q: %w", host, err)
|
return nil, fmt.Errorf("dnsfilter failed to check host %q: %w", host, err)
|
||||||
} else if res.IsFiltered {
|
} else if res.IsFiltered {
|
||||||
log.Tracef("Host %s is filtered, reason - %q, matched rule: %q", host, res.Reason, res.Rules[0].Text)
|
log.Tracef("Host %s is filtered, reason - %q, matched rule: %q", host, res.Reason, res.Rule)
|
||||||
d.Res = s.genDNSFilterMessage(d, &res)
|
d.Res = s.genDNSFilterMessage(d, &res)
|
||||||
} else if res.Reason.In(dnsfilter.ReasonRewrite, dnsfilter.DNSRewriteRule) &&
|
} else if res.Reason == dnsfilter.ReasonRewrite && len(res.CanonName) != 0 && len(res.IPList) == 0 {
|
||||||
res.CanonName != "" &&
|
|
||||||
len(res.IPList) == 0 {
|
|
||||||
// Resolve the new canonical name, not the original host
|
|
||||||
// name. The original question is readded in
|
|
||||||
// processFilteringAfterResponse.
|
|
||||||
ctx.origQuestion = d.Req.Question[0]
|
ctx.origQuestion = d.Req.Question[0]
|
||||||
|
// resolve canonical name, not the original host name
|
||||||
d.Req.Question[0].Name = dns.Fqdn(res.CanonName)
|
d.Req.Question[0].Name = dns.Fqdn(res.CanonName)
|
||||||
} else if res.Reason == dnsfilter.RewriteAutoHosts && len(res.ReverseHosts) != 0 {
|
} else if res.Reason == dnsfilter.RewriteEtcHosts && len(res.ReverseHosts) != 0 {
|
||||||
resp := s.makeResponse(req)
|
resp := s.makeResponse(req)
|
||||||
for _, h := range res.ReverseHosts {
|
for _, h := range res.ReverseHosts {
|
||||||
hdr := dns.RR_Header{
|
hdr := dns.RR_Header{
|
||||||
@@ -82,33 +77,28 @@ func (s *Server) filterDNSRequest(ctx *dnsContext) (*dnsfilter.Result, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
d.Res = resp
|
d.Res = resp
|
||||||
} else if res.Reason == dnsfilter.ReasonRewrite || res.Reason == dnsfilter.RewriteAutoHosts {
|
} else if res.Reason == dnsfilter.ReasonRewrite || res.Reason == dnsfilter.RewriteEtcHosts {
|
||||||
resp := s.makeResponse(req)
|
resp := s.makeResponse(req)
|
||||||
|
|
||||||
name := host
|
name := host
|
||||||
if len(res.CanonName) != 0 {
|
if len(res.CanonName) != 0 {
|
||||||
resp.Answer = append(resp.Answer, s.genAnswerCNAME(req, res.CanonName))
|
resp.Answer = append(resp.Answer, s.genCNAMEAnswer(req, res.CanonName))
|
||||||
name = res.CanonName
|
name = res.CanonName
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, ip := range res.IPList {
|
for _, ip := range res.IPList {
|
||||||
if req.Question[0].Qtype == dns.TypeA {
|
if req.Question[0].Qtype == dns.TypeA {
|
||||||
a := s.genAnswerA(req, ip.To4())
|
a := s.genAAnswer(req, ip.To4())
|
||||||
a.Hdr.Name = dns.Fqdn(name)
|
a.Hdr.Name = dns.Fqdn(name)
|
||||||
resp.Answer = append(resp.Answer, a)
|
resp.Answer = append(resp.Answer, a)
|
||||||
} else if req.Question[0].Qtype == dns.TypeAAAA {
|
} else if req.Question[0].Qtype == dns.TypeAAAA {
|
||||||
a := s.genAnswerAAAA(req, ip)
|
a := s.genAAAAAnswer(req, ip)
|
||||||
a.Hdr.Name = dns.Fqdn(name)
|
a.Hdr.Name = dns.Fqdn(name)
|
||||||
resp.Answer = append(resp.Answer, a)
|
resp.Answer = append(resp.Answer, a)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
d.Res = resp
|
d.Res = resp
|
||||||
} else if res.Reason == dnsfilter.DNSRewriteRule {
|
|
||||||
err = s.filterDNSRewrite(req, res, d)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return &res, err
|
return &res, err
|
||||||
|
|||||||
@@ -167,12 +167,11 @@ func (req *dnsConfig) checkCacheTTL() bool {
|
|||||||
if req.CacheMinTTL == nil && req.CacheMaxTTL == nil {
|
if req.CacheMinTTL == nil && req.CacheMaxTTL == nil {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
var min, max uint32
|
var min, max uint32
|
||||||
if req.CacheMinTTL != nil {
|
if req.CacheMinTTL != nil {
|
||||||
min = *req.CacheMinTTL
|
min = *req.CacheMinTTL
|
||||||
}
|
}
|
||||||
if req.CacheMaxTTL != nil {
|
if req.CacheMaxTTL == nil {
|
||||||
max = *req.CacheMaxTTL
|
max = *req.CacheMaxTTL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,22 +7,16 @@ import (
|
|||||||
"github.com/AdguardTeam/AdGuardHome/internal/dnsfilter"
|
"github.com/AdguardTeam/AdGuardHome/internal/dnsfilter"
|
||||||
"github.com/AdguardTeam/dnsproxy/proxy"
|
"github.com/AdguardTeam/dnsproxy/proxy"
|
||||||
"github.com/AdguardTeam/golibs/log"
|
"github.com/AdguardTeam/golibs/log"
|
||||||
"github.com/AdguardTeam/urlfilter/rules"
|
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Create a DNS response by DNS request and set necessary flags
|
// Create a DNS response by DNS request and set necessary flags
|
||||||
func (s *Server) makeResponse(req *dns.Msg) (resp *dns.Msg) {
|
func (s *Server) makeResponse(req *dns.Msg) *dns.Msg {
|
||||||
resp = &dns.Msg{
|
resp := dns.Msg{}
|
||||||
MsgHdr: dns.MsgHdr{
|
|
||||||
RecursionAvailable: true,
|
|
||||||
},
|
|
||||||
Compress: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
resp.SetReply(req)
|
resp.SetReply(req)
|
||||||
|
resp.RecursionAvailable = true
|
||||||
return resp
|
resp.Compress = true
|
||||||
|
return &resp
|
||||||
}
|
}
|
||||||
|
|
||||||
// genDNSFilterMessage generates a DNS message corresponding to the filtering result
|
// genDNSFilterMessage generates a DNS message corresponding to the filtering result
|
||||||
@@ -45,10 +39,8 @@ func (s *Server) genDNSFilterMessage(d *proxy.DNSContext, result *dnsfilter.Resu
|
|||||||
// If the query was filtered by "Safe search", dnsfilter also must return
|
// If the query was filtered by "Safe search", dnsfilter also must return
|
||||||
// the IP address that must be used in response.
|
// the IP address that must be used in response.
|
||||||
// In this case regardless of the filtering method, we should return it
|
// In this case regardless of the filtering method, we should return it
|
||||||
if result.Reason == dnsfilter.FilteredSafeSearch &&
|
if result.Reason == dnsfilter.FilteredSafeSearch && result.IP != nil {
|
||||||
len(result.Rules) > 0 &&
|
return s.genResponseWithIP(m, result.IP)
|
||||||
result.Rules[0].IP != nil {
|
|
||||||
return s.genResponseWithIP(m, result.Rules[0].IP)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.conf.BlockingMode == "null_ip" {
|
if s.conf.BlockingMode == "null_ip" {
|
||||||
@@ -76,8 +68,8 @@ func (s *Server) genDNSFilterMessage(d *proxy.DNSContext, result *dnsfilter.Resu
|
|||||||
// Default blocking mode
|
// Default blocking mode
|
||||||
// If there's an IP specified in the rule, return it
|
// If there's an IP specified in the rule, return it
|
||||||
// For host-type rules, return null IP
|
// For host-type rules, return null IP
|
||||||
if len(result.Rules) > 0 && result.Rules[0].IP != nil {
|
if result.IP != nil {
|
||||||
return s.genResponseWithIP(m, result.Rules[0].IP)
|
return s.genResponseWithIP(m, result.IP)
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.makeResponseNullIP(m)
|
return s.makeResponseNullIP(m)
|
||||||
@@ -93,66 +85,38 @@ func (s *Server) genServerFailure(request *dns.Msg) *dns.Msg {
|
|||||||
|
|
||||||
func (s *Server) genARecord(request *dns.Msg, ip net.IP) *dns.Msg {
|
func (s *Server) genARecord(request *dns.Msg, ip net.IP) *dns.Msg {
|
||||||
resp := s.makeResponse(request)
|
resp := s.makeResponse(request)
|
||||||
resp.Answer = append(resp.Answer, s.genAnswerA(request, ip))
|
resp.Answer = append(resp.Answer, s.genAAnswer(request, ip))
|
||||||
return resp
|
return resp
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) genAAAARecord(request *dns.Msg, ip net.IP) *dns.Msg {
|
func (s *Server) genAAAARecord(request *dns.Msg, ip net.IP) *dns.Msg {
|
||||||
resp := s.makeResponse(request)
|
resp := s.makeResponse(request)
|
||||||
resp.Answer = append(resp.Answer, s.genAnswerAAAA(request, ip))
|
resp.Answer = append(resp.Answer, s.genAAAAAnswer(request, ip))
|
||||||
return resp
|
return resp
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) hdr(req *dns.Msg, rrType rules.RRType) (h dns.RR_Header) {
|
func (s *Server) genAAnswer(req *dns.Msg, ip net.IP) *dns.A {
|
||||||
return dns.RR_Header{
|
answer := new(dns.A)
|
||||||
|
answer.Hdr = dns.RR_Header{
|
||||||
Name: req.Question[0].Name,
|
Name: req.Question[0].Name,
|
||||||
Rrtype: rrType,
|
Rrtype: dns.TypeA,
|
||||||
Ttl: s.conf.BlockedResponseTTL,
|
Ttl: s.conf.BlockedResponseTTL,
|
||||||
Class: dns.ClassINET,
|
Class: dns.ClassINET,
|
||||||
}
|
}
|
||||||
|
answer.A = ip
|
||||||
|
return answer
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) genAnswerA(req *dns.Msg, ip net.IP) (ans *dns.A) {
|
func (s *Server) genAAAAAnswer(req *dns.Msg, ip net.IP) *dns.AAAA {
|
||||||
return &dns.A{
|
answer := new(dns.AAAA)
|
||||||
Hdr: s.hdr(req, dns.TypeA),
|
answer.Hdr = dns.RR_Header{
|
||||||
A: ip,
|
Name: req.Question[0].Name,
|
||||||
}
|
Rrtype: dns.TypeAAAA,
|
||||||
}
|
Ttl: s.conf.BlockedResponseTTL,
|
||||||
|
Class: dns.ClassINET,
|
||||||
func (s *Server) genAnswerAAAA(req *dns.Msg, ip net.IP) (ans *dns.AAAA) {
|
|
||||||
return &dns.AAAA{
|
|
||||||
Hdr: s.hdr(req, dns.TypeAAAA),
|
|
||||||
AAAA: ip,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) genAnswerCNAME(req *dns.Msg, cname string) (ans *dns.CNAME) {
|
|
||||||
return &dns.CNAME{
|
|
||||||
Hdr: s.hdr(req, dns.TypeCNAME),
|
|
||||||
Target: dns.Fqdn(cname),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) genAnswerMX(req *dns.Msg, mx *rules.DNSMX) (ans *dns.MX) {
|
|
||||||
return &dns.MX{
|
|
||||||
Hdr: s.hdr(req, dns.TypePTR),
|
|
||||||
Preference: mx.Preference,
|
|
||||||
Mx: mx.Exchange,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) genAnswerPTR(req *dns.Msg, ptr string) (ans *dns.PTR) {
|
|
||||||
return &dns.PTR{
|
|
||||||
Hdr: s.hdr(req, dns.TypePTR),
|
|
||||||
Ptr: ptr,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) genAnswerTXT(req *dns.Msg, strs []string) (ans *dns.TXT) {
|
|
||||||
return &dns.TXT{
|
|
||||||
Hdr: s.hdr(req, dns.TypeTXT),
|
|
||||||
Txt: strs,
|
|
||||||
}
|
}
|
||||||
|
answer.AAAA = ip
|
||||||
|
return answer
|
||||||
}
|
}
|
||||||
|
|
||||||
// generate DNS response message with an IP address
|
// generate DNS response message with an IP address
|
||||||
@@ -215,6 +179,19 @@ func (s *Server) genBlockedHost(request *dns.Msg, newAddr string, d *proxy.DNSCo
|
|||||||
return resp
|
return resp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make a CNAME response
|
||||||
|
func (s *Server) genCNAMEAnswer(req *dns.Msg, cname string) *dns.CNAME {
|
||||||
|
answer := new(dns.CNAME)
|
||||||
|
answer.Hdr = dns.RR_Header{
|
||||||
|
Name: req.Question[0].Name,
|
||||||
|
Rrtype: dns.TypeCNAME,
|
||||||
|
Ttl: s.conf.BlockedResponseTTL,
|
||||||
|
Class: dns.ClassINET,
|
||||||
|
}
|
||||||
|
answer.Target = dns.Fqdn(cname)
|
||||||
|
return answer
|
||||||
|
}
|
||||||
|
|
||||||
// Create REFUSED DNS response
|
// Create REFUSED DNS response
|
||||||
func (s *Server) makeResponseREFUSED(request *dns.Msg) *dns.Msg {
|
func (s *Server) makeResponseREFUSED(request *dns.Msg) *dns.Msg {
|
||||||
resp := dns.Msg{}
|
resp := dns.Msg{}
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ func (s *Server) updateStats(d *proxy.DNSContext, elapsed time.Duration, res dns
|
|||||||
case dnsfilter.FilteredSafeSearch:
|
case dnsfilter.FilteredSafeSearch:
|
||||||
e.Result = stats.RSafeSearch
|
e.Result = stats.RSafeSearch
|
||||||
|
|
||||||
case dnsfilter.FilteredBlockList:
|
case dnsfilter.FilteredBlackList:
|
||||||
fallthrough
|
fallthrough
|
||||||
case dnsfilter.FilteredInvalid:
|
case dnsfilter.FilteredInvalid:
|
||||||
fallthrough
|
fallthrough
|
||||||
|
|||||||
@@ -1,168 +0,0 @@
|
|||||||
package dnsforward
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/base64"
|
|
||||||
"net"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/AdguardTeam/golibs/log"
|
|
||||||
"github.com/AdguardTeam/urlfilter/rules"
|
|
||||||
"github.com/miekg/dns"
|
|
||||||
)
|
|
||||||
|
|
||||||
// genAnswerHTTPS returns a properly initialized HTTPS resource record.
|
|
||||||
//
|
|
||||||
// See the comment on genAnswerSVCB for a list of current restrictions on
|
|
||||||
// parameter values.
|
|
||||||
func (s *Server) genAnswerHTTPS(req *dns.Msg, svcb *rules.DNSSVCB) (ans *dns.HTTPS) {
|
|
||||||
ans = &dns.HTTPS{
|
|
||||||
SVCB: *s.genAnswerSVCB(req, svcb),
|
|
||||||
}
|
|
||||||
|
|
||||||
ans.Hdr.Rrtype = dns.TypeHTTPS
|
|
||||||
|
|
||||||
return ans
|
|
||||||
}
|
|
||||||
|
|
||||||
// strToSVCBKey is the string-to-svcb-key mapping.
|
|
||||||
//
|
|
||||||
// See https://github.com/miekg/dns/blob/23c4faca9d32b0abbb6e179aa1aadc45ac53a916/svcb.go#L27.
|
|
||||||
//
|
|
||||||
// TODO(a.garipov): Propose exporting this API or something similar in the
|
|
||||||
// github.com/miekg/dns module.
|
|
||||||
var strToSVCBKey = map[string]dns.SVCBKey{
|
|
||||||
"alpn": dns.SVCB_ALPN,
|
|
||||||
"echconfig": dns.SVCB_ECHCONFIG,
|
|
||||||
"ipv4hint": dns.SVCB_IPV4HINT,
|
|
||||||
"ipv6hint": dns.SVCB_IPV6HINT,
|
|
||||||
"mandatory": dns.SVCB_MANDATORY,
|
|
||||||
"no-default-alpn": dns.SVCB_NO_DEFAULT_ALPN,
|
|
||||||
"port": dns.SVCB_PORT,
|
|
||||||
}
|
|
||||||
|
|
||||||
// svcbKeyHandler is a handler for one SVCB parameter key.
|
|
||||||
type svcbKeyHandler func(valStr string) (val dns.SVCBKeyValue)
|
|
||||||
|
|
||||||
// svcbKeyHandlers are the supported SVCB parameters handlers.
|
|
||||||
var svcbKeyHandlers = map[string]svcbKeyHandler{
|
|
||||||
"alpn": func(valStr string) (val dns.SVCBKeyValue) {
|
|
||||||
return &dns.SVCBAlpn{
|
|
||||||
Alpn: []string{valStr},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
"echconfig": func(valStr string) (val dns.SVCBKeyValue) {
|
|
||||||
ech, err := base64.StdEncoding.DecodeString(valStr)
|
|
||||||
if err != nil {
|
|
||||||
log.Debug("can't parse svcb/https echconfig: %s; ignoring", err)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return &dns.SVCBECHConfig{
|
|
||||||
ECH: ech,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
"ipv4hint": func(valStr string) (val dns.SVCBKeyValue) {
|
|
||||||
ip := net.ParseIP(valStr)
|
|
||||||
if ip4 := ip.To4(); ip == nil || ip4 == nil {
|
|
||||||
log.Debug("can't parse svcb/https ipv4 hint %q; ignoring", valStr)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return &dns.SVCBIPv4Hint{
|
|
||||||
Hint: []net.IP{ip},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
"ipv6hint": func(valStr string) (val dns.SVCBKeyValue) {
|
|
||||||
ip := net.ParseIP(valStr)
|
|
||||||
if ip == nil {
|
|
||||||
log.Debug("can't parse svcb/https ipv6 hint %q; ignoring", valStr)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return &dns.SVCBIPv6Hint{
|
|
||||||
Hint: []net.IP{ip},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
"mandatory": func(valStr string) (val dns.SVCBKeyValue) {
|
|
||||||
code, ok := strToSVCBKey[valStr]
|
|
||||||
if !ok {
|
|
||||||
log.Debug("unknown svcb/https mandatory key %q, ignoring", valStr)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return &dns.SVCBMandatory{
|
|
||||||
Code: []dns.SVCBKey{code},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
"no-default-alpn": func(_ string) (val dns.SVCBKeyValue) {
|
|
||||||
return &dns.SVCBNoDefaultAlpn{}
|
|
||||||
},
|
|
||||||
|
|
||||||
"port": func(valStr string) (val dns.SVCBKeyValue) {
|
|
||||||
port64, err := strconv.ParseUint(valStr, 10, 16)
|
|
||||||
if err != nil {
|
|
||||||
log.Debug("can't parse svcb/https port: %s; ignoring", err)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return &dns.SVCBPort{
|
|
||||||
Port: uint16(port64),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// genAnswerSVCB returns a properly initialized SVCB resource record.
|
|
||||||
//
|
|
||||||
// Currently, there are several restrictions on how the parameters are parsed.
|
|
||||||
// Firstly, the parsing of non-contiguous values isn't supported. Secondly, the
|
|
||||||
// parsing of value-lists is not supported either.
|
|
||||||
//
|
|
||||||
// ipv4hint=127.0.0.1 // Supported.
|
|
||||||
// ipv4hint="127.0.0.1" // Unsupported.
|
|
||||||
// ipv4hint=127.0.0.1,127.0.0.2 // Unsupported.
|
|
||||||
// ipv4hint="127.0.0.1,127.0.0.2" // Unsupported.
|
|
||||||
//
|
|
||||||
// TODO(a.garipov): Support all of these.
|
|
||||||
func (s *Server) genAnswerSVCB(req *dns.Msg, svcb *rules.DNSSVCB) (ans *dns.SVCB) {
|
|
||||||
ans = &dns.SVCB{
|
|
||||||
Hdr: s.hdr(req, dns.TypeSVCB),
|
|
||||||
Priority: svcb.Priority,
|
|
||||||
Target: svcb.Target,
|
|
||||||
}
|
|
||||||
if len(svcb.Params) == 0 {
|
|
||||||
return ans
|
|
||||||
}
|
|
||||||
|
|
||||||
values := make([]dns.SVCBKeyValue, 0, len(svcb.Params))
|
|
||||||
for k, valStr := range svcb.Params {
|
|
||||||
handler, ok := svcbKeyHandlers[k]
|
|
||||||
if !ok {
|
|
||||||
log.Debug("unknown svcb/https key %q, ignoring", k)
|
|
||||||
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
val := handler(valStr)
|
|
||||||
if val == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
values = append(values, val)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(values) > 0 {
|
|
||||||
ans.Value = values
|
|
||||||
}
|
|
||||||
|
|
||||||
return ans
|
|
||||||
}
|
|
||||||
@@ -1,154 +0,0 @@
|
|||||||
package dnsforward
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/AdguardTeam/urlfilter/rules"
|
|
||||||
"github.com/miekg/dns"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestGenAnswerHTTPS_andSVCB(t *testing.T) {
|
|
||||||
// Preconditions.
|
|
||||||
|
|
||||||
s := &Server{
|
|
||||||
conf: ServerConfig{
|
|
||||||
FilteringConfig: FilteringConfig{
|
|
||||||
BlockedResponseTTL: 3600,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
req := &dns.Msg{
|
|
||||||
Question: []dns.Question{{
|
|
||||||
Name: "abcd",
|
|
||||||
}},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Constants and helper values.
|
|
||||||
|
|
||||||
const host = "example.com"
|
|
||||||
const prio = 32
|
|
||||||
|
|
||||||
ip4 := net.IPv4(127, 0, 0, 1)
|
|
||||||
ip6 := net.IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}
|
|
||||||
|
|
||||||
// Helper functions.
|
|
||||||
|
|
||||||
dnssvcb := func(key, value string) (svcb *rules.DNSSVCB) {
|
|
||||||
svcb = &rules.DNSSVCB{
|
|
||||||
Target: host,
|
|
||||||
Priority: prio,
|
|
||||||
}
|
|
||||||
|
|
||||||
if key == "" {
|
|
||||||
return svcb
|
|
||||||
}
|
|
||||||
|
|
||||||
svcb.Params = map[string]string{
|
|
||||||
key: value,
|
|
||||||
}
|
|
||||||
|
|
||||||
return svcb
|
|
||||||
}
|
|
||||||
|
|
||||||
wantsvcb := func(kv dns.SVCBKeyValue) (want *dns.SVCB) {
|
|
||||||
want = &dns.SVCB{
|
|
||||||
Hdr: s.hdr(req, dns.TypeSVCB),
|
|
||||||
Priority: prio,
|
|
||||||
Target: host,
|
|
||||||
}
|
|
||||||
|
|
||||||
if kv == nil {
|
|
||||||
return want
|
|
||||||
}
|
|
||||||
|
|
||||||
want.Value = []dns.SVCBKeyValue{kv}
|
|
||||||
|
|
||||||
return want
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tests.
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
svcb *rules.DNSSVCB
|
|
||||||
want *dns.SVCB
|
|
||||||
name string
|
|
||||||
}{{
|
|
||||||
svcb: dnssvcb("", ""),
|
|
||||||
want: wantsvcb(nil),
|
|
||||||
name: "no_params",
|
|
||||||
}, {
|
|
||||||
svcb: dnssvcb("foo", "bar"),
|
|
||||||
want: wantsvcb(nil),
|
|
||||||
name: "invalid",
|
|
||||||
}, {
|
|
||||||
svcb: dnssvcb("alpn", "h3"),
|
|
||||||
want: wantsvcb(&dns.SVCBAlpn{Alpn: []string{"h3"}}),
|
|
||||||
name: "alpn",
|
|
||||||
}, {
|
|
||||||
svcb: dnssvcb("echconfig", "AAAA"),
|
|
||||||
want: wantsvcb(&dns.SVCBECHConfig{ECH: []byte{0, 0, 0}}),
|
|
||||||
name: "echconfig",
|
|
||||||
}, {
|
|
||||||
svcb: dnssvcb("echconfig", "%BAD%"),
|
|
||||||
want: wantsvcb(nil),
|
|
||||||
name: "echconfig_invalid",
|
|
||||||
}, {
|
|
||||||
svcb: dnssvcb("ipv4hint", "127.0.0.1"),
|
|
||||||
want: wantsvcb(&dns.SVCBIPv4Hint{Hint: []net.IP{ip4}}),
|
|
||||||
name: "ipv4hint",
|
|
||||||
}, {
|
|
||||||
svcb: dnssvcb("ipv4hint", "127.0.01"),
|
|
||||||
want: wantsvcb(nil),
|
|
||||||
name: "ipv4hint_invalid",
|
|
||||||
}, {
|
|
||||||
svcb: dnssvcb("ipv6hint", "::1"),
|
|
||||||
want: wantsvcb(&dns.SVCBIPv6Hint{Hint: []net.IP{ip6}}),
|
|
||||||
name: "ipv6hint",
|
|
||||||
}, {
|
|
||||||
svcb: dnssvcb("ipv6hint", ":::1"),
|
|
||||||
want: wantsvcb(nil),
|
|
||||||
name: "ipv6hint_invalid",
|
|
||||||
}, {
|
|
||||||
svcb: dnssvcb("mandatory", "alpn"),
|
|
||||||
want: wantsvcb(&dns.SVCBMandatory{Code: []dns.SVCBKey{dns.SVCB_ALPN}}),
|
|
||||||
name: "mandatory",
|
|
||||||
}, {
|
|
||||||
svcb: dnssvcb("mandatory", "alpnn"),
|
|
||||||
want: wantsvcb(nil),
|
|
||||||
name: "mandatory_invalid",
|
|
||||||
}, {
|
|
||||||
svcb: dnssvcb("no-default-alpn", ""),
|
|
||||||
want: wantsvcb(&dns.SVCBNoDefaultAlpn{}),
|
|
||||||
name: "no-default-alpn",
|
|
||||||
}, {
|
|
||||||
svcb: dnssvcb("port", "8080"),
|
|
||||||
want: wantsvcb(&dns.SVCBPort{Port: 8080}),
|
|
||||||
name: "port",
|
|
||||||
}, {
|
|
||||||
svcb: dnssvcb("port", "1005008080"),
|
|
||||||
want: wantsvcb(nil),
|
|
||||||
name: "port",
|
|
||||||
}}
|
|
||||||
|
|
||||||
for _, tc := range testCases {
|
|
||||||
t.Run("https", func(t *testing.T) {
|
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
|
||||||
want := &dns.HTTPS{SVCB: *tc.want}
|
|
||||||
want.Hdr.Rrtype = dns.TypeHTTPS
|
|
||||||
|
|
||||||
got := s.genAnswerHTTPS(req, tc.svcb)
|
|
||||||
assert.Equal(t, want, got)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("svcb", func(t *testing.T) {
|
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
|
||||||
got := s.genAnswerSVCB(req, tc.svcb)
|
|
||||||
assert.Equal(t, tc.want, got)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -59,10 +59,10 @@ func (s *session) deserialize(data []byte) bool {
|
|||||||
// Auth - global object
|
// Auth - global object
|
||||||
type Auth struct {
|
type Auth struct {
|
||||||
db *bbolt.DB
|
db *bbolt.DB
|
||||||
sessions map[string]*session
|
sessions map[string]*session // session name -> session data
|
||||||
users []User
|
|
||||||
lock sync.Mutex
|
lock sync.Mutex
|
||||||
sessionTTL uint32
|
users []User
|
||||||
|
sessionTTL uint32 // in seconds
|
||||||
}
|
}
|
||||||
|
|
||||||
// User object
|
// User object
|
||||||
@@ -223,35 +223,24 @@ func (a *Auth) removeSession(sess []byte) {
|
|||||||
log.Debug("Auth: removed session from DB")
|
log.Debug("Auth: removed session from DB")
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkSessionResult is the result of checking a session.
|
// CheckSession - check if session is valid
|
||||||
type checkSessionResult int
|
// Return 0 if OK; -1 if session doesn't exist; 1 if session has expired
|
||||||
|
func (a *Auth) CheckSession(sess string) int {
|
||||||
// checkSessionResult constants.
|
|
||||||
const (
|
|
||||||
checkSessionOK checkSessionResult = 0
|
|
||||||
checkSessionNotFound checkSessionResult = -1
|
|
||||||
checkSessionExpired checkSessionResult = 1
|
|
||||||
)
|
|
||||||
|
|
||||||
// checkSession checks if the session is valid.
|
|
||||||
func (a *Auth) checkSession(sess string) (res checkSessionResult) {
|
|
||||||
now := uint32(time.Now().UTC().Unix())
|
now := uint32(time.Now().UTC().Unix())
|
||||||
update := false
|
update := false
|
||||||
|
|
||||||
a.lock.Lock()
|
a.lock.Lock()
|
||||||
defer a.lock.Unlock()
|
|
||||||
|
|
||||||
s, ok := a.sessions[sess]
|
s, ok := a.sessions[sess]
|
||||||
if !ok {
|
if !ok {
|
||||||
return checkSessionNotFound
|
a.lock.Unlock()
|
||||||
|
return -1
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.expire <= now {
|
if s.expire <= now {
|
||||||
delete(a.sessions, sess)
|
delete(a.sessions, sess)
|
||||||
key, _ := hex.DecodeString(sess)
|
key, _ := hex.DecodeString(sess)
|
||||||
a.removeSession(key)
|
a.removeSession(key)
|
||||||
|
a.lock.Unlock()
|
||||||
return checkSessionExpired
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
newExpire := now + a.sessionTTL
|
newExpire := now + a.sessionTTL
|
||||||
@@ -261,6 +250,8 @@ func (a *Auth) checkSession(sess string) (res checkSessionResult) {
|
|||||||
s.expire = newExpire
|
s.expire = newExpire
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a.lock.Unlock()
|
||||||
|
|
||||||
if update {
|
if update {
|
||||||
key, _ := hex.DecodeString(sess)
|
key, _ := hex.DecodeString(sess)
|
||||||
if a.storeSession(key, s) {
|
if a.storeSession(key, s) {
|
||||||
@@ -268,7 +259,7 @@ func (a *Auth) checkSession(sess string) (res checkSessionResult) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return checkSessionOK
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveSession - remove session
|
// RemoveSession - remove session
|
||||||
@@ -401,8 +392,8 @@ func optionalAuthThird(w http.ResponseWriter, r *http.Request) (authFirst bool)
|
|||||||
ok = true
|
ok = true
|
||||||
|
|
||||||
} else if err == nil {
|
} else if err == nil {
|
||||||
r := Context.auth.checkSession(cookie.Value)
|
r := Context.auth.CheckSession(cookie.Value)
|
||||||
if r == checkSessionOK {
|
if r == 0 {
|
||||||
ok = true
|
ok = true
|
||||||
} else if r < 0 {
|
} else if r < 0 {
|
||||||
log.Debug("Auth: invalid cookie value: %s", cookie)
|
log.Debug("Auth: invalid cookie value: %s", cookie)
|
||||||
@@ -443,13 +434,12 @@ func optionalAuth(handler func(http.ResponseWriter, *http.Request)) func(http.Re
|
|||||||
authRequired := Context.auth != nil && Context.auth.AuthRequired()
|
authRequired := Context.auth != nil && Context.auth.AuthRequired()
|
||||||
cookie, err := r.Cookie(sessionCookieName)
|
cookie, err := r.Cookie(sessionCookieName)
|
||||||
if authRequired && err == nil {
|
if authRequired && err == nil {
|
||||||
r := Context.auth.checkSession(cookie.Value)
|
r := Context.auth.CheckSession(cookie.Value)
|
||||||
if r == checkSessionOK {
|
if r == 0 {
|
||||||
w.Header().Set("Location", "/")
|
w.Header().Set("Location", "/")
|
||||||
w.WriteHeader(http.StatusFound)
|
w.WriteHeader(http.StatusFound)
|
||||||
|
|
||||||
return
|
return
|
||||||
} else if r == checkSessionNotFound {
|
} else if r < 0 {
|
||||||
log.Debug("Auth: invalid cookie value: %s", cookie)
|
log.Debug("Auth: invalid cookie value: %s", cookie)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -513,34 +503,32 @@ func (a *Auth) UserFind(login, password string) User {
|
|||||||
return User{}
|
return User{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// getCurrentUser returns the current user. It returns an empty User if the
|
// GetCurrentUser - get the current user
|
||||||
// user is not found.
|
func (a *Auth) GetCurrentUser(r *http.Request) User {
|
||||||
func (a *Auth) getCurrentUser(r *http.Request) User {
|
|
||||||
cookie, err := r.Cookie(sessionCookieName)
|
cookie, err := r.Cookie(sessionCookieName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// There's no Cookie, check Basic authentication.
|
// there's no Cookie, check Basic authentication
|
||||||
user, pass, ok := r.BasicAuth()
|
user, pass, ok := r.BasicAuth()
|
||||||
if ok {
|
if ok {
|
||||||
return Context.auth.UserFind(user, pass)
|
u := Context.auth.UserFind(user, pass)
|
||||||
|
return u
|
||||||
}
|
}
|
||||||
|
|
||||||
return User{}
|
return User{}
|
||||||
}
|
}
|
||||||
|
|
||||||
a.lock.Lock()
|
a.lock.Lock()
|
||||||
defer a.lock.Unlock()
|
|
||||||
|
|
||||||
s, ok := a.sessions[cookie.Value]
|
s, ok := a.sessions[cookie.Value]
|
||||||
if !ok {
|
if !ok {
|
||||||
|
a.lock.Unlock()
|
||||||
return User{}
|
return User{}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, u := range a.users {
|
for _, u := range a.users {
|
||||||
if u.Name == s.userName {
|
if u.Name == s.userName {
|
||||||
|
a.lock.Unlock()
|
||||||
return u
|
return u
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
a.lock.Unlock()
|
||||||
return User{}
|
return User{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ func TestAuth(t *testing.T) {
|
|||||||
user := User{Name: "name"}
|
user := User{Name: "name"}
|
||||||
a.UserAdd(&user, "password")
|
a.UserAdd(&user, "password")
|
||||||
|
|
||||||
assert.Equal(t, checkSessionNotFound, a.checkSession("notfound"))
|
assert.True(t, a.CheckSession("notfound") == -1)
|
||||||
a.RemoveSession("notfound")
|
a.RemoveSession("notfound")
|
||||||
|
|
||||||
sess, err := getSession(&users[0])
|
sess, err := getSession(&users[0])
|
||||||
@@ -49,13 +49,13 @@ func TestAuth(t *testing.T) {
|
|||||||
// check expiration
|
// check expiration
|
||||||
s.expire = uint32(now)
|
s.expire = uint32(now)
|
||||||
a.addSession(sess, &s)
|
a.addSession(sess, &s)
|
||||||
assert.Equal(t, checkSessionExpired, a.checkSession(sessStr))
|
assert.True(t, a.CheckSession(sessStr) == 1)
|
||||||
|
|
||||||
// add session with TTL = 2 sec
|
// add session with TTL = 2 sec
|
||||||
s = session{}
|
s = session{}
|
||||||
s.expire = uint32(time.Now().UTC().Unix() + 2)
|
s.expire = uint32(time.Now().UTC().Unix() + 2)
|
||||||
a.addSession(sess, &s)
|
a.addSession(sess, &s)
|
||||||
assert.Equal(t, checkSessionOK, a.checkSession(sessStr))
|
assert.True(t, a.CheckSession(sessStr) == 0)
|
||||||
|
|
||||||
a.Close()
|
a.Close()
|
||||||
|
|
||||||
@@ -63,8 +63,8 @@ func TestAuth(t *testing.T) {
|
|||||||
a = InitAuth(fn, users, 60)
|
a = InitAuth(fn, users, 60)
|
||||||
|
|
||||||
// the session is still alive
|
// the session is still alive
|
||||||
assert.Equal(t, checkSessionOK, a.checkSession(sessStr))
|
assert.True(t, a.CheckSession(sessStr) == 0)
|
||||||
// reset our expiration time because checkSession() has just updated it
|
// reset our expiration time because CheckSession() has just updated it
|
||||||
s.expire = uint32(time.Now().UTC().Unix() + 2)
|
s.expire = uint32(time.Now().UTC().Unix() + 2)
|
||||||
a.storeSession(sess, &s)
|
a.storeSession(sess, &s)
|
||||||
a.Close()
|
a.Close()
|
||||||
@@ -76,7 +76,7 @@ func TestAuth(t *testing.T) {
|
|||||||
|
|
||||||
// load and remove expired sessions
|
// load and remove expired sessions
|
||||||
a = InitAuth(fn, users, 60)
|
a = InitAuth(fn, users, 60)
|
||||||
assert.Equal(t, checkSessionNotFound, a.checkSession(sessStr))
|
assert.True(t, a.CheckSession(sessStr) == -1)
|
||||||
|
|
||||||
a.Close()
|
a.Close()
|
||||||
os.Remove(fn)
|
os.Remove(fn)
|
||||||
@@ -111,7 +111,7 @@ func TestAuthHTTP(t *testing.T) {
|
|||||||
Context.auth = InitAuth(fn, users, 60)
|
Context.auth = InitAuth(fn, users, 60)
|
||||||
|
|
||||||
handlerCalled := false
|
handlerCalled := false
|
||||||
handler := func(_ http.ResponseWriter, _ *http.Request) {
|
handler := func(w http.ResponseWriter, r *http.Request) {
|
||||||
handlerCalled = true
|
handlerCalled = true
|
||||||
}
|
}
|
||||||
handler2 := optionalAuth(handler)
|
handler2 := optionalAuth(handler)
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ type profileJSON struct {
|
|||||||
|
|
||||||
func handleGetProfile(w http.ResponseWriter, r *http.Request) {
|
func handleGetProfile(w http.ResponseWriter, r *http.Request) {
|
||||||
pj := profileJSON{}
|
pj := profileJSON{}
|
||||||
u := Context.auth.getCurrentUser(r)
|
u := Context.auth.GetCurrentUser(r)
|
||||||
pj.Name = u.Name
|
pj.Name = u.Name
|
||||||
|
|
||||||
data, err := json.Marshal(pj)
|
data, err := json.Marshal(pj)
|
||||||
|
|||||||
@@ -346,25 +346,10 @@ func (f *Filtering) handleFilteringConfig(w http.ResponseWriter, r *http.Request
|
|||||||
enableFilters(true)
|
enableFilters(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
type checkHostRespRule struct {
|
|
||||||
FilterListID int64 `json:"filter_list_id"`
|
|
||||||
Text string `json:"text"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type checkHostResp struct {
|
type checkHostResp struct {
|
||||||
Reason string `json:"reason"`
|
Reason string `json:"reason"`
|
||||||
|
FilterID int64 `json:"filter_id"`
|
||||||
// FilterID is the ID of the rule's filter list.
|
Rule string `json:"rule"`
|
||||||
//
|
|
||||||
// Deprecated: Use Rules[*].FilterListID.
|
|
||||||
FilterID int64 `json:"filter_id"`
|
|
||||||
|
|
||||||
// Rule is the text of the matched rule.
|
|
||||||
//
|
|
||||||
// Deprecated: Use Rules[*].Text.
|
|
||||||
Rule string `json:"rule"`
|
|
||||||
|
|
||||||
Rules []*checkHostRespRule `json:"rules"`
|
|
||||||
|
|
||||||
// for FilteredBlockedService:
|
// for FilteredBlockedService:
|
||||||
SvcName string `json:"service_name"`
|
SvcName string `json:"service_name"`
|
||||||
@@ -389,23 +374,11 @@ func (f *Filtering) handleCheckHost(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
resp := checkHostResp{}
|
resp := checkHostResp{}
|
||||||
resp.Reason = result.Reason.String()
|
resp.Reason = result.Reason.String()
|
||||||
|
resp.FilterID = result.FilterID
|
||||||
|
resp.Rule = result.Rule
|
||||||
resp.SvcName = result.ServiceName
|
resp.SvcName = result.ServiceName
|
||||||
resp.CanonName = result.CanonName
|
resp.CanonName = result.CanonName
|
||||||
resp.IPList = result.IPList
|
resp.IPList = result.IPList
|
||||||
|
|
||||||
if len(result.Rules) > 0 {
|
|
||||||
resp.FilterID = result.Rules[0].FilterListID
|
|
||||||
resp.Rule = result.Rules[0].Text
|
|
||||||
}
|
|
||||||
|
|
||||||
resp.Rules = make([]*checkHostRespRule, len(result.Rules))
|
|
||||||
for i, r := range result.Rules {
|
|
||||||
resp.Rules[i] = &checkHostRespRule{
|
|
||||||
FilterListID: r.FilterListID,
|
|
||||||
Text: r.Text,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
js, err := json.Marshal(resp)
|
js, err := json.Marshal(resp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, http.StatusInternalServerError, "json encode: %s", err)
|
httpError(w, http.StatusInternalServerError, "json encode: %s", err)
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ type homeContext struct {
|
|||||||
dnsServer *dnsforward.Server // DNS module
|
dnsServer *dnsforward.Server // DNS module
|
||||||
rdns *RDNS // rDNS module
|
rdns *RDNS // rDNS module
|
||||||
whois *Whois // WHOIS module
|
whois *Whois // WHOIS module
|
||||||
dnsFilter *dnsfilter.DNSFilter // DNS filtering module
|
dnsFilter *dnsfilter.Dnsfilter // DNS filtering module
|
||||||
dhcpServer *dhcpd.Server // DHCP module
|
dhcpServer *dhcpd.Server // DHCP module
|
||||||
auth *Auth // HTTP authentication module
|
auth *Auth // HTTP authentication module
|
||||||
filters Filtering // DNS filtering module
|
filters Filtering // DNS filtering module
|
||||||
|
|||||||
@@ -4,13 +4,11 @@ import (
|
|||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/dnsfilter"
|
"github.com/AdguardTeam/AdGuardHome/internal/dnsfilter"
|
||||||
"github.com/AdguardTeam/golibs/log"
|
"github.com/AdguardTeam/golibs/log"
|
||||||
"github.com/AdguardTeam/urlfilter/rules"
|
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -87,6 +85,54 @@ var logEntryHandlers = map[string]logEntryHandler{
|
|||||||
ent.OrigAnswer, err = base64.StdEncoding.DecodeString(v)
|
ent.OrigAnswer, err = base64.StdEncoding.DecodeString(v)
|
||||||
return err
|
return err
|
||||||
},
|
},
|
||||||
|
"IsFiltered": func(t json.Token, ent *logEntry) error {
|
||||||
|
v, ok := t.(bool)
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
ent.Result.IsFiltered = v
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
"Rule": func(t json.Token, ent *logEntry) error {
|
||||||
|
v, ok := t.(string)
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
ent.Result.Rule = v
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
"FilterID": func(t json.Token, ent *logEntry) error {
|
||||||
|
v, ok := t.(json.Number)
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
i, err := v.Int64()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ent.Result.FilterID = i
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
"Reason": func(t json.Token, ent *logEntry) error {
|
||||||
|
v, ok := t.(json.Number)
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
i, err := v.Int64()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ent.Result.Reason = dnsfilter.Reason(i)
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
"ServiceName": func(t json.Token, ent *logEntry) error {
|
||||||
|
v, ok := t.(string)
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
ent.Result.ServiceName = v
|
||||||
|
return nil
|
||||||
|
},
|
||||||
"Upstream": func(t json.Token, ent *logEntry) error {
|
"Upstream": func(t json.Token, ent *logEntry) error {
|
||||||
v, ok := t.(string)
|
v, ok := t.(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -107,411 +153,42 @@ var logEntryHandlers = map[string]logEntryHandler{
|
|||||||
ent.Elapsed = time.Duration(i)
|
ent.Elapsed = time.Duration(i)
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
}
|
"Result": func(json.Token, *logEntry) error {
|
||||||
|
|
||||||
var resultHandlers = map[string]logEntryHandler{
|
|
||||||
"IsFiltered": func(t json.Token, ent *logEntry) error {
|
|
||||||
v, ok := t.(bool)
|
|
||||||
if !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
ent.Result.IsFiltered = v
|
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
"Rule": func(t json.Token, ent *logEntry) error {
|
"Question": func(t json.Token, ent *logEntry) error {
|
||||||
s, ok := t.(string)
|
v, ok := t.(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
var qstr []byte
|
||||||
l := len(ent.Result.Rules)
|
qstr, err := base64.StdEncoding.DecodeString(v)
|
||||||
if l == 0 {
|
|
||||||
ent.Result.Rules = []*dnsfilter.ResultRule{{}}
|
|
||||||
l++
|
|
||||||
}
|
|
||||||
|
|
||||||
ent.Result.Rules[l-1].Text = s
|
|
||||||
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
"FilterID": func(t json.Token, ent *logEntry) error {
|
|
||||||
n, ok := t.(json.Number)
|
|
||||||
if !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
i, err := n.Int64()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
q := new(dns.Msg)
|
||||||
l := len(ent.Result.Rules)
|
err = q.Unpack(qstr)
|
||||||
if l == 0 {
|
|
||||||
ent.Result.Rules = []*dnsfilter.ResultRule{{}}
|
|
||||||
l++
|
|
||||||
}
|
|
||||||
|
|
||||||
ent.Result.Rules[l-1].FilterListID = i
|
|
||||||
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
"Reason": func(t json.Token, ent *logEntry) error {
|
|
||||||
v, ok := t.(json.Number)
|
|
||||||
if !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
i, err := v.Int64()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
ent.Result.Reason = dnsfilter.Reason(i)
|
ent.QHost = q.Question[0].Name
|
||||||
|
if len(ent.QHost) == 0 {
|
||||||
|
return nil // nil???
|
||||||
|
}
|
||||||
|
ent.QHost = ent.QHost[:len(ent.QHost)-1]
|
||||||
|
ent.QType = dns.TypeToString[q.Question[0].Qtype]
|
||||||
|
ent.QClass = dns.ClassToString[q.Question[0].Qclass]
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
"ServiceName": func(t json.Token, ent *logEntry) error {
|
"Time": func(t json.Token, ent *logEntry) error {
|
||||||
s, ok := t.(string)
|
v, ok := t.(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
var err error
|
||||||
ent.Result.ServiceName = s
|
ent.Time, err = time.Parse(time.RFC3339, v)
|
||||||
|
return err
|
||||||
return nil
|
|
||||||
},
|
},
|
||||||
"CanonName": func(t json.Token, ent *logEntry) error {
|
|
||||||
s, ok := t.(string)
|
|
||||||
if !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
ent.Result.CanonName = s
|
|
||||||
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func decodeResultRuleKey(key string, i int, dec *json.Decoder, ent *logEntry) {
|
|
||||||
switch key {
|
|
||||||
case "FilterListID":
|
|
||||||
vToken, err := dec.Token()
|
|
||||||
if err != nil {
|
|
||||||
if err != io.EOF {
|
|
||||||
log.Debug("decodeResultRuleKey %s err: %s", key, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(ent.Result.Rules) < i+1 {
|
|
||||||
ent.Result.Rules = append(ent.Result.Rules, &dnsfilter.ResultRule{})
|
|
||||||
}
|
|
||||||
|
|
||||||
if n, ok := vToken.(json.Number); ok {
|
|
||||||
ent.Result.Rules[i].FilterListID, _ = n.Int64()
|
|
||||||
}
|
|
||||||
case "IP":
|
|
||||||
vToken, err := dec.Token()
|
|
||||||
if err != nil {
|
|
||||||
if err != io.EOF {
|
|
||||||
log.Debug("decodeResultRuleKey %s err: %s", key, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(ent.Result.Rules) < i+1 {
|
|
||||||
ent.Result.Rules = append(ent.Result.Rules, &dnsfilter.ResultRule{})
|
|
||||||
}
|
|
||||||
|
|
||||||
if ipStr, ok := vToken.(string); ok {
|
|
||||||
ent.Result.Rules[i].IP = net.ParseIP(ipStr)
|
|
||||||
}
|
|
||||||
case "Text":
|
|
||||||
vToken, err := dec.Token()
|
|
||||||
if err != nil {
|
|
||||||
if err != io.EOF {
|
|
||||||
log.Debug("decodeResultRuleKey %s err: %s", key, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(ent.Result.Rules) < i+1 {
|
|
||||||
ent.Result.Rules = append(ent.Result.Rules, &dnsfilter.ResultRule{})
|
|
||||||
}
|
|
||||||
|
|
||||||
if s, ok := vToken.(string); ok {
|
|
||||||
ent.Result.Rules[i].Text = s
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
// Go on.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func decodeResultRules(dec *json.Decoder, ent *logEntry) {
|
|
||||||
for {
|
|
||||||
delimToken, err := dec.Token()
|
|
||||||
if err != nil {
|
|
||||||
if err != io.EOF {
|
|
||||||
log.Debug("decodeResultRules err: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if d, ok := delimToken.(json.Delim); ok {
|
|
||||||
if d != '[' {
|
|
||||||
log.Debug("decodeResultRules: unexpected delim %q", d)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
i := 0
|
|
||||||
for {
|
|
||||||
keyToken, err := dec.Token()
|
|
||||||
if err != nil {
|
|
||||||
if err != io.EOF {
|
|
||||||
log.Debug("decodeResultRules err: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if d, ok := keyToken.(json.Delim); ok {
|
|
||||||
if d == '}' {
|
|
||||||
i++
|
|
||||||
} else if d == ']' {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
key, ok := keyToken.(string)
|
|
||||||
if !ok {
|
|
||||||
log.Debug("decodeResultRules: keyToken is %T (%[1]v) and not string", keyToken)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
decodeResultRuleKey(key, i, dec, ent)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func decodeResultReverseHosts(dec *json.Decoder, ent *logEntry) {
|
|
||||||
for {
|
|
||||||
itemToken, err := dec.Token()
|
|
||||||
if err != nil {
|
|
||||||
if err != io.EOF {
|
|
||||||
log.Debug("decodeResultReverseHosts err: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch v := itemToken.(type) {
|
|
||||||
case json.Delim:
|
|
||||||
if v == '[' {
|
|
||||||
continue
|
|
||||||
} else if v == ']' {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Debug("decodeResultReverseHosts: unexpected delim %q", v)
|
|
||||||
|
|
||||||
return
|
|
||||||
case string:
|
|
||||||
ent.Result.ReverseHosts = append(ent.Result.ReverseHosts, v)
|
|
||||||
default:
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func decodeResultIPList(dec *json.Decoder, ent *logEntry) {
|
|
||||||
for {
|
|
||||||
itemToken, err := dec.Token()
|
|
||||||
if err != nil {
|
|
||||||
if err != io.EOF {
|
|
||||||
log.Debug("decodeResultIPList err: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch v := itemToken.(type) {
|
|
||||||
case json.Delim:
|
|
||||||
if v == '[' {
|
|
||||||
continue
|
|
||||||
} else if v == ']' {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Debug("decodeResultIPList: unexpected delim %q", v)
|
|
||||||
|
|
||||||
return
|
|
||||||
case string:
|
|
||||||
ip := net.ParseIP(v)
|
|
||||||
if ip != nil {
|
|
||||||
ent.Result.IPList = append(ent.Result.IPList, ip)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func decodeResultDNSRewriteResult(dec *json.Decoder, ent *logEntry) {
|
|
||||||
for {
|
|
||||||
keyToken, err := dec.Token()
|
|
||||||
if err != nil {
|
|
||||||
if err != io.EOF {
|
|
||||||
log.Debug("decodeResultDNSRewriteResult err: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if d, ok := keyToken.(json.Delim); ok {
|
|
||||||
if d == '}' {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
key, ok := keyToken.(string)
|
|
||||||
if !ok {
|
|
||||||
log.Debug("decodeResultDNSRewriteResult: keyToken is %T (%[1]v) and not string", keyToken)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(a.garipov): Refactor this into a separate
|
|
||||||
// function à la decodeResultRuleKey if we keep this
|
|
||||||
// code for a longer time than planned.
|
|
||||||
switch key {
|
|
||||||
case "RCode":
|
|
||||||
vToken, err := dec.Token()
|
|
||||||
if err != nil {
|
|
||||||
if err != io.EOF {
|
|
||||||
log.Debug("decodeResultDNSRewriteResult err: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if ent.Result.DNSRewriteResult == nil {
|
|
||||||
ent.Result.DNSRewriteResult = &dnsfilter.DNSRewriteResult{}
|
|
||||||
}
|
|
||||||
|
|
||||||
if n, ok := vToken.(json.Number); ok {
|
|
||||||
rcode64, _ := n.Int64()
|
|
||||||
ent.Result.DNSRewriteResult.RCode = rules.RCode(rcode64)
|
|
||||||
}
|
|
||||||
|
|
||||||
continue
|
|
||||||
case "Response":
|
|
||||||
if ent.Result.DNSRewriteResult == nil {
|
|
||||||
ent.Result.DNSRewriteResult = &dnsfilter.DNSRewriteResult{}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ent.Result.DNSRewriteResult.Response == nil {
|
|
||||||
ent.Result.DNSRewriteResult.Response = dnsfilter.DNSRewriteResultResponse{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(a.garipov): I give up. This whole file
|
|
||||||
// is a mess. Luckily, we can assume that this
|
|
||||||
// field is relatively rare and just use the
|
|
||||||
// normal decoding and correct the values.
|
|
||||||
err = dec.Decode(&ent.Result.DNSRewriteResult.Response)
|
|
||||||
if err != nil {
|
|
||||||
log.Debug("decodeResultDNSRewriteResult response err: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for rrType, rrValues := range ent.Result.DNSRewriteResult.Response {
|
|
||||||
switch rrType {
|
|
||||||
case dns.TypeA, dns.TypeAAAA:
|
|
||||||
for i, v := range rrValues {
|
|
||||||
s, _ := v.(string)
|
|
||||||
rrValues[i] = net.ParseIP(s)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
// Go on.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
continue
|
|
||||||
default:
|
|
||||||
// Go on.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func decodeResult(dec *json.Decoder, ent *logEntry) {
|
|
||||||
for {
|
|
||||||
keyToken, err := dec.Token()
|
|
||||||
if err != nil {
|
|
||||||
if err != io.EOF {
|
|
||||||
log.Debug("decodeResult err: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if d, ok := keyToken.(json.Delim); ok {
|
|
||||||
if d == '}' {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
key, ok := keyToken.(string)
|
|
||||||
if !ok {
|
|
||||||
log.Debug("decodeResult: keyToken is %T (%[1]v) and not string", keyToken)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch key {
|
|
||||||
case "ReverseHosts":
|
|
||||||
decodeResultReverseHosts(dec, ent)
|
|
||||||
|
|
||||||
continue
|
|
||||||
case "IPList":
|
|
||||||
decodeResultIPList(dec, ent)
|
|
||||||
|
|
||||||
continue
|
|
||||||
case "Rules":
|
|
||||||
decodeResultRules(dec, ent)
|
|
||||||
|
|
||||||
continue
|
|
||||||
case "DNSRewriteResult":
|
|
||||||
decodeResultDNSRewriteResult(dec, ent)
|
|
||||||
|
|
||||||
continue
|
|
||||||
default:
|
|
||||||
// Go on.
|
|
||||||
}
|
|
||||||
|
|
||||||
handler, ok := resultHandlers[key]
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
val, err := dec.Token()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = handler(val, ent); err != nil {
|
|
||||||
log.Debug("decodeResult handler err: %s", err)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func decodeLogEntry(ent *logEntry, str string) {
|
func decodeLogEntry(ent *logEntry, str string) {
|
||||||
@@ -523,27 +200,18 @@ func decodeLogEntry(ent *logEntry, str string) {
|
|||||||
if err != io.EOF {
|
if err != io.EOF {
|
||||||
log.Debug("decodeLogEntry err: %s", err)
|
log.Debug("decodeLogEntry err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := keyToken.(json.Delim); ok {
|
if _, ok := keyToken.(json.Delim); ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
key, ok := keyToken.(string)
|
key, ok := keyToken.(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
log.Debug("decodeLogEntry: keyToken is %T (%[1]v) and not string", keyToken)
|
log.Debug("decodeLogEntry: keyToken is %T and not string", keyToken)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if key == "Result" {
|
|
||||||
decodeResult(dec, ent)
|
|
||||||
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
handler, ok := logEntryHandlers[key]
|
handler, ok := logEntryHandlers[key]
|
||||||
if !ok {
|
if !ok {
|
||||||
continue
|
continue
|
||||||
@@ -555,8 +223,7 @@ func decodeLogEntry(ent *logEntry, str string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err = handler(val, ent); err != nil {
|
if err = handler(val, ent); err != nil {
|
||||||
log.Debug("decodeLogEntry handler err: %s", err)
|
log.Debug("decodeLogEntry err: %s", err)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,181 +2,107 @@ package querylog
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/base64"
|
|
||||||
"net"
|
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/dnsfilter"
|
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/testutil"
|
"github.com/AdguardTeam/AdGuardHome/internal/testutil"
|
||||||
"github.com/AdguardTeam/golibs/log"
|
"github.com/AdguardTeam/golibs/log"
|
||||||
"github.com/AdguardTeam/urlfilter/rules"
|
|
||||||
"github.com/miekg/dns"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestDecodeLogEntry(t *testing.T) {
|
func TestDecode_decodeQueryLog(t *testing.T) {
|
||||||
logOutput := &bytes.Buffer{}
|
logOutput := &bytes.Buffer{}
|
||||||
|
|
||||||
testutil.ReplaceLogWriter(t, logOutput)
|
testutil.ReplaceLogWriter(t, logOutput)
|
||||||
testutil.ReplaceLogLevel(t, log.DEBUG)
|
testutil.ReplaceLogLevel(t, log.DEBUG)
|
||||||
|
|
||||||
t.Run("success", func(t *testing.T) {
|
|
||||||
const ansStr = `Qz+BgAABAAEAAAAAAmFuBnlhbmRleAJydQAAAQABwAwAAQABAAAACgAEAAAAAA==`
|
|
||||||
const data = `{"IP":"127.0.0.1",` +
|
|
||||||
`"T":"2020-11-25T18:55:56.519796+03:00",` +
|
|
||||||
`"QH":"an.yandex.ru",` +
|
|
||||||
`"QT":"A",` +
|
|
||||||
`"QC":"IN",` +
|
|
||||||
`"CP":"",` +
|
|
||||||
`"Answer":"` + ansStr + `",` +
|
|
||||||
`"Result":{` +
|
|
||||||
`"IsFiltered":true,` +
|
|
||||||
`"Reason":3,` +
|
|
||||||
`"ReverseHosts":["example.net"],` +
|
|
||||||
`"IPList":["127.0.0.2"],` +
|
|
||||||
`"Rules":[{"FilterListID":42,"Text":"||an.yandex.ru","IP":"127.0.0.2"},` +
|
|
||||||
`{"FilterListID":43,"Text":"||an2.yandex.ru","IP":"127.0.0.3"}],` +
|
|
||||||
`"CanonName":"example.com",` +
|
|
||||||
`"ServiceName":"example.org",` +
|
|
||||||
`"DNSRewriteResult":{"RCode":0,"Response":{"1":["127.0.0.2"]}}},` +
|
|
||||||
`"Elapsed":837429}`
|
|
||||||
|
|
||||||
ans, err := base64.StdEncoding.DecodeString(ansStr)
|
|
||||||
assert.Nil(t, err)
|
|
||||||
|
|
||||||
want := &logEntry{
|
|
||||||
IP: "127.0.0.1",
|
|
||||||
Time: time.Date(2020, 11, 25, 15, 55, 56, 519796000, time.UTC),
|
|
||||||
QHost: "an.yandex.ru",
|
|
||||||
QType: "A",
|
|
||||||
QClass: "IN",
|
|
||||||
ClientProto: "",
|
|
||||||
Answer: ans,
|
|
||||||
Result: dnsfilter.Result{
|
|
||||||
IsFiltered: true,
|
|
||||||
Reason: dnsfilter.FilteredBlockList,
|
|
||||||
ReverseHosts: []string{"example.net"},
|
|
||||||
IPList: []net.IP{net.IPv4(127, 0, 0, 2)},
|
|
||||||
Rules: []*dnsfilter.ResultRule{{
|
|
||||||
FilterListID: 42,
|
|
||||||
Text: "||an.yandex.ru",
|
|
||||||
IP: net.IPv4(127, 0, 0, 2),
|
|
||||||
}, {
|
|
||||||
FilterListID: 43,
|
|
||||||
Text: "||an2.yandex.ru",
|
|
||||||
IP: net.IPv4(127, 0, 0, 3),
|
|
||||||
}},
|
|
||||||
CanonName: "example.com",
|
|
||||||
ServiceName: "example.org",
|
|
||||||
DNSRewriteResult: &dnsfilter.DNSRewriteResult{
|
|
||||||
RCode: dns.RcodeSuccess,
|
|
||||||
Response: dnsfilter.DNSRewriteResultResponse{
|
|
||||||
dns.TypeA: []rules.RRValue{net.IPv4(127, 0, 0, 2)},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Elapsed: 837429,
|
|
||||||
}
|
|
||||||
|
|
||||||
got := &logEntry{}
|
|
||||||
decodeLogEntry(got, data)
|
|
||||||
|
|
||||||
s := logOutput.String()
|
|
||||||
assert.Equal(t, "", s)
|
|
||||||
|
|
||||||
// Correct for time zones.
|
|
||||||
got.Time = got.Time.UTC()
|
|
||||||
assert.Equal(t, want, got)
|
|
||||||
})
|
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
log string
|
log string
|
||||||
want string
|
want string
|
||||||
}{{
|
}{{
|
||||||
name: "all_right_old_rule",
|
name: "back_compatibility_all_right",
|
||||||
log: `{"IP":"127.0.0.1","T":"2020-11-25T18:55:56.519796+03:00","QH":"an.yandex.ru","QT":"A","QC":"IN","CP":"","Answer":"Qz+BgAABAAEAAAAAAmFuBnlhbmRleAJydQAAAQABwAwAAQABAAAACgAEAAAAAA==","Result":{"IsFiltered":true,"Reason":3,"Rule":"||an.yandex.","FilterID":1,"ReverseHosts":["example.com"],"IPList":["127.0.0.1"]},"Elapsed":837429}`,
|
log: `{"Question":"ULgBAAABAAAAAAAAC2FkZ3VhcmR0ZWFtBmdpdGh1YgJpbwAAHAAB","Answer":"ULiBgAABAAAAAQAAC2FkZ3VhcmR0ZWFtBmdpdGh1YgJpbwAAHAABwBgABgABAAADQgBLB25zLTE2MjIJYXdzZG5zLTEwAmNvAnVrABFhd3NkbnMtaG9zdG1hc3RlcgZhbWF6b24DY29tAAAAAAEAABwgAAADhAASdQAAAVGA","Result":{},"Time":"2020-11-13T12:41:25.970861+03:00","Elapsed":244066501,"IP":"127.0.0.1","Upstream":"https://1.1.1.1:443/dns-query"}`,
|
||||||
want: "",
|
want: "default",
|
||||||
}, {
|
}, {
|
||||||
name: "bad_filter_id_old_rule",
|
name: "back_compatibility_bad_msg",
|
||||||
log: `{"IP":"127.0.0.1","T":"2020-11-25T18:55:56.519796+03:00","QH":"an.yandex.ru","QT":"A","QC":"IN","CP":"","Answer":"Qz+BgAABAAEAAAAAAmFuBnlhbmRleAJydQAAAQABwAwAAQABAAAACgAEAAAAAA==","Result":{"IsFiltered":true,"Reason":3,"FilterID":1.5},"Elapsed":837429}`,
|
log: `{"Question":"","Answer":"ULiBgAABAAAAAQAAC2FkZ3VhcmR0ZWFtBmdpdGh1YgJpbwAAHAABwBgABgABAAADQgBLB25zLTE2MjIJYXdzZG5zLTEwAmNvAnVrABFhd3NkbnMtaG9zdG1hc3RlcgZhbWF6b24DY29tAAAAAAEAABwgAAADhAASdQAAAVGA","Result":{},"Time":"2020-11-13T12:41:25.970861+03:00","Elapsed":244066501,"IP":"127.0.0.1","Upstream":"https://1.1.1.1:443/dns-query"}`,
|
||||||
want: "decodeResult handler err: strconv.ParseInt: parsing \"1.5\": invalid syntax\n",
|
want: "decodeLogEntry err: dns: overflow unpacking uint16\n",
|
||||||
|
}, {
|
||||||
|
name: "back_compatibility_bad_decoding",
|
||||||
|
log: `{"Question":"LgBAAABAAAAAAAAC2FkZ3VhcmR0ZWFtBmdpdGh1YgJpbwAAHAAB","Answer":"ULiBgAABAAAAAQAAC2FkZ3VhcmR0ZWFtBmdpdGh1YgJpbwAAHAABwBgABgABAAADQgBLB25zLTE2MjIJYXdzZG5zLTEwAmNvAnVrABFhd3NkbnMtaG9zdG1hc3RlcgZhbWF6b24DY29tAAAAAAEAABwgAAADhAASdQAAAVGA","Result":{},"Time":"2020-11-13T12:41:25.970861+03:00","Elapsed":244066501,"IP":"127.0.0.1","Upstream":"https://1.1.1.1:443/dns-query"}`,
|
||||||
|
want: "decodeLogEntry err: illegal base64 data at input byte 48\n",
|
||||||
|
}, {
|
||||||
|
name: "modern_all_right",
|
||||||
|
log: `{"IP":"127.0.0.1","T":"2020-11-25T18:55:56.519796+03:00","QH":"an.yandex.ru","QT":"A","QC":"IN","CP":"","Answer":"Qz+BgAABAAEAAAAAAmFuBnlhbmRleAJydQAAAQABwAwAAQABAAAACgAEAAAAAA==","Result":{"IsFiltered":true,"Reason":3,"Rule":"||an.yandex.","FilterID":1},"Elapsed":837429}`,
|
||||||
|
want: "default",
|
||||||
|
}, {
|
||||||
|
name: "bad_filter_id",
|
||||||
|
log: `{"IP":"127.0.0.1","T":"2020-11-25T18:55:56.519796+03:00","QH":"an.yandex.ru","QT":"A","QC":"IN","CP":"","Answer":"Qz+BgAABAAEAAAAAAmFuBnlhbmRleAJydQAAAQABwAwAAQABAAAACgAEAAAAAA==","Result":{"IsFiltered":true,"Reason":3,"Rule":"||an.yandex.","FilterID":1.5},"Elapsed":837429}`,
|
||||||
|
want: "decodeLogEntry err: strconv.ParseInt: parsing \"1.5\": invalid syntax\n",
|
||||||
}, {
|
}, {
|
||||||
name: "bad_is_filtered",
|
name: "bad_is_filtered",
|
||||||
log: `{"IP":"127.0.0.1","T":"2020-11-25T18:55:56.519796+03:00","QH":"an.yandex.ru","QT":"A","QC":"IN","CP":"","Answer":"Qz+BgAABAAEAAAAAAmFuBnlhbmRleAJydQAAAQABwAwAAQABAAAACgAEAAAAAA==","Result":{"IsFiltered":trooe,"Reason":3},"Elapsed":837429}`,
|
log: `{"IP":"127.0.0.1","T":"2020-11-25T18:55:56.519796+03:00","QH":"an.yandex.ru","QT":"A","QC":"IN","CP":"","Answer":"Qz+BgAABAAEAAAAAAmFuBnlhbmRleAJydQAAAQABwAwAAQABAAAACgAEAAAAAA==","Result":{"IsFiltered":trooe,"Reason":3,"Rule":"||an.yandex.","FilterID":1},"Elapsed":837429}`,
|
||||||
want: "decodeLogEntry err: invalid character 'o' in literal true (expecting 'u')\n",
|
want: "default",
|
||||||
}, {
|
}, {
|
||||||
name: "bad_elapsed",
|
name: "bad_elapsed",
|
||||||
log: `{"IP":"127.0.0.1","T":"2020-11-25T18:55:56.519796+03:00","QH":"an.yandex.ru","QT":"A","QC":"IN","CP":"","Answer":"Qz+BgAABAAEAAAAAAmFuBnlhbmRleAJydQAAAQABwAwAAQABAAAACgAEAAAAAA==","Result":{"IsFiltered":true,"Reason":3},"Elapsed":-1}`,
|
log: `{"IP":"127.0.0.1","T":"2020-11-25T18:55:56.519796+03:00","QH":"an.yandex.ru","QT":"A","QC":"IN","CP":"","Answer":"Qz+BgAABAAEAAAAAAmFuBnlhbmRleAJydQAAAQABwAwAAQABAAAACgAEAAAAAA==","Result":{"IsFiltered":true,"Reason":3,"Rule":"||an.yandex.","FilterID":1},"Elapsed":-1}`,
|
||||||
want: "",
|
want: "default",
|
||||||
}, {
|
}, {
|
||||||
name: "bad_ip",
|
name: "bad_ip",
|
||||||
log: `{"IP":127001,"T":"2020-11-25T18:55:56.519796+03:00","QH":"an.yandex.ru","QT":"A","QC":"IN","CP":"","Answer":"Qz+BgAABAAEAAAAAAmFuBnlhbmRleAJydQAAAQABwAwAAQABAAAACgAEAAAAAA==","Result":{"IsFiltered":true,"Reason":3},"Elapsed":837429}`,
|
log: `{"IP":127001,"T":"2020-11-25T18:55:56.519796+03:00","QH":"an.yandex.ru","QT":"A","QC":"IN","CP":"","Answer":"Qz+BgAABAAEAAAAAAmFuBnlhbmRleAJydQAAAQABwAwAAQABAAAACgAEAAAAAA==","Result":{"IsFiltered":true,"Reason":3,"Rule":"||an.yandex.","FilterID":1},"Elapsed":837429}`,
|
||||||
want: "",
|
want: "default",
|
||||||
}, {
|
}, {
|
||||||
name: "bad_time",
|
name: "bad_time",
|
||||||
log: `{"IP":"127.0.0.1","T":"12/09/1998T15:00:00.000000+05:00","QH":"an.yandex.ru","QT":"A","QC":"IN","CP":"","Answer":"Qz+BgAABAAEAAAAAAmFuBnlhbmRleAJydQAAAQABwAwAAQABAAAACgAEAAAAAA==","Result":{"IsFiltered":true,"Reason":3},"Elapsed":837429}`,
|
log: `{"IP":"127.0.0.1","T":"12/09/1998T15:00:00.000000+05:00","QH":"an.yandex.ru","QT":"A","QC":"IN","CP":"","Answer":"Qz+BgAABAAEAAAAAAmFuBnlhbmRleAJydQAAAQABwAwAAQABAAAACgAEAAAAAA==","Result":{"IsFiltered":true,"Reason":3,"Rule":"||an.yandex.","FilterID":1},"Elapsed":837429}`,
|
||||||
want: "decodeLogEntry handler err: parsing time \"12/09/1998T15:00:00.000000+05:00\" as \"2006-01-02T15:04:05Z07:00\": cannot parse \"9/1998T15:00:00.000000+05:00\" as \"2006\"\n",
|
want: "decodeLogEntry err: parsing time \"12/09/1998T15:00:00.000000+05:00\" as \"2006-01-02T15:04:05Z07:00\": cannot parse \"9/1998T15:00:00.000000+05:00\" as \"2006\"\n",
|
||||||
}, {
|
}, {
|
||||||
name: "bad_host",
|
name: "bad_host",
|
||||||
log: `{"IP":"127.0.0.1","T":"2020-11-25T18:55:56.519796+03:00","QH":6,"QT":"A","QC":"IN","CP":"","Answer":"Qz+BgAABAAEAAAAAAmFuBnlhbmRleAJydQAAAQABwAwAAQABAAAACgAEAAAAAA==","Result":{"IsFiltered":true,"Reason":3},"Elapsed":837429}`,
|
log: `{"IP":"127.0.0.1","T":"2020-11-25T18:55:56.519796+03:00","QH":6,"QT":"A","QC":"IN","CP":"","Answer":"Qz+BgAABAAEAAAAAAmFuBnlhbmRleAJydQAAAQABwAwAAQABAAAACgAEAAAAAA==","Result":{"IsFiltered":true,"Reason":3,"Rule":"||an.yandex.","FilterID":1},"Elapsed":837429}`,
|
||||||
want: "",
|
want: "default",
|
||||||
}, {
|
}, {
|
||||||
name: "bad_type",
|
name: "bad_type",
|
||||||
log: `{"IP":"127.0.0.1","T":"2020-11-25T18:55:56.519796+03:00","QH":"an.yandex.ru","QT":true,"QC":"IN","CP":"","Answer":"Qz+BgAABAAEAAAAAAmFuBnlhbmRleAJydQAAAQABwAwAAQABAAAACgAEAAAAAA==","Result":{"IsFiltered":true,"Reason":3},"Elapsed":837429}`,
|
log: `{"IP":"127.0.0.1","T":"2020-11-25T18:55:56.519796+03:00","QH":"an.yandex.ru","QT":true,"QC":"IN","CP":"","Answer":"Qz+BgAABAAEAAAAAAmFuBnlhbmRleAJydQAAAQABwAwAAQABAAAACgAEAAAAAA==","Result":{"IsFiltered":true,"Reason":3,"Rule":"||an.yandex.","FilterID":1},"Elapsed":837429}`,
|
||||||
want: "",
|
want: "default",
|
||||||
}, {
|
}, {
|
||||||
name: "bad_class",
|
name: "bad_class",
|
||||||
log: `{"IP":"127.0.0.1","T":"2020-11-25T18:55:56.519796+03:00","QH":"an.yandex.ru","QT":"A","QC":false,"CP":"","Answer":"Qz+BgAABAAEAAAAAAmFuBnlhbmRleAJydQAAAQABwAwAAQABAAAACgAEAAAAAA==","Result":{"IsFiltered":true,"Reason":3},"Elapsed":837429}`,
|
log: `{"IP":"127.0.0.1","T":"2020-11-25T18:55:56.519796+03:00","QH":"an.yandex.ru","QT":"A","QC":false,"CP":"","Answer":"Qz+BgAABAAEAAAAAAmFuBnlhbmRleAJydQAAAQABwAwAAQABAAAACgAEAAAAAA==","Result":{"IsFiltered":true,"Reason":3,"Rule":"||an.yandex.","FilterID":1},"Elapsed":837429}`,
|
||||||
want: "",
|
want: "default",
|
||||||
}, {
|
}, {
|
||||||
name: "bad_client_proto",
|
name: "bad_client_proto",
|
||||||
log: `{"IP":"127.0.0.1","T":"2020-11-25T18:55:56.519796+03:00","QH":"an.yandex.ru","QT":"A","QC":"IN","CP":8,"Answer":"Qz+BgAABAAEAAAAAAmFuBnlhbmRleAJydQAAAQABwAwAAQABAAAACgAEAAAAAA==","Result":{"IsFiltered":true,"Reason":3},"Elapsed":837429}`,
|
log: `{"IP":"127.0.0.1","T":"2020-11-25T18:55:56.519796+03:00","QH":"an.yandex.ru","QT":"A","QC":"IN","CP":8,"Answer":"Qz+BgAABAAEAAAAAAmFuBnlhbmRleAJydQAAAQABwAwAAQABAAAACgAEAAAAAA==","Result":{"IsFiltered":true,"Reason":3,"Rule":"||an.yandex.","FilterID":1},"Elapsed":837429}`,
|
||||||
want: "",
|
want: "default",
|
||||||
}, {
|
}, {
|
||||||
name: "very_bad_client_proto",
|
name: "very_bad_client_proto",
|
||||||
log: `{"IP":"127.0.0.1","T":"2020-11-25T18:55:56.519796+03:00","QH":"an.yandex.ru","QT":"A","QC":"IN","CP":"dog","Answer":"Qz+BgAABAAEAAAAAAmFuBnlhbmRleAJydQAAAQABwAwAAQABAAAACgAEAAAAAA==","Result":{"IsFiltered":true,"Reason":3},"Elapsed":837429}`,
|
log: `{"IP":"127.0.0.1","T":"2020-11-25T18:55:56.519796+03:00","QH":"an.yandex.ru","QT":"A","QC":"IN","CP":"dog","Answer":"Qz+BgAABAAEAAAAAAmFuBnlhbmRleAJydQAAAQABwAwAAQABAAAACgAEAAAAAA==","Result":{"IsFiltered":true,"Reason":3,"Rule":"||an.yandex.","FilterID":1},"Elapsed":837429}`,
|
||||||
want: "decodeLogEntry handler err: invalid client proto: \"dog\"\n",
|
want: "decodeLogEntry err: invalid client proto: \"dog\"\n",
|
||||||
}, {
|
}, {
|
||||||
name: "bad_answer",
|
name: "bad_answer",
|
||||||
log: `{"IP":"127.0.0.1","T":"2020-11-25T18:55:56.519796+03:00","QH":"an.yandex.ru","QT":"A","QC":"IN","CP":"","Answer":0.9,"Result":{"IsFiltered":true,"Reason":3},"Elapsed":837429}`,
|
log: `{"IP":"127.0.0.1","T":"2020-11-25T18:55:56.519796+03:00","QH":"an.yandex.ru","QT":"A","QC":"IN","CP":"","Answer":0.9,"Result":{"IsFiltered":true,"Reason":3,"Rule":"||an.yandex.","FilterID":1},"Elapsed":837429}`,
|
||||||
want: "",
|
want: "default",
|
||||||
}, {
|
}, {
|
||||||
name: "very_bad_answer",
|
name: "very_bad_answer",
|
||||||
log: `{"IP":"127.0.0.1","T":"2020-11-25T18:55:56.519796+03:00","QH":"an.yandex.ru","QT":"A","QC":"IN","CP":"","Answer":"Qz+BgAABAAEAAAAAAmuBnlhbmRleAJydQAAAQABwAwAAQABAAAACgAEAAAAAA==","Result":{"IsFiltered":true,"Reason":3},"Elapsed":837429}`,
|
log: `{"IP":"127.0.0.1","T":"2020-11-25T18:55:56.519796+03:00","QH":"an.yandex.ru","QT":"A","QC":"IN","CP":"","Answer":"Qz+BgAABAAEAAAAAAmuBnlhbmRleAJydQAAAQABwAwAAQABAAAACgAEAAAAAA==","Result":{"IsFiltered":true,"Reason":3,"Rule":"||an.yandex.","FilterID":1},"Elapsed":837429}`,
|
||||||
want: "decodeLogEntry handler err: illegal base64 data at input byte 61\n",
|
want: "decodeLogEntry err: illegal base64 data at input byte 61\n",
|
||||||
}, {
|
}, {
|
||||||
name: "bad_rule",
|
name: "bad_rule",
|
||||||
log: `{"IP":"127.0.0.1","T":"2020-11-25T18:55:56.519796+03:00","QH":"an.yandex.ru","QT":"A","QC":"IN","CP":"","Answer":"Qz+BgAABAAEAAAAAAmFuBnlhbmRleAJydQAAAQABwAwAAQABAAAACgAEAAAAAA==","Result":{"IsFiltered":true,"Reason":3,"Rule":false},"Elapsed":837429}`,
|
log: `{"IP":"127.0.0.1","T":"2020-11-25T18:55:56.519796+03:00","QH":"an.yandex.ru","QT":"A","QC":"IN","CP":"","Answer":"Qz+BgAABAAEAAAAAAmFuBnlhbmRleAJydQAAAQABwAwAAQABAAAACgAEAAAAAA==","Result":{"IsFiltered":true,"Reason":3,"Rule":false,"FilterID":1},"Elapsed":837429}`,
|
||||||
want: "",
|
want: "default",
|
||||||
}, {
|
}, {
|
||||||
name: "bad_reason",
|
name: "bad_reason",
|
||||||
log: `{"IP":"127.0.0.1","T":"2020-11-25T18:55:56.519796+03:00","QH":"an.yandex.ru","QT":"A","QC":"IN","CP":"","Answer":"Qz+BgAABAAEAAAAAAmFuBnlhbmRleAJydQAAAQABwAwAAQABAAAACgAEAAAAAA==","Result":{"IsFiltered":true,"Reason":true},"Elapsed":837429}`,
|
log: `{"IP":"127.0.0.1","T":"2020-11-25T18:55:56.519796+03:00","QH":"an.yandex.ru","QT":"A","QC":"IN","CP":"","Answer":"Qz+BgAABAAEAAAAAAmFuBnlhbmRleAJydQAAAQABwAwAAQABAAAACgAEAAAAAA==","Result":{"IsFiltered":true,"Reason":true,"Rule":"||an.yandex.","FilterID":1},"Elapsed":837429}`,
|
||||||
want: "",
|
want: "default",
|
||||||
}, {
|
|
||||||
name: "bad_reverse_hosts",
|
|
||||||
log: `{"IP":"127.0.0.1","T":"2020-11-25T18:55:56.519796+03:00","QH":"an.yandex.ru","QT":"A","QC":"IN","CP":"","Answer":"Qz+BgAABAAEAAAAAAmFuBnlhbmRleAJydQAAAQABwAwAAQABAAAACgAEAAAAAA==","Result":{"IsFiltered":true,"Reason":3,"ReverseHosts":[{}]},"Elapsed":837429}`,
|
|
||||||
want: "decodeResultReverseHosts: unexpected delim \"{\"\n",
|
|
||||||
}, {
|
|
||||||
name: "bad_ip_list",
|
|
||||||
log: `{"IP":"127.0.0.1","T":"2020-11-25T18:55:56.519796+03:00","QH":"an.yandex.ru","QT":"A","QC":"IN","CP":"","Answer":"Qz+BgAABAAEAAAAAAmFuBnlhbmRleAJydQAAAQABwAwAAQABAAAACgAEAAAAAA==","Result":{"IsFiltered":true,"Reason":3,"ReverseHosts":["example.net"],"IPList":[{}]},"Elapsed":837429}`,
|
|
||||||
want: "decodeResultIPList: unexpected delim \"{\"\n",
|
|
||||||
}}
|
}}
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
_, err := logOutput.Write([]byte("default"))
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
l := &logEntry{}
|
l := &logEntry{}
|
||||||
decodeLogEntry(l, tc.log)
|
decodeLogEntry(l, tc.log)
|
||||||
|
|
||||||
s := logOutput.String()
|
assert.True(t, strings.HasSuffix(logOutput.String(), tc.want), logOutput.String())
|
||||||
if tc.want == "" {
|
|
||||||
assert.Equal(t, "", s)
|
|
||||||
} else {
|
|
||||||
assert.True(t, strings.HasSuffix(s, tc.want),
|
|
||||||
"got %q", s)
|
|
||||||
}
|
|
||||||
|
|
||||||
logOutput.Reset()
|
logOutput.Reset()
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -6,13 +6,10 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/dnsfilter"
|
|
||||||
"github.com/AdguardTeam/golibs/log"
|
"github.com/AdguardTeam/golibs/log"
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO(a.garipov): Use a proper structured approach here.
|
|
||||||
|
|
||||||
// Get Client IP address
|
// Get Client IP address
|
||||||
func (l *queryLog) getClientIP(clientIP string) string {
|
func (l *queryLog) getClientIP(clientIP string) string {
|
||||||
if l.conf.AnonymizeClientIP {
|
if l.conf.AnonymizeClientIP {
|
||||||
@@ -32,12 +29,10 @@ func (l *queryLog) getClientIP(clientIP string) string {
|
|||||||
return clientIP
|
return clientIP
|
||||||
}
|
}
|
||||||
|
|
||||||
// jobject is a JSON object alias.
|
// entriesToJSON - converts log entries to JSON
|
||||||
type jobject = map[string]interface{}
|
func (l *queryLog) entriesToJSON(entries []*logEntry, oldest time.Time) map[string]interface{} {
|
||||||
|
// init the response object
|
||||||
// entriesToJSON converts query log entries to JSON.
|
data := []map[string]interface{}{}
|
||||||
func (l *queryLog) entriesToJSON(entries []*logEntry, oldest time.Time) (res jobject) {
|
|
||||||
data := []jobject{}
|
|
||||||
|
|
||||||
// the elements order is already reversed (from newer to older)
|
// the elements order is already reversed (from newer to older)
|
||||||
for i := 0; i < len(entries); i++ {
|
for i := 0; i < len(entries); i++ {
|
||||||
@@ -46,18 +41,17 @@ func (l *queryLog) entriesToJSON(entries []*logEntry, oldest time.Time) (res job
|
|||||||
data = append(data, jsonEntry)
|
data = append(data, jsonEntry)
|
||||||
}
|
}
|
||||||
|
|
||||||
res = jobject{
|
result := map[string]interface{}{}
|
||||||
"data": data,
|
result["oldest"] = ""
|
||||||
"oldest": "",
|
|
||||||
}
|
|
||||||
if !oldest.IsZero() {
|
if !oldest.IsZero() {
|
||||||
res["oldest"] = oldest.Format(time.RFC3339Nano)
|
result["oldest"] = oldest.Format(time.RFC3339Nano)
|
||||||
}
|
}
|
||||||
|
result["data"] = data
|
||||||
|
|
||||||
return res
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *queryLog) logEntryToJSONEntry(entry *logEntry) (jsonEntry jobject) {
|
func (l *queryLog) logEntryToJSONEntry(entry *logEntry) map[string]interface{} {
|
||||||
var msg *dns.Msg
|
var msg *dns.Msg
|
||||||
|
|
||||||
if len(entry.Answer) > 0 {
|
if len(entry.Answer) > 0 {
|
||||||
@@ -68,18 +62,17 @@ func (l *queryLog) logEntryToJSONEntry(entry *logEntry) (jsonEntry jobject) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
jsonEntry = jobject{
|
jsonEntry := map[string]interface{}{
|
||||||
"reason": entry.Result.Reason.String(),
|
"reason": entry.Result.Reason.String(),
|
||||||
"elapsedMs": strconv.FormatFloat(entry.Elapsed.Seconds()*1000, 'f', -1, 64),
|
"elapsedMs": strconv.FormatFloat(entry.Elapsed.Seconds()*1000, 'f', -1, 64),
|
||||||
"time": entry.Time.Format(time.RFC3339Nano),
|
"time": entry.Time.Format(time.RFC3339Nano),
|
||||||
"client": l.getClientIP(entry.IP),
|
"client": l.getClientIP(entry.IP),
|
||||||
"client_proto": entry.ClientProto,
|
"client_proto": entry.ClientProto,
|
||||||
"upstream": entry.Upstream,
|
}
|
||||||
"question": jobject{
|
jsonEntry["question"] = map[string]interface{}{
|
||||||
"host": entry.QHost,
|
"host": entry.QHost,
|
||||||
"type": entry.QType,
|
"type": entry.QType,
|
||||||
"class": entry.QClass,
|
"class": entry.QClass,
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if msg != nil {
|
if msg != nil {
|
||||||
@@ -90,15 +83,12 @@ func (l *queryLog) logEntryToJSONEntry(entry *logEntry) (jsonEntry jobject) {
|
|||||||
if opt != nil {
|
if opt != nil {
|
||||||
dnssecOk = opt.Do()
|
dnssecOk = opt.Do()
|
||||||
}
|
}
|
||||||
|
|
||||||
jsonEntry["answer_dnssec"] = dnssecOk
|
jsonEntry["answer_dnssec"] = dnssecOk
|
||||||
}
|
}
|
||||||
|
|
||||||
jsonEntry["rules"] = resultRulesToJSONRules(entry.Result.Rules)
|
if len(entry.Result.Rule) > 0 {
|
||||||
|
jsonEntry["rule"] = entry.Result.Rule
|
||||||
if len(entry.Result.Rules) > 0 && len(entry.Result.Rules[0].Text) > 0 {
|
jsonEntry["filterId"] = entry.Result.FilterID
|
||||||
jsonEntry["rule"] = entry.Result.Rules[0].Text
|
|
||||||
jsonEntry["filterId"] = entry.Result.Rules[0].FilterListID
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(entry.Result.ServiceName) != 0 {
|
if len(entry.Result.ServiceName) != 0 {
|
||||||
@@ -123,30 +113,20 @@ func (l *queryLog) logEntryToJSONEntry(entry *logEntry) (jsonEntry jobject) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
jsonEntry["upstream"] = entry.Upstream
|
||||||
|
|
||||||
return jsonEntry
|
return jsonEntry
|
||||||
}
|
}
|
||||||
|
|
||||||
func resultRulesToJSONRules(rules []*dnsfilter.ResultRule) (jsonRules []jobject) {
|
func answerToMap(a *dns.Msg) []map[string]interface{} {
|
||||||
jsonRules = make([]jobject, len(rules))
|
|
||||||
for i, r := range rules {
|
|
||||||
jsonRules[i] = jobject{
|
|
||||||
"filter_list_id": r.FilterListID,
|
|
||||||
"text": r.Text,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return jsonRules
|
|
||||||
}
|
|
||||||
|
|
||||||
func answerToMap(a *dns.Msg) (answers []jobject) {
|
|
||||||
if a == nil || len(a.Answer) == 0 {
|
if a == nil || len(a.Answer) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
answers = []jobject{}
|
answers := []map[string]interface{}{}
|
||||||
for _, k := range a.Answer {
|
for _, k := range a.Answer {
|
||||||
header := k.Header()
|
header := k.Header()
|
||||||
answer := jobject{
|
answer := map[string]interface{}{
|
||||||
"type": dns.TypeToString[header.Rrtype],
|
"type": dns.TypeToString[header.Rrtype],
|
||||||
"ttl": header.Ttl,
|
"ttl": header.Ttl,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -236,12 +236,10 @@ func addEntry(l *queryLog, host, answerStr, client string) {
|
|||||||
a.Answer = append(a.Answer, answer)
|
a.Answer = append(a.Answer, answer)
|
||||||
res := dnsfilter.Result{
|
res := dnsfilter.Result{
|
||||||
IsFiltered: true,
|
IsFiltered: true,
|
||||||
|
Rule: "SomeRule",
|
||||||
Reason: dnsfilter.ReasonRewrite,
|
Reason: dnsfilter.ReasonRewrite,
|
||||||
ServiceName: "SomeService",
|
ServiceName: "SomeService",
|
||||||
Rules: []*dnsfilter.ResultRule{{
|
FilterID: 1,
|
||||||
FilterListID: 1,
|
|
||||||
Text: "SomeRule",
|
|
||||||
}},
|
|
||||||
}
|
}
|
||||||
params := AddParams{
|
params := AddParams{
|
||||||
Question: &q,
|
Question: &q,
|
||||||
|
|||||||
@@ -115,14 +115,14 @@ func (c *searchCriteria) ctFilteringStatusCase(res dnsfilter.Result) bool {
|
|||||||
case filteringStatusFiltered:
|
case filteringStatusFiltered:
|
||||||
return res.IsFiltered ||
|
return res.IsFiltered ||
|
||||||
res.Reason.In(
|
res.Reason.In(
|
||||||
dnsfilter.NotFilteredAllowList,
|
dnsfilter.NotFilteredWhiteList,
|
||||||
dnsfilter.ReasonRewrite,
|
dnsfilter.ReasonRewrite,
|
||||||
dnsfilter.RewriteAutoHosts,
|
dnsfilter.RewriteEtcHosts,
|
||||||
)
|
)
|
||||||
|
|
||||||
case filteringStatusBlocked:
|
case filteringStatusBlocked:
|
||||||
return res.IsFiltered &&
|
return res.IsFiltered &&
|
||||||
res.Reason.In(dnsfilter.FilteredBlockList, dnsfilter.FilteredBlockedService)
|
res.Reason.In(dnsfilter.FilteredBlackList, dnsfilter.FilteredBlockedService)
|
||||||
|
|
||||||
case filteringStatusBlockedService:
|
case filteringStatusBlockedService:
|
||||||
return res.IsFiltered && res.Reason == dnsfilter.FilteredBlockedService
|
return res.IsFiltered && res.Reason == dnsfilter.FilteredBlockedService
|
||||||
@@ -134,19 +134,19 @@ func (c *searchCriteria) ctFilteringStatusCase(res dnsfilter.Result) bool {
|
|||||||
return res.IsFiltered && res.Reason == dnsfilter.FilteredSafeBrowsing
|
return res.IsFiltered && res.Reason == dnsfilter.FilteredSafeBrowsing
|
||||||
|
|
||||||
case filteringStatusWhitelisted:
|
case filteringStatusWhitelisted:
|
||||||
return res.Reason == dnsfilter.NotFilteredAllowList
|
return res.Reason == dnsfilter.NotFilteredWhiteList
|
||||||
|
|
||||||
case filteringStatusRewritten:
|
case filteringStatusRewritten:
|
||||||
return res.Reason.In(dnsfilter.ReasonRewrite, dnsfilter.RewriteAutoHosts)
|
return res.Reason.In(dnsfilter.ReasonRewrite, dnsfilter.RewriteEtcHosts)
|
||||||
|
|
||||||
case filteringStatusSafeSearch:
|
case filteringStatusSafeSearch:
|
||||||
return res.IsFiltered && res.Reason == dnsfilter.FilteredSafeSearch
|
return res.IsFiltered && res.Reason == dnsfilter.FilteredSafeSearch
|
||||||
|
|
||||||
case filteringStatusProcessed:
|
case filteringStatusProcessed:
|
||||||
return !res.Reason.In(
|
return !res.Reason.In(
|
||||||
dnsfilter.FilteredBlockList,
|
dnsfilter.FilteredBlackList,
|
||||||
dnsfilter.FilteredBlockedService,
|
dnsfilter.FilteredBlockedService,
|
||||||
dnsfilter.NotFilteredAllowList,
|
dnsfilter.NotFilteredWhiteList,
|
||||||
)
|
)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ require (
|
|||||||
golang.org/x/mod v0.4.0 // indirect
|
golang.org/x/mod v0.4.0 // indirect
|
||||||
golang.org/x/tools v0.0.0-20201208062317-e652b2f42cc7
|
golang.org/x/tools v0.0.0-20201208062317-e652b2f42cc7
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
honnef.co/go/tools v0.1.0
|
honnef.co/go/tools v0.0.1-2020.1.6
|
||||||
mvdan.cc/gofumpt v0.0.0-20201129102820-5c11c50e9475
|
mvdan.cc/gofumpt v0.0.0-20201129102820-5c11c50e9475
|
||||||
mvdan.cc/unparam v0.0.0-20200501210554-b37ab49443f7
|
mvdan.cc/unparam v0.0.0-20200501210554-b37ab49443f7
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -123,7 +123,6 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
|
|||||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
golang.org/x/tools v0.0.0-20200410194907-79a7a3126eef/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
golang.org/x/tools v0.0.0-20200410194907-79a7a3126eef/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
golang.org/x/tools v0.0.0-20200426102838-f3a5411a4c3b/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
golang.org/x/tools v0.0.0-20200426102838-f3a5411a4c3b/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
golang.org/x/tools v0.0.0-20200609164405-eb789aa7ce50/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
|
||||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
golang.org/x/tools v0.0.0-20200710042808-f1c4188a97a1/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
golang.org/x/tools v0.0.0-20200710042808-f1c4188a97a1/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||||
golang.org/x/tools v0.0.0-20201007032633-0806396f153e/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
|
golang.org/x/tools v0.0.0-20201007032633-0806396f153e/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
|
||||||
@@ -159,8 +158,6 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
|||||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
honnef.co/go/tools v0.0.1-2020.1.6 h1:W18jzjh8mfPez+AwGLxmOImucz/IFjpNlrKVnaj2YVc=
|
honnef.co/go/tools v0.0.1-2020.1.6 h1:W18jzjh8mfPez+AwGLxmOImucz/IFjpNlrKVnaj2YVc=
|
||||||
honnef.co/go/tools v0.0.1-2020.1.6/go.mod h1:pyyisuGw24ruLjrr1ddx39WE0y9OooInRzEYLhQB2YY=
|
honnef.co/go/tools v0.0.1-2020.1.6/go.mod h1:pyyisuGw24ruLjrr1ddx39WE0y9OooInRzEYLhQB2YY=
|
||||||
honnef.co/go/tools v0.1.0 h1:AWNL1W1i7f0wNZ8VwOKNJ0sliKvOF/adn0EHenfUh+c=
|
|
||||||
honnef.co/go/tools v0.1.0/go.mod h1:XtegFAyX/PfluP4921rXU5IkjkqBCDnUq4W8VCIoKvM=
|
|
||||||
mvdan.cc/gofumpt v0.0.0-20201129102820-5c11c50e9475 h1:5ZmJGYyuTlhdlIpRxSFhdJqkXQweXETFCEaLhRAX3e8=
|
mvdan.cc/gofumpt v0.0.0-20201129102820-5c11c50e9475 h1:5ZmJGYyuTlhdlIpRxSFhdJqkXQweXETFCEaLhRAX3e8=
|
||||||
mvdan.cc/gofumpt v0.0.0-20201129102820-5c11c50e9475/go.mod h1:E4LOcu9JQEtnYXtB1Y51drqh2Qr2Ngk9J3YrRCwcbd0=
|
mvdan.cc/gofumpt v0.0.0-20201129102820-5c11c50e9475/go.mod h1:E4LOcu9JQEtnYXtB1Y51drqh2Qr2Ngk9J3YrRCwcbd0=
|
||||||
mvdan.cc/unparam v0.0.0-20200501210554-b37ab49443f7 h1:kAREL6MPwpsk1/PQPFD3Eg7WAQR5mPTWZJaBiG5LDbY=
|
mvdan.cc/unparam v0.0.0-20200501210554-b37ab49443f7 h1:kAREL6MPwpsk1/PQPFD3Eg7WAQR5mPTWZJaBiG5LDbY=
|
||||||
|
|||||||
@@ -1,67 +1,5 @@
|
|||||||
# AdGuard Home API Change Log
|
# AdGuard Home API Change Log
|
||||||
|
|
||||||
<!-- TODO(a.garipov): Reformat in accordance with the KeepAChangelog spec. -->
|
|
||||||
|
|
||||||
## v0.105: API changes
|
|
||||||
|
|
||||||
### New `"reason"` in `GET /filtering/check_host` and `GET /querylog`
|
|
||||||
|
|
||||||
* The new `DNSRewriteRule` reason is added to `GET /filtering/check_host` and
|
|
||||||
`GET /querylog`.
|
|
||||||
|
|
||||||
* Also, the reason which was incorrectly documented as `"ReasonRewrite"` is now
|
|
||||||
correctly documented as `"Rewrite"`, and the previously undocumented
|
|
||||||
`"RewriteEtcHosts"` is now documented as well.
|
|
||||||
|
|
||||||
### Multiple matched rules in `GET /filtering/check_host` and `GET /querylog`
|
|
||||||
|
|
||||||
* The properties `rule` and `filter_id` are now deprecated. API users should
|
|
||||||
inspect the newly-added `rules` object array instead. For most rules, it's
|
|
||||||
either empty or contains one object, which contains the same things as the old
|
|
||||||
two properties did, but under more correct names:
|
|
||||||
|
|
||||||
```js
|
|
||||||
{
|
|
||||||
// …
|
|
||||||
|
|
||||||
// Deprecated.
|
|
||||||
"rule": "||example.com^",
|
|
||||||
// Deprecated.
|
|
||||||
"filter_id": 42,
|
|
||||||
// Newly-added.
|
|
||||||
"rules": [{
|
|
||||||
"text": "||example.com^",
|
|
||||||
"filter_list_id": 42
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
For `$dnsrewrite` rules, they contain all rules that contributed to the
|
|
||||||
result. For example, if you have the following filtering rules:
|
|
||||||
|
|
||||||
```
|
|
||||||
||example.com^$dnsrewrite=127.0.0.1
|
|
||||||
||example.com^$dnsrewrite=127.0.0.2
|
|
||||||
```
|
|
||||||
|
|
||||||
The `"rules"` will be something like:
|
|
||||||
|
|
||||||
```js
|
|
||||||
{
|
|
||||||
// …
|
|
||||||
|
|
||||||
"rules": [{
|
|
||||||
"text": "||example.com^$dnsrewrite=127.0.0.1",
|
|
||||||
"filter_list_id": 0
|
|
||||||
}, {
|
|
||||||
"text": "||example.com^$dnsrewrite=127.0.0.2",
|
|
||||||
"filter_list_id": 0
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The old fields will be removed in v0.106.0.
|
|
||||||
|
|
||||||
## v0.103: API changes
|
## v0.103: API changes
|
||||||
|
|
||||||
### API: replace settings in GET /control/dns_info & POST /control/dns_config
|
### API: replace settings in GET /control/dns_info & POST /control/dns_config
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
'info':
|
'info':
|
||||||
'title': 'AdGuard Home'
|
'title': 'AdGuard Home'
|
||||||
'description': >
|
'description': >
|
||||||
AdGuard Home REST-ish API. Our admin web interface is built on top of this
|
AdGuard Home REST API. Our admin web interface is built on top of this REST
|
||||||
REST-ish API.
|
API.
|
||||||
'version': '0.105'
|
'version': '0.104'
|
||||||
'contact':
|
'contact':
|
||||||
'name': 'AdGuard Home'
|
'name': 'AdGuard Home'
|
||||||
'url': 'https://github.com/AdguardTeam/AdGuardHome'
|
'url': 'https://github.com/AdguardTeam/AdGuardHome'
|
||||||
@@ -523,7 +523,7 @@
|
|||||||
Reload filtering rules from URLs. This might be needed if new URL was
|
Reload filtering rules from URLs. This might be needed if new URL was
|
||||||
just added and you dont want to wait for automatic refresh to kick in.
|
just added and you dont want to wait for automatic refresh to kick in.
|
||||||
This API request is ratelimited, so you can call it freely as often as
|
This API request is ratelimited, so you can call it freely as often as
|
||||||
you like, it wont create unnecessary burden on servers that host the
|
you like, it wont create unneccessary burden on servers that host the
|
||||||
URL. This should work as intended, a `force` parameter is offered as
|
URL. This should work as intended, a `force` parameter is offered as
|
||||||
last-resort attempt to make filter lists fresh. If you ever find
|
last-resort attempt to make filter lists fresh. If you ever find
|
||||||
yourself using `force` to make something work that otherwise wont, this
|
yourself using `force` to make something work that otherwise wont, this
|
||||||
@@ -1246,7 +1246,7 @@
|
|||||||
'properties':
|
'properties':
|
||||||
'reason':
|
'reason':
|
||||||
'type': 'string'
|
'type': 'string'
|
||||||
'description': 'Request filtering status.'
|
'description': 'DNS filter status'
|
||||||
'enum':
|
'enum':
|
||||||
- 'NotFilteredNotFound'
|
- 'NotFilteredNotFound'
|
||||||
- 'NotFilteredWhiteList'
|
- 'NotFilteredWhiteList'
|
||||||
@@ -1257,41 +1257,24 @@
|
|||||||
- 'FilteredInvalid'
|
- 'FilteredInvalid'
|
||||||
- 'FilteredSafeSearch'
|
- 'FilteredSafeSearch'
|
||||||
- 'FilteredBlockedService'
|
- 'FilteredBlockedService'
|
||||||
- 'Rewrite'
|
- 'ReasonRewrite'
|
||||||
- 'RewriteEtcHosts'
|
|
||||||
- 'DNSRewriteRule'
|
|
||||||
'filter_id':
|
'filter_id':
|
||||||
'deprecated': true
|
|
||||||
'description': >
|
|
||||||
In case if there's a rule applied to this DNS request, this is ID of
|
|
||||||
the filter list that the rule belongs to.
|
|
||||||
|
|
||||||
Deprecated: use `rules[*].filter_list_id` instead.
|
|
||||||
'type': 'integer'
|
'type': 'integer'
|
||||||
'rule':
|
'rule':
|
||||||
'deprecated': true
|
|
||||||
'type': 'string'
|
'type': 'string'
|
||||||
'example': '||example.org^'
|
'example': '||example.org^'
|
||||||
'description': >
|
'description': 'Filtering rule applied to the request (if any)'
|
||||||
Filtering rule applied to the request (if any).
|
|
||||||
|
|
||||||
Deprecated: use `rules[*].text` instead.
|
|
||||||
'rules':
|
|
||||||
'description': 'Applied rules.'
|
|
||||||
'type': 'array'
|
|
||||||
'items':
|
|
||||||
'$ref': '#/components/schemas/ResultRule'
|
|
||||||
'service_name':
|
'service_name':
|
||||||
'type': 'string'
|
'type': 'string'
|
||||||
'description': 'Set if reason=FilteredBlockedService'
|
'description': 'Set if reason=FilteredBlockedService'
|
||||||
'cname':
|
'cname':
|
||||||
'type': 'string'
|
'type': 'string'
|
||||||
'description': 'Set if reason=Rewrite'
|
'description': 'Set if reason=ReasonRewrite'
|
||||||
'ip_addrs':
|
'ip_addrs':
|
||||||
'type': 'array'
|
'type': 'array'
|
||||||
'items':
|
'items':
|
||||||
'type': 'string'
|
'type': 'string'
|
||||||
'description': 'Set if reason=Rewrite'
|
'description': 'Set if reason=ReasonRewrite'
|
||||||
'FilterRefreshResponse':
|
'FilterRefreshResponse':
|
||||||
'type': 'object'
|
'type': 'object'
|
||||||
'description': '/filtering/refresh response data'
|
'description': '/filtering/refresh response data'
|
||||||
@@ -1627,30 +1610,18 @@
|
|||||||
'question':
|
'question':
|
||||||
'$ref': '#/components/schemas/DnsQuestion'
|
'$ref': '#/components/schemas/DnsQuestion'
|
||||||
'filterId':
|
'filterId':
|
||||||
'deprecated': true
|
|
||||||
'type': 'integer'
|
'type': 'integer'
|
||||||
'example': 123123
|
'example': 123123
|
||||||
'description': >
|
'description': >
|
||||||
In case if there's a rule applied to this DNS request, this is ID of
|
In case if there's a rule applied to this DNS request, this is ID of
|
||||||
the filter list that the rule belongs to.
|
the filter that rule belongs to.
|
||||||
|
|
||||||
Deprecated: use `rules[*].filter_list_id` instead.
|
|
||||||
'rule':
|
'rule':
|
||||||
'deprecated': true
|
|
||||||
'type': 'string'
|
'type': 'string'
|
||||||
'example': '||example.org^'
|
'example': '||example.org^'
|
||||||
'description': >
|
'description': 'Filtering rule applied to the request (if any)'
|
||||||
Filtering rule applied to the request (if any).
|
|
||||||
|
|
||||||
Deprecated: use `rules[*].text` instead.
|
|
||||||
'rules':
|
|
||||||
'description': 'Applied rules.'
|
|
||||||
'type': 'array'
|
|
||||||
'items':
|
|
||||||
'$ref': '#/components/schemas/ResultRule'
|
|
||||||
'reason':
|
'reason':
|
||||||
'type': 'string'
|
'type': 'string'
|
||||||
'description': 'Request filtering status.'
|
'description': 'DNS filter status'
|
||||||
'enum':
|
'enum':
|
||||||
- 'NotFilteredNotFound'
|
- 'NotFilteredNotFound'
|
||||||
- 'NotFilteredWhiteList'
|
- 'NotFilteredWhiteList'
|
||||||
@@ -1661,9 +1632,7 @@
|
|||||||
- 'FilteredInvalid'
|
- 'FilteredInvalid'
|
||||||
- 'FilteredSafeSearch'
|
- 'FilteredSafeSearch'
|
||||||
- 'FilteredBlockedService'
|
- 'FilteredBlockedService'
|
||||||
- 'Rewrite'
|
- 'ReasonRewrite'
|
||||||
- 'RewriteEtcHosts'
|
|
||||||
- 'DNSRewriteRule'
|
|
||||||
'service_name':
|
'service_name':
|
||||||
'type': 'string'
|
'type': 'string'
|
||||||
'description': 'Set if reason=FilteredBlockedService'
|
'description': 'Set if reason=FilteredBlockedService'
|
||||||
@@ -1699,22 +1668,6 @@
|
|||||||
'anonymize_client_ip':
|
'anonymize_client_ip':
|
||||||
'type': 'boolean'
|
'type': 'boolean'
|
||||||
'description': "Anonymize clients' IP addresses"
|
'description': "Anonymize clients' IP addresses"
|
||||||
'ResultRule':
|
|
||||||
'description': 'Applied rule.'
|
|
||||||
'properties':
|
|
||||||
'filter_list_id':
|
|
||||||
'description': >
|
|
||||||
In case if there's a rule applied to this DNS request, this is ID of
|
|
||||||
the filter list that the rule belongs to.
|
|
||||||
'example': 123123
|
|
||||||
'format': 'int64'
|
|
||||||
'type': 'integer'
|
|
||||||
'text':
|
|
||||||
'description': >
|
|
||||||
The text of the filtering rule applied to the request (if any).
|
|
||||||
'example': '||example.org^'
|
|
||||||
'type': 'string'
|
|
||||||
'type': 'object'
|
|
||||||
'TlsConfig':
|
'TlsConfig':
|
||||||
'type': 'object'
|
'type': 'object'
|
||||||
'description': 'TLS configuration settings and status'
|
'description': 'TLS configuration settings and status'
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ ineffassign .
|
|||||||
|
|
||||||
unparam ./...
|
unparam ./...
|
||||||
|
|
||||||
git ls-files -- '*.go' '*.md' '*.yaml' '*.yml' | xargs misspell --error
|
misspell --error ./...
|
||||||
|
|
||||||
looppointer ./...
|
looppointer ./...
|
||||||
|
|
||||||
@@ -112,4 +112,4 @@ exit_on_output sh -c '
|
|||||||
{ grep -e "defer" -e "_test\.go:" -v || exit 0; }
|
{ grep -e "defer" -e "_test\.go:" -v || exit 0; }
|
||||||
'
|
'
|
||||||
|
|
||||||
staticcheck ./...
|
staticcheck --checks='all' ./...
|
||||||
|
|||||||
@@ -190,7 +190,6 @@ main() {
|
|||||||
SCRIPT_URL="https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/master/scripts/install.sh"
|
SCRIPT_URL="https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/master/scripts/install.sh"
|
||||||
URL="https://static.adguard.com/adguardhome/${CHANNEL}/${PKG_NAME}"
|
URL="https://static.adguard.com/adguardhome/${CHANNEL}/${PKG_NAME}"
|
||||||
OUT_DIR=/opt
|
OUT_DIR=/opt
|
||||||
AGH_DIR="${OUT_DIR}/AdGuardHome"
|
|
||||||
|
|
||||||
# Root check
|
# Root check
|
||||||
if [ "$(id -u)" -eq 0 ]; then
|
if [ "$(id -u)" -eq 0 ]; then
|
||||||
@@ -209,22 +208,22 @@ main() {
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
log_info "AdGuard Home will be installed to ${AGH_DIR}"
|
log_info "AdGuard Home will be installed to ${OUT_DIR}/AdGuardHome"
|
||||||
|
|
||||||
[ -d "${AGH_DIR}" ] && [ -n "$(ls -1 -A -q ${AGH_DIR})" ] && error_exit "Directory ${AGH_DIR} is not empty, abort installation"
|
[ -d "${OUT_DIR}/AdGuardHome" ] && error_exit "Directory ${OUT_DIR}/AdGuardHome already exists, abort installation"
|
||||||
|
|
||||||
download "${URL}" "${PKG_NAME}" || error_exit "Cannot download the package"
|
download "${URL}" "${PKG_NAME}" || error_exit "Cannot download the package"
|
||||||
|
|
||||||
unpack "${PKG_NAME}" "${OUT_DIR}" "${PKG_EXT}" || error_exit "Cannot unpack the package"
|
unpack "${PKG_NAME}" "${OUT_DIR}" "${PKG_EXT}" || error_exit "Cannot unpack the package"
|
||||||
|
|
||||||
# Install AdGuard Home service and run it
|
# Install AdGuard Home service and run it
|
||||||
${AGH_DIR}/AdGuardHome -s install || error_exit "Cannot install AdGuardHome as a service"
|
${OUT_DIR}/AdGuardHome/AdGuardHome -s install || error_exit "Cannot install AdGuardHome as a service"
|
||||||
|
|
||||||
rm "${PKG_NAME}"
|
rm "${PKG_NAME}"
|
||||||
|
|
||||||
log_info "AdGuard Home is now installed and running."
|
log_info "AdGuard Home is now installed and running."
|
||||||
log_info "You can control the service status with the following commands:"
|
log_info "You can control the service status with the following commands:"
|
||||||
log_info " sudo ${AGH_DIR}/AdGuardHome -s start|stop|restart|status|install|uninstall"
|
log_info " sudo ${OUT_DIR}/AdGuardHome/AdGuardHome -s start|stop|restart|status|install|uninstall"
|
||||||
}
|
}
|
||||||
|
|
||||||
main "$@"
|
main "$@"
|
||||||
@@ -2,6 +2,11 @@ const fs = require('fs');
|
|||||||
const readline = require('readline');
|
const readline = require('readline');
|
||||||
const dnsPacket = require('dns-packet')
|
const dnsPacket = require('dns-packet')
|
||||||
|
|
||||||
|
const decodeBase64 = (data) => {
|
||||||
|
let buff = new Buffer(data, 'base64');
|
||||||
|
return buff.toString('ascii');
|
||||||
|
}
|
||||||
|
|
||||||
const processLineByLine = async (source, callback) => {
|
const processLineByLine = async (source, callback) => {
|
||||||
const fileStream = fs.createReadStream(source);
|
const fileStream = fs.createReadStream(source);
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
## Twosky integration script
|
## Twosky intergration script
|
||||||
|
|
||||||
### Usage
|
### Usage
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
checks = ["all"]
|
|
||||||
initialisms = [
|
|
||||||
# See https://github.com/dominikh/go-tools/blob/master/config/config.go.
|
|
||||||
"inherit"
|
|
||||||
, "DHCP"
|
|
||||||
, "DOH"
|
|
||||||
, "DOQ"
|
|
||||||
, "DOT"
|
|
||||||
, "EDNS"
|
|
||||||
, "MX"
|
|
||||||
, "PTR"
|
|
||||||
, "QUIC"
|
|
||||||
, "SDNS"
|
|
||||||
, "SVCB"
|
|
||||||
]
|
|
||||||
dot_import_whitelist = []
|
|
||||||
http_status_code_whitelist = []
|
|
||||||
Reference in New Issue
Block a user