Sync v2.7.0

This commit is contained in:
Andrey Meshkov 2024-06-07 14:27:46 +03:00
parent b6e37914aa
commit 5690301129
221 changed files with 17414 additions and 16436 deletions

View File

@ -11,6 +11,98 @@ The format is **not** based on [Keep a Changelog][kec], since the project
## AGDNS-2048 / Build 750
* The environment variables `RESEARCH_LOGS` and `RESEARCH_METRICS` have been
removed.
## AGDNS-2022 / Build 746
* The property `block_page_redirect` of objects within `server_groups` array
has been removed.
## AGDNS-1981 / Build 744
* The objects within `server_groups` array had a change in their
`block_page_redirect` configuration, it now supports arrays of IP addresses
in `ipv4` and `ipv6` fields.
* Profile's file cache version was incremented. In case of
`BlockingModeCustomIP` the `profile.blocking_mode` IPv4/IPv6 fields are now
arrays of IP addresses.
## AGDNS-2012 / Build 732
* The querylog now has a new field, `"rn"`, which is a 16-bit unsigned random
number. Field `"u"`, the unique request ID, is deprecated and may be
removed in the future.
## AGDNS-1879 / Build 729
* Profile's file cache version was incremented. The new field
`authentication` has been added to profile's device object.
## AGDNS-1934 / Build 728
* The object `filters` has new properties: `index_refresh_timeout`, and
`rule_list_refresh_timeout`. So replace this:
```yaml
filters:
# …
```
with this:
```yaml
filters:
# …
index_refresh_timeout: 1m
rule_list_refresh_timeout: 1m
```
* The objects `safe_browsing` and `adult_blocking` have a new property:
`refresh_timeout`. So replace this:
```yaml
safe_browsing:
# …
# …
adult_blocking:
# …
```
with this:
```yaml
safe_browsing:
# …
refresh_timeout: 1m
# …
adult_blocking:
# …
refresh_timeout: 1m
```
## AGDNS-1954 / Build 726
* The object `web` has a new optional property, `general_blocking`. Its
format is the same as in `adult_blocking` and `safe_browsing`.
## AGDNS-1954 / Build 719
* The objects within `server_groups` array have a new property
@ -37,6 +129,9 @@ The format is **not** based on [Keep a Changelog][kec], since the project
probability: 0.01
```
> [!NOTE]
> For `ipv4` and `ipv6` only one address is currently supported.
For server groups that do not require a block-page redirect, set:
```yaml

View File

@ -8,7 +8,7 @@
# Makefile. Bump this number every time a significant change is made to
# this Makefile.
#
# AdGuard-Project-Version: 4
# AdGuard-Project-Version: 5
# Don't name these macros "GO" etc., because GNU Make apparently makes
# them exported environment variables with the literal value of
@ -22,8 +22,8 @@ VERBOSE.MACRO = $${VERBOSE:-0}
BRANCH = $$( git rev-parse --abbrev-ref HEAD )
GOAMD64 = v1
GOPROXY = https://goproxy.cn|https://proxy.golang.org|direct
GOTOOLCHAIN = go1.21.8
GOPROXY = https://proxy.golang.org|direct
GOTOOLCHAIN = go1.22.4
RACE = 0
REVISION = $$( git rev-parse --short HEAD )
VERSION = 0

View File

@ -196,33 +196,44 @@ web:
certificates:
- certificate: './test/cert.crt'
key: './test/cert.key'
# Optional safe browsing web server configuration. static_content is not
# Optional adult blocking web server configuration. static_content is not
# served on these addresses. The addresses should be the same as in the
# safe_browsing object.
safe_browsing:
# general_blocking and safe_browsing objects.
adult_blocking:
bind:
- address: '127.0.0.1:9081'
- address: '127.0.0.1:9444'
certificates:
- certificate: './test/cert.crt'
key: './test/cert.key'
block_page: './test/block_page_sb.html'
# Optional adult blocking web server configuration. static_content is not
block_page: './test/block_page_adult.html'
# Optional general blocking web server configuration. static_content is not
# served on these addresses. The addresses should be the same as in the
# adult_blocking object.
adult_blocking:
# adult_blocking and safe_browsing objects.
general_blocking:
bind:
- address: '127.0.0.1:9082'
- address: '127.0.0.1:9445'
certificates:
- certificate: './test/cert.crt'
key: './test/cert.key'
block_page: './test/block_page_adult.html'
block_page: './test/block_page_general.html'
# Optional safe browsing web server configuration. static_content is not
# served on these addresses. The addresses should be the same as in the
# general_blocking and safe_browsing objects.
safe_browsing:
bind:
- address: '127.0.0.1:9083'
- address: '127.0.0.1:9446'
certificates:
- certificate: './test/cert.crt'
key: './test/cert.key'
block_page: './test/block_page_sb.html'
# Listen addresses for the web service in addition to the ones in the
# DNS-over-HTTPS handlers.
non_doh_bind:
- address: '127.0.0.1:9083'
- address: '127.0.0.1:9446'
- address: '127.0.0.1:9084'
- address: '127.0.0.1:9447'
certificates:
- certificate: './test/cert.crt'
key: './test/cert.key'
@ -253,6 +264,7 @@ safe_browsing:
cache_size: 1024
cache_ttl: 1h
refresh_interval: 1h
refresh_timeout: 1m
# AdGuard adult content blocking filter configuration.
adult_blocking:
@ -260,8 +272,12 @@ adult_blocking:
cache_size: 1024
cache_ttl: 1h
refresh_interval: 1h
refresh_timeout: 1m
# Settings for rule-list-based filters.
#
# TODO(a.garipov): Add the timeout for the blocked-service index refresh. It
# is currently hardcoded to 3 minutes.
filters:
# The TTL to set for responses to requests for filtered domains.
response_ttl: 5m
@ -273,9 +289,17 @@ filters:
# How often to update filters from the index. See the documentation for the
# FILTER_INDEX_URL environment variable.
refresh_interval: 1h
# The timeout for the entire filter update operation. Be aware that each
# individual refresh operation also has its own hardcoded 3m timeout.
# The timeout for the entire filter update operation. Note that filter
# rule-list index and each filter rule-list update operations have their own
# timeouts, see index_refresh_timeout and rule_list_refresh_timeout.
refresh_timeout: 5m
# The timeout for the filter rule-list index update operation. See also
# refresh_timeout for the entire filter update operation.
index_refresh_timeout: 1m
# The timeout for the filter update operation of each rule-list, including
# the safe-search ones. See also refresh_timeout for the entire filter
# update operation.
rule_list_refresh_timeout: 1m
# MaxSize is the maximum size of the downloadable filtering rule-list.
max_size: 256MB
# Rule list cache.
@ -351,38 +375,6 @@ server_groups:
- name: 'adguard_dns_default'
# This filtering_group is used for all anonymous clients.
filtering_group: 'default'
# Settings for redirection to a block page.
block_page_redirect:
# If enabled is false, other fields can be skipped.
enabled: true
# Addresses to use for A queries. If enabled is true, ipv4, ipv6, or
# both must be filled.
ipv4:
- address: '127.0.0.1'
- address: '127.0.0.2'
# Addresses to use for AAAA queries. If enabled is true, ipv4, ipv6, or
# both must be filled.
ipv6:
- address: '::1'
- address: '::2'
# Request parameters based on which the block page is always shown. For
# requests matching these parameters, both skip and probability are
# ignored.
apply:
client:
- address: '192.168.0.0/16'
- address: '1.2.3.4'
# Request parameters based on which the block page is never shown. For
# requests matching these parameters, probability is ignored.
skip:
client:
- address: '1.2.0.0/16'
- address: '5.6.7.8'
question:
- domain: 'do-not-show-block.site.example'
# The probability of responding with the block page IPs based on remote
# address. Must be between 0.0 and 1.0.
probability: 0.01
ddr:
enabled: true
# Device ID domain name suffix to DDR record template mapping. Keep in

View File

@ -28,7 +28,6 @@ configuration file with comments.
* [Filtering groups](#filtering_groups)
* [Network interface listeners](#interface_listeners)
* [Server groups](#server_groups)
* [Block-page redirecting](#server_groups-*-block_page_redirect)
* [DDR](#server_groups-*-ddr)
* [TLS](#server_groups-*-tls)
* [Servers](#server_groups-*-servers-*)
@ -609,9 +608,9 @@ The optional `web` object has the following properties:
```
* <a href="#web-safe_browsing" id="web-safe_browsing" name="web-safe_browsing">`safe_browsing`</a>:
The optional safe browsing web server configurations. Every request is
responded with the content from the file to which the `block_page` property
points.
The optional safe browsing block-page web server configurations. Every
request is responded with the content from the file to which the
`block_page` property points.
See the [full description of this API][http-block-pages] on the HTTP API
page.
@ -639,10 +638,15 @@ The optional `web` object has the following properties:
```
* <a href="#web-adult_blocking" id="web-adult_blocking" name="web-adult_blocking">`adult_blocking`</a>:
The optional adult blocking web server configurations. The format of the
The optional adult block-page web server configuration. The format of the
values is the same as in the [`safe_browsing`](#web-safe_browsing) object
above.
* <a href="#web-general_blocking" id="web-general_blocking" name="web-general_blocking">`general_blocking`</a>:
The optional general block-page web server configuration. The format of
the values is the same as in the [`safe_browsing`](#web-safe_browsing)
object above.
* <a href="#web-non_doh_bind" id="web-non_doh_bind" name="web-non_doh_bind">`non_doh_bind`</a>:
The optional listen addresses and optional TLS configuration for the web
service in addition to the ones in the DNS-over-HTTPS handlers. The
@ -669,6 +673,9 @@ The optional `web` object has the following properties:
Inside of the `headers` map, the header `Content-Type` is required.
> [!NOTE]
> Paths are case-sensitive.
**Property example:**
```yaml
@ -735,6 +742,11 @@ The `safe_browsing` object has the following properties:
**Example:** `1m`.
* <a href="#safe_browsing-refresh_timeout" id="safe_browsing-refresh_timeout" name="safe_browsing-refresh_timeout">`refresh_timeout`</a>:
The timeout for the update operation, as a human-readable duration.
**Example:** `1m`.
## <a href="#adult_blocking" id="adult_blocking" name="adult_blocking">Adult-content blocking</a>
@ -746,6 +758,9 @@ The `adult_blocking` object has the same properties as the
## <a href="#filters" id="filters" name="filters">Filter Lists</a>
**TODO(a.garipov):** Add the timeout for the blocked-service index refresh. It
is currently hardcoded to 3 minutes.
The `filters` object has the following properties:
* <a href="#filters-response_ttl" id="filters-response_ttl" name="filters-response_ttl">`response_ttl`</a>:
@ -777,11 +792,29 @@ The `filters` object has the following properties:
* <a href="#filters-refresh_timeout" id="filters-refresh_timeout" name="filters-refresh_timeout">`refresh_timeout`</a>:
The timeout for the *entire* filter update operation, as a human-readable
duration. Be aware that each individual refresh operation also has its own
hardcoded 3m timeout.
duration. Note that filter rule-list index and each filter rule-list
update operations have their own timeouts, see
[`index_refresh_timeout`](#filters-index_refresh_timeout) and
[`rule_list_refresh_timeout`](#filters-rule_list_refresh_timeout).
**Example:** `5m`.
* <a href="#filters-index_refresh_timeout" id="filters-index_refresh_timeout" name="filters-index_refresh_timeout">`index_refresh_timeout`</a>:
The timeout for the filter rule-list index update operation, as a
human-readable duration. See also
[`refresh_timeout`](#filters-refresh_timeout) for the entire filter update
operation.
**Example:** `1m`.
* <a href="#filters-rule_list_refresh_timeout" id="filters-rule_list_refresh_timeout" name="filters-rule_list_refresh_timeout">`rule_list_refresh_timeout`</a>:
The timeout for the filter update operation of each rule-list, including the
safe-search ones, as a human-readable duration. See also
[`refresh_timeout`](#filters-refresh_timeout) for the entire filter update
operation.
**Example:** `1m`.
* <a href="#filters-max_size" id="filters-max_size" name="filters-max_size">`max_size`</a>:
The maximum size of the downloadable content for a rule-list in a
human-readable format.
@ -939,9 +972,6 @@ The items of the `server_groups` array have the following properties:
**Example:** `default`.
* `block_page_redirect`: The block-page redirect configuration object. See
[below](#server_groups-*-block_page_redirect).
* `ddr`: The DDR configuration object. See [below](#server_groups-*-ddr).
* `tls`: The TLS configuration object. See [below](#server_groups-*-tls).
@ -956,69 +986,6 @@ The items of the `server_groups` array have the following properties:
### <a href="#server_groups-*-block_page_redirect" id="server_groups-*-block_page_redirect" name="server_groups-*-block_page_redirect">Block-page redirecting</a>
The block-page redirect configuration object. If enabled, AdGuard DNS responds
with the configured IP addresses to “redirect” users to an informative block
page.
* <a href="#sg-*-bpr-enabled" id="sg-*-bpr-enabled" name="sg-*-bpr-enabled">`enabled`</a>:
Shows if the block-page redirect is enabled. If `false`, the fields below
can be skipped.
* <a href="#sg-*-bpr-ipv4" id="sg-*-bpr-ipv4" name="sg-*-bpr-ipv4">`ipv4`</a>:
Arrays of IPv4 addresses with which to respond to blocked `A` queries.
If `enabled` is true, `ipv4`, `ipv6`, or both must be filled.
* <a href="#sg-*-bpr-ipv6" id="sg-*-bpr-ipv6" name="sg-*-bpr-ipv6">`ipv6`</a>:
Arrays of IPv6 addresses with which to respond to blocked `AAAA` queries.
If `enabled` is true, `ipv4`, `ipv6`, or both must be filled.
* <a href="#sg-*-bpr-apply" id="sg-*-bpr-apply" name="sg-*-bpr-apply">`apply`</a>:
Request parameters based on which the block-page redirect is always
performed. For requests matching these parameters, both `skip` and
`probability` are
ignored.
**Property example:**
```yaml
apply:
client:
- address: '192.168.0.0/16'
- address: '1.2.3.4'
```
* <a href="#sg-*-bpr-skip" id="sg-*-bpr-skip" name="sg-*-bpr-skip">`skip`</a>:
Request parameters based on which the block-page redirect is never
performed. For requests matching these parameters, `probability` is
ignored.
**Property example:**
```yaml
skip:
client:
- address: '1.2.0.0/16'
- address: '5.6.7.8'
question:
- domain: 'do-not-show-block.site.example'
```
* <a href="#sg-*-bpr-probability" id="sg-*-bpr-probability" name="sg-*-bpr-probability">`probability`</a>:
The probability of responding with the block page IPs based on remote
address. Must be between `0.0` and `1.0`.
### <a href="#server_groups-*-ddr" id="server_groups-*-ddr" name="server_groups-*-ddr">DDR</a>
The DDR configuration object. Many of these data duplicate data from objects in

View File

@ -13,7 +13,7 @@
Development is supported on Linux and macOS (aka Darwin) systems.
1. Install Go 1.21 or later.
1. Install Go 1.22 or later.
1. Call `make init` to set up the Git pre-commit hook.
@ -219,15 +219,15 @@ curl 'https://raw.githubusercontent.com/maxmind/MaxMind-DB/main/test-data/GeoIP2
You'll need to supply the following:
* [`ADULT_BLOCKING_URL`](#env-ADULT_BLOCKING_URL)
* [`BILLSTAT_URL`](#env-BILLSTAT_URL)
* [`CONSUL_ALLOWLIST_URL`](#env-CONSUL_ALLOWLIST_URL)
* [`GENERAL_SAFE_SEARCH_URL`](#env-GENERAL_SAFE_SEARCH_URL)
* [`LINKED_IP_TARGET_URL`](#env-LINKED_IP_TARGET_URL)
* [`NEW_REG_DOMAINS_URL`](#env-NEW_REG_DOMAINS_URL)
* [`PROFILES_URL`](#env-PROFILES_URL)
* [`SAFE_BROWSING_URL`](#env-SAFE_BROWSING_URL)
* [`YOUTUBE_SAFE_SEARCH_URL`](#env-YOUTUBE_SAFE_SEARCH_URL)
* [`ADULT_BLOCKING_URL`][env-ADULT_BLOCKING_URL]
* [`BILLSTAT_URL`][env-BILLSTAT_URL]
* [`CONSUL_ALLOWLIST_URL`][env-CONSUL_ALLOWLIST_URL]
* [`GENERAL_SAFE_SEARCH_URL`][env-GENERAL_SAFE_SEARCH_URL]
* [`LINKED_IP_TARGET_URL`][env-LINKED_IP_TARGET_URL]
* [`NEW_REG_DOMAINS_URL`][env-NEW_REG_DOMAINS_URL]
* [`PROFILES_URL`][env-PROFILES_URL]
* [`SAFE_BROWSING_URL`][env-SAFE_BROWSING_URL]
* [`YOUTUBE_SAFE_SEARCH_URL`][env-YOUTUBE_SAFE_SEARCH_URL]
See the [external HTTP API documentation][externalhttp].
@ -280,6 +280,16 @@ env \
./AdGuardDNS
```
[env-ADULT_BLOCKING_URL]: environment.md#ADULT_BLOCKING_URL
[env-BILLSTAT_URL]: environment.md#BILLSTAT_URL
[env-CONSUL_ALLOWLIST_URL]: environment.md#CONSUL_ALLOWLIST_URL
[env-GENERAL_SAFE_SEARCH_URL]: environment.md#GENERAL_SAFE_SEARCH_URL
[env-LINKED_IP_TARGET_URL]: environment.md#LINKED_IP_TARGET_URL
[env-NEW_REG_DOMAINS_URL]: environment.md#NEW_REG_DOMAINS_URL
[env-PROFILES_URL]: environment.md#PROFILES_URL
[env-SAFE_BROWSING_URL]: environment.md#SAFE_BROWSING_URL
[env-YOUTUBE_SAFE_SEARCH_URL]: environment.md#YOUTUBE_SAFE_SEARCH_URL
[externalhttp]: externalhttp.md

View File

@ -26,8 +26,6 @@ sensitive configuration. All other configuration is stored in the
* [`PROFILES_ENABLED`](#PROFILES_ENABLED)
* [`PROFILES_URL`](#PROFILES_URL)
* [`QUERYLOG_PATH`](#QUERYLOG_PATH)
* [`RESEARCH_LOGS`](#RESEARCH_LOGS)
* [`RESEARCH_METRICS`](#RESEARCH_METRICS)
* [`RULESTAT_URL`](#RULESTAT_URL)
* [`SAFE_BROWSING_URL`](#SAFE_BROWSING_URL)
* [`SENTRY_DSN`](#SENTRY_DSN)
@ -264,25 +262,6 @@ The path to the file into which the query log is going to be written.
## <a href="#RESEARCH_METRICS" id="RESEARCH_METRICS" name="RESEARCH_METRICS">`RESEARCH_METRICS`</a>
If `1`, enable collection of a set of special prometheus metrics (prefix is
`dns_research`). If `0`, disable collection of those metrics.
**Default:** `0`.
## <a href="#RESEARCH_LOGS" id="RESEARCH_LOGS" name="RESEARCH_LOGS">`RESEARCH_LOGS`</a>
If `1`, enable logging of additional info that may be required for research
purposes (prefix `research:`). The log will only be written when
`RESEARCH_METRICS` is also set to `1`. If `0`, disable logging of this info.
**Default:** `0`.
## <a href="#RULESTAT_URL" id="RULESTAT_URL" name="RULESTAT_URL">`RULESTAT_URL`</a>
The URL to send filtering rule list statistics to. If empty or unset, the

View File

@ -17,9 +17,9 @@ appropriately.
## <a href="#block-pages" id="block-pages" name="block-pages">Block Pages</a>
The safe browsing and adult blocking servers. Every request is responded with
the content from the configured file, with the exception of `GET /favicon.ico`
and `GET /robots.txt` requests, which are handled separately:
The safe-browsing, adult-blocking, and popup-blocking servers. Every request is
responded with the content from the configured file, with the exception of `GET
/favicon.ico` and `GET /robots.txt` requests, which are handled separately:
* `GET /favicon.ico` requests are responded with a plain-text `404 Not Found`
response.

View File

@ -5,8 +5,8 @@ entries are designed to be concise and easily compressable. An example of the
log output:
```jsonl
{"u":"ABCD","b":"prof1234","i":"dev1234","c":"RU","d":"US","n":"example.com.","l":"cdef5678","m":"||example.com^","t":1628590394000,"a":1234,"e":5,"q":1,"f":2,"s":0,"p":8,"r":0}
{"u":"DEFG","b":"prof1234","i":"dev1234","c":"RU","d":"JP","n":"example.org.","l":"hijk9012","m":"||example.org^","t":1628590394100,"a":6789,"e":6,"q":1,"f":2,"s":0,"p":8,"r":0}
{"u":"ABCD","b":"prof1234","i":"dev1234","c":"RU","d":"US","n":"example.com.","l":"cdef5678","m":"||example.com^","t":1628590394000,"a":1234,"e":5,"q":1,"rn":1234,"f":2,"s":0,"p":8,"r":0}
{"u":"DEFG","b":"prof1234","i":"dev1234","c":"RU","d":"JP","n":"example.org.","l":"hijk9012","m":"||example.org^","t":1628590394100,"a":6789,"e":6,"q":1,"rn":56789,"f":2,"s":0,"p":8,"r":0}
```
AdGuard DNS opens and closes the log file on each write to prevent issues with
@ -24,6 +24,9 @@ rules to remember, which property means what. The properties are:
* <a href="#properties-u" id="properties-u" name="properties-u">`u`</a>:
The unique ID of the request. The short name `u` stands for “unique”.
> [!NOTE]
> This field is deprecated and may be removed in the future.
**Example:** `"ABCD1234"`
* <a href="#properties-b" id="properties-b" name="properties-b">`b`</a>:
@ -141,6 +144,12 @@ rules to remember, which property means what. The properties are:
See [this Wikipedia list][wiki-dnsrr] for numeric values and their meanings.
* <a href="#properties-rn" id="properties-rn" name="properties-rn">`rn`</a>:
A random 16-bit unsigned integer added to an entry for easier deduplication
when `"u"` is not used for that.
**Example:** `12345`
* <a href="#properties-f" id="properties-f" name="properties-f">`f`</a>:
The action taken with this request. The short name `f` stands for
“filtering”. The possible values are:

48
go.mod
View File

@ -1,13 +1,13 @@
module github.com/AdguardTeam/AdGuardDNS
go 1.21.8
go 1.22.4
require (
github.com/AdguardTeam/AdGuardDNS/internal/dnsserver v0.0.0-00010101000000-000000000000
github.com/AdguardTeam/golibs v0.20.1
github.com/AdguardTeam/golibs v0.23.2
github.com/AdguardTeam/urlfilter v0.18.0
github.com/ameshkov/dnscrypt/v2 v2.2.7
github.com/axiomhq/hyperloglog v0.0.0-20240124082744-24bca3a5b39b
github.com/ameshkov/dnscrypt/v2 v2.3.0
github.com/axiomhq/hyperloglog v0.0.0-20240319100328-84253e514e02
github.com/bluele/gcache v0.0.2
github.com/c2h5oh/datasize v0.0.0-20231215233829-aa82cc1e6500
github.com/caarlos0/env/v7 v7.1.0
@ -16,17 +16,18 @@ require (
github.com/miekg/dns v1.1.58
github.com/oschwald/maxminddb-golang v1.12.0
github.com/patrickmn/go-cache v2.1.1-0.20191004192108-46f407853014+incompatible
github.com/prometheus/client_golang v1.18.0
github.com/prometheus/client_model v0.5.0
github.com/prometheus/common v0.46.0
github.com/quic-go/quic-go v0.41.0
github.com/stretchr/testify v1.8.4
golang.org/x/exp v0.0.0-20240213143201-ec583247a57a
golang.org/x/net v0.21.0
golang.org/x/sys v0.17.0
github.com/prometheus/client_golang v1.19.0
github.com/prometheus/client_model v0.6.1
github.com/prometheus/common v0.52.3
github.com/quic-go/quic-go v0.42.0
github.com/stretchr/testify v1.9.0
golang.org/x/crypto v0.22.0
golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8
golang.org/x/net v0.24.0
golang.org/x/sys v0.19.0
golang.org/x/time v0.5.0
google.golang.org/grpc v1.61.1
google.golang.org/protobuf v1.32.0
google.golang.org/grpc v1.63.2
google.golang.org/protobuf v1.33.0
gopkg.in/yaml.v2 v2.4.0
)
@ -35,23 +36,22 @@ require (
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 // indirect
github.com/ameshkov/dnsstamps v1.0.3 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/pprof v0.0.0-20240207164012-fb44976bdcd5 // indirect
github.com/onsi/ginkgo/v2 v2.15.0 // indirect
github.com/panjf2000/ants/v2 v2.9.0 // indirect
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd // indirect
github.com/onsi/ginkgo/v2 v2.17.1 // indirect
github.com/panjf2000/ants/v2 v2.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
github.com/prometheus/procfs v0.13.0 // indirect
github.com/quic-go/qpack v0.4.0 // indirect
go.uber.org/mock v0.4.0 // indirect
golang.org/x/crypto v0.19.0 // indirect
golang.org/x/mod v0.15.0 // indirect
golang.org/x/mod v0.17.0 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/tools v0.18.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240213162025-012b6fc9bca9 // indirect
golang.org/x/tools v0.20.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240412170617-26222e5d3d56 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

101
go.sum
View File

@ -1,17 +1,17 @@
github.com/AdguardTeam/golibs v0.20.1 h1:ol8qLjWGZhU9paMMwN+OLWVTUigGsXa29iVTyd62VKY=
github.com/AdguardTeam/golibs v0.20.1/go.mod h1:bgcMgRviCKyU6mkrX+RtT/OsKPFzyppelfRsksMG3KU=
github.com/AdguardTeam/golibs v0.23.2 h1:rMjYantwtQ39e8G4zBQ6ZLlm4s3XH30Bc9VxhoOHwao=
github.com/AdguardTeam/golibs v0.23.2/go.mod h1:o9i55Sx6v7qogRQeqaBfmLbC/pZqeMBWi015U5PTDY0=
github.com/AdguardTeam/urlfilter v0.18.0 h1:ZZzwODC/ADpjJSODxySrrUnt/fvOCfGFaCW6j+wsGfQ=
github.com/AdguardTeam/urlfilter v0.18.0/go.mod h1:IXxBwedLiZA2viyHkaFxY/8mjub0li2PXRg8a3d9Z1s=
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY=
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA=
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 h1:52m0LGchQBBVqJRyYYufQuIbVqRawmubW3OFGqK1ekw=
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635/go.mod h1:lmLxL+FV291OopO93Bwf9fQLQeLyt33VJRUg5VJ30us=
github.com/ameshkov/dnscrypt/v2 v2.2.7 h1:aEitLIR8HcxVodZ79mgRcCiC0A0I5kZPBuWGFwwulAw=
github.com/ameshkov/dnscrypt/v2 v2.2.7/go.mod h1:qPWhwz6FdSmuK7W4sMyvogrez4MWdtzosdqlr0Rg3ow=
github.com/ameshkov/dnscrypt/v2 v2.3.0 h1:pDXDF7eFa6Lw+04C0hoMh8kCAQM8NwUdFEllSP2zNLs=
github.com/ameshkov/dnscrypt/v2 v2.3.0/go.mod h1:N5hDwgx2cNb4Ay7AhvOSKst+eUiOZ/vbKRO9qMpQttE=
github.com/ameshkov/dnsstamps v1.0.3 h1:Srzik+J9mivH1alRACTbys2xOxs0lRH9qnTA7Y1OYVo=
github.com/ameshkov/dnsstamps v1.0.3/go.mod h1:Ii3eUu73dx4Vw5O4wjzmT5+lkCwovjzaEZZ4gKyIH5A=
github.com/axiomhq/hyperloglog v0.0.0-20240124082744-24bca3a5b39b h1:F3yMzKumBUQ6Fn0sYI1YQ16vQRucpZOfBQ9HXWl5+XI=
github.com/axiomhq/hyperloglog v0.0.0-20240124082744-24bca3a5b39b/go.mod h1:k08r+Yj1PRAmuayFiRK6MYuR5Ve4IuZtTfxErMIh0+c=
github.com/axiomhq/hyperloglog v0.0.0-20240319100328-84253e514e02 h1:bXAPYSbdYbS5VTy92NIUbeDI1qyggi+JYh5op9IFlcQ=
github.com/axiomhq/hyperloglog v0.0.0-20240319100328-84253e514e02/go.mod h1:k08r+Yj1PRAmuayFiRK6MYuR5Ve4IuZtTfxErMIh0+c=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bluele/gcache v0.0.2 h1:WcbfdXICg7G/DGBh1PFfcirkWOQV+v077yF1pSy3DGw=
@ -20,8 +20,8 @@ github.com/c2h5oh/datasize v0.0.0-20231215233829-aa82cc1e6500 h1:6lhrsTEnloDPXye
github.com/c2h5oh/datasize v0.0.0-20231215233829-aa82cc1e6500/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M=
github.com/caarlos0/env/v7 v7.1.0 h1:9lzTF5amyQeWHZzuZeKlCb5FWSUxpG1js43mhbY8ozg=
github.com/caarlos0/env/v7 v7.1.0/go.mod h1:LPPWniDUq4JaO6Q41vtlyikhMknqymCLBw0eX4dcH1E=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -38,14 +38,12 @@ 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-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/pprof v0.0.0-20240207164012-fb44976bdcd5 h1:E/LAvt58di64hlYjx7AsNS6C/ysHWYo+2qPCZKTQhRo=
github.com/google/pprof v0.0.0-20240207164012-fb44976bdcd5/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo=
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
github.com/google/renameio/v2 v2.0.0 h1:UifI23ZTGY8Tt29JbYFiuyIU3eX+RNFtUwefq9qAhxg=
github.com/google/renameio/v2 v2.0.0/go.mod h1:BtmJXm5YlszgC+TD4HOEEUFgkJP3nLxehU6hfe7jRt4=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
@ -56,14 +54,14 @@ github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4=
github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY=
github.com/onsi/ginkgo/v2 v2.15.0 h1:79HwNRBAZHOEwrczrgSOPy+eFTTlIGELKy5as+ClttY=
github.com/onsi/ginkgo/v2 v2.15.0/go.mod h1:HlxMHtYF57y6Dpf+mc5529KKmSq9h2FpCF+/ZkwUxKM=
github.com/onsi/ginkgo/v2 v2.17.1 h1:V++EzdbhI4ZV4ev0UTIj0PzhzOcReJFyJaLjtSF55M8=
github.com/onsi/ginkgo/v2 v2.17.1/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs=
github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8=
github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
github.com/oschwald/maxminddb-golang v1.12.0 h1:9FnTOD0YOhP7DGxGsq4glzpGy5+w7pq50AS6wALUMYs=
github.com/oschwald/maxminddb-golang v1.12.0/go.mod h1:q0Nob5lTCqyQ8WT6FYgS1L7PXKVVbgiymefNwIjPzgY=
github.com/panjf2000/ants/v2 v2.9.0 h1:SztCLkVxBRigbg+vt0S5QvF5vxAbxbKt09/YfAJ0tEo=
github.com/panjf2000/ants/v2 v2.9.0/go.mod h1:7ZxyxsqE4vvW0M7LSD8aI3cKwgFhBHbxnlN8mDqHa1I=
github.com/panjf2000/ants/v2 v2.9.1 h1:Q5vh5xohbsZXGcD6hhszzGqB7jSSc2/CRr3QKIga8Kw=
github.com/panjf2000/ants/v2 v2.9.1/go.mod h1:7ZxyxsqE4vvW0M7LSD8aI3cKwgFhBHbxnlN8mDqHa1I=
github.com/patrickmn/go-cache v2.1.1-0.20191004192108-46f407853014+incompatible h1:IWzUvJ72xMjmrjR9q3H1PF+jwdN0uNQiR2t1BLNalyo=
github.com/patrickmn/go-cache v2.1.1-0.20191004192108-46f407853014+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
@ -74,18 +72,18 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/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.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk=
github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA=
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
github.com/prometheus/common v0.46.0 h1:doXzt5ybi1HBKpsZOL0sSkaNHJJqkyfEWZGGqqScV0Y=
github.com/prometheus/common v0.46.0/go.mod h1:Tp0qkxpb9Jsg54QMe+EAmqXkSV7Evdy1BTn+g2pa/hQ=
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU=
github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/common v0.52.3 h1:5f8uj6ZwHSscOGNdIQg6OiZv/ybiK2CO2q2drVZAQSA=
github.com/prometheus/common v0.52.3/go.mod h1:BrxBKv3FWBIGXw89Mg1AeBq7FSyRzXWI3l3e7W3RN5U=
github.com/prometheus/procfs v0.13.0 h1:GqzLlQyfsPbaEHaQkO7tbDlriv/4o5Hudv6OXHGKX7o=
github.com/prometheus/procfs v0.13.0/go.mod h1:cd4PFCR54QLnGKPaKGA6l+cfuNXtht43ZKY6tow0Y1g=
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
github.com/quic-go/quic-go v0.41.0 h1:aD8MmHfgqTURWNJy48IYFg2OnxwHT3JL7ahGs73lb4k=
github.com/quic-go/quic-go v0.41.0/go.mod h1:qCkNjqczPEvgsOnxZ0eCD14lv+B2LHlFAB++CNOh9hA=
github.com/quic-go/quic-go v0.42.0 h1:uSfdap0eveIl8KXnipv9K7nlwZ5IqLlYOpJ58u5utpM=
github.com/quic-go/quic-go v0.42.0/go.mod h1:132kz4kL3F9vxhW3CtQJLDVwcFe5wdWeJXXijhsO57M=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/shirou/gopsutil/v3 v3.23.7 h1:C+fHO8hfIppoJ1WdsVm1RoI0RwXoNdfTK7yWXV0wVj4=
@ -99,8 +97,8 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM=
github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI=
github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms=
@ -109,34 +107,31 @@ github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFi
github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/exp v0.0.0-20240213143201-ec583247a57a h1:HinSgX1tJRX3KsL//Gxynpw5CTOAIPhgL4W8PNiIpVE=
golang.org/x/exp v0.0.0-20240213143201-ec583247a57a/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc=
golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8 h1:ESSUROHIBHg7USnszlcdmjBEwdMj9VUvU+OPk4yl2mc=
golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI=
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ=
golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240213162025-012b6fc9bca9 h1:hZB7eLIaYlW9qXRfCq/qDaPdbeY3757uARz5Vvfv+cY=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:YUWgXUFRPfoYK1IHMuxH5K6nPEXSCzIMljnQ59lLRCk=
google.golang.org/grpc v1.61.1 h1:kLAiWrZs7YeDM6MumDe7m3y4aM6wacLzM1Y/wiLP9XY=
google.golang.org/grpc v1.61.1/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY=
golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240412170617-26222e5d3d56 h1:zviK8GX4VlMstrK3JkexM5UHjH1VOkRebH9y3jhSBGk=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240412170617-26222e5d3d56/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM=
google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
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=

View File

@ -1,4 +1,4 @@
go 1.21.8
go 1.22.4
use (
.

View File

@ -6,7 +6,10 @@ cloud.google.com/go v0.65.0 h1:Dg9iHVQfrhq82rUNu9ZxUDrJLaxFUe/HlCVaLyRruq8=
cloud.google.com/go/bigquery v1.8.0 h1:PQcPefKFdaIzjQFbiyOgAqyx8q5djaE7x9Sqe712DPA=
cloud.google.com/go/compute v1.21.0 h1:JNBsyXVoOoNJtTQcnEY5uYpZIbeCTYIeDe0Xh1bySMk=
cloud.google.com/go/compute v1.21.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM=
cloud.google.com/go/compute v1.23.3 h1:6sVlXXBmbd7jNX0Ipq0trII3e4n1/MsADLK6a+aiVlk=
cloud.google.com/go/compute v1.23.3/go.mod h1:VCgBUoMnIVIR0CscqQiPJLAG25E3ZRZMzcFZeQ+h8CI=
cloud.google.com/go/compute v1.24.0 h1:phWcR2eWzRJaL/kOiJwfFsPs4BaKq1j6vnpZrc1YlVg=
cloud.google.com/go/compute v1.24.0/go.mod h1:kw1/T+h/+tK2LJK0wiPPx1intgdAM3j/g3hFDlscY40=
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=
@ -51,6 +54,7 @@ github.com/Shopify/goreferrer v0.0.0-20220729165902-8cddb4f5de06/go.mod h1:7erjK
github.com/alecthomas/kingpin/v2 v2.3.1/go.mod h1:oYL5vtsvEHZGHxU7DMp32Dvx+qL+ptGn6lWaot2vCNE=
github.com/alecthomas/kingpin/v2 v2.3.2 h1:H0aULhgmSzN8xQ3nX1uxtdlTHYoPLu5AhHxWrKI6ocU=
github.com/alecthomas/kingpin/v2 v2.3.2/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE=
github.com/alecthomas/kingpin/v2 v2.4.0 h1:f48lwail6p8zpO1bC4TxtqACaGqHYA22qkHjHpqDjYY=
github.com/alecthomas/kingpin/v2 v2.4.0/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d h1:UQZhZ2O0vMHr2cI+DC1Mbh0TJxzA3RcLoMsFw+aXw7E=
@ -100,7 +104,10 @@ github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe h1:QQ3GSy+MqSHxm/d8nC
github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 h1:/inchEIKaYC1Akx+H+gqO04wryn5h75LSazbRlnya1k=
github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20231109132714-523115ebc101 h1:7To3pQ+pZo0i3dsWEbinPNFs5gPSBOsJtx3wTT94VBY=
github.com/cncf/xds/go v0.0.0-20231109132714-523115ebc101/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa h1:jQCWAUqqlij9Pgj2i/PB79y4KOPYVyFYdROxgaCwdTQ=
github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa/go.mod h1:x/1Gn8zydmfq8dk6e9PdstVsDgu9RuyIIJqAaF//0IM=
github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0 h1:sDMmm+q/3+BukdIpxwO365v/Rbspp2Nt5XntgQRXq8Q=
github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM=
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d h1:t5Wuyh53qYyg9eqn4BbnlIT+vmhyww0TatL+zT3uWgI=
@ -113,9 +120,13 @@ github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZi
github.com/envoyproxy/go-control-plane v0.9.4 h1:rEvIZUSZ3fx39WIi3JkQqQBitGwpELBIYWeBVh6wn+E=
github.com/envoyproxy/go-control-plane v0.11.1 h1:wSUXTlLfiAQRWs2F+p+EKOY9rUyis1MyGqJ2DIk5HpM=
github.com/envoyproxy/go-control-plane v0.11.1/go.mod h1:uhMcXKCQMEJHiAb0w+YGefQLaTEw+YhGluxZkrTmD0g=
github.com/envoyproxy/go-control-plane v0.12.0 h1:4X+VP1GHd1Mhj6IB5mMeGbLCleqxjletLK6K0rbxyZI=
github.com/envoyproxy/go-control-plane v0.12.0/go.mod h1:ZBTaoJ23lqITozF0M6G4/IragXCQKCnYbmlmtHvwRG0=
github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A=
github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA=
github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE=
github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A=
github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew=
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
github.com/flosch/pongo2/v4 v4.0.2 h1:gv+5Pe3vaSVmiJvh/BZa82b7/00YUGm0PIyVVLop0Hw=
@ -171,7 +182,10 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekf
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE=
github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ=
github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo=
github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ=
github.com/golang/glog v1.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68=
github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY=
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7 h1:2hRPrmiwPrp3fQX967rNJIhQPtiGXdlQWAxKbKw3VHA=
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
@ -200,7 +214,10 @@ github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLe
github.com/google/pprof v0.0.0-20230821062121-407c9e7a662f/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go v2.0.0+incompatible h1:j0GKcs05QVmm7yesiZq2+9cxHkNK9YM6zKx4D2qucQU=
github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
@ -223,6 +240,8 @@ github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:
github.com/ianlancetaylor/demangle v0.0.0-20220517205856-0058ec4f073c/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
github.com/ianlancetaylor/demangle v0.0.0-20230524184225-eabc099b10ab h1:BA4a7pe6ZTd9F8kXETBoijjFJ/ntaa//1wiH9BZu4zU=
github.com/ianlancetaylor/demangle v0.0.0-20230524184225-eabc099b10ab/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw=
github.com/ianlancetaylor/demangle v0.0.0-20240312041847-bd984b5ce465 h1:KwWnWVWCNtNq/ewIX7HIKnELmEx2nDP42yskD/pi7QE=
github.com/ianlancetaylor/demangle v0.0.0-20240312041847-bd984b5ce465/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw=
github.com/influxdata/influxdb v1.7.6 h1:8mQ7A/V+3noMGCt/P9pD09ISaiz9XvgCk303UYA3gcs=
github.com/iris-contrib/jade v1.1.4 h1:WoYdfyJFfZIUgqNAeOyRfTNQZOksSlZ6+FnXR3AEpX0=
github.com/iris-contrib/jade v1.1.4/go.mod h1:EDqR+ur9piDl6DUgs6qRrlfzmlx/D5UybogqrXvJTBE=
@ -313,6 +332,7 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg=
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k=
github.com/microcosm-cc/bluemonday v1.0.1 h1:SIYunPjnlXcW+gVfvm0IlSeR5U3WZUOLfVmqg85Go44=
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
@ -424,6 +444,8 @@ github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1
github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
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.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
@ -477,12 +499,15 @@ golang.org/x/crypto v0.0.0-20220517005047-85d78b3ac167/go.mod h1:IxCIyHEi3zRg3s0
golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80=
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20221019170559-20944726eadf/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/exp v0.0.0-20230306221820-f0f767cdffd6/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/exp v0.0.0-20230807204917-050eac23e9de/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63/go.mod h1:0v4NqG35kSWCMzLaMeX+IQrlSnVE/bqGSyC2cz/9Le8=
golang.org/x/exp v0.0.0-20240205201215-2c58cdc269a3/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4=
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@ -514,6 +539,9 @@ golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ=
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@ -524,7 +552,10 @@ golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8=
golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE=
golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8=
golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI=
golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ=
golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o=
golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI=
golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8=
golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852 h1:xYq6+9AtI+xP3M4r0N1hCkHrInHDBohhquRgx9Kk6gI=
golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -553,7 +584,13 @@ golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/telemetry v0.0.0-20240208230135-b75ee8823808 h1:+Kc94D8UVEVxJnLXp/+FMfqQARZtWHfVrcRtcG8aT3g=
golang.org/x/telemetry v0.0.0-20240208230135-b75ee8823808/go.mod h1:KG1lNk5ZFNssSZLrpVb4sMXKMpGwGXOxSG3rnu2gZQQ=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2 h1:IRJeR9r1pYWsHKTRe/IInb7lYvbBVIqOgsX/u0mbOWY=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw=
golang.org/x/term v0.3.0 h1:qoo4akIqOcDME5bhc/NgxUdovd6BSS2uMsVjB56q1xI=
@ -568,7 +605,12 @@ golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q=
golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
@ -591,6 +633,7 @@ golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM
golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
@ -604,6 +647,7 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc=
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
@ -615,11 +659,16 @@ google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5 h1:L6iMMGrtzgHsWof
google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5/go.mod h1:oH/ZOT02u4kWEp7oYBGYFFkCdKS/uYR9Z7+0/xuuFp8=
google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97 h1:SeZZZx0cP0fqUyA+oRzP9k7cSwJlvDFiROO72uwD6i0=
google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97/go.mod h1:t1VqOqqvce95G3hIDCT5FeO3YUc6Q4Oe24L/+rNMxRk=
google.golang.org/genproto v0.0.0-20240205150955-31a09d347014 h1:g/4bk7P6TPMkAUbUhquq98xey1slwvuVJPosdBqYJlU=
google.golang.org/genproto v0.0.0-20240205150955-31a09d347014/go.mod h1:xEgQu1e4stdSSsxPDK8Azkrk/ECl5HvdPf6nbZrTS5M=
google.golang.org/genproto v0.0.0-20240125205218-1f4bbc51befe h1:USL2DhxfgRchafRvt/wYyyQNzwgL7ZiURcozOE/Pkvo=
google.golang.org/genproto v0.0.0-20240125205218-1f4bbc51befe/go.mod h1:cc8bqMqtv9gMOr0zHg2Vzff5ULhhL2IXP4sbcn32Dro=
google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de h1:F6qOa9AZTYJXOUEr4jDysRDLrm4PHePlge4v4TGAlxY=
google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:VUhTRKeHn9wwcdrk73nvdC9gF178Tzhmt/qyaFcPLSo=
google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98 h1:FmF5cCW94Ij59cfpoLiwTgodWmm60eEV0CjlsVg2fuw=
google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98/go.mod h1:rsr7RhLuwsDKL7RmgDDCUc6yaGr1iqceVb5Wv6f6YvQ=
google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 h1:JpwMPBpFN3uKhdaekDpiNlImDdkUAyiJ6ez/uxGaUSo=
google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:0xJLfVdJqpAPl8tDg1ujOCGzx6LFLttXT5NhllGOXY4=
google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de h1:jFNzHPIeuzhdRwVhbZdiym9q0ory/xY3sA+v2wPg8I0=
google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:5iCWqnniDlqZHrd3neWVTOwvh/v6s3232omMecelax8=
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=

View File

@ -71,7 +71,6 @@ func TestBlockedHostEngine_IsBlocked(t *testing.T) {
}}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
@ -90,7 +89,7 @@ func TestBlockedHostEngine_IsBlocked_concurrent(t *testing.T) {
engine := newBlockedHostEngine(rules)
wg := &sync.WaitGroup{}
for i := 0; i < routinesLimit; i++ {
for i := range routinesLimit {
wg.Add(1)
host := fmt.Sprintf("%d.%s", i, "block.test")

View File

@ -236,7 +236,7 @@ func BenchmarkDefaultProfile_IsBlocked(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
for range b.N {
_ = a.IsBlocked(passReq, passAddrPort, nil)
}
})
@ -247,7 +247,7 @@ func BenchmarkDefaultProfile_IsBlocked(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
for range b.N {
_ = a.IsBlocked(blockReq, passAddrPort, nil)
}
})

View File

@ -5,6 +5,7 @@ import (
"net/netip"
"unicode/utf8"
"github.com/AdguardTeam/AdGuardDNS/internal/agdpasswd"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/netutil"
)
@ -16,6 +17,9 @@ import (
// NOTE: Do not change fields of this structure without incrementing
// [internal/profiledb/internal.FileCacheVersion].
type Device struct {
// Auth defines authentication settings of this device. It's never nil.
Auth *AuthSettings
// ID is the unique ID of the device.
ID DeviceID
@ -104,3 +108,20 @@ func NewDeviceName(s string) (n DeviceName, err error) {
return DeviceName(s), nil
}
// AuthSettings are the authentication settings of a device.
//
// NOTE: Do not change fields of this structure without incrementing
// [internal/profiledb/internal.FileCacheVersion].
type AuthSettings struct {
// PasswordHash is the hash of the auth password. It is never nil.
PasswordHash agdpasswd.Authenticator
// Enabled tells whether the authentication should be enabled at all.
// This must be true in order for all parameters to work.
Enabled bool
// DoHAuthOnly defines if the device should only be authenticated through
// DoH protocol.
DoHAuthOnly bool
}

View File

@ -42,7 +42,6 @@ func TestNewDeviceName(t *testing.T) {
}}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()

View File

@ -17,33 +17,41 @@ const (
// FilterListIDNone means that no filter were applied at all.
FilterListIDNone FilterListID = ""
// FilterListIDBlockedService is the shared filter list ID used when a
// FilterListIDBlockedService is the shared filter-list ID used when a
// request was blocked by the service blocker.
FilterListIDBlockedService FilterListID = "blocked_service"
// FilterListIDCustom is the special shared filter list ID used when
// FilterListIDCustom is the special shared filter-list ID used when
// a request was filtered by a custom profile rule.
FilterListIDCustom FilterListID = "custom"
// FilterListIDAdultBlocking is the special shared filter list ID used when
// FilterListIDAdultBlocking is the special shared filter-list ID used when
// a request was filtered by the adult content blocking filter.
FilterListIDAdultBlocking FilterListID = "adult_blocking"
// FilterListIDSafeBrowsing is the special shared filter list ID used when
// FilterListIDSafeBrowsing is the special shared filter-list ID used when
// a request was filtered by the safe browsing filter.
FilterListIDSafeBrowsing FilterListID = "safe_browsing"
// FilterListIDNewRegDomains is the special shared filter list ID used when
// FilterListIDNewRegDomains is the special shared filter-list ID used when
// a request was filtered by the newly registered domains filter.
FilterListIDNewRegDomains FilterListID = "newly_registered_domains"
// FilterListIDGeneralSafeSearch is the shared filter list ID used when
// FilterListIDGeneralSafeSearch is the shared filter-list ID used when
// a request was modified by the general safe search filter.
FilterListIDGeneralSafeSearch FilterListID = "general_safe_search"
// FilterListIDYoutubeSafeSearch is the special shared filter list ID used
// FilterListIDYoutubeSafeSearch is the special shared filter-list ID used
// when a request was modified by the YouTube safe search filter.
FilterListIDYoutubeSafeSearch FilterListID = "youtube_safe_search"
// FilterListIDAdGuardDNS is the special filter-list ID of the main AdGuard
// DNS filtering-rule list. For this list, rule statistics are collected.
FilterListIDAdGuardDNS FilterListID = "adguard_dns_filter"
// FilterListIDAdGuardPopup is the special filter-list ID of the AdGuard DNS
// list of popup domains.
FilterListIDAdGuardPopup FilterListID = "adguard_popup_filter"
)
// The maximum and minimum lengths of a filter list ID.
@ -72,6 +80,21 @@ func NewFilterListID(s string) (id FilterListID, err error) {
return FilterListID(s), nil
}
// SupportsDNSRewrite returns true if the $dnsrewrite rules in filtering-rule
// lists with this ID should be processed.
func (id FilterListID) SupportsDNSRewrite() (ok bool) {
switch id {
case
FilterListIDAdGuardPopup,
FilterListIDCustom,
FilterListIDGeneralSafeSearch,
FilterListIDYoutubeSafeSearch:
return true
default:
return false
}
}
// FilterRuleText is the text of a single rule within a filter.
type FilterRuleText string

View File

@ -37,7 +37,6 @@ func TestNewFilterListID(t *testing.T) {
}}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
@ -86,7 +85,6 @@ func TestNewFilterRuleText(t *testing.T) {
}}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()

View File

@ -14,7 +14,7 @@ func BenchmarkNewRequestID(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
for range b.N {
reqIDSink = agd.NewRequestID()
}

View File

@ -2,23 +2,14 @@ package agd
import (
"crypto/tls"
"fmt"
"math"
"net/netip"
"github.com/AdguardTeam/golibs/stringutil"
"github.com/AdguardTeam/golibs/container"
"github.com/miekg/dns"
)
// ServerGroup is a group of DNS servers all of which use the same filtering
// settings.
type ServerGroup struct {
// BlockPageRedirect is the configuration for the server group's block page.
// BlockPageRedirect is never nil.
//
// TODO(a.garipov): Use.
BlockPageRedirect *BlockPageRedirect
// DDR is the configuration for the server group's Discovery Of Designated
// Resolvers (DDR) handlers. DDR is never nil.
DDR *DDR
@ -60,11 +51,11 @@ type TLS struct {
type DDR struct {
// DeviceTargets is the set of all domain names, subdomains of which should
// be checked for DDR queries with device IDs.
DeviceTargets *stringutil.Set
DeviceTargets *container.MapSet[string]
// PublicTargets is the set of all public domain names, DDR queries for
// which should be processed.
PublicTargets *stringutil.Set
PublicTargets *container.MapSet[string]
// DeviceRecordTemplates are used to respond to DDR queries from recognized
// devices.
@ -78,81 +69,3 @@ type DDR struct {
// name queries receive an NXDOMAIN response.
Enabled bool
}
// BlockPageRedirect is the configuration for a [ServerGroup]'s block page.
type BlockPageRedirect struct {
// Apply defines request parameters based on which the block page is shown
// always. If a request matches Apply, both [BlockPageRedirect.Skip] and
// [BlockPageRedirect.Probability] are ignored.
//
// If [BlockPageRedirect.Enabled] is true, Apply must not be nil.
Apply *BlockPageRedirectApply
// Skip defines request parameters based on which the block page is not
// shown, regardless of [BlockPageRedirect.Probability].
//
// If [BlockPageRedirect.Enabled] is true, Skip must not be nil.
Skip *BlockPageRedirectSkip
// IPv4 are the IPv4 addresses of the block page, used to respond to A
// queries.
//
// If [BlockPageRedirect.Enabled] is true, IPv4, [BlockPageRedirect.IPv6],
// or both must be filled.
IPv4 []netip.Addr
// IPv6 are the IPv6 addresses of the block page, used to respond to AAAA
// queries.
//
// If [BlockPageRedirect.Enabled] is true, [BlockPageRedirect.IPv4], IPv6,
// or both must be filled.
IPv6 []netip.Addr
// Probability defines the probability of responding with the block page IPs
// based on remote address. Probability must be between 0.0 and 1.0.
Probability Probability
// Enabled defines whether the block-page feature is enabled.
Enabled bool
}
// Probability is a type for probabilities ranging from 0.0 to 1.0.
type Probability float64
// NewProbability returns a properly converted Probability or an error.
func NewProbability(f float64) (prob Probability, err error) {
if math.IsNaN(f) || f < 0.0 || f > 1.0 {
return 0, fmt.Errorf("probability must be between 0.0 and 1.0; got %v", f)
}
return Probability(f), nil
}
// MustNewProbability returns a properly converted Probability or panics with an
// error.
func MustNewProbability(f float64) (prob Probability) {
prob, err := NewProbability(f)
if err != nil {
panic(err)
}
return prob
}
// BlockPageRedirectApply defines the conditions for applying the block-page
// logic for a particular request.
type BlockPageRedirectApply struct {
// ClientSubnets are the subnets for which block page is always enabled.
ClientSubnets []netip.Prefix
}
// BlockPageRedirectSkip defines the conditions for skipping the block page
// logic for a particular request.
type BlockPageRedirectSkip struct {
// ClientSubnets are the subnets for which block page is always disabled.
ClientSubnets []netip.Prefix
// QuestionDomains are the domain names for which block page is always
// disabled.
QuestionDomains []string
}

View File

@ -0,0 +1,59 @@
// Package agdcache contains cache interfaces, helpers, and implementations.
package agdcache
import (
"time"
)
// Interface is the cache interface.
type Interface[K, T any] interface {
// Set sets key and val as cache pair.
Set(key K, val T)
// SetWithExpire sets key and val as cache pair with expiration time.
SetWithExpire(key K, val T, expiration time.Duration)
// Get gets val from the cache using key.
Get(key K) (val T, ok bool)
// Clearer completely clears cache.
Clearer
// Len returns the number of items in the cache.
Len() (n int)
}
// Clearer is a partial cache interface.
type Clearer interface {
// Clear completely clears cache.
Clear()
}
// Empty is an [Interface] implementation that does nothing.
type Empty[K, T any] struct{}
// type check
var _ Interface[any, any] = Empty[any, any]{}
// Set implements the [Interface] interface for Empty.
func (c Empty[K, T]) Set(key K, val T) {}
// SetWithExpire implements the [Interface] interface for Empty.
func (c Empty[K, T]) SetWithExpire(key K, val T, expiration time.Duration) {}
// Get implements the [Interface] interface for Empty.
func (c Empty[K, T]) Get(key K) (val T, ok bool) {
return val, false
}
// type check
var _ Clearer = Empty[any, any]{}
// Clear implements the [Interface] interface for Empty.
func (c Empty[K, T]) Clear() {}
// Len implements the [Interface] interface for Empty. n may include items that
// have expired, but have not yet been cleaned up.
func (c Empty[K, T]) Len() (n int) {
return 0
}

106
internal/agdcache/lru.go Normal file
View File

@ -0,0 +1,106 @@
package agdcache
import (
"fmt"
"time"
"github.com/AdguardTeam/golibs/errors"
"github.com/bluele/gcache"
)
// LRUConfig is a configuration structure of a cache.
type LRUConfig struct {
Size int
}
// LRU is an [Interface] implementation.
type LRU[K, T any] struct {
cache gcache.Cache
}
// NewLRU returns a new initialized LRU cache.
func NewLRU[K, T any](conf *LRUConfig) (c *LRU[K, T]) {
return &LRU[K, T]{
cache: gcache.New(conf.Size).LRU().Build(),
}
}
// type check
var _ Interface[any, any] = (*LRU[any, any])(nil)
// Set implements the [Interface] interface for *LRU.
func (c *LRU[K, T]) Set(key K, val T) {
err := c.cache.Set(key, val)
if err != nil {
// Shouldn't happen, since we don't set a serialization function.
panic(fmt.Errorf("agdcache: setting cache item: %w", err))
}
}
// SetWithExpire implements the [Interface] interface for *LRU.
func (c *LRU[K, T]) SetWithExpire(key K, val T, expiration time.Duration) {
err := c.cache.SetWithExpire(key, val, expiration)
if err != nil {
// Shouldn't happen, since we don't set a serialization function.
panic(fmt.Errorf("agdcache: setting cache item with expiration: %w", err))
}
}
// Get implements the [Interface] interface for *LRU.
func (c *LRU[K, T]) Get(key K) (val T, ok bool) {
v, err := c.cache.Get(key)
if err != nil {
if !errors.Is(err, gcache.KeyNotFoundError) {
// Shouldn't happen, since we don't set a serialization function.
panic(fmt.Errorf("agdcache: getting cache item: %w", err))
}
return val, false
}
// T may be an interface type, so check v against nil explicitly to prevent
// v.(T) below from panicking.
if v == nil {
return val, true
}
return v.(T), true
}
// type check
var _ Clearer = (*LRU[any, any])(nil)
// Clear implements the [Interface] interface for *LRU.
func (c *LRU[K, T]) Clear() {
c.cache.Purge()
}
// Len implements the [Interface] interface for *LRU. n may include items
// that have expired, but have not yet been cleaned up.
func (c *LRU[K, T]) Len() (n int) {
const checkExpired = false
return c.cache.Len(checkExpired)
}
// Manager manages caches.
type Manager struct {
caches map[string]Clearer
}
// NewManager returns a new initialized *Manager.
func NewManager() (m *Manager) {
return &Manager{
caches: map[string]Clearer{},
}
}
// Add adds cache by id.
func (m *Manager) Add(id string, cache Clearer) {
m.caches[id] = cache
}
// ClearByID clears cache by id.
func (m *Manager) ClearByID(id string) {
m.caches[id].Clear()
}

View File

@ -0,0 +1,69 @@
package agdcache_test
import (
"testing"
"github.com/AdguardTeam/AdGuardDNS/internal/agdcache"
"github.com/stretchr/testify/assert"
)
func TestLRU(t *testing.T) {
const (
key = "key"
val = 123
nonExistingKey = "nonExistingKey"
)
cache := agdcache.NewLRU[string, int](&agdcache.LRUConfig{
Size: 10,
})
cache.Set(key, val)
assert.Equal(t, 1, cache.Len())
v, ok := cache.Get(key)
assert.Equal(t, val, v)
assert.True(t, ok)
v, ok = cache.Get(nonExistingKey)
assert.Equal(t, 0, v)
assert.False(t, ok)
cache.Clear()
assert.Equal(t, 0, cache.Len())
}
func TestManager(t *testing.T) {
const (
key = "key"
val = 123
id = "cacheID"
nonExistingKey = "nonExistingKey"
)
cache := agdcache.NewLRU[string, int](&agdcache.LRUConfig{
Size: 10,
})
cache.Set(key, val)
assert.Equal(t, 1, cache.Len())
v, ok := cache.Get(key)
assert.Equal(t, val, v)
assert.True(t, ok)
v, ok = cache.Get(nonExistingKey)
assert.Equal(t, 0, v)
assert.False(t, ok)
m := agdcache.NewManager()
m.Add(id, cache)
m.ClearByID(id)
assert.Equal(t, 0, cache.Len())
}

View File

@ -15,6 +15,7 @@ import (
// HTTP header value constants.
const (
HdrValApplicationJSON = "application/json"
HdrValGzip = "gzip"
HdrValTextCSV = "text/csv"
HdrValTextHTML = "text/html"
HdrValTextPlain = "text/plain"

View File

@ -34,22 +34,18 @@ func NewClient(conf *ClientConfig) (c *Client) {
}
}
// Get is a wrapper around http.Client.Get.
// Get is a wrapper around [http.Client.Get].
//
// When err is nil, resp always contains a non-nil resp.Body. Caller should
// close resp.Body when done reading from it.
//
// See also go doc http.Client.Get.
func (c *Client) Get(ctx context.Context, u *url.URL) (resp *http.Response, err error) {
return c.do(ctx, http.MethodGet, u, "", nil)
}
// Post is a wrapper around http.Client.Post.
// Post is a wrapper around [http.Client.Post].
//
// When err is nil, resp always contains a non-nil resp.Body. Caller should
// close resp.Body when done reading from it.
//
// See also go doc http.Client.Post.
func (c *Client) Post(
ctx context.Context,
u *url.URL,
@ -59,7 +55,7 @@ func (c *Client) Post(
return c.do(ctx, http.MethodPost, u, contentType, body)
}
// Put is a wrapper around http.Client.Do.
// Put is a wrapper around [http.Client.Put].
//
// When err is nil, resp always contains a non-nil resp.Body. Caller should
// close resp.Body when done reading from it.
@ -72,7 +68,7 @@ func (c *Client) Put(
return c.do(ctx, http.MethodPut, u, contentType, body)
}
// do is a wrapper around http.Client.Do.
// do is a wrapper around [http.Client.Do].
func (c *Client) do(
ctx context.Context,
method string,
@ -98,7 +94,8 @@ func (c *Client) do(
resp, err = c.http.Do(req)
if err != nil && resp != nil && resp.Header != nil {
// A non-nil Response with a non-nil error only occurs when CheckRedirect fails.
// A non-nil Response with a non-nil error only occurs when
// CheckRedirect fails.
return resp, WrapServerError(err, resp)
}

View File

@ -1,213 +0,0 @@
package agdnet
import (
"context"
"fmt"
"math"
"net"
"net/netip"
"sync"
"time"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/netutil"
)
// Resolver is the DNS resolver interface.
//
// See [net.Resolver].
//
// TODO(a.garipov): Move to golibs and unite with the one in module dnsproxy.
type Resolver interface {
// LookupNetIP returns a slice of host's IP addresses of family specified by
// fam, which must be either [netutil.AddrFamilyIPv4] or
// [netutil.AddrFamilyIPv6].
LookupNetIP(
ctx context.Context,
fam netutil.AddrFamily,
host string,
) (ips []netip.Addr, err error)
}
// DefaultResolver uses [net.DefaultResolver] to resolve addresses.
type DefaultResolver struct{}
// type check
var _ Resolver = DefaultResolver{}
// LookupNetIP implements the [Resolver] interface for DefaultResolver.
func (DefaultResolver) LookupNetIP(
ctx context.Context,
fam netutil.AddrFamily,
host string,
) (ips []netip.Addr, err error) {
switch fam {
case netutil.AddrFamilyIPv4:
return net.DefaultResolver.LookupNetIP(ctx, "ip4", host)
case netutil.AddrFamilyIPv6:
return net.DefaultResolver.LookupNetIP(ctx, "ip6", host)
default:
return nil, net.UnknownNetworkError(fam.String())
}
}
// resolveCache is a simple address resolving cache.
type resolveCache map[string]*resolveCacheItem
// resolveCacheItem is an item of [resolveCache].
type resolveCacheItem struct {
refrTime time.Time
ips []netip.Addr
}
// CachingResolver caches resolved results for hosts for a certain time,
// regardless of the actual TTLs of the records. It is used for caching the
// results of lookups of hostnames that don't change their IP addresses often.
type CachingResolver struct {
resolver Resolver
// mu protects ip4 and ip6.
mu *sync.Mutex
ipv4 resolveCache
ipv6 resolveCache
ttl time.Duration
}
// NewCachingResolver returns a new caching resolver.
func NewCachingResolver(resolver Resolver, ttl time.Duration) (c *CachingResolver) {
return &CachingResolver{
resolver: resolver,
mu: &sync.Mutex{},
ipv4: resolveCache{},
ipv6: resolveCache{},
ttl: ttl,
}
}
// type check
var _ Resolver = (*CachingResolver)(nil)
// LookupNetIP implements the [Resolver] interface for *CachingResolver. host
// should be normalized. Slice ips and its elements must not be mutated.
func (c *CachingResolver) LookupNetIP(
ctx context.Context,
fam netutil.AddrFamily,
host string,
) (ips []netip.Addr, err error) {
c.mu.Lock()
defer c.mu.Unlock()
var item *resolveCacheItem
switch fam {
case netutil.AddrFamilyIPv4:
item = c.ipv4[host]
case netutil.AddrFamilyIPv6:
item = c.ipv6[host]
default:
return nil, net.UnknownNetworkError(fam.String())
}
if item == nil || time.Since(item.refrTime) > c.ttl {
item, err = c.resolve(ctx, fam, host)
if err != nil {
return nil, err
}
}
return item.ips, nil
}
// resolve looks up the IP addresses for host and puts them into the cache.
func (c *CachingResolver) resolve(
ctx context.Context,
fam netutil.AddrFamily,
host string,
) (item *resolveCacheItem, err error) {
var ips []netip.Addr
refrTime := time.Now()
// Don't resolve IP addresses.
ip := ipFromHost(host, fam)
if ip != (netip.Addr{}) {
ips = []netip.Addr{ip}
// Set the refresh time to the maximum date that time.Duration allows to
// prevent this item from refreshing.
refrTime = time.Unix(0, math.MaxInt64)
} else {
ips, err = c.resolver.LookupNetIP(ctx, fam, host)
if err != nil {
if !isExpectedLookupError(fam, err) {
return nil, fmt.Errorf("resolving %s addr for %q: %w", fam, host, err)
}
log.Debug("caching resolver: warning: %s", err)
}
}
var cache resolveCache
if fam == netutil.AddrFamilyIPv4 {
cache = c.ipv4
} else {
cache = c.ipv6
}
item = &resolveCacheItem{
refrTime: refrTime,
ips: ips,
}
cache[host] = item
return item, nil
}
// ipFromHost parses host as if it'd be an IP address of specified fam. It
// returns an empty netip.
func ipFromHost(host string, fam netutil.AddrFamily) (ip netip.Addr) {
var famFunc func(netip.Addr) (ok bool)
switch fam {
case netutil.AddrFamilyIPv4:
famFunc = netip.Addr.Is4
case netutil.AddrFamilyIPv6:
famFunc = netip.Addr.Is6
default:
return netip.Addr{}
}
ip, err := netip.ParseAddr(host)
if err != nil || !famFunc(ip) {
return netip.Addr{}
}
return ip
}
// isExpectedLookupError returns true if the error is an expected lookup error.
func isExpectedLookupError(fam netutil.AddrFamily, err error) (ok bool) {
var dnsErr *net.DNSError
if fam == netutil.AddrFamilyIPv6 && errors.As(err, &dnsErr) {
// It's expected that Go default DNS resolver returns a DNS error in
// some cases when it receives an empty response. It's unclear what
// exactly triggers this error, though.
//
// TODO(ameshkov): Consider researching this in detail.
return true
}
var addrErr *net.AddrError
if !errors.As(err, &addrErr) {
return false
}
// Expect the error about no suitable addresses. For example, no IPv6
// addresses for a host that does have IPv4 ones.
//
// See function filterAddrList in ${GOROOT}/src/net/ipsock.go.
return addrErr.Err == "no suitable address found"
}

View File

@ -1,88 +0,0 @@
package agdnet_test
import (
"context"
"net/netip"
"testing"
"github.com/AdguardTeam/AdGuardDNS/internal/agdnet"
"github.com/AdguardTeam/AdGuardDNS/internal/agdtest"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/timeutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestCachingResolver_Resolve(t *testing.T) {
const testHost = "addr.example"
var numLookups uint64
wantIPv4 := []netip.Addr{netip.MustParseAddr("1.2.3.4")}
wantIPv6 := []netip.Addr{netip.MustParseAddr("1234::5678")}
r := &agdtest.Resolver{
OnLookupNetIP: func(
ctx context.Context,
fam netutil.AddrFamily,
host string,
) (ips []netip.Addr, err error) {
numLookups++
if fam == netutil.AddrFamilyIPv4 {
return wantIPv4, nil
}
return nil, nil
},
}
cached := agdnet.NewCachingResolver(r, 1*timeutil.Day)
testCases := []struct {
name string
host string
wantIPs []netip.Addr
wantNum uint64
fam netutil.AddrFamily
}{{
name: "initial",
host: testHost,
wantIPs: wantIPv4,
wantNum: 1,
fam: netutil.AddrFamilyIPv4,
}, {
name: "cached",
host: testHost,
wantIPs: wantIPv4,
wantNum: 1,
fam: netutil.AddrFamilyIPv4,
}, {
name: "other_network",
host: testHost,
wantIPs: nil,
wantNum: 2,
fam: netutil.AddrFamilyIPv6,
}, {
name: "ipv4",
host: wantIPv4[0].String(),
wantIPs: wantIPv4,
wantNum: 2,
fam: netutil.AddrFamilyIPv4,
}, {
name: "ipv6",
host: wantIPv6[0].String(),
wantIPs: wantIPv6,
wantNum: 2,
fam: netutil.AddrFamilyIPv6,
}}
ctx := context.Background()
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
got, err := cached.LookupNetIP(ctx, tc.fam, tc.host)
require.NoError(t, err)
assert.Equal(t, tc.wantNum, numLookups)
assert.Equal(t, tc.wantIPs, got)
})
}
}

View File

@ -0,0 +1,51 @@
// Package agdpasswd contains authentication utils.
package agdpasswd
import (
"context"
"golang.org/x/crypto/bcrypt"
)
// Authenticator represents a password authenticator.
type Authenticator interface {
// Authenticate returns true if the given passwd is allowed.
Authenticate(ctx context.Context, passwd []byte) (ok bool)
}
// AllowAuthenticator is an empty authenticator implementation that always
// grants access, regardless of any restrictions.
type AllowAuthenticator struct{}
// type check
var _ Authenticator = AllowAuthenticator{}
// Authenticate implements the [Authenticator] interface for AllowAuthenticator.
func (AllowAuthenticator) Authenticate(_ context.Context, _ []byte) (ok bool) {
return true
}
// PasswordHashBcrypt is the Bcrypt implementation of [Authenticator].
type PasswordHashBcrypt struct {
// bytes contains the password hash.
bytes []byte
}
// NewPasswordHashBcrypt returns a new bcrypt hashed password authenticator.
func NewPasswordHashBcrypt(hashedPassword []byte) (p *PasswordHashBcrypt) {
return &PasswordHashBcrypt{bytes: hashedPassword}
}
// PasswordHash returns password hash bytes slice.
func (p *PasswordHashBcrypt) PasswordHash() (b []byte) {
return p.bytes
}
// type check
var _ Authenticator = (*PasswordHashBcrypt)(nil)
// Authenticate implements the [Authenticator] interface for
// *PasswordHashBcrypt.
func (p *PasswordHashBcrypt) Authenticate(_ context.Context, passwd []byte) (ok bool) {
return bcrypt.CompareHashAndPassword(p.bytes, passwd) == nil
}

View File

@ -0,0 +1,49 @@
package agdpasswd_test
import (
"context"
"testing"
"github.com/AdguardTeam/AdGuardDNS/internal/agdpasswd"
"github.com/AdguardTeam/golibs/testutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/crypto/bcrypt"
)
func TestMain(m *testing.M) {
testutil.DiscardLogOutput(m)
}
func TestPasswordHashBcrypt_Authenticate(t *testing.T) {
t.Parallel()
const passwd = "mypassword"
hash, err := bcrypt.GenerateFromPassword([]byte(passwd), 0)
require.NoError(t, err)
authenticator := agdpasswd.NewPasswordHashBcrypt(hash)
testCases := []struct {
want assert.BoolAssertionFunc
name string
pass string
}{{
want: assert.True,
name: "success",
pass: passwd,
}, {
want: assert.False,
name: "fail",
pass: "an-other-passwd",
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
tc.want(t, authenticator.Authenticate(context.Background(), []byte(tc.pass)))
})
}
}

View File

@ -13,6 +13,10 @@ import (
// Refresher is the interface for entities that can update themselves.
type Refresher interface {
// Refresh is called by a [RefreshWorker]. The error returned by Refresh is
// only returned from [RefreshWorker.Shutdown] and only when
// [RefreshWorkerConfig.RefreshOnShutdown] is true. In all other cases, the
// error is ignored, and refreshers must handle error reporting themselves.
Refresh(ctx context.Context) (err error)
}
@ -21,11 +25,9 @@ type Refresher interface {
type RefreshWorker struct {
done chan unit
context func() (ctx context.Context, cancel context.CancelFunc)
logRoutine func(format string, args ...any)
tick *time.Ticker
rand *rand.Rand
refr Refresher
errColl errcoll.Interface
name string
maxStartSleep time.Duration
@ -40,12 +42,6 @@ type RefreshWorkerConfig struct {
// Refresher is the entity being refreshed.
Refresher Refresher
// ErrColl is used to collect errors during refreshes.
//
// TODO(a.garipov): Remove this and make all Refreshers handle their own
// errors.
ErrColl errcoll.Interface
// Name is the name of this worker. It is used for logging and error
// collecting.
//
@ -64,12 +60,6 @@ type RefreshWorkerConfig struct {
// that should persist to disk or remote storage before shutting down.
RefreshOnShutdown bool
// RoutineLogsAreDebug, if true, instructs the worker to write initial and
// final log messages for each singular refresh on the Debug level rather
// than on the Info one. This is useful to prevent routine logs from
// workers with a small interval from overflowing with messages.
RoutineLogsAreDebug bool
// RandomizeStart, if true, instructs the worker to sleep before starting a
// refresh. The duration of the sleep is a random duration of up to 10 % of
// Interval.
@ -82,14 +72,6 @@ type RefreshWorkerConfig struct {
// NewRefreshWorker returns a new valid *RefreshWorker with the provided
// parameters. c must not be nil.
func NewRefreshWorker(c *RefreshWorkerConfig) (w *RefreshWorker) {
// TODO(a.garipov): Add log.WithLevel.
var logRoutine func(format string, args ...any)
if c.RoutineLogsAreDebug {
logRoutine = log.Debug
} else {
logRoutine = log.Info
}
var maxStartSleep time.Duration
var rng *rand.Rand
if c.RandomizeStart {
@ -100,11 +82,9 @@ func NewRefreshWorker(c *RefreshWorkerConfig) (w *RefreshWorker) {
return &RefreshWorker{
done: make(chan unit),
context: c.Context,
logRoutine: logRoutine,
tick: time.NewTicker(c.Interval),
rand: rng,
refr: c.Refresher,
errColl: c.ErrColl,
name: c.Name,
maxStartSleep: maxStartSleep,
refrOnShutdown: c.RefreshOnShutdown,
@ -172,7 +152,7 @@ func (w *RefreshWorker) sleepRandom() (shouldRefresh bool) {
}
sleepDur := time.Duration(w.rand.Int63n(int64(w.maxStartSleep)))
w.logRoutine("worker %q: sleeping for %s before refresh", w.name, sleepDur)
log.Debug("worker %q: sleeping for %s before refresh", w.name, sleepDur)
timer := time.NewTimer(sleepDur)
defer func() {
@ -197,24 +177,51 @@ func (w *RefreshWorker) sleepRandom() (shouldRefresh bool) {
// refresh refreshes the entity and logs the status of the refresh.
func (w *RefreshWorker) refresh() {
name := w.name
w.logRoutine("worker %q: refreshing", name)
// TODO(a.garipov): Consider adding a helper for enriching errors with
// context deadline data without duplication. See an example in method
// filter.refreshableFilter.refresh.
ctx, cancel := w.context()
defer cancel()
log.Debug("worker %q: starting refresh", name)
err := w.refr.Refresh(ctx)
log.Debug("worker %q: finished refresh", name)
_ = w.refr.Refresh(ctx)
}
// RefresherWithErrColl reports all refresh errors to errColl and logs them
// using a provided logging function.
type RefresherWithErrColl struct {
refr Refresher
log func(format string, args ...any)
errColl errcoll.Interface
prefix string
}
// NewRefresherWithErrColl wraps refr into a refresher that collects errors and
// logs them.
func NewRefresherWithErrColl(
refr Refresher,
logFunc func(format string, args ...any),
errColl errcoll.Interface,
prefix string,
) (wrapped *RefresherWithErrColl) {
return &RefresherWithErrColl{
refr: refr,
log: logFunc,
errColl: errColl,
prefix: prefix,
}
}
// type check
var _ Refresher = (*RefresherWithErrColl)(nil)
// Refresh implements the [Refresher] interface for *RefresherWithErrColl.
func (r *RefresherWithErrColl) Refresh(ctx context.Context) (err error) {
err = r.refr.Refresh(ctx)
if err != nil {
errcoll.Collectf(ctx, w.errColl, "%s: %w", name, err)
return
err = fmt.Errorf("%s: %w", r.prefix, err)
r.log("%s", err)
r.errColl.Collect(ctx, err)
}
w.logRoutine("worker %q: refreshed successfully", name)
return err
}

View File

@ -42,34 +42,23 @@ func newTestRefresher(t *testing.T, respErr error) (refr *agdtest.Refresher, syn
return refr, syncCh
}
// newRefrConf returns worker configuration.
func newRefrConf(
// newRefrConfig returns worker configuration.
func newRefrConfig(
t *testing.T,
refr agdservice.Refresher,
ivl time.Duration,
refrOnShutDown bool,
errCh chan sig,
) (conf *agdservice.RefreshWorkerConfig) {
t.Helper()
pt := testutil.PanicT{}
errColl := &agdtest.ErrorCollector{
OnCollect: func(_ context.Context, _ error) {
testutil.RequireSend(pt, errCh, sig{}, testTimeout)
},
}
return &agdservice.RefreshWorkerConfig{
Context: func() (ctx context.Context, cancel context.CancelFunc) {
return context.WithTimeout(context.Background(), testTimeout)
},
Refresher: refr,
ErrColl: errColl,
Name: name,
Interval: ivl,
RefreshOnShutdown: refrOnShutDown,
RoutineLogsAreDebug: false,
RandomizeStart: false,
}
}
@ -77,17 +66,15 @@ func newRefrConf(
func TestRefreshWorker(t *testing.T) {
t.Run("success", func(t *testing.T) {
refr, syncCh := newTestRefresher(t, nil)
errCh := make(chan sig, 1)
w := agdservice.NewRefreshWorker(newRefrConf(t, refr, testIvl, false, errCh))
w := agdservice.NewRefreshWorker(newRefrConfig(t, refr, testIvl, false))
err := w.Start(agdtest.ContextWithTimeout(t, testTimeout))
err := w.Start(testutil.ContextWithTimeout(t, testTimeout))
require.NoError(t, err)
testutil.RequireReceive(t, syncCh, testTimeout)
require.Empty(t, errCh)
err = w.Shutdown(agdtest.ContextWithTimeout(t, testTimeout))
err = w.Shutdown(testutil.ContextWithTimeout(t, testTimeout))
require.NoError(t, err)
})
@ -95,12 +82,12 @@ func TestRefreshWorker(t *testing.T) {
refr, syncCh := newTestRefresher(t, nil)
errCh := make(chan sig, 1)
w := agdservice.NewRefreshWorker(newRefrConf(t, refr, testIvlLong, true, errCh))
w := agdservice.NewRefreshWorker(newRefrConfig(t, refr, testIvlLong, true))
err := w.Start(agdtest.ContextWithTimeout(t, testTimeout))
err := w.Start(testutil.ContextWithTimeout(t, testTimeout))
require.NoError(t, err)
err = w.Shutdown(agdtest.ContextWithTimeout(t, testTimeout))
err = w.Shutdown(testutil.ContextWithTimeout(t, testTimeout))
require.NoError(t, err)
testutil.RequireReceive(t, syncCh, testTimeout)
@ -109,33 +96,29 @@ func TestRefreshWorker(t *testing.T) {
t.Run("error", func(t *testing.T) {
errRefr, syncCh := newTestRefresher(t, testError)
errCh := make(chan sig, 1)
w := agdservice.NewRefreshWorker(newRefrConf(t, errRefr, testIvl, false, errCh))
w := agdservice.NewRefreshWorker(newRefrConfig(t, errRefr, testIvl, false))
err := w.Start(agdtest.ContextWithTimeout(t, testTimeout))
err := w.Start(testutil.ContextWithTimeout(t, testTimeout))
require.NoError(t, err)
testutil.RequireReceive(t, syncCh, testTimeout)
testutil.RequireReceive(t, errCh, testTimeout)
err = w.Shutdown(agdtest.ContextWithTimeout(t, testTimeout))
err = w.Shutdown(testutil.ContextWithTimeout(t, testTimeout))
require.NoError(t, err)
})
t.Run("error_on_shutdown", func(t *testing.T) {
errRefr, syncCh := newTestRefresher(t, testError)
errCh := make(chan sig, 1)
w := agdservice.NewRefreshWorker(newRefrConf(t, errRefr, testIvlLong, true, errCh))
w := agdservice.NewRefreshWorker(newRefrConfig(t, errRefr, testIvlLong, true))
err := w.Start(agdtest.ContextWithTimeout(t, testTimeout))
err := w.Start(testutil.ContextWithTimeout(t, testTimeout))
require.NoError(t, err)
err = w.Shutdown(agdtest.ContextWithTimeout(t, testTimeout))
err = w.Shutdown(testutil.ContextWithTimeout(t, testTimeout))
assert.ErrorIs(t, err, testError)
testutil.RequireReceive(t, syncCh, testTimeout)
require.Empty(t, errCh)
})
}

View File

@ -3,8 +3,6 @@
package agdtest
import (
"context"
"testing"
"time"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
@ -27,16 +25,3 @@ func NewConstructor() (c *dnsmsg.Constructor) {
func NewCloner() (c *dnsmsg.Cloner) {
return dnsmsg.NewCloner(dnsmsg.EmptyClonerStat{})
}
// ContextWithTimeout is a helper that creates a new context with timeout and
// registers ctx's cleanup with t.Cleanup.
//
// TODO(a.garipov): Move to golibs.
func ContextWithTimeout(tb testing.TB, timeout time.Duration) (ctx context.Context) {
tb.Helper()
ctx, cancel := context.WithTimeout(context.Background(), timeout)
tb.Cleanup(cancel)
return ctx
}

View File

@ -8,7 +8,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/access"
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
"github.com/AdguardTeam/AdGuardDNS/internal/agdnet"
"github.com/AdguardTeam/AdGuardDNS/internal/agdpasswd"
"github.com/AdguardTeam/AdGuardDNS/internal/agdservice"
"github.com/AdguardTeam/AdGuardDNS/internal/billstat"
"github.com/AdguardTeam/AdGuardDNS/internal/dnscheck"
@ -52,27 +52,20 @@ func (a *AccessManager) IsBlockedIP(ip netip.Addr) (blocked bool) {
return a.OnIsBlockedIP(ip)
}
// Package agdnet
// Package agdpasswd
// type check
var _ agdnet.Resolver = (*Resolver)(nil)
var _ agdpasswd.Authenticator = (*Authenticator)(nil)
// Resolver is an [agdnet.Resolver] for tests.
type Resolver struct {
OnLookupNetIP func(
ctx context.Context,
fam netutil.AddrFamily,
host string,
) (ips []netip.Addr, err error)
// Authenticator is an [agdpasswd.Authenticator] for tests.
type Authenticator struct {
OnAuthenticate func(ctx context.Context, passwd []byte) (ok bool)
}
// LookupNetIP implements the [agdnet.Resolver] interface for *Resolver.
func (r *Resolver) LookupNetIP(
ctx context.Context,
fam netutil.AddrFamily,
host string,
) (ips []netip.Addr, err error) {
return r.OnLookupNetIP(ctx, fam, host)
// Authenticate implements the [agdpasswd.Authenticator] interface for
// *Authenticator.
func (a *Authenticator) Authenticate(ctx context.Context, passwd []byte) (ok bool) {
return a.OnAuthenticate(ctx, passwd)
}
// Package agdservice

View File

@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.31.0
// protoc v4.25.1
// protoc-gen-go v1.33.0
// protoc v4.25.3
// source: backend.proto
package backendpb
@ -364,6 +364,7 @@ type DeviceSettings struct {
FilteringEnabled bool `protobuf:"varint,3,opt,name=filtering_enabled,json=filteringEnabled,proto3" json:"filtering_enabled,omitempty"`
LinkedIp []byte `protobuf:"bytes,4,opt,name=linked_ip,json=linkedIp,proto3" json:"linked_ip,omitempty"`
DedicatedIps [][]byte `protobuf:"bytes,5,rep,name=dedicated_ips,json=dedicatedIps,proto3" json:"dedicated_ips,omitempty"`
Authentication *AuthenticationSettings `protobuf:"bytes,6,opt,name=authentication,proto3" json:"authentication,omitempty"`
}
func (x *DeviceSettings) Reset() {
@ -433,6 +434,13 @@ func (x *DeviceSettings) GetDedicatedIps() [][]byte {
return nil
}
func (x *DeviceSettings) GetAuthentication() *AuthenticationSettings {
if x != nil {
return x.Authentication
}
return nil
}
type ParentalSettings struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@ -1179,6 +1187,81 @@ func (x *CidrRange) GetPrefix() uint32 {
return 0
}
type AuthenticationSettings struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
DohAuthOnly bool `protobuf:"varint,1,opt,name=doh_auth_only,json=dohAuthOnly,proto3" json:"doh_auth_only,omitempty"`
// Types that are assignable to DohPasswordHash:
//
// *AuthenticationSettings_PasswordHashBcrypt
DohPasswordHash isAuthenticationSettings_DohPasswordHash `protobuf_oneof:"doh_password_hash"`
}
func (x *AuthenticationSettings) Reset() {
*x = AuthenticationSettings{}
if protoimpl.UnsafeEnabled {
mi := &file_backend_proto_msgTypes[16]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *AuthenticationSettings) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*AuthenticationSettings) ProtoMessage() {}
func (x *AuthenticationSettings) ProtoReflect() protoreflect.Message {
mi := &file_backend_proto_msgTypes[16]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use AuthenticationSettings.ProtoReflect.Descriptor instead.
func (*AuthenticationSettings) Descriptor() ([]byte, []int) {
return file_backend_proto_rawDescGZIP(), []int{16}
}
func (x *AuthenticationSettings) GetDohAuthOnly() bool {
if x != nil {
return x.DohAuthOnly
}
return false
}
func (m *AuthenticationSettings) GetDohPasswordHash() isAuthenticationSettings_DohPasswordHash {
if m != nil {
return m.DohPasswordHash
}
return nil
}
func (x *AuthenticationSettings) GetPasswordHashBcrypt() []byte {
if x, ok := x.GetDohPasswordHash().(*AuthenticationSettings_PasswordHashBcrypt); ok {
return x.PasswordHashBcrypt
}
return nil
}
type isAuthenticationSettings_DohPasswordHash interface {
isAuthenticationSettings_DohPasswordHash()
}
type AuthenticationSettings_PasswordHashBcrypt struct {
PasswordHashBcrypt []byte `protobuf:"bytes,2,opt,name=password_hash_bcrypt,json=passwordHashBcrypt,proto3,oneof"`
}
func (*AuthenticationSettings_PasswordHashBcrypt) isAuthenticationSettings_DohPasswordHash() {}
var File_backend_proto protoreflect.FileDescriptor
var file_backend_proto_rawDesc = []byte{
@ -1263,7 +1346,7 @@ var file_backend_proto_rawDesc = []byte{
0x63, 0x6b, 0x44, 0x61, 0x6e, 0x67, 0x65, 0x72, 0x6f, 0x75, 0x73, 0x44, 0x6f, 0x6d, 0x61, 0x69,
0x6e, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6e, 0x72, 0x64, 0x18,
0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x4e, 0x72, 0x64, 0x22,
0xa3, 0x01, 0x0a, 0x0e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e,
0xe4, 0x01, 0x0a, 0x0e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e,
0x67, 0x73, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02,
0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2b, 0x0a, 0x11, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72,
@ -1273,110 +1356,123 @@ var file_backend_proto_rawDesc = []byte{
0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x49, 0x70,
0x12, 0x23, 0x0a, 0x0d, 0x64, 0x65, 0x64, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x69, 0x70,
0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0c, 0x64, 0x65, 0x64, 0x69, 0x63, 0x61, 0x74,
0x65, 0x64, 0x49, 0x70, 0x73, 0x22, 0x87, 0x02, 0x0a, 0x10, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74,
0x61, 0x6c, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e,
0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61,
0x62, 0x6c, 0x65, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x61, 0x64,
0x75, 0x6c, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b,
0x41, 0x64, 0x75, 0x6c, 0x74, 0x12, 0x2e, 0x0a, 0x13, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c,
0x5f, 0x73, 0x61, 0x66, 0x65, 0x5f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x18, 0x03, 0x20, 0x01,
0x28, 0x08, 0x52, 0x11, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x53, 0x61, 0x66, 0x65, 0x53,
0x65, 0x61, 0x72, 0x63, 0x68, 0x12, 0x2e, 0x0a, 0x13, 0x79, 0x6f, 0x75, 0x74, 0x75, 0x62, 0x65,
0x5f, 0x73, 0x61, 0x66, 0x65, 0x5f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x18, 0x04, 0x20, 0x01,
0x28, 0x08, 0x52, 0x11, 0x79, 0x6f, 0x75, 0x74, 0x75, 0x62, 0x65, 0x53, 0x61, 0x66, 0x65, 0x53,
0x65, 0x61, 0x72, 0x63, 0x68, 0x12, 0x29, 0x0a, 0x10, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64,
0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52,
0x0f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73,
0x12, 0x2d, 0x0a, 0x08, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x06, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x11, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x53, 0x65, 0x74,
0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x08, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x22,
0x54, 0x0a, 0x10, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69,
0x6e, 0x67, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x6d, 0x7a, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
0x52, 0x03, 0x74, 0x6d, 0x7a, 0x12, 0x2e, 0x0a, 0x0b, 0x77, 0x65, 0x65, 0x6b, 0x6c, 0x79, 0x52,
0x61, 0x6e, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x57, 0x65, 0x65,
0x6b, 0x6c, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0b, 0x77, 0x65, 0x65, 0x6b, 0x6c, 0x79,
0x52, 0x61, 0x6e, 0x67, 0x65, 0x22, 0xd8, 0x01, 0x0a, 0x0b, 0x57, 0x65, 0x65, 0x6b, 0x6c, 0x79,
0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x1b, 0x0a, 0x03, 0x6d, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x09, 0x2e, 0x44, 0x61, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x03, 0x6d,
0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x03, 0x74, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x09, 0x2e, 0x44, 0x61, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x03, 0x74, 0x75, 0x65, 0x12,
0x1b, 0x0a, 0x03, 0x77, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x44,
0x61, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x03, 0x77, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x03,
0x74, 0x68, 0x75, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x44, 0x61, 0x79, 0x52,
0x61, 0x6e, 0x67, 0x65, 0x52, 0x03, 0x74, 0x68, 0x75, 0x12, 0x1b, 0x0a, 0x03, 0x66, 0x72, 0x69,
0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x44, 0x61, 0x79, 0x52, 0x61, 0x6e, 0x67,
0x65, 0x52, 0x03, 0x66, 0x72, 0x69, 0x12, 0x1b, 0x0a, 0x03, 0x73, 0x61, 0x74, 0x18, 0x06, 0x20,
0x65, 0x64, 0x49, 0x70, 0x73, 0x12, 0x3f, 0x0a, 0x0e, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74,
0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e,
0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65,
0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x0e, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69,
0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x87, 0x02, 0x0a, 0x10, 0x50, 0x61, 0x72, 0x65, 0x6e,
0x74, 0x61, 0x6c, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65,
0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e,
0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x61,
0x64, 0x75, 0x6c, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x62, 0x6c, 0x6f, 0x63,
0x6b, 0x41, 0x64, 0x75, 0x6c, 0x74, 0x12, 0x2e, 0x0a, 0x13, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61,
0x6c, 0x5f, 0x73, 0x61, 0x66, 0x65, 0x5f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x18, 0x03, 0x20,
0x01, 0x28, 0x08, 0x52, 0x11, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x53, 0x61, 0x66, 0x65,
0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x12, 0x2e, 0x0a, 0x13, 0x79, 0x6f, 0x75, 0x74, 0x75, 0x62,
0x65, 0x5f, 0x73, 0x61, 0x66, 0x65, 0x5f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x18, 0x04, 0x20,
0x01, 0x28, 0x08, 0x52, 0x11, 0x79, 0x6f, 0x75, 0x74, 0x75, 0x62, 0x65, 0x53, 0x61, 0x66, 0x65,
0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x12, 0x29, 0x0a, 0x10, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x65,
0x64, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09,
0x52, 0x0f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
0x73, 0x12, 0x2d, 0x0a, 0x08, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x06, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x53, 0x65,
0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x08, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65,
0x22, 0x54, 0x0a, 0x10, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x53, 0x65, 0x74, 0x74,
0x69, 0x6e, 0x67, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x6d, 0x7a, 0x18, 0x01, 0x20, 0x01, 0x28,
0x09, 0x52, 0x03, 0x74, 0x6d, 0x7a, 0x12, 0x2e, 0x0a, 0x0b, 0x77, 0x65, 0x65, 0x6b, 0x6c, 0x79,
0x52, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x57, 0x65,
0x65, 0x6b, 0x6c, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0b, 0x77, 0x65, 0x65, 0x6b, 0x6c,
0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x22, 0xd8, 0x01, 0x0a, 0x0b, 0x57, 0x65, 0x65, 0x6b, 0x6c,
0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x1b, 0x0a, 0x03, 0x6d, 0x6f, 0x6e, 0x18, 0x01, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x44, 0x61, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x03,
0x73, 0x61, 0x74, 0x12, 0x1b, 0x0a, 0x03, 0x73, 0x75, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x09, 0x2e, 0x44, 0x61, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x03, 0x73, 0x75, 0x6e,
0x22, 0x68, 0x0a, 0x08, 0x44, 0x61, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x2f, 0x0a, 0x05,
0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f,
0x6d, 0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x03, 0x74, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x09, 0x2e, 0x44, 0x61, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x03, 0x74, 0x75, 0x65,
0x12, 0x1b, 0x0a, 0x03, 0x77, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e,
0x44, 0x61, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x03, 0x77, 0x65, 0x64, 0x12, 0x1b, 0x0a,
0x03, 0x74, 0x68, 0x75, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x44, 0x61, 0x79,
0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x03, 0x74, 0x68, 0x75, 0x12, 0x1b, 0x0a, 0x03, 0x66, 0x72,
0x69, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x44, 0x61, 0x79, 0x52, 0x61, 0x6e,
0x67, 0x65, 0x52, 0x03, 0x66, 0x72, 0x69, 0x12, 0x1b, 0x0a, 0x03, 0x73, 0x61, 0x74, 0x18, 0x06,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x44, 0x61, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52,
0x03, 0x73, 0x61, 0x74, 0x12, 0x1b, 0x0a, 0x03, 0x73, 0x75, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x09, 0x2e, 0x44, 0x61, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x03, 0x73, 0x75,
0x6e, 0x22, 0x68, 0x0a, 0x08, 0x44, 0x61, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x2f, 0x0a,
0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67,
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44,
0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x2b,
0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f,
0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75,
0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x2b, 0x0a,
0x03, 0x65, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f,
0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72,
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x22, 0x3f, 0x0a, 0x11, 0x52, 0x75,
0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x73, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12,
0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08,
0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x69, 0x64, 0x73,
0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x03, 0x69, 0x64, 0x73, 0x22, 0x3e, 0x0a, 0x14, 0x42,
0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x4d, 0x6f, 0x64, 0x65, 0x43, 0x75, 0x73, 0x74, 0x6f,
0x6d, 0x49, 0x50, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x70, 0x76, 0x34, 0x18, 0x01, 0x20, 0x01, 0x28,
0x0c, 0x52, 0x04, 0x69, 0x70, 0x76, 0x34, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x70, 0x76, 0x36, 0x18,
0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x69, 0x70, 0x76, 0x36, 0x22, 0x16, 0x0a, 0x14, 0x42,
0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x4d, 0x6f, 0x64, 0x65, 0x4e, 0x58, 0x44, 0x4f, 0x4d,
0x41, 0x49, 0x4e, 0x22, 0x14, 0x0a, 0x12, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x4d,
0x6f, 0x64, 0x65, 0x4e, 0x75, 0x6c, 0x6c, 0x49, 0x50, 0x22, 0x15, 0x0a, 0x13, 0x42, 0x6c, 0x6f,
0x63, 0x6b, 0x69, 0x6e, 0x67, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x45, 0x46, 0x55, 0x53, 0x45, 0x44,
0x22, 0xe3, 0x01, 0x0a, 0x11, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x42, 0x69, 0x6c, 0x6c, 0x69,
0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x12, 0x48, 0x0a, 0x12, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x61,
0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x10,
0x6c, 0x61, 0x73, 0x74, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x54, 0x69, 0x6d, 0x65,
0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20,
0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x12, 0x25, 0x0a,
0x0e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x18,
0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x75,
0x6e, 0x74, 0x72, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x18, 0x04, 0x20,
0x01, 0x28, 0x0d, 0x52, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x73,
0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x61, 0x73, 0x6e, 0x12, 0x18, 0x0a, 0x07,
0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x71,
0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x22, 0x90, 0x02, 0x0a, 0x0e, 0x41, 0x63, 0x63, 0x65, 0x73,
0x73, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x31, 0x0a, 0x0e, 0x61, 0x6c, 0x6c,
0x6f, 0x77, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x63, 0x69, 0x64, 0x72, 0x18, 0x01, 0x20, 0x03, 0x28,
0x0b, 0x32, 0x0a, 0x2e, 0x43, 0x69, 0x64, 0x72, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0d, 0x61,
0x6c, 0x6c, 0x6f, 0x77, 0x6c, 0x69, 0x73, 0x74, 0x43, 0x69, 0x64, 0x72, 0x12, 0x31, 0x0a, 0x0e,
0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x63, 0x69, 0x64, 0x72, 0x18, 0x02,
0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x43, 0x69, 0x64, 0x72, 0x52, 0x61, 0x6e, 0x67, 0x65,
0x52, 0x0d, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x6c, 0x69, 0x73, 0x74, 0x43, 0x69, 0x64, 0x72, 0x12,
0x23, 0x0a, 0x0d, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x61, 0x73, 0x6e,
0x18, 0x03, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0c, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x6c, 0x69, 0x73,
0x74, 0x41, 0x73, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x6c, 0x69, 0x73,
0x74, 0x5f, 0x61, 0x73, 0x6e, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0c, 0x62, 0x6c, 0x6f,
0x63, 0x6b, 0x6c, 0x69, 0x73, 0x74, 0x41, 0x73, 0x6e, 0x12, 0x34, 0x0a, 0x16, 0x62, 0x6c, 0x6f,
0x63, 0x6b, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x72, 0x75,
0x6c, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x14, 0x62, 0x6c, 0x6f, 0x63, 0x6b,
0x6c, 0x69, 0x73, 0x74, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x12,
0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08,
0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x3d, 0x0a, 0x09, 0x43, 0x69, 0x64,
0x72, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73,
0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73,
0x12, 0x16, 0x0a, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d,
0x52, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x32, 0x8a, 0x01, 0x0a, 0x0a, 0x44, 0x4e, 0x53,
0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x34, 0x0a, 0x0e, 0x67, 0x65, 0x74, 0x44, 0x4e,
0x53, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x13, 0x2e, 0x44, 0x4e, 0x53, 0x50,
0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0b,
0x2e, 0x44, 0x4e, 0x53, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x30, 0x01, 0x12, 0x46, 0x0a,
0x16, 0x73, 0x61, 0x76, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x42, 0x69, 0x6c, 0x6c,
0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x12, 0x12, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65,
0x42, 0x69, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f,
0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d,
0x70, 0x74, 0x79, 0x28, 0x01, 0x42, 0x3d, 0x0a, 0x21, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x64, 0x67,
0x75, 0x61, 0x72, 0x64, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2e, 0x64, 0x6e, 0x73,
0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x42, 0x10, 0x44, 0x4e, 0x53, 0x50,
0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0xa2, 0x02,
0x03, 0x44, 0x4e, 0x53, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x22, 0x3f, 0x0a, 0x11, 0x52,
0x75, 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x73, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73,
0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x69, 0x64,
0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x03, 0x69, 0x64, 0x73, 0x22, 0x3e, 0x0a, 0x14,
0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x4d, 0x6f, 0x64, 0x65, 0x43, 0x75, 0x73, 0x74,
0x6f, 0x6d, 0x49, 0x50, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x70, 0x76, 0x34, 0x18, 0x01, 0x20, 0x01,
0x28, 0x0c, 0x52, 0x04, 0x69, 0x70, 0x76, 0x34, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x70, 0x76, 0x36,
0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x69, 0x70, 0x76, 0x36, 0x22, 0x16, 0x0a, 0x14,
0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x4d, 0x6f, 0x64, 0x65, 0x4e, 0x58, 0x44, 0x4f,
0x4d, 0x41, 0x49, 0x4e, 0x22, 0x14, 0x0a, 0x12, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67,
0x4d, 0x6f, 0x64, 0x65, 0x4e, 0x75, 0x6c, 0x6c, 0x49, 0x50, 0x22, 0x15, 0x0a, 0x13, 0x42, 0x6c,
0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x45, 0x46, 0x55, 0x53, 0x45,
0x44, 0x22, 0xe3, 0x01, 0x0a, 0x11, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x42, 0x69, 0x6c, 0x6c,
0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x12, 0x48, 0x0a, 0x12, 0x6c, 0x61, 0x73, 0x74, 0x5f,
0x61, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52,
0x10, 0x6c, 0x61, 0x73, 0x74, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x54, 0x69, 0x6d,
0x65, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02,
0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x12, 0x25,
0x0a, 0x0e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79,
0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x6f,
0x75, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x18, 0x04,
0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x10, 0x0a, 0x03, 0x61,
0x73, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x61, 0x73, 0x6e, 0x12, 0x18, 0x0a,
0x07, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07,
0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x22, 0x90, 0x02, 0x0a, 0x0e, 0x41, 0x63, 0x63, 0x65,
0x73, 0x73, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x31, 0x0a, 0x0e, 0x61, 0x6c,
0x6c, 0x6f, 0x77, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x63, 0x69, 0x64, 0x72, 0x18, 0x01, 0x20, 0x03,
0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x43, 0x69, 0x64, 0x72, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0d,
0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x6c, 0x69, 0x73, 0x74, 0x43, 0x69, 0x64, 0x72, 0x12, 0x31, 0x0a,
0x0e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x63, 0x69, 0x64, 0x72, 0x18,
0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x43, 0x69, 0x64, 0x72, 0x52, 0x61, 0x6e, 0x67,
0x65, 0x52, 0x0d, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x6c, 0x69, 0x73, 0x74, 0x43, 0x69, 0x64, 0x72,
0x12, 0x23, 0x0a, 0x0d, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x61, 0x73,
0x6e, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0c, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x6c, 0x69,
0x73, 0x74, 0x41, 0x73, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x6c, 0x69,
0x73, 0x74, 0x5f, 0x61, 0x73, 0x6e, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0c, 0x62, 0x6c,
0x6f, 0x63, 0x6b, 0x6c, 0x69, 0x73, 0x74, 0x41, 0x73, 0x6e, 0x12, 0x34, 0x0a, 0x16, 0x62, 0x6c,
0x6f, 0x63, 0x6b, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x72,
0x75, 0x6c, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x14, 0x62, 0x6c, 0x6f, 0x63,
0x6b, 0x6c, 0x69, 0x73, 0x74, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x75, 0x6c, 0x65, 0x73,
0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28,
0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x3d, 0x0a, 0x09, 0x43, 0x69,
0x64, 0x72, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65,
0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73,
0x73, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28,
0x0d, 0x52, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x22, 0x85, 0x01, 0x0a, 0x16, 0x41, 0x75,
0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x74, 0x74,
0x69, 0x6e, 0x67, 0x73, 0x12, 0x22, 0x0a, 0x0d, 0x64, 0x6f, 0x68, 0x5f, 0x61, 0x75, 0x74, 0x68,
0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x64, 0x6f, 0x68,
0x41, 0x75, 0x74, 0x68, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x32, 0x0a, 0x14, 0x70, 0x61, 0x73, 0x73,
0x77, 0x6f, 0x72, 0x64, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x5f, 0x62, 0x63, 0x72, 0x79, 0x70, 0x74,
0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x12, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f,
0x72, 0x64, 0x48, 0x61, 0x73, 0x68, 0x42, 0x63, 0x72, 0x79, 0x70, 0x74, 0x42, 0x13, 0x0a, 0x11,
0x64, 0x6f, 0x68, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x5f, 0x68, 0x61, 0x73,
0x68, 0x32, 0x8a, 0x01, 0x0a, 0x0a, 0x44, 0x4e, 0x53, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
0x12, 0x34, 0x0a, 0x0e, 0x67, 0x65, 0x74, 0x44, 0x4e, 0x53, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c,
0x65, 0x73, 0x12, 0x13, 0x2e, 0x44, 0x4e, 0x53, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0b, 0x2e, 0x44, 0x4e, 0x53, 0x50, 0x72, 0x6f,
0x66, 0x69, 0x6c, 0x65, 0x30, 0x01, 0x12, 0x46, 0x0a, 0x16, 0x73, 0x61, 0x76, 0x65, 0x44, 0x65,
0x76, 0x69, 0x63, 0x65, 0x73, 0x42, 0x69, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74,
0x12, 0x12, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x42, 0x69, 0x6c, 0x6c, 0x69, 0x6e, 0x67,
0x53, 0x74, 0x61, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x28, 0x01, 0x42, 0x3d,
0x0a, 0x21, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x64, 0x67, 0x75, 0x61, 0x72, 0x64, 0x2e, 0x62, 0x61,
0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61,
0x74, 0x65, 0x64, 0x42, 0x10, 0x44, 0x4e, 0x53, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73,
0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0xa2, 0x02, 0x03, 0x44, 0x4e, 0x53, 0x62, 0x06, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
@ -1391,7 +1487,7 @@ func file_backend_proto_rawDescGZIP() []byte {
return file_backend_proto_rawDescData
}
var file_backend_proto_msgTypes = make([]protoimpl.MessageInfo, 16)
var file_backend_proto_msgTypes = make([]protoimpl.MessageInfo, 17)
var file_backend_proto_goTypes = []interface{}{
(*DNSProfilesRequest)(nil), // 0: DNSProfilesRequest
(*DNSProfile)(nil), // 1: DNSProfile
@ -1409,45 +1505,47 @@ var file_backend_proto_goTypes = []interface{}{
(*DeviceBillingStat)(nil), // 13: DeviceBillingStat
(*AccessSettings)(nil), // 14: AccessSettings
(*CidrRange)(nil), // 15: CidrRange
(*timestamppb.Timestamp)(nil), // 16: google.protobuf.Timestamp
(*durationpb.Duration)(nil), // 17: google.protobuf.Duration
(*emptypb.Empty)(nil), // 18: google.protobuf.Empty
(*AuthenticationSettings)(nil), // 16: AuthenticationSettings
(*timestamppb.Timestamp)(nil), // 17: google.protobuf.Timestamp
(*durationpb.Duration)(nil), // 18: google.protobuf.Duration
(*emptypb.Empty)(nil), // 19: google.protobuf.Empty
}
var file_backend_proto_depIdxs = []int32{
16, // 0: DNSProfilesRequest.sync_time:type_name -> google.protobuf.Timestamp
17, // 0: DNSProfilesRequest.sync_time:type_name -> google.protobuf.Timestamp
2, // 1: DNSProfile.safe_browsing:type_name -> SafeBrowsingSettings
4, // 2: DNSProfile.parental:type_name -> ParentalSettings
8, // 3: DNSProfile.rule_lists:type_name -> RuleListsSettings
3, // 4: DNSProfile.devices:type_name -> DeviceSettings
17, // 5: DNSProfile.filtered_response_ttl:type_name -> google.protobuf.Duration
18, // 5: DNSProfile.filtered_response_ttl:type_name -> google.protobuf.Duration
9, // 6: DNSProfile.blocking_mode_custom_ip:type_name -> BlockingModeCustomIP
10, // 7: DNSProfile.blocking_mode_nxdomain:type_name -> BlockingModeNXDOMAIN
11, // 8: DNSProfile.blocking_mode_null_ip:type_name -> BlockingModeNullIP
12, // 9: DNSProfile.blocking_mode_refused:type_name -> BlockingModeREFUSED
14, // 10: DNSProfile.access:type_name -> AccessSettings
5, // 11: ParentalSettings.schedule:type_name -> ScheduleSettings
6, // 12: ScheduleSettings.weeklyRange:type_name -> WeeklyRange
7, // 13: WeeklyRange.mon:type_name -> DayRange
7, // 14: WeeklyRange.tue:type_name -> DayRange
7, // 15: WeeklyRange.wed:type_name -> DayRange
7, // 16: WeeklyRange.thu:type_name -> DayRange
7, // 17: WeeklyRange.fri:type_name -> DayRange
7, // 18: WeeklyRange.sat:type_name -> DayRange
7, // 19: WeeklyRange.sun:type_name -> DayRange
17, // 20: DayRange.start:type_name -> google.protobuf.Duration
17, // 21: DayRange.end:type_name -> google.protobuf.Duration
16, // 22: DeviceBillingStat.last_activity_time:type_name -> google.protobuf.Timestamp
15, // 23: AccessSettings.allowlist_cidr:type_name -> CidrRange
15, // 24: AccessSettings.blocklist_cidr:type_name -> CidrRange
0, // 25: DNSService.getDNSProfiles:input_type -> DNSProfilesRequest
13, // 26: DNSService.saveDevicesBillingStat:input_type -> DeviceBillingStat
1, // 27: DNSService.getDNSProfiles:output_type -> DNSProfile
18, // 28: DNSService.saveDevicesBillingStat:output_type -> google.protobuf.Empty
27, // [27:29] is the sub-list for method output_type
25, // [25:27] is the sub-list for method input_type
25, // [25:25] is the sub-list for extension type_name
25, // [25:25] is the sub-list for extension extendee
0, // [0:25] is the sub-list for field type_name
16, // 11: DeviceSettings.authentication:type_name -> AuthenticationSettings
5, // 12: ParentalSettings.schedule:type_name -> ScheduleSettings
6, // 13: ScheduleSettings.weeklyRange:type_name -> WeeklyRange
7, // 14: WeeklyRange.mon:type_name -> DayRange
7, // 15: WeeklyRange.tue:type_name -> DayRange
7, // 16: WeeklyRange.wed:type_name -> DayRange
7, // 17: WeeklyRange.thu:type_name -> DayRange
7, // 18: WeeklyRange.fri:type_name -> DayRange
7, // 19: WeeklyRange.sat:type_name -> DayRange
7, // 20: WeeklyRange.sun:type_name -> DayRange
18, // 21: DayRange.start:type_name -> google.protobuf.Duration
18, // 22: DayRange.end:type_name -> google.protobuf.Duration
17, // 23: DeviceBillingStat.last_activity_time:type_name -> google.protobuf.Timestamp
15, // 24: AccessSettings.allowlist_cidr:type_name -> CidrRange
15, // 25: AccessSettings.blocklist_cidr:type_name -> CidrRange
0, // 26: DNSService.getDNSProfiles:input_type -> DNSProfilesRequest
13, // 27: DNSService.saveDevicesBillingStat:input_type -> DeviceBillingStat
1, // 28: DNSService.getDNSProfiles:output_type -> DNSProfile
19, // 29: DNSService.saveDevicesBillingStat:output_type -> google.protobuf.Empty
28, // [28:30] is the sub-list for method output_type
26, // [26:28] is the sub-list for method input_type
26, // [26:26] is the sub-list for extension type_name
26, // [26:26] is the sub-list for extension extendee
0, // [0:26] is the sub-list for field type_name
}
func init() { file_backend_proto_init() }
@ -1648,6 +1746,18 @@ func file_backend_proto_init() {
return nil
}
}
file_backend_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*AuthenticationSettings); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
file_backend_proto_msgTypes[1].OneofWrappers = []interface{}{
(*DNSProfile_BlockingModeCustomIp)(nil),
@ -1655,13 +1765,16 @@ func file_backend_proto_init() {
(*DNSProfile_BlockingModeNullIp)(nil),
(*DNSProfile_BlockingModeRefused)(nil),
}
file_backend_proto_msgTypes[16].OneofWrappers = []interface{}{
(*AuthenticationSettings_PasswordHashBcrypt)(nil),
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_backend_proto_rawDesc,
NumEnums: 0,
NumMessages: 16,
NumMessages: 17,
NumExtensions: 0,
NumServices: 1,
},

View File

@ -66,6 +66,7 @@ message DeviceSettings {
bool filtering_enabled = 3;
bytes linked_ip = 4;
repeated bytes dedicated_ips = 5;
AuthenticationSettings authentication = 6;
}
message ParentalSettings {
@ -136,3 +137,10 @@ message CidrRange {
bytes address = 1;
uint32 prefix = 2;
}
message AuthenticationSettings {
bool doh_auth_only = 1;
oneof doh_password_hash {
bytes password_hash_bcrypt = 2;
}
}

View File

@ -1,7 +1,7 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.3.0
// - protoc v4.25.1
// - protoc v4.25.3
// source: backend.proto
package backendpb

View File

@ -11,6 +11,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/access"
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
"github.com/AdguardTeam/AdGuardDNS/internal/agdpasswd"
"github.com/AdguardTeam/AdGuardDNS/internal/agdprotobuf"
"github.com/AdguardTeam/AdGuardDNS/internal/agdtime"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
@ -355,6 +356,37 @@ func (x *ScheduleSettings) toInternal() (sch *agd.ParentalProtectionSchedule, er
return sch, nil
}
// toInternal converts a protobuf custom blocking-mode to an internal one.
// Assumes that at least one IP address is specified in the result blocking-mode
// object.
func (pbm *BlockingModeCustomIP) toInternal() (m dnsmsg.BlockingMode, err error) {
custom := &dnsmsg.BlockingModeCustomIP{}
// TODO(a.garipov): Only one IPv4 address is supported on protobuf side.
var ipv4Addr netip.Addr
err = ipv4Addr.UnmarshalBinary(pbm.Ipv4)
if err != nil {
return nil, fmt.Errorf("bad custom ipv4: %w", err)
} else if ipv4Addr.IsValid() {
custom.IPv4 = []netip.Addr{ipv4Addr}
}
// TODO(a.garipov): Only one IPv6 address is supported on protobuf side.
var ipv6Addr netip.Addr
err = ipv6Addr.UnmarshalBinary(pbm.Ipv6)
if err != nil {
return nil, fmt.Errorf("bad custom ipv6: %w", err)
} else if ipv6Addr.IsValid() {
custom.IPv6 = []netip.Addr{ipv6Addr}
}
if len(custom.IPv4)+len(custom.IPv6) == 0 {
return nil, errors.Error("no valid custom ips found")
}
return custom, nil
}
// blockingModeToInternal converts a protobuf blocking-mode sum-type to an
// internal one. If pbm is nil, blockingModeToInternal returns a null-IP
// blocking mode.
@ -363,18 +395,7 @@ func blockingModeToInternal(pbm isDNSProfile_BlockingMode) (m dnsmsg.BlockingMod
case nil:
return &dnsmsg.BlockingModeNullIP{}, nil
case *DNSProfile_BlockingModeCustomIp:
custom := &dnsmsg.BlockingModeCustomIP{}
err = custom.IPv4.UnmarshalBinary(pbm.BlockingModeCustomIp.Ipv4)
if err != nil {
return nil, fmt.Errorf("bad custom ipv4: %w", err)
}
err = custom.IPv6.UnmarshalBinary(pbm.BlockingModeCustomIp.Ipv6)
if err != nil {
return nil, fmt.Errorf("bad custom ipv6: %w", err)
}
return custom, nil
return pbm.BlockingModeCustomIp.toInternal()
case *DNSProfile_BlockingModeNxdomain:
return &dnsmsg.BlockingModeNXDOMAIN{}, nil
case *DNSProfile_BlockingModeNullIp:
@ -443,6 +464,11 @@ func (ds *DeviceSettings) toInternal(bindSet netutil.SubnetSet) (dev *agd.Device
}
}
auth, err := ds.Authentication.toInternal()
if err != nil {
return nil, fmt.Errorf("auth: %s: %w", ds.Id, err)
}
id, err := agd.NewDeviceID(ds.Id)
if err != nil {
return nil, fmt.Errorf("device id: %s: %w", ds.Id, err)
@ -454,6 +480,7 @@ func (ds *DeviceSettings) toInternal(bindSet netutil.SubnetSet) (dev *agd.Device
}
return &agd.Device{
Auth: auth,
ID: id,
Name: name,
LinkedIP: linkedIP,
@ -462,6 +489,44 @@ func (ds *DeviceSettings) toInternal(bindSet netutil.SubnetSet) (dev *agd.Device
}, nil
}
// toInternal converts a protobuf auth settings structure to an internal one.
// If x is nil, toInternal returns non-nil settings with enabled field set to
// false.
func (x *AuthenticationSettings) toInternal() (s *agd.AuthSettings, err error) {
if x == nil {
return &agd.AuthSettings{
Enabled: false,
PasswordHash: agdpasswd.AllowAuthenticator{},
}, nil
}
ph, err := dohPasswordToInternal(x.DohPasswordHash)
if err != nil {
return nil, fmt.Errorf("password hash: %w", err)
}
return &agd.AuthSettings{
PasswordHash: ph,
Enabled: true,
DoHAuthOnly: x.DohAuthOnly,
}, nil
}
// dohPasswordToInternal converts a protobuf DoH password hash sum-type to an
// internal one.
func dohPasswordToInternal(
pbp isAuthenticationSettings_DohPasswordHash,
) (p agdpasswd.Authenticator, err error) {
switch pbp := pbp.(type) {
case nil:
return agdpasswd.AllowAuthenticator{}, nil
case *AuthenticationSettings_PasswordHashBcrypt:
return agdpasswd.NewPasswordHashBcrypt(pbp.PasswordHashBcrypt), nil
default:
return nil, fmt.Errorf("bad pb auth doh password hash %T(%[1]v)", pbp)
}
}
// rulesToInternal is a helper that converts the filter rules from the backend
// response to AdGuard DNS filtering rules.
func rulesToInternal(

View File

@ -9,6 +9,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/access"
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
"github.com/AdguardTeam/AdGuardDNS/internal/agdpasswd"
"github.com/AdguardTeam/AdGuardDNS/internal/agdtest"
"github.com/AdguardTeam/AdGuardDNS/internal/agdtime"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
@ -92,7 +93,7 @@ func TestDNSProfile_ToInternal(t *testing.T) {
assert.NotEqual(t, newProfile(t), got)
assert.NotEqual(t, newDevices(t), gotDevices)
assert.Len(t, gotDevices, 1)
assert.Len(t, gotDevices, 2)
})
t.Run("empty", func(t *testing.T) {
@ -153,6 +154,16 @@ func TestDNSProfile_ToInternal(t *testing.T) {
testutil.AssertErrorMsg(t, "blocking mode: bad custom ipv6: unexpected slice size", err)
})
t.Run("nil_ips_blocking_mode", func(t *testing.T) {
dp := NewTestDNSProfile(t)
bm := dp.BlockingMode.(*DNSProfile_BlockingModeCustomIp)
bm.BlockingModeCustomIp.Ipv4 = nil
bm.BlockingModeCustomIp.Ipv6 = nil
_, _, err := dp.toInternal(ctx, TestUpdTime, testBind, errColl)
testutil.AssertErrorMsg(t, "blocking mode: no valid custom ips found", err)
})
t.Run("nil_blocking_mode", func(t *testing.T) {
dp := NewTestDNSProfile(t)
dp.BlockingMode = nil
@ -244,17 +255,33 @@ func NewTestDNSProfile(tb testing.TB) (dp *DNSProfile) {
}
devices := []*DeviceSettings{{
Id: "118ffe93",
Name: "118ffe93-name",
Id: "1111aaaa",
Name: "1111aaaa-name",
FilteringEnabled: false,
LinkedIp: ipToBytes(tb, netip.MustParseAddr("1.1.1.1")),
DedicatedIps: [][]byte{ipToBytes(tb, netip.MustParseAddr("1.1.1.2"))},
}, {
Id: "b9e1a762",
Name: "b9e1a762-name",
Id: "2222bbbb",
Name: "2222bbbb-name",
FilteringEnabled: true,
LinkedIp: ipToBytes(tb, netip.MustParseAddr("2.2.2.2")),
DedicatedIps: nil,
Authentication: &AuthenticationSettings{
DohAuthOnly: true,
DohPasswordHash: &AuthenticationSettings_PasswordHashBcrypt{
PasswordHashBcrypt: []byte("test-hash"),
},
},
}, {
Id: "3333cccc",
Name: "3333cccc-name",
FilteringEnabled: false,
LinkedIp: ipToBytes(tb, netip.MustParseAddr("3.3.3.3")),
DedicatedIps: nil,
Authentication: &AuthenticationSettings{
DohAuthOnly: false,
DohPasswordHash: nil,
},
}}
return &DNSProfile{
@ -358,8 +385,8 @@ func newProfile(tb testing.TB) (p *agd.Profile) {
}
wantBlockingMode := &dnsmsg.BlockingModeCustomIP{
IPv4: netip.MustParseAddr("1.2.3.4"),
IPv6: netip.MustParseAddr("1234::cdef"),
IPv4: []netip.Addr{netip.MustParseAddr("1.2.3.4")},
IPv6: []netip.Addr{netip.MustParseAddr("1234::cdef")},
}
wantAccess := access.NewDefaultProfile(&access.ProfileConfig{
@ -376,8 +403,9 @@ func newProfile(tb testing.TB) (p *agd.Profile) {
ID: testProfileID,
UpdateTime: TestUpdTime,
DeviceIDs: []agd.DeviceID{
"118ffe93",
"b9e1a762",
"1111aaaa",
"2222bbbb",
"3333cccc",
},
RuleListIDs: []agd.FilterListID{"1"},
CustomRules: []agd.FilterRuleText{"||example.org^"},
@ -399,17 +427,38 @@ func newDevices(t *testing.T) (d []*agd.Device) {
t.Helper()
return []*agd.Device{{
ID: "118ffe93",
Auth: &agd.AuthSettings{
Enabled: false,
DoHAuthOnly: false,
PasswordHash: agdpasswd.AllowAuthenticator{},
},
ID: "1111aaaa",
LinkedIP: netip.MustParseAddr("1.1.1.1"),
Name: "118ffe93-name",
Name: "1111aaaa-name",
DedicatedIPs: []netip.Addr{netip.MustParseAddr("1.1.1.2")},
FilteringEnabled: false,
}, {
ID: "b9e1a762",
Auth: &agd.AuthSettings{
Enabled: true,
DoHAuthOnly: true,
PasswordHash: agdpasswd.NewPasswordHashBcrypt([]byte("test-hash")),
},
ID: "2222bbbb",
LinkedIP: netip.MustParseAddr("2.2.2.2"),
Name: "b9e1a762-name",
Name: "2222bbbb-name",
DedicatedIPs: nil,
FilteringEnabled: true,
}, {
Auth: &agd.AuthSettings{
Enabled: true,
DoHAuthOnly: false,
PasswordHash: agdpasswd.AllowAuthenticator{},
},
ID: "3333cccc",
LinkedIP: netip.MustParseAddr("3.3.3.3"),
Name: "3333cccc-name",
DedicatedIPs: nil,
FilteringEnabled: false,
}}
}
@ -479,7 +528,7 @@ func BenchmarkDNSProfile_ToInternal(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
for range b.N {
profSink, _, errSink = dp.toInternal(ctx, TestUpdTime, testBind, errColl)
}

View File

@ -81,7 +81,7 @@ func BenchmarkProfileStorage_Profiles(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
for range b.N {
respSink, errSink = s.Profiles(ctx, req)
}

View File

@ -2,12 +2,12 @@ package billstat
import (
"context"
"fmt"
"sync"
"time"
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
"github.com/AdguardTeam/AdGuardDNS/internal/agdservice"
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
"github.com/AdguardTeam/AdGuardDNS/internal/geoip"
"github.com/AdguardTeam/AdGuardDNS/internal/metrics"
"github.com/AdguardTeam/golibs/log"
@ -18,6 +18,9 @@ import (
// RuntimeRecorderConfig is the configuration structure for a runtime billing
// statistics recorder. All fields must be non-empty.
type RuntimeRecorderConfig struct {
// ErrColl is used to collect errors during refreshes.
ErrColl errcoll.Interface
// Uploader is used to upload the billing statistics records to.
Uploader Uploader
}
@ -29,6 +32,7 @@ func NewRuntimeRecorder(c *RuntimeRecorderConfig) (r *RuntimeRecorder) {
mu: &sync.Mutex{},
records: Records{},
uploader: c.Uploader,
errColl: c.ErrColl,
}
}
@ -44,6 +48,9 @@ type RuntimeRecorder struct {
// uploader is the uploader to which the billing statistics records are
// uploaded.
uploader Uploader
// errColl is used to collect errors during refreshes.
errColl errcoll.Interface
}
// type check
@ -58,6 +65,10 @@ func (r *RuntimeRecorder) Record(
start time.Time,
proto agd.Protocol,
) {
// TODO(a.garipov): Use slog.
log.Debug("billstat_refresh: started")
defer log.Debug("billstat_refresh: finished")
r.mu.Lock()
defer r.mu.Unlock()
@ -96,7 +107,7 @@ func (r *RuntimeRecorder) Refresh(ctx context.Context) (err error) {
if err != nil {
r.remergeRecords(records)
log.Info("billstat: refresh failed, records remerged")
log.Info("billstat_refresh: failed, records remerged")
} else {
metrics.BillStatUploadTimestamp.SetToCurrentTime()
}
@ -106,10 +117,10 @@ func (r *RuntimeRecorder) Refresh(ctx context.Context) (err error) {
err = r.uploader.Upload(ctx, records)
if err != nil {
return fmt.Errorf("uploading billstat records: %w", err)
errcoll.Collectf(ctx, r.errColl, "billstat_refresh: %w", err)
}
return nil
return err
}
// resetRecords returns the current data and resets the records map to an empty

View File

@ -30,6 +30,11 @@ const (
func TestRuntimeRecorder_success(t *testing.T) {
var gotRecord *billstat.Record
c := &billstat.RuntimeRecorderConfig{
ErrColl: &agdtest.ErrorCollector{
OnCollect: func(_ context.Context, _ error) {
panic("not implemented")
},
},
Uploader: &agdtest.BillStatUploader{
OnUpload: func(_ context.Context, records billstat.Records) (err error) {
gotRecord = records[devID]
@ -48,7 +53,7 @@ func TestRuntimeRecorder_success(t *testing.T) {
// incremented.
const reqNum = 2
var err error
for i := 0; i < reqNum; i++ {
for range reqNum {
r.Record(ctx, devID, clientCtry, clientASN, start, proto)
}
@ -89,7 +94,13 @@ func TestRuntimeRecorder_fail(t *testing.T) {
return nil
}
var gotCollErr error
c := &billstat.RuntimeRecorderConfig{
ErrColl: &agdtest.ErrorCollector{
OnCollect: func(_ context.Context, err error) {
gotCollErr = err
},
},
Uploader: &agdtest.BillStatUploader{
OnUpload: onUpload,
},
@ -118,6 +129,7 @@ func TestRuntimeRecorder_fail(t *testing.T) {
err := r.Refresh(context.Background())
require.ErrorIs(t, err, testError)
require.ErrorIs(t, gotCollErr, testError)
require.Nil(t, gotRecord)
// Request the backend again, expect the correct, merged data.

View File

@ -46,6 +46,7 @@ func newChanPacketConn(
sessions chan *packetSession,
subnet netip.Prefix,
writeRequests chan *packetConnWriteReq,
writeRequestsGauge prometheus.Gauge,
laddr net.Addr,
) (c *chanPacketConn) {
return &chanPacketConn{
@ -54,7 +55,7 @@ func newChanPacketConn(
writeRequests: writeRequests,
sessionsGauge: metrics.BindToDeviceUDPSessionsChanSize.WithLabelValues(subnet.String()),
writeRequestsGauge: metrics.BindToDeviceUDPWriteRequestsChanSize.WithLabelValues(subnet.String()),
writeRequestsGauge: writeRequestsGauge,
deadlineMu: &sync.RWMutex{},
@ -64,9 +65,9 @@ func newChanPacketConn(
}
// packetConnWriteReq is a request to write a piece of data to the original
// packet connection. resp, body, and either raddr or session must be set.
// packet connection. respCh, body, and either raddr or session must be set.
type packetConnWriteReq struct {
resp chan *packetConnWriteResp
respCh chan *packetConnWriteResp
session *packetSession
raddr net.Addr
deadline time.Time
@ -265,7 +266,7 @@ func (c *chanPacketConn) writeToSession(
resp := make(chan *packetConnWriteResp, 1)
req := &packetConnWriteReq{
resp: resp,
respCh: resp,
session: s,
raddr: raddr,
deadline: deadline,

View File

@ -8,13 +8,14 @@ import (
"time"
"github.com/AdguardTeam/golibs/testutil"
"github.com/prometheus/client_golang/prometheus"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestChanPacketConn_Close(t *testing.T) {
sessions := make(chan *packetSession)
c := newChanPacketConn(sessions, testSubnetIPv4, nil, testLAddr)
c := newChanPacketConn(sessions, testSubnetIPv4, nil, nil, testLAddr)
err := c.Close()
assert.NoError(t, err)
@ -23,14 +24,14 @@ func TestChanPacketConn_Close(t *testing.T) {
}
func TestChanPacketConn_LocalAddr(t *testing.T) {
c := newChanPacketConn(nil, testSubnetIPv4, nil, testLAddr)
c := newChanPacketConn(nil, testSubnetIPv4, nil, nil, testLAddr)
got := c.LocalAddr()
assert.Equal(t, testLAddr, got)
}
func TestChanPacketConn_ReadFromSession(t *testing.T) {
sessions := make(chan *packetSession, 1)
c := newChanPacketConn(sessions, testSubnetIPv4, nil, testLAddr)
c := newChanPacketConn(sessions, testSubnetIPv4, nil, nil, testLAddr)
body := []byte("hello")
bodyLen := len(body)
@ -79,7 +80,9 @@ func TestChanPacketConn_ReadFromSession(t *testing.T) {
func TestChanPacketConn_WriteToSession(t *testing.T) {
sessions := make(chan *packetSession, 1)
writes := make(chan *packetConnWriteReq, 1)
c := newChanPacketConn(sessions, testSubnetIPv4, writes, testLAddr)
gauge := prometheus.NewGauge(prometheus.GaugeOpts{})
c := newChanPacketConn(sessions, testSubnetIPv4, writes, gauge, testLAddr)
body := []byte("hello")
bodyLen := len(body)
@ -125,7 +128,7 @@ func checkWriteReqAndRespond(
req, ok := testutil.RequireReceive(pt, writes, testTimeout)
require.NotNil(pt, req)
require.NotNil(pt, req.resp)
require.NotNil(pt, req.respCh)
require.True(pt, ok)
if wantRaddr != nil {
@ -141,14 +144,14 @@ func checkWriteReqAndRespond(
assert.Equal(pt, wantDeadline, req.deadline)
assert.Equal(pt, wantBody, req.body)
testutil.RequireSend(pt, req.resp, &packetConnWriteResp{
testutil.RequireSend(pt, req.respCh, &packetConnWriteResp{
err: nil,
written: len(wantBody),
}, testTimeout)
}
func TestChanPacketConn_deadlines(t *testing.T) {
c := newChanPacketConn(nil, testSubnetIPv4, nil, testLAddr)
c := newChanPacketConn(nil, testSubnetIPv4, nil, nil, testLAddr)
deadline := time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC)
testCases := []struct {

View File

@ -15,6 +15,7 @@ import (
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/syncutil"
"github.com/prometheus/client_golang/prometheus"
)
// interfaceListener contains information about a single interface listener.
@ -26,6 +27,8 @@ type interfaceListener struct {
writeRequests chan *packetConnWriteReq
done chan unit
errColl errcoll.Interface
writeRequestsGauge prometheus.Gauge
writeDurationHist prometheus.Observer
ifaceName string
port uint16
}
@ -113,7 +116,7 @@ func (l *interfaceListener) listenUDP(errCh chan<- error) {
errCh <- nil
go l.writeUDP(udpConn)
go l.writeUDPResponses(udpConn)
logPrefix := fmt.Sprintf("bindtodevice: listener %s:%d: udp", l.ifaceName, l.port)
@ -185,46 +188,60 @@ func (l *interfaceListener) readUDP(c *net.UDPConn, logPrefix string) (err error
return nil
}
// writeUDP runs the UDP write loop. It is intended to be used as a goroutine.
func (l *interfaceListener) writeUDP(c *net.UDPConn) {
// writeUDPResponses runs the UDP write loop. It is intended to be used as a
// goroutine.
func (l *interfaceListener) writeUDPResponses(c *net.UDPConn) {
defer log.OnPanic("interfaceListener.writeUDP")
logPrefix := fmt.Sprintf("bindtodevice: listener %s:%d: udp write", l.ifaceName, l.port)
for {
var req *packetConnWriteReq
select {
case <-l.done:
optlog.Debug1("%s: done", logPrefix)
optlog.Debug2("bindtodevice: listener %s:%d: udp write: done", l.ifaceName, l.port)
return
case req = <-l.writeRequests:
// Go on.
case req := <-l.writeRequests:
l.writeUDP(c, req)
}
}
}
// writeUDP handles a single write operation and writes a response to
// req.respCh.
func (l *interfaceListener) writeUDP(c *net.UDPConn, req *packetConnWriteReq) {
resp := &packetConnWriteResp{}
resp.err = c.SetWriteDeadline(req.deadline)
if resp.err != nil {
req.resp <- resp
req.respCh <- resp
continue
return
}
if s := req.session; s == nil {
l.writeToUDPConn(c, req, resp)
resetDeadlineErr := c.SetWriteDeadline(time.Time{})
resp.err = errors.WithDeferred(resp.err, resetDeadlineErr)
req.respCh <- resp
}
// writeToUDPConn writes to c, depending on what kind of session req contains,
// and sets resp.written and resp.err accordingly.
func (l *interfaceListener) writeToUDPConn(
c *net.UDPConn,
req *packetConnWriteReq,
resp *packetConnWriteResp,
) {
start := time.Now()
defer func() { l.writeDurationHist.Observe(time.Since(start).Seconds()) }()
s := req.session
if s == nil {
resp.written, resp.err = c.WriteTo(req.body, req.raddr)
} else {
resp.written, _, resp.err = c.WriteMsgUDP(
req.body,
s.respOOB,
req.session.raddr,
)
return
}
resp.written, _, resp.err = c.WriteMsgUDP(req.body, s.respOOB, req.session.raddr)
l.bodyPool.Put(&s.readBody)
}
resetDeadlineErr := c.SetWriteDeadline(time.Time{})
resp.err = errors.WithDeferred(resp.err, resetDeadlineErr)
req.resp <- resp
}
}

View File

@ -12,7 +12,7 @@ import (
)
func TestListenConfig(t *testing.T) {
pc := newChanPacketConn(nil, testSubnetIPv4, nil, testLAddr)
pc := newChanPacketConn(nil, testSubnetIPv4, nil, nil, testLAddr)
lsnr := newChanListener(nil, testSubnetIPv4, testLAddr)
addr := &agdnet.PrefixNetAddr{
Prefix: testSubnetIPv4,

View File

@ -12,6 +12,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/agdnet"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/netext"
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
"github.com/AdguardTeam/AdGuardDNS/internal/metrics"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/mapsutil"
@ -84,7 +85,7 @@ func (m *Manager) Add(id ID, ifaceName string, port uint16, ctrlConf *ControlCon
return nil
}
err = mapsutil.OrderedRangeError(m.ifaceListeners, validateDup)
err = mapsutil.SortedRangeError(m.ifaceListeners, validateDup)
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return err
@ -116,6 +117,8 @@ func (m *Manager) newInterfaceListener(
writeRequests: make(chan *packetConnWriteReq, m.chanBufSize),
done: m.done,
errColl: m.errColl,
writeRequestsGauge: metrics.BindToDeviceUDPWriteRequestsChanSize.WithLabelValues(ifaceName),
writeDurationHist: metrics.BindToDeviceUDPWriteDurationSeconds.WithLabelValues(ifaceName),
ifaceName: ifaceName,
port: port,
}
@ -159,11 +162,17 @@ func (m *Manager) ListenConfig(id ID, subnet netip.Prefix) (c *ListenConfig, err
}
sessCh := make(chan *packetSession, m.chanBufSize)
pConn := newChanPacketConn(sessCh, subnet, l.writeRequests, &agdnet.PrefixNetAddr{
pConn := newChanPacketConn(
sessCh,
subnet,
l.writeRequests,
l.writeRequestsGauge,
&agdnet.PrefixNetAddr{
Prefix: subnet,
Net: "udp",
Port: l.port,
})
},
)
err = l.conns.addPacketConn(pConn)
if err != nil {

View File

@ -233,10 +233,10 @@ func TestManager(t *testing.T) {
require.NoError(t, err)
require.NotNil(t, lc)
err = m.Start(agdtest.ContextWithTimeout(t, testTimeout))
err = m.Start(testutil.ContextWithTimeout(t, testTimeout))
require.NoError(t, err)
testutil.CleanupAndRequireSuccess(t, func() (err error) {
return m.Shutdown(agdtest.ContextWithTimeout(t, testTimeout))
return m.Shutdown(testutil.ContextWithTimeout(t, testTimeout))
})
t.Run("tcp", func(t *testing.T) {

View File

@ -113,9 +113,7 @@ func SubtestListenControlTCP(
ifaceName string,
ifaceNet *net.IPNet,
) {
ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
t.Cleanup(cancel)
ctx := testutil.ContextWithTimeout(t, testTimeout)
lsnr, err := lc.Listen(ctx, "tcp", "0.0.0.0:0")
require.NoError(t, err)
testutil.CleanupAndRequireSuccess(t, lsnr.Close)
@ -194,9 +192,7 @@ func SubtestListenControlUDP(
ifaceName string,
ifaceNet *net.IPNet,
) {
ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
t.Cleanup(cancel)
ctx := testutil.ContextWithTimeout(t, testTimeout)
packetConn, err := lc.ListenPacket(ctx, "udp", "0.0.0.0:0")
require.NoError(t, err)
testutil.CleanupAndRequireSuccess(t, packetConn.Close)
@ -528,7 +524,7 @@ func BenchmarkReadPacketSession(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
for range b.N {
sessSink, errSink = readPacketSession(c, body, oob)
}

View File

@ -14,7 +14,7 @@ type additionalInfo map[string]string
// validateAdditionalInfo return an error is the section is invalid.
func (c additionalInfo) validate() (err error) {
return mapsutil.OrderedRangeError(c, func(k, _ string) (keyErr error) {
return mapsutil.SortedRangeError(c, func(k, _ string) (keyErr error) {
if model.LabelName(k).IsValid() {
return nil
}

View File

@ -110,6 +110,7 @@ func setupBillStat(
}
rec = billstat.NewRuntimeRecorder(&billstat.RuntimeRecorderConfig{
ErrColl: errColl,
Uploader: billStatUploader,
})
@ -121,11 +122,9 @@ func setupBillStat(
return context.WithTimeout(context.Background(), timeout)
},
Refresher: rec,
ErrColl: errColl,
Name: "billstat",
Interval: refrIvl,
RefreshOnShutdown: true,
RoutineLogsAreDebug: true,
RandomizeStart: false,
})
err = billStatRefr.Start(context.Background())
@ -157,6 +156,7 @@ func setupProfDB(
timeout := conf.Timeout.Duration
profDB, err = profiledb.New(&profiledb.Config{
Storage: profStrg,
ErrColl: errColl,
FullSyncIvl: conf.FullRefreshIvl.Duration,
FullSyncRetryIvl: conf.FullRefreshRetryIvl.Duration,
InitialTimeout: timeout,
@ -173,11 +173,9 @@ func setupProfDB(
return context.WithTimeout(context.Background(), timeout)
},
Refresher: profDB,
ErrColl: errColl,
Name: "profiledb",
Interval: refrIvl,
RefreshOnShutdown: false,
RoutineLogsAreDebug: true,
RandomizeStart: true,
})
err = profDBRefr.Start(context.Background())

View File

@ -10,7 +10,6 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/access"
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
"github.com/AdguardTeam/AdGuardDNS/internal/agdnet"
"github.com/AdguardTeam/AdGuardDNS/internal/agdservice"
"github.com/AdguardTeam/AdGuardDNS/internal/debugsvc"
"github.com/AdguardTeam/AdGuardDNS/internal/dnscheck"
@ -27,7 +26,6 @@ import (
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/service"
"github.com/AdguardTeam/golibs/timeutil"
)
// Main is the entry point of application.
@ -92,9 +90,6 @@ func Main() {
// Safe-browsing and adult-blocking filters
// TODO(ameshkov): Consider making configurable.
filteringResolver := agdnet.NewCachingResolver(agdnet.DefaultResolver{}, 1*timeutil.Day)
err = os.MkdirAll(envs.FilterCachePath, agd.DefaultDirPerm)
check(err)
@ -105,7 +100,6 @@ func Main() {
cloner := dnsmsg.NewCloner(metrics.ClonerStat{})
safeBrowsingHashes, safeBrowsingFilter, err := setupHashPrefixFilter(
c.SafeBrowsing,
filteringResolver,
cloner,
agd.FilterListIDSafeBrowsing,
envs.SafeBrowsingURL,
@ -118,7 +112,6 @@ func Main() {
adultBlockingHashes, adultBlockingFilter, err := setupHashPrefixFilter(
c.AdultBlocking,
filteringResolver,
cloner,
agd.FilterListIDAdultBlocking,
envs.AdultBlockingURL,
@ -132,7 +125,6 @@ func Main() {
_, newRegDomainsFilter, err := setupHashPrefixFilter(
// Reuse general safe browsing filter configuration.
c.SafeBrowsing,
filteringResolver,
cloner,
agd.FilterListIDNewRegDomains,
envs.NewRegDomainsURL,
@ -147,8 +139,6 @@ func Main() {
fltStrgConf := c.Filters.toInternal(
errColl,
filteringResolver,
cloner,
envs,
safeBrowsingFilter,
adultBlockingFilter,
@ -156,7 +146,7 @@ func Main() {
)
fltRefrTimeout := c.Filters.RefreshTimeout.Duration
fltStrg, err := setupFilterStorage(fltStrgConf, sigHdlr, errColl, fltRefrTimeout)
fltStrg, err := setupFilterStorage(fltStrgConf, sigHdlr, fltRefrTimeout)
check(err)
fltGroups, err := c.FilteringGroups.toInternal(fltStrg)
@ -293,8 +283,6 @@ func Main() {
UseCacheTTLOverride: c.Cache.TTLOverride.Enabled,
UseECSCache: c.Cache.Type == cacheTypeECS,
ProfileDBEnabled: bool(envs.ProfilesEnabled),
ResearchMetrics: bool(envs.ResearchMetrics),
ResearchLogs: bool(envs.ResearchLogs),
}
dnsSvc, err := dnssvc.New(dnsConf)

View File

@ -101,6 +101,7 @@ func (c *configuration) buildQueryLog(envs *environments) (l querylog.Interface)
return querylog.NewFileSystem(&querylog.FileSystemConfig{
Path: envs.QueryLogPath,
RandSeed: uint64(time.Now().UnixNano()),
})
}

View File

@ -9,9 +9,9 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
"github.com/AdguardTeam/golibs/container"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/stringutil"
"github.com/miekg/dns"
)
@ -50,8 +50,8 @@ func (c *ddrConfig) toInternal(msgs *dnsmsg.Constructor) (conf *agd.DDR) {
func ddrRecsToSVCBTmpls(
msgs *dnsmsg.Constructor,
records map[string]*ddrRecord,
) (targets *stringutil.Set, tmpls []*dns.SVCB) {
targets = stringutil.NewSet()
) (targets *container.MapSet[string], tmpls []*dns.SVCB) {
targets = container.NewMapSet[string]()
for target, r := range records {
target = strings.TrimPrefix(target, "*.")
targets.Add(target)

View File

@ -60,8 +60,6 @@ type environments struct {
LogTimestamp strictBool `env:"LOG_TIMESTAMP" envDefault:"1"`
LogVerbose strictBool `env:"VERBOSE" envDefault:"0"`
ProfilesEnabled strictBool `env:"PROFILES_ENABLED" envDefault:"1"`
ResearchMetrics strictBool `env:"RESEARCH_METRICS" envDefault:"0"`
ResearchLogs strictBool `env:"RESEARCH_LOGS" envDefault:"0"`
}
// readEnvs reads the configuration.
@ -117,9 +115,7 @@ func (envs *environments) buildErrColl() (errColl errcoll.Interface, err error)
}
// geoIP returns an GeoIP database implementation from environment.
func (envs *environments) geoIP(
c *geoIPConfig,
) (g *geoip.File, err error) {
func (envs *environments) geoIP(c *geoIPConfig) (g *geoip.File, err error) {
log.Debug("using geoip files %q and %q", envs.GeoIPASNPath, envs.GeoIPCountryPath)
g, err = geoip.NewFile(&geoip.FileConfig{
@ -180,18 +176,17 @@ func (envs *environments) buildRuleStat(
}
httpRuleStat := rulestat.NewHTTP(&rulestat.HTTPConfig{
ErrColl: errColl,
URL: &envs.RuleStatURL.URL,
})
refr := agdservice.NewRefreshWorker(&agdservice.RefreshWorkerConfig{
Context: ctxWithDefaultTimeout,
Refresher: httpRuleStat,
ErrColl: errColl,
Name: "rulestat",
// TODO(ameshkov): Consider making configurable.
Interval: 10 * time.Minute,
RefreshOnShutdown: true,
RoutineLogsAreDebug: false,
RandomizeStart: false,
})
err = refr.Start(context.Background())

View File

@ -16,24 +16,6 @@ func check(err error) {
}
}
// coalesceError returns the first non-nil error. It is named after function
// COALESCE in SQL. If all errors are nil, it returns nil.
//
// TODO(a.garipov): Consider a similar helper to group errors together to show
// as many errors as possible.
//
// TODO(a.garipov): Think of ways to merge with [aghalg.Coalesce] in AdGuard
// Home.
func coalesceError(errors ...error) (res error) {
for _, err := range errors {
if err != nil {
return err
}
}
return nil
}
// numberOrDuration is the constraint for integer types along with
// timeutil.Duration.
type numberOrDuration interface {

View File

@ -5,9 +5,7 @@ import (
"fmt"
"time"
"github.com/AdguardTeam/AdGuardDNS/internal/agdnet"
"github.com/AdguardTeam/AdGuardDNS/internal/agdservice"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
"github.com/AdguardTeam/AdGuardDNS/internal/filter"
"github.com/AdguardTeam/AdGuardDNS/internal/filter/hashprefix"
@ -17,10 +15,11 @@ import (
"github.com/c2h5oh/datasize"
)
// Filters configuration
// filtersConfig contains the configuration for the filter lists and filtering
// storage to be used.
//
// TODO(a.garipov): Add the timeout for the blocked-service index refresh. It
// is currently hardcoded to 3 minutes.
type filtersConfig struct {
// RuleListCache is the cache settings for the filtering rule-list.
RuleListCache *fltRuleListCache `yaml:"rule_list_cache"`
@ -41,10 +40,21 @@ type filtersConfig struct {
RefreshIvl timeutil.Duration `yaml:"refresh_interval"`
// RefreshTimeout is the timeout for the entire filter update operation.
// Note that each individual refresh operation also has its own hardcoded
// 30s timeout.
// Note that filter rule-list index and each filter rule-list update
// operations have their own timeouts, see IndexRefreshTimeout and
// RuleListRefreshTimeout.
RefreshTimeout timeutil.Duration `yaml:"refresh_timeout"`
// IndexRefreshTimeout is the timeout for the filter rule-list index update
// operation. See also RefreshTimeout for the entire filter update
// operation.
IndexRefreshTimeout timeutil.Duration `yaml:"index_refresh_timeout"`
// RuleListRefreshTimeout is the timeout for the filter update operation of
// each rule-list, which includes safe-search filters. See also
// RefreshTimeout for the entire filter update operation.
RuleListRefreshTimeout timeutil.Duration `yaml:"rule_list_refresh_timeout"`
// MaxSize is the maximum size of the downloadable filtering rule-list.
MaxSize datasize.ByteSize `yaml:"max_size"`
}
@ -53,8 +63,6 @@ type filtersConfig struct {
// cacheDir must exist. c is assumed to be valid.
func (c *filtersConfig) toInternal(
errColl errcoll.Interface,
resolver agdnet.Resolver,
cloner *dnsmsg.Cloner,
envs *environments,
safeBrowsing *hashprefix.Filter,
adultBlocking *hashprefix.Filter,
@ -70,8 +78,6 @@ func (c *filtersConfig) toInternal(
NewRegDomains: newRegDomains,
Now: time.Now,
ErrColl: errColl,
Resolver: resolver,
Cloner: cloner,
CacheDir: envs.FilterCachePath,
CustomFilterCacheSize: c.CustomFilterCacheSize,
SafeSearchCacheSize: c.SafeSearchCacheSize,
@ -79,6 +85,8 @@ func (c *filtersConfig) toInternal(
SafeSearchCacheTTL: 1 * time.Hour,
RuleListCacheSize: c.RuleListCache.Size,
RefreshIvl: c.RefreshIvl.Duration,
IndexRefreshTimeout: c.IndexRefreshTimeout.Duration,
RuleListRefreshTimeout: c.RuleListRefreshTimeout.Duration,
UseRuleListCache: c.RuleListCache.Enabled,
MaxRuleListSize: c.MaxSize.Bytes(),
}
@ -97,6 +105,10 @@ func (c *filtersConfig) validate() (err error) {
return newMustBePositiveError("refresh_interval", c.RefreshIvl)
case c.RefreshTimeout.Duration <= 0:
return newMustBePositiveError("refresh_timeout", c.RefreshTimeout)
case c.IndexRefreshTimeout.Duration <= 0:
return newMustBePositiveError("index_refresh_timeout", c.IndexRefreshTimeout)
case c.RuleListRefreshTimeout.Duration <= 0:
return newMustBePositiveError("rule_list_refresh_timeout", c.RuleListRefreshTimeout)
case c.MaxSize <= 0:
return newMustBePositiveError("max_size", c.MaxSize)
default:
@ -138,7 +150,6 @@ func (c *fltRuleListCache) validate() (err error) {
func setupFilterStorage(
conf *filter.DefaultStorageConfig,
sigHdlr *service.SignalHandler,
errColl errcoll.Interface,
refreshTimeout time.Duration,
) (strg *filter.DefaultStorage, err error) {
strg, err = filter.NewDefaultStorage(conf)
@ -151,11 +162,9 @@ func setupFilterStorage(
return context.WithTimeout(context.Background(), refreshTimeout)
},
Refresher: strg,
ErrColl: errColl,
Name: "filters",
Interval: conf.RefreshIvl,
RefreshOnShutdown: false,
RoutineLogsAreDebug: false,
RandomizeStart: false,
})
err = refr.Start(context.Background())

View File

@ -5,8 +5,8 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
"github.com/AdguardTeam/AdGuardDNS/internal/filter"
"github.com/AdguardTeam/golibs/container"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/stringutil"
)
// Filtering Groups Configuration
@ -94,7 +94,7 @@ func (g *filteringGroup) validate() (err error) {
return errors.Error("no parental")
}
fltIDs := stringutil.NewSet()
fltIDs := container.NewMapSet[string]()
for i, fltID := range g.RuleLists.IDs {
if fltIDs.Has(fltID) {
return fmt.Errorf("rule_lists: at index %d: duplicate id %q", i, fltID)
@ -160,7 +160,7 @@ func (groups filteringGroups) validate() (err error) {
return errors.Error("no filtering_groups")
}
ids := stringutil.NewSet()
ids := container.NewMapSet[string]()
for i, g := range groups {
err = g.validate()
if err != nil {

View File

@ -7,6 +7,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/agdservice"
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
"github.com/AdguardTeam/AdGuardDNS/internal/geoip"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/timeutil"
)
@ -64,12 +65,17 @@ func setupGeoIP(
refr := agdservice.NewRefreshWorker(&agdservice.RefreshWorkerConfig{
Context: ctxWithDefaultTimeout,
Refresher: geoIP,
ErrColl: errColl,
// Do not add errColl to geoip's config, as that would create an import
// cycle.
Refresher: agdservice.NewRefresherWithErrColl(
geoIP,
log.Info,
errColl,
"geoip_refresh",
),
Name: "geoip",
Interval: conf.RefreshIvl.Duration,
RefreshOnShutdown: false,
RoutineLogsAreDebug: false,
RandomizeStart: false,
})
err = refr.Start(context.Background())

View File

@ -35,7 +35,7 @@ func (c *interfaceListenersConfig) toInternal(
ChannelBufferSize: c.ChannelBufferSize,
})
err = mapsutil.OrderedRangeError(
err = mapsutil.SortedRangeError(
c.List,
func(id bindtodevice.ID, l *interfaceListener) (addErr error) {
return errors.Annotate(m.Add(id, l.Interface, l.Port, ctrlConf), "adding listener %q: %w", id)
@ -66,7 +66,7 @@ func (c *interfaceListenersConfig) validate() (err error) {
// Go on.
}
err = mapsutil.OrderedRangeError(
err = mapsutil.SortedRangeError(
c.List,
func(id bindtodevice.ID, l *interfaceListener) (lsnrErr error) {
return errors.Annotate(l.validate(), "interface %q: %w", id)

View File

@ -1,6 +1,7 @@
package cmd
import (
"cmp"
"context"
"fmt"
"net/url"
@ -88,7 +89,7 @@ func (o *rateLimitOptions) validate() (err error) {
return errNilConfig
}
return coalesceError(
return cmp.Or(
validatePositive("rps", o.RPS),
validatePositive("subnet_key_len", o.SubnetKeyLen),
)
@ -120,7 +121,7 @@ func (c *rateLimitConfig) validate() (err error) {
return fmt.Errorf("allowlist: %w", errNilConfig)
}
return coalesceError(
return cmp.Or(
validateProp("connection_limit", c.ConnectionLimit.validate),
validateProp("ipv4", c.IPv4.validate),
validateProp("ipv6", c.IPv6.validate),
@ -144,7 +145,7 @@ func setupRateLimiter(
) (rateLimiter *ratelimit.Backoff, connLimiter *connlimiter.Limiter, err error) {
allowSubnets := netutil.UnembedPrefixes(conf.Allowlist.List)
allowlist := ratelimit.NewDynamicAllowlist(allowSubnets, nil)
refresher, err := consul.NewAllowlistRefresher(allowlist, consulAllowlist)
refresher, err := consul.NewAllowlistRefresher(allowlist, consulAllowlist, errColl)
if err != nil {
return nil, nil, fmt.Errorf("creating allowlist refresher: %w", err)
}
@ -152,11 +153,9 @@ func setupRateLimiter(
refr := agdservice.NewRefreshWorker(&agdservice.RefreshWorkerConfig{
Context: ctxWithDefaultTimeout,
Refresher: refresher,
ErrColl: errColl,
Name: "allowlist",
Interval: conf.Allowlist.RefreshIvl.Duration,
RefreshOnShutdown: false,
RoutineLogsAreDebug: false,
RandomizeStart: false,
})

View File

@ -6,7 +6,6 @@ import (
"path/filepath"
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
"github.com/AdguardTeam/AdGuardDNS/internal/agdnet"
"github.com/AdguardTeam/AdGuardDNS/internal/agdservice"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
@ -37,13 +36,15 @@ type safeBrowsingConfig struct {
// RefreshIvl defines how often AdGuard DNS refreshes the filter.
RefreshIvl timeutil.Duration `yaml:"refresh_interval"`
// RefreshTimeout is the timeout for the filter update operation.
RefreshTimeout timeutil.Duration `yaml:"refresh_timeout"`
}
// toInternal converts c to the safe browsing filter configuration for the
// filter storage of the DNS server. c is assumed to be valid.
func (c *safeBrowsingConfig) toInternal(
errColl errcoll.Interface,
resolver agdnet.Resolver,
cloner *dnsmsg.Cloner,
id agd.FilterListID,
url *urlutil.URL,
@ -60,11 +61,11 @@ func (c *safeBrowsingConfig) toInternal(
Hashes: hashes,
URL: netutil.CloneURL(&url.URL),
ErrColl: errColl,
Resolver: resolver,
ID: id,
CachePath: filepath.Join(cacheDir, string(id)),
ReplacementHost: c.BlockHost,
Staleness: c.RefreshIvl.Duration,
RefreshTimeout: c.RefreshTimeout.Duration,
CacheTTL: c.CacheTTL.Duration,
CacheSize: c.CacheSize,
MaxSize: maxSize,
@ -85,6 +86,8 @@ func (c *safeBrowsingConfig) validate() (err error) {
return newMustBePositiveError("cache_ttl", c.CacheTTL)
case c.RefreshIvl.Duration <= 0:
return newMustBePositiveError("refresh_interval", c.RefreshIvl)
case c.RefreshTimeout.Duration <= 0:
return newMustBePositiveError("refresh_timeout", c.RefreshTimeout)
default:
return nil
}
@ -94,7 +97,6 @@ func (c *safeBrowsingConfig) validate() (err error) {
// starts and registers its refresher in the signal handler.
func setupHashPrefixFilter(
conf *safeBrowsingConfig,
resolver *agdnet.CachingResolver,
cloner *dnsmsg.Cloner,
id agd.FilterListID,
url *urlutil.URL,
@ -103,7 +105,7 @@ func setupHashPrefixFilter(
sigHdlr *service.SignalHandler,
errColl errcoll.Interface,
) (strg *hashprefix.Storage, flt *hashprefix.Filter, err error) {
fltConf, err := conf.toInternal(errColl, resolver, cloner, id, url, cachePath, maxSize)
fltConf, err := conf.toInternal(errColl, cloner, id, url, cachePath, maxSize)
if err != nil {
return nil, nil, fmt.Errorf("configuring hash prefix filter %s: %w", id, err)
}
@ -114,13 +116,15 @@ func setupHashPrefixFilter(
}
refr := agdservice.NewRefreshWorker(&agdservice.RefreshWorkerConfig{
Context: ctxWithDefaultTimeout,
// Note that we also set the same timeout for the http.Client in
// [hashprefix.NewFilter].
Context: func() (ctx context.Context, cancel context.CancelFunc) {
return context.WithTimeout(context.Background(), fltConf.RefreshTimeout)
},
Refresher: flt,
ErrColl: errColl,
Name: string(id),
Interval: fltConf.Staleness,
RefreshOnShutdown: false,
RoutineLogsAreDebug: false,
RandomizeStart: false,
})
err = refr.Start(context.Background())

View File

@ -7,8 +7,8 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
"github.com/AdguardTeam/AdGuardDNS/internal/bindtodevice"
"github.com/AdguardTeam/AdGuardDNS/internal/metrics"
"github.com/AdguardTeam/golibs/container"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/stringutil"
)
// toInternal returns the configuration of DNS servers for a single server
@ -95,7 +95,7 @@ func (srvs servers) validate() (needsTLS bool, err error) {
return false, errors.Error("no servers")
}
names := stringutil.NewSet()
names := container.NewMapSet[string]()
for i, s := range srvs {
if s == nil {
return false, fmt.Errorf("at index %d: no server", i)
@ -323,19 +323,17 @@ func (c *serverBindInterface) validate() (err error) {
// Go on.
}
set := map[netip.Prefix]struct{}{}
set := container.NewMapSet[netip.Prefix]()
for i, subnet := range c.Subnets {
if !subnet.IsValid() {
return fmt.Errorf("bad subnet at index %d", i)
}
_, ok := set[subnet]
if ok {
if set.Has(subnet) {
return fmt.Errorf("duplicate subnet %s at index %d", subnet, i)
}
set[subnet] = struct{}{}
set.Add(subnet)
}
return nil

View File

@ -2,14 +2,12 @@ package cmd
import (
"fmt"
"net/netip"
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
"github.com/AdguardTeam/AdGuardDNS/internal/bindtodevice"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
"github.com/AdguardTeam/golibs/container"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/stringutil"
)
// serverGroups are the DNS server groups. A valid instance of serverGroups has
@ -41,7 +39,6 @@ func (srvGrps serverGroups) toInternal(
}
svcSrvGrps[i] = &agd.ServerGroup{
BlockPageRedirect: g.BlockPageRedirect.toInternal(),
DDR: g.DDR.toInternal(messages),
TLS: tlsConf,
Name: agd.ServerGroupName(g.Name),
@ -63,7 +60,7 @@ func (srvGrps serverGroups) validate() (err error) {
return errors.Error("no server groups")
}
names := stringutil.NewSet()
names := container.NewMapSet[string]()
for i, g := range srvGrps {
err = g.validate()
if err != nil {
@ -86,9 +83,6 @@ func (srvGrps serverGroups) validate() (err error) {
// TODO(a.garipov): Think about more consistent naming, since this object is a
// configuration, but it also stores other configurations.
type serverGroup struct {
// BlockPageRedirect is the configuration for the server group's block page.
BlockPageRedirect *serverGroupBlockPageConfig `yaml:"block_page_redirect"`
// DDR is the Discovery Of Designated Resolvers (DDR) configuration for this
// server group.
DDR *ddrConfig `yaml:"ddr"`
@ -117,11 +111,6 @@ func (g *serverGroup) validate() (err error) {
return errors.Error("no filtering_group")
}
err = g.BlockPageRedirect.validate()
if err != nil {
return fmt.Errorf("block_page_redirect: %w", err)
}
err = g.DDR.validate()
if err != nil {
return fmt.Errorf("ddr: %w", err)
@ -139,255 +128,3 @@ func (g *serverGroup) validate() (err error) {
return nil
}
// serverGroupBlockPageConfig is the configuration for a [serverGroup]'s block
// page. See [agd.BlockPageRedirect] and the related types for more
// documentation and contracts.
type serverGroupBlockPageConfig struct {
// Apply defines request parameters based on which the block page is always
// shown.
Apply *serverGroupBlockPageApplyConfig `yaml:"apply"`
// Skip defines request parameters based on which the block page is never
// shown, regardless of the probability.
Skip *serverGroupBlockPageSkipConfig `yaml:"skip"`
// IPv4 are the IPv4 records of the block page, used to respond to A
// queries.
IPv4 []*serverGroupBlockPageRecord `yaml:"ipv4"`
// IPv6 are the IPv6 records of the block page, used to respond to AAAA
// queries.
IPv6 []*serverGroupBlockPageRecord `yaml:"ipv6"`
// Probability defines the probability of responding with the block page IPs
// based on remote address.
Probability float64 `yaml:"probability"`
// Enabled defines whether the block-page feature is enabled.
Enabled bool `yaml:"enabled"`
}
// toInternal returns the block-page redirect configuration for a server group.
// c is assumed to be valid.
func (c *serverGroupBlockPageConfig) toInternal() (conf *agd.BlockPageRedirect) {
if !c.Enabled {
return &agd.BlockPageRedirect{}
}
var ipv4 []netip.Addr
for _, r := range c.IPv4 {
ipv4 = append(ipv4, r.Address)
}
var ipv6 []netip.Addr
for _, r := range c.IPv6 {
ipv6 = append(ipv6, r.Address)
}
return &agd.BlockPageRedirect{
Apply: c.Apply.toInternal(),
Skip: c.Skip.toInternal(),
IPv4: ipv4,
IPv6: ipv6,
Probability: agd.MustNewProbability(c.Probability),
Enabled: c.Enabled,
}
}
// validate returns an error if the block-page redirect configuration is
// invalid.
func (c *serverGroupBlockPageConfig) validate() (err error) {
switch {
case c == nil:
return errNilConfig
case !c.Enabled:
return nil
case len(c.IPv4) == 0 && len(c.IPv6) == 0:
return errors.Error("ipv4, ipv6, or both must be set")
}
_, err = agd.NewProbability(c.Probability)
if err != nil {
return fmt.Errorf("probability: %w", err)
}
err = c.validateAddrs()
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return err
}
err = c.Apply.validate()
if err != nil {
return fmt.Errorf("apply: %w", err)
}
err = c.Skip.validate()
if err != nil {
return fmt.Errorf("skip: %w", err)
}
return nil
}
// validateAddrs returns an error if the block-page redirect if the IP addresses
// in the block-page redirect configuration are invalid.
func (c *serverGroupBlockPageConfig) validateAddrs() (err error) {
for i, r := range c.IPv4 {
err = r.validate()
if err != nil {
return fmt.Errorf("ipv4: at index %d: address: %w", i, err)
} else if !r.Address.Is4() {
return fmt.Errorf("ipv4: at index %d: address: not ipv4: %v", i, r.Address)
}
}
for i, r := range c.IPv6 {
err = r.validate()
if err != nil {
return fmt.Errorf("ipv6: at index %d: %w", i, err)
} else if !r.Address.Is6() {
return fmt.Errorf("ipv6: at index %d: address: not ipv6: %v", i, r.Address)
}
}
return nil
}
// serverGroupBlockPageRecord is a structure for defining answer records in
// [serverGroupBlockPageConfig].
type serverGroupBlockPageRecord struct {
Address netip.Addr `yaml:"address"`
}
// validate returns an error if the record configuration is invalid.
func (c *serverGroupBlockPageRecord) validate() (err error) {
switch {
case c == nil:
return errNilConfig
case !c.Address.IsValid():
return errors.Error("invalid addr")
default:
return nil
}
}
// serverGroupBlockPageApplyConfig defines the conditions for applying the
// block-page logic for a particular request.
type serverGroupBlockPageApplyConfig struct {
// Client are the parameters for clients for which block page is always
// enabled.
Client []*serverGroupBlockPageClientConfig `yaml:"client"`
}
// toInternal returns the block-page redirect applying configuration for a
// server group. c is assumed to be valid.
func (c *serverGroupBlockPageApplyConfig) toInternal() (conf *agd.BlockPageRedirectApply) {
var subnets []netip.Prefix
for _, cli := range c.Client {
subnets = append(subnets, cli.Address.Prefix)
}
return &agd.BlockPageRedirectApply{
ClientSubnets: subnets,
}
}
// validate returns an error if the block-page redirect applying configuration
// is invalid.
func (c *serverGroupBlockPageApplyConfig) validate() (err error) {
if c == nil {
return errNilConfig
}
for i, cli := range c.Client {
err = cli.validate()
if err != nil {
return fmt.Errorf("client: at index %d: %w", i, err)
}
}
return nil
}
// serverGroupBlockPageClientConfig is a common structure for defining clients
// in [serverGroupBlockPageSkipConfig] and [serverGroupBlockPageApplyConfig].
type serverGroupBlockPageClientConfig struct {
Address netutil.Prefix `yaml:"address"`
}
// validate returns an error if the client configuration is invalid.
func (c *serverGroupBlockPageClientConfig) validate() (err error) {
switch {
case c == nil:
return errNilConfig
case !c.Address.IsValid():
return errors.Error("invalid addr")
default:
return nil
}
}
// serverGroupBlockPageSkipConfig defines the conditions for skipping the block
// page logic for a particular request.
type serverGroupBlockPageSkipConfig struct {
// Client are the parameters for clients for which block page is always
// disabled.
Client []*serverGroupBlockPageClientConfig `yaml:"client"`
// QuestionDomains are the parameters for request questions for which block
// page is always disabled.
Question []*serverGroupBlockPageQuestionConfig `yaml:"question"`
}
// toInternal returns the block-page redirect skipping configuration for a
// server group. c is assumed to be valid.
func (c *serverGroupBlockPageSkipConfig) toInternal() (conf *agd.BlockPageRedirectSkip) {
var subnets []netip.Prefix
for _, cli := range c.Client {
subnets = append(subnets, cli.Address.Prefix)
}
var domains []string
for _, q := range c.Question {
domains = append(domains, q.Domain)
}
return &agd.BlockPageRedirectSkip{
ClientSubnets: subnets,
QuestionDomains: domains,
}
}
// validate returns an error if the block-page redirect skipping configuration
// is invalid.
func (c *serverGroupBlockPageSkipConfig) validate() (err error) {
if c == nil {
return errNilConfig
}
for i, cli := range c.Client {
err = cli.validate()
if err != nil {
return fmt.Errorf("client: at index %d: %w", i, err)
}
}
for i, q := range c.Question {
switch {
case q == nil:
return fmt.Errorf("question: at index %d: %w", i, errNilConfig)
case q.Domain == "":
return fmt.Errorf("question: at index %d: %w", i, errors.Error("empty domain"))
}
}
return nil
}
// serverGroupBlockPageQuestionConfig is a structure for defining question
// domains in [serverGroupBlockPageRedirectSkip].
type serverGroupBlockPageQuestionConfig struct {
Domain string `yaml:"domain"`
}

View File

@ -15,9 +15,10 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/agdservice"
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
"github.com/AdguardTeam/AdGuardDNS/internal/metrics"
"github.com/AdguardTeam/golibs/container"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/service"
"github.com/AdguardTeam/golibs/stringutil"
"github.com/prometheus/client_golang/prometheus"
)
@ -96,7 +97,7 @@ func (c *tlsConfig) validate(needsTLS bool) (err error) {
// validateDeviceIDWildcards returns an error if the device ID domain wildcards
// are invalid.
func validateDeviceIDWildcards(wildcards []string) (err error) {
s := stringutil.NewSet()
s := container.NewMapSet[string]()
for i, w := range wildcards {
// TODO(e.burkov): Consider removing this requirement.
if !strings.HasPrefix(w, "*.") {
@ -185,6 +186,7 @@ func (certs tlsConfigCerts) validate() (err error) {
// ticketRotator is a refresh worker that rereads and resets TLS session tickets.
type ticketRotator struct {
errColl errcoll.Interface
confs map[*tls.Config][]string
}
@ -192,7 +194,10 @@ type ticketRotator struct {
// tickets for the TLS configurations of all servers in grps.
//
// grps is assumed to be valid.
func newTicketRotator(grps []*agd.ServerGroup) (tr *ticketRotator, err error) {
func newTicketRotator(
errColl errcoll.Interface,
grps []*agd.ServerGroup,
) (tr *ticketRotator, err error) {
confs := map[*tls.Config][]string{}
for _, g := range grps {
@ -209,6 +214,7 @@ func newTicketRotator(grps []*agd.ServerGroup) (tr *ticketRotator, err error) {
}
tr = &ticketRotator{
errColl: errColl,
confs: confs,
}
@ -231,7 +237,17 @@ const sessTickLen = 32
var _ agdservice.Refresher = (*ticketRotator)(nil)
// Refresh implements the [agdservice.Refresher] interface for *ticketRotator.
func (r *ticketRotator) Refresh(_ context.Context) (err error) {
func (r *ticketRotator) Refresh(ctx context.Context) (err error) {
// TODO(a.garipov): Use slog.
log.Debug("tickrot_refresh: started")
defer log.Debug("tickrot_refresh: finished")
defer func() {
if err != nil {
errcoll.Collectf(ctx, r.errColl, "tickrot_refresh: %w", err)
}
}()
for conf, files := range r.confs {
keys := make([][sessTickLen]byte, 0, len(files))
@ -304,7 +320,7 @@ func setupTicketRotator(
sigHdlr *service.SignalHandler,
errColl errcoll.Interface,
) (err error) {
tickRot, err := newTicketRotator(srvGrps)
tickRot, err := newTicketRotator(errColl, srvGrps)
if err != nil {
return fmt.Errorf("setting up ticket rotator: %w", err)
}
@ -312,12 +328,10 @@ func setupTicketRotator(
refr := agdservice.NewRefreshWorker(&agdservice.RefreshWorkerConfig{
Context: ctxWithDefaultTimeout,
Refresher: tickRot,
ErrColl: errColl,
Name: "tickrot",
// TODO(ameshkov): Consider making configurable.
Interval: 1 * time.Minute,
RefreshOnShutdown: false,
RoutineLogsAreDebug: true,
RandomizeStart: false,
})
err = refr.Start(context.Background())

View File

@ -1,6 +1,7 @@
package cmd
import (
"cmp"
"context"
"fmt"
"net/netip"
@ -13,6 +14,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/prometheus"
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/service"
"github.com/AdguardTeam/golibs/timeutil"
)
@ -73,7 +75,7 @@ func (c *upstreamConfig) validate() (err error) {
}
}
return coalesceError(
return cmp.Or(
validateProp("fallback", c.Fallback.validate),
validateProp("healthcheck", c.Healthcheck.validate),
)
@ -171,12 +173,15 @@ func newUpstreamHealthcheck(
conf.Healthcheck.Timeout.Duration,
)
},
Refresher: handler,
ErrColl: errColl,
Refresher: agdservice.NewRefresherWithErrColl(
handler,
log.Debug,
errColl,
"upstream_healthcheck_refresh",
),
Name: "upstream healthcheck",
Interval: conf.Healthcheck.Interval.Duration,
RefreshOnShutdown: false,
RoutineLogsAreDebug: true,
RandomizeStart: false,
})
}

View File

@ -25,12 +25,15 @@ type webConfig struct {
// LinkedIP is the optional linked IP web server.
LinkedIP *linkedIPServer `yaml:"linked_ip"`
// SafeBrowsing is the optional safe browsing block page web server.
SafeBrowsing *blockPageServer `yaml:"safe_browsing"`
// AdultBlocking is the optional adult blocking block page web server.
AdultBlocking *blockPageServer `yaml:"adult_blocking"`
// GeneralBlocking is the optional general block-page web server.
GeneralBlocking *blockPageServer `yaml:"general_blocking"`
// SafeBrowsing is the optional safe browsing block page web server.
SafeBrowsing *blockPageServer `yaml:"safe_browsing"`
// RootRedirectURL is the URL to which non-DNS and non-Debug HTTP requests
// are redirected. If not set, a 404 page is shown.
RootRedirectURL *urlutil.URL `yaml:"root_redirect_url"`
@ -86,6 +89,11 @@ func (c *webConfig) toInternal(
return nil, fmt.Errorf("converting adult_blocking: %w", err)
}
conf.GeneralBlocking, err = c.GeneralBlocking.toInternal()
if err != nil {
return nil, fmt.Errorf("converting general_blocking: %w", err)
}
conf.SafeBrowsing, err = c.SafeBrowsing.toInternal()
if err != nil {
return nil, fmt.Errorf("converting safe_browsing: %w", err)
@ -146,16 +154,21 @@ func (c *webConfig) validate() (err error) {
return fmt.Errorf("linked_ip: %w", err)
}
err = c.SafeBrowsing.validate()
if err != nil {
return fmt.Errorf("safe_browsing: %w", err)
}
err = c.AdultBlocking.validate()
if err != nil {
return fmt.Errorf("adult_blocking: %w", err)
}
err = c.GeneralBlocking.validate()
if err != nil {
return fmt.Errorf("general_blocking: %w", err)
}
err = c.SafeBrowsing.validate()
if err != nil {
return fmt.Errorf("safe_browsing: %w", err)
}
err = c.StaticContent.validate()
if err != nil {
return fmt.Errorf("static_content: %w", err)

View File

@ -13,6 +13,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/agdhttp"
"github.com/AdguardTeam/AdGuardDNS/internal/agdservice"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/ratelimit"
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
"github.com/AdguardTeam/AdGuardDNS/internal/metrics"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
@ -23,12 +24,14 @@ type AllowlistRefresher struct {
allowlist *ratelimit.DynamicAllowlist
http *agdhttp.Client
url *url.URL
errColl errcoll.Interface
}
// NewAllowlistRefresher returns a properly initialized *AllowlistRefresher.
func NewAllowlistRefresher(
allowlist *ratelimit.DynamicAllowlist,
consulURL *url.URL,
errColl errcoll.Interface,
) (l *AllowlistRefresher, err error) {
l = &AllowlistRefresher{
allowlist: allowlist,
@ -37,6 +40,7 @@ func NewAllowlistRefresher(
Timeout: 15 * time.Second,
}),
url: consulURL,
errColl: errColl,
}
err = l.Refresh(context.Background())
@ -53,6 +57,10 @@ var _ agdservice.Refresher = (*AllowlistRefresher)(nil)
// Refresh implements the [agdservice.Refresher] interface for
// *AllowlistRefresher.
func (l *AllowlistRefresher) Refresh(ctx context.Context) (err error) {
// TODO(a.garipov): Use slog.
log.Info("allowlist_refresh: started")
defer log.Info("allowlist_refresh: finished")
defer func() {
metrics.ConsulAllowlistUpdateTime.SetToCurrentTime()
metrics.SetStatusGauge(metrics.ConsulAllowlistUpdateStatus, err)
@ -60,7 +68,10 @@ func (l *AllowlistRefresher) Refresh(ctx context.Context) (err error) {
consulNets, err := l.loadConsul(ctx)
if err != nil {
return fmt.Errorf("reading consul: %w", err)
errcoll.Collectf(ctx, l.errColl, "allowlist_refresh: %w", err)
// Don't wrap the error, because it's informative enough as is.
return err
}
log.Info("allowlist: loaded %d records from %s", len(consulNets), l.url)

View File

@ -2,6 +2,7 @@ package consul_test
import (
"context"
"io"
"net/http"
"net/http/httptest"
"net/netip"
@ -9,6 +10,7 @@ import (
"testing"
"github.com/AdguardTeam/AdGuardDNS/internal/agdhttp"
"github.com/AdguardTeam/AdGuardDNS/internal/agdtest"
"github.com/AdguardTeam/AdGuardDNS/internal/consul"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/ratelimit"
"github.com/AdguardTeam/golibs/testutil"
@ -70,12 +72,18 @@ func TestNewAllowlistRefresher(t *testing.T) {
u := handleWithURL(t, http.HandlerFunc(func(rw http.ResponseWriter, _ *http.Request) {
pt := testutil.PanicT{}
_, err := rw.Write([]byte(testCases[i].resp))
_, err := io.WriteString(rw, testCases[i].resp)
require.NoError(pt, err)
}))
t.Run(tc.name, func(t *testing.T) {
_, err := consul.NewAllowlistRefresher(al, u)
errColl := &agdtest.ErrorCollector{
OnCollect: func(_ context.Context, err error) {
panic("not implemented")
},
}
_, err := consul.NewAllowlistRefresher(al, u, errColl)
require.NoError(t, err)
for _, ip := range tc.wantAllow {
@ -104,10 +112,18 @@ func TestNewAllowlistRefresher(t *testing.T) {
}))
wantErr := &agdhttp.StatusError{}
_, err := consul.NewAllowlistRefresher(al, u)
var gotCollErr error
errColl := &agdtest.ErrorCollector{
OnCollect: func(_ context.Context, err error) {
gotCollErr = err
},
}
_, err := consul.NewAllowlistRefresher(al, u, errColl)
require.ErrorAs(t, err, &wantErr)
assert.Equal(t, wantErr.Got, status)
assert.ErrorAs(t, gotCollErr, &wantErr)
})
}
@ -116,11 +132,18 @@ func TestAllowlistRefresher_Refresh_deadline(t *testing.T) {
u := handleWithURL(t, http.HandlerFunc(func(rw http.ResponseWriter, _ *http.Request) {
pt := testutil.PanicT{}
_, err := rw.Write([]byte(`[]`))
_, err := io.WriteString(rw, `[]`)
require.NoError(pt, err)
}))
c, err := consul.NewAllowlistRefresher(al, u)
var gotCollErr error
errColl := &agdtest.ErrorCollector{
OnCollect: func(_ context.Context, err error) {
gotCollErr = err
},
}
c, err := consul.NewAllowlistRefresher(al, u, errColl)
require.NoError(t, err)
ctx, cancel := context.WithCancel(context.Background())
@ -128,4 +151,5 @@ func TestAllowlistRefresher_Refresh_deadline(t *testing.T) {
err = c.Refresh(ctx)
assert.ErrorIs(t, err, context.Canceled)
assert.ErrorIs(t, gotCollErr, context.Canceled)
}

View File

@ -7,7 +7,6 @@ import (
"testing"
"time"
"github.com/AdguardTeam/AdGuardDNS/internal/agdtest"
"github.com/AdguardTeam/AdGuardDNS/internal/debugsvc"
"github.com/AdguardTeam/golibs/testutil"
"github.com/stretchr/testify/assert"
@ -45,11 +44,11 @@ func TestService_Start(t *testing.T) {
var err error
require.NotPanics(t, func() {
err = svc.Start(agdtest.ContextWithTimeout(t, testTimeout))
err = svc.Start(testutil.ContextWithTimeout(t, testTimeout))
})
require.NoError(t, err)
testutil.CleanupAndRequireSuccess(t, func() (err error) {
return svc.Shutdown(agdtest.ContextWithTimeout(t, testTimeout))
return svc.Shutdown(testutil.ContextWithTimeout(t, testTimeout))
})
client := http.Client{

View File

@ -5,6 +5,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
"github.com/AdguardTeam/AdGuardDNS/internal/metrics"
"github.com/AdguardTeam/golibs/container"
"github.com/miekg/dns"
)
@ -63,7 +64,7 @@ func (b *buffer) all() (records []*record) {
defer b.mu.Unlock()
for key, val := range b.entries {
if len(val.answers) == 0 {
if val.answers.Len() == 0 {
records = append(records, &record{
target: key.target,
hits: val.hits,
@ -73,7 +74,7 @@ func (b *buffer) all() (records []*record) {
continue
}
for a := range val.answers {
val.answers.Range(func(a recordAnswer) (cont bool) {
records = append(records, &record{
target: key.target,
answer: a.value,
@ -81,24 +82,26 @@ func (b *buffer) all() (records []*record) {
rrType: a.rrType,
rcode: a.rcode,
})
}
return true
})
}
return records
}
// toAnswerSet converts a slice of [dns.RR] to a map that can easier be
// toAnswerSet converts a slice of [dns.RR] to a set that can easier be
// serialized to a csv.
func toAnswerSet(answers []dns.RR, rc dnsmsg.RCode) (answerSet map[recordAnswer]unit) {
answerSet = map[recordAnswer]unit{}
func toAnswerSet(answers []dns.RR, rc dnsmsg.RCode) (answerSet *container.MapSet[recordAnswer]) {
answerSet = container.NewMapSet[recordAnswer]()
for _, a := range answers {
ansStr := answerString(a)
if ansStr != "" {
answerSet[recordAnswer{
answerSet.Add(recordAnswer{
value: ansStr,
rrType: a.Header().Rrtype,
rcode: rc,
}] = unit{}
})
}
}

View File

@ -38,9 +38,12 @@ func (db *Default) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}()
var rw io.Writer = w
// TODO(a.garipov): Consider parsing the quality value.
if strings.Contains(r.Header.Get(httphdr.AcceptEncoding), "gzip") {
h.Set(httphdr.ContentEncoding, "gzip")
// TODO(a.garipov): Parse the quality value.
//
// TODO(a.garipov): Support other compression algorithms.
if strings.Contains(r.Header.Get(httphdr.AcceptEncoding), agdhttp.HdrValGzip) {
h.Set(httphdr.ContentEncoding, agdhttp.HdrValGzip)
gw := gzip.NewWriter(w)
defer func() { err = errors.WithDeferred(err, gw.Close()) }()

View File

@ -31,7 +31,7 @@ func TestDefault_ServeHTTP(t *testing.T) {
successHdr := http.Header{
httphdr.ContentType: []string{agdhttp.HdrValTextCSV},
httphdr.Trailer: []string{httphdr.XError},
httphdr.ContentEncoding: []string{"gzip"},
httphdr.ContentEncoding: []string{agdhttp.HdrValGzip},
}
newMsg := func(rcode int, name string, qtype uint16) (m *dns.Msg) {
@ -112,7 +112,7 @@ func TestDefault_ServeHTTP(t *testing.T) {
(&url.URL{Scheme: "http", Host: "example.com"}).String(),
nil,
)
r.Header.Add(httphdr.AcceptEncoding, "gzip")
r.Header.Add(httphdr.AcceptEncoding, agdhttp.HdrValGzip)
for _, tc := range testCases {
db := dnsdb.New(&dnsdb.DefaultConfig{

View File

@ -5,6 +5,7 @@ import (
"strings"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
"github.com/AdguardTeam/golibs/container"
"github.com/miekg/dns"
)
@ -69,12 +70,9 @@ type recordKey struct {
qt dnsmsg.RRType
}
// unit is a convenient alias for struct{}.
type unit = struct{}
// recordValue contains the values for a single record key.
type recordValue struct {
answers map[recordAnswer]unit
answers *container.MapSet[recordAnswer]
hits uint64
}

View File

@ -17,11 +17,15 @@ type BlockingMode interface {
// BlockingModeCustomIP makes the [dnsmsg.Constructor] return responses with
// custom IP addresses to A and AAAA requests. For all other types of requests,
// as well as if one of the addresses isn't set, it returns a response with no
// answers (aka NODATA).
// as well as in case the address corresponding to IP version is not set, it
// returns a response with no answers (aka NODATA).
type BlockingModeCustomIP struct {
IPv4 netip.Addr
IPv6 netip.Addr
// IPv4 is a slice of valid IPv4 addresses used in responses to A requests.
IPv4 []netip.Addr
// IPv6 is a slice of valid IPv6 addresses used in responses to AAAA
// requests.
IPv6 []netip.Addr
}
// isBlockingMode implements the BlockingMode interface for

View File

@ -9,8 +9,8 @@ import (
// Cloner is a pool that can clone common parts of DNS messages with fewer
// allocations.
//
// TODO(a.garipov): Use in filtering when cloning a [filter.ResultModified]
// message.
// TODO(a.garipov): Use in filtering when cloning a
// [filter.ResultModifiedResponse] message.
//
// TODO(a.garipov): Use in [Constructor].
type Cloner struct {

View File

@ -328,7 +328,7 @@ func BenchmarkClone(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
for range b.N {
msgSink = dnsmsg.Clone(tc.msg)
}
@ -372,7 +372,7 @@ func BenchmarkCloner_Clone(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
for i := range b.N {
msgSink = c.Clone(tc.msg)
if i < b.N-1 {
// Don't dispose of the last one to be sure that we can

View File

@ -33,6 +33,17 @@ func NewConstructor(cloner *Cloner, bm BlockingMode, respTTL time.Duration) (c *
}
}
// Cloner returns the constructor's Cloner.
func (c *Constructor) Cloner() (cloner *Cloner) {
return c.cloner
}
// FilteredResponseTTL returns the TTL that the constructor uses to build
// blocked responses.
func (c *Constructor) FilteredResponseTTL() (ttl time.Duration) {
return c.fltRespTTL
}
// NewBlockedRespMsg returns a blocked DNS response message based on the
// constructor's blocking mode.
func (c *Constructor) NewBlockedRespMsg(req *dns.Msg) (msg *dns.Msg, err error) {
@ -65,12 +76,12 @@ func (c *Constructor) newBlockedCustomIPRespMsg(
) (msg *dns.Msg, err error) {
switch qt := req.Question[0].Qtype; qt {
case dns.TypeA:
if m.IPv4.IsValid() {
return c.NewIPRespMsg(req, m.IPv4)
if len(m.IPv4) > 0 {
return c.NewIPRespMsg(req, m.IPv4...)
}
case dns.TypeAAAA:
if m.IPv6.IsValid() {
return c.NewIPRespMsg(req, m.IPv6)
if len(m.IPv6) > 0 {
return c.NewIPRespMsg(req, m.IPv6...)
}
default:
// Go on.
@ -202,7 +213,7 @@ func (c *Constructor) AppendDebugExtra(req, resp *dns.Msg, str string) (err erro
// TODO(a.garipov): Use slices.Chunk in Go 1.23.
newStr := make([]string, strNum)
for i := 0; i < strNum; i++ {
for i := range strNum {
start := i * MaxTXTStringLen
var cutStr string

View File

@ -2,6 +2,7 @@ package dnsmsg_test
import (
"net"
"net/netip"
"strings"
"testing"
@ -81,26 +82,34 @@ func TestConstructor_NewBlockedRespMsg_customIP(t *testing.T) {
wantAAAA bool
}{{
messages: dnsmsg.NewConstructor(nil, &dnsmsg.BlockingModeCustomIP{
IPv4: testIPv4,
IPv6: testIPv6,
IPv4: []netip.Addr{testIPv4},
IPv6: []netip.Addr{testIPv6},
}, testFltRespTTL),
name: "both",
wantA: true,
wantAAAA: true,
}, {
messages: dnsmsg.NewConstructor(nil, &dnsmsg.BlockingModeCustomIP{
IPv4: testIPv4,
IPv4: []netip.Addr{testIPv4},
}, testFltRespTTL),
name: "ipv4_only",
wantA: true,
wantAAAA: false,
}, {
messages: dnsmsg.NewConstructor(nil, &dnsmsg.BlockingModeCustomIP{
IPv6: testIPv6,
IPv6: []netip.Addr{testIPv6},
}, testFltRespTTL),
name: "ipv6_only",
wantA: false,
wantAAAA: true,
}, {
messages: dnsmsg.NewConstructor(nil, &dnsmsg.BlockingModeCustomIP{
IPv4: []netip.Addr{},
IPv6: []netip.Addr{},
}, testFltRespTTL),
name: "empty",
wantA: false,
wantAAAA: false,
}}
for _, tc := range testCases {

View File

@ -194,7 +194,7 @@ func TestMiddleware_Wrap(t *testing.T) {
var err error
var nrw *dnsserver.NonWriterResponseWriter
for i := 0; i < N; i++ {
for range N {
addr := &net.UDPAddr{IP: net.IP{1, 2, 3, 4}, Port: 53}
nrw = dnsserver.NewNonWriterResponseWriter(addr, addr)
err = withCache.ServeDNS(context.Background(), nrw, tc.req)

View File

@ -1,9 +1,7 @@
package dnsserver_test
import (
"context"
"testing"
"time"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver"
"github.com/AdguardTeam/golibs/testutil"
@ -15,16 +13,3 @@ func TestMain(m *testing.M) {
// testTimeout is a common timeout for tests.
const testTimeout = dnsserver.DefaultReadTimeout
// contextWithTimeout is a helper that creates a new context with timeout and
// registers ctx's cleanup with t.Cleanup.
//
// TODO(a.garipov): Move to golibs and DRY.
func contextWithTimeout(tb testing.TB, timeout time.Duration) (ctx context.Context) {
tb.Helper()
ctx, cancel := context.WithTimeout(context.Background(), timeout)
tb.Cleanup(cancel)
return ctx
}

View File

@ -33,7 +33,7 @@ func CreateTestHandler(recordsCount int) (h dnsserver.Handler) {
}
ip := netutil.IPv4Localhost().Prev()
for i := 0; i < recordsCount; i++ {
for range recordsCount {
// Add 1 to make sure that each IP is valid.
ip = ip.Next()
ans = append(ans, &dns.A{Hdr: hdr, A: ip.AsSlice()})

View File

@ -34,6 +34,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/miekg/dns"
"golang.org/x/exp/rand"
)
@ -265,6 +266,10 @@ func (h *Handler) exchange(
// upstreams is detected to be up again, requests are redirected back to the
// main upstreams.
func (h *Handler) Refresh(ctx context.Context) (err error) {
// TODO(a.garipov): Use slog.
log.Debug("upstream_healthcheck_refresh: started")
defer log.Debug("upstream_healthcheck_refresh: finished")
return h.refresh(ctx, false)
}

View File

@ -21,18 +21,6 @@ func TestMain(m *testing.M) {
// testTimeout is the timeout for tests.
const testTimeout = 1 * time.Second
// newTimeoutCtx is a test helper that returns a context with a timeout of
// [testTimeout] and its cancel function being called in the test cleanup. It
// should not be used where cancelation is expected sooner.
func newTimeoutCtx(tb testing.TB, parent context.Context) (ctx context.Context) {
tb.Helper()
ctx, cancel := context.WithTimeout(parent, testTimeout)
tb.Cleanup(cancel)
return ctx
}
func TestHandler_ServeDNS(t *testing.T) {
srv, addr := dnsservertest.RunDNSServer(t, dnsservertest.DefaultHandler())
@ -49,7 +37,7 @@ func TestHandler_ServeDNS(t *testing.T) {
rw := dnsserver.NewNonWriterResponseWriter(srv.LocalUDPAddr(), srv.LocalUDPAddr())
// Check the handler's ServeDNS method
err := handler.ServeDNS(newTimeoutCtx(t, context.Background()), rw, req)
err := handler.ServeDNS(testutil.ContextWithTimeout(t, testTimeout), rw, req)
require.NoError(t, err)
res := rw.Msg()

View File

@ -9,6 +9,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/dnsservertest"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/forward"
"github.com/AdguardTeam/golibs/testutil"
"github.com/miekg/dns"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@ -63,32 +64,30 @@ func TestHandler_Refresh(t *testing.T) {
req := dnsservertest.CreateMessage("example.org.", dns.TypeA)
rw := dnsserver.NewNonWriterResponseWriter(fallback.LocalUDPAddr(), fallback.LocalUDPAddr())
ctx := context.Background()
err := handler.ServeDNS(newTimeoutCtx(t, ctx), rw, req)
err := handler.ServeDNS(testutil.ContextWithTimeout(t, testTimeout), rw, req)
require.Error(t, err)
assert.Equal(t, int64(2), upstreamRequestsCount.Load())
err = handler.Refresh(newTimeoutCtx(t, ctx))
err = handler.Refresh(testutil.ContextWithTimeout(t, testTimeout))
require.Error(t, err)
assert.Equal(t, int64(4), upstreamRequestsCount.Load())
err = handler.ServeDNS(newTimeoutCtx(t, ctx), rw, req)
err = handler.ServeDNS(testutil.ContextWithTimeout(t, testTimeout), rw, req)
require.NoError(t, err)
assert.Equal(t, int64(4), upstreamRequestsCount.Load())
// Now, set upstream up.
upstreamIsUp.Store(true)
err = handler.ServeDNS(newTimeoutCtx(t, ctx), rw, req)
err = handler.ServeDNS(testutil.ContextWithTimeout(t, testTimeout), rw, req)
require.NoError(t, err)
assert.Equal(t, int64(4), upstreamRequestsCount.Load())
err = handler.Refresh(newTimeoutCtx(t, ctx))
err = handler.Refresh(testutil.ContextWithTimeout(t, testTimeout))
require.NoError(t, err)
assert.Equal(t, int64(5), upstreamRequestsCount.Load())
err = handler.ServeDNS(newTimeoutCtx(t, ctx), rw, req)
err = handler.ServeDNS(testutil.ContextWithTimeout(t, testTimeout), rw, req)
require.NoError(t, err)
assert.Equal(t, int64(6), upstreamRequestsCount.Load())
}

View File

@ -44,7 +44,7 @@ func TestUpstreamPlain_Exchange(t *testing.T) {
defer log.OnCloserError(u, log.DEBUG)
req := dnsservertest.CreateMessage("example.org.", dns.TypeA)
res, nw, err := u.Exchange(newTimeoutCtx(t, context.Background()), req)
res, nw, err := u.Exchange(testutil.ContextWithTimeout(t, testTimeout), req)
require.NoError(t, err)
require.NotNil(t, res)
dnsservertest.RequireResponse(t, req, res, 1, dns.RcodeSuccess, false)
@ -95,9 +95,7 @@ func TestUpstreamPlain_Exchange_truncated(t *testing.T) {
})
defer log.OnCloserError(uUDP, log.DEBUG)
ctx := context.Background()
res, nw, err := uUDP.Exchange(newTimeoutCtx(t, ctx), req)
res, nw, err := uUDP.Exchange(testutil.ContextWithTimeout(t, testTimeout), req)
require.NoError(t, err)
dnsservertest.RequireResponse(t, req, res, 0, dns.RcodeSuccess, true)
@ -110,7 +108,7 @@ func TestUpstreamPlain_Exchange_truncated(t *testing.T) {
})
defer log.OnCloserError(uTCP, log.DEBUG)
res, nw, err = uTCP.Exchange(newTimeoutCtx(t, ctx), req)
res, nw, err = uTCP.Exchange(testutil.ContextWithTimeout(t, testTimeout), req)
require.NoError(t, err)
dnsservertest.RequireResponse(t, req, res, 1, dns.RcodeSuccess, false)
@ -124,7 +122,7 @@ func TestUpstreamPlain_Exchange_truncated(t *testing.T) {
})
defer log.OnCloserError(uAny, log.DEBUG)
res, nw, err = uAny.Exchange(newTimeoutCtx(t, ctx), req)
res, nw, err = uAny.Exchange(testutil.ContextWithTimeout(t, testTimeout), req)
require.NoError(t, err)
dnsservertest.RequireResponse(t, req, res, 1, dns.RcodeSuccess, false)
@ -165,7 +163,8 @@ func TestUpstreamPlain_Exchange_fallbackFail(t *testing.T) {
var resp *dns.Msg
var err error
go func() {
resp, _, err = u.Exchange(newTimeoutCtx(t, context.Background()), req)
ctx := testutil.ContextWithTimeout(t, testTimeout)
resp, _, err = u.Exchange(ctx, req)
testutil.RequireSend(pt, respCh, struct{}{}, testTimeout)
}()
@ -266,7 +265,8 @@ func TestUpstreamPlain_Exchange_fallbackSuccess(t *testing.T) {
var actualResp *dns.Msg
var err error
go func() {
actualResp, _, err = u.Exchange(newTimeoutCtx(t, context.Background()), clonedReq)
ctx := testutil.ContextWithTimeout(t, testTimeout)
actualResp, _, err = u.Exchange(ctx, clonedReq)
testutil.RequireSend(pt, respCh, struct{}{}, testTimeout)
}()

View File

@ -1,44 +1,45 @@
module github.com/AdguardTeam/AdGuardDNS/internal/dnsserver
go 1.21.8
go 1.22.4
require (
github.com/AdguardTeam/golibs v0.20.1
github.com/ameshkov/dnscrypt/v2 v2.2.7
github.com/AdguardTeam/golibs v0.23.2
github.com/ameshkov/dnscrypt/v2 v2.3.0
github.com/ameshkov/dnsstamps v1.0.3
github.com/bluele/gcache v0.0.2
github.com/miekg/dns v1.1.58
github.com/panjf2000/ants/v2 v2.9.0
github.com/panjf2000/ants/v2 v2.9.1
github.com/patrickmn/go-cache v2.1.1-0.20191004192108-46f407853014+incompatible
github.com/prometheus/client_golang v1.18.0
github.com/quic-go/quic-go v0.41.0
github.com/stretchr/testify v1.8.4
golang.org/x/exp v0.0.0-20240213143201-ec583247a57a
golang.org/x/net v0.21.0
golang.org/x/sys v0.17.0
github.com/prometheus/client_golang v1.19.0
github.com/quic-go/quic-go v0.42.0
github.com/stretchr/testify v1.9.0
golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8
golang.org/x/net v0.24.0
golang.org/x/sys v0.19.0
)
require (
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-logr/logr v1.4.1 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/google/pprof v0.0.0-20240207164012-fb44976bdcd5 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd // indirect
github.com/kr/text v0.2.0 // indirect
github.com/onsi/ginkgo/v2 v2.15.0 // indirect
github.com/onsi/ginkgo/v2 v2.17.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_model v0.5.0 // indirect
github.com/prometheus/common v0.46.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.52.3 // indirect
github.com/prometheus/procfs v0.13.0 // indirect
github.com/quic-go/qpack v0.4.0 // indirect
go.uber.org/mock v0.4.0 // indirect
golang.org/x/crypto v0.19.0 // indirect
golang.org/x/mod v0.15.0 // indirect
golang.org/x/crypto v0.22.0 // indirect
golang.org/x/mod v0.17.0 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/tools v0.18.0 // indirect
google.golang.org/protobuf v1.32.0 // indirect
golang.org/x/tools v0.20.0 // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

View File

@ -1,19 +1,19 @@
github.com/AdguardTeam/golibs v0.20.1 h1:ol8qLjWGZhU9paMMwN+OLWVTUigGsXa29iVTyd62VKY=
github.com/AdguardTeam/golibs v0.20.1/go.mod h1:bgcMgRviCKyU6mkrX+RtT/OsKPFzyppelfRsksMG3KU=
github.com/AdguardTeam/golibs v0.23.2 h1:rMjYantwtQ39e8G4zBQ6ZLlm4s3XH30Bc9VxhoOHwao=
github.com/AdguardTeam/golibs v0.23.2/go.mod h1:o9i55Sx6v7qogRQeqaBfmLbC/pZqeMBWi015U5PTDY0=
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY=
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA=
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 h1:52m0LGchQBBVqJRyYYufQuIbVqRawmubW3OFGqK1ekw=
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635/go.mod h1:lmLxL+FV291OopO93Bwf9fQLQeLyt33VJRUg5VJ30us=
github.com/ameshkov/dnscrypt/v2 v2.2.7 h1:aEitLIR8HcxVodZ79mgRcCiC0A0I5kZPBuWGFwwulAw=
github.com/ameshkov/dnscrypt/v2 v2.2.7/go.mod h1:qPWhwz6FdSmuK7W4sMyvogrez4MWdtzosdqlr0Rg3ow=
github.com/ameshkov/dnscrypt/v2 v2.3.0 h1:pDXDF7eFa6Lw+04C0hoMh8kCAQM8NwUdFEllSP2zNLs=
github.com/ameshkov/dnscrypt/v2 v2.3.0/go.mod h1:N5hDwgx2cNb4Ay7AhvOSKst+eUiOZ/vbKRO9qMpQttE=
github.com/ameshkov/dnsstamps v1.0.3 h1:Srzik+J9mivH1alRACTbys2xOxs0lRH9qnTA7Y1OYVo=
github.com/ameshkov/dnsstamps v1.0.3/go.mod h1:Ii3eUu73dx4Vw5O4wjzmT5+lkCwovjzaEZZ4gKyIH5A=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bluele/gcache v0.0.2 h1:WcbfdXICg7G/DGBh1PFfcirkWOQV+v077yF1pSy3DGw=
github.com/bluele/gcache v0.0.2/go.mod h1:m15KV+ECjptwSPxKhOhQoAFQVtUFjTVkc3H8o0t/fp0=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
@ -22,40 +22,40 @@ github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/pprof v0.0.0-20240207164012-fb44976bdcd5 h1:E/LAvt58di64hlYjx7AsNS6C/ysHWYo+2qPCZKTQhRo=
github.com/google/pprof v0.0.0-20240207164012-fb44976bdcd5/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo=
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
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=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4=
github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY=
github.com/onsi/ginkgo/v2 v2.15.0 h1:79HwNRBAZHOEwrczrgSOPy+eFTTlIGELKy5as+ClttY=
github.com/onsi/ginkgo/v2 v2.15.0/go.mod h1:HlxMHtYF57y6Dpf+mc5529KKmSq9h2FpCF+/ZkwUxKM=
github.com/onsi/ginkgo/v2 v2.17.1 h1:V++EzdbhI4ZV4ev0UTIj0PzhzOcReJFyJaLjtSF55M8=
github.com/onsi/ginkgo/v2 v2.17.1/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs=
github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8=
github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
github.com/panjf2000/ants/v2 v2.9.0 h1:SztCLkVxBRigbg+vt0S5QvF5vxAbxbKt09/YfAJ0tEo=
github.com/panjf2000/ants/v2 v2.9.0/go.mod h1:7ZxyxsqE4vvW0M7LSD8aI3cKwgFhBHbxnlN8mDqHa1I=
github.com/panjf2000/ants/v2 v2.9.1 h1:Q5vh5xohbsZXGcD6hhszzGqB7jSSc2/CRr3QKIga8Kw=
github.com/panjf2000/ants/v2 v2.9.1/go.mod h1:7ZxyxsqE4vvW0M7LSD8aI3cKwgFhBHbxnlN8mDqHa1I=
github.com/patrickmn/go-cache v2.1.1-0.20191004192108-46f407853014+incompatible h1:IWzUvJ72xMjmrjR9q3H1PF+jwdN0uNQiR2t1BLNalyo=
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.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk=
github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA=
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
github.com/prometheus/common v0.46.0 h1:doXzt5ybi1HBKpsZOL0sSkaNHJJqkyfEWZGGqqScV0Y=
github.com/prometheus/common v0.46.0/go.mod h1:Tp0qkxpb9Jsg54QMe+EAmqXkSV7Evdy1BTn+g2pa/hQ=
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU=
github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/common v0.52.3 h1:5f8uj6ZwHSscOGNdIQg6OiZv/ybiK2CO2q2drVZAQSA=
github.com/prometheus/common v0.52.3/go.mod h1:BrxBKv3FWBIGXw89Mg1AeBq7FSyRzXWI3l3e7W3RN5U=
github.com/prometheus/procfs v0.13.0 h1:GqzLlQyfsPbaEHaQkO7tbDlriv/4o5Hudv6OXHGKX7o=
github.com/prometheus/procfs v0.13.0/go.mod h1:cd4PFCR54QLnGKPaKGA6l+cfuNXtht43ZKY6tow0Y1g=
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
github.com/quic-go/quic-go v0.41.0 h1:aD8MmHfgqTURWNJy48IYFg2OnxwHT3JL7ahGs73lb4k=
github.com/quic-go/quic-go v0.41.0/go.mod h1:qCkNjqczPEvgsOnxZ0eCD14lv+B2LHlFAB++CNOh9hA=
github.com/quic-go/quic-go v0.42.0 h1:uSfdap0eveIl8KXnipv9K7nlwZ5IqLlYOpJ58u5utpM=
github.com/quic-go/quic-go v0.42.0/go.mod h1:132kz4kL3F9vxhW3CtQJLDVwcFe5wdWeJXXijhsO57M=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@ -65,29 +65,31 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/exp v0.0.0-20240213143201-ec583247a57a h1:HinSgX1tJRX3KsL//Gxynpw5CTOAIPhgL4W8PNiIpVE=
golang.org/x/exp v0.0.0-20240213143201-ec583247a57a/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc=
golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8 h1:ESSUROHIBHg7USnszlcdmjBEwdMj9VUvU+OPk4yl2mc=
golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI=
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ=
golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg=
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY=
golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
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=

View File

@ -24,7 +24,7 @@ func TestSessionPacketConn(t *testing.T) {
// Try the test multiple times to reduce flakiness due to UDP failures.
var success4, success6 bool
for i := 0; i < numTries; i++ {
for i := range numTries {
var isTimeout4, isTimeout6 bool
success4 = t.Run(fmt.Sprintf("ipv4_%d", i), func(t *testing.T) {
isTimeout4 = testSessionPacketConn(t, "udp4", "0.0.0.0:0", net.IP{127, 0, 0, 1})

View File

@ -29,7 +29,7 @@ func TestCacheMetricsListener_integration_cache(t *testing.T) {
// Pass 10 requests through the middleware. This way we'll increment and
// set both hits and misses.
for i := 0; i < 10; i++ {
for range 10 {
ctx := dnsserver.ContextWithServerInfo(context.Background(), testServerInfo)
ctx = dnsserver.ContextWithRequestInfo(ctx, &dnsserver.RequestInfo{
StartTime: time.Now(),

View File

@ -11,7 +11,7 @@ import (
func TestInitSyncMap(t *testing.T) {
numCalls := atomic.Uint32{}
m := newInitSyncMap[int, int](func(k int) (v int) {
m := newInitSyncMap(func(k int) (v int) {
numCalls.Add(1)
return k + 1
@ -26,13 +26,13 @@ func TestInitSyncMap(t *testing.T) {
results := make(chan int, n)
for i := 0; i < n; i++ {
for range n {
go func() {
results <- m.get(key)
}()
}
for i := 0; i < n; i++ {
for range n {
got, _ := testutil.RequireReceive(t, results, 1*time.Second)
assert.Equal(t, want, got)
}

View File

@ -40,7 +40,7 @@ func TestRateLimiterMetricsListener_integration_cache(t *testing.T) {
)
// Pass 10 requests through the middleware.
for i := 0; i < 10; i++ {
for i := range 10 {
ctx := dnsserver.ContextWithServerInfo(context.Background(), testServerInfo)
ctx = dnsserver.ContextWithRequestInfo(ctx, &dnsserver.RequestInfo{
StartTime: time.Now(),
@ -73,7 +73,7 @@ func BenchmarkRateLimitMetricsListener(b *testing.B) {
b.Run("OnAllowlisted", func(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
for range b.N {
l.OnAllowlisted(ctx, req, rw)
}
})
@ -81,7 +81,7 @@ func BenchmarkRateLimitMetricsListener(b *testing.B) {
b.Run("OnRateLimited", func(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
for range b.N {
l.OnRateLimited(ctx, req, rw)
}
})

View File

@ -48,7 +48,7 @@ func TestServerMetricsListener_integration_requestLifetime(t *testing.T) {
addr := srv.LocalUDPAddr().String()
// Pass 10 requests to make the test less flaky.
for i := 0; i < 10; i++ {
for range 10 {
res, _, exchErr := c.Exchange(req, addr)
require.NoError(t, exchErr)
require.NotNil(t, res)
@ -96,7 +96,7 @@ func BenchmarkServerMetricsListener(b *testing.B) {
b.Run("OnRequest", func(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
for range b.N {
l.OnRequest(ctx, info, rw)
}
})
@ -104,7 +104,7 @@ func BenchmarkServerMetricsListener(b *testing.B) {
b.Run("OnInvalidMsg", func(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
for range b.N {
l.OnInvalidMsg(ctx)
}
})
@ -112,7 +112,7 @@ func BenchmarkServerMetricsListener(b *testing.B) {
b.Run("OnError", func(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
for range b.N {
l.OnError(ctx, nil)
}
})
@ -120,7 +120,7 @@ func BenchmarkServerMetricsListener(b *testing.B) {
b.Run("OnPanic", func(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
for range b.N {
l.OnPanic(ctx, nil)
}
})

View File

@ -12,8 +12,6 @@ import (
cache "github.com/patrickmn/go-cache"
)
// Backoff Rate Limiter
// BackoffConfig is the configuration structure for a backoff rate limiter.
type BackoffConfig struct {
// Allowlist defines which IP networks are excluded from rate limiting.
@ -158,9 +156,8 @@ func validateAddr(addr netip.Addr) (err error) {
// CountResponses implements the Interface interface for *Backoff.
func (l *Backoff) CountResponses(ctx context.Context, resp *dns.Msg, ip netip.Addr) {
respLimit := l.respSzEst
respSize := resp.Len()
for i := 0; i < respSize/respLimit; i++ {
estRespNum := resp.Len() / l.respSzEst
for range estRespNum {
_, _, _ = l.IsRateLimited(ctx, resp, ip)
}
}

View File

@ -124,7 +124,7 @@ func TestRatelimitMiddleware(t *testing.T) {
})
n := 0
for i := 0; i < tc.reqsNum; i++ {
for range tc.reqsNum {
nrw := dnsserver.NewNonWriterResponseWriter(
&net.UDPAddr{IP: []byte{1, 2, 3, 4}},
tc.remoteAddr,

View File

@ -61,14 +61,13 @@ func BenchmarkServeDNS(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
for range b.N {
_, err = conn.Write(msg)
require.NoError(b, err)
err = readMsg(resBuf, tc.network, conn)
require.NoError(b, err)
}
b.StopTimer()
})
}
}
@ -130,7 +129,7 @@ func BenchmarkServeTLS(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
for range b.N {
_, err = conn.Write(msg)
require.NoError(b, err)
@ -147,7 +146,6 @@ func BenchmarkServeTLS(b *testing.B) {
require.GreaterOrEqual(b, n, dnsserver.DNSHeaderSize)
}
b.StopTimer()
}
func BenchmarkServeDoH(b *testing.B) {
@ -207,7 +205,7 @@ func BenchmarkServeDoH(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
for range b.N {
var res *http.Response
res, err = client.Do(req)
require.NoError(b, err)
@ -271,13 +269,12 @@ func BenchmarkServeDNSCrypt(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
for range b.N {
var resp *dns.Msg
resp, err = client.ExchangeConn(conn, req, ri)
require.NoError(b, err)
require.True(b, resp.Response)
}
b.StopTimer()
})
}
}
@ -312,10 +309,9 @@ func BenchmarkServeQUIC(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
for range b.N {
resp := requireSendQUICMessage(b, sess, req)
require.NotNil(b, resp)
require.True(b, resp.Response)
}
b.StopTimer()
}

View File

@ -1,6 +1,7 @@
package dnsserver
import (
"cmp"
"context"
"net"
"sync"
@ -108,26 +109,14 @@ func NewServerDNS(conf ConfigDNS) (s *ServerDNS) {
// server with a TLS layer on top of it.
func newServerDNS(proto Protocol, conf ConfigDNS) (s *ServerDNS) {
// Init default settings first.
//
// TODO(a.garipov): Use cmp.Or in Go 1.22.
if conf.ReadTimeout == 0 {
conf.ReadTimeout = DefaultReadTimeout
}
if conf.WriteTimeout == 0 {
conf.WriteTimeout = DefaultWriteTimeout
}
if conf.TCPIdleTimeout == 0 {
conf.TCPIdleTimeout = DefaultTCPIdleTimeout
}
conf.ReadTimeout = cmp.Or(conf.ReadTimeout, DefaultReadTimeout)
conf.WriteTimeout = cmp.Or(conf.WriteTimeout, DefaultWriteTimeout)
conf.TCPIdleTimeout = cmp.Or(conf.TCPIdleTimeout, DefaultTCPIdleTimeout)
// Use dns.MinMsgSize since 99% of DNS queries fit this size, so this is
// a sensible default.
if conf.UDPSize == 0 {
conf.UDPSize = dns.MinMsgSize
}
if conf.TCPSize == 0 {
conf.TCPSize = dns.MinMsgSize
}
// Use dns.MinMsgSize since 99% of DNS queries fit this size, so this is a
// sensible default.
conf.UDPSize = cmp.Or(conf.UDPSize, dns.MinMsgSize)
conf.TCPSize = cmp.Or(conf.TCPSize, dns.MinMsgSize)
if conf.ListenConfig == nil {
conf.ListenConfig = netext.DefaultListenConfigWithOOB(nil)

View File

@ -315,7 +315,7 @@ func TestServerDNS_integration_tcpQueriesPipelining(t *testing.T) {
const queriesNum = 100
sentIDs := make(map[uint16]string, queriesNum)
for i := 0; i < queriesNum; i++ {
for i := range queriesNum {
name := fmt.Sprintf("host%d.org", i)
req := dnsservertest.CreateMessage(name, dns.TypeA)
req.Id = uint16(i + 1)
@ -341,7 +341,7 @@ func TestServerDNS_integration_tcpQueriesPipelining(t *testing.T) {
// Read the responses and check their IDs.
receivedIDs := make(map[uint16]string, queriesNum)
for i := 0; i < queriesNum; i++ {
for range queriesNum {
err = conn.SetReadDeadline(time.Now().Add(time.Second))
require.NoError(t, err)
@ -466,7 +466,6 @@ func TestServerDNS_integration_tcpMsgIgnore(t *testing.T) {
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()

View File

@ -51,12 +51,22 @@ func (s *ServerDNS) acceptUDPMsg(ctx context.Context, conn net.PacketConn) (err
s.wg.Add(1)
reqCtx, reqCancel := s.requestContext()
reqCtx = ContextWithRequestInfo(reqCtx, &RequestInfo{StartTime: time.Now()})
// Save the start time here, but create the context inside the goroutine,
// since s.reqCtx.New can be slow.
//
// TODO(a.garipov): The slowness is likely due to constant reallocation of
// timers in [context.WithTimeout]. Consider creating an optimized reusable
// version.
startTime := time.Now()
return s.workerPool.Submit(func() {
reqCtx, reqCancel := s.requestContext()
defer reqCancel()
reqCtx = ContextWithRequestInfo(reqCtx, &RequestInfo{
StartTime: startTime,
})
s.serveUDPPacket(reqCtx, (*bufPtr)[:n], conn, sess)
s.udpPool.Put(bufPtr)
})

View File

@ -95,6 +95,9 @@ type ServerHTTPS struct {
// quicListener is a listener that we use to serve DoH3 requests.
quicListener *quic.EarlyListener
// quicTransport is saved here to close it later.
quicTransport *quic.Transport
conf ConfigHTTPS
}
@ -264,21 +267,33 @@ func (s *ServerHTTPS) shutdown(ctx context.Context) (err error) {
}
// Finally, shutdown the HTTP/3 server.
if s.h3Server != nil {
err = s.quicListener.Close()
s.shutdownH3()
return nil
}
// shutdownH3 shuts down the HTTP/3 server, if enabled, and logs all errors.
func (s *ServerHTTPS) shutdownH3() {
if s.h3Server == nil {
return
}
err := s.quicListener.Close()
if err != nil {
log.Debug("[%s]: quic listener shutdown: %v", s.Name(), err)
log.Debug("[%s]: quic listener shutdown: %s", s.Name(), err)
}
err = s.quicTransport.Close()
if err != nil {
log.Debug("[%s]: quic transport shutdown: %s", s.Name(), err)
}
err = s.h3Server.Close()
if err != nil {
log.Debug("[%s]: http/3 server shutdown: %v", s.Name(), err)
log.Debug("[%s]: http/3 server shutdown: %s", s.Name(), err)
}
}
return nil
}
// serveHTTPS is launched in a worker goroutine and serves HTTP/1.1 and HTTP/2
// requests.
func (s *ServerHTTPS) serveHTTPS(ctx context.Context, hs *http.Server, l net.Listener) {
@ -523,16 +538,23 @@ func (s *ServerHTTPS) listenQUIC(ctx context.Context) (err error) {
conn, err := s.listenConfig.ListenPacket(ctx, "udp", s.addr)
if err != nil {
return err
return fmt.Errorf("listening udp for quic: %w", err)
}
qConf := newServerQUICConfig(s.metrics, s.conf.QUICLimitsEnabled, s.conf.MaxStreamsPerPeer)
ql, err := quic.ListenEarly(conn, tlsConf, qConf)
v := newQUICAddrValidator(quicAddrValidatorCacheSize, s.metrics, quicAddrValidatorCacheTTL)
transport := &quic.Transport{
Conn: conn,
VerifySourceAddress: v.requiresValidation,
}
qConf := newServerQUICConfig(s.conf.QUICLimitsEnabled, s.conf.MaxStreamsPerPeer)
ql, err := transport.ListenEarly(tlsConf, qConf)
if err != nil {
return err
return fmt.Errorf("listening quic: %w", err)
}
s.udpListener = conn
s.quicTransport = transport
s.quicListener = ql
return nil

View File

@ -104,7 +104,6 @@ func TestServerHTTPS_integration_serveRequests(t *testing.T) {
}}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()

Some files were not shown because too many files have changed in this diff Show More