Compare commits
8 Commits
v0.105.0-b
...
102-dns-re
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a9efc39306 | ||
|
|
f924523f6a | ||
|
|
6e6ee9697a | ||
|
|
aff09211b2 | ||
|
|
bad1c6acdc | ||
|
|
fcb582679e | ||
|
|
6b60598025 | ||
|
|
b338bf9b3f |
@@ -11,7 +11,7 @@ fi
|
|||||||
found=0
|
found=0
|
||||||
git diff --cached --name-only | grep -q '.go$' && found=1
|
git diff --cached --name-only | grep -q '.go$' && found=1
|
||||||
if [ $found == 1 ]; then
|
if [ $found == 1 ]; then
|
||||||
make go-lint || exit 1
|
make lint-go || exit 1
|
||||||
go test ./... || exit 1
|
go test ./... || exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
15
.github/workflows/lint.yml
vendored
15
.github/workflows/lint.yml
vendored
@@ -1,4 +1,4 @@
|
|||||||
'name': 'lint'
|
'name': 'golangci-lint'
|
||||||
'on':
|
'on':
|
||||||
'push':
|
'push':
|
||||||
'tags':
|
'tags':
|
||||||
@@ -7,13 +7,16 @@
|
|||||||
- '*'
|
- '*'
|
||||||
'pull_request':
|
'pull_request':
|
||||||
'jobs':
|
'jobs':
|
||||||
'go-lint':
|
'golangci':
|
||||||
'runs-on': 'ubuntu-latest'
|
'runs-on': 'ubuntu-latest'
|
||||||
'steps':
|
'steps':
|
||||||
- 'uses': 'actions/checkout@v2'
|
- 'uses': 'actions/checkout@v2'
|
||||||
- 'name': 'run-lint'
|
- 'name': 'golangci-lint'
|
||||||
'run': >
|
'uses': 'golangci/golangci-lint-action@v2.3.0'
|
||||||
make go-install-tools go-lint
|
'with':
|
||||||
|
# This field is required. Don't set the patch version to always use
|
||||||
|
# the latest patch version.
|
||||||
|
'version': 'v1.32'
|
||||||
'eslint':
|
'eslint':
|
||||||
'runs-on': 'ubuntu-latest'
|
'runs-on': 'ubuntu-latest'
|
||||||
'steps':
|
'steps':
|
||||||
@@ -24,7 +27,7 @@
|
|||||||
'run': 'npm --prefix client run lint'
|
'run': 'npm --prefix client run lint'
|
||||||
'notify':
|
'notify':
|
||||||
'needs':
|
'needs':
|
||||||
- 'go-lint'
|
- 'golangci'
|
||||||
- 'eslint'
|
- 'eslint'
|
||||||
# Secrets are not passed to workflows that are triggered by a pull request
|
# Secrets are not passed to workflows that are triggered by a pull request
|
||||||
# from a fork.
|
# from a fork.
|
||||||
|
|||||||
47
.gitignore
vendored
47
.gitignore
vendored
@@ -1,22 +1,31 @@
|
|||||||
# Please, DO NOT put your text editors' temporary files here. The more are
|
.DS_Store
|
||||||
# added, the harder it gets to maintain and manage projects' gitignores. Put
|
/.vscode
|
||||||
# them into your global gitignore file instead.
|
.idea
|
||||||
#
|
/AdGuardHome
|
||||||
# See https://stackoverflow.com/a/7335487/1892060.
|
/AdGuardHome.exe
|
||||||
#
|
/AdGuardHome.yaml
|
||||||
# Only build, run, and test outputs here. Sorted.
|
/AdGuardHome.log
|
||||||
*-packr.go
|
|
||||||
*.db
|
|
||||||
*.snap
|
|
||||||
/bin/
|
|
||||||
/build/
|
|
||||||
/data/
|
/data/
|
||||||
|
/build/
|
||||||
/dist/
|
/dist/
|
||||||
/dnsfilter/tests/dnsfilter.TestLotsOfRules*.pprof
|
/client/node_modules/
|
||||||
/dnsfilter/tests/top-1m.csv
|
/querylog.json
|
||||||
/launchpad_credentials
|
/querylog.json.1
|
||||||
/querylog.json*
|
|
||||||
/snapcraft_login
|
|
||||||
AdGuardHome*
|
|
||||||
coverage.txt
|
coverage.txt
|
||||||
node_modules/
|
leases.db
|
||||||
|
|
||||||
|
# Test output
|
||||||
|
dnsfilter/tests/top-1m.csv
|
||||||
|
dnsfilter/tests/dnsfilter.TestLotsOfRules*.pprof
|
||||||
|
|
||||||
|
# Snapcraft build temporary files
|
||||||
|
*.snap
|
||||||
|
launchpad_credentials
|
||||||
|
snapcraft_login
|
||||||
|
snapcraft.yaml.bak
|
||||||
|
|
||||||
|
# IntelliJ IDEA project files
|
||||||
|
*.iml
|
||||||
|
|
||||||
|
# Packr
|
||||||
|
*-packr.go
|
||||||
|
|||||||
77
.golangci.yml
Normal file
77
.golangci.yml
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
# options for analysis running
|
||||||
|
'run':
|
||||||
|
# default concurrency is a available CPU number
|
||||||
|
'concurrency': 4
|
||||||
|
|
||||||
|
# timeout for analysis, e.g. 30s, 5m, default is 1m
|
||||||
|
'deadline': '2m'
|
||||||
|
|
||||||
|
# which files to skip: they will be analyzed, but issues from them
|
||||||
|
# won't be reported. Default value is empty list, but there is
|
||||||
|
# no need to include all autogenerated files, we confidently recognize
|
||||||
|
# autogenerated files. If it's not please let us know.
|
||||||
|
'skip-files':
|
||||||
|
- '.*generated.*'
|
||||||
|
- 'dnsfilter/rule_to_regexp.go'
|
||||||
|
- 'util/pprof.go'
|
||||||
|
- '.*_test.go'
|
||||||
|
- 'client/.*'
|
||||||
|
- 'build/.*'
|
||||||
|
- 'dist/.*'
|
||||||
|
|
||||||
|
# all available settings of specific linters
|
||||||
|
'linters-settings':
|
||||||
|
'errcheck':
|
||||||
|
# [deprecated] comma-separated list of pairs of the form pkg:regex
|
||||||
|
# the regex is used to ignore names within pkg. (default "fmt:.*").
|
||||||
|
# see https://github.com/kisielk/errcheck#the-deprecated-method for details
|
||||||
|
'ignore': 'fmt:.*,net:SetReadDeadline,net/http:^Write'
|
||||||
|
'gocyclo':
|
||||||
|
'min-complexity': 20
|
||||||
|
'lll':
|
||||||
|
'line-length': 200
|
||||||
|
|
||||||
|
'linters':
|
||||||
|
'enable':
|
||||||
|
- 'bodyclose'
|
||||||
|
- 'deadcode'
|
||||||
|
- 'depguard'
|
||||||
|
- 'dupl'
|
||||||
|
- 'errcheck'
|
||||||
|
- 'gocyclo'
|
||||||
|
- 'goimports'
|
||||||
|
- 'golint'
|
||||||
|
- 'gosec'
|
||||||
|
- 'govet'
|
||||||
|
- 'ineffassign'
|
||||||
|
- 'misspell'
|
||||||
|
- 'staticcheck'
|
||||||
|
- 'stylecheck'
|
||||||
|
- 'unconvert'
|
||||||
|
- 'unused'
|
||||||
|
- 'varcheck'
|
||||||
|
'disable-all': true
|
||||||
|
'fast': true
|
||||||
|
|
||||||
|
'issues':
|
||||||
|
# List of regexps of issue texts to exclude, empty list by default.
|
||||||
|
# But independently from this option we use default exclude patterns,
|
||||||
|
# it can be disabled by `exclude-use-default: false`. To list all
|
||||||
|
# excluded by default patterns execute `golangci-lint run --help`
|
||||||
|
'exclude':
|
||||||
|
# structcheck cannot detect usages while they're there
|
||||||
|
- '.parentalServer. is unused'
|
||||||
|
- '.safeBrowsingServer. is unused'
|
||||||
|
# errcheck
|
||||||
|
- 'Error return value of .s.closeConn. is not checked'
|
||||||
|
- 'Error return value of ..*.Shutdown.'
|
||||||
|
# goconst
|
||||||
|
- 'string .forcesafesearch.google.com. has 3 occurrences'
|
||||||
|
# gosec: Profiling endpoint is automatically exposed on /debug/pprof
|
||||||
|
- 'G108'
|
||||||
|
# gosec: Subprocess launched with function call as argument or cmd arguments
|
||||||
|
- 'G204'
|
||||||
|
# gosec: Potential DoS vulnerability via decompression bomb
|
||||||
|
- 'G110'
|
||||||
|
# gosec: Expect WriteFile permissions to be 0600 or less
|
||||||
|
- 'G306'
|
||||||
@@ -1558,6 +1558,7 @@ Strict matching can be enabled by enclosing the value in double quotes: e.g. `"a
|
|||||||
* blocked_services - blocked services
|
* blocked_services - blocked services
|
||||||
* blocked_safebrowsing - blocked by safebrowsing
|
* blocked_safebrowsing - blocked by safebrowsing
|
||||||
* blocked_parental - blocked by parental control
|
* blocked_parental - blocked by parental control
|
||||||
|
* blocked_dns_rebinding - blocked by DNS rebinding protection
|
||||||
* whitelisted - whitelisted
|
* whitelisted - whitelisted
|
||||||
* rewritten - all kinds of rewrites
|
* rewritten - all kinds of rewrites
|
||||||
* safe_search - enforced safe search
|
* safe_search - enforced safe search
|
||||||
|
|||||||
@@ -31,16 +31,14 @@ and this project adheres to
|
|||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Post-updating relaunch possibility is now determined OS-dependently ([#2231],
|
- Post-updating relaunch possibility is now determined OS-dependently ([#2231], [#2391]).
|
||||||
[#2391]).
|
|
||||||
- Made the mobileconfig HTTP API more robust and predictable, add parameters and
|
- Made the mobileconfig HTTP API more robust and predictable, add parameters and
|
||||||
improve error response ([#2358]).
|
improve error response ([#2358]).
|
||||||
- Improved HTTP requests handling and timeouts ([#2343]).
|
- Improved HTTP requests handling and timeouts ([#2343]).
|
||||||
- Our snap package now uses the `core20` image as its base ([#2306]).
|
- Our snap package now uses the `core20` image as its base ([#2306]).
|
||||||
- Various internal improvements ([#2267], [#2271], [#2297]).
|
- Various internal improvements ([#2271], [#2297]).
|
||||||
|
|
||||||
[#2231]: https://github.com/AdguardTeam/AdGuardHome/issues/2231
|
[#2231]: https://github.com/AdguardTeam/AdGuardHome/issues/2231
|
||||||
[#2267]: https://github.com/AdguardTeam/AdGuardHome/issues/2267
|
|
||||||
[#2271]: https://github.com/AdguardTeam/AdGuardHome/issues/2271
|
[#2271]: https://github.com/AdguardTeam/AdGuardHome/issues/2271
|
||||||
[#2297]: https://github.com/AdguardTeam/AdGuardHome/issues/2297
|
[#2297]: https://github.com/AdguardTeam/AdGuardHome/issues/2297
|
||||||
[#2306]: https://github.com/AdguardTeam/AdGuardHome/issues/2306
|
[#2306]: https://github.com/AdguardTeam/AdGuardHome/issues/2306
|
||||||
|
|||||||
28
HACKING.md
28
HACKING.md
@@ -1,9 +1,9 @@
|
|||||||
# *AdGuardHome* Developer Guidelines
|
# *AdGuardHome* Developer Guidelines
|
||||||
|
|
||||||
As of **December 2020**, this document is partially a work-in-progress, but
|
As of **2020-11-27**, this document is a work-in-progress, but should still be
|
||||||
should still be followed. Some of the rules aren't enforced as thoroughly or
|
followed. Some of the rules aren't enforced as thoroughly or remain broken in
|
||||||
remain broken in old code, but this is still the place to find out about what we
|
old code, but this is still the place to find out about what we **want** our
|
||||||
**want** our code to look like.
|
code to look like.
|
||||||
|
|
||||||
The rules are mostly sorted in the alphabetical order.
|
The rules are mostly sorted in the alphabetical order.
|
||||||
|
|
||||||
@@ -32,11 +32,6 @@ The rules are mostly sorted in the alphabetical order.
|
|||||||
|
|
||||||
## *Go*
|
## *Go*
|
||||||
|
|
||||||
> Not Golang, not GO, not GOLANG, not GoLang. It is Go in natural language,
|
|
||||||
> golang for others.
|
|
||||||
|
|
||||||
— [@rakyll](https://twitter.com/rakyll/status/1229850223184269312)
|
|
||||||
|
|
||||||
### Code And Naming
|
### Code And Naming
|
||||||
|
|
||||||
* Avoid `goto`.
|
* Avoid `goto`.
|
||||||
@@ -117,6 +112,8 @@ The rules are mostly sorted in the alphabetical order.
|
|||||||
|
|
||||||
* Use `gofumpt --extra -s`.
|
* Use `gofumpt --extra -s`.
|
||||||
|
|
||||||
|
**TODO(a.garipov):** Add to the linters.
|
||||||
|
|
||||||
* Write slices of struct like this:
|
* Write slices of struct like this:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
@@ -144,19 +141,6 @@ The rules are mostly sorted in the alphabetical order.
|
|||||||
|
|
||||||
* **TODO(a.garipov):** Define our *Markdown* conventions.
|
* **TODO(a.garipov):** Define our *Markdown* conventions.
|
||||||
|
|
||||||
## Shell Scripting
|
|
||||||
|
|
||||||
* Avoid bashisms, prefer *POSIX* features only.
|
|
||||||
|
|
||||||
* Prefer `'raw strings'` to `"double quoted strings"` whenever possible.
|
|
||||||
|
|
||||||
* Put spaces within `$( cmd )`, `$(( expr ))`, and `{ cmd; }`.
|
|
||||||
|
|
||||||
* 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.
|
|
||||||
|
|
||||||
## Text, Including Comments
|
## Text, Including Comments
|
||||||
|
|
||||||
* End sentences with appropriate punctuation.
|
* End sentences with appropriate punctuation.
|
||||||
|
|||||||
39
Makefile
39
Makefile
@@ -26,8 +26,7 @@
|
|||||||
# * DOCKER_IMAGE_NAME - adguard/adguard-home
|
# * DOCKER_IMAGE_NAME - adguard/adguard-home
|
||||||
# * DOCKER_OUTPUT - type=image,name=adguard/adguard-home,push=true
|
# * DOCKER_OUTPUT - type=image,name=adguard/adguard-home,push=true
|
||||||
|
|
||||||
GO := go
|
GOPATH := $(shell go env GOPATH)
|
||||||
GOPATH := $(shell $(GO) env GOPATH)
|
|
||||||
PWD := $(shell pwd)
|
PWD := $(shell pwd)
|
||||||
TARGET=AdGuardHome
|
TARGET=AdGuardHome
|
||||||
BASE_URL="https://static.adguard.com/adguardhome/$(CHANNEL)"
|
BASE_URL="https://static.adguard.com/adguardhome/$(CHANNEL)"
|
||||||
@@ -123,9 +122,9 @@ init:
|
|||||||
git config core.hooksPath .githooks
|
git config core.hooksPath .githooks
|
||||||
|
|
||||||
build: client_with_deps
|
build: client_with_deps
|
||||||
$(GO) mod download
|
go mod download
|
||||||
PATH=$(GOPATH)/bin:$(PATH) $(GO) generate ./...
|
PATH=$(GOPATH)/bin:$(PATH) go generate ./...
|
||||||
CGO_ENABLED=0 $(GO) build -ldflags="-s -w -X main.version=$(VERSION) -X main.channel=$(CHANNEL) -X main.goarm=$(GOARM)"
|
CGO_ENABLED=0 go build -ldflags="-s -w -X main.version=$(VERSION) -X main.channel=$(CHANNEL) -X main.goarm=$(GOARM)"
|
||||||
PATH=$(GOPATH)/bin:$(PATH) packr clean
|
PATH=$(GOPATH)/bin:$(PATH) packr clean
|
||||||
|
|
||||||
client:
|
client:
|
||||||
@@ -152,40 +151,38 @@ docker:
|
|||||||
@echo Now you can run the docker image:
|
@echo Now you can run the docker image:
|
||||||
@echo docker run --name "adguard-home" -p 53:53/tcp -p 53:53/udp -p 80:80/tcp -p 443:443/tcp -p 853:853/tcp -p 3000:3000/tcp $(DOCKER_IMAGE_NAME)
|
@echo docker run --name "adguard-home" -p 53:53/tcp -p 53:53/udp -p 80:80/tcp -p 443:443/tcp -p 853:853/tcp -p 3000:3000/tcp $(DOCKER_IMAGE_NAME)
|
||||||
|
|
||||||
lint: js-lint go-lint
|
lint: lint-js lint-go
|
||||||
|
|
||||||
js-lint: dependencies
|
lint-js: dependencies
|
||||||
|
@echo Running js linter
|
||||||
npm --prefix client run lint
|
npm --prefix client run lint
|
||||||
|
|
||||||
go-install-tools:
|
lint-go:
|
||||||
env GO=$(GO) sh ./scripts/go-install-tools.sh
|
@echo Running go linter
|
||||||
|
golangci-lint run
|
||||||
|
|
||||||
go-lint:
|
test: test-js test-go
|
||||||
env GO=$(GO) PATH="$$PWD/bin:$$PATH" sh ./scripts/go-lint.sh
|
|
||||||
|
|
||||||
test: js-test go-test
|
test-js:
|
||||||
|
|
||||||
js-test:
|
|
||||||
npm run test --prefix client
|
npm run test --prefix client
|
||||||
|
|
||||||
go-test:
|
test-go:
|
||||||
$(GO) test $(TEST_FLAGS) --coverprofile coverage.txt ./...
|
go test $(TEST_FLAGS) --coverprofile coverage.txt ./...
|
||||||
|
|
||||||
ci: client_with_deps
|
ci: client_with_deps
|
||||||
$(GO) mod download
|
go mod download
|
||||||
$(MAKE) test
|
$(MAKE) test
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
npm --prefix client ci
|
npm --prefix client ci
|
||||||
$(GO) mod download
|
go mod download
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f ./AdGuardHome ./AdGuardHome.exe ./coverage.txt
|
rm -f ./AdGuardHome ./AdGuardHome.exe ./coverage.txt
|
||||||
rm -f -r ./build/ ./client/node_modules/ ./data/ ./$(DIST_DIR)/
|
rm -f -r ./build/ ./client/node_modules/ ./data/ $(DIST_DIR)
|
||||||
# Set the GOPATH explicitly in case make clean is called from under sudo
|
# Set the GOPATH explicitly in case make clean is called from under sudo
|
||||||
# after a Docker build.
|
# after a Docker build.
|
||||||
env PATH="$(GOPATH)/bin:$$PATH" packr clean
|
env PATH="$(GOPATH)/bin:$$PATH" packr clean
|
||||||
rm -f -r ./bin/
|
|
||||||
|
|
||||||
docker-multi-arch:
|
docker-multi-arch:
|
||||||
DOCKER_CLI_EXPERIMENTAL=enabled \
|
DOCKER_CLI_EXPERIMENTAL=enabled \
|
||||||
@@ -203,7 +200,7 @@ docker-multi-arch:
|
|||||||
@echo docker run --name "adguard-home" -p 53:53/tcp -p 53:53/udp -p 80:80/tcp -p 443:443/tcp -p 853:853/tcp -p 3000:3000/tcp $(DOCKER_IMAGE_NAME)
|
@echo docker run --name "adguard-home" -p 53:53/tcp -p 53:53/udp -p 80:80/tcp -p 443:443/tcp -p 853:853/tcp -p 3000:3000/tcp $(DOCKER_IMAGE_NAME)
|
||||||
|
|
||||||
release: client_with_deps
|
release: client_with_deps
|
||||||
$(GO) mod download
|
go mod download
|
||||||
@echo Starting release build: version $(VERSION), channel $(CHANNEL)
|
@echo Starting release build: version $(VERSION), channel $(CHANNEL)
|
||||||
CHANNEL=$(CHANNEL) $(GORELEASER_COMMAND)
|
CHANNEL=$(CHANNEL) $(GORELEASER_COMMAND)
|
||||||
$(call write_version_file,$(VERSION))
|
$(call write_version_file,$(VERSION))
|
||||||
|
|||||||
@@ -171,6 +171,9 @@ You will need this to build AdGuard Home:
|
|||||||
* [node.js](https://nodejs.org/en/download/) v10.16.2 or later.
|
* [node.js](https://nodejs.org/en/download/) v10.16.2 or later.
|
||||||
* [npm](https://www.npmjs.com/) v6.14 or later.
|
* [npm](https://www.npmjs.com/) v6.14 or later.
|
||||||
|
|
||||||
|
Optionally, for Go devs:
|
||||||
|
* [golangci-lint](https://github.com/golangci/golangci-lint)
|
||||||
|
|
||||||
### Building
|
### Building
|
||||||
|
|
||||||
Open Terminal and execute these commands:
|
Open Terminal and execute these commands:
|
||||||
@@ -183,7 +186,7 @@ make
|
|||||||
|
|
||||||
Check the [`Makefile`](https://github.com/AdguardTeam/AdGuardHome/blob/master/Makefile) to learn about other commands.
|
Check the [`Makefile`](https://github.com/AdguardTeam/AdGuardHome/blob/master/Makefile) to learn about other commands.
|
||||||
|
|
||||||
**Building for a different platform.** You can build AdGuard for any OS/ARCH just like any other Go project.
|
**Building for a different platform.** You can build AdGuard for any OS/ARCH just like any other Golang project.
|
||||||
In order to do this, specify `GOOS` and `GOARCH` env variables before running make.
|
In order to do this, specify `GOOS` and `GOARCH` env variables before running make.
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|||||||
@@ -587,5 +587,12 @@
|
|||||||
"port_53_faq_link": "Port 53 is often occupied by \"DNSStubListener\" or \"systemd-resolved\" services. Please read <0>this instruction</0> on how to resolve this.",
|
"port_53_faq_link": "Port 53 is often occupied by \"DNSStubListener\" or \"systemd-resolved\" services. Please read <0>this instruction</0> on how to resolve this.",
|
||||||
"adg_will_drop_dns_queries": "AdGuard Home will be dropping all DNS queries from this client.",
|
"adg_will_drop_dns_queries": "AdGuard Home will be dropping all DNS queries from this client.",
|
||||||
"client_not_in_allowed_clients": "The client is not allowed because it is not in the \"Allowed clients\" list.",
|
"client_not_in_allowed_clients": "The client is not allowed because it is not in the \"Allowed clients\" list.",
|
||||||
"experimental": "Experimental"
|
"experimental": "Experimental",
|
||||||
|
"rebinding_title": "DNS Rebinding Protection",
|
||||||
|
"rebinding_desc": "Here you can configure protection against DNS rebinding attacks",
|
||||||
|
"rebinding_protection_enabled": "Enable protection from DNS rebinding attacks",
|
||||||
|
"rebinding_protection_enabled_desc": "If enabled, AdGuard Home will block responses containing host on the local network.",
|
||||||
|
"rebinding_allowed_hosts_title": "Allowed domains",
|
||||||
|
"rebinding_allowed_hosts_desc": "A list of domains. If configured, AdGuard Home will allow responses containing host on the local network from these domains. Here you can specify the exact domain names, wildcards and urlfilter-rules, e.g. 'example.org', '*.example.org' or '||example.org^'.",
|
||||||
|
"blocked_dns_rebinding": "Blocked DNS rebinding"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -273,15 +273,15 @@ describe('sortIp', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
describe('invalid input', () => {
|
describe('invalid input', () => {
|
||||||
const originalWarn = console.warn;
|
const originalError = console.error;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
console.warn = jest.fn();
|
console.error = jest.fn();
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
expect(console.warn).toHaveBeenCalled();
|
expect(console.error).toHaveBeenCalled();
|
||||||
console.warn = originalWarn;
|
console.error = originalError;
|
||||||
});
|
});
|
||||||
|
|
||||||
test('invalid strings', () => {
|
test('invalid strings', () => {
|
||||||
|
|||||||
@@ -37,6 +37,10 @@ export const setDnsConfig = (config) => async (dispatch) => {
|
|||||||
data.upstream_dns = splitByNewLine(config.upstream_dns);
|
data.upstream_dns = splitByNewLine(config.upstream_dns);
|
||||||
hasDnsSettings = true;
|
hasDnsSettings = true;
|
||||||
}
|
}
|
||||||
|
if (Object.prototype.hasOwnProperty.call(data, 'rebinding_allowed_hosts')) {
|
||||||
|
data.rebinding_allowed_hosts = splitByNewLine(config.rebinding_allowed_hosts);
|
||||||
|
hasDnsSettings = true;
|
||||||
|
}
|
||||||
|
|
||||||
await apiClient.setDnsConfig(data);
|
await apiClient.setDnsConfig(data);
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import React, { Component } from 'react';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import ReactTable from 'react-table';
|
import ReactTable from 'react-table';
|
||||||
import { withTranslation } from 'react-i18next';
|
import { withTranslation } from 'react-i18next';
|
||||||
import { sortIp } from '../../../helpers/helpers';
|
|
||||||
|
|
||||||
class Table extends Component {
|
class Table extends Component {
|
||||||
cellWrap = ({ value }) => (
|
cellWrap = ({ value }) => (
|
||||||
@@ -22,7 +21,6 @@ class Table extends Component {
|
|||||||
{
|
{
|
||||||
Header: this.props.t('answer'),
|
Header: this.props.t('answer'),
|
||||||
accessor: 'answer',
|
accessor: 'answer',
|
||||||
sortMethod: sortIp,
|
|
||||||
Cell: this.cellWrap,
|
Cell: this.cellWrap,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -259,7 +259,7 @@ let Form = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="form__desc mt-0 mb-2">
|
<div className="form__desc mt-0 mb-2">
|
||||||
<Trans components={[
|
<Trans components={[
|
||||||
<a target="_blank" rel="noopener noreferrer" href="https://github.com/AdguardTeam/AdGuardHome/wiki/Hosts-Blocklists#ctag"
|
<a href="https://github.com/AdguardTeam/AdGuardHome/wiki/Hosts-Blocklists#ctag"
|
||||||
key="0">link</a>,
|
key="0">link</a>,
|
||||||
]}>
|
]}>
|
||||||
tags_desc
|
tags_desc
|
||||||
|
|||||||
@@ -113,6 +113,9 @@ const Dhcp = () => {
|
|||||||
const enteredSomeValue = enteredSomeV4Value || enteredSomeV6Value || interfaceName;
|
const enteredSomeValue = enteredSomeV4Value || enteredSomeV6Value || interfaceName;
|
||||||
|
|
||||||
const getToggleDhcpButton = () => {
|
const getToggleDhcpButton = () => {
|
||||||
|
const otherDhcpFound = check && (check.v4.other_server.found === STATUS_RESPONSE.YES
|
||||||
|
|| check.v6.other_server.found === STATUS_RESPONSE.YES);
|
||||||
|
|
||||||
const filledConfig = interface_name && (Object.values(v4)
|
const filledConfig = interface_name && (Object.values(v4)
|
||||||
.every(Boolean) || Object.values(v6)
|
.every(Boolean) || Object.values(v6)
|
||||||
.every(Boolean));
|
.every(Boolean));
|
||||||
@@ -138,7 +141,7 @@ const Dhcp = () => {
|
|||||||
className={className}
|
className={className}
|
||||||
onClick={enabled ? onClickDisable : onClickEnable}
|
onClick={enabled ? onClickDisable : onClickEnable}
|
||||||
disabled={processingDhcp || processingConfig
|
disabled={processingDhcp || processingConfig
|
||||||
|| (!enabled && (!filledConfig || !check))}
|
|| (!enabled && (!filledConfig || !check || otherDhcpFound))}
|
||||||
>
|
>
|
||||||
<Trans>{enabled ? 'dhcp_disable' : 'dhcp_enable'}</Trans>
|
<Trans>{enabled ? 'dhcp_disable' : 'dhcp_enable'}</Trans>
|
||||||
</button>;
|
</button>;
|
||||||
|
|||||||
91
client/src/components/Settings/Dns/Rebinding/Form.js
Normal file
91
client/src/components/Settings/Dns/Rebinding/Form.js
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { Field, reduxForm } from 'redux-form';
|
||||||
|
import { Trans, useTranslation } from 'react-i18next';
|
||||||
|
import { useSelector } from 'react-redux';
|
||||||
|
import { renderTextareaField, CheckboxField } from '../../../../helpers/form';
|
||||||
|
import { removeEmptyLines } from '../../../../helpers/helpers';
|
||||||
|
import { FORM_NAME } from '../../../../helpers/constants';
|
||||||
|
|
||||||
|
const fields = [
|
||||||
|
{
|
||||||
|
id: 'rebinding_allowed_hosts',
|
||||||
|
title: 'rebinding_allowed_hosts_title',
|
||||||
|
subtitle: 'rebinding_allowed_hosts_desc',
|
||||||
|
normalizeOnBlur: removeEmptyLines,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const Form = ({
|
||||||
|
handleSubmit, submitting, invalid,
|
||||||
|
}) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const processingSetConfig = useSelector((state) => state.dnsConfig.processingSetConfig);
|
||||||
|
|
||||||
|
const renderField = ({
|
||||||
|
id, title, subtitle, disabled = processingSetConfig, normalizeOnBlur,
|
||||||
|
}) => <div key={id} className="form__group mb-5">
|
||||||
|
<label className="form__label form__label--with-desc" htmlFor={id}>
|
||||||
|
<Trans>{title}</Trans>
|
||||||
|
</label>
|
||||||
|
<div className="form__desc form__desc--top">
|
||||||
|
<Trans>{subtitle}</Trans>
|
||||||
|
</div>
|
||||||
|
<Field
|
||||||
|
id={id}
|
||||||
|
name={id}
|
||||||
|
component={renderTextareaField}
|
||||||
|
type="text"
|
||||||
|
className="form-control form-control--textarea font-monospace"
|
||||||
|
disabled={disabled}
|
||||||
|
normalizeOnBlur={normalizeOnBlur}
|
||||||
|
/>
|
||||||
|
</div>;
|
||||||
|
|
||||||
|
renderField.propTypes = {
|
||||||
|
id: PropTypes.string,
|
||||||
|
title: PropTypes.string,
|
||||||
|
subtitle: PropTypes.string,
|
||||||
|
disabled: PropTypes.bool,
|
||||||
|
normalizeOnBlur: PropTypes.func,
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form onSubmit={handleSubmit}>
|
||||||
|
<div className="col-12">
|
||||||
|
<div className="form__group form__group--settings">
|
||||||
|
<Field
|
||||||
|
name={'rebinding_protection_enabled'}
|
||||||
|
type="checkbox"
|
||||||
|
component={CheckboxField}
|
||||||
|
placeholder={t('rebinding_protection_enabled')}
|
||||||
|
subtitle={t('rebinding_protection_enabled_desc')}
|
||||||
|
disabled={processingSetConfig}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{fields.map(renderField)}
|
||||||
|
|
||||||
|
<div className="card-actions">
|
||||||
|
<div className="btn-list">
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="btn btn-success btn-standard"
|
||||||
|
disabled={submitting || invalid || processingSetConfig}
|
||||||
|
>
|
||||||
|
<Trans>save_config</Trans>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
Form.propTypes = {
|
||||||
|
handleSubmit: PropTypes.func.isRequired,
|
||||||
|
submitting: PropTypes.bool.isRequired,
|
||||||
|
invalid: PropTypes.bool.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default reduxForm({ form: FORM_NAME.REBINDING })(Form);
|
||||||
36
client/src/components/Settings/Dns/Rebinding/index.js
Normal file
36
client/src/components/Settings/Dns/Rebinding/index.js
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
|
||||||
|
import Form from './Form';
|
||||||
|
import Card from '../../../ui/Card';
|
||||||
|
import { setDnsConfig } from '../../../../actions/dnsConfig';
|
||||||
|
|
||||||
|
const RebindingConfig = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const {
|
||||||
|
rebinding_protection_enabled, rebinding_allowed_hosts,
|
||||||
|
} = useSelector((state) => state.dnsConfig, shallowEqual);
|
||||||
|
|
||||||
|
const handleFormSubmit = (values) => {
|
||||||
|
dispatch(setDnsConfig(values));
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
title={t('rebinding_title')}
|
||||||
|
subtitle={t('rebinding_desc')}
|
||||||
|
bodyType="card-body box-body--settings"
|
||||||
|
>
|
||||||
|
<Form
|
||||||
|
initialValues={{
|
||||||
|
rebinding_protection_enabled,
|
||||||
|
rebinding_allowed_hosts,
|
||||||
|
}}
|
||||||
|
onSubmit={handleFormSubmit}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default RebindingConfig;
|
||||||
@@ -8,6 +8,7 @@ import Config from './Config';
|
|||||||
import PageTitle from '../../ui/PageTitle';
|
import PageTitle from '../../ui/PageTitle';
|
||||||
import Loading from '../../ui/Loading';
|
import Loading from '../../ui/Loading';
|
||||||
import CacheConfig from './Cache';
|
import CacheConfig from './Cache';
|
||||||
|
import RebindingConfig from './Rebinding';
|
||||||
import { getDnsConfig } from '../../../actions/dnsConfig';
|
import { getDnsConfig } from '../../../actions/dnsConfig';
|
||||||
import { getAccessList } from '../../../actions/access';
|
import { getAccessList } from '../../../actions/access';
|
||||||
|
|
||||||
@@ -33,6 +34,7 @@ const Dns = () => {
|
|||||||
<Config />
|
<Config />
|
||||||
<CacheConfig />
|
<CacheConfig />
|
||||||
<Access />
|
<Access />
|
||||||
|
<RebindingConfig />
|
||||||
</>}
|
</>}
|
||||||
</>;
|
</>;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -232,7 +232,7 @@ let Form = (props) => {
|
|||||||
<Trans
|
<Trans
|
||||||
values={{ link: 'letsencrypt.org' }}
|
values={{ link: 'letsencrypt.org' }}
|
||||||
components={[
|
components={[
|
||||||
<a target="_blank" rel="noopener noreferrer" href="https://letsencrypt.org/" key="0">
|
<a href="https://letsencrypt.org/" key="0">
|
||||||
link
|
link
|
||||||
</a>,
|
</a>,
|
||||||
]}
|
]}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import flow from 'lodash/flow';
|
|||||||
import { CheckboxField, toNumber } from '../../../helpers/form';
|
import { CheckboxField, toNumber } from '../../../helpers/form';
|
||||||
import {
|
import {
|
||||||
FILTERS_INTERVALS_HOURS,
|
FILTERS_INTERVALS_HOURS,
|
||||||
FILTERS_RELATIVE_LINK,
|
FILTERS_LINK,
|
||||||
FORM_NAME,
|
FORM_NAME,
|
||||||
} from '../../../helpers/constants';
|
} from '../../../helpers/constants';
|
||||||
|
|
||||||
@@ -45,7 +45,7 @@ const Form = (props) => {
|
|||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const components = {
|
const components = {
|
||||||
a: <a href={FILTERS_RELATIVE_LINK} rel="noopener noreferrer" />,
|
a: <a href={FILTERS_LINK} rel="noopener noreferrer" />,
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -6,50 +6,6 @@ import { useSelector } from 'react-redux';
|
|||||||
import Topline from './Topline';
|
import Topline from './Topline';
|
||||||
import { EMPTY_DATE } from '../../helpers/constants';
|
import { EMPTY_DATE } from '../../helpers/constants';
|
||||||
|
|
||||||
const EXPIRATION_ENUM = {
|
|
||||||
VALID: 'VALID',
|
|
||||||
EXPIRED: 'EXPIRED',
|
|
||||||
EXPIRING: 'EXPIRING',
|
|
||||||
};
|
|
||||||
|
|
||||||
const EXPIRATION_STATE = {
|
|
||||||
[EXPIRATION_ENUM.EXPIRED]: {
|
|
||||||
toplineType: 'danger',
|
|
||||||
i18nKey: 'topline_expired_certificate',
|
|
||||||
},
|
|
||||||
[EXPIRATION_ENUM.EXPIRING]: {
|
|
||||||
toplineType: 'warning',
|
|
||||||
i18nKey: 'topline_expiring_certificate',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const getExpirationFlags = (not_after) => {
|
|
||||||
const DAYS_BEFORE_EXPIRATION = 5;
|
|
||||||
|
|
||||||
const now = Date.now();
|
|
||||||
const isExpiring = isAfter(addDays(now, DAYS_BEFORE_EXPIRATION), not_after);
|
|
||||||
const isExpired = isAfter(now, not_after);
|
|
||||||
|
|
||||||
return {
|
|
||||||
isExpiring,
|
|
||||||
isExpired,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const getExpirationEnumKey = (not_after) => {
|
|
||||||
const { isExpiring, isExpired } = getExpirationFlags(not_after);
|
|
||||||
|
|
||||||
if (isExpired) {
|
|
||||||
return EXPIRATION_ENUM.EXPIRED;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isExpiring) {
|
|
||||||
return EXPIRATION_ENUM.EXPIRING;
|
|
||||||
}
|
|
||||||
|
|
||||||
return EXPIRATION_ENUM.VALID;
|
|
||||||
};
|
|
||||||
|
|
||||||
const EncryptionTopline = () => {
|
const EncryptionTopline = () => {
|
||||||
const not_after = useSelector((state) => state.encryption.not_after);
|
const not_after = useSelector((state) => state.encryption.not_after);
|
||||||
|
|
||||||
@@ -57,21 +13,30 @@ const EncryptionTopline = () => {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const expirationStateKey = getExpirationEnumKey(not_after);
|
const isAboutExpire = isAfter(addDays(Date.now(), 30), not_after);
|
||||||
|
const isExpired = isAfter(Date.now(), not_after);
|
||||||
if (expirationStateKey === EXPIRATION_ENUM.VALID) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { toplineType, i18nKey } = EXPIRATION_STATE[expirationStateKey];
|
|
||||||
|
|
||||||
|
if (isExpired) {
|
||||||
return (
|
return (
|
||||||
<Topline type={toplineType}>
|
<Topline type="danger">
|
||||||
<Trans components={[<a href="#encryption" key="0">link</a>]}>
|
<Trans components={[<a href="#encryption" key="0">link</a>]}>
|
||||||
{i18nKey}
|
topline_expired_certificate
|
||||||
</Trans>
|
</Trans>
|
||||||
</Topline>
|
</Topline>
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isAboutExpire) {
|
||||||
|
return (
|
||||||
|
<Topline type="warning">
|
||||||
|
<Trans components={[<a href="#encryption" key="0">link</a>]}>
|
||||||
|
topline_expiring_certificate
|
||||||
|
</Trans>
|
||||||
|
</Topline>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default EncryptionTopline;
|
export default EncryptionTopline;
|
||||||
|
|||||||
@@ -53,9 +53,9 @@ export const REPOSITORY = {
|
|||||||
export const PRIVACY_POLICY_LINK = 'https://adguard.com/privacy/home.html';
|
export const PRIVACY_POLICY_LINK = 'https://adguard.com/privacy/home.html';
|
||||||
export const PORT_53_FAQ_LINK = 'https://github.com/AdguardTeam/AdGuardHome/wiki/FAQ#bindinuse';
|
export const PORT_53_FAQ_LINK = 'https://github.com/AdguardTeam/AdGuardHome/wiki/FAQ#bindinuse';
|
||||||
export const UPSTREAM_CONFIGURATION_WIKI_LINK = 'https://github.com/AdguardTeam/AdGuardHome/wiki/Configuration#upstreams';
|
export const UPSTREAM_CONFIGURATION_WIKI_LINK = 'https://github.com/AdguardTeam/AdGuardHome/wiki/Configuration#upstreams';
|
||||||
export const GETTING_STARTED_LINK = 'https://github.com/AdguardTeam/AdGuardHome/wiki/Getting-Started#update';
|
export const FILTERS_LINK = '#filters';
|
||||||
|
|
||||||
export const FILTERS_RELATIVE_LINK = '#filters';
|
export const GETTING_STARTED_LINK = 'https://github.com/AdguardTeam/AdGuardHome/wiki/Getting-Started#update';
|
||||||
|
|
||||||
export const ADDRESS_IN_USE_TEXT = 'address already in use';
|
export const ADDRESS_IN_USE_TEXT = 'address already in use';
|
||||||
|
|
||||||
@@ -341,6 +341,7 @@ export const FILTERED_STATUS = {
|
|||||||
REWRITE_HOSTS: 'RewriteEtcHosts',
|
REWRITE_HOSTS: 'RewriteEtcHosts',
|
||||||
FILTERED_SAFE_SEARCH: 'FilteredSafeSearch',
|
FILTERED_SAFE_SEARCH: 'FilteredSafeSearch',
|
||||||
FILTERED_SAFE_BROWSING: 'FilteredSafeBrowsing',
|
FILTERED_SAFE_BROWSING: 'FilteredSafeBrowsing',
|
||||||
|
FILTERED_REBIND: 'FilteredRebind',
|
||||||
FILTERED_PARENTAL: 'FilteredParental',
|
FILTERED_PARENTAL: 'FilteredParental',
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -373,6 +374,10 @@ export const RESPONSE_FILTER = {
|
|||||||
QUERY: 'blocked_parental',
|
QUERY: 'blocked_parental',
|
||||||
LABEL: 'blocked_adult_websites',
|
LABEL: 'blocked_adult_websites',
|
||||||
},
|
},
|
||||||
|
BLOCKED_DNS_REBINDING: {
|
||||||
|
QUERY: 'blocked_dns_rebinding',
|
||||||
|
LABEL: 'blocked_dns_rebinding',
|
||||||
|
},
|
||||||
ALLOWED: {
|
ALLOWED: {
|
||||||
QUERY: 'whitelisted',
|
QUERY: 'whitelisted',
|
||||||
LABEL: 'allowed',
|
LABEL: 'allowed',
|
||||||
@@ -414,6 +419,10 @@ export const FILTERED_STATUS_TO_META_MAP = {
|
|||||||
LABEL: 'blocked_service',
|
LABEL: 'blocked_service',
|
||||||
COLOR: QUERY_STATUS_COLORS.RED,
|
COLOR: QUERY_STATUS_COLORS.RED,
|
||||||
},
|
},
|
||||||
|
[FILTERED_STATUS.FILTERED_REBIND]: {
|
||||||
|
LABEL: RESPONSE_FILTER.BLOCKED_DNS_REBINDING.LABEL,
|
||||||
|
COLOR: QUERY_STATUS_COLORS.RED,
|
||||||
|
},
|
||||||
[FILTERED_STATUS.FILTERED_SAFE_SEARCH]: {
|
[FILTERED_STATUS.FILTERED_SAFE_SEARCH]: {
|
||||||
LABEL: RESPONSE_FILTER.SAFE_SEARCH.LABEL,
|
LABEL: RESPONSE_FILTER.SAFE_SEARCH.LABEL,
|
||||||
COLOR: QUERY_STATUS_COLORS.YELLOW,
|
COLOR: QUERY_STATUS_COLORS.YELLOW,
|
||||||
@@ -509,6 +518,7 @@ export const FORM_NAME = {
|
|||||||
INSTALL: 'install',
|
INSTALL: 'install',
|
||||||
LOGIN: 'login',
|
LOGIN: 'login',
|
||||||
CACHE: 'cache',
|
CACHE: 'cache',
|
||||||
|
REBINDING: 'rebinding',
|
||||||
...DHCP_FORM_NAMES,
|
...DHCP_FORM_NAMES,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -687,7 +687,7 @@ export const sortIp = (a, b) => {
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn(e);
|
console.error(e);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ const renderItem = ({
|
|||||||
|
|
||||||
return <li key={ip}>{isDns
|
return <li key={ip}>{isDns
|
||||||
? <strong>{dnsAddress}</strong>
|
? <strong>{dnsAddress}</strong>
|
||||||
: <a href={webAddress} target="_blank" rel="noopener noreferrer">{webAddress}</a>
|
: <a href={webAddress}>{webAddress}</a>
|
||||||
}
|
}
|
||||||
</li>;
|
</li>;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ const dnsConfig = handleActions(
|
|||||||
blocking_ipv6,
|
blocking_ipv6,
|
||||||
upstream_dns,
|
upstream_dns,
|
||||||
bootstrap_dns,
|
bootstrap_dns,
|
||||||
|
rebinding_allowed_hosts,
|
||||||
...values
|
...values
|
||||||
} = payload;
|
} = payload;
|
||||||
|
|
||||||
@@ -24,8 +25,9 @@ const dnsConfig = handleActions(
|
|||||||
...values,
|
...values,
|
||||||
blocking_ipv4: blocking_ipv4 || DEFAULT_BLOCKING_IPV4,
|
blocking_ipv4: blocking_ipv4 || DEFAULT_BLOCKING_IPV4,
|
||||||
blocking_ipv6: blocking_ipv6 || DEFAULT_BLOCKING_IPV6,
|
blocking_ipv6: blocking_ipv6 || DEFAULT_BLOCKING_IPV6,
|
||||||
upstream_dns: (upstream_dns && upstream_dns.join('\n')) || '',
|
upstream_dns: upstream_dns?.join('\n') || '',
|
||||||
bootstrap_dns: (bootstrap_dns && bootstrap_dns.join('\n')) || '',
|
bootstrap_dns: bootstrap_dns?.join('\n') || '',
|
||||||
|
rebinding_allowed_hosts: rebinding_allowed_hosts?.join('\n') || '',
|
||||||
processingGetConfig: false,
|
processingGetConfig: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -65,9 +65,3 @@ func (e *manyError) Unwrap() error {
|
|||||||
|
|
||||||
return e.underlying[0]
|
return e.underlying[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
// wrapper is a copy of the hidden errors.wrapper interface for tests, linting,
|
|
||||||
// etc.
|
|
||||||
type wrapper interface {
|
|
||||||
Unwrap() error
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -41,8 +41,6 @@ func TestError_Error(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestError_Unwrap(t *testing.T) {
|
func TestError_Unwrap(t *testing.T) {
|
||||||
var _ wrapper = &manyError{}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
errSimple = iota
|
errSimple = iota
|
||||||
errWrapped
|
errWrapped
|
||||||
|
|||||||
@@ -129,6 +129,8 @@ const (
|
|||||||
NotFilteredNotFound Reason = iota
|
NotFilteredNotFound Reason = iota
|
||||||
// NotFilteredWhiteList - the host is explicitly whitelisted
|
// NotFilteredWhiteList - the host is explicitly whitelisted
|
||||||
NotFilteredWhiteList
|
NotFilteredWhiteList
|
||||||
|
// NotFilteredError - there was a transitive error during check
|
||||||
|
NotFilteredError
|
||||||
|
|
||||||
// reasons for filtering
|
// reasons for filtering
|
||||||
|
|
||||||
@@ -144,6 +146,8 @@ const (
|
|||||||
FilteredSafeSearch
|
FilteredSafeSearch
|
||||||
// FilteredBlockedService - the host is blocked by "blocked services" settings
|
// FilteredBlockedService - the host is blocked by "blocked services" settings
|
||||||
FilteredBlockedService
|
FilteredBlockedService
|
||||||
|
// FilteredRebind - the request was blocked due to DNS rebinding protection
|
||||||
|
FilteredRebind
|
||||||
|
|
||||||
// ReasonRewrite - rewrite rule was applied
|
// ReasonRewrite - rewrite rule was applied
|
||||||
ReasonRewrite
|
ReasonRewrite
|
||||||
@@ -163,6 +167,7 @@ var reasonNames = []string{
|
|||||||
"FilteredInvalid",
|
"FilteredInvalid",
|
||||||
"FilteredSafeSearch",
|
"FilteredSafeSearch",
|
||||||
"FilteredBlockedService",
|
"FilteredBlockedService",
|
||||||
|
"FilteredRebind",
|
||||||
|
|
||||||
"Rewrite",
|
"Rewrite",
|
||||||
"RewriteEtcHosts",
|
"RewriteEtcHosts",
|
||||||
@@ -775,3 +780,12 @@ func (d *Dnsfilter) Start() {
|
|||||||
d.registerBlockedServicesHandlers()
|
d.registerBlockedServicesHandlers()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// stats
|
||||||
|
//
|
||||||
|
|
||||||
|
// GetStats return dns filtering stats since startup.
|
||||||
|
func (d *Dnsfilter) GetStats() Stats {
|
||||||
|
return gctx.stats
|
||||||
|
}
|
||||||
|
|||||||
@@ -76,6 +76,11 @@ type FilteringConfig struct {
|
|||||||
CacheMinTTL uint32 `yaml:"cache_ttl_min"` // override TTL value (minimum) received from upstream server
|
CacheMinTTL uint32 `yaml:"cache_ttl_min"` // override TTL value (minimum) received from upstream server
|
||||||
CacheMaxTTL uint32 `yaml:"cache_ttl_max"` // override TTL value (maximum) received from upstream server
|
CacheMaxTTL uint32 `yaml:"cache_ttl_max"` // override TTL value (maximum) received from upstream server
|
||||||
|
|
||||||
|
// DNS rebinding protection settings
|
||||||
|
// --
|
||||||
|
RebindingProtectionEnabled bool `yaml:"rebinding_protection_enabled"`
|
||||||
|
RebindingAllowedHosts []string `yaml:"rebinding_allowed_hosts"`
|
||||||
|
|
||||||
// Other settings
|
// Other settings
|
||||||
// --
|
// --
|
||||||
|
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ func (s *Server) handleDNSRequest(_ *proxy.Proxy, d *proxy.DNSContext) error {
|
|||||||
processFilteringBeforeRequest,
|
processFilteringBeforeRequest,
|
||||||
processUpstream,
|
processUpstream,
|
||||||
processDNSSECAfterResponse,
|
processDNSSECAfterResponse,
|
||||||
|
processRebindingFilteringAfterResponse,
|
||||||
processFilteringAfterResponse,
|
processFilteringAfterResponse,
|
||||||
s.ipset.process,
|
s.ipset.process,
|
||||||
processQueryLogsAndStats,
|
processQueryLogsAndStats,
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ type Server struct {
|
|||||||
queryLog querylog.QueryLog // Query log instance
|
queryLog querylog.QueryLog // Query log instance
|
||||||
stats stats.Stats
|
stats stats.Stats
|
||||||
access *accessCtx
|
access *accessCtx
|
||||||
|
rebinding *dnsRebindChecker
|
||||||
|
|
||||||
ipset ipsetCtx
|
ipset ipsetCtx
|
||||||
|
|
||||||
@@ -122,6 +123,7 @@ func (s *Server) WriteDiskConfig(c *FilteringConfig) {
|
|||||||
c.DisallowedClients = stringArrayDup(sc.DisallowedClients)
|
c.DisallowedClients = stringArrayDup(sc.DisallowedClients)
|
||||||
c.BlockedHosts = stringArrayDup(sc.BlockedHosts)
|
c.BlockedHosts = stringArrayDup(sc.BlockedHosts)
|
||||||
c.UpstreamDNS = stringArrayDup(sc.UpstreamDNS)
|
c.UpstreamDNS = stringArrayDup(sc.UpstreamDNS)
|
||||||
|
c.RebindingAllowedHosts = stringArrayDup(sc.RebindingAllowedHosts)
|
||||||
s.RUnlock()
|
s.RUnlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -221,6 +223,13 @@ func (s *Server) Prepare(config *ServerConfig) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialize DNS rebinding module
|
||||||
|
// --
|
||||||
|
s.rebinding, err = newRebindChecker(s.conf.RebindingAllowedHosts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// Register web handlers if necessary
|
// Register web handlers if necessary
|
||||||
// --
|
// --
|
||||||
if !webRegistered && s.conf.HTTPRegister != nil {
|
if !webRegistered && s.conf.HTTPRegister != nil {
|
||||||
|
|||||||
@@ -738,6 +738,100 @@ func TestRewrite(t *testing.T) {
|
|||||||
_ = s.Stop()
|
_ = s.Stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBlockedDNSRebinding(t *testing.T) {
|
||||||
|
s := createTestServer(t)
|
||||||
|
|
||||||
|
err := s.Start()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to start server: %s", err)
|
||||||
|
}
|
||||||
|
addr := s.dnsProxy.Addr(proxy.ProtoUDP)
|
||||||
|
|
||||||
|
//
|
||||||
|
// DNS rebinding protection
|
||||||
|
//
|
||||||
|
req := dns.Msg{}
|
||||||
|
req.Id = dns.Id()
|
||||||
|
req.RecursionDesired = true
|
||||||
|
req.Question = []dns.Question{
|
||||||
|
{Name: "192-168-1-250.nip.io.", Qtype: dns.TypeA, Qclass: dns.ClassINET},
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Lock()
|
||||||
|
s.conf.RebindingProtectionEnabled = true
|
||||||
|
s.Unlock()
|
||||||
|
|
||||||
|
reply, err := dns.Exchange(&req, addr.String())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Couldn't talk to server %s: %s", addr, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(reply.Answer) != 1 {
|
||||||
|
t.Fatalf("DNS server %s returned reply with wrong number of answers - %d", addr, len(reply.Answer))
|
||||||
|
}
|
||||||
|
|
||||||
|
a, ok := reply.Answer[0].(*dns.A)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("DNS server %s returned wrong answer type instead of A: %v", addr, reply.Answer[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
if !net.IPv4zero.Equal(a.A) {
|
||||||
|
t.Fatalf("DNS server %s returned wrong answer instead of 0.0.0.0: %v", addr, a.A)
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Lock()
|
||||||
|
s.conf.RebindingProtectionEnabled = false
|
||||||
|
s.Unlock()
|
||||||
|
|
||||||
|
reply, err = dns.Exchange(&req, addr.String())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Couldn't talk to server %s: %s", addr, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(reply.Answer) != 1 {
|
||||||
|
t.Fatalf("DNS server %s returned reply with wrong number of answers - %d", addr, len(reply.Answer))
|
||||||
|
}
|
||||||
|
|
||||||
|
a, ok = reply.Answer[0].(*dns.A)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("DNS server %s returned wrong answer type instead of A: %v", addr, reply.Answer[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
if !net.IPv4(192, 168, 1, 250).Equal(a.A) {
|
||||||
|
t.Fatalf("DNS server %s returned wrong answer instead of 192.168.1.250: %v", addr, a.A)
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Lock()
|
||||||
|
s.conf.RebindingProtectionEnabled = true
|
||||||
|
s.rebinding, _ = newRebindChecker([]string{
|
||||||
|
"||nip.io^",
|
||||||
|
})
|
||||||
|
s.Unlock()
|
||||||
|
|
||||||
|
reply, err = dns.Exchange(&req, addr.String())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Couldn't talk to server %s: %s", addr, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(reply.Answer) != 1 {
|
||||||
|
t.Fatalf("DNS server %s returned reply with wrong number of answers - %d", addr, len(reply.Answer))
|
||||||
|
}
|
||||||
|
|
||||||
|
a, ok = reply.Answer[0].(*dns.A)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("DNS server %s returned wrong answer type instead of A: %v", addr, reply.Answer[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
if !net.IPv4(192, 168, 1, 250).Equal(a.A) {
|
||||||
|
t.Fatalf("DNS server %s returned wrong answer instead of 192.168.1.250: %v", addr, a.A)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = s.Stop()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("DNS server failed to stop: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func createTestServer(t *testing.T) *Server {
|
func createTestServer(t *testing.T) *Server {
|
||||||
rules := `||nxdomain.example.org
|
rules := `||nxdomain.example.org
|
||||||
||null.example.org^
|
||null.example.org^
|
||||||
|
|||||||
@@ -113,8 +113,8 @@ func (s *Server) filterDNSResponse(ctx *dnsContext) (*dnsfilter.Result, error) {
|
|||||||
|
|
||||||
switch v := a.(type) {
|
switch v := a.(type) {
|
||||||
case *dns.CNAME:
|
case *dns.CNAME:
|
||||||
log.Debug("DNSFwd: Checking CNAME %s for %s", v.Target, v.Hdr.Name)
|
|
||||||
host = strings.TrimSuffix(v.Target, ".")
|
host = strings.TrimSuffix(v.Target, ".")
|
||||||
|
log.Debug("DNSFwd: Checking CNAME %s for %s", v.Target, v.Hdr.Name)
|
||||||
|
|
||||||
case *dns.A:
|
case *dns.A:
|
||||||
host = v.A.String()
|
host = v.A.String()
|
||||||
|
|||||||
@@ -37,6 +37,9 @@ type dnsConfig struct {
|
|||||||
CacheSize *uint32 `json:"cache_size"`
|
CacheSize *uint32 `json:"cache_size"`
|
||||||
CacheMinTTL *uint32 `json:"cache_ttl_min"`
|
CacheMinTTL *uint32 `json:"cache_ttl_min"`
|
||||||
CacheMaxTTL *uint32 `json:"cache_ttl_max"`
|
CacheMaxTTL *uint32 `json:"cache_ttl_max"`
|
||||||
|
|
||||||
|
RebindingProtectionEnabled *bool `json:"rebinding_protection_enabled"`
|
||||||
|
RebindingAllowedHosts *[]string `json:"rebinding_allowed_hosts"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) getDNSConfig() dnsConfig {
|
func (s *Server) getDNSConfig() dnsConfig {
|
||||||
@@ -61,6 +64,8 @@ func (s *Server) getDNSConfig() dnsConfig {
|
|||||||
} else if s.conf.AllServers {
|
} else if s.conf.AllServers {
|
||||||
upstreamMode = "parallel"
|
upstreamMode = "parallel"
|
||||||
}
|
}
|
||||||
|
rebindingEnabled := s.conf.RebindingProtectionEnabled
|
||||||
|
rebindingAllowedHosts := stringArrayDup(s.conf.RebindingAllowedHosts)
|
||||||
s.RUnlock()
|
s.RUnlock()
|
||||||
return dnsConfig{
|
return dnsConfig{
|
||||||
Upstreams: &upstreams,
|
Upstreams: &upstreams,
|
||||||
@@ -78,6 +83,8 @@ func (s *Server) getDNSConfig() dnsConfig {
|
|||||||
CacheMinTTL: &CacheMinTTL,
|
CacheMinTTL: &CacheMinTTL,
|
||||||
CacheMaxTTL: &CacheMaxTTL,
|
CacheMaxTTL: &CacheMaxTTL,
|
||||||
UpstreamMode: &upstreamMode,
|
UpstreamMode: &upstreamMode,
|
||||||
|
RebindingProtectionEnabled: &rebindingEnabled,
|
||||||
|
RebindingAllowedHosts: &rebindingAllowedHosts,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -301,6 +308,8 @@ func (s *Server) setConfig(dc dnsConfig) (restart bool) {
|
|||||||
s.conf.FastestAddr = false
|
s.conf.FastestAddr = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
restart = restart || s.setRebindingConfig(dc)
|
||||||
s.Unlock()
|
s.Unlock()
|
||||||
s.conf.ConfigModified()
|
s.conf.ConfigModified()
|
||||||
return restart
|
return restart
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ func TestDNSForwardHTTTP_handleGetConfig(t *testing.T) {
|
|||||||
conf: func() ServerConfig {
|
conf: func() ServerConfig {
|
||||||
return defaultConf
|
return defaultConf
|
||||||
},
|
},
|
||||||
want: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0}\n",
|
want: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0,\"rebinding_protection_enabled\":false,\"rebinding_allowed_hosts\":[]}\n",
|
||||||
}, {
|
}, {
|
||||||
name: "fastest_addr",
|
name: "fastest_addr",
|
||||||
conf: func() ServerConfig {
|
conf: func() ServerConfig {
|
||||||
@@ -37,7 +37,7 @@ func TestDNSForwardHTTTP_handleGetConfig(t *testing.T) {
|
|||||||
conf.FastestAddr = true
|
conf.FastestAddr = true
|
||||||
return conf
|
return conf
|
||||||
},
|
},
|
||||||
want: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"fastest_addr\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0}\n",
|
want: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"fastest_addr\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0,\"rebinding_protection_enabled\":false,\"rebinding_allowed_hosts\":[]}\n",
|
||||||
}, {
|
}, {
|
||||||
name: "parallel",
|
name: "parallel",
|
||||||
conf: func() ServerConfig {
|
conf: func() ServerConfig {
|
||||||
@@ -45,7 +45,7 @@ func TestDNSForwardHTTTP_handleGetConfig(t *testing.T) {
|
|||||||
conf.AllServers = true
|
conf.AllServers = true
|
||||||
return conf
|
return conf
|
||||||
},
|
},
|
||||||
want: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"parallel\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0}\n",
|
want: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"parallel\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0,\"rebinding_protection_enabled\":false,\"rebinding_allowed_hosts\":[]}\n",
|
||||||
}}
|
}}
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
@@ -73,7 +73,7 @@ func TestDNSForwardHTTTP_handleSetConfig(t *testing.T) {
|
|||||||
|
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
|
|
||||||
const defaultConfJSON = "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0}\n"
|
const defaultConfJSON = "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0,\"rebinding_protection_enabled\":false,\"rebinding_allowed_hosts\":[]}\n"
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
req string
|
req string
|
||||||
@@ -83,52 +83,52 @@ func TestDNSForwardHTTTP_handleSetConfig(t *testing.T) {
|
|||||||
name: "upstream_dns",
|
name: "upstream_dns",
|
||||||
req: "{\"upstream_dns\":[\"8.8.8.8:77\",\"8.8.4.4:77\"]}",
|
req: "{\"upstream_dns\":[\"8.8.8.8:77\",\"8.8.4.4:77\"]}",
|
||||||
wantSet: "",
|
wantSet: "",
|
||||||
wantGet: "{\"upstream_dns\":[\"8.8.8.8:77\",\"8.8.4.4:77\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0}\n",
|
wantGet: "{\"upstream_dns\":[\"8.8.8.8:77\",\"8.8.4.4:77\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0,\"rebinding_protection_enabled\":false,\"rebinding_allowed_hosts\":[]}\n",
|
||||||
}, {
|
}, {
|
||||||
name: "bootstraps",
|
name: "bootstraps",
|
||||||
req: "{\"bootstrap_dns\":[\"9.9.9.10\"]}",
|
req: "{\"bootstrap_dns\":[\"9.9.9.10\"]}",
|
||||||
wantSet: "",
|
wantSet: "",
|
||||||
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0}\n",
|
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0,\"rebinding_protection_enabled\":false,\"rebinding_allowed_hosts\":[]}\n",
|
||||||
}, {
|
}, {
|
||||||
name: "blocking_mode_good",
|
name: "blocking_mode_good",
|
||||||
req: "{\"blocking_mode\":\"refused\"}",
|
req: "{\"blocking_mode\":\"refused\"}",
|
||||||
wantSet: "",
|
wantSet: "",
|
||||||
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"refused\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0}\n",
|
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"refused\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0,\"rebinding_protection_enabled\":false,\"rebinding_allowed_hosts\":[]}\n",
|
||||||
}, {
|
}, {
|
||||||
name: "blocking_mode_bad",
|
name: "blocking_mode_bad",
|
||||||
req: "{\"blocking_mode\":\"custom_ip\"}",
|
req: "{\"blocking_mode\":\"custom_ip\"}",
|
||||||
wantSet: "blocking_mode: incorrect value\n",
|
wantSet: "blocking_mode: incorrect value\n",
|
||||||
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0}\n",
|
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0,\"rebinding_protection_enabled\":false,\"rebinding_allowed_hosts\":[]}\n",
|
||||||
}, {
|
}, {
|
||||||
name: "ratelimit",
|
name: "ratelimit",
|
||||||
req: "{\"ratelimit\":6}",
|
req: "{\"ratelimit\":6}",
|
||||||
wantSet: "",
|
wantSet: "",
|
||||||
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":6,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0}\n",
|
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":6,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0,\"rebinding_protection_enabled\":false,\"rebinding_allowed_hosts\":[]}\n",
|
||||||
}, {
|
}, {
|
||||||
name: "edns_cs_enabled",
|
name: "edns_cs_enabled",
|
||||||
req: "{\"edns_cs_enabled\":true}",
|
req: "{\"edns_cs_enabled\":true}",
|
||||||
wantSet: "",
|
wantSet: "",
|
||||||
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":true,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0}\n",
|
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":true,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0,\"rebinding_protection_enabled\":false,\"rebinding_allowed_hosts\":[]}\n",
|
||||||
}, {
|
}, {
|
||||||
name: "dnssec_enabled",
|
name: "dnssec_enabled",
|
||||||
req: "{\"dnssec_enabled\":true}",
|
req: "{\"dnssec_enabled\":true}",
|
||||||
wantSet: "",
|
wantSet: "",
|
||||||
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":true,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0}\n",
|
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":true,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0,\"rebinding_protection_enabled\":false,\"rebinding_allowed_hosts\":[]}\n",
|
||||||
}, {
|
}, {
|
||||||
name: "cache_size",
|
name: "cache_size",
|
||||||
req: "{\"cache_size\":1024}",
|
req: "{\"cache_size\":1024}",
|
||||||
wantSet: "",
|
wantSet: "",
|
||||||
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":1024,\"cache_ttl_min\":0,\"cache_ttl_max\":0}\n",
|
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":1024,\"cache_ttl_min\":0,\"cache_ttl_max\":0,\"rebinding_protection_enabled\":false,\"rebinding_allowed_hosts\":[]}\n",
|
||||||
}, {
|
}, {
|
||||||
name: "upstream_mode_parallel",
|
name: "upstream_mode_parallel",
|
||||||
req: "{\"upstream_mode\":\"parallel\"}",
|
req: "{\"upstream_mode\":\"parallel\"}",
|
||||||
wantSet: "",
|
wantSet: "",
|
||||||
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"parallel\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0}\n",
|
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"parallel\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0,\"rebinding_protection_enabled\":false,\"rebinding_allowed_hosts\":[]}\n",
|
||||||
}, {
|
}, {
|
||||||
name: "upstream_mode_fastest_addr",
|
name: "upstream_mode_fastest_addr",
|
||||||
req: "{\"upstream_mode\":\"fastest_addr\"}",
|
req: "{\"upstream_mode\":\"fastest_addr\"}",
|
||||||
wantSet: "",
|
wantSet: "",
|
||||||
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"fastest_addr\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0}\n",
|
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"fastest_addr\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0,\"rebinding_protection_enabled\":false,\"rebinding_allowed_hosts\":[]}\n",
|
||||||
}, {
|
}, {
|
||||||
name: "upstream_dns_bad",
|
name: "upstream_dns_bad",
|
||||||
req: "{\"upstream_dns\":[\"\"]}",
|
req: "{\"upstream_dns\":[\"\"]}",
|
||||||
|
|||||||
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,6 +21,7 @@ const (
|
|||||||
filteringStatusBlockedService = "blocked_services" // blocked
|
filteringStatusBlockedService = "blocked_services" // blocked
|
||||||
filteringStatusBlockedSafebrowsing = "blocked_safebrowsing" // blocked by safebrowsing
|
filteringStatusBlockedSafebrowsing = "blocked_safebrowsing" // blocked by safebrowsing
|
||||||
filteringStatusBlockedParental = "blocked_parental" // blocked by parental control
|
filteringStatusBlockedParental = "blocked_parental" // blocked by parental control
|
||||||
|
filteringStatusBlockedRebind = "blocked_dns_rebinding" // blocked by DNS rebinding protection
|
||||||
filteringStatusWhitelisted = "whitelisted" // whitelisted
|
filteringStatusWhitelisted = "whitelisted" // whitelisted
|
||||||
filteringStatusRewritten = "rewritten" // all kinds of rewrites
|
filteringStatusRewritten = "rewritten" // all kinds of rewrites
|
||||||
filteringStatusSafeSearch = "safe_search" // enforced safe search
|
filteringStatusSafeSearch = "safe_search" // enforced safe search
|
||||||
@@ -32,7 +33,7 @@ var filteringStatusValues = []string{
|
|||||||
filteringStatusAll, filteringStatusFiltered, filteringStatusBlocked,
|
filteringStatusAll, filteringStatusFiltered, filteringStatusBlocked,
|
||||||
filteringStatusBlockedService, filteringStatusBlockedSafebrowsing, filteringStatusBlockedParental,
|
filteringStatusBlockedService, filteringStatusBlockedSafebrowsing, filteringStatusBlockedParental,
|
||||||
filteringStatusWhitelisted, filteringStatusRewritten, filteringStatusSafeSearch,
|
filteringStatusWhitelisted, filteringStatusRewritten, filteringStatusSafeSearch,
|
||||||
filteringStatusProcessed,
|
filteringStatusProcessed, filteringStatusBlockedRebind,
|
||||||
}
|
}
|
||||||
|
|
||||||
// searchCriteria - every search request may contain a list of different search criteria
|
// searchCriteria - every search request may contain a list of different search criteria
|
||||||
|
|||||||
@@ -3,9 +3,8 @@
|
|||||||
package sysutil
|
package sysutil
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"log"
|
||||||
"log/syslog"
|
"log/syslog"
|
||||||
|
|
||||||
"github.com/AdguardTeam/golibs/log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func configureSyslog(serviceName string) error {
|
func configureSyslog(serviceName string) error {
|
||||||
|
|||||||
@@ -3,9 +3,9 @@
|
|||||||
package sysutil
|
package sysutil
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"log"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/AdguardTeam/golibs/log"
|
|
||||||
"golang.org/x/sys/windows"
|
"golang.org/x/sys/windows"
|
||||||
"golang.org/x/sys/windows/svc/eventlog"
|
"golang.org/x/sys/windows/svc/eventlog"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
module github.com/AdguardTeam/AdGuardHome/internal/tools
|
|
||||||
|
|
||||||
go 1.15
|
|
||||||
|
|
||||||
require (
|
|
||||||
dmitri.shuralyov.com/go/generated v0.0.0-20170818220700-b1254a446363 // indirect
|
|
||||||
github.com/client9/misspell v0.3.4 // indirect
|
|
||||||
github.com/fzipp/gocyclo v0.3.1
|
|
||||||
github.com/golangci/misspell v0.3.5
|
|
||||||
github.com/google/go-cmp v0.5.4 // indirect
|
|
||||||
github.com/gookit/color v1.3.3 // indirect
|
|
||||||
github.com/gordonklaus/ineffassign v0.0.0-20201107091007-3b93a8888063
|
|
||||||
github.com/kisielk/errcheck v1.4.0
|
|
||||||
github.com/kyoh86/looppointer v0.1.7
|
|
||||||
github.com/kyoh86/nolint v0.0.1 // indirect
|
|
||||||
github.com/securego/gosec/v2 v2.5.0
|
|
||||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b
|
|
||||||
golang.org/x/mod v0.4.0 // indirect
|
|
||||||
golang.org/x/tools v0.0.0-20201208062317-e652b2f42cc7
|
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
|
||||||
honnef.co/go/tools v0.0.1-2020.1.6
|
|
||||||
mvdan.cc/gofumpt v0.0.0-20201129102820-5c11c50e9475
|
|
||||||
mvdan.cc/unparam v0.0.0-20200501210554-b37ab49443f7
|
|
||||||
)
|
|
||||||
@@ -1,164 +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-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=
|
|
||||||
mvdan.cc/gofumpt v0.0.0-20201129102820-5c11c50e9475 h1:5ZmJGYyuTlhdlIpRxSFhdJqkXQweXETFCEaLhRAX3e8=
|
|
||||||
mvdan.cc/gofumpt v0.0.0-20201129102820-5c11c50e9475/go.mod h1:E4LOcu9JQEtnYXtB1Y51drqh2Qr2Ngk9J3YrRCwcbd0=
|
|
||||||
mvdan.cc/unparam v0.0.0-20200501210554-b37ab49443f7 h1:kAREL6MPwpsk1/PQPFD3Eg7WAQR5mPTWZJaBiG5LDbY=
|
|
||||||
mvdan.cc/unparam v0.0.0-20200501210554-b37ab49443f7/go.mod h1:HGC5lll35J70Y5v7vCGb9oLhHoScFwkHDJm/05RdSTc=
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
// +build tools
|
|
||||||
|
|
||||||
// Package tools and its main module are a nested internal module containing our
|
|
||||||
// development tool dependencies.
|
|
||||||
//
|
|
||||||
// See https://github.com/golang/go/wiki/Modules#how-can-i-track-tool-dependencies-for-a-module.
|
|
||||||
package tools
|
|
||||||
|
|
||||||
import (
|
|
||||||
_ "github.com/fzipp/gocyclo/cmd/gocyclo"
|
|
||||||
_ "github.com/golangci/misspell/cmd/misspell"
|
|
||||||
_ "github.com/gordonklaus/ineffassign"
|
|
||||||
_ "github.com/kisielk/errcheck"
|
|
||||||
_ "github.com/kyoh86/looppointer"
|
|
||||||
_ "github.com/securego/gosec/v2/cmd/gosec"
|
|
||||||
_ "golang.org/x/lint/golint"
|
|
||||||
_ "golang.org/x/tools/go/analysis/passes/nilness/cmd/nilness"
|
|
||||||
_ "golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow"
|
|
||||||
_ "honnef.co/go/tools/cmd/staticcheck"
|
|
||||||
_ "mvdan.cc/gofumpt/gofumports"
|
|
||||||
_ "mvdan.cc/unparam"
|
|
||||||
)
|
|
||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"path"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@@ -40,6 +41,13 @@ func RunCommand(command string, arguments ...string) (int, string, error) {
|
|||||||
return cmd.ProcessState.ExitCode(), string(out), nil
|
return cmd.ProcessState.ExitCode(), string(out), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func FuncName() string {
|
||||||
|
pc := make([]uintptr, 10) // at least 1 entry needed
|
||||||
|
runtime.Callers(2, pc)
|
||||||
|
f := runtime.FuncForPC(pc[0])
|
||||||
|
return path.Base(f.Name())
|
||||||
|
}
|
||||||
|
|
||||||
// SplitNext - split string by a byte and return the first chunk
|
// SplitNext - split string by a byte and return the first chunk
|
||||||
// Skip empty chunks
|
// Skip empty chunks
|
||||||
// Whitespace is trimmed
|
// Whitespace is trimmed
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package util
|
package util
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"log"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -18,6 +19,6 @@ func TestGetValidNetInterfacesForWeb(t *testing.T) {
|
|||||||
t.Fatalf("No addresses found for %s", iface.Name)
|
t.Fatalf("No addresses found for %s", iface.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Logf("%v", iface)
|
log.Printf("%v", iface)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -190,6 +190,7 @@
|
|||||||
- 'blocked'
|
- 'blocked'
|
||||||
- 'blocked_safebrowsing'
|
- 'blocked_safebrowsing'
|
||||||
- 'blocked_parental'
|
- 'blocked_parental'
|
||||||
|
- 'blocked_dns_rebinding'
|
||||||
- 'whitelisted'
|
- 'whitelisted'
|
||||||
- 'rewritten'
|
- 'rewritten'
|
||||||
- 'safe_search'
|
- 'safe_search'
|
||||||
|
|||||||
@@ -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,95 +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
|
|
||||||
|
|
||||||
# blocklistimports 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.
|
|
||||||
blocklistimports () {
|
|
||||||
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; }
|
|
||||||
}
|
|
||||||
|
|
||||||
# exitonoutput exits with a nonzero exit code if there is anything in
|
|
||||||
# the command's combined output.
|
|
||||||
exitonoutput() {
|
|
||||||
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"
|
|
||||||
}
|
|
||||||
|
|
||||||
exitonoutput blocklistimports
|
|
||||||
|
|
||||||
exitonoutput underscores
|
|
||||||
|
|
||||||
exitonoutput gofumpt --extra -l -s .
|
|
||||||
|
|
||||||
golint --set_exit_status ./...
|
|
||||||
|
|
||||||
"$GO" vet ./...
|
|
||||||
|
|
||||||
gocyclo --over 20 .
|
|
||||||
|
|
||||||
gosec --quiet .
|
|
||||||
|
|
||||||
ineffassign .
|
|
||||||
|
|
||||||
unparam ./...
|
|
||||||
|
|
||||||
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 ./...
|
|
||||||
exitonoutput sh -c '
|
|
||||||
errcheck --asserts ./... |\
|
|
||||||
{ grep -e "defer" -e "_test\.go:" -v || exit 0; }
|
|
||||||
'
|
|
||||||
|
|
||||||
staticcheck --checks='all' ./...
|
|
||||||
Reference in New Issue
Block a user