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
|
||||
|
||||
if [ "$(git diff --cached --name-only -- '*.js')" ]
|
||||
then
|
||||
make js-lint js-test
|
||||
found=0
|
||||
git diff --cached --name-only | grep -q '.js$' && found=1
|
||||
if [ $found == 1 ]; then
|
||||
npm --prefix client run lint || exit 1
|
||||
npm run test --prefix client || exit 1
|
||||
fi
|
||||
|
||||
if [ "$(git diff --cached --name-only -- '*.go' 'go.mod')" ]
|
||||
then
|
||||
make go-lint go-test
|
||||
found=0
|
||||
git diff --cached --name-only | grep -q '.go$' && found=1
|
||||
if [ $found == 1 ]; then
|
||||
make lint-go || exit 1
|
||||
go test ./... || exit 1
|
||||
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':
|
||||
'push':
|
||||
'tags':
|
||||
@@ -7,13 +7,16 @@
|
||||
- '*'
|
||||
'pull_request':
|
||||
'jobs':
|
||||
'go-lint':
|
||||
'golangci':
|
||||
'runs-on': 'ubuntu-latest'
|
||||
'steps':
|
||||
- 'uses': 'actions/checkout@v2'
|
||||
- 'name': 'run-lint'
|
||||
'run': >
|
||||
make go-install-tools go-lint
|
||||
- 'name': 'golangci-lint'
|
||||
'uses': 'golangci/golangci-lint-action@v2.3.0'
|
||||
'with':
|
||||
# This field is required. Don't set the patch version to always use
|
||||
# the latest patch version.
|
||||
'version': 'v1.32'
|
||||
'eslint':
|
||||
'runs-on': 'ubuntu-latest'
|
||||
'steps':
|
||||
@@ -24,7 +27,7 @@
|
||||
'run': 'npm --prefix client run lint'
|
||||
'notify':
|
||||
'needs':
|
||||
- 'go-lint'
|
||||
- 'golangci'
|
||||
- 'eslint'
|
||||
# Secrets are not passed to workflows that are triggered by a pull request
|
||||
# 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
|
||||
# added, the harder it gets to maintain and manage projects' gitignores. Put
|
||||
# them into your global gitignore file instead.
|
||||
#
|
||||
# See https://stackoverflow.com/a/7335487/1892060.
|
||||
#
|
||||
# Only build, run, and test outputs here. Sorted.
|
||||
*-packr.go
|
||||
*.db
|
||||
*.snap
|
||||
/bin/
|
||||
/build/
|
||||
.DS_Store
|
||||
/.vscode
|
||||
.idea
|
||||
/AdGuardHome
|
||||
/AdGuardHome.exe
|
||||
/AdGuardHome.yaml
|
||||
/AdGuardHome.log
|
||||
/data/
|
||||
/build/
|
||||
/dist/
|
||||
/dnsfilter/tests/dnsfilter.TestLotsOfRules*.pprof
|
||||
/dnsfilter/tests/top-1m.csv
|
||||
/launchpad_credentials
|
||||
/querylog.json*
|
||||
/snapcraft_login
|
||||
AdGuardHome*
|
||||
/client/node_modules/
|
||||
/querylog.json
|
||||
/querylog.json.1
|
||||
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':
|
||||
'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_safebrowsing - blocked by safebrowsing
|
||||
* blocked_parental - blocked by parental control
|
||||
* blocked_dns_rebinding - blocked by DNS rebinding protection
|
||||
* whitelisted - whitelisted
|
||||
* rewritten - all kinds of rewrites
|
||||
* safe_search - enforced safe search
|
||||
@@ -1833,22 +1834,16 @@ Response:
|
||||
200 OK
|
||||
|
||||
{
|
||||
"reason":"FilteredBlackList",
|
||||
"rules":{
|
||||
"filter_list_id":42,
|
||||
"text":"||doubleclick.net^",
|
||||
},
|
||||
// If we have "reason":"FilteredBlockedService".
|
||||
"service_name": "...",
|
||||
// If we have "reason":"Rewrite".
|
||||
"cname": "...",
|
||||
"ip_addrs": ["1.2.3.4", ...]
|
||||
"reason":"FilteredBlackList",
|
||||
"filter_id":1,
|
||||
"rule":"||doubleclick.net^",
|
||||
"service_name": "...", // set if reason=FilteredBlockedService
|
||||
|
||||
// if reason=ReasonRewrite:
|
||||
"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
|
||||
|
||||
|
||||
24
CHANGELOG.md
24
CHANGELOG.md
@@ -15,19 +15,15 @@ and this project adheres to
|
||||
|
||||
### Added
|
||||
|
||||
- `$dnsrewrite` modifier for filters ([#2102]).
|
||||
- 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
|
||||
- Detecting of network interface configurated to have static IP address via
|
||||
`/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
|
||||
address ([#2304]).
|
||||
- `$dnstype` modifier for filters ([#2337]).
|
||||
- HTTP API request body size limit ([#2305]).
|
||||
|
||||
[#1361]: https://github.com/AdguardTeam/AdGuardHome/issues/1361
|
||||
[#2102]: https://github.com/AdguardTeam/AdGuardHome/issues/2102
|
||||
[#2302]: https://github.com/AdguardTeam/AdGuardHome/issues/2302
|
||||
[#2304]: https://github.com/AdguardTeam/AdGuardHome/issues/2304
|
||||
[#2305]: https://github.com/AdguardTeam/AdGuardHome/issues/2305
|
||||
@@ -35,46 +31,34 @@ and this project adheres to
|
||||
|
||||
### Changed
|
||||
|
||||
- When `dns.bogus_nxdomain` option is used, the server will now transform
|
||||
responses if there is at least one bogus address instead of all of them
|
||||
([#2394]). The new behavior is the same as in `dnsmasq`.
|
||||
- Post-updating relaunch possibility is now determined OS-dependently ([#2231],
|
||||
[#2391]).
|
||||
- Post-updating relaunch possibility is now determined OS-dependently ([#2231], [#2391]).
|
||||
- Made the mobileconfig HTTP API more robust and predictable, add parameters and
|
||||
improve error response ([#2358]).
|
||||
- Improved HTTP requests handling and timeouts ([#2343]).
|
||||
- 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
|
||||
[#2267]: https://github.com/AdguardTeam/AdGuardHome/issues/2267
|
||||
[#2271]: https://github.com/AdguardTeam/AdGuardHome/issues/2271
|
||||
[#2297]: https://github.com/AdguardTeam/AdGuardHome/issues/2297
|
||||
[#2306]: https://github.com/AdguardTeam/AdGuardHome/issues/2306
|
||||
[#2343]: https://github.com/AdguardTeam/AdGuardHome/issues/2343
|
||||
[#2358]: https://github.com/AdguardTeam/AdGuardHome/issues/2358
|
||||
[#2391]: https://github.com/AdguardTeam/AdGuardHome/issues/2391
|
||||
[#2394]: https://github.com/AdguardTeam/AdGuardHome/issues/2394
|
||||
|
||||
### 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
|
||||
page ([#2293]).
|
||||
- A JSON parsing error in query log ([#2345]).
|
||||
- Incorrect detection of the IPv6 address of an interface as well as another
|
||||
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
|
||||
[#2345]: https://github.com/AdguardTeam/AdGuardHome/issues/2345
|
||||
[#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
|
||||
|
||||
|
||||
70
HACKING.md
70
HACKING.md
@@ -1,9 +1,9 @@
|
||||
# *AdGuardHome* Developer Guidelines
|
||||
|
||||
As of **December 2020**, this document is partially a work-in-progress, but
|
||||
should still be followed. Some of the rules aren't enforced as thoroughly or
|
||||
remain broken in old code, but this is still the place to find out about what we
|
||||
**want** our code to look like.
|
||||
As of **2020-11-27**, this document is a work-in-progress, but should still be
|
||||
followed. Some of the rules aren't enforced as thoroughly or remain broken in
|
||||
old code, but this is still the place to find out about what we **want** our
|
||||
code to look like.
|
||||
|
||||
The rules are mostly sorted in the alphabetical order.
|
||||
|
||||
@@ -32,11 +32,6 @@ The rules are mostly sorted in the alphabetical order.
|
||||
|
||||
## *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
|
||||
|
||||
* 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
|
||||
[constant errors] instead of `errors.New`.
|
||||
|
||||
* Unused arguments in anonymous functions must be called `_`:
|
||||
|
||||
```go
|
||||
v.onSuccess = func(_ int, msg string) {
|
||||
// …
|
||||
}
|
||||
```
|
||||
|
||||
* Use linters.
|
||||
|
||||
* Use named returns to improve readability of function signatures.
|
||||
@@ -114,16 +101,7 @@ The rules are mostly sorted in the alphabetical order.
|
||||
```go
|
||||
// Foo implements the Fooer interface for *foo.
|
||||
func (f *foo) Foo() {
|
||||
// …
|
||||
}
|
||||
```
|
||||
|
||||
When the implemented interface is unexported:
|
||||
|
||||
```go
|
||||
// Unwrap implements the hidden wrapper interface for *fooError.
|
||||
func (err *fooError) Unwrap() (unwrapped error) {
|
||||
// …
|
||||
// …
|
||||
}
|
||||
```
|
||||
|
||||
@@ -134,6 +112,8 @@ The rules are mostly sorted in the alphabetical order.
|
||||
|
||||
* Use `gofumpt --extra -s`.
|
||||
|
||||
**TODO(a.garipov):** Add to the linters.
|
||||
|
||||
* Write slices of struct like this:
|
||||
|
||||
```go
|
||||
@@ -161,42 +141,6 @@ The rules are mostly sorted in the alphabetical order.
|
||||
|
||||
* **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
|
||||
|
||||
* End sentences with appropriate punctuation.
|
||||
|
||||
47
Makefile
47
Makefile
@@ -26,8 +26,7 @@
|
||||
# * DOCKER_IMAGE_NAME - adguard/adguard-home
|
||||
# * 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)
|
||||
TARGET=AdGuardHome
|
||||
BASE_URL="https://static.adguard.com/adguardhome/$(CHANNEL)"
|
||||
@@ -35,7 +34,6 @@ GPG_KEY := devteam@adguard.com
|
||||
GPG_KEY_PASSPHRASE :=
|
||||
GPG_CMD := gpg --detach-sig --default-key $(GPG_KEY) --pinentry-mode loopback --passphrase $(GPG_KEY_PASSPHRASE)
|
||||
VERBOSE := -v
|
||||
REBUILD_CLIENT = 1
|
||||
|
||||
# See release target
|
||||
DIST_DIR=dist
|
||||
@@ -67,9 +65,7 @@ endif
|
||||
|
||||
# Version properties
|
||||
COMMIT=$(shell git rev-parse --short HEAD)
|
||||
# TODO(a.garipov): The cut call is a temporary solution to trim
|
||||
# prerelease versions. See the comment in .goreleaser.yml.
|
||||
TAG_NAME=$(shell git describe --abbrev=0 | cut -c 1-8)
|
||||
TAG_NAME=$(shell git describe --abbrev=0)
|
||||
RELEASE_VERSION=$(TAG_NAME)
|
||||
SNAPSHOT_VERSION=$(RELEASE_VERSION)-SNAPSHOT-$(COMMIT)
|
||||
|
||||
@@ -125,11 +121,10 @@ all: build
|
||||
init:
|
||||
git config core.hooksPath .githooks
|
||||
|
||||
build:
|
||||
test '$(REBUILD_CLIENT)' = '1' && $(MAKE) client_with_deps || exit 0
|
||||
$(GO) mod download
|
||||
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)"
|
||||
build: client_with_deps
|
||||
go mod download
|
||||
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)"
|
||||
PATH=$(GOPATH)/bin:$(PATH) packr clean
|
||||
|
||||
client:
|
||||
@@ -156,40 +151,38 @@ docker:
|
||||
@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)
|
||||
|
||||
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
|
||||
|
||||
go-install-tools:
|
||||
env GO=$(GO) sh ./scripts/go-install-tools.sh
|
||||
lint-go:
|
||||
@echo Running go linter
|
||||
golangci-lint run
|
||||
|
||||
go-lint:
|
||||
env GO=$(GO) PATH="$$PWD/bin:$$PATH" sh ./scripts/go-lint.sh
|
||||
test: test-js test-go
|
||||
|
||||
test: js-test go-test
|
||||
|
||||
js-test:
|
||||
test-js:
|
||||
npm run test --prefix client
|
||||
|
||||
go-test:
|
||||
$(GO) test $(TEST_FLAGS) --coverprofile coverage.txt ./...
|
||||
test-go:
|
||||
go test $(TEST_FLAGS) --coverprofile coverage.txt ./...
|
||||
|
||||
ci: client_with_deps
|
||||
$(GO) mod download
|
||||
go mod download
|
||||
$(MAKE) test
|
||||
|
||||
dependencies:
|
||||
npm --prefix client ci
|
||||
$(GO) mod download
|
||||
go mod download
|
||||
|
||||
clean:
|
||||
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
|
||||
# after a Docker build.
|
||||
env PATH="$(GOPATH)/bin:$$PATH" packr clean
|
||||
rm -f -r ./bin/
|
||||
|
||||
docker-multi-arch:
|
||||
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)
|
||||
|
||||
release: client_with_deps
|
||||
$(GO) mod download
|
||||
go mod download
|
||||
@echo Starting release build: version $(VERSION), channel $(CHANNEL)
|
||||
CHANNEL=$(CHANNEL) $(GORELEASER_COMMAND)
|
||||
$(call write_version_file,$(VERSION))
|
||||
|
||||
11
README.md
11
README.md
@@ -171,6 +171,9 @@ You will need this to build AdGuard Home:
|
||||
* [node.js](https://nodejs.org/en/download/) v10.16.2 or later.
|
||||
* [npm](https://www.npmjs.com/) v6.14 or later.
|
||||
|
||||
Optionally, for Go devs:
|
||||
* [golangci-lint](https://github.com/golangci/golangci-lint)
|
||||
|
||||
### Building
|
||||
|
||||
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.
|
||||
|
||||
**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.
|
||||
|
||||
For example:
|
||||
@@ -255,7 +258,7 @@ curl -sSL https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/master/scrip
|
||||
|
||||
* 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 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)
|
||||
* 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)
|
||||
@@ -264,7 +267,7 @@ curl -sSL https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/master/scrip
|
||||
|
||||
* 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 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)
|
||||
* 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)
|
||||
@@ -328,4 +331,4 @@ For a full list of all node.js packages in use, please take a look at [client/pa
|
||||
<a id="privacy"></a>
|
||||
## Privacy
|
||||
|
||||
Our main idea is that you are the one, who should be in control of your data. So it is only natural, that AdGuard Home does not collect any usage statistics, and does not use any web services unless you configure it to do so. Full policy with every bit that _could in theory be_ sent by AdGuard Home is available [here](https://adguard.com/en/privacy/home.html).
|
||||
Our main idea is that you are the one, who should be in control of your data. So it is only natural, that AdGuard Home does not collect any usage statistics, and does not use any web services unless you configure it to do so. Full policy with every bit that _could in theory be_ sent by AdGuard Home is available [here](https://adguard.com/en/privacy/home.html).
|
||||
@@ -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.",
|
||||
"adg_will_drop_dns_queries": "AdGuard Home will be dropping all DNS queries from this client.",
|
||||
"client_not_in_allowed_clients": "The client is not allowed because it is not in the \"Allowed clients\" list.",
|
||||
"experimental": "Experimental"
|
||||
"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', () => {
|
||||
const originalWarn = console.warn;
|
||||
const originalError = console.error;
|
||||
|
||||
beforeEach(() => {
|
||||
console.warn = jest.fn();
|
||||
console.error = jest.fn();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
expect(console.warn).toHaveBeenCalled();
|
||||
console.warn = originalWarn;
|
||||
expect(console.error).toHaveBeenCalled();
|
||||
console.error = originalError;
|
||||
});
|
||||
|
||||
test('invalid strings', () => {
|
||||
|
||||
@@ -37,6 +37,10 @@ export const setDnsConfig = (config) => async (dispatch) => {
|
||||
data.upstream_dns = splitByNewLine(config.upstream_dns);
|
||||
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);
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import ReactTable from 'react-table';
|
||||
import { withTranslation } from 'react-i18next';
|
||||
import { sortIp } from '../../../helpers/helpers';
|
||||
|
||||
class Table extends Component {
|
||||
cellWrap = ({ value }) => (
|
||||
@@ -22,7 +21,6 @@ class Table extends Component {
|
||||
{
|
||||
Header: this.props.t('answer'),
|
||||
accessor: 'answer',
|
||||
sortMethod: sortIp,
|
||||
Cell: this.cellWrap,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -259,7 +259,7 @@ let Form = (props) => {
|
||||
</div>
|
||||
<div className="form__desc mt-0 mb-2">
|
||||
<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>,
|
||||
]}>
|
||||
tags_desc
|
||||
|
||||
@@ -113,6 +113,9 @@ const Dhcp = () => {
|
||||
const enteredSomeValue = enteredSomeV4Value || enteredSomeV6Value || interfaceName;
|
||||
|
||||
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)
|
||||
.every(Boolean) || Object.values(v6)
|
||||
.every(Boolean));
|
||||
@@ -138,7 +141,7 @@ const Dhcp = () => {
|
||||
className={className}
|
||||
onClick={enabled ? onClickDisable : onClickEnable}
|
||||
disabled={processingDhcp || processingConfig
|
||||
|| (!enabled && (!filledConfig || !check))}
|
||||
|| (!enabled && (!filledConfig || !check || otherDhcpFound))}
|
||||
>
|
||||
<Trans>{enabled ? 'dhcp_disable' : 'dhcp_enable'}</Trans>
|
||||
</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 Loading from '../../ui/Loading';
|
||||
import CacheConfig from './Cache';
|
||||
import RebindingConfig from './Rebinding';
|
||||
import { getDnsConfig } from '../../../actions/dnsConfig';
|
||||
import { getAccessList } from '../../../actions/access';
|
||||
|
||||
@@ -33,6 +34,7 @@ const Dns = () => {
|
||||
<Config />
|
||||
<CacheConfig />
|
||||
<Access />
|
||||
<RebindingConfig />
|
||||
</>}
|
||||
</>;
|
||||
};
|
||||
|
||||
@@ -232,7 +232,7 @@ let Form = (props) => {
|
||||
<Trans
|
||||
values={{ link: 'letsencrypt.org' }}
|
||||
components={[
|
||||
<a target="_blank" rel="noopener noreferrer" href="https://letsencrypt.org/" key="0">
|
||||
<a href="https://letsencrypt.org/" key="0">
|
||||
link
|
||||
</a>,
|
||||
]}
|
||||
|
||||
@@ -7,7 +7,7 @@ import flow from 'lodash/flow';
|
||||
import { CheckboxField, toNumber } from '../../../helpers/form';
|
||||
import {
|
||||
FILTERS_INTERVALS_HOURS,
|
||||
FILTERS_RELATIVE_LINK,
|
||||
FILTERS_LINK,
|
||||
FORM_NAME,
|
||||
} from '../../../helpers/constants';
|
||||
|
||||
@@ -45,7 +45,7 @@ const Form = (props) => {
|
||||
} = props;
|
||||
|
||||
const components = {
|
||||
a: <a href={FILTERS_RELATIVE_LINK} rel="noopener noreferrer" />,
|
||||
a: <a href={FILTERS_LINK} rel="noopener noreferrer" />,
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -6,50 +6,6 @@ import { useSelector } from 'react-redux';
|
||||
import Topline from './Topline';
|
||||
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 not_after = useSelector((state) => state.encryption.not_after);
|
||||
|
||||
@@ -57,21 +13,30 @@ const EncryptionTopline = () => {
|
||||
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) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { toplineType, i18nKey } = EXPIRATION_STATE[expirationStateKey];
|
||||
|
||||
return (
|
||||
<Topline type={toplineType}>
|
||||
if (isExpired) {
|
||||
return (
|
||||
<Topline type="danger">
|
||||
<Trans components={[<a href="#encryption" key="0">link</a>]}>
|
||||
{i18nKey}
|
||||
topline_expired_certificate
|
||||
</Trans>
|
||||
</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;
|
||||
|
||||
@@ -53,9 +53,9 @@ export const REPOSITORY = {
|
||||
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 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';
|
||||
|
||||
@@ -341,6 +341,7 @@ export const FILTERED_STATUS = {
|
||||
REWRITE_HOSTS: 'RewriteEtcHosts',
|
||||
FILTERED_SAFE_SEARCH: 'FilteredSafeSearch',
|
||||
FILTERED_SAFE_BROWSING: 'FilteredSafeBrowsing',
|
||||
FILTERED_REBIND: 'FilteredRebind',
|
||||
FILTERED_PARENTAL: 'FilteredParental',
|
||||
};
|
||||
|
||||
@@ -373,6 +374,10 @@ export const RESPONSE_FILTER = {
|
||||
QUERY: 'blocked_parental',
|
||||
LABEL: 'blocked_adult_websites',
|
||||
},
|
||||
BLOCKED_DNS_REBINDING: {
|
||||
QUERY: 'blocked_dns_rebinding',
|
||||
LABEL: 'blocked_dns_rebinding',
|
||||
},
|
||||
ALLOWED: {
|
||||
QUERY: 'whitelisted',
|
||||
LABEL: 'allowed',
|
||||
@@ -414,6 +419,10 @@ export const FILTERED_STATUS_TO_META_MAP = {
|
||||
LABEL: 'blocked_service',
|
||||
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]: {
|
||||
LABEL: RESPONSE_FILTER.SAFE_SEARCH.LABEL,
|
||||
COLOR: QUERY_STATUS_COLORS.YELLOW,
|
||||
@@ -509,6 +518,7 @@ export const FORM_NAME = {
|
||||
INSTALL: 'install',
|
||||
LOGIN: 'login',
|
||||
CACHE: 'cache',
|
||||
REBINDING: 'rebinding',
|
||||
...DHCP_FORM_NAMES,
|
||||
};
|
||||
|
||||
|
||||
@@ -687,7 +687,7 @@ export const sortIp = (a, b) => {
|
||||
|
||||
return 0;
|
||||
} catch (e) {
|
||||
console.warn(e);
|
||||
console.error(e);
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -64,7 +64,7 @@ export const validateClientId = (value) => {
|
||||
if (!value) {
|
||||
return undefined;
|
||||
}
|
||||
const formattedValue = value.trim();
|
||||
const formattedValue = value ? value.trim() : value;
|
||||
if (formattedValue && !(
|
||||
R_IPV4.test(formattedValue)
|
||||
|| R_IPV6.test(formattedValue)
|
||||
|
||||
@@ -12,7 +12,7 @@ const renderItem = ({
|
||||
|
||||
return <li key={ip}>{isDns
|
||||
? <strong>{dnsAddress}</strong>
|
||||
: <a href={webAddress} target="_blank" rel="noopener noreferrer">{webAddress}</a>
|
||||
: <a href={webAddress}>{webAddress}</a>
|
||||
}
|
||||
</li>;
|
||||
};
|
||||
|
||||
@@ -16,6 +16,7 @@ const dnsConfig = handleActions(
|
||||
blocking_ipv6,
|
||||
upstream_dns,
|
||||
bootstrap_dns,
|
||||
rebinding_allowed_hosts,
|
||||
...values
|
||||
} = payload;
|
||||
|
||||
@@ -24,8 +25,9 @@ const dnsConfig = handleActions(
|
||||
...values,
|
||||
blocking_ipv4: blocking_ipv4 || DEFAULT_BLOCKING_IPV4,
|
||||
blocking_ipv6: blocking_ipv6 || DEFAULT_BLOCKING_IPV6,
|
||||
upstream_dns: (upstream_dns && upstream_dns.join('\n')) || '',
|
||||
bootstrap_dns: (bootstrap_dns && bootstrap_dns.join('\n')) || '',
|
||||
upstream_dns: upstream_dns?.join('\n') || '',
|
||||
bootstrap_dns: bootstrap_dns?.join('\n') || '',
|
||||
rebinding_allowed_hosts: rebinding_allowed_hosts?.join('\n') || '',
|
||||
processingGetConfig: false,
|
||||
};
|
||||
},
|
||||
|
||||
17
go.mod
17
go.mod
@@ -3,11 +3,12 @@ module github.com/AdguardTeam/AdGuardHome
|
||||
go 1.14
|
||||
|
||||
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/urlfilter v0.14.1
|
||||
github.com/AdguardTeam/urlfilter v0.13.0
|
||||
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/go-ping/ping v0.0.0-20201115131931-3300c582a663
|
||||
github.com/gobuffalo/envy v1.9.0 // indirect
|
||||
@@ -15,8 +16,10 @@ require (
|
||||
github.com/gobuffalo/packr/v2 v2.8.1 // indirect
|
||||
github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714
|
||||
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/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/raw v0.0.0-20191009151244-50f2db8cc065
|
||||
github.com/miekg/dns v1.1.35
|
||||
@@ -27,12 +30,14 @@ require (
|
||||
github.com/stretchr/testify v1.6.1
|
||||
github.com/u-root/u-root v7.0.0+incompatible
|
||||
go.etcd.io/bbolt v1.3.5
|
||||
golang.org/x/crypto v0.0.0-20201217014255-9d1352758620
|
||||
golang.org/x/net v0.0.0-20201216054612-986b41b23924
|
||||
golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 // indirect
|
||||
golang.org/x/sys v0.0.0-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
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0
|
||||
gopkg.in/yaml.v2 v2.3.0
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 // indirect
|
||||
howett.net/plist v0.0.0-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/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
|
||||
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
|
||||
github.com/AdguardTeam/dnsproxy v0.33.7 h1:DXsLTJoBSUejB2ZqVHyMG0/kXD8PzuVPbLCsGKBdaDc=
|
||||
github.com/AdguardTeam/dnsproxy v0.33.7/go.mod h1:dkI9VWh43XlOzF2XogDm1EmoVl7PANOR4isQV6X9LZs=
|
||||
github.com/AdguardTeam/dnsproxy v0.33.2 h1:k5aMcsw3TA/G2DR8EjIkwutDPuuRkKh8xij4cFWC6Fk=
|
||||
github.com/AdguardTeam/dnsproxy v0.33.2/go.mod h1:kLi6lMpErnZThy5haiRSis4q0KTB8uPWO4JQsU1EDJA=
|
||||
github.com/AdguardTeam/golibs v0.4.0/go.mod h1:skKsDKIBB7kkFflLJBpfGX+G8QFTx0WKUzB6TIgtUj4=
|
||||
github.com/AdguardTeam/golibs v0.4.2 h1:7M28oTZFoFwNmp8eGPb3ImmYbxGaJLyQXeIFVHjME0o=
|
||||
github.com/AdguardTeam/golibs v0.4.2/go.mod h1:skKsDKIBB7kkFflLJBpfGX+G8QFTx0WKUzB6TIgtUj4=
|
||||
github.com/AdguardTeam/golibs v0.4.3 h1:nXTLLLlIyU4BSRF0An5azS0uimSK/YpIMOBAO0/v1RY=
|
||||
github.com/AdguardTeam/golibs v0.4.3/go.mod h1:skKsDKIBB7kkFflLJBpfGX+G8QFTx0WKUzB6TIgtUj4=
|
||||
github.com/AdguardTeam/golibs v0.4.4 h1:cM9UySQiYFW79zo5XRwnaIWVzfW4eNXmZktMrWbthpw=
|
||||
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/urlfilter v0.14.1 h1:imYls0fit9ojA6pP1hWFUEIjyoXbDF85ZM+G67bI48c=
|
||||
github.com/AdguardTeam/urlfilter v0.14.1/go.mod h1:klx4JbOfc4EaNb5lWLqOwfg+pVcyRukmoJRvO55lL5U=
|
||||
github.com/AdguardTeam/urlfilter v0.13.0 h1:MfO46K81JVTkhgP6gRu/buKl5wAOSfusjiDwjT1JN1c=
|
||||
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/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
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/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/ameshkov/dnscrypt/v2 v2.0.1 h1:igNVNM6NLBOqYUzHXaDUxn8i+wJXOsosY0/xEBirixA=
|
||||
github.com/ameshkov/dnscrypt/v2 v2.0.1/go.mod h1:nbZnxJt4edIPx2Haa8n2XtC2g5AWcsdQiSuXkNH8eDI=
|
||||
github.com/ameshkov/dnscrypt/v2 v2.0.0 h1:i83G8MeGLrAFgUL8GSu98TVhtFDEifF7SIS7Qi/RZ3U=
|
||||
github.com/ameshkov/dnscrypt/v2 v2.0.0/go.mod h1:nbZnxJt4edIPx2Haa8n2XtC2g5AWcsdQiSuXkNH8eDI=
|
||||
github.com/ameshkov/dnsstamps v1.0.1 h1:LhGvgWDzhNJh+kBQd/AfUlq1vfVe109huiXw4JhnPug=
|
||||
github.com/ameshkov/dnsstamps v1.0.1/go.mod h1:Ii3eUu73dx4Vw5O4wjzmT5+lkCwovjzaEZZ4gKyIH5A=
|
||||
github.com/ameshkov/dnsstamps v1.0.3 h1:Srzik+J9mivH1alRACTbys2xOxs0lRH9qnTA7Y1OYVo=
|
||||
@@ -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/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/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/go.mod h1:6YNgTHLutezwnBvyneBbwvB8C82y3dcoOj5EQJIdGXA=
|
||||
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.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
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.19.3/go.mod h1:ADXpNbTQjq1hIzCpB+y/k5iz4n4z4IwqoLb94Kh5Hu8=
|
||||
github.com/lucas-clemente/quic-go v0.18.1 h1:DMR7guC0NtVS8zNZR3IO7NARZvZygkSC56GGtC6cyys=
|
||||
github.com/lucas-clemente/quic-go v0.18.1/go.mod h1:yXttHsSNxQi8AWijC/vLP+OJczXqzHSOcJrM5ITUlCg=
|
||||
github.com/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/magiconair/properties v1.8.0/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/safe v1.0.1 h1:yjZkbvRM6IzKj9tlu/zMJLS0n/V351OZWRnF3QfaUxI=
|
||||
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/qtls v0.10.0 h1:ECsuYUKalRL240rRD4Ri33ISb7kAQ3qGDlrrl55b2pc=
|
||||
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/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I=
|
||||
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.1.29 h1:xHBEhR+t5RzcFJjBLJlax2daXOrTYtr9z4WdKEfWFzg=
|
||||
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/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
||||
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-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
|
||||
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-20201208171446-5f87f3452ae9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.0.0-20201217014255-9d1352758620 h1:3wPMTskHO3+O6jqTEXyFcsnuxMQOqYSaHsDxcbUXpqA=
|
||||
golang.org/x/crypto v0.0.0-20201217014255-9d1352758620/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E=
|
||||
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9 h1:phUcVbl53swtrUN8kQEXFhUxPlIlWyBfKmidCu7P95o=
|
||||
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-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
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-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-20201016165138-7b1cca2348c0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME=
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/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-20181017192945-9dcd33a902f4/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-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-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-20201112073958-5cba982894dd h1:5CtCZbICpIOFdgO940moixOPjc0178IU44m4EjOO5IY=
|
||||
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/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/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.3.0/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/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c h1:grhR+C34yXImVGp7EzNk+DTIk+323eIUWOmEevy6bDo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
|
||||
|
||||
@@ -65,9 +65,3 @@ func (e *manyError) Unwrap() error {
|
||||
|
||||
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) {
|
||||
var _ wrapper = &manyError{}
|
||||
|
||||
const (
|
||||
errSimple = iota
|
||||
errWrapped
|
||||
|
||||
@@ -188,7 +188,7 @@ func BlockedSvcKnown(s string) bool {
|
||||
}
|
||||
|
||||
// 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{}
|
||||
if global {
|
||||
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()
|
||||
list := d.Config.BlockedServices
|
||||
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{}
|
||||
err := json.NewDecoder(r.Body).Decode(&list)
|
||||
if err != nil {
|
||||
@@ -241,7 +241,7 @@ func (d *DNSFilter) handleBlockedServicesSet(w http.ResponseWriter, r *http.Requ
|
||||
}
|
||||
|
||||
// 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("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
|
||||
|
||||
import (
|
||||
@@ -91,12 +91,12 @@ type filtersInitializerParams struct {
|
||||
blockFilters []Filter
|
||||
}
|
||||
|
||||
// DNSFilter matches hostnames and DNS requests against filtering rules.
|
||||
type DNSFilter struct {
|
||||
// Dnsfilter holds added rules and performs hostname matches against the rules
|
||||
type Dnsfilter struct {
|
||||
rulesStorage *filterlist.RuleStorage
|
||||
filteringEngine *urlfilter.DNSEngine
|
||||
rulesStorageAllow *filterlist.RuleStorage
|
||||
filteringEngineAllow *urlfilter.DNSEngine
|
||||
rulesStorageWhite *filterlist.RuleStorage
|
||||
filteringEngineWhite *urlfilter.DNSEngine
|
||||
engineLock sync.RWMutex
|
||||
|
||||
parentalServer string // access via methods
|
||||
@@ -127,16 +127,15 @@ const (
|
||||
|
||||
// NotFilteredNotFound - host was not find in any checks, default value for result
|
||||
NotFilteredNotFound Reason = iota
|
||||
// NotFilteredAllowList - the host is explicitly allowed
|
||||
NotFilteredAllowList
|
||||
// NotFilteredError is returned when there was an error during
|
||||
// checking. Reserved, currently unused.
|
||||
// NotFilteredWhiteList - the host is explicitly whitelisted
|
||||
NotFilteredWhiteList
|
||||
// NotFilteredError - there was a transitive error during check
|
||||
NotFilteredError
|
||||
|
||||
// reasons for filtering
|
||||
|
||||
// FilteredBlockList - the host was matched to be advertising host
|
||||
FilteredBlockList
|
||||
// FilteredBlackList - the host was matched to be advertising host
|
||||
FilteredBlackList
|
||||
// FilteredSafeBrowsing - the host was matched to be malicious/phishing
|
||||
FilteredSafeBrowsing
|
||||
// FilteredParental - the host was matched to be outside of parental control settings
|
||||
@@ -147,46 +146,37 @@ const (
|
||||
FilteredSafeSearch
|
||||
// FilteredBlockedService - the host is blocked by "blocked services" settings
|
||||
FilteredBlockedService
|
||||
// FilteredRebind - the request was blocked due to DNS rebinding protection
|
||||
FilteredRebind
|
||||
|
||||
// ReasonRewrite is returned when there was a rewrite by
|
||||
// a legacy DNS Rewrite rule.
|
||||
// ReasonRewrite - rewrite rule was applied
|
||||
ReasonRewrite
|
||||
|
||||
// RewriteAutoHosts is returned when there was a rewrite by
|
||||
// autohosts rules (/etc/hosts and so on).
|
||||
RewriteAutoHosts
|
||||
|
||||
// DNSRewriteRule is returned when a $dnsrewrite filter rule was
|
||||
// applied.
|
||||
DNSRewriteRule
|
||||
// RewriteEtcHosts - rewrite by /etc/hosts rule
|
||||
RewriteEtcHosts
|
||||
)
|
||||
|
||||
// TODO(a.garipov): Resync with actual code names or replace completely
|
||||
// in HTTP API v1.
|
||||
var reasonNames = []string{
|
||||
NotFilteredNotFound: "NotFilteredNotFound",
|
||||
NotFilteredAllowList: "NotFilteredWhiteList",
|
||||
NotFilteredError: "NotFilteredError",
|
||||
"NotFilteredNotFound",
|
||||
"NotFilteredWhiteList",
|
||||
"NotFilteredError",
|
||||
|
||||
FilteredBlockList: "FilteredBlackList",
|
||||
FilteredSafeBrowsing: "FilteredSafeBrowsing",
|
||||
FilteredParental: "FilteredParental",
|
||||
FilteredInvalid: "FilteredInvalid",
|
||||
FilteredSafeSearch: "FilteredSafeSearch",
|
||||
FilteredBlockedService: "FilteredBlockedService",
|
||||
"FilteredBlackList",
|
||||
"FilteredSafeBrowsing",
|
||||
"FilteredParental",
|
||||
"FilteredInvalid",
|
||||
"FilteredSafeSearch",
|
||||
"FilteredBlockedService",
|
||||
"FilteredRebind",
|
||||
|
||||
ReasonRewrite: "Rewrite",
|
||||
|
||||
RewriteAutoHosts: "RewriteEtcHosts",
|
||||
|
||||
DNSRewriteRule: "DNSRewriteRule",
|
||||
"Rewrite",
|
||||
"RewriteEtcHosts",
|
||||
}
|
||||
|
||||
func (r Reason) String() string {
|
||||
if r < 0 || int(r) >= len(reasonNames) {
|
||||
if uint(r) >= uint(len(reasonNames)) {
|
||||
return ""
|
||||
}
|
||||
|
||||
return reasonNames[r]
|
||||
}
|
||||
|
||||
@@ -201,7 +191,7 @@ func (r Reason) In(reasons ...Reason) bool {
|
||||
}
|
||||
|
||||
// GetConfig - get configuration
|
||||
func (d *DNSFilter) GetConfig() RequestFilteringSettings {
|
||||
func (d *Dnsfilter) GetConfig() RequestFilteringSettings {
|
||||
c := RequestFilteringSettings{}
|
||||
// d.confLock.RLock()
|
||||
c.SafeSearchEnabled = d.Config.SafeSearchEnabled
|
||||
@@ -212,7 +202,7 @@ func (d *DNSFilter) GetConfig() RequestFilteringSettings {
|
||||
}
|
||||
|
||||
// WriteDiskConfig - write configuration
|
||||
func (d *DNSFilter) WriteDiskConfig(c *Config) {
|
||||
func (d *Dnsfilter) WriteDiskConfig(c *Config) {
|
||||
d.confLock.Lock()
|
||||
*c = d.Config
|
||||
c.Rewrites = rewriteArrayDup(d.Config.Rewrites)
|
||||
@@ -223,7 +213,7 @@ func (d *DNSFilter) WriteDiskConfig(c *Config) {
|
||||
// SetFilters - set new filters (synchronously or asynchronously)
|
||||
// When filters are set asynchronously, the old filters continue working until the new filters are ready.
|
||||
// In this case the caller must ensure that the old filter files are intact.
|
||||
func (d *DNSFilter) SetFilters(blockFilters, allowFilters []Filter, async bool) error {
|
||||
func (d *Dnsfilter) SetFilters(blockFilters, allowFilters []Filter, async bool) error {
|
||||
if async {
|
||||
params := filtersInitializerParams{
|
||||
allowFilters: allowFilters,
|
||||
@@ -257,7 +247,7 @@ func (d *DNSFilter) SetFilters(blockFilters, allowFilters []Filter, async bool)
|
||||
}
|
||||
|
||||
// Starts initializing new filters by signal from channel
|
||||
func (d *DNSFilter) filtersInitializer() {
|
||||
func (d *Dnsfilter) filtersInitializer() {
|
||||
for {
|
||||
params := <-d.filtersInitializerChan
|
||||
err := d.initFiltering(params.allowFilters, params.blockFilters)
|
||||
@@ -269,13 +259,13 @@ func (d *DNSFilter) filtersInitializer() {
|
||||
}
|
||||
|
||||
// Close - close the object
|
||||
func (d *DNSFilter) Close() {
|
||||
func (d *Dnsfilter) Close() {
|
||||
d.engineLock.Lock()
|
||||
defer d.engineLock.Unlock()
|
||||
d.reset()
|
||||
}
|
||||
|
||||
func (d *DNSFilter) reset() {
|
||||
func (d *Dnsfilter) reset() {
|
||||
var err error
|
||||
|
||||
if d.rulesStorage != nil {
|
||||
@@ -285,15 +275,16 @@ func (d *DNSFilter) reset() {
|
||||
}
|
||||
}
|
||||
|
||||
if d.rulesStorageAllow != nil {
|
||||
err = d.rulesStorageAllow.Close()
|
||||
if d.rulesStorageWhite != nil {
|
||||
err = d.rulesStorageWhite.Close()
|
||||
if err != nil {
|
||||
log.Error("dnsfilter: rulesStorageAllow.Close: %s", err)
|
||||
log.Error("dnsfilter: rulesStorageWhite.Close: %s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type dnsFilterContext struct {
|
||||
stats Stats
|
||||
safebrowsingCache cache.Cache
|
||||
parentalCache cache.Cache
|
||||
safeSearchCache cache.Cache
|
||||
@@ -301,63 +292,34 @@ type dnsFilterContext struct {
|
||||
|
||||
var gctx dnsFilterContext // global dnsfilter context
|
||||
|
||||
// ResultRule contains information about applied rules.
|
||||
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?
|
||||
// Result holds state of hostname check
|
||||
type Result struct {
|
||||
// IsFiltered is true if the request is filtered.
|
||||
IsFiltered bool `json:",omitempty"`
|
||||
IsFiltered bool `json:",omitempty"` // True if the host name is filtered
|
||||
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.
|
||||
Reason Reason `json:",omitempty"`
|
||||
// for ReasonRewrite:
|
||||
CanonName string `json:",omitempty"` // CNAME value
|
||||
|
||||
// Rules are applied rules. If Rules are not empty, each rule
|
||||
// is not nil.
|
||||
Rules []*ResultRule `json:",omitempty"`
|
||||
|
||||
// ReverseHosts is the reverse lookup rewrite result. It is
|
||||
// empty unless Reason is set to RewriteAutoHosts.
|
||||
// for RewriteEtcHosts:
|
||||
ReverseHosts []string `json:",omitempty"`
|
||||
|
||||
// IPList is the lookup rewrite result. It is empty unless
|
||||
// Reason is set to RewriteAutoHosts or ReasonRewrite.
|
||||
IPList []net.IP `json:",omitempty"`
|
||||
// for ReasonRewrite & RewriteEtcHosts:
|
||||
IPList []net.IP `json:",omitempty"` // list of IP addresses
|
||||
|
||||
// CanonName is the CNAME value from the lookup rewrite result.
|
||||
// It is empty unless Reason is set to ReasonRewrite.
|
||||
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"`
|
||||
// for FilteredBlockedService:
|
||||
ServiceName string `json:",omitempty"` // Name of the blocked service
|
||||
}
|
||||
|
||||
// Matched returns true if any match at all was found regardless of
|
||||
// whether it was filtered or not.
|
||||
// Matched can be used to see if any match at all was found, no matter filtered or not
|
||||
func (r Reason) Matched() bool {
|
||||
return r != NotFilteredNotFound
|
||||
}
|
||||
|
||||
// CheckHostRules tries to match the host against filtering rules only.
|
||||
func (d *DNSFilter) CheckHostRules(host string, qtype uint16, setts *RequestFilteringSettings) (Result, error) {
|
||||
// CheckHostRules tries to match the host against filtering rules only
|
||||
func (d *Dnsfilter) CheckHostRules(host string, qtype uint16, setts *RequestFilteringSettings) (Result, error) {
|
||||
if !setts.FilteringEnabled {
|
||||
return Result{}, nil
|
||||
}
|
||||
@@ -365,9 +327,9 @@ func (d *DNSFilter) CheckHostRules(host string, qtype uint16, setts *RequestFilt
|
||||
return d.matchHost(host, qtype, *setts)
|
||||
}
|
||||
|
||||
// CheckHost tries to match the host against filtering rules, then
|
||||
// safebrowsing and parental control rules, if they are enabled.
|
||||
func (d *DNSFilter) CheckHost(host string, qtype uint16, setts *RequestFilteringSettings) (Result, error) {
|
||||
// CheckHost tries to match the host against filtering rules,
|
||||
// then safebrowsing and parental if they are enabled
|
||||
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
|
||||
if host == "" {
|
||||
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 {
|
||||
result, err = d.matchHost(host, qtype, *setts)
|
||||
if err != nil {
|
||||
@@ -450,10 +415,10 @@ func (d *DNSFilter) CheckHost(host string, qtype uint16, setts *RequestFiltering
|
||||
return Result{}, nil
|
||||
}
|
||||
|
||||
func (d *DNSFilter) checkAutoHosts(host string, qtype uint16, result *Result) (matched bool) {
|
||||
func (d *Dnsfilter) checkAutoHosts(host string, qtype uint16, result *Result) (matched bool) {
|
||||
ips := d.Config.AutoHosts.Process(host, qtype)
|
||||
if ips != nil {
|
||||
result.Reason = RewriteAutoHosts
|
||||
result.Reason = RewriteEtcHosts
|
||||
result.IPList = ips
|
||||
|
||||
return true
|
||||
@@ -461,7 +426,7 @@ func (d *DNSFilter) checkAutoHosts(host string, qtype uint16, result *Result) (m
|
||||
|
||||
revHosts := d.Config.AutoHosts.ProcessReverse(host, qtype)
|
||||
if len(revHosts) != 0 {
|
||||
result.Reason = RewriteAutoHosts
|
||||
result.Reason = RewriteEtcHosts
|
||||
|
||||
// TODO(a.garipov): Optimize this with a buffer.
|
||||
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)
|
||||
// . 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
|
||||
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()
|
||||
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)
|
||||
|
||||
if host == rr[0].Answer { // "host == CNAME" is an exception
|
||||
res.Reason = NotFilteredNotFound
|
||||
|
||||
res.Reason = 0
|
||||
return res
|
||||
}
|
||||
|
||||
@@ -540,16 +506,9 @@ func matchBlockedServicesRules(host string, svcs []ServiceEntry) Result {
|
||||
res.Reason = FilteredBlockedService
|
||||
res.IsFiltered = true
|
||||
res.ServiceName = s.Name
|
||||
|
||||
ruleText := rule.Text()
|
||||
res.Rules = []*ResultRule{{
|
||||
FilterListID: int64(rule.GetFilterListID()),
|
||||
Text: ruleText,
|
||||
}}
|
||||
|
||||
log.Debug("blocked services: matched rule: %s host: %s service: %s",
|
||||
ruleText, host, s.Name)
|
||||
|
||||
res.Rule = rule.Text()
|
||||
log.Debug("Blocked Services: matched rule: %s host: %s service: %s",
|
||||
res.Rule, host, s.Name)
|
||||
return res
|
||||
}
|
||||
}
|
||||
@@ -616,12 +575,12 @@ func createFilteringEngine(filters []Filter) (*filterlist.RuleStorage, *urlfilte
|
||||
}
|
||||
|
||||
// Initialize urlfilter objects.
|
||||
func (d *DNSFilter) initFiltering(allowFilters, blockFilters []Filter) error {
|
||||
func (d *Dnsfilter) initFiltering(allowFilters, blockFilters []Filter) error {
|
||||
rulesStorage, filteringEngine, err := createFilteringEngine(blockFilters)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rulesStorageAllow, filteringEngineAllow, err := createFilteringEngine(allowFilters)
|
||||
rulesStorageWhite, filteringEngineWhite, err := createFilteringEngine(allowFilters)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -630,8 +589,8 @@ func (d *DNSFilter) initFiltering(allowFilters, blockFilters []Filter) error {
|
||||
d.reset()
|
||||
d.rulesStorage = rulesStorage
|
||||
d.filteringEngine = filteringEngine
|
||||
d.rulesStorageAllow = rulesStorageAllow
|
||||
d.filteringEngineAllow = filteringEngineAllow
|
||||
d.rulesStorageWhite = rulesStorageWhite
|
||||
d.filteringEngineWhite = filteringEngineWhite
|
||||
d.engineLock.Unlock()
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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,
|
||||
// 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()
|
||||
// Keep in mind that this lock must be held no just when calling Match()
|
||||
// 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,
|
||||
}
|
||||
|
||||
if d.filteringEngineAllow != nil {
|
||||
dnsres, ok := d.filteringEngineAllow.MatchRequest(ureq)
|
||||
if d.filteringEngineWhite != nil {
|
||||
rr, ok := d.filteringEngineWhite.MatchRequest(ureq)
|
||||
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
|
||||
}
|
||||
|
||||
dnsres, ok := d.filteringEngine.MatchRequest(ureq)
|
||||
|
||||
// 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 {
|
||||
rr, ok := d.filteringEngine.MatchRequest(ureq)
|
||||
if !ok {
|
||||
return Result{}, nil
|
||||
}
|
||||
|
||||
if dnsres.NetworkRule != nil {
|
||||
if rr.NetworkRule != nil {
|
||||
log.Debug("Filtering: found rule for host %q: %q list_id: %d",
|
||||
host, dnsres.NetworkRule.Text(), dnsres.NetworkRule.GetFilterListID())
|
||||
reason := FilteredBlockList
|
||||
if dnsres.NetworkRule.Whitelist {
|
||||
reason = NotFilteredAllowList
|
||||
host, rr.NetworkRule.Text(), rr.NetworkRule.GetFilterListID())
|
||||
reason := FilteredBlackList
|
||||
if rr.NetworkRule.Whitelist {
|
||||
reason = NotFilteredWhiteList
|
||||
}
|
||||
|
||||
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()
|
||||
|
||||
res := makeResult(rr.NetworkRule, reason)
|
||||
return res, nil
|
||||
}
|
||||
|
||||
if qtype == dns.TypeAAAA && dnsres.HostRulesV6 != nil {
|
||||
rule := dnsres.HostRulesV6[0] // note that we process only 1 matched rule
|
||||
if qtype == dns.TypeA && rr.HostRulesV4 != nil {
|
||||
rule := rr.HostRulesV4[0] // note that we process only 1 matched rule
|
||||
log.Debug("Filtering: found rule for host %q: %q list_id: %d",
|
||||
host, rule.Text(), rule.GetFilterListID())
|
||||
res = makeResult(rule, FilteredBlockList)
|
||||
res.Rules[0].IP = rule.IP
|
||||
|
||||
res := makeResult(rule, FilteredBlackList)
|
||||
res.IP = rule.IP.To4()
|
||||
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
|
||||
// Return the first matched host rule, but without an IP address
|
||||
var rule rules.Rule
|
||||
if dnsres.HostRulesV4 != nil {
|
||||
rule = dnsres.HostRulesV4[0]
|
||||
} else if dnsres.HostRulesV6 != nil {
|
||||
rule = dnsres.HostRulesV6[0]
|
||||
if rr.HostRulesV4 != nil {
|
||||
rule = rr.HostRulesV4[0]
|
||||
} else if rr.HostRulesV6 != nil {
|
||||
rule = rr.HostRulesV6[0]
|
||||
}
|
||||
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 = net.IP{}
|
||||
|
||||
res := makeResult(rule, FilteredBlackList)
|
||||
res.IP = net.IP{}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
return Result{}, nil
|
||||
}
|
||||
|
||||
// makeResult returns a properly constructed Result.
|
||||
// Construct Result object
|
||||
func makeResult(rule rules.Rule, reason Reason) Result {
|
||||
res := Result{
|
||||
Reason: reason,
|
||||
Rules: []*ResultRule{{
|
||||
FilterListID: int64(rule.GetFilterListID()),
|
||||
Text: rule.Text(),
|
||||
}},
|
||||
}
|
||||
|
||||
if reason == FilteredBlockList {
|
||||
res := Result{}
|
||||
res.FilterID = int64(rule.GetFilterListID())
|
||||
res.Rule = rule.Text()
|
||||
res.Reason = reason
|
||||
if reason == FilteredBlackList {
|
||||
res.IsFiltered = true
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
@@ -780,7 +710,7 @@ func InitModule() {
|
||||
}
|
||||
|
||||
// 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 {
|
||||
cacheConf := cache.Config{
|
||||
EnableLRU: true,
|
||||
@@ -802,7 +732,7 @@ func New(c *Config, blockFilters []Filter) *DNSFilter {
|
||||
}
|
||||
}
|
||||
|
||||
d := new(DNSFilter)
|
||||
d := new(Dnsfilter)
|
||||
|
||||
err := d.initSecurityServices()
|
||||
if err != nil {
|
||||
@@ -840,7 +770,7 @@ func New(c *Config, blockFilters []Filter) *DNSFilter {
|
||||
// Start - start the module:
|
||||
// . start async filtering initializer goroutine
|
||||
// . register web handlers
|
||||
func (d *DNSFilter) Start() {
|
||||
func (d *Dnsfilter) Start() {
|
||||
d.filtersInitializerChan = make(chan filtersInitializerParams, 1)
|
||||
go d.filtersInitializer()
|
||||
|
||||
@@ -850,3 +780,12 @@ func (d *DNSFilter) Start() {
|
||||
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.FilteringEnabled = true
|
||||
if c != nil {
|
||||
@@ -58,48 +58,38 @@ func NewForTest(c *Config, filters []Filter) *DNSFilter {
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *DNSFilter) checkMatch(t *testing.T, hostname string) {
|
||||
func (d *Dnsfilter) checkMatch(t *testing.T, hostname string) {
|
||||
t.Helper()
|
||||
res, err := d.CheckHost(hostname, dns.TypeA, &setts)
|
||||
ret, err := d.CheckHost(hostname, dns.TypeA, &setts)
|
||||
if err != nil {
|
||||
t.Errorf("Error while matching host %s: %s", hostname, err)
|
||||
}
|
||||
if !res.IsFiltered {
|
||||
if !ret.IsFiltered {
|
||||
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()
|
||||
|
||||
res, err := d.CheckHost(hostname, qtype, &setts)
|
||||
ret, err := d.CheckHost(hostname, qtype, &setts)
|
||||
if err != nil {
|
||||
t.Errorf("Error while matching host %s: %s", hostname, err)
|
||||
}
|
||||
|
||||
if !res.IsFiltered {
|
||||
if !ret.IsFiltered {
|
||||
t.Errorf("Expected hostname %s to match", hostname)
|
||||
}
|
||||
|
||||
if len(res.Rules) == 0 {
|
||||
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)
|
||||
if ret.IP == nil || ret.IP.String() != ip {
|
||||
t.Errorf("Expected ip %s to match, actual: %v", ip, ret.IP)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DNSFilter) checkMatchEmpty(t *testing.T, hostname string) {
|
||||
func (d *Dnsfilter) checkMatchEmpty(t *testing.T, hostname string) {
|
||||
t.Helper()
|
||||
res, err := d.CheckHost(hostname, dns.TypeA, &setts)
|
||||
ret, err := d.CheckHost(hostname, dns.TypeA, &setts)
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -130,43 +120,26 @@ func TestEtcHostsMatching(t *testing.T) {
|
||||
d.checkMatchIP(t, "block.com", "0.0.0.0", dns.TypeA)
|
||||
|
||||
// ...but empty IPv6
|
||||
res, err := d.CheckHost("block.com", dns.TypeAAAA, &setts)
|
||||
assert.Nil(t, err)
|
||||
assert.True(t, res.IsFiltered)
|
||||
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)
|
||||
}
|
||||
ret, err := d.CheckHost("block.com", dns.TypeAAAA, &setts)
|
||||
assert.True(t, err == nil && ret.IsFiltered && ret.IP != nil && len(ret.IP) == 0)
|
||||
assert.True(t, ret.Rule == "0.0.0.0 block.com")
|
||||
|
||||
// IPv6
|
||||
d.checkMatchIP(t, "ipv6.com", addr6, dns.TypeAAAA)
|
||||
|
||||
// ...but empty IPv4
|
||||
res, err = d.CheckHost("ipv6.com", dns.TypeA, &setts)
|
||||
assert.Nil(t, err)
|
||||
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)
|
||||
}
|
||||
ret, err = d.CheckHost("ipv6.com", dns.TypeA, &setts)
|
||||
assert.True(t, err == nil && ret.IsFiltered && ret.IP != nil && len(ret.IP) == 0)
|
||||
|
||||
// 2 IPv4 (return only the first one)
|
||||
res, err = d.CheckHost("host2", dns.TypeA, &setts)
|
||||
assert.Nil(t, err)
|
||||
assert.True(t, res.IsFiltered)
|
||||
if assert.Len(t, res.Rules, 1) {
|
||||
loopback4 := net.IP{0, 0, 0, 1}
|
||||
assert.Equal(t, res.Rules[0].IP, loopback4)
|
||||
}
|
||||
ret, err = d.CheckHost("host2", dns.TypeA, &setts)
|
||||
assert.True(t, err == nil && ret.IsFiltered)
|
||||
assert.True(t, ret.IP != nil && ret.IP.Equal(net.ParseIP("0.0.0.1")))
|
||||
|
||||
// ...and 1 IPv6 address
|
||||
res, err = d.CheckHost("host2", dns.TypeAAAA, &setts)
|
||||
assert.Nil(t, err)
|
||||
assert.True(t, res.IsFiltered)
|
||||
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)
|
||||
}
|
||||
ret, err = d.CheckHost("host2", dns.TypeAAAA, &setts)
|
||||
assert.True(t, err == nil && ret.IsFiltered)
|
||||
assert.True(t, ret.IP != nil && ret.IP.Equal(net.ParseIP("::1")))
|
||||
}
|
||||
|
||||
// SAFE BROWSING
|
||||
@@ -178,6 +151,7 @@ func TestSafeBrowsing(t *testing.T) {
|
||||
|
||||
d := NewForTest(&Config{SafeBrowsingEnabled: true}, nil)
|
||||
defer d.Close()
|
||||
gctx.stats.Safebrowsing.Requests = 0
|
||||
d.checkMatch(t, "wmconvirus.narod.ru")
|
||||
|
||||
assert.True(t, strings.Contains(logOutput.String(), "SafeBrowsing lookup for wmconvirus.narod.ru"))
|
||||
@@ -232,11 +206,13 @@ func TestCheckHostSafeSearchYandex(t *testing.T) {
|
||||
|
||||
// Check host for each domain
|
||||
for _, host := range yandex {
|
||||
res, err := d.CheckHost(host, dns.TypeA, &setts)
|
||||
assert.Nil(t, err)
|
||||
assert.True(t, res.IsFiltered)
|
||||
if assert.Len(t, res.Rules, 1) {
|
||||
assert.Equal(t, res.Rules[0].IP.String(), "213.180.193.56")
|
||||
result, err := d.CheckHost(host, dns.TypeA, &setts)
|
||||
if err != nil {
|
||||
t.Errorf("SafeSearch doesn't work for yandex domain `%s` cause %s", host, err)
|
||||
}
|
||||
|
||||
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
|
||||
for _, host := range googleDomains {
|
||||
res, err := d.CheckHost(host, dns.TypeA, &setts)
|
||||
assert.Nil(t, err)
|
||||
assert.True(t, res.IsFiltered)
|
||||
if assert.Len(t, res.Rules, 1) {
|
||||
assert.NotEqual(t, res.Rules[0].IP.String(), "0.0.0.0")
|
||||
result, err := d.CheckHost(host, dns.TypeA, &setts)
|
||||
if err != nil {
|
||||
t.Errorf("SafeSearch doesn't work for %s cause %s", host, err)
|
||||
}
|
||||
|
||||
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()
|
||||
domain := "yandex.ru"
|
||||
|
||||
// Check host with disabled safesearch.
|
||||
res, err := d.CheckHost(domain, dns.TypeA, &setts)
|
||||
assert.Nil(t, err)
|
||||
assert.False(t, res.IsFiltered)
|
||||
assert.Len(t, res.Rules, 0)
|
||||
var result Result
|
||||
var err error
|
||||
|
||||
// Check host with disabled safesearch
|
||||
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)
|
||||
defer d.Close()
|
||||
|
||||
res, err = d.CheckHost(domain, dns.TypeA, &setts)
|
||||
result, err = d.CheckHost(domain, dns.TypeA, &setts)
|
||||
if err != nil {
|
||||
t.Fatalf("CheckHost for safesearh domain %s failed cause %s", domain, err)
|
||||
}
|
||||
|
||||
// For yandex we already know valid ip.
|
||||
if assert.Len(t, res.Rules, 1) {
|
||||
assert.Equal(t, res.Rules[0].IP.String(), "213.180.193.56")
|
||||
// Fir yandex we already know valid ip
|
||||
if result.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)
|
||||
assert.True(t, isFound)
|
||||
if assert.Len(t, cachedValue.Rules, 1) {
|
||||
assert.Equal(t, cachedValue.Rules[0].IP.String(), "213.180.193.56")
|
||||
|
||||
if !isFound {
|
||||
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)
|
||||
defer d.Close()
|
||||
domain := "www.google.ru"
|
||||
res, err := d.CheckHost(domain, dns.TypeA, &setts)
|
||||
assert.Nil(t, err)
|
||||
assert.False(t, res.IsFiltered)
|
||||
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!")
|
||||
}
|
||||
|
||||
d = NewForTest(&Config{SafeSearchEnabled: true}, nil)
|
||||
defer d.Close()
|
||||
@@ -322,17 +313,25 @@ func TestSafeSearchCacheGoogle(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
res, err = d.CheckHost(domain, dns.TypeA, &setts)
|
||||
assert.Nil(t, err)
|
||||
if assert.Len(t, res.Rules, 1) {
|
||||
assert.True(t, res.Rules[0].IP.Equal(ip))
|
||||
result, err = d.CheckHost(domain, dns.TypeA, &setts)
|
||||
if err != nil {
|
||||
t.Fatalf("CheckHost for safesearh domain %s failed cause %s", domain, err)
|
||||
}
|
||||
|
||||
// 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)
|
||||
assert.True(t, isFound)
|
||||
if assert.Len(t, cachedValue.Rules, 1) {
|
||||
assert.True(t, cachedValue.Rules[0].IP.Equal(ip))
|
||||
|
||||
if !isFound {
|
||||
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 (
|
||||
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
|
||||
regexRules = `/example\.org/` + nl + `@@||test.example.org^` + nl
|
||||
maskRules = `test*.example.org^` + nl + `exam*.com` + nl
|
||||
@@ -380,49 +379,49 @@ var tests = []struct {
|
||||
reason Reason
|
||||
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^", "doubleclick.net.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, "test.example.org", true, FilteredBlockList, dns.TypeA},
|
||||
{"blocking", blockingRules, "test.test.example.org", true, FilteredBlockList, dns.TypeA},
|
||||
{"blocking", blockingRules, "example.org", true, FilteredBlackList, dns.TypeA},
|
||||
{"blocking", blockingRules, "test.example.org", true, FilteredBlackList, dns.TypeA},
|
||||
{"blocking", blockingRules, "test.test.example.org", true, FilteredBlackList, dns.TypeA},
|
||||
{"blocking", blockingRules, "testexample.org", false, NotFilteredNotFound, dns.TypeA},
|
||||
{"blocking", blockingRules, "onemoreexample.org", false, NotFilteredNotFound, dns.TypeA},
|
||||
|
||||
{"allowlist", allowlistRules, "example.org", true, FilteredBlockList, dns.TypeA},
|
||||
{"allowlist", allowlistRules, "test.example.org", false, NotFilteredAllowList, dns.TypeA},
|
||||
{"allowlist", allowlistRules, "test.test.example.org", false, NotFilteredAllowList, dns.TypeA},
|
||||
{"allowlist", allowlistRules, "testexample.org", false, NotFilteredNotFound, dns.TypeA},
|
||||
{"allowlist", allowlistRules, "onemoreexample.org", false, NotFilteredNotFound, dns.TypeA},
|
||||
{"whitelist", whitelistRules, "example.org", true, FilteredBlackList, dns.TypeA},
|
||||
{"whitelist", whitelistRules, "test.example.org", false, NotFilteredWhiteList, dns.TypeA},
|
||||
{"whitelist", whitelistRules, "test.test.example.org", false, NotFilteredWhiteList, dns.TypeA},
|
||||
{"whitelist", whitelistRules, "testexample.org", false, NotFilteredNotFound, dns.TypeA},
|
||||
{"whitelist", whitelistRules, "onemoreexample.org", false, NotFilteredNotFound, dns.TypeA},
|
||||
|
||||
{"important", importantRules, "example.org", false, NotFilteredAllowList, dns.TypeA},
|
||||
{"important", importantRules, "test.example.org", true, FilteredBlockList, dns.TypeA},
|
||||
{"important", importantRules, "test.test.example.org", true, FilteredBlockList, dns.TypeA},
|
||||
{"important", importantRules, "example.org", false, NotFilteredWhiteList, dns.TypeA},
|
||||
{"important", importantRules, "test.example.org", true, FilteredBlackList, dns.TypeA},
|
||||
{"important", importantRules, "test.test.example.org", true, FilteredBlackList, dns.TypeA},
|
||||
{"important", importantRules, "testexample.org", false, NotFilteredNotFound, dns.TypeA},
|
||||
{"important", importantRules, "onemoreexample.org", false, NotFilteredNotFound, dns.TypeA},
|
||||
|
||||
{"regex", regexRules, "example.org", true, FilteredBlockList, dns.TypeA},
|
||||
{"regex", regexRules, "test.example.org", false, NotFilteredAllowList, dns.TypeA},
|
||||
{"regex", regexRules, "test.test.example.org", false, NotFilteredAllowList, dns.TypeA},
|
||||
{"regex", regexRules, "testexample.org", true, FilteredBlockList, dns.TypeA},
|
||||
{"regex", regexRules, "onemoreexample.org", true, FilteredBlockList, dns.TypeA},
|
||||
{"regex", regexRules, "example.org", true, FilteredBlackList, dns.TypeA},
|
||||
{"regex", regexRules, "test.example.org", false, NotFilteredWhiteList, dns.TypeA},
|
||||
{"regex", regexRules, "test.test.example.org", false, NotFilteredWhiteList, dns.TypeA},
|
||||
{"regex", regexRules, "testexample.org", true, FilteredBlackList, dns.TypeA},
|
||||
{"regex", regexRules, "onemoreexample.org", true, FilteredBlackList, dns.TypeA},
|
||||
|
||||
{"mask", maskRules, "test.example.org", true, FilteredBlockList, dns.TypeA},
|
||||
{"mask", maskRules, "test2.example.org", true, FilteredBlockList, dns.TypeA},
|
||||
{"mask", maskRules, "example.com", true, FilteredBlockList, dns.TypeA},
|
||||
{"mask", maskRules, "exampleeee.com", true, FilteredBlockList, dns.TypeA},
|
||||
{"mask", maskRules, "onemoreexamsite.com", true, FilteredBlockList, dns.TypeA},
|
||||
{"mask", maskRules, "test.example.org", true, FilteredBlackList, dns.TypeA},
|
||||
{"mask", maskRules, "test2.example.org", true, FilteredBlackList, dns.TypeA},
|
||||
{"mask", maskRules, "example.com", true, FilteredBlackList, dns.TypeA},
|
||||
{"mask", maskRules, "exampleeee.com", true, FilteredBlackList, dns.TypeA},
|
||||
{"mask", maskRules, "onemoreexamsite.com", true, FilteredBlackList, dns.TypeA},
|
||||
{"mask", maskRules, "example.org", false, NotFilteredNotFound, dns.TypeA},
|
||||
{"mask", maskRules, "testexample.org", false, NotFilteredNotFound, dns.TypeA},
|
||||
{"mask", maskRules, "example.co.uk", false, NotFilteredNotFound, dns.TypeA},
|
||||
|
||||
{"dnstype", dnstypeRules, "onemoreexample.org", false, NotFilteredNotFound, dns.TypeA},
|
||||
{"dnstype", dnstypeRules, "example.org", false, NotFilteredNotFound, dns.TypeA},
|
||||
{"dnstype", dnstypeRules, "example.org", true, FilteredBlockList, dns.TypeAAAA},
|
||||
{"dnstype", dnstypeRules, "test.example.org", false, NotFilteredAllowList, dns.TypeA},
|
||||
{"dnstype", dnstypeRules, "test.example.org", false, NotFilteredAllowList, dns.TypeAAAA},
|
||||
{"dnstype", dnstypeRules, "example.org", true, FilteredBlackList, dns.TypeAAAA},
|
||||
{"dnstype", dnstypeRules, "test.example.org", false, NotFilteredWhiteList, dns.TypeA},
|
||||
{"dnstype", dnstypeRules, "test.example.org", false, NotFilteredWhiteList, dns.TypeAAAA},
|
||||
}
|
||||
|
||||
func TestMatching(t *testing.T) {
|
||||
@@ -434,15 +433,15 @@ func TestMatching(t *testing.T) {
|
||||
d := NewForTest(nil, filters)
|
||||
defer d.Close()
|
||||
|
||||
res, err := d.CheckHost(test.hostname, test.dnsType, &setts)
|
||||
ret, err := d.CheckHost(test.hostname, test.dnsType, &setts)
|
||||
if err != nil {
|
||||
t.Errorf("Error while matching host %s: %s", test.hostname, err)
|
||||
}
|
||||
if res.IsFiltered != test.isFiltered {
|
||||
t.Errorf("Hostname %s has wrong result (%v must be %v)", test.hostname, res.IsFiltered, test.isFiltered)
|
||||
if ret.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 {
|
||||
t.Errorf("Hostname %s has wrong reason (%v must be %v)", test.hostname, res.Reason.String(), test.reason.String())
|
||||
if ret.Reason != test.reason {
|
||||
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()
|
||||
|
||||
// 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, !res.IsFiltered && res.Reason == NotFilteredAllowList)
|
||||
if assert.Len(t, res.Rules, 1) {
|
||||
assert.True(t, res.Rules[0].Text == "||host1^")
|
||||
}
|
||||
assert.True(t, !ret.IsFiltered && ret.Reason == NotFilteredWhiteList)
|
||||
assert.True(t, ret.Rule == "||host1^")
|
||||
|
||||
// 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, res.IsFiltered && res.Reason == FilteredBlockList)
|
||||
if assert.Len(t, res.Rules, 1) {
|
||||
assert.True(t, res.Rules[0].Text == "||host2^")
|
||||
}
|
||||
assert.True(t, ret.IsFiltered && ret.Reason == FilteredBlackList)
|
||||
assert.True(t, ret.Rule == "||host2^")
|
||||
}
|
||||
|
||||
// CLIENT SETTINGS
|
||||
@@ -511,8 +506,8 @@ func TestClientSettings(t *testing.T) {
|
||||
|
||||
// blocked by filters
|
||||
r, _ = d.CheckHost("example.org", dns.TypeA, &setts)
|
||||
if !r.IsFiltered || r.Reason != FilteredBlockList {
|
||||
t.Fatalf("CheckHost FilteredBlockList")
|
||||
if !r.IsFiltered || r.Reason != FilteredBlackList {
|
||||
t.Fatalf("CheckHost FilteredBlackList")
|
||||
}
|
||||
|
||||
// blocked by parental
|
||||
@@ -564,11 +559,11 @@ func BenchmarkSafeBrowsing(b *testing.B) {
|
||||
defer d.Close()
|
||||
for n := 0; n < b.N; n++ {
|
||||
hostname := "wmconvirus.narod.ru"
|
||||
res, err := d.CheckHost(hostname, dns.TypeA, &setts)
|
||||
ret, err := d.CheckHost(hostname, dns.TypeA, &setts)
|
||||
if err != nil {
|
||||
b.Errorf("Error while matching host %s: %s", hostname, err)
|
||||
}
|
||||
if !res.IsFiltered {
|
||||
if !ret.IsFiltered {
|
||||
b.Errorf("Expected hostname %s to match", hostname)
|
||||
}
|
||||
}
|
||||
@@ -580,11 +575,11 @@ func BenchmarkSafeBrowsingParallel(b *testing.B) {
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
hostname := "wmconvirus.narod.ru"
|
||||
res, err := d.CheckHost(hostname, dns.TypeA, &setts)
|
||||
ret, err := d.CheckHost(hostname, dns.TypeA, &setts)
|
||||
if err != nil {
|
||||
b.Errorf("Error while matching host %s: %s", hostname, err)
|
||||
}
|
||||
if !res.IsFiltered {
|
||||
if !ret.IsFiltered {
|
||||
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 {
|
||||
d.Rewrites[i].prepare()
|
||||
}
|
||||
@@ -148,7 +148,7 @@ type rewriteEntryJSON struct {
|
||||
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{}
|
||||
|
||||
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{}
|
||||
err := json.NewDecoder(r.Body).Decode(&jsent)
|
||||
if err != nil {
|
||||
@@ -191,7 +191,7 @@ func (d *DNSFilter) handleRewriteAdd(w http.ResponseWriter, r *http.Request) {
|
||||
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{}
|
||||
err := json.NewDecoder(r.Body).Decode(&jsent)
|
||||
if err != nil {
|
||||
@@ -218,7 +218,7 @@ func (d *DNSFilter) handleRewriteDelete(w http.ResponseWriter, r *http.Request)
|
||||
d.Config.ConfigModified()
|
||||
}
|
||||
|
||||
func (d *DNSFilter) registerRewritesHandlers() {
|
||||
func (d *Dnsfilter) registerRewritesHandlers() {
|
||||
d.Config.HTTPRegister("GET", "/control/rewrite/list", d.handleRewriteList)
|
||||
d.Config.HTTPRegister("POST", "/control/rewrite/add", d.handleRewriteAdd)
|
||||
d.Config.HTTPRegister("POST", "/control/rewrite/delete", d.handleRewriteDelete)
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
)
|
||||
|
||||
func TestRewrites(t *testing.T) {
|
||||
d := DNSFilter{}
|
||||
d := Dnsfilter{}
|
||||
// CNAME, A, AAAA
|
||||
d.Rewrites = []RewriteEntry{
|
||||
{"somecname", "somehost.com", 0, nil},
|
||||
@@ -104,7 +104,7 @@ func TestRewrites(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRewritesLevels(t *testing.T) {
|
||||
d := DNSFilter{}
|
||||
d := Dnsfilter{}
|
||||
// exact host, wildcard L2, wildcard L3
|
||||
d.Rewrites = []RewriteEntry{
|
||||
{"host.com", "1.1.1.1", 0, nil},
|
||||
@@ -133,7 +133,7 @@ func TestRewritesLevels(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRewritesExceptionCNAME(t *testing.T) {
|
||||
d := DNSFilter{}
|
||||
d := Dnsfilter{}
|
||||
// wildcard; exception for a sub-domain
|
||||
d.Rewrites = []RewriteEntry{
|
||||
{"*.host.com", "2.2.2.2", 0, nil},
|
||||
@@ -153,7 +153,7 @@ func TestRewritesExceptionCNAME(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRewritesExceptionWC(t *testing.T) {
|
||||
d := DNSFilter{}
|
||||
d := Dnsfilter{}
|
||||
// wildcard; exception for a sub-wildcard
|
||||
d.Rewrites = []RewriteEntry{
|
||||
{"*.host.com", "2.2.2.2", 0, nil},
|
||||
@@ -173,7 +173,7 @@ func TestRewritesExceptionWC(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRewritesExceptionIP(t *testing.T) {
|
||||
d := DNSFilter{}
|
||||
d := Dnsfilter{}
|
||||
// exception for AAAA record
|
||||
d.Rewrites = []RewriteEntry{
|
||||
{"host.com", "1.2.3.4", 0, nil},
|
||||
|
||||
@@ -18,7 +18,7 @@ import (
|
||||
expire byte[4]
|
||||
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
|
||||
|
||||
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
|
||||
func (d *DNSFilter) SafeSearchDomain(host string) (string, bool) {
|
||||
func (d *Dnsfilter) SafeSearchDomain(host string) (string, bool) {
|
||||
val, ok := safeSearchDomains[host]
|
||||
return val, ok
|
||||
}
|
||||
|
||||
func (d *DNSFilter) checkSafeSearch(host string) (Result, error) {
|
||||
func (d *Dnsfilter) checkSafeSearch(host string) (Result, error) {
|
||||
if log.GetLevel() >= log.DEBUG {
|
||||
timer := log.StartTimer()
|
||||
defer timer.LogElapsed("SafeSearch: lookup for %s", host)
|
||||
@@ -87,52 +87,49 @@ func (d *DNSFilter) checkSafeSearch(host string) (Result, error) {
|
||||
return Result{}, nil
|
||||
}
|
||||
|
||||
res := Result{
|
||||
IsFiltered: true,
|
||||
Reason: FilteredSafeSearch,
|
||||
Rules: []*ResultRule{{}},
|
||||
}
|
||||
|
||||
res := Result{IsFiltered: true, Reason: FilteredSafeSearch}
|
||||
if ip := net.ParseIP(safeHost); ip != nil {
|
||||
res.Rules[0].IP = ip
|
||||
res.IP = ip
|
||||
valLen := d.setCacheResult(gctx.safeSearchCache, host, res)
|
||||
log.Debug("SafeSearch: stored in cache: %s (%d bytes)", host, valLen)
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// 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 {
|
||||
log.Tracef("SafeSearchDomain for %s was found but failed to lookup for %s cause %s", host, safeHost, err)
|
||||
return Result{}, err
|
||||
}
|
||||
|
||||
for _, ip := range ips {
|
||||
if ipv4 := ip.To4(); ipv4 != nil {
|
||||
res.Rules[0].IP = ipv4
|
||||
|
||||
l := d.setCacheResult(gctx.safeSearchCache, host, res)
|
||||
log.Debug("SafeSearch: stored in cache: %s (%d bytes)", host, l)
|
||||
|
||||
return res, nil
|
||||
for _, i := range addrs {
|
||||
if ipv4 := i.To4(); ipv4 != nil {
|
||||
res.IP = ipv4
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
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.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.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{}{
|
||||
"enabled": d.Config.SafeSearchEnabled,
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// Safe Browsing, Parental Control
|
||||
|
||||
package dnsfilter
|
||||
|
||||
import (
|
||||
@@ -20,8 +22,6 @@ import (
|
||||
"golang.org/x/net/publicsuffix"
|
||||
)
|
||||
|
||||
// Safe browsing and parental control methods.
|
||||
|
||||
const (
|
||||
dnsTimeout = 3 * time.Second
|
||||
defaultSafebrowsingServer = `https://dns-family.adguard.com/dns-query`
|
||||
@@ -30,7 +30,7 @@ const (
|
||||
pcTXTSuffix = `pc.dns.adguard.com.`
|
||||
)
|
||||
|
||||
func (d *DNSFilter) initSecurityServices() error {
|
||||
func (d *Dnsfilter) initSecurityServices() error {
|
||||
var err error
|
||||
d.safeBrowsingServer = defaultSafebrowsingServer
|
||||
d.parentalServer = defaultParentalServer
|
||||
@@ -287,7 +287,7 @@ func check(c *sbCtx, r Result, u upstream.Upstream) (Result, error) {
|
||||
return Result{}, nil
|
||||
}
|
||||
|
||||
func (d *DNSFilter) checkSafeBrowsing(host string) (Result, error) {
|
||||
func (d *Dnsfilter) checkSafeBrowsing(host string) (Result, error) {
|
||||
if log.GetLevel() >= log.DEBUG {
|
||||
timer := log.StartTimer()
|
||||
defer timer.LogElapsed("SafeBrowsing lookup for %s", host)
|
||||
@@ -301,14 +301,12 @@ func (d *DNSFilter) checkSafeBrowsing(host string) (Result, error) {
|
||||
res := Result{
|
||||
IsFiltered: true,
|
||||
Reason: FilteredSafeBrowsing,
|
||||
Rules: []*ResultRule{{
|
||||
Text: "adguard-malware-shavar",
|
||||
}},
|
||||
Rule: "adguard-malware-shavar",
|
||||
}
|
||||
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 {
|
||||
timer := log.StartTimer()
|
||||
defer timer.LogElapsed("Parental lookup for %s", host)
|
||||
@@ -322,9 +320,7 @@ func (d *DNSFilter) checkParental(host string) (Result, error) {
|
||||
res := Result{
|
||||
IsFiltered: true,
|
||||
Reason: FilteredParental,
|
||||
Rules: []*ResultRule{{
|
||||
Text: "parental CATEGORY_BLACKLISTED",
|
||||
}},
|
||||
Rule: "parental CATEGORY_BLACKLISTED",
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
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.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.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{}{
|
||||
"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.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.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{}{
|
||||
"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/disable", d.handleSafeBrowsingDisable)
|
||||
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
|
||||
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
|
||||
// --
|
||||
|
||||
|
||||
@@ -48,6 +48,7 @@ func (s *Server) handleDNSRequest(_ *proxy.Proxy, d *proxy.DNSContext) error {
|
||||
processFilteringBeforeRequest,
|
||||
processUpstream,
|
||||
processDNSSECAfterResponse,
|
||||
processRebindingFilteringAfterResponse,
|
||||
processFilteringAfterResponse,
|
||||
s.ipset.process,
|
||||
processQueryLogsAndStats,
|
||||
@@ -366,9 +367,7 @@ func processFilteringAfterResponse(ctx *dnsContext) int {
|
||||
var err error
|
||||
|
||||
switch res.Reason {
|
||||
case dnsfilter.ReasonRewrite,
|
||||
dnsfilter.DNSRewriteRule:
|
||||
|
||||
case dnsfilter.ReasonRewrite:
|
||||
if len(ctx.origQuestion.Name) == 0 {
|
||||
// origQuestion is set in case we get only CNAME without IP from rewrites table
|
||||
break
|
||||
@@ -379,12 +378,12 @@ func processFilteringAfterResponse(ctx *dnsContext) int {
|
||||
|
||||
if len(d.Res.Answer) != 0 {
|
||||
answer := []dns.RR{}
|
||||
answer = append(answer, s.genAnswerCNAME(d.Req, res.CanonName))
|
||||
answer = append(answer, d.Res.Answer...)
|
||||
answer = append(answer, s.genCNAMEAnswer(d.Req, res.CanonName))
|
||||
answer = append(answer, d.Res.Answer...) // host -> IP
|
||||
d.Res.Answer = answer
|
||||
}
|
||||
|
||||
case dnsfilter.NotFilteredAllowList:
|
||||
case dnsfilter.NotFilteredWhiteList:
|
||||
// nothing
|
||||
|
||||
default:
|
||||
|
||||
@@ -48,11 +48,12 @@ var webRegistered bool
|
||||
// The zero Server is empty and ready for use.
|
||||
type Server struct {
|
||||
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)
|
||||
queryLog querylog.QueryLog // Query log instance
|
||||
stats stats.Stats
|
||||
access *accessCtx
|
||||
rebinding *dnsRebindChecker
|
||||
|
||||
ipset ipsetCtx
|
||||
|
||||
@@ -74,7 +75,7 @@ type Server struct {
|
||||
|
||||
// DNSCreateParams - parameters for NewServer()
|
||||
type DNSCreateParams struct {
|
||||
DNSFilter *dnsfilter.DNSFilter
|
||||
DNSFilter *dnsfilter.Dnsfilter
|
||||
Stats stats.Stats
|
||||
QueryLog querylog.QueryLog
|
||||
DHCPServer dhcpd.ServerInterface
|
||||
@@ -122,6 +123,7 @@ func (s *Server) WriteDiskConfig(c *FilteringConfig) {
|
||||
c.DisallowedClients = stringArrayDup(sc.DisallowedClients)
|
||||
c.BlockedHosts = stringArrayDup(sc.BlockedHosts)
|
||||
c.UpstreamDNS = stringArrayDup(sc.UpstreamDNS)
|
||||
c.RebindingAllowedHosts = stringArrayDup(sc.RebindingAllowedHosts)
|
||||
s.RUnlock()
|
||||
}
|
||||
|
||||
@@ -221,6 +223,13 @@ func (s *Server) Prepare(config *ServerConfig) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Initialize DNS rebinding module
|
||||
// --
|
||||
s.rebinding, err = newRebindChecker(s.conf.RebindingAllowedHosts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Register web handlers if necessary
|
||||
// --
|
||||
if !webRegistered && s.conf.HTTPRegister != nil {
|
||||
|
||||
@@ -296,7 +296,7 @@ func TestBlockedRequest(t *testing.T) {
|
||||
|
||||
func TestServerCustomClientUpstream(t *testing.T) {
|
||||
s := createTestServer(t)
|
||||
s.conf.GetCustomUpstreamByClient = func(_ string) *proxy.UpstreamConfig {
|
||||
s.conf.GetCustomUpstreamByClient = func(clientAddr string) *proxy.UpstreamConfig {
|
||||
uc := &proxy.UpstreamConfig{}
|
||||
u := &testUpstream{}
|
||||
u.ipv4 = map[string][]net.IP{}
|
||||
@@ -473,7 +473,7 @@ func TestBlockCNAME(t *testing.T) {
|
||||
func TestClientRulesForCNAMEMatching(t *testing.T) {
|
||||
s := createTestServer(t)
|
||||
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
|
||||
}
|
||||
err := s.startWithUpstream(testUpstm)
|
||||
@@ -738,6 +738,100 @@ func TestRewrite(t *testing.T) {
|
||||
_ = 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 {
|
||||
rules := `||nxdomain.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) {
|
||||
t.Helper()
|
||||
|
||||
req := createTestMessage(host)
|
||||
reply, _, err := client.Exchange(req, addr.String())
|
||||
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) {
|
||||
t.Helper()
|
||||
|
||||
if len(reply.Answer) != 1 {
|
||||
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
|
||||
}
|
||||
|
||||
// filterDNSRequest applies the dnsFilter and sets d.Res if the request
|
||||
// was filtered.
|
||||
// filterDNSRequest applies the dnsFilter and sets d.Res if the request was filtered
|
||||
func (s *Server) filterDNSRequest(ctx *dnsContext) (*dnsfilter.Result, error) {
|
||||
d := ctx.proxyCtx
|
||||
req := d.Req
|
||||
@@ -53,17 +52,13 @@ func (s *Server) filterDNSRequest(ctx *dnsContext) (*dnsfilter.Result, error) {
|
||||
// Return immediately if there's an error
|
||||
return nil, fmt.Errorf("dnsfilter failed to check host %q: %w", host, err)
|
||||
} 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)
|
||||
} else if res.Reason.In(dnsfilter.ReasonRewrite, dnsfilter.DNSRewriteRule) &&
|
||||
res.CanonName != "" &&
|
||||
len(res.IPList) == 0 {
|
||||
// Resolve the new canonical name, not the original host
|
||||
// name. The original question is readded in
|
||||
// processFilteringAfterResponse.
|
||||
} else if res.Reason == dnsfilter.ReasonRewrite && len(res.CanonName) != 0 && len(res.IPList) == 0 {
|
||||
ctx.origQuestion = d.Req.Question[0]
|
||||
// resolve canonical name, not the original host name
|
||||
d.Req.Question[0].Name = dns.Fqdn(res.CanonName)
|
||||
} else if res.Reason == dnsfilter.RewriteAutoHosts && len(res.ReverseHosts) != 0 {
|
||||
} else if res.Reason == dnsfilter.RewriteEtcHosts && len(res.ReverseHosts) != 0 {
|
||||
resp := s.makeResponse(req)
|
||||
for _, h := range res.ReverseHosts {
|
||||
hdr := dns.RR_Header{
|
||||
@@ -82,33 +77,28 @@ func (s *Server) filterDNSRequest(ctx *dnsContext) (*dnsfilter.Result, error) {
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
name := host
|
||||
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
|
||||
}
|
||||
|
||||
for _, ip := range res.IPList {
|
||||
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)
|
||||
resp.Answer = append(resp.Answer, a)
|
||||
} else if req.Question[0].Qtype == dns.TypeAAAA {
|
||||
a := s.genAnswerAAAA(req, ip)
|
||||
a := s.genAAAAAnswer(req, ip)
|
||||
a.Hdr.Name = dns.Fqdn(name)
|
||||
resp.Answer = append(resp.Answer, a)
|
||||
}
|
||||
}
|
||||
|
||||
d.Res = resp
|
||||
} else if res.Reason == dnsfilter.DNSRewriteRule {
|
||||
err = s.filterDNSRewrite(req, res, d)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &res, err
|
||||
@@ -123,8 +113,8 @@ func (s *Server) filterDNSResponse(ctx *dnsContext) (*dnsfilter.Result, error) {
|
||||
|
||||
switch v := a.(type) {
|
||||
case *dns.CNAME:
|
||||
log.Debug("DNSFwd: Checking CNAME %s for %s", v.Target, v.Hdr.Name)
|
||||
host = strings.TrimSuffix(v.Target, ".")
|
||||
log.Debug("DNSFwd: Checking CNAME %s for %s", v.Target, v.Hdr.Name)
|
||||
|
||||
case *dns.A:
|
||||
host = v.A.String()
|
||||
|
||||
@@ -37,6 +37,9 @@ type dnsConfig struct {
|
||||
CacheSize *uint32 `json:"cache_size"`
|
||||
CacheMinTTL *uint32 `json:"cache_ttl_min"`
|
||||
CacheMaxTTL *uint32 `json:"cache_ttl_max"`
|
||||
|
||||
RebindingProtectionEnabled *bool `json:"rebinding_protection_enabled"`
|
||||
RebindingAllowedHosts *[]string `json:"rebinding_allowed_hosts"`
|
||||
}
|
||||
|
||||
func (s *Server) getDNSConfig() dnsConfig {
|
||||
@@ -61,23 +64,27 @@ func (s *Server) getDNSConfig() dnsConfig {
|
||||
} else if s.conf.AllServers {
|
||||
upstreamMode = "parallel"
|
||||
}
|
||||
rebindingEnabled := s.conf.RebindingProtectionEnabled
|
||||
rebindingAllowedHosts := stringArrayDup(s.conf.RebindingAllowedHosts)
|
||||
s.RUnlock()
|
||||
return dnsConfig{
|
||||
Upstreams: &upstreams,
|
||||
UpstreamsFile: &upstreamFile,
|
||||
Bootstraps: &bootstraps,
|
||||
ProtectionEnabled: &protectionEnabled,
|
||||
BlockingMode: &blockingMode,
|
||||
BlockingIPv4: &BlockingIPv4,
|
||||
BlockingIPv6: &BlockingIPv6,
|
||||
RateLimit: &Ratelimit,
|
||||
EDNSCSEnabled: &EnableEDNSClientSubnet,
|
||||
DNSSECEnabled: &EnableDNSSEC,
|
||||
DisableIPv6: &AAAADisabled,
|
||||
CacheSize: &CacheSize,
|
||||
CacheMinTTL: &CacheMinTTL,
|
||||
CacheMaxTTL: &CacheMaxTTL,
|
||||
UpstreamMode: &upstreamMode,
|
||||
Upstreams: &upstreams,
|
||||
UpstreamsFile: &upstreamFile,
|
||||
Bootstraps: &bootstraps,
|
||||
ProtectionEnabled: &protectionEnabled,
|
||||
BlockingMode: &blockingMode,
|
||||
BlockingIPv4: &BlockingIPv4,
|
||||
BlockingIPv6: &BlockingIPv6,
|
||||
RateLimit: &Ratelimit,
|
||||
EDNSCSEnabled: &EnableEDNSClientSubnet,
|
||||
DNSSECEnabled: &EnableDNSSEC,
|
||||
DisableIPv6: &AAAADisabled,
|
||||
CacheSize: &CacheSize,
|
||||
CacheMinTTL: &CacheMinTTL,
|
||||
CacheMaxTTL: &CacheMaxTTL,
|
||||
UpstreamMode: &upstreamMode,
|
||||
RebindingProtectionEnabled: &rebindingEnabled,
|
||||
RebindingAllowedHosts: &rebindingAllowedHosts,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,12 +174,11 @@ func (req *dnsConfig) checkCacheTTL() bool {
|
||||
if req.CacheMinTTL == nil && req.CacheMaxTTL == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
var min, max uint32
|
||||
if req.CacheMinTTL != nil {
|
||||
min = *req.CacheMinTTL
|
||||
}
|
||||
if req.CacheMaxTTL != nil {
|
||||
if req.CacheMaxTTL == nil {
|
||||
max = *req.CacheMaxTTL
|
||||
}
|
||||
|
||||
@@ -302,6 +308,8 @@ func (s *Server) setConfig(dc dnsConfig) (restart bool) {
|
||||
s.conf.FastestAddr = false
|
||||
}
|
||||
}
|
||||
|
||||
restart = restart || s.setRebindingConfig(dc)
|
||||
s.Unlock()
|
||||
s.conf.ConfigModified()
|
||||
return restart
|
||||
|
||||
@@ -29,7 +29,7 @@ func TestDNSForwardHTTTP_handleGetConfig(t *testing.T) {
|
||||
conf: func() ServerConfig {
|
||||
return defaultConf
|
||||
},
|
||||
want: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0}\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",
|
||||
conf: func() ServerConfig {
|
||||
@@ -37,7 +37,7 @@ func TestDNSForwardHTTTP_handleGetConfig(t *testing.T) {
|
||||
conf.FastestAddr = true
|
||||
return conf
|
||||
},
|
||||
want: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"fastest_addr\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0}\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",
|
||||
conf: func() ServerConfig {
|
||||
@@ -45,7 +45,7 @@ func TestDNSForwardHTTTP_handleGetConfig(t *testing.T) {
|
||||
conf.AllServers = true
|
||||
return conf
|
||||
},
|
||||
want: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"parallel\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0}\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 {
|
||||
@@ -73,7 +73,7 @@ func TestDNSForwardHTTTP_handleSetConfig(t *testing.T) {
|
||||
|
||||
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 {
|
||||
name string
|
||||
req string
|
||||
@@ -83,52 +83,52 @@ func TestDNSForwardHTTTP_handleSetConfig(t *testing.T) {
|
||||
name: "upstream_dns",
|
||||
req: "{\"upstream_dns\":[\"8.8.8.8:77\",\"8.8.4.4:77\"]}",
|
||||
wantSet: "",
|
||||
wantGet: "{\"upstream_dns\":[\"8.8.8.8:77\",\"8.8.4.4:77\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0}\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",
|
||||
req: "{\"bootstrap_dns\":[\"9.9.9.10\"]}",
|
||||
wantSet: "",
|
||||
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0}\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",
|
||||
req: "{\"blocking_mode\":\"refused\"}",
|
||||
wantSet: "",
|
||||
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"refused\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0}\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",
|
||||
req: "{\"blocking_mode\":\"custom_ip\"}",
|
||||
wantSet: "blocking_mode: incorrect value\n",
|
||||
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0}\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",
|
||||
req: "{\"ratelimit\":6}",
|
||||
wantSet: "",
|
||||
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":6,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0}\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",
|
||||
req: "{\"edns_cs_enabled\":true}",
|
||||
wantSet: "",
|
||||
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":true,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0}\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",
|
||||
req: "{\"dnssec_enabled\":true}",
|
||||
wantSet: "",
|
||||
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":true,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0}\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",
|
||||
req: "{\"cache_size\":1024}",
|
||||
wantSet: "",
|
||||
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":1024,\"cache_ttl_min\":0,\"cache_ttl_max\":0}\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",
|
||||
req: "{\"upstream_mode\":\"parallel\"}",
|
||||
wantSet: "",
|
||||
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"parallel\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0}\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",
|
||||
req: "{\"upstream_mode\":\"fastest_addr\"}",
|
||||
wantSet: "",
|
||||
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"fastest_addr\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0}\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",
|
||||
req: "{\"upstream_dns\":[\"\"]}",
|
||||
|
||||
@@ -7,22 +7,16 @@ import (
|
||||
"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"
|
||||
)
|
||||
|
||||
// Create a DNS response by DNS request and set necessary flags
|
||||
func (s *Server) makeResponse(req *dns.Msg) (resp *dns.Msg) {
|
||||
resp = &dns.Msg{
|
||||
MsgHdr: dns.MsgHdr{
|
||||
RecursionAvailable: true,
|
||||
},
|
||||
Compress: true,
|
||||
}
|
||||
|
||||
func (s *Server) makeResponse(req *dns.Msg) *dns.Msg {
|
||||
resp := dns.Msg{}
|
||||
resp.SetReply(req)
|
||||
|
||||
return resp
|
||||
resp.RecursionAvailable = true
|
||||
resp.Compress = true
|
||||
return &resp
|
||||
}
|
||||
|
||||
// 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
|
||||
// the IP address that must be used in response.
|
||||
// In this case regardless of the filtering method, we should return it
|
||||
if result.Reason == dnsfilter.FilteredSafeSearch &&
|
||||
len(result.Rules) > 0 &&
|
||||
result.Rules[0].IP != nil {
|
||||
return s.genResponseWithIP(m, result.Rules[0].IP)
|
||||
if result.Reason == dnsfilter.FilteredSafeSearch && result.IP != nil {
|
||||
return s.genResponseWithIP(m, result.IP)
|
||||
}
|
||||
|
||||
if s.conf.BlockingMode == "null_ip" {
|
||||
@@ -76,8 +68,8 @@ func (s *Server) genDNSFilterMessage(d *proxy.DNSContext, result *dnsfilter.Resu
|
||||
// Default blocking mode
|
||||
// If there's an IP specified in the rule, return it
|
||||
// For host-type rules, return null IP
|
||||
if len(result.Rules) > 0 && result.Rules[0].IP != nil {
|
||||
return s.genResponseWithIP(m, result.Rules[0].IP)
|
||||
if result.IP != nil {
|
||||
return s.genResponseWithIP(m, result.IP)
|
||||
}
|
||||
|
||||
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 {
|
||||
resp := s.makeResponse(request)
|
||||
resp.Answer = append(resp.Answer, s.genAnswerA(request, ip))
|
||||
resp.Answer = append(resp.Answer, s.genAAnswer(request, ip))
|
||||
return resp
|
||||
}
|
||||
|
||||
func (s *Server) genAAAARecord(request *dns.Msg, ip net.IP) *dns.Msg {
|
||||
resp := s.makeResponse(request)
|
||||
resp.Answer = append(resp.Answer, s.genAnswerAAAA(request, ip))
|
||||
resp.Answer = append(resp.Answer, s.genAAAAAnswer(request, ip))
|
||||
return resp
|
||||
}
|
||||
|
||||
func (s *Server) hdr(req *dns.Msg, rrType rules.RRType) (h dns.RR_Header) {
|
||||
return dns.RR_Header{
|
||||
func (s *Server) genAAnswer(req *dns.Msg, ip net.IP) *dns.A {
|
||||
answer := new(dns.A)
|
||||
answer.Hdr = dns.RR_Header{
|
||||
Name: req.Question[0].Name,
|
||||
Rrtype: rrType,
|
||||
Rrtype: dns.TypeA,
|
||||
Ttl: s.conf.BlockedResponseTTL,
|
||||
Class: dns.ClassINET,
|
||||
}
|
||||
answer.A = ip
|
||||
return answer
|
||||
}
|
||||
|
||||
func (s *Server) genAnswerA(req *dns.Msg, ip net.IP) (ans *dns.A) {
|
||||
return &dns.A{
|
||||
Hdr: s.hdr(req, dns.TypeA),
|
||||
A: ip,
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
func (s *Server) genAAAAAnswer(req *dns.Msg, ip net.IP) *dns.AAAA {
|
||||
answer := new(dns.AAAA)
|
||||
answer.Hdr = dns.RR_Header{
|
||||
Name: req.Question[0].Name,
|
||||
Rrtype: dns.TypeAAAA,
|
||||
Ttl: s.conf.BlockedResponseTTL,
|
||||
Class: dns.ClassINET,
|
||||
}
|
||||
answer.AAAA = ip
|
||||
return answer
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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
|
||||
func (s *Server) makeResponseREFUSED(request *dns.Msg) *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:
|
||||
e.Result = stats.RSafeSearch
|
||||
|
||||
case dnsfilter.FilteredBlockList:
|
||||
case dnsfilter.FilteredBlackList:
|
||||
fallthrough
|
||||
case dnsfilter.FilteredInvalid:
|
||||
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
|
||||
type Auth struct {
|
||||
db *bbolt.DB
|
||||
sessions map[string]*session
|
||||
users []User
|
||||
sessions map[string]*session // session name -> session data
|
||||
lock sync.Mutex
|
||||
sessionTTL uint32
|
||||
users []User
|
||||
sessionTTL uint32 // in seconds
|
||||
}
|
||||
|
||||
// User object
|
||||
@@ -223,35 +223,24 @@ func (a *Auth) removeSession(sess []byte) {
|
||||
log.Debug("Auth: removed session from DB")
|
||||
}
|
||||
|
||||
// checkSessionResult is the result of checking a session.
|
||||
type checkSessionResult 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) {
|
||||
// CheckSession - check if session is valid
|
||||
// Return 0 if OK; -1 if session doesn't exist; 1 if session has expired
|
||||
func (a *Auth) CheckSession(sess string) int {
|
||||
now := uint32(time.Now().UTC().Unix())
|
||||
update := false
|
||||
|
||||
a.lock.Lock()
|
||||
defer a.lock.Unlock()
|
||||
|
||||
s, ok := a.sessions[sess]
|
||||
if !ok {
|
||||
return checkSessionNotFound
|
||||
a.lock.Unlock()
|
||||
return -1
|
||||
}
|
||||
|
||||
if s.expire <= now {
|
||||
delete(a.sessions, sess)
|
||||
key, _ := hex.DecodeString(sess)
|
||||
a.removeSession(key)
|
||||
|
||||
return checkSessionExpired
|
||||
a.lock.Unlock()
|
||||
return 1
|
||||
}
|
||||
|
||||
newExpire := now + a.sessionTTL
|
||||
@@ -261,6 +250,8 @@ func (a *Auth) checkSession(sess string) (res checkSessionResult) {
|
||||
s.expire = newExpire
|
||||
}
|
||||
|
||||
a.lock.Unlock()
|
||||
|
||||
if update {
|
||||
key, _ := hex.DecodeString(sess)
|
||||
if a.storeSession(key, s) {
|
||||
@@ -268,7 +259,7 @@ func (a *Auth) checkSession(sess string) (res checkSessionResult) {
|
||||
}
|
||||
}
|
||||
|
||||
return checkSessionOK
|
||||
return 0
|
||||
}
|
||||
|
||||
// RemoveSession - remove session
|
||||
@@ -401,8 +392,8 @@ func optionalAuthThird(w http.ResponseWriter, r *http.Request) (authFirst bool)
|
||||
ok = true
|
||||
|
||||
} else if err == nil {
|
||||
r := Context.auth.checkSession(cookie.Value)
|
||||
if r == checkSessionOK {
|
||||
r := Context.auth.CheckSession(cookie.Value)
|
||||
if r == 0 {
|
||||
ok = true
|
||||
} else if r < 0 {
|
||||
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()
|
||||
cookie, err := r.Cookie(sessionCookieName)
|
||||
if authRequired && err == nil {
|
||||
r := Context.auth.checkSession(cookie.Value)
|
||||
if r == checkSessionOK {
|
||||
r := Context.auth.CheckSession(cookie.Value)
|
||||
if r == 0 {
|
||||
w.Header().Set("Location", "/")
|
||||
w.WriteHeader(http.StatusFound)
|
||||
|
||||
return
|
||||
} else if r == checkSessionNotFound {
|
||||
} else if r < 0 {
|
||||
log.Debug("Auth: invalid cookie value: %s", cookie)
|
||||
}
|
||||
}
|
||||
@@ -513,34 +503,32 @@ func (a *Auth) UserFind(login, password string) User {
|
||||
return User{}
|
||||
}
|
||||
|
||||
// getCurrentUser returns the current user. It returns an empty User if the
|
||||
// user is not found.
|
||||
func (a *Auth) getCurrentUser(r *http.Request) User {
|
||||
// GetCurrentUser - get the current user
|
||||
func (a *Auth) GetCurrentUser(r *http.Request) User {
|
||||
cookie, err := r.Cookie(sessionCookieName)
|
||||
if err != nil {
|
||||
// There's no Cookie, check Basic authentication.
|
||||
// there's no Cookie, check Basic authentication
|
||||
user, pass, ok := r.BasicAuth()
|
||||
if ok {
|
||||
return Context.auth.UserFind(user, pass)
|
||||
u := Context.auth.UserFind(user, pass)
|
||||
return u
|
||||
}
|
||||
|
||||
return User{}
|
||||
}
|
||||
|
||||
a.lock.Lock()
|
||||
defer a.lock.Unlock()
|
||||
|
||||
s, ok := a.sessions[cookie.Value]
|
||||
if !ok {
|
||||
a.lock.Unlock()
|
||||
return User{}
|
||||
}
|
||||
|
||||
for _, u := range a.users {
|
||||
if u.Name == s.userName {
|
||||
a.lock.Unlock()
|
||||
return u
|
||||
}
|
||||
}
|
||||
|
||||
a.lock.Unlock()
|
||||
return User{}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ func TestAuth(t *testing.T) {
|
||||
user := User{Name: "name"}
|
||||
a.UserAdd(&user, "password")
|
||||
|
||||
assert.Equal(t, checkSessionNotFound, a.checkSession("notfound"))
|
||||
assert.True(t, a.CheckSession("notfound") == -1)
|
||||
a.RemoveSession("notfound")
|
||||
|
||||
sess, err := getSession(&users[0])
|
||||
@@ -49,13 +49,13 @@ func TestAuth(t *testing.T) {
|
||||
// check expiration
|
||||
s.expire = uint32(now)
|
||||
a.addSession(sess, &s)
|
||||
assert.Equal(t, checkSessionExpired, a.checkSession(sessStr))
|
||||
assert.True(t, a.CheckSession(sessStr) == 1)
|
||||
|
||||
// add session with TTL = 2 sec
|
||||
s = session{}
|
||||
s.expire = uint32(time.Now().UTC().Unix() + 2)
|
||||
a.addSession(sess, &s)
|
||||
assert.Equal(t, checkSessionOK, a.checkSession(sessStr))
|
||||
assert.True(t, a.CheckSession(sessStr) == 0)
|
||||
|
||||
a.Close()
|
||||
|
||||
@@ -63,8 +63,8 @@ func TestAuth(t *testing.T) {
|
||||
a = InitAuth(fn, users, 60)
|
||||
|
||||
// the session is still alive
|
||||
assert.Equal(t, checkSessionOK, a.checkSession(sessStr))
|
||||
// reset our expiration time because checkSession() has just updated it
|
||||
assert.True(t, a.CheckSession(sessStr) == 0)
|
||||
// reset our expiration time because CheckSession() has just updated it
|
||||
s.expire = uint32(time.Now().UTC().Unix() + 2)
|
||||
a.storeSession(sess, &s)
|
||||
a.Close()
|
||||
@@ -76,7 +76,7 @@ func TestAuth(t *testing.T) {
|
||||
|
||||
// load and remove expired sessions
|
||||
a = InitAuth(fn, users, 60)
|
||||
assert.Equal(t, checkSessionNotFound, a.checkSession(sessStr))
|
||||
assert.True(t, a.CheckSession(sessStr) == -1)
|
||||
|
||||
a.Close()
|
||||
os.Remove(fn)
|
||||
@@ -111,7 +111,7 @@ func TestAuthHTTP(t *testing.T) {
|
||||
Context.auth = InitAuth(fn, users, 60)
|
||||
|
||||
handlerCalled := false
|
||||
handler := func(_ http.ResponseWriter, _ *http.Request) {
|
||||
handler := func(w http.ResponseWriter, r *http.Request) {
|
||||
handlerCalled = true
|
||||
}
|
||||
handler2 := optionalAuth(handler)
|
||||
|
||||
@@ -89,7 +89,7 @@ type profileJSON struct {
|
||||
|
||||
func handleGetProfile(w http.ResponseWriter, r *http.Request) {
|
||||
pj := profileJSON{}
|
||||
u := Context.auth.getCurrentUser(r)
|
||||
u := Context.auth.GetCurrentUser(r)
|
||||
pj.Name = u.Name
|
||||
|
||||
data, err := json.Marshal(pj)
|
||||
|
||||
@@ -346,25 +346,10 @@ func (f *Filtering) handleFilteringConfig(w http.ResponseWriter, r *http.Request
|
||||
enableFilters(true)
|
||||
}
|
||||
|
||||
type checkHostRespRule struct {
|
||||
FilterListID int64 `json:"filter_list_id"`
|
||||
Text string `json:"text"`
|
||||
}
|
||||
|
||||
type checkHostResp struct {
|
||||
Reason string `json:"reason"`
|
||||
|
||||
// FilterID is the ID of the rule's filter list.
|
||||
//
|
||||
// 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"`
|
||||
Reason string `json:"reason"`
|
||||
FilterID int64 `json:"filter_id"`
|
||||
Rule string `json:"rule"`
|
||||
|
||||
// for FilteredBlockedService:
|
||||
SvcName string `json:"service_name"`
|
||||
@@ -389,23 +374,11 @@ func (f *Filtering) handleCheckHost(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
resp := checkHostResp{}
|
||||
resp.Reason = result.Reason.String()
|
||||
resp.FilterID = result.FilterID
|
||||
resp.Rule = result.Rule
|
||||
resp.SvcName = result.ServiceName
|
||||
resp.CanonName = result.CanonName
|
||||
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)
|
||||
if err != nil {
|
||||
httpError(w, http.StatusInternalServerError, "json encode: %s", err)
|
||||
|
||||
@@ -58,7 +58,7 @@ type homeContext struct {
|
||||
dnsServer *dnsforward.Server // DNS module
|
||||
rdns *RDNS // rDNS module
|
||||
whois *Whois // WHOIS module
|
||||
dnsFilter *dnsfilter.DNSFilter // DNS filtering module
|
||||
dnsFilter *dnsfilter.Dnsfilter // DNS filtering module
|
||||
dhcpServer *dhcpd.Server // DHCP module
|
||||
auth *Auth // HTTP authentication module
|
||||
filters Filtering // DNS filtering module
|
||||
|
||||
@@ -4,13 +4,11 @@ import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/dnsfilter"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/AdguardTeam/urlfilter/rules"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
@@ -87,6 +85,54 @@ var logEntryHandlers = map[string]logEntryHandler{
|
||||
ent.OrigAnswer, err = base64.StdEncoding.DecodeString(v)
|
||||
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 {
|
||||
v, ok := t.(string)
|
||||
if !ok {
|
||||
@@ -107,411 +153,42 @@ var logEntryHandlers = map[string]logEntryHandler{
|
||||
ent.Elapsed = time.Duration(i)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
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
|
||||
"Result": func(json.Token, *logEntry) error {
|
||||
return nil
|
||||
},
|
||||
"Rule": func(t json.Token, ent *logEntry) error {
|
||||
s, ok := t.(string)
|
||||
"Question": func(t json.Token, ent *logEntry) error {
|
||||
v, ok := t.(string)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
l := len(ent.Result.Rules)
|
||||
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()
|
||||
var qstr []byte
|
||||
qstr, err := base64.StdEncoding.DecodeString(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
l := len(ent.Result.Rules)
|
||||
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()
|
||||
q := new(dns.Msg)
|
||||
err = q.Unpack(qstr)
|
||||
if err != nil {
|
||||
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
|
||||
},
|
||||
"ServiceName": func(t json.Token, ent *logEntry) error {
|
||||
s, ok := t.(string)
|
||||
"Time": func(t json.Token, ent *logEntry) error {
|
||||
v, ok := t.(string)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
ent.Result.ServiceName = s
|
||||
|
||||
return nil
|
||||
var err error
|
||||
ent.Time, err = time.Parse(time.RFC3339, v)
|
||||
return err
|
||||
},
|
||||
"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) {
|
||||
@@ -523,27 +200,18 @@ func decodeLogEntry(ent *logEntry, str string) {
|
||||
if err != io.EOF {
|
||||
log.Debug("decodeLogEntry err: %s", err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if _, ok := keyToken.(json.Delim); ok {
|
||||
continue
|
||||
}
|
||||
|
||||
key, ok := keyToken.(string)
|
||||
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
|
||||
}
|
||||
|
||||
if key == "Result" {
|
||||
decodeResult(dec, ent)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
handler, ok := logEntryHandlers[key]
|
||||
if !ok {
|
||||
continue
|
||||
@@ -555,8 +223,7 @@ func decodeLogEntry(ent *logEntry, str string) {
|
||||
}
|
||||
|
||||
if err = handler(val, ent); err != nil {
|
||||
log.Debug("decodeLogEntry handler err: %s", err)
|
||||
|
||||
log.Debug("decodeLogEntry err: %s", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,181 +2,107 @@ package querylog
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"net"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/dnsfilter"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/testutil"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/AdguardTeam/urlfilter/rules"
|
||||
"github.com/miekg/dns"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestDecodeLogEntry(t *testing.T) {
|
||||
func TestDecode_decodeQueryLog(t *testing.T) {
|
||||
logOutput := &bytes.Buffer{}
|
||||
|
||||
testutil.ReplaceLogWriter(t, logOutput)
|
||||
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 {
|
||||
name string
|
||||
log string
|
||||
want string
|
||||
}{{
|
||||
name: "all_right_old_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":"||an.yandex.","FilterID":1,"ReverseHosts":["example.com"],"IPList":["127.0.0.1"]},"Elapsed":837429}`,
|
||||
want: "",
|
||||
name: "back_compatibility_all_right",
|
||||
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: "default",
|
||||
}, {
|
||||
name: "bad_filter_id_old_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,"FilterID":1.5},"Elapsed":837429}`,
|
||||
want: "decodeResult handler err: strconv.ParseInt: parsing \"1.5\": invalid syntax\n",
|
||||
name: "back_compatibility_bad_msg",
|
||||
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: "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",
|
||||
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}`,
|
||||
want: "decodeLogEntry err: invalid character 'o' in literal true (expecting 'u')\n",
|
||||
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: "default",
|
||||
}, {
|
||||
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}`,
|
||||
want: "",
|
||||
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: "default",
|
||||
}, {
|
||||
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}`,
|
||||
want: "",
|
||||
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: "default",
|
||||
}, {
|
||||
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}`,
|
||||
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",
|
||||
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 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",
|
||||
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}`,
|
||||
want: "",
|
||||
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: "default",
|
||||
}, {
|
||||
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}`,
|
||||
want: "",
|
||||
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: "default",
|
||||
}, {
|
||||
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}`,
|
||||
want: "",
|
||||
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: "default",
|
||||
}, {
|
||||
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}`,
|
||||
want: "",
|
||||
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: "default",
|
||||
}, {
|
||||
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}`,
|
||||
want: "decodeLogEntry handler err: invalid client proto: \"dog\"\n",
|
||||
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 err: invalid client proto: \"dog\"\n",
|
||||
}, {
|
||||
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}`,
|
||||
want: "",
|
||||
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: "default",
|
||||
}, {
|
||||
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}`,
|
||||
want: "decodeLogEntry handler err: illegal base64 data at input byte 61\n",
|
||||
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 err: illegal base64 data at input byte 61\n",
|
||||
}, {
|
||||
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}`,
|
||||
want: "",
|
||||
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: "default",
|
||||
}, {
|
||||
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}`,
|
||||
want: "",
|
||||
}, {
|
||||
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",
|
||||
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: "default",
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
_, err := logOutput.Write([]byte("default"))
|
||||
assert.Nil(t, err)
|
||||
|
||||
l := &logEntry{}
|
||||
decodeLogEntry(l, tc.log)
|
||||
|
||||
s := logOutput.String()
|
||||
if tc.want == "" {
|
||||
assert.Equal(t, "", s)
|
||||
} else {
|
||||
assert.True(t, strings.HasSuffix(s, tc.want),
|
||||
"got %q", s)
|
||||
}
|
||||
assert.True(t, strings.HasSuffix(logOutput.String(), tc.want), logOutput.String())
|
||||
|
||||
logOutput.Reset()
|
||||
})
|
||||
|
||||
@@ -6,13 +6,10 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/dnsfilter"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
// TODO(a.garipov): Use a proper structured approach here.
|
||||
|
||||
// Get Client IP address
|
||||
func (l *queryLog) getClientIP(clientIP string) string {
|
||||
if l.conf.AnonymizeClientIP {
|
||||
@@ -32,12 +29,10 @@ func (l *queryLog) getClientIP(clientIP string) string {
|
||||
return clientIP
|
||||
}
|
||||
|
||||
// jobject is a JSON object alias.
|
||||
type jobject = map[string]interface{}
|
||||
|
||||
// entriesToJSON converts query log entries to JSON.
|
||||
func (l *queryLog) entriesToJSON(entries []*logEntry, oldest time.Time) (res jobject) {
|
||||
data := []jobject{}
|
||||
// entriesToJSON - converts log entries to JSON
|
||||
func (l *queryLog) entriesToJSON(entries []*logEntry, oldest time.Time) map[string]interface{} {
|
||||
// init the response object
|
||||
data := []map[string]interface{}{}
|
||||
|
||||
// the elements order is already reversed (from newer to older)
|
||||
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)
|
||||
}
|
||||
|
||||
res = jobject{
|
||||
"data": data,
|
||||
"oldest": "",
|
||||
}
|
||||
result := map[string]interface{}{}
|
||||
result["oldest"] = ""
|
||||
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
|
||||
|
||||
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(),
|
||||
"elapsedMs": strconv.FormatFloat(entry.Elapsed.Seconds()*1000, 'f', -1, 64),
|
||||
"time": entry.Time.Format(time.RFC3339Nano),
|
||||
"client": l.getClientIP(entry.IP),
|
||||
"client_proto": entry.ClientProto,
|
||||
"upstream": entry.Upstream,
|
||||
"question": jobject{
|
||||
"host": entry.QHost,
|
||||
"type": entry.QType,
|
||||
"class": entry.QClass,
|
||||
},
|
||||
}
|
||||
jsonEntry["question"] = map[string]interface{}{
|
||||
"host": entry.QHost,
|
||||
"type": entry.QType,
|
||||
"class": entry.QClass,
|
||||
}
|
||||
|
||||
if msg != nil {
|
||||
@@ -90,15 +83,12 @@ func (l *queryLog) logEntryToJSONEntry(entry *logEntry) (jsonEntry jobject) {
|
||||
if opt != nil {
|
||||
dnssecOk = opt.Do()
|
||||
}
|
||||
|
||||
jsonEntry["answer_dnssec"] = dnssecOk
|
||||
}
|
||||
|
||||
jsonEntry["rules"] = resultRulesToJSONRules(entry.Result.Rules)
|
||||
|
||||
if len(entry.Result.Rules) > 0 && len(entry.Result.Rules[0].Text) > 0 {
|
||||
jsonEntry["rule"] = entry.Result.Rules[0].Text
|
||||
jsonEntry["filterId"] = entry.Result.Rules[0].FilterListID
|
||||
if len(entry.Result.Rule) > 0 {
|
||||
jsonEntry["rule"] = entry.Result.Rule
|
||||
jsonEntry["filterId"] = entry.Result.FilterID
|
||||
}
|
||||
|
||||
if len(entry.Result.ServiceName) != 0 {
|
||||
@@ -123,30 +113,20 @@ func (l *queryLog) logEntryToJSONEntry(entry *logEntry) (jsonEntry jobject) {
|
||||
}
|
||||
}
|
||||
|
||||
jsonEntry["upstream"] = entry.Upstream
|
||||
|
||||
return jsonEntry
|
||||
}
|
||||
|
||||
func resultRulesToJSONRules(rules []*dnsfilter.ResultRule) (jsonRules []jobject) {
|
||||
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) {
|
||||
func answerToMap(a *dns.Msg) []map[string]interface{} {
|
||||
if a == nil || len(a.Answer) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
answers = []jobject{}
|
||||
answers := []map[string]interface{}{}
|
||||
for _, k := range a.Answer {
|
||||
header := k.Header()
|
||||
answer := jobject{
|
||||
answer := map[string]interface{}{
|
||||
"type": dns.TypeToString[header.Rrtype],
|
||||
"ttl": header.Ttl,
|
||||
}
|
||||
|
||||
@@ -236,12 +236,10 @@ func addEntry(l *queryLog, host, answerStr, client string) {
|
||||
a.Answer = append(a.Answer, answer)
|
||||
res := dnsfilter.Result{
|
||||
IsFiltered: true,
|
||||
Rule: "SomeRule",
|
||||
Reason: dnsfilter.ReasonRewrite,
|
||||
ServiceName: "SomeService",
|
||||
Rules: []*dnsfilter.ResultRule{{
|
||||
FilterListID: 1,
|
||||
Text: "SomeRule",
|
||||
}},
|
||||
FilterID: 1,
|
||||
}
|
||||
params := AddParams{
|
||||
Question: &q,
|
||||
|
||||
@@ -17,14 +17,15 @@ const (
|
||||
filteringStatusAll = "all"
|
||||
filteringStatusFiltered = "filtered" // all kinds of filtering
|
||||
|
||||
filteringStatusBlocked = "blocked" // blocked or blocked services
|
||||
filteringStatusBlockedService = "blocked_services" // blocked
|
||||
filteringStatusBlockedSafebrowsing = "blocked_safebrowsing" // blocked by safebrowsing
|
||||
filteringStatusBlockedParental = "blocked_parental" // blocked by parental control
|
||||
filteringStatusWhitelisted = "whitelisted" // whitelisted
|
||||
filteringStatusRewritten = "rewritten" // all kinds of rewrites
|
||||
filteringStatusSafeSearch = "safe_search" // enforced safe search
|
||||
filteringStatusProcessed = "processed" // not blocked, not white-listed entries
|
||||
filteringStatusBlocked = "blocked" // blocked or blocked services
|
||||
filteringStatusBlockedService = "blocked_services" // blocked
|
||||
filteringStatusBlockedSafebrowsing = "blocked_safebrowsing" // blocked by safebrowsing
|
||||
filteringStatusBlockedParental = "blocked_parental" // blocked by parental control
|
||||
filteringStatusBlockedRebind = "blocked_dns_rebinding" // blocked by DNS rebinding protection
|
||||
filteringStatusWhitelisted = "whitelisted" // whitelisted
|
||||
filteringStatusRewritten = "rewritten" // all kinds of rewrites
|
||||
filteringStatusSafeSearch = "safe_search" // enforced safe search
|
||||
filteringStatusProcessed = "processed" // not blocked, not white-listed entries
|
||||
)
|
||||
|
||||
// filteringStatusValues -- array with all possible filteringStatus values
|
||||
@@ -32,7 +33,7 @@ var filteringStatusValues = []string{
|
||||
filteringStatusAll, filteringStatusFiltered, filteringStatusBlocked,
|
||||
filteringStatusBlockedService, filteringStatusBlockedSafebrowsing, filteringStatusBlockedParental,
|
||||
filteringStatusWhitelisted, filteringStatusRewritten, filteringStatusSafeSearch,
|
||||
filteringStatusProcessed,
|
||||
filteringStatusProcessed, filteringStatusBlockedRebind,
|
||||
}
|
||||
|
||||
// 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:
|
||||
return res.IsFiltered ||
|
||||
res.Reason.In(
|
||||
dnsfilter.NotFilteredAllowList,
|
||||
dnsfilter.NotFilteredWhiteList,
|
||||
dnsfilter.ReasonRewrite,
|
||||
dnsfilter.RewriteAutoHosts,
|
||||
dnsfilter.RewriteEtcHosts,
|
||||
)
|
||||
|
||||
case filteringStatusBlocked:
|
||||
return res.IsFiltered &&
|
||||
res.Reason.In(dnsfilter.FilteredBlockList, dnsfilter.FilteredBlockedService)
|
||||
res.Reason.In(dnsfilter.FilteredBlackList, dnsfilter.FilteredBlockedService)
|
||||
|
||||
case filteringStatusBlockedService:
|
||||
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
|
||||
|
||||
case filteringStatusWhitelisted:
|
||||
return res.Reason == dnsfilter.NotFilteredAllowList
|
||||
return res.Reason == dnsfilter.NotFilteredWhiteList
|
||||
|
||||
case filteringStatusRewritten:
|
||||
return res.Reason.In(dnsfilter.ReasonRewrite, dnsfilter.RewriteAutoHosts)
|
||||
return res.Reason.In(dnsfilter.ReasonRewrite, dnsfilter.RewriteEtcHosts)
|
||||
|
||||
case filteringStatusSafeSearch:
|
||||
return res.IsFiltered && res.Reason == dnsfilter.FilteredSafeSearch
|
||||
|
||||
case filteringStatusProcessed:
|
||||
return !res.Reason.In(
|
||||
dnsfilter.FilteredBlockList,
|
||||
dnsfilter.FilteredBlackList,
|
||||
dnsfilter.FilteredBlockedService,
|
||||
dnsfilter.NotFilteredAllowList,
|
||||
dnsfilter.NotFilteredWhiteList,
|
||||
)
|
||||
|
||||
default:
|
||||
|
||||
@@ -3,9 +3,8 @@
|
||||
package sysutil
|
||||
|
||||
import (
|
||||
"log"
|
||||
"log/syslog"
|
||||
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
)
|
||||
|
||||
func configureSyslog(serviceName string) error {
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
package sysutil
|
||||
|
||||
import (
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"golang.org/x/sys/windows"
|
||||
"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"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
@@ -40,6 +41,13 @@ func RunCommand(command string, arguments ...string) (int, string, error) {
|
||||
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
|
||||
// Skip empty chunks
|
||||
// Whitespace is trimmed
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"log"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -18,6 +19,6 @@ func TestGetValidNetInterfacesForWeb(t *testing.T) {
|
||||
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
|
||||
|
||||
<!-- 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
|
||||
|
||||
### API: replace settings in GET /control/dns_info & POST /control/dns_config
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
'info':
|
||||
'title': 'AdGuard Home'
|
||||
'description': >
|
||||
AdGuard Home REST-ish API. Our admin web interface is built on top of this
|
||||
REST-ish API.
|
||||
'version': '0.105'
|
||||
AdGuard Home REST API. Our admin web interface is built on top of this REST
|
||||
API.
|
||||
'version': '0.104'
|
||||
'contact':
|
||||
'name': 'AdGuard Home'
|
||||
'url': 'https://github.com/AdguardTeam/AdGuardHome'
|
||||
@@ -190,6 +190,7 @@
|
||||
- 'blocked'
|
||||
- 'blocked_safebrowsing'
|
||||
- 'blocked_parental'
|
||||
- 'blocked_dns_rebinding'
|
||||
- 'whitelisted'
|
||||
- 'rewritten'
|
||||
- 'safe_search'
|
||||
@@ -523,7 +524,7 @@
|
||||
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.
|
||||
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
|
||||
last-resort attempt to make filter lists fresh. If you ever find
|
||||
yourself using `force` to make something work that otherwise wont, this
|
||||
@@ -1246,7 +1247,7 @@
|
||||
'properties':
|
||||
'reason':
|
||||
'type': 'string'
|
||||
'description': 'Request filtering status.'
|
||||
'description': 'DNS filter status'
|
||||
'enum':
|
||||
- 'NotFilteredNotFound'
|
||||
- 'NotFilteredWhiteList'
|
||||
@@ -1257,41 +1258,24 @@
|
||||
- 'FilteredInvalid'
|
||||
- 'FilteredSafeSearch'
|
||||
- 'FilteredBlockedService'
|
||||
- 'Rewrite'
|
||||
- 'RewriteEtcHosts'
|
||||
- 'DNSRewriteRule'
|
||||
- 'ReasonRewrite'
|
||||
'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'
|
||||
'rule':
|
||||
'deprecated': true
|
||||
'type': 'string'
|
||||
'example': '||example.org^'
|
||||
'description': >
|
||||
Filtering rule applied to the request (if any).
|
||||
|
||||
Deprecated: use `rules[*].text` instead.
|
||||
'rules':
|
||||
'description': 'Applied rules.'
|
||||
'type': 'array'
|
||||
'items':
|
||||
'$ref': '#/components/schemas/ResultRule'
|
||||
'description': 'Filtering rule applied to the request (if any)'
|
||||
'service_name':
|
||||
'type': 'string'
|
||||
'description': 'Set if reason=FilteredBlockedService'
|
||||
'cname':
|
||||
'type': 'string'
|
||||
'description': 'Set if reason=Rewrite'
|
||||
'description': 'Set if reason=ReasonRewrite'
|
||||
'ip_addrs':
|
||||
'type': 'array'
|
||||
'items':
|
||||
'type': 'string'
|
||||
'description': 'Set if reason=Rewrite'
|
||||
'description': 'Set if reason=ReasonRewrite'
|
||||
'FilterRefreshResponse':
|
||||
'type': 'object'
|
||||
'description': '/filtering/refresh response data'
|
||||
@@ -1627,30 +1611,18 @@
|
||||
'question':
|
||||
'$ref': '#/components/schemas/DnsQuestion'
|
||||
'filterId':
|
||||
'deprecated': true
|
||||
'type': 'integer'
|
||||
'example': 123123
|
||||
'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.
|
||||
the filter that rule belongs to.
|
||||
'rule':
|
||||
'deprecated': true
|
||||
'type': 'string'
|
||||
'example': '||example.org^'
|
||||
'description': >
|
||||
Filtering rule applied to the request (if any).
|
||||
|
||||
Deprecated: use `rules[*].text` instead.
|
||||
'rules':
|
||||
'description': 'Applied rules.'
|
||||
'type': 'array'
|
||||
'items':
|
||||
'$ref': '#/components/schemas/ResultRule'
|
||||
'description': 'Filtering rule applied to the request (if any)'
|
||||
'reason':
|
||||
'type': 'string'
|
||||
'description': 'Request filtering status.'
|
||||
'description': 'DNS filter status'
|
||||
'enum':
|
||||
- 'NotFilteredNotFound'
|
||||
- 'NotFilteredWhiteList'
|
||||
@@ -1661,9 +1633,7 @@
|
||||
- 'FilteredInvalid'
|
||||
- 'FilteredSafeSearch'
|
||||
- 'FilteredBlockedService'
|
||||
- 'Rewrite'
|
||||
- 'RewriteEtcHosts'
|
||||
- 'DNSRewriteRule'
|
||||
- 'ReasonRewrite'
|
||||
'service_name':
|
||||
'type': 'string'
|
||||
'description': 'Set if reason=FilteredBlockedService'
|
||||
@@ -1699,22 +1669,6 @@
|
||||
'anonymize_client_ip':
|
||||
'type': 'boolean'
|
||||
'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':
|
||||
'type': 'object'
|
||||
'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"
|
||||
URL="https://static.adguard.com/adguardhome/${CHANNEL}/${PKG_NAME}"
|
||||
OUT_DIR=/opt
|
||||
AGH_DIR="${OUT_DIR}/AdGuardHome"
|
||||
|
||||
# Root check
|
||||
if [ "$(id -u)" -eq 0 ]; then
|
||||
@@ -209,22 +208,22 @@ main() {
|
||||
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"
|
||||
|
||||
unpack "${PKG_NAME}" "${OUT_DIR}" "${PKG_EXT}" || error_exit "Cannot unpack the package"
|
||||
|
||||
# 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}"
|
||||
|
||||
log_info "AdGuard Home is now installed and running."
|
||||
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 "$@"
|
||||
@@ -2,6 +2,11 @@ const fs = require('fs');
|
||||
const readline = require('readline');
|
||||
const dnsPacket = require('dns-packet')
|
||||
|
||||
const decodeBase64 = (data) => {
|
||||
let buff = new Buffer(data, 'base64');
|
||||
return buff.toString('ascii');
|
||||
}
|
||||
|
||||
const processLineByLine = async (source, callback) => {
|
||||
const fileStream = fs.createReadStream(source);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
## Twosky integration script
|
||||
## Twosky intergration script
|
||||
|
||||
### 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