mirror of
https://github.com/AdguardTeam/AdGuardDNS.git
synced 2026-02-18 15:24:26 -05:00
Sync v2.16.0
This commit is contained in:
@@ -21,5 +21,6 @@
|
||||
},
|
||||
"line-length": false,
|
||||
"no-bare-urls": false,
|
||||
"no-emphasis-as-heading": false,
|
||||
"link-fragments": false
|
||||
}
|
||||
|
||||
10
CHANGELOG.md
10
CHANGELOG.md
@@ -7,6 +7,16 @@ The format is **not** based on [Keep a Changelog][kec], since the project **does
|
||||
[kec]: https://keepachangelog.com/en/1.0.0/
|
||||
[sem]: https://semver.org/spec/v2.0.0.html
|
||||
|
||||
## AGDNS-3241 / Build 1067
|
||||
|
||||
- The environment variables `QUERYLOG_SEMAPHORE_ENABLED` and `QUERYLOG_SEMAPHORE_LIMIT` have been added.
|
||||
|
||||
- The environment variable `MAX_THREADS` has been added.
|
||||
|
||||
## AGDNS-3228 / Build 1063
|
||||
|
||||
- The environment variables `CRASH_OUTPUT_DIR`, `CRASH_OUTPUT_ENABLED`, and `CRASH_OUTPUT_PREFIX` have been added.
|
||||
|
||||
## AGDNS-2998 / Build 1042
|
||||
|
||||
- Profiles’ file cache version has been incremented. The new field `StandardEnabled` has been added to access’ object.
|
||||
|
||||
16
Makefile
16
Makefile
@@ -24,7 +24,7 @@ BRANCH = $${BRANCH:-$$(git rev-parse --abbrev-ref HEAD)}
|
||||
GOAMD64 = v1
|
||||
GOPROXY = https://proxy.golang.org|direct
|
||||
GOTELEMETRY = off
|
||||
GOTOOLCHAIN = go1.24.6
|
||||
GOTOOLCHAIN = go1.25.1
|
||||
RACE = 0
|
||||
REVISION = $${REVISION:-$$(git rev-parse --short HEAD)}
|
||||
VERSION = 0
|
||||
@@ -36,6 +36,7 @@ ENV = env \
|
||||
GOPROXY='$(GOPROXY)' \
|
||||
GOTELEMETRY='$(GOTELEMETRY)' \
|
||||
GOTOOLCHAIN='$(GOTOOLCHAIN)' \
|
||||
ONLY="$(ONLY)" \
|
||||
PATH="$${PWD}/bin:$$("$(GO.MACRO)" env GOPATH)/bin:$${PATH}" \
|
||||
RACE='$(RACE)' \
|
||||
REVISION="$(REVISION)" \
|
||||
@@ -52,12 +53,16 @@ ENV_MISC = env \
|
||||
|
||||
# Keep this target first, so that a naked make invocation triggers a
|
||||
# full build.
|
||||
.PHONY: build
|
||||
build: go-deps go-build
|
||||
|
||||
.PHONY: init
|
||||
init: ; git config core.hooksPath ./scripts/hooks
|
||||
|
||||
.PHONY: test
|
||||
test: go-test
|
||||
|
||||
.PHONY: go-bench go-build go-deps go-env go-fuzz go-gen go-lint go-test go-tools go-upd-tools
|
||||
go-bench: ; $(ENV) "$(SHELL)" ./scripts/make/go-bench.sh
|
||||
go-build: ; $(ENV) "$(SHELL)" ./scripts/make/go-build.sh
|
||||
go-deps: ; $(ENV) "$(SHELL)" ./scripts/make/go-deps.sh
|
||||
@@ -69,23 +74,28 @@ go-test: ; $(ENV) RACE='1' "$(SHELL)" ./scripts/make/go-test.sh
|
||||
go-tools: ; $(ENV) "$(SHELL)" ./scripts/make/go-tools.sh
|
||||
go-upd-tools: ; $(ENV) "$(SHELL)" ./scripts/make/go-upd-tools.sh
|
||||
|
||||
.PHONY: go-check
|
||||
go-check: go-tools go-lint go-test
|
||||
|
||||
# A quick check to make sure that all operating systems relevant to the
|
||||
# development of the project can be typechecked and built successfully.
|
||||
.PHONY: go-os-check
|
||||
go-os-check:
|
||||
$(ENV) GOOS='darwin' "$(GO.MACRO)" vet ./internal/... ./internal/dnsserver/...
|
||||
$(ENV) GOOS='linux' "$(GO.MACRO)" vet ./internal/... ./internal/dnsserver/...
|
||||
$(ENV) GOOS='darwin' "$(GO.MACRO)" vet work
|
||||
$(ENV) GOOS='linux' "$(GO.MACRO)" vet work
|
||||
# Additionally, check the AdGuard Home OSs in the dnsserver module.
|
||||
$(ENV) GOOS='freebsd' "$(GO.MACRO)" vet ./internal/dnsserver/...
|
||||
$(ENV) GOOS='openbsd' "$(GO.MACRO)" vet ./internal/dnsserver/...
|
||||
$(ENV) GOOS='windows' "$(GO.MACRO)" vet ./internal/dnsserver/...
|
||||
|
||||
.PHONY: txt-lint
|
||||
txt-lint: ; $(ENV) "$(SHELL)" ./scripts/make/txt-lint.sh
|
||||
|
||||
.PHONY: md-lint
|
||||
md-lint: ; $(ENV_MISC) "$(SHELL)" ./scripts/make/md-lint.sh
|
||||
sh-lint: ; $(ENV_MISC) "$(SHELL)" ./scripts/make/sh-lint.sh
|
||||
|
||||
# Targets related to AdGuard DNS start here.
|
||||
|
||||
.PHONY: sync-github
|
||||
sync-github: ; $(ENV) "$(SHELL)" ./scripts/make/github-sync.sh
|
||||
|
||||
@@ -640,7 +640,7 @@ The items of the `filtering_groups` array have the following properties:
|
||||
|
||||
**Example:** `true`.
|
||||
|
||||
- <a href="#fg-*-block_chrome_prefetch" id="fg-*-block_chrome_prefetch" name="fg-*-block_chrome_prefetch">`block_chrome_prefetch`</a>: If true, Chrome prefetch domain queries are blocked for requests using this filtering group, forcing the preferch proxy into preflight mode.
|
||||
- <a href="#fg-*-block_chrome_prefetch" id="fg-*-block_chrome_prefetch" name="fg-*-block_chrome_prefetch">`block_chrome_prefetch`</a>: If true, Chrome prefetch domain queries are blocked for requests using this filtering group, forcing the prefetch proxy into preflight mode.
|
||||
|
||||
**Example:** `true`.
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
Development is supported on Linux and macOS (aka Darwin) systems.
|
||||
|
||||
1. Install Go 1.24 or later.
|
||||
1. Install Go 1.25 or later.
|
||||
|
||||
2. Call `make init` to set up the Git pre-commit hook.
|
||||
|
||||
@@ -43,13 +43,27 @@ This is not an extensive list. See `../Makefile`.
|
||||
|
||||
- `../internal/backendpb/dns.pb.go`
|
||||
- `../internal/backendpb/dns_grpc.pb.go`
|
||||
- `../internal/ecscache/ecsblockilist_generate.go`
|
||||
- `../internal/ecscache/ecsblocklist_generate.go`
|
||||
- `../internal/geoip/asntops_generate.go`
|
||||
- `../internal/geoip/country_generate.go`
|
||||
- `../internal/profiledb/internal/filecachepb/filecache.pb.go`
|
||||
|
||||
You'll need to [install `protoc`][protoc] for the last one.
|
||||
|
||||
Use the `ONLY` environment variable to generate selected parts. This generates only the backend and file-cache protobuf files:
|
||||
|
||||
```sh
|
||||
make ONLY='backendpb filecachepb' go-gen
|
||||
```
|
||||
|
||||
Available values:
|
||||
|
||||
- `backendpb`
|
||||
- `ecscache`
|
||||
- `filecachepb`
|
||||
- `geoip_asntops`
|
||||
- `geoip_country`
|
||||
|
||||
- `make go-lint`: Run Go checkers and static analysis.
|
||||
|
||||
- `make go-test`: Run Go tests.
|
||||
|
||||
@@ -16,6 +16,9 @@ AdGuard DNS uses [environment variables][wiki-env] to store some of the more sen
|
||||
- [`CONSUL_ALLOWLIST_URL`](#CONSUL_ALLOWLIST_URL)
|
||||
- [`CONSUL_DNSCHECK_KV_URL`](#CONSUL_DNSCHECK_KV_URL)
|
||||
- [`CONSUL_DNSCHECK_SESSION_URL`](#CONSUL_DNSCHECK_SESSION_URL)
|
||||
- [`CRASH_OUTPUT_DIR`](#CRASH_OUTPUT_DIR)
|
||||
- [`CRASH_OUTPUT_ENABLED`](#CRASH_OUTPUT_ENABLED)
|
||||
- [`CRASH_OUTPUT_PREFIX`](#CRASH_OUTPUT_PREFIX)
|
||||
- [`CUSTOM_DOMAINS_API_KEY`](#CUSTOM_DOMAINS_API_KEY)
|
||||
- [`CUSTOM_DOMAINS_CACHE_PATH`](#CUSTOM_DOMAINS_CACHE_PATH)
|
||||
- [`CUSTOM_DOMAINS_ENABLED`](#CUSTOM_DOMAINS_ENABLED)
|
||||
@@ -36,6 +39,7 @@ AdGuard DNS uses [environment variables][wiki-env] to store some of the more sen
|
||||
- [`LISTEN_PORT`](#LISTEN_PORT)
|
||||
- [`LOG_FORMAT`](#LOG_FORMAT)
|
||||
- [`LOG_TIMESTAMP`](#LOG_TIMESTAMP)
|
||||
- [`MAX_THREADS`](#MAX_THREADS)
|
||||
- [`METRICS_NAMESPACE`](#METRICS_NAMESPACE)
|
||||
- [`NEW_REG_DOMAINS_ENABLED`](#NEW_REG_DOMAINS_ENABLED)
|
||||
- [`NEW_REG_DOMAINS_URL`](#NEW_REG_DOMAINS_URL)
|
||||
@@ -54,6 +58,8 @@ AdGuard DNS uses [environment variables][wiki-env] to store some of the more sen
|
||||
- [`REDIS_PORT`](#REDIS_PORT)
|
||||
- [`REDIS_WAIT`](#REDIS_WAIT)
|
||||
- [`QUERYLOG_PATH`](#QUERYLOG_PATH)
|
||||
- [`QUERYLOG_SEMAPHORE_ENABLED`](#QUERYLOG_SEMAPHORE_ENABLED)
|
||||
- [`QUERYLOG_SEMAPHORE_LIMIT`](#QUERYLOG_SEMAPHORE_LIMIT)
|
||||
- [`RATELIMIT_ALLOWLIST_TYPE`](#RATELIMIT_ALLOWLIST_TYPE)
|
||||
- [`RULESTAT_URL`](#RULESTAT_URL)
|
||||
- [`SAFE_BROWSING_ENABLED`](#SAFE_BROWSING_ENABLED)
|
||||
@@ -166,6 +172,24 @@ The HTTP(S) URL of the session API of the Consul instance used as a key-value da
|
||||
|
||||
**Default:** **Unset.**
|
||||
|
||||
## <a href="#CRASH_OUTPUT_DIR" id="CRASH_OUTPUT_DIR" name="CRASH_OUTPUT_DIR">`CRASH_OUTPUT_DIR`</a>
|
||||
|
||||
The path to the directory used to create crash reports. The directory must exist.
|
||||
|
||||
**Default:** No default value, the variable is required if `CRASH_OUTPUT_ENABLED` is set to `1`.
|
||||
|
||||
## <a href="#CRASH_OUTPUT_ENABLED" id="CRASH_OUTPUT_ENABLED" name="CRASH_OUTPUT_ENABLED">`CRASH_OUTPUT_ENABLED`</a>
|
||||
|
||||
When set to `1`, put a crash report to `CRASH_OUTPUT_DIR`.
|
||||
|
||||
**Default:** `0`.
|
||||
|
||||
## <a href="#CRASH_OUTPUT_PREFIX" id="CRASH_OUTPUT_PREFIX" name="CRASH_OUTPUT_PREFIX">`CRASH_OUTPUT_PREFIX`</a>
|
||||
|
||||
The prefix to use for the crash report files. The variable is required if `CRASH_OUTPUT_ENABLED` is set to `1`.
|
||||
|
||||
**Default:** `agdns`.
|
||||
|
||||
## <a href="#CUSTOM_DOMAINS_API_KEY" id="CUSTOM_DOMAINS_API_KEY" name="CUSTOM_DOMAINS_API_KEY">`CUSTOM_DOMAINS_API_KEY`</a>
|
||||
|
||||
The API key to use when authenticating queries to the backend custom-domain API, if any. The API key should be valid as defined by [RFC 6750].
|
||||
@@ -311,6 +335,12 @@ If `1`, show timestamps in the plain text logs. If `0`, don't show the timestamp
|
||||
|
||||
**Default:** `1`.
|
||||
|
||||
## <a href="#MAX_THREADS" id="MAX_THREADS" name="MAX_THREADS">`MAX_THREADS`</a>
|
||||
|
||||
If greater than zero, sets the maximum number of threads for the Go runtime. If zero, the number remains the default one, which is 10 000. It must not be negative.
|
||||
|
||||
**Default:** `0`.
|
||||
|
||||
## <a href="#METRICS_NAMESPACE" id="METRICS_NAMESPACE" name="METRICS_NAMESPACE">`METRICS_NAMESPACE`</a>
|
||||
|
||||
The namespace to be used for Prometheus metrics. It must be a valid Prometheus metric label.
|
||||
@@ -454,6 +484,18 @@ The path to the file into which the query log is going to be written.
|
||||
|
||||
**Default:** `./querylog.jsonl`.
|
||||
|
||||
## <a href="#QUERYLOG_SEMAPHORE_ENABLED" id="QUERYLOG_SEMAPHORE_ENABLED" name="QUERYLOG_SEMAPHORE_ENABLED">`QUERYLOG_SEMAPHORE_ENABLED`</a>
|
||||
|
||||
If `1`, enabled the querylog semaphore used to limit the parallelism of writing to the querylog and thus reducing the amount of OS threads that are created.
|
||||
|
||||
**Default:** `0`.
|
||||
|
||||
## <a href="#QUERYLOG_SEMAPHORE_LIMIT" id="QUERYLOG_SEMAPHORE_LIMIT" name="QUERYLOG_SEMAPHORE_LIMIT">`QUERYLOG_SEMAPHORE_LIMIT`</a>
|
||||
|
||||
The amount of writes to the querylog that can run in parallel.
|
||||
|
||||
**Default:** No default value, the variable is required if `QUERYLOG_SEMAPHORE_ENABLED` is set to `1`.
|
||||
|
||||
## <a href="#RATELIMIT_ALLOWLIST_TYPE" id="RATELIMIT_ALLOWLIST_TYPE" name="RATELIMIT_ALLOWLIST_TYPE">`RATELIMIT_ALLOWLIST_TYPE`</a>
|
||||
|
||||
Defines where the rate limit settings are received from. Allowed values are `backend` and `consul`.
|
||||
|
||||
76
go.mod
76
go.mod
@@ -1,12 +1,12 @@
|
||||
module github.com/AdguardTeam/AdGuardDNS
|
||||
|
||||
go 1.24.6
|
||||
go 1.25.1
|
||||
|
||||
require (
|
||||
// NOTE: Do not change the pseudoversion.
|
||||
github.com/AdguardTeam/AdGuardDNS/internal/dnsserver v0.0.0-00010101000000-000000000000
|
||||
github.com/AdguardTeam/golibs v0.34.0
|
||||
github.com/AdguardTeam/urlfilter v0.21.0
|
||||
github.com/AdguardTeam/golibs v0.34.1
|
||||
github.com/AdguardTeam/urlfilter v0.22.0
|
||||
github.com/ameshkov/dnscrypt/v2 v2.4.0
|
||||
github.com/axiomhq/hyperloglog v0.2.5
|
||||
github.com/bluele/gcache v0.0.2
|
||||
@@ -19,23 +19,23 @@ require (
|
||||
github.com/miekg/dns v1.1.68
|
||||
github.com/oschwald/maxminddb-golang v1.13.1
|
||||
github.com/patrickmn/go-cache v2.1.1-0.20191004192108-46f407853014+incompatible
|
||||
github.com/prometheus/client_golang v1.23.0
|
||||
github.com/prometheus/client_golang v1.23.1
|
||||
github.com/prometheus/client_model v0.6.2
|
||||
github.com/prometheus/common v0.65.0
|
||||
github.com/prometheus/common v0.66.0
|
||||
github.com/quic-go/quic-go v0.54.0
|
||||
github.com/stretchr/testify v1.11.1
|
||||
github.com/viktordanov/golang-lru v0.5.6
|
||||
golang.org/x/crypto v0.41.0
|
||||
golang.org/x/net v0.43.0
|
||||
golang.org/x/sys v0.35.0
|
||||
golang.org/x/time v0.12.0
|
||||
google.golang.org/grpc v1.75.0
|
||||
google.golang.org/protobuf v1.36.8
|
||||
golang.org/x/crypto v0.42.0
|
||||
golang.org/x/net v0.44.0
|
||||
golang.org/x/sys v0.36.0
|
||||
golang.org/x/time v0.13.0
|
||||
google.golang.org/grpc v1.75.1
|
||||
google.golang.org/protobuf v1.36.9
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.121.6 // indirect
|
||||
cloud.google.com/go v0.122.0 // indirect
|
||||
cloud.google.com/go/ai v0.12.1 // indirect
|
||||
cloud.google.com/go/auth v0.16.5 // indirect
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
|
||||
@@ -61,8 +61,9 @@ require (
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.15.0 // indirect
|
||||
github.com/gookit/color v1.5.4 // indirect
|
||||
github.com/gookit/color v1.6.0 // indirect
|
||||
github.com/gordonklaus/ineffassign v0.2.0 // indirect
|
||||
github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // indirect
|
||||
github.com/jstemmer/go-junit-report/v2 v2.1.0 // indirect
|
||||
github.com/kamstrup/intmap v0.5.1 // indirect
|
||||
github.com/kisielk/errcheck v1.9.0 // indirect
|
||||
@@ -77,35 +78,46 @@ require (
|
||||
github.com/securego/gosec/v2 v2.22.8 // indirect
|
||||
github.com/uudashr/gocognit v1.2.0 // indirect
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.62.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0 // indirect
|
||||
go.opentelemetry.io/otel v1.37.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.37.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.37.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 // indirect
|
||||
go.opentelemetry.io/otel v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.38.0 // indirect
|
||||
go.uber.org/mock v0.6.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b // indirect
|
||||
golang.org/x/exp/typeparams v0.0.0-20250819193227-8b4c13bb791b // indirect
|
||||
golang.org/x/mod v0.27.0 // indirect
|
||||
golang.org/x/oauth2 v0.30.0 // indirect
|
||||
golang.org/x/sync v0.16.0 // indirect
|
||||
golang.org/x/telemetry v0.0.0-20250822161441-f407b8c191ff // indirect
|
||||
golang.org/x/term v0.34.0 // indirect
|
||||
golang.org/x/text v0.28.0 // indirect
|
||||
golang.org/x/tools v0.36.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250911091902-df9299821621 // indirect
|
||||
golang.org/x/exp/typeparams v0.0.0-20250911091902-df9299821621 // indirect
|
||||
golang.org/x/mod v0.28.0 // indirect
|
||||
golang.org/x/oauth2 v0.31.0 // indirect
|
||||
golang.org/x/sync v0.17.0 // indirect
|
||||
golang.org/x/telemetry v0.0.0-20250908211612-aef8a434d053 // indirect
|
||||
golang.org/x/term v0.35.0 // indirect
|
||||
golang.org/x/text v0.29.0 // indirect
|
||||
golang.org/x/tools v0.37.0 // indirect
|
||||
golang.org/x/vuln v1.1.4 // indirect
|
||||
google.golang.org/api v0.248.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250826171959-ef028d996bc1 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250826171959-ef028d996bc1 // indirect
|
||||
google.golang.org/api v0.249.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20250908214217-97024824d090 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250908214217-97024824d090 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090 // indirect
|
||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
honnef.co/go/tools v0.6.1 // indirect
|
||||
mvdan.cc/editorconfig v0.3.0 // indirect
|
||||
mvdan.cc/gofumpt v0.8.0 // indirect
|
||||
mvdan.cc/gofumpt v0.9.1 // indirect
|
||||
mvdan.cc/sh/v3 v3.12.0 // indirect
|
||||
mvdan.cc/unparam v0.0.0-20250301125049-0df0534333a4 // indirect
|
||||
)
|
||||
|
||||
// NOTE: Keep in sync with .gitignore.
|
||||
ignore (
|
||||
./bin/
|
||||
./filters/
|
||||
./github-mirror/
|
||||
./test-reports/
|
||||
./test/
|
||||
./tmp/
|
||||
)
|
||||
|
||||
tool (
|
||||
github.com/fzipp/gocyclo/cmd/gocyclo
|
||||
github.com/golangci/misspell/cmd/misspell
|
||||
|
||||
156
go.sum
156
go.sum
@@ -1,5 +1,5 @@
|
||||
cloud.google.com/go v0.121.6 h1:waZiuajrI28iAf40cWgycWNgaXPO06dupuS+sgibK6c=
|
||||
cloud.google.com/go v0.121.6/go.mod h1:coChdst4Ea5vUpiALcYKXEpR1S9ZgXbhEzzMcMR66vI=
|
||||
cloud.google.com/go v0.122.0 h1:0JTLGrcSIs3HIGsgVPvTx3cfyFSP/k9CI8vLPHTd6Wc=
|
||||
cloud.google.com/go v0.122.0/go.mod h1:xBoMV08QcqUGuPW65Qfm1o9Y4zKZBpGS+7bImXLTAZU=
|
||||
cloud.google.com/go/ai v0.12.1 h1:m1n/VjUuHS+pEO/2R4/VbuuEIkgk0w67fDQvFaMngM0=
|
||||
cloud.google.com/go/ai v0.12.1/go.mod h1:5vIPNe1ZQsVZqCliXIPL4QnhObQQY4d9hAGHdVc4iw4=
|
||||
cloud.google.com/go/auth v0.16.5 h1:mFWNQ2FEVWAliEQWpAdH80omXFokmrnbDhUS9cBywsI=
|
||||
@@ -10,10 +10,10 @@ cloud.google.com/go/compute/metadata v0.8.0 h1:HxMRIbao8w17ZX6wBnjhcDkW6lTFpgcao
|
||||
cloud.google.com/go/compute/metadata v0.8.0/go.mod h1:sYOGTp851OV9bOFJ9CH7elVvyzopvWQFNNghtDQ/Biw=
|
||||
cloud.google.com/go/longrunning v0.6.7 h1:IGtfDWHhQCgCjwQjV9iiLnUta9LBCo8R9QmAFsS/PrE=
|
||||
cloud.google.com/go/longrunning v0.6.7/go.mod h1:EAFV3IZAKmM56TyiE6VAP3VoTzhZzySwI/YI1s/nRsY=
|
||||
github.com/AdguardTeam/golibs v0.34.0 h1:JQK024DkTYxE7vsPVsYsoyDHW/53Nun7OYb9qscniK8=
|
||||
github.com/AdguardTeam/golibs v0.34.0/go.mod h1:K4C2EbfSEM1zY5YXoti9SfbTAHN/kIX97LpDtCwORrM=
|
||||
github.com/AdguardTeam/urlfilter v0.21.0 h1:ThIxiP7yoaXt8JTEroGQeU5ftQSoFpUq+t1L+TIx2pA=
|
||||
github.com/AdguardTeam/urlfilter v0.21.0/go.mod h1:xoZ3AF5qpE9ngbbeSShY9hgVeyHtm9MdH5xH1u714Wg=
|
||||
github.com/AdguardTeam/golibs v0.34.1 h1:RyBpZiXnJqlO3T+xjWldlxsEZDelmaFfKvXiJHDZZFQ=
|
||||
github.com/AdguardTeam/golibs v0.34.1/go.mod h1:K4C2EbfSEM1zY5YXoti9SfbTAHN/kIX97LpDtCwORrM=
|
||||
github.com/AdguardTeam/urlfilter v0.22.0 h1:ybOz3FywbpGDGC+8gFFkM1LMUOSosY7CWSBXIYXnG1U=
|
||||
github.com/AdguardTeam/urlfilter v0.22.0/go.mod h1:q0lWKapXlYTA4TUWUM1YDwU6Q0PKvQEokztcvRV2OW0=
|
||||
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
|
||||
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
github.com/Masterminds/semver/v3 v3.3.1 h1:QtNSWtVZ3nBfk8mAOu/B6v7FMJ+NHTIgUPi7rj+4nv4=
|
||||
@@ -57,8 +57,6 @@ github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI=
|
||||
github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||
@@ -90,10 +88,14 @@ github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA=
|
||||
github.com/googleapis/gax-go/v2 v2.15.0 h1:SyjDc1mGgZU5LncH8gimWo9lW1DtIfPibOG81vgd/bo=
|
||||
github.com/googleapis/gax-go/v2 v2.15.0/go.mod h1:zVVkkxAQHa1RQpg9z2AUCMnKhi0Qld9rcmyfL1OZhoc=
|
||||
github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0=
|
||||
github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w=
|
||||
github.com/gookit/assert v0.1.1 h1:lh3GcawXe/p+cU7ESTZ5Ui3Sm/x8JWpIis4/1aF0mY0=
|
||||
github.com/gookit/assert v0.1.1/go.mod h1:jS5bmIVQZTIwk42uXl4lyj4iaaxx32tqH16CFj0VX2E=
|
||||
github.com/gookit/color v1.6.0 h1:JjJXBTk1ETNyqyilJhkTXJYYigHG24TM9Xa2M1xAhRA=
|
||||
github.com/gookit/color v1.6.0/go.mod h1:9ACFc7/1IpHGBW8RwuDm/0YEnhg3dwwXpoMsmtyHfjs=
|
||||
github.com/gordonklaus/ineffassign v0.2.0 h1:Uths4KnmwxNJNzq87fwQQDDnbNb7De00VOk9Nu0TySs=
|
||||
github.com/gordonklaus/ineffassign v0.2.0/go.mod h1:TIpymnagPSexySzs7F9FnO1XFTy8IT3a59vmZp5Y9Lw=
|
||||
github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc h1:GN2Lv3MGO7AS6PrRoT6yV5+wkrOpcszoIsO4+4ds248=
|
||||
github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc/go.mod h1:+JKpmjMGhpgPL+rXZ5nsZieVzvarn86asRlBg4uNGnk=
|
||||
github.com/jstemmer/go-junit-report/v2 v2.1.0 h1:X3+hPYlSczH9IMIpSC9CQSZA0L+BipYafciZUWHEmsc=
|
||||
github.com/jstemmer/go-junit-report/v2 v2.1.0/go.mod h1:mgHVr7VUo5Tn8OLVr1cKnLuEy0M92wdRntM99h7RkgQ=
|
||||
github.com/kamstrup/intmap v0.5.1 h1:ENGAowczZA+PJPYYlreoqJvWgQVtAmX1l899WfYFVK0=
|
||||
@@ -108,8 +110,6 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
|
||||
github.com/miekg/dns v1.1.68 h1:jsSRkNozw7G/mnmXULynzMNIsgY2dHC8LO6U6Ij2JEA=
|
||||
github.com/miekg/dns v1.1.68/go.mod h1:fujopn7TB3Pu3JM69XaawiU0wqjpL9/8xGop5UrTPps=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
@@ -130,14 +130,12 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/prometheus/client_golang v1.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc=
|
||||
github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE=
|
||||
github.com/prometheus/client_golang v1.23.1 h1:w6gXMLQGgd0jXXlote9lRHMe0nG01EbnJT+C0EJru2Y=
|
||||
github.com/prometheus/client_golang v1.23.1/go.mod h1:br8j//v2eg2K5Vvna5klK8Ku5pcU5r4ll73v6ik5dIQ=
|
||||
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
||||
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||
github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE=
|
||||
github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8=
|
||||
github.com/prometheus/common v0.66.0 h1:K/rJPHrG3+AoQs50r2+0t7zMnMzek2Vbv31OFVsMeVY=
|
||||
github.com/prometheus/common v0.66.0/go.mod h1:Ux6NtV1B4LatamKE63tJBntoxD++xmtI/lK0VtEplN4=
|
||||
github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0=
|
||||
github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw=
|
||||
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
|
||||
@@ -150,74 +148,64 @@ github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0t
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/securego/gosec/v2 v2.22.8 h1:3NMpmfXO8wAVFZPNsd3EscOTa32Jyo6FLLlW53bexMI=
|
||||
github.com/securego/gosec/v2 v2.22.8/go.mod h1:ZAw8K2ikuH9qDlfdV87JmNghnVfKB1XC7+TVzk6Utto=
|
||||
github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=
|
||||
github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk=
|
||||
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
|
||||
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
|
||||
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
|
||||
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
|
||||
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
|
||||
github.com/uudashr/gocognit v1.2.0 h1:3BU9aMr1xbhPlvJLSydKwdLN3tEUUrzPSSM8S4hDYRA=
|
||||
github.com/uudashr/gocognit v1.2.0/go.mod h1:k/DdKPI6XBZO1q7HgoV2juESI2/Ofj9AcHPZhBBdrTU=
|
||||
github.com/viktordanov/golang-lru v0.5.6 h1:wEyMgglEo5IZ7Maxeh8E2jCPskpQnt6FJAYl1/TJ6ac=
|
||||
github.com/viktordanov/golang-lru v0.5.6/go.mod h1:R91CBCcMhp6TYUy8NHP/PJ09sk5BTDwb8KMO21CELes=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
||||
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.62.0 h1:rbRJ8BBoVMsQShESYZ0FkvcITu8X8QNwJogcLUmDNNw=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.62.0/go.mod h1:ru6KHrNtNHxM4nD/vd6QrLVWgKhxPYgblq4VAtNawTQ=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0 h1:Hf9xI/XLML9ElpiHVDNwvqI0hIFlzV8dgIr35kV1kRU=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0/go.mod h1:NfchwuyNoMcZ5MLHwPrODwUF1HWCXWrL31s8gSAdIKY=
|
||||
go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ=
|
||||
go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I=
|
||||
go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE=
|
||||
go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E=
|
||||
go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI=
|
||||
go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps=
|
||||
go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=
|
||||
go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
|
||||
go.opentelemetry.io/auto/sdk v1.2.0 h1:YpRtUFjvhSymycLS2T81lT6IGhcUP+LUPtv0iv1N8bM=
|
||||
go.opentelemetry.io/auto/sdk v1.2.0/go.mod h1:1deq2zL7rwjwC8mR7XgY2N+tlIl6pjmEUoLDENMEzwk=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 h1:YH4g8lQroajqUwWbq/tr2QX1JFmEXaDLgG+ew9bLMWo=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0/go.mod h1:fvPi2qXDqFs8M4B4fmJhE92TyQs9Ydjlg3RvfUp+NbQ=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg=
|
||||
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
|
||||
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
|
||||
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
|
||||
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
|
||||
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
|
||||
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
|
||||
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
|
||||
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
|
||||
go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
|
||||
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
|
||||
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
|
||||
golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
|
||||
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
|
||||
golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b h1:DXr+pvt3nC887026GRP39Ej11UATqWDmWuS99x26cD0=
|
||||
golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b/go.mod h1:4QTo5u+SEIbbKW1RacMZq1YEfOBqeXa19JeshGi+zc4=
|
||||
golang.org/x/exp/typeparams v0.0.0-20250819193227-8b4c13bb791b h1:GU1ttDuJS89SePnuEsEuLj7dMMFP2JkGsDV1Z51iDXo=
|
||||
golang.org/x/exp/typeparams v0.0.0-20250819193227-8b4c13bb791b/go.mod h1:4Mzdyp/6jzw9auFDJ3OMF5qksa7UvPnzKqTVGcb04ms=
|
||||
golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ=
|
||||
golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc=
|
||||
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
|
||||
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
|
||||
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
|
||||
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
|
||||
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
||||
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
|
||||
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/telemetry v0.0.0-20250822161441-f407b8c191ff h1:ey0rzo8V0Po6WkHMbhGVL6zNkuYDBe9iP5toIRchj9Q=
|
||||
golang.org/x/telemetry v0.0.0-20250822161441-f407b8c191ff/go.mod h1:JIJwPkb04vX0KeIBbQ7epGtgIjA8ihHbsAtW4A/lIQ4=
|
||||
golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4=
|
||||
golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw=
|
||||
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
|
||||
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
|
||||
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
|
||||
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
|
||||
golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
|
||||
golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI=
|
||||
golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8=
|
||||
golang.org/x/exp v0.0.0-20250911091902-df9299821621 h1:2id6c1/gto0kaHYyrixvknJ8tUK/Qs5IsmBtrc+FtgU=
|
||||
golang.org/x/exp v0.0.0-20250911091902-df9299821621/go.mod h1:TwQYMMnGpvZyc+JpB/UAuTNIsVJifOlSkrZkhcvpVUk=
|
||||
golang.org/x/exp/typeparams v0.0.0-20250911091902-df9299821621 h1:Yl4H5w2RV7L/dvSHp2GerziT5K2CORgFINPaMFxWGWw=
|
||||
golang.org/x/exp/typeparams v0.0.0-20250911091902-df9299821621/go.mod h1:4Mzdyp/6jzw9auFDJ3OMF5qksa7UvPnzKqTVGcb04ms=
|
||||
golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U=
|
||||
golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI=
|
||||
golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I=
|
||||
golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
|
||||
golang.org/x/oauth2 v0.31.0 h1:8Fq0yVZLh4j4YA47vHKFTa9Ew5XIrCP8LC6UeNZnLxo=
|
||||
golang.org/x/oauth2 v0.31.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
||||
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
|
||||
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
|
||||
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/telemetry v0.0.0-20250908211612-aef8a434d053 h1:dHQOQddU4YHS5gY33/6klKjq7Gp3WwMyOXGNp5nzRj8=
|
||||
golang.org/x/telemetry v0.0.0-20250908211612-aef8a434d053/go.mod h1:+nZKN+XVh4LCiA9DV3ywrzN4gumyCnKjau3NGb9SGoE=
|
||||
golang.org/x/term v0.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ=
|
||||
golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA=
|
||||
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
|
||||
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
|
||||
golang.org/x/time v0.13.0 h1:eUlYslOIt32DgYD6utsuUeHs4d7AsEYLuIAdg7FlYgI=
|
||||
golang.org/x/time v0.13.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
|
||||
golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE=
|
||||
golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w=
|
||||
golang.org/x/tools/go/expect v0.1.1-deprecated h1:jpBZDwmgPhXsKZC6WhL20P4b/wmnpsEAGHaNy0n/rJM=
|
||||
golang.org/x/tools/go/expect v0.1.1-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY=
|
||||
golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated h1:1h2MnaIAIXISqTFKdENegdpAgUXz6NrPEsbIeWaBRvM=
|
||||
@@ -226,20 +214,20 @@ golang.org/x/vuln v1.1.4 h1:Ju8QsuyhX3Hk8ma3CesTbO8vfJD9EvUBgHvkxHBzj0I=
|
||||
golang.org/x/vuln v1.1.4/go.mod h1:F+45wmU18ym/ca5PLTPLsSzr2KppzswxPP603ldA67s=
|
||||
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||
google.golang.org/api v0.248.0 h1:hUotakSkcwGdYUqzCRc5yGYsg4wXxpkKlW5ryVqvC1Y=
|
||||
google.golang.org/api v0.248.0/go.mod h1:yAFUAF56Li7IuIQbTFoLwXTCI6XCFKueOlS7S9e4F9k=
|
||||
google.golang.org/genproto v0.0.0-20250603155806-513f23925822 h1:rHWScKit0gvAPuOnu87KpaYtjK5zBMLcULh7gxkCXu4=
|
||||
google.golang.org/genproto v0.0.0-20250603155806-513f23925822/go.mod h1:HubltRL7rMh0LfnQPkMH4NPDFEWp0jw3vixw7jEM53s=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250826171959-ef028d996bc1 h1:APHvLLYBhtZvsbnpkfknDZ7NyH4z5+ub/I0u8L3Oz6g=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250826171959-ef028d996bc1/go.mod h1:xUjFWUnWDpZ/C0Gu0qloASKFb6f8/QXiiXhSPFsD668=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250826171959-ef028d996bc1 h1:pmJpJEvT846VzausCQ5d7KreSROcDqmO388w5YbnltA=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250826171959-ef028d996bc1/go.mod h1:GmFNa4BdJZ2a8G+wCe9Bg3wwThLrJun751XstdJt5Og=
|
||||
google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4=
|
||||
google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ=
|
||||
google.golang.org/api v0.249.0 h1:0VrsWAKzIZi058aeq+I86uIXbNhm9GxSHpbmZ92a38w=
|
||||
google.golang.org/api v0.249.0/go.mod h1:dGk9qyI0UYPwO/cjt2q06LG/EhUpwZGdAbYF14wHHrQ=
|
||||
google.golang.org/genproto v0.0.0-20250908214217-97024824d090 h1:ywCL7vA2n3vVHyf+bx1ZV/knaTPRI8GIeKY0MEhEeOc=
|
||||
google.golang.org/genproto v0.0.0-20250908214217-97024824d090/go.mod h1:zwJI9HzbJJlw2KXy0wX+lmT2JuZoaKK9JC4ppqmxxjk=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250908214217-97024824d090 h1:d8Nakh1G+ur7+P3GcMjpRDEkoLUcLW2iU92XVqR+XMQ=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250908214217-97024824d090/go.mod h1:U8EXRNSd8sUYyDfs/It7KVWodQr+Hf9xtxyxWudSwEw=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090 h1:/OQuEa4YWtDt7uQWHd3q3sUMb+QOLQUg1xa8CEsRv5w=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090/go.mod h1:GmFNa4BdJZ2a8G+wCe9Bg3wwThLrJun751XstdJt5Og=
|
||||
google.golang.org/grpc v1.75.1 h1:/ODCNEuf9VghjgO3rqLcfg8fiOP0nSluljWFlDxELLI=
|
||||
google.golang.org/grpc v1.75.1/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ=
|
||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1 h1:F29+wU6Ee6qgu9TddPgooOdaqsxTMunOoj8KA5yuS5A=
|
||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1/go.mod h1:5KF+wpkbTSbGcR9zteSqZV6fqFOWBl4Yde8En8MryZA=
|
||||
google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc=
|
||||
google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
|
||||
google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw=
|
||||
google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
@@ -251,8 +239,8 @@ honnef.co/go/tools v0.6.1 h1:R094WgE8K4JirYjBaOpz/AvTyUu/3wbmAoskKN/pxTI=
|
||||
honnef.co/go/tools v0.6.1/go.mod h1:3puzxxljPCe8RGJX7BIy1plGbxEOZni5mR2aXe3/uk4=
|
||||
mvdan.cc/editorconfig v0.3.0 h1:D1D2wLYEYGpawWT5SpM5pRivgEgXjtEXwC9MWhEY0gQ=
|
||||
mvdan.cc/editorconfig v0.3.0/go.mod h1:NcJHuDtNOTEJ6251indKiWuzK6+VcrMuLzGMLKBFupQ=
|
||||
mvdan.cc/gofumpt v0.8.0 h1:nZUCeC2ViFaerTcYKstMmfysj6uhQrA2vJe+2vwGU6k=
|
||||
mvdan.cc/gofumpt v0.8.0/go.mod h1:vEYnSzyGPmjvFkqJWtXkh79UwPWP9/HMxQdGEXZHjpg=
|
||||
mvdan.cc/gofumpt v0.9.1 h1:p5YT2NfFWsYyTieYgwcQ8aKV3xRvFH4uuN/zB2gBbMQ=
|
||||
mvdan.cc/gofumpt v0.9.1/go.mod h1:3xYtNemnKiXaTh6R4VtlqDATFwBbdXI8lJvH/4qk7mw=
|
||||
mvdan.cc/sh/v3 v3.12.0 h1:ejKUR7ONP5bb+UGHGEG/k9V5+pRVIyD+LsZz7o8KHrI=
|
||||
mvdan.cc/sh/v3 v3.12.0/go.mod h1:Se6Cj17eYSn+sNooLZiEUnNNmNxg0imoYlTu4CyaGyg=
|
||||
mvdan.cc/unparam v0.0.0-20250301125049-0df0534333a4 h1:WjUu4yQoT5BHT1w8Zu56SP8367OuBV5jvo+4Ulppyf8=
|
||||
|
||||
121
go.work.sum
121
go.work.sum
@@ -11,6 +11,7 @@ cel.dev/expr v0.20.0 h1:OunBvVCfvpWlt4dN7zg3FM6TDkzOePe1+foGJ9AXeeI=
|
||||
cel.dev/expr v0.20.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
|
||||
cel.dev/expr v0.23.0 h1:wUb94w6OYQS4uXraxo9U+wUAs9jT47Xvl4iPgAwM2ss=
|
||||
cel.dev/expr v0.23.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw=
|
||||
cel.dev/expr v0.24.0 h1:56OvJKSH3hDGL0ml5uSxZmz3/3Pq4tJ+fb1unVLAFcY=
|
||||
cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw=
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
@@ -31,21 +32,37 @@ cloud.google.com/go v0.60.0/go.mod h1:yw2G51M9IfRboUH61Us8GqCeF1PzPblB823Mn2q2eA
|
||||
cloud.google.com/go v0.65.0 h1:Dg9iHVQfrhq82rUNu9ZxUDrJLaxFUe/HlCVaLyRruq8=
|
||||
cloud.google.com/go v0.112.1/go.mod h1:+Vbu+Y1UU+I1rjmzeMOb/8RfkKJK2Gyxi1X6jJCZLo4=
|
||||
cloud.google.com/go/accessapproval v1.7.5/go.mod h1:g88i1ok5dvQ9XJsxpUInWWvUBrIZhyPDPbk4T01OoJ0=
|
||||
cloud.google.com/go/accessapproval v1.8.7/go.mod h1:BFvZOW4GJjJnl6aA/YDEg0TGViFHyusa/bMdcVFmh8A=
|
||||
cloud.google.com/go/accesscontextmanager v1.8.5/go.mod h1:TInEhcZ7V9jptGNqN3EzZ5XMhT6ijWxTGjzyETwmL0Q=
|
||||
cloud.google.com/go/accesscontextmanager v1.9.6/go.mod h1:884XHwy1AQpCX5Cj2VqYse77gfLaq9f8emE2bYriilk=
|
||||
cloud.google.com/go/aiplatform v1.60.0/go.mod h1:eTlGuHOahHprZw3Hio5VKmtThIOak5/qy6pzdsqcQnM=
|
||||
cloud.google.com/go/aiplatform v1.100.0/go.mod h1:oZUOTz6+cMt9eVNe62CXPfIQQQ+QjR4rW3GBGD9r6Fg=
|
||||
cloud.google.com/go/analytics v0.23.0/go.mod h1:YPd7Bvik3WS95KBok2gPXDqQPHy08TsCQG6CdUCb+u0=
|
||||
cloud.google.com/go/analytics v0.30.0/go.mod h1:dneJtsGmmK6EkEPg59vRlncKFWt3xzmKNOc9aKXCTrI=
|
||||
cloud.google.com/go/apigateway v1.6.5/go.mod h1:6wCwvYRckRQogyDDltpANi3zsCDl6kWi0b4Je+w2UiI=
|
||||
cloud.google.com/go/apigateway v1.7.7/go.mod h1:j1bCmrUK1BzVHpiIyTApxB7cRyhivKzltqLmp6j6i7U=
|
||||
cloud.google.com/go/apigeeconnect v1.6.5/go.mod h1:MEKm3AiT7s11PqTfKE3KZluZA9O91FNysvd3E6SJ6Ow=
|
||||
cloud.google.com/go/apigeeconnect v1.7.7/go.mod h1:ftGK3nca0JePiVLl0A6alaMjKdOc5C+sAkFMyH2RH8U=
|
||||
cloud.google.com/go/apigeeregistry v0.8.3/go.mod h1:aInOWnqF4yMQx8kTjDqHNXjZGh/mxeNlAf52YqtASUs=
|
||||
cloud.google.com/go/apigeeregistry v0.9.6/go.mod h1:AFEepJBKPtGDfgabG2HWaLH453VVWWFFs3P4W00jbPs=
|
||||
cloud.google.com/go/appengine v1.8.5/go.mod h1:uHBgNoGLTS5di7BvU25NFDuKa82v0qQLjyMJLuPQrVo=
|
||||
cloud.google.com/go/appengine v1.9.7/go.mod h1:y1XpGVeAhbsNzHida79cHbr3pFRsym0ob8xnC8yphbo=
|
||||
cloud.google.com/go/area120 v0.8.5/go.mod h1:BcoFCbDLZjsfe4EkCnEq1LKvHSK0Ew/zk5UFu6GMyA0=
|
||||
cloud.google.com/go/area120 v0.9.7/go.mod h1:5nJ0yksmjOMfc4Zpk+okWfJ3A1004FvB82rfia+ZLaY=
|
||||
cloud.google.com/go/artifactregistry v1.14.7/go.mod h1:0AUKhzWQzfmeTvT4SjfI4zjot72EMfrkvL9g9aRjnnM=
|
||||
cloud.google.com/go/artifactregistry v1.17.1/go.mod h1:06gLv5QwQPWtaudI2fWO37gfwwRUHwxm3gA8Fe568Hc=
|
||||
cloud.google.com/go/asset v1.17.2/go.mod h1:SVbzde67ehddSoKf5uebOD1sYw8Ab/jD/9EIeWg99q4=
|
||||
cloud.google.com/go/asset v1.21.1/go.mod h1:7AzY1GCC+s1O73yzLM1IpHFLHz3ws2OigmCpOQHwebk=
|
||||
cloud.google.com/go/assuredworkloads v1.11.5/go.mod h1:FKJ3g3ZvkL2D7qtqIGnDufFkHxwIpNM9vtmhvt+6wqk=
|
||||
cloud.google.com/go/assuredworkloads v1.12.6/go.mod h1:QyZHd7nH08fmZ+G4ElihV1zoZ7H0FQCpgS0YWtwjCKo=
|
||||
cloud.google.com/go/automl v1.13.5/go.mod h1:MDw3vLem3yh+SvmSgeYUmUKqyls6NzSumDm9OJ3xJ1Y=
|
||||
cloud.google.com/go/automl v1.14.7/go.mod h1:8a4XbIH5pdvrReOU72oB+H3pOw2JBxo9XTk39oljObE=
|
||||
cloud.google.com/go/baremetalsolution v1.2.4/go.mod h1:BHCmxgpevw9IEryE99HbYEfxXkAEA3hkMJbYYsHtIuY=
|
||||
cloud.google.com/go/baremetalsolution v1.3.6/go.mod h1:7/CS0LzpLccRGO0HL3q2Rofxas2JwjREKut414sE9iM=
|
||||
cloud.google.com/go/batch v1.8.0/go.mod h1:k8V7f6VE2Suc0zUM4WtoibNrA6D3dqBpB+++e3vSGYc=
|
||||
cloud.google.com/go/batch v1.12.2/go.mod h1:tbnuTN/Iw59/n1yjAYKV2aZUjvMM2VJqAgvUgft6UEU=
|
||||
cloud.google.com/go/beyondcorp v1.0.4/go.mod h1:Gx8/Rk2MxrvWfn4WIhHIG1NV7IBfg14pTKv1+EArVcc=
|
||||
cloud.google.com/go/beyondcorp v1.1.6/go.mod h1:V1PigSWPGh5L/vRRmyutfnjAbkxLI2aWqJDdxKbwvsQ=
|
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||
@@ -54,13 +71,22 @@ cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4g
|
||||
cloud.google.com/go/bigquery v1.8.0 h1:PQcPefKFdaIzjQFbiyOgAqyx8q5djaE7x9Sqe712DPA=
|
||||
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
|
||||
cloud.google.com/go/bigquery v1.59.1/go.mod h1:VP1UJYgevyTwsV7desjzNzDND5p6hZB+Z8gZJN1GQUc=
|
||||
cloud.google.com/go/bigquery v1.70.0/go.mod h1:6lEAkgTJN+H2JcaX1eKiuEHTKyqBaJq5U3SpLGbSvwI=
|
||||
cloud.google.com/go/bigtable v1.39.0/go.mod h1:zgL2Vxux9Bx+TcARDJDUxVyE+BCUfP2u4Zm9qeHF+g0=
|
||||
cloud.google.com/go/billing v1.18.2/go.mod h1:PPIwVsOOQ7xzbADCwNe8nvK776QpfrOAUkvKjCUcpSE=
|
||||
cloud.google.com/go/billing v1.20.4/go.mod h1:hBm7iUmGKGCnBm6Wp439YgEdt+OnefEq/Ib9SlJYxIU=
|
||||
cloud.google.com/go/binaryauthorization v1.8.1/go.mod h1:1HVRyBerREA/nhI7yLang4Zn7vfNVA3okoAR9qYQJAQ=
|
||||
cloud.google.com/go/binaryauthorization v1.9.5/go.mod h1:CV5GkS2eiY461Bzv+OH3r5/AsuB6zny+MruRju3ccB8=
|
||||
cloud.google.com/go/certificatemanager v1.7.5/go.mod h1:uX+v7kWqy0Y3NG/ZhNvffh0kuqkKZIXdvlZRO7z0VtM=
|
||||
cloud.google.com/go/certificatemanager v1.9.5/go.mod h1:kn7gxT/80oVGhjL8rurMUYD36AOimgtzSBPadtAeffs=
|
||||
cloud.google.com/go/channel v1.17.5/go.mod h1:FlpaOSINDAXgEext0KMaBq/vwpLMkkPAw9b2mApQeHc=
|
||||
cloud.google.com/go/channel v1.20.0/go.mod h1:nBR1Lz+/1TjSA16HTllvW9Y+QULODj3o3jEKrNNeOp4=
|
||||
cloud.google.com/go/cloudbuild v1.15.1/go.mod h1:gIofXZSu+XD2Uy+qkOrGKEx45zd7s28u/k8f99qKals=
|
||||
cloud.google.com/go/cloudbuild v1.23.0/go.mod h1:BkxnZUIHUHkl+oNpEbwc7n9id4pZRDQRVKIa6sDCuJI=
|
||||
cloud.google.com/go/clouddms v1.7.4/go.mod h1:RdrVqoFG9RWI5AvZ81SxJ/xvxPdtcRhFotwdE79DieY=
|
||||
cloud.google.com/go/clouddms v1.8.7/go.mod h1:DhWLd3nzHP8GoHkA6hOhso0R9Iou+IGggNqlVaq/KZ4=
|
||||
cloud.google.com/go/cloudtasks v1.12.6/go.mod h1:b7c7fe4+TJsFZfDyzO51F7cjq7HLUlRi/KZQLQjDsaY=
|
||||
cloud.google.com/go/cloudtasks v1.13.6/go.mod h1:/IDaQqGKMixD+ayM43CfsvWF2k36GeomEuy9gL4gLmU=
|
||||
cloud.google.com/go/compute v1.20.1/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM=
|
||||
cloud.google.com/go/compute v1.21.0 h1:JNBsyXVoOoNJtTQcnEY5uYpZIbeCTYIeDe0Xh1bySMk=
|
||||
cloud.google.com/go/compute v1.21.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM=
|
||||
@@ -70,6 +96,8 @@ cloud.google.com/go/compute v1.24.0 h1:phWcR2eWzRJaL/kOiJwfFsPs4BaKq1j6vnpZrc1Yl
|
||||
cloud.google.com/go/compute v1.24.0/go.mod h1:kw1/T+h/+tK2LJK0wiPPx1intgdAM3j/g3hFDlscY40=
|
||||
cloud.google.com/go/compute v1.25.1 h1:ZRpHJedLtTpKgr3RV1Fx23NuaAEN1Zfx9hw1u4aJdjU=
|
||||
cloud.google.com/go/compute v1.25.1/go.mod h1:oopOIR53ly6viBYxaDhBfJwzUAxf1zE//uf3IB011ls=
|
||||
cloud.google.com/go/compute v1.45.0 h1:bcq5kVYiC6O62afoM/rh40jnLpLUw6GP1O+8a8NiI+Y=
|
||||
cloud.google.com/go/compute v1.45.0/go.mod h1:wQjjP1m9aYkZAPbYxilUyJ0RSAAb+/PFNGHBVLzDiRM=
|
||||
cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
|
||||
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
|
||||
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
|
||||
@@ -80,38 +108,68 @@ cloud.google.com/go/compute/metadata v0.5.2 h1:UxK4uu/Tn+I3p2dYWTfiX4wva7aYlKixA
|
||||
cloud.google.com/go/compute/metadata v0.5.2/go.mod h1:C66sj2AluDcIqakBq/M8lw8/ybHgOZqin2obFxa/E5k=
|
||||
cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg=
|
||||
cloud.google.com/go/contactcenterinsights v1.13.0/go.mod h1:ieq5d5EtHsu8vhe2y3amtZ+BE+AQwX5qAy7cpo0POsI=
|
||||
cloud.google.com/go/contactcenterinsights v1.17.3/go.mod h1:7Uu2CpxS3f6XxhRdlEzYAkrChpR5P5QfcdGAFEdHOG8=
|
||||
cloud.google.com/go/container v1.31.0/go.mod h1:7yABn5s3Iv3lmw7oMmyGbeV6tQj86njcTijkkGuvdZA=
|
||||
cloud.google.com/go/container v1.44.0/go.mod h1:tVK2o4UZUTkg9WpBcgj4qRzwGA1dSFdWA3mil3YkLIQ=
|
||||
cloud.google.com/go/containeranalysis v0.11.4/go.mod h1:cVZT7rXYBS9NG1rhQbWL9pWbXCKHWJPYraE8/FTSYPE=
|
||||
cloud.google.com/go/containeranalysis v0.14.1/go.mod h1:28e+tlZgauWGHmEbnI5UfIsjMmrkoR1tFN0K2i71jBI=
|
||||
cloud.google.com/go/datacatalog v1.19.3/go.mod h1:ra8V3UAsciBpJKQ+z9Whkxzxv7jmQg1hfODr3N3YPJ4=
|
||||
cloud.google.com/go/datacatalog v1.26.0/go.mod h1:bLN2HLBAwB3kLTFT5ZKLHVPj/weNz6bR0c7nYp0LE14=
|
||||
cloud.google.com/go/dataflow v0.9.5/go.mod h1:udl6oi8pfUHnL0z6UN9Lf9chGqzDMVqcYTcZ1aPnCZQ=
|
||||
cloud.google.com/go/dataflow v0.11.0/go.mod h1:gNHC9fUjlV9miu0hd4oQaXibIuVYTQvZhMdPievKsPk=
|
||||
cloud.google.com/go/dataform v0.9.2/go.mod h1:S8cQUwPNWXo7m/g3DhWHsLBoufRNn9EgFrMgne2j7cI=
|
||||
cloud.google.com/go/dataform v0.12.0/go.mod h1:PuDIEY0lSVuPrZqcFji1fmr5RRvz3DGz4YP/cONc8g4=
|
||||
cloud.google.com/go/datafusion v1.7.5/go.mod h1:bYH53Oa5UiqahfbNK9YuYKteeD4RbQSNMx7JF7peGHc=
|
||||
cloud.google.com/go/datafusion v1.8.6/go.mod h1:fCyKJF2zUKC+O3hc2F9ja5EUCAbT4zcH692z8HiFZFw=
|
||||
cloud.google.com/go/datalabeling v0.8.5/go.mod h1:IABB2lxQnkdUbMnQaOl2prCOfms20mcPxDBm36lps+s=
|
||||
cloud.google.com/go/datalabeling v0.9.6/go.mod h1:n7o4x0vtPensZOoFwFa4UfZgkSZm8Qs0Pg/T3kQjXSM=
|
||||
cloud.google.com/go/dataplex v1.14.2/go.mod h1:0oGOSFlEKef1cQeAHXy4GZPB/Ife0fz/PxBf+ZymA2U=
|
||||
cloud.google.com/go/dataplex v1.26.0/go.mod h1:12R9nlLUzxOscbb2HgoYnkGNibmv4sXEVMXxrdw2a90=
|
||||
cloud.google.com/go/dataproc/v2 v2.4.0/go.mod h1:3B1Ht2aRB8VZIteGxQS/iNSJGzt9+CA0WGnDVMEm7Z4=
|
||||
cloud.google.com/go/dataproc/v2 v2.14.0/go.mod h1:AqfdObN5w70H7meRXZOEY52WMK4yMrLtiOd9kROahSM=
|
||||
cloud.google.com/go/dataqna v0.8.5/go.mod h1:vgihg1mz6n7pb5q2YJF7KlXve6tCglInd6XO0JGOlWM=
|
||||
cloud.google.com/go/dataqna v0.9.7/go.mod h1:4ac3r7zm7Wqm8NAc8sDIDM0v7Dz7d1e/1Ka1yMFanUM=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/datastore v1.1.0 h1:/May9ojXjRkPBNVrq+oWLqmWCkr4OU5uRY29bu0mRyQ=
|
||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||
cloud.google.com/go/datastore v1.15.0/go.mod h1:GAeStMBIt9bPS7jMJA85kgkpsMkvseWWXiaHya9Jes8=
|
||||
cloud.google.com/go/datastore v1.20.0/go.mod h1:uFo3e+aEpRfHgtp5pp0+6M0o147KoPaYNaPAKpfh8Ew=
|
||||
cloud.google.com/go/datastream v1.10.4/go.mod h1:7kRxPdxZxhPg3MFeCSulmAJnil8NJGGvSNdn4p1sRZo=
|
||||
cloud.google.com/go/datastream v1.15.0/go.mod h1:eA4ZWd7e21YtG6Yx5SWSwRV5U9wbAb9rKHTcb0x20cQ=
|
||||
cloud.google.com/go/deploy v1.17.1/go.mod h1:SXQyfsXrk0fBmgBHRzBjQbZhMfKZ3hMQBw5ym7MN/50=
|
||||
cloud.google.com/go/deploy v1.27.2/go.mod h1:4NHWE7ENry2A4O1i/4iAPfXHnJCZ01xckAKpZQwhg1M=
|
||||
cloud.google.com/go/dialogflow v1.49.0/go.mod h1:dhVrXKETtdPlpPhE7+2/k4Z8FRNUp6kMV3EW3oz/fe0=
|
||||
cloud.google.com/go/dialogflow v1.69.0/go.mod h1:+2drAzrguQ8vltf6qn6foBPHrT/fFa1S3FQ40byV2WU=
|
||||
cloud.google.com/go/dlp v1.11.2/go.mod h1:9Czi+8Y/FegpWzgSfkRlyz+jwW6Te9Rv26P3UfU/h/w=
|
||||
cloud.google.com/go/dlp v1.24.0/go.mod h1:y6EsWNgMDye72NtqjGHYZjN/wUDnO9CUygLV8iuFeW0=
|
||||
cloud.google.com/go/documentai v1.25.0/go.mod h1:ftLnzw5VcXkLItp6pw1mFic91tMRyfv6hHEY5br4KzY=
|
||||
cloud.google.com/go/documentai v1.38.0/go.mod h1:zNhZmHJ4/VbvhA0h2U5JRbOHm2BTMq4FxJ276mYAohk=
|
||||
cloud.google.com/go/domains v0.9.5/go.mod h1:dBzlxgepazdFhvG7u23XMhmMKBjrkoUNaw0A8AQB55Y=
|
||||
cloud.google.com/go/domains v0.10.6/go.mod h1:3xzG+hASKsVBA8dOPc4cIaoV3OdBHl1qgUpAvXK7pGY=
|
||||
cloud.google.com/go/edgecontainer v1.1.5/go.mod h1:rgcjrba3DEDEQAidT4yuzaKWTbkTI5zAMu3yy6ZWS0M=
|
||||
cloud.google.com/go/edgecontainer v1.4.3/go.mod h1:q9Ojw2ox0uhAvFisnfPRAXFTB1nfRIOIXVWzdXMZLcE=
|
||||
cloud.google.com/go/errorreporting v0.3.0/go.mod h1:xsP2yaAp+OAW4OIm60An2bbLpqIhKXdWR/tawvl7QzU=
|
||||
cloud.google.com/go/errorreporting v0.3.2/go.mod h1:s5kjs5r3l6A8UUyIsgvAhGq6tkqyBCUss0FRpsoVTww=
|
||||
cloud.google.com/go/essentialcontacts v1.6.6/go.mod h1:XbqHJGaiH0v2UvtuucfOzFXN+rpL/aU5BCZLn4DYl1Q=
|
||||
cloud.google.com/go/essentialcontacts v1.7.6/go.mod h1:/Ycn2egr4+XfmAfxpLYsJeJlVf9MVnq9V7OMQr9R4lA=
|
||||
cloud.google.com/go/eventarc v1.13.4/go.mod h1:zV5sFVoAa9orc/52Q+OuYUG9xL2IIZTbbuTHC6JSY8s=
|
||||
cloud.google.com/go/eventarc v1.15.5/go.mod h1:vDCqGqyY7SRiickhEGt1Zhuj81Ya4F/NtwwL3OZNskg=
|
||||
cloud.google.com/go/filestore v1.8.1/go.mod h1:MbN9KcaM47DRTIuLfQhJEsjaocVebNtNQhSLhKCF5GM=
|
||||
cloud.google.com/go/filestore v1.10.2/go.mod h1:w0Pr8uQeSRQfCPRsL0sYKW6NKyooRgixCkV9yyLykR4=
|
||||
cloud.google.com/go/firestore v1.14.0/go.mod h1:96MVaHLsEhbvkBEdZgfN+AS/GIkco1LRpH9Xp9YZfzQ=
|
||||
cloud.google.com/go/firestore v1.18.0/go.mod h1:5ye0v48PhseZBdcl0qbl3uttu7FIEwEYVaWm0UIEOEU=
|
||||
cloud.google.com/go/functions v1.16.0/go.mod h1:nbNpfAG7SG7Duw/o1iZ6ohvL7mc6MapWQVpqtM29n8k=
|
||||
cloud.google.com/go/functions v1.19.6/go.mod h1:0G0RnIlbM4MJEycfbPZlCzSf2lPOjL7toLDwl+r0ZBw=
|
||||
cloud.google.com/go/gkebackup v1.3.5/go.mod h1:KJ77KkNN7Wm1LdMopOelV6OodM01pMuK2/5Zt1t4Tvc=
|
||||
cloud.google.com/go/gkebackup v1.8.0/go.mod h1:FjsjNldDilC9MWKEHExnK3kKJyTDaSdO1vF0QeWSOPU=
|
||||
cloud.google.com/go/gkeconnect v0.8.5/go.mod h1:LC/rS7+CuJ5fgIbXv8tCD/mdfnlAadTaUufgOkmijuk=
|
||||
cloud.google.com/go/gkeconnect v0.12.4/go.mod h1:bvpU9EbBpZnXGo3nqJ1pzbHWIfA9fYqgBMJ1VjxaZdk=
|
||||
cloud.google.com/go/gkehub v0.14.5/go.mod h1:6bzqxM+a+vEH/h8W8ec4OJl4r36laxTs3A/fMNHJ0wA=
|
||||
cloud.google.com/go/gkehub v0.15.6/go.mod h1:sRT0cOPAgI1jUJrS3gzwdYCJ1NEzVVwmnMKEwrS2QaM=
|
||||
cloud.google.com/go/gkemulticloud v1.1.1/go.mod h1:C+a4vcHlWeEIf45IB5FFR5XGjTeYhF83+AYIpTy4i2Q=
|
||||
cloud.google.com/go/gkemulticloud v1.5.3/go.mod h1:KPFf+/RcfvmuScqwS9/2MF5exZAmXSuoSLPuaQ98Xlk=
|
||||
cloud.google.com/go/gsuiteaddons v1.6.5/go.mod h1:Lo4P2IvO8uZ9W+RaC6s1JVxo42vgy+TX5a6hfBZ0ubs=
|
||||
cloud.google.com/go/gsuiteaddons v1.7.7/go.mod h1:zTGmmKG/GEBCONsvMOY2ckDiEsq3FN+lzWGUiXccF9o=
|
||||
cloud.google.com/go/iam v1.1.6/go.mod h1:O0zxdPeGBoFdWW3HWmBxJsk0pfvNM/p/qa82rWOGTwI=
|
||||
cloud.google.com/go/iam v1.4.0/go.mod h1:gMBgqPaERlriaOV0CUl//XUzDhSfXevn4OEUbg6VRs4=
|
||||
cloud.google.com/go/iam v1.4.1 h1:cFC25Nv+u5BkTR/BT1tXdoF2daiVbZ1RLx2eqfQ9RMM=
|
||||
@@ -119,34 +177,58 @@ cloud.google.com/go/iam v1.4.1/go.mod h1:2vUEJpUG3Q9p2UdsyksaKpDzlwOrnMzS30isdRe
|
||||
cloud.google.com/go/iam v1.5.2 h1:qgFRAGEmd8z6dJ/qyEchAuL9jpswyODjA2lS+w234g8=
|
||||
cloud.google.com/go/iam v1.5.2/go.mod h1:SE1vg0N81zQqLzQEwxL2WI6yhetBdbNQuTvIKCSkUHE=
|
||||
cloud.google.com/go/iap v1.9.4/go.mod h1:vO4mSq0xNf/Pu6E5paORLASBwEmphXEjgCFg7aeNu1w=
|
||||
cloud.google.com/go/iap v1.11.2/go.mod h1:Bh99DMUpP5CitL9lK0BC8MYgjjYO4b3FbyhgW1VHJvg=
|
||||
cloud.google.com/go/ids v1.4.5/go.mod h1:p0ZnyzjMWxww6d2DvMGnFwCsSxDJM666Iir1bK1UuBo=
|
||||
cloud.google.com/go/ids v1.5.6/go.mod h1:y3SGLmEf9KiwKsH7OHvYYVNIJAtXybqsD2z8gppsziQ=
|
||||
cloud.google.com/go/iot v1.7.5/go.mod h1:nq3/sqTz3HGaWJi1xNiX7F41ThOzpud67vwk0YsSsqs=
|
||||
cloud.google.com/go/iot v1.8.6/go.mod h1:MThnkiihNkMysWNeNje2Hp0GSOpEq2Wkb/DkBCVYa0U=
|
||||
cloud.google.com/go/kms v1.15.7/go.mod h1:ub54lbsa6tDkUwnu4W7Yt1aAIFLnspgh0kPGToDukeI=
|
||||
cloud.google.com/go/kms v1.22.0/go.mod h1:U7mf8Sva5jpOb4bxYZdtw/9zsbIjrklYwPcvMk34AL8=
|
||||
cloud.google.com/go/language v1.12.3/go.mod h1:evFX9wECX6mksEva8RbRnr/4wi/vKGYnAJrTRXU8+f8=
|
||||
cloud.google.com/go/language v1.14.5/go.mod h1:nl2cyAVjcBct1Hk73tzxuKebk0t2eULFCaruhetdZIA=
|
||||
cloud.google.com/go/lifesciences v0.9.5/go.mod h1:OdBm0n7C0Osh5yZB7j9BXyrMnTRGBJIZonUMxo5CzPw=
|
||||
cloud.google.com/go/lifesciences v0.10.6/go.mod h1:1nnZwaZcBThDujs9wXzECnd1S5d+UiDkPuJWAmhRi7Q=
|
||||
cloud.google.com/go/logging v1.9.0/go.mod h1:1Io0vnZv4onoUnsVUQY3HZ3Igb1nBchky0A0y7BBBhE=
|
||||
cloud.google.com/go/logging v1.13.0/go.mod h1:36CoKh6KA/M0PbhPKMq6/qety2DCAErbhXT62TuXALA=
|
||||
cloud.google.com/go/longrunning v0.5.5/go.mod h1:WV2LAxD8/rg5Z1cNW6FJ/ZpX4E4VnDnoTk0yawPBB7s=
|
||||
cloud.google.com/go/managedidentities v1.6.5/go.mod h1:fkFI2PwwyRQbjLxlm5bQ8SjtObFMW3ChBGNqaMcgZjI=
|
||||
cloud.google.com/go/managedidentities v1.7.6/go.mod h1:pYCWPaI1AvR8Q027Vtp+SFSM/VOVgbjBF4rxp1/z5p4=
|
||||
cloud.google.com/go/maps v1.6.4/go.mod h1:rhjqRy8NWmDJ53saCfsXQ0LKwBHfi6OSh5wkq6BaMhI=
|
||||
cloud.google.com/go/maps v1.23.0/go.mod h1:8tjxLplMV7FEoR9FIwqoY7siDnaOdE7FBWnjaXK/xts=
|
||||
cloud.google.com/go/mediatranslation v0.8.5/go.mod h1:y7kTHYIPCIfgyLbKncgqouXJtLsU+26hZhHEEy80fSs=
|
||||
cloud.google.com/go/mediatranslation v0.9.6/go.mod h1:WS3QmObhRtr2Xu5laJBQSsjnWFPPthsyetlOyT9fJvE=
|
||||
cloud.google.com/go/memcache v1.10.5/go.mod h1:/FcblbNd0FdMsx4natdj+2GWzTq+cjZvMa1I+9QsuMA=
|
||||
cloud.google.com/go/memcache v1.11.6/go.mod h1:ZM6xr1mw3F8TWO+In7eq9rKlJc3jlX2MDt4+4H+/+cc=
|
||||
cloud.google.com/go/metastore v1.13.4/go.mod h1:FMv9bvPInEfX9Ac1cVcRXp8EBBQnBcqH6gz3KvJ9BAE=
|
||||
cloud.google.com/go/metastore v1.14.7/go.mod h1:0dka99KQofeUgdfu+K/Jk1KeT9veWZlxuZdJpZPtuYU=
|
||||
cloud.google.com/go/monitoring v1.18.0/go.mod h1:c92vVBCeq/OB4Ioyo+NbN2U7tlg5ZH41PZcdvfc+Lcg=
|
||||
cloud.google.com/go/monitoring v1.24.0 h1:csSKiCJ+WVRgNkRzzz3BPoGjFhjPY23ZTcaenToJxMM=
|
||||
cloud.google.com/go/monitoring v1.24.0/go.mod h1:Bd1PRK5bmQBQNnuGwHBfUamAV1ys9049oEPHnn4pcsc=
|
||||
cloud.google.com/go/monitoring v1.24.2/go.mod h1:x7yzPWcgDRnPEv3sI+jJGBkwl5qINf+6qY4eq0I9B4U=
|
||||
cloud.google.com/go/networkconnectivity v1.14.4/go.mod h1:PU12q++/IMnDJAB+3r+tJtuCXCfwfN+C6Niyj6ji1Po=
|
||||
cloud.google.com/go/networkconnectivity v1.18.0/go.mod h1:8MFjpAsCqTKUO+U5y9C6iGAsq2KkrfpQ43/XbqSbICc=
|
||||
cloud.google.com/go/networkmanagement v1.9.4/go.mod h1:daWJAl0KTFytFL7ar33I6R/oNBH8eEOX/rBNHrC/8TA=
|
||||
cloud.google.com/go/networkmanagement v1.20.0/go.mod h1:t/GQe1ICzaxeETse/6EPEjmjOr9zGyNImVLlxAX+YB4=
|
||||
cloud.google.com/go/networksecurity v0.9.5/go.mod h1:KNkjH/RsylSGyyZ8wXpue8xpCEK+bTtvof8SBfIhMG8=
|
||||
cloud.google.com/go/networksecurity v0.10.6/go.mod h1:FTZvabFPvK2kR/MRIH3l/OoQ/i53eSix2KA1vhBMJec=
|
||||
cloud.google.com/go/notebooks v1.11.3/go.mod h1:0wQyI2dQC3AZyQqWnRsp+yA+kY4gC7ZIVP4Qg3AQcgo=
|
||||
cloud.google.com/go/notebooks v1.12.6/go.mod h1:3Z4TMEqAKP3pu6DI/U+aEXrNJw9hGZIVbp+l3zw8EuA=
|
||||
cloud.google.com/go/optimization v1.6.3/go.mod h1:8ve3svp3W6NFcAEFr4SfJxrldzhUl4VMUJmhrqVKtYA=
|
||||
cloud.google.com/go/optimization v1.7.6/go.mod h1:4MeQslrSJGv+FY4rg0hnZBR/tBX2awJ1gXYp6jZpsYY=
|
||||
cloud.google.com/go/orchestration v1.8.5/go.mod h1:C1J7HesE96Ba8/hZ71ISTV2UAat0bwN+pi85ky38Yq8=
|
||||
cloud.google.com/go/orchestration v1.11.9/go.mod h1:KKXK67ROQaPt7AxUS1V/iK0Gs8yabn3bzJ1cLHw4XBg=
|
||||
cloud.google.com/go/orgpolicy v1.12.1/go.mod h1:aibX78RDl5pcK3jA8ysDQCFkVxLj3aOQqrbBaUL2V5I=
|
||||
cloud.google.com/go/orgpolicy v1.15.0/go.mod h1:NTQLwgS8N5cJtdfK55tAnMGtvPSsy95JJhESwYHaJVs=
|
||||
cloud.google.com/go/osconfig v1.12.5/go.mod h1:D9QFdxzfjgw3h/+ZaAb5NypM8bhOMqBzgmbhzWViiW8=
|
||||
cloud.google.com/go/osconfig v1.15.0/go.mod h1:0nY8bfGKWJB0Ft5bBKd2zMkjT4Uf0rM3NBFrAGUv1Lk=
|
||||
cloud.google.com/go/oslogin v1.13.1/go.mod h1:vS8Sr/jR7QvPWpCjNqy6LYZr5Zs1e8ZGW/KPn9gmhws=
|
||||
cloud.google.com/go/oslogin v1.14.6/go.mod h1:xEvcRZTkMXHfNSKdZ8adxD6wvRzeyAq3cQX3F3kbMRw=
|
||||
cloud.google.com/go/phishingprotection v0.8.5/go.mod h1:g1smd68F7mF1hgQPuYn3z8HDbNre8L6Z0b7XMYFmX7I=
|
||||
cloud.google.com/go/phishingprotection v0.9.6/go.mod h1:VmuGg03DCI0wRp/FLSvNyjFj+J8V7+uITgHjCD/x4RQ=
|
||||
cloud.google.com/go/policytroubleshooter v1.10.3/go.mod h1:+ZqG3agHT7WPb4EBIRqUv4OyIwRTZvsVDHZ8GlZaoxk=
|
||||
cloud.google.com/go/policytroubleshooter v1.11.6/go.mod h1:jdjYGIveoYolk38Dm2JjS5mPkn8IjVqPsDHccTMu3mY=
|
||||
cloud.google.com/go/privatecatalog v0.9.5/go.mod h1:fVWeBOVe7uj2n3kWRGlUQqR/pOd450J9yZoOECcQqJk=
|
||||
cloud.google.com/go/privatecatalog v0.10.7/go.mod h1:Fo/PF/B6m4A9vUYt0nEF1xd0U6Kk19/Je3eZGrQ6l60=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
||||
@@ -154,24 +236,43 @@ cloud.google.com/go/pubsub v1.3.1 h1:ukjixP1wl0LpnZ6LWtZJ0mX5tBmjp1f8Sqer8Z2OMUU
|
||||
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
|
||||
cloud.google.com/go/pubsub v1.5.0/go.mod h1:ZEwJccE3z93Z2HWvstpri00jOg7oO4UZDtKhwDwqF0w=
|
||||
cloud.google.com/go/pubsub v1.36.1/go.mod h1:iYjCa9EzWOoBiTdd4ps7QoMtMln5NwaZQpK1hbRfBDE=
|
||||
cloud.google.com/go/pubsub v1.50.1/go.mod h1:6YVJv3MzWJUVdvQXG081sFvS0dWQOdnV+oTo++q/xFk=
|
||||
cloud.google.com/go/pubsub/v2 v2.0.0/go.mod h1:0aztFxNzVQIRSZ8vUr79uH2bS3jwLebwK6q1sgEub+E=
|
||||
cloud.google.com/go/pubsublite v1.8.1/go.mod h1:fOLdU4f5xldK4RGJrBMm+J7zMWNj/k4PxwEZXy39QS0=
|
||||
cloud.google.com/go/pubsublite v1.8.2/go.mod h1:4r8GSa9NznExjuLPEJlF1VjOPOpgf3IT6k8x/YgaOPI=
|
||||
cloud.google.com/go/recaptchaenterprise/v2 v2.9.2/go.mod h1:trwwGkfhCmp05Ll5MSJPXY7yvnO0p4v3orGANAFHAuU=
|
||||
cloud.google.com/go/recaptchaenterprise/v2 v2.20.4/go.mod h1:3H8nb8j8N7Ss2eJ+zr+/H7gyorfzcxiDEtVBDvDjwDQ=
|
||||
cloud.google.com/go/recommendationengine v0.8.5/go.mod h1:A38rIXHGFvoPvmy6pZLozr0g59NRNREz4cx7F58HAsQ=
|
||||
cloud.google.com/go/recommendationengine v0.9.6/go.mod h1:nZnjKJu1vvoxbmuRvLB5NwGuh6cDMMQdOLXTnkukUOE=
|
||||
cloud.google.com/go/recommender v1.12.1/go.mod h1:gf95SInWNND5aPas3yjwl0I572dtudMhMIG4ni8nr+0=
|
||||
cloud.google.com/go/recommender v1.13.5/go.mod h1:v7x/fzk38oC62TsN5Qkdpn0eoMBh610UgArJtDIgH/E=
|
||||
cloud.google.com/go/redis v1.14.2/go.mod h1:g0Lu7RRRz46ENdFKQ2EcQZBAJ2PtJHJLuiiRuEXwyQw=
|
||||
cloud.google.com/go/redis v1.18.2/go.mod h1:q6mPRhLiR2uLf584Lcl4tsiRn0xiFlu6fnJLwCORMtY=
|
||||
cloud.google.com/go/resourcemanager v1.9.5/go.mod h1:hep6KjelHA+ToEjOfO3garMKi/CLYwTqeAw7YiEI9x8=
|
||||
cloud.google.com/go/resourcemanager v1.10.6/go.mod h1:VqMoDQ03W4yZmxzLPrB+RuAoVkHDS5tFUUQUhOtnRTg=
|
||||
cloud.google.com/go/resourcesettings v1.6.5/go.mod h1:WBOIWZraXZOGAgoR4ukNj0o0HiSMO62H9RpFi9WjP9I=
|
||||
cloud.google.com/go/resourcesettings v1.8.3/go.mod h1:BzgfXFHIWOOmHe6ZV9+r3OWfpHJgnqXy8jqwx4zTMLw=
|
||||
cloud.google.com/go/retail v1.16.0/go.mod h1:LW7tllVveZo4ReWt68VnldZFWJRzsh9np+01J9dYWzE=
|
||||
cloud.google.com/go/retail v1.24.0/go.mod h1:pvLFfRzTnqGf3yHNnIq4R+A5nfEy56SYE9optVPOuSk=
|
||||
cloud.google.com/go/run v1.3.4/go.mod h1:FGieuZvQ3tj1e9GnzXqrMABSuir38AJg5xhiYq+SF3o=
|
||||
cloud.google.com/go/run v1.12.0/go.mod h1:/APJ89UqgGdIdaD1yaTiSYXozx3fNoqKR/cueDFRueI=
|
||||
cloud.google.com/go/scheduler v1.10.6/go.mod h1:pe2pNCtJ+R01E06XCDOJs1XvAMbv28ZsQEbqknxGOuE=
|
||||
cloud.google.com/go/scheduler v1.11.7/go.mod h1:gqYs8ndLx2M5D0oMJh48aGS630YYvC432tHCnVWN13s=
|
||||
cloud.google.com/go/secretmanager v1.11.5/go.mod h1:eAGv+DaCHkeVyQi0BeXgAHOU0RdrMeZIASKc+S7VqH4=
|
||||
cloud.google.com/go/secretmanager v1.15.0/go.mod h1:1hQSAhKK7FldiYw//wbR/XPfPc08eQ81oBsnRUHEvUc=
|
||||
cloud.google.com/go/security v1.15.5/go.mod h1:KS6X2eG3ynWjqcIX976fuToN5juVkF6Ra6c7MPnldtc=
|
||||
cloud.google.com/go/security v1.19.1/go.mod h1:+T4yyeDXqBYESnCzswqbq/Oip+IYkIrTfRF4UmeT4Bk=
|
||||
cloud.google.com/go/securitycenter v1.24.4/go.mod h1:PSccin+o1EMYKcFQzz9HMMnZ2r9+7jbc+LvPjXhpwcU=
|
||||
cloud.google.com/go/securitycenter v1.37.0/go.mod h1:DdQi6OEzw1rmLtPpqtUx6bqnQq8ZdCVuG9eZRYz2QAE=
|
||||
cloud.google.com/go/servicedirectory v1.11.4/go.mod h1:Bz2T9t+/Ehg6x+Y7Ycq5xiShYLD96NfEsWNHyitj1qM=
|
||||
cloud.google.com/go/servicedirectory v1.12.6/go.mod h1:OojC1KhOMDYC45oyTn3Mup08FY/S0Kj7I58dxUMMTpg=
|
||||
cloud.google.com/go/shell v1.7.5/go.mod h1:hL2++7F47/IfpfTO53KYf1EC+F56k3ThfNEXd4zcuiE=
|
||||
cloud.google.com/go/shell v1.8.6/go.mod h1:GNbTWf1QA/eEtYa+kWSr+ef/XTCDkUzRpV3JPw0LqSk=
|
||||
cloud.google.com/go/spanner v1.7.0/go.mod h1:sd3K2gZ9Fd0vMPLXzeCrF6fq4i63Q7aTLW/lBIfBkIk=
|
||||
cloud.google.com/go/spanner v1.56.0/go.mod h1:DndqtUKQAt3VLuV2Le+9Y3WTnq5cNKrnLb/Piqcj+h0=
|
||||
cloud.google.com/go/spanner v1.85.0/go.mod h1:9zhmtOEoYV06nE4Orbin0dc/ugHzZW9yXuvaM61rpxs=
|
||||
cloud.google.com/go/speech v1.21.1/go.mod h1:E5GHZXYQlkqWQwY5xRSLHw2ci5NMQNG52FfMU1aZrIA=
|
||||
cloud.google.com/go/speech v1.28.0/go.mod h1:hJf6oa+1rzCW/CeDE/qCXedV20B2TXEUje5iaGwW+JI=
|
||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
|
||||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||
@@ -186,22 +287,37 @@ cloud.google.com/go/storage v1.53.0/go.mod h1:7/eO2a/srr9ImZW9k5uufcNahT2+fPb8w5
|
||||
cloud.google.com/go/storage v1.55.0/go.mod h1:ztSmTTwzsdXe5syLVS0YsbFxXuvEmEyZj7v7zChEmuY=
|
||||
cloud.google.com/go/storage v1.56.0/go.mod h1:Tpuj6t4NweCLzlNbw9Z9iwxEkrSem20AetIeH/shgVU=
|
||||
cloud.google.com/go/storagetransfer v1.10.4/go.mod h1:vef30rZKu5HSEf/x1tK3WfWrL0XVoUQN/EPDRGPzjZs=
|
||||
cloud.google.com/go/storagetransfer v1.13.0/go.mod h1:+aov7guRxXBYgR3WCqedkyibbTICdQOiXOdpPcJCKl8=
|
||||
cloud.google.com/go/talent v1.6.6/go.mod h1:y/WQDKrhVz12WagoarpAIyKKMeKGKHWPoReZ0g8tseQ=
|
||||
cloud.google.com/go/talent v1.8.3/go.mod h1:oD3/BilJpJX8/ad8ZUAxlXHCslTg2YBbafFH3ciZSLQ=
|
||||
cloud.google.com/go/texttospeech v1.7.5/go.mod h1:tzpCuNWPwrNJnEa4Pu5taALuZL4QRRLcb+K9pbhXT6M=
|
||||
cloud.google.com/go/texttospeech v1.14.0/go.mod h1:l25ywjIgXS+mSE2f5LQdXdU7r3MOLwVOGaYZQMiYIWE=
|
||||
cloud.google.com/go/tpu v1.6.5/go.mod h1:P9DFOEBIBhuEcZhXi+wPoVy/cji+0ICFi4TtTkMHSSs=
|
||||
cloud.google.com/go/tpu v1.8.3/go.mod h1:Do6Gq+/Jx6Xs3LcY2WhHyGwKDKVw++9jIJp+X+0rxRE=
|
||||
cloud.google.com/go/trace v1.10.5/go.mod h1:9hjCV1nGBCtXbAE4YK7OqJ8pmPYSxPA0I67JwRd5s3M=
|
||||
cloud.google.com/go/trace v1.11.6/go.mod h1:GA855OeDEBiBMzcckLPE2kDunIpC72N+Pq8WFieFjnI=
|
||||
cloud.google.com/go/translate v1.10.1/go.mod h1:adGZcQNom/3ogU65N9UXHOnnSvjPwA/jKQUMnsYXOyk=
|
||||
cloud.google.com/go/translate v1.10.3 h1:g+B29z4gtRGsiKDoTF+bNeH25bLRokAaElygX2FcZkE=
|
||||
cloud.google.com/go/translate v1.10.3/go.mod h1:GW0vC1qvPtd3pgtypCv4k4U8B7EdgK9/QEF2aJEUovs=
|
||||
cloud.google.com/go/translate v1.12.6/go.mod h1:nB3AXuX+iHbV8ZURmElcW85qkEDWZw68sf4kqMT/E5o=
|
||||
cloud.google.com/go/video v1.20.4/go.mod h1:LyUVjyW+Bwj7dh3UJnUGZfyqjEto9DnrvTe1f/+QrW0=
|
||||
cloud.google.com/go/video v1.26.0/go.mod h1:iqsrblPUfkxvyH31rnS02Z0dp9p5lySdq7+I0XzozQI=
|
||||
cloud.google.com/go/videointelligence v1.11.5/go.mod h1:/PkeQjpRponmOerPeJxNPuxvi12HlW7Em0lJO14FC3I=
|
||||
cloud.google.com/go/videointelligence v1.12.6/go.mod h1:/l34WMndN5/bt04lHodxiYchLVuWPQjCU6SaiTswrIw=
|
||||
cloud.google.com/go/vision/v2 v2.8.0/go.mod h1:ocqDiA2j97pvgogdyhoxiQp2ZkDCyr0HWpicywGGRhU=
|
||||
cloud.google.com/go/vision/v2 v2.9.5/go.mod h1:1SiNZPpypqZDbOzU052ZYRiyKjwOcyqgGgqQCI/nlx8=
|
||||
cloud.google.com/go/vmmigration v1.7.5/go.mod h1:pkvO6huVnVWzkFioxSghZxIGcsstDvYiVCxQ9ZH3eYI=
|
||||
cloud.google.com/go/vmmigration v1.8.6/go.mod h1:uZ6/KXmekwK3JmC8PzBM/cKQmq404TTfWtThF6bbf0U=
|
||||
cloud.google.com/go/vmwareengine v1.1.1/go.mod h1:nMpdsIVkUrSaX8UvmnBhzVzG7PPvNYc5BszcvIVudYs=
|
||||
cloud.google.com/go/vmwareengine v1.3.5/go.mod h1:QuVu2/b/eo8zcIkxBYY5QSwiyEcAy6dInI7N+keI+Jg=
|
||||
cloud.google.com/go/vpcaccess v1.7.5/go.mod h1:slc5ZRvvjP78c2dnL7m4l4R9GwL3wDLcpIWz6P/ziig=
|
||||
cloud.google.com/go/vpcaccess v1.8.6/go.mod h1:61yymNplV1hAbo8+kBOFO7Vs+4ZHYI244rSFgmsHC6E=
|
||||
cloud.google.com/go/webrisk v1.9.5/go.mod h1:aako0Fzep1Q714cPEM5E+mtYX8/jsfegAuS8aivxy3U=
|
||||
cloud.google.com/go/webrisk v1.11.1/go.mod h1:+9SaepGg2lcp1p0pXuHyz3R2Yi2fHKKb4c1Q9y0qbtA=
|
||||
cloud.google.com/go/websecurityscanner v1.6.5/go.mod h1:QR+DWaxAz2pWooylsBF854/Ijvuoa3FCyS1zBa1rAVQ=
|
||||
cloud.google.com/go/websecurityscanner v1.7.6/go.mod h1:ucaaTO5JESFn5f2pjdX01wGbQ8D6h79KHrmO2uGZeiY=
|
||||
cloud.google.com/go/workflows v1.12.4/go.mod h1:yQ7HUqOkdJK4duVtMeBCAOPiN1ZF1E9pAMX51vpwB/w=
|
||||
cloud.google.com/go/workflows v1.14.2/go.mod h1:5nqKjMD+MsJs41sJhdVrETgvD5cOK3hUcAs8ygqYvXQ=
|
||||
contrib.go.opencensus.io/exporter/stackdriver v0.13.4/go.mod h1:aXENhDJ1Y4lIg4EUaVTwzvYETVNZk10Pu26tevFKLUc=
|
||||
dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3 h1:hJiie5Bf3QucGRa4ymsAUOxyhYwGEz1xrsVk0P8erlw=
|
||||
dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU=
|
||||
@@ -234,8 +350,6 @@ github.com/AdguardTeam/golibs v0.32.11/go.mod h1:LXr0gqqZuVpt+L+bP3Nnr0/CecLmm3r
|
||||
github.com/AdguardTeam/gomitmproxy v0.2.0 h1:rvCOf17pd1/CnMyMQW891zrEiIQBpQ8cIGjKN9pinUU=
|
||||
github.com/AdguardTeam/gomitmproxy v0.2.1 h1:p9gr8Er1TYvf+7ic81Ax1sZ62UNCsMTZNbm7tC59S9o=
|
||||
github.com/AdguardTeam/gomitmproxy v0.2.1/go.mod h1:Qdv0Mktnzer5zpdpi5rAwixNJzW2FN91LjKJCkVbYGU=
|
||||
github.com/AdguardTeam/urlfilter v0.21.0 h1:ThIxiP7yoaXt8JTEroGQeU5ftQSoFpUq+t1L+TIx2pA=
|
||||
github.com/AdguardTeam/urlfilter v0.21.0/go.mod h1:xoZ3AF5qpE9ngbbeSShY9hgVeyHtm9MdH5xH1u714Wg=
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0=
|
||||
@@ -370,6 +484,7 @@ github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42 h1:Om6kYQYDUk5wWbT0t0q
|
||||
github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
|
||||
github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f h1:C5bqEmzEPLsHm9Mv73lSE9e9bKV23aB1vxOsmZrkl3k=
|
||||
github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
|
||||
github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443 h1:aQ3y1lwWyqYPiWZThqv1aFbZMiM9vblcSArJRf2Irls=
|
||||
github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
|
||||
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
|
||||
github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0 h1:sDMmm+q/3+BukdIpxwO365v/Rbspp2Nt5XntgQRXq8Q=
|
||||
@@ -461,6 +576,7 @@ github.com/go-jose/go-jose/v4 v4.0.4 h1:VsjPI33J0SB9vQM6PLmNjoHqMQNGPiZ0rHL7Ni7Q
|
||||
github.com/go-jose/go-jose/v4 v4.0.4/go.mod h1:NKb5HO1EZccyMpiZNbdUw/14tiXNyUJh188dfnMCAfc=
|
||||
github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE=
|
||||
github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA=
|
||||
github.com/go-jose/go-jose/v4 v4.1.1 h1:JYhSgy4mXXzAdF3nUx3ygx347LRXJRrpgyU3adRmkAI=
|
||||
github.com/go-jose/go-jose/v4 v4.1.1/go.mod h1:BdsZGqgdO3b6tTc6LSE56wcDbMMLuPsw5d4ZD5f94kA=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.9.0 h1:wDJmvq38kDhkVxi50ni9ykkdUr1PKgqKOoi01fa0Mdk=
|
||||
@@ -1174,6 +1290,7 @@ go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzK
|
||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
|
||||
|
||||
@@ -139,7 +139,9 @@ func BenchmarkGlobal_IsBlockedHost(b *testing.B) {
|
||||
|
||||
for _, tc := range testCases {
|
||||
b.Run(tc.name, func(b *testing.B) {
|
||||
var blocked bool
|
||||
// Warmup to fill the pools and the slices.
|
||||
blocked := global.IsBlockedHost(tc.host, tc.qt)
|
||||
tc.want(b, blocked)
|
||||
|
||||
b.ReportAllocs()
|
||||
for b.Loop() {
|
||||
@@ -151,19 +153,18 @@ func BenchmarkGlobal_IsBlockedHost(b *testing.B) {
|
||||
}
|
||||
|
||||
// Most recent results:
|
||||
//
|
||||
// goos: linux
|
||||
// goarch: amd64
|
||||
// pkg: github.com/AdguardTeam/AdGuardDNS/internal/access
|
||||
// cpu: AMD Ryzen 7 PRO 4750U with Radeon Graphics
|
||||
// BenchmarkGlobal_IsBlockedHost/pass-16 2313513 515.0 ns/op 16 B/op 1 allocs/op
|
||||
// BenchmarkGlobal_IsBlockedHost/blocked_domain_a-16 1604049 683.4 ns/op 24 B/op 1 allocs/op
|
||||
// BenchmarkGlobal_IsBlockedHost/blocked_domain_https-16 1981204 597.7 ns/op 24 B/op 1 allocs/op
|
||||
// BenchmarkGlobal_IsBlockedHost/uppercase_domain-16 2093197 590.5 ns/op 24 B/op 1 allocs/op
|
||||
// BenchmarkGlobal_IsBlockedHost/pass_qt-16 1961065 653.3 ns/op 24 B/op 1 allocs/op
|
||||
// BenchmarkGlobal_IsBlockedHost/block_qt-16 768783 1567 ns/op 24 B/op 1 allocs/op
|
||||
// BenchmarkGlobal_IsBlockedHost/allowlist_block-16 759159 1890 ns/op 32 B/op 1 allocs/op
|
||||
// BenchmarkGlobal_IsBlockedHost/allowlist_test-16 371722 3170 ns/op 32 B/op 1 allocs/op
|
||||
// BenchmarkGlobal_IsBlockedHost/pass-16 3085917 384.7 ns/op 16 B/op 1 allocs/op
|
||||
// BenchmarkGlobal_IsBlockedHost/blocked_domain_a-16 2521102 475.8 ns/op 24 B/op 1 allocs/op
|
||||
// BenchmarkGlobal_IsBlockedHost/blocked_domain_https-16 2520067 476.0 ns/op 24 B/op 1 allocs/op
|
||||
// BenchmarkGlobal_IsBlockedHost/uppercase_domain-16 2445049 490.7 ns/op 24 B/op 1 allocs/op
|
||||
// BenchmarkGlobal_IsBlockedHost/pass_qt-16 2228846 535.7 ns/op 24 B/op 1 allocs/op
|
||||
// BenchmarkGlobal_IsBlockedHost/block_qt-16 822028 1375 ns/op 24 B/op 1 allocs/op
|
||||
// BenchmarkGlobal_IsBlockedHost/allowlist_block-16 765939 1530 ns/op 32 B/op 1 allocs/op
|
||||
// BenchmarkGlobal_IsBlockedHost/allowlist_test-16 448230 2677 ns/op 32 B/op 1 allocs/op
|
||||
}
|
||||
|
||||
func BenchmarkGlobal_IsBlockedIP(b *testing.B) {
|
||||
@@ -199,11 +200,10 @@ func BenchmarkGlobal_IsBlockedIP(b *testing.B) {
|
||||
})
|
||||
|
||||
// Most recent results:
|
||||
//
|
||||
// goos: linux
|
||||
// goarch: amd64
|
||||
// pkg: github.com/AdguardTeam/AdGuardDNS/internal/access
|
||||
// cpu: AMD Ryzen 7 PRO 4750U with Radeon Graphics
|
||||
// BenchmarkGlobal_IsBlockedIP/pass-16 100000000 10.18 ns/op 0 B/op 0 allocs/op
|
||||
// BenchmarkGlobal_IsBlockedIP/block-16 141876058 8.545 ns/op 0 B/op 0 allocs/op
|
||||
// BenchmarkGlobal_IsBlockedIP/pass-16 152113686 7.893 ns/op 0 B/op 0 allocs/op
|
||||
// BenchmarkGlobal_IsBlockedIP/block-16 156828942 7.659 ns/op 0 B/op 0 allocs/op
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/AdguardTeam/golibs/testutil"
|
||||
"github.com/miekg/dns"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// testTimeout is the common timeout for tests.
|
||||
@@ -95,16 +96,12 @@ func TestBlockedHostEngine_IsBlocked_concurrent(t *testing.T) {
|
||||
|
||||
wg := &sync.WaitGroup{}
|
||||
for i := range routinesLimit {
|
||||
wg.Add(1)
|
||||
|
||||
host := fmt.Sprintf("%d.%s", i, "block.test")
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
||||
wg.Go(func() {
|
||||
req := dnsservertest.NewReq(host, dns.TypeA, dns.ClassINET)
|
||||
assert.True(t, engine.isBlocked(testutil.ContextWithTimeout(t, testTimeout), req))
|
||||
}()
|
||||
})
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
@@ -120,35 +117,38 @@ func BenchmarkBlockedHostEngine_IsBlocked(b *testing.B) {
|
||||
b.Run("pass", func(b *testing.B) {
|
||||
req := dnsservertest.NewReq("pass.test", dns.TypeA, dns.ClassINET)
|
||||
|
||||
var blocked bool
|
||||
// Warmup to fill the pools and the slices.
|
||||
blocked := engine.isBlocked(ctx, req)
|
||||
require.False(b, blocked)
|
||||
|
||||
b.ReportAllocs()
|
||||
for b.Loop() {
|
||||
blocked = engine.isBlocked(ctx, req)
|
||||
}
|
||||
|
||||
assert.False(b, blocked)
|
||||
require.False(b, blocked)
|
||||
})
|
||||
|
||||
b.Run("block", func(b *testing.B) {
|
||||
req := dnsservertest.NewReq("block.test", dns.TypeA, dns.ClassINET)
|
||||
|
||||
var blocked bool
|
||||
// Warmup to fill the pools and the slices.
|
||||
blocked := engine.isBlocked(ctx, req)
|
||||
require.True(b, blocked)
|
||||
|
||||
b.ReportAllocs()
|
||||
for b.Loop() {
|
||||
blocked = engine.isBlocked(ctx, req)
|
||||
}
|
||||
|
||||
assert.True(b, blocked)
|
||||
require.True(b, blocked)
|
||||
})
|
||||
|
||||
// Most recent results:
|
||||
//
|
||||
// goos: linux
|
||||
// goarch: amd64
|
||||
// pkg: github.com/AdguardTeam/AdGuardDNS/internal/access
|
||||
// cpu: AMD Ryzen 7 PRO 4750U with Radeon Graphics
|
||||
// BenchmarkBlockedHostEngine_IsBlocked/pass-16 3362199 369.1 ns/op 16 B/op 1 allocs/op
|
||||
// BenchmarkBlockedHostEngine_IsBlocked/block-16 2299890 510.6 ns/op 24 B/op 1 allocs/op
|
||||
// BenchmarkBlockedHostEngine_IsBlocked/pass-16 3750295 317.8 ns/op 16 B/op 1 allocs/op
|
||||
// BenchmarkBlockedHostEngine_IsBlocked/block-16 3407104 350.2 ns/op 24 B/op 1 allocs/op
|
||||
}
|
||||
|
||||
@@ -385,8 +385,11 @@ func BenchmarkDefaultProfile_IsBlocked(b *testing.B) {
|
||||
|
||||
for _, bc := range benchCases {
|
||||
b.Run(bc.name, func(b *testing.B) {
|
||||
// Warmup to fill the pools and the slices.
|
||||
blocked := a.IsBlocked(ctx, bc.req, passAddrPort, nil)
|
||||
bc.want(b, blocked)
|
||||
|
||||
b.ReportAllocs()
|
||||
var blocked bool
|
||||
for b.Loop() {
|
||||
blocked = a.IsBlocked(ctx, bc.req, passAddrPort, nil)
|
||||
}
|
||||
@@ -396,11 +399,10 @@ func BenchmarkDefaultProfile_IsBlocked(b *testing.B) {
|
||||
}
|
||||
|
||||
// Most recent results:
|
||||
//
|
||||
// goos: linux
|
||||
// goarch: amd64
|
||||
// pkg: github.com/AdguardTeam/AdGuardDNS/internal/access
|
||||
// cpu: AMD Ryzen 7 PRO 4750U with Radeon Graphics
|
||||
// BenchmarkDefaultProfile_IsBlocked/pass-16 2679700 468.8 ns/op 16 B/op 1 allocs/op
|
||||
// BenchmarkDefaultProfile_IsBlocked/block-16 2081113 576.4 ns/op 24 B/op 1 allocs/op
|
||||
// BenchmarkDefaultProfile_IsBlocked/pass-16 2638284 452.4 ns/op 16 B/op 1 allocs/op
|
||||
// BenchmarkDefaultProfile_IsBlocked/block-16 2224564 539.1 ns/op 24 B/op 1 allocs/op
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/dnsservertest"
|
||||
"github.com/AdguardTeam/golibs/testutil"
|
||||
"github.com/miekg/dns"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func BenchmarkStandardBlocker_IsBlocked(b *testing.B) {
|
||||
@@ -24,35 +24,38 @@ func BenchmarkStandardBlocker_IsBlocked(b *testing.B) {
|
||||
b.Run("pass", func(b *testing.B) {
|
||||
req := dnsservertest.NewReq("pass.test", dns.TypeA, dns.ClassINET)
|
||||
|
||||
var blocked bool
|
||||
// Warmup to fill the pools and the slices.
|
||||
blocked := blocker.IsBlocked(ctx, req, remoteAddr, nil)
|
||||
require.False(b, blocked)
|
||||
|
||||
b.ReportAllocs()
|
||||
for b.Loop() {
|
||||
blocked = blocker.IsBlocked(ctx, req, remoteAddr, nil)
|
||||
}
|
||||
|
||||
assert.False(b, blocked)
|
||||
require.False(b, blocked)
|
||||
})
|
||||
|
||||
b.Run("block", func(b *testing.B) {
|
||||
req := dnsservertest.NewReq("block.test", dns.TypeA, dns.ClassINET)
|
||||
|
||||
var blocked bool
|
||||
// Warmup to fill the pools and the slices.
|
||||
blocked := blocker.IsBlocked(ctx, req, remoteAddr, nil)
|
||||
require.True(b, blocked)
|
||||
|
||||
b.ReportAllocs()
|
||||
for b.Loop() {
|
||||
blocked = blocker.IsBlocked(ctx, req, remoteAddr, nil)
|
||||
}
|
||||
|
||||
assert.True(b, blocked)
|
||||
require.True(b, blocked)
|
||||
})
|
||||
|
||||
// Most recent results:
|
||||
//
|
||||
// goos: linux
|
||||
// goarch: amd64
|
||||
// pkg: github.com/AdguardTeam/AdGuardDNS/internal/access
|
||||
// cpu: AMD Ryzen 7 PRO 4750U with Radeon Graphics
|
||||
// BenchmarkStandardBlocker_IsBlocked/pass-16 3009312 378.2 ns/op 16 B/op 1 allocs/op
|
||||
// BenchmarkStandardBlocker_IsBlocked/block-16 2518006 421.9 ns/op 24 B/op 1 allocs/op
|
||||
// BenchmarkStandardBlocker_IsBlocked/pass-16 3568975 335.4 ns/op 16 B/op 1 allocs/op
|
||||
// BenchmarkStandardBlocker_IsBlocked/block-16 3286392 364.1 ns/op 24 B/op 1 allocs/op
|
||||
}
|
||||
|
||||
@@ -47,9 +47,7 @@ type CustomDomainStateCurrent struct {
|
||||
|
||||
// CertName is the unique name for fetching the actual certificate data. If
|
||||
// [CustomDomainStateCurrent.Enabled] is true, it must not be empty.
|
||||
//
|
||||
// TODO(a.garipov): Make a newtype.
|
||||
CertName string
|
||||
CertName CertificateName
|
||||
|
||||
// Enabled shows if this certificate is enabled.
|
||||
Enabled bool
|
||||
|
||||
53
internal/agd/tls.go
Normal file
53
internal/agd/tls.go
Normal file
@@ -0,0 +1,53 @@
|
||||
package agd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/validate"
|
||||
)
|
||||
|
||||
// maxCertificateNameLen is the maximum length of a [CertificateName].
|
||||
const maxCertificateNameLen = 32
|
||||
|
||||
// CertificateName is the unique name identifying the TLS certificate.
|
||||
type CertificateName string
|
||||
|
||||
// NewCertificateName creates a new CertificateName from the given string.
|
||||
func NewCertificateName(str string) (name CertificateName, err error) {
|
||||
if str == "" {
|
||||
return "", errors.ErrEmptyValue
|
||||
}
|
||||
|
||||
err = validate.InRange("length", len(str), 1, maxCertificateNameLen)
|
||||
if err != nil {
|
||||
// Don't wrap the error, since it's informative enough as is.
|
||||
return "", err
|
||||
}
|
||||
|
||||
for i, r := range str {
|
||||
// Don't use [agdvalidate.FirstNonIDRune] as it allows invalid symbols
|
||||
// for file names.
|
||||
if !isValidCertNameRune(r) {
|
||||
return "", fmt.Errorf("at index %d: bad symbol: %q", i, r)
|
||||
}
|
||||
}
|
||||
|
||||
return CertificateName(str), nil
|
||||
}
|
||||
|
||||
// isValidCertNameRune returns true if the given rune is valid to be used in a
|
||||
// [CertificateName]. It essentially allows alphanumeric symbols, underscores,
|
||||
// and hyphens.
|
||||
func isValidCertNameRune(r rune) (ok bool) {
|
||||
switch {
|
||||
case
|
||||
r >= 'a' && r <= 'z',
|
||||
r >= 'A' && r <= 'Z',
|
||||
r >= '0' && r <= '9',
|
||||
r == '_', r == '-':
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
51
internal/agd/tls_test.go
Normal file
51
internal/agd/tls_test.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package agd_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
|
||||
"github.com/AdguardTeam/golibs/testutil"
|
||||
)
|
||||
|
||||
func TestNewCertificateName(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
value string
|
||||
wantErrMsg string
|
||||
}{{
|
||||
name: "empty",
|
||||
value: "",
|
||||
wantErrMsg: "empty value",
|
||||
}, {
|
||||
name: "bad_symbol",
|
||||
value: "not valid",
|
||||
wantErrMsg: "at index 3: bad symbol: ' '",
|
||||
}, {
|
||||
name: "bad_base_name",
|
||||
value: "bad/base_name",
|
||||
wantErrMsg: "at index 3: bad symbol: '/'",
|
||||
}, {
|
||||
name: "too_long",
|
||||
value: "this_is_a_very_long_certificate_name",
|
||||
wantErrMsg: "length: out of range: must be no greater than 32, got 36",
|
||||
}, {
|
||||
name: "ok",
|
||||
value: "ok_cert_name",
|
||||
wantErrMsg: "",
|
||||
}, {
|
||||
name: "ok_numeric",
|
||||
value: "1234567890",
|
||||
wantErrMsg: "",
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, err := agd.NewCertificateName(tc.value)
|
||||
testutil.AssertErrorMsg(t, tc.wantErrMsg, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -270,34 +270,52 @@ var _ geoip.Interface = (*GeoIP)(nil)
|
||||
|
||||
// GeoIP is a [geoip.Interface] for tests.
|
||||
type GeoIP struct {
|
||||
OnData func(host string, ip netip.Addr) (l *geoip.Location, err error)
|
||||
OnSubnetByLocation func(l *geoip.Location, fam netutil.AddrFamily) (n netip.Prefix, err error)
|
||||
OnData func(
|
||||
сtx context.Context,
|
||||
host string,
|
||||
ip netip.Addr,
|
||||
) (l *geoip.Location, err error)
|
||||
OnSubnetByLocation func(
|
||||
ctx context.Context,
|
||||
l *geoip.Location,
|
||||
fam netutil.AddrFamily,
|
||||
) (n netip.Prefix, err error)
|
||||
}
|
||||
|
||||
// Data implements the [geoip.Interface] interface for *GeoIP.
|
||||
func (g *GeoIP) Data(host string, ip netip.Addr) (l *geoip.Location, err error) {
|
||||
return g.OnData(host, ip)
|
||||
func (g *GeoIP) Data(
|
||||
ctx context.Context,
|
||||
host string,
|
||||
ip netip.Addr,
|
||||
) (l *geoip.Location, err error) {
|
||||
return g.OnData(ctx, host, ip)
|
||||
}
|
||||
|
||||
// SubnetByLocation implements the [geoip.Interface] interface for *GeoIP.
|
||||
func (g *GeoIP) SubnetByLocation(
|
||||
ctx context.Context,
|
||||
l *geoip.Location,
|
||||
fam netutil.AddrFamily,
|
||||
) (n netip.Prefix, err error) {
|
||||
return g.OnSubnetByLocation(l, fam)
|
||||
return g.OnSubnetByLocation(ctx, l, fam)
|
||||
}
|
||||
|
||||
// NewGeoIP returns a new *GeoIP all methods of which panic.
|
||||
func NewGeoIP() (c *GeoIP) {
|
||||
return &GeoIP{
|
||||
OnData: func(host string, ip netip.Addr) (l *geoip.Location, err error) {
|
||||
panic(testutil.UnexpectedCall(host, ip))
|
||||
OnData: func(
|
||||
ctx context.Context,
|
||||
host string,
|
||||
ip netip.Addr,
|
||||
) (l *geoip.Location, err error) {
|
||||
panic(testutil.UnexpectedCall(ctx, host, ip))
|
||||
},
|
||||
OnSubnetByLocation: func(
|
||||
ctx context.Context,
|
||||
l *geoip.Location,
|
||||
fam netutil.AddrFamily,
|
||||
) (n netip.Prefix, err error) {
|
||||
panic(testutil.UnexpectedCall(l, fam))
|
||||
panic(testutil.UnexpectedCall(ctx, l, fam))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/tlsconfig"
|
||||
"github.com/AdguardTeam/golibs/timeutil"
|
||||
)
|
||||
@@ -76,7 +77,7 @@ var _ tlsconfig.CustomDomainStorage = (*CustomDomainStorage)(nil)
|
||||
// *CustomDomainStorage.
|
||||
func (s *CustomDomainStorage) CertificateData(
|
||||
ctx context.Context,
|
||||
name string,
|
||||
name agd.CertificateName,
|
||||
) (cert, key []byte, err error) {
|
||||
start := s.clock.Now()
|
||||
defer func() { s.metrics.ObserveRequest(ctx, time.Since(start), err) }()
|
||||
@@ -84,7 +85,7 @@ func (s *CustomDomainStorage) CertificateData(
|
||||
s.logger.DebugContext(ctx, "getting cert data", "name", name)
|
||||
|
||||
req := &CustomDomainCertificateRequest{
|
||||
CertName: name,
|
||||
CertName: string(name),
|
||||
}
|
||||
|
||||
ctx = ctxWithAuthentication(ctx, s.apiKey)
|
||||
|
||||
@@ -248,8 +248,14 @@ func (x *CustomDomain) toInternal() (c *agd.CustomDomainConfig, err error) {
|
||||
|
||||
switch s := x.State.(type) {
|
||||
case *CustomDomain_Current_:
|
||||
var certName agd.CertificateName
|
||||
certName, err = agd.NewCertificateName(s.Current.CertName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("certificate name: %q: %w", s.Current.CertName, err)
|
||||
}
|
||||
|
||||
st := &agd.CustomDomainStateCurrent{
|
||||
CertName: s.Current.CertName,
|
||||
CertName: certName,
|
||||
NotBefore: s.Current.NotBefore.AsTime(),
|
||||
NotAfter: s.Current.NotAfter.AsTime(),
|
||||
Enabled: s.Current.Enabled,
|
||||
|
||||
@@ -20,7 +20,7 @@ var _ validate.Interface = additionalInfo(nil)
|
||||
func (c additionalInfo) Validate() (err error) {
|
||||
var errs []error
|
||||
for _, k := range slices.Sorted(maps.Keys(c)) {
|
||||
if !model.LabelName(k).IsValid() {
|
||||
if !model.LegacyValidation.IsValidLabelName(k) {
|
||||
errs = append(errs, fmt.Errorf(
|
||||
"prometheus labels must match %s, got %q",
|
||||
model.LabelNameRE,
|
||||
|
||||
@@ -50,6 +50,7 @@ import (
|
||||
"github.com/AdguardTeam/golibs/netutil/urlutil"
|
||||
"github.com/AdguardTeam/golibs/osutil"
|
||||
"github.com/AdguardTeam/golibs/service"
|
||||
"github.com/AdguardTeam/golibs/syncutil"
|
||||
"github.com/AdguardTeam/golibs/timeutil"
|
||||
"github.com/c2h5oh/datasize"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
@@ -200,6 +201,32 @@ func newBuilder(c *builderConfig) (b *builder) {
|
||||
}
|
||||
}
|
||||
|
||||
// initCrashReporter initializes the crash reporter.
|
||||
func (b *builder) initCrashReporter(ctx context.Context) (err error) {
|
||||
crashRep, err := newCrashReporter(&crashReporterConfig{
|
||||
logger: b.baseLogger.With(slogutil.KeyPrefix, "crash_reporter"),
|
||||
dirPath: b.env.CrashOutputDir,
|
||||
prefix: b.env.CrashOutputPrefix,
|
||||
enabled: bool(b.env.CrashOutputEnabled),
|
||||
})
|
||||
if err != nil {
|
||||
// Don't wrap the error, because it's informative enough as is.
|
||||
return err
|
||||
}
|
||||
|
||||
err = crashRep.Start(ctx)
|
||||
if err != nil {
|
||||
// Don't wrap the error, because it's informative enough as is.
|
||||
return err
|
||||
}
|
||||
|
||||
b.sigHdlr.AddService(crashRep)
|
||||
|
||||
b.logger.DebugContext(ctx, "initialized crash reporter")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// startGeoIP starts the concurrent initialization of the GeoIP database. The
|
||||
// GeoIP initialization is started early and concurrently, because it takes
|
||||
// time. Later methods wait for the completion and continue with GeoIP.
|
||||
@@ -880,11 +907,19 @@ func (b *builder) initQueryLog(ctx context.Context) (err error) {
|
||||
return fmt.Errorf("registering querylog metrics: %w", err)
|
||||
}
|
||||
|
||||
var sema syncutil.Semaphore
|
||||
if b.env.QueryLogSemaphoreEnabled {
|
||||
sema = syncutil.NewChanSemaphore(b.env.QueryLogSemaphoreLimit)
|
||||
} else {
|
||||
sema = syncutil.EmptySemaphore{}
|
||||
}
|
||||
|
||||
b.queryLog = querylog.NewFileSystem(&querylog.FileSystemConfig{
|
||||
Logger: b.baseLogger.With(slogutil.KeyPrefix, "querylog"),
|
||||
Path: b.env.QueryLogPath,
|
||||
Metrics: mtrc,
|
||||
RandSeed: randutil.MustNewSeed(),
|
||||
Logger: b.baseLogger.With(slogutil.KeyPrefix, "querylog"),
|
||||
Path: b.env.QueryLogPath,
|
||||
Metrics: mtrc,
|
||||
Semaphore: sema,
|
||||
RandSeed: randutil.MustNewSeed(),
|
||||
})
|
||||
|
||||
b.logger.DebugContext(ctx, "initialized file-based query log")
|
||||
|
||||
@@ -65,6 +65,8 @@ func Main(plugins *plugin.Registry) {
|
||||
|
||||
defer reportPanics(ctx, errColl, mainLogger)
|
||||
|
||||
setMaxThreads(ctx, mainLogger, envs.MaxThreads)
|
||||
|
||||
c := errors.Must(parseConfig(envs.ConfPath))
|
||||
|
||||
errors.Check(c.Validate())
|
||||
@@ -84,6 +86,8 @@ func Main(plugins *plugin.Registry) {
|
||||
profilesEnabled: profilesEnabled,
|
||||
})
|
||||
|
||||
errors.Check(b.initCrashReporter(ctx))
|
||||
|
||||
errors.Check(experiment.Init(baseLogger, b.promRegisterer))
|
||||
|
||||
errors.Check(metrics.SetAdditionalInfo(b.promRegisterer, c.AdditionalMetricsInfo))
|
||||
|
||||
145
internal/cmd/crash.go
Normal file
145
internal/cmd/crash.go
Normal file
@@ -0,0 +1,145 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
"runtime/debug"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/service"
|
||||
)
|
||||
|
||||
// crashReporter is a helper that sets a file for Go runtime crashes and
|
||||
// unhandled panics.
|
||||
type crashReporter struct {
|
||||
file *os.File
|
||||
logger *slog.Logger
|
||||
|
||||
dirPath string
|
||||
pattern string
|
||||
}
|
||||
|
||||
// crashReporterConfig is the configuration structure for a [crashReporter].
|
||||
type crashReporterConfig struct {
|
||||
// logger is used to log the operation of the crash reporter. If enabled is
|
||||
// true, logger must not be nil.
|
||||
logger *slog.Logger
|
||||
|
||||
// dirPath is the path to the directory where the crash report is created.
|
||||
// If enabled is true, dirPath should not be nil and should point to a
|
||||
// directory.
|
||||
dirPath string
|
||||
|
||||
// prefix is the prefix to use when creating the file. If enabled is true,
|
||||
// prefix should not be nil.
|
||||
prefix string
|
||||
|
||||
// enabled shows if a crash report file should be created.
|
||||
enabled bool
|
||||
}
|
||||
|
||||
// newCrashReporter returns a new properly initialized crash reporter. c must
|
||||
// not be nil and must be valid.
|
||||
//
|
||||
// TODO(a.garipov): Consider moving to golibs.
|
||||
func newCrashReporter(c *crashReporterConfig) (r *crashReporter, err error) {
|
||||
defer func() { err = errors.Annotate(err, "crash reporter: %w") }()
|
||||
|
||||
if !c.enabled {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
err = validateDir(c.dirPath)
|
||||
if err != nil {
|
||||
// Don't wrap the error, because it's informative enough as is, and
|
||||
// there is already errors.Annotate here.
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pat := fmt.Sprintf(
|
||||
"%s_%s_%07d_*.txt",
|
||||
c.prefix,
|
||||
time.Now().Format("20060102150405"),
|
||||
os.Getpid(),
|
||||
)
|
||||
|
||||
return &crashReporter{
|
||||
logger: c.logger,
|
||||
dirPath: c.dirPath,
|
||||
pattern: pat,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ service.Interface = (*crashReporter)(nil)
|
||||
|
||||
// Start implements the [service.Interface] for *crashReporter. If r is nil,
|
||||
// err is nil.
|
||||
func (r *crashReporter) Start(ctx context.Context) (err error) {
|
||||
if r == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
defer func() { err = errors.Annotate(err, "starting crash reporter: %w") }()
|
||||
|
||||
r.logger.InfoContext(ctx, "creating crash output file")
|
||||
|
||||
r.file, err = os.CreateTemp(r.dirPath, r.pattern)
|
||||
if err != nil {
|
||||
// Don't wrap the error, because it's informative enough as is, and
|
||||
// there is already errors.Annotate here.
|
||||
return err
|
||||
}
|
||||
|
||||
r.logger = r.logger.With("path", r.file.Name())
|
||||
|
||||
r.logger.InfoContext(ctx, "setting crash output")
|
||||
|
||||
err = debug.SetCrashOutput(r.file, debug.CrashOptions{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("setting crash output: %w", err)
|
||||
}
|
||||
|
||||
r.logger.DebugContext(ctx, "set crash output")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Shutdown implements the [service.Interface] for *crashReporter. If r is nil,
|
||||
// err is nil.
|
||||
func (r *crashReporter) Shutdown(ctx context.Context) (err error) {
|
||||
if r == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
r.logger.InfoContext(ctx, "closing crash output")
|
||||
|
||||
s, err := r.file.Stat()
|
||||
if err != nil {
|
||||
return fmt.Errorf("getting stat of crash file: %w", err)
|
||||
}
|
||||
|
||||
if s.Size() > 0 {
|
||||
r.logger.InfoContext(ctx, "crash output is not empty; not removing")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
name := r.file.Name()
|
||||
err = r.file.Close()
|
||||
if err != nil {
|
||||
return fmt.Errorf("closing crash file: %w", err)
|
||||
}
|
||||
|
||||
r.logger.InfoContext(ctx, "crash output is empty; removing")
|
||||
|
||||
err = os.Remove(name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("removing crash file: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -55,6 +55,8 @@ type environment struct {
|
||||
BackendRateLimitAPIKey string `env:"BACKEND_RATELIMIT_API_KEY"`
|
||||
BillStatAPIKey string `env:"BILLSTAT_API_KEY"`
|
||||
ConfPath string `env:"CONFIG_PATH" envDefault:"./config.yaml"`
|
||||
CrashOutputDir string `env:"CRASH_OUTPUT_DIR"`
|
||||
CrashOutputPrefix string `env:"CRASH_OUTPUT_PREFIX" envDefault:"agdns"`
|
||||
CustomDomainsAPIKey string `env:"CUSTOM_DOMAINS_API_KEY"`
|
||||
CustomDomainsCachePath string `env:"CUSTOM_DOMAINS_CACHE_PATH"`
|
||||
DNSCheckKVType string `env:"DNSCHECK_KV_TYPE"`
|
||||
@@ -94,17 +96,22 @@ type environment struct {
|
||||
|
||||
// TODO(a.garipov): Rename to DNSCHECK_CACHE_KV_COUNT?
|
||||
DNSCheckCacheKVSize int `env:"DNSCHECK_CACHE_KV_SIZE"`
|
||||
MaxThreads int `env:"MAX_THREADS"`
|
||||
|
||||
QueryLogSemaphoreLimit uint `env:"QUERYLOG_SEMAPHORE_LIMIT"`
|
||||
|
||||
ListenPort uint16 `env:"LISTEN_PORT" envDefault:"8181"`
|
||||
|
||||
Verbosity uint8 `env:"VERBOSE" envDefault:"0"`
|
||||
|
||||
AdultBlockingEnabled strictBool `env:"ADULT_BLOCKING_ENABLED" envDefault:"1"`
|
||||
CrashOutputEnabled strictBool `env:"CRASH_OUTPUT_ENABLED" envDefault:"0"`
|
||||
CustomDomainsEnabled strictBool `env:"CUSTOM_DOMAINS_ENABLED" envDefault:"1"`
|
||||
LogTimestamp strictBool `env:"LOG_TIMESTAMP" envDefault:"1"`
|
||||
NewRegDomainsEnabled strictBool `env:"NEW_REG_DOMAINS_ENABLED" envDefault:"1"`
|
||||
SafeBrowsingEnabled strictBool `env:"SAFE_BROWSING_ENABLED" envDefault:"1"`
|
||||
BlockedServiceEnabled strictBool `env:"BLOCKED_SERVICE_ENABLED" envDefault:"1"`
|
||||
QueryLogSemaphoreEnabled strictBool `env:"QUERYLOG_SEMAPHORE_ENABLED"`
|
||||
GeneralSafeSearchEnabled strictBool `env:"GENERAL_SAFE_SEARCH_ENABLED" envDefault:"1"`
|
||||
YoutubeSafeSearchEnabled strictBool `env:"YOUTUBE_SAFE_SEARCH_ENABLED" envDefault:"1"`
|
||||
WebStaticDirEnabled strictBool `env:"WEB_STATIC_DIR_ENABLED" envDefault:"0"`
|
||||
@@ -126,7 +133,9 @@ var _ validate.Interface = (*environment)(nil)
|
||||
|
||||
// Validate implements the [validate.Interface] interface for *environment.
|
||||
func (envs *environment) Validate() (err error) {
|
||||
var errs []error
|
||||
errs := []error{
|
||||
validate.NotNegative("MAX_THREADS", envs.MaxThreads),
|
||||
}
|
||||
|
||||
errs = envs.validateHTTPURLs(errs)
|
||||
|
||||
@@ -153,12 +162,14 @@ func (envs *environment) Validate() (err error) {
|
||||
errs = append(errs, fmt.Errorf("VERBOSE: %w", err))
|
||||
}
|
||||
|
||||
errs = envs.validateCrashOutput(errs)
|
||||
errs = envs.validateCustomDomains(errs)
|
||||
errs = envs.validateDNSCheck(errs)
|
||||
errs = envs.validateQueryLogSemaphore(errs)
|
||||
errs = envs.validateRateLimit(errs)
|
||||
errs = envs.validateRateLimitURLs(errs)
|
||||
errs = envs.validateSessionTickets(errs)
|
||||
errs = envs.validateStandardAccess(errs)
|
||||
errs = envs.validateRateLimitURLs(errs)
|
||||
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
@@ -244,13 +255,19 @@ func (envs *environment) validateWebStaticDir() (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
dir := envs.WebStaticDir
|
||||
if dir == "" {
|
||||
dirPath := envs.WebStaticDir
|
||||
if dirPath == "" {
|
||||
return errors.ErrEmptyValue
|
||||
}
|
||||
|
||||
// Use a best-effort check to make sure the directory exists.
|
||||
fi, err := os.Stat(dir)
|
||||
return validateDir(dirPath)
|
||||
}
|
||||
|
||||
// validateDir is a best-effort check to make sure the directory exists.
|
||||
//
|
||||
// TODO(a.garipov): Consider moving to golibs.
|
||||
func validateDir(dirPath string) (err error) {
|
||||
fi, err := os.Stat(dirPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -262,6 +279,29 @@ func (envs *environment) validateWebStaticDir() (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateCrashOutput appends validation errors to errs if the environment
|
||||
// variables for crash reporting contain errors.
|
||||
func (envs *environment) validateCrashOutput(orig []error) (errs []error) {
|
||||
errs = orig
|
||||
|
||||
if !envs.CrashOutputEnabled {
|
||||
return errs
|
||||
}
|
||||
|
||||
dirPath := envs.WebStaticDir
|
||||
if dirPath != "" {
|
||||
err := validateDir(dirPath)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
|
||||
return append(errs,
|
||||
validate.NotEmpty("CRASH_OUTPUT_DIR", envs.CrashOutputDir),
|
||||
validate.NotEmpty("CRASH_OUTPUT_PREFIX", envs.CrashOutputPrefix),
|
||||
)
|
||||
}
|
||||
|
||||
// validateCustomDomains appends validation errors to errs if the environment
|
||||
// variables for custom domains contain errors.
|
||||
func (envs *environment) validateCustomDomains(errs []error) (res []error) {
|
||||
@@ -420,6 +460,24 @@ func (envs *environment) validateProfilesConf(profilesEnabled bool) (err error)
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
|
||||
// validateCache appends validation errors to orig if environment variables for
|
||||
// the querylog semaphore contain errors.
|
||||
func (envs *environment) validateQueryLogSemaphore(orig []error) (errs []error) {
|
||||
errs = orig
|
||||
|
||||
if !envs.QueryLogSemaphoreEnabled {
|
||||
return errs
|
||||
}
|
||||
|
||||
err := validate.Positive("QUERYLOG_SEMAPHORE_LIMIT", envs.QueryLogSemaphoreLimit)
|
||||
if err != nil {
|
||||
// Don't wrap the error, because it's informative enough as is.
|
||||
errs = append(errs, err)
|
||||
}
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
// validateCache appends validation errors to the given errs if environment
|
||||
// variables for KV Cache contain errors.
|
||||
func (envs *environment) validateCache(errs []error) (res []error) {
|
||||
|
||||
23
internal/cmd/runtime.go
Normal file
23
internal/cmd/runtime.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
"runtime/debug"
|
||||
|
||||
"github.com/AdguardTeam/golibs/logutil/slogutil"
|
||||
)
|
||||
|
||||
// setMaxThreads sets the maximum number of threads for the Go runtime, if
|
||||
// necessary. l must not be nil, envs must not be negative.
|
||||
func setMaxThreads(ctx context.Context, l *slog.Logger, n int) {
|
||||
if n == 0 {
|
||||
l.Log(ctx, slogutil.LevelTrace, "go max threads not set")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
debug.SetMaxThreads(n)
|
||||
|
||||
l.InfoContext(ctx, "set go max threads", "n", n)
|
||||
}
|
||||
@@ -2,7 +2,6 @@ package dnsserver_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
"net/netip"
|
||||
"os"
|
||||
|
||||
@@ -30,7 +29,6 @@ func ExampleNewServerDNS() {
|
||||
|
||||
baseLogger := slogutil.New(&slogutil.Config{
|
||||
Format: slogutil.FormatText,
|
||||
Level: slog.LevelDebug,
|
||||
}).With("server_name", "test")
|
||||
|
||||
// Init the server with this handler func
|
||||
@@ -81,7 +79,6 @@ func ExampleWithMiddlewares() {
|
||||
|
||||
baseLogger := slogutil.New(&slogutil.Config{
|
||||
Format: slogutil.FormatText,
|
||||
Level: slog.LevelDebug,
|
||||
})
|
||||
|
||||
middleware := querylog.NewLogMiddleware(os.Stdout, baseLogger)
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
module github.com/AdguardTeam/AdGuardDNS/internal/dnsserver
|
||||
|
||||
go 1.24.6
|
||||
go 1.25.1
|
||||
|
||||
require (
|
||||
github.com/AdguardTeam/golibs v0.34.0
|
||||
github.com/AdguardTeam/golibs v0.34.1
|
||||
github.com/ameshkov/dnscrypt/v2 v2.4.0
|
||||
github.com/ameshkov/dnsstamps v1.0.3
|
||||
github.com/bluele/gcache v0.0.2
|
||||
@@ -11,32 +11,34 @@ require (
|
||||
github.com/miekg/dns v1.1.68
|
||||
github.com/panjf2000/ants/v2 v2.11.3
|
||||
github.com/patrickmn/go-cache v2.1.1-0.20191004192108-46f407853014+incompatible
|
||||
github.com/prometheus/client_golang v1.23.0
|
||||
github.com/prometheus/client_golang v1.23.1
|
||||
github.com/quic-go/quic-go v0.54.0
|
||||
github.com/stretchr/testify v1.11.1
|
||||
golang.org/x/net v0.43.0
|
||||
golang.org/x/sys v0.35.0
|
||||
golang.org/x/net v0.44.0
|
||||
golang.org/x/sys v0.36.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/prometheus/client_model v0.6.2 // indirect
|
||||
github.com/prometheus/common v0.65.0 // indirect
|
||||
github.com/prometheus/common v0.66.0 // indirect
|
||||
github.com/prometheus/procfs v0.17.0 // indirect
|
||||
github.com/quic-go/qpack v0.5.1 // indirect
|
||||
github.com/robfig/cron/v3 v3.0.1 // indirect
|
||||
go.uber.org/mock v0.6.0 // indirect
|
||||
golang.org/x/crypto v0.41.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b // indirect
|
||||
golang.org/x/mod v0.27.0 // indirect
|
||||
golang.org/x/sync v0.16.0 // indirect
|
||||
golang.org/x/text v0.28.0 // indirect
|
||||
golang.org/x/tools v0.36.0 // indirect
|
||||
google.golang.org/protobuf v1.36.8 // indirect
|
||||
golang.org/x/crypto v0.42.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250911091902-df9299821621 // indirect
|
||||
golang.org/x/mod v0.28.0 // indirect
|
||||
golang.org/x/sync v0.17.0 // indirect
|
||||
golang.org/x/text v0.29.0 // indirect
|
||||
golang.org/x/tools v0.37.0 // indirect
|
||||
google.golang.org/protobuf v1.36.9 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
github.com/AdguardTeam/golibs v0.34.0 h1:JQK024DkTYxE7vsPVsYsoyDHW/53Nun7OYb9qscniK8=
|
||||
github.com/AdguardTeam/golibs v0.34.0/go.mod h1:K4C2EbfSEM1zY5YXoti9SfbTAHN/kIX97LpDtCwORrM=
|
||||
github.com/AdguardTeam/golibs v0.34.1 h1:RyBpZiXnJqlO3T+xjWldlxsEZDelmaFfKvXiJHDZZFQ=
|
||||
github.com/ameshkov/dnscrypt/v2 v2.4.0 h1:if6ZG2cuQmcP2TwSY+D0+8+xbPfoatufGlOQTMNkI9o=
|
||||
github.com/ameshkov/dnscrypt/v2 v2.4.0/go.mod h1:WpEFV2uhebXb8Jhes/5/fSdpmhGV8TL22RDaeWwV6hI=
|
||||
github.com/ameshkov/dnsstamps v1.0.3 h1:Srzik+J9mivH1alRACTbys2xOxs0lRH9qnTA7Y1OYVo=
|
||||
@@ -17,6 +16,7 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc h1:GN2Lv3MGO7AS6PrRoT6yV5+wkrOpcszoIsO4+4ds248=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
@@ -31,12 +31,10 @@ github.com/patrickmn/go-cache v2.1.1-0.20191004192108-46f407853014+incompatible
|
||||
github.com/patrickmn/go-cache v2.1.1-0.20191004192108-46f407853014+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v1.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc=
|
||||
github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE=
|
||||
github.com/prometheus/client_golang v1.23.1 h1:w6gXMLQGgd0jXXlote9lRHMe0nG01EbnJT+C0EJru2Y=
|
||||
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
||||
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||
github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE=
|
||||
github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8=
|
||||
github.com/prometheus/common v0.66.0 h1:K/rJPHrG3+AoQs50r2+0t7zMnMzek2Vbv31OFVsMeVY=
|
||||
github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0=
|
||||
github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw=
|
||||
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
|
||||
@@ -53,26 +51,18 @@ go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
|
||||
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
|
||||
golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
|
||||
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
|
||||
golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b h1:DXr+pvt3nC887026GRP39Ej11UATqWDmWuS99x26cD0=
|
||||
golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b/go.mod h1:4QTo5u+SEIbbKW1RacMZq1YEfOBqeXa19JeshGi+zc4=
|
||||
golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ=
|
||||
golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc=
|
||||
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
|
||||
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
|
||||
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
||||
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
|
||||
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
|
||||
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
|
||||
golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
|
||||
golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
|
||||
google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc=
|
||||
google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
|
||||
golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI=
|
||||
golang.org/x/exp v0.0.0-20250911091902-df9299821621 h1:2id6c1/gto0kaHYyrixvknJ8tUK/Qs5IsmBtrc+FtgU=
|
||||
golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U=
|
||||
golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I=
|
||||
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
|
||||
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
|
||||
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
|
||||
golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE=
|
||||
google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
@@ -103,9 +103,12 @@ type ServerBase struct {
|
||||
// that don't use UDP.
|
||||
udpListener net.PacketConn
|
||||
|
||||
// wg tracks active workers, both listeners and workers processing queries.
|
||||
// Shutdown won't finish until there's at least one active worker.
|
||||
wg *sync.WaitGroup
|
||||
// activeTaskWG tracks goroutines processing UDP and TCP connections and
|
||||
// queries. Shutdown doesn't finish as long as there's at least one active
|
||||
// task.
|
||||
//
|
||||
// TODO(a.garipov): Consider also using it for listeners.
|
||||
activeTaskWG *sync.WaitGroup
|
||||
|
||||
// name is used for logging and it may be used for perf counters reporting.
|
||||
//
|
||||
@@ -153,7 +156,7 @@ func newServerBase(proto Protocol, c *ConfigBase) (s *ServerBase) {
|
||||
disposer: cmp.Or[Disposer](c.Disposer, EmptyDisposer{}),
|
||||
listenConfig: c.ListenConfig,
|
||||
mu: &sync.RWMutex{},
|
||||
wg: &sync.WaitGroup{},
|
||||
activeTaskWG: &sync.WaitGroup{},
|
||||
name: c.Name,
|
||||
addr: c.Addr,
|
||||
network: c.Network,
|
||||
@@ -465,21 +468,12 @@ func (s *ServerBase) handlePanicAndExit(ctx context.Context) {
|
||||
func (s *ServerBase) handlePanic(ctx context.Context, v any) {
|
||||
s.metrics.OnPanic(ctx, v)
|
||||
|
||||
logger, ok := slogutil.LoggerFromContext(ctx)
|
||||
l, ok := slogutil.LoggerFromContext(ctx)
|
||||
if !ok {
|
||||
logger = s.baseLogger
|
||||
l = s.baseLogger
|
||||
}
|
||||
|
||||
var args []any
|
||||
err, ok := v.(error)
|
||||
if ok {
|
||||
args = []any{slogutil.KeyError, err}
|
||||
} else {
|
||||
args = []any{"value", v}
|
||||
}
|
||||
|
||||
logger.ErrorContext(ctx, "recovered from panic", args...)
|
||||
slogutil.PrintStack(ctx, logger, slog.LevelError)
|
||||
slogutil.PrintRecovered(ctx, l, v)
|
||||
}
|
||||
|
||||
// handlePanicAndRecover writes panic info to log, reports it to the registered
|
||||
@@ -558,20 +552,17 @@ func (s *ServerBase) waitShutdown(ctx context.Context) (err error) {
|
||||
go func() {
|
||||
defer slogutil.RecoverAndLog(ctx, s.baseLogger)
|
||||
|
||||
// wait until all queries are processed
|
||||
s.wg.Wait()
|
||||
// Wait until all tasks exit.
|
||||
s.activeTaskWG.Wait()
|
||||
close(closed)
|
||||
}()
|
||||
|
||||
var ctxErr error
|
||||
select {
|
||||
case <-closed:
|
||||
// Do nothing here
|
||||
return nil
|
||||
case <-ctx.Done():
|
||||
ctxErr = ctx.Err()
|
||||
return ctx.Err()
|
||||
}
|
||||
|
||||
return ctxErr
|
||||
}
|
||||
|
||||
// isStarted returns true if the server is started.
|
||||
|
||||
@@ -15,7 +15,6 @@ import (
|
||||
"github.com/AdguardTeam/golibs/logutil/slogutil"
|
||||
"github.com/AdguardTeam/golibs/syncutil"
|
||||
"github.com/miekg/dns"
|
||||
"github.com/panjf2000/ants/v2"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -84,11 +83,9 @@ type ConfigDNS struct {
|
||||
type ServerDNS struct {
|
||||
*ServerBase
|
||||
|
||||
// workerPool is a goroutine workerPool we use to process DNS queries.
|
||||
// Complicated logic may require growing the goroutine's stack, and we
|
||||
// experienced it in AdGuard DNS. The easiest way to avoid spending extra
|
||||
// time on this is to reuse already existing goroutines.
|
||||
workerPool *ants.Pool
|
||||
// taskPool is a goroutine pool used to process DNS queries. It is used to
|
||||
// prevent excessive growth of goroutine stacks.
|
||||
taskPool *taskPool
|
||||
|
||||
// udpPool is a pool for UDP request buffers.
|
||||
udpPool *syncutil.Pool[[]byte]
|
||||
@@ -171,7 +168,9 @@ func newServerDNS(proto Protocol, c *ConfigDNS) (s *ServerDNS) {
|
||||
maxPipelineEnabled: c.MaxPipelineEnabled,
|
||||
}
|
||||
|
||||
s.workerPool = mustNewPoolNonblocking(s.baseLogger)
|
||||
s.taskPool = mustNewTaskPool(&taskPoolConfig{
|
||||
logger: s.baseLogger,
|
||||
})
|
||||
|
||||
return s
|
||||
}
|
||||
@@ -206,8 +205,9 @@ func (s *ServerDNS) Start(ctx context.Context) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
s.wg.Add(1)
|
||||
go s.startServeUDP(ctx)
|
||||
s.activeTaskWG.Go(func() {
|
||||
s.serveUDP(ctx, s.udpListener)
|
||||
})
|
||||
}
|
||||
|
||||
// Start listening to TCP on the specified address.
|
||||
@@ -217,8 +217,9 @@ func (s *ServerDNS) Start(ctx context.Context) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
s.wg.Add(1)
|
||||
go s.startServeTCP(ctx)
|
||||
s.activeTaskWG.Go(func() {
|
||||
s.serveTCP(ctx, s.tcpListener, "tcp")
|
||||
})
|
||||
}
|
||||
|
||||
s.started = true
|
||||
@@ -245,42 +246,13 @@ func (s *ServerDNS) Shutdown(ctx context.Context) (err error) {
|
||||
err = s.waitShutdown(ctx)
|
||||
|
||||
// Close the workerPool and releases all workers.
|
||||
s.workerPool.Release()
|
||||
s.taskPool.Release()
|
||||
|
||||
s.baseLogger.InfoContext(ctx, "server has been shut down")
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// startServeUDP starts the UDP listener loop.
|
||||
func (s *ServerDNS) startServeUDP(ctx context.Context) {
|
||||
// Do not recover from panics here since if this goroutine panics, the
|
||||
// application won't be able to continue listening to UDP.
|
||||
defer s.handlePanicAndExit(ctx)
|
||||
defer s.wg.Done()
|
||||
|
||||
s.baseLogger.InfoContext(ctx, "starting listening udp")
|
||||
|
||||
err := s.serveUDP(ctx, s.udpListener)
|
||||
if err != nil {
|
||||
s.baseLogger.WarnContext(ctx, "listening udp failed", slogutil.KeyError, err)
|
||||
}
|
||||
}
|
||||
|
||||
// startServeTCP starts the TCP listener loop.
|
||||
func (s *ServerDNS) startServeTCP(ctx context.Context) {
|
||||
// Do not recover from panics here since if this goroutine panics, the
|
||||
// application won't be able to continue listening to TCP.
|
||||
defer s.handlePanicAndExit(ctx)
|
||||
defer s.wg.Done()
|
||||
|
||||
s.baseLogger.InfoContext(ctx, "starting listening tcp")
|
||||
err := s.serveTCP(ctx, s.tcpListener)
|
||||
if err != nil {
|
||||
s.baseLogger.WarnContext(ctx, "listening tcp failed", slogutil.KeyError, err)
|
||||
}
|
||||
}
|
||||
|
||||
// shutdown marks the server as stopped and closes active listeners.
|
||||
func (s *ServerDNS) shutdown(ctx context.Context) (err error) {
|
||||
s.mu.Lock()
|
||||
|
||||
@@ -18,22 +18,42 @@ import (
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
// serveTCP runs the TCP serving loop.
|
||||
func (s *ServerDNS) serveTCP(ctx context.Context, l net.Listener) (err error) {
|
||||
// serveTCP runs the TCP serving loop. It is intended to be used as a
|
||||
// goroutine. l must not be nil.
|
||||
func (s *ServerDNS) serveTCP(ctx context.Context, l net.Listener, proto string) {
|
||||
// Do not recover from panics here since if this goroutine panics, the
|
||||
// application won't be able to continue listening to TCP.
|
||||
defer s.handlePanicAndExit(ctx)
|
||||
|
||||
s.baseLogger.InfoContext(ctx, "starting listening tcp")
|
||||
defer func() { closeWithLog(ctx, s.baseLogger, "closing tcp listener", l) }()
|
||||
|
||||
for s.isStarted() {
|
||||
err = s.acceptTCPConn(ctx, l)
|
||||
if err != nil {
|
||||
if !s.isStarted() {
|
||||
return nil
|
||||
}
|
||||
|
||||
return err
|
||||
err := s.acceptTCPConn(ctx, l)
|
||||
if err == nil {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
// TODO(ameshkov): Consider the situation where the server is shut down
|
||||
// and restarted between the two calls to isStarted.
|
||||
if !s.isStarted() {
|
||||
s.baseLogger.DebugContext(
|
||||
ctx,
|
||||
"listening tcp failed: server not started",
|
||||
"proto", proto,
|
||||
slogutil.KeyError, err,
|
||||
)
|
||||
} else {
|
||||
s.baseLogger.ErrorContext(
|
||||
ctx,
|
||||
"listening tcp failed",
|
||||
"proto", proto,
|
||||
slogutil.KeyError, err,
|
||||
)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// acceptTCPConn reads and starts processing a single TCP connection.
|
||||
@@ -60,9 +80,7 @@ func (s *ServerDNS) acceptTCPConn(ctx context.Context, l net.Listener) (err erro
|
||||
s.tcpConns.Add(conn)
|
||||
}()
|
||||
|
||||
s.wg.Add(1)
|
||||
|
||||
return s.workerPool.Submit(func() {
|
||||
return s.taskPool.submitWG(s.activeTaskWG, func() {
|
||||
s.serveTCPConn(ctx, conn)
|
||||
})
|
||||
}
|
||||
@@ -89,16 +107,14 @@ func handshake(conn net.Conn, timeout time.Duration) (err error) {
|
||||
return shaker.HandshakeContext(ctx)
|
||||
}
|
||||
|
||||
// serveTCPConn serves a single TCP connection.
|
||||
// serveTCPConn serves a single TCP connection. It is intended to be used as a
|
||||
// goroutine. conn must not be nil.
|
||||
func (s *ServerDNS) serveTCPConn(ctx context.Context, conn net.Conn) {
|
||||
// Use this to wait until all queries from this connection has been
|
||||
// processed before closing it.
|
||||
wg := &sync.WaitGroup{}
|
||||
defer s.handlePanicAndRecover(ctx)
|
||||
|
||||
connWG := &sync.WaitGroup{}
|
||||
defer func() {
|
||||
defer s.wg.Done()
|
||||
|
||||
wg.Wait()
|
||||
connWG.Wait()
|
||||
|
||||
closeWithLog(ctx, s.baseLogger, "closing tcp conn", conn)
|
||||
|
||||
@@ -108,8 +124,6 @@ func (s *ServerDNS) serveTCPConn(ctx context.Context, conn net.Conn) {
|
||||
s.tcpConns.Delete(conn)
|
||||
}()
|
||||
|
||||
defer s.handlePanicAndRecover(ctx)
|
||||
|
||||
var msgSema syncutil.Semaphore = syncutil.EmptySemaphore{}
|
||||
if s.maxPipelineEnabled {
|
||||
msgSema = syncutil.NewChanSemaphore(s.maxPipelineCount)
|
||||
@@ -129,7 +143,7 @@ func (s *ServerDNS) serveTCPConn(ctx context.Context, conn net.Conn) {
|
||||
}
|
||||
|
||||
for s.isStarted() {
|
||||
err = s.acceptTCPMsg(conn, wg, writeMu, timeout, msgSema)
|
||||
err = s.acceptTCPMsg(conn, connWG, writeMu, timeout, msgSema)
|
||||
if err != nil {
|
||||
s.logReadErr(ctx, "reading from conn", err)
|
||||
|
||||
@@ -155,7 +169,7 @@ func (s *ServerDNS) logReadErr(ctx context.Context, msg string, err error) {
|
||||
// TLS connection, the handshake must have already been performed.
|
||||
func (s *ServerDNS) acceptTCPMsg(
|
||||
conn net.Conn,
|
||||
wg *sync.WaitGroup,
|
||||
connWG *sync.WaitGroup,
|
||||
writeMu *sync.Mutex,
|
||||
timeout time.Duration,
|
||||
msgSema syncutil.Semaphore,
|
||||
@@ -182,13 +196,11 @@ func (s *ServerDNS) acceptTCPMsg(
|
||||
|
||||
// RFC 7766 recommends implementing query pipelining, i.e. process all
|
||||
// incoming queries concurrently and write responses out of order.
|
||||
wg.Add(1)
|
||||
|
||||
return s.workerPool.Submit(func() {
|
||||
return s.taskPool.submitWG(connWG, func() {
|
||||
defer reqCancel()
|
||||
defer msgSema.Release()
|
||||
|
||||
s.serveTCPMessage(reqCtx, wg, writeMu, *bufPtr, conn)
|
||||
s.serveTCPMessage(reqCtx, writeMu, *bufPtr, conn)
|
||||
s.tcpPool.Put(bufPtr)
|
||||
})
|
||||
}
|
||||
@@ -199,31 +211,27 @@ type tlsConnectionStater interface {
|
||||
ConnectionState() tls.ConnectionState
|
||||
}
|
||||
|
||||
// serveTCPMessage processes a single TCP message.
|
||||
// serveTCPMessage processes a single TCP message. It is intended to be used as
|
||||
// a goroutine. All arguments must not be nil.
|
||||
func (s *ServerDNS) serveTCPMessage(
|
||||
ctx context.Context,
|
||||
wg *sync.WaitGroup,
|
||||
writeMu *sync.Mutex,
|
||||
buf []byte,
|
||||
conn net.Conn,
|
||||
) {
|
||||
defer wg.Done()
|
||||
defer s.handlePanicAndRecover(ctx)
|
||||
|
||||
rw := &tcpResponseWriter{
|
||||
written := s.serveDNS(ctx, buf, &tcpResponseWriter{
|
||||
respPool: s.respPool,
|
||||
writeMu: writeMu,
|
||||
conn: conn,
|
||||
writeTimeout: s.writeTimeout,
|
||||
idleTimeout: s.tcpIdleTimeout,
|
||||
}
|
||||
written := s.serveDNS(ctx, buf, rw)
|
||||
|
||||
})
|
||||
if !written {
|
||||
// Nothing has been written, we should close the connection in order to
|
||||
// avoid hanging connections. Than might happen if the handler
|
||||
// rate-limited connections or if we received garbage data instead of
|
||||
// a DNS query.
|
||||
// Nothing has been written, so close the connection in order to avoid
|
||||
// hanging connections. That can happen when the handler rate-limited a
|
||||
// connection or if garbage data has been received.
|
||||
slogutil.CloseAndLog(ctx, s.baseLogger, conn, slog.LevelDebug)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,28 +8,41 @@ import (
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/netext"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/logutil/slogutil"
|
||||
"github.com/AdguardTeam/golibs/syncutil"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
// serveUDP runs the UDP serving loop.
|
||||
func (s *ServerDNS) serveUDP(ctx context.Context, conn net.PacketConn) (err error) {
|
||||
// serveUDP runs the UDP serving loop. It is intended to be used as a
|
||||
// goroutine. conn must not be nil.
|
||||
func (s *ServerDNS) serveUDP(ctx context.Context, conn net.PacketConn) {
|
||||
// Do not recover from panics here since if this goroutine panics, the
|
||||
// application won't be able to continue listening to UDP.
|
||||
defer s.handlePanicAndExit(ctx)
|
||||
|
||||
s.baseLogger.InfoContext(ctx, "starting listening udp")
|
||||
defer func() { closeWithLog(ctx, s.baseLogger, "closing udp conn", conn) }()
|
||||
|
||||
for s.isStarted() {
|
||||
err = s.acceptUDPMsg(ctx, conn)
|
||||
if err != nil {
|
||||
// TODO(ameshkov): Consider the situation where the server is shut
|
||||
// down and restarted between the two calls to isStarted.
|
||||
if !s.isStarted() {
|
||||
return nil
|
||||
}
|
||||
|
||||
return err
|
||||
err := s.acceptUDPMsg(ctx, conn)
|
||||
if err == nil {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
// TODO(ameshkov): Consider the situation where the server is shut down
|
||||
// and restarted between the two calls to isStarted.
|
||||
if !s.isStarted() {
|
||||
s.baseLogger.DebugContext(
|
||||
ctx,
|
||||
"listening udp failed: server not started",
|
||||
slogutil.KeyError, err,
|
||||
)
|
||||
} else {
|
||||
s.baseLogger.ErrorContext(ctx, "listening udp failed", slogutil.KeyError, err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// acceptUDPMsg reads and starts processing a single UDP message.
|
||||
@@ -48,8 +61,6 @@ func (s *ServerDNS) acceptUDPMsg(ctx context.Context, conn net.PacketConn) (err
|
||||
return err
|
||||
}
|
||||
|
||||
s.wg.Add(1)
|
||||
|
||||
// Save the start time here, but create the context inside the goroutine,
|
||||
// since s.requestContext can be slow.
|
||||
//
|
||||
@@ -58,7 +69,7 @@ func (s *ServerDNS) acceptUDPMsg(ctx context.Context, conn net.PacketConn) (err
|
||||
// version.
|
||||
startTime := time.Now()
|
||||
|
||||
return s.workerPool.Submit(func() {
|
||||
return s.taskPool.submitWG(s.activeTaskWG, func() {
|
||||
reqCtx, reqCancel := s.requestContext(context.Background())
|
||||
defer reqCancel()
|
||||
|
||||
@@ -71,24 +82,23 @@ func (s *ServerDNS) acceptUDPMsg(ctx context.Context, conn net.PacketConn) (err
|
||||
})
|
||||
}
|
||||
|
||||
// serveUDPPacket serves a new UDP request.
|
||||
// serveUDPPacket serves a new UDP request. It is intended to be used as a
|
||||
// goroutine. buf, conn, and sess must not be nil.
|
||||
func (s *ServerDNS) serveUDPPacket(
|
||||
ctx context.Context,
|
||||
buf []byte,
|
||||
conn net.PacketConn,
|
||||
sess netext.PacketSession,
|
||||
) {
|
||||
defer s.wg.Done()
|
||||
defer s.handlePanicAndRecover(ctx)
|
||||
|
||||
rw := &udpResponseWriter{
|
||||
s.serveDNS(ctx, buf, &udpResponseWriter{
|
||||
respPool: s.respPool,
|
||||
udpSession: sess,
|
||||
conn: conn,
|
||||
writeTimeout: s.writeTimeout,
|
||||
maxRespSize: s.maxUDPRespSize,
|
||||
}
|
||||
s.serveDNS(ctx, buf, rw)
|
||||
})
|
||||
}
|
||||
|
||||
// readUDPMsg reads the next incoming DNS message.
|
||||
|
||||
@@ -231,8 +231,9 @@ func (s *ServerHTTPS) startHTTPSServer(ctx context.Context) (err error) {
|
||||
}
|
||||
|
||||
// Start the server worker goroutine.
|
||||
s.wg.Add(1)
|
||||
go s.serveHTTPS(ctx, s.httpServer, s.tcpListener)
|
||||
s.activeTaskWG.Go(func() {
|
||||
s.serveHTTPS(ctx, s.httpServer, s.tcpListener)
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -257,8 +258,9 @@ func (s *ServerHTTPS) startH3Server(ctx context.Context) (err error) {
|
||||
}
|
||||
|
||||
// Start the server worker goroutine.
|
||||
s.wg.Add(1)
|
||||
go s.serveH3(ctx, s.h3Server, s.quicListener)
|
||||
s.activeTaskWG.Go(func() {
|
||||
s.serveH3(ctx, s.h3Server, s.quicListener)
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -320,10 +322,9 @@ func (s *ServerHTTPS) shutdownH3(ctx context.Context) {
|
||||
}
|
||||
|
||||
// serveHTTPS is launched in a worker goroutine and serves HTTP/1.1 and HTTP/2
|
||||
// requests.
|
||||
// requests. It is intended to be used as a goroutine. All arguments must not
|
||||
// be nil.
|
||||
func (s *ServerHTTPS) serveHTTPS(ctx context.Context, hs *http.Server, l net.Listener) {
|
||||
defer s.wg.Done()
|
||||
|
||||
// Do not recover from panics here since if this goroutine panics, the
|
||||
// application won't be able to continue listening to DoH.
|
||||
defer s.handlePanicAndExit(ctx)
|
||||
@@ -341,10 +342,9 @@ func (s *ServerHTTPS) serveHTTPS(ctx context.Context, hs *http.Server, l net.Lis
|
||||
}
|
||||
}
|
||||
|
||||
// serveH3 is launched in a worker goroutine and serves HTTP/3 requests.
|
||||
// serveH3 is launched in a worker goroutine and serves HTTP/3 requests. It is
|
||||
// intended to be used as a goroutine. All arguments must not be nil.
|
||||
func (s *ServerHTTPS) serveH3(ctx context.Context, hs *http3.Server, ql *quic.EarlyListener) {
|
||||
defer s.wg.Done()
|
||||
|
||||
// Do not recover from panics here since if this goroutine panics, the
|
||||
// application won't be able to continue listening to DoH.
|
||||
defer s.handlePanicAndExit(ctx)
|
||||
|
||||
@@ -18,7 +18,6 @@ import (
|
||||
"github.com/AdguardTeam/golibs/syncutil"
|
||||
"github.com/bluele/gcache"
|
||||
"github.com/miekg/dns"
|
||||
"github.com/panjf2000/ants/v2"
|
||||
"github.com/quic-go/quic-go"
|
||||
)
|
||||
|
||||
@@ -94,11 +93,9 @@ type ConfigQUIC struct {
|
||||
type ServerQUIC struct {
|
||||
*ServerBase
|
||||
|
||||
// pool is a goroutine pool we use to process DNS queries. Complicated
|
||||
// logic may require growing the goroutine's stack and we experienced it
|
||||
// in AdGuard DNS. The easiest way to avoid spending extra time on this is
|
||||
// to reuse already existing goroutines.
|
||||
pool *ants.Pool
|
||||
// taskPool is a goroutine pool used to process DNS queries. It is used to
|
||||
// prevent excessive growth of goroutine stacks.
|
||||
taskPool *taskPool
|
||||
|
||||
// reqPool is a pool to avoid unnecessary allocations when reading
|
||||
// DNS packets.
|
||||
@@ -140,7 +137,9 @@ func NewServerQUIC(c *ConfigQUIC) (s *ServerQUIC) {
|
||||
quicLimitsEnabled: c.QUICLimitsEnabled,
|
||||
}
|
||||
|
||||
s.pool = mustNewPoolNonblocking(s.baseLogger)
|
||||
s.taskPool = mustNewTaskPool(&taskPoolConfig{
|
||||
logger: s.baseLogger,
|
||||
})
|
||||
|
||||
return s
|
||||
}
|
||||
@@ -173,9 +172,9 @@ func (s *ServerQUIC) Start(ctx context.Context) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
// Run the serving goroutine.
|
||||
s.wg.Add(1)
|
||||
go s.startServeQUIC(ctx)
|
||||
s.activeTaskWG.Go(func() {
|
||||
s.serveQUIC(ctx, s.quicListener)
|
||||
})
|
||||
|
||||
s.started = true
|
||||
|
||||
@@ -199,8 +198,8 @@ func (s *ServerQUIC) Shutdown(ctx context.Context) (err error) {
|
||||
|
||||
err = s.waitShutdown(ctx)
|
||||
|
||||
// Close the workerPool and releases all workers.
|
||||
s.pool.Release()
|
||||
// Close the taskPool and release all workers.
|
||||
s.taskPool.Release()
|
||||
|
||||
s.baseLogger.InfoContext(ctx, "server has been shut down")
|
||||
|
||||
@@ -234,45 +233,43 @@ func (s *ServerQUIC) shutdown(ctx context.Context) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// startServeQUIC starts the QUIC listener loop.
|
||||
func (s *ServerQUIC) startServeQUIC(ctx context.Context) {
|
||||
// serveQUIC listens for incoming QUIC connections.
|
||||
func (s *ServerQUIC) serveQUIC(ctx context.Context, l *quic.Listener) {
|
||||
// We do not recover from panics here since if this go routine panics
|
||||
// the application won't be able to continue listening to DoQ.
|
||||
defer s.handlePanicAndExit(ctx)
|
||||
defer s.wg.Done()
|
||||
|
||||
s.baseLogger.InfoContext(ctx, "starting listening quic")
|
||||
|
||||
err := s.serveQUIC(ctx, s.quicListener)
|
||||
if err != nil {
|
||||
s.baseLogger.WarnContext(ctx, "listening quic failed", slogutil.KeyError, err)
|
||||
}
|
||||
}
|
||||
|
||||
// serveQUIC listens for incoming QUIC connections.
|
||||
func (s *ServerQUIC) serveQUIC(ctx context.Context, l *quic.Listener) (err error) {
|
||||
wg := &sync.WaitGroup{}
|
||||
// Wait until all conns are processed before exiting this method
|
||||
defer wg.Wait()
|
||||
|
||||
// Use a context that is canceled once this connection ends to mitigate
|
||||
// quic-go's mishandling of contexts. See TODO in serveQUICConn.
|
||||
var cancel context.CancelFunc
|
||||
ctx, cancel = context.WithCancel(ctx)
|
||||
// quic-go's mishandling of contexts. See the TODO in
|
||||
// [ServerQUIC.serveQUICConn].
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
|
||||
for s.isStarted() {
|
||||
err = s.acceptQUICConn(ctx, l, wg)
|
||||
if err != nil {
|
||||
if !s.isStarted() {
|
||||
return nil
|
||||
}
|
||||
|
||||
return err
|
||||
err := s.acceptQUICConn(ctx, l, wg)
|
||||
if err == nil {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
// TODO(ameshkov): Consider the situation where the server is shut down
|
||||
// and restarted between the two calls to isStarted.
|
||||
if !s.isStarted() {
|
||||
s.baseLogger.DebugContext(
|
||||
ctx,
|
||||
"listening quic failed: server not started",
|
||||
slogutil.KeyError, err,
|
||||
)
|
||||
} else {
|
||||
s.baseLogger.ErrorContext(ctx, "listening quic failed", slogutil.KeyError, err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// acceptQUICConn reads and starts processing a single QUIC connection.
|
||||
@@ -295,10 +292,8 @@ func (s *ServerQUIC) acceptQUICConn(
|
||||
return err
|
||||
}
|
||||
|
||||
wg.Add(1)
|
||||
|
||||
err = s.pool.Submit(func() {
|
||||
s.serveQUICConnAsync(ctx, conn, wg)
|
||||
err = s.taskPool.submitWG(wg, func() {
|
||||
s.serveQUICConnAsync(ctx, conn)
|
||||
})
|
||||
if err != nil {
|
||||
// Most likely the workerPool is closed, and we can exit right away.
|
||||
@@ -311,15 +306,13 @@ func (s *ServerQUIC) acceptQUICConn(
|
||||
return nil
|
||||
}
|
||||
|
||||
// serveQUICConnAsync wraps serveQUICConn call and handles all possible errors
|
||||
// that might happen there. It also makes sure that the WaitGroup will be
|
||||
// decremented.
|
||||
func (s *ServerQUIC) serveQUICConnAsync(
|
||||
ctx context.Context,
|
||||
conn *quic.Conn,
|
||||
connWg *sync.WaitGroup,
|
||||
) {
|
||||
defer connWg.Done()
|
||||
// serveQUICConnAsync wraps [ServerQUIC.serveQUICConn] call and handles errors
|
||||
// that could happen in it. It is intended to be used as a goroutine. conn
|
||||
// must not be nil.
|
||||
//
|
||||
// TODO(a.garipov): Refactor ServerQUIC.serveQUICConn and merge this one into
|
||||
// it.
|
||||
func (s *ServerQUIC) serveQUICConnAsync(ctx context.Context, conn *quic.Conn) {
|
||||
defer s.handlePanicAndRecover(ctx)
|
||||
|
||||
err := s.serveQUICConn(ctx, conn)
|
||||
@@ -379,12 +372,10 @@ func (s *ServerQUIC) serveQUICConn(ctx context.Context, conn *quic.Conn) (err er
|
||||
reqCtx, reqCancel := s.requestContext(context.Background())
|
||||
reqCtx = ContextWithRequestInfo(reqCtx, ri)
|
||||
|
||||
streamWg.Add(1)
|
||||
|
||||
err = s.pool.Submit(func() {
|
||||
err = s.taskPool.submitWG(streamWg, func() {
|
||||
defer reqCancel()
|
||||
|
||||
s.serveQUICStreamAsync(reqCtx, stream, conn, streamWg)
|
||||
s.serveQUICStreamAsync(reqCtx, stream, conn)
|
||||
})
|
||||
if err != nil {
|
||||
// The workerPool is closed, we should simply exit. Make sure that
|
||||
@@ -398,16 +389,17 @@ func (s *ServerQUIC) serveQUICConn(ctx context.Context, conn *quic.Conn) (err er
|
||||
return nil
|
||||
}
|
||||
|
||||
// serveQUICStreamAsync wraps serveQUICStream call and handles all possible
|
||||
// errors that might happen there. It also makes sure that the WaitGroup will
|
||||
// be decremented.
|
||||
// serveQUICStreamAsync wraps [ServerQUIC.serveQUICStream] call and handle
|
||||
// errors that could happen in it. It is intended to be used as a goroutine.
|
||||
// stream and conn must not be nil.
|
||||
//
|
||||
// TODO(a.garipov): Refactor ServerQUIC.serveQUICStream and merge this one into
|
||||
// it.
|
||||
func (s *ServerQUIC) serveQUICStreamAsync(
|
||||
ctx context.Context,
|
||||
stream *quic.Stream,
|
||||
conn *quic.Conn,
|
||||
wg *sync.WaitGroup,
|
||||
) {
|
||||
defer wg.Done()
|
||||
defer s.handlePanicAndRecover(ctx)
|
||||
|
||||
err := s.serveQUICStream(ctx, stream, conn)
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"crypto/tls"
|
||||
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/logutil/slogutil"
|
||||
)
|
||||
|
||||
// ConfigTLS is a struct that needs to be passed to NewServerTLS to
|
||||
@@ -71,10 +70,10 @@ func (s *ServerTLS) Start(ctx context.Context) (err error) {
|
||||
|
||||
// Start the TLS server loop
|
||||
if s.tcpListener != nil {
|
||||
go s.startServeTCP(ctx)
|
||||
go s.serveTCP(ctx, s.tcpListener, "tls")
|
||||
}
|
||||
|
||||
// TODO(ameshkov): Consider only setting s.started to true once the
|
||||
// TODO(ameshkov): Consider only setting s.started to true once the
|
||||
// listeners are up.
|
||||
s.started = true
|
||||
|
||||
@@ -90,20 +89,6 @@ func (s *ServerTLS) Shutdown(ctx context.Context) (err error) {
|
||||
return s.ServerDNS.Shutdown(ctx)
|
||||
}
|
||||
|
||||
// startServeTCP starts the TCP listen loop and handles errors if any.
|
||||
func (s *ServerTLS) startServeTCP(ctx context.Context) {
|
||||
// We do not recover from panics here since if this go routine panics
|
||||
// the application won't be able to continue listening to DoT
|
||||
defer s.handlePanicAndExit(ctx)
|
||||
|
||||
s.baseLogger.InfoContext(ctx, "starting listening tls")
|
||||
|
||||
err := s.serveTCP(ctx, s.tcpListener)
|
||||
if err != nil {
|
||||
s.baseLogger.WarnContext(ctx, "listening tls failed", slogutil.KeyError, err)
|
||||
}
|
||||
}
|
||||
|
||||
// listenTLS creates the TLS listener for s.addr.
|
||||
func (s *ServerTLS) listenTLS(ctx context.Context) (err error) {
|
||||
l, err := s.listenConfig.Listen(ctx, "tcp", s.addr)
|
||||
|
||||
81
internal/dnsserver/task.go
Normal file
81
internal/dnsserver/task.go
Normal file
@@ -0,0 +1,81 @@
|
||||
package dnsserver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/logutil/slogutil"
|
||||
"github.com/panjf2000/ants/v2"
|
||||
)
|
||||
|
||||
// task is a function that is intended to be used as a goroutine in a
|
||||
// [taskPool].
|
||||
type task func()
|
||||
|
||||
// taskPool is a wrapper around [ants.Pool] with convenience methods for using
|
||||
// it with [sync.WaitGroup]s.
|
||||
type taskPool struct {
|
||||
ants.Pool
|
||||
}
|
||||
|
||||
// taskPoolConfig is the configuration for a [taskPool].
|
||||
type taskPoolConfig struct {
|
||||
// logger is used for logging the operation of the task pool. It must not
|
||||
// be nil.
|
||||
logger *slog.Logger
|
||||
}
|
||||
|
||||
// mustNewTaskPool creates a new properly initialized *taskPool configured
|
||||
// optimally for using it in DNS servers. It panics if there are errors.
|
||||
// c must not be nil and must be valid.
|
||||
func mustNewTaskPool(c *taskPoolConfig) (p *taskPool) {
|
||||
pool, err := ants.NewPool(0, ants.WithOptions(ants.Options{
|
||||
ExpiryDuration: time.Minute,
|
||||
PreAlloc: false,
|
||||
Nonblocking: true,
|
||||
DisablePurge: false,
|
||||
Logger: &antsLogger{
|
||||
logger: c.logger,
|
||||
},
|
||||
}))
|
||||
errors.Check(err)
|
||||
|
||||
return &taskPool{
|
||||
Pool: *pool,
|
||||
}
|
||||
}
|
||||
|
||||
// submitWG is a convenience method that submits t to the pool and accounts
|
||||
// for it in wg. All arguments must not be nil.
|
||||
func (p *taskPool) submitWG(wg *sync.WaitGroup, t task) (err error) {
|
||||
wg.Add(1)
|
||||
|
||||
err = p.Submit(func() {
|
||||
defer wg.Done()
|
||||
|
||||
t()
|
||||
})
|
||||
if err != nil {
|
||||
// Decrease the counter if the goroutine hasn't been started.
|
||||
wg.Done()
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// antsLogger implements the [ants.Logger] interface and writes everything
|
||||
// to its logger.
|
||||
type antsLogger struct {
|
||||
logger *slog.Logger
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ ants.Logger = (*antsLogger)(nil)
|
||||
|
||||
// Printf implements the [ants.Logger] interface for *antsLogger.
|
||||
func (l *antsLogger) Printf(format string, args ...any) {
|
||||
l.logger.Info("ants pool", slogutil.KeyMessage, fmt.Sprintf(format, args...))
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
package dnsserver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/logutil/slogutil"
|
||||
"github.com/panjf2000/ants/v2"
|
||||
)
|
||||
|
||||
// antsLogger implements the [ants.Logger] interface and writes everything
|
||||
// to its logger.
|
||||
type antsLogger struct {
|
||||
logger *slog.Logger
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ ants.Logger = (*antsLogger)(nil)
|
||||
|
||||
// Printf implements the [ants.Logger] interface for *antsLogger.
|
||||
func (l *antsLogger) Printf(format string, args ...interface{}) {
|
||||
l.logger.Info("ants pool", slogutil.KeyMessage, fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
// mustNewPoolNonblocking creates a new instance of [*ants.Pool] configured
|
||||
// optimally for using it in DNS servers. It panics if there are errors.
|
||||
// logger must not be nil.
|
||||
func mustNewPoolNonblocking(logger *slog.Logger) (p *ants.Pool) {
|
||||
p, err := ants.NewPool(0, ants.WithOptions(ants.Options{
|
||||
ExpiryDuration: time.Minute,
|
||||
PreAlloc: false,
|
||||
Nonblocking: true,
|
||||
DisablePurge: false,
|
||||
Logger: &antsLogger{
|
||||
logger: logger,
|
||||
},
|
||||
}))
|
||||
errors.Check(err)
|
||||
|
||||
return p
|
||||
}
|
||||
@@ -126,7 +126,11 @@ func newTestService(
|
||||
}
|
||||
|
||||
geoIP := agdtest.NewGeoIP()
|
||||
geoIP.OnData = func(host string, _ netip.Addr) (l *geoip.Location, err error) {
|
||||
geoIP.OnData = func(
|
||||
_ context.Context,
|
||||
host string,
|
||||
_ netip.Addr,
|
||||
) (l *geoip.Location, err error) {
|
||||
testutil.RequireSend(pt, geoIPCh, host, dnssvctest.Timeout)
|
||||
|
||||
return loc, nil
|
||||
|
||||
@@ -118,7 +118,11 @@ func TestMiddleware_Wrap(t *testing.T) {
|
||||
}
|
||||
|
||||
geoIP := agdtest.NewGeoIP()
|
||||
geoIP.OnData = func(host string, addr netip.Addr) (l *geoip.Location, err error) {
|
||||
geoIP.OnData = func(
|
||||
_ context.Context,
|
||||
host string,
|
||||
addr netip.Addr,
|
||||
) (l *geoip.Location, err error) {
|
||||
pt := testutil.PanicT{}
|
||||
require.Equal(pt, dnssvctest.Domain, host)
|
||||
if addr.Is4() {
|
||||
@@ -419,7 +423,11 @@ func TestMiddleware_Wrap_filtering(t *testing.T) {
|
||||
)
|
||||
|
||||
geoIP := agdtest.NewGeoIP()
|
||||
geoIP.OnData = func(_ string, _ netip.Addr) (l *geoip.Location, err error) {
|
||||
geoIP.OnData = func(
|
||||
_ context.Context,
|
||||
_ string,
|
||||
_ netip.Addr,
|
||||
) (l *geoip.Location, err error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -226,7 +226,7 @@ func ipFromHTTPSRRKV(kv dns.SVCBKeyValue) (fam netutil.AddrFamily, netIP net.IP)
|
||||
// country is a wrapper around the GeoIP call that contains the handling of
|
||||
// non-critical GeoIP errors.
|
||||
func (mw *Middleware) country(ctx context.Context, host string, ip netip.Addr) (c geoip.Country) {
|
||||
l, err := mw.geoIP.Data(host, ip)
|
||||
l, err := mw.geoIP.Data(ctx, host, ip)
|
||||
if err != nil {
|
||||
// Consider GeoIP errors non-critical.
|
||||
errcoll.Collect(ctx, mw.errColl, mw.logger, "getting geoip data", err)
|
||||
|
||||
@@ -135,7 +135,11 @@ func TestMiddleware_recordQueryInfo_respCtry(t *testing.T) {
|
||||
}
|
||||
|
||||
geoIP := agdtest.NewGeoIP()
|
||||
geoIP.OnData = func(_ string, _ netip.Addr) (l *geoip.Location, err error) {
|
||||
geoIP.OnData = func(
|
||||
_ context.Context,
|
||||
_ string,
|
||||
_ netip.Addr,
|
||||
) (l *geoip.Location, err error) {
|
||||
if !tc.wantGeoIP {
|
||||
t.Error("unexpected call to geoip")
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ func TestMiddleware_Wrap_access(t *testing.T) {
|
||||
require.NoError(t, accessErr)
|
||||
|
||||
geoIP := agdtest.NewGeoIP()
|
||||
geoIP.OnData = func(_ string, _ netip.Addr) (l *geoip.Location, err error) {
|
||||
geoIP.OnData = func(_ context.Context, _ string, _ netip.Addr) (l *geoip.Location, err error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -102,7 +102,7 @@ func (mw *Middleware) locationData(
|
||||
ip netip.Addr,
|
||||
typ string,
|
||||
) (l *geoip.Location) {
|
||||
l, err := mw.geoIP.Data("", ip)
|
||||
l, err := mw.geoIP.Data(ctx, "", ip)
|
||||
if err != nil {
|
||||
// Consider GeoIP errors non-critical. Report and go on.
|
||||
err = fmt.Errorf("getting data for %s ip: %w", typ, err)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -36,6 +36,8 @@ func main() {
|
||||
resp := errors.Must(c.Do(req))
|
||||
defer slogutil.CloseAndLog(ctx, logger, resp.Body, slog.LevelError)
|
||||
|
||||
errors.Check(agdhttp.CheckStatus(resp, http.StatusOK))
|
||||
|
||||
out := errors.Must(os.OpenFile("./ecsblocklist.go", os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0o664))
|
||||
defer slogutil.CloseAndLog(ctx, logger, out, slog.LevelError)
|
||||
|
||||
@@ -48,8 +50,7 @@ func main() {
|
||||
|
||||
tmpl := template.Must(template.New("main").Parse(tmplStr))
|
||||
|
||||
err := tmpl.Execute(out, lines)
|
||||
errors.Check(err)
|
||||
errors.Check(tmpl.Execute(out, lines))
|
||||
}
|
||||
|
||||
// fakeECSBlocklistURL is the default URL from where to get ECS fake domains.
|
||||
|
||||
@@ -322,7 +322,7 @@ func (mh *mwHandler) ServeDNS(
|
||||
cr.subnet = netutil.ZeroPrefix(ecsFam)
|
||||
} else {
|
||||
loc := locFromReq(ri)
|
||||
cr.subnet, err = mw.geoIP.SubnetByLocation(loc, ecsFam)
|
||||
cr.subnet, err = mw.geoIP.SubnetByLocation(ctx, loc, ecsFam)
|
||||
if err != nil {
|
||||
return fmt.Errorf(
|
||||
"getting subnet for country %s (family: %d): %w",
|
||||
|
||||
@@ -670,6 +670,7 @@ func newWithCache(
|
||||
|
||||
// TODO(a.garipov): Actually test ASNs once we have the data.
|
||||
geoIP.OnSubnetByLocation = func(
|
||||
_ context.Context,
|
||||
l *geoip.Location,
|
||||
_ netutil.AddrFamily,
|
||||
) (n netip.Prefix, err error) {
|
||||
|
||||
@@ -25,7 +25,7 @@ import (
|
||||
func Init(l *slog.Logger, reg prometheus.Registerer) (err error) {
|
||||
expStr := os.Getenv("EXPERIMENTS")
|
||||
if expStr == "" {
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
||||
expIDs := stringutil.SplitTrimmed(expStr, ",")
|
||||
|
||||
@@ -71,7 +71,7 @@ func TestFilter(t *testing.T) {
|
||||
require.True(t, ok)
|
||||
require.NotNil(t, res.NetworkRule)
|
||||
|
||||
assert.Equal(t, tc.wantRuleStr, res.NetworkRule.RuleText)
|
||||
assert.Equal(t, tc.wantRuleStr, res.NetworkRule.Text())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,7 +81,10 @@ type StandardAccess struct {
|
||||
// NewStandardAccess creates a new properly initialized standard access. c must
|
||||
// be valid. It uses the latest cached settings if available, use the
|
||||
// [StandardAccess.Refresh] method to update them.
|
||||
func NewStandardAccess(ctx context.Context, c *StandardAccessConfig) (s *StandardAccess, err error) {
|
||||
func NewStandardAccess(
|
||||
ctx context.Context,
|
||||
c *StandardAccessConfig,
|
||||
) (s *StandardAccess, err error) {
|
||||
cachePath := filepath.Join(c.CacheDir, indexFileNameStandardProfileAccess)
|
||||
|
||||
refr, err := refreshable.New(&refreshable.Config{
|
||||
|
||||
@@ -75,6 +75,8 @@ func (s *Storage) Hashes(prefs []Prefix) (hashes []string) {
|
||||
//
|
||||
// The fact that we iterate over the [s.hashSuffixes] map twice shouldn't
|
||||
// matter, since we assume that len(hps) will be below 5 most of the time.
|
||||
//
|
||||
// TODO(a.garipov): Reuse in pools.
|
||||
b := &strings.Builder{}
|
||||
b.Grow(l * hashEncLen)
|
||||
|
||||
|
||||
@@ -106,15 +106,14 @@ func BenchmarkStorage_Hashes(b *testing.B) {
|
||||
}
|
||||
|
||||
// Most recent results:
|
||||
//
|
||||
// goos: darwin
|
||||
// goarch: arm64
|
||||
// goos: linux
|
||||
// goarch: amd64
|
||||
// pkg: github.com/AdguardTeam/AdGuardDNS/internal/filter/hashprefix
|
||||
// cpu: Apple M1 Pro
|
||||
// BenchmarkStorage_Hashes/1-8 10519970 102.7 ns/op 80 B/op 2 allocs/op
|
||||
// BenchmarkStorage_Hashes/2-8 10045784 118.1 ns/op 80 B/op 2 allocs/op
|
||||
// BenchmarkStorage_Hashes/3-8 9088449 129.2 ns/op 80 B/op 2 allocs/op
|
||||
// BenchmarkStorage_Hashes/4-8 8577764 139.4 ns/op 80 B/op 2 allocs/op
|
||||
// cpu: AMD Ryzen 7 PRO 4750U with Radeon Graphics
|
||||
// BenchmarkStorage_Hashes/1-16 6934150 174.6 ns/op 80 B/op 2 allocs/op
|
||||
// BenchmarkStorage_Hashes/2-16 5912390 203.5 ns/op 80 B/op 2 allocs/op
|
||||
// BenchmarkStorage_Hashes/3-16 5288085 225.6 ns/op 80 B/op 2 allocs/op
|
||||
// BenchmarkStorage_Hashes/4-16 4613066 259.9 ns/op 80 B/op 2 allocs/op
|
||||
}
|
||||
|
||||
func BenchmarkStorage_ResetHosts(b *testing.B) {
|
||||
@@ -137,10 +136,9 @@ func BenchmarkStorage_ResetHosts(b *testing.B) {
|
||||
require.NoError(b, err)
|
||||
|
||||
// Most recent results:
|
||||
//
|
||||
// goos: darwin
|
||||
// goarch: arm64
|
||||
// goos: linux
|
||||
// goarch: amd64
|
||||
// pkg: github.com/AdguardTeam/AdGuardDNS/internal/filter/hashprefix
|
||||
// cpu: Apple M1 Pro
|
||||
// BenchmarkStorage_ResetHosts-8 8610 128756 ns/op 118380 B/op 1009 allocs/op
|
||||
// cpu: AMD Ryzen 7 PRO 4750U with Radeon Graphics
|
||||
// BenchmarkStorage_ResetHosts-16 6469 186021 ns/op 118392 B/op 1009 allocs/op
|
||||
}
|
||||
|
||||
@@ -39,10 +39,9 @@ func BenchmarkFilter_FilterReqWithRuleLists(b *testing.B) {
|
||||
assert.NotNil(b, result)
|
||||
|
||||
// Most recent results:
|
||||
//
|
||||
// goos: linux
|
||||
// goarch: amd64
|
||||
// pkg: github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/composite
|
||||
// cpu: AMD Ryzen 7 PRO 4750U with Radeon Graphics
|
||||
// BenchmarkFilter_FilterReqWithRuleLists-16 807964 1904 ns/op 469 B/op 8 allocs/op
|
||||
// BenchmarkFilter_FilterReqWithRuleLists-16 748243 1394 ns/op 468 B/op 8 allocs/op
|
||||
}
|
||||
|
||||
@@ -13,6 +13,8 @@ import (
|
||||
// urlFilterResultCollector is an entity simplifying the collection and
|
||||
// compilation of urlfilter results. It contains per-pointer indexes of the IDs
|
||||
// of filters producing network and host rules.
|
||||
//
|
||||
// TODO(a.garipov): Reuse these structures.
|
||||
type urlFilterResultCollector struct {
|
||||
netRuleIDs map[*rules.NetworkRule]filter.ID
|
||||
hostRuleIDs map[*rules.HostRule]filter.ID
|
||||
@@ -109,7 +111,7 @@ func (c *urlFilterResultCollector) netRuleDataToResult(nr *rules.NetworkRule) (r
|
||||
|
||||
rule = filter.RuleText(svcID)
|
||||
} else {
|
||||
rule = filter.RuleText(nr.RuleText)
|
||||
rule = filter.RuleText(nr.Text())
|
||||
}
|
||||
|
||||
if nr.Whitelist {
|
||||
@@ -174,7 +176,7 @@ func (c *urlFilterResultCollector) hostRuleDataToResult(hr *rules.HostRule) (res
|
||||
|
||||
rule = filter.RuleText(svcID)
|
||||
} else {
|
||||
rule = filter.RuleText(hr.RuleText)
|
||||
rule = filter.RuleText(hr.Text())
|
||||
}
|
||||
|
||||
return &filter.ResultBlocked{
|
||||
|
||||
@@ -23,9 +23,14 @@ func ProcessDNSRewrites(
|
||||
return nil
|
||||
}
|
||||
|
||||
dnsRewriteResult := processDNSRewriteRules(dnsr)
|
||||
// Use a value and not a pointer so that the dnsRewriteResult value stays on
|
||||
// the stack and not produce an extra allocation.
|
||||
dnsRwRes := dnsRewriteResult{
|
||||
Response: dnsRewriteResultResponse{},
|
||||
}
|
||||
processDNSRewriteRules(dnsr, &dnsRwRes)
|
||||
|
||||
if resCanonName := dnsRewriteResult.CanonName; resCanonName != "" {
|
||||
if resCanonName := dnsRwRes.CanonName; resCanonName != "" {
|
||||
// Rewrite the question name to a matched CNAME.
|
||||
if strings.EqualFold(resCanonName, req.Host) {
|
||||
// A rewrite of a host to itself.
|
||||
@@ -38,24 +43,24 @@ func ProcessDNSRewrites(
|
||||
return &filter.ResultModifiedRequest{
|
||||
Msg: modReq,
|
||||
List: id,
|
||||
Rule: dnsRewriteResult.ResRuleText,
|
||||
Rule: dnsRwRes.ResRuleText,
|
||||
}
|
||||
}
|
||||
|
||||
if dnsRewriteResult.RCode != dns.RcodeSuccess {
|
||||
if dnsRwRes.RCode != dns.RcodeSuccess {
|
||||
// #nosec G115 -- The value of dnsRewriteResult.RCode comes from the
|
||||
// urlfilter package, where it either parsed by [dns.StringToRcode] or
|
||||
// defined statically.
|
||||
resp := req.Messages.NewBlockedRespRCode(req.DNS, dnsmsg.RCode(dnsRewriteResult.RCode))
|
||||
resp := req.Messages.NewBlockedRespRCode(req.DNS, dnsmsg.RCode(dnsRwRes.RCode))
|
||||
|
||||
return &filter.ResultModifiedResponse{
|
||||
Msg: resp,
|
||||
List: id,
|
||||
Rule: dnsRewriteResult.ResRuleText,
|
||||
Rule: dnsRwRes.ResRuleText,
|
||||
}
|
||||
}
|
||||
|
||||
resp, err := filterDNSRewrite(req, dnsRewriteResult)
|
||||
resp, err := filterDNSRewrite(req, &dnsRwRes)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
@@ -71,7 +76,6 @@ type dnsRewriteResult struct {
|
||||
Response dnsRewriteResultResponse
|
||||
CanonName string
|
||||
ResRuleText filter.RuleText
|
||||
Rules []*rules.NetworkRule
|
||||
RCode rules.RCode
|
||||
}
|
||||
|
||||
@@ -79,48 +83,39 @@ type dnsRewriteResult struct {
|
||||
// the server returns.
|
||||
type dnsRewriteResultResponse map[rules.RRType][]rules.RRValue
|
||||
|
||||
// processDNSRewriteRules processes DNS rewrite rules in dnsr. The result will
|
||||
// have either CanonName or RCode or Response set.
|
||||
//
|
||||
// TODO(a.garipov): Reuse dnsRewriteResult structures.
|
||||
func processDNSRewriteRules(dnsr []*rules.NetworkRule) (res *dnsRewriteResult) {
|
||||
dnsrr := &dnsRewriteResult{
|
||||
Response: dnsRewriteResultResponse{},
|
||||
}
|
||||
|
||||
// processDNSRewriteRules processes DNS rewrite rules in dnsr. res will have
|
||||
// either CanonName or RCode or Response set. res and res.Response must not be
|
||||
// nil.
|
||||
func processDNSRewriteRules(dnsr []*rules.NetworkRule, res *dnsRewriteResult) {
|
||||
for _, rule := range dnsr {
|
||||
dr := rule.DNSRewrite
|
||||
if dr.NewCNAME != "" {
|
||||
// NewCNAME rules have a higher priority than other rules.
|
||||
return &dnsRewriteResult{
|
||||
ResRuleText: filter.RuleText(rule.RuleText),
|
||||
CanonName: dr.NewCNAME,
|
||||
}
|
||||
res.CanonName = dr.NewCNAME
|
||||
res.ResRuleText = filter.RuleText(rule.Text())
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
switch dr.RCode {
|
||||
case dns.RcodeSuccess:
|
||||
dnsrr.RCode = dr.RCode
|
||||
dnsrr.Response[dr.RRType] = append(dnsrr.Response[dr.RRType], dr.Value)
|
||||
dnsrr.Rules = append(dnsrr.Rules, rule)
|
||||
default:
|
||||
// RcodeRefused and other such codes have higher priority. Return
|
||||
// immediately.
|
||||
return &dnsRewriteResult{
|
||||
ResRuleText: filter.RuleText(rule.RuleText),
|
||||
RCode: dr.RCode,
|
||||
}
|
||||
if dr.RCode != dns.RcodeSuccess {
|
||||
// [dns.RcodeRefused] and other such codes have higher priority.
|
||||
// Set and return immediately.
|
||||
res.ResRuleText = filter.RuleText(rule.Text())
|
||||
res.RCode = dr.RCode
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
res.Response[dr.RRType] = append(res.Response[dr.RRType], dr.Value)
|
||||
res.RCode = dr.RCode
|
||||
}
|
||||
|
||||
return dnsrr
|
||||
}
|
||||
|
||||
// filterDNSRewrite handles dnsrewrite filters. It constructs a DNS response
|
||||
// and returns it. dnsrr.RCode should be [dns.RcodeSuccess] and contain a
|
||||
// non-empty dnsrr.Response.
|
||||
func filterDNSRewrite(req *filter.Request, dnsrr *dnsRewriteResult) (resp *dns.Msg, err error) {
|
||||
if dnsrr.Response == nil {
|
||||
// and returns it. req and res must not be nil. res.RCode should be
|
||||
// [dns.RcodeSuccess] and contain a non-empty Response.
|
||||
func filterDNSRewrite(req *filter.Request, res *dnsRewriteResult) (resp *dns.Msg, err error) {
|
||||
if res.Response == nil {
|
||||
return nil, errors.Error("no dns rewrite rule responses")
|
||||
}
|
||||
|
||||
@@ -130,7 +125,7 @@ func filterDNSRewrite(req *filter.Request, dnsrr *dnsRewriteResult) (resp *dns.M
|
||||
resp = req.Messages.NewBlockedRespRCode(dnsReq, dns.RcodeSuccess)
|
||||
|
||||
rr := dnsReq.Question[0].Qtype
|
||||
values := dnsrr.Response[rr]
|
||||
values := res.Response[rr]
|
||||
for i, v := range values {
|
||||
var ans dns.RR
|
||||
ans, err = filterDNSRewriteResponse(req, rr, v)
|
||||
|
||||
@@ -132,7 +132,6 @@ func BenchmarkRefreshable_SetURLFilterResult(b *testing.B) {
|
||||
)
|
||||
|
||||
ctx := testutil.ContextWithTimeout(b, filtertest.Timeout)
|
||||
res := &urlfilter.DNSResult{}
|
||||
|
||||
b.Run("blocked", func(b *testing.B) {
|
||||
req := &urlfilter.DNSRequest{
|
||||
@@ -141,7 +140,12 @@ func BenchmarkRefreshable_SetURLFilterResult(b *testing.B) {
|
||||
DNSType: dns.TypeA,
|
||||
}
|
||||
|
||||
var ok bool
|
||||
res := &urlfilter.DNSResult{}
|
||||
|
||||
// Warmup to fill the slices.
|
||||
ok := rl.SetURLFilterResult(ctx, req, res)
|
||||
require.True(b, ok)
|
||||
|
||||
b.ReportAllocs()
|
||||
for b.Loop() {
|
||||
res.Reset()
|
||||
@@ -158,7 +162,12 @@ func BenchmarkRefreshable_SetURLFilterResult(b *testing.B) {
|
||||
DNSType: dns.TypeA,
|
||||
}
|
||||
|
||||
var ok bool
|
||||
res := &urlfilter.DNSResult{}
|
||||
|
||||
// Warmup to fill the slices.
|
||||
ok := rl.SetURLFilterResult(ctx, req, res)
|
||||
require.False(b, ok)
|
||||
|
||||
b.ReportAllocs()
|
||||
for b.Loop() {
|
||||
res.Reset()
|
||||
@@ -169,11 +178,10 @@ func BenchmarkRefreshable_SetURLFilterResult(b *testing.B) {
|
||||
})
|
||||
|
||||
// Most recent results:
|
||||
//
|
||||
// goos: linux
|
||||
// goarch: amd64
|
||||
// pkg: github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/rulelist
|
||||
// cpu: AMD Ryzen 7 PRO 4750U with Radeon Graphics
|
||||
// BenchmarkRefreshable_SetURLFilterResult/blocked-16 1340384 918.6 ns/op 24 B/op 1 allocs/op
|
||||
// BenchmarkRefreshable_SetURLFilterResult/other-16 2127038 589.3 ns/op 24 B/op 1 allocs/op
|
||||
// BenchmarkRefreshable_SetURLFilterResult/blocked-16 1352236 887.3 ns/op 24 B/op 1 allocs/op
|
||||
// BenchmarkRefreshable_SetURLFilterResult/other-16 2772519 432.6 ns/op 24 B/op 1 allocs/op
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ func (f *baseFilter) SetURLFilterResult(
|
||||
return false
|
||||
}
|
||||
|
||||
shallowCloneInto(res, cachedRes)
|
||||
cachedRes.ShallowCloneInto(res)
|
||||
|
||||
return true
|
||||
}
|
||||
@@ -111,7 +111,7 @@ func (f *baseFilter) SetURLFilterResult(
|
||||
|
||||
if ok {
|
||||
cachedRes = &urlfilter.DNSResult{}
|
||||
shallowCloneInto(cachedRes, res)
|
||||
res.ShallowCloneInto(cachedRes)
|
||||
}
|
||||
|
||||
f.cache.Set(cacheKey, cachedRes)
|
||||
@@ -119,17 +119,6 @@ func (f *baseFilter) SetURLFilterResult(
|
||||
return ok
|
||||
}
|
||||
|
||||
// shallowCloneInto sets properties in other, as if making a shallow clone.
|
||||
// other must not be nil and should be empty or reset using [DNSResult.Reset].
|
||||
//
|
||||
// TODO(a.garipov): Add to urlfilter.
|
||||
func shallowCloneInto(other, res *urlfilter.DNSResult) {
|
||||
other.NetworkRule = res.NetworkRule
|
||||
other.HostRulesV4 = append(other.HostRulesV4, res.HostRulesV4...)
|
||||
other.HostRulesV6 = append(other.HostRulesV6, res.HostRulesV6...)
|
||||
other.NetworkRules = append(other.NetworkRules, res.NetworkRules...)
|
||||
}
|
||||
|
||||
// ID returns the filter list ID of this rule list filter, as well as the ID of
|
||||
// the blocked service, if any.
|
||||
func (f *baseFilter) ID() (id filter.ID, svcID filter.BlockedServiceID) {
|
||||
|
||||
@@ -41,7 +41,6 @@ func BenchmarkBaseFilter_SetURLFilterResult(b *testing.B) {
|
||||
const qt = dns.TypeA
|
||||
|
||||
ctx := context.Background()
|
||||
res := &urlfilter.DNSResult{}
|
||||
|
||||
b.Run("blocked", func(b *testing.B) {
|
||||
req := &urlfilter.DNSRequest{
|
||||
@@ -50,7 +49,12 @@ func BenchmarkBaseFilter_SetURLFilterResult(b *testing.B) {
|
||||
DNSType: qt,
|
||||
}
|
||||
|
||||
var ok bool
|
||||
res := &urlfilter.DNSResult{}
|
||||
|
||||
// Warmup to fill the slices.
|
||||
ok := f.SetURLFilterResult(ctx, req, res)
|
||||
require.True(b, ok)
|
||||
|
||||
b.ReportAllocs()
|
||||
for b.Loop() {
|
||||
res.Reset()
|
||||
@@ -67,7 +71,12 @@ func BenchmarkBaseFilter_SetURLFilterResult(b *testing.B) {
|
||||
DNSType: qt,
|
||||
}
|
||||
|
||||
var ok bool
|
||||
res := &urlfilter.DNSResult{}
|
||||
|
||||
// Warmup to fill the slices.
|
||||
ok := f.SetURLFilterResult(ctx, req, res)
|
||||
require.False(b, ok)
|
||||
|
||||
b.ReportAllocs()
|
||||
for b.Loop() {
|
||||
res.Reset()
|
||||
@@ -78,11 +87,10 @@ func BenchmarkBaseFilter_SetURLFilterResult(b *testing.B) {
|
||||
})
|
||||
|
||||
// Most recent results:
|
||||
//
|
||||
// goos: linux
|
||||
// goarch: amd64
|
||||
// pkg: github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/rulelist
|
||||
// cpu: AMD Ryzen 7 PRO 4750U with Radeon Graphics
|
||||
// BenchmarkBaseFilter_SetURLFilterResult/blocked-16 906486 1372 ns/op 24 B/op 1 allocs/op
|
||||
// BenchmarkBaseFilter_SetURLFilterResult/other-16 2203561 609.1 ns/op 24 B/op 1 allocs/op
|
||||
// BenchmarkBaseFilter_SetURLFilterResult/blocked-16 911409 1315 ns/op 24 B/op 1 allocs/op
|
||||
// BenchmarkBaseFilter_SetURLFilterResult/other-16 2824462 425.0 ns/op 24 B/op 1 allocs/op
|
||||
}
|
||||
|
||||
@@ -146,9 +146,6 @@ func BenchmarkFilter_FilterRequestUF(b *testing.B) {
|
||||
f := newTestFilter(b)
|
||||
|
||||
b.Run("no_match", func(b *testing.B) {
|
||||
var res filter.Result
|
||||
var err error
|
||||
|
||||
ctx := testutil.ContextWithTimeout(b, filtertest.Timeout)
|
||||
req := newRequest(b, testOther, qt)
|
||||
|
||||
@@ -159,20 +156,22 @@ func BenchmarkFilter_FilterRequestUF(b *testing.B) {
|
||||
|
||||
ufRes := &urlfilter.DNSResult{}
|
||||
|
||||
// Warmup to fill the slices.
|
||||
res, err := f.FilterRequestUF(ctx, req, ufReq, ufRes)
|
||||
require.NoError(b, err)
|
||||
require.Nil(b, res)
|
||||
|
||||
b.ReportAllocs()
|
||||
for b.Loop() {
|
||||
ufRes.Reset()
|
||||
res, err = f.FilterRequestUF(ctx, req, ufReq, ufRes)
|
||||
}
|
||||
|
||||
assert.NoError(b, err)
|
||||
assert.Nil(b, res)
|
||||
require.NoError(b, err)
|
||||
require.Nil(b, res)
|
||||
})
|
||||
|
||||
b.Run("ip", func(b *testing.B) {
|
||||
var res filter.Result
|
||||
var err error
|
||||
|
||||
ctx := testutil.ContextWithTimeout(b, filtertest.Timeout)
|
||||
req := newRequest(b, testEngineWithIP, qt)
|
||||
|
||||
@@ -183,20 +182,22 @@ func BenchmarkFilter_FilterRequestUF(b *testing.B) {
|
||||
|
||||
ufRes := &urlfilter.DNSResult{}
|
||||
|
||||
// Warmup to fill the slices.
|
||||
res, err := f.FilterRequestUF(ctx, req, ufReq, ufRes)
|
||||
require.NoError(b, err)
|
||||
require.NotNil(b, res)
|
||||
|
||||
b.ReportAllocs()
|
||||
for b.Loop() {
|
||||
ufRes.Reset()
|
||||
res, err = f.FilterRequestUF(ctx, req, ufReq, ufRes)
|
||||
}
|
||||
|
||||
assert.NoError(b, err)
|
||||
assert.NotNil(b, res)
|
||||
require.NoError(b, err)
|
||||
require.NotNil(b, res)
|
||||
})
|
||||
|
||||
b.Run("domain", func(b *testing.B) {
|
||||
var res filter.Result
|
||||
var err error
|
||||
|
||||
ctx := testutil.ContextWithTimeout(b, filtertest.Timeout)
|
||||
req := newRequest(b, testEngineWithDomain, qt)
|
||||
|
||||
@@ -207,25 +208,29 @@ func BenchmarkFilter_FilterRequestUF(b *testing.B) {
|
||||
|
||||
ufRes := &urlfilter.DNSResult{}
|
||||
|
||||
// Warmup to fill the slices.
|
||||
res, err := f.FilterRequestUF(ctx, req, ufReq, ufRes)
|
||||
require.NoError(b, err)
|
||||
require.NotNil(b, res)
|
||||
|
||||
b.ReportAllocs()
|
||||
for b.Loop() {
|
||||
ufRes.Reset()
|
||||
res, err = f.FilterRequestUF(ctx, req, ufReq, ufRes)
|
||||
}
|
||||
|
||||
assert.NoError(b, err)
|
||||
assert.NotNil(b, res)
|
||||
require.NoError(b, err)
|
||||
require.NotNil(b, res)
|
||||
})
|
||||
|
||||
// Most recent results:
|
||||
//
|
||||
// goos: darwin
|
||||
// goarch: arm64
|
||||
// pkg: github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/safesearch
|
||||
// cpu: Apple M1 Pro
|
||||
// BenchmarkFilter_FilterRequestUF/no_match-8 27804783 43.23 ns/op 0 B/op 0 allocs/op
|
||||
// BenchmarkFilter_FilterRequestUF/ip-8 3136018 382.3 ns/op 672 B/op 11 allocs/op
|
||||
// BenchmarkFilter_FilterRequestUF/domain-8 4929343 237.8 ns/op 400 B/op 7 allocs/op
|
||||
// goos: linux
|
||||
// goarch: amd64
|
||||
// pkg: github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/safesearch
|
||||
// cpu: AMD Ryzen 7 PRO 4750U with Radeon Graphics
|
||||
// BenchmarkFilter_FilterRequestUF/no_match-16 18863545 62.65 ns/op 0 B/op 0 allocs/op
|
||||
// BenchmarkFilter_FilterRequestUF/ip-16 1516423 784.0 ns/op 664 B/op 10 allocs/op
|
||||
// BenchmarkFilter_FilterRequestUF/domain-16 2859573 424.6 ns/op 320 B/op 6 allocs/op
|
||||
}
|
||||
|
||||
// newTestFilter creates a new [*safesearch.Filter] for testing, and refreshes
|
||||
|
||||
@@ -35,15 +35,13 @@ func main() {
|
||||
resp := errors.Must(c.Do(req))
|
||||
defer slogutil.CloseAndLog(ctx, logger, resp.Body, slog.LevelError)
|
||||
|
||||
err := agdhttp.CheckStatus(resp, http.StatusOK)
|
||||
errors.Check(err)
|
||||
errors.Check(agdhttp.CheckStatus(resp, http.StatusOK))
|
||||
|
||||
out := errors.Must(os.OpenFile("./asntops.go", os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0o664))
|
||||
defer slogutil.CloseAndLog(ctx, logger, out, slog.LevelError)
|
||||
|
||||
defaultCountryTopASNs := map[geoip.Country][]geoip.ASN{}
|
||||
err = json.NewDecoder(resp.Body).Decode(&defaultCountryTopASNs)
|
||||
errors.Check(err)
|
||||
errors.Check(json.NewDecoder(resp.Body).Decode(&defaultCountryTopASNs))
|
||||
|
||||
// Don't use a *container.MapSet here, because the map is iterated over in
|
||||
// the template.
|
||||
@@ -67,9 +65,7 @@ func main() {
|
||||
}
|
||||
|
||||
tmpl := template.Must(template.New("main").Parse(tmplStr))
|
||||
|
||||
err = tmpl.Execute(out, tmplData)
|
||||
errors.Check(err)
|
||||
errors.Check(tmpl.Execute(out, tmplData))
|
||||
}
|
||||
|
||||
// countriesASNURL is the default URL to get the per-country top ASN statistics
|
||||
|
||||
@@ -36,6 +36,8 @@ func main() {
|
||||
resp := errors.Must(c.Do(req))
|
||||
defer slogutil.CloseAndLog(ctx, logger, resp.Body, slog.LevelError)
|
||||
|
||||
errors.Check(agdhttp.CheckStatus(resp, http.StatusOK))
|
||||
|
||||
out := errors.Must(os.OpenFile("./country.go", os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0o664))
|
||||
defer slogutil.CloseAndLog(ctx, logger, out, slog.LevelError)
|
||||
|
||||
@@ -52,9 +54,7 @@ func main() {
|
||||
})
|
||||
|
||||
tmpl := template.Must(template.New("main").Parse(tmplStr))
|
||||
|
||||
err := tmpl.Execute(out, rows)
|
||||
errors.Check(err)
|
||||
errors.Check(tmpl.Execute(out, rows))
|
||||
}
|
||||
|
||||
// csvURL is the default URL of the information about country codes.
|
||||
|
||||
@@ -206,7 +206,11 @@ var _ Interface = (*File)(nil)
|
||||
// subnet is returned. If the information about the most used ASNs is not
|
||||
// available, the first subnet from the country that is broad enough (see
|
||||
// resetCountrySubnets) is chosen.
|
||||
func (f *File) SubnetByLocation(l *Location, fam netutil.AddrFamily) (n netip.Prefix, err error) {
|
||||
func (f *File) SubnetByLocation(
|
||||
_ context.Context,
|
||||
l *Location,
|
||||
fam netutil.AddrFamily,
|
||||
) (n netip.Prefix, err error) {
|
||||
var ctrySubnets countrySubnets
|
||||
var locSubnets locationSubnets
|
||||
|
||||
@@ -247,10 +251,7 @@ func (f *File) SubnetByLocation(l *Location, fam netutil.AddrFamily) (n netip.Pr
|
||||
|
||||
// Data implements the Interface interface for *File. If ip is netip.Addr{},
|
||||
// Data tries to lookup and return the data based on host, unless it's empty.
|
||||
func (f *File) Data(host string, ip netip.Addr) (l *Location, err error) {
|
||||
// TODO(e.burkov): Add context to the [Interface] methods.
|
||||
ctx := context.TODO()
|
||||
|
||||
func (f *File) Data(ctx context.Context, host string, ip netip.Addr) (l *Location, err error) {
|
||||
if ip == (netip.Addr{}) {
|
||||
return f.dataByHost(ctx, host), nil
|
||||
} else if ip.Is4In6() {
|
||||
@@ -425,14 +426,11 @@ func (f *File) resetSubnetMappings(
|
||||
asn *maxminddb.Reader,
|
||||
country *maxminddb.Reader,
|
||||
) (err error) {
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(2)
|
||||
wg := &sync.WaitGroup{}
|
||||
|
||||
var locErr, ctryErr error
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
||||
wg.Go(func() {
|
||||
var ipv4, ipv6 locationSubnets
|
||||
ipv4, ipv6, locErr = f.resetLocationSubnets(ctx, asn, country)
|
||||
|
||||
@@ -448,11 +446,9 @@ func (f *File) resetSubnetMappings(
|
||||
defer f.mu.Unlock()
|
||||
|
||||
f.ipv4LocationSubnets, f.ipv6LocationSubnets = ipv4, ipv6
|
||||
}()
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
})
|
||||
|
||||
wg.Go(func() {
|
||||
var ipv4, ipv6 countrySubnets
|
||||
ipv4, ipv6, ctryErr = resetCountrySubnets(ctx, f.logger, country)
|
||||
|
||||
@@ -466,7 +462,7 @@ func (f *File) resetSubnetMappings(
|
||||
defer f.mu.Unlock()
|
||||
|
||||
f.ipv4CountrySubnets, f.ipv6CountrySubnets = ipv4, ipv6
|
||||
}()
|
||||
})
|
||||
|
||||
wg.Wait()
|
||||
|
||||
|
||||
@@ -43,13 +43,14 @@ func TestFile_Data_cityDB(t *testing.T) {
|
||||
}
|
||||
|
||||
g := newFile(t, conf)
|
||||
ctx := testutil.ContextWithTimeout(t, testTimeout)
|
||||
|
||||
d, err := g.Data(testHost, testIPWithASN)
|
||||
d, err := g.Data(ctx, testHost, testIPWithASN)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, testASN, d.ASN)
|
||||
|
||||
d, err = g.Data(testHost, testIPWithSubdiv)
|
||||
d, err = g.Data(ctx, testHost, testIPWithSubdiv)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, testCtry, d.Country)
|
||||
@@ -71,13 +72,14 @@ func TestFile_Data_countryDB(t *testing.T) {
|
||||
}
|
||||
|
||||
g := newFile(t, conf)
|
||||
ctx := testutil.ContextWithTimeout(t, testTimeout)
|
||||
|
||||
d, err := g.Data(testHost, testIPWithASN)
|
||||
d, err := g.Data(ctx, testHost, testIPWithASN)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, testASN, d.ASN)
|
||||
|
||||
d, err = g.Data(testHost, testIPWithSubdiv)
|
||||
d, err = g.Data(ctx, testHost, testIPWithSubdiv)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, testCtry, d.Country)
|
||||
@@ -100,17 +102,19 @@ func TestFile_Data_hostCache(t *testing.T) {
|
||||
|
||||
g := newFile(t, conf)
|
||||
|
||||
d, err := g.Data(testHost, testIPWithASN)
|
||||
ctx := testutil.ContextWithTimeout(t, testTimeout)
|
||||
|
||||
d, err := g.Data(ctx, testHost, testIPWithASN)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, testASN, d.ASN)
|
||||
|
||||
d, err = g.Data(testHost, netip.Addr{})
|
||||
d, err = g.Data(ctx, testHost, netip.Addr{})
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, testASN, d.ASN)
|
||||
|
||||
d, err = g.Data(testOtherHost, netip.Addr{})
|
||||
d, err = g.Data(ctx, testOtherHost, netip.Addr{})
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Nil(t, d)
|
||||
@@ -177,12 +181,15 @@ func TestFile_SubnetByLocation(t *testing.T) {
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
ctrySubnet, err := g.SubnetByLocation(&geoip.Location{
|
||||
Country: tc.country,
|
||||
Continent: "",
|
||||
TopSubdivision: tc.subdiv,
|
||||
ASN: tc.asn,
|
||||
}, tc.fam)
|
||||
ctx := testutil.ContextWithTimeout(t, testTimeout)
|
||||
ctrySubnet, err := g.SubnetByLocation(
|
||||
ctx,
|
||||
&geoip.Location{
|
||||
Country: tc.country,
|
||||
Continent: "",
|
||||
TopSubdivision: tc.subdiv,
|
||||
ASN: tc.asn,
|
||||
}, tc.fam)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, tc.want, ctrySubnet)
|
||||
@@ -204,6 +211,7 @@ func BenchmarkFile_Data(b *testing.B) {
|
||||
}
|
||||
|
||||
g := newFile(b, conf)
|
||||
ctx := testutil.ContextWithTimeout(b, testTimeout)
|
||||
|
||||
ipCountry1 := testIPWithCountry
|
||||
|
||||
@@ -218,7 +226,7 @@ func BenchmarkFile_Data(b *testing.B) {
|
||||
b.Run("cache", func(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
for b.Loop() {
|
||||
loc, err = g.Data(testHost, testIPWithASN)
|
||||
loc, err = g.Data(ctx, testHost, testIPWithASN)
|
||||
}
|
||||
|
||||
assert.Equal(b, testASN, loc.ASN)
|
||||
@@ -231,9 +239,9 @@ func BenchmarkFile_Data(b *testing.B) {
|
||||
for i := 0; b.Loop(); i++ {
|
||||
// Alternate between the two IPs to force cache misses.
|
||||
if i%2 == 0 {
|
||||
loc, err = g.Data(testHost, ipCountry1)
|
||||
loc, err = g.Data(ctx, testHost, ipCountry1)
|
||||
} else {
|
||||
loc, err = g.Data(testHost, ipCountry2)
|
||||
loc, err = g.Data(ctx, testHost, ipCountry2)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
package geoip
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/netip"
|
||||
|
||||
"github.com/AdguardTeam/golibs/netutil"
|
||||
@@ -13,9 +14,13 @@ type Interface interface {
|
||||
// SubnetByLocation returns the default subnet for location, if there is
|
||||
// one. If there isn't, n is an unspecified subnet. fam must be either
|
||||
// [netutil.AddrFamilyIPv4] or [netutil.AddrFamilyIPv6].
|
||||
SubnetByLocation(l *Location, fam netutil.AddrFamily) (n netip.Prefix, err error)
|
||||
SubnetByLocation(
|
||||
ctx context.Context,
|
||||
l *Location,
|
||||
fam netutil.AddrFamily,
|
||||
) (n netip.Prefix, err error)
|
||||
|
||||
// Data returns the GeoIP data for ip. It may use host to get cached GeoIP
|
||||
// data if ip is netip.Addr{}.
|
||||
Data(host string, ip netip.Addr) (l *Location, err error)
|
||||
Data(ctx context.Context, host string, ip netip.Addr) (l *Location, err error)
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ func IncrementCond(cond bool, trueCounter, falseCounter prometheus.Counter) {
|
||||
// SetAdditionalInfo does nothing. reg must not be nil.
|
||||
func SetAdditionalInfo(reg prometheus.Registerer, info map[string]string) (err error) {
|
||||
if info == nil {
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
||||
gauge := prometheus.NewGauge(prometheus.GaugeOpts{
|
||||
|
||||
@@ -16,7 +16,6 @@ import (
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/geoip"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/metrics"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/profiledb"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/querylog"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/rulestat"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/tlsconfig"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/websvc"
|
||||
@@ -48,7 +47,6 @@ var (
|
||||
_ geoip.Metrics = (*metrics.GeoIP)(nil)
|
||||
_ hashprefix.Metrics = (*metrics.HashPrefixFilter)(nil)
|
||||
_ profiledb.Metrics = (*metrics.ProfileDB)(nil)
|
||||
_ querylog.Metrics = (*metrics.QueryLog)(nil)
|
||||
_ rulestat.Metrics = (*metrics.RuleStat)(nil)
|
||||
_ tlsconfig.CustomDomainDBMetrics = (*metrics.CustomDomainDB)(nil)
|
||||
_ tlsconfig.ManagerMetrics = (*metrics.TLSConfigManager)(nil)
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/querylog"
|
||||
"github.com/AdguardTeam/golibs/container"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/c2h5oh/datasize"
|
||||
@@ -82,19 +83,17 @@ func NewQueryLog(namespace string, reg prometheus.Registerer) (m *QueryLog, err
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// IncrementItemsCount implements the [querylog.Metrics] interface for
|
||||
// *QueryLog.
|
||||
func (m *QueryLog) IncrementItemsCount(_ context.Context) {
|
||||
m.itemsTotal.Inc()
|
||||
}
|
||||
// type check
|
||||
var _ querylog.Metrics = (*QueryLog)(nil)
|
||||
|
||||
// ObserveItemSize implements the [querylog.Metrics] interface for *QueryLog.
|
||||
func (m *QueryLog) ObserveItemSize(_ context.Context, size datasize.ByteSize) {
|
||||
m.itemSize.Observe(float64(size))
|
||||
}
|
||||
|
||||
// ObserveWriteDuration implements the [querylog.Metrics] interface for
|
||||
// ObserveWrite implements the [querylog.Metrics] interface for
|
||||
// *QueryLog.
|
||||
func (m *QueryLog) ObserveWriteDuration(_ context.Context, dur time.Duration) {
|
||||
func (m *QueryLog) ObserveWrite(_ context.Context, dur time.Duration) {
|
||||
m.itemsTotal.Inc()
|
||||
m.writeDuration.Observe(dur.Seconds())
|
||||
}
|
||||
|
||||
@@ -284,16 +284,13 @@ func matchServerNames(sni string, devDomains []string, srvCerts []*tls.Certifica
|
||||
|
||||
// matchDeviceDomains matches sni to device domains.
|
||||
func matchDeviceDomains(sni string, domains []string) (matchedDomain string) {
|
||||
matchedDomain = ""
|
||||
for _, domain := range domains {
|
||||
if netutil.IsImmediateSubdomain(sni, domain) {
|
||||
matchedDomain = domain
|
||||
|
||||
break
|
||||
return domain
|
||||
}
|
||||
}
|
||||
|
||||
return matchedDomain
|
||||
return ""
|
||||
}
|
||||
|
||||
// matchSrvCerts matches sni to DNSNames in srvCerts.
|
||||
|
||||
@@ -293,7 +293,7 @@ func (db *Default) setProfileCerts(ctx context.Context, p *agd.Profile) (err err
|
||||
if !cd.Enabled {
|
||||
// Assume that the expired pending paths are cleaned up either by
|
||||
// [db.IsValidWellKnownRequest] or in a full sync.
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
||||
var errs []error
|
||||
|
||||
@@ -287,8 +287,9 @@ func (x *CustomDomainConfig) toInternal() (c *agd.CustomDomainConfig, err error)
|
||||
state = &agd.CustomDomainStateCurrent{
|
||||
NotBefore: s.StateCurrent.NotBefore.AsTime(),
|
||||
NotAfter: s.StateCurrent.NotAfter.AsTime(),
|
||||
CertName: s.StateCurrent.CertName,
|
||||
Enabled: s.StateCurrent.Enabled,
|
||||
// Consider certificate names to have been prevalidated.
|
||||
CertName: agd.CertificateName(s.StateCurrent.CertName),
|
||||
Enabled: s.StateCurrent.Enabled,
|
||||
}
|
||||
case *CustomDomainConfig_StatePending_:
|
||||
state = &agd.CustomDomainStatePending{
|
||||
@@ -524,7 +525,7 @@ func customDomainConfigsToProtobuf(
|
||||
curr := &CustomDomainConfig_StateCurrent{
|
||||
NotBefore: timestamppb.New(s.NotBefore),
|
||||
NotAfter: timestamppb.New(s.NotAfter),
|
||||
CertName: s.CertName,
|
||||
CertName: string(s.CertName),
|
||||
Enabled: s.Enabled,
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ import (
|
||||
"github.com/c2h5oh/datasize"
|
||||
)
|
||||
|
||||
// FileSystemConfig is the configuration of the file system query log. All
|
||||
// FileSystemConfig is the configuration of the filesystem query log. All
|
||||
// fields must not be empty.
|
||||
type FileSystemConfig struct {
|
||||
// Logger is used for debug logging. It must not be nil.
|
||||
@@ -32,6 +32,11 @@ type FileSystemConfig struct {
|
||||
// not be nil.
|
||||
Metrics Metrics
|
||||
|
||||
// Semaphore is used to limit the number of simultaneous writes. This is
|
||||
// important to prevent excessive thread creation arising from i/o latency
|
||||
// issues. It must not be nil.
|
||||
Semaphore syncutil.Semaphore
|
||||
|
||||
// Path is the path to the log file. It must not be empty.
|
||||
Path string
|
||||
|
||||
@@ -46,27 +51,24 @@ type entryBuffer struct {
|
||||
buf *bytes.Buffer
|
||||
}
|
||||
|
||||
// FileSystem is the file system implementation of the AdGuard DNS query log.
|
||||
// FileSystem is the filesystem implementation of the AdGuard DNS query log.
|
||||
type FileSystem struct {
|
||||
// logger is used for debug logging.
|
||||
logger *slog.Logger
|
||||
|
||||
// bufferPool is a pool with [*entryBuffer] instances used to avoid extra
|
||||
// allocations when serializing query log items to JSON and writing them.
|
||||
bufferPool *syncutil.Pool[entryBuffer]
|
||||
|
||||
logger *slog.Logger
|
||||
|
||||
// rng is used to generate random numbers for the "rn" property in the
|
||||
// resulting JSON.
|
||||
rng *rand.Rand
|
||||
|
||||
// metrics is used for the collection of the query log statistics.
|
||||
metrics Metrics
|
||||
|
||||
// path is the path to the query log file.
|
||||
path string
|
||||
sema syncutil.Semaphore
|
||||
path string
|
||||
}
|
||||
|
||||
// NewFileSystem creates a new file system query log. The log is safe for
|
||||
// NewFileSystem creates a new filesystem query log. The log is safe for
|
||||
// concurrent use. c must not be nil.
|
||||
func NewFileSystem(c *FileSystemConfig) (l *FileSystem) {
|
||||
src := rand.NewChaCha8(c.RandSeed)
|
||||
@@ -83,6 +85,7 @@ func NewFileSystem(c *FileSystemConfig) (l *FileSystem) {
|
||||
}),
|
||||
rng: rng,
|
||||
metrics: c.Metrics,
|
||||
sema: c.Semaphore,
|
||||
path: c.Path,
|
||||
}
|
||||
}
|
||||
@@ -104,10 +107,13 @@ func (l *FileSystem) Write(ctx context.Context, e *Entry) (err error) {
|
||||
}()
|
||||
|
||||
startTime := time.Now()
|
||||
defer func() {
|
||||
l.metrics.ObserveWriteDuration(ctx, time.Since(startTime))
|
||||
l.metrics.IncrementItemsCount(ctx)
|
||||
}()
|
||||
defer func() { l.metrics.ObserveWrite(ctx, time.Since(startTime)) }()
|
||||
|
||||
err = l.sema.Acquire(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("acquiring semaphore: %w", err)
|
||||
}
|
||||
defer l.sema.Release()
|
||||
|
||||
entBuf := l.bufferPool.Get()
|
||||
defer l.bufferPool.Put(entBuf)
|
||||
@@ -141,8 +147,7 @@ func (l *FileSystem) Write(ctx context.Context, e *Entry) (err error) {
|
||||
RemoteIP: remoteIP,
|
||||
}
|
||||
|
||||
var f *os.File
|
||||
f, err = os.OpenFile(l.path, agd.DefaultWOFlags, agd.DefaultPerm)
|
||||
f, err := os.OpenFile(l.path, agd.DefaultWOFlags, agd.DefaultPerm)
|
||||
if err != nil {
|
||||
return fmt.Errorf("opening query log file: %w", err)
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/geoip"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/querylog"
|
||||
"github.com/AdguardTeam/golibs/logutil/slogutil"
|
||||
"github.com/AdguardTeam/golibs/syncutil"
|
||||
"github.com/miekg/dns"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -20,10 +21,11 @@ func TestFileSystem_Write(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
l := querylog.NewFileSystem(&querylog.FileSystemConfig{
|
||||
Logger: slogutil.NewDiscardLogger(),
|
||||
Metrics: querylog.EmptyMetrics{},
|
||||
Path: f.Name(),
|
||||
RandSeed: [32]byte{},
|
||||
Logger: slogutil.NewDiscardLogger(),
|
||||
Metrics: querylog.EmptyMetrics{},
|
||||
Semaphore: syncutil.EmptySemaphore{},
|
||||
Path: f.Name(),
|
||||
RandSeed: [32]byte{},
|
||||
})
|
||||
|
||||
ctx := context.Background()
|
||||
@@ -110,10 +112,11 @@ func BenchmarkFileSystem_Write_file(b *testing.B) {
|
||||
require.NoError(b, err)
|
||||
|
||||
l := querylog.NewFileSystem(&querylog.FileSystemConfig{
|
||||
Logger: slogutil.NewDiscardLogger(),
|
||||
Metrics: querylog.EmptyMetrics{},
|
||||
Path: f.Name(),
|
||||
RandSeed: [32]byte{},
|
||||
Logger: slogutil.NewDiscardLogger(),
|
||||
Metrics: querylog.EmptyMetrics{},
|
||||
Semaphore: syncutil.EmptySemaphore{},
|
||||
Path: f.Name(),
|
||||
RandSeed: [32]byte{},
|
||||
})
|
||||
|
||||
e := testEntry()
|
||||
|
||||
@@ -10,15 +10,12 @@ import (
|
||||
// Metrics is an interface that is used for the collection of the query log
|
||||
// statistics.
|
||||
type Metrics interface {
|
||||
// IncrementItemsCount increments the total number of query log entries
|
||||
// written.
|
||||
IncrementItemsCount(ctx context.Context)
|
||||
|
||||
// ObserveItemSize stores the size of written query log entry.
|
||||
ObserveItemSize(ctx context.Context, size datasize.ByteSize)
|
||||
|
||||
// ObserveWriteDuration stores the duration of the write operation.
|
||||
ObserveWriteDuration(ctx context.Context, dur time.Duration)
|
||||
// ObserveWrite stores the duration of the write operation and increments
|
||||
// the write counter.
|
||||
ObserveWrite(ctx context.Context, dur time.Duration)
|
||||
}
|
||||
|
||||
// EmptyMetrics is the implementation of the [Metrics] interface that does
|
||||
@@ -28,11 +25,8 @@ type EmptyMetrics struct{}
|
||||
// type check
|
||||
var _ Metrics = EmptyMetrics{}
|
||||
|
||||
// IncrementItemsCount implements the [Metrics] interface for EmptyMetrics.
|
||||
func (EmptyMetrics) IncrementItemsCount(_ context.Context) {}
|
||||
|
||||
// ObserveItemSize implements the [Metrics] interface for EmptyMetrics.
|
||||
func (EmptyMetrics) ObserveItemSize(_ context.Context, _ datasize.ByteSize) {}
|
||||
|
||||
// ObserveWriteDuration implements the [Metrics] interface for EmptyMetrics.
|
||||
func (EmptyMetrics) ObserveWriteDuration(_ context.Context, _ time.Duration) {}
|
||||
// ObserveWrite implements the [Metrics] interface for EmptyMetrics.
|
||||
func (EmptyMetrics) ObserveWrite(_ context.Context, _ time.Duration) {}
|
||||
|
||||
@@ -16,11 +16,11 @@ type certPaths struct {
|
||||
isCustom bool
|
||||
}
|
||||
|
||||
// certStorage holds TLS certificates and their associated file paths. Each
|
||||
// entry in the slices corresponds to a certificate and its respective paths for
|
||||
// the certificate and key files. Using this struct allows us to reduce
|
||||
// certIndex holds TLS certificates and their associated file paths. Each entry
|
||||
// in the slices corresponds to a certificate and its respective paths for the
|
||||
// certificate and key files. Using this struct allows us to reduce
|
||||
// allocations.
|
||||
type certStorage struct {
|
||||
type certIndex struct {
|
||||
// certs contains the list of TLS certificates. All elements must not be
|
||||
// nil.
|
||||
certs []*tls.Certificate
|
||||
@@ -32,21 +32,21 @@ type certStorage struct {
|
||||
|
||||
// add saves the TLS certificate and its paths. Certificate paths must only be
|
||||
// added once, see [certStorage.contains]. cert and cp must not be nil.
|
||||
func (s *certStorage) add(cert *tls.Certificate, cp *certPaths) {
|
||||
func (s *certIndex) add(cert *tls.Certificate, cp *certPaths) {
|
||||
s.certs = append(s.certs, cert)
|
||||
s.paths = append(s.paths, cp)
|
||||
}
|
||||
|
||||
// contains returns true if the TLS certificate has already been added using the
|
||||
// provided file paths. cp must not be nil.
|
||||
func (s *certStorage) contains(cp *certPaths) (ok bool) {
|
||||
func (s *certIndex) contains(cp *certPaths) (ok bool) {
|
||||
return slices.ContainsFunc(s.paths, func(p *certPaths) (found bool) {
|
||||
return *cp == *p
|
||||
})
|
||||
}
|
||||
|
||||
// count returns the number of saved TLS certificates.
|
||||
func (s *certStorage) count() (n int) {
|
||||
func (s *certIndex) count() (n int) {
|
||||
return len(s.certs)
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ func (s *certStorage) count() (n int) {
|
||||
//
|
||||
// TODO(a.garipov): Explore the above situation and consider fixes to allow
|
||||
// custom IP-only certs.
|
||||
func (s *certStorage) certFor(chi *tls.ClientHelloInfo) (cert *tls.Certificate, err error) {
|
||||
func (s *certIndex) certFor(chi *tls.ClientHelloInfo) (cert *tls.Certificate, err error) {
|
||||
var errs []error
|
||||
for _, c := range s.certs {
|
||||
err = chi.SupportsCertificate(c)
|
||||
@@ -75,7 +75,7 @@ func (s *certStorage) certFor(chi *tls.ClientHelloInfo) (cert *tls.Certificate,
|
||||
|
||||
// rangeFn calls fn for each stored TLS certificate and its paths. fn must not
|
||||
// be nil. Neither cert nor cp must be modified.
|
||||
func (s *certStorage) rangeFn(fn func(cert *tls.Certificate, cp *certPaths) (cont bool)) {
|
||||
func (s *certIndex) rangeFn(fn func(cert *tls.Certificate, cp *certPaths) (cont bool)) {
|
||||
for i, p := range s.paths {
|
||||
if !fn(s.certs[i], p) {
|
||||
return
|
||||
@@ -84,7 +84,7 @@ func (s *certStorage) rangeFn(fn func(cert *tls.Certificate, cp *certPaths) (con
|
||||
}
|
||||
|
||||
// remove deletes the certificate from s. cp must not be nil.
|
||||
func (s *certStorage) remove(cp *certPaths) {
|
||||
func (s *certIndex) remove(cp *certPaths) {
|
||||
i := slices.IndexFunc(s.paths, func(p *certPaths) (found bool) {
|
||||
return *cp == *p
|
||||
})
|
||||
@@ -97,7 +97,7 @@ func (s *certStorage) remove(cp *certPaths) {
|
||||
}
|
||||
|
||||
// stored returns the list of saved TLS certificates.
|
||||
func (s *certStorage) stored() (certs []*tls.Certificate) {
|
||||
func (s *certIndex) stored() (certs []*tls.Certificate) {
|
||||
return s.certs
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ func (s *certStorage) stored() (certs []*tls.Certificate) {
|
||||
//
|
||||
// TODO(a.garipov): Think of a better way to do this that doesn't involve code
|
||||
// that looks like iterator invalidation.
|
||||
func (s *certStorage) update(cp *certPaths, c *tls.Certificate) (ok bool) {
|
||||
func (s *certIndex) update(cp *certPaths, c *tls.Certificate) (ok bool) {
|
||||
for i, p := range s.paths {
|
||||
if *cp == *p {
|
||||
s.certs[i] = c
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestCertStorage(t *testing.T) {
|
||||
func TestCertIndex(t *testing.T) {
|
||||
const (
|
||||
domainA = "a.com"
|
||||
domainB = "b.org"
|
||||
@@ -51,19 +51,19 @@ func TestCertStorage(t *testing.T) {
|
||||
paths: pathsDomainB,
|
||||
}}
|
||||
|
||||
s := &certStorage{}
|
||||
idx := &certIndex{}
|
||||
for _, cp := range certWithPaths {
|
||||
s.add(cp.cert, cp.paths)
|
||||
idx.add(cp.cert, cp.paths)
|
||||
}
|
||||
|
||||
assert.True(t, s.contains(pathsDomainA))
|
||||
assert.True(t, idx.contains(pathsDomainA))
|
||||
|
||||
copyPathsDomainsB := *pathsDomainB
|
||||
assert.True(t, s.contains(©PathsDomainsB))
|
||||
assert.False(t, s.contains(nonAddedPaths))
|
||||
assert.Equal(t, len(certWithPaths), s.count())
|
||||
assert.True(t, idx.contains(©PathsDomainsB))
|
||||
assert.False(t, idx.contains(nonAddedPaths))
|
||||
assert.Equal(t, len(certWithPaths), idx.count())
|
||||
|
||||
got, err := s.certFor(&tls.ClientHelloInfo{
|
||||
got, err := idx.certFor(&tls.ClientHelloInfo{
|
||||
ServerName: domainA,
|
||||
SupportedVersions: []uint16{tls.VersionTLS13},
|
||||
})
|
||||
@@ -71,17 +71,17 @@ func TestCertStorage(t *testing.T) {
|
||||
|
||||
assert.Equal(t, certDomainA, got)
|
||||
|
||||
got, err = s.certFor(&tls.ClientHelloInfo{
|
||||
got, err = idx.certFor(&tls.ClientHelloInfo{
|
||||
ServerName: domainB,
|
||||
SupportedVersions: []uint16{tls.VersionTLS13},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, certDomainB, got)
|
||||
assert.Equal(t, []*tls.Certificate{certDomainA, certDomainB}, s.stored())
|
||||
assert.Equal(t, []*tls.Certificate{certDomainA, certDomainB}, idx.stored())
|
||||
|
||||
i := 0
|
||||
s.rangeFn(func(c *tls.Certificate, cp *certPaths) (cont bool) {
|
||||
idx.rangeFn(func(c *tls.Certificate, cp *certPaths) (cont bool) {
|
||||
assert.Equal(t, certWithPaths[i].cert, c)
|
||||
assert.Equal(t, certWithPaths[i].paths, cp)
|
||||
|
||||
@@ -176,7 +176,7 @@ func (db *CustomDomainDB) AddCertificate(
|
||||
func (db *CustomDomainDB) removeCertData(
|
||||
ctx context.Context,
|
||||
l *slog.Logger,
|
||||
certName string,
|
||||
certName agd.CertificateName,
|
||||
profID agd.ProfileID,
|
||||
domains []string,
|
||||
reason string,
|
||||
@@ -228,9 +228,9 @@ const (
|
||||
)
|
||||
|
||||
// cachePaths returns the cache paths for the given certificate name.
|
||||
func (db *CustomDomainDB) cachePaths(certName string) (certPath, keyPath string) {
|
||||
certPath = filepath.Join(db.cacheDir, certName+".crt.pem")
|
||||
keyPath = filepath.Join(db.cacheDir, certName+".key.pem")
|
||||
func (db *CustomDomainDB) cachePaths(certName agd.CertificateName) (certPath, keyPath string) {
|
||||
certPath = filepath.Join(db.cacheDir, string(certName)+".crt.pem")
|
||||
keyPath = filepath.Join(db.cacheDir, string(certName)+".key.pem")
|
||||
|
||||
return certPath, keyPath
|
||||
}
|
||||
@@ -345,8 +345,8 @@ var _ service.Refresher = (*CustomDomainDB)(nil)
|
||||
// Refresh implements the [service.Refresher] interface for *CustomDomainDB.
|
||||
// Refresh will retry network and ratelimiting errors.
|
||||
func (db *CustomDomainDB) Refresh(ctx context.Context) (err error) {
|
||||
var updatedCertNames []string
|
||||
retries := map[string]*customDomainRetry{}
|
||||
var updatedCertNames []agd.CertificateName
|
||||
retries := map[agd.CertificateName]*customDomainRetry{}
|
||||
func() {
|
||||
db.customCertsMu.Lock()
|
||||
defer db.customCertsMu.Unlock()
|
||||
@@ -403,7 +403,7 @@ func (db *CustomDomainDB) Refresh(ctx context.Context) (err error) {
|
||||
// true, refreshCert should be retried later.
|
||||
func (db *CustomDomainDB) refreshCert(
|
||||
ctx context.Context,
|
||||
certName string,
|
||||
certName agd.CertificateName,
|
||||
) (needsRetry bool, err error) {
|
||||
l := db.logger.With("cert_name", certName)
|
||||
|
||||
@@ -527,7 +527,7 @@ func (db *CustomDomainDB) certDataToPEM(
|
||||
// filesystem. Errors are logged and reported to Sentry.
|
||||
func (db *CustomDomainDB) needsRefresh(
|
||||
ctx context.Context,
|
||||
certName string,
|
||||
certName agd.CertificateName,
|
||||
certPath string,
|
||||
keyPath string,
|
||||
) (ok bool) {
|
||||
@@ -567,8 +567,8 @@ func (db *CustomDomainDB) needsRefresh(
|
||||
// addRetry adds the certificate into the retry map. retries must not be nil.
|
||||
func (db *CustomDomainDB) addRetry(
|
||||
ctx context.Context,
|
||||
retries map[string]*customDomainRetry,
|
||||
certName string,
|
||||
retries map[agd.CertificateName]*customDomainRetry,
|
||||
certName agd.CertificateName,
|
||||
now time.Time,
|
||||
) {
|
||||
db.logger.DebugContext(ctx, "retrying later", "cert_name", certName)
|
||||
@@ -590,7 +590,7 @@ func (db *CustomDomainDB) addRetry(
|
||||
// are reported to db.errColl and logged.
|
||||
func (db *CustomDomainDB) performRetries(
|
||||
ctx context.Context,
|
||||
retries map[string]*customDomainRetry,
|
||||
retries map[agd.CertificateName]*customDomainRetry,
|
||||
now time.Time,
|
||||
) {
|
||||
var errs []error
|
||||
|
||||
@@ -27,7 +27,7 @@ import (
|
||||
)
|
||||
|
||||
// testCertName is the common certificate name for tests.
|
||||
const testCertName = "cert1234"
|
||||
const testCertName agd.CertificateName = "cert1234"
|
||||
|
||||
// testProfileID is the common profile ID for tests.
|
||||
const testProfileID agd.ProfileID = "prof1234"
|
||||
@@ -468,7 +468,10 @@ func TestCustomDomainDB_Match_futureCert(t *testing.T) {
|
||||
|
||||
// testCustomDomainStorage is the [tlsconfig.CustomDomainStorage] for tests.
|
||||
type testCustomDomainStorage struct {
|
||||
onCertificateData func(ctx context.Context, certName string) (cert, key []byte, err error)
|
||||
onCertificateData func(
|
||||
ctx context.Context,
|
||||
certName agd.CertificateName,
|
||||
) (cert, key []byte, err error)
|
||||
}
|
||||
|
||||
// type check
|
||||
@@ -478,7 +481,7 @@ var _ tlsconfig.CustomDomainStorage = (*testCustomDomainStorage)(nil)
|
||||
// *testCustomDomainStorage
|
||||
func (s *testCustomDomainStorage) CertificateData(
|
||||
ctx context.Context,
|
||||
certName string,
|
||||
certName agd.CertificateName,
|
||||
) (cert, key []byte, err error) {
|
||||
return s.onCertificateData(ctx, certName)
|
||||
}
|
||||
@@ -573,7 +576,10 @@ func TestCustomDomainDB_Refresh(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
strg := &testCustomDomainStorage{
|
||||
onCertificateData: func(ctx context.Context, certName string) (cert, key []byte, err error) {
|
||||
onCertificateData: func(
|
||||
ctx context.Context,
|
||||
certName agd.CertificateName,
|
||||
) (cert, key []byte, err error) {
|
||||
assert.Equal(t, testCertName, certName)
|
||||
|
||||
certDER, rsaKey := newCertAndKey(t, 1)
|
||||
@@ -662,8 +668,8 @@ func TestCustomDomainDB_Refresh(t *testing.T) {
|
||||
// newCertAndKeyPaths is a helper that returns paths for the certificate and
|
||||
// the key using the test's temporary directory.
|
||||
func newCertAndKeyPaths(cacheDir string) (certPath, keyPath string) {
|
||||
return filepath.Join(cacheDir, testCertName+tlsconfig.CustomDomainCertExt),
|
||||
filepath.Join(cacheDir, testCertName+tlsconfig.CustomDomainKeyExt)
|
||||
return filepath.Join(cacheDir, string(testCertName)+tlsconfig.CustomDomainCertExt),
|
||||
filepath.Join(cacheDir, string(testCertName)+tlsconfig.CustomDomainKeyExt)
|
||||
}
|
||||
|
||||
func TestCustomDomainDB_Refresh_retry(t *testing.T) {
|
||||
@@ -693,7 +699,7 @@ func TestCustomDomainDB_Refresh_retry(t *testing.T) {
|
||||
strg := &testCustomDomainStorage{
|
||||
onCertificateData: func(
|
||||
ctx context.Context,
|
||||
certName string,
|
||||
certName agd.CertificateName,
|
||||
) (cert, key []byte, err error) {
|
||||
if !shouldCall {
|
||||
panic(testutil.UnexpectedCall(ctx, certName))
|
||||
@@ -793,7 +799,7 @@ func TestCustomDomainDB_Refresh_present(t *testing.T) {
|
||||
Storage: &testCustomDomainStorage{
|
||||
onCertificateData: func(
|
||||
ctx context.Context,
|
||||
certName string,
|
||||
certName agd.CertificateName,
|
||||
) (cert, key []byte, err error) {
|
||||
assert.Equal(t, testCertName, certName)
|
||||
|
||||
|
||||
@@ -16,10 +16,10 @@ import (
|
||||
// customDomainIndex optimizes the search for custom-domain data.
|
||||
type customDomainIndex struct {
|
||||
// changed is the set of names of certificates that need updating.
|
||||
changed *container.SortedSliceSet[string]
|
||||
changed *container.SortedSliceSet[agd.CertificateName]
|
||||
|
||||
// retries contains the data for retrying refreshes of custom-domain data.
|
||||
retries map[string]*customDomainRetry
|
||||
retries map[agd.CertificateName]*customDomainRetry
|
||||
|
||||
// data is the mapping of custom domain or wildcard suffix to records that
|
||||
// relate to it. The key must be a valid domain name, not a wildcard.
|
||||
@@ -30,9 +30,9 @@ type customDomainIndex struct {
|
||||
// newCustomDomainIndex returns a new properly initialized *customDomainIndex.
|
||||
func newCustomDomainIndex() (idx *customDomainIndex) {
|
||||
return &customDomainIndex{
|
||||
changed: container.NewSortedSliceSet[string](),
|
||||
changed: container.NewSortedSliceSet[agd.CertificateName](),
|
||||
retries: map[agd.CertificateName]*customDomainRetry{},
|
||||
data: map[string][]*customDomainIndexItem{},
|
||||
retries: map[string]*customDomainRetry{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ type customDomainIndexItem struct {
|
||||
|
||||
// certName is the unique name for fetching the actual certificate data. It
|
||||
// must not be empty.
|
||||
certName string
|
||||
certName agd.CertificateName
|
||||
|
||||
// domain is the original domain name or wildcard from which the domain
|
||||
// pattern has been derived. It must be a valid domain name or wildcard.
|
||||
@@ -205,7 +205,7 @@ func (idx *customDomainIndex) match(
|
||||
func (idx *customDomainIndex) remove(
|
||||
ctx context.Context,
|
||||
l *slog.Logger,
|
||||
certName string,
|
||||
certName agd.CertificateName,
|
||||
profID agd.ProfileID,
|
||||
domains []string,
|
||||
) {
|
||||
|
||||
@@ -3,6 +3,7 @@ package tlsconfig
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
)
|
||||
|
||||
@@ -12,7 +13,7 @@ type CustomDomainStorage interface {
|
||||
// CertificateData returns the certificate data for the name. If err is
|
||||
// nil, cert and key must not be nil. If the certificate could not be
|
||||
// found, err must contain [ErrCertificateNotFound].
|
||||
CertificateData(ctx context.Context, name string) (cert, key []byte, err error)
|
||||
CertificateData(ctx context.Context, name agd.CertificateName) (cert, key []byte, err error)
|
||||
}
|
||||
|
||||
// ErrCertificateNotFound is returned (optionally wrapped) by
|
||||
@@ -31,7 +32,7 @@ var _ CustomDomainStorage = EmptyCustomDomainStorage{}
|
||||
// EmptyCustomDomainStorage
|
||||
func (EmptyCustomDomainStorage) CertificateData(
|
||||
_ context.Context,
|
||||
_ string,
|
||||
_ agd.CertificateName,
|
||||
) (_, _ []byte, _ error) {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ type DefaultManager struct {
|
||||
errColl errcoll.Interface
|
||||
metrics ManagerMetrics
|
||||
tickDB TicketDB
|
||||
certStorage *certStorage
|
||||
idx *certIndex
|
||||
original *tls.Config
|
||||
clones []*tls.Config
|
||||
clonesWithMetrics []*tls.Config
|
||||
@@ -110,12 +110,12 @@ func NewDefaultManager(c *DefaultManagerConfig) (m *DefaultManager, err error) {
|
||||
}
|
||||
|
||||
m = &DefaultManager{
|
||||
mu: &sync.Mutex{},
|
||||
logger: c.Logger,
|
||||
errColl: c.ErrColl,
|
||||
metrics: c.Metrics,
|
||||
tickDB: c.TicketDB,
|
||||
certStorage: &certStorage{},
|
||||
mu: &sync.Mutex{},
|
||||
logger: c.Logger,
|
||||
errColl: c.ErrColl,
|
||||
metrics: c.Metrics,
|
||||
tickDB: c.TicketDB,
|
||||
idx: &certIndex{},
|
||||
}
|
||||
|
||||
m.original = &tls.Config{
|
||||
@@ -147,7 +147,7 @@ func (m *DefaultManager) Add(
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
if m.certStorage.contains(cp) {
|
||||
if m.idx.contains(cp) {
|
||||
m.logger.InfoContext(
|
||||
ctx,
|
||||
"skipping already added certificate",
|
||||
@@ -164,7 +164,7 @@ func (m *DefaultManager) Add(
|
||||
return fmt.Errorf("adding certificate: %w", err)
|
||||
}
|
||||
|
||||
m.certStorage.add(cert, cp)
|
||||
m.idx.add(cert, cp)
|
||||
|
||||
m.logger.InfoContext(
|
||||
ctx,
|
||||
@@ -214,11 +214,11 @@ func (m *DefaultManager) getCertificate(chi *tls.ClientHelloInfo) (c *tls.Certif
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
if m.certStorage.count() == 0 {
|
||||
if m.idx.count() == 0 {
|
||||
return nil, errors.Error("no certificates")
|
||||
}
|
||||
|
||||
return m.certStorage.certFor(chi)
|
||||
return m.idx.certFor(chi)
|
||||
}
|
||||
|
||||
// CloneWithMetrics implements the [Manager] interface for *DefaultManager.
|
||||
@@ -240,7 +240,7 @@ func (m *DefaultManager) CloneWithMetrics(
|
||||
proto,
|
||||
srvName,
|
||||
deviceDomains,
|
||||
m.certStorage.stored(),
|
||||
m.idx.stored(),
|
||||
)
|
||||
|
||||
m.clonesWithMetrics = append(m.clonesWithMetrics, clone)
|
||||
@@ -266,7 +266,7 @@ func (m *DefaultManager) Refresh(ctx context.Context) (err error) {
|
||||
defer m.mu.Unlock()
|
||||
|
||||
var errs []error
|
||||
m.certStorage.rangeFn(func(_ *tls.Certificate, cp *certPaths) (cont bool) {
|
||||
m.idx.rangeFn(func(_ *tls.Certificate, cp *certPaths) (cont bool) {
|
||||
cert, loadErr := m.load(ctx, cp)
|
||||
if err != nil {
|
||||
errs = append(errs, loadErr)
|
||||
@@ -275,7 +275,7 @@ func (m *DefaultManager) Refresh(ctx context.Context) (err error) {
|
||||
}
|
||||
|
||||
msg, lvl := "refreshed certificate", slog.LevelInfo
|
||||
if !m.certStorage.update(cp, cert) {
|
||||
if !m.idx.update(cp, cert) {
|
||||
msg, lvl = "certificate did not refresh", slog.LevelWarn
|
||||
}
|
||||
|
||||
@@ -289,7 +289,7 @@ func (m *DefaultManager) Refresh(ctx context.Context) (err error) {
|
||||
return fmt.Errorf("refreshing tls certificates: %w", err)
|
||||
}
|
||||
|
||||
m.logger.InfoContext(ctx, "refresh successful", "num_configs", m.certStorage.count())
|
||||
m.logger.InfoContext(ctx, "refresh successful", "num_configs", m.idx.count())
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -311,7 +311,7 @@ func (m *DefaultManager) Remove(
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
m.certStorage.remove(cp)
|
||||
m.idx.remove(cp)
|
||||
|
||||
m.logger.InfoContext(
|
||||
ctx,
|
||||
@@ -372,7 +372,7 @@ func (m *DefaultManager) RotateTickets(ctx context.Context) (err error) {
|
||||
m.logger.InfoContext(
|
||||
ctx,
|
||||
"ticket rotation successful",
|
||||
"num_configs", m.certStorage.count(),
|
||||
"num_configs", m.idx.count(),
|
||||
"num_tickets", len(tickets),
|
||||
"num_clones", len(m.clones),
|
||||
"num_clones_with_metrics", len(m.clonesWithMetrics),
|
||||
|
||||
@@ -118,6 +118,8 @@ func (s *server) localAddr() (addr net.Addr) {
|
||||
//
|
||||
// TODO(a.garipov): Improve error handling.
|
||||
func (s *server) serve(ctx context.Context, baseLogger *slog.Logger) {
|
||||
defer slogutil.RecoverAndLog(ctx, s.logger)
|
||||
|
||||
tcpListener, err := net.ListenTCP("tcp", net.TCPAddrFromAddrPort(s.initialAddr))
|
||||
if err != nil {
|
||||
s.logger.ErrorContext(ctx, "listening tcp", slogutil.KeyError, err)
|
||||
|
||||
@@ -49,4 +49,4 @@ readonly go count_flags shuffle_flags timeout_flags
|
||||
--benchmem \
|
||||
--benchtime='1s' \
|
||||
--run='^$' \
|
||||
./...
|
||||
work
|
||||
|
||||
@@ -21,31 +21,7 @@ set -e -f -u
|
||||
go="${GO:-go}"
|
||||
readonly go
|
||||
|
||||
# TODO(a.garipov): Add the ability to generate things separately.
|
||||
(
|
||||
cd ./internal/geoip/
|
||||
"$go" run ./country_generate.go
|
||||
)
|
||||
|
||||
(
|
||||
cd ./internal/geoip/
|
||||
"$go" run ./asntops_generate.go
|
||||
)
|
||||
|
||||
(
|
||||
cd ./internal/ecscache/
|
||||
"$go" run ./ecsblocklist_generate.go
|
||||
# Force format code, because it's not possible to make an accurate
|
||||
# template for a long list of strings with different lengths.
|
||||
gofumpt -l -w ./ecsblocklist.go
|
||||
)
|
||||
|
||||
(
|
||||
cd ./internal/profiledb/internal/filecachepb/
|
||||
protoc --go_opt=paths=source_relative --go_out=. ./filecache.proto
|
||||
)
|
||||
|
||||
(
|
||||
backendpb() (
|
||||
cd ./internal/backendpb/
|
||||
protoc \
|
||||
--go-grpc_opt=Mdns.proto=./backendpb \
|
||||
@@ -56,3 +32,56 @@ readonly go
|
||||
--go_out=. \
|
||||
./dns.proto
|
||||
)
|
||||
|
||||
ecscache() (
|
||||
cd ./internal/ecscache/
|
||||
"$go" run ./ecsblocklist_generate.go
|
||||
# Force format code, because it's not possible to make an accurate
|
||||
# template for a long list of strings with different lengths.
|
||||
gofumpt -l -w ./ecsblocklist.go
|
||||
)
|
||||
|
||||
filecachepb() (
|
||||
cd ./internal/profiledb/internal/filecachepb/
|
||||
protoc --go_opt=paths=source_relative --go_out=. ./filecache.proto
|
||||
)
|
||||
|
||||
geoip_asntops() (
|
||||
cd ./internal/geoip/
|
||||
"$go" run ./asntops_generate.go
|
||||
)
|
||||
|
||||
geoip_country() (
|
||||
cd ./internal/geoip/
|
||||
"$go" run ./country_generate.go
|
||||
)
|
||||
|
||||
if [ -z "${ONLY:-}" ]; then
|
||||
backendpb
|
||||
ecscache
|
||||
filecachepb
|
||||
geoip_asntops
|
||||
geoip_country
|
||||
else
|
||||
padded=" ${ONLY} "
|
||||
|
||||
if [ "${padded##* backendpb *}" = '' ]; then
|
||||
backendpb
|
||||
fi
|
||||
|
||||
if [ "${padded##* ecscache *}" = '' ]; then
|
||||
ecscache
|
||||
fi
|
||||
|
||||
if [ "${padded##* filecachepb *}" = '' ]; then
|
||||
filecachepb
|
||||
fi
|
||||
|
||||
if [ "${padded##* geoip_asntops *}" = '' ]; then
|
||||
geoip_asntops
|
||||
fi
|
||||
|
||||
if [ "${padded##* geoip_country *}" = '' ]; then
|
||||
geoip_country
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -158,10 +158,6 @@ underscores() {
|
||||
|
||||
# Checks
|
||||
|
||||
# TODO(a.garipov): Remove the dnsserver stuff once it is separated.
|
||||
dnssrvmod='github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/...'
|
||||
readonly dnssrvmod
|
||||
|
||||
run_linter -e blocklist_imports
|
||||
|
||||
run_linter -e method_const
|
||||
@@ -170,9 +166,9 @@ run_linter -e underscores
|
||||
|
||||
run_linter -e gofumpt --extra -e -l .
|
||||
|
||||
run_linter "${GO:-go}" vet ./... "$dnssrvmod"
|
||||
run_linter "${GO:-go}" vet work
|
||||
|
||||
run_linter govulncheck ./... "$dnssrvmod"
|
||||
run_linter govulncheck work
|
||||
|
||||
# NOTE: For AdGuard DNS, ignore the generated protobuf files.
|
||||
run_linter gocyclo --ignore '\.pb\.go$' --over 10 .
|
||||
@@ -180,9 +176,9 @@ run_linter gocyclo --ignore '\.pb\.go$' --over 10 .
|
||||
# NOTE: For AdGuard DNS, ignore the generated protobuf files.
|
||||
run_linter gocognit --ignore '\.pb\.go$' --over 10 .
|
||||
|
||||
run_linter ineffassign ./... "$dnssrvmod"
|
||||
run_linter ineffassign work
|
||||
|
||||
run_linter unparam ./... "$dnssrvmod"
|
||||
run_linter unparam work
|
||||
|
||||
find_with_ignore \
|
||||
-type 'f' \
|
||||
@@ -197,13 +193,13 @@ find_with_ignore \
|
||||
')' \
|
||||
-exec 'misspell' '--error' '{}' '+'
|
||||
|
||||
run_linter nilness ./... "$dnssrvmod"
|
||||
run_linter nilness work
|
||||
|
||||
# TODO(a.garipov): Remove the grep crutch once golang/go#60509 is fixed.
|
||||
#
|
||||
# TODO(a.garipov): Add a filtering function to run_linter.
|
||||
fieldalignment_output="$(
|
||||
fieldalignment ./... "$dnssrvmod" 2>&1 \
|
||||
fieldalignment work 2>&1 \
|
||||
| grep -e '\.pb\.go' -v \
|
||||
|| :
|
||||
)"
|
||||
@@ -217,7 +213,7 @@ fi
|
||||
|
||||
# TODO(a.garipov): Remove the grep crutch once golang/go#61574 is fixed.
|
||||
shadow_output="$(
|
||||
shadow --strict ./... "$dnssrvmod" 2>&1 \
|
||||
shadow --strict work 2>&1 \
|
||||
| grep -e '\.pb\.go' -v \
|
||||
|| :
|
||||
)"
|
||||
@@ -229,9 +225,9 @@ if [ "$shadow_output" != '' ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
run_linter gosec --exclude-generated --quiet ./... "$dnssrvmod"
|
||||
run_linter gosec --exclude-generated --quiet work
|
||||
|
||||
run_linter errcheck ./... "$dnssrvmod"
|
||||
run_linter errcheck work
|
||||
|
||||
staticcheck_matrix='
|
||||
darwin: GOOS=darwin
|
||||
@@ -240,4 +236,4 @@ linux: GOOS=linux
|
||||
readonly staticcheck_matrix
|
||||
|
||||
printf '%s' "$staticcheck_matrix" \
|
||||
| run_linter staticcheck --matrix ./... "$dnssrvmod"
|
||||
| run_linter staticcheck --matrix work
|
||||
|
||||
@@ -45,7 +45,6 @@ timeout_flags="${TIMEOUT_FLAGS:---timeout=120s}"
|
||||
readonly count_flags cover_flags go shuffle_flags timeout_flags
|
||||
|
||||
go_test() {
|
||||
# TODO(a.garipov): Remove the dnsserver stuff once it is separated.
|
||||
"$go" test \
|
||||
"$count_flags" \
|
||||
"$cover_flags" \
|
||||
@@ -54,8 +53,7 @@ go_test() {
|
||||
"$timeout_flags" \
|
||||
"$v_flags" \
|
||||
"$x_flags" \
|
||||
./... \
|
||||
github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/... \
|
||||
work \
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# This comment is used to simplify checking local copies of the script. Bump
|
||||
# this number every time a significant change is made to this script.
|
||||
#
|
||||
# AdGuard-Project-Version: 9
|
||||
# AdGuard-Project-Version: 10
|
||||
|
||||
verbose="${VERBOSE:-0}"
|
||||
readonly verbose
|
||||
@@ -74,6 +74,8 @@ trailing_whitespace() {
|
||||
done
|
||||
}
|
||||
|
||||
# TODO(a.garipov): Consider using jq for JSON validation.
|
||||
|
||||
run_linter -e trailing_newlines
|
||||
|
||||
run_linter -e trailing_whitespace
|
||||
|
||||
Reference in New Issue
Block a user