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 ## AGDNS-1954 / Build 719
* The objects within `server_groups` array have a new property * 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 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: For server groups that do not require a block-page redirect, set:
```yaml ```yaml

View File

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

View File

@ -196,33 +196,44 @@ web:
certificates: certificates:
- certificate: './test/cert.crt' - certificate: './test/cert.crt'
key: './test/cert.key' 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 # served on these addresses. The addresses should be the same as in the
# safe_browsing object. # general_blocking and safe_browsing objects.
safe_browsing: adult_blocking:
bind: bind:
- address: '127.0.0.1:9081' - address: '127.0.0.1:9081'
- address: '127.0.0.1:9444' - address: '127.0.0.1:9444'
certificates: certificates:
- certificate: './test/cert.crt' - certificate: './test/cert.crt'
key: './test/cert.key' key: './test/cert.key'
block_page: './test/block_page_sb.html' block_page: './test/block_page_adult.html'
# Optional adult blocking web server configuration. static_content is not # Optional general blocking web server configuration. static_content is not
# served on these addresses. The addresses should be the same as in the # served on these addresses. The addresses should be the same as in the
# adult_blocking object. # adult_blocking and safe_browsing objects.
adult_blocking: general_blocking:
bind: bind:
- address: '127.0.0.1:9082' - address: '127.0.0.1:9082'
- address: '127.0.0.1:9445' - address: '127.0.0.1:9445'
certificates: certificates:
- certificate: './test/cert.crt' - certificate: './test/cert.crt'
key: './test/cert.key' 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 # Listen addresses for the web service in addition to the ones in the
# DNS-over-HTTPS handlers. # DNS-over-HTTPS handlers.
non_doh_bind: non_doh_bind:
- address: '127.0.0.1:9083' - address: '127.0.0.1:9084'
- address: '127.0.0.1:9446' - address: '127.0.0.1:9447'
certificates: certificates:
- certificate: './test/cert.crt' - certificate: './test/cert.crt'
key: './test/cert.key' key: './test/cert.key'
@ -253,6 +264,7 @@ safe_browsing:
cache_size: 1024 cache_size: 1024
cache_ttl: 1h cache_ttl: 1h
refresh_interval: 1h refresh_interval: 1h
refresh_timeout: 1m
# AdGuard adult content blocking filter configuration. # AdGuard adult content blocking filter configuration.
adult_blocking: adult_blocking:
@ -260,8 +272,12 @@ adult_blocking:
cache_size: 1024 cache_size: 1024
cache_ttl: 1h cache_ttl: 1h
refresh_interval: 1h refresh_interval: 1h
refresh_timeout: 1m
# Settings for rule-list-based filters. # 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: filters:
# The TTL to set for responses to requests for filtered domains. # The TTL to set for responses to requests for filtered domains.
response_ttl: 5m response_ttl: 5m
@ -273,9 +289,17 @@ filters:
# How often to update filters from the index. See the documentation for the # How often to update filters from the index. See the documentation for the
# FILTER_INDEX_URL environment variable. # FILTER_INDEX_URL environment variable.
refresh_interval: 1h refresh_interval: 1h
# The timeout for the entire filter update operation. Be aware that each # The timeout for the entire filter update operation. Note that filter
# individual refresh operation also has its own hardcoded 3m timeout. # 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 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. # MaxSize is the maximum size of the downloadable filtering rule-list.
max_size: 256MB max_size: 256MB
# Rule list cache. # Rule list cache.
@ -351,38 +375,6 @@ server_groups:
- name: 'adguard_dns_default' - name: 'adguard_dns_default'
# This filtering_group is used for all anonymous clients. # This filtering_group is used for all anonymous clients.
filtering_group: 'default' 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: ddr:
enabled: true enabled: true
# Device ID domain name suffix to DDR record template mapping. Keep in # 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) * [Filtering groups](#filtering_groups)
* [Network interface listeners](#interface_listeners) * [Network interface listeners](#interface_listeners)
* [Server groups](#server_groups) * [Server groups](#server_groups)
* [Block-page redirecting](#server_groups-*-block_page_redirect)
* [DDR](#server_groups-*-ddr) * [DDR](#server_groups-*-ddr)
* [TLS](#server_groups-*-tls) * [TLS](#server_groups-*-tls)
* [Servers](#server_groups-*-servers-*) * [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>: * <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 The optional safe browsing block-page web server configurations. Every
responded with the content from the file to which the `block_page` property request is responded with the content from the file to which the
points. `block_page` property points.
See the [full description of this API][http-block-pages] on the HTTP API See the [full description of this API][http-block-pages] on the HTTP API
page. 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>: * <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 values is the same as in the [`safe_browsing`](#web-safe_browsing) object
above. 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>: * <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 The optional listen addresses and optional TLS configuration for the web
service in addition to the ones in the DNS-over-HTTPS handlers. The 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. Inside of the `headers` map, the header `Content-Type` is required.
> [!NOTE]
> Paths are case-sensitive.
**Property example:** **Property example:**
```yaml ```yaml
@ -735,6 +742,11 @@ The `safe_browsing` object has the following properties:
**Example:** `1m`. **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> ## <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> ## <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: The `filters` object has the following properties:
* <a href="#filters-response_ttl" id="filters-response_ttl" name="filters-response_ttl">`response_ttl`</a>: * <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>: * <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 The timeout for the *entire* filter update operation, as a human-readable
duration. Be aware that each individual refresh operation also has its own duration. Note that filter rule-list index and each filter rule-list
hardcoded 3m timeout. 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`. **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>: * <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 The maximum size of the downloadable content for a rule-list in a
human-readable format. human-readable format.
@ -939,9 +972,6 @@ The items of the `server_groups` array have the following properties:
**Example:** `default`. **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). * `ddr`: The DDR configuration object. See [below](#server_groups-*-ddr).
* `tls`: The TLS configuration object. See [below](#server_groups-*-tls). * `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> ### <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 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. 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. 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: You'll need to supply the following:
* [`ADULT_BLOCKING_URL`](#env-ADULT_BLOCKING_URL) * [`ADULT_BLOCKING_URL`][env-ADULT_BLOCKING_URL]
* [`BILLSTAT_URL`](#env-BILLSTAT_URL) * [`BILLSTAT_URL`][env-BILLSTAT_URL]
* [`CONSUL_ALLOWLIST_URL`](#env-CONSUL_ALLOWLIST_URL) * [`CONSUL_ALLOWLIST_URL`][env-CONSUL_ALLOWLIST_URL]
* [`GENERAL_SAFE_SEARCH_URL`](#env-GENERAL_SAFE_SEARCH_URL) * [`GENERAL_SAFE_SEARCH_URL`][env-GENERAL_SAFE_SEARCH_URL]
* [`LINKED_IP_TARGET_URL`](#env-LINKED_IP_TARGET_URL) * [`LINKED_IP_TARGET_URL`][env-LINKED_IP_TARGET_URL]
* [`NEW_REG_DOMAINS_URL`](#env-NEW_REG_DOMAINS_URL) * [`NEW_REG_DOMAINS_URL`][env-NEW_REG_DOMAINS_URL]
* [`PROFILES_URL`](#env-PROFILES_URL) * [`PROFILES_URL`][env-PROFILES_URL]
* [`SAFE_BROWSING_URL`](#env-SAFE_BROWSING_URL) * [`SAFE_BROWSING_URL`][env-SAFE_BROWSING_URL]
* [`YOUTUBE_SAFE_SEARCH_URL`](#env-YOUTUBE_SAFE_SEARCH_URL) * [`YOUTUBE_SAFE_SEARCH_URL`][env-YOUTUBE_SAFE_SEARCH_URL]
See the [external HTTP API documentation][externalhttp]. See the [external HTTP API documentation][externalhttp].
@ -280,6 +280,16 @@ env \
./AdGuardDNS ./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 [externalhttp]: externalhttp.md

View File

@ -26,8 +26,6 @@ sensitive configuration. All other configuration is stored in the
* [`PROFILES_ENABLED`](#PROFILES_ENABLED) * [`PROFILES_ENABLED`](#PROFILES_ENABLED)
* [`PROFILES_URL`](#PROFILES_URL) * [`PROFILES_URL`](#PROFILES_URL)
* [`QUERYLOG_PATH`](#QUERYLOG_PATH) * [`QUERYLOG_PATH`](#QUERYLOG_PATH)
* [`RESEARCH_LOGS`](#RESEARCH_LOGS)
* [`RESEARCH_METRICS`](#RESEARCH_METRICS)
* [`RULESTAT_URL`](#RULESTAT_URL) * [`RULESTAT_URL`](#RULESTAT_URL)
* [`SAFE_BROWSING_URL`](#SAFE_BROWSING_URL) * [`SAFE_BROWSING_URL`](#SAFE_BROWSING_URL)
* [`SENTRY_DSN`](#SENTRY_DSN) * [`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> ## <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 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> ## <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 safe-browsing, adult-blocking, and popup-blocking servers. Every request is
the content from the configured file, with the exception of `GET /favicon.ico` responded with the content from the configured file, with the exception of `GET
and `GET /robots.txt` requests, which are handled separately: /favicon.ico` and `GET /robots.txt` requests, which are handled separately:
* `GET /favicon.ico` requests are responded with a plain-text `404 Not Found` * `GET /favicon.ico` requests are responded with a plain-text `404 Not Found`
response. response.

View File

@ -5,8 +5,8 @@ entries are designed to be concise and easily compressable. An example of the
log output: log output:
```jsonl ```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":"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,"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 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>: * <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”. 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"` **Example:** `"ABCD1234"`
* <a href="#properties-b" id="properties-b" name="properties-b">`b`</a>: * <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. 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>: * <a href="#properties-f" id="properties-f" name="properties-f">`f`</a>:
The action taken with this request. The short name `f` stands for The action taken with this request. The short name `f` stands for
“filtering”. The possible values are: “filtering”. The possible values are:

48
go.mod
View File

@ -1,13 +1,13 @@
module github.com/AdguardTeam/AdGuardDNS module github.com/AdguardTeam/AdGuardDNS
go 1.21.8 go 1.22.4
require ( require (
github.com/AdguardTeam/AdGuardDNS/internal/dnsserver v0.0.0-00010101000000-000000000000 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/AdguardTeam/urlfilter v0.18.0
github.com/ameshkov/dnscrypt/v2 v2.2.7 github.com/ameshkov/dnscrypt/v2 v2.3.0
github.com/axiomhq/hyperloglog v0.0.0-20240124082744-24bca3a5b39b github.com/axiomhq/hyperloglog v0.0.0-20240319100328-84253e514e02
github.com/bluele/gcache v0.0.2 github.com/bluele/gcache v0.0.2
github.com/c2h5oh/datasize v0.0.0-20231215233829-aa82cc1e6500 github.com/c2h5oh/datasize v0.0.0-20231215233829-aa82cc1e6500
github.com/caarlos0/env/v7 v7.1.0 github.com/caarlos0/env/v7 v7.1.0
@ -16,17 +16,18 @@ require (
github.com/miekg/dns v1.1.58 github.com/miekg/dns v1.1.58
github.com/oschwald/maxminddb-golang v1.12.0 github.com/oschwald/maxminddb-golang v1.12.0
github.com/patrickmn/go-cache v2.1.1-0.20191004192108-46f407853014+incompatible github.com/patrickmn/go-cache v2.1.1-0.20191004192108-46f407853014+incompatible
github.com/prometheus/client_golang v1.18.0 github.com/prometheus/client_golang v1.19.0
github.com/prometheus/client_model v0.5.0 github.com/prometheus/client_model v0.6.1
github.com/prometheus/common v0.46.0 github.com/prometheus/common v0.52.3
github.com/quic-go/quic-go v0.41.0 github.com/quic-go/quic-go v0.42.0
github.com/stretchr/testify v1.8.4 github.com/stretchr/testify v1.9.0
golang.org/x/exp v0.0.0-20240213143201-ec583247a57a golang.org/x/crypto v0.22.0
golang.org/x/net v0.21.0 golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8
golang.org/x/sys v0.17.0 golang.org/x/net v0.24.0
golang.org/x/sys v0.19.0
golang.org/x/time v0.5.0 golang.org/x/time v0.5.0
google.golang.org/grpc v1.61.1 google.golang.org/grpc v1.63.2
google.golang.org/protobuf v1.32.0 google.golang.org/protobuf v1.33.0
gopkg.in/yaml.v2 v2.4.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/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 // indirect
github.com/ameshkov/dnsstamps v1.0.3 // indirect github.com/ameshkov/dnsstamps v1.0.3 // indirect
github.com/beorn7/perks v1.0.1 // 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/davecgh/go-spew v1.1.1 // indirect
github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 // 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/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-20240409012703-83162a5b38cd // indirect
github.com/google/pprof v0.0.0-20240207164012-fb44976bdcd5 // indirect github.com/onsi/ginkgo/v2 v2.17.1 // indirect
github.com/onsi/ginkgo/v2 v2.15.0 // indirect github.com/panjf2000/ants/v2 v2.9.1 // indirect
github.com/panjf2000/ants/v2 v2.9.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // 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 github.com/quic-go/qpack v0.4.0 // indirect
go.uber.org/mock 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.17.0 // indirect
golang.org/x/mod v0.15.0 // indirect golang.org/x/sync v0.7.0 // indirect
golang.org/x/text v0.14.0 // indirect golang.org/x/text v0.14.0 // indirect
golang.org/x/tools v0.18.0 // indirect golang.org/x/tools v0.20.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240213162025-012b6fc9bca9 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240412170617-26222e5d3d56 // indirect
gopkg.in/yaml.v3 v3.0.1 // 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.23.2 h1:rMjYantwtQ39e8G4zBQ6ZLlm4s3XH30Bc9VxhoOHwao=
github.com/AdguardTeam/golibs v0.20.1/go.mod h1:bgcMgRviCKyU6mkrX+RtT/OsKPFzyppelfRsksMG3KU= 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 h1:ZZzwODC/ADpjJSODxySrrUnt/fvOCfGFaCW6j+wsGfQ=
github.com/AdguardTeam/urlfilter v0.18.0/go.mod h1:IXxBwedLiZA2viyHkaFxY/8mjub0li2PXRg8a3d9Z1s= 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 h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY=
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA= 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 h1:52m0LGchQBBVqJRyYYufQuIbVqRawmubW3OFGqK1ekw=
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635/go.mod h1:lmLxL+FV291OopO93Bwf9fQLQeLyt33VJRUg5VJ30us= github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635/go.mod h1:lmLxL+FV291OopO93Bwf9fQLQeLyt33VJRUg5VJ30us=
github.com/ameshkov/dnscrypt/v2 v2.2.7 h1:aEitLIR8HcxVodZ79mgRcCiC0A0I5kZPBuWGFwwulAw= github.com/ameshkov/dnscrypt/v2 v2.3.0 h1:pDXDF7eFa6Lw+04C0hoMh8kCAQM8NwUdFEllSP2zNLs=
github.com/ameshkov/dnscrypt/v2 v2.2.7/go.mod h1:qPWhwz6FdSmuK7W4sMyvogrez4MWdtzosdqlr0Rg3ow= 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 h1:Srzik+J9mivH1alRACTbys2xOxs0lRH9qnTA7Y1OYVo=
github.com/ameshkov/dnsstamps v1.0.3/go.mod h1:Ii3eUu73dx4Vw5O4wjzmT5+lkCwovjzaEZZ4gKyIH5A= 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-20240319100328-84253e514e02 h1:bXAPYSbdYbS5VTy92NIUbeDI1qyggi+JYh5op9IFlcQ=
github.com/axiomhq/hyperloglog v0.0.0-20240124082744-24bca3a5b39b/go.mod h1:k08r+Yj1PRAmuayFiRK6MYuR5Ve4IuZtTfxErMIh0+c= 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 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 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 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/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 h1:9lzTF5amyQeWHZzuZeKlCb5FWSUxpG1js43mhbY8ozg=
github.com/caarlos0/env/v7 v7.1.0/go.mod h1:LPPWniDUq4JaO6Q41vtlyikhMknqymCLBw0eX4dcH1E= 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.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 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.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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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-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 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= 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.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
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/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 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/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-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo=
github.com/google/pprof v0.0.0-20240207164012-fb44976bdcd5/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= 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 h1:UifI23ZTGY8Tt29JbYFiuyIU3eX+RNFtUwefq9qAhxg=
github.com/google/renameio/v2 v2.0.0/go.mod h1:BtmJXm5YlszgC+TD4HOEEUFgkJP3nLxehU6hfe7jRt4= github.com/google/renameio/v2 v2.0.0/go.mod h1:BtmJXm5YlszgC+TD4HOEEUFgkJP3nLxehU6hfe7jRt4=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 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/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 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4=
github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY= 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.17.1 h1:V++EzdbhI4ZV4ev0UTIj0PzhzOcReJFyJaLjtSF55M8=
github.com/onsi/ginkgo/v2 v2.15.0/go.mod h1:HlxMHtYF57y6Dpf+mc5529KKmSq9h2FpCF+/ZkwUxKM= 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 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8=
github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= 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 h1:9FnTOD0YOhP7DGxGsq4glzpGy5+w7pq50AS6wALUMYs=
github.com/oschwald/maxminddb-golang v1.12.0/go.mod h1:q0Nob5lTCqyQ8WT6FYgS1L7PXKVVbgiymefNwIjPzgY= 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.1 h1:Q5vh5xohbsZXGcD6hhszzGqB7jSSc2/CRr3QKIga8Kw=
github.com/panjf2000/ants/v2 v2.9.0/go.mod h1:7ZxyxsqE4vvW0M7LSD8aI3cKwgFhBHbxnlN8mDqHa1I= 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 h1:IWzUvJ72xMjmrjR9q3H1PF+jwdN0uNQiR2t1BLNalyo=
github.com/patrickmn/go-cache v2.1.1-0.20191004192108-46f407853014+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= 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= 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/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 h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= 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.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU=
github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA= github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k=
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/common v0.46.0 h1:doXzt5ybi1HBKpsZOL0sSkaNHJJqkyfEWZGGqqScV0Y= github.com/prometheus/common v0.52.3 h1:5f8uj6ZwHSscOGNdIQg6OiZv/ybiK2CO2q2drVZAQSA=
github.com/prometheus/common v0.46.0/go.mod h1:Tp0qkxpb9Jsg54QMe+EAmqXkSV7Evdy1BTn+g2pa/hQ= github.com/prometheus/common v0.52.3/go.mod h1:BrxBKv3FWBIGXw89Mg1AeBq7FSyRzXWI3l3e7W3RN5U=
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= github.com/prometheus/procfs v0.13.0 h1:GqzLlQyfsPbaEHaQkO7tbDlriv/4o5Hudv6OXHGKX7o=
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= 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 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= 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.42.0 h1:uSfdap0eveIl8KXnipv9K7nlwZ5IqLlYOpJ58u5utpM=
github.com/quic-go/quic-go v0.41.0/go.mod h1:qCkNjqczPEvgsOnxZ0eCD14lv+B2LHlFAB++CNOh9hA= 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 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= 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= 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.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.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.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 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 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM=
github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI= github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI=
github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms= 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= 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 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= 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.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
golang.org/x/exp v0.0.0-20240213143201-ec583247a57a h1:HinSgX1tJRX3KsL//Gxynpw5CTOAIPhgL4W8PNiIpVE= golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8 h1:ESSUROHIBHg7USnszlcdmjBEwdMj9VUvU+OPk4yl2mc=
golang.org/x/exp v0.0.0-20240213143201-ec583247a57a/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI=
golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= 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.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.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 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 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 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 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= 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.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY=
golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg= golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/genproto/googleapis/rpc v0.0.0-20240412170617-26222e5d3d56 h1:zviK8GX4VlMstrK3JkexM5UHjH1VOkRebH9y3jhSBGk=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240213162025-012b6fc9bca9 h1:hZB7eLIaYlW9qXRfCq/qDaPdbeY3757uARz5Vvfv+cY= google.golang.org/genproto/googleapis/rpc v0.0.0-20240412170617-26222e5d3d56/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:YUWgXUFRPfoYK1IHMuxH5K6nPEXSCzIMljnQ59lLRCk= google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM=
google.golang.org/grpc v1.61.1 h1:kLAiWrZs7YeDM6MumDe7m3y4aM6wacLzM1Y/wiLP9XY= google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
google.golang.org/grpc v1.61.1/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
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=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 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 h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 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 ( 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/bigquery v1.8.0 h1:PQcPefKFdaIzjQFbiyOgAqyx8q5djaE7x9Sqe712DPA=
cloud.google.com/go/compute v1.21.0 h1:JNBsyXVoOoNJtTQcnEY5uYpZIbeCTYIeDe0Xh1bySMk= 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.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.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.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 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= 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.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 h1:H0aULhgmSzN8xQ3nX1uxtdlTHYoPLu5AhHxWrKI6ocU=
github.com/alecthomas/kingpin/v2 v2.3.2/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE= 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/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/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d h1:UQZhZ2O0vMHr2cI+DC1Mbh0TJxzA3RcLoMsFw+aXw7E= 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/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 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-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-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 h1:sDMmm+q/3+BukdIpxwO365v/Rbspp2Nt5XntgQRXq8Q=
github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= 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= 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.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 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.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 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 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.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 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= 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= 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 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 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE=
github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= 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.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/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 h1:2hRPrmiwPrp3fQX967rNJIhQPtiGXdlQWAxKbKw3VHA=
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= 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/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 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 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.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 h1:j0GKcs05QVmm7yesiZq2+9cxHkNK9YM6zKx4D2qucQU=
github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= 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= 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-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 h1:BA4a7pe6ZTd9F8kXETBoijjFJ/ntaa//1wiH9BZu4zU=
github.com/ianlancetaylor/demangle v0.0.0-20230524184225-eabc099b10ab/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw= 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/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 h1:WoYdfyJFfZIUgqNAeOyRfTNQZOksSlZ6+FnXR3AEpX0=
github.com/iris-contrib/jade v1.1.4/go.mod h1:EDqR+ur9piDl6DUgs6qRrlfzmlx/D5UybogqrXvJTBE= 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 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 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 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/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 h1:SIYunPjnlXcW+gVfvm0IlSeR5U3WZUOLfVmqg85Go44=
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= 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.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4= 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.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.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 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.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.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= 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-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-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-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-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-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-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/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-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/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.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= 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.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-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@ -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.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE=
golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8= 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.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.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 h1:xYq6+9AtI+xP3M4r0N1hCkHrInHDBohhquRgx9Kk6gI=
golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= 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= 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.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.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.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-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.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw= golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw=
golang.org/x/term v0.3.0 h1:qoo4akIqOcDME5bhc/NgxUdovd6BSS2uMsVjB56q1xI= 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 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= 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.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.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.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 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.12.1-0.20230815132531-74c255bcf846/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= 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 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-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 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= 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.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc=
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= 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.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/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-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/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-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 h1:SeZZZx0cP0fqUyA+oRzP9k7cSwJlvDFiROO72uwD6i0=
google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97/go.mod h1:t1VqOqqvce95G3hIDCT5FeO3YUc6Q4Oe24L/+rNMxRk= 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-20240125205218-1f4bbc51befe h1:USL2DhxfgRchafRvt/wYyyQNzwgL7ZiURcozOE/Pkvo=
google.golang.org/genproto v0.0.0-20240205150955-31a09d347014/go.mod h1:xEgQu1e4stdSSsxPDK8Azkrk/ECl5HvdPf6nbZrTS5M= 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 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-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-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.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= 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= 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 { for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
t.Parallel() t.Parallel()
@ -90,7 +89,7 @@ func TestBlockedHostEngine_IsBlocked_concurrent(t *testing.T) {
engine := newBlockedHostEngine(rules) engine := newBlockedHostEngine(rules)
wg := &sync.WaitGroup{} wg := &sync.WaitGroup{}
for i := 0; i < routinesLimit; i++ { for i := range routinesLimit {
wg.Add(1) wg.Add(1)
host := fmt.Sprintf("%d.%s", i, "block.test") host := fmt.Sprintf("%d.%s", i, "block.test")

View File

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

View File

@ -5,6 +5,7 @@ import (
"net/netip" "net/netip"
"unicode/utf8" "unicode/utf8"
"github.com/AdguardTeam/AdGuardDNS/internal/agdpasswd"
"github.com/AdguardTeam/golibs/errors" "github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/netutil" "github.com/AdguardTeam/golibs/netutil"
) )
@ -16,6 +17,9 @@ import (
// NOTE: Do not change fields of this structure without incrementing // NOTE: Do not change fields of this structure without incrementing
// [internal/profiledb/internal.FileCacheVersion]. // [internal/profiledb/internal.FileCacheVersion].
type Device struct { 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 is the unique ID of the device.
ID DeviceID ID DeviceID
@ -104,3 +108,20 @@ func NewDeviceName(s string) (n DeviceName, err error) {
return DeviceName(s), nil 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 { for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
t.Parallel() t.Parallel()

View File

@ -17,33 +17,41 @@ const (
// FilterListIDNone means that no filter were applied at all. // FilterListIDNone means that no filter were applied at all.
FilterListIDNone FilterListID = "" 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. // request was blocked by the service blocker.
FilterListIDBlockedService FilterListID = "blocked_service" 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. // a request was filtered by a custom profile rule.
FilterListIDCustom FilterListID = "custom" 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. // a request was filtered by the adult content blocking filter.
FilterListIDAdultBlocking FilterListID = "adult_blocking" 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. // a request was filtered by the safe browsing filter.
FilterListIDSafeBrowsing FilterListID = "safe_browsing" 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. // a request was filtered by the newly registered domains filter.
FilterListIDNewRegDomains FilterListID = "newly_registered_domains" 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. // a request was modified by the general safe search filter.
FilterListIDGeneralSafeSearch FilterListID = "general_safe_search" 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. // when a request was modified by the YouTube safe search filter.
FilterListIDYoutubeSafeSearch FilterListID = "youtube_safe_search" 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. // 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 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. // FilterRuleText is the text of a single rule within a filter.
type FilterRuleText string type FilterRuleText string

View File

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

View File

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

View File

@ -2,23 +2,14 @@ package agd
import ( import (
"crypto/tls" "crypto/tls"
"fmt"
"math"
"net/netip"
"github.com/AdguardTeam/golibs/stringutil" "github.com/AdguardTeam/golibs/container"
"github.com/miekg/dns" "github.com/miekg/dns"
) )
// ServerGroup is a group of DNS servers all of which use the same filtering // ServerGroup is a group of DNS servers all of which use the same filtering
// settings. // settings.
type ServerGroup struct { 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 // DDR is the configuration for the server group's Discovery Of Designated
// Resolvers (DDR) handlers. DDR is never nil. // Resolvers (DDR) handlers. DDR is never nil.
DDR *DDR DDR *DDR
@ -60,11 +51,11 @@ type TLS struct {
type DDR struct { type DDR struct {
// DeviceTargets is the set of all domain names, subdomains of which should // DeviceTargets is the set of all domain names, subdomains of which should
// be checked for DDR queries with device IDs. // 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 // PublicTargets is the set of all public domain names, DDR queries for
// which should be processed. // which should be processed.
PublicTargets *stringutil.Set PublicTargets *container.MapSet[string]
// DeviceRecordTemplates are used to respond to DDR queries from recognized // DeviceRecordTemplates are used to respond to DDR queries from recognized
// devices. // devices.
@ -78,81 +69,3 @@ type DDR struct {
// name queries receive an NXDOMAIN response. // name queries receive an NXDOMAIN response.
Enabled bool 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. // HTTP header value constants.
const ( const (
HdrValApplicationJSON = "application/json" HdrValApplicationJSON = "application/json"
HdrValGzip = "gzip"
HdrValTextCSV = "text/csv" HdrValTextCSV = "text/csv"
HdrValTextHTML = "text/html" HdrValTextHTML = "text/html"
HdrValTextPlain = "text/plain" 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 // When err is nil, resp always contains a non-nil resp.Body. Caller should
// close resp.Body when done reading from it. // 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) { func (c *Client) Get(ctx context.Context, u *url.URL) (resp *http.Response, err error) {
return c.do(ctx, http.MethodGet, u, "", nil) 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 // When err is nil, resp always contains a non-nil resp.Body. Caller should
// close resp.Body when done reading from it. // close resp.Body when done reading from it.
//
// See also go doc http.Client.Post.
func (c *Client) Post( func (c *Client) Post(
ctx context.Context, ctx context.Context,
u *url.URL, u *url.URL,
@ -59,7 +55,7 @@ func (c *Client) Post(
return c.do(ctx, http.MethodPost, u, contentType, body) 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 // When err is nil, resp always contains a non-nil resp.Body. Caller should
// close resp.Body when done reading from it. // 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) 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( func (c *Client) do(
ctx context.Context, ctx context.Context,
method string, method string,
@ -98,7 +94,8 @@ func (c *Client) do(
resp, err = c.http.Do(req) resp, err = c.http.Do(req)
if err != nil && resp != nil && resp.Header != nil { 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) 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. // Refresher is the interface for entities that can update themselves.
type Refresher interface { 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) Refresh(ctx context.Context) (err error)
} }
@ -21,11 +25,9 @@ type Refresher interface {
type RefreshWorker struct { type RefreshWorker struct {
done chan unit done chan unit
context func() (ctx context.Context, cancel context.CancelFunc) context func() (ctx context.Context, cancel context.CancelFunc)
logRoutine func(format string, args ...any)
tick *time.Ticker tick *time.Ticker
rand *rand.Rand rand *rand.Rand
refr Refresher refr Refresher
errColl errcoll.Interface
name string name string
maxStartSleep time.Duration maxStartSleep time.Duration
@ -40,12 +42,6 @@ type RefreshWorkerConfig struct {
// Refresher is the entity being refreshed. // Refresher is the entity being refreshed.
Refresher Refresher 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 // Name is the name of this worker. It is used for logging and error
// collecting. // collecting.
// //
@ -64,12 +60,6 @@ type RefreshWorkerConfig struct {
// that should persist to disk or remote storage before shutting down. // that should persist to disk or remote storage before shutting down.
RefreshOnShutdown bool 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 // 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 // refresh. The duration of the sleep is a random duration of up to 10 % of
// Interval. // Interval.
@ -82,14 +72,6 @@ type RefreshWorkerConfig struct {
// NewRefreshWorker returns a new valid *RefreshWorker with the provided // NewRefreshWorker returns a new valid *RefreshWorker with the provided
// parameters. c must not be nil. // parameters. c must not be nil.
func NewRefreshWorker(c *RefreshWorkerConfig) (w *RefreshWorker) { 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 maxStartSleep time.Duration
var rng *rand.Rand var rng *rand.Rand
if c.RandomizeStart { if c.RandomizeStart {
@ -100,11 +82,9 @@ func NewRefreshWorker(c *RefreshWorkerConfig) (w *RefreshWorker) {
return &RefreshWorker{ return &RefreshWorker{
done: make(chan unit), done: make(chan unit),
context: c.Context, context: c.Context,
logRoutine: logRoutine,
tick: time.NewTicker(c.Interval), tick: time.NewTicker(c.Interval),
rand: rng, rand: rng,
refr: c.Refresher, refr: c.Refresher,
errColl: c.ErrColl,
name: c.Name, name: c.Name,
maxStartSleep: maxStartSleep, maxStartSleep: maxStartSleep,
refrOnShutdown: c.RefreshOnShutdown, refrOnShutdown: c.RefreshOnShutdown,
@ -172,7 +152,7 @@ func (w *RefreshWorker) sleepRandom() (shouldRefresh bool) {
} }
sleepDur := time.Duration(w.rand.Int63n(int64(w.maxStartSleep))) 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) timer := time.NewTimer(sleepDur)
defer func() { defer func() {
@ -197,24 +177,51 @@ func (w *RefreshWorker) sleepRandom() (shouldRefresh bool) {
// refresh refreshes the entity and logs the status of the refresh. // refresh refreshes the entity and logs the status of the refresh.
func (w *RefreshWorker) 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 // TODO(a.garipov): Consider adding a helper for enriching errors with
// context deadline data without duplication. See an example in method // context deadline data without duplication. See an example in method
// filter.refreshableFilter.refresh. // filter.refreshableFilter.refresh.
ctx, cancel := w.context() ctx, cancel := w.context()
defer cancel() defer cancel()
log.Debug("worker %q: starting refresh", name) _ = w.refr.Refresh(ctx)
err := w.refr.Refresh(ctx) }
log.Debug("worker %q: finished refresh", name)
// 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 { if err != nil {
errcoll.Collectf(ctx, w.errColl, "%s: %w", name, err) err = fmt.Errorf("%s: %w", r.prefix, err)
r.log("%s", err)
return r.errColl.Collect(ctx, err)
} }
w.logRoutine("worker %q: refreshed successfully", name) return err
} }

View File

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

View File

@ -3,8 +3,6 @@
package agdtest package agdtest
import ( import (
"context"
"testing"
"time" "time"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg" "github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
@ -27,16 +25,3 @@ func NewConstructor() (c *dnsmsg.Constructor) {
func NewCloner() (c *dnsmsg.Cloner) { func NewCloner() (c *dnsmsg.Cloner) {
return dnsmsg.NewCloner(dnsmsg.EmptyClonerStat{}) 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/access"
"github.com/AdguardTeam/AdGuardDNS/internal/agd" "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/agdservice"
"github.com/AdguardTeam/AdGuardDNS/internal/billstat" "github.com/AdguardTeam/AdGuardDNS/internal/billstat"
"github.com/AdguardTeam/AdGuardDNS/internal/dnscheck" "github.com/AdguardTeam/AdGuardDNS/internal/dnscheck"
@ -52,27 +52,20 @@ func (a *AccessManager) IsBlockedIP(ip netip.Addr) (blocked bool) {
return a.OnIsBlockedIP(ip) return a.OnIsBlockedIP(ip)
} }
// Package agdnet // Package agdpasswd
// type check // type check
var _ agdnet.Resolver = (*Resolver)(nil) var _ agdpasswd.Authenticator = (*Authenticator)(nil)
// Resolver is an [agdnet.Resolver] for tests. // Authenticator is an [agdpasswd.Authenticator] for tests.
type Resolver struct { type Authenticator struct {
OnLookupNetIP func( OnAuthenticate func(ctx context.Context, passwd []byte) (ok bool)
ctx context.Context,
fam netutil.AddrFamily,
host string,
) (ips []netip.Addr, err error)
} }
// LookupNetIP implements the [agdnet.Resolver] interface for *Resolver. // Authenticate implements the [agdpasswd.Authenticator] interface for
func (r *Resolver) LookupNetIP( // *Authenticator.
ctx context.Context, func (a *Authenticator) Authenticate(ctx context.Context, passwd []byte) (ok bool) {
fam netutil.AddrFamily, return a.OnAuthenticate(ctx, passwd)
host string,
) (ips []netip.Addr, err error) {
return r.OnLookupNetIP(ctx, fam, host)
} }
// Package agdservice // Package agdservice

View File

@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.31.0 // protoc-gen-go v1.33.0
// protoc v4.25.1 // protoc v4.25.3
// source: backend.proto // source: backend.proto
package backendpb package backendpb
@ -359,11 +359,12 @@ type DeviceSettings struct {
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
FilteringEnabled bool `protobuf:"varint,3,opt,name=filtering_enabled,json=filteringEnabled,proto3" json:"filtering_enabled,omitempty"` 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"` 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"` 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() { func (x *DeviceSettings) Reset() {
@ -433,6 +434,13 @@ func (x *DeviceSettings) GetDedicatedIps() [][]byte {
return nil return nil
} }
func (x *DeviceSettings) GetAuthentication() *AuthenticationSettings {
if x != nil {
return x.Authentication
}
return nil
}
type ParentalSettings struct { type ParentalSettings struct {
state protoimpl.MessageState state protoimpl.MessageState
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
@ -1179,6 +1187,81 @@ func (x *CidrRange) GetPrefix() uint32 {
return 0 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 protoreflect.FileDescriptor
var file_backend_proto_rawDesc = []byte{ 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 0x65, 0x64, 0x49, 0x70, 0x73, 0x12, 0x3f, 0x0a, 0x0e, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74,
0x61, 0x6c, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e,
0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65,
0x62, 0x6c, 0x65, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x61, 0x64, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x0e, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69,
0x75, 0x6c, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x87, 0x02, 0x0a, 0x10, 0x50, 0x61, 0x72, 0x65, 0x6e,
0x41, 0x64, 0x75, 0x6c, 0x74, 0x12, 0x2e, 0x0a, 0x13, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x74, 0x61, 0x6c, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65,
0x5f, 0x73, 0x61, 0x66, 0x65, 0x5f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x18, 0x03, 0x20, 0x01, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e,
0x28, 0x08, 0x52, 0x11, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x53, 0x61, 0x66, 0x65, 0x53, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x61,
0x65, 0x61, 0x72, 0x63, 0x68, 0x12, 0x2e, 0x0a, 0x13, 0x79, 0x6f, 0x75, 0x74, 0x75, 0x62, 0x65, 0x64, 0x75, 0x6c, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x62, 0x6c, 0x6f, 0x63,
0x5f, 0x73, 0x61, 0x66, 0x65, 0x5f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x18, 0x04, 0x20, 0x01, 0x6b, 0x41, 0x64, 0x75, 0x6c, 0x74, 0x12, 0x2e, 0x0a, 0x13, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61,
0x28, 0x08, 0x52, 0x11, 0x79, 0x6f, 0x75, 0x74, 0x75, 0x62, 0x65, 0x53, 0x61, 0x66, 0x65, 0x53, 0x6c, 0x5f, 0x73, 0x61, 0x66, 0x65, 0x5f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x18, 0x03, 0x20,
0x65, 0x61, 0x72, 0x63, 0x68, 0x12, 0x29, 0x0a, 0x10, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x01, 0x28, 0x08, 0x52, 0x11, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x53, 0x61, 0x66, 0x65,
0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x12, 0x2e, 0x0a, 0x13, 0x79, 0x6f, 0x75, 0x74, 0x75, 0x62,
0x0f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x65, 0x5f, 0x73, 0x61, 0x66, 0x65, 0x5f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x18, 0x04, 0x20,
0x12, 0x2d, 0x0a, 0x08, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x06, 0x20, 0x01, 0x01, 0x28, 0x08, 0x52, 0x11, 0x79, 0x6f, 0x75, 0x74, 0x75, 0x62, 0x65, 0x53, 0x61, 0x66, 0x65,
0x28, 0x0b, 0x32, 0x11, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x53, 0x65, 0x74, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x12, 0x29, 0x0a, 0x10, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x65,
0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x08, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x22, 0x64, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09,
0x54, 0x0a, 0x10, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x52, 0x0f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
0x6e, 0x67, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x6d, 0x7a, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x73, 0x12, 0x2d, 0x0a, 0x08, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x06, 0x20,
0x52, 0x03, 0x74, 0x6d, 0x7a, 0x12, 0x2e, 0x0a, 0x0b, 0x77, 0x65, 0x65, 0x6b, 0x6c, 0x79, 0x52, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x53, 0x65,
0x61, 0x6e, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x57, 0x65, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x08, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65,
0x6b, 0x6c, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0b, 0x77, 0x65, 0x65, 0x6b, 0x6c, 0x79, 0x22, 0x54, 0x0a, 0x10, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x53, 0x65, 0x74, 0x74,
0x52, 0x61, 0x6e, 0x67, 0x65, 0x22, 0xd8, 0x01, 0x0a, 0x0b, 0x57, 0x65, 0x65, 0x6b, 0x6c, 0x79, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x6d, 0x7a, 0x18, 0x01, 0x20, 0x01, 0x28,
0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x1b, 0x0a, 0x03, 0x6d, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x09, 0x52, 0x03, 0x74, 0x6d, 0x7a, 0x12, 0x2e, 0x0a, 0x0b, 0x77, 0x65, 0x65, 0x6b, 0x6c, 0x79,
0x28, 0x0b, 0x32, 0x09, 0x2e, 0x44, 0x61, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x03, 0x6d, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x57, 0x65,
0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x03, 0x74, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x65, 0x6b, 0x6c, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0b, 0x77, 0x65, 0x65, 0x6b, 0x6c,
0x09, 0x2e, 0x44, 0x61, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x03, 0x74, 0x75, 0x65, 0x12, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x22, 0xd8, 0x01, 0x0a, 0x0b, 0x57, 0x65, 0x65, 0x6b, 0x6c,
0x1b, 0x0a, 0x03, 0x77, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x44, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x1b, 0x0a, 0x03, 0x6d, 0x6f, 0x6e, 0x18, 0x01, 0x20,
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, 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, 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, 0x73, 0x75, 0x6e, 0x32, 0x09, 0x2e, 0x44, 0x61, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x03, 0x74, 0x75, 0x65,
0x22, 0x68, 0x0a, 0x08, 0x44, 0x61, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x2f, 0x0a, 0x05, 0x12, 0x1b, 0x0a, 0x03, 0x77, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e,
0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 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, 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, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x22, 0x3f, 0x0a, 0x11, 0x52,
0x03, 0x65, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x75, 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x73, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73,
0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x22, 0x3f, 0x0a, 0x11, 0x52, 0x75, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x69, 0x64,
0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x73, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x03, 0x69, 0x64, 0x73, 0x22, 0x3e, 0x0a, 0x14,
0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x4d, 0x6f, 0x64, 0x65, 0x43, 0x75, 0x73, 0x74,
0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x69, 0x64, 0x73, 0x6f, 0x6d, 0x49, 0x50, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x70, 0x76, 0x34, 0x18, 0x01, 0x20, 0x01,
0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x03, 0x69, 0x64, 0x73, 0x22, 0x3e, 0x0a, 0x14, 0x42, 0x28, 0x0c, 0x52, 0x04, 0x69, 0x70, 0x76, 0x34, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x70, 0x76, 0x36,
0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x4d, 0x6f, 0x64, 0x65, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x69, 0x70, 0x76, 0x36, 0x22, 0x16, 0x0a, 0x14,
0x6d, 0x49, 0x50, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x70, 0x76, 0x34, 0x18, 0x01, 0x20, 0x01, 0x28, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x4d, 0x6f, 0x64, 0x65, 0x4e, 0x58, 0x44, 0x4f,
0x0c, 0x52, 0x04, 0x69, 0x70, 0x76, 0x34, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x70, 0x76, 0x36, 0x18, 0x4d, 0x41, 0x49, 0x4e, 0x22, 0x14, 0x0a, 0x12, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67,
0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x69, 0x70, 0x76, 0x36, 0x22, 0x16, 0x0a, 0x14, 0x42, 0x4d, 0x6f, 0x64, 0x65, 0x4e, 0x75, 0x6c, 0x6c, 0x49, 0x50, 0x22, 0x15, 0x0a, 0x13, 0x42, 0x6c,
0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x4d, 0x6f, 0x64, 0x65, 0x4e, 0x58, 0x44, 0x4f, 0x4d, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x45, 0x46, 0x55, 0x53, 0x45,
0x41, 0x49, 0x4e, 0x22, 0x14, 0x0a, 0x12, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x4d, 0x44, 0x22, 0xe3, 0x01, 0x0a, 0x11, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x42, 0x69, 0x6c, 0x6c,
0x6f, 0x64, 0x65, 0x4e, 0x75, 0x6c, 0x6c, 0x49, 0x50, 0x22, 0x15, 0x0a, 0x13, 0x42, 0x6c, 0x6f, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x12, 0x48, 0x0a, 0x12, 0x6c, 0x61, 0x73, 0x74, 0x5f,
0x63, 0x6b, 0x69, 0x6e, 0x67, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x45, 0x46, 0x55, 0x53, 0x45, 0x44, 0x61, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20,
0x22, 0xe3, 0x01, 0x0a, 0x11, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x42, 0x69, 0x6c, 0x6c, 0x69, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x12, 0x48, 0x0a, 0x12, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x61, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52,
0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x10, 0x6c, 0x61, 0x73, 0x74, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x54, 0x69, 0x6d,
0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02,
0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x10, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x12, 0x25,
0x6c, 0x61, 0x73, 0x74, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x54, 0x69, 0x6d, 0x65, 0x0a, 0x0e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79,
0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x6f,
0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x12, 0x25, 0x0a, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x18, 0x04,
0x0e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x10, 0x0a, 0x03, 0x61,
0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x75, 0x73, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x61, 0x73, 0x6e, 0x12, 0x18, 0x0a,
0x6e, 0x74, 0x72, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x18, 0x04, 0x20, 0x07, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07,
0x01, 0x28, 0x0d, 0x52, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x73, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x22, 0x90, 0x02, 0x0a, 0x0e, 0x41, 0x63, 0x63, 0x65,
0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x61, 0x73, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x73, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x31, 0x0a, 0x0e, 0x61, 0x6c,
0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x71, 0x6c, 0x6f, 0x77, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x63, 0x69, 0x64, 0x72, 0x18, 0x01, 0x20, 0x03,
0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x22, 0x90, 0x02, 0x0a, 0x0e, 0x41, 0x63, 0x63, 0x65, 0x73, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x43, 0x69, 0x64, 0x72, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0d,
0x73, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x31, 0x0a, 0x0e, 0x61, 0x6c, 0x6c, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x6c, 0x69, 0x73, 0x74, 0x43, 0x69, 0x64, 0x72, 0x12, 0x31, 0x0a,
0x6f, 0x77, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x63, 0x69, 0x64, 0x72, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x63, 0x69, 0x64, 0x72, 0x18,
0x0b, 0x32, 0x0a, 0x2e, 0x43, 0x69, 0x64, 0x72, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0d, 0x61, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x43, 0x69, 0x64, 0x72, 0x52, 0x61, 0x6e, 0x67,
0x6c, 0x6c, 0x6f, 0x77, 0x6c, 0x69, 0x73, 0x74, 0x43, 0x69, 0x64, 0x72, 0x12, 0x31, 0x0a, 0x0e, 0x65, 0x52, 0x0d, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x6c, 0x69, 0x73, 0x74, 0x43, 0x69, 0x64, 0x72,
0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x63, 0x69, 0x64, 0x72, 0x18, 0x02, 0x12, 0x23, 0x0a, 0x0d, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x61, 0x73,
0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x43, 0x69, 0x64, 0x72, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0c, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x6c, 0x69,
0x52, 0x0d, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x6c, 0x69, 0x73, 0x74, 0x43, 0x69, 0x64, 0x72, 0x12, 0x73, 0x74, 0x41, 0x73, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x6c, 0x69,
0x23, 0x0a, 0x0d, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x61, 0x73, 0x6e, 0x73, 0x74, 0x5f, 0x61, 0x73, 0x6e, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0c, 0x62, 0x6c,
0x18, 0x03, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0c, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x6c, 0x69, 0x73, 0x6f, 0x63, 0x6b, 0x6c, 0x69, 0x73, 0x74, 0x41, 0x73, 0x6e, 0x12, 0x34, 0x0a, 0x16, 0x62, 0x6c,
0x74, 0x41, 0x73, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x6c, 0x69, 0x73, 0x6f, 0x63, 0x6b, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x72,
0x74, 0x5f, 0x61, 0x73, 0x6e, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0c, 0x62, 0x6c, 0x6f, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x14, 0x62, 0x6c, 0x6f, 0x63,
0x63, 0x6b, 0x6c, 0x69, 0x73, 0x74, 0x41, 0x73, 0x6e, 0x12, 0x34, 0x0a, 0x16, 0x62, 0x6c, 0x6f, 0x6b, 0x6c, 0x69, 0x73, 0x74, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x75, 0x6c, 0x65, 0x73,
0x63, 0x6b, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x72, 0x75, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28,
0x6c, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x14, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x3d, 0x0a, 0x09, 0x43, 0x69,
0x6c, 0x69, 0x73, 0x74, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x64, 0x72, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65,
0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73,
0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x3d, 0x0a, 0x09, 0x43, 0x69, 0x64, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28,
0x72, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x0d, 0x52, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x22, 0x85, 0x01, 0x0a, 0x16, 0x41, 0x75,
0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x74, 0x74,
0x12, 0x16, 0x0a, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x22, 0x0a, 0x0d, 0x64, 0x6f, 0x68, 0x5f, 0x61, 0x75, 0x74, 0x68,
0x52, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x32, 0x8a, 0x01, 0x0a, 0x0a, 0x44, 0x4e, 0x53, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x64, 0x6f, 0x68,
0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x34, 0x0a, 0x0e, 0x67, 0x65, 0x74, 0x44, 0x4e, 0x41, 0x75, 0x74, 0x68, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x32, 0x0a, 0x14, 0x70, 0x61, 0x73, 0x73,
0x53, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x13, 0x2e, 0x44, 0x4e, 0x53, 0x50, 0x77, 0x6f, 0x72, 0x64, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x5f, 0x62, 0x63, 0x72, 0x79, 0x70, 0x74,
0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x12, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f,
0x2e, 0x44, 0x4e, 0x53, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x30, 0x01, 0x12, 0x46, 0x0a, 0x72, 0x64, 0x48, 0x61, 0x73, 0x68, 0x42, 0x63, 0x72, 0x79, 0x70, 0x74, 0x42, 0x13, 0x0a, 0x11,
0x16, 0x73, 0x61, 0x76, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x42, 0x69, 0x6c, 0x6c, 0x64, 0x6f, 0x68, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x5f, 0x68, 0x61, 0x73,
0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x12, 0x12, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x68, 0x32, 0x8a, 0x01, 0x0a, 0x0a, 0x44, 0x4e, 0x53, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
0x42, 0x69, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x12, 0x34, 0x0a, 0x0e, 0x67, 0x65, 0x74, 0x44, 0x4e, 0x53, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c,
0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x65, 0x73, 0x12, 0x13, 0x2e, 0x44, 0x4e, 0x53, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73,
0x70, 0x74, 0x79, 0x28, 0x01, 0x42, 0x3d, 0x0a, 0x21, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x64, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0b, 0x2e, 0x44, 0x4e, 0x53, 0x50, 0x72, 0x6f,
0x75, 0x61, 0x72, 0x64, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2e, 0x64, 0x6e, 0x73, 0x66, 0x69, 0x6c, 0x65, 0x30, 0x01, 0x12, 0x46, 0x0a, 0x16, 0x73, 0x61, 0x76, 0x65, 0x44, 0x65,
0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x42, 0x10, 0x44, 0x4e, 0x53, 0x50, 0x76, 0x69, 0x63, 0x65, 0x73, 0x42, 0x69, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74,
0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0xa2, 0x02, 0x12, 0x12, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x42, 0x69, 0x6c, 0x6c, 0x69, 0x6e, 0x67,
0x03, 0x44, 0x4e, 0x53, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 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 ( var (
@ -1391,63 +1487,65 @@ func file_backend_proto_rawDescGZIP() []byte {
return file_backend_proto_rawDescData 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{}{ var file_backend_proto_goTypes = []interface{}{
(*DNSProfilesRequest)(nil), // 0: DNSProfilesRequest (*DNSProfilesRequest)(nil), // 0: DNSProfilesRequest
(*DNSProfile)(nil), // 1: DNSProfile (*DNSProfile)(nil), // 1: DNSProfile
(*SafeBrowsingSettings)(nil), // 2: SafeBrowsingSettings (*SafeBrowsingSettings)(nil), // 2: SafeBrowsingSettings
(*DeviceSettings)(nil), // 3: DeviceSettings (*DeviceSettings)(nil), // 3: DeviceSettings
(*ParentalSettings)(nil), // 4: ParentalSettings (*ParentalSettings)(nil), // 4: ParentalSettings
(*ScheduleSettings)(nil), // 5: ScheduleSettings (*ScheduleSettings)(nil), // 5: ScheduleSettings
(*WeeklyRange)(nil), // 6: WeeklyRange (*WeeklyRange)(nil), // 6: WeeklyRange
(*DayRange)(nil), // 7: DayRange (*DayRange)(nil), // 7: DayRange
(*RuleListsSettings)(nil), // 8: RuleListsSettings (*RuleListsSettings)(nil), // 8: RuleListsSettings
(*BlockingModeCustomIP)(nil), // 9: BlockingModeCustomIP (*BlockingModeCustomIP)(nil), // 9: BlockingModeCustomIP
(*BlockingModeNXDOMAIN)(nil), // 10: BlockingModeNXDOMAIN (*BlockingModeNXDOMAIN)(nil), // 10: BlockingModeNXDOMAIN
(*BlockingModeNullIP)(nil), // 11: BlockingModeNullIP (*BlockingModeNullIP)(nil), // 11: BlockingModeNullIP
(*BlockingModeREFUSED)(nil), // 12: BlockingModeREFUSED (*BlockingModeREFUSED)(nil), // 12: BlockingModeREFUSED
(*DeviceBillingStat)(nil), // 13: DeviceBillingStat (*DeviceBillingStat)(nil), // 13: DeviceBillingStat
(*AccessSettings)(nil), // 14: AccessSettings (*AccessSettings)(nil), // 14: AccessSettings
(*CidrRange)(nil), // 15: CidrRange (*CidrRange)(nil), // 15: CidrRange
(*timestamppb.Timestamp)(nil), // 16: google.protobuf.Timestamp (*AuthenticationSettings)(nil), // 16: AuthenticationSettings
(*durationpb.Duration)(nil), // 17: google.protobuf.Duration (*timestamppb.Timestamp)(nil), // 17: google.protobuf.Timestamp
(*emptypb.Empty)(nil), // 18: google.protobuf.Empty (*durationpb.Duration)(nil), // 18: google.protobuf.Duration
(*emptypb.Empty)(nil), // 19: google.protobuf.Empty
} }
var file_backend_proto_depIdxs = []int32{ 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 2, // 1: DNSProfile.safe_browsing:type_name -> SafeBrowsingSettings
4, // 2: DNSProfile.parental:type_name -> ParentalSettings 4, // 2: DNSProfile.parental:type_name -> ParentalSettings
8, // 3: DNSProfile.rule_lists:type_name -> RuleListsSettings 8, // 3: DNSProfile.rule_lists:type_name -> RuleListsSettings
3, // 4: DNSProfile.devices:type_name -> DeviceSettings 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 9, // 6: DNSProfile.blocking_mode_custom_ip:type_name -> BlockingModeCustomIP
10, // 7: DNSProfile.blocking_mode_nxdomain:type_name -> BlockingModeNXDOMAIN 10, // 7: DNSProfile.blocking_mode_nxdomain:type_name -> BlockingModeNXDOMAIN
11, // 8: DNSProfile.blocking_mode_null_ip:type_name -> BlockingModeNullIP 11, // 8: DNSProfile.blocking_mode_null_ip:type_name -> BlockingModeNullIP
12, // 9: DNSProfile.blocking_mode_refused:type_name -> BlockingModeREFUSED 12, // 9: DNSProfile.blocking_mode_refused:type_name -> BlockingModeREFUSED
14, // 10: DNSProfile.access:type_name -> AccessSettings 14, // 10: DNSProfile.access:type_name -> AccessSettings
5, // 11: ParentalSettings.schedule:type_name -> ScheduleSettings 16, // 11: DeviceSettings.authentication:type_name -> AuthenticationSettings
6, // 12: ScheduleSettings.weeklyRange:type_name -> WeeklyRange 5, // 12: ParentalSettings.schedule:type_name -> ScheduleSettings
7, // 13: WeeklyRange.mon:type_name -> DayRange 6, // 13: ScheduleSettings.weeklyRange:type_name -> WeeklyRange
7, // 14: WeeklyRange.tue:type_name -> DayRange 7, // 14: WeeklyRange.mon:type_name -> DayRange
7, // 15: WeeklyRange.wed:type_name -> DayRange 7, // 15: WeeklyRange.tue:type_name -> DayRange
7, // 16: WeeklyRange.thu:type_name -> DayRange 7, // 16: WeeklyRange.wed:type_name -> DayRange
7, // 17: WeeklyRange.fri:type_name -> DayRange 7, // 17: WeeklyRange.thu:type_name -> DayRange
7, // 18: WeeklyRange.sat:type_name -> DayRange 7, // 18: WeeklyRange.fri:type_name -> DayRange
7, // 19: WeeklyRange.sun:type_name -> DayRange 7, // 19: WeeklyRange.sat:type_name -> DayRange
17, // 20: DayRange.start:type_name -> google.protobuf.Duration 7, // 20: WeeklyRange.sun:type_name -> DayRange
17, // 21: DayRange.end:type_name -> google.protobuf.Duration 18, // 21: DayRange.start:type_name -> google.protobuf.Duration
16, // 22: DeviceBillingStat.last_activity_time:type_name -> google.protobuf.Timestamp 18, // 22: DayRange.end:type_name -> google.protobuf.Duration
15, // 23: AccessSettings.allowlist_cidr:type_name -> CidrRange 17, // 23: DeviceBillingStat.last_activity_time:type_name -> google.protobuf.Timestamp
15, // 24: AccessSettings.blocklist_cidr:type_name -> CidrRange 15, // 24: AccessSettings.allowlist_cidr:type_name -> CidrRange
0, // 25: DNSService.getDNSProfiles:input_type -> DNSProfilesRequest 15, // 25: AccessSettings.blocklist_cidr:type_name -> CidrRange
13, // 26: DNSService.saveDevicesBillingStat:input_type -> DeviceBillingStat 0, // 26: DNSService.getDNSProfiles:input_type -> DNSProfilesRequest
1, // 27: DNSService.getDNSProfiles:output_type -> DNSProfile 13, // 27: DNSService.saveDevicesBillingStat:input_type -> DeviceBillingStat
18, // 28: DNSService.saveDevicesBillingStat:output_type -> google.protobuf.Empty 1, // 28: DNSService.getDNSProfiles:output_type -> DNSProfile
27, // [27:29] is the sub-list for method output_type 19, // 29: DNSService.saveDevicesBillingStat:output_type -> google.protobuf.Empty
25, // [25:27] is the sub-list for method input_type 28, // [28:30] is the sub-list for method output_type
25, // [25:25] is the sub-list for extension type_name 26, // [26:28] is the sub-list for method input_type
25, // [25:25] is the sub-list for extension extendee 26, // [26:26] is the sub-list for extension type_name
0, // [0:25] is the sub-list for field 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() } func init() { file_backend_proto_init() }
@ -1648,6 +1746,18 @@ func file_backend_proto_init() {
return nil 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{}{ file_backend_proto_msgTypes[1].OneofWrappers = []interface{}{
(*DNSProfile_BlockingModeCustomIp)(nil), (*DNSProfile_BlockingModeCustomIp)(nil),
@ -1655,13 +1765,16 @@ func file_backend_proto_init() {
(*DNSProfile_BlockingModeNullIp)(nil), (*DNSProfile_BlockingModeNullIp)(nil),
(*DNSProfile_BlockingModeRefused)(nil), (*DNSProfile_BlockingModeRefused)(nil),
} }
file_backend_proto_msgTypes[16].OneofWrappers = []interface{}{
(*AuthenticationSettings_PasswordHashBcrypt)(nil),
}
type x struct{} type x struct{}
out := protoimpl.TypeBuilder{ out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{ File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_backend_proto_rawDesc, RawDescriptor: file_backend_proto_rawDesc,
NumEnums: 0, NumEnums: 0,
NumMessages: 16, NumMessages: 17,
NumExtensions: 0, NumExtensions: 0,
NumServices: 1, NumServices: 1,
}, },

View File

@ -66,6 +66,7 @@ message DeviceSettings {
bool filtering_enabled = 3; bool filtering_enabled = 3;
bytes linked_ip = 4; bytes linked_ip = 4;
repeated bytes dedicated_ips = 5; repeated bytes dedicated_ips = 5;
AuthenticationSettings authentication = 6;
} }
message ParentalSettings { message ParentalSettings {
@ -136,3 +137,10 @@ message CidrRange {
bytes address = 1; bytes address = 1;
uint32 prefix = 2; 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. // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions: // versions:
// - protoc-gen-go-grpc v1.3.0 // - protoc-gen-go-grpc v1.3.0
// - protoc v4.25.1 // - protoc v4.25.3
// source: backend.proto // source: backend.proto
package backendpb package backendpb

View File

@ -11,6 +11,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/access" "github.com/AdguardTeam/AdGuardDNS/internal/access"
"github.com/AdguardTeam/AdGuardDNS/internal/agd" "github.com/AdguardTeam/AdGuardDNS/internal/agd"
"github.com/AdguardTeam/AdGuardDNS/internal/agdpasswd"
"github.com/AdguardTeam/AdGuardDNS/internal/agdprotobuf" "github.com/AdguardTeam/AdGuardDNS/internal/agdprotobuf"
"github.com/AdguardTeam/AdGuardDNS/internal/agdtime" "github.com/AdguardTeam/AdGuardDNS/internal/agdtime"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg" "github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
@ -355,6 +356,37 @@ func (x *ScheduleSettings) toInternal() (sch *agd.ParentalProtectionSchedule, er
return sch, nil 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 // blockingModeToInternal converts a protobuf blocking-mode sum-type to an
// internal one. If pbm is nil, blockingModeToInternal returns a null-IP // internal one. If pbm is nil, blockingModeToInternal returns a null-IP
// blocking mode. // blocking mode.
@ -363,18 +395,7 @@ func blockingModeToInternal(pbm isDNSProfile_BlockingMode) (m dnsmsg.BlockingMod
case nil: case nil:
return &dnsmsg.BlockingModeNullIP{}, nil return &dnsmsg.BlockingModeNullIP{}, nil
case *DNSProfile_BlockingModeCustomIp: case *DNSProfile_BlockingModeCustomIp:
custom := &dnsmsg.BlockingModeCustomIP{} return pbm.BlockingModeCustomIp.toInternal()
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
case *DNSProfile_BlockingModeNxdomain: case *DNSProfile_BlockingModeNxdomain:
return &dnsmsg.BlockingModeNXDOMAIN{}, nil return &dnsmsg.BlockingModeNXDOMAIN{}, nil
case *DNSProfile_BlockingModeNullIp: 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) id, err := agd.NewDeviceID(ds.Id)
if err != nil { if err != nil {
return nil, fmt.Errorf("device id: %s: %w", ds.Id, err) 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{ return &agd.Device{
Auth: auth,
ID: id, ID: id,
Name: name, Name: name,
LinkedIP: linkedIP, LinkedIP: linkedIP,
@ -462,6 +489,44 @@ func (ds *DeviceSettings) toInternal(bindSet netutil.SubnetSet) (dev *agd.Device
}, nil }, 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 // rulesToInternal is a helper that converts the filter rules from the backend
// response to AdGuard DNS filtering rules. // response to AdGuard DNS filtering rules.
func rulesToInternal( func rulesToInternal(

View File

@ -9,6 +9,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/access" "github.com/AdguardTeam/AdGuardDNS/internal/access"
"github.com/AdguardTeam/AdGuardDNS/internal/agd" "github.com/AdguardTeam/AdGuardDNS/internal/agd"
"github.com/AdguardTeam/AdGuardDNS/internal/agdpasswd"
"github.com/AdguardTeam/AdGuardDNS/internal/agdtest" "github.com/AdguardTeam/AdGuardDNS/internal/agdtest"
"github.com/AdguardTeam/AdGuardDNS/internal/agdtime" "github.com/AdguardTeam/AdGuardDNS/internal/agdtime"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg" "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, newProfile(t), got)
assert.NotEqual(t, newDevices(t), gotDevices) assert.NotEqual(t, newDevices(t), gotDevices)
assert.Len(t, gotDevices, 1) assert.Len(t, gotDevices, 2)
}) })
t.Run("empty", func(t *testing.T) { 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) 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) { t.Run("nil_blocking_mode", func(t *testing.T) {
dp := NewTestDNSProfile(t) dp := NewTestDNSProfile(t)
dp.BlockingMode = nil dp.BlockingMode = nil
@ -244,17 +255,33 @@ func NewTestDNSProfile(tb testing.TB) (dp *DNSProfile) {
} }
devices := []*DeviceSettings{{ devices := []*DeviceSettings{{
Id: "118ffe93", Id: "1111aaaa",
Name: "118ffe93-name", Name: "1111aaaa-name",
FilteringEnabled: false, FilteringEnabled: false,
LinkedIp: ipToBytes(tb, netip.MustParseAddr("1.1.1.1")), LinkedIp: ipToBytes(tb, netip.MustParseAddr("1.1.1.1")),
DedicatedIps: [][]byte{ipToBytes(tb, netip.MustParseAddr("1.1.1.2"))}, DedicatedIps: [][]byte{ipToBytes(tb, netip.MustParseAddr("1.1.1.2"))},
}, { }, {
Id: "b9e1a762", Id: "2222bbbb",
Name: "b9e1a762-name", Name: "2222bbbb-name",
FilteringEnabled: true, FilteringEnabled: true,
LinkedIp: ipToBytes(tb, netip.MustParseAddr("2.2.2.2")), LinkedIp: ipToBytes(tb, netip.MustParseAddr("2.2.2.2")),
DedicatedIps: nil, 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{ return &DNSProfile{
@ -358,8 +385,8 @@ func newProfile(tb testing.TB) (p *agd.Profile) {
} }
wantBlockingMode := &dnsmsg.BlockingModeCustomIP{ wantBlockingMode := &dnsmsg.BlockingModeCustomIP{
IPv4: netip.MustParseAddr("1.2.3.4"), IPv4: []netip.Addr{netip.MustParseAddr("1.2.3.4")},
IPv6: netip.MustParseAddr("1234::cdef"), IPv6: []netip.Addr{netip.MustParseAddr("1234::cdef")},
} }
wantAccess := access.NewDefaultProfile(&access.ProfileConfig{ wantAccess := access.NewDefaultProfile(&access.ProfileConfig{
@ -376,8 +403,9 @@ func newProfile(tb testing.TB) (p *agd.Profile) {
ID: testProfileID, ID: testProfileID,
UpdateTime: TestUpdTime, UpdateTime: TestUpdTime,
DeviceIDs: []agd.DeviceID{ DeviceIDs: []agd.DeviceID{
"118ffe93", "1111aaaa",
"b9e1a762", "2222bbbb",
"3333cccc",
}, },
RuleListIDs: []agd.FilterListID{"1"}, RuleListIDs: []agd.FilterListID{"1"},
CustomRules: []agd.FilterRuleText{"||example.org^"}, CustomRules: []agd.FilterRuleText{"||example.org^"},
@ -399,17 +427,38 @@ func newDevices(t *testing.T) (d []*agd.Device) {
t.Helper() t.Helper()
return []*agd.Device{{ return []*agd.Device{{
ID: "118ffe93", Auth: &agd.AuthSettings{
Enabled: false,
DoHAuthOnly: false,
PasswordHash: agdpasswd.AllowAuthenticator{},
},
ID: "1111aaaa",
LinkedIP: netip.MustParseAddr("1.1.1.1"), LinkedIP: netip.MustParseAddr("1.1.1.1"),
Name: "118ffe93-name", Name: "1111aaaa-name",
DedicatedIPs: []netip.Addr{netip.MustParseAddr("1.1.1.2")}, DedicatedIPs: []netip.Addr{netip.MustParseAddr("1.1.1.2")},
FilteringEnabled: false, 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"), LinkedIP: netip.MustParseAddr("2.2.2.2"),
Name: "b9e1a762-name", Name: "2222bbbb-name",
DedicatedIPs: nil, DedicatedIPs: nil,
FilteringEnabled: true, 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.ReportAllocs()
b.ResetTimer() b.ResetTimer()
for i := 0; i < b.N; i++ { for range b.N {
profSink, _, errSink = dp.toInternal(ctx, TestUpdTime, testBind, errColl) profSink, _, errSink = dp.toInternal(ctx, TestUpdTime, testBind, errColl)
} }

View File

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

View File

@ -2,12 +2,12 @@ package billstat
import ( import (
"context" "context"
"fmt"
"sync" "sync"
"time" "time"
"github.com/AdguardTeam/AdGuardDNS/internal/agd" "github.com/AdguardTeam/AdGuardDNS/internal/agd"
"github.com/AdguardTeam/AdGuardDNS/internal/agdservice" "github.com/AdguardTeam/AdGuardDNS/internal/agdservice"
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
"github.com/AdguardTeam/AdGuardDNS/internal/geoip" "github.com/AdguardTeam/AdGuardDNS/internal/geoip"
"github.com/AdguardTeam/AdGuardDNS/internal/metrics" "github.com/AdguardTeam/AdGuardDNS/internal/metrics"
"github.com/AdguardTeam/golibs/log" "github.com/AdguardTeam/golibs/log"
@ -18,6 +18,9 @@ import (
// RuntimeRecorderConfig is the configuration structure for a runtime billing // RuntimeRecorderConfig is the configuration structure for a runtime billing
// statistics recorder. All fields must be non-empty. // statistics recorder. All fields must be non-empty.
type RuntimeRecorderConfig struct { 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 is used to upload the billing statistics records to.
Uploader Uploader Uploader Uploader
} }
@ -29,6 +32,7 @@ func NewRuntimeRecorder(c *RuntimeRecorderConfig) (r *RuntimeRecorder) {
mu: &sync.Mutex{}, mu: &sync.Mutex{},
records: Records{}, records: Records{},
uploader: c.Uploader, uploader: c.Uploader,
errColl: c.ErrColl,
} }
} }
@ -44,6 +48,9 @@ type RuntimeRecorder struct {
// uploader is the uploader to which the billing statistics records are // uploader is the uploader to which the billing statistics records are
// uploaded. // uploaded.
uploader Uploader uploader Uploader
// errColl is used to collect errors during refreshes.
errColl errcoll.Interface
} }
// type check // type check
@ -58,6 +65,10 @@ func (r *RuntimeRecorder) Record(
start time.Time, start time.Time,
proto agd.Protocol, proto agd.Protocol,
) { ) {
// TODO(a.garipov): Use slog.
log.Debug("billstat_refresh: started")
defer log.Debug("billstat_refresh: finished")
r.mu.Lock() r.mu.Lock()
defer r.mu.Unlock() defer r.mu.Unlock()
@ -96,7 +107,7 @@ func (r *RuntimeRecorder) Refresh(ctx context.Context) (err error) {
if err != nil { if err != nil {
r.remergeRecords(records) r.remergeRecords(records)
log.Info("billstat: refresh failed, records remerged") log.Info("billstat_refresh: failed, records remerged")
} else { } else {
metrics.BillStatUploadTimestamp.SetToCurrentTime() metrics.BillStatUploadTimestamp.SetToCurrentTime()
} }
@ -106,10 +117,10 @@ func (r *RuntimeRecorder) Refresh(ctx context.Context) (err error) {
err = r.uploader.Upload(ctx, records) err = r.uploader.Upload(ctx, records)
if err != nil { 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 // 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) { func TestRuntimeRecorder_success(t *testing.T) {
var gotRecord *billstat.Record var gotRecord *billstat.Record
c := &billstat.RuntimeRecorderConfig{ c := &billstat.RuntimeRecorderConfig{
ErrColl: &agdtest.ErrorCollector{
OnCollect: func(_ context.Context, _ error) {
panic("not implemented")
},
},
Uploader: &agdtest.BillStatUploader{ Uploader: &agdtest.BillStatUploader{
OnUpload: func(_ context.Context, records billstat.Records) (err error) { OnUpload: func(_ context.Context, records billstat.Records) (err error) {
gotRecord = records[devID] gotRecord = records[devID]
@ -48,7 +53,7 @@ func TestRuntimeRecorder_success(t *testing.T) {
// incremented. // incremented.
const reqNum = 2 const reqNum = 2
var err error var err error
for i := 0; i < reqNum; i++ { for range reqNum {
r.Record(ctx, devID, clientCtry, clientASN, start, proto) r.Record(ctx, devID, clientCtry, clientASN, start, proto)
} }
@ -89,7 +94,13 @@ func TestRuntimeRecorder_fail(t *testing.T) {
return nil return nil
} }
var gotCollErr error
c := &billstat.RuntimeRecorderConfig{ c := &billstat.RuntimeRecorderConfig{
ErrColl: &agdtest.ErrorCollector{
OnCollect: func(_ context.Context, err error) {
gotCollErr = err
},
},
Uploader: &agdtest.BillStatUploader{ Uploader: &agdtest.BillStatUploader{
OnUpload: onUpload, OnUpload: onUpload,
}, },
@ -118,6 +129,7 @@ func TestRuntimeRecorder_fail(t *testing.T) {
err := r.Refresh(context.Background()) err := r.Refresh(context.Background())
require.ErrorIs(t, err, testError) require.ErrorIs(t, err, testError)
require.ErrorIs(t, gotCollErr, testError)
require.Nil(t, gotRecord) require.Nil(t, gotRecord)
// Request the backend again, expect the correct, merged data. // Request the backend again, expect the correct, merged data.

View File

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

View File

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

View File

@ -15,19 +15,22 @@ import (
"github.com/AdguardTeam/golibs/log" "github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/netutil" "github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/syncutil" "github.com/AdguardTeam/golibs/syncutil"
"github.com/prometheus/client_golang/prometheus"
) )
// interfaceListener contains information about a single interface listener. // interfaceListener contains information about a single interface listener.
type interfaceListener struct { type interfaceListener struct {
conns *connIndex conns *connIndex
listenConf *net.ListenConfig listenConf *net.ListenConfig
bodyPool *syncutil.Pool[[]byte] bodyPool *syncutil.Pool[[]byte]
oobPool *syncutil.Pool[[]byte] oobPool *syncutil.Pool[[]byte]
writeRequests chan *packetConnWriteReq writeRequests chan *packetConnWriteReq
done chan unit done chan unit
errColl errcoll.Interface errColl errcoll.Interface
ifaceName string writeRequestsGauge prometheus.Gauge
port uint16 writeDurationHist prometheus.Observer
ifaceName string
port uint16
} }
// listenTCP runs the TCP listening loop. It is intended to be used as a // listenTCP runs the TCP listening loop. It is intended to be used as a
@ -113,7 +116,7 @@ func (l *interfaceListener) listenUDP(errCh chan<- error) {
errCh <- nil errCh <- nil
go l.writeUDP(udpConn) go l.writeUDPResponses(udpConn)
logPrefix := fmt.Sprintf("bindtodevice: listener %s:%d: udp", l.ifaceName, l.port) 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 return nil
} }
// writeUDP runs the UDP write loop. It is intended to be used as a goroutine. // writeUDPResponses runs the UDP write loop. It is intended to be used as a
func (l *interfaceListener) writeUDP(c *net.UDPConn) { // goroutine.
func (l *interfaceListener) writeUDPResponses(c *net.UDPConn) {
defer log.OnPanic("interfaceListener.writeUDP") defer log.OnPanic("interfaceListener.writeUDP")
logPrefix := fmt.Sprintf("bindtodevice: listener %s:%d: udp write", l.ifaceName, l.port)
for { for {
var req *packetConnWriteReq
select { select {
case <-l.done: case <-l.done:
optlog.Debug1("%s: done", logPrefix) optlog.Debug2("bindtodevice: listener %s:%d: udp write: done", l.ifaceName, l.port)
return return
case req = <-l.writeRequests: case req := <-l.writeRequests:
// Go on. l.writeUDP(c, req)
} }
resp := &packetConnWriteResp{}
resp.err = c.SetWriteDeadline(req.deadline)
if resp.err != nil {
req.resp <- resp
continue
}
if s := req.session; 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,
)
l.bodyPool.Put(&s.readBody)
}
resetDeadlineErr := c.SetWriteDeadline(time.Time{})
resp.err = errors.WithDeferred(resp.err, resetDeadlineErr)
req.resp <- resp
} }
} }
// 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.respCh <- resp
return
}
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)
return
}
resp.written, _, resp.err = c.WriteMsgUDP(req.body, s.respOOB, req.session.raddr)
l.bodyPool.Put(&s.readBody)
}

View File

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

View File

@ -12,6 +12,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/agdnet" "github.com/AdguardTeam/AdGuardDNS/internal/agdnet"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/netext" "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/netext"
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll" "github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
"github.com/AdguardTeam/AdGuardDNS/internal/metrics"
"github.com/AdguardTeam/golibs/errors" "github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log" "github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/mapsutil" "github.com/AdguardTeam/golibs/mapsutil"
@ -84,7 +85,7 @@ func (m *Manager) Add(id ID, ifaceName string, port uint16, ctrlConf *ControlCon
return nil return nil
} }
err = mapsutil.OrderedRangeError(m.ifaceListeners, validateDup) err = mapsutil.SortedRangeError(m.ifaceListeners, validateDup)
if err != nil { if err != nil {
// Don't wrap the error, because it's informative enough as is. // Don't wrap the error, because it's informative enough as is.
return err return err
@ -109,15 +110,17 @@ func (m *Manager) newInterfaceListener(
port uint16, port uint16,
) (l *interfaceListener) { ) (l *interfaceListener) {
return &interfaceListener{ return &interfaceListener{
conns: &connIndex{}, conns: &connIndex{},
listenConf: newListenConfig(ifaceName, ctrlConf), listenConf: newListenConfig(ifaceName, ctrlConf),
bodyPool: syncutil.NewSlicePool[byte](bodySize), bodyPool: syncutil.NewSlicePool[byte](bodySize),
oobPool: syncutil.NewSlicePool[byte](netext.IPDstOOBSize), oobPool: syncutil.NewSlicePool[byte](netext.IPDstOOBSize),
writeRequests: make(chan *packetConnWriteReq, m.chanBufSize), writeRequests: make(chan *packetConnWriteReq, m.chanBufSize),
done: m.done, done: m.done,
errColl: m.errColl, errColl: m.errColl,
ifaceName: ifaceName, writeRequestsGauge: metrics.BindToDeviceUDPWriteRequestsChanSize.WithLabelValues(ifaceName),
port: port, 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) sessCh := make(chan *packetSession, m.chanBufSize)
pConn := newChanPacketConn(sessCh, subnet, l.writeRequests, &agdnet.PrefixNetAddr{ pConn := newChanPacketConn(
Prefix: subnet, sessCh,
Net: "udp", subnet,
Port: l.port, l.writeRequests,
}) l.writeRequestsGauge,
&agdnet.PrefixNetAddr{
Prefix: subnet,
Net: "udp",
Port: l.port,
},
)
err = l.conns.addPacketConn(pConn) err = l.conns.addPacketConn(pConn)
if err != nil { if err != nil {

View File

@ -233,10 +233,10 @@ func TestManager(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
require.NotNil(t, lc) require.NotNil(t, lc)
err = m.Start(agdtest.ContextWithTimeout(t, testTimeout)) err = m.Start(testutil.ContextWithTimeout(t, testTimeout))
require.NoError(t, err) require.NoError(t, err)
testutil.CleanupAndRequireSuccess(t, func() (err error) { 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) { t.Run("tcp", func(t *testing.T) {

View File

@ -113,9 +113,7 @@ func SubtestListenControlTCP(
ifaceName string, ifaceName string,
ifaceNet *net.IPNet, ifaceNet *net.IPNet,
) { ) {
ctx, cancel := context.WithTimeout(context.Background(), testTimeout) ctx := testutil.ContextWithTimeout(t, testTimeout)
t.Cleanup(cancel)
lsnr, err := lc.Listen(ctx, "tcp", "0.0.0.0:0") lsnr, err := lc.Listen(ctx, "tcp", "0.0.0.0:0")
require.NoError(t, err) require.NoError(t, err)
testutil.CleanupAndRequireSuccess(t, lsnr.Close) testutil.CleanupAndRequireSuccess(t, lsnr.Close)
@ -194,9 +192,7 @@ func SubtestListenControlUDP(
ifaceName string, ifaceName string,
ifaceNet *net.IPNet, ifaceNet *net.IPNet,
) { ) {
ctx, cancel := context.WithTimeout(context.Background(), testTimeout) ctx := testutil.ContextWithTimeout(t, testTimeout)
t.Cleanup(cancel)
packetConn, err := lc.ListenPacket(ctx, "udp", "0.0.0.0:0") packetConn, err := lc.ListenPacket(ctx, "udp", "0.0.0.0:0")
require.NoError(t, err) require.NoError(t, err)
testutil.CleanupAndRequireSuccess(t, packetConn.Close) testutil.CleanupAndRequireSuccess(t, packetConn.Close)
@ -528,7 +524,7 @@ func BenchmarkReadPacketSession(b *testing.B) {
b.ReportAllocs() b.ReportAllocs()
b.ResetTimer() b.ResetTimer()
for i := 0; i < b.N; i++ { for range b.N {
sessSink, errSink = readPacketSession(c, body, oob) 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. // validateAdditionalInfo return an error is the section is invalid.
func (c additionalInfo) validate() (err error) { 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() { if model.LabelName(k).IsValid() {
return nil return nil
} }

View File

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

View File

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

View File

@ -100,7 +100,8 @@ func (c *configuration) buildQueryLog(envs *environments) (l querylog.Interface)
} }
return querylog.NewFileSystem(&querylog.FileSystemConfig{ return querylog.NewFileSystem(&querylog.FileSystemConfig{
Path: envs.QueryLogPath, 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/agd"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg" "github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
"github.com/AdguardTeam/golibs/container"
"github.com/AdguardTeam/golibs/errors" "github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/netutil" "github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/stringutil"
"github.com/miekg/dns" "github.com/miekg/dns"
) )
@ -50,8 +50,8 @@ func (c *ddrConfig) toInternal(msgs *dnsmsg.Constructor) (conf *agd.DDR) {
func ddrRecsToSVCBTmpls( func ddrRecsToSVCBTmpls(
msgs *dnsmsg.Constructor, msgs *dnsmsg.Constructor,
records map[string]*ddrRecord, records map[string]*ddrRecord,
) (targets *stringutil.Set, tmpls []*dns.SVCB) { ) (targets *container.MapSet[string], tmpls []*dns.SVCB) {
targets = stringutil.NewSet() targets = container.NewMapSet[string]()
for target, r := range records { for target, r := range records {
target = strings.TrimPrefix(target, "*.") target = strings.TrimPrefix(target, "*.")
targets.Add(target) targets.Add(target)

View File

@ -60,8 +60,6 @@ type environments struct {
LogTimestamp strictBool `env:"LOG_TIMESTAMP" envDefault:"1"` LogTimestamp strictBool `env:"LOG_TIMESTAMP" envDefault:"1"`
LogVerbose strictBool `env:"VERBOSE" envDefault:"0"` LogVerbose strictBool `env:"VERBOSE" envDefault:"0"`
ProfilesEnabled strictBool `env:"PROFILES_ENABLED" envDefault:"1"` 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. // 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. // geoIP returns an GeoIP database implementation from environment.
func (envs *environments) geoIP( func (envs *environments) geoIP(c *geoIPConfig) (g *geoip.File, err error) {
c *geoIPConfig,
) (g *geoip.File, err error) {
log.Debug("using geoip files %q and %q", envs.GeoIPASNPath, envs.GeoIPCountryPath) log.Debug("using geoip files %q and %q", envs.GeoIPASNPath, envs.GeoIPCountryPath)
g, err = geoip.NewFile(&geoip.FileConfig{ g, err = geoip.NewFile(&geoip.FileConfig{
@ -180,19 +176,18 @@ func (envs *environments) buildRuleStat(
} }
httpRuleStat := rulestat.NewHTTP(&rulestat.HTTPConfig{ httpRuleStat := rulestat.NewHTTP(&rulestat.HTTPConfig{
URL: &envs.RuleStatURL.URL, ErrColl: errColl,
URL: &envs.RuleStatURL.URL,
}) })
refr := agdservice.NewRefreshWorker(&agdservice.RefreshWorkerConfig{ refr := agdservice.NewRefreshWorker(&agdservice.RefreshWorkerConfig{
Context: ctxWithDefaultTimeout, Context: ctxWithDefaultTimeout,
Refresher: httpRuleStat, Refresher: httpRuleStat,
ErrColl: errColl,
Name: "rulestat", Name: "rulestat",
// TODO(ameshkov): Consider making configurable. // TODO(ameshkov): Consider making configurable.
Interval: 10 * time.Minute, Interval: 10 * time.Minute,
RefreshOnShutdown: true, RefreshOnShutdown: true,
RoutineLogsAreDebug: false, RandomizeStart: false,
RandomizeStart: false,
}) })
err = refr.Start(context.Background()) err = refr.Start(context.Background())
if err != nil { if err != nil {

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 // numberOrDuration is the constraint for integer types along with
// timeutil.Duration. // timeutil.Duration.
type numberOrDuration interface { type numberOrDuration interface {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -2,14 +2,12 @@ package cmd
import ( import (
"fmt" "fmt"
"net/netip"
"github.com/AdguardTeam/AdGuardDNS/internal/agd" "github.com/AdguardTeam/AdGuardDNS/internal/agd"
"github.com/AdguardTeam/AdGuardDNS/internal/bindtodevice" "github.com/AdguardTeam/AdGuardDNS/internal/bindtodevice"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg" "github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
"github.com/AdguardTeam/golibs/container"
"github.com/AdguardTeam/golibs/errors" "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 // serverGroups are the DNS server groups. A valid instance of serverGroups has
@ -41,11 +39,10 @@ func (srvGrps serverGroups) toInternal(
} }
svcSrvGrps[i] = &agd.ServerGroup{ svcSrvGrps[i] = &agd.ServerGroup{
BlockPageRedirect: g.BlockPageRedirect.toInternal(), DDR: g.DDR.toInternal(messages),
DDR: g.DDR.toInternal(messages), TLS: tlsConf,
TLS: tlsConf, Name: agd.ServerGroupName(g.Name),
Name: agd.ServerGroupName(g.Name), FilteringGroup: fltGrpID,
FilteringGroup: fltGrpID,
} }
svcSrvGrps[i].Servers, err = g.Servers.toInternal(tlsConf, btdMgr, ratelimitConf, dnsConf) svcSrvGrps[i].Servers, err = g.Servers.toInternal(tlsConf, btdMgr, ratelimitConf, dnsConf)
@ -63,7 +60,7 @@ func (srvGrps serverGroups) validate() (err error) {
return errors.Error("no server groups") return errors.Error("no server groups")
} }
names := stringutil.NewSet() names := container.NewMapSet[string]()
for i, g := range srvGrps { for i, g := range srvGrps {
err = g.validate() err = g.validate()
if err != nil { 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 // TODO(a.garipov): Think about more consistent naming, since this object is a
// configuration, but it also stores other configurations. // configuration, but it also stores other configurations.
type serverGroup struct { 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 // DDR is the Discovery Of Designated Resolvers (DDR) configuration for this
// server group. // server group.
DDR *ddrConfig `yaml:"ddr"` DDR *ddrConfig `yaml:"ddr"`
@ -117,11 +111,6 @@ func (g *serverGroup) validate() (err error) {
return errors.Error("no filtering_group") 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() err = g.DDR.validate()
if err != nil { if err != nil {
return fmt.Errorf("ddr: %w", err) return fmt.Errorf("ddr: %w", err)
@ -139,255 +128,3 @@ func (g *serverGroup) validate() (err error) {
return nil 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/agdservice"
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll" "github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
"github.com/AdguardTeam/AdGuardDNS/internal/metrics" "github.com/AdguardTeam/AdGuardDNS/internal/metrics"
"github.com/AdguardTeam/golibs/container"
"github.com/AdguardTeam/golibs/errors" "github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/service" "github.com/AdguardTeam/golibs/service"
"github.com/AdguardTeam/golibs/stringutil"
"github.com/prometheus/client_golang/prometheus" "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 // validateDeviceIDWildcards returns an error if the device ID domain wildcards
// are invalid. // are invalid.
func validateDeviceIDWildcards(wildcards []string) (err error) { func validateDeviceIDWildcards(wildcards []string) (err error) {
s := stringutil.NewSet() s := container.NewMapSet[string]()
for i, w := range wildcards { for i, w := range wildcards {
// TODO(e.burkov): Consider removing this requirement. // TODO(e.burkov): Consider removing this requirement.
if !strings.HasPrefix(w, "*.") { if !strings.HasPrefix(w, "*.") {
@ -185,14 +186,18 @@ func (certs tlsConfigCerts) validate() (err error) {
// ticketRotator is a refresh worker that rereads and resets TLS session tickets. // ticketRotator is a refresh worker that rereads and resets TLS session tickets.
type ticketRotator struct { type ticketRotator struct {
confs map[*tls.Config][]string errColl errcoll.Interface
confs map[*tls.Config][]string
} }
// newTicketRotator creates a new TLS session ticket rotator that rotates // newTicketRotator creates a new TLS session ticket rotator that rotates
// tickets for the TLS configurations of all servers in grps. // tickets for the TLS configurations of all servers in grps.
// //
// grps is assumed to be valid. // 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{} confs := map[*tls.Config][]string{}
for _, g := range grps { for _, g := range grps {
@ -209,7 +214,8 @@ func newTicketRotator(grps []*agd.ServerGroup) (tr *ticketRotator, err error) {
} }
tr = &ticketRotator{ tr = &ticketRotator{
confs: confs, errColl: errColl,
confs: confs,
} }
err = tr.Refresh(context.Background()) err = tr.Refresh(context.Background())
@ -231,7 +237,17 @@ const sessTickLen = 32
var _ agdservice.Refresher = (*ticketRotator)(nil) var _ agdservice.Refresher = (*ticketRotator)(nil)
// Refresh implements the [agdservice.Refresher] interface for *ticketRotator. // 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 { for conf, files := range r.confs {
keys := make([][sessTickLen]byte, 0, len(files)) keys := make([][sessTickLen]byte, 0, len(files))
@ -304,7 +320,7 @@ func setupTicketRotator(
sigHdlr *service.SignalHandler, sigHdlr *service.SignalHandler,
errColl errcoll.Interface, errColl errcoll.Interface,
) (err error) { ) (err error) {
tickRot, err := newTicketRotator(srvGrps) tickRot, err := newTicketRotator(errColl, srvGrps)
if err != nil { if err != nil {
return fmt.Errorf("setting up ticket rotator: %w", err) return fmt.Errorf("setting up ticket rotator: %w", err)
} }
@ -312,13 +328,11 @@ func setupTicketRotator(
refr := agdservice.NewRefreshWorker(&agdservice.RefreshWorkerConfig{ refr := agdservice.NewRefreshWorker(&agdservice.RefreshWorkerConfig{
Context: ctxWithDefaultTimeout, Context: ctxWithDefaultTimeout,
Refresher: tickRot, Refresher: tickRot,
ErrColl: errColl,
Name: "tickrot", Name: "tickrot",
// TODO(ameshkov): Consider making configurable. // TODO(ameshkov): Consider making configurable.
Interval: 1 * time.Minute, Interval: 1 * time.Minute,
RefreshOnShutdown: false, RefreshOnShutdown: false,
RoutineLogsAreDebug: true, RandomizeStart: false,
RandomizeStart: false,
}) })
err = refr.Start(context.Background()) err = refr.Start(context.Background())
if err != nil { if err != nil {

View File

@ -1,6 +1,7 @@
package cmd package cmd
import ( import (
"cmp"
"context" "context"
"fmt" "fmt"
"net/netip" "net/netip"
@ -13,6 +14,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/prometheus" "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/prometheus"
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll" "github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
"github.com/AdguardTeam/golibs/errors" "github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/service" "github.com/AdguardTeam/golibs/service"
"github.com/AdguardTeam/golibs/timeutil" "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("fallback", c.Fallback.validate),
validateProp("healthcheck", c.Healthcheck.validate), validateProp("healthcheck", c.Healthcheck.validate),
) )
@ -171,13 +173,16 @@ func newUpstreamHealthcheck(
conf.Healthcheck.Timeout.Duration, conf.Healthcheck.Timeout.Duration,
) )
}, },
Refresher: handler, Refresher: agdservice.NewRefresherWithErrColl(
ErrColl: errColl, handler,
Name: "upstream healthcheck", log.Debug,
Interval: conf.Healthcheck.Interval.Duration, errColl,
RefreshOnShutdown: false, "upstream_healthcheck_refresh",
RoutineLogsAreDebug: true, ),
RandomizeStart: false, Name: "upstream healthcheck",
Interval: conf.Healthcheck.Interval.Duration,
RefreshOnShutdown: false,
RandomizeStart: false,
}) })
} }

View File

@ -25,12 +25,15 @@ type webConfig struct {
// LinkedIP is the optional linked IP web server. // LinkedIP is the optional linked IP web server.
LinkedIP *linkedIPServer `yaml:"linked_ip"` 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 is the optional adult blocking block page web server.
AdultBlocking *blockPageServer `yaml:"adult_blocking"` 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 // RootRedirectURL is the URL to which non-DNS and non-Debug HTTP requests
// are redirected. If not set, a 404 page is shown. // are redirected. If not set, a 404 page is shown.
RootRedirectURL *urlutil.URL `yaml:"root_redirect_url"` RootRedirectURL *urlutil.URL `yaml:"root_redirect_url"`
@ -86,6 +89,11 @@ func (c *webConfig) toInternal(
return nil, fmt.Errorf("converting adult_blocking: %w", err) 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() conf.SafeBrowsing, err = c.SafeBrowsing.toInternal()
if err != nil { if err != nil {
return nil, fmt.Errorf("converting safe_browsing: %w", err) 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) 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() err = c.AdultBlocking.validate()
if err != nil { if err != nil {
return fmt.Errorf("adult_blocking: %w", err) 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() err = c.StaticContent.validate()
if err != nil { if err != nil {
return fmt.Errorf("static_content: %w", err) 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/agdhttp"
"github.com/AdguardTeam/AdGuardDNS/internal/agdservice" "github.com/AdguardTeam/AdGuardDNS/internal/agdservice"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/ratelimit" "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/ratelimit"
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
"github.com/AdguardTeam/AdGuardDNS/internal/metrics" "github.com/AdguardTeam/AdGuardDNS/internal/metrics"
"github.com/AdguardTeam/golibs/errors" "github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log" "github.com/AdguardTeam/golibs/log"
@ -23,12 +24,14 @@ type AllowlistRefresher struct {
allowlist *ratelimit.DynamicAllowlist allowlist *ratelimit.DynamicAllowlist
http *agdhttp.Client http *agdhttp.Client
url *url.URL url *url.URL
errColl errcoll.Interface
} }
// NewAllowlistRefresher returns a properly initialized *AllowlistRefresher. // NewAllowlistRefresher returns a properly initialized *AllowlistRefresher.
func NewAllowlistRefresher( func NewAllowlistRefresher(
allowlist *ratelimit.DynamicAllowlist, allowlist *ratelimit.DynamicAllowlist,
consulURL *url.URL, consulURL *url.URL,
errColl errcoll.Interface,
) (l *AllowlistRefresher, err error) { ) (l *AllowlistRefresher, err error) {
l = &AllowlistRefresher{ l = &AllowlistRefresher{
allowlist: allowlist, allowlist: allowlist,
@ -36,7 +39,8 @@ func NewAllowlistRefresher(
// TODO(a.garipov): Consider making configurable. // TODO(a.garipov): Consider making configurable.
Timeout: 15 * time.Second, Timeout: 15 * time.Second,
}), }),
url: consulURL, url: consulURL,
errColl: errColl,
} }
err = l.Refresh(context.Background()) err = l.Refresh(context.Background())
@ -53,6 +57,10 @@ var _ agdservice.Refresher = (*AllowlistRefresher)(nil)
// Refresh implements the [agdservice.Refresher] interface for // Refresh implements the [agdservice.Refresher] interface for
// *AllowlistRefresher. // *AllowlistRefresher.
func (l *AllowlistRefresher) Refresh(ctx context.Context) (err error) { 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() { defer func() {
metrics.ConsulAllowlistUpdateTime.SetToCurrentTime() metrics.ConsulAllowlistUpdateTime.SetToCurrentTime()
metrics.SetStatusGauge(metrics.ConsulAllowlistUpdateStatus, err) metrics.SetStatusGauge(metrics.ConsulAllowlistUpdateStatus, err)
@ -60,7 +68,10 @@ func (l *AllowlistRefresher) Refresh(ctx context.Context) (err error) {
consulNets, err := l.loadConsul(ctx) consulNets, err := l.loadConsul(ctx)
if err != nil { 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) log.Info("allowlist: loaded %d records from %s", len(consulNets), l.url)

View File

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

View File

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

View File

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

View File

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

View File

@ -31,7 +31,7 @@ func TestDefault_ServeHTTP(t *testing.T) {
successHdr := http.Header{ successHdr := http.Header{
httphdr.ContentType: []string{agdhttp.HdrValTextCSV}, httphdr.ContentType: []string{agdhttp.HdrValTextCSV},
httphdr.Trailer: []string{httphdr.XError}, 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) { 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(), (&url.URL{Scheme: "http", Host: "example.com"}).String(),
nil, nil,
) )
r.Header.Add(httphdr.AcceptEncoding, "gzip") r.Header.Add(httphdr.AcceptEncoding, agdhttp.HdrValGzip)
for _, tc := range testCases { for _, tc := range testCases {
db := dnsdb.New(&dnsdb.DefaultConfig{ db := dnsdb.New(&dnsdb.DefaultConfig{

View File

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

View File

@ -17,11 +17,15 @@ type BlockingMode interface {
// BlockingModeCustomIP makes the [dnsmsg.Constructor] return responses with // BlockingModeCustomIP makes the [dnsmsg.Constructor] return responses with
// custom IP addresses to A and AAAA requests. For all other types of requests, // 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 // as well as in case the address corresponding to IP version is not set, it
// answers (aka NODATA). // returns a response with no answers (aka NODATA).
type BlockingModeCustomIP struct { type BlockingModeCustomIP struct {
IPv4 netip.Addr // IPv4 is a slice of valid IPv4 addresses used in responses to A requests.
IPv6 netip.Addr 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 // 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 // Cloner is a pool that can clone common parts of DNS messages with fewer
// allocations. // allocations.
// //
// TODO(a.garipov): Use in filtering when cloning a [filter.ResultModified] // TODO(a.garipov): Use in filtering when cloning a
// message. // [filter.ResultModifiedResponse] message.
// //
// TODO(a.garipov): Use in [Constructor]. // TODO(a.garipov): Use in [Constructor].
type Cloner struct { type Cloner struct {

View File

@ -328,7 +328,7 @@ func BenchmarkClone(b *testing.B) {
b.ReportAllocs() b.ReportAllocs()
b.ResetTimer() b.ResetTimer()
for i := 0; i < b.N; i++ { for range b.N {
msgSink = dnsmsg.Clone(tc.msg) msgSink = dnsmsg.Clone(tc.msg)
} }
@ -372,7 +372,7 @@ func BenchmarkCloner_Clone(b *testing.B) {
b.ReportAllocs() b.ReportAllocs()
b.ResetTimer() b.ResetTimer()
for i := 0; i < b.N; i++ { for i := range b.N {
msgSink = c.Clone(tc.msg) msgSink = c.Clone(tc.msg)
if i < b.N-1 { if i < b.N-1 {
// Don't dispose of the last one to be sure that we can // 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 // NewBlockedRespMsg returns a blocked DNS response message based on the
// constructor's blocking mode. // constructor's blocking mode.
func (c *Constructor) NewBlockedRespMsg(req *dns.Msg) (msg *dns.Msg, err error) { 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) { ) (msg *dns.Msg, err error) {
switch qt := req.Question[0].Qtype; qt { switch qt := req.Question[0].Qtype; qt {
case dns.TypeA: case dns.TypeA:
if m.IPv4.IsValid() { if len(m.IPv4) > 0 {
return c.NewIPRespMsg(req, m.IPv4) return c.NewIPRespMsg(req, m.IPv4...)
} }
case dns.TypeAAAA: case dns.TypeAAAA:
if m.IPv6.IsValid() { if len(m.IPv6) > 0 {
return c.NewIPRespMsg(req, m.IPv6) return c.NewIPRespMsg(req, m.IPv6...)
} }
default: default:
// Go on. // 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. // TODO(a.garipov): Use slices.Chunk in Go 1.23.
newStr := make([]string, strNum) newStr := make([]string, strNum)
for i := 0; i < strNum; i++ { for i := range strNum {
start := i * MaxTXTStringLen start := i * MaxTXTStringLen
var cutStr string var cutStr string

View File

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

View File

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

View File

@ -14,7 +14,7 @@ import (
) )
// Handler is an interface that defines how the DNS server would process DNS // Handler is an interface that defines how the DNS server would process DNS
// queries. Inspired by net/http.Server and it's Handler. // queries. Inspired by net/http.Server and it's Handler.
type Handler interface { type Handler interface {
// ServeDNS should process the request and write a DNS response to the // ServeDNS should process the request and write a DNS response to the
// specified ResponseWriter. // specified ResponseWriter.
@ -28,7 +28,7 @@ type Handler interface {
} }
// The HandlerFunc type is an adapter to allow the use of ordinary functions // The HandlerFunc type is an adapter to allow the use of ordinary functions
// as DNS handlers. If f is a function with the appropriate signature, // as DNS handlers. If f is a function with the appropriate signature,
// HandlerFunc(f) is a Handler that calls f. // HandlerFunc(f) is a Handler that calls f.
type HandlerFunc func(context.Context, ResponseWriter, *dns.Msg) (err error) type HandlerFunc func(context.Context, ResponseWriter, *dns.Msg) (err error)

View File

@ -1,9 +1,7 @@
package dnsserver_test package dnsserver_test
import ( import (
"context"
"testing" "testing"
"time"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver" "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver"
"github.com/AdguardTeam/golibs/testutil" "github.com/AdguardTeam/golibs/testutil"
@ -15,16 +13,3 @@ func TestMain(m *testing.M) {
// testTimeout is a common timeout for tests. // testTimeout is a common timeout for tests.
const testTimeout = dnsserver.DefaultReadTimeout 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() ip := netutil.IPv4Localhost().Prev()
for i := 0; i < recordsCount; i++ { for range recordsCount {
// Add 1 to make sure that each IP is valid. // Add 1 to make sure that each IP is valid.
ip = ip.Next() ip = ip.Next()
ans = append(ans, &dns.A{Hdr: hdr, A: ip.AsSlice()}) 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/AdGuardDNS/internal/dnsserver"
"github.com/AdguardTeam/golibs/errors" "github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/miekg/dns" "github.com/miekg/dns"
"golang.org/x/exp/rand" "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 // upstreams is detected to be up again, requests are redirected back to the
// main upstreams. // main upstreams.
func (h *Handler) Refresh(ctx context.Context) (err error) { 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) return h.refresh(ctx, false)
} }

View File

@ -21,18 +21,6 @@ func TestMain(m *testing.M) {
// testTimeout is the timeout for tests. // testTimeout is the timeout for tests.
const testTimeout = 1 * time.Second 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) { func TestHandler_ServeDNS(t *testing.T) {
srv, addr := dnsservertest.RunDNSServer(t, dnsservertest.DefaultHandler()) srv, addr := dnsservertest.RunDNSServer(t, dnsservertest.DefaultHandler())
@ -49,7 +37,7 @@ func TestHandler_ServeDNS(t *testing.T) {
rw := dnsserver.NewNonWriterResponseWriter(srv.LocalUDPAddr(), srv.LocalUDPAddr()) rw := dnsserver.NewNonWriterResponseWriter(srv.LocalUDPAddr(), srv.LocalUDPAddr())
// Check the handler's ServeDNS method // 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) require.NoError(t, err)
res := rw.Msg() res := rw.Msg()

View File

@ -9,6 +9,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver" "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/dnsservertest" "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/dnsservertest"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/forward" "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/forward"
"github.com/AdguardTeam/golibs/testutil"
"github.com/miekg/dns" "github.com/miekg/dns"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@ -63,32 +64,30 @@ func TestHandler_Refresh(t *testing.T) {
req := dnsservertest.CreateMessage("example.org.", dns.TypeA) req := dnsservertest.CreateMessage("example.org.", dns.TypeA)
rw := dnsserver.NewNonWriterResponseWriter(fallback.LocalUDPAddr(), fallback.LocalUDPAddr()) rw := dnsserver.NewNonWriterResponseWriter(fallback.LocalUDPAddr(), fallback.LocalUDPAddr())
ctx := context.Background() err := handler.ServeDNS(testutil.ContextWithTimeout(t, testTimeout), rw, req)
err := handler.ServeDNS(newTimeoutCtx(t, ctx), rw, req)
require.Error(t, err) require.Error(t, err)
assert.Equal(t, int64(2), upstreamRequestsCount.Load()) assert.Equal(t, int64(2), upstreamRequestsCount.Load())
err = handler.Refresh(newTimeoutCtx(t, ctx)) err = handler.Refresh(testutil.ContextWithTimeout(t, testTimeout))
require.Error(t, err) require.Error(t, err)
assert.Equal(t, int64(4), upstreamRequestsCount.Load()) 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) require.NoError(t, err)
assert.Equal(t, int64(4), upstreamRequestsCount.Load()) assert.Equal(t, int64(4), upstreamRequestsCount.Load())
// Now, set upstream up. // Now, set upstream up.
upstreamIsUp.Store(true) upstreamIsUp.Store(true)
err = handler.ServeDNS(newTimeoutCtx(t, ctx), rw, req) err = handler.ServeDNS(testutil.ContextWithTimeout(t, testTimeout), rw, req)
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, int64(4), upstreamRequestsCount.Load()) assert.Equal(t, int64(4), upstreamRequestsCount.Load())
err = handler.Refresh(newTimeoutCtx(t, ctx)) err = handler.Refresh(testutil.ContextWithTimeout(t, testTimeout))
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, int64(5), upstreamRequestsCount.Load()) 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) require.NoError(t, err)
assert.Equal(t, int64(6), upstreamRequestsCount.Load()) 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) defer log.OnCloserError(u, log.DEBUG)
req := dnsservertest.CreateMessage("example.org.", dns.TypeA) 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.NoError(t, err)
require.NotNil(t, res) require.NotNil(t, res)
dnsservertest.RequireResponse(t, req, res, 1, dns.RcodeSuccess, false) 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) defer log.OnCloserError(uUDP, log.DEBUG)
ctx := context.Background() res, nw, err := uUDP.Exchange(testutil.ContextWithTimeout(t, testTimeout), req)
res, nw, err := uUDP.Exchange(newTimeoutCtx(t, ctx), req)
require.NoError(t, err) require.NoError(t, err)
dnsservertest.RequireResponse(t, req, res, 0, dns.RcodeSuccess, true) 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) 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) require.NoError(t, err)
dnsservertest.RequireResponse(t, req, res, 1, dns.RcodeSuccess, false) 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) 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) require.NoError(t, err)
dnsservertest.RequireResponse(t, req, res, 1, dns.RcodeSuccess, false) 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 resp *dns.Msg
var err error var err error
go func() { 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) testutil.RequireSend(pt, respCh, struct{}{}, testTimeout)
}() }()
@ -266,7 +265,8 @@ func TestUpstreamPlain_Exchange_fallbackSuccess(t *testing.T) {
var actualResp *dns.Msg var actualResp *dns.Msg
var err error var err error
go func() { 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) testutil.RequireSend(pt, respCh, struct{}{}, testTimeout)
}() }()

View File

@ -1,44 +1,45 @@
module github.com/AdguardTeam/AdGuardDNS/internal/dnsserver module github.com/AdguardTeam/AdGuardDNS/internal/dnsserver
go 1.21.8 go 1.22.4
require ( require (
github.com/AdguardTeam/golibs v0.20.1 github.com/AdguardTeam/golibs v0.23.2
github.com/ameshkov/dnscrypt/v2 v2.2.7 github.com/ameshkov/dnscrypt/v2 v2.3.0
github.com/ameshkov/dnsstamps v1.0.3 github.com/ameshkov/dnsstamps v1.0.3
github.com/bluele/gcache v0.0.2 github.com/bluele/gcache v0.0.2
github.com/miekg/dns v1.1.58 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/patrickmn/go-cache v2.1.1-0.20191004192108-46f407853014+incompatible
github.com/prometheus/client_golang v1.18.0 github.com/prometheus/client_golang v1.19.0
github.com/quic-go/quic-go v0.41.0 github.com/quic-go/quic-go v0.42.0
github.com/stretchr/testify v1.8.4 github.com/stretchr/testify v1.9.0
golang.org/x/exp v0.0.0-20240213143201-ec583247a57a golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8
golang.org/x/net v0.21.0 golang.org/x/net v0.24.0
golang.org/x/sys v0.17.0 golang.org/x/sys v0.19.0
) )
require ( require (
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 // indirect github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 // indirect
github.com/beorn7/perks v1.0.1 // 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/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/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/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/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_model v0.5.0 // indirect github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.46.0 // indirect github.com/prometheus/common v0.52.3 // 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 github.com/quic-go/qpack v0.4.0 // indirect
go.uber.org/mock v0.4.0 // indirect go.uber.org/mock v0.4.0 // indirect
golang.org/x/crypto v0.19.0 // indirect golang.org/x/crypto v0.22.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/text v0.14.0 // indirect
golang.org/x/tools v0.18.0 // indirect golang.org/x/tools v0.20.0 // indirect
google.golang.org/protobuf v1.32.0 // indirect google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // 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.23.2 h1:rMjYantwtQ39e8G4zBQ6ZLlm4s3XH30Bc9VxhoOHwao=
github.com/AdguardTeam/golibs v0.20.1/go.mod h1:bgcMgRviCKyU6mkrX+RtT/OsKPFzyppelfRsksMG3KU= 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 h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY=
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA= 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 h1:52m0LGchQBBVqJRyYYufQuIbVqRawmubW3OFGqK1ekw=
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635/go.mod h1:lmLxL+FV291OopO93Bwf9fQLQeLyt33VJRUg5VJ30us= github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635/go.mod h1:lmLxL+FV291OopO93Bwf9fQLQeLyt33VJRUg5VJ30us=
github.com/ameshkov/dnscrypt/v2 v2.2.7 h1:aEitLIR8HcxVodZ79mgRcCiC0A0I5kZPBuWGFwwulAw= github.com/ameshkov/dnscrypt/v2 v2.3.0 h1:pDXDF7eFa6Lw+04C0hoMh8kCAQM8NwUdFEllSP2zNLs=
github.com/ameshkov/dnscrypt/v2 v2.2.7/go.mod h1:qPWhwz6FdSmuK7W4sMyvogrez4MWdtzosdqlr0Rg3ow= 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 h1:Srzik+J9mivH1alRACTbys2xOxs0lRH9qnTA7Y1OYVo=
github.com/ameshkov/dnsstamps v1.0.3/go.mod h1:Ii3eUu73dx4Vw5O4wjzmT5+lkCwovjzaEZZ4gKyIH5A= 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 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 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 h1:WcbfdXICg7G/DGBh1PFfcirkWOQV+v077yF1pSy3DGw=
github.com/bluele/gcache v0.0.2/go.mod h1:m15KV+ECjptwSPxKhOhQoAFQVtUFjTVkc3H8o0t/fp0= 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.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 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/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.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 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-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 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= 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.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 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 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 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-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo=
github.com/google/pprof v0.0.0-20240207164012-fb44976bdcd5/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= 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 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= 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 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4= 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/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.17.1 h1:V++EzdbhI4ZV4ev0UTIj0PzhzOcReJFyJaLjtSF55M8=
github.com/onsi/ginkgo/v2 v2.15.0/go.mod h1:HlxMHtYF57y6Dpf+mc5529KKmSq9h2FpCF+/ZkwUxKM= 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 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8=
github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= 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.1 h1:Q5vh5xohbsZXGcD6hhszzGqB7jSSc2/CRr3QKIga8Kw=
github.com/panjf2000/ants/v2 v2.9.0/go.mod h1:7ZxyxsqE4vvW0M7LSD8aI3cKwgFhBHbxnlN8mDqHa1I= 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 h1:IWzUvJ72xMjmrjR9q3H1PF+jwdN0uNQiR2t1BLNalyo=
github.com/patrickmn/go-cache v2.1.1-0.20191004192108-46f407853014+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= 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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 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.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU=
github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA= github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k=
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/common v0.46.0 h1:doXzt5ybi1HBKpsZOL0sSkaNHJJqkyfEWZGGqqScV0Y= github.com/prometheus/common v0.52.3 h1:5f8uj6ZwHSscOGNdIQg6OiZv/ybiK2CO2q2drVZAQSA=
github.com/prometheus/common v0.46.0/go.mod h1:Tp0qkxpb9Jsg54QMe+EAmqXkSV7Evdy1BTn+g2pa/hQ= github.com/prometheus/common v0.52.3/go.mod h1:BrxBKv3FWBIGXw89Mg1AeBq7FSyRzXWI3l3e7W3RN5U=
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= github.com/prometheus/procfs v0.13.0 h1:GqzLlQyfsPbaEHaQkO7tbDlriv/4o5Hudv6OXHGKX7o=
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= 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 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= 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.42.0 h1:uSfdap0eveIl8KXnipv9K7nlwZ5IqLlYOpJ58u5utpM=
github.com/quic-go/quic-go v0.41.0/go.mod h1:qCkNjqczPEvgsOnxZ0eCD14lv+B2LHlFAB++CNOh9hA= 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 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= 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= 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.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.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.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 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 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= 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.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
golang.org/x/exp v0.0.0-20240213143201-ec583247a57a h1:HinSgX1tJRX3KsL//Gxynpw5CTOAIPhgL4W8PNiIpVE= golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8 h1:ESSUROHIBHg7USnszlcdmjBEwdMj9VUvU+OPk4yl2mc=
golang.org/x/exp v0.0.0-20240213143201-ec583247a57a/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI=
golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= 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.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.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 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 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 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/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY=
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= 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 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 h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 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. // Try the test multiple times to reduce flakiness due to UDP failures.
var success4, success6 bool var success4, success6 bool
for i := 0; i < numTries; i++ { for i := range numTries {
var isTimeout4, isTimeout6 bool var isTimeout4, isTimeout6 bool
success4 = t.Run(fmt.Sprintf("ipv4_%d", i), func(t *testing.T) { 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}) isTimeout4 = testSessionPacketConn(t, "udp4", "0.0.0.0:0", net.IP{127, 0, 0, 1})

View File

@ -6,7 +6,7 @@ import (
) )
// Conn wraps a net.Conn and contains additional info that could be required // Conn wraps a net.Conn and contains additional info that could be required
// by the Pool instance. It can be used directly instead of a net.Conn or you // by the Pool instance. It can be used directly instead of a net.Conn or you
// may choose to use the underlying Conn.Conn instead. // may choose to use the underlying Conn.Conn instead.
type Conn struct { type Conn struct {
net.Conn net.Conn

View File

@ -14,7 +14,7 @@ import (
// ErrClosed indicates that the Pool is closed and cannot be used anymore. // ErrClosed indicates that the Pool is closed and cannot be used anymore.
const ErrClosed = errors.Error("the pool is closed") const ErrClosed = errors.Error("the pool is closed")
// Factory is a type for the Pool's factory method. Factory implementation // Factory is a type for the Pool's factory method. Factory implementation
// must use the context's deadline if it's specified. // must use the context's deadline if it's specified.
type Factory func(ctx context.Context) (conn net.Conn, err error) type Factory func(ctx context.Context) (conn net.Conn, err error)
@ -35,8 +35,8 @@ type Pool struct {
factory Factory factory Factory
} }
// NewPool creates a new Pool instance. maxCapacity configures the maximum // NewPool creates a new Pool instance. maxCapacity configures the maximum
// number of idle connections in the pool. If the pool is full, // number of idle connections in the pool. If the pool is full,
// Put will close the connection instead of adding it to the pool. // Put will close the connection instead of adding it to the pool.
func NewPool(maxCapacity int, factory Factory) (p *Pool) { func NewPool(maxCapacity int, factory Factory) (p *Pool) {
return &Pool{ return &Pool{
@ -45,7 +45,7 @@ func NewPool(maxCapacity int, factory Factory) (p *Pool) {
} }
} }
// Get returns a free connection from the pool. If there are no connections it // Get returns a free connection from the pool. If there are no connections it
// will use the Factory method to create a new one. // will use the Factory method to create a new one.
func (p *Pool) Get(ctx context.Context) (conn *Conn, err error) { func (p *Pool) Get(ctx context.Context) (conn *Conn, err error) {
p.connsChanMu.RLock() p.connsChanMu.RLock()
@ -80,7 +80,7 @@ func (p *Pool) Get(ctx context.Context) (conn *Conn, err error) {
} }
} }
// Put puts the connection back to the pool. If the pool is closed, // Put puts the connection back to the pool. If the pool is closed,
// the connection will be simply closed instead. // the connection will be simply closed instead.
func (p *Pool) Put(conn *Conn) (err error) { func (p *Pool) Put(conn *Conn) (err error) {
p.connsChanMu.RLock() p.connsChanMu.RLock()
@ -101,7 +101,7 @@ func (p *Pool) Put(conn *Conn) (err error) {
} }
} }
// Close closes the Pool. After that it cannot be used anymore, every method // Close closes the Pool. After that it cannot be used anymore, every method
// will return ErrClosed. // will return ErrClosed.
func (p *Pool) Close() (err error) { func (p *Pool) Close() (err error) {
p.connsChanMu.Lock() p.connsChanMu.Lock()
@ -126,7 +126,7 @@ func (p *Pool) Close() (err error) {
return errors.Annotate(errors.Join(errs...), "closing pool: %w") return errors.Annotate(errors.Join(errs...), "closing pool: %w")
} }
// closeConn is used when the pool is closed. In this case we attempt to close // closeConn is used when the pool is closed. In this case we attempt to close
// the connection immediately. // the connection immediately.
func (p *Pool) closeConn(conn *Conn) (err error) { func (p *Pool) closeConn(conn *Conn) (err error) {
err = conn.Close() err = conn.Close()

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 // Pass 10 requests through the middleware. This way we'll increment and
// set both hits and misses. // set both hits and misses.
for i := 0; i < 10; i++ { for range 10 {
ctx := dnsserver.ContextWithServerInfo(context.Background(), testServerInfo) ctx := dnsserver.ContextWithServerInfo(context.Background(), testServerInfo)
ctx = dnsserver.ContextWithRequestInfo(ctx, &dnsserver.RequestInfo{ ctx = dnsserver.ContextWithRequestInfo(ctx, &dnsserver.RequestInfo{
StartTime: time.Now(), StartTime: time.Now(),

View File

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

View File

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

View File

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

View File

@ -85,7 +85,7 @@ func (p Protocol) IsStdEncrypted() (ok bool) {
// Used for a kind of validation. // Used for a kind of validation.
type Network string type Network string
// Network enum members. Note that we use "tcp" and "udp" strings so that // Network enum members. Note that we use "tcp" and "udp" strings so that
// we could use these constants when calling golang net package functions. // we could use these constants when calling golang net package functions.
const ( const (
NetworkTCP Network = "tcp" NetworkTCP Network = "tcp"

View File

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

View File

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

View File

@ -268,7 +268,7 @@ func (s *ServerBase) dispose(rw ResponseWriter, resp *dns.Msg) {
} }
// serveDNSMsgInternal serves the DNS request and uses recorder as a // serveDNSMsgInternal serves the DNS request and uses recorder as a
// ResponseWriter. This method is supposed to be called from serveDNSMsg, // ResponseWriter. This method is supposed to be called from serveDNSMsg,
// the recorded response is used for counting metrics. // the recorded response is used for counting metrics.
func (s *ServerBase) serveDNSMsgInternal( func (s *ServerBase) serveDNSMsgInternal(
ctx context.Context, ctx context.Context,

View File

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

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