Compare commits
8 Commits
v0.105.0-b
...
102-dns-re
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a9efc39306 | ||
|
|
f924523f6a | ||
|
|
6e6ee9697a | ||
|
|
aff09211b2 | ||
|
|
bad1c6acdc | ||
|
|
fcb582679e | ||
|
|
6b60598025 | ||
|
|
b338bf9b3f |
@@ -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 lint-go || exit 1
|
||||||
|
go test ./... || exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
exit 0;
|
||||||
|
|||||||
15
.github/workflows/lint.yml
vendored
15
.github/workflows/lint.yml
vendored
@@ -1,4 +1,4 @@
|
|||||||
'name': 'lint'
|
'name': 'golangci-lint'
|
||||||
'on':
|
'on':
|
||||||
'push':
|
'push':
|
||||||
'tags':
|
'tags':
|
||||||
@@ -7,13 +7,16 @@
|
|||||||
- '*'
|
- '*'
|
||||||
'pull_request':
|
'pull_request':
|
||||||
'jobs':
|
'jobs':
|
||||||
'go-lint':
|
'golangci':
|
||||||
'runs-on': 'ubuntu-latest'
|
'runs-on': 'ubuntu-latest'
|
||||||
'steps':
|
'steps':
|
||||||
- 'uses': 'actions/checkout@v2'
|
- 'uses': 'actions/checkout@v2'
|
||||||
- 'name': 'run-lint'
|
- 'name': 'golangci-lint'
|
||||||
'run': >
|
'uses': 'golangci/golangci-lint-action@v2.3.0'
|
||||||
make go-install-tools go-lint
|
'with':
|
||||||
|
# This field is required. Don't set the patch version to always use
|
||||||
|
# the latest patch version.
|
||||||
|
'version': 'v1.32'
|
||||||
'eslint':
|
'eslint':
|
||||||
'runs-on': 'ubuntu-latest'
|
'runs-on': 'ubuntu-latest'
|
||||||
'steps':
|
'steps':
|
||||||
@@ -24,7 +27,7 @@
|
|||||||
'run': 'npm --prefix client run lint'
|
'run': 'npm --prefix client run lint'
|
||||||
'notify':
|
'notify':
|
||||||
'needs':
|
'needs':
|
||||||
- 'go-lint'
|
- 'golangci'
|
||||||
- 'eslint'
|
- 'eslint'
|
||||||
# Secrets are not passed to workflows that are triggered by a pull request
|
# Secrets are not passed to workflows that are triggered by a pull request
|
||||||
# from a fork.
|
# from a fork.
|
||||||
|
|||||||
47
.gitignore
vendored
47
.gitignore
vendored
@@ -1,22 +1,31 @@
|
|||||||
# Please, DO NOT put your text editors' temporary files here. The more are
|
.DS_Store
|
||||||
# added, the harder it gets to maintain and manage projects' gitignores. Put
|
/.vscode
|
||||||
# them into your global gitignore file instead.
|
.idea
|
||||||
#
|
/AdGuardHome
|
||||||
# See https://stackoverflow.com/a/7335487/1892060.
|
/AdGuardHome.exe
|
||||||
#
|
/AdGuardHome.yaml
|
||||||
# Only build, run, and test outputs here. Sorted.
|
/AdGuardHome.log
|
||||||
*-packr.go
|
|
||||||
*.db
|
|
||||||
*.snap
|
|
||||||
/bin/
|
|
||||||
/build/
|
|
||||||
/data/
|
/data/
|
||||||
|
/build/
|
||||||
/dist/
|
/dist/
|
||||||
/dnsfilter/tests/dnsfilter.TestLotsOfRules*.pprof
|
/client/node_modules/
|
||||||
/dnsfilter/tests/top-1m.csv
|
/querylog.json
|
||||||
/launchpad_credentials
|
/querylog.json.1
|
||||||
/querylog.json*
|
|
||||||
/snapcraft_login
|
|
||||||
AdGuardHome*
|
|
||||||
coverage.txt
|
coverage.txt
|
||||||
node_modules/
|
leases.db
|
||||||
|
|
||||||
|
# Test output
|
||||||
|
dnsfilter/tests/top-1m.csv
|
||||||
|
dnsfilter/tests/dnsfilter.TestLotsOfRules*.pprof
|
||||||
|
|
||||||
|
# Snapcraft build temporary files
|
||||||
|
*.snap
|
||||||
|
launchpad_credentials
|
||||||
|
snapcraft_login
|
||||||
|
snapcraft.yaml.bak
|
||||||
|
|
||||||
|
# IntelliJ IDEA project files
|
||||||
|
*.iml
|
||||||
|
|
||||||
|
# Packr
|
||||||
|
*-packr.go
|
||||||
|
|||||||
77
.golangci.yml
Normal file
77
.golangci.yml
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
# options for analysis running
|
||||||
|
'run':
|
||||||
|
# default concurrency is a available CPU number
|
||||||
|
'concurrency': 4
|
||||||
|
|
||||||
|
# timeout for analysis, e.g. 30s, 5m, default is 1m
|
||||||
|
'deadline': '2m'
|
||||||
|
|
||||||
|
# which files to skip: they will be analyzed, but issues from them
|
||||||
|
# won't be reported. Default value is empty list, but there is
|
||||||
|
# no need to include all autogenerated files, we confidently recognize
|
||||||
|
# autogenerated files. If it's not please let us know.
|
||||||
|
'skip-files':
|
||||||
|
- '.*generated.*'
|
||||||
|
- 'dnsfilter/rule_to_regexp.go'
|
||||||
|
- 'util/pprof.go'
|
||||||
|
- '.*_test.go'
|
||||||
|
- 'client/.*'
|
||||||
|
- 'build/.*'
|
||||||
|
- 'dist/.*'
|
||||||
|
|
||||||
|
# all available settings of specific linters
|
||||||
|
'linters-settings':
|
||||||
|
'errcheck':
|
||||||
|
# [deprecated] comma-separated list of pairs of the form pkg:regex
|
||||||
|
# the regex is used to ignore names within pkg. (default "fmt:.*").
|
||||||
|
# see https://github.com/kisielk/errcheck#the-deprecated-method for details
|
||||||
|
'ignore': 'fmt:.*,net:SetReadDeadline,net/http:^Write'
|
||||||
|
'gocyclo':
|
||||||
|
'min-complexity': 20
|
||||||
|
'lll':
|
||||||
|
'line-length': 200
|
||||||
|
|
||||||
|
'linters':
|
||||||
|
'enable':
|
||||||
|
- 'bodyclose'
|
||||||
|
- 'deadcode'
|
||||||
|
- 'depguard'
|
||||||
|
- 'dupl'
|
||||||
|
- 'errcheck'
|
||||||
|
- 'gocyclo'
|
||||||
|
- 'goimports'
|
||||||
|
- 'golint'
|
||||||
|
- 'gosec'
|
||||||
|
- 'govet'
|
||||||
|
- 'ineffassign'
|
||||||
|
- 'misspell'
|
||||||
|
- 'staticcheck'
|
||||||
|
- 'stylecheck'
|
||||||
|
- 'unconvert'
|
||||||
|
- 'unused'
|
||||||
|
- 'varcheck'
|
||||||
|
'disable-all': true
|
||||||
|
'fast': true
|
||||||
|
|
||||||
|
'issues':
|
||||||
|
# List of regexps of issue texts to exclude, empty list by default.
|
||||||
|
# But independently from this option we use default exclude patterns,
|
||||||
|
# it can be disabled by `exclude-use-default: false`. To list all
|
||||||
|
# excluded by default patterns execute `golangci-lint run --help`
|
||||||
|
'exclude':
|
||||||
|
# structcheck cannot detect usages while they're there
|
||||||
|
- '.parentalServer. is unused'
|
||||||
|
- '.safeBrowsingServer. is unused'
|
||||||
|
# errcheck
|
||||||
|
- 'Error return value of .s.closeConn. is not checked'
|
||||||
|
- 'Error return value of ..*.Shutdown.'
|
||||||
|
# goconst
|
||||||
|
- 'string .forcesafesearch.google.com. has 3 occurrences'
|
||||||
|
# gosec: Profiling endpoint is automatically exposed on /debug/pprof
|
||||||
|
- 'G108'
|
||||||
|
# gosec: Subprocess launched with function call as argument or cmd arguments
|
||||||
|
- 'G204'
|
||||||
|
# gosec: Potential DoS vulnerability via decompression bomb
|
||||||
|
- 'G110'
|
||||||
|
# gosec: Expect WriteFile permissions to be 0600 or less
|
||||||
|
- 'G306'
|
||||||
@@ -105,11 +105,3 @@
|
|||||||
|
|
||||||
'checksum':
|
'checksum':
|
||||||
'name_template': 'checksums.txt'
|
'name_template': 'checksums.txt'
|
||||||
|
|
||||||
'snapshot':
|
|
||||||
# TODO(a.garipov): A temporary solution to trim the prerelease versions.
|
|
||||||
# A real solution would consist of making a better versioning scheme that also
|
|
||||||
# doesn't break SemVer or Snapcraft.
|
|
||||||
#
|
|
||||||
# See https://github.com/AdguardTeam/AdGuardHome/issues/2412.
|
|
||||||
'name_template': '{{ slice .Tag 0 8 }}-SNAPSHOT-{{ .ShortCommit }}'
|
|
||||||
|
|||||||
@@ -1558,6 +1558,7 @@ Strict matching can be enabled by enclosing the value in double quotes: e.g. `"a
|
|||||||
* blocked_services - blocked services
|
* blocked_services - blocked services
|
||||||
* blocked_safebrowsing - blocked by safebrowsing
|
* blocked_safebrowsing - blocked by safebrowsing
|
||||||
* blocked_parental - blocked by parental control
|
* blocked_parental - blocked by parental control
|
||||||
|
* blocked_dns_rebinding - blocked by DNS rebinding protection
|
||||||
* whitelisted - whitelisted
|
* whitelisted - whitelisted
|
||||||
* rewritten - all kinds of rewrites
|
* rewritten - all kinds of rewrites
|
||||||
* safe_search - enforced safe search
|
* safe_search - enforced safe search
|
||||||
@@ -1833,22 +1834,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
|
||||||
|
|
||||||
|
|||||||
24
CHANGELOG.md
24
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,46 +31,34 @@ and this project adheres to
|
|||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- When `dns.bogus_nxdomain` option is used, the server will now transform
|
- Post-updating relaunch possibility is now determined OS-dependently ([#2231], [#2391]).
|
||||||
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],
|
|
||||||
[#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
|
||||||
improve error response ([#2358]).
|
improve error response ([#2358]).
|
||||||
- Improved HTTP requests handling and timeouts ([#2343]).
|
- Improved HTTP requests handling and timeouts ([#2343]).
|
||||||
- Our snap package now uses the `core20` image as its base ([#2306]).
|
- Our snap package now uses the `core20` image as its base ([#2306]).
|
||||||
- Various internal improvements ([#2267], [#2271], [#2297]).
|
- Various internal improvements ([#2271], [#2297]).
|
||||||
|
|
||||||
[#2231]: https://github.com/AdguardTeam/AdGuardHome/issues/2231
|
[#2231]: https://github.com/AdguardTeam/AdGuardHome/issues/2231
|
||||||
[#2267]: https://github.com/AdguardTeam/AdGuardHome/issues/2267
|
|
||||||
[#2271]: https://github.com/AdguardTeam/AdGuardHome/issues/2271
|
[#2271]: https://github.com/AdguardTeam/AdGuardHome/issues/2271
|
||||||
[#2297]: https://github.com/AdguardTeam/AdGuardHome/issues/2297
|
[#2297]: https://github.com/AdguardTeam/AdGuardHome/issues/2297
|
||||||
[#2306]: https://github.com/AdguardTeam/AdGuardHome/issues/2306
|
[#2306]: https://github.com/AdguardTeam/AdGuardHome/issues/2306
|
||||||
[#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
|
||||||
|
|
||||||
|
|||||||
70
HACKING.md
70
HACKING.md
@@ -1,9 +1,9 @@
|
|||||||
# *AdGuardHome* Developer Guidelines
|
# *AdGuardHome* Developer Guidelines
|
||||||
|
|
||||||
As of **December 2020**, this document is partially a work-in-progress, but
|
As of **2020-11-27**, this document is a work-in-progress, but should still be
|
||||||
should still be followed. Some of the rules aren't enforced as thoroughly or
|
followed. Some of the rules aren't enforced as thoroughly or remain broken in
|
||||||
remain broken in old code, but this is still the place to find out about what we
|
old code, but this is still the place to find out about what we **want** our
|
||||||
**want** our code to look like.
|
code to look like.
|
||||||
|
|
||||||
The rules are mostly sorted in the alphabetical order.
|
The rules are mostly sorted in the alphabetical order.
|
||||||
|
|
||||||
@@ -32,11 +32,6 @@ The rules are mostly sorted in the alphabetical order.
|
|||||||
|
|
||||||
## *Go*
|
## *Go*
|
||||||
|
|
||||||
> Not Golang, not GO, not GOLANG, not GoLang. It is Go in natural language,
|
|
||||||
> golang for others.
|
|
||||||
|
|
||||||
— [@rakyll](https://twitter.com/rakyll/status/1229850223184269312)
|
|
||||||
|
|
||||||
### Code And Naming
|
### Code And Naming
|
||||||
|
|
||||||
* Avoid `goto`.
|
* Avoid `goto`.
|
||||||
@@ -78,14 +73,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 +101,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) {
|
|
||||||
// …
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -134,6 +112,8 @@ The rules are mostly sorted in the alphabetical order.
|
|||||||
|
|
||||||
* Use `gofumpt --extra -s`.
|
* Use `gofumpt --extra -s`.
|
||||||
|
|
||||||
|
**TODO(a.garipov):** Add to the linters.
|
||||||
|
|
||||||
* Write slices of struct like this:
|
* Write slices of struct like this:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
@@ -161,42 +141,6 @@ The rules are mostly sorted in the alphabetical order.
|
|||||||
|
|
||||||
* **TODO(a.garipov):** Define our *Markdown* conventions.
|
* **TODO(a.garipov):** Define our *Markdown* conventions.
|
||||||
|
|
||||||
## Shell Scripting
|
|
||||||
|
|
||||||
* Avoid bashisms and GNUisms, prefer *POSIX* features only.
|
|
||||||
|
|
||||||
* Prefer `'raw strings'` to `"double quoted strings"` whenever possible.
|
|
||||||
|
|
||||||
* Put spaces within `$( cmd )`, `$(( expr ))`, and `{ cmd; }`.
|
|
||||||
|
|
||||||
* Put utility flags in the ASCII order and **don't** group them together. For
|
|
||||||
example, `ls -1 -A -q`.
|
|
||||||
|
|
||||||
* `snake_case`, not `camelCase`.
|
|
||||||
|
|
||||||
* Use `set -e -f -u` and also `set -x` in verbose mode.
|
|
||||||
|
|
||||||
* Use the `"$var"` form instead of the `$var` form, unless word splitting is
|
|
||||||
required.
|
|
||||||
|
|
||||||
* When concatenating, always use the form with curly braces to prevent
|
|
||||||
accidental bad variable names. That is, `"${var}_tmp.txt"` and **not**
|
|
||||||
`"$var_tmp.txt"`. The latter will try to lookup variable `var_tmp`.
|
|
||||||
|
|
||||||
* When concatenating, surround the whole string with quotes. That is, use
|
|
||||||
this:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
dir="${TOP_DIR}/sub"
|
|
||||||
```
|
|
||||||
|
|
||||||
And **not** this:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
# Bad!
|
|
||||||
dir="${TOP_DIR}"/sub
|
|
||||||
```
|
|
||||||
|
|
||||||
## Text, Including Comments
|
## Text, Including Comments
|
||||||
|
|
||||||
* End sentences with appropriate punctuation.
|
* End sentences with appropriate punctuation.
|
||||||
|
|||||||
47
Makefile
47
Makefile
@@ -26,8 +26,7 @@
|
|||||||
# * DOCKER_IMAGE_NAME - adguard/adguard-home
|
# * DOCKER_IMAGE_NAME - adguard/adguard-home
|
||||||
# * DOCKER_OUTPUT - type=image,name=adguard/adguard-home,push=true
|
# * DOCKER_OUTPUT - type=image,name=adguard/adguard-home,push=true
|
||||||
|
|
||||||
GO := go
|
GOPATH := $(shell go env GOPATH)
|
||||||
GOPATH := $(shell $(GO) env GOPATH)
|
|
||||||
PWD := $(shell pwd)
|
PWD := $(shell pwd)
|
||||||
TARGET=AdGuardHome
|
TARGET=AdGuardHome
|
||||||
BASE_URL="https://static.adguard.com/adguardhome/$(CHANNEL)"
|
BASE_URL="https://static.adguard.com/adguardhome/$(CHANNEL)"
|
||||||
@@ -35,7 +34,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
|
||||||
@@ -67,9 +65,7 @@ endif
|
|||||||
|
|
||||||
# Version properties
|
# Version properties
|
||||||
COMMIT=$(shell git rev-parse --short HEAD)
|
COMMIT=$(shell git rev-parse --short HEAD)
|
||||||
# TODO(a.garipov): The cut call is a temporary solution to trim
|
TAG_NAME=$(shell git describe --abbrev=0)
|
||||||
# prerelease versions. See the comment in .goreleaser.yml.
|
|
||||||
TAG_NAME=$(shell git describe --abbrev=0 | cut -c 1-8)
|
|
||||||
RELEASE_VERSION=$(TAG_NAME)
|
RELEASE_VERSION=$(TAG_NAME)
|
||||||
SNAPSHOT_VERSION=$(RELEASE_VERSION)-SNAPSHOT-$(COMMIT)
|
SNAPSHOT_VERSION=$(RELEASE_VERSION)-SNAPSHOT-$(COMMIT)
|
||||||
|
|
||||||
@@ -125,11 +121,10 @@ 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)"
|
|
||||||
PATH=$(GOPATH)/bin:$(PATH) packr clean
|
PATH=$(GOPATH)/bin:$(PATH) packr clean
|
||||||
|
|
||||||
client:
|
client:
|
||||||
@@ -156,40 +151,38 @@ docker:
|
|||||||
@echo Now you can run the docker image:
|
@echo Now you can run the docker image:
|
||||||
@echo docker run --name "adguard-home" -p 53:53/tcp -p 53:53/udp -p 80:80/tcp -p 443:443/tcp -p 853:853/tcp -p 3000:3000/tcp $(DOCKER_IMAGE_NAME)
|
@echo docker run --name "adguard-home" -p 53:53/tcp -p 53:53/udp -p 80:80/tcp -p 443:443/tcp -p 853:853/tcp -p 3000:3000/tcp $(DOCKER_IMAGE_NAME)
|
||||||
|
|
||||||
lint: js-lint go-lint
|
lint: lint-js lint-go
|
||||||
|
|
||||||
js-lint: dependencies
|
lint-js: dependencies
|
||||||
|
@echo Running js linter
|
||||||
npm --prefix client run lint
|
npm --prefix client run lint
|
||||||
|
|
||||||
go-install-tools:
|
lint-go:
|
||||||
env GO=$(GO) sh ./scripts/go-install-tools.sh
|
@echo Running go linter
|
||||||
|
golangci-lint run
|
||||||
|
|
||||||
go-lint:
|
test: test-js test-go
|
||||||
env GO=$(GO) PATH="$$PWD/bin:$$PATH" sh ./scripts/go-lint.sh
|
|
||||||
|
|
||||||
test: js-test go-test
|
test-js:
|
||||||
|
|
||||||
js-test:
|
|
||||||
npm run test --prefix client
|
npm run test --prefix client
|
||||||
|
|
||||||
go-test:
|
test-go:
|
||||||
$(GO) test $(TEST_FLAGS) --coverprofile coverage.txt ./...
|
go test $(TEST_FLAGS) --coverprofile coverage.txt ./...
|
||||||
|
|
||||||
ci: client_with_deps
|
ci: client_with_deps
|
||||||
$(GO) mod download
|
go mod download
|
||||||
$(MAKE) test
|
$(MAKE) test
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
npm --prefix client ci
|
npm --prefix client ci
|
||||||
$(GO) mod download
|
go mod download
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f ./AdGuardHome ./AdGuardHome.exe ./coverage.txt
|
rm -f ./AdGuardHome ./AdGuardHome.exe ./coverage.txt
|
||||||
rm -f -r ./build/ ./client/node_modules/ ./data/ ./$(DIST_DIR)/
|
rm -f -r ./build/ ./client/node_modules/ ./data/ $(DIST_DIR)
|
||||||
# Set the GOPATH explicitly in case make clean is called from under sudo
|
# Set the GOPATH explicitly in case make clean is called from under sudo
|
||||||
# after a Docker build.
|
# after a Docker build.
|
||||||
env PATH="$(GOPATH)/bin:$$PATH" packr clean
|
env PATH="$(GOPATH)/bin:$$PATH" packr clean
|
||||||
rm -f -r ./bin/
|
|
||||||
|
|
||||||
docker-multi-arch:
|
docker-multi-arch:
|
||||||
DOCKER_CLI_EXPERIMENTAL=enabled \
|
DOCKER_CLI_EXPERIMENTAL=enabled \
|
||||||
@@ -207,7 +200,7 @@ docker-multi-arch:
|
|||||||
@echo docker run --name "adguard-home" -p 53:53/tcp -p 53:53/udp -p 80:80/tcp -p 443:443/tcp -p 853:853/tcp -p 3000:3000/tcp $(DOCKER_IMAGE_NAME)
|
@echo docker run --name "adguard-home" -p 53:53/tcp -p 53:53/udp -p 80:80/tcp -p 443:443/tcp -p 853:853/tcp -p 3000:3000/tcp $(DOCKER_IMAGE_NAME)
|
||||||
|
|
||||||
release: client_with_deps
|
release: client_with_deps
|
||||||
$(GO) mod download
|
go mod download
|
||||||
@echo Starting release build: version $(VERSION), channel $(CHANNEL)
|
@echo Starting release build: version $(VERSION), channel $(CHANNEL)
|
||||||
CHANNEL=$(CHANNEL) $(GORELEASER_COMMAND)
|
CHANNEL=$(CHANNEL) $(GORELEASER_COMMAND)
|
||||||
$(call write_version_file,$(VERSION))
|
$(call write_version_file,$(VERSION))
|
||||||
|
|||||||
@@ -171,6 +171,9 @@ You will need this to build AdGuard Home:
|
|||||||
* [node.js](https://nodejs.org/en/download/) v10.16.2 or later.
|
* [node.js](https://nodejs.org/en/download/) v10.16.2 or later.
|
||||||
* [npm](https://www.npmjs.com/) v6.14 or later.
|
* [npm](https://www.npmjs.com/) v6.14 or later.
|
||||||
|
|
||||||
|
Optionally, for Go devs:
|
||||||
|
* [golangci-lint](https://github.com/golangci/golangci-lint)
|
||||||
|
|
||||||
### Building
|
### Building
|
||||||
|
|
||||||
Open Terminal and execute these commands:
|
Open Terminal and execute these commands:
|
||||||
@@ -183,7 +186,7 @@ make
|
|||||||
|
|
||||||
Check the [`Makefile`](https://github.com/AdguardTeam/AdGuardHome/blob/master/Makefile) to learn about other commands.
|
Check the [`Makefile`](https://github.com/AdguardTeam/AdGuardHome/blob/master/Makefile) to learn about other commands.
|
||||||
|
|
||||||
**Building for a different platform.** You can build AdGuard for any OS/ARCH just like any other Go project.
|
**Building for a different platform.** You can build AdGuard for any OS/ARCH just like any other Golang project.
|
||||||
In order to do this, specify `GOOS` and `GOARCH` env variables before running make.
|
In order to do this, specify `GOOS` and `GOARCH` env variables before running make.
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
@@ -255,7 +258,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 +267,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)
|
||||||
|
|||||||
@@ -587,5 +587,12 @@
|
|||||||
"port_53_faq_link": "Port 53 is often occupied by \"DNSStubListener\" or \"systemd-resolved\" services. Please read <0>this instruction</0> on how to resolve this.",
|
"port_53_faq_link": "Port 53 is often occupied by \"DNSStubListener\" or \"systemd-resolved\" services. Please read <0>this instruction</0> on how to resolve this.",
|
||||||
"adg_will_drop_dns_queries": "AdGuard Home will be dropping all DNS queries from this client.",
|
"adg_will_drop_dns_queries": "AdGuard Home will be dropping all DNS queries from this client.",
|
||||||
"client_not_in_allowed_clients": "The client is not allowed because it is not in the \"Allowed clients\" list.",
|
"client_not_in_allowed_clients": "The client is not allowed because it is not in the \"Allowed clients\" list.",
|
||||||
"experimental": "Experimental"
|
"experimental": "Experimental",
|
||||||
|
"rebinding_title": "DNS Rebinding Protection",
|
||||||
|
"rebinding_desc": "Here you can configure protection against DNS rebinding attacks",
|
||||||
|
"rebinding_protection_enabled": "Enable protection from DNS rebinding attacks",
|
||||||
|
"rebinding_protection_enabled_desc": "If enabled, AdGuard Home will block responses containing host on the local network.",
|
||||||
|
"rebinding_allowed_hosts_title": "Allowed domains",
|
||||||
|
"rebinding_allowed_hosts_desc": "A list of domains. If configured, AdGuard Home will allow responses containing host on the local network from these domains. Here you can specify the exact domain names, wildcards and urlfilter-rules, e.g. 'example.org', '*.example.org' or '||example.org^'.",
|
||||||
|
"blocked_dns_rebinding": "Blocked DNS rebinding"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -273,15 +273,15 @@ describe('sortIp', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
describe('invalid input', () => {
|
describe('invalid input', () => {
|
||||||
const originalWarn = console.warn;
|
const originalError = console.error;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
console.warn = jest.fn();
|
console.error = jest.fn();
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
expect(console.warn).toHaveBeenCalled();
|
expect(console.error).toHaveBeenCalled();
|
||||||
console.warn = originalWarn;
|
console.error = originalError;
|
||||||
});
|
});
|
||||||
|
|
||||||
test('invalid strings', () => {
|
test('invalid strings', () => {
|
||||||
|
|||||||
@@ -37,6 +37,10 @@ export const setDnsConfig = (config) => async (dispatch) => {
|
|||||||
data.upstream_dns = splitByNewLine(config.upstream_dns);
|
data.upstream_dns = splitByNewLine(config.upstream_dns);
|
||||||
hasDnsSettings = true;
|
hasDnsSettings = true;
|
||||||
}
|
}
|
||||||
|
if (Object.prototype.hasOwnProperty.call(data, 'rebinding_allowed_hosts')) {
|
||||||
|
data.rebinding_allowed_hosts = splitByNewLine(config.rebinding_allowed_hosts);
|
||||||
|
hasDnsSettings = true;
|
||||||
|
}
|
||||||
|
|
||||||
await apiClient.setDnsConfig(data);
|
await apiClient.setDnsConfig(data);
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import React, { Component } from 'react';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import ReactTable from 'react-table';
|
import ReactTable from 'react-table';
|
||||||
import { withTranslation } from 'react-i18next';
|
import { withTranslation } from 'react-i18next';
|
||||||
import { sortIp } from '../../../helpers/helpers';
|
|
||||||
|
|
||||||
class Table extends Component {
|
class Table extends Component {
|
||||||
cellWrap = ({ value }) => (
|
cellWrap = ({ value }) => (
|
||||||
@@ -22,7 +21,6 @@ class Table extends Component {
|
|||||||
{
|
{
|
||||||
Header: this.props.t('answer'),
|
Header: this.props.t('answer'),
|
||||||
accessor: 'answer',
|
accessor: 'answer',
|
||||||
sortMethod: sortIp,
|
|
||||||
Cell: this.cellWrap,
|
Cell: this.cellWrap,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -259,7 +259,7 @@ let Form = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="form__desc mt-0 mb-2">
|
<div className="form__desc mt-0 mb-2">
|
||||||
<Trans components={[
|
<Trans components={[
|
||||||
<a target="_blank" rel="noopener noreferrer" href="https://github.com/AdguardTeam/AdGuardHome/wiki/Hosts-Blocklists#ctag"
|
<a href="https://github.com/AdguardTeam/AdGuardHome/wiki/Hosts-Blocklists#ctag"
|
||||||
key="0">link</a>,
|
key="0">link</a>,
|
||||||
]}>
|
]}>
|
||||||
tags_desc
|
tags_desc
|
||||||
|
|||||||
@@ -113,6 +113,9 @@ const Dhcp = () => {
|
|||||||
const enteredSomeValue = enteredSomeV4Value || enteredSomeV6Value || interfaceName;
|
const enteredSomeValue = enteredSomeV4Value || enteredSomeV6Value || interfaceName;
|
||||||
|
|
||||||
const getToggleDhcpButton = () => {
|
const getToggleDhcpButton = () => {
|
||||||
|
const otherDhcpFound = check && (check.v4.other_server.found === STATUS_RESPONSE.YES
|
||||||
|
|| check.v6.other_server.found === STATUS_RESPONSE.YES);
|
||||||
|
|
||||||
const filledConfig = interface_name && (Object.values(v4)
|
const filledConfig = interface_name && (Object.values(v4)
|
||||||
.every(Boolean) || Object.values(v6)
|
.every(Boolean) || Object.values(v6)
|
||||||
.every(Boolean));
|
.every(Boolean));
|
||||||
@@ -138,7 +141,7 @@ const Dhcp = () => {
|
|||||||
className={className}
|
className={className}
|
||||||
onClick={enabled ? onClickDisable : onClickEnable}
|
onClick={enabled ? onClickDisable : onClickEnable}
|
||||||
disabled={processingDhcp || processingConfig
|
disabled={processingDhcp || processingConfig
|
||||||
|| (!enabled && (!filledConfig || !check))}
|
|| (!enabled && (!filledConfig || !check || otherDhcpFound))}
|
||||||
>
|
>
|
||||||
<Trans>{enabled ? 'dhcp_disable' : 'dhcp_enable'}</Trans>
|
<Trans>{enabled ? 'dhcp_disable' : 'dhcp_enable'}</Trans>
|
||||||
</button>;
|
</button>;
|
||||||
|
|||||||
91
client/src/components/Settings/Dns/Rebinding/Form.js
Normal file
91
client/src/components/Settings/Dns/Rebinding/Form.js
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { Field, reduxForm } from 'redux-form';
|
||||||
|
import { Trans, useTranslation } from 'react-i18next';
|
||||||
|
import { useSelector } from 'react-redux';
|
||||||
|
import { renderTextareaField, CheckboxField } from '../../../../helpers/form';
|
||||||
|
import { removeEmptyLines } from '../../../../helpers/helpers';
|
||||||
|
import { FORM_NAME } from '../../../../helpers/constants';
|
||||||
|
|
||||||
|
const fields = [
|
||||||
|
{
|
||||||
|
id: 'rebinding_allowed_hosts',
|
||||||
|
title: 'rebinding_allowed_hosts_title',
|
||||||
|
subtitle: 'rebinding_allowed_hosts_desc',
|
||||||
|
normalizeOnBlur: removeEmptyLines,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const Form = ({
|
||||||
|
handleSubmit, submitting, invalid,
|
||||||
|
}) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const processingSetConfig = useSelector((state) => state.dnsConfig.processingSetConfig);
|
||||||
|
|
||||||
|
const renderField = ({
|
||||||
|
id, title, subtitle, disabled = processingSetConfig, normalizeOnBlur,
|
||||||
|
}) => <div key={id} className="form__group mb-5">
|
||||||
|
<label className="form__label form__label--with-desc" htmlFor={id}>
|
||||||
|
<Trans>{title}</Trans>
|
||||||
|
</label>
|
||||||
|
<div className="form__desc form__desc--top">
|
||||||
|
<Trans>{subtitle}</Trans>
|
||||||
|
</div>
|
||||||
|
<Field
|
||||||
|
id={id}
|
||||||
|
name={id}
|
||||||
|
component={renderTextareaField}
|
||||||
|
type="text"
|
||||||
|
className="form-control form-control--textarea font-monospace"
|
||||||
|
disabled={disabled}
|
||||||
|
normalizeOnBlur={normalizeOnBlur}
|
||||||
|
/>
|
||||||
|
</div>;
|
||||||
|
|
||||||
|
renderField.propTypes = {
|
||||||
|
id: PropTypes.string,
|
||||||
|
title: PropTypes.string,
|
||||||
|
subtitle: PropTypes.string,
|
||||||
|
disabled: PropTypes.bool,
|
||||||
|
normalizeOnBlur: PropTypes.func,
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form onSubmit={handleSubmit}>
|
||||||
|
<div className="col-12">
|
||||||
|
<div className="form__group form__group--settings">
|
||||||
|
<Field
|
||||||
|
name={'rebinding_protection_enabled'}
|
||||||
|
type="checkbox"
|
||||||
|
component={CheckboxField}
|
||||||
|
placeholder={t('rebinding_protection_enabled')}
|
||||||
|
subtitle={t('rebinding_protection_enabled_desc')}
|
||||||
|
disabled={processingSetConfig}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{fields.map(renderField)}
|
||||||
|
|
||||||
|
<div className="card-actions">
|
||||||
|
<div className="btn-list">
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="btn btn-success btn-standard"
|
||||||
|
disabled={submitting || invalid || processingSetConfig}
|
||||||
|
>
|
||||||
|
<Trans>save_config</Trans>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
Form.propTypes = {
|
||||||
|
handleSubmit: PropTypes.func.isRequired,
|
||||||
|
submitting: PropTypes.bool.isRequired,
|
||||||
|
invalid: PropTypes.bool.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default reduxForm({ form: FORM_NAME.REBINDING })(Form);
|
||||||
36
client/src/components/Settings/Dns/Rebinding/index.js
Normal file
36
client/src/components/Settings/Dns/Rebinding/index.js
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
|
||||||
|
import Form from './Form';
|
||||||
|
import Card from '../../../ui/Card';
|
||||||
|
import { setDnsConfig } from '../../../../actions/dnsConfig';
|
||||||
|
|
||||||
|
const RebindingConfig = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const {
|
||||||
|
rebinding_protection_enabled, rebinding_allowed_hosts,
|
||||||
|
} = useSelector((state) => state.dnsConfig, shallowEqual);
|
||||||
|
|
||||||
|
const handleFormSubmit = (values) => {
|
||||||
|
dispatch(setDnsConfig(values));
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
title={t('rebinding_title')}
|
||||||
|
subtitle={t('rebinding_desc')}
|
||||||
|
bodyType="card-body box-body--settings"
|
||||||
|
>
|
||||||
|
<Form
|
||||||
|
initialValues={{
|
||||||
|
rebinding_protection_enabled,
|
||||||
|
rebinding_allowed_hosts,
|
||||||
|
}}
|
||||||
|
onSubmit={handleFormSubmit}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default RebindingConfig;
|
||||||
@@ -8,6 +8,7 @@ import Config from './Config';
|
|||||||
import PageTitle from '../../ui/PageTitle';
|
import PageTitle from '../../ui/PageTitle';
|
||||||
import Loading from '../../ui/Loading';
|
import Loading from '../../ui/Loading';
|
||||||
import CacheConfig from './Cache';
|
import CacheConfig from './Cache';
|
||||||
|
import RebindingConfig from './Rebinding';
|
||||||
import { getDnsConfig } from '../../../actions/dnsConfig';
|
import { getDnsConfig } from '../../../actions/dnsConfig';
|
||||||
import { getAccessList } from '../../../actions/access';
|
import { getAccessList } from '../../../actions/access';
|
||||||
|
|
||||||
@@ -33,6 +34,7 @@ const Dns = () => {
|
|||||||
<Config />
|
<Config />
|
||||||
<CacheConfig />
|
<CacheConfig />
|
||||||
<Access />
|
<Access />
|
||||||
|
<RebindingConfig />
|
||||||
</>}
|
</>}
|
||||||
</>;
|
</>;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -232,7 +232,7 @@ let Form = (props) => {
|
|||||||
<Trans
|
<Trans
|
||||||
values={{ link: 'letsencrypt.org' }}
|
values={{ link: 'letsencrypt.org' }}
|
||||||
components={[
|
components={[
|
||||||
<a target="_blank" rel="noopener noreferrer" href="https://letsencrypt.org/" key="0">
|
<a href="https://letsencrypt.org/" key="0">
|
||||||
link
|
link
|
||||||
</a>,
|
</a>,
|
||||||
]}
|
]}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import flow from 'lodash/flow';
|
|||||||
import { CheckboxField, toNumber } from '../../../helpers/form';
|
import { CheckboxField, toNumber } from '../../../helpers/form';
|
||||||
import {
|
import {
|
||||||
FILTERS_INTERVALS_HOURS,
|
FILTERS_INTERVALS_HOURS,
|
||||||
FILTERS_RELATIVE_LINK,
|
FILTERS_LINK,
|
||||||
FORM_NAME,
|
FORM_NAME,
|
||||||
} from '../../../helpers/constants';
|
} from '../../../helpers/constants';
|
||||||
|
|
||||||
@@ -45,7 +45,7 @@ const Form = (props) => {
|
|||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const components = {
|
const components = {
|
||||||
a: <a href={FILTERS_RELATIVE_LINK} rel="noopener noreferrer" />,
|
a: <a href={FILTERS_LINK} rel="noopener noreferrer" />,
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -6,50 +6,6 @@ import { useSelector } from 'react-redux';
|
|||||||
import Topline from './Topline';
|
import Topline from './Topline';
|
||||||
import { EMPTY_DATE } from '../../helpers/constants';
|
import { EMPTY_DATE } from '../../helpers/constants';
|
||||||
|
|
||||||
const EXPIRATION_ENUM = {
|
|
||||||
VALID: 'VALID',
|
|
||||||
EXPIRED: 'EXPIRED',
|
|
||||||
EXPIRING: 'EXPIRING',
|
|
||||||
};
|
|
||||||
|
|
||||||
const EXPIRATION_STATE = {
|
|
||||||
[EXPIRATION_ENUM.EXPIRED]: {
|
|
||||||
toplineType: 'danger',
|
|
||||||
i18nKey: 'topline_expired_certificate',
|
|
||||||
},
|
|
||||||
[EXPIRATION_ENUM.EXPIRING]: {
|
|
||||||
toplineType: 'warning',
|
|
||||||
i18nKey: 'topline_expiring_certificate',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const getExpirationFlags = (not_after) => {
|
|
||||||
const DAYS_BEFORE_EXPIRATION = 5;
|
|
||||||
|
|
||||||
const now = Date.now();
|
|
||||||
const isExpiring = isAfter(addDays(now, DAYS_BEFORE_EXPIRATION), not_after);
|
|
||||||
const isExpired = isAfter(now, not_after);
|
|
||||||
|
|
||||||
return {
|
|
||||||
isExpiring,
|
|
||||||
isExpired,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const getExpirationEnumKey = (not_after) => {
|
|
||||||
const { isExpiring, isExpired } = getExpirationFlags(not_after);
|
|
||||||
|
|
||||||
if (isExpired) {
|
|
||||||
return EXPIRATION_ENUM.EXPIRED;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isExpiring) {
|
|
||||||
return EXPIRATION_ENUM.EXPIRING;
|
|
||||||
}
|
|
||||||
|
|
||||||
return EXPIRATION_ENUM.VALID;
|
|
||||||
};
|
|
||||||
|
|
||||||
const EncryptionTopline = () => {
|
const EncryptionTopline = () => {
|
||||||
const not_after = useSelector((state) => state.encryption.not_after);
|
const not_after = useSelector((state) => state.encryption.not_after);
|
||||||
|
|
||||||
@@ -57,21 +13,30 @@ const EncryptionTopline = () => {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const expirationStateKey = getExpirationEnumKey(not_after);
|
const isAboutExpire = isAfter(addDays(Date.now(), 30), not_after);
|
||||||
|
const isExpired = isAfter(Date.now(), not_after);
|
||||||
|
|
||||||
if (expirationStateKey === EXPIRATION_ENUM.VALID) {
|
if (isExpired) {
|
||||||
return null;
|
return (
|
||||||
}
|
<Topline type="danger">
|
||||||
|
|
||||||
const { toplineType, i18nKey } = EXPIRATION_STATE[expirationStateKey];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Topline type={toplineType}>
|
|
||||||
<Trans components={[<a href="#encryption" key="0">link</a>]}>
|
<Trans components={[<a href="#encryption" key="0">link</a>]}>
|
||||||
{i18nKey}
|
topline_expired_certificate
|
||||||
</Trans>
|
</Trans>
|
||||||
</Topline>
|
</Topline>
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isAboutExpire) {
|
||||||
|
return (
|
||||||
|
<Topline type="warning">
|
||||||
|
<Trans components={[<a href="#encryption" key="0">link</a>]}>
|
||||||
|
topline_expiring_certificate
|
||||||
|
</Trans>
|
||||||
|
</Topline>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default EncryptionTopline;
|
export default EncryptionTopline;
|
||||||
|
|||||||
@@ -53,9 +53,9 @@ export const REPOSITORY = {
|
|||||||
export const PRIVACY_POLICY_LINK = 'https://adguard.com/privacy/home.html';
|
export const PRIVACY_POLICY_LINK = 'https://adguard.com/privacy/home.html';
|
||||||
export const PORT_53_FAQ_LINK = 'https://github.com/AdguardTeam/AdGuardHome/wiki/FAQ#bindinuse';
|
export const PORT_53_FAQ_LINK = 'https://github.com/AdguardTeam/AdGuardHome/wiki/FAQ#bindinuse';
|
||||||
export const UPSTREAM_CONFIGURATION_WIKI_LINK = 'https://github.com/AdguardTeam/AdGuardHome/wiki/Configuration#upstreams';
|
export const UPSTREAM_CONFIGURATION_WIKI_LINK = 'https://github.com/AdguardTeam/AdGuardHome/wiki/Configuration#upstreams';
|
||||||
export const GETTING_STARTED_LINK = 'https://github.com/AdguardTeam/AdGuardHome/wiki/Getting-Started#update';
|
export const FILTERS_LINK = '#filters';
|
||||||
|
|
||||||
export const FILTERS_RELATIVE_LINK = '#filters';
|
export const GETTING_STARTED_LINK = 'https://github.com/AdguardTeam/AdGuardHome/wiki/Getting-Started#update';
|
||||||
|
|
||||||
export const ADDRESS_IN_USE_TEXT = 'address already in use';
|
export const ADDRESS_IN_USE_TEXT = 'address already in use';
|
||||||
|
|
||||||
@@ -341,6 +341,7 @@ export const FILTERED_STATUS = {
|
|||||||
REWRITE_HOSTS: 'RewriteEtcHosts',
|
REWRITE_HOSTS: 'RewriteEtcHosts',
|
||||||
FILTERED_SAFE_SEARCH: 'FilteredSafeSearch',
|
FILTERED_SAFE_SEARCH: 'FilteredSafeSearch',
|
||||||
FILTERED_SAFE_BROWSING: 'FilteredSafeBrowsing',
|
FILTERED_SAFE_BROWSING: 'FilteredSafeBrowsing',
|
||||||
|
FILTERED_REBIND: 'FilteredRebind',
|
||||||
FILTERED_PARENTAL: 'FilteredParental',
|
FILTERED_PARENTAL: 'FilteredParental',
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -373,6 +374,10 @@ export const RESPONSE_FILTER = {
|
|||||||
QUERY: 'blocked_parental',
|
QUERY: 'blocked_parental',
|
||||||
LABEL: 'blocked_adult_websites',
|
LABEL: 'blocked_adult_websites',
|
||||||
},
|
},
|
||||||
|
BLOCKED_DNS_REBINDING: {
|
||||||
|
QUERY: 'blocked_dns_rebinding',
|
||||||
|
LABEL: 'blocked_dns_rebinding',
|
||||||
|
},
|
||||||
ALLOWED: {
|
ALLOWED: {
|
||||||
QUERY: 'whitelisted',
|
QUERY: 'whitelisted',
|
||||||
LABEL: 'allowed',
|
LABEL: 'allowed',
|
||||||
@@ -414,6 +419,10 @@ export const FILTERED_STATUS_TO_META_MAP = {
|
|||||||
LABEL: 'blocked_service',
|
LABEL: 'blocked_service',
|
||||||
COLOR: QUERY_STATUS_COLORS.RED,
|
COLOR: QUERY_STATUS_COLORS.RED,
|
||||||
},
|
},
|
||||||
|
[FILTERED_STATUS.FILTERED_REBIND]: {
|
||||||
|
LABEL: RESPONSE_FILTER.BLOCKED_DNS_REBINDING.LABEL,
|
||||||
|
COLOR: QUERY_STATUS_COLORS.RED,
|
||||||
|
},
|
||||||
[FILTERED_STATUS.FILTERED_SAFE_SEARCH]: {
|
[FILTERED_STATUS.FILTERED_SAFE_SEARCH]: {
|
||||||
LABEL: RESPONSE_FILTER.SAFE_SEARCH.LABEL,
|
LABEL: RESPONSE_FILTER.SAFE_SEARCH.LABEL,
|
||||||
COLOR: QUERY_STATUS_COLORS.YELLOW,
|
COLOR: QUERY_STATUS_COLORS.YELLOW,
|
||||||
@@ -509,6 +518,7 @@ export const FORM_NAME = {
|
|||||||
INSTALL: 'install',
|
INSTALL: 'install',
|
||||||
LOGIN: 'login',
|
LOGIN: 'login',
|
||||||
CACHE: 'cache',
|
CACHE: 'cache',
|
||||||
|
REBINDING: 'rebinding',
|
||||||
...DHCP_FORM_NAMES,
|
...DHCP_FORM_NAMES,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -687,7 +687,7 @@ export const sortIp = (a, b) => {
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn(e);
|
console.error(e);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ 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)
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ const renderItem = ({
|
|||||||
|
|
||||||
return <li key={ip}>{isDns
|
return <li key={ip}>{isDns
|
||||||
? <strong>{dnsAddress}</strong>
|
? <strong>{dnsAddress}</strong>
|
||||||
: <a href={webAddress} target="_blank" rel="noopener noreferrer">{webAddress}</a>
|
: <a href={webAddress}>{webAddress}</a>
|
||||||
}
|
}
|
||||||
</li>;
|
</li>;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ const dnsConfig = handleActions(
|
|||||||
blocking_ipv6,
|
blocking_ipv6,
|
||||||
upstream_dns,
|
upstream_dns,
|
||||||
bootstrap_dns,
|
bootstrap_dns,
|
||||||
|
rebinding_allowed_hosts,
|
||||||
...values
|
...values
|
||||||
} = payload;
|
} = payload;
|
||||||
|
|
||||||
@@ -24,8 +25,9 @@ const dnsConfig = handleActions(
|
|||||||
...values,
|
...values,
|
||||||
blocking_ipv4: blocking_ipv4 || DEFAULT_BLOCKING_IPV4,
|
blocking_ipv4: blocking_ipv4 || DEFAULT_BLOCKING_IPV4,
|
||||||
blocking_ipv6: blocking_ipv6 || DEFAULT_BLOCKING_IPV6,
|
blocking_ipv6: blocking_ipv6 || DEFAULT_BLOCKING_IPV6,
|
||||||
upstream_dns: (upstream_dns && upstream_dns.join('\n')) || '',
|
upstream_dns: upstream_dns?.join('\n') || '',
|
||||||
bootstrap_dns: (bootstrap_dns && bootstrap_dns.join('\n')) || '',
|
bootstrap_dns: bootstrap_dns?.join('\n') || '',
|
||||||
|
rebinding_allowed_hosts: rebinding_allowed_hosts?.join('\n') || '',
|
||||||
processingGetConfig: false,
|
processingGetConfig: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|||||||
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=
|
||||||
|
|||||||
@@ -65,9 +65,3 @@ func (e *manyError) Unwrap() error {
|
|||||||
|
|
||||||
return e.underlying[0]
|
return e.underlying[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
// wrapper is a copy of the hidden errors.wrapper interface for tests, linting,
|
|
||||||
// etc.
|
|
||||||
type wrapper interface {
|
|
||||||
Unwrap() error
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -41,8 +41,6 @@ func TestError_Error(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestError_Unwrap(t *testing.T) {
|
func TestError_Unwrap(t *testing.T) {
|
||||||
var _ wrapper = &manyError{}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
errSimple = iota
|
errSimple = iota
|
||||||
errWrapped
|
errWrapped
|
||||||
|
|||||||
@@ -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,15 @@ 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
|
// NotFilteredError - there was a transitive error during check
|
||||||
// checking. Reserved, currently unused.
|
|
||||||
NotFilteredError
|
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
|
||||||
@@ -147,46 +146,37 @@ const (
|
|||||||
FilteredSafeSearch
|
FilteredSafeSearch
|
||||||
// FilteredBlockedService - the host is blocked by "blocked services" settings
|
// FilteredBlockedService - the host is blocked by "blocked services" settings
|
||||||
FilteredBlockedService
|
FilteredBlockedService
|
||||||
|
// FilteredRebind - the request was blocked due to DNS rebinding protection
|
||||||
|
FilteredRebind
|
||||||
|
|
||||||
// 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",
|
||||||
|
"FilteredRebind",
|
||||||
|
|
||||||
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 +191,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 +202,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 +213,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 +247,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 +259,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 +275,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 +292,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 +327,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 +354,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 +415,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 +426,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 +447,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 +464,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 +506,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 +575,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 +589,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 +600,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 +616,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 +639,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 +710,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 +732,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 +770,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()
|
||||||
|
|
||||||
@@ -850,3 +780,12 @@ func (d *DNSFilter) Start() {
|
|||||||
d.registerBlockedServicesHandlers()
|
d.registerBlockedServicesHandlers()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// stats
|
||||||
|
//
|
||||||
|
|
||||||
|
// GetStats return dns filtering stats since startup.
|
||||||
|
func (d *Dnsfilter) GetStats() Stats {
|
||||||
|
return gctx.stats
|
||||||
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
@@ -76,6 +76,11 @@ type FilteringConfig struct {
|
|||||||
CacheMinTTL uint32 `yaml:"cache_ttl_min"` // override TTL value (minimum) received from upstream server
|
CacheMinTTL uint32 `yaml:"cache_ttl_min"` // override TTL value (minimum) received from upstream server
|
||||||
CacheMaxTTL uint32 `yaml:"cache_ttl_max"` // override TTL value (maximum) received from upstream server
|
CacheMaxTTL uint32 `yaml:"cache_ttl_max"` // override TTL value (maximum) received from upstream server
|
||||||
|
|
||||||
|
// DNS rebinding protection settings
|
||||||
|
// --
|
||||||
|
RebindingProtectionEnabled bool `yaml:"rebinding_protection_enabled"`
|
||||||
|
RebindingAllowedHosts []string `yaml:"rebinding_allowed_hosts"`
|
||||||
|
|
||||||
// Other settings
|
// Other settings
|
||||||
// --
|
// --
|
||||||
|
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ func (s *Server) handleDNSRequest(_ *proxy.Proxy, d *proxy.DNSContext) error {
|
|||||||
processFilteringBeforeRequest,
|
processFilteringBeforeRequest,
|
||||||
processUpstream,
|
processUpstream,
|
||||||
processDNSSECAfterResponse,
|
processDNSSECAfterResponse,
|
||||||
|
processRebindingFilteringAfterResponse,
|
||||||
processFilteringAfterResponse,
|
processFilteringAfterResponse,
|
||||||
s.ipset.process,
|
s.ipset.process,
|
||||||
processQueryLogsAndStats,
|
processQueryLogsAndStats,
|
||||||
@@ -366,9 +367,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 +378,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,11 +48,12 @@ 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
|
||||||
access *accessCtx
|
access *accessCtx
|
||||||
|
rebinding *dnsRebindChecker
|
||||||
|
|
||||||
ipset ipsetCtx
|
ipset ipsetCtx
|
||||||
|
|
||||||
@@ -74,7 +75,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
|
||||||
@@ -122,6 +123,7 @@ func (s *Server) WriteDiskConfig(c *FilteringConfig) {
|
|||||||
c.DisallowedClients = stringArrayDup(sc.DisallowedClients)
|
c.DisallowedClients = stringArrayDup(sc.DisallowedClients)
|
||||||
c.BlockedHosts = stringArrayDup(sc.BlockedHosts)
|
c.BlockedHosts = stringArrayDup(sc.BlockedHosts)
|
||||||
c.UpstreamDNS = stringArrayDup(sc.UpstreamDNS)
|
c.UpstreamDNS = stringArrayDup(sc.UpstreamDNS)
|
||||||
|
c.RebindingAllowedHosts = stringArrayDup(sc.RebindingAllowedHosts)
|
||||||
s.RUnlock()
|
s.RUnlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -221,6 +223,13 @@ func (s *Server) Prepare(config *ServerConfig) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialize DNS rebinding module
|
||||||
|
// --
|
||||||
|
s.rebinding, err = newRebindChecker(s.conf.RebindingAllowedHosts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// Register web handlers if necessary
|
// Register web handlers if necessary
|
||||||
// --
|
// --
|
||||||
if !webRegistered && s.conf.HTTPRegister != nil {
|
if !webRegistered && s.conf.HTTPRegister != nil {
|
||||||
|
|||||||
@@ -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)
|
||||||
@@ -738,6 +738,100 @@ func TestRewrite(t *testing.T) {
|
|||||||
_ = s.Stop()
|
_ = s.Stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBlockedDNSRebinding(t *testing.T) {
|
||||||
|
s := createTestServer(t)
|
||||||
|
|
||||||
|
err := s.Start()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to start server: %s", err)
|
||||||
|
}
|
||||||
|
addr := s.dnsProxy.Addr(proxy.ProtoUDP)
|
||||||
|
|
||||||
|
//
|
||||||
|
// DNS rebinding protection
|
||||||
|
//
|
||||||
|
req := dns.Msg{}
|
||||||
|
req.Id = dns.Id()
|
||||||
|
req.RecursionDesired = true
|
||||||
|
req.Question = []dns.Question{
|
||||||
|
{Name: "192-168-1-250.nip.io.", Qtype: dns.TypeA, Qclass: dns.ClassINET},
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Lock()
|
||||||
|
s.conf.RebindingProtectionEnabled = true
|
||||||
|
s.Unlock()
|
||||||
|
|
||||||
|
reply, err := dns.Exchange(&req, addr.String())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Couldn't talk to server %s: %s", addr, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(reply.Answer) != 1 {
|
||||||
|
t.Fatalf("DNS server %s returned reply with wrong number of answers - %d", addr, len(reply.Answer))
|
||||||
|
}
|
||||||
|
|
||||||
|
a, ok := reply.Answer[0].(*dns.A)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("DNS server %s returned wrong answer type instead of A: %v", addr, reply.Answer[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
if !net.IPv4zero.Equal(a.A) {
|
||||||
|
t.Fatalf("DNS server %s returned wrong answer instead of 0.0.0.0: %v", addr, a.A)
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Lock()
|
||||||
|
s.conf.RebindingProtectionEnabled = false
|
||||||
|
s.Unlock()
|
||||||
|
|
||||||
|
reply, err = dns.Exchange(&req, addr.String())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Couldn't talk to server %s: %s", addr, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(reply.Answer) != 1 {
|
||||||
|
t.Fatalf("DNS server %s returned reply with wrong number of answers - %d", addr, len(reply.Answer))
|
||||||
|
}
|
||||||
|
|
||||||
|
a, ok = reply.Answer[0].(*dns.A)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("DNS server %s returned wrong answer type instead of A: %v", addr, reply.Answer[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
if !net.IPv4(192, 168, 1, 250).Equal(a.A) {
|
||||||
|
t.Fatalf("DNS server %s returned wrong answer instead of 192.168.1.250: %v", addr, a.A)
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Lock()
|
||||||
|
s.conf.RebindingProtectionEnabled = true
|
||||||
|
s.rebinding, _ = newRebindChecker([]string{
|
||||||
|
"||nip.io^",
|
||||||
|
})
|
||||||
|
s.Unlock()
|
||||||
|
|
||||||
|
reply, err = dns.Exchange(&req, addr.String())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Couldn't talk to server %s: %s", addr, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(reply.Answer) != 1 {
|
||||||
|
t.Fatalf("DNS server %s returned reply with wrong number of answers - %d", addr, len(reply.Answer))
|
||||||
|
}
|
||||||
|
|
||||||
|
a, ok = reply.Answer[0].(*dns.A)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("DNS server %s returned wrong answer type instead of A: %v", addr, reply.Answer[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
if !net.IPv4(192, 168, 1, 250).Equal(a.A) {
|
||||||
|
t.Fatalf("DNS server %s returned wrong answer instead of 192.168.1.250: %v", addr, a.A)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = s.Stop()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("DNS server failed to stop: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func createTestServer(t *testing.T) *Server {
|
func createTestServer(t *testing.T) *Server {
|
||||||
rules := `||nxdomain.example.org
|
rules := `||nxdomain.example.org
|
||||||
||null.example.org^
|
||null.example.org^
|
||||||
@@ -863,8 +957,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 +994,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
|
||||||
@@ -123,8 +113,8 @@ func (s *Server) filterDNSResponse(ctx *dnsContext) (*dnsfilter.Result, error) {
|
|||||||
|
|
||||||
switch v := a.(type) {
|
switch v := a.(type) {
|
||||||
case *dns.CNAME:
|
case *dns.CNAME:
|
||||||
log.Debug("DNSFwd: Checking CNAME %s for %s", v.Target, v.Hdr.Name)
|
|
||||||
host = strings.TrimSuffix(v.Target, ".")
|
host = strings.TrimSuffix(v.Target, ".")
|
||||||
|
log.Debug("DNSFwd: Checking CNAME %s for %s", v.Target, v.Hdr.Name)
|
||||||
|
|
||||||
case *dns.A:
|
case *dns.A:
|
||||||
host = v.A.String()
|
host = v.A.String()
|
||||||
|
|||||||
@@ -37,6 +37,9 @@ type dnsConfig struct {
|
|||||||
CacheSize *uint32 `json:"cache_size"`
|
CacheSize *uint32 `json:"cache_size"`
|
||||||
CacheMinTTL *uint32 `json:"cache_ttl_min"`
|
CacheMinTTL *uint32 `json:"cache_ttl_min"`
|
||||||
CacheMaxTTL *uint32 `json:"cache_ttl_max"`
|
CacheMaxTTL *uint32 `json:"cache_ttl_max"`
|
||||||
|
|
||||||
|
RebindingProtectionEnabled *bool `json:"rebinding_protection_enabled"`
|
||||||
|
RebindingAllowedHosts *[]string `json:"rebinding_allowed_hosts"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) getDNSConfig() dnsConfig {
|
func (s *Server) getDNSConfig() dnsConfig {
|
||||||
@@ -61,23 +64,27 @@ func (s *Server) getDNSConfig() dnsConfig {
|
|||||||
} else if s.conf.AllServers {
|
} else if s.conf.AllServers {
|
||||||
upstreamMode = "parallel"
|
upstreamMode = "parallel"
|
||||||
}
|
}
|
||||||
|
rebindingEnabled := s.conf.RebindingProtectionEnabled
|
||||||
|
rebindingAllowedHosts := stringArrayDup(s.conf.RebindingAllowedHosts)
|
||||||
s.RUnlock()
|
s.RUnlock()
|
||||||
return dnsConfig{
|
return dnsConfig{
|
||||||
Upstreams: &upstreams,
|
Upstreams: &upstreams,
|
||||||
UpstreamsFile: &upstreamFile,
|
UpstreamsFile: &upstreamFile,
|
||||||
Bootstraps: &bootstraps,
|
Bootstraps: &bootstraps,
|
||||||
ProtectionEnabled: &protectionEnabled,
|
ProtectionEnabled: &protectionEnabled,
|
||||||
BlockingMode: &blockingMode,
|
BlockingMode: &blockingMode,
|
||||||
BlockingIPv4: &BlockingIPv4,
|
BlockingIPv4: &BlockingIPv4,
|
||||||
BlockingIPv6: &BlockingIPv6,
|
BlockingIPv6: &BlockingIPv6,
|
||||||
RateLimit: &Ratelimit,
|
RateLimit: &Ratelimit,
|
||||||
EDNSCSEnabled: &EnableEDNSClientSubnet,
|
EDNSCSEnabled: &EnableEDNSClientSubnet,
|
||||||
DNSSECEnabled: &EnableDNSSEC,
|
DNSSECEnabled: &EnableDNSSEC,
|
||||||
DisableIPv6: &AAAADisabled,
|
DisableIPv6: &AAAADisabled,
|
||||||
CacheSize: &CacheSize,
|
CacheSize: &CacheSize,
|
||||||
CacheMinTTL: &CacheMinTTL,
|
CacheMinTTL: &CacheMinTTL,
|
||||||
CacheMaxTTL: &CacheMaxTTL,
|
CacheMaxTTL: &CacheMaxTTL,
|
||||||
UpstreamMode: &upstreamMode,
|
UpstreamMode: &upstreamMode,
|
||||||
|
RebindingProtectionEnabled: &rebindingEnabled,
|
||||||
|
RebindingAllowedHosts: &rebindingAllowedHosts,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,12 +174,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
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -302,6 +308,8 @@ func (s *Server) setConfig(dc dnsConfig) (restart bool) {
|
|||||||
s.conf.FastestAddr = false
|
s.conf.FastestAddr = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
restart = restart || s.setRebindingConfig(dc)
|
||||||
s.Unlock()
|
s.Unlock()
|
||||||
s.conf.ConfigModified()
|
s.conf.ConfigModified()
|
||||||
return restart
|
return restart
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ func TestDNSForwardHTTTP_handleGetConfig(t *testing.T) {
|
|||||||
conf: func() ServerConfig {
|
conf: func() ServerConfig {
|
||||||
return defaultConf
|
return defaultConf
|
||||||
},
|
},
|
||||||
want: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0}\n",
|
want: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0,\"rebinding_protection_enabled\":false,\"rebinding_allowed_hosts\":[]}\n",
|
||||||
}, {
|
}, {
|
||||||
name: "fastest_addr",
|
name: "fastest_addr",
|
||||||
conf: func() ServerConfig {
|
conf: func() ServerConfig {
|
||||||
@@ -37,7 +37,7 @@ func TestDNSForwardHTTTP_handleGetConfig(t *testing.T) {
|
|||||||
conf.FastestAddr = true
|
conf.FastestAddr = true
|
||||||
return conf
|
return conf
|
||||||
},
|
},
|
||||||
want: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"fastest_addr\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0}\n",
|
want: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"fastest_addr\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0,\"rebinding_protection_enabled\":false,\"rebinding_allowed_hosts\":[]}\n",
|
||||||
}, {
|
}, {
|
||||||
name: "parallel",
|
name: "parallel",
|
||||||
conf: func() ServerConfig {
|
conf: func() ServerConfig {
|
||||||
@@ -45,7 +45,7 @@ func TestDNSForwardHTTTP_handleGetConfig(t *testing.T) {
|
|||||||
conf.AllServers = true
|
conf.AllServers = true
|
||||||
return conf
|
return conf
|
||||||
},
|
},
|
||||||
want: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"parallel\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0}\n",
|
want: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"parallel\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0,\"rebinding_protection_enabled\":false,\"rebinding_allowed_hosts\":[]}\n",
|
||||||
}}
|
}}
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
@@ -73,7 +73,7 @@ func TestDNSForwardHTTTP_handleSetConfig(t *testing.T) {
|
|||||||
|
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
|
|
||||||
const defaultConfJSON = "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0}\n"
|
const defaultConfJSON = "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0,\"rebinding_protection_enabled\":false,\"rebinding_allowed_hosts\":[]}\n"
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
req string
|
req string
|
||||||
@@ -83,52 +83,52 @@ func TestDNSForwardHTTTP_handleSetConfig(t *testing.T) {
|
|||||||
name: "upstream_dns",
|
name: "upstream_dns",
|
||||||
req: "{\"upstream_dns\":[\"8.8.8.8:77\",\"8.8.4.4:77\"]}",
|
req: "{\"upstream_dns\":[\"8.8.8.8:77\",\"8.8.4.4:77\"]}",
|
||||||
wantSet: "",
|
wantSet: "",
|
||||||
wantGet: "{\"upstream_dns\":[\"8.8.8.8:77\",\"8.8.4.4:77\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0}\n",
|
wantGet: "{\"upstream_dns\":[\"8.8.8.8:77\",\"8.8.4.4:77\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0,\"rebinding_protection_enabled\":false,\"rebinding_allowed_hosts\":[]}\n",
|
||||||
}, {
|
}, {
|
||||||
name: "bootstraps",
|
name: "bootstraps",
|
||||||
req: "{\"bootstrap_dns\":[\"9.9.9.10\"]}",
|
req: "{\"bootstrap_dns\":[\"9.9.9.10\"]}",
|
||||||
wantSet: "",
|
wantSet: "",
|
||||||
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0}\n",
|
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0,\"rebinding_protection_enabled\":false,\"rebinding_allowed_hosts\":[]}\n",
|
||||||
}, {
|
}, {
|
||||||
name: "blocking_mode_good",
|
name: "blocking_mode_good",
|
||||||
req: "{\"blocking_mode\":\"refused\"}",
|
req: "{\"blocking_mode\":\"refused\"}",
|
||||||
wantSet: "",
|
wantSet: "",
|
||||||
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"refused\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0}\n",
|
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"refused\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0,\"rebinding_protection_enabled\":false,\"rebinding_allowed_hosts\":[]}\n",
|
||||||
}, {
|
}, {
|
||||||
name: "blocking_mode_bad",
|
name: "blocking_mode_bad",
|
||||||
req: "{\"blocking_mode\":\"custom_ip\"}",
|
req: "{\"blocking_mode\":\"custom_ip\"}",
|
||||||
wantSet: "blocking_mode: incorrect value\n",
|
wantSet: "blocking_mode: incorrect value\n",
|
||||||
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0}\n",
|
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0,\"rebinding_protection_enabled\":false,\"rebinding_allowed_hosts\":[]}\n",
|
||||||
}, {
|
}, {
|
||||||
name: "ratelimit",
|
name: "ratelimit",
|
||||||
req: "{\"ratelimit\":6}",
|
req: "{\"ratelimit\":6}",
|
||||||
wantSet: "",
|
wantSet: "",
|
||||||
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":6,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0}\n",
|
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":6,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0,\"rebinding_protection_enabled\":false,\"rebinding_allowed_hosts\":[]}\n",
|
||||||
}, {
|
}, {
|
||||||
name: "edns_cs_enabled",
|
name: "edns_cs_enabled",
|
||||||
req: "{\"edns_cs_enabled\":true}",
|
req: "{\"edns_cs_enabled\":true}",
|
||||||
wantSet: "",
|
wantSet: "",
|
||||||
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":true,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0}\n",
|
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":true,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0,\"rebinding_protection_enabled\":false,\"rebinding_allowed_hosts\":[]}\n",
|
||||||
}, {
|
}, {
|
||||||
name: "dnssec_enabled",
|
name: "dnssec_enabled",
|
||||||
req: "{\"dnssec_enabled\":true}",
|
req: "{\"dnssec_enabled\":true}",
|
||||||
wantSet: "",
|
wantSet: "",
|
||||||
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":true,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0}\n",
|
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":true,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0,\"rebinding_protection_enabled\":false,\"rebinding_allowed_hosts\":[]}\n",
|
||||||
}, {
|
}, {
|
||||||
name: "cache_size",
|
name: "cache_size",
|
||||||
req: "{\"cache_size\":1024}",
|
req: "{\"cache_size\":1024}",
|
||||||
wantSet: "",
|
wantSet: "",
|
||||||
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":1024,\"cache_ttl_min\":0,\"cache_ttl_max\":0}\n",
|
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":1024,\"cache_ttl_min\":0,\"cache_ttl_max\":0,\"rebinding_protection_enabled\":false,\"rebinding_allowed_hosts\":[]}\n",
|
||||||
}, {
|
}, {
|
||||||
name: "upstream_mode_parallel",
|
name: "upstream_mode_parallel",
|
||||||
req: "{\"upstream_mode\":\"parallel\"}",
|
req: "{\"upstream_mode\":\"parallel\"}",
|
||||||
wantSet: "",
|
wantSet: "",
|
||||||
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"parallel\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0}\n",
|
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"parallel\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0,\"rebinding_protection_enabled\":false,\"rebinding_allowed_hosts\":[]}\n",
|
||||||
}, {
|
}, {
|
||||||
name: "upstream_mode_fastest_addr",
|
name: "upstream_mode_fastest_addr",
|
||||||
req: "{\"upstream_mode\":\"fastest_addr\"}",
|
req: "{\"upstream_mode\":\"fastest_addr\"}",
|
||||||
wantSet: "",
|
wantSet: "",
|
||||||
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"fastest_addr\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0}\n",
|
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"fastest_addr\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0,\"rebinding_protection_enabled\":false,\"rebinding_allowed_hosts\":[]}\n",
|
||||||
}, {
|
}, {
|
||||||
name: "upstream_dns_bad",
|
name: "upstream_dns_bad",
|
||||||
req: "{\"upstream_dns\":[\"\"]}",
|
req: "{\"upstream_dns\":[\"\"]}",
|
||||||
|
|||||||
@@ -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{}
|
||||||
|
|||||||
227
internal/dnsforward/rebind.go
Normal file
227
internal/dnsforward/rebind.go
Normal file
@@ -0,0 +1,227 @@
|
|||||||
|
// DNS Rebinding protection
|
||||||
|
|
||||||
|
package dnsforward
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/AdguardTeam/AdGuardHome/internal/dnsfilter"
|
||||||
|
"github.com/AdguardTeam/golibs/log"
|
||||||
|
"github.com/AdguardTeam/urlfilter"
|
||||||
|
"github.com/AdguardTeam/urlfilter/filterlist"
|
||||||
|
"github.com/miekg/dns"
|
||||||
|
)
|
||||||
|
|
||||||
|
type dnsRebindChecker struct {
|
||||||
|
allowDomainEngine *urlfilter.DNSEngine
|
||||||
|
}
|
||||||
|
|
||||||
|
func newRebindChecker(allowedHosts []string) (*dnsRebindChecker, error) {
|
||||||
|
buf := strings.Builder{}
|
||||||
|
for _, s := range allowedHosts {
|
||||||
|
buf.WriteString(s)
|
||||||
|
buf.WriteString("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
rulesStorage, err := filterlist.NewRuleStorage([]filterlist.RuleList{
|
||||||
|
&filterlist.StringRuleList{
|
||||||
|
ID: int(0),
|
||||||
|
RulesText: buf.String(),
|
||||||
|
IgnoreCosmetic: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &dnsRebindChecker{
|
||||||
|
allowDomainEngine: urlfilter.NewDNSEngine(rulesStorage),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *dnsRebindChecker) isAllowedDomain(domain string) bool {
|
||||||
|
_, ok := c.allowDomainEngine.Match(domain)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsPrivate reports whether ip is a private address, according to
|
||||||
|
// RFC 1918 (IPv4 addresses) and RFC 4193 (IPv6 addresses).
|
||||||
|
func (*dnsRebindChecker) isPrivate(ip net.IP) bool {
|
||||||
|
//TODO: remove once https://github.com/golang/go/pull/42793 makes it to stdlib
|
||||||
|
if ip4 := ip.To4(); ip4 != nil {
|
||||||
|
return ip4[0] == 10 ||
|
||||||
|
(ip4[0] == 172 && ip4[1]&0xf0 == 16) ||
|
||||||
|
(ip4[0] == 192 && ip4[1] == 168)
|
||||||
|
}
|
||||||
|
return len(ip) == net.IPv6len && ip[0]&0xfe == 0xfc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *dnsRebindChecker) isRebindHost(host string) bool {
|
||||||
|
if ip := net.ParseIP(host); ip != nil {
|
||||||
|
return c.isRebindIP(ip)
|
||||||
|
}
|
||||||
|
|
||||||
|
return host == "localhost"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *dnsRebindChecker) isLocalNetworkV4(ip4 net.IP) bool {
|
||||||
|
switch {
|
||||||
|
case ip4[0] == 0:
|
||||||
|
/* 0.0.0.0/8 (RFC 5735 section 3. "here" network) */
|
||||||
|
case ip4[0] == 10:
|
||||||
|
/* 10.0.0.0/8 (private) */
|
||||||
|
case ip4[0] == 172 && ip4[1]&0x10 == 0x10:
|
||||||
|
/* 172.16.0.0/12 (private) */
|
||||||
|
case ip4[0] == 169 && ip4[1] == 254:
|
||||||
|
/* 169.254.0.0/16 (zeroconf) */
|
||||||
|
case ip4[0] == 192 && ip4[1] == 0 && ip4[2] == 2:
|
||||||
|
/* 192.0.2.0/24 (test-net) */
|
||||||
|
case ip4[0] == 198 && ip4[1] == 51 && ip4[2] == 100:
|
||||||
|
/* 198.51.100.0/24(test-net) */
|
||||||
|
case ip4[0] == 203 && ip4[1] == 0 && ip4[2] == 113:
|
||||||
|
/* 203.0.113.0/24 (test-net) */
|
||||||
|
case ip4.Equal(net.IPv4bcast):
|
||||||
|
/* 255.255.255.255/32 (broadcast)*/
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *dnsRebindChecker) isLocalNetworkV6(ip6 net.IP) bool {
|
||||||
|
return ip6.Equal(net.IPv6zero) ||
|
||||||
|
ip6.Equal(net.IPv6unspecified) ||
|
||||||
|
ip6.Equal(net.IPv6interfacelocalallnodes) ||
|
||||||
|
ip6.Equal(net.IPv6linklocalallnodes) ||
|
||||||
|
ip6.Equal(net.IPv6linklocalallrouters)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *dnsRebindChecker) isRebindIP(ip net.IP) bool {
|
||||||
|
// This is compatible with dnsmasq definition
|
||||||
|
// See: https://github.com/imp/dnsmasq/blob/4e7694d7107d2299f4aaededf8917fceb5dfb924/src/rfc1035.c#L412
|
||||||
|
|
||||||
|
rebind := false
|
||||||
|
if ip4 := ip.To4(); ip4 != nil {
|
||||||
|
rebind = c.isLocalNetworkV4(ip4)
|
||||||
|
} else {
|
||||||
|
rebind = c.isLocalNetworkV6(ip)
|
||||||
|
}
|
||||||
|
|
||||||
|
return rebind || c.isPrivate(ip) || ip.IsLoopback()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks DNS rebinding attacks
|
||||||
|
// Note both whitelisted and cached hosts will bypass rebinding check (see: processFilteringAfterResponse()).
|
||||||
|
func (s *Server) isResponseRebind(domain, host string) bool {
|
||||||
|
if !s.conf.RebindingProtectionEnabled {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if log.GetLevel() >= log.DEBUG {
|
||||||
|
timer := log.StartTimer()
|
||||||
|
defer timer.LogElapsed("DNS Rebinding check for %s -> %s", domain, host)
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.rebinding.isAllowedDomain(domain) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.rebinding.isRebindHost(host)
|
||||||
|
}
|
||||||
|
|
||||||
|
func processRebindingFilteringAfterResponse(ctx *dnsContext) int {
|
||||||
|
s := ctx.srv
|
||||||
|
d := ctx.proxyCtx
|
||||||
|
res := ctx.result
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if !ctx.responseFromUpstream || res.Reason == dnsfilter.ReasonRewrite {
|
||||||
|
return resultDone
|
||||||
|
}
|
||||||
|
|
||||||
|
originalRes := d.Res
|
||||||
|
ctx.result, err = s.preventRebindResponse(ctx)
|
||||||
|
if err != nil {
|
||||||
|
ctx.err = err
|
||||||
|
return resultError
|
||||||
|
}
|
||||||
|
if ctx.result != nil {
|
||||||
|
ctx.origResp = originalRes // matched by response
|
||||||
|
} else {
|
||||||
|
ctx.result = &dnsfilter.Result{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultDone
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) setRebindingConfig(dc dnsConfig) bool {
|
||||||
|
restart := false
|
||||||
|
|
||||||
|
if dc.RebindingProtectionEnabled != nil {
|
||||||
|
s.conf.RebindingProtectionEnabled = *dc.RebindingProtectionEnabled
|
||||||
|
}
|
||||||
|
|
||||||
|
if dc.RebindingAllowedHosts != nil {
|
||||||
|
s.conf.RebindingAllowedHosts = *dc.RebindingAllowedHosts
|
||||||
|
restart = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return restart
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) preventRebindResponse(ctx *dnsContext) (*dnsfilter.Result, error) {
|
||||||
|
d := ctx.proxyCtx
|
||||||
|
|
||||||
|
for _, a := range d.Res.Answer {
|
||||||
|
m := ""
|
||||||
|
domainName := ""
|
||||||
|
host := ""
|
||||||
|
|
||||||
|
switch v := a.(type) {
|
||||||
|
case *dns.CNAME:
|
||||||
|
host = strings.TrimSuffix(v.Target, ".")
|
||||||
|
domainName = v.Hdr.Name
|
||||||
|
m = fmt.Sprintf("DNSRebind: Checking CNAME %s for %s", v.Target, v.Hdr.Name)
|
||||||
|
|
||||||
|
case *dns.A:
|
||||||
|
host = v.A.String()
|
||||||
|
domainName = v.Hdr.Name
|
||||||
|
m = fmt.Sprintf("DNSRebind: Checking record A (%s) for %s", host, v.Hdr.Name)
|
||||||
|
|
||||||
|
case *dns.AAAA:
|
||||||
|
host = v.AAAA.String()
|
||||||
|
domainName = v.Hdr.Name
|
||||||
|
m = fmt.Sprintf("DNSRebind: Checking record AAAA (%s) for %s", host, v.Hdr.Name)
|
||||||
|
|
||||||
|
default:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
s.RLock()
|
||||||
|
if !s.conf.RebindingProtectionEnabled {
|
||||||
|
s.RUnlock()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debug(m)
|
||||||
|
blocked := s.isResponseRebind(strings.TrimSuffix(domainName, "."), host)
|
||||||
|
s.RUnlock()
|
||||||
|
|
||||||
|
if blocked {
|
||||||
|
res := &dnsfilter.Result{
|
||||||
|
IsFiltered: true,
|
||||||
|
Reason: dnsfilter.FilteredRebind,
|
||||||
|
Rule: "adguard-rebind-protection",
|
||||||
|
}
|
||||||
|
|
||||||
|
d.Res = s.genDNSFilterMessage(d, res)
|
||||||
|
log.Debug("DNSRebind: Matched %s by response: %s", d.Req.Question[0].Name, host)
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
113
internal/dnsforward/rebind_test.go
Normal file
113
internal/dnsforward/rebind_test.go
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
package dnsforward
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/rand"
|
||||||
|
"net"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRebindingPrivateAddresses(t *testing.T) {
|
||||||
|
c, _ := newRebindChecker(nil)
|
||||||
|
|
||||||
|
r1 := byte(rand.Int31() & 0xFE)
|
||||||
|
r2 := byte(rand.Int31() & 0xFE)
|
||||||
|
r3 := byte(rand.Int31() & 0xFE)
|
||||||
|
|
||||||
|
for _, ip := range []net.IP{
|
||||||
|
net.IPv4(0, r1, r2, r3), /* 0.0.0.0/8 (RFC 5735 section 3. "here" network) */
|
||||||
|
net.IPv4(127, r1, r2, r3), /* 127.0.0.0/8 (loopback) */
|
||||||
|
net.IPv4(10, r1, r2, r3), /* 10.0.0.0/8 (private) */
|
||||||
|
net.IPv4(172, 0x10|byte(1&rand.Int31()), r2, r3), /* 172.16.0.0/12 (private) */
|
||||||
|
net.IPv4(192, 168, r2, r3), /* 192.168.0.0/16 (private) */
|
||||||
|
net.IPv4(169, 254, r2, r3), /* 169.254.0.0/16 (zeroconf) */
|
||||||
|
net.IPv4(192, 0, 2, r3), /* 192.0.2.0/24 (test-net) */
|
||||||
|
net.IPv4(198, 51, 100, r3), /* 198.51.100.0/24(test-net) */
|
||||||
|
net.IPv4(203, 0, 113, r3), /* 203.0.113.0/24 (test-net) */
|
||||||
|
net.IPv4(255, 255, 255, 255), /* 255.255.255.255/32 (broadcast)*/
|
||||||
|
|
||||||
|
/* RFC 6303 4.3 (unspecified & loopback) */
|
||||||
|
net.IPv6zero,
|
||||||
|
net.IPv6unspecified,
|
||||||
|
|
||||||
|
/* RFC 6303 4.4 */
|
||||||
|
/* RFC 6303 4.5 */
|
||||||
|
/* RFC 6303 4.6 */
|
||||||
|
net.IPv6interfacelocalallnodes,
|
||||||
|
net.IPv6linklocalallnodes,
|
||||||
|
net.IPv6linklocalallrouters,
|
||||||
|
|
||||||
|
/* (TODO) Check IPv4-mapped IPv6 addresses */
|
||||||
|
} {
|
||||||
|
assert.Truef(t, c.isRebindIP(ip), "%s is not a rebind", ip)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRebindLocalhost(t *testing.T) {
|
||||||
|
c := &dnsRebindChecker{}
|
||||||
|
assert.False(t, c.isRebindHost("example.com"))
|
||||||
|
assert.False(t, c.isRebindHost("200.0.0.1"))
|
||||||
|
assert.True(t, c.isRebindHost("127.0.0.1"))
|
||||||
|
assert.True(t, c.isRebindHost("localhost"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsResponseRebind(t *testing.T) {
|
||||||
|
c, _ := newRebindChecker([]string{
|
||||||
|
"||totally-safe.com^",
|
||||||
|
})
|
||||||
|
s := &Server{
|
||||||
|
rebinding: c,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, host := range []string{
|
||||||
|
"0.1.2.3", /* 0.0.0.0/8 (RFC 5735 section 3. "here" network) */
|
||||||
|
"127.1.2.3", /* 127.0.0.0/8 (loopback) */
|
||||||
|
"10.1.2.3", /* 10.0.0.0/8 (private) */
|
||||||
|
"172.16.2.3", /* 172.16.0.0/12 (private) */
|
||||||
|
"192.168.2.3", /* 192.168.0.0/16 (private) */
|
||||||
|
"169.254.2.3", /* 169.254.0.0/16 (zeroconf) */
|
||||||
|
"192.0.2.3", /* 192.0.2.0/24 (test-net) */
|
||||||
|
"198.51.100.3", /* 198.51.100.0/24(test-net) */
|
||||||
|
"203.0.113.3", /* 203.0.113.0/24 (test-net) */
|
||||||
|
"255.255.255.255", /* 255.255.255.255/32 (broadcast)*/
|
||||||
|
|
||||||
|
/* RFC 6303 4.3 (unspecified & loopback) */
|
||||||
|
net.IPv6zero.String(),
|
||||||
|
net.IPv6unspecified.String(),
|
||||||
|
|
||||||
|
/* RFC 6303 4.4 */
|
||||||
|
/* RFC 6303 4.5 */
|
||||||
|
/* RFC 6303 4.6 */
|
||||||
|
net.IPv6interfacelocalallnodes.String(),
|
||||||
|
net.IPv6linklocalallnodes.String(),
|
||||||
|
net.IPv6linklocalallrouters.String(),
|
||||||
|
|
||||||
|
"localhost",
|
||||||
|
} {
|
||||||
|
s.conf.RebindingProtectionEnabled = true
|
||||||
|
assert.Truef(t, s.isResponseRebind("example.com", host), "host: %s", host)
|
||||||
|
assert.Falsef(t, s.isResponseRebind("totally-safe.com", host), "host: %s", host)
|
||||||
|
assert.Falsef(t, s.isResponseRebind("absolutely.totally-safe.com", host), "host: %s", host)
|
||||||
|
|
||||||
|
s.conf.RebindingProtectionEnabled = false
|
||||||
|
assert.Falsef(t, s.isResponseRebind("example.com", host), "host: %s", host)
|
||||||
|
assert.Falsef(t, s.isResponseRebind("totally-safe.com", host), "host: %s", host)
|
||||||
|
assert.Falsef(t, s.isResponseRebind("absolutely.totally-safe.com", host), "host: %s", host)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, host := range []string{
|
||||||
|
"200.168.2.3",
|
||||||
|
"another-example.com",
|
||||||
|
} {
|
||||||
|
s.conf.RebindingProtectionEnabled = true
|
||||||
|
assert.Falsef(t, s.isResponseRebind("example.com", host), "host: %s", host)
|
||||||
|
assert.Falsef(t, s.isResponseRebind("totally-safe.com", host), "host: %s", host)
|
||||||
|
assert.Falsef(t, s.isResponseRebind("absolutely.totally-legit.com", host), "host: %s", host)
|
||||||
|
|
||||||
|
s.conf.RebindingProtectionEnabled = false
|
||||||
|
assert.Falsef(t, s.isResponseRebind("example.com", host), "host: %s", host)
|
||||||
|
assert.Falsef(t, s.isResponseRebind("totally-safe.com", host), "host: %s", host)
|
||||||
|
assert.Falsef(t, s.isResponseRebind("absolutely.totally-legit.com", host), "host: %s", host)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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,
|
||||||
|
|||||||
@@ -17,14 +17,15 @@ const (
|
|||||||
filteringStatusAll = "all"
|
filteringStatusAll = "all"
|
||||||
filteringStatusFiltered = "filtered" // all kinds of filtering
|
filteringStatusFiltered = "filtered" // all kinds of filtering
|
||||||
|
|
||||||
filteringStatusBlocked = "blocked" // blocked or blocked services
|
filteringStatusBlocked = "blocked" // blocked or blocked services
|
||||||
filteringStatusBlockedService = "blocked_services" // blocked
|
filteringStatusBlockedService = "blocked_services" // blocked
|
||||||
filteringStatusBlockedSafebrowsing = "blocked_safebrowsing" // blocked by safebrowsing
|
filteringStatusBlockedSafebrowsing = "blocked_safebrowsing" // blocked by safebrowsing
|
||||||
filteringStatusBlockedParental = "blocked_parental" // blocked by parental control
|
filteringStatusBlockedParental = "blocked_parental" // blocked by parental control
|
||||||
filteringStatusWhitelisted = "whitelisted" // whitelisted
|
filteringStatusBlockedRebind = "blocked_dns_rebinding" // blocked by DNS rebinding protection
|
||||||
filteringStatusRewritten = "rewritten" // all kinds of rewrites
|
filteringStatusWhitelisted = "whitelisted" // whitelisted
|
||||||
filteringStatusSafeSearch = "safe_search" // enforced safe search
|
filteringStatusRewritten = "rewritten" // all kinds of rewrites
|
||||||
filteringStatusProcessed = "processed" // not blocked, not white-listed entries
|
filteringStatusSafeSearch = "safe_search" // enforced safe search
|
||||||
|
filteringStatusProcessed = "processed" // not blocked, not white-listed entries
|
||||||
)
|
)
|
||||||
|
|
||||||
// filteringStatusValues -- array with all possible filteringStatus values
|
// filteringStatusValues -- array with all possible filteringStatus values
|
||||||
@@ -32,7 +33,7 @@ var filteringStatusValues = []string{
|
|||||||
filteringStatusAll, filteringStatusFiltered, filteringStatusBlocked,
|
filteringStatusAll, filteringStatusFiltered, filteringStatusBlocked,
|
||||||
filteringStatusBlockedService, filteringStatusBlockedSafebrowsing, filteringStatusBlockedParental,
|
filteringStatusBlockedService, filteringStatusBlockedSafebrowsing, filteringStatusBlockedParental,
|
||||||
filteringStatusWhitelisted, filteringStatusRewritten, filteringStatusSafeSearch,
|
filteringStatusWhitelisted, filteringStatusRewritten, filteringStatusSafeSearch,
|
||||||
filteringStatusProcessed,
|
filteringStatusProcessed, filteringStatusBlockedRebind,
|
||||||
}
|
}
|
||||||
|
|
||||||
// searchCriteria - every search request may contain a list of different search criteria
|
// searchCriteria - every search request may contain a list of different search criteria
|
||||||
@@ -115,14 +116,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 +135,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:
|
||||||
|
|||||||
@@ -3,9 +3,8 @@
|
|||||||
package sysutil
|
package sysutil
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"log"
|
||||||
"log/syslog"
|
"log/syslog"
|
||||||
|
|
||||||
"github.com/AdguardTeam/golibs/log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func configureSyslog(serviceName string) error {
|
func configureSyslog(serviceName string) error {
|
||||||
|
|||||||
@@ -3,9 +3,9 @@
|
|||||||
package sysutil
|
package sysutil
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"log"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/AdguardTeam/golibs/log"
|
|
||||||
"golang.org/x/sys/windows"
|
"golang.org/x/sys/windows"
|
||||||
"golang.org/x/sys/windows/svc/eventlog"
|
"golang.org/x/sys/windows/svc/eventlog"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
module github.com/AdguardTeam/AdGuardHome/internal/tools
|
|
||||||
|
|
||||||
go 1.15
|
|
||||||
|
|
||||||
require (
|
|
||||||
dmitri.shuralyov.com/go/generated v0.0.0-20170818220700-b1254a446363 // indirect
|
|
||||||
github.com/client9/misspell v0.3.4 // indirect
|
|
||||||
github.com/fzipp/gocyclo v0.3.1
|
|
||||||
github.com/golangci/misspell v0.3.5
|
|
||||||
github.com/google/go-cmp v0.5.4 // indirect
|
|
||||||
github.com/gookit/color v1.3.3 // indirect
|
|
||||||
github.com/gordonklaus/ineffassign v0.0.0-20201107091007-3b93a8888063
|
|
||||||
github.com/kisielk/errcheck v1.4.0
|
|
||||||
github.com/kyoh86/looppointer v0.1.7
|
|
||||||
github.com/kyoh86/nolint v0.0.1 // indirect
|
|
||||||
github.com/securego/gosec/v2 v2.5.0
|
|
||||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b
|
|
||||||
golang.org/x/mod v0.4.0 // indirect
|
|
||||||
golang.org/x/tools v0.0.0-20201208062317-e652b2f42cc7
|
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
|
||||||
honnef.co/go/tools v0.1.0
|
|
||||||
mvdan.cc/gofumpt v0.0.0-20201129102820-5c11c50e9475
|
|
||||||
mvdan.cc/unparam v0.0.0-20200501210554-b37ab49443f7
|
|
||||||
)
|
|
||||||
@@ -1,167 +0,0 @@
|
|||||||
dmitri.shuralyov.com/go/generated v0.0.0-20170818220700-b1254a446363 h1:o4lAkfETerCnr1kF9/qwkwjICnU+YLHNDCM8h2xj7as=
|
|
||||||
dmitri.shuralyov.com/go/generated v0.0.0-20170818220700-b1254a446363/go.mod h1:WG7q7swWsS2f9PYpt5DoEP/EBYWx8We5UoRltn9vJl8=
|
|
||||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
|
||||||
github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI=
|
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
|
||||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
|
||||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
|
||||||
github.com/fzipp/gocyclo v0.3.1 h1:A9UeX3HJSXTBzvHzhqoYVuE0eAhe+aM8XBCCwsPMZOc=
|
|
||||||
github.com/fzipp/gocyclo v0.3.1/go.mod h1:DJHO6AUmbdqj2ET4Z9iArSuwWgYDRryYt2wASxc7x3E=
|
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
|
||||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
|
||||||
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
|
|
||||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
|
||||||
github.com/golangci/misspell v0.3.5 h1:pLzmVdl3VxTOncgzHcvLOKirdvcx/TydsClUQXTehjo=
|
|
||||||
github.com/golangci/misspell v0.3.5/go.mod h1:dEbvlSfYbMQDtrpRMQU675gSDLDNa8sCPPChZ7PhiVA=
|
|
||||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
|
||||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
|
||||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M=
|
|
||||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
|
||||||
github.com/gookit/color v1.3.1 h1:PPD/C7sf8u2L8XQPdPgsWRoAiLQGZEZOzU3cf5IYYUk=
|
|
||||||
github.com/gookit/color v1.3.1/go.mod h1:R3ogXq2B9rTbXoSHJ1HyUVAZ3poOJHpd9nQmyGZsfvQ=
|
|
||||||
github.com/gookit/color v1.3.3 h1:6IAwxWCdABGNc4gI+YFBMYw0Hz8g5+lpYeKRAaALQoQ=
|
|
||||||
github.com/gookit/color v1.3.3/go.mod h1:GqqLKF1le3EfrbHbYsYa5WdLqfc/PHMdMRbt6tMnqIc=
|
|
||||||
github.com/gordonklaus/ineffassign v0.0.0-20201107091007-3b93a8888063 h1:dKprcOvlsvqfWn/iGvz+oYuC2axESeSMuF8dDrWMNsE=
|
|
||||||
github.com/gordonklaus/ineffassign v0.0.0-20201107091007-3b93a8888063/go.mod h1:cuNKsD1zp2v6XfE/orVX2QE1LC+i254ceGcVeDT3pTU=
|
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
|
||||||
github.com/kisielk/errcheck v1.4.0 h1:ueN6QYA+c7eDQo7ebpNdYR8mUJZThiGz9PEoJEMGPzA=
|
|
||||||
github.com/kisielk/errcheck v1.4.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
|
||||||
github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg=
|
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
|
||||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
|
||||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
|
||||||
github.com/kyoh86/looppointer v0.1.7 h1:q5sZOhFvmvQ6ZoZxvPB/Mjj2croWX7L49BBuI4XQWCM=
|
|
||||||
github.com/kyoh86/looppointer v0.1.7/go.mod h1:l0cRF49N6xDPx8IuBGC/imZo8Yn1BBLJY0vzI+4fepc=
|
|
||||||
github.com/kyoh86/nolint v0.0.0-20200711045849-7a7b0d649b7a h1:WKwgzTn8xp2JuhOUsTbi+h+QygLZSfVGwaYZUejGMMw=
|
|
||||||
github.com/kyoh86/nolint v0.0.0-20200711045849-7a7b0d649b7a/go.mod h1:hPeUNhNOZ22wXzQKMzeYKXVFTBjJ9czxjeIDyI1ueVM=
|
|
||||||
github.com/kyoh86/nolint v0.0.1 h1:GjNxDEkVn2wAxKHtP7iNTrRxytRZ1wXxLV5j4XzGfRU=
|
|
||||||
github.com/kyoh86/nolint v0.0.1/go.mod h1:1ZiZZ7qqrZ9dZegU96phwVcdQOMKIqRzFJL3ewq9gtI=
|
|
||||||
github.com/mozilla/tls-observatory v0.0.0-20200317151703-4fa42e1c2dee/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk=
|
|
||||||
github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d h1:AREM5mwr4u1ORQBMvzfzBgpsctsbQikCVpvC+tX285E=
|
|
||||||
github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU=
|
|
||||||
github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
|
|
||||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
|
||||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
|
||||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
|
||||||
github.com/onsi/ginkgo v1.14.1 h1:jMU0WaQrP0a/YAEq8eJmJKjBoMs+pClEr1vDMlM/Do4=
|
|
||||||
github.com/onsi/ginkgo v1.14.1/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
|
|
||||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
|
||||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
|
||||||
github.com/onsi/gomega v1.10.2 h1:aY/nuoWlKJud2J6U0E3NWsjlg+0GtwXxgEqthRdzlcs=
|
|
||||||
github.com/onsi/gomega v1.10.2/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
|
||||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
|
||||||
github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
|
||||||
github.com/rogpeppe/go-internal v1.6.2 h1:aIihoIOHCiLZHxyoNQ+ABL4NKhFTgKLBdMLyEAh98m0=
|
|
||||||
github.com/rogpeppe/go-internal v1.6.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
|
||||||
github.com/securego/gosec/v2 v2.5.0 h1:kjfXLeKdk98gBe2+eYRFMpC4+mxmQQtbidpiiOQ69Qc=
|
|
||||||
github.com/securego/gosec/v2 v2.5.0/go.mod h1:L/CDXVntIff5ypVHIkqPXbtRpJiNCh6c6Amn68jXDjo=
|
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
|
||||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
|
||||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
|
||||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
|
||||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
|
||||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k=
|
|
||||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
|
||||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
|
||||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|
||||||
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
|
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|
||||||
golang.org/x/mod v0.4.0 h1:8pl+sMODzuvGJkmj2W4kZihvVb5mKm8pB/X44PIQHv8=
|
|
||||||
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|
||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
|
||||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
|
||||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
|
||||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI=
|
|
||||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA=
|
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
|
||||||
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
|
||||||
golang.org/x/tools v0.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-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-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-20201121010211-780cb80bd7fb/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
|
||||||
golang.org/x/tools v0.0.0-20201208062317-e652b2f42cc7 h1:2OSu5vYyX4LVqZAtqZXnFEcN26SDKIJYlEVIRl1tj8U=
|
|
||||||
golang.org/x/tools v0.0.0-20201208062317-e652b2f42cc7/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
|
||||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
|
||||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
|
||||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
|
||||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
|
||||||
google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
|
|
||||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8=
|
|
||||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
|
||||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
|
||||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
|
||||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
|
||||||
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.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/go.mod h1:E4LOcu9JQEtnYXtB1Y51drqh2Qr2Ngk9J3YrRCwcbd0=
|
|
||||||
mvdan.cc/unparam v0.0.0-20200501210554-b37ab49443f7 h1:kAREL6MPwpsk1/PQPFD3Eg7WAQR5mPTWZJaBiG5LDbY=
|
|
||||||
mvdan.cc/unparam v0.0.0-20200501210554-b37ab49443f7/go.mod h1:HGC5lll35J70Y5v7vCGb9oLhHoScFwkHDJm/05RdSTc=
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
// +build tools
|
|
||||||
|
|
||||||
// Package tools and its main module are a nested internal module containing our
|
|
||||||
// development tool dependencies.
|
|
||||||
//
|
|
||||||
// See https://github.com/golang/go/wiki/Modules#how-can-i-track-tool-dependencies-for-a-module.
|
|
||||||
package tools
|
|
||||||
|
|
||||||
import (
|
|
||||||
_ "github.com/fzipp/gocyclo/cmd/gocyclo"
|
|
||||||
_ "github.com/golangci/misspell/cmd/misspell"
|
|
||||||
_ "github.com/gordonklaus/ineffassign"
|
|
||||||
_ "github.com/kisielk/errcheck"
|
|
||||||
_ "github.com/kyoh86/looppointer"
|
|
||||||
_ "github.com/securego/gosec/v2/cmd/gosec"
|
|
||||||
_ "golang.org/x/lint/golint"
|
|
||||||
_ "golang.org/x/tools/go/analysis/passes/nilness/cmd/nilness"
|
|
||||||
_ "golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow"
|
|
||||||
_ "honnef.co/go/tools/cmd/staticcheck"
|
|
||||||
_ "mvdan.cc/gofumpt/gofumports"
|
|
||||||
_ "mvdan.cc/unparam"
|
|
||||||
)
|
|
||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"path"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@@ -40,6 +41,13 @@ func RunCommand(command string, arguments ...string) (int, string, error) {
|
|||||||
return cmd.ProcessState.ExitCode(), string(out), nil
|
return cmd.ProcessState.ExitCode(), string(out), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func FuncName() string {
|
||||||
|
pc := make([]uintptr, 10) // at least 1 entry needed
|
||||||
|
runtime.Callers(2, pc)
|
||||||
|
f := runtime.FuncForPC(pc[0])
|
||||||
|
return path.Base(f.Name())
|
||||||
|
}
|
||||||
|
|
||||||
// SplitNext - split string by a byte and return the first chunk
|
// SplitNext - split string by a byte and return the first chunk
|
||||||
// Skip empty chunks
|
// Skip empty chunks
|
||||||
// Whitespace is trimmed
|
// Whitespace is trimmed
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package util
|
package util
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"log"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -18,6 +19,6 @@ func TestGetValidNetInterfacesForWeb(t *testing.T) {
|
|||||||
t.Fatalf("No addresses found for %s", iface.Name)
|
t.Fatalf("No addresses found for %s", iface.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Logf("%v", iface)
|
log.Printf("%v", iface)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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'
|
||||||
@@ -190,6 +190,7 @@
|
|||||||
- 'blocked'
|
- 'blocked'
|
||||||
- 'blocked_safebrowsing'
|
- 'blocked_safebrowsing'
|
||||||
- 'blocked_parental'
|
- 'blocked_parental'
|
||||||
|
- 'blocked_dns_rebinding'
|
||||||
- 'whitelisted'
|
- 'whitelisted'
|
||||||
- 'rewritten'
|
- 'rewritten'
|
||||||
- 'safe_search'
|
- 'safe_search'
|
||||||
@@ -523,7 +524,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 +1247,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 +1258,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 +1611,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 +1633,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 +1669,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'
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
test "$VERBOSE" = '1' && set -x
|
|
||||||
set -e -f -u
|
|
||||||
|
|
||||||
# TODO(a.garipov): Add goconst?
|
|
||||||
|
|
||||||
env GOBIN="${PWD}/bin" "$GO" install --modfile=./internal/tools/go.mod\
|
|
||||||
github.com/fzipp/gocyclo/cmd/gocyclo\
|
|
||||||
github.com/golangci/misspell/cmd/misspell\
|
|
||||||
github.com/gordonklaus/ineffassign\
|
|
||||||
github.com/kisielk/errcheck\
|
|
||||||
github.com/kyoh86/looppointer/cmd/looppointer\
|
|
||||||
github.com/securego/gosec/v2/cmd/gosec\
|
|
||||||
golang.org/x/lint/golint\
|
|
||||||
golang.org/x/tools/go/analysis/passes/nilness/cmd/nilness\
|
|
||||||
golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow\
|
|
||||||
honnef.co/go/tools/cmd/staticcheck\
|
|
||||||
mvdan.cc/gofumpt\
|
|
||||||
mvdan.cc/unparam\
|
|
||||||
;
|
|
||||||
@@ -1,115 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
# Verbosity levels:
|
|
||||||
# 0 = Don't print anything except for errors.
|
|
||||||
# 1 = Print commands, but not nested commands.
|
|
||||||
# 2 = Print everything.
|
|
||||||
test "${VERBOSE:=0}" -gt '0' && set -x
|
|
||||||
|
|
||||||
# Set $EXITONERROR to zero to see all errors.
|
|
||||||
test "${EXITONERROR:=1}" = '0' && set +e || set -e
|
|
||||||
|
|
||||||
# We don't need glob expansions and we want to see errors about unset
|
|
||||||
# variables.
|
|
||||||
set -f -u
|
|
||||||
|
|
||||||
not_found_msg='
|
|
||||||
looks like a binary not found error.
|
|
||||||
make sure you have installed the linter binaries using:
|
|
||||||
|
|
||||||
$ make go-install-tools
|
|
||||||
'
|
|
||||||
|
|
||||||
not_found() {
|
|
||||||
if [ "$?" = '127' ]
|
|
||||||
then
|
|
||||||
# Code 127 is the exit status a shell uses when
|
|
||||||
# a command or a file is not found, according to the
|
|
||||||
# Bash Hackers wiki.
|
|
||||||
#
|
|
||||||
# See https://wiki.bash-hackers.org/dict/terms/exit_status.
|
|
||||||
echo "$not_found_msg" 1>&2
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
trap not_found EXIT
|
|
||||||
|
|
||||||
# blocklist_imports is a simple check against unwanted packages.
|
|
||||||
# Currently it only looks for package log which is replaced by our own
|
|
||||||
# package github.com/AdguardTeam/golibs/log.
|
|
||||||
blocklist_imports() {
|
|
||||||
git grep -F -e '"log"' -- '*.go' || exit 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
# underscores is a simple check against Go filenames with underscores.
|
|
||||||
underscores() {
|
|
||||||
git ls-files '*_*.go' | { grep -F -e '_darwin.go' \
|
|
||||||
-e '_freebsd.go' -e '_linux.go' -e '_others.go' \
|
|
||||||
-e '_test.go' -e '_unix.go' -e '_windows.go' \
|
|
||||||
-v || exit 0; }
|
|
||||||
}
|
|
||||||
|
|
||||||
# exit_on_output exits with a nonzero exit code if there is anything in
|
|
||||||
# the command's combined output.
|
|
||||||
exit_on_output() {
|
|
||||||
test "$VERBOSE" -lt '2' && set +x
|
|
||||||
|
|
||||||
cmd="$1"
|
|
||||||
shift
|
|
||||||
|
|
||||||
exitcode='0'
|
|
||||||
output="$("$cmd" "$@" 2>&1)"
|
|
||||||
if [ "$output" != '' ]
|
|
||||||
then
|
|
||||||
if [ "$*" != '' ]
|
|
||||||
then
|
|
||||||
echo "combined output of '$cmd $@':"
|
|
||||||
else
|
|
||||||
echo "combined output of '$cmd':"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "$output"
|
|
||||||
|
|
||||||
exitcode='1'
|
|
||||||
fi
|
|
||||||
|
|
||||||
test "$VERBOSE" -gt '0' && set -x
|
|
||||||
|
|
||||||
return "$exitcode"
|
|
||||||
}
|
|
||||||
|
|
||||||
exit_on_output blocklist_imports
|
|
||||||
|
|
||||||
exit_on_output underscores
|
|
||||||
|
|
||||||
exit_on_output gofumpt --extra -l -s .
|
|
||||||
|
|
||||||
golint --set_exit_status ./...
|
|
||||||
|
|
||||||
"$GO" vet ./...
|
|
||||||
|
|
||||||
gocyclo --over 20 .
|
|
||||||
|
|
||||||
gosec --quiet .
|
|
||||||
|
|
||||||
ineffassign .
|
|
||||||
|
|
||||||
unparam ./...
|
|
||||||
|
|
||||||
git ls-files -- '*.go' '*.md' '*.yaml' '*.yml' | xargs misspell --error
|
|
||||||
|
|
||||||
looppointer ./...
|
|
||||||
|
|
||||||
nilness ./...
|
|
||||||
|
|
||||||
# TODO(a.garipov): Enable shadow after fixing all of the shadowing.
|
|
||||||
# shadow --strict ./...
|
|
||||||
|
|
||||||
# TODO(a.garipov): Enable errcheck fully after handling all errors,
|
|
||||||
# including the deferred ones, properly. Also, perhaps, enable --blank.
|
|
||||||
# errcheck ./...
|
|
||||||
exit_on_output sh -c '
|
|
||||||
errcheck --asserts ./... |\
|
|
||||||
{ grep -e "defer" -e "_test\.go:" -v || exit 0; }
|
|
||||||
'
|
|
||||||
|
|
||||||
staticcheck ./...
|
|
||||||
@@ -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