mirror of
https://github.com/AdguardTeam/AdGuardDNS.git
synced 2025-02-20 11:23:36 +08:00
Sync v2.5.1
This commit is contained in:
parent
cfb4caf935
commit
150f2d733f
242
CHANGELOG.md
242
CHANGELOG.md
@ -11,6 +11,242 @@ The format is **not** based on [Keep a Changelog][kec], since the project
|
||||
|
||||
|
||||
|
||||
## AGDNS-1761 / Build 702
|
||||
|
||||
* The property `upstream` has been modified. Its property `timeout` has been
|
||||
replaced with the new property `servers.timeout` for each server in the
|
||||
`servers` list. Concomitantly the `fallback.timeout` has been replaced with
|
||||
`fallback.servers.timeout` for each fallback server. The `fallback.servers`
|
||||
now supports not only the addresses of the servers, but URLs in the
|
||||
`[scheme://]ip:port` format like it's done with the main servers. So replace
|
||||
this:
|
||||
|
||||
```yaml
|
||||
upstream:
|
||||
# …
|
||||
servers:
|
||||
- 'tcp://1.1.1.1:53'
|
||||
- '127.0.0.1:5358'
|
||||
timeout: 2s
|
||||
fallback:
|
||||
servers:
|
||||
- 8.8.4.4:53
|
||||
timeout: 1s
|
||||
```
|
||||
|
||||
with this:
|
||||
|
||||
```yaml
|
||||
upstream:
|
||||
# …
|
||||
servers:
|
||||
- address: 'tcp://1.1.1.1:53'
|
||||
timeout: 2s
|
||||
- address: '127.0.0.1:5358'
|
||||
timeout: 2s
|
||||
fallback:
|
||||
servers:
|
||||
- address: '8.8.4.4:53'
|
||||
timeout: 1s
|
||||
```
|
||||
|
||||
Adjust the value and add new ones, if necessary.
|
||||
|
||||
|
||||
|
||||
## AGDNS-698 / Build 701
|
||||
|
||||
* The object `dns` has new properties: `read_timeout`, `tcp_idle_timeout`, and
|
||||
`write_timeout`. So replace this:
|
||||
|
||||
```yaml
|
||||
dns:
|
||||
max_udp_response_size: 1024B
|
||||
```
|
||||
|
||||
with this:
|
||||
|
||||
```yaml
|
||||
dns:
|
||||
read_timeout: 2s
|
||||
tcp_idle_timeout: 30s
|
||||
write_timeout: 2s
|
||||
handle_timeout: 1s
|
||||
max_udp_response_size: 1024B
|
||||
```
|
||||
|
||||
The values in the example are previous defaults.
|
||||
|
||||
|
||||
|
||||
## AGDNS-1751 / Build 691
|
||||
|
||||
* The property `upstream.server` has been removed. Its former content is
|
||||
moved to the newly added property `servers`, which now extended to contain
|
||||
a list of URLs of main upstream servers. So replace this:
|
||||
|
||||
```yaml
|
||||
upstream:
|
||||
# …
|
||||
server: `8.8.8.8:53`
|
||||
```
|
||||
|
||||
with this:
|
||||
|
||||
```yaml
|
||||
upstream:
|
||||
# …
|
||||
servers:
|
||||
- `8.8.8.8:53`
|
||||
```
|
||||
|
||||
Adjust the value and add new ones, if necessary.
|
||||
|
||||
|
||||
|
||||
## AGDNS-1759 / Build 684
|
||||
|
||||
* The object `backend` has a new property, `full_refresh_retry_interval`. So
|
||||
replace this:
|
||||
|
||||
```yaml
|
||||
backend:
|
||||
# …
|
||||
full_refresh_interval: 24h
|
||||
```
|
||||
|
||||
with this:
|
||||
|
||||
```yaml
|
||||
backend:
|
||||
# …
|
||||
full_refresh_interval: 24h
|
||||
full_refresh_retry_interval: 1h
|
||||
```
|
||||
|
||||
Adjust the value, if necessary.
|
||||
|
||||
|
||||
|
||||
## AGDNS-1744 / Build 681
|
||||
|
||||
* Metric `forward_request_total` has a new label `network`. This label
|
||||
describes the network type (`tcp` or `udp`), over which an upstream has
|
||||
finished processing request.
|
||||
|
||||
|
||||
|
||||
## AGDNS-1738 / Build 678
|
||||
|
||||
* Object `dns` has a new property, describing maximum size of DNS response
|
||||
over UDP protocol.
|
||||
|
||||
```yaml
|
||||
dns:
|
||||
max_udp_response_size: 1024B
|
||||
handle_timeout: 1s
|
||||
```
|
||||
|
||||
|
||||
|
||||
## AGDNS-1735 / Build 677
|
||||
|
||||
* The property `upstream.fallback` has been changed. Its former content is
|
||||
moved to the newly added property `servers`. The new property `timeout`,
|
||||
which describes query timeout to fallback servers, was added. So replace
|
||||
this:
|
||||
|
||||
```yaml
|
||||
upstream:
|
||||
fallback:
|
||||
- 1.1.1.1:53
|
||||
- 8.8.8.8:53
|
||||
```
|
||||
|
||||
with this:
|
||||
|
||||
```yaml
|
||||
upstream:
|
||||
fallback:
|
||||
servers:
|
||||
- 1.1.1.1:53
|
||||
- 8.8.8.8:53
|
||||
timeout: 1s
|
||||
```
|
||||
|
||||
Adjust the new values, if necessary. Note that the query timeout to fallback
|
||||
servers was previously defined with `upstream.timeout` property, which now
|
||||
describes the query timeout to the primary servers only.
|
||||
|
||||
|
||||
|
||||
## AGDNS-1178 / Build 676
|
||||
|
||||
* The new object `dns` has been added:
|
||||
|
||||
```yaml
|
||||
dns:
|
||||
handle_timeout: 1s
|
||||
```
|
||||
|
||||
|
||||
|
||||
## AGDNS-1620 / Build 673
|
||||
|
||||
* Object `ratelimit` has two new properties: `quic` and `tcp`. They configure
|
||||
QUIC and TCP connection limits. Example configuration:
|
||||
|
||||
```yaml
|
||||
ratelimit:
|
||||
# …
|
||||
quic:
|
||||
enabled: true
|
||||
max_streams_per_peer: 100
|
||||
tcp:
|
||||
enabled: true
|
||||
max_pipeline_count: 100
|
||||
```
|
||||
|
||||
|
||||
|
||||
## AGDNS-1684 / Build 661
|
||||
|
||||
* Profile's file cache version was incremented. The new field `access` has
|
||||
been added.
|
||||
|
||||
|
||||
|
||||
## AGDNS-1664 / Build 636
|
||||
|
||||
* The environment variables `BILLSTAT_URL` and `PROFILES_URL` no longer
|
||||
support HTTP(s) endpoints. Use GRPC(S) instead.
|
||||
|
||||
|
||||
|
||||
## AGDNS-1667 / Build 633
|
||||
|
||||
* `ratelimit` configuration properties `back_off_count`, `back_off_duration`
|
||||
and `back_off_period` have been renamed to `backoff_count`,
|
||||
`backoff_duration` and `backoff_period`. So replace this:
|
||||
|
||||
```yaml
|
||||
ratelimit:
|
||||
back_off_period: 10m
|
||||
back_off_count: 1000
|
||||
back_off_duration: 30m
|
||||
```
|
||||
|
||||
with this:
|
||||
|
||||
```yaml
|
||||
ratelimit:
|
||||
backoff_period: 10m
|
||||
backoff_count: 1000
|
||||
backoff_duration: 30m
|
||||
```
|
||||
|
||||
|
||||
|
||||
## AGDNS-1607 / Build 617
|
||||
|
||||
* New configuration `access` has been added, it has an a list of AdBlock rules
|
||||
@ -33,6 +269,7 @@ The format is **not** based on [Keep a Changelog][kec], since the project
|
||||
|
||||
* Added a new metric `bill_stat_upload_duration` that counts the duration of
|
||||
billing statistics upload.
|
||||
|
||||
* The environment variable `BILLSTAT_URL`, which describes the endpoint for
|
||||
backend billing statistics uploader API, now supports GRPC endpoints.
|
||||
|
||||
@ -57,7 +294,7 @@ The format is **not** based on [Keep a Changelog][kec], since the project
|
||||
|
||||
* The optional property `bind_interfaces` of `server_groups.*.servers`
|
||||
objects has been changed, property `subnet` is now an array and has been
|
||||
ranamed to `subnets`. So replace this:
|
||||
renamed to `subnets`. So replace this:
|
||||
|
||||
```yaml
|
||||
bind_interfaces:
|
||||
@ -98,6 +335,7 @@ The format is **not** based on [Keep a Changelog][kec], since the project
|
||||
## AGDNS-1580 / Build 562
|
||||
|
||||
* The environment variable `DNSDB_PATH` has been removed.
|
||||
|
||||
* New configuration `dnsdb` has been added, it has an enabled/disabled flag
|
||||
and the property `max_size` which describes the maximum amount of records in
|
||||
the in-memory buffer. Example configuration:
|
||||
@ -944,7 +1182,7 @@ The format is **not** based on [Keep a Changelog][kec], since the project
|
||||
identifiers, grouped by endpoint identifier and known server names. All
|
||||
unknown server names are grouped in `other` label:
|
||||
|
||||
```
|
||||
```none
|
||||
# TYPE dns_tls_handshake_total counter
|
||||
dns_tls_handshake_total{cipher_suite="TLS_AES_128_GCM_SHA256",did_resume="0",negotiated_proto="",proto="tls",server_name="default_dot: other",tls_version="tls1.3"} 4
|
||||
```
|
||||
|
10
Makefile
10
Makefile
@ -23,6 +23,7 @@ VERBOSE.MACRO = $${VERBOSE:-0}
|
||||
BRANCH = $$( git rev-parse --abbrev-ref HEAD )
|
||||
GOAMD64 = v1
|
||||
GOPROXY = https://goproxy.cn|https://proxy.golang.org|direct
|
||||
GOTOOLCHAIN = go1.21.5
|
||||
RACE = 0
|
||||
REVISION = $$( git rev-parse --short HEAD )
|
||||
VERSION = 0
|
||||
@ -32,6 +33,7 @@ ENV = env\
|
||||
GO="$(GO.MACRO)"\
|
||||
GOAMD64='$(GOAMD64)'\
|
||||
GOPROXY='$(GOPROXY)'\
|
||||
GOTOOLCHAIN='$(GOTOOLCHAIN)'\
|
||||
PATH="$${PWD}/bin:$$( "$(GO.MACRO)" env GOPATH )/bin:$${PATH}"\
|
||||
RACE='$(RACE)'\
|
||||
REVISION="$(REVISION)"\
|
||||
@ -51,6 +53,7 @@ test: go-test
|
||||
go-bench: ; $(ENV) "$(SHELL)" ./scripts/make/go-bench.sh
|
||||
go-build: ; $(ENV) "$(SHELL)" ./scripts/make/go-build.sh
|
||||
go-deps: ; $(ENV) "$(SHELL)" ./scripts/make/go-deps.sh
|
||||
go-fuzz: ; $(ENV) "$(SHELL)" ./scripts/make/go-fuzz.sh
|
||||
go-gen: ; $(ENV) "$(SHELL)" ./scripts/make/go-gen.sh
|
||||
go-lint: ; $(ENV) "$(SHELL)" ./scripts/make/go-lint.sh
|
||||
go-test: ; $(ENV) RACE='1' "$(SHELL)" ./scripts/make/go-test.sh
|
||||
@ -70,4 +73,11 @@ go-os-check:
|
||||
|
||||
txt-lint: ; $(ENV) "$(SHELL)" ./scripts/make/txt-lint.sh
|
||||
|
||||
# TODO(a.garipov): Consider adding to scripts/ and the common project
|
||||
# structure.
|
||||
go-upd-tools:
|
||||
cd ./internal/tools/ &&\
|
||||
"$(GO.MACRO)" get -u &&\
|
||||
"$(GO.MACRO)" mod tidy
|
||||
|
||||
sync-github: ; $(ENV) "$(SHELL)" ./scripts/make/github-sync.sh
|
||||
|
@ -24,15 +24,13 @@ ratelimit:
|
||||
subnet_key_len: 48
|
||||
# The time during which to count the number of times a client has hit the
|
||||
# rate limit for a back off.
|
||||
#
|
||||
# TODO(a.garipov): Rename to "backoff_period" along with others.
|
||||
back_off_period: 10m
|
||||
backoff_period: 10m
|
||||
# How many times a client hits the rate limit before being held in the back
|
||||
# off.
|
||||
back_off_count: 1000
|
||||
backoff_count: 1000
|
||||
# How much a client that has hit the rate limit too often stays in the back
|
||||
# off.
|
||||
back_off_duration: 30m
|
||||
backoff_duration: 30m
|
||||
|
||||
# Configuration for the allowlist.
|
||||
allowlist:
|
||||
@ -52,6 +50,19 @@ ratelimit:
|
||||
stop: 1000
|
||||
resume: 800
|
||||
|
||||
# Configuration of QUIC streams limiting.
|
||||
quic:
|
||||
enabled: true
|
||||
# The maximum number of concurrent streams that a peer is allowed to
|
||||
# open.
|
||||
max_streams_per_peer: 100
|
||||
|
||||
# Configuration of TCP pipeline limiting.
|
||||
tcp:
|
||||
enabled: true
|
||||
# The maximum number of processing TCP messages per one connection.
|
||||
max_pipeline_count: 100
|
||||
|
||||
# Access settings.
|
||||
access:
|
||||
# Domains to block.
|
||||
@ -78,11 +89,17 @@ cache:
|
||||
|
||||
# DNS upstream configuration.
|
||||
upstream:
|
||||
server: '8.8.8.8:53'
|
||||
servers:
|
||||
- address: 'tcp://1.1.1.1:53'
|
||||
timeout: 2s
|
||||
- address: '8.8.4.4:53'
|
||||
timeout: 2s
|
||||
fallback:
|
||||
- 1.1.1.1:53
|
||||
- 8.8.8.8:53
|
||||
servers:
|
||||
- address: '1.1.1.1:53'
|
||||
timeout: 1s
|
||||
- address: '8.8.8.8:53'
|
||||
timeout: 1s
|
||||
healthcheck:
|
||||
enabled: true
|
||||
interval: 2s
|
||||
@ -90,6 +107,25 @@ upstream:
|
||||
backoff_duration: 30s
|
||||
domain_template: '${RANDOM}.neverssl.com'
|
||||
|
||||
# Common DNS settings.
|
||||
#
|
||||
# TODO(a.garipov): Consider making these settings per-server-group.
|
||||
dns:
|
||||
# The timeout for any read from a UDP connection or the first read from
|
||||
# a TCP/TLS connection. It currently doesn't affect DNSCrypt, QUIC, or
|
||||
# HTTPS.
|
||||
read_timeout: 2s
|
||||
# The timeout for consecutive reads from a TCP/TLS connection. It currently
|
||||
# doesn't affect DNSCrypt, QUIC, or HTTPS.
|
||||
tcp_idle_timeout: 30s
|
||||
# The timeout for writing to a UDP or TCP/TLS connection. It currently
|
||||
# doesn't affect DNSCrypt, QUIC, or HTTPS.
|
||||
write_timeout: 2s
|
||||
# The timeout for the entire handling of a single query.
|
||||
handle_timeout: 1s
|
||||
# UDP response size limit.
|
||||
max_udp_response_size: 1024B
|
||||
|
||||
# DNSDB configuration.
|
||||
dnsdb:
|
||||
enabled: true
|
||||
@ -106,6 +142,9 @@ backend:
|
||||
refresh_interval: 15s
|
||||
# How often AdGuard DNS performs full synchronization.
|
||||
full_refresh_interval: 24h
|
||||
# How long to wait before attempting a new full synchronization after a
|
||||
# failure.
|
||||
full_refresh_retry_interval: 1h
|
||||
# How often AdGuard DNS sends the billing statistics to the backend.
|
||||
bill_stat_interval: 15s
|
||||
|
||||
|
@ -15,6 +15,7 @@ configuration file with comments.
|
||||
* [Cache](#cache)
|
||||
* [Upstream](#upstream)
|
||||
* [Healthcheck](#upstream-healthcheck)
|
||||
* [Common DNS settings](#dns)
|
||||
* [DNSDB](#dnsdb)
|
||||
* [Backend](#backend)
|
||||
* [Query log](#query_log)
|
||||
@ -130,13 +131,13 @@ The `ratelimit` object has the following properties:
|
||||
|
||||
**Example:** `1KB`.
|
||||
|
||||
* <a href="#ratelimit-back_off_period" id="ratelimit-back_off_period" name="ratelimit-back_off_period">`back_off_period`</a>:
|
||||
* <a href="#ratelimit-backoff_period" id="ratelimit-backoff_period" name="ratelimit-backoff_period">`backoff_period`</a>:
|
||||
The time during which to count the number of requests that a client has sent
|
||||
over the RPS.
|
||||
|
||||
**Example:** `10m`.
|
||||
|
||||
* <a href="#ratelimit-back_off_duration" id="ratelimit-back_off_duration" name="ratelimit-back_off_duration">`back_off_duration`</a>:
|
||||
* <a href="#ratelimit-backoff_duration" id="ratelimit-backoff_duration" name="ratelimit-backoff_duration">`backoff_duration`</a>:
|
||||
How long a client that has hit the RPS too often stays in the backoff state.
|
||||
|
||||
**Example:** `30m`.
|
||||
@ -159,10 +160,10 @@ The `ratelimit` object has the following properties:
|
||||
The `ipv6` configuration object has the same properties as the `ipv4` one
|
||||
above.
|
||||
|
||||
* <a href="#ratelimit-back_off_count" id="ratelimit-back_off_count" name="ratelimit-back_off_count">`back_off_count`</a>:
|
||||
Maximum number of requests a client can make above the RPS within
|
||||
a `back_off_period`. When a client exceeds this limit, requests aren't
|
||||
allowed from client's subnet until `back_off_duration` ends.
|
||||
* <a href="#ratelimit-backoff_count" id="ratelimit-backoff_count" name="ratelimit-backoff_count">`backoff_count`</a>:
|
||||
Maximum number of requests a client can make above the RPS within a
|
||||
`backoff_period`. When a client exceeds this limit, requests aren't allowed
|
||||
from client's subnet until `backoff_duration` ends.
|
||||
|
||||
**Example:** `1000`.
|
||||
|
||||
@ -188,11 +189,11 @@ The `ratelimit` object has the following properties:
|
||||
|
||||
**Example:** `30s`.
|
||||
|
||||
For example, if `back_off_period` is `1m`, `back_off_count` is `10`, and
|
||||
For example, if `backoff_period` is `1m`, `backoff_count` is `10`, and
|
||||
`ipv4-rps` is `5`, a client (meaning all IP addresses within the subnet defined
|
||||
by `ipv4-subnet_key_len`) that made 15 requests in one second or 6 requests
|
||||
(one above `rps`) every second for 10 seconds within one minute, the client is
|
||||
blocked for `back_off_duration`.
|
||||
blocked for `backoff_duration`.
|
||||
|
||||
### <a href="#ratelimit-connection_limit" id="ratelimit-connection_limit" name="ratelimit-connection_limit">Stream connection limit</a>
|
||||
|
||||
@ -218,6 +219,35 @@ The `connection_limit` object has the following properties:
|
||||
|
||||
See also [notes on these parameters](#recommended-connection_limit).
|
||||
|
||||
### <a href="#ratelimit-quic" id="ratelimit-quic" name="ratelimit-quic">QUIC rate limiting</a>
|
||||
|
||||
The `quic` object has the following properties:
|
||||
|
||||
* <a href="#ratelimit-quic-enabled" id="ratelimit-quic-enabled" name="ratelimit-quic-enabled">`enabled`</a>:
|
||||
Whether or not the QUIC connections rate limiting should be enforced.
|
||||
|
||||
**Example:** `true`.
|
||||
|
||||
* <a href="#ratelimit-quic-max_streams_per_peer" id="ratelimit-quic-max_streams_per_peer" name="ratelimit-quic-max_streams_per_peer">`max_streams_per_peer`</a>:
|
||||
The maximum number of concurrent streams that a peer is allowed to open.
|
||||
|
||||
**Example:** `1000`.
|
||||
|
||||
### <a href="#ratelimit-tcp" id="ratelimit-tcp" name="ratelimit-tcp">TCP rate limiting</a>
|
||||
|
||||
The `tcp` object has the following properties:
|
||||
|
||||
* <a href="#ratelimit-tcp-enabled" id="ratelimit-tcp-enabled" name="ratelimit-tcp-enabled">`enabled`</a>:
|
||||
Whether or not the TCP rate limiting should be enforced.
|
||||
|
||||
**Example:** `true`.
|
||||
|
||||
* <a href="#ratelimit-tcp-max_pipeline_count" id="ratelimit-tcp-max_pipeline_count" name="ratelimit-tcp-max_pipeline_count">`max_pipeline_count`</a>:
|
||||
The maximum number of simultaneously processing TCP messages per one
|
||||
connection.
|
||||
|
||||
**Example:** `1000`.
|
||||
|
||||
[env-consul_allowlist_url]: environment.md#CONSUL_ALLOWLIST_URL
|
||||
|
||||
|
||||
@ -269,26 +299,45 @@ The `cache` object has the following properties:
|
||||
|
||||
The `upstream` object has the following properties:
|
||||
|
||||
* <a href="#upstream-server" id="upstream-server" name="upstream-server">`server`</a>:
|
||||
The URL of the main upstream server, in the `[scheme://]ip:port` format.
|
||||
* <a href="#upstream-servers" id="upstream-servers" name="upstream-servers">`servers`</a>:
|
||||
The array of the main upstream servers URLs, in the `[scheme://]ip:port`
|
||||
format and its timeouts for main upstream DNS requests, as a human-readable
|
||||
duration.
|
||||
|
||||
**Examples:**
|
||||
**Property example:**
|
||||
|
||||
- `8.8.8.8:53`: regular DNS (over UDP with TCP fallback).
|
||||
- `tcp://1.1.1.1:53`: regular DNS (over TCP).
|
||||
- `udp://1.1.1.1:53`: regular DNS (over UDP).
|
||||
|
||||
* <a href="#upstream-timeout" id="upstream-timeout" name="upstream-timeout">`timeout`</a>:
|
||||
Timeout for all outgoing DNS requests, as a human-readable duration.
|
||||
|
||||
**Example:** `2s`.
|
||||
```yaml
|
||||
'servers':
|
||||
# Regular DNS (over UDP with TCP fallback).
|
||||
- address: '8.8.8.8:53'
|
||||
timeout: 2s
|
||||
# Regular DNS (over TCP).
|
||||
- address: 'tcp://1.1.1.1:53'
|
||||
timeout: 2s
|
||||
# Regular DNS (over UDP).
|
||||
- address: 'udp://1.1.1.1:53'
|
||||
timeout: 2s
|
||||
```
|
||||
|
||||
* <a href="#upstream-fallback" id="upstream-fallback" name="upstream-fallback">`fallback`</a>:
|
||||
The array of addresses of the fallback upstream servers, in the `ip:port`
|
||||
format. These are use used in case a network error occurs while requesting
|
||||
the main upstream server.
|
||||
Fallback servers configuration. It has the following properties:
|
||||
|
||||
**Example:** `['1.1.1.1:53', '[2001:4860:4860::8888]:53']`.
|
||||
* <a href="#upstream-fallback-servers" id="upstream-fallback-servers" name="upstream-fallback-servers">`servers`</a>:
|
||||
The array of the fallback upstream servers URLs, in the
|
||||
`[scheme://]ip:port` format and its timeouts for upstream DNS requests,
|
||||
as a human-readable duration. These are use used in case a network error
|
||||
occurs while requesting the main upstream server. This property has the
|
||||
same format as [`upstream-servers`](#upstream-servers) above.
|
||||
|
||||
**Property example:**
|
||||
|
||||
```yaml
|
||||
'servers':
|
||||
- address: '1.1.1.1:53'
|
||||
timeout: 2s
|
||||
- address: '[2001:4860:4860::8888]:53'
|
||||
timeout: 2s
|
||||
```
|
||||
|
||||
* `healthcheck`: Healthcheck configuration. See
|
||||
[below](#upstream-healthcheck).
|
||||
@ -341,9 +390,46 @@ connection to the main upstream as restored, and requests are routed back to it.
|
||||
|
||||
|
||||
|
||||
## <a href="#dns" id="dns" name="dns">DNS</a>
|
||||
|
||||
The `dns` object has the following properties:
|
||||
|
||||
* <a href="#dns-read_timeout" id="dns-read_timeout" name="dns-read_timeout">`read_timeout`</a>:
|
||||
The timeout for any read from a UDP connection or the first read from
|
||||
a TCP/TLS connection, as a human-readable duration. It currently doesn't
|
||||
affect DNSCrypt, QUIC, or HTTPS.
|
||||
|
||||
**Example:** `2s`.
|
||||
|
||||
* <a href="#dns-tcp_idle_timeout" id="dns-tcp_idle_timeout" name="dns-tcp_idle_timeout">`tcp_idle_timeout`</a>:
|
||||
The timeout for consecutive reads from a TCP/TLS connection, as a
|
||||
human-readable duration. It currently doesn't affect DNSCrypt, QUIC, or
|
||||
HTTPS.
|
||||
|
||||
**Example:** `30s`.
|
||||
|
||||
* <a href="#dns-write_timeout" id="dns-write_timeout" name="dns-write_timeout">`write_timeout`</a>:
|
||||
The timeout for writing to a UDP or TCP/TLS connection, as a human-readable
|
||||
duration. It currently doesn't affect DNSCrypt, QUIC, or HTTPS.
|
||||
|
||||
**Example:** `2s`.
|
||||
|
||||
* <a href="#dns-handle_timeout" id="dns-handle_timeout" name="dns-handle_timeout">`handle_timeout`</a>:
|
||||
The timeout for the entire handling of a single query, as a human-readable
|
||||
duration.
|
||||
|
||||
**Example:** `1s`.
|
||||
|
||||
* <a href="#dns-max_udp_response_size" id="dns-max_udp_response_size" name="dns-max_udp_response_size">`max_udp_response_size`</a>:
|
||||
The maximum size of DNS response over UDP protocol.
|
||||
|
||||
**Example:** `1024B`.
|
||||
|
||||
|
||||
|
||||
## <a href="#dnsdb" id="dnsdb" name="dnsdb">DNSDB</a>
|
||||
|
||||
The `DNSDB` object has the following properties:
|
||||
The `dnsdb` object has the following properties:
|
||||
|
||||
* <a href="#dnsdb-enabled" id="dnsdb-enabled" name="dnsdb-enabled">`enabled`</a>:
|
||||
If true, the DNSDB memory buffer is enabled.
|
||||
@ -382,6 +468,13 @@ The `backend` object has the following properties:
|
||||
|
||||
**Example:** `24h`.
|
||||
|
||||
* <a href="#backend-full_refresh_retry_interval" id="backend-full_refresh_retry_interval" name="backend-full_refresh_retry_interval">`full_refresh_retry_interval`</a>:
|
||||
How long to wait before attempting a new full profile synchronization after
|
||||
a failure, as a human-readable duration. It is recommended to keep this
|
||||
value greater than [`refresh_interval`](#backend-refresh_interval).
|
||||
|
||||
**Example:** `1h`.
|
||||
|
||||
* <a href="#backend-bill_stat_interval" id="backend-bill_stat_interval" name="backend-bill_stat_interval">`bill_stat_interval`</a>:
|
||||
How often AdGuard DNS sends the billing statistics to the backend, as
|
||||
a human-readable duration.
|
||||
@ -552,9 +645,9 @@ The optional `web` object has the following properties:
|
||||
The optional listen addresses and optional TLS configuration for the web
|
||||
service in addition to the ones in the DNS-over-HTTPS handlers. The
|
||||
`certificates` array has the same format as the one in a server group's [TLS
|
||||
settings](#sg-*-tls). In the special case of `GET /robots.txt` requests, a
|
||||
special response is served; this response could be overwritten with static
|
||||
content.
|
||||
settings](#server_groups-*-tls). In the special case of `GET /robots.txt`
|
||||
requests, a special response is served; this response could be overwritten
|
||||
with static content.
|
||||
|
||||
**Property example:**
|
||||
|
||||
@ -604,7 +697,6 @@ The optional `web` object has the following properties:
|
||||
**Example:** `30s`.
|
||||
|
||||
[http-block-pages]: http.md#block-pages
|
||||
[http-dnscheck-test]: http.md#dhscheck-test
|
||||
[http-linked-ip-proxy]: http.md#linked-ip-proxy
|
||||
|
||||
|
||||
@ -677,7 +769,7 @@ The `filters` object has the following properties:
|
||||
* <a href="#filters-refresh_interval" id="filters-refresh_interval" name="filters-refresh_interval">`refresh_interval`</a>:
|
||||
How often AdGuard DNS refreshes the rule-list filters from the filter index,
|
||||
as well as the blocked services list from the [blocked list
|
||||
index][env-blocked_services)].
|
||||
index][env-blocked_services].
|
||||
|
||||
**Example:** `1h`.
|
||||
|
||||
|
@ -13,7 +13,7 @@
|
||||
|
||||
Development is supported on Linux and macOS (aka Darwin) systems.
|
||||
|
||||
1. Install Go 1.20 or later.
|
||||
1. Install Go 1.21 or later.
|
||||
|
||||
1. Call `make init` to set up the Git pre-commit hook.
|
||||
|
||||
@ -74,11 +74,14 @@ This is not an extensive list. See `../Makefile`.
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
<code>../internal/agd/country_generate.go</code>;
|
||||
<code>../internal/geoip/country_generate.go</code>;
|
||||
</li>
|
||||
<li>
|
||||
<code>../internal/geoip/asntops_generate.go</code>;
|
||||
</li>
|
||||
<li>
|
||||
<code>../internal/ecscache/ecsblockilist_generate.go</code>;
|
||||
</li>
|
||||
<li>
|
||||
<code>../internal/profiledb/internal/filecachepb/filecache.pb.go</code>.
|
||||
</li>
|
||||
@ -207,7 +210,7 @@ rm -f -r ./test/cache/
|
||||
mkdir ./test/cache
|
||||
curl 'https://raw.githubusercontent.com/maxmind/MaxMind-DB/main/test-data/GeoIP2-Country-Test.mmdb' -o ./test/GeoIP2-Country-Test.mmdb
|
||||
curl 'https://raw.githubusercontent.com/maxmind/MaxMind-DB/main/test-data/GeoIP2-City-Test.mmdb' -o ./test/GeoIP2-City-Test.mmdb
|
||||
curl 'https://raw.githubusercontent.com/maxmind/MaxMind-DB/main/test-data/GeoLite2-ASN-Test.mmdb' -o ./test/GeoLite2-ASN-Test.mmdb
|
||||
curl 'https://raw.githubusercontent.com/maxmind/MaxMind-DB/main/test-data/GeoIP2-ISP-Test.mmdb' -o ./test/GeoIP2-ISP-Test.mmdb
|
||||
```
|
||||
|
||||
|
||||
@ -228,6 +231,9 @@ You'll need to supply the following:
|
||||
|
||||
See the [external HTTP API documentation][externalhttp].
|
||||
|
||||
You may use `go run ./scripts/backend` to start mock GRPC server for
|
||||
`BILLSTAT_URL` and `PROFILES_URL` endpoints.
|
||||
|
||||
You may need to change the listen ports in `config.yaml` which are less than
|
||||
1024 to some other ports. Otherwise, `sudo` or `doas` is required to run
|
||||
`AdGuardDNS`.
|
||||
@ -250,7 +256,7 @@ If you're using an OS different from Linux, you also need to make these changes:
|
||||
```sh
|
||||
env \
|
||||
ADULT_BLOCKING_URL='https://raw.githubusercontent.com/ameshkov/stuff/master/DNS/adult_blocking.txt' \
|
||||
BILLSTAT_URL='https://httpbin.agrd.workers.dev/post' \
|
||||
BILLSTAT_URL='grpc://localhost:6062' \
|
||||
BLOCKED_SERVICE_INDEX_URL='https://adguardteam.github.io/HostlistsRegistry/assets/services.json' \
|
||||
CONSUL_ALLOWLIST_URL='https://raw.githubusercontent.com/ameshkov/stuff/master/DNS/consul_allowlist.json' \
|
||||
CONFIG_PATH='./config.yaml' \
|
||||
@ -258,10 +264,10 @@ env \
|
||||
FILTER_CACHE_PATH='./test/cache' \
|
||||
NEW_REG_DOMAINS_URL='https://raw.githubusercontent.com/ameshkov/stuff/master/DNS/nrd.txt' \
|
||||
PROFILES_CACHE_PATH='./test/profilecache.pb' \
|
||||
PROFILES_URL='https://raw.githubusercontent.com/ameshkov/stuff/master/DNS/profiles' \
|
||||
PROFILES_URL='grpc://localhost:6062' \
|
||||
SAFE_BROWSING_URL='https://raw.githubusercontent.com/ameshkov/stuff/master/DNS/safe_browsing.txt' \
|
||||
GENERAL_SAFE_SEARCH_URL='https://adguardteam.github.io/HostlistsRegistry/assets/engines_safe_search.txt' \
|
||||
GEOIP_ASN_PATH='./test/GeoLite2-ASN-Test.mmdb' \
|
||||
GEOIP_ASN_PATH='./test/GeoIP2-ISP-Test.mmdb' \
|
||||
GEOIP_COUNTRY_PATH='./test/GeoIP2-City-Test.mmdb' \
|
||||
QUERYLOG_PATH='./test/cache/querylog.jsonl' \
|
||||
LINKED_IP_TARGET_URL='https://httpbin.agrd.workers.dev/anything' \
|
||||
|
@ -9,12 +9,12 @@ sensitive configuration. All other configuration is stored in the
|
||||
* [`ADULT_BLOCKING_URL`](#ADULT_BLOCKING_URL)
|
||||
* [`BILLSTAT_URL`](#BILLSTAT_URL)
|
||||
* [`BLOCKED_SERVICE_INDEX_URL`](#BLOCKED_SERVICE_INDEX_URL)
|
||||
* [`CONFIG_PATH`](#CONFIG_PATH)
|
||||
* [`CONSUL_ALLOWLIST_URL`](#CONSUL_ALLOWLIST_URL)
|
||||
* [`CONSUL_DNSCHECK_KV_URL`](#CONSUL_DNSCHECK_KV_URL)
|
||||
* [`CONSUL_DNSCHECK_SESSION_URL`](#CONSUL_DNSCHECK_SESSION_URL)
|
||||
* [`CONFIG_PATH`](#CONFIG_PATH)
|
||||
* [`FILTER_INDEX_URL`](#FILTER_INDEX_URL)
|
||||
* [`FILTER_CACHE_PATH`](#FILTER_CACHE_PATH)
|
||||
* [`FILTER_INDEX_URL`](#FILTER_INDEX_URL)
|
||||
* [`GENERAL_SAFE_SEARCH_URL`](#GENERAL_SAFE_SEARCH_URL)
|
||||
* [`GEOIP_ASN_PATH` and `GEOIP_COUNTRY_PATH`](#GEOIP_ASN_PATH)
|
||||
* [`LINKED_IP_TARGET_URL`](#LINKED_IP_TARGET_URL)
|
||||
@ -25,8 +25,8 @@ sensitive configuration. All other configuration is stored in the
|
||||
* [`PROFILES_CACHE_PATH`](#PROFILES_CACHE_PATH)
|
||||
* [`PROFILES_URL`](#PROFILES_URL)
|
||||
* [`QUERYLOG_PATH`](#QUERYLOG_PATH)
|
||||
* [`RESEARCH_METRICS`](#RESEARCH_METRICS)
|
||||
* [`RESEARCH_LOGS`](#RESEARCH_LOGS)
|
||||
* [`RESEARCH_METRICS`](#RESEARCH_METRICS)
|
||||
* [`RULESTAT_URL`](#RULESTAT_URL)
|
||||
* [`SAFE_BROWSING_URL`](#SAFE_BROWSING_URL)
|
||||
* [`SENTRY_DSN`](#SENTRY_DSN)
|
||||
@ -49,9 +49,8 @@ The URL of source list of rules for adult blocking filter.
|
||||
|
||||
## <a href="#BILLSTAT_URL" id="BILLSTAT_URL" name="BILLSTAT_URL">`BILLSTAT_URL`</a>
|
||||
|
||||
The base backend URL for backend billing statistics uploader API. Supports HTTP
|
||||
and GRPC protocols. In case of HTTP the backend endpoint must reply with a 200
|
||||
status code on success. See the [external HTTP API requirements
|
||||
The base backend URL for backend billing statistics uploader API. Supports
|
||||
GRPC (`grpc://` and`grpcs://`) URLs. See the [external HTTP API requirements
|
||||
section][ext-billstat].
|
||||
|
||||
**Default:** No default value, the variable is **required.**
|
||||
@ -239,14 +238,12 @@ The profile cache is read on start and is later updated on every
|
||||
|
||||
## <a href="#PROFILES_URL" id="PROFILES_URL" name="PROFILES_URL">`PROFILES_URL`</a>
|
||||
|
||||
The base backend URL for profiles API. Supports HTTP (`http://` and `https://`)
|
||||
and GRPC (`grpc://` and `grpcs://`) URLs. In case of HTTP the backend endpoint
|
||||
must reply with a 200 status code on success. See the [external API
|
||||
requirements section][ext-profiles].
|
||||
The base backend URL for profiles API. Supports GRPC (`grpc://` and`grpcs://`)
|
||||
URLs. See the [external API requirements section][ext-profiles].
|
||||
|
||||
**Default:** No default value, the variable is **required.**
|
||||
|
||||
[ext-profiles]: externalhttp.md#profiles-backend
|
||||
[ext-profiles]: externalhttp.md#backend-profiles
|
||||
|
||||
|
||||
|
||||
|
@ -29,26 +29,8 @@ document should set the `Server` header in their replies.
|
||||
## <a href="#backend-billstat" id="backend-billstat" name="backend-billstat">Backend Billing Statistics</a>
|
||||
|
||||
This is the service to which the [`BILLSTAT_URL`][env-billstat_url] environment
|
||||
variable points. Supports `http(s):` and `grpc(s)` URLs. In case of GRPC
|
||||
protocol, the service must correspond to `./internal/backendpb/backend.proto`.
|
||||
In case of HTTP protocol this service must provide one endpoint:
|
||||
`POST /dns_api/v1/devices_activity`, it must respond with a `200 OK` response
|
||||
code and accept a JSON document in the following format:
|
||||
|
||||
```json
|
||||
{
|
||||
"devices": [
|
||||
{
|
||||
"client_country": "AU",
|
||||
"device_id": "abcd1234",
|
||||
"time_ms": 1624443079309,
|
||||
"asn": 1234,
|
||||
"queries": 1000,
|
||||
"proto": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
variable points. Supports `grpc(s)` URLs. The service must correspond to
|
||||
`./internal/backendpb/backend.proto`.
|
||||
|
||||
[env-billstat_url]: environment.md#BILLSTAT_URL
|
||||
|
||||
@ -57,80 +39,8 @@ code and accept a JSON document in the following format:
|
||||
## <a href="#backend-profiles" id="backend-profiles" name="backend-profiles">Backend Profiles Service</a>
|
||||
|
||||
This is the service to which the [`PROFILES_URL`][env-profiles_url] environment
|
||||
variable points. Supports `http(s):` and `grpc(s)` URLs. In case of GRPC
|
||||
protocol, the service must correspond to `./internal/backendpb/backend.proto`.
|
||||
In case of HTTP protocol this service must provide one endpoint:
|
||||
`GET /dns_api/v1/settings`, it must respond with a `200 OK` response code and
|
||||
accept a JSON document in the following format:
|
||||
|
||||
```json
|
||||
{
|
||||
"sync_time": 1624443079309,
|
||||
"settings": [
|
||||
{
|
||||
"dns_id": "83f3ea8f",
|
||||
"filtering_enabled": true,
|
||||
"query_log_enabled": true,
|
||||
"safe_browsing":
|
||||
{
|
||||
"enabled": true
|
||||
},
|
||||
"deleted": true,
|
||||
"block_private_relay": false,
|
||||
"devices": [
|
||||
{
|
||||
"id": "0d7724fa",
|
||||
"name": "Device 1",
|
||||
"filtering_enabled": true,
|
||||
"linked_ip": "1.2.3.4"
|
||||
}
|
||||
],
|
||||
"parental": {
|
||||
"enabled": false,
|
||||
"block_adult": false,
|
||||
"general_safe_search": false,
|
||||
"youtube_safe_search": false,
|
||||
"blocked_services": [
|
||||
"youtube"
|
||||
],
|
||||
"schedule": {
|
||||
"tmz": "GMT",
|
||||
"mon": [
|
||||
"0s",
|
||||
"59m"
|
||||
],
|
||||
"tue": [
|
||||
"0s",
|
||||
"59m"
|
||||
],
|
||||
"wed": [
|
||||
"0s",
|
||||
"59m"
|
||||
],
|
||||
"thu": [
|
||||
"0s",
|
||||
"59m"
|
||||
],
|
||||
"fri": [
|
||||
"0s",
|
||||
"59m"
|
||||
]
|
||||
}
|
||||
},
|
||||
"rule_lists": {
|
||||
"enabled": true,
|
||||
"ids": [
|
||||
"1"
|
||||
]
|
||||
},
|
||||
"filtered_response_ttl": 3600,
|
||||
"custom_rules": [
|
||||
"||example.org^"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
variable points. Supports `grpc(s)` URLs. The service must correspond to
|
||||
`./internal/backendpb/backend.proto`.
|
||||
|
||||
[env-profiles_url]: environment.md#PROFILES_URL
|
||||
|
||||
|
@ -249,6 +249,13 @@ rules to remember, which property means what. The properties are:
|
||||
|
||||
See [this IANA list][iana-rcode] for numeric values and their meanings.
|
||||
|
||||
* <a href="#properties-ip" id="properties-ip" name="properties-ip">`ip`</a>:
|
||||
The IP address of the client. This field is omitted in case the IP logging
|
||||
is turned off for the corresponding profile. The short name `ip` stands for
|
||||
“IP”.
|
||||
|
||||
**Example:** `1.2.3.4`
|
||||
|
||||
See also [file `internal/querylog/entry.go`][file-entry.go] for an explanation
|
||||
of the properties, their names, and mnemonics.
|
||||
|
||||
|
52
go.mod
52
go.mod
@ -1,32 +1,32 @@
|
||||
module github.com/AdguardTeam/AdGuardDNS
|
||||
|
||||
go 1.20
|
||||
go 1.21.5
|
||||
|
||||
require (
|
||||
github.com/AdguardTeam/AdGuardDNS/internal/dnsserver v0.100.0
|
||||
github.com/AdguardTeam/golibs v0.15.0
|
||||
github.com/AdguardTeam/urlfilter v0.17.0
|
||||
github.com/AdguardTeam/AdGuardDNS/internal/dnsserver v0.0.0-00010101000000-000000000000
|
||||
github.com/AdguardTeam/golibs v0.18.1
|
||||
github.com/AdguardTeam/urlfilter v0.17.2
|
||||
github.com/ameshkov/dnscrypt/v2 v2.2.7
|
||||
github.com/axiomhq/hyperloglog v0.0.0-20230201085229-3ddf4bad03dc
|
||||
github.com/bluele/gcache v0.0.2
|
||||
github.com/c2h5oh/datasize v0.0.0-20220606134207-859f65c6625b
|
||||
github.com/caarlos0/env/v7 v7.1.0
|
||||
github.com/getsentry/sentry-go v0.21.0
|
||||
github.com/getsentry/sentry-go v0.25.0
|
||||
github.com/google/renameio/v2 v2.0.0
|
||||
github.com/miekg/dns v1.1.55
|
||||
github.com/oschwald/maxminddb-golang v1.10.0
|
||||
github.com/miekg/dns v1.1.56
|
||||
github.com/oschwald/maxminddb-golang v1.12.0
|
||||
github.com/patrickmn/go-cache v2.1.1-0.20191004192108-46f407853014+incompatible
|
||||
github.com/prometheus/client_golang v1.15.1
|
||||
github.com/prometheus/client_model v0.4.0
|
||||
github.com/prometheus/client_golang v1.17.0
|
||||
github.com/prometheus/client_model v0.5.0
|
||||
github.com/prometheus/common v0.44.0
|
||||
github.com/quic-go/quic-go v0.38.0
|
||||
github.com/quic-go/quic-go v0.39.0
|
||||
github.com/stretchr/testify v1.8.4
|
||||
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63
|
||||
golang.org/x/net v0.14.0
|
||||
golang.org/x/sys v0.11.0
|
||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d
|
||||
golang.org/x/net v0.17.0
|
||||
golang.org/x/sys v0.13.0
|
||||
golang.org/x/time v0.3.0
|
||||
google.golang.org/grpc v1.56.2
|
||||
google.golang.org/protobuf v1.30.0
|
||||
google.golang.org/grpc v1.58.3
|
||||
google.golang.org/protobuf v1.31.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
)
|
||||
|
||||
@ -39,21 +39,21 @@ require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
|
||||
github.com/golang/mock v1.6.0 // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/google/pprof v0.0.0-20230821062121-407c9e7a662f // indirect
|
||||
github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.11.0 // indirect
|
||||
github.com/panjf2000/ants/v2 v2.7.5 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.13.0 // indirect
|
||||
github.com/panjf2000/ants/v2 v2.8.2 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/procfs v0.10.1 // indirect
|
||||
github.com/prometheus/procfs v0.12.0 // indirect
|
||||
github.com/quic-go/qpack v0.4.0 // indirect
|
||||
github.com/quic-go/qtls-go1-20 v0.3.3 // indirect
|
||||
golang.org/x/crypto v0.12.0 // indirect
|
||||
golang.org/x/mod v0.12.0 // indirect
|
||||
golang.org/x/text v0.12.0 // indirect
|
||||
golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect
|
||||
github.com/quic-go/qtls-go1-20 v0.3.4 // indirect
|
||||
go.uber.org/mock v0.3.0 // indirect
|
||||
golang.org/x/crypto v0.14.0 // indirect
|
||||
golang.org/x/mod v0.13.0 // indirect
|
||||
golang.org/x/text v0.13.0 // indirect
|
||||
golang.org/x/tools v0.14.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231009173412-8bfb1ae86b6c // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
|
146
go.sum
146
go.sum
@ -1,7 +1,7 @@
|
||||
github.com/AdguardTeam/golibs v0.15.0 h1:yOv/fdVkJIOWKr0NlUXAE9RA0DK9GKiBbiGzq47vY7o=
|
||||
github.com/AdguardTeam/golibs v0.15.0/go.mod h1:66ZLs8P7nk/3IfKroQ1rqtieLk+5eXYXMBKXlVL7KeI=
|
||||
github.com/AdguardTeam/urlfilter v0.17.0 h1:tUzhtR9wMx704GIP3cibsDQJrixlMHfwoQbYJfPdFow=
|
||||
github.com/AdguardTeam/urlfilter v0.17.0/go.mod h1:bbuZjPUzm/Ip+nz5qPPbwIP+9rZyQbQad8Lt/0fCulU=
|
||||
github.com/AdguardTeam/golibs v0.18.1 h1:6u0fvrIj2qjUsRdbIGJ9AR0g5QRSWdKIo/DYl3tp5aM=
|
||||
github.com/AdguardTeam/golibs v0.18.1/go.mod h1:DKhCIXHcUYtBhU8ibTLKh1paUL96n5zhQBlx763sj+U=
|
||||
github.com/AdguardTeam/urlfilter v0.17.2 h1:xTntfr1UWah8m6wwoXJmFgplFk/+kL/hDu204ptrM1U=
|
||||
github.com/AdguardTeam/urlfilter v0.17.2/go.mod h1:Jru7jFfeH2CoDf150uDs+rRYcZBzHHBz05r9REyDKyE=
|
||||
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY=
|
||||
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA=
|
||||
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 h1:52m0LGchQBBVqJRyYYufQuIbVqRawmubW3OFGqK1ekw=
|
||||
@ -28,128 +28,124 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
||||
github.com/dgryski/go-metro v0.0.0-20180109044635-280f6062b5bc/go.mod h1:c9O8+fpSOX1DM8cPNSkX/qsBWdkD4yd2dpciOWQjpBw=
|
||||
github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 h1:y7y0Oa6UawqTFPCDw9JG6pdKt4F9pAhHv0B7FMGaGD0=
|
||||
github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140/go.mod h1:c9O8+fpSOX1DM8cPNSkX/qsBWdkD4yd2dpciOWQjpBw=
|
||||
github.com/getsentry/sentry-go v0.21.0 h1:c9l5F1nPF30JIppulk4veau90PK6Smu3abgVtVQWon4=
|
||||
github.com/getsentry/sentry-go v0.21.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY=
|
||||
github.com/getsentry/sentry-go v0.25.0 h1:q6Eo+hS+yoJlTO3uu/azhQadsD8V+jQn2D8VvX1eOyI=
|
||||
github.com/getsentry/sentry-go v0.25.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY=
|
||||
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
|
||||
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
|
||||
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
|
||||
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
|
||||
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/pprof v0.0.0-20230821062121-407c9e7a662f h1:pDhu5sgp8yJlEF/g6osliIIpF9K4F5jvkULXa4daRDQ=
|
||||
github.com/google/pprof v0.0.0-20230821062121-407c9e7a662f/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98 h1:pUa4ghanp6q4IJHwE9RwLgmVFfReJN+KbQ8ExNEUUoQ=
|
||||
github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
|
||||
github.com/google/renameio/v2 v2.0.0 h1:UifI23ZTGY8Tt29JbYFiuyIU3eX+RNFtUwefq9qAhxg=
|
||||
github.com/google/renameio/v2 v2.0.0/go.mod h1:BtmJXm5YlszgC+TD4HOEEUFgkJP3nLxehU6hfe7jRt4=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
||||
github.com/miekg/dns v1.1.55 h1:GoQ4hpsj0nFLYe+bWiCToyrBEJXkQfOOIvFGFy0lEgo=
|
||||
github.com/miekg/dns v1.1.55/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
|
||||
github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU=
|
||||
github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM=
|
||||
github.com/onsi/gomega v1.27.8 h1:gegWiwZjBsf2DgiSbf5hpokZ98JVDMcWkUiigk6/KXc=
|
||||
github.com/oschwald/maxminddb-golang v1.10.0 h1:Xp1u0ZhqkSuopaKmk1WwHtjF0H9Hd9181uj2MQ5Vndg=
|
||||
github.com/oschwald/maxminddb-golang v1.10.0/go.mod h1:Y2ELenReaLAZ0b400URyGwvYxHV1dLIxBuyOsyYjHK0=
|
||||
github.com/panjf2000/ants/v2 v2.7.5 h1:/vhh0Hza9G1vP1PdCj9hl6MUzCRbmtcTJL0OsnmytuU=
|
||||
github.com/panjf2000/ants/v2 v2.7.5/go.mod h1:KIBmYG9QQX5U2qzFP/yQJaq/nSb6rahS9iEHkrCMgM8=
|
||||
github.com/miekg/dns v1.1.56 h1:5imZaSeoRNvpM9SzWNhEcP9QliKiz20/dA2QabIGVnE=
|
||||
github.com/miekg/dns v1.1.56/go.mod h1:cRm6Oo2C8TY9ZS/TqsSrseAcncm74lfK5G+ikN2SWWY=
|
||||
github.com/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4=
|
||||
github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o=
|
||||
github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI=
|
||||
github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M=
|
||||
github.com/oschwald/maxminddb-golang v1.12.0 h1:9FnTOD0YOhP7DGxGsq4glzpGy5+w7pq50AS6wALUMYs=
|
||||
github.com/oschwald/maxminddb-golang v1.12.0/go.mod h1:q0Nob5lTCqyQ8WT6FYgS1L7PXKVVbgiymefNwIjPzgY=
|
||||
github.com/panjf2000/ants/v2 v2.8.2 h1:D1wfANttg8uXhC9149gRt1PDQ+dLVFjNXkCEycMcvQQ=
|
||||
github.com/panjf2000/ants/v2 v2.8.2/go.mod h1:7ZxyxsqE4vvW0M7LSD8aI3cKwgFhBHbxnlN8mDqHa1I=
|
||||
github.com/patrickmn/go-cache v2.1.1-0.20191004192108-46f407853014+incompatible h1:IWzUvJ72xMjmrjR9q3H1PF+jwdN0uNQiR2t1BLNalyo=
|
||||
github.com/patrickmn/go-cache v2.1.1-0.20191004192108-46f407853014+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
||||
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
|
||||
github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
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/prometheus/client_golang v1.15.1 h1:8tXpTmJbyH5lydzFPoxSIJ0J46jdh3tylbvM1xCv0LI=
|
||||
github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk=
|
||||
github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY=
|
||||
github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU=
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q=
|
||||
github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY=
|
||||
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
|
||||
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
|
||||
github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY=
|
||||
github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY=
|
||||
github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg=
|
||||
github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM=
|
||||
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
|
||||
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
|
||||
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/qtls-go1-20 v0.3.3 h1:17/glZSLI9P9fDAeyCHBFSWSqJcwx1byhLwP5eUIDCM=
|
||||
github.com/quic-go/qtls-go1-20 v0.3.3/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
|
||||
github.com/quic-go/quic-go v0.38.0 h1:T45lASr5q/TrVwt+jrVccmqHhPL2XuSyoCLVCpfOSLc=
|
||||
github.com/quic-go/quic-go v0.38.0/go.mod h1:MPCuRq7KBK2hNcfKj/1iD1BGuN3eAYMeNxp3T42LRUg=
|
||||
github.com/quic-go/qtls-go1-20 v0.3.4 h1:MfFAPULvst4yoMgY9QmtpYmfij/em7O8UUi+bNVm7Cg=
|
||||
github.com/quic-go/qtls-go1-20 v0.3.4/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
|
||||
github.com/quic-go/quic-go v0.39.0 h1:AgP40iThFMY0bj8jGxROhw3S0FMGa8ryqsmi9tBH3So=
|
||||
github.com/quic-go/quic-go v0.39.0/go.mod h1:T09QsDQWjLiQ74ZmacDfqZmhY/NLnw5BC40MANNNZ1Q=
|
||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||
github.com/shirou/gopsutil/v3 v3.23.7 h1:C+fHO8hfIppoJ1WdsVm1RoI0RwXoNdfTK7yWXV0wVj4=
|
||||
github.com/shirou/gopsutil/v3 v3.23.7/go.mod h1:c4gnmoRC0hQuaLqvxnx1//VXQ0Ms/X9UnJF8pddY5z4=
|
||||
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
|
||||
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.6.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.1/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.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM=
|
||||
github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI=
|
||||
github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4=
|
||||
github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
|
||||
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
|
||||
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 h1:m64FZMko/V45gv0bNmrNYoDEq8U5YUhetc9cBWKS1TQ=
|
||||
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63/go.mod h1:0v4NqG35kSWCMzLaMeX+IQrlSnVE/bqGSyC2cz/9Le8=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14=
|
||||
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
|
||||
github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
go.uber.org/mock v0.3.0 h1:3mUxI1No2/60yUYax92Pt8eNOEecx2D3lcXZh2NEZJo=
|
||||
go.uber.org/mock v0.3.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
|
||||
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
|
||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
|
||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
|
||||
golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY=
|
||||
golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
|
||||
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
|
||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc=
|
||||
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ=
|
||||
golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
|
||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
|
||||
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846 h1:Vve/L0v7CXXuxUmaMGIEK/dEeq7uiqb5qBgQrZzIE7E=
|
||||
golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc=
|
||||
golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A=
|
||||
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU=
|
||||
google.golang.org/grpc v1.56.2 h1:fVRFRnXvU+x6C4IlHZewvJOVHoOv1TUuQyoRsYnB4bI=
|
||||
google.golang.org/grpc v1.56.2/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231009173412-8bfb1ae86b6c h1:jHkCUWkseRf+W+edG5hMzr/Uh1xkDREY4caybAq4dpY=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231009173412-8bfb1ae86b6c/go.mod h1:4cYg8o5yUbm77w8ZX00LhMVNl/YVBFJRYWDc0uYWMs0=
|
||||
google.golang.org/grpc v1.58.3 h1:BjnpXut1btbtgN/6sp+brB2Kbm2LjNXnidYujAVbSoQ=
|
||||
google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
|
||||
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
|
||||
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
64
go.work.sum
64
go.work.sum
@ -4,7 +4,9 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
|
||||
cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo=
|
||||
cloud.google.com/go v0.65.0 h1:Dg9iHVQfrhq82rUNu9ZxUDrJLaxFUe/HlCVaLyRruq8=
|
||||
cloud.google.com/go/bigquery v1.8.0 h1:PQcPefKFdaIzjQFbiyOgAqyx8q5djaE7x9Sqe712DPA=
|
||||
cloud.google.com/go/compute v1.21.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM=
|
||||
cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
|
||||
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
|
||||
cloud.google.com/go/datastore v1.1.0 h1:/May9ojXjRkPBNVrq+oWLqmWCkr4OU5uRY29bu0mRyQ=
|
||||
cloud.google.com/go/pubsub v1.3.1 h1:ukjixP1wl0LpnZ6LWtZJ0mX5tBmjp1f8Sqer8Z2OMUU=
|
||||
cloud.google.com/go/storage v1.10.0 h1:STgFzyU5/8miMl0//zKh2aQeTyeaUH3WN9bSUiJ09bA=
|
||||
@ -20,7 +22,9 @@ dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D
|
||||
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999 h1:OR8VhtwhcAI3U48/rzBsVOuHi0zDPzYI1xASVcdSgR8=
|
||||
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
|
||||
github.com/AdguardTeam/golibs v0.10.7/go.mod h1:rSfQRGHIdgfxriDDNgNJ7HmE5zRoURq8R+VdR81Zuzw=
|
||||
github.com/AdguardTeam/golibs v0.17.2/go.mod h1:DKhCIXHcUYtBhU8ibTLKh1paUL96n5zhQBlx763sj+U=
|
||||
github.com/AdguardTeam/gomitmproxy v0.2.0 h1:rvCOf17pd1/CnMyMQW891zrEiIQBpQ8cIGjKN9pinUU=
|
||||
github.com/AdguardTeam/gomitmproxy v0.2.1/go.mod h1:Qdv0Mktnzer5zpdpi5rAwixNJzW2FN91LjKJCkVbYGU=
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0=
|
||||
@ -60,7 +64,11 @@ github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBT
|
||||
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23 h1:D21IyuvjDCshj1/qq+pCNd3VZOAEI9jy6Bi131YlXgI=
|
||||
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk=
|
||||
github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw=
|
||||
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||
github.com/chromedp/cdproto v0.0.0-20230802225258-3cf4e6d46a89/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs=
|
||||
github.com/chromedp/chromedp v0.9.2/go.mod h1:LkSXJKONWTCHAfQasKFUZI+mxqS4tZqhmtGzzhLsnLs=
|
||||
github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww=
|
||||
github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY=
|
||||
@ -78,6 +86,8 @@ github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38
|
||||
github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f h1:WBZRG4aNOuI15bLRrCgN8fCq8E5Xuty6jGbmSNEvSsU=
|
||||
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/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0 h1:sDMmm+q/3+BukdIpxwO365v/Rbspp2Nt5XntgQRXq8Q=
|
||||
github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM=
|
||||
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d h1:t5Wuyh53qYyg9eqn4BbnlIT+vmhyww0TatL+zT3uWgI=
|
||||
@ -88,7 +98,9 @@ github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25Kn
|
||||
github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385 h1:clC1lXBpe2kTj2VHdaIu9ajZQe4kcEY9j0NsnDDBZ3o=
|
||||
github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4 h1:rEvIZUSZ3fx39WIi3JkQqQBitGwpELBIYWeBVh6wn+E=
|
||||
github.com/envoyproxy/go-control-plane v0.11.1/go.mod h1:uhMcXKCQMEJHiAb0w+YGefQLaTEw+YhGluxZkrTmD0g=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE=
|
||||
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
|
||||
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
||||
github.com/flosch/pongo2/v4 v4.0.2 h1:gv+5Pe3vaSVmiJvh/BZa82b7/00YUGm0PIyVVLop0Hw=
|
||||
@ -111,7 +123,6 @@ github.com/gliderlabs/ssh v0.1.1 h1:j3L6gSLQalDETeEg/Jg0mGY0/y/N6zI2xX1978P0Uqw=
|
||||
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||
github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
|
||||
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
||||
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1 h1:QbL/5oDUmRBzO9/Z7Seo6zf912W/a6Sr4Eu0G/3Jho0=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4 h1:WtGNWLvXpe6ZudgnXrq0barxBImvnnJoMEhXAzcbM0I=
|
||||
github.com/go-kit/kit v0.9.0 h1:wDJmvq38kDhkVxi50ni9ykkdUr1PKgqKOoi01fa0Mdk=
|
||||
@ -119,8 +130,8 @@ github.com/go-kit/log v0.2.0 h1:7i2K3eKTos3Vc0enKCfnVcgHh2olr/MyfboYq7cAcFw=
|
||||
github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU=
|
||||
github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
|
||||
github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA=
|
||||
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
|
||||
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab h1:xveKWz2iaueeTaUgdetzel+U7exyigDYBryyVfV/rZk=
|
||||
github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8=
|
||||
github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
|
||||
@ -130,6 +141,9 @@ github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl
|
||||
github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ=
|
||||
github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU=
|
||||
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
|
||||
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
|
||||
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
|
||||
github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY=
|
||||
github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk=
|
||||
github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo=
|
||||
@ -137,6 +151,7 @@ github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY=
|
||||
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7 h1:2hRPrmiwPrp3fQX967rNJIhQPtiGXdlQWAxKbKw3VHA=
|
||||
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
|
||||
@ -150,7 +165,6 @@ github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Z
|
||||
github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY=
|
||||
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
|
||||
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
|
||||
@ -163,6 +177,7 @@ github.com/google/martian/v3 v3.0.0 h1:pMen7vLs8nvgEYhywH3KDWJIJTeEr2ULsVWHWYHQy
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99 h1:Ak8CrdlwwXwAZxzS66vgPt4U8yUZX7JwLvVR58FN5jM=
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20230821062121-407c9e7a662f/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go v2.0.0+incompatible h1:j0GKcs05QVmm7yesiZq2+9cxHkNK9YM6zKx4D2qucQU=
|
||||
@ -200,11 +215,14 @@ github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=
|
||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o=
|
||||
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
|
||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||
github.com/kataras/blocks v0.0.7 h1:cF3RDY/vxnSRezc7vLFlQFTYXG/yAr1o7WImJuZbzC4=
|
||||
github.com/kataras/blocks v0.0.7/go.mod h1:UJIU97CluDo0f+zEjbnbkeMRlvYORtmc1304EeyXf4I=
|
||||
github.com/kataras/golog v0.1.7 h1:0TY5tHn5L5DlRIikepcaRR/6oInIr9AiWsxzt0vvlBE=
|
||||
@ -232,10 +250,11 @@ github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.3 h1:/Um6a/ZmD5tF7peoOJ5oN5KMQ0DrGVQSXLNwyckutPk=
|
||||
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/labstack/echo/v4 v4.9.0 h1:wPOF1CE6gvt/kmbMR4dGzWvHMPT+sAEUJOwOTtvITVY=
|
||||
github.com/labstack/echo/v4 v4.9.0/go.mod h1:xkCDAdFCIf8jsFQ5NnbK7oqaF/yU1A1X20Ltm0OvSks=
|
||||
github.com/labstack/echo/v4 v4.10.0 h1:5CiyngihEO4HXsz3vVsJn7f8xAlWwRr3aY6Ih280ZKA=
|
||||
@ -244,6 +263,7 @@ github.com/labstack/gommon v0.3.1 h1:OomWaJXm7xR6L1HmEtGyQf26TEn7V6X88mktX9kee9o
|
||||
github.com/labstack/gommon v0.3.1/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
|
||||
github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8=
|
||||
github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
|
||||
github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs=
|
||||
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
|
||||
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
||||
github.com/lucas-clemente/quic-go v0.25.0/go.mod h1:YtzP8bxRVCBlO77yRanE264+fY/T2U9ZlW1AaHOsMOg=
|
||||
@ -281,9 +301,12 @@ github.com/microcosm-cc/bluemonday v1.0.23/go.mod h1:mN70sk7UkkF8TUr2IGBpNN0jAgS
|
||||
github.com/miekg/dns v1.1.47/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86 h1:D6paGObi5Wud7xg83MaEFyjxQB1W5bz5d0IFppr+ymk=
|
||||
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
|
||||
github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab h1:eFXv9Nu1lGbrNbj619aWwZfVF5HBrm9Plte8aNptuTI=
|
||||
@ -301,11 +324,9 @@ github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+q
|
||||
github.com/onsi/gomega v1.27.7/go.mod h1:1p8OOlwo2iUUDsHnOrjE5UKYJ+e3W8eQ3qSlRahPmr4=
|
||||
github.com/openzipkin/zipkin-go v0.1.1 h1:A/ADD6HaPnAKj3yS7HjGHRK77qi41Hi0DirOOIQAeIw=
|
||||
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
|
||||
github.com/oschwald/maxminddb-golang v1.10.0 h1:Xp1u0ZhqkSuopaKmk1WwHtjF0H9Hd9181uj2MQ5Vndg=
|
||||
github.com/oschwald/maxminddb-golang v1.10.0/go.mod h1:Y2ELenReaLAZ0b400URyGwvYxHV1dLIxBuyOsyYjHK0=
|
||||
github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0=
|
||||
github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg=
|
||||
github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas=
|
||||
github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
@ -317,7 +338,6 @@ github.com/quic-go/qtls-go1-20 v0.3.3/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58
|
||||
github.com/quic-go/quic-go v0.38.0/go.mod h1:MPCuRq7KBK2hNcfKj/1iD1BGuN3eAYMeNxp3T42LRUg=
|
||||
github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
@ -326,8 +346,6 @@ github.com/schollz/closestmatch v2.1.0+incompatible h1:Uel2GXEpJqOWBrlyI+oY9LTiy
|
||||
github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=
|
||||
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/shirou/gopsutil/v3 v3.23.7 h1:C+fHO8hfIppoJ1WdsVm1RoI0RwXoNdfTK7yWXV0wVj4=
|
||||
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
|
||||
github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4 h1:Fth6mevc5rX7glNLpbAMJnqKlfIkcTjZCSHEeqvKbcI=
|
||||
github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY=
|
||||
github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48 h1:vabduItPAIz9px5iryD5peyx7O3Ya8TBThapgXim98o=
|
||||
@ -392,7 +410,6 @@ github.com/tdewolff/minify/v2 v2.12.4 h1:kejsHQMM17n6/gwdw53qsi6lg0TGddZADVyQOz1
|
||||
github.com/tdewolff/minify/v2 v2.12.4/go.mod h1:h+SRvSIX3kwgwTFOpSckvSxgax3uy8kZTSF1Ojrr3bk=
|
||||
github.com/tdewolff/parse/v2 v2.6.4 h1:KCkDvNUMof10e3QExio9OPZJT8SbdKojLBumw8YZycQ=
|
||||
github.com/tdewolff/parse/v2 v2.6.4/go.mod h1:woz0cgbLwFdtbjJu8PIKxhW05KplTFQkOdX78o+Jgrs=
|
||||
github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM=
|
||||
github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
|
||||
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
|
||||
github.com/urfave/negroni v1.0.0 h1:kIimOitoypq34K7TG7DUaJ9kq/N4Ofuwi1sjz0KipXc=
|
||||
@ -451,7 +468,7 @@ golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2
|
||||
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@ -471,6 +488,7 @@ golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ=
|
||||
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
|
||||
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
|
||||
golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
@ -479,6 +497,7 @@ golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b h1:clP8eMhB30EHdc0bd2Twtq
|
||||
golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I=
|
||||
golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8=
|
||||
golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE=
|
||||
golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI=
|
||||
golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852 h1:xYq6+9AtI+xP3M4r0N1hCkHrInHDBohhquRgx9Kk6gI=
|
||||
golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@ -486,7 +505,6 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@ -503,6 +521,7 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
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/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
|
||||
golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw=
|
||||
@ -512,14 +531,14 @@ golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
|
||||
golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c=
|
||||
golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=
|
||||
golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
|
||||
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
||||
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
|
||||
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.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
|
||||
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
@ -531,7 +550,9 @@ golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
|
||||
golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
|
||||
golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM=
|
||||
golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||
@ -551,20 +572,20 @@ google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoA
|
||||
google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
|
||||
google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987 h1:PDIOdWxZ8eRizhKa1AAvY53xsvLB1cWorMjslvY3VA8=
|
||||
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A=
|
||||
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU=
|
||||
google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5 h1:L6iMMGrtzgHsWofoFcihmDEMYeDR9KN/ThbPWGrh++g=
|
||||
google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5/go.mod h1:oH/ZOT02u4kWEp7oYBGYFFkCdKS/uYR9Z7+0/xuuFp8=
|
||||
google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97 h1:SeZZZx0cP0fqUyA+oRzP9k7cSwJlvDFiROO72uwD6i0=
|
||||
google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97/go.mod h1:t1VqOqqvce95G3hIDCT5FeO3YUc6Q4Oe24L/+rNMxRk=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98/go.mod h1:rsr7RhLuwsDKL7RmgDDCUc6yaGr1iqceVb5Wv6f6YvQ=
|
||||
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
|
||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.31.0 h1:T7P4R73V3SSDPhH7WW7ATbfViLtmamH0DKrP3f9AuDI=
|
||||
google.golang.org/grpc v1.56.2 h1:fVRFRnXvU+x6C4IlHZewvJOVHoOv1TUuQyoRsYnB4bI=
|
||||
google.golang.org/grpc v1.56.2/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8=
|
||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||
@ -572,8 +593,7 @@ gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919 h1:tmXTu+dfa+d9Evp8NpJdgOy6+rt8/x4yG7qPBrtNfLY=
|
||||
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
|
||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
@ -11,8 +11,10 @@ import (
|
||||
"github.com/AdguardTeam/urlfilter/filterlist"
|
||||
)
|
||||
|
||||
// unit is a convenient alias for struct{}
|
||||
type unit = struct{}
|
||||
// blocklistFilterID ia the ID for the urlfilter rule list to use in the
|
||||
// internal access engines. As there is only one rule list in the engine it
|
||||
// could simply be 0.
|
||||
const blocklistFilterID = 0
|
||||
|
||||
// Interface is the access manager interface.
|
||||
type Interface interface {
|
||||
@ -24,24 +26,24 @@ type Interface interface {
|
||||
IsBlockedIP(ip netip.Addr) (blocked bool, rule string)
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ Interface = (*Manager)(nil)
|
||||
|
||||
// Manager controls IP and client blocking that takes place before all
|
||||
// other processing. An Manager is safe for concurrent use.
|
||||
type Manager struct {
|
||||
blockedIPs map[netip.Addr]unit
|
||||
// Global controls IP and client blocking that takes place before all other
|
||||
// processing. Global is safe for concurrent use.
|
||||
type Global struct {
|
||||
blockedIPs map[netip.Addr]string
|
||||
blockedHostsEng *urlfilter.DNSEngine
|
||||
blockedNets []netip.Prefix
|
||||
}
|
||||
|
||||
// New create an Manager. The parameters assumed to be valid.
|
||||
func New(blockedDomains, blockedSubnets []string) (am *Manager, err error) {
|
||||
am = &Manager{
|
||||
blockedIPs: map[netip.Addr]unit{},
|
||||
// NewGlobal create a new Global from provided parameters.
|
||||
func NewGlobal(blockedDomains, blockedSubnets []string) (g *Global, err error) {
|
||||
g = &Global{
|
||||
blockedIPs: map[netip.Addr]string{},
|
||||
}
|
||||
|
||||
processAccessList(blockedSubnets, am.blockedIPs, &am.blockedNets)
|
||||
err = processAccessList(blockedSubnets, g.blockedIPs, &g.blockedNets)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("adding blocked hosts: %w", err)
|
||||
}
|
||||
|
||||
b := &strings.Builder{}
|
||||
for _, h := range blockedDomains {
|
||||
@ -50,7 +52,7 @@ func New(blockedDomains, blockedSubnets []string) (am *Manager, err error) {
|
||||
|
||||
lists := []filterlist.RuleList{
|
||||
&filterlist.StringRuleList{
|
||||
ID: 0,
|
||||
ID: blocklistFilterID,
|
||||
RulesText: b.String(),
|
||||
IgnoreCosmetic: true,
|
||||
},
|
||||
@ -61,44 +63,53 @@ func New(blockedDomains, blockedSubnets []string) (am *Manager, err error) {
|
||||
return nil, fmt.Errorf("adding blocked hosts: %w", err)
|
||||
}
|
||||
|
||||
am.blockedHostsEng = urlfilter.NewDNSEngine(rulesStrg)
|
||||
g.blockedHostsEng = urlfilter.NewDNSEngine(rulesStrg)
|
||||
|
||||
return am, nil
|
||||
return g, nil
|
||||
}
|
||||
|
||||
// processAccessList is a helper for processing a list of strings, each of them
|
||||
// assumed be a valid IP address or a valid CIDR.
|
||||
func processAccessList(strs []string, ips map[netip.Addr]unit, nets *[]netip.Prefix) {
|
||||
// could be an IP address or a CIDR.
|
||||
func processAccessList(strs []string, ips map[netip.Addr]string, nets *[]netip.Prefix) (err error) {
|
||||
for _, s := range strs {
|
||||
var err error
|
||||
var ip netip.Addr
|
||||
var ipnet netip.Prefix
|
||||
if ip, err = netip.ParseAddr(s); err == nil {
|
||||
ips[ip] = unit{}
|
||||
ips[ip] = ip.String()
|
||||
} else if ipnet, err = netip.ParsePrefix(s); err == nil {
|
||||
*nets = append(*nets, ipnet)
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("cannot parse subnet or ip address: %q", s)
|
||||
}
|
||||
}
|
||||
|
||||
// IsBlockedHost returns true if host should be blocked.
|
||||
func (am *Manager) IsBlockedHost(host string, qt uint16) (blocked bool) {
|
||||
_, blocked = am.blockedHostsEng.MatchRequest(&urlfilter.DNSRequest{
|
||||
return nil
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ Interface = (*Global)(nil)
|
||||
|
||||
// IsBlockedHost implements the [Interface] interface for *Global.
|
||||
func (g *Global) IsBlockedHost(host string, qt uint16) (blocked bool) {
|
||||
res, matched := g.blockedHostsEng.MatchRequest(&urlfilter.DNSRequest{
|
||||
Hostname: host,
|
||||
DNSType: qt,
|
||||
})
|
||||
|
||||
return blocked
|
||||
if matched && res.NetworkRule != nil {
|
||||
return !res.NetworkRule.Whitelist
|
||||
}
|
||||
|
||||
// IsBlockedIP returns the status of the IP address blocking as well as the rule
|
||||
// that blocked it.
|
||||
func (am *Manager) IsBlockedIP(ip netip.Addr) (blocked bool, rule string) {
|
||||
if _, ok := am.blockedIPs[ip]; ok {
|
||||
return true, ip.String()
|
||||
return matched
|
||||
}
|
||||
|
||||
for _, ipnet := range am.blockedNets {
|
||||
// IsBlockedIP implements the [Interface] interface for *Global.
|
||||
func (g *Global) IsBlockedIP(ip netip.Addr) (blocked bool, rule string) {
|
||||
if ipStr, ok := g.blockedIPs[ip]; ok {
|
||||
return true, ipStr
|
||||
}
|
||||
|
||||
for _, ipnet := range g.blockedNets {
|
||||
if ipnet.Contains(ip) {
|
||||
return true, ipnet.String()
|
||||
}
|
||||
|
@ -10,11 +10,13 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestAccessManager_IsBlockedHost(t *testing.T) {
|
||||
am, err := access.New([]string{
|
||||
func TestGlobal_IsBlockedHost(t *testing.T) {
|
||||
global, err := access.NewGlobal([]string{
|
||||
"block.test",
|
||||
"UPPERCASE.test",
|
||||
"||block_aaaa.test^$dnstype=AAAA",
|
||||
"||allowlist.test^",
|
||||
"@@||allow.allowlist.test^",
|
||||
}, []string{})
|
||||
require.NoError(t, err)
|
||||
|
||||
@ -53,18 +55,28 @@ func TestAccessManager_IsBlockedHost(t *testing.T) {
|
||||
name: "block_qt",
|
||||
host: "block_aaaa.test",
|
||||
qt: dns.TypeAAAA,
|
||||
}, {
|
||||
want: assert.True,
|
||||
name: "allowlist_block",
|
||||
host: "block.allowlist.test",
|
||||
qt: dns.TypeA,
|
||||
}, {
|
||||
want: assert.False,
|
||||
name: "allowlist_test",
|
||||
host: "allow.allowlist.test",
|
||||
qt: dns.TypeA,
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
blocked := am.IsBlockedHost(tc.host, tc.qt)
|
||||
blocked := global.IsBlockedHost(tc.host, tc.qt)
|
||||
tc.want(t, blocked)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAccessManager_IsBlockedIP(t *testing.T) {
|
||||
am, err := access.New([]string{}, []string{
|
||||
func TestGlobal_IsBlockedIP(t *testing.T) {
|
||||
global, err := access.NewGlobal([]string{}, []string{
|
||||
"1.1.1.1",
|
||||
"2.2.2.0/8",
|
||||
})
|
||||
@ -99,7 +111,7 @@ func TestAccessManager_IsBlockedIP(t *testing.T) {
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
blocked, rule := am.IsBlockedIP(tc.ip)
|
||||
blocked, rule := global.IsBlockedIP(tc.ip)
|
||||
tc.want(t, blocked)
|
||||
assert.Equal(t, tc.wantRule, rule)
|
||||
})
|
||||
|
77
internal/access/engine.go
Normal file
77
internal/access/engine.go
Normal file
@ -0,0 +1,77 @@
|
||||
package access
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agdnet"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/metrics"
|
||||
"github.com/AdguardTeam/golibs/stringutil"
|
||||
"github.com/AdguardTeam/urlfilter"
|
||||
"github.com/AdguardTeam/urlfilter/filterlist"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
// blockedHostEngine is a lazy blocklist rules engine.
|
||||
type blockedHostEngine struct {
|
||||
lazyEngine *urlfilter.DNSEngine
|
||||
initOnce *sync.Once
|
||||
rules []string
|
||||
}
|
||||
|
||||
// newBlockedHostEngine creates a new blockedHostEngine.
|
||||
func newBlockedHostEngine(rules []string) (e *blockedHostEngine) {
|
||||
return &blockedHostEngine{
|
||||
rules: rules,
|
||||
initOnce: &sync.Once{},
|
||||
}
|
||||
}
|
||||
|
||||
// isBlocked returns true if the req is blocked by this engine.
|
||||
func (e *blockedHostEngine) isBlocked(req *dns.Msg) (blocked bool) {
|
||||
e.initOnce.Do(func() {
|
||||
start := time.Now()
|
||||
|
||||
e.lazyEngine = e.init()
|
||||
|
||||
metrics.AccessProfileInitDuration.Observe(time.Since(start).Seconds())
|
||||
})
|
||||
|
||||
q := req.Question[0]
|
||||
res, matched := e.lazyEngine.MatchRequest(&urlfilter.DNSRequest{
|
||||
Hostname: agdnet.NormalizeQueryDomain(q.Name),
|
||||
DNSType: q.Qtype,
|
||||
})
|
||||
|
||||
if matched && res.NetworkRule != nil {
|
||||
return !res.NetworkRule.Whitelist
|
||||
}
|
||||
|
||||
return matched
|
||||
}
|
||||
|
||||
// init returns new properly initialized dns engine.
|
||||
func (e *blockedHostEngine) init() (eng *urlfilter.DNSEngine) {
|
||||
b := &strings.Builder{}
|
||||
for _, h := range e.rules {
|
||||
stringutil.WriteToBuilder(b, strings.ToLower(h), "\n")
|
||||
}
|
||||
|
||||
lists := []filterlist.RuleList{
|
||||
&filterlist.StringRuleList{
|
||||
ID: blocklistFilterID,
|
||||
RulesText: b.String(),
|
||||
IgnoreCosmetic: true,
|
||||
},
|
||||
}
|
||||
|
||||
rulesStrg, err := filterlist.NewRuleStorage(lists)
|
||||
if err != nil {
|
||||
// Should never happen, since the storage has only one list.
|
||||
panic(fmt.Errorf("unexpected access config error: %w", err))
|
||||
}
|
||||
|
||||
return urlfilter.NewDNSEngine(rulesStrg)
|
||||
}
|
107
internal/access/engine_internal_test.go
Normal file
107
internal/access/engine_internal_test.go
Normal file
@ -0,0 +1,107 @@
|
||||
package access
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/dnsservertest"
|
||||
"github.com/miekg/dns"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestBlockedHostEngine_IsBlocked(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
rules := []string{
|
||||
"block.test",
|
||||
"UPPERCASE.test",
|
||||
"||block_aaaa.test^$dnstype=AAAA",
|
||||
"||allowlist.test^",
|
||||
"@@||allow.allowlist.test^",
|
||||
}
|
||||
|
||||
engine := newBlockedHostEngine(rules)
|
||||
|
||||
testCases := []struct {
|
||||
want assert.BoolAssertionFunc
|
||||
name string
|
||||
host string
|
||||
qt uint16
|
||||
}{{
|
||||
want: assert.False,
|
||||
name: "pass",
|
||||
host: "pass.test",
|
||||
qt: dns.TypeA,
|
||||
}, {
|
||||
want: assert.True,
|
||||
name: "blocked_domain_A",
|
||||
host: "block.test",
|
||||
qt: dns.TypeA,
|
||||
}, {
|
||||
want: assert.True,
|
||||
name: "blocked_domain_HTTPS",
|
||||
host: "block.test",
|
||||
qt: dns.TypeHTTPS,
|
||||
}, {
|
||||
want: assert.True,
|
||||
name: "uppercase_domain",
|
||||
host: "uppercase.test",
|
||||
qt: dns.TypeHTTPS,
|
||||
}, {
|
||||
want: assert.False,
|
||||
name: "pass_qt",
|
||||
host: "block_aaaa.test",
|
||||
qt: dns.TypeA,
|
||||
}, {
|
||||
want: assert.True,
|
||||
name: "block_qt",
|
||||
host: "block_aaaa.test",
|
||||
qt: dns.TypeAAAA,
|
||||
}, {
|
||||
want: assert.True,
|
||||
name: "allowlist_block",
|
||||
host: "block.allowlist.test",
|
||||
qt: dns.TypeA,
|
||||
}, {
|
||||
want: assert.False,
|
||||
name: "allowlist_test",
|
||||
host: "allow.allowlist.test",
|
||||
qt: dns.TypeA,
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
req := dnsservertest.NewReq(tc.host, tc.qt, dns.ClassINET)
|
||||
|
||||
blocked := engine.isBlocked(req)
|
||||
tc.want(t, blocked)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBlockedHostEngine_IsBlocked_concurrent(t *testing.T) {
|
||||
const routinesLimit = 50
|
||||
|
||||
rules := []string{"||block.test^"}
|
||||
engine := newBlockedHostEngine(rules)
|
||||
|
||||
wg := &sync.WaitGroup{}
|
||||
for i := 0; i < routinesLimit; i++ {
|
||||
wg.Add(1)
|
||||
|
||||
host := fmt.Sprintf("%d.%s", i, "block.test")
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
||||
req := dnsservertest.NewReq(host, dns.TypeA, dns.ClassINET)
|
||||
assert.True(t, engine.isBlocked(req))
|
||||
}()
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
}
|
137
internal/access/profile.go
Normal file
137
internal/access/profile.go
Normal file
@ -0,0 +1,137 @@
|
||||
package access
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
"slices"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/geoip"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
// Profile is the profile access manager interface.
|
||||
type Profile interface {
|
||||
// Config returns profile access configuration.
|
||||
Config() (conf *ProfileConfig)
|
||||
|
||||
// IsBlocked returns true if the req should be blocked. req must not be
|
||||
// nil, and req.Question must have one item.
|
||||
IsBlocked(req *dns.Msg, rAddr netip.AddrPort, l *geoip.Location) (blocked bool)
|
||||
}
|
||||
|
||||
// EmptyProfile is an empty profile implementation that does nothing.
|
||||
type EmptyProfile struct{}
|
||||
|
||||
// type check
|
||||
var _ Profile = EmptyProfile{}
|
||||
|
||||
// Config implements the [Profile] interface for EmptyProfile. It always
|
||||
// returns nil.
|
||||
func (EmptyProfile) Config() (conf *ProfileConfig) { return nil }
|
||||
|
||||
// IsBlocked implements the [Profile] interface for EmptyProfile. It always
|
||||
// returns false.
|
||||
func (EmptyProfile) IsBlocked(_ *dns.Msg, _ netip.AddrPort, _ *geoip.Location) (blocked bool) {
|
||||
return false
|
||||
}
|
||||
|
||||
// ProfileConfig is a profile specific access configuration.
|
||||
//
|
||||
// NOTE: Do not change fields of this structure without incrementing
|
||||
// [internal/profiledb/internal.FileCacheVersion].
|
||||
type ProfileConfig struct {
|
||||
// AllowedNets is slice of CIDRs to be allowed.
|
||||
AllowedNets []netip.Prefix
|
||||
|
||||
// BlockedNets is slice of CIDRs to be blocked.
|
||||
BlockedNets []netip.Prefix
|
||||
|
||||
// AllowedNets is slice of location ASNs to be allowed.
|
||||
AllowedASN []geoip.ASN
|
||||
|
||||
// BlockedASN is slice of location ASNs to be blocked.
|
||||
BlockedASN []geoip.ASN
|
||||
|
||||
// BlocklistDomainRules is slice of rules to match requests.
|
||||
BlocklistDomainRules []string
|
||||
}
|
||||
|
||||
// DefaultProfile controls profile specific IP and client blocking that take
|
||||
// place before all other processing. DefaultProfile is safe for concurrent
|
||||
// use.
|
||||
type DefaultProfile struct {
|
||||
blockedHostsEng *blockedHostEngine
|
||||
|
||||
allowedNets []netip.Prefix
|
||||
blockedNets []netip.Prefix
|
||||
|
||||
// TODO(d.kolyshev): Change to map[geoip.ASN]unit to improve performance.
|
||||
allowedASN []geoip.ASN
|
||||
blockedASN []geoip.ASN
|
||||
|
||||
blocklistDomainRules []string
|
||||
}
|
||||
|
||||
// NewDefaultProfile creates a new *DefaultProfile. conf is assumed to be
|
||||
// valid.
|
||||
func NewDefaultProfile(conf *ProfileConfig) (p *DefaultProfile) {
|
||||
return &DefaultProfile{
|
||||
allowedNets: conf.AllowedNets,
|
||||
blockedNets: conf.BlockedNets,
|
||||
allowedASN: conf.AllowedASN,
|
||||
blockedASN: conf.BlockedASN,
|
||||
blocklistDomainRules: conf.BlocklistDomainRules,
|
||||
blockedHostsEng: newBlockedHostEngine(conf.BlocklistDomainRules),
|
||||
}
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ Profile = (*DefaultProfile)(nil)
|
||||
|
||||
// Config implements the [Profile] interface for *DefaultProfile.
|
||||
func (p *DefaultProfile) Config() (conf *ProfileConfig) {
|
||||
return &ProfileConfig{
|
||||
AllowedNets: slices.Clone(p.allowedNets),
|
||||
BlockedNets: slices.Clone(p.blockedNets),
|
||||
AllowedASN: slices.Clone(p.allowedASN),
|
||||
BlockedASN: slices.Clone(p.blockedASN),
|
||||
BlocklistDomainRules: slices.Clone(p.blocklistDomainRules),
|
||||
}
|
||||
}
|
||||
|
||||
// IsBlocked implements the [Profile] interface for *DefaultProfile.
|
||||
func (p *DefaultProfile) IsBlocked(req *dns.Msg, rAddr netip.AddrPort, l *geoip.Location) (blocked bool) {
|
||||
ip := rAddr.Addr()
|
||||
|
||||
return p.isBlockedByNets(ip, l) || p.isBlockedByHostsEng(req)
|
||||
}
|
||||
|
||||
// isBlockedByNets returns true if ip or l is blocked by current profile.
|
||||
func (p *DefaultProfile) isBlockedByNets(ip netip.Addr, l *geoip.Location) (blocked bool) {
|
||||
if matchASNs(p.allowedASN, l) || matchNets(p.allowedNets, ip) {
|
||||
return false
|
||||
}
|
||||
|
||||
return matchASNs(p.blockedASN, l) || matchNets(p.blockedNets, ip)
|
||||
}
|
||||
|
||||
// matchNets returns true if ip is contained by any of the subnets.
|
||||
func matchNets(nets []netip.Prefix, ip netip.Addr) (ok bool) {
|
||||
for _, n := range nets {
|
||||
if n.Contains(ip) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// matchASNs returns true if l is not nil and its asn is included in asns.
|
||||
func matchASNs(asns []geoip.ASN, l *geoip.Location) (ok bool) {
|
||||
return l != nil && slices.Contains(asns, l.ASN)
|
||||
}
|
||||
|
||||
// isBlockedByHostsEng returns true if the req is blocked by
|
||||
// BlocklistDomainRules.
|
||||
func (p *DefaultProfile) isBlockedByHostsEng(req *dns.Msg) (blocked bool) {
|
||||
return p.blockedHostsEng.isBlocked(req)
|
||||
}
|
265
internal/access/profile_test.go
Normal file
265
internal/access/profile_test.go
Normal file
@ -0,0 +1,265 @@
|
||||
package access_test
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
"testing"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/access"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/dnsservertest"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/geoip"
|
||||
"github.com/miekg/dns"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestDefaultProfile_Config(t *testing.T) {
|
||||
conf := &access.ProfileConfig{
|
||||
AllowedNets: []netip.Prefix{netip.MustParsePrefix("1.1.1.0/24")},
|
||||
BlockedNets: []netip.Prefix{netip.MustParsePrefix("2.2.2.0/24")},
|
||||
AllowedASN: []geoip.ASN{1},
|
||||
BlockedASN: []geoip.ASN{1, 2},
|
||||
BlocklistDomainRules: []string{"block.test"},
|
||||
}
|
||||
|
||||
a := access.NewDefaultProfile(conf)
|
||||
got := a.Config()
|
||||
assert.Equal(t, conf, got)
|
||||
}
|
||||
|
||||
func TestDefaultProfile_IsBlocked(t *testing.T) {
|
||||
passAddrPort := netip.MustParseAddrPort("3.3.3.3:3333")
|
||||
|
||||
conf := &access.ProfileConfig{
|
||||
AllowedNets: []netip.Prefix{netip.MustParsePrefix("1.1.1.1/32")},
|
||||
BlockedNets: []netip.Prefix{netip.MustParsePrefix("1.1.1.0/24")},
|
||||
AllowedASN: []geoip.ASN{1},
|
||||
BlockedASN: []geoip.ASN{1, 2},
|
||||
BlocklistDomainRules: []string{
|
||||
"block.test",
|
||||
"UPPERCASE.test",
|
||||
"||block_aaaa.test^$dnstype=AAAA",
|
||||
"||allowlist.test^",
|
||||
"@@||allow.allowlist.test^",
|
||||
},
|
||||
}
|
||||
|
||||
a := access.NewDefaultProfile(conf)
|
||||
|
||||
testCases := []struct {
|
||||
loc *geoip.Location
|
||||
want assert.BoolAssertionFunc
|
||||
rAddr netip.AddrPort
|
||||
name string
|
||||
host string
|
||||
qt uint16
|
||||
}{{
|
||||
want: assert.False,
|
||||
name: "pass",
|
||||
host: "pass.test",
|
||||
qt: dns.TypeA,
|
||||
rAddr: passAddrPort,
|
||||
loc: nil,
|
||||
}, {
|
||||
want: assert.True,
|
||||
name: "blocked_domain_A",
|
||||
host: "block.test",
|
||||
qt: dns.TypeA,
|
||||
rAddr: passAddrPort,
|
||||
loc: nil,
|
||||
}, {
|
||||
want: assert.True,
|
||||
name: "blocked_domain_HTTPS",
|
||||
host: "block.test",
|
||||
qt: dns.TypeHTTPS,
|
||||
rAddr: passAddrPort,
|
||||
loc: nil,
|
||||
}, {
|
||||
want: assert.True,
|
||||
name: "uppercase_domain",
|
||||
host: "uppercase.test",
|
||||
qt: dns.TypeHTTPS,
|
||||
rAddr: passAddrPort,
|
||||
loc: nil,
|
||||
}, {
|
||||
want: assert.False,
|
||||
name: "pass_qt",
|
||||
host: "block_aaaa.test",
|
||||
qt: dns.TypeA,
|
||||
rAddr: passAddrPort,
|
||||
loc: nil,
|
||||
}, {
|
||||
want: assert.True,
|
||||
name: "block_qt",
|
||||
host: "block_aaaa.test",
|
||||
qt: dns.TypeAAAA,
|
||||
rAddr: passAddrPort,
|
||||
loc: nil,
|
||||
}, {
|
||||
want: assert.True,
|
||||
name: "allowlist_block",
|
||||
host: "block.allowlist.test",
|
||||
qt: dns.TypeA,
|
||||
rAddr: passAddrPort,
|
||||
loc: nil,
|
||||
}, {
|
||||
want: assert.False,
|
||||
name: "allowlist_test",
|
||||
host: "allow.allowlist.test",
|
||||
qt: dns.TypeA,
|
||||
rAddr: passAddrPort,
|
||||
loc: nil,
|
||||
}, {
|
||||
want: assert.False,
|
||||
name: "pass_ip",
|
||||
rAddr: netip.MustParseAddrPort("1.1.1.1:57"),
|
||||
host: "pass.test",
|
||||
qt: dns.TypeA,
|
||||
loc: nil,
|
||||
}, {
|
||||
want: assert.True,
|
||||
name: "block_subnet",
|
||||
rAddr: netip.MustParseAddrPort("1.1.1.2:57"),
|
||||
host: "pass.test",
|
||||
qt: dns.TypeA,
|
||||
loc: nil,
|
||||
}, {
|
||||
want: assert.False,
|
||||
name: "pass_subnet",
|
||||
rAddr: netip.MustParseAddrPort("1.2.2.2:57"),
|
||||
host: "pass.test",
|
||||
qt: dns.TypeA,
|
||||
loc: nil,
|
||||
}, {
|
||||
want: assert.True,
|
||||
name: "block_host_pass_asn",
|
||||
rAddr: passAddrPort,
|
||||
host: "block.test",
|
||||
qt: dns.TypeA,
|
||||
loc: &geoip.Location{ASN: 1},
|
||||
}, {
|
||||
want: assert.False,
|
||||
name: "pass_asn",
|
||||
rAddr: passAddrPort,
|
||||
host: "pass.test",
|
||||
qt: dns.TypeA,
|
||||
loc: &geoip.Location{ASN: 1},
|
||||
}, {
|
||||
want: assert.True,
|
||||
name: "block_asn",
|
||||
rAddr: passAddrPort,
|
||||
host: "pass.test",
|
||||
qt: dns.TypeA,
|
||||
loc: &geoip.Location{ASN: 2},
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
req := dnsservertest.NewReq(tc.host, tc.qt, dns.ClassINET)
|
||||
|
||||
blocked := a.IsBlocked(req, tc.rAddr, tc.loc)
|
||||
tc.want(t, blocked)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefaultProfile_IsBlocked_prefixAllowlist(t *testing.T) {
|
||||
conf := &access.ProfileConfig{
|
||||
AllowedNets: []netip.Prefix{
|
||||
netip.MustParsePrefix("2.2.2.0/24"),
|
||||
netip.MustParsePrefix("3.3.0.0/16"),
|
||||
},
|
||||
BlockedNets: []netip.Prefix{netip.MustParsePrefix("0.0.0.0/0")},
|
||||
AllowedASN: nil,
|
||||
BlockedASN: nil,
|
||||
BlocklistDomainRules: nil,
|
||||
}
|
||||
|
||||
a := access.NewDefaultProfile(conf)
|
||||
|
||||
testCases := []struct {
|
||||
want assert.BoolAssertionFunc
|
||||
rAddr netip.AddrPort
|
||||
name string
|
||||
}{{
|
||||
want: assert.True,
|
||||
name: "block_before",
|
||||
rAddr: netip.MustParseAddrPort("1.1.1.1:2222"),
|
||||
}, {
|
||||
want: assert.False,
|
||||
name: "allow_first",
|
||||
rAddr: netip.MustParseAddrPort("2.2.2.1:2222"),
|
||||
}, {
|
||||
want: assert.False,
|
||||
name: "allow_second",
|
||||
rAddr: netip.MustParseAddrPort("3.3.1.1:2222"),
|
||||
}, {
|
||||
want: assert.True,
|
||||
name: "block_second",
|
||||
rAddr: netip.MustParseAddrPort("3.4.1.1:2222"),
|
||||
}, {
|
||||
want: assert.True,
|
||||
name: "block_after",
|
||||
rAddr: netip.MustParseAddrPort("4.4.1.1:2222"),
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
req := dnsservertest.NewReq("pass.test", dns.TypeA, dns.ClassINET)
|
||||
|
||||
blocked := a.IsBlocked(req, tc.rAddr, nil)
|
||||
tc.want(t, blocked)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkDefaultProfile_IsBlocked(b *testing.B) {
|
||||
passAddrPort := netip.MustParseAddrPort("3.3.3.3:3333")
|
||||
|
||||
conf := &access.ProfileConfig{
|
||||
AllowedNets: []netip.Prefix{netip.MustParsePrefix("1.1.1.1/32")},
|
||||
BlockedNets: []netip.Prefix{netip.MustParsePrefix("1.1.1.0/24")},
|
||||
AllowedASN: []geoip.ASN{1},
|
||||
BlockedASN: []geoip.ASN{1, 2},
|
||||
BlocklistDomainRules: []string{
|
||||
"block.test",
|
||||
"UPPERCASE.test",
|
||||
"||block_aaaa.test^$dnstype=AAAA",
|
||||
"||allowlist.test^",
|
||||
"@@||allow.allowlist.test^",
|
||||
},
|
||||
}
|
||||
|
||||
a := access.NewDefaultProfile(conf)
|
||||
|
||||
passReq := dnsservertest.NewReq("pass.test", dns.TypeA, dns.ClassINET)
|
||||
|
||||
b.Run("pass", func(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = a.IsBlocked(passReq, passAddrPort, nil)
|
||||
}
|
||||
})
|
||||
|
||||
blockReq := dnsservertest.NewReq("block.test", dns.TypeA, dns.ClassINET)
|
||||
|
||||
b.Run("block", func(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = a.IsBlocked(blockReq, passAddrPort, nil)
|
||||
}
|
||||
})
|
||||
|
||||
// Most recent results, on a MBP 14 with Apple M1 Pro chip:
|
||||
//
|
||||
// goos: darwin
|
||||
// goarch: arm64
|
||||
// pkg: github.com/AdguardTeam/AdGuardDNS/internal/access
|
||||
// BenchmarkDefaultProfile_IsBlocked
|
||||
// BenchmarkDefaultProfile_IsBlocked/pass
|
||||
// BenchmarkDefaultProfile_IsBlocked/pass-8 2935430 357.7 ns/op 384 B/op 4 allocs/op
|
||||
// BenchmarkDefaultProfile_IsBlocked/block
|
||||
// BenchmarkDefaultProfile_IsBlocked/block-8 2706435 443.7 ns/op 416 B/op 6 allocs/op
|
||||
}
|
@ -7,9 +7,6 @@ import (
|
||||
|
||||
// Common Constants, Types, And Utilities
|
||||
|
||||
// unit is a convenient alias for struct{}.
|
||||
type unit = struct{}
|
||||
|
||||
// firstNonIDRune returns the first non-printable or non-ASCII rune and its
|
||||
// index. If slashes is true, it also looks for slashes. If there are no such
|
||||
// runes, i is -1.
|
||||
|
@ -2,16 +2,10 @@ package agd_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/golibs/testutil"
|
||||
)
|
||||
|
||||
// Common Constants And Utilities
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
testutil.DiscardLogOutput(m)
|
||||
}
|
||||
|
||||
// testTimeout is the timeout for common test operations.
|
||||
const testTimeout = 1 * time.Second
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"net/netip"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/geoip"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
)
|
||||
|
||||
@ -73,7 +74,7 @@ type RequestInfo struct {
|
||||
Profile *Profile
|
||||
|
||||
// Location is the GeoIP location data about the remote IP address, if any.
|
||||
Location *Location
|
||||
Location *geoip.Location
|
||||
|
||||
// ECS contains the EDNS Client Subnet option information of the request, if
|
||||
// any.
|
||||
@ -107,9 +108,10 @@ type RequestInfo struct {
|
||||
QType dnsmsg.RRType
|
||||
|
||||
// QClass is the class of question for this request.
|
||||
//
|
||||
// TODO(a.garipov): Use more.
|
||||
QClass dnsmsg.Class
|
||||
|
||||
// Proto is the protocol by which this request is made.
|
||||
Proto Protocol
|
||||
}
|
||||
|
||||
// ECS is the content of the EDNS Client Subnet option of a DNS message.
|
||||
@ -118,7 +120,7 @@ type RequestInfo struct {
|
||||
type ECS struct {
|
||||
// Location is the GeoIP location data about the IP address from the
|
||||
// request's ECS data, if any.
|
||||
Location *Location
|
||||
Location *geoip.Location
|
||||
|
||||
// Subnet is the source subnet.
|
||||
Subnet netip.Prefix
|
||||
|
@ -24,27 +24,3 @@ func (err *ArgumentError) Error() (msg string) {
|
||||
|
||||
return fmt.Sprintf("argument %s is invalid: %s", err.Name, err.Message)
|
||||
}
|
||||
|
||||
// NotACountryError is returned from NewCountry when the string doesn't represent
|
||||
// a valid country.
|
||||
type NotACountryError struct {
|
||||
// Code is the code presented to NewCountry.
|
||||
Code string
|
||||
}
|
||||
|
||||
// Error implements the error interface for *NotACountryError.
|
||||
func (err *NotACountryError) Error() (msg string) {
|
||||
return fmt.Sprintf("%q is not a valid iso 3166-1 alpha-2 code", err.Code)
|
||||
}
|
||||
|
||||
// NotAContinentError is returned from NewContinent when the string doesn't
|
||||
// represent a valid continent.
|
||||
type NotAContinentError struct {
|
||||
// Code is the code presented to NewContinent.
|
||||
Code string
|
||||
}
|
||||
|
||||
// Error implements the error interface for *NotAContinentError.
|
||||
func (err *NotAContinentError) Error() (msg string) {
|
||||
return fmt.Sprintf("%q is not a valid continent code", err.Code)
|
||||
}
|
||||
|
@ -1,24 +0,0 @@
|
||||
package agd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
)
|
||||
|
||||
// Error Collector
|
||||
|
||||
// ErrorCollector collects information about errors, possibly sending them to
|
||||
// a remote location.
|
||||
type ErrorCollector interface {
|
||||
Collect(ctx context.Context, err error)
|
||||
}
|
||||
|
||||
// Collectf is a helper method for reporting non-critical errors. It writes the
|
||||
// resulting error into the log and also into the error collector.
|
||||
func Collectf(ctx context.Context, errColl ErrorCollector, format string, args ...any) {
|
||||
err := fmt.Errorf(format, args...)
|
||||
log.Error("%s", err)
|
||||
errColl.Collect(ctx, err)
|
||||
}
|
@ -5,6 +5,7 @@ import (
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/access"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agdtime"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
@ -37,11 +38,17 @@ type Profile struct {
|
||||
// [internal/profiledb/internal.FileCacheVersion].
|
||||
SafeBrowsing *SafeBrowsingSettings
|
||||
|
||||
// Access is the access manager for this profile. Access is never nil.
|
||||
//
|
||||
// NOTE: Do not change fields of this structure without incrementing
|
||||
// [internal/profiledb/internal.FileCacheVersion].
|
||||
Access access.Profile
|
||||
|
||||
// BlockingMode defines the way blocked responses are constructed.
|
||||
//
|
||||
// NOTE: Do not change fields of this structure without incrementing
|
||||
// [internal/profiledb/internal.FileCacheVersion].
|
||||
BlockingMode dnsmsg.BlockingModeCodec
|
||||
BlockingMode dnsmsg.BlockingMode
|
||||
|
||||
// ID is the unique ID of this profile.
|
||||
//
|
||||
@ -126,6 +133,12 @@ type Profile struct {
|
||||
// NOTE: Do not change fields of this structure without incrementing
|
||||
// [internal/profiledb/internal.FileCacheVersion].
|
||||
BlockFirefoxCanary bool
|
||||
|
||||
// IPLogEnabled shows if client IP addresses are logged.
|
||||
//
|
||||
// NOTE: Do not change fields of this structure without incrementing
|
||||
// [internal/profiledb/internal.FileCacheVersion].
|
||||
IPLogEnabled bool
|
||||
}
|
||||
|
||||
// ProfileID is the ID of a profile. It is an opaque string.
|
||||
|
@ -2,9 +2,13 @@ package agd
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net/netip"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agdnet"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/netext"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/stringutil"
|
||||
"github.com/ameshkov/dnscrypt/v2"
|
||||
"github.com/miekg/dns"
|
||||
@ -84,12 +88,34 @@ type Server struct {
|
||||
// TLS is the TLS configuration for this server, if any.
|
||||
TLS *tls.Config
|
||||
|
||||
// QUICConf is the QUIC configuration for this server.
|
||||
QUICConf *QUICConfig
|
||||
|
||||
// TCPConf is the TCP configuration for this server.
|
||||
TCPConf *TCPConfig
|
||||
|
||||
// UDPConf is the UDP configuration for this server.
|
||||
UDPConf *UDPConfig
|
||||
|
||||
// Name is the unique name of the server. Not to be confused with a TLS
|
||||
// Server Name.
|
||||
Name ServerName
|
||||
|
||||
// BindData are the socket binding data for this server.
|
||||
BindData []*ServerBindData
|
||||
// bindData are the socket binding data for this server.
|
||||
bindData []*ServerBindData
|
||||
|
||||
// ReadTimeout defines the timeout for any read from a UDP connection or the
|
||||
// first read from a TCP/TLS connection. It currently doesn't affect
|
||||
// DNSCrypt, QUIC, or HTTPS.
|
||||
//
|
||||
// TODO(a.garipov): Make it work for DNSCrypt, QUIC, and HTTPS.
|
||||
ReadTimeout time.Duration `yaml:"read_timeout"`
|
||||
|
||||
// WriteTimeout defines the timeout for writing to a UDP or TCP/TLS
|
||||
// connection. It currently doesn't affect DNSCrypt, QUIC, or HTTPS.
|
||||
//
|
||||
// TODO(a.garipov): Make it work for DNSCrypt, QUIC, and HTTPS.
|
||||
WriteTimeout time.Duration `yaml:"write_timeout"`
|
||||
|
||||
// Protocol is the protocol of the server.
|
||||
Protocol Protocol
|
||||
@ -99,8 +125,75 @@ type Server struct {
|
||||
LinkedIPEnabled bool
|
||||
}
|
||||
|
||||
// BindData returns the bind data of this server. The elements of the slice
|
||||
// must not be mutated.
|
||||
func (s *Server) BindData() (data []*ServerBindData) {
|
||||
return s.bindData
|
||||
}
|
||||
|
||||
// SetBindData sets the bind data of this server. data must have at least one
|
||||
// element and all of its elements must be of the same underlying type. The
|
||||
// elements of data must not be mutated after calling SetBindData.
|
||||
func (s *Server) SetBindData(data []*ServerBindData) {
|
||||
switch len(data) {
|
||||
case 0:
|
||||
panic(errors.Error("empty bind data"))
|
||||
case 1:
|
||||
s.bindData = data
|
||||
default:
|
||||
firstIsAddrPort := data[0].PrefixAddr == nil
|
||||
for i, bd := range data[1:] {
|
||||
if (bd.PrefixAddr == nil) != firstIsAddrPort {
|
||||
panic(fmt.Errorf("at index %d: inconsistent type of bind data", i+1))
|
||||
}
|
||||
}
|
||||
|
||||
s.bindData = data
|
||||
}
|
||||
}
|
||||
|
||||
// HasAddr returns true if addr is within the server's bind data. HasAddr does
|
||||
// not check prefix addresses unless they are single-IP subnets.
|
||||
func (s *Server) HasAddr(addr netip.AddrPort) (ok bool) {
|
||||
for _, bd := range s.bindData {
|
||||
prefAddr := bd.PrefixAddr
|
||||
if prefAddr == nil {
|
||||
if bd.AddrPort == addr {
|
||||
return true
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
p := prefAddr.Prefix
|
||||
if p.IsSingleIP() && p.Addr() == addr.Addr() && prefAddr.Port == addr.Port() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// HasIPv6 returns true if the bind data of this server contains an IPv6
|
||||
// address. For a server with no bind data, HasIPv6 returns false.
|
||||
func (s *Server) HasIPv6() (ok bool) {
|
||||
for _, bd := range s.bindData {
|
||||
if bd.AddrPort.Addr().Is6() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// BindsToInterfaces returns true if server binds to interfaces. For a server
|
||||
// with no bind data, BindsToInterfaces returns false.
|
||||
func (s *Server) BindsToInterfaces() (ok bool) {
|
||||
return len(s.bindData) > 0 && s.bindData[0].PrefixAddr != nil
|
||||
}
|
||||
|
||||
// ServerBindData are the socket binding data for a server. Either AddrPort or
|
||||
// ListenConfig with Address must be set.
|
||||
// ListenConfig with PrefixAddr must be set.
|
||||
//
|
||||
// TODO(a.garipov): Consider turning this into a sum type.
|
||||
//
|
||||
@ -108,7 +201,7 @@ type Server struct {
|
||||
// like BindConfig.
|
||||
type ServerBindData struct {
|
||||
ListenConfig netext.ListenConfig
|
||||
Address string
|
||||
PrefixAddr *agdnet.PrefixNetAddr
|
||||
|
||||
AddrPort netip.AddrPort
|
||||
}
|
||||
@ -124,3 +217,35 @@ type DNSCryptConfig struct {
|
||||
// ProviderName is the name of the DNSCrypt provider.
|
||||
ProviderName string
|
||||
}
|
||||
|
||||
// TCPConfig is the TCP configuration of a DNS server.
|
||||
type TCPConfig struct {
|
||||
// IdleTimeout defines the timeout for consecutive reads from a TCP/TLS
|
||||
// connection.
|
||||
IdleTimeout time.Duration
|
||||
|
||||
// MaxPipelineCount is the maximum number of simultaneously processing TCP
|
||||
// messages per one connection. If MaxPipelineEnabled is true, it must be
|
||||
// greater than zero.
|
||||
MaxPipelineCount uint
|
||||
|
||||
// MaxPipelineEnabled, if true, enables TCP pipeline limiting.
|
||||
MaxPipelineEnabled bool
|
||||
}
|
||||
|
||||
// UDPConfig is the UDP configuration of a DNS server.
|
||||
type UDPConfig struct {
|
||||
// MaxRespSize is the maximum size in bytes of DNS response over UDP
|
||||
// protocol.
|
||||
MaxRespSize uint16
|
||||
}
|
||||
|
||||
// QUICConfig is the QUIC configuration of a DNS server.
|
||||
type QUICConfig struct {
|
||||
// MaxStreamsPerPeer is the maximum number of concurrent streams that a peer
|
||||
// is allowed to open.
|
||||
MaxStreamsPerPeer int
|
||||
|
||||
// QUICLimitsEnabled, if true, enables QUIC limiting.
|
||||
QUICLimitsEnabled bool
|
||||
}
|
||||
|
152
internal/agd/server_test.go
Normal file
152
internal/agd/server_test.go
Normal file
@ -0,0 +1,152 @@
|
||||
package agd_test
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
"testing"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agdnet"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agdtest"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// Common variables for tests.
|
||||
var (
|
||||
bindDataAddrPortV4 = &agd.ServerBindData{
|
||||
AddrPort: netip.MustParseAddrPort("1.2.3.4:53"),
|
||||
}
|
||||
|
||||
bindDataAddrPortV6 = &agd.ServerBindData{
|
||||
AddrPort: netip.MustParseAddrPort("[1234::cdef]:53"),
|
||||
}
|
||||
|
||||
bindDataIface = &agd.ServerBindData{
|
||||
ListenConfig: &agdtest.ListenConfig{},
|
||||
PrefixAddr: &agdnet.PrefixNetAddr{
|
||||
Prefix: netip.MustParsePrefix("1.2.3.0/24"),
|
||||
Net: "",
|
||||
Port: 53,
|
||||
},
|
||||
}
|
||||
|
||||
bindDataIfaceSingleIP = &agd.ServerBindData{
|
||||
ListenConfig: &agdtest.ListenConfig{},
|
||||
PrefixAddr: &agdnet.PrefixNetAddr{
|
||||
Prefix: netip.MustParsePrefix("1.2.3.4/32"),
|
||||
Net: "",
|
||||
Port: 53,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func TestServer_SetBindData(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
wantPanicMsg string
|
||||
in []*agd.ServerBindData
|
||||
}{{
|
||||
name: "nil",
|
||||
wantPanicMsg: "empty bind data",
|
||||
in: nil,
|
||||
}, {
|
||||
name: "empty",
|
||||
wantPanicMsg: "empty bind data",
|
||||
in: []*agd.ServerBindData{},
|
||||
}, {
|
||||
name: "one",
|
||||
wantPanicMsg: "",
|
||||
in: []*agd.ServerBindData{bindDataAddrPortV4},
|
||||
}, {
|
||||
name: "two_same_type",
|
||||
wantPanicMsg: "",
|
||||
in: []*agd.ServerBindData{
|
||||
bindDataAddrPortV4,
|
||||
bindDataAddrPortV6,
|
||||
},
|
||||
}, {
|
||||
name: "two_diff_type",
|
||||
wantPanicMsg: "at index 1: inconsistent type of bind data",
|
||||
in: []*agd.ServerBindData{
|
||||
bindDataAddrPortV4,
|
||||
bindDataIface,
|
||||
},
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
f := func() {
|
||||
s := &agd.Server{}
|
||||
s.SetBindData(tc.in)
|
||||
|
||||
assert.NotEmpty(t, s.BindData())
|
||||
}
|
||||
|
||||
if tc.wantPanicMsg == "" {
|
||||
assert.NotPanics(t, f)
|
||||
} else {
|
||||
assert.PanicsWithError(t, tc.wantPanicMsg, f)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestServer_BindsToInterfaces(t *testing.T) {
|
||||
s := &agd.Server{}
|
||||
assert.False(t, s.BindsToInterfaces())
|
||||
|
||||
s.SetBindData([]*agd.ServerBindData{bindDataAddrPortV4})
|
||||
assert.False(t, s.BindsToInterfaces())
|
||||
|
||||
s.SetBindData([]*agd.ServerBindData{bindDataIface})
|
||||
assert.True(t, s.BindsToInterfaces())
|
||||
|
||||
s.SetBindData([]*agd.ServerBindData{bindDataIfaceSingleIP})
|
||||
assert.True(t, s.BindsToInterfaces())
|
||||
}
|
||||
|
||||
func TestServer_HasAddr(t *testing.T) {
|
||||
testCases := []struct {
|
||||
bindData *agd.ServerBindData
|
||||
want assert.BoolAssertionFunc
|
||||
name string
|
||||
}{{
|
||||
bindData: bindDataAddrPortV4,
|
||||
want: assert.True,
|
||||
name: "addr_has",
|
||||
}, {
|
||||
bindData: bindDataAddrPortV6,
|
||||
want: assert.False,
|
||||
name: "addr_missing",
|
||||
}, {
|
||||
bindData: bindDataIfaceSingleIP,
|
||||
want: assert.True,
|
||||
name: "prefix_has",
|
||||
}, {
|
||||
bindData: bindDataIface,
|
||||
want: assert.False,
|
||||
name: "prefix_missing",
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
s := &agd.Server{}
|
||||
require.NotPanics(t, func() {
|
||||
s.SetBindData([]*agd.ServerBindData{tc.bindData})
|
||||
})
|
||||
|
||||
tc.want(t, s.HasAddr(bindDataAddrPortV4.AddrPort))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestServer_HasIPv6(t *testing.T) {
|
||||
s := &agd.Server{}
|
||||
assert.False(t, s.HasIPv6())
|
||||
|
||||
s.SetBindData([]*agd.ServerBindData{bindDataAddrPortV4})
|
||||
assert.False(t, s.HasIPv6())
|
||||
|
||||
s.SetBindData([]*agd.ServerBindData{bindDataAddrPortV6})
|
||||
assert.True(t, s.HasIPv6())
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
package agd
|
||||
|
||||
import "context"
|
||||
|
||||
// Service is the interface for API servers.
|
||||
type Service interface {
|
||||
// Start starts the service. It must not block.
|
||||
Start() (err error)
|
||||
|
||||
// Shutdown gracefully stops the service. ctx is used to determine
|
||||
// a timeout before trying to stop the service less gracefully.
|
||||
Shutdown(ctx context.Context) (err error)
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ Service = EmptyService{}
|
||||
|
||||
// EmptyService is an agd.Service that does nothing.
|
||||
type EmptyService struct{}
|
||||
|
||||
// Start implements the Service interface for EmptyService.
|
||||
func (EmptyService) Start() (err error) { return nil }
|
||||
|
||||
// Shutdown implements the Service interface for EmptyService.
|
||||
func (EmptyService) Shutdown(_ context.Context) (err error) { return nil }
|
@ -36,15 +36,3 @@ func ParseHTTPURL(s string) (u *url.URL, err error) {
|
||||
return u, nil
|
||||
}
|
||||
}
|
||||
|
||||
// URL is a wrapper around *url.URL that can unmarshal itself from JSON or YAML.
|
||||
//
|
||||
// TODO(a.garipov): Move to netutil if we need it somewhere else.
|
||||
type URL struct {
|
||||
url.URL
|
||||
}
|
||||
|
||||
// UnmarshalText implements the encoding.TextUnmarshaler interface for *URL.
|
||||
func (u *URL) UnmarshalText(b []byte) (err error) {
|
||||
return u.UnmarshalBinary(b)
|
||||
}
|
||||
|
@ -61,13 +61,6 @@ func TestParseHTTPURL(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestURL_UnmarshalText(t *testing.T) {
|
||||
u := &agdhttp.URL{
|
||||
URL: *testURL(),
|
||||
}
|
||||
testutil.AssertUnmarshalText(t, u.String(), u)
|
||||
}
|
||||
|
||||
func testURL() (u *url.URL) {
|
||||
return &url.URL{
|
||||
Scheme: "http",
|
||||
|
@ -1,58 +0,0 @@
|
||||
// Package agdio contains extensions and utilities for package io from the
|
||||
// standard library.
|
||||
//
|
||||
// TODO(a.garipov): Move to module golibs.
|
||||
package agdio
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/AdguardTeam/golibs/mathutil"
|
||||
)
|
||||
|
||||
// LimitError is returned when the Limit is reached.
|
||||
type LimitError struct {
|
||||
// Limit is the limit that triggered the error.
|
||||
Limit int64
|
||||
}
|
||||
|
||||
// Error implements the error interface for *LimitError.
|
||||
func (err *LimitError) Error() string {
|
||||
return fmt.Sprintf("cannot read more than %d bytes", err.Limit)
|
||||
}
|
||||
|
||||
// limitedReader is a wrapper for io.Reader that has a reading limit.
|
||||
type limitedReader struct {
|
||||
r io.Reader
|
||||
limit int64
|
||||
n int64
|
||||
}
|
||||
|
||||
// Read implements the io.Reader interface for *limitedReader.
|
||||
func (lr *limitedReader) Read(p []byte) (n int, err error) {
|
||||
if lr.n == 0 {
|
||||
return 0, &LimitError{
|
||||
Limit: lr.limit,
|
||||
}
|
||||
}
|
||||
|
||||
l := mathutil.Min(int64(len(p)), lr.n)
|
||||
p = p[:l]
|
||||
|
||||
n, err = lr.r.Read(p)
|
||||
lr.n -= int64(n)
|
||||
|
||||
return n, err
|
||||
}
|
||||
|
||||
// LimitReader returns an io.Reader that reads up to n bytes. Once that limit
|
||||
// is reached, ErrLimit is returned from limited's Read method. Method
|
||||
// Read of limited is not safe for concurrent use. n must be non-negative.
|
||||
func LimitReader(r io.Reader, n int64) (limited io.Reader) {
|
||||
return &limitedReader{
|
||||
r: r,
|
||||
limit: n,
|
||||
n: n,
|
||||
}
|
||||
}
|
@ -1,69 +0,0 @@
|
||||
package agdio_test
|
||||
|
||||
import (
|
||||
"io"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agdio"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestLimitedReader_Read(t *testing.T) {
|
||||
testCases := []struct {
|
||||
err error
|
||||
name string
|
||||
rStr string
|
||||
limit int64
|
||||
want int
|
||||
}{{
|
||||
err: nil,
|
||||
name: "perfectly_match",
|
||||
rStr: "abc",
|
||||
limit: 3,
|
||||
want: 3,
|
||||
}, {
|
||||
err: io.EOF,
|
||||
name: "eof",
|
||||
rStr: "",
|
||||
limit: 3,
|
||||
want: 0,
|
||||
}, {
|
||||
err: &agdio.LimitError{
|
||||
Limit: 0,
|
||||
},
|
||||
name: "limit_reached",
|
||||
rStr: "abc",
|
||||
limit: 0,
|
||||
want: 0,
|
||||
}, {
|
||||
err: nil,
|
||||
name: "truncated",
|
||||
rStr: "abc",
|
||||
limit: 2,
|
||||
want: 2,
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
readCloser := io.NopCloser(strings.NewReader(tc.rStr))
|
||||
buf := make([]byte, tc.limit+1)
|
||||
|
||||
lreader := agdio.LimitReader(readCloser, tc.limit)
|
||||
n, err := lreader.Read(buf)
|
||||
require.Equal(t, tc.err, err)
|
||||
|
||||
assert.Equal(t, tc.want, n)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestLimitError_Error(t *testing.T) {
|
||||
err := &agdio.LimitError{
|
||||
Limit: 0,
|
||||
}
|
||||
|
||||
const want = "cannot read more than 0 bytes"
|
||||
assert.Equal(t, want, err.Error())
|
||||
}
|
@ -81,3 +81,16 @@ func ParseSubnets(strs ...string) (subnets []netip.Prefix, err error) {
|
||||
func NormalizeDomain(fqdn string) (host string) {
|
||||
return strings.ToLower(strings.TrimSuffix(fqdn, "."))
|
||||
}
|
||||
|
||||
// NormalizeQueryDomain returns a lowercased version of the host without the
|
||||
// final dot, unless the host is ".", in which case it returns the unchanged
|
||||
// host. That is the special case to allow matching queries like:
|
||||
//
|
||||
// dig IN NS '.'
|
||||
func NormalizeQueryDomain(host string) (norm string) {
|
||||
if host == "." {
|
||||
return host
|
||||
}
|
||||
|
||||
return NormalizeDomain(host)
|
||||
}
|
||||
|
9
internal/agdnet/agdnet_test.go
Normal file
9
internal/agdnet/agdnet_test.go
Normal file
@ -0,0 +1,9 @@
|
||||
package agdnet_test
|
||||
|
||||
import "net/netip"
|
||||
|
||||
// Common subnets for tests.
|
||||
var (
|
||||
testSubnetIPv4 = netip.MustParsePrefix("1.2.3.0/24")
|
||||
testSubnetIPv6 = netip.MustParsePrefix("1234:5678::/64")
|
||||
)
|
@ -2,18 +2,34 @@ package agdnet
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/netip"
|
||||
)
|
||||
|
||||
// FormatPrefixAddr returns either a simple IP:port address or one with the
|
||||
// prefix length appended after a slash, depending on whether or not subnet is a
|
||||
// single-address subnet. This is done to make using the IP:port part easier to
|
||||
// split off using functions like [strings.Cut].
|
||||
func FormatPrefixAddr(subnet netip.Prefix, port uint16) (s string) {
|
||||
addrPort := netip.AddrPortFrom(subnet.Addr(), port)
|
||||
if subnet.IsSingleIP() {
|
||||
// PrefixNetAddr is a wrapper around netip.Prefix that makes it a [net.Addr].
|
||||
type PrefixNetAddr struct {
|
||||
Prefix netip.Prefix
|
||||
Net string
|
||||
Port uint16
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ net.Addr = (*PrefixNetAddr)(nil)
|
||||
|
||||
// String implements the [net.Addr] interface for *PrefixNetAddr. It returns
|
||||
// either a simple IP:port address or one with the prefix length appended after
|
||||
// a slash, depending on whether or not subnet is a single-address subnet. This
|
||||
// is done to make using the IP:port part easier to split off using functions
|
||||
// like [strings.Cut].
|
||||
func (addr *PrefixNetAddr) String() (n string) {
|
||||
p := addr.Prefix
|
||||
addrPort := netip.AddrPortFrom(p.Addr(), addr.Port)
|
||||
if p.IsSingleIP() {
|
||||
return addrPort.String()
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s/%d", addrPort, subnet.Bits())
|
||||
return fmt.Sprintf("%s/%d", addrPort, p.Bits())
|
||||
}
|
||||
|
||||
// Network implements the [net.Addr] interface for *PrefixNetAddr.
|
||||
func (addr *PrefixNetAddr) Network() (n string) { return addr.Net }
|
||||
|
@ -7,9 +7,17 @@ import (
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agdnet"
|
||||
)
|
||||
|
||||
func ExampeFormatPrefixAddr() {
|
||||
fmt.Println(agdnet.FormatPrefixAddr(netip.MustParsePrefix("1.2.3.4/32"), 5678))
|
||||
fmt.Println(agdnet.FormatPrefixAddr(netip.MustParsePrefix("1.2.3.0/24"), 5678))
|
||||
func ExamplePrefixNetAddr_string() {
|
||||
fmt.Println(&agdnet.PrefixNetAddr{
|
||||
Prefix: netip.MustParsePrefix("1.2.3.4/32"),
|
||||
Net: "",
|
||||
Port: 5678,
|
||||
})
|
||||
fmt.Println(&agdnet.PrefixNetAddr{
|
||||
Prefix: netip.MustParsePrefix("1.2.3.0/24"),
|
||||
Net: "",
|
||||
Port: 5678,
|
||||
})
|
||||
|
||||
// Output:
|
||||
// 1.2.3.4:5678
|
||||
|
@ -1,12 +1,11 @@
|
||||
//go:build linux
|
||||
|
||||
package bindtodevice
|
||||
package agdnet_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/netip"
|
||||
"testing"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agdnet"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@ -19,14 +18,14 @@ func TestPrefixAddr(t *testing.T) {
|
||||
fullPrefix := netip.MustParsePrefix("1.2.3.4/32")
|
||||
|
||||
testCases := []struct {
|
||||
in *prefixNetAddr
|
||||
in *agdnet.PrefixNetAddr
|
||||
want string
|
||||
name string
|
||||
}{{
|
||||
in: &prefixNetAddr{
|
||||
prefix: testSubnetIPv4,
|
||||
network: network,
|
||||
port: port,
|
||||
in: &agdnet.PrefixNetAddr{
|
||||
Prefix: testSubnetIPv4,
|
||||
Net: network,
|
||||
Port: port,
|
||||
},
|
||||
want: fmt.Sprintf(
|
||||
"%s/%d",
|
||||
@ -34,10 +33,10 @@ func TestPrefixAddr(t *testing.T) {
|
||||
),
|
||||
name: "ipv4",
|
||||
}, {
|
||||
in: &prefixNetAddr{
|
||||
prefix: testSubnetIPv6,
|
||||
network: network,
|
||||
port: port,
|
||||
in: &agdnet.PrefixNetAddr{
|
||||
Prefix: testSubnetIPv6,
|
||||
Net: network,
|
||||
Port: port,
|
||||
},
|
||||
want: fmt.Sprintf(
|
||||
"%s/%d",
|
||||
@ -45,10 +44,10 @@ func TestPrefixAddr(t *testing.T) {
|
||||
),
|
||||
name: "ipv6",
|
||||
}, {
|
||||
in: &prefixNetAddr{
|
||||
prefix: fullPrefix,
|
||||
network: network,
|
||||
port: port,
|
||||
in: &agdnet.PrefixNetAddr{
|
||||
Prefix: fullPrefix,
|
||||
Net: network,
|
||||
Port: port,
|
||||
},
|
||||
want: netip.AddrPortFrom(fullPrefix.Addr(), port).String(),
|
||||
name: "ipv4_full",
|
@ -14,11 +14,11 @@ import (
|
||||
"github.com/AdguardTeam/golibs/netutil"
|
||||
)
|
||||
|
||||
// Resolvers
|
||||
|
||||
// Resolver is the DNS resolver interface.
|
||||
//
|
||||
// See go doc net.Resolver.
|
||||
// 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
|
||||
|
38
internal/agdservice/agdservice.go
Normal file
38
internal/agdservice/agdservice.go
Normal file
@ -0,0 +1,38 @@
|
||||
// Package agdservice defines types and interfaces for long-running services.
|
||||
//
|
||||
// TODO(a.garipov): Move to golibs.
|
||||
package agdservice
|
||||
|
||||
import "context"
|
||||
|
||||
// Interface is the interface for long-running services.
|
||||
//
|
||||
// TODO(a.garipov): Define whether or not a service should finish starting or
|
||||
// shutting down before returning from these methods.
|
||||
type Interface interface {
|
||||
// Start starts the service. ctx is used for cancelation.
|
||||
//
|
||||
// TODO(a.garipov): Use contexts with timeouts everywhere.
|
||||
Start(ctx context.Context) (err error)
|
||||
|
||||
// Shutdown gracefully stops the service. ctx is used to determine
|
||||
// a timeout before trying to stop the service less gracefully.
|
||||
//
|
||||
// TODO(a.garipov): Use contexts with timeouts everywhere.
|
||||
Shutdown(ctx context.Context) (err error)
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ Interface = Empty{}
|
||||
|
||||
// Empty is an [Interface] implementation that does nothing.
|
||||
type Empty struct{}
|
||||
|
||||
// Start implements the [Interface] interface for Empty.
|
||||
func (Empty) Start(_ context.Context) (err error) { return nil }
|
||||
|
||||
// Shutdown implements the [Interface] interface for Empty.
|
||||
func (Empty) Shutdown(_ context.Context) (err error) { return nil }
|
||||
|
||||
// unit is a convenient alias for struct{}.
|
||||
type unit = struct{}
|
@ -1,7 +1,8 @@
|
||||
package backend_test
|
||||
package agdservice_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/golibs/testutil"
|
||||
)
|
||||
@ -9,3 +10,6 @@ import (
|
||||
func TestMain(m *testing.M) {
|
||||
testutil.DiscardLogOutput(m)
|
||||
}
|
||||
|
||||
// testTimeout is the timeout for common test operations.
|
||||
const testTimeout = 1 * time.Second
|
@ -1,32 +1,32 @@
|
||||
package agd
|
||||
package agdservice
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"golang.org/x/exp/rand"
|
||||
)
|
||||
|
||||
// Refreshable Entities And Utilities
|
||||
|
||||
// Refresher is the interface for entities that can update themselves.
|
||||
type Refresher interface {
|
||||
Refresh(ctx context.Context) (err error)
|
||||
}
|
||||
|
||||
var _ Service = (*RefreshWorker)(nil)
|
||||
|
||||
// RefreshWorker is a Service that updates its refreshable entity every tick of
|
||||
// the provided ticker.
|
||||
// RefreshWorker is an [Interface] implementation that updates its [Refresher]
|
||||
// every tick of the provided ticker.
|
||||
type RefreshWorker struct {
|
||||
done chan unit
|
||||
context func() (ctx context.Context, cancel context.CancelFunc)
|
||||
logRoutine func(format string, args ...any)
|
||||
tick *time.Ticker
|
||||
rand *rand.Rand
|
||||
refr Refresher
|
||||
errColl ErrorCollector
|
||||
errColl errcoll.Interface
|
||||
name string
|
||||
maxStartSleep time.Duration
|
||||
|
||||
refrOnShutdown bool
|
||||
}
|
||||
@ -40,13 +40,22 @@ type RefreshWorkerConfig struct {
|
||||
Refresher Refresher
|
||||
|
||||
// ErrColl is used to collect errors during refreshes.
|
||||
ErrColl ErrorCollector
|
||||
//
|
||||
// TODO(a.garipov): Remove this and make all Refreshers handle their own
|
||||
// errors.
|
||||
ErrColl errcoll.Interface
|
||||
|
||||
// Name is the name of this worker. It is used for logging and error
|
||||
// collecting.
|
||||
//
|
||||
// TODO(a.garipov): Consider accepting a slog.Logger or removing this and
|
||||
// making all Refreshers handle their own logging.
|
||||
Name string
|
||||
|
||||
// Interval is the refresh interval. Must be greater than zero.
|
||||
//
|
||||
// TODO(a.garipov): Consider switching to an interface à la
|
||||
// github.com/robfig/cron/v3.Schedule.
|
||||
Interval time.Duration
|
||||
|
||||
// RefreshOnShutdown, if true, instructs the worker to call the Refresher's
|
||||
@ -59,6 +68,14 @@ type RefreshWorkerConfig struct {
|
||||
// than on the Info one. This is useful to prevent routine logs from
|
||||
// workers with a small interval from overflowing with messages.
|
||||
RoutineLogsAreDebug bool
|
||||
|
||||
// RandomizeStart, if true, instructs the worker to sleep before starting a
|
||||
// refresh. The duration of the sleep is a random duration of up to 10 % of
|
||||
// Interval.
|
||||
//
|
||||
// TODO(a.garipov): Switch to something like a cron schedule and see if this
|
||||
// is still necessary
|
||||
RandomizeStart bool
|
||||
}
|
||||
|
||||
// NewRefreshWorker returns a new valid *RefreshWorker with the provided
|
||||
@ -72,27 +89,39 @@ func NewRefreshWorker(c *RefreshWorkerConfig) (w *RefreshWorker) {
|
||||
logRoutine = log.Info
|
||||
}
|
||||
|
||||
var maxStartSleep time.Duration
|
||||
var rng *rand.Rand
|
||||
if c.RandomizeStart {
|
||||
maxStartSleep = c.Interval / 10
|
||||
rng = rand.New(rand.NewSource(uint64(time.Now().UnixNano())))
|
||||
}
|
||||
|
||||
return &RefreshWorker{
|
||||
done: make(chan unit),
|
||||
context: c.Context,
|
||||
logRoutine: logRoutine,
|
||||
tick: time.NewTicker(c.Interval),
|
||||
rand: rng,
|
||||
refr: c.Refresher,
|
||||
errColl: c.ErrColl,
|
||||
name: c.Name,
|
||||
maxStartSleep: maxStartSleep,
|
||||
refrOnShutdown: c.RefreshOnShutdown,
|
||||
}
|
||||
}
|
||||
|
||||
// Start implements the Service interface for *RefreshWorker. err is always
|
||||
// type check
|
||||
var _ Interface = (*RefreshWorker)(nil)
|
||||
|
||||
// Start implements the [Interface] interface for *RefreshWorker. err is always
|
||||
// nil.
|
||||
func (w *RefreshWorker) Start() (err error) {
|
||||
func (w *RefreshWorker) Start(_ context.Context) (err error) {
|
||||
go w.refreshInALoop()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Shutdown implements the Service interface for *RefreshWorker.
|
||||
// Shutdown implements the [Interface] interface for *RefreshWorker.
|
||||
func (w *RefreshWorker) Shutdown(ctx context.Context) (err error) {
|
||||
if w.refrOnShutdown {
|
||||
err = w.refr.Refresh(ctx)
|
||||
@ -105,9 +134,8 @@ func (w *RefreshWorker) Shutdown(ctx context.Context) (err error) {
|
||||
name := w.name
|
||||
if err != nil {
|
||||
err = fmt.Errorf("refresh on shutdown: %w", err)
|
||||
log.Error("%s: shut down with error: %s", name, err)
|
||||
} else {
|
||||
log.Info("%s: shut down successfully", name)
|
||||
log.Info("worker %q: shut down successfully", name)
|
||||
}
|
||||
|
||||
return err
|
||||
@ -119,24 +147,57 @@ func (w *RefreshWorker) refreshInALoop() {
|
||||
name := w.name
|
||||
defer log.OnPanic(name)
|
||||
|
||||
log.Info("%s: starting refresh loop", name)
|
||||
log.Info("worker %q: starting refresh loop", name)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-w.done:
|
||||
log.Info("%s: finished refresh loop", name)
|
||||
log.Info("worker %q: finished refresh loop", name)
|
||||
|
||||
return
|
||||
case <-w.tick.C:
|
||||
if w.sleepRandom() {
|
||||
w.refresh()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// sleepRandom sleeps for up to maxStartSleep unless it's zero. shouldRefresh
|
||||
// shows if a refresh should be performed once the sleep is finished.
|
||||
func (w *RefreshWorker) sleepRandom() (shouldRefresh bool) {
|
||||
if w.maxStartSleep == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
sleepDur := time.Duration(w.rand.Int63n(int64(w.maxStartSleep)))
|
||||
w.logRoutine("worker %q: sleeping for %s before refresh", w.name, sleepDur)
|
||||
|
||||
timer := time.NewTimer(sleepDur)
|
||||
defer func() {
|
||||
if !timer.Stop() {
|
||||
// We don't know if the timer's value has been consumed yet or not,
|
||||
// so use a select with default to make sure that this doesn't
|
||||
// block.
|
||||
select {
|
||||
case <-timer.C:
|
||||
default:
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-w.done:
|
||||
return false
|
||||
case <-timer.C:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// refresh refreshes the entity and logs the status of the refresh.
|
||||
func (w *RefreshWorker) refresh() {
|
||||
name := w.name
|
||||
w.logRoutine("%s: refreshing", name)
|
||||
w.logRoutine("worker %q: refreshing", name)
|
||||
|
||||
// TODO(a.garipov): Consider adding a helper for enriching errors with
|
||||
// context deadline data without duplication. See an example in method
|
||||
@ -144,15 +205,15 @@ func (w *RefreshWorker) refresh() {
|
||||
ctx, cancel := w.context()
|
||||
defer cancel()
|
||||
|
||||
log.Debug("%s: starting refresh", name)
|
||||
log.Debug("worker %q: starting refresh", name)
|
||||
err := w.refr.Refresh(ctx)
|
||||
log.Debug("%s: finished refresh", name)
|
||||
log.Debug("worker %q: finished refresh", name)
|
||||
|
||||
if err != nil {
|
||||
Collectf(ctx, w.errColl, "%s: %w", name, err)
|
||||
errcoll.Collectf(ctx, w.errColl, "%s: %w", name, err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
w.logRoutine("%s: refreshed successfully", name)
|
||||
w.logRoutine("worker %q: refreshed successfully", name)
|
||||
}
|
@ -1,11 +1,11 @@
|
||||
package agd_test
|
||||
package agdservice_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agdservice"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agdtest"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/testutil"
|
||||
@ -45,11 +45,11 @@ func newTestRefresher(t *testing.T, respErr error) (refr *agdtest.Refresher, syn
|
||||
// newRefrConf returns worker configuration.
|
||||
func newRefrConf(
|
||||
t *testing.T,
|
||||
refr agd.Refresher,
|
||||
refr agdservice.Refresher,
|
||||
ivl time.Duration,
|
||||
refrOnShutDown bool,
|
||||
errCh chan sig,
|
||||
) (conf *agd.RefreshWorkerConfig) {
|
||||
) (conf *agdservice.RefreshWorkerConfig) {
|
||||
t.Helper()
|
||||
|
||||
pt := testutil.PanicT{}
|
||||
@ -60,7 +60,7 @@ func newRefrConf(
|
||||
},
|
||||
}
|
||||
|
||||
return &agd.RefreshWorkerConfig{
|
||||
return &agdservice.RefreshWorkerConfig{
|
||||
Context: func() (ctx context.Context, cancel context.CancelFunc) {
|
||||
return context.WithTimeout(context.Background(), testTimeout)
|
||||
},
|
||||
@ -70,6 +70,7 @@ func newRefrConf(
|
||||
Interval: ivl,
|
||||
RefreshOnShutdown: refrOnShutDown,
|
||||
RoutineLogsAreDebug: false,
|
||||
RandomizeStart: false,
|
||||
}
|
||||
}
|
||||
|
||||
@ -78,18 +79,15 @@ func TestRefreshWorker(t *testing.T) {
|
||||
refr, syncCh := newTestRefresher(t, nil)
|
||||
errCh := make(chan sig, 1)
|
||||
|
||||
w := agd.NewRefreshWorker(newRefrConf(t, refr, testIvl, false, errCh))
|
||||
w := agdservice.NewRefreshWorker(newRefrConf(t, refr, testIvl, false, errCh))
|
||||
|
||||
err := w.Start()
|
||||
err := w.Start(agdtest.ContextWithTimeout(t, testTimeout))
|
||||
require.NoError(t, err)
|
||||
|
||||
testutil.RequireReceive(t, syncCh, testTimeout)
|
||||
require.Empty(t, errCh)
|
||||
|
||||
shutdown, cancel := context.WithTimeout(context.Background(), testTimeout)
|
||||
defer cancel()
|
||||
|
||||
err = w.Shutdown(shutdown)
|
||||
err = w.Shutdown(agdtest.ContextWithTimeout(t, testTimeout))
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
@ -97,15 +95,12 @@ func TestRefreshWorker(t *testing.T) {
|
||||
refr, syncCh := newTestRefresher(t, nil)
|
||||
errCh := make(chan sig, 1)
|
||||
|
||||
w := agd.NewRefreshWorker(newRefrConf(t, refr, testIvlLong, true, errCh))
|
||||
w := agdservice.NewRefreshWorker(newRefrConf(t, refr, testIvlLong, true, errCh))
|
||||
|
||||
err := w.Start()
|
||||
err := w.Start(agdtest.ContextWithTimeout(t, testTimeout))
|
||||
require.NoError(t, err)
|
||||
|
||||
shutdown, cancel := context.WithTimeout(context.Background(), testTimeout)
|
||||
defer cancel()
|
||||
|
||||
err = w.Shutdown(shutdown)
|
||||
err = w.Shutdown(agdtest.ContextWithTimeout(t, testTimeout))
|
||||
require.NoError(t, err)
|
||||
|
||||
testutil.RequireReceive(t, syncCh, testTimeout)
|
||||
@ -116,18 +111,15 @@ func TestRefreshWorker(t *testing.T) {
|
||||
errRefr, syncCh := newTestRefresher(t, testError)
|
||||
errCh := make(chan sig, 1)
|
||||
|
||||
w := agd.NewRefreshWorker(newRefrConf(t, errRefr, testIvl, false, errCh))
|
||||
w := agdservice.NewRefreshWorker(newRefrConf(t, errRefr, testIvl, false, errCh))
|
||||
|
||||
err := w.Start()
|
||||
err := w.Start(agdtest.ContextWithTimeout(t, testTimeout))
|
||||
require.NoError(t, err)
|
||||
|
||||
testutil.RequireReceive(t, syncCh, testTimeout)
|
||||
testutil.RequireReceive(t, errCh, testTimeout)
|
||||
|
||||
shutdown, cancel := context.WithTimeout(context.Background(), testTimeout)
|
||||
defer cancel()
|
||||
|
||||
err = w.Shutdown(shutdown)
|
||||
err = w.Shutdown(agdtest.ContextWithTimeout(t, testTimeout))
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
@ -135,15 +127,12 @@ func TestRefreshWorker(t *testing.T) {
|
||||
errRefr, syncCh := newTestRefresher(t, testError)
|
||||
errCh := make(chan sig, 1)
|
||||
|
||||
w := agd.NewRefreshWorker(newRefrConf(t, errRefr, testIvlLong, true, errCh))
|
||||
w := agdservice.NewRefreshWorker(newRefrConf(t, errRefr, testIvlLong, true, errCh))
|
||||
|
||||
err := w.Start()
|
||||
err := w.Start(agdtest.ContextWithTimeout(t, testTimeout))
|
||||
require.NoError(t, err)
|
||||
|
||||
shutdown, cancel := context.WithTimeout(context.Background(), testTimeout)
|
||||
defer cancel()
|
||||
|
||||
err = w.Shutdown(shutdown)
|
||||
err = w.Shutdown(agdtest.ContextWithTimeout(t, testTimeout))
|
||||
assert.ErrorIs(t, err, testError)
|
||||
|
||||
testutil.RequireReceive(t, syncCh, testTimeout)
|
@ -1,37 +0,0 @@
|
||||
// Package agdsync contains extensions and utilities for package sync from the
|
||||
// standard library.
|
||||
//
|
||||
// TODO(a.garipov): Move to module golibs.
|
||||
package agdsync
|
||||
|
||||
import "sync"
|
||||
|
||||
// TypedPool is the strongly typed version of [sync.Pool] that manages pointers
|
||||
// to T.
|
||||
type TypedPool[T any] struct {
|
||||
pool *sync.Pool
|
||||
}
|
||||
|
||||
// NewTypedPool returns a new strongly typed pool. newFunc must not be nil.
|
||||
func NewTypedPool[T any](newFunc func() (v *T)) (p *TypedPool[T]) {
|
||||
return &TypedPool[T]{
|
||||
pool: &sync.Pool{
|
||||
New: func() (v any) { return newFunc() },
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Get selects an arbitrary item from the pool, removes it from the pool, and
|
||||
// returns it to the caller.
|
||||
//
|
||||
// See [sync.Pool.Get].
|
||||
func (p *TypedPool[T]) Get() (v *T) {
|
||||
return p.pool.Get().(*T)
|
||||
}
|
||||
|
||||
// Put adds v to the pool.
|
||||
//
|
||||
// See [sync.Pool.Put].
|
||||
func (p *TypedPool[T]) Put(v *T) {
|
||||
p.pool.Put(v)
|
||||
}
|
@ -3,6 +3,8 @@
|
||||
package agdtest
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
|
||||
@ -18,5 +20,23 @@ const FilteredResponseTTLSec = 10
|
||||
|
||||
// NewConstructor returns a standard dnsmsg.Constructor for tests.
|
||||
func NewConstructor() (c *dnsmsg.Constructor) {
|
||||
return dnsmsg.NewConstructor(&dnsmsg.BlockingModeNullIP{}, FilteredResponseTTL)
|
||||
return dnsmsg.NewConstructor(nil, &dnsmsg.BlockingModeNullIP{}, FilteredResponseTTL)
|
||||
}
|
||||
|
||||
// NewCloner returns a standard dnsmsg.Cloner for tests.
|
||||
func NewCloner() (c *dnsmsg.Cloner) {
|
||||
return dnsmsg.NewCloner(dnsmsg.EmptyClonerStat{})
|
||||
}
|
||||
|
||||
// ContextWithTimeout is a helper that creates a new context with timeout and
|
||||
// registers ctx's cleanup with t.Cleanup.
|
||||
//
|
||||
// TODO(a.garipov): Move to golibs.
|
||||
func ContextWithTimeout(tb testing.TB, timeout time.Duration) (ctx context.Context) {
|
||||
tb.Helper()
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||
tb.Cleanup(cancel)
|
||||
|
||||
return ctx
|
||||
}
|
||||
|
@ -9,11 +9,13 @@ import (
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/access"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agdnet"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agdservice"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/billstat"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/dnscheck"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/dnsdb"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/netext"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/ratelimit"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/filter"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/geoip"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/profiledb"
|
||||
@ -29,34 +31,6 @@ import (
|
||||
|
||||
// Module AdGuardDNS
|
||||
|
||||
// type check
|
||||
var _ agd.ErrorCollector = (*ErrorCollector)(nil)
|
||||
|
||||
// ErrorCollector is an agd.ErrorCollector for tests.
|
||||
//
|
||||
// TODO(a.garipov): Actually test the error collection where this is used.
|
||||
type ErrorCollector struct {
|
||||
OnCollect func(ctx context.Context, err error)
|
||||
}
|
||||
|
||||
// Collect implements the agd.ErrorCollector interface for *ErrorCollector.
|
||||
func (c *ErrorCollector) Collect(ctx context.Context, err error) {
|
||||
c.OnCollect(ctx, err)
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ agd.Refresher = (*Refresher)(nil)
|
||||
|
||||
// Refresher is an agd.Refresher for tests.
|
||||
type Refresher struct {
|
||||
OnRefresh func(ctx context.Context) (err error)
|
||||
}
|
||||
|
||||
// Refresh implements the agd.Refresher interface for *Refresher.
|
||||
func (r *Refresher) Refresh(ctx context.Context) (err error) {
|
||||
return r.OnRefresh(ctx)
|
||||
}
|
||||
|
||||
// Package access
|
||||
|
||||
// type check
|
||||
@ -83,7 +57,7 @@ func (a *AccessManager) IsBlockedIP(ip netip.Addr) (blocked bool, rule string) {
|
||||
// type check
|
||||
var _ agdnet.Resolver = (*Resolver)(nil)
|
||||
|
||||
// Resolver is an agd.Resolver for tests.
|
||||
// Resolver is an [agdnet.Resolver] for tests.
|
||||
type Resolver struct {
|
||||
OnLookupNetIP func(
|
||||
ctx context.Context,
|
||||
@ -92,7 +66,7 @@ type Resolver struct {
|
||||
) (ips []netip.Addr, err error)
|
||||
}
|
||||
|
||||
// LookupNetIP implements the [agd.Resolver] interface for *Resolver.
|
||||
// LookupNetIP implements the [agdnet.Resolver] interface for *Resolver.
|
||||
func (r *Resolver) LookupNetIP(
|
||||
ctx context.Context,
|
||||
fam netutil.AddrFamily,
|
||||
@ -101,6 +75,21 @@ func (r *Resolver) LookupNetIP(
|
||||
return r.OnLookupNetIP(ctx, fam, host)
|
||||
}
|
||||
|
||||
// Package agdservice
|
||||
|
||||
// type check
|
||||
var _ agdservice.Refresher = (*Refresher)(nil)
|
||||
|
||||
// Refresher is an [agdservice.Refresher] for tests.
|
||||
type Refresher struct {
|
||||
OnRefresh func(ctx context.Context) (err error)
|
||||
}
|
||||
|
||||
// Refresh implements the [agdservice.Refresher] interface for *Refresher.
|
||||
func (r *Refresher) Refresh(ctx context.Context) (err error) {
|
||||
return r.OnRefresh(ctx)
|
||||
}
|
||||
|
||||
// Package billstat
|
||||
|
||||
// type check
|
||||
@ -111,8 +100,8 @@ type BillStatRecorder struct {
|
||||
OnRecord func(
|
||||
ctx context.Context,
|
||||
id agd.DeviceID,
|
||||
ctry agd.Country,
|
||||
asn agd.ASN,
|
||||
ctry geoip.Country,
|
||||
asn geoip.ASN,
|
||||
start time.Time,
|
||||
proto agd.Protocol,
|
||||
)
|
||||
@ -122,8 +111,8 @@ type BillStatRecorder struct {
|
||||
func (r *BillStatRecorder) Record(
|
||||
ctx context.Context,
|
||||
id agd.DeviceID,
|
||||
ctry agd.Country,
|
||||
asn agd.ASN,
|
||||
ctry geoip.Country,
|
||||
asn geoip.ASN,
|
||||
start time.Time,
|
||||
proto agd.Protocol,
|
||||
) {
|
||||
@ -177,6 +166,23 @@ func (db *DNSDB) Record(ctx context.Context, resp *dns.Msg, ri *agd.RequestInfo)
|
||||
db.OnRecord(ctx, resp, ri)
|
||||
}
|
||||
|
||||
// Package errcoll
|
||||
|
||||
// type check
|
||||
var _ errcoll.Interface = (*ErrorCollector)(nil)
|
||||
|
||||
// ErrorCollector is an [errcoll.Interface] for tests.
|
||||
//
|
||||
// TODO(a.garipov): Actually test the error collection where this is used.
|
||||
type ErrorCollector struct {
|
||||
OnCollect func(ctx context.Context, err error)
|
||||
}
|
||||
|
||||
// Collect implements the [errcoll.Interface] interface for *ErrorCollector.
|
||||
func (c *ErrorCollector) Collect(ctx context.Context, err error) {
|
||||
c.OnCollect(ctx, err)
|
||||
}
|
||||
|
||||
// Package filter
|
||||
|
||||
// type check
|
||||
@ -263,25 +269,18 @@ var _ geoip.Interface = (*GeoIP)(nil)
|
||||
|
||||
// GeoIP is a geoip.Interface for tests.
|
||||
type GeoIP struct {
|
||||
OnSubnetByLocation func(
|
||||
c agd.Country,
|
||||
a agd.ASN,
|
||||
fam netutil.AddrFamily,
|
||||
) (n netip.Prefix, err error)
|
||||
OnData func(host string, ip netip.Addr) (l *agd.Location, err error)
|
||||
OnSubnetByLocation func(l *geoip.Location, fam netutil.AddrFamily) (n netip.Prefix, err error)
|
||||
OnData func(host string, ip netip.Addr) (l *geoip.Location, err error)
|
||||
}
|
||||
|
||||
// SubnetByLocation implements the geoip.Interface interface for *GeoIP.
|
||||
func (g *GeoIP) SubnetByLocation(
|
||||
c agd.Country,
|
||||
a agd.ASN,
|
||||
fam netutil.AddrFamily,
|
||||
func (g *GeoIP) SubnetByLocation(l *geoip.Location, fam netutil.AddrFamily,
|
||||
) (n netip.Prefix, err error) {
|
||||
return g.OnSubnetByLocation(c, a, fam)
|
||||
return g.OnSubnetByLocation(l, fam)
|
||||
}
|
||||
|
||||
// Data implements the geoip.Interface interface for *GeoIP.
|
||||
func (g *GeoIP) Data(host string, ip netip.Addr) (l *agd.Location, err error) {
|
||||
func (g *GeoIP) Data(host string, ip netip.Addr) (l *geoip.Location, err error) {
|
||||
return g.OnData(host, ip)
|
||||
}
|
||||
|
||||
|
@ -1,11 +0,0 @@
|
||||
// Package backend contains implementations of several interfaces that send or
|
||||
// receive information to or from the business logic backend service.
|
||||
package backend
|
||||
|
||||
// Common Constants, Types, And Utilities
|
||||
|
||||
// Path constants.
|
||||
const (
|
||||
PathDNSAPIV1DevicesActivity = "/dns_api/v1/devices_activity"
|
||||
PathDNSAPIV1Settings = "/dns_api/v1/settings"
|
||||
)
|
@ -1,131 +0,0 @@
|
||||
package backend
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agdhttp"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/billstat"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/mapsutil"
|
||||
)
|
||||
|
||||
// Billing Statistics Uploader
|
||||
|
||||
// BillStatConfig is the configuration structure for the business logic backend
|
||||
// billing statistics uploader.
|
||||
type BillStatConfig struct {
|
||||
// BaseEndpoint is the base URL to which API paths are appended.
|
||||
BaseEndpoint *url.URL
|
||||
}
|
||||
|
||||
// NewBillStat creates a new billing statistics uploader. c must not be nil.
|
||||
func NewBillStat(c *BillStatConfig) (b *BillStat) {
|
||||
return &BillStat{
|
||||
apiURL: c.BaseEndpoint.JoinPath(PathDNSAPIV1DevicesActivity),
|
||||
// Assume that the timeouts are handled by the context in Upload.
|
||||
http: agdhttp.NewClient(&agdhttp.ClientConfig{}),
|
||||
}
|
||||
}
|
||||
|
||||
// BillStat is the implementation of the [billstat.Uploader] interface that
|
||||
// uploads the billing statistics to the business logic backend. It is safe for
|
||||
// concurrent use.
|
||||
//
|
||||
// TODO(a.garipov): Consider uniting with [ProfileStorage] into a single
|
||||
// backend.Client.
|
||||
type BillStat struct {
|
||||
apiURL *url.URL
|
||||
http *agdhttp.Client
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ billstat.Uploader = (*BillStat)(nil)
|
||||
|
||||
// Upload implements the [billstat.Uploader] interface for *BillStat.
|
||||
func (b *BillStat) Upload(ctx context.Context, records billstat.Records) (err error) {
|
||||
if len(records) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
req := &v1DevicesActivityReq{
|
||||
Devices: billStatRecsToReq(records),
|
||||
}
|
||||
data, err := json.Marshal(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("encoding billstat req: %w", err)
|
||||
}
|
||||
|
||||
reqURL := b.apiURL.Redacted()
|
||||
resp, err := b.http.Post(ctx, b.apiURL, agdhttp.HdrValApplicationJSON, bytes.NewReader(data))
|
||||
if err != nil {
|
||||
return fmt.Errorf("sending to %s: %w", reqURL, err)
|
||||
}
|
||||
defer func() { err = errors.WithDeferred(err, resp.Body.Close()) }()
|
||||
|
||||
err = agdhttp.CheckStatus(resp, http.StatusOK)
|
||||
if err != nil {
|
||||
// Don't wrap the error, because it's informative enough as is.
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// v1DevicesActivityReq is a request to the devices activity HTTP API.
|
||||
type v1DevicesActivityReq struct {
|
||||
Devices []*v1DevicesActivityReqDevice `json:"devices"`
|
||||
}
|
||||
|
||||
// v1DevicesActivityReqDevice is a single device within a request to the devices
|
||||
// activity HTTP API.
|
||||
type v1DevicesActivityReqDevice struct {
|
||||
// ClientCountry is the detected country of the client's IP address, if any.
|
||||
ClientCountry agd.Country `json:"client_country"`
|
||||
|
||||
// DeviceID is the ID of the device.
|
||||
DeviceID agd.DeviceID `json:"device_id"`
|
||||
|
||||
// Time is the time of the most recent query from the device, in Unix time
|
||||
// in milliseconds.
|
||||
Time int64 `json:"time_ms"`
|
||||
|
||||
// ASN is the detected ASN of the client's IP address, if any.
|
||||
ASN agd.ASN `json:"asn"`
|
||||
|
||||
// Queries is the total number of Queries the device has performed since the
|
||||
// most recent sync. This value is an int32 to be in sync with the business
|
||||
// logic backend which uses this type. Change it if it is changed there.
|
||||
Queries int32 `json:"queries"`
|
||||
|
||||
// Proto is the numeric value of the DNS protocol of the most recent query
|
||||
// from the device. It is a uint8 and not an agd.Protocol to make sure that
|
||||
// it always remains numeric even if we implement json.Marshal on
|
||||
// agd.Protocol in the future.
|
||||
Proto uint8 `json:"proto"`
|
||||
}
|
||||
|
||||
// billStatRecsToReq converts billing statistics records into devices for the
|
||||
// devices activity HTTP API.
|
||||
func billStatRecsToReq(records billstat.Records) (devices []*v1DevicesActivityReqDevice) {
|
||||
devices = make([]*v1DevicesActivityReqDevice, 0, len(records))
|
||||
mapsutil.OrderedRange(records, func(id agd.DeviceID, rec *billstat.Record) (cont bool) {
|
||||
devices = append(devices, &v1DevicesActivityReqDevice{
|
||||
ClientCountry: rec.Country,
|
||||
DeviceID: id,
|
||||
Time: rec.Time.UnixMilli(),
|
||||
ASN: rec.ASN,
|
||||
Queries: rec.Queries,
|
||||
Proto: uint8(rec.Proto),
|
||||
})
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
return devices
|
||||
}
|
@ -1,103 +0,0 @@
|
||||
package backend_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/backend"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/billstat"
|
||||
"github.com/AdguardTeam/golibs/testutil"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestBillStat_Upload(t *testing.T) {
|
||||
var reqURLStr string
|
||||
var body []byte
|
||||
h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
pt := testutil.PanicT{}
|
||||
|
||||
reqURLStr = r.URL.String()
|
||||
|
||||
var err error
|
||||
body, err = io.ReadAll(r.Body)
|
||||
require.NoError(pt, err)
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
})
|
||||
|
||||
// TODO(a.garipov): Don't listen on actual sockets and use piped conns
|
||||
// instead. Perhaps, add these to a new network test utility package in the
|
||||
// golibs module.
|
||||
srv := httptest.NewServer(h)
|
||||
t.Cleanup(srv.Close)
|
||||
|
||||
u, err := url.Parse(srv.URL)
|
||||
require.NoError(t, err)
|
||||
|
||||
c := &backend.BillStatConfig{
|
||||
BaseEndpoint: u,
|
||||
}
|
||||
|
||||
b := backend.NewBillStat(c)
|
||||
require.NotNil(t, b)
|
||||
|
||||
reqTime := time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||
records := billstat.Records{
|
||||
"dev1234": &billstat.Record{
|
||||
Time: reqTime,
|
||||
Country: agd.CountryAD,
|
||||
Queries: 123,
|
||||
ASN: 1230,
|
||||
Proto: agd.ProtoDoH,
|
||||
},
|
||||
"dev5678": &billstat.Record{
|
||||
Time: reqTime.Add(1 * time.Second),
|
||||
Country: agd.CountryAE,
|
||||
Queries: 42,
|
||||
ASN: 420,
|
||||
Proto: agd.ProtoDoQ,
|
||||
},
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
err = b.Upload(ctx, records)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Compare against a relative URL since the URL inside an HTTP handler
|
||||
// seems to always be relative.
|
||||
assert.Equal(t, backend.PathDNSAPIV1DevicesActivity, reqURLStr)
|
||||
|
||||
type jobj = map[string]any
|
||||
type jarr = []any
|
||||
|
||||
wantData := jobj{
|
||||
"devices": jarr{jobj{
|
||||
"client_country": "AD",
|
||||
"device_id": "dev1234",
|
||||
"time_ms": 1640995200000,
|
||||
"queries": 123,
|
||||
"asn": 1230,
|
||||
"proto": 3,
|
||||
}, jobj{
|
||||
"client_country": "AE",
|
||||
"device_id": "dev5678",
|
||||
"time_ms": 1640995201000,
|
||||
"queries": 42,
|
||||
"asn": 420,
|
||||
"proto": 4,
|
||||
}},
|
||||
}
|
||||
|
||||
wantBody, err := json.Marshal(wantData)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.JSONEq(t, string(wantBody), string(body))
|
||||
}
|
@ -1,561 +0,0 @@
|
||||
package backend
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/netip"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agdhttp"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agdtime"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/profiledb"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/netutil"
|
||||
"github.com/AdguardTeam/golibs/timeutil"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
// Profile Storage
|
||||
|
||||
// ProfileStorageConfig is the configuration for the business logic backend
|
||||
// profile storage.
|
||||
type ProfileStorageConfig struct {
|
||||
// BaseEntpoint is the base URL to which API paths are appended.
|
||||
BaseEndpoint *url.URL
|
||||
|
||||
// Now is returns the current time, typically time.Now. It is used to set
|
||||
// UpdateTime on profiles.
|
||||
Now func() (t time.Time)
|
||||
|
||||
// ErrColl is the error collector that is used to collect critical and
|
||||
// non-critical errors.
|
||||
ErrColl agd.ErrorCollector
|
||||
}
|
||||
|
||||
// NewProfileStorage returns a new [ProfileStorage] that retrieves information
|
||||
// from the business logic backend.
|
||||
func NewProfileStorage(c *ProfileStorageConfig) (s *ProfileStorage) {
|
||||
return &ProfileStorage{
|
||||
apiURL: c.BaseEndpoint.JoinPath(PathDNSAPIV1Settings),
|
||||
// Assume that the timeouts are handled by the context in Profiles.
|
||||
http: agdhttp.NewClient(&agdhttp.ClientConfig{}),
|
||||
now: c.Now,
|
||||
errColl: c.ErrColl,
|
||||
}
|
||||
}
|
||||
|
||||
// ProfileStorage is the implementation of the [profiledb.Storage] interface
|
||||
// that retrieves the profile and device information from the business logic
|
||||
// backend. It is safe for concurrent use.
|
||||
//
|
||||
// TODO(a.garipov): Consider uniting with [BillStat] into a single
|
||||
// backend.Client.
|
||||
type ProfileStorage struct {
|
||||
apiURL *url.URL
|
||||
http *agdhttp.Client
|
||||
now func() (t time.Time)
|
||||
errColl agd.ErrorCollector
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ profiledb.Storage = (*ProfileStorage)(nil)
|
||||
|
||||
// Profiles implements the [profiledb.Storage] interface for *ProfileStorage.
|
||||
func (s *ProfileStorage) Profiles(
|
||||
ctx context.Context,
|
||||
req *profiledb.StorageRequest,
|
||||
) (resp *profiledb.StorageResponse, err error) {
|
||||
q := url.Values{}
|
||||
if !req.SyncTime.IsZero() {
|
||||
syncTimeStr := strconv.FormatInt(req.SyncTime.UnixMilli(), 10)
|
||||
q.Add("sync_time", syncTimeStr)
|
||||
}
|
||||
|
||||
u := netutil.CloneURL(s.apiURL)
|
||||
u.RawQuery = q.Encode()
|
||||
redURL := u.Redacted()
|
||||
|
||||
settResp, err := s.loadSettingsResponse(ctx, u)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("loading from url %s: %w", redURL, err)
|
||||
}
|
||||
|
||||
return settResp.toInternal(ctx, s.now(), s.errColl), nil
|
||||
}
|
||||
|
||||
// loadSettingsResponse fetches, decodes, and returns the settings response.
|
||||
func (s *ProfileStorage) loadSettingsResponse(
|
||||
ctx context.Context,
|
||||
u *url.URL,
|
||||
) (resp *v1SettingsResp, err error) {
|
||||
httpResp, err := s.http.Get(ctx, u)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("calling backend: %w", err)
|
||||
}
|
||||
defer func() { err = errors.WithDeferred(err, httpResp.Body.Close()) }()
|
||||
|
||||
err = agdhttp.CheckStatus(httpResp, http.StatusOK)
|
||||
if err != nil {
|
||||
// Don't wrap the error, because it's informative enough as is.
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp = &v1SettingsResp{}
|
||||
err = json.NewDecoder(httpResp.Body).Decode(resp)
|
||||
if err != nil {
|
||||
return nil, agdhttp.WrapServerError(
|
||||
fmt.Errorf("decoding response: %w", err),
|
||||
httpResp,
|
||||
)
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// v1SettingsRespSchedule is the structure for decoding the
|
||||
// settings.*.parental.schedule property of the response from the backend.
|
||||
type v1SettingsRespSchedule struct {
|
||||
// TODO(a.garipov): Consider making a custom type detecting an absence of
|
||||
// value to remove these pointers.
|
||||
Monday *[2]timeutil.Duration `json:"mon"`
|
||||
Tuesday *[2]timeutil.Duration `json:"tue"`
|
||||
Wednesday *[2]timeutil.Duration `json:"wed"`
|
||||
Thursday *[2]timeutil.Duration `json:"thu"`
|
||||
Friday *[2]timeutil.Duration `json:"fri"`
|
||||
Saturday *[2]timeutil.Duration `json:"sat"`
|
||||
Sunday *[2]timeutil.Duration `json:"sun"`
|
||||
|
||||
// TimeZone is the tzdata name of the time zone.
|
||||
//
|
||||
// NOTE: Do not use *agdtime.Location here so that lookup failures are
|
||||
// properly mitigated in [v1SettingsRespParental.toInternal].
|
||||
TimeZone string `json:"tmz"`
|
||||
}
|
||||
|
||||
// v1SettingsRespParental is the structure for decoding the settings.*.parental
|
||||
// property of the response from the backend.
|
||||
type v1SettingsRespParental struct {
|
||||
Schedule *v1SettingsRespSchedule `json:"schedule"`
|
||||
|
||||
BlockedServices []string `json:"blocked_services"`
|
||||
|
||||
Enabled bool `json:"enabled"`
|
||||
BlockAdult bool `json:"block_adult"`
|
||||
GeneralSafeSearch bool `json:"general_safe_search"`
|
||||
YoutubeSafeSearch bool `json:"youtube_safe_search"`
|
||||
}
|
||||
|
||||
// v1SettingsRespDevice is the structure for decoding the settings.devices
|
||||
// property of the response from the backend.
|
||||
type v1SettingsRespDevice struct {
|
||||
LinkedIP netip.Addr `json:"linked_ip"`
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
DedicatedIPs []netip.Addr `json:"dedicated_ips"`
|
||||
FilteringEnabled bool `json:"filtering_enabled"`
|
||||
}
|
||||
|
||||
// v1SettingsRespSettings is the structure for decoding the settings property of
|
||||
// the response from the backend.
|
||||
type v1SettingsRespSettings struct {
|
||||
DNSID string `json:"dns_id"`
|
||||
Parental *v1SettingsRespParental `json:"parental"`
|
||||
RuleLists *v1SettingsRespRuleLists `json:"rule_lists"`
|
||||
SafeBrowsing *v1SettingsRespSafeBrowsing `json:"safe_browsing"`
|
||||
BlockingMode dnsmsg.BlockingModeCodec `json:"blocking_mode"`
|
||||
Devices []*v1SettingsRespDevice `json:"devices"`
|
||||
CustomRules []string `json:"custom_rules"`
|
||||
FilteredResponseTTL uint32 `json:"filtered_response_ttl"`
|
||||
QueryLogEnabled bool `json:"query_log_enabled"`
|
||||
FilteringEnabled bool `json:"filtering_enabled"`
|
||||
Deleted bool `json:"deleted"`
|
||||
BlockPrivateRelay bool `json:"block_private_relay"`
|
||||
BlockFirefoxCanary bool `json:"block_firefox_canary"`
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ json.Unmarshaler = (*v1SettingsRespSettings)(nil)
|
||||
|
||||
// UnmarshalJSON implements the [json.Unmarshaler] interface for
|
||||
// *v1SettingsRespSettings. It puts default value into BlockFirefoxCanary
|
||||
// field while it is not implemented on the backend side.
|
||||
//
|
||||
// TODO(a.garipov): Remove once the backend starts to always send it.
|
||||
func (rs *v1SettingsRespSettings) UnmarshalJSON(b []byte) (err error) {
|
||||
type defaultDec v1SettingsRespSettings
|
||||
|
||||
s := defaultDec{
|
||||
BlockingMode: dnsmsg.BlockingModeCodec{
|
||||
Mode: &dnsmsg.BlockingModeNullIP{},
|
||||
},
|
||||
BlockFirefoxCanary: true,
|
||||
}
|
||||
|
||||
if err = json.Unmarshal(b, &s); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*rs = v1SettingsRespSettings(s)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// v1SettingsRespRuleLists is the structure for decoding filtering rule lists
|
||||
// based filtering settings from the backend.
|
||||
type v1SettingsRespRuleLists struct {
|
||||
IDs []string `json:"ids"`
|
||||
Enabled bool `json:"enabled"`
|
||||
}
|
||||
|
||||
// v1SettingsRespSafeBrowsing is the structure for decoding the general safe
|
||||
// browsing filtering settings from the backend.
|
||||
type v1SettingsRespSafeBrowsing struct {
|
||||
BlockDangerousDomains *bool `json:"block_dangerous_domains"`
|
||||
BlockNewlyRegisteredDomains bool `json:"block_nrd"`
|
||||
Enabled bool `json:"enabled"`
|
||||
}
|
||||
|
||||
// toInternal converts s to an [agd.SafeBrowsingSettings] instance.
|
||||
func (s *v1SettingsRespSafeBrowsing) toInternal() (res *agd.SafeBrowsingSettings) {
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO(d.kolyshev): Don't make this migration after AGDNS-1537.
|
||||
blockDangerDomains := s.Enabled
|
||||
if s.BlockDangerousDomains != nil {
|
||||
blockDangerDomains = *s.BlockDangerousDomains
|
||||
}
|
||||
|
||||
return &agd.SafeBrowsingSettings{
|
||||
Enabled: s.Enabled,
|
||||
BlockDangerousDomains: blockDangerDomains,
|
||||
BlockNewlyRegisteredDomains: s.BlockNewlyRegisteredDomains,
|
||||
}
|
||||
}
|
||||
|
||||
// v1SettingsResp is the structure for decoding the response from the backend.
|
||||
type v1SettingsResp struct {
|
||||
Settings []*v1SettingsRespSettings `json:"settings"`
|
||||
|
||||
SyncTime int64 `json:"sync_time"`
|
||||
}
|
||||
|
||||
// toInternal converts p to an [agd.ParentalProtectionSettings] instance.
|
||||
func (p *v1SettingsRespParental) toInternal(
|
||||
ctx context.Context,
|
||||
errColl agd.ErrorCollector,
|
||||
settIdx int,
|
||||
) (res *agd.ParentalProtectionSettings, err error) {
|
||||
if p == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var sch *agd.ParentalProtectionSchedule
|
||||
if psch := p.Schedule; psch != nil {
|
||||
sch = &agd.ParentalProtectionSchedule{}
|
||||
|
||||
// TODO(a.garipov): Cache location lookup results.
|
||||
sch.TimeZone, err = agdtime.LoadLocation(psch.TimeZone)
|
||||
if err != nil {
|
||||
// Report the error and assume UTC.
|
||||
reportf(ctx, errColl, "settings at index %d: schedule: time zone: %w", settIdx, err)
|
||||
|
||||
sch.TimeZone = agdtime.UTC()
|
||||
}
|
||||
|
||||
sch.Week = &agd.WeeklySchedule{}
|
||||
days := []*[2]timeutil.Duration{
|
||||
psch.Sunday,
|
||||
psch.Monday,
|
||||
psch.Tuesday,
|
||||
psch.Wednesday,
|
||||
psch.Thursday,
|
||||
psch.Friday,
|
||||
psch.Saturday,
|
||||
}
|
||||
for i, d := range days {
|
||||
if d == nil {
|
||||
sch.Week[i] = agd.ZeroLengthDayRange()
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
sch.Week[i] = agd.DayRange{
|
||||
Start: uint16(d[0].Minutes()),
|
||||
End: uint16(d[1].Minutes()),
|
||||
}
|
||||
}
|
||||
|
||||
for i, r := range sch.Week {
|
||||
err = r.Validate()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("weekday %s: %w", time.Weekday(i), err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
blockedSvcs := blockedSvcsToInternal(ctx, errColl, settIdx, p.BlockedServices)
|
||||
|
||||
return &agd.ParentalProtectionSettings{
|
||||
Schedule: sch,
|
||||
|
||||
BlockedServices: blockedSvcs,
|
||||
|
||||
Enabled: p.Enabled,
|
||||
BlockAdult: p.BlockAdult,
|
||||
GeneralSafeSearch: p.GeneralSafeSearch,
|
||||
YoutubeSafeSearch: p.YoutubeSafeSearch,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// blockedSvcsToInternal is a helper that converts the blocked service IDs from
|
||||
// the backend response to AdGuard DNS blocked service IDs.
|
||||
func blockedSvcsToInternal(
|
||||
ctx context.Context,
|
||||
errColl agd.ErrorCollector,
|
||||
settIdx int,
|
||||
respSvcs []string,
|
||||
) (svcs []agd.BlockedServiceID) {
|
||||
l := len(respSvcs)
|
||||
if l == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
svcs = make([]agd.BlockedServiceID, 0, l)
|
||||
for i, s := range respSvcs {
|
||||
id, err := agd.NewBlockedServiceID(s)
|
||||
if err != nil {
|
||||
reportf(
|
||||
ctx,
|
||||
errColl,
|
||||
"settings at index %d: blocked service at index %d: %w",
|
||||
settIdx,
|
||||
i,
|
||||
err,
|
||||
)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
svcs = append(svcs, id)
|
||||
}
|
||||
|
||||
return svcs
|
||||
}
|
||||
|
||||
// settsRespPrefix is the logging prefix for logs by v1SettingsResp.
|
||||
const settsRespPrefix = "backend.v1SettingsResp"
|
||||
|
||||
// devicesToInternal is a helper that converts the devices from the backend
|
||||
// response to AdGuard DNS devices.
|
||||
func devicesToInternal(
|
||||
ctx context.Context,
|
||||
errColl agd.ErrorCollector,
|
||||
settIdx int,
|
||||
respDevices []*v1SettingsRespDevice,
|
||||
) (devices []*agd.Device, ids []agd.DeviceID) {
|
||||
l := len(respDevices)
|
||||
if l == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
devices = make([]*agd.Device, 0, l)
|
||||
for i, d := range respDevices {
|
||||
if d == nil {
|
||||
reportf(ctx, errColl, "settings at index %d: device at index %d: is nil", settIdx, i)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
// TODO(a.garipov): Consider validating uniqueness of linked and
|
||||
// dedicated IPs.
|
||||
dev := &agd.Device{
|
||||
LinkedIP: d.LinkedIP,
|
||||
DedicatedIPs: slices.Clone(d.DedicatedIPs),
|
||||
FilteringEnabled: d.FilteringEnabled,
|
||||
}
|
||||
|
||||
// Use the same error message format string from now on, since all
|
||||
// constructors and validators return informative errors.
|
||||
const msgFmt = "settings at index %d: device at index %d: %w"
|
||||
|
||||
var err error
|
||||
dev.ID, err = agd.NewDeviceID(d.ID)
|
||||
if err != nil {
|
||||
reportf(ctx, errColl, msgFmt, settIdx, i, err)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
dev.Name, err = agd.NewDeviceName(d.Name)
|
||||
if err != nil {
|
||||
reportf(ctx, errColl, msgFmt, settIdx, i, err)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
ids = append(ids, dev.ID)
|
||||
devices = append(devices, dev)
|
||||
}
|
||||
|
||||
return devices, ids
|
||||
}
|
||||
|
||||
// filterListsToInternal is a helper that converts the filter lists from the
|
||||
// backend response to AdGuard DNS devices.
|
||||
func filterListsToInternal(
|
||||
ctx context.Context,
|
||||
errColl agd.ErrorCollector,
|
||||
settIdx int,
|
||||
respFilters *v1SettingsRespRuleLists,
|
||||
) (enabled bool, filterLists []agd.FilterListID) {
|
||||
if respFilters == nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
lists := respFilters.IDs
|
||||
l := len(lists)
|
||||
if l == 0 {
|
||||
return respFilters.Enabled, nil
|
||||
}
|
||||
|
||||
filterLists = make([]agd.FilterListID, 0, l)
|
||||
for i, f := range lists {
|
||||
id, err := agd.NewFilterListID(f)
|
||||
if err != nil {
|
||||
reportf(ctx, errColl, "settings at index %d: filter at index %d: %w", settIdx, i, err)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
filterLists = append(filterLists, id)
|
||||
}
|
||||
|
||||
return respFilters.Enabled, filterLists
|
||||
}
|
||||
|
||||
// rulesToInternal is a helper that converts the filter rules from the backend
|
||||
// response to AdGuard DNS filtering rules.
|
||||
func rulesToInternal(
|
||||
ctx context.Context,
|
||||
errColl agd.ErrorCollector,
|
||||
settIdx int,
|
||||
respRules []string,
|
||||
) (rules []agd.FilterRuleText) {
|
||||
l := len(respRules)
|
||||
if l == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
rules = make([]agd.FilterRuleText, 0, l)
|
||||
for i, r := range respRules {
|
||||
text, err := agd.NewFilterRuleText(r)
|
||||
if err != nil {
|
||||
reportf(ctx, errColl, "settings at index %d: rule at index %d: %w", settIdx, i, err)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
rules = append(rules, text)
|
||||
}
|
||||
|
||||
return rules
|
||||
}
|
||||
|
||||
// maxFltRespTTL is the maximum allowed filtered response TTL.
|
||||
const maxFltRespTTL = 1 * time.Hour
|
||||
|
||||
// fltRespTTLToInternal converts respTTL to the filtered response TTL. If
|
||||
// respTTL is invalid, it returns an error describing the validation error as
|
||||
// well as the closest valid value to use.
|
||||
func fltRespTTLToInternal(respTTL uint32) (ttl time.Duration, err error) {
|
||||
ttl = time.Duration(respTTL) * time.Second
|
||||
if ttl > maxFltRespTTL {
|
||||
ttl = maxFltRespTTL
|
||||
err = fmt.Errorf("too high: got %s, max %s", ttl, maxFltRespTTL)
|
||||
}
|
||||
|
||||
return ttl, err
|
||||
}
|
||||
|
||||
// toInternal converts r to an [agd.DSProfilesResponse] instance.
|
||||
func (r *v1SettingsResp) toInternal(
|
||||
ctx context.Context,
|
||||
updTime time.Time,
|
||||
// TODO(a.garipov): Here and in other functions, consider just adding the
|
||||
// error collector to the context.
|
||||
errColl agd.ErrorCollector,
|
||||
) (pr *profiledb.StorageResponse) {
|
||||
if r == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
pr = &profiledb.StorageResponse{
|
||||
SyncTime: time.Unix(0, r.SyncTime*1_000_000),
|
||||
Profiles: make([]*agd.Profile, 0, len(r.Settings)),
|
||||
}
|
||||
|
||||
for i, s := range r.Settings {
|
||||
parental, err := s.Parental.toInternal(ctx, errColl, i)
|
||||
if err != nil {
|
||||
reportf(ctx, errColl, "settings at index %d: parental: %w", i, err)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
devices, deviceIDs := devicesToInternal(ctx, errColl, i, s.Devices)
|
||||
rlEnabled, ruleLists := filterListsToInternal(ctx, errColl, i, s.RuleLists)
|
||||
rules := rulesToInternal(ctx, errColl, i, s.CustomRules)
|
||||
|
||||
id, err := agd.NewProfileID(s.DNSID)
|
||||
if err != nil {
|
||||
reportf(ctx, errColl, "settings at index %d: profile id: %w", i, err)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
fltRespTTL, err := fltRespTTLToInternal(s.FilteredResponseTTL)
|
||||
if err != nil {
|
||||
reportf(ctx, errColl, "settings at index %d: filtered resp ttl: %w", i, err)
|
||||
|
||||
// Go on and use the fixed value.
|
||||
//
|
||||
// TODO(ameshkov, a.garipov): Consider continuing, like with all
|
||||
// other validation errors.
|
||||
}
|
||||
|
||||
pr.Devices = append(pr.Devices, devices...)
|
||||
|
||||
pr.Profiles = append(pr.Profiles, &agd.Profile{
|
||||
Parental: parental,
|
||||
BlockingMode: s.BlockingMode,
|
||||
ID: id,
|
||||
UpdateTime: updTime,
|
||||
DeviceIDs: deviceIDs,
|
||||
RuleListIDs: ruleLists,
|
||||
CustomRules: rules,
|
||||
FilteredResponseTTL: fltRespTTL,
|
||||
SafeBrowsing: s.SafeBrowsing.toInternal(),
|
||||
RuleListsEnabled: rlEnabled,
|
||||
FilteringEnabled: s.FilteringEnabled,
|
||||
QueryLogEnabled: s.QueryLogEnabled,
|
||||
Deleted: s.Deleted,
|
||||
BlockPrivateRelay: s.BlockPrivateRelay,
|
||||
BlockFirefoxCanary: s.BlockFirefoxCanary,
|
||||
})
|
||||
}
|
||||
|
||||
return pr
|
||||
}
|
||||
|
||||
// reportf is a helper method for reporting non-critical errors.
|
||||
func reportf(ctx context.Context, errColl agd.ErrorCollector, format string, args ...any) {
|
||||
agd.Collectf(ctx, errColl, settsRespPrefix+": "+format, args...)
|
||||
}
|
@ -1,209 +0,0 @@
|
||||
package backend_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/netip"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agdtime"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/backend"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/profiledb"
|
||||
"github.com/AdguardTeam/golibs/testutil"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// Common test constants.
|
||||
var (
|
||||
syncTime = time.Unix(0, 1_624_443_079_309_000_000)
|
||||
updTime = time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||
)
|
||||
|
||||
func TestProfileStorage_Profiles(t *testing.T) {
|
||||
reqURLStr := ""
|
||||
h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
pt := testutil.PanicT{}
|
||||
|
||||
// NOTE: Keep testdata/profiles.json formatted using jq. Example of a
|
||||
// formatting command using sponge(1) from the current directory:
|
||||
//
|
||||
// jq '.' ./testdata/profiles.json | sponge ./testdata/profiles.json
|
||||
reqURLStr = r.URL.String()
|
||||
b, err := os.ReadFile(filepath.Join("testdata", "profiles.json"))
|
||||
require.NoError(pt, err)
|
||||
|
||||
_, err = w.Write(b)
|
||||
require.NoError(pt, err)
|
||||
})
|
||||
|
||||
// TODO(a.garipov): Don't listen on actual sockets and use piped conns
|
||||
// instead. Perhaps, add these to a new network test utility package in the
|
||||
// golibs module.
|
||||
srv := httptest.NewServer(h)
|
||||
t.Cleanup(srv.Close)
|
||||
|
||||
u, err := url.Parse(srv.URL)
|
||||
require.NoError(t, err)
|
||||
|
||||
c := &backend.ProfileStorageConfig{
|
||||
BaseEndpoint: u,
|
||||
Now: func() (t time.Time) { return updTime },
|
||||
}
|
||||
|
||||
ds := backend.NewProfileStorage(c)
|
||||
require.NotNil(t, ds)
|
||||
|
||||
ctx := context.Background()
|
||||
req := &profiledb.StorageRequest{
|
||||
SyncTime: syncTime,
|
||||
}
|
||||
|
||||
resp, err := ds.Profiles(ctx, req)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, resp)
|
||||
|
||||
// Compare against a relative URL since the URL inside an HTTP handler
|
||||
// seems to always be relative.
|
||||
wantURLStr := fmt.Sprintf("%s?sync_time=%d", backend.PathDNSAPIV1Settings, syncTime.UnixMilli())
|
||||
assert.Equal(t, wantURLStr, reqURLStr)
|
||||
|
||||
want := testProfileResp(t)
|
||||
assert.Equal(t, want, resp)
|
||||
}
|
||||
|
||||
// testProfileResp returns profile resp corresponding with testdata.
|
||||
//
|
||||
// Keep in sync with the testdata one.
|
||||
func testProfileResp(t *testing.T) (resp *profiledb.StorageResponse) {
|
||||
t.Helper()
|
||||
|
||||
wantLoc, err := agdtime.LoadLocation("GMT")
|
||||
require.NoError(t, err)
|
||||
|
||||
dayRange := agd.DayRange{
|
||||
Start: 0,
|
||||
End: 59,
|
||||
}
|
||||
|
||||
wantParental := &agd.ParentalProtectionSettings{
|
||||
Schedule: &agd.ParentalProtectionSchedule{
|
||||
Week: &agd.WeeklySchedule{
|
||||
agd.ZeroLengthDayRange(),
|
||||
dayRange,
|
||||
dayRange,
|
||||
dayRange,
|
||||
dayRange,
|
||||
dayRange,
|
||||
agd.ZeroLengthDayRange(),
|
||||
},
|
||||
TimeZone: wantLoc,
|
||||
},
|
||||
BlockedServices: []agd.BlockedServiceID{"youtube"},
|
||||
Enabled: false,
|
||||
BlockAdult: false,
|
||||
GeneralSafeSearch: false,
|
||||
YoutubeSafeSearch: false,
|
||||
}
|
||||
|
||||
wantSafeBrowsing := &agd.SafeBrowsingSettings{
|
||||
Enabled: true,
|
||||
BlockDangerousDomains: true,
|
||||
BlockNewlyRegisteredDomains: false,
|
||||
}
|
||||
|
||||
wantLinkedIP := netip.AddrFrom4([4]byte{1, 2, 3, 4})
|
||||
|
||||
wantBlockingMode := dnsmsg.BlockingModeCodec{
|
||||
Mode: &dnsmsg.BlockingModeCustomIP{
|
||||
IPv4: netip.MustParseAddr("1.2.3.4"),
|
||||
IPv6: netip.MustParseAddr("1234::cdef"),
|
||||
},
|
||||
}
|
||||
|
||||
want := &profiledb.StorageResponse{
|
||||
SyncTime: syncTime,
|
||||
Profiles: []*agd.Profile{{
|
||||
Parental: nil,
|
||||
BlockingMode: dnsmsg.BlockingModeCodec{
|
||||
Mode: &dnsmsg.BlockingModeNullIP{},
|
||||
},
|
||||
ID: "37f97ee9",
|
||||
UpdateTime: updTime,
|
||||
DeviceIDs: []agd.DeviceID{
|
||||
"118ffe93",
|
||||
"b9e1a762",
|
||||
},
|
||||
RuleListIDs: []agd.FilterListID{"1"},
|
||||
CustomRules: nil,
|
||||
FilteredResponseTTL: 10 * time.Second,
|
||||
SafeBrowsing: wantSafeBrowsing,
|
||||
RuleListsEnabled: true,
|
||||
FilteringEnabled: true,
|
||||
QueryLogEnabled: true,
|
||||
Deleted: false,
|
||||
BlockPrivateRelay: true,
|
||||
BlockFirefoxCanary: true,
|
||||
}, {
|
||||
Parental: wantParental,
|
||||
BlockingMode: wantBlockingMode,
|
||||
ID: "83f3ea8f",
|
||||
UpdateTime: updTime,
|
||||
DeviceIDs: []agd.DeviceID{
|
||||
"0d7724fa",
|
||||
"6d2ac775",
|
||||
"94d4c481",
|
||||
"ada436e3",
|
||||
},
|
||||
RuleListIDs: []agd.FilterListID{"1"},
|
||||
CustomRules: []agd.FilterRuleText{"||example.org^"},
|
||||
FilteredResponseTTL: 3600 * time.Second,
|
||||
SafeBrowsing: wantSafeBrowsing,
|
||||
RuleListsEnabled: true,
|
||||
FilteringEnabled: true,
|
||||
QueryLogEnabled: true,
|
||||
Deleted: true,
|
||||
BlockPrivateRelay: false,
|
||||
BlockFirefoxCanary: false,
|
||||
}},
|
||||
Devices: []*agd.Device{{
|
||||
ID: "118ffe93",
|
||||
Name: "Device 1",
|
||||
FilteringEnabled: true,
|
||||
}, {
|
||||
ID: "b9e1a762",
|
||||
Name: "Device 2",
|
||||
FilteringEnabled: true,
|
||||
}, {
|
||||
ID: "0d7724fa",
|
||||
Name: "Device 1",
|
||||
FilteringEnabled: true,
|
||||
}, {
|
||||
ID: "6d2ac775",
|
||||
Name: "Device 2",
|
||||
FilteringEnabled: true,
|
||||
}, {
|
||||
ID: "94d4c481",
|
||||
Name: "Device 3",
|
||||
DedicatedIPs: []netip.Addr{
|
||||
netip.MustParseAddr("1.2.3.4"),
|
||||
},
|
||||
FilteringEnabled: true,
|
||||
}, {
|
||||
ID: "ada436e3",
|
||||
LinkedIP: wantLinkedIP,
|
||||
Name: "Device 4",
|
||||
FilteringEnabled: true,
|
||||
}},
|
||||
}
|
||||
|
||||
return want
|
||||
}
|
121
internal/backend/testdata/profiles.json
vendored
121
internal/backend/testdata/profiles.json
vendored
@ -1,121 +0,0 @@
|
||||
{
|
||||
"sync_time": 1624443079309,
|
||||
"settings": [
|
||||
{
|
||||
"dns_id": "37f97ee9",
|
||||
"filtering_enabled": true,
|
||||
"query_log_enabled": true,
|
||||
"safe_browsing": {
|
||||
"enabled": true
|
||||
},
|
||||
"deleted": false,
|
||||
"block_private_relay": true,
|
||||
"devices": [
|
||||
{
|
||||
"id": "118ffe93",
|
||||
"name": "Device 1",
|
||||
"filtering_enabled": true
|
||||
},
|
||||
{
|
||||
"id": "b9e1a762",
|
||||
"name": "Device 2",
|
||||
"filtering_enabled": true
|
||||
}
|
||||
],
|
||||
"rule_lists": {
|
||||
"enabled": true,
|
||||
"ids": [
|
||||
"1"
|
||||
]
|
||||
},
|
||||
"filtered_response_ttl": 10,
|
||||
"custom_rules": []
|
||||
},
|
||||
{
|
||||
"dns_id": "83f3ea8f",
|
||||
"filtering_enabled": true,
|
||||
"query_log_enabled": true,
|
||||
"safe_browsing": {
|
||||
"enabled": true
|
||||
},
|
||||
"deleted": true,
|
||||
"block_private_relay": false,
|
||||
"block_firefox_canary": false,
|
||||
"devices": [
|
||||
{
|
||||
"id": "0d7724fa",
|
||||
"name": "Device 1",
|
||||
"filtering_enabled": true
|
||||
},
|
||||
{
|
||||
"id": "6d2ac775",
|
||||
"name": "Device 2",
|
||||
"linked_ip": null,
|
||||
"filtering_enabled": true
|
||||
},
|
||||
{
|
||||
"id": "94d4c481",
|
||||
"name": "Device 3",
|
||||
"linked_ip": "",
|
||||
"dedicated_ips": [
|
||||
"1.2.3.4"
|
||||
],
|
||||
"filtering_enabled": true
|
||||
},
|
||||
{
|
||||
"id": "ada436e3",
|
||||
"name": "Device 4",
|
||||
"linked_ip": "1.2.3.4",
|
||||
"filtering_enabled": true
|
||||
}
|
||||
],
|
||||
"parental": {
|
||||
"enabled": false,
|
||||
"block_adult": false,
|
||||
"general_safe_search": false,
|
||||
"youtube_safe_search": false,
|
||||
"blocked_services": [
|
||||
"youtube"
|
||||
],
|
||||
"schedule": {
|
||||
"tmz": "GMT",
|
||||
"mon": [
|
||||
"0s",
|
||||
"59m"
|
||||
],
|
||||
"tue": [
|
||||
"0s",
|
||||
"59m"
|
||||
],
|
||||
"wed": [
|
||||
"0s",
|
||||
"59m"
|
||||
],
|
||||
"thu": [
|
||||
"0s",
|
||||
"59m"
|
||||
],
|
||||
"fri": [
|
||||
"0s",
|
||||
"59m"
|
||||
]
|
||||
}
|
||||
},
|
||||
"rule_lists": {
|
||||
"enabled": true,
|
||||
"ids": [
|
||||
"1"
|
||||
]
|
||||
},
|
||||
"filtered_response_ttl": 3600,
|
||||
"blocking_mode": {
|
||||
"type": "custom_ip",
|
||||
"ipv4": "1.2.3.4",
|
||||
"ipv6": "1234::cdef"
|
||||
},
|
||||
"custom_rules": [
|
||||
"||example.org^"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.31.0
|
||||
// protoc v4.23.4
|
||||
// protoc v4.25.1
|
||||
// source: backend.proto
|
||||
|
||||
package backendpb
|
||||
@ -94,6 +94,8 @@ type DNSProfile struct {
|
||||
// *DNSProfile_BlockingModeNullIp
|
||||
// *DNSProfile_BlockingModeRefused
|
||||
BlockingMode isDNSProfile_BlockingMode `protobuf_oneof:"blocking_mode"`
|
||||
IpLogEnabled bool `protobuf:"varint,17,opt,name=ip_log_enabled,json=ipLogEnabled,proto3" json:"ip_log_enabled,omitempty"`
|
||||
Access *AccessSettings `protobuf:"bytes,18,opt,name=access,proto3" json:"access,omitempty"`
|
||||
}
|
||||
|
||||
func (x *DNSProfile) Reset() {
|
||||
@ -247,6 +249,20 @@ func (x *DNSProfile) GetBlockingModeRefused() *BlockingModeREFUSED {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *DNSProfile) GetIpLogEnabled() bool {
|
||||
if x != nil {
|
||||
return x.IpLogEnabled
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *DNSProfile) GetAccess() *AccessSettings {
|
||||
if x != nil {
|
||||
return x.Access
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type isDNSProfile_BlockingMode interface {
|
||||
isDNSProfile_BlockingMode()
|
||||
}
|
||||
@ -1021,6 +1037,148 @@ func (x *DeviceBillingStat) GetQueries() uint32 {
|
||||
return 0
|
||||
}
|
||||
|
||||
type AccessSettings struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
AllowlistCidr []*CidrRange `protobuf:"bytes,1,rep,name=allowlist_cidr,json=allowlistCidr,proto3" json:"allowlist_cidr,omitempty"`
|
||||
BlocklistCidr []*CidrRange `protobuf:"bytes,2,rep,name=blocklist_cidr,json=blocklistCidr,proto3" json:"blocklist_cidr,omitempty"`
|
||||
AllowlistAsn []uint32 `protobuf:"varint,3,rep,packed,name=allowlist_asn,json=allowlistAsn,proto3" json:"allowlist_asn,omitempty"`
|
||||
BlocklistAsn []uint32 `protobuf:"varint,4,rep,packed,name=blocklist_asn,json=blocklistAsn,proto3" json:"blocklist_asn,omitempty"`
|
||||
BlocklistDomainRules []string `protobuf:"bytes,5,rep,name=blocklist_domain_rules,json=blocklistDomainRules,proto3" json:"blocklist_domain_rules,omitempty"`
|
||||
Enabled bool `protobuf:"varint,6,opt,name=enabled,proto3" json:"enabled,omitempty"`
|
||||
}
|
||||
|
||||
func (x *AccessSettings) Reset() {
|
||||
*x = AccessSettings{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_backend_proto_msgTypes[14]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *AccessSettings) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*AccessSettings) ProtoMessage() {}
|
||||
|
||||
func (x *AccessSettings) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_backend_proto_msgTypes[14]
|
||||
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 AccessSettings.ProtoReflect.Descriptor instead.
|
||||
func (*AccessSettings) Descriptor() ([]byte, []int) {
|
||||
return file_backend_proto_rawDescGZIP(), []int{14}
|
||||
}
|
||||
|
||||
func (x *AccessSettings) GetAllowlistCidr() []*CidrRange {
|
||||
if x != nil {
|
||||
return x.AllowlistCidr
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *AccessSettings) GetBlocklistCidr() []*CidrRange {
|
||||
if x != nil {
|
||||
return x.BlocklistCidr
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *AccessSettings) GetAllowlistAsn() []uint32 {
|
||||
if x != nil {
|
||||
return x.AllowlistAsn
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *AccessSettings) GetBlocklistAsn() []uint32 {
|
||||
if x != nil {
|
||||
return x.BlocklistAsn
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *AccessSettings) GetBlocklistDomainRules() []string {
|
||||
if x != nil {
|
||||
return x.BlocklistDomainRules
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *AccessSettings) GetEnabled() bool {
|
||||
if x != nil {
|
||||
return x.Enabled
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type CidrRange struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Address []byte `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"`
|
||||
Prefix uint32 `protobuf:"varint,2,opt,name=prefix,proto3" json:"prefix,omitempty"`
|
||||
}
|
||||
|
||||
func (x *CidrRange) Reset() {
|
||||
*x = CidrRange{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_backend_proto_msgTypes[15]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *CidrRange) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*CidrRange) ProtoMessage() {}
|
||||
|
||||
func (x *CidrRange) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_backend_proto_msgTypes[15]
|
||||
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 CidrRange.ProtoReflect.Descriptor instead.
|
||||
func (*CidrRange) Descriptor() ([]byte, []int) {
|
||||
return file_backend_proto_rawDescGZIP(), []int{15}
|
||||
}
|
||||
|
||||
func (x *CidrRange) GetAddress() []byte {
|
||||
if x != nil {
|
||||
return x.Address
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *CidrRange) GetPrefix() uint32 {
|
||||
if x != nil {
|
||||
return x.Prefix
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
var File_backend_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_backend_proto_rawDesc = []byte{
|
||||
@ -1035,7 +1193,7 @@ var file_backend_proto_rawDesc = []byte{
|
||||
0x65, 0x73, 0x74, 0x12, 0x37, 0x0a, 0x09, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x74, 0x69, 0x6d, 0x65,
|
||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61,
|
||||
0x6d, 0x70, 0x52, 0x08, 0x73, 0x79, 0x6e, 0x63, 0x54, 0x69, 0x6d, 0x65, 0x22, 0xf9, 0x06, 0x0a,
|
||||
0x6d, 0x70, 0x52, 0x08, 0x73, 0x79, 0x6e, 0x63, 0x54, 0x69, 0x6d, 0x65, 0x22, 0xc8, 0x07, 0x0a,
|
||||
0x0a, 0x44, 0x4e, 0x53, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x15, 0x0a, 0x06, 0x64,
|
||||
0x6e, 0x73, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x64, 0x6e, 0x73,
|
||||
0x49, 0x64, 0x12, 0x2b, 0x0a, 0x11, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x5f,
|
||||
@ -1090,110 +1248,135 @@ var file_backend_proto_rawDesc = []byte{
|
||||
0x75, 0x73, 0x65, 0x64, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x42, 0x6c, 0x6f,
|
||||
0x63, 0x6b, 0x69, 0x6e, 0x67, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x45, 0x46, 0x55, 0x53, 0x45, 0x44,
|
||||
0x48, 0x00, 0x52, 0x13, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x4d, 0x6f, 0x64, 0x65,
|
||||
0x52, 0x65, 0x66, 0x75, 0x73, 0x65, 0x64, 0x42, 0x0f, 0x0a, 0x0d, 0x62, 0x6c, 0x6f, 0x63, 0x6b,
|
||||
0x69, 0x6e, 0x67, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x22, 0x85, 0x01, 0x0a, 0x14, 0x53, 0x61, 0x66,
|
||||
0x65, 0x42, 0x72, 0x6f, 0x77, 0x73, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67,
|
||||
0x73, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01,
|
||||
0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x36, 0x0a, 0x17, 0x62,
|
||||
0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x64, 0x61, 0x6e, 0x67, 0x65, 0x72, 0x6f, 0x75, 0x73, 0x5f, 0x64,
|
||||
0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x15, 0x62, 0x6c,
|
||||
0x6f, 0x63, 0x6b, 0x44, 0x61, 0x6e, 0x67, 0x65, 0x72, 0x6f, 0x75, 0x73, 0x44, 0x6f, 0x6d, 0x61,
|
||||
0x69, 0x6e, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6e, 0x72, 0x64,
|
||||
0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x4e, 0x72, 0x64,
|
||||
0x22, 0xa3, 0x01, 0x0a, 0x0e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69,
|
||||
0x6e, 0x67, 0x73, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2b, 0x0a, 0x11, 0x66, 0x69, 0x6c, 0x74, 0x65,
|
||||
0x72, 0x69, 0x6e, 0x67, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01,
|
||||
0x28, 0x08, 0x52, 0x10, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x45, 0x6e, 0x61,
|
||||
0x62, 0x6c, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x5f, 0x69,
|
||||
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, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0c, 0x64, 0x65, 0x64, 0x69, 0x63, 0x61,
|
||||
0x74, 0x65, 0x64, 0x49, 0x70, 0x73, 0x22, 0x87, 0x02, 0x0a, 0x10, 0x50, 0x61, 0x72, 0x65, 0x6e,
|
||||
0x74, 0x61, 0x6c, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65,
|
||||
0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e,
|
||||
0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x61,
|
||||
0x64, 0x75, 0x6c, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x62, 0x6c, 0x6f, 0x63,
|
||||
0x6b, 0x41, 0x64, 0x75, 0x6c, 0x74, 0x12, 0x2e, 0x0a, 0x13, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61,
|
||||
0x6c, 0x5f, 0x73, 0x61, 0x66, 0x65, 0x5f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x18, 0x03, 0x20,
|
||||
0x01, 0x28, 0x08, 0x52, 0x11, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x53, 0x61, 0x66, 0x65,
|
||||
0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x12, 0x2e, 0x0a, 0x13, 0x79, 0x6f, 0x75, 0x74, 0x75, 0x62,
|
||||
0x65, 0x5f, 0x73, 0x61, 0x66, 0x65, 0x5f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x18, 0x04, 0x20,
|
||||
0x01, 0x28, 0x08, 0x52, 0x11, 0x79, 0x6f, 0x75, 0x74, 0x75, 0x62, 0x65, 0x53, 0x61, 0x66, 0x65,
|
||||
0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x12, 0x29, 0x0a, 0x10, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x65,
|
||||
0x64, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09,
|
||||
0x52, 0x0f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
|
||||
0x73, 0x12, 0x2d, 0x0a, 0x08, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x06, 0x20,
|
||||
0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x53, 0x65,
|
||||
0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x08, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65,
|
||||
0x22, 0x54, 0x0a, 0x10, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x53, 0x65, 0x74, 0x74,
|
||||
0x69, 0x6e, 0x67, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x6d, 0x7a, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x03, 0x74, 0x6d, 0x7a, 0x12, 0x2e, 0x0a, 0x0b, 0x77, 0x65, 0x65, 0x6b, 0x6c, 0x79,
|
||||
0x52, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x57, 0x65,
|
||||
0x65, 0x6b, 0x6c, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0b, 0x77, 0x65, 0x65, 0x6b, 0x6c,
|
||||
0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x22, 0xd8, 0x01, 0x0a, 0x0b, 0x57, 0x65, 0x65, 0x6b, 0x6c,
|
||||
0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x1b, 0x0a, 0x03, 0x6d, 0x6f, 0x6e, 0x18, 0x01, 0x20,
|
||||
0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x44, 0x61, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x03,
|
||||
0x6d, 0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x03, 0x74, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b,
|
||||
0x32, 0x09, 0x2e, 0x44, 0x61, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x03, 0x74, 0x75, 0x65,
|
||||
0x12, 0x1b, 0x0a, 0x03, 0x77, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e,
|
||||
0x44, 0x61, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x03, 0x77, 0x65, 0x64, 0x12, 0x1b, 0x0a,
|
||||
0x03, 0x74, 0x68, 0x75, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x44, 0x61, 0x79,
|
||||
0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x03, 0x74, 0x68, 0x75, 0x12, 0x1b, 0x0a, 0x03, 0x66, 0x72,
|
||||
0x69, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x44, 0x61, 0x79, 0x52, 0x61, 0x6e,
|
||||
0x67, 0x65, 0x52, 0x03, 0x66, 0x72, 0x69, 0x12, 0x1b, 0x0a, 0x03, 0x73, 0x61, 0x74, 0x18, 0x06,
|
||||
0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x44, 0x61, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52,
|
||||
0x03, 0x73, 0x61, 0x74, 0x12, 0x1b, 0x0a, 0x03, 0x73, 0x75, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28,
|
||||
0x0b, 0x32, 0x09, 0x2e, 0x44, 0x61, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x03, 0x73, 0x75,
|
||||
0x6e, 0x22, 0x68, 0x0a, 0x08, 0x44, 0x61, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x2f, 0x0a,
|
||||
0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67,
|
||||
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44,
|
||||
0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x2b,
|
||||
0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f,
|
||||
0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75,
|
||||
0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x22, 0x3f, 0x0a, 0x11, 0x52,
|
||||
0x75, 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x73, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73,
|
||||
0x52, 0x65, 0x66, 0x75, 0x73, 0x65, 0x64, 0x12, 0x24, 0x0a, 0x0e, 0x69, 0x70, 0x5f, 0x6c, 0x6f,
|
||||
0x67, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x11, 0x20, 0x01, 0x28, 0x08, 0x52,
|
||||
0x0c, 0x69, 0x70, 0x4c, 0x6f, 0x67, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x27, 0x0a,
|
||||
0x06, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e,
|
||||
0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x06,
|
||||
0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x42, 0x0f, 0x0a, 0x0d, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x69,
|
||||
0x6e, 0x67, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x22, 0x85, 0x01, 0x0a, 0x14, 0x53, 0x61, 0x66, 0x65,
|
||||
0x42, 0x72, 0x6f, 0x77, 0x73, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73,
|
||||
0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x69, 0x64,
|
||||
0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x03, 0x69, 0x64, 0x73, 0x22, 0x3e, 0x0a, 0x14,
|
||||
0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x4d, 0x6f, 0x64, 0x65, 0x43, 0x75, 0x73, 0x74,
|
||||
0x6f, 0x6d, 0x49, 0x50, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x70, 0x76, 0x34, 0x18, 0x01, 0x20, 0x01,
|
||||
0x28, 0x0c, 0x52, 0x04, 0x69, 0x70, 0x76, 0x34, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x70, 0x76, 0x36,
|
||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x69, 0x70, 0x76, 0x36, 0x22, 0x16, 0x0a, 0x14,
|
||||
0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x4d, 0x6f, 0x64, 0x65, 0x4e, 0x58, 0x44, 0x4f,
|
||||
0x4d, 0x41, 0x49, 0x4e, 0x22, 0x14, 0x0a, 0x12, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67,
|
||||
0x4d, 0x6f, 0x64, 0x65, 0x4e, 0x75, 0x6c, 0x6c, 0x49, 0x50, 0x22, 0x15, 0x0a, 0x13, 0x42, 0x6c,
|
||||
0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x45, 0x46, 0x55, 0x53, 0x45,
|
||||
0x44, 0x22, 0xe3, 0x01, 0x0a, 0x11, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x42, 0x69, 0x6c, 0x6c,
|
||||
0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x12, 0x48, 0x0a, 0x12, 0x6c, 0x61, 0x73, 0x74, 0x5f,
|
||||
0x61, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20,
|
||||
0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52,
|
||||
0x10, 0x6c, 0x61, 0x73, 0x74, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x54, 0x69, 0x6d,
|
||||
0x65, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x12, 0x25,
|
||||
0x0a, 0x0e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79,
|
||||
0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x6f,
|
||||
0x75, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x18, 0x04,
|
||||
0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x10, 0x0a, 0x03, 0x61,
|
||||
0x73, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x61, 0x73, 0x6e, 0x12, 0x18, 0x0a,
|
||||
0x07, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07,
|
||||
0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x32, 0x8a, 0x01, 0x0a, 0x0a, 0x44, 0x4e, 0x53, 0x53,
|
||||
0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x34, 0x0a, 0x0e, 0x67, 0x65, 0x74, 0x44, 0x4e, 0x53,
|
||||
0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x13, 0x2e, 0x44, 0x4e, 0x53, 0x50, 0x72,
|
||||
0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0b, 0x2e,
|
||||
0x44, 0x4e, 0x53, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x30, 0x01, 0x12, 0x46, 0x0a, 0x16,
|
||||
0x73, 0x61, 0x76, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x42, 0x69, 0x6c, 0x6c, 0x69,
|
||||
0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x12, 0x12, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x42,
|
||||
0x69, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f,
|
||||
0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70,
|
||||
0x74, 0x79, 0x28, 0x01, 0x42, 0x4a, 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, 0x5a, 0x0b, 0x2e,
|
||||
0x2f, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x70, 0x62, 0xa2, 0x02, 0x03, 0x44, 0x4e, 0x53,
|
||||
0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x36, 0x0a, 0x17, 0x62, 0x6c,
|
||||
0x6f, 0x63, 0x6b, 0x5f, 0x64, 0x61, 0x6e, 0x67, 0x65, 0x72, 0x6f, 0x75, 0x73, 0x5f, 0x64, 0x6f,
|
||||
0x6d, 0x61, 0x69, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x15, 0x62, 0x6c, 0x6f,
|
||||
0x63, 0x6b, 0x44, 0x61, 0x6e, 0x67, 0x65, 0x72, 0x6f, 0x75, 0x73, 0x44, 0x6f, 0x6d, 0x61, 0x69,
|
||||
0x6e, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6e, 0x72, 0x64, 0x18,
|
||||
0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x4e, 0x72, 0x64, 0x22,
|
||||
0xa3, 0x01, 0x0a, 0x0e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e,
|
||||
0x67, 0x73, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02,
|
||||
0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
|
||||
0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2b, 0x0a, 0x11, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72,
|
||||
0x69, 0x6e, 0x67, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28,
|
||||
0x08, 0x52, 0x10, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x45, 0x6e, 0x61, 0x62,
|
||||
0x6c, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x5f, 0x69, 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,
|
||||
0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0c, 0x64, 0x65, 0x64, 0x69, 0x63, 0x61, 0x74,
|
||||
0x65, 0x64, 0x49, 0x70, 0x73, 0x22, 0x87, 0x02, 0x0a, 0x10, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74,
|
||||
0x61, 0x6c, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e,
|
||||
0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61,
|
||||
0x62, 0x6c, 0x65, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x61, 0x64,
|
||||
0x75, 0x6c, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b,
|
||||
0x41, 0x64, 0x75, 0x6c, 0x74, 0x12, 0x2e, 0x0a, 0x13, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c,
|
||||
0x5f, 0x73, 0x61, 0x66, 0x65, 0x5f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x18, 0x03, 0x20, 0x01,
|
||||
0x28, 0x08, 0x52, 0x11, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x53, 0x61, 0x66, 0x65, 0x53,
|
||||
0x65, 0x61, 0x72, 0x63, 0x68, 0x12, 0x2e, 0x0a, 0x13, 0x79, 0x6f, 0x75, 0x74, 0x75, 0x62, 0x65,
|
||||
0x5f, 0x73, 0x61, 0x66, 0x65, 0x5f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x18, 0x04, 0x20, 0x01,
|
||||
0x28, 0x08, 0x52, 0x11, 0x79, 0x6f, 0x75, 0x74, 0x75, 0x62, 0x65, 0x53, 0x61, 0x66, 0x65, 0x53,
|
||||
0x65, 0x61, 0x72, 0x63, 0x68, 0x12, 0x29, 0x0a, 0x10, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64,
|
||||
0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52,
|
||||
0x0f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73,
|
||||
0x12, 0x2d, 0x0a, 0x08, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x06, 0x20, 0x01,
|
||||
0x28, 0x0b, 0x32, 0x11, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x53, 0x65, 0x74,
|
||||
0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x08, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x22,
|
||||
0x54, 0x0a, 0x10, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69,
|
||||
0x6e, 0x67, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x6d, 0x7a, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
|
||||
0x52, 0x03, 0x74, 0x6d, 0x7a, 0x12, 0x2e, 0x0a, 0x0b, 0x77, 0x65, 0x65, 0x6b, 0x6c, 0x79, 0x52,
|
||||
0x61, 0x6e, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x57, 0x65, 0x65,
|
||||
0x6b, 0x6c, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0b, 0x77, 0x65, 0x65, 0x6b, 0x6c, 0x79,
|
||||
0x52, 0x61, 0x6e, 0x67, 0x65, 0x22, 0xd8, 0x01, 0x0a, 0x0b, 0x57, 0x65, 0x65, 0x6b, 0x6c, 0x79,
|
||||
0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x1b, 0x0a, 0x03, 0x6d, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01,
|
||||
0x28, 0x0b, 0x32, 0x09, 0x2e, 0x44, 0x61, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x03, 0x6d,
|
||||
0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x03, 0x74, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32,
|
||||
0x09, 0x2e, 0x44, 0x61, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x03, 0x74, 0x75, 0x65, 0x12,
|
||||
0x1b, 0x0a, 0x03, 0x77, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x44,
|
||||
0x61, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x03, 0x77, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x03,
|
||||
0x74, 0x68, 0x75, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x44, 0x61, 0x79, 0x52,
|
||||
0x61, 0x6e, 0x67, 0x65, 0x52, 0x03, 0x74, 0x68, 0x75, 0x12, 0x1b, 0x0a, 0x03, 0x66, 0x72, 0x69,
|
||||
0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x44, 0x61, 0x79, 0x52, 0x61, 0x6e, 0x67,
|
||||
0x65, 0x52, 0x03, 0x66, 0x72, 0x69, 0x12, 0x1b, 0x0a, 0x03, 0x73, 0x61, 0x74, 0x18, 0x06, 0x20,
|
||||
0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x44, 0x61, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x03,
|
||||
0x73, 0x61, 0x74, 0x12, 0x1b, 0x0a, 0x03, 0x73, 0x75, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b,
|
||||
0x32, 0x09, 0x2e, 0x44, 0x61, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x03, 0x73, 0x75, 0x6e,
|
||||
0x22, 0x68, 0x0a, 0x08, 0x44, 0x61, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x2f, 0x0a, 0x05,
|
||||
0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f,
|
||||
0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75,
|
||||
0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x2b, 0x0a,
|
||||
0x03, 0x65, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f,
|
||||
0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72,
|
||||
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x22, 0x3f, 0x0a, 0x11, 0x52, 0x75,
|
||||
0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x73, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12,
|
||||
0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08,
|
||||
0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x69, 0x64, 0x73,
|
||||
0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x03, 0x69, 0x64, 0x73, 0x22, 0x3e, 0x0a, 0x14, 0x42,
|
||||
0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x4d, 0x6f, 0x64, 0x65, 0x43, 0x75, 0x73, 0x74, 0x6f,
|
||||
0x6d, 0x49, 0x50, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x70, 0x76, 0x34, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x0c, 0x52, 0x04, 0x69, 0x70, 0x76, 0x34, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x70, 0x76, 0x36, 0x18,
|
||||
0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x69, 0x70, 0x76, 0x36, 0x22, 0x16, 0x0a, 0x14, 0x42,
|
||||
0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x4d, 0x6f, 0x64, 0x65, 0x4e, 0x58, 0x44, 0x4f, 0x4d,
|
||||
0x41, 0x49, 0x4e, 0x22, 0x14, 0x0a, 0x12, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x4d,
|
||||
0x6f, 0x64, 0x65, 0x4e, 0x75, 0x6c, 0x6c, 0x49, 0x50, 0x22, 0x15, 0x0a, 0x13, 0x42, 0x6c, 0x6f,
|
||||
0x63, 0x6b, 0x69, 0x6e, 0x67, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x45, 0x46, 0x55, 0x53, 0x45, 0x44,
|
||||
0x22, 0xe3, 0x01, 0x0a, 0x11, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x42, 0x69, 0x6c, 0x6c, 0x69,
|
||||
0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x12, 0x48, 0x0a, 0x12, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x61,
|
||||
0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01,
|
||||
0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x10,
|
||||
0x6c, 0x61, 0x73, 0x74, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x54, 0x69, 0x6d, 0x65,
|
||||
0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x12, 0x25, 0x0a,
|
||||
0x0e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x18,
|
||||
0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x75,
|
||||
0x6e, 0x74, 0x72, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x18, 0x04, 0x20,
|
||||
0x01, 0x28, 0x0d, 0x52, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x73,
|
||||
0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x61, 0x73, 0x6e, 0x12, 0x18, 0x0a, 0x07,
|
||||
0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x71,
|
||||
0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x22, 0x90, 0x02, 0x0a, 0x0e, 0x41, 0x63, 0x63, 0x65, 0x73,
|
||||
0x73, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x31, 0x0a, 0x0e, 0x61, 0x6c, 0x6c,
|
||||
0x6f, 0x77, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x63, 0x69, 0x64, 0x72, 0x18, 0x01, 0x20, 0x03, 0x28,
|
||||
0x0b, 0x32, 0x0a, 0x2e, 0x43, 0x69, 0x64, 0x72, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0d, 0x61,
|
||||
0x6c, 0x6c, 0x6f, 0x77, 0x6c, 0x69, 0x73, 0x74, 0x43, 0x69, 0x64, 0x72, 0x12, 0x31, 0x0a, 0x0e,
|
||||
0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x63, 0x69, 0x64, 0x72, 0x18, 0x02,
|
||||
0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x43, 0x69, 0x64, 0x72, 0x52, 0x61, 0x6e, 0x67, 0x65,
|
||||
0x52, 0x0d, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x6c, 0x69, 0x73, 0x74, 0x43, 0x69, 0x64, 0x72, 0x12,
|
||||
0x23, 0x0a, 0x0d, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x61, 0x73, 0x6e,
|
||||
0x18, 0x03, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0c, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x6c, 0x69, 0x73,
|
||||
0x74, 0x41, 0x73, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x6c, 0x69, 0x73,
|
||||
0x74, 0x5f, 0x61, 0x73, 0x6e, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0c, 0x62, 0x6c, 0x6f,
|
||||
0x63, 0x6b, 0x6c, 0x69, 0x73, 0x74, 0x41, 0x73, 0x6e, 0x12, 0x34, 0x0a, 0x16, 0x62, 0x6c, 0x6f,
|
||||
0x63, 0x6b, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x72, 0x75,
|
||||
0x6c, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x14, 0x62, 0x6c, 0x6f, 0x63, 0x6b,
|
||||
0x6c, 0x69, 0x73, 0x74, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x12,
|
||||
0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08,
|
||||
0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x3d, 0x0a, 0x09, 0x43, 0x69, 0x64,
|
||||
0x72, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73,
|
||||
0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73,
|
||||
0x12, 0x16, 0x0a, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d,
|
||||
0x52, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x32, 0x8a, 0x01, 0x0a, 0x0a, 0x44, 0x4e, 0x53,
|
||||
0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x34, 0x0a, 0x0e, 0x67, 0x65, 0x74, 0x44, 0x4e,
|
||||
0x53, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x13, 0x2e, 0x44, 0x4e, 0x53, 0x50,
|
||||
0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0b,
|
||||
0x2e, 0x44, 0x4e, 0x53, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x30, 0x01, 0x12, 0x46, 0x0a,
|
||||
0x16, 0x73, 0x61, 0x76, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x42, 0x69, 0x6c, 0x6c,
|
||||
0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x12, 0x12, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65,
|
||||
0x42, 0x69, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f,
|
||||
0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d,
|
||||
0x70, 0x74, 0x79, 0x28, 0x01, 0x42, 0x3d, 0x0a, 0x21, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x64, 0x67,
|
||||
0x75, 0x61, 0x72, 0x64, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2e, 0x64, 0x6e, 0x73,
|
||||
0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x42, 0x10, 0x44, 0x4e, 0x53, 0x50,
|
||||
0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0xa2, 0x02,
|
||||
0x03, 0x44, 0x4e, 0x53, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
@ -1208,7 +1391,7 @@ func file_backend_proto_rawDescGZIP() []byte {
|
||||
return file_backend_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_backend_proto_msgTypes = make([]protoimpl.MessageInfo, 14)
|
||||
var file_backend_proto_msgTypes = make([]protoimpl.MessageInfo, 16)
|
||||
var file_backend_proto_goTypes = []interface{}{
|
||||
(*DNSProfilesRequest)(nil), // 0: DNSProfilesRequest
|
||||
(*DNSProfile)(nil), // 1: DNSProfile
|
||||
@ -1224,42 +1407,47 @@ var file_backend_proto_goTypes = []interface{}{
|
||||
(*BlockingModeNullIP)(nil), // 11: BlockingModeNullIP
|
||||
(*BlockingModeREFUSED)(nil), // 12: BlockingModeREFUSED
|
||||
(*DeviceBillingStat)(nil), // 13: DeviceBillingStat
|
||||
(*timestamppb.Timestamp)(nil), // 14: google.protobuf.Timestamp
|
||||
(*durationpb.Duration)(nil), // 15: google.protobuf.Duration
|
||||
(*emptypb.Empty)(nil), // 16: google.protobuf.Empty
|
||||
(*AccessSettings)(nil), // 14: AccessSettings
|
||||
(*CidrRange)(nil), // 15: CidrRange
|
||||
(*timestamppb.Timestamp)(nil), // 16: google.protobuf.Timestamp
|
||||
(*durationpb.Duration)(nil), // 17: google.protobuf.Duration
|
||||
(*emptypb.Empty)(nil), // 18: google.protobuf.Empty
|
||||
}
|
||||
var file_backend_proto_depIdxs = []int32{
|
||||
14, // 0: DNSProfilesRequest.sync_time:type_name -> google.protobuf.Timestamp
|
||||
16, // 0: DNSProfilesRequest.sync_time:type_name -> google.protobuf.Timestamp
|
||||
2, // 1: DNSProfile.safe_browsing:type_name -> SafeBrowsingSettings
|
||||
4, // 2: DNSProfile.parental:type_name -> ParentalSettings
|
||||
8, // 3: DNSProfile.rule_lists:type_name -> RuleListsSettings
|
||||
3, // 4: DNSProfile.devices:type_name -> DeviceSettings
|
||||
15, // 5: DNSProfile.filtered_response_ttl:type_name -> google.protobuf.Duration
|
||||
17, // 5: DNSProfile.filtered_response_ttl:type_name -> google.protobuf.Duration
|
||||
9, // 6: DNSProfile.blocking_mode_custom_ip:type_name -> BlockingModeCustomIP
|
||||
10, // 7: DNSProfile.blocking_mode_nxdomain:type_name -> BlockingModeNXDOMAIN
|
||||
11, // 8: DNSProfile.blocking_mode_null_ip:type_name -> BlockingModeNullIP
|
||||
12, // 9: DNSProfile.blocking_mode_refused:type_name -> BlockingModeREFUSED
|
||||
5, // 10: ParentalSettings.schedule:type_name -> ScheduleSettings
|
||||
6, // 11: ScheduleSettings.weeklyRange:type_name -> WeeklyRange
|
||||
7, // 12: WeeklyRange.mon:type_name -> DayRange
|
||||
7, // 13: WeeklyRange.tue:type_name -> DayRange
|
||||
7, // 14: WeeklyRange.wed:type_name -> DayRange
|
||||
7, // 15: WeeklyRange.thu:type_name -> DayRange
|
||||
7, // 16: WeeklyRange.fri:type_name -> DayRange
|
||||
7, // 17: WeeklyRange.sat:type_name -> DayRange
|
||||
7, // 18: WeeklyRange.sun:type_name -> DayRange
|
||||
15, // 19: DayRange.start:type_name -> google.protobuf.Duration
|
||||
15, // 20: DayRange.end:type_name -> google.protobuf.Duration
|
||||
14, // 21: DeviceBillingStat.last_activity_time:type_name -> google.protobuf.Timestamp
|
||||
0, // 22: DNSService.getDNSProfiles:input_type -> DNSProfilesRequest
|
||||
13, // 23: DNSService.saveDevicesBillingStat:input_type -> DeviceBillingStat
|
||||
1, // 24: DNSService.getDNSProfiles:output_type -> DNSProfile
|
||||
16, // 25: DNSService.saveDevicesBillingStat:output_type -> google.protobuf.Empty
|
||||
24, // [24:26] is the sub-list for method output_type
|
||||
22, // [22:24] is the sub-list for method input_type
|
||||
22, // [22:22] is the sub-list for extension type_name
|
||||
22, // [22:22] is the sub-list for extension extendee
|
||||
0, // [0:22] is the sub-list for field type_name
|
||||
14, // 10: DNSProfile.access:type_name -> AccessSettings
|
||||
5, // 11: ParentalSettings.schedule:type_name -> ScheduleSettings
|
||||
6, // 12: ScheduleSettings.weeklyRange:type_name -> WeeklyRange
|
||||
7, // 13: WeeklyRange.mon:type_name -> DayRange
|
||||
7, // 14: WeeklyRange.tue:type_name -> DayRange
|
||||
7, // 15: WeeklyRange.wed:type_name -> DayRange
|
||||
7, // 16: WeeklyRange.thu:type_name -> DayRange
|
||||
7, // 17: WeeklyRange.fri:type_name -> DayRange
|
||||
7, // 18: WeeklyRange.sat:type_name -> DayRange
|
||||
7, // 19: WeeklyRange.sun:type_name -> DayRange
|
||||
17, // 20: DayRange.start:type_name -> google.protobuf.Duration
|
||||
17, // 21: DayRange.end:type_name -> google.protobuf.Duration
|
||||
16, // 22: DeviceBillingStat.last_activity_time:type_name -> google.protobuf.Timestamp
|
||||
15, // 23: AccessSettings.allowlist_cidr:type_name -> CidrRange
|
||||
15, // 24: AccessSettings.blocklist_cidr:type_name -> CidrRange
|
||||
0, // 25: DNSService.getDNSProfiles:input_type -> DNSProfilesRequest
|
||||
13, // 26: DNSService.saveDevicesBillingStat:input_type -> DeviceBillingStat
|
||||
1, // 27: DNSService.getDNSProfiles:output_type -> DNSProfile
|
||||
18, // 28: DNSService.saveDevicesBillingStat:output_type -> google.protobuf.Empty
|
||||
27, // [27:29] is the sub-list for method output_type
|
||||
25, // [25:27] is the sub-list for method input_type
|
||||
25, // [25:25] is the sub-list for extension type_name
|
||||
25, // [25:25] is the sub-list for extension extendee
|
||||
0, // [0:25] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_backend_proto_init() }
|
||||
@ -1436,6 +1624,30 @@ func file_backend_proto_init() {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_backend_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*AccessSettings); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_backend_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*CidrRange); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
file_backend_proto_msgTypes[1].OneofWrappers = []interface{}{
|
||||
(*DNSProfile_BlockingModeCustomIp)(nil),
|
||||
@ -1449,7 +1661,7 @@ func file_backend_proto_init() {
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_backend_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 14,
|
||||
NumMessages: 16,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
|
@ -1,7 +1,5 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option go_package = "./backendpb";
|
||||
|
||||
import "google/protobuf/duration.proto";
|
||||
import "google/protobuf/timestamp.proto";
|
||||
import "google/protobuf/empty.proto";
|
||||
@ -52,6 +50,8 @@ message DNSProfile {
|
||||
BlockingModeNullIP blocking_mode_null_ip = 15;
|
||||
BlockingModeREFUSED blocking_mode_refused = 16;
|
||||
}
|
||||
bool ip_log_enabled = 17;
|
||||
AccessSettings access = 18;
|
||||
}
|
||||
|
||||
message SafeBrowsingSettings {
|
||||
@ -122,3 +122,17 @@ message DeviceBillingStat {
|
||||
uint32 asn = 5;
|
||||
uint32 queries = 6;
|
||||
}
|
||||
|
||||
message AccessSettings {
|
||||
repeated CidrRange allowlist_cidr = 1;
|
||||
repeated CidrRange blocklist_cidr = 2;
|
||||
repeated uint32 allowlist_asn = 3;
|
||||
repeated uint32 blocklist_asn = 4;
|
||||
repeated string blocklist_domain_rules = 5;
|
||||
bool enabled = 6;
|
||||
}
|
||||
|
||||
message CidrRange {
|
||||
bytes address = 1;
|
||||
uint32 prefix = 2;
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.3.0
|
||||
// - protoc v4.23.4
|
||||
// - protoc v4.25.1
|
||||
// source: backend.proto
|
||||
|
||||
package backendpb
|
||||
|
@ -6,7 +6,7 @@ import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
@ -38,6 +38,6 @@ func newClient(apiURL *url.URL) (client DNSServiceClient, err error) {
|
||||
}
|
||||
|
||||
// reportf is a helper method for reporting non-critical errors.
|
||||
func reportf(ctx context.Context, errColl agd.ErrorCollector, format string, args ...any) {
|
||||
agd.Collectf(ctx, errColl, "backendpb: "+format, args...)
|
||||
func reportf(ctx context.Context, errColl errcoll.Interface, format string, args ...any) {
|
||||
errcoll.Collectf(ctx, errColl, "backendpb: "+format, args...)
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/billstat"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
@ -17,7 +18,7 @@ import (
|
||||
type BillStatConfig struct {
|
||||
// ErrColl is the error collector that is used to collect critical and
|
||||
// non-critical errors.
|
||||
ErrColl agd.ErrorCollector
|
||||
ErrColl errcoll.Interface
|
||||
|
||||
// Endpoint is the backend API URL. The scheme should be either "grpc" or
|
||||
// "grpcs".
|
||||
@ -46,7 +47,7 @@ func NewBillStat(c *BillStatConfig) (b *BillStat, err error) {
|
||||
// TODO(a.garipov): Consider uniting with [ProfileStorage] into a single
|
||||
// backendpb.Client.
|
||||
type BillStat struct {
|
||||
errColl agd.ErrorCollector
|
||||
errColl errcoll.Interface
|
||||
|
||||
// client is the current GRPC client.
|
||||
client DNSServiceClient
|
||||
|
@ -12,6 +12,7 @@ import (
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agdtest"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/backendpb"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/billstat"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/geoip"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/testutil"
|
||||
"github.com/stretchr/testify/assert"
|
||||
@ -30,7 +31,7 @@ func TestBillStat_Upload(t *testing.T) {
|
||||
|
||||
wantRecord := &billstat.Record{
|
||||
Time: time.Time{},
|
||||
Country: agd.CountryCY,
|
||||
Country: geoip.CountryCY,
|
||||
ASN: 1221,
|
||||
Queries: 1122,
|
||||
Proto: agd.ProtoDNS,
|
||||
|
@ -9,13 +9,18 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/access"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agdprotobuf"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agdtime"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/geoip"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/profiledb"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/status"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
|
||||
@ -24,7 +29,7 @@ import (
|
||||
type ProfileStorageConfig struct {
|
||||
// ErrColl is the error collector that is used to collect critical and
|
||||
// non-critical errors.
|
||||
ErrColl agd.ErrorCollector
|
||||
ErrColl errcoll.Interface
|
||||
|
||||
// Endpoint is the backend API URL. The scheme should be either "grpc" or
|
||||
// "grpcs".
|
||||
@ -35,7 +40,7 @@ type ProfileStorageConfig struct {
|
||||
// that retrieves the profile and device information from the business logic
|
||||
// backend. It is safe for concurrent use.
|
||||
type ProfileStorage struct {
|
||||
errColl agd.ErrorCollector
|
||||
errColl errcoll.Interface
|
||||
|
||||
// client is the current GRPC client.
|
||||
client DNSServiceClient
|
||||
@ -66,7 +71,7 @@ func (s *ProfileStorage) Profiles(
|
||||
) (resp *profiledb.StorageResponse, err error) {
|
||||
stream, err := s.client.GetDNSProfiles(ctx, toProtobuf(req))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("loading profiles: %w", err)
|
||||
return nil, fmt.Errorf("loading profiles: %w", fixGRPCError(err))
|
||||
}
|
||||
defer func() { err = errors.WithDeferred(err, stream.CloseSend()) }()
|
||||
|
||||
@ -76,7 +81,7 @@ func (s *ProfileStorage) Profiles(
|
||||
}
|
||||
|
||||
stats := &profilesCallStats{
|
||||
isFullSync: req.SyncTime == time.Time{},
|
||||
isFullSync: req.SyncTime.IsZero(),
|
||||
}
|
||||
|
||||
for {
|
||||
@ -87,7 +92,7 @@ func (s *ProfileStorage) Profiles(
|
||||
break
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("receiving profile: %w", profErr)
|
||||
return nil, fmt.Errorf("receiving profile: %w", fixGRPCError(profErr))
|
||||
}
|
||||
stats.endRecv()
|
||||
|
||||
@ -115,11 +120,27 @@ func (s *ProfileStorage) Profiles(
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// fixGRPCError wraps GRPC error if needed. As the GRPC deadline error is not
|
||||
// correctly wrapped, this helper detects it by the status code and replaces it
|
||||
// with a simple DeadlineExceeded error.
|
||||
//
|
||||
// See https://github.com/grpc/grpc-go/issues/4822.
|
||||
//
|
||||
// TODO(d.kolyshev): Remove after the grpc-go issue is fixed.
|
||||
func fixGRPCError(err error) (wErr error) {
|
||||
st, ok := status.FromError(err)
|
||||
if ok && st.Code() == codes.DeadlineExceeded {
|
||||
err = fmt.Errorf("grpc: %w", context.DeadlineExceeded)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// toInternal converts the protobuf-encoded data into a profile structure.
|
||||
func (x *DNSProfile) toInternal(
|
||||
ctx context.Context,
|
||||
updTime time.Time,
|
||||
errColl agd.ErrorCollector,
|
||||
errColl errcoll.Interface,
|
||||
) (profile *agd.Profile, devices []*agd.Device, err error) {
|
||||
if x == nil {
|
||||
return nil, nil, fmt.Errorf("profile is nil")
|
||||
@ -159,11 +180,13 @@ func (x *DNSProfile) toInternal(
|
||||
FilteredResponseTTL: fltRespTTL,
|
||||
FilteringEnabled: x.FilteringEnabled,
|
||||
SafeBrowsing: x.SafeBrowsing.toInternal(),
|
||||
Access: x.Access.toInternal(ctx, errColl),
|
||||
RuleListsEnabled: listsEnabled,
|
||||
QueryLogEnabled: x.QueryLogEnabled,
|
||||
Deleted: x.Deleted,
|
||||
BlockPrivateRelay: x.BlockPrivateRelay,
|
||||
BlockFirefoxCanary: x.BlockFirefoxCanary,
|
||||
IPLogEnabled: x.IpLogEnabled,
|
||||
}, devices, nil
|
||||
}
|
||||
|
||||
@ -171,7 +194,7 @@ func (x *DNSProfile) toInternal(
|
||||
// one. If x is nil, toInternal returns nil.
|
||||
func (x *ParentalSettings) toInternal(
|
||||
ctx context.Context,
|
||||
errColl agd.ErrorCollector,
|
||||
errColl errcoll.Interface,
|
||||
) (s *agd.ParentalProtectionSettings, err error) {
|
||||
if x == nil {
|
||||
return nil, nil
|
||||
@ -206,11 +229,61 @@ func (x *SafeBrowsingSettings) toInternal() (sb *agd.SafeBrowsingSettings) {
|
||||
}
|
||||
}
|
||||
|
||||
// toInternal converts protobuf access settings to an internal structure. If x
|
||||
// is nil, toInternal returns [access.EmptyProfile].
|
||||
func (x *AccessSettings) toInternal(
|
||||
ctx context.Context,
|
||||
errColl errcoll.Interface,
|
||||
) (a access.Profile) {
|
||||
if x == nil || !x.Enabled {
|
||||
return access.EmptyProfile{}
|
||||
}
|
||||
|
||||
return access.NewDefaultProfile(&access.ProfileConfig{
|
||||
AllowedNets: cidrRangeToInternal(ctx, errColl, x.AllowlistCidr),
|
||||
BlockedNets: cidrRangeToInternal(ctx, errColl, x.BlocklistCidr),
|
||||
AllowedASN: asnToInternal(x.AllowlistAsn),
|
||||
BlockedASN: asnToInternal(x.BlocklistAsn),
|
||||
BlocklistDomainRules: x.BlocklistDomainRules,
|
||||
})
|
||||
}
|
||||
|
||||
// cidrRangeToInternal is a helper that converts a slice of CidrRange to the
|
||||
// slice of [netip.Prefix].
|
||||
func cidrRangeToInternal(
|
||||
ctx context.Context,
|
||||
errColl errcoll.Interface,
|
||||
cidrs []*CidrRange,
|
||||
) (out []netip.Prefix) {
|
||||
for i, c := range cidrs {
|
||||
addr, ok := netip.AddrFromSlice(c.Address)
|
||||
if !ok {
|
||||
reportf(ctx, errColl, "invalid cidr at index %d: %w", i)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
out = append(out, netip.PrefixFrom(addr, int(c.Prefix)))
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
// asnToInternal is a helper that converts a slice of ASNs to the slice of
|
||||
// [geoip.ASN].
|
||||
func asnToInternal(asns []uint32) (out []geoip.ASN) {
|
||||
for _, asn := range asns {
|
||||
out = append(out, geoip.ASN(asn))
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
// blockedSvcsToInternal is a helper that converts the blocked service IDs from
|
||||
// the backend response to AdGuard DNS blocked service IDs.
|
||||
func blockedSvcsToInternal(
|
||||
ctx context.Context,
|
||||
errColl agd.ErrorCollector,
|
||||
errColl errcoll.Interface,
|
||||
respSvcs []string,
|
||||
) (svcs []agd.BlockedServiceID) {
|
||||
l := len(respSvcs)
|
||||
@ -277,35 +350,33 @@ func (x *ScheduleSettings) toInternal() (sch *agd.ParentalProtectionSchedule, er
|
||||
// blockingModeToInternal converts a protobuf blocking-mode sum-type to an
|
||||
// internal one. If pbm is nil, blockingModeToInternal returns a null-IP
|
||||
// blocking mode.
|
||||
func blockingModeToInternal(pbm isDNSProfile_BlockingMode) (m dnsmsg.BlockingModeCodec, err error) {
|
||||
func blockingModeToInternal(pbm isDNSProfile_BlockingMode) (m dnsmsg.BlockingMode, err error) {
|
||||
switch pbm := pbm.(type) {
|
||||
case nil:
|
||||
m.Mode = &dnsmsg.BlockingModeNullIP{}
|
||||
return &dnsmsg.BlockingModeNullIP{}, nil
|
||||
case *DNSProfile_BlockingModeCustomIp:
|
||||
custom := &dnsmsg.BlockingModeCustomIP{}
|
||||
err = custom.IPv4.UnmarshalBinary(pbm.BlockingModeCustomIp.Ipv4)
|
||||
if err != nil {
|
||||
return dnsmsg.BlockingModeCodec{}, fmt.Errorf("bad custom ipv4: %w", err)
|
||||
return nil, fmt.Errorf("bad custom ipv4: %w", err)
|
||||
}
|
||||
|
||||
err = custom.IPv6.UnmarshalBinary(pbm.BlockingModeCustomIp.Ipv6)
|
||||
if err != nil {
|
||||
return dnsmsg.BlockingModeCodec{}, fmt.Errorf("bad custom ipv6: %w", err)
|
||||
return nil, fmt.Errorf("bad custom ipv6: %w", err)
|
||||
}
|
||||
|
||||
m.Mode = custom
|
||||
return custom, nil
|
||||
case *DNSProfile_BlockingModeNxdomain:
|
||||
m.Mode = &dnsmsg.BlockingModeNXDOMAIN{}
|
||||
return &dnsmsg.BlockingModeNXDOMAIN{}, nil
|
||||
case *DNSProfile_BlockingModeNullIp:
|
||||
m.Mode = &dnsmsg.BlockingModeNullIP{}
|
||||
return &dnsmsg.BlockingModeNullIP{}, nil
|
||||
case *DNSProfile_BlockingModeRefused:
|
||||
m.Mode = &dnsmsg.BlockingModeREFUSED{}
|
||||
return &dnsmsg.BlockingModeREFUSED{}, nil
|
||||
default:
|
||||
// Consider unhandled type-switch cases programmer errors.
|
||||
panic(fmt.Errorf("bad pb blocking mode %T(%[1]v)", pbm))
|
||||
return nil, fmt.Errorf("bad pb blocking mode %T(%[1]v)", pbm)
|
||||
}
|
||||
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// devicesToInternal is a helper that converts the devices from protobuf to
|
||||
@ -313,7 +384,7 @@ func blockingModeToInternal(pbm isDNSProfile_BlockingMode) (m dnsmsg.BlockingMod
|
||||
func devicesToInternal(
|
||||
ctx context.Context,
|
||||
ds []*DeviceSettings,
|
||||
errColl agd.ErrorCollector,
|
||||
errColl errcoll.Interface,
|
||||
) (out []*agd.Device, ids []agd.DeviceID) {
|
||||
l := len(ds)
|
||||
if l == 0 {
|
||||
@ -379,7 +450,7 @@ func (ds *DeviceSettings) toInternal() (dev *agd.Device, err error) {
|
||||
func rulesToInternal(
|
||||
ctx context.Context,
|
||||
respRules []string,
|
||||
errColl agd.ErrorCollector,
|
||||
errColl errcoll.Interface,
|
||||
) (rules []agd.FilterRuleText) {
|
||||
l := len(respRules)
|
||||
if l == 0 {
|
||||
@ -406,7 +477,7 @@ func rulesToInternal(
|
||||
// false and nil.
|
||||
func (x *RuleListsSettings) toInternal(
|
||||
ctx context.Context,
|
||||
errColl agd.ErrorCollector,
|
||||
errColl errcoll.Interface,
|
||||
) (enabled bool, filterLists []agd.FilterListID) {
|
||||
if x == nil {
|
||||
return false, nil
|
||||
|
@ -7,10 +7,12 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/access"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agdtest"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agdtime"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/geoip"
|
||||
"github.com/AdguardTeam/golibs/testutil"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
@ -60,13 +62,7 @@ func TestDNSProfile_ToInternal(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.Error(t, errCollErr)
|
||||
|
||||
// See the TODO in [blockingModeToInternal].
|
||||
wantProf := newProfile(t)
|
||||
wantProf.BlockingMode = dnsmsg.BlockingModeCodec{
|
||||
Mode: &dnsmsg.BlockingModeNullIP{},
|
||||
}
|
||||
|
||||
assert.Equal(t, wantProf, got)
|
||||
assert.Equal(t, newProfile(t), got)
|
||||
assert.Equal(t, newDevices(t), gotDevices)
|
||||
})
|
||||
|
||||
@ -127,31 +123,55 @@ func TestDNSProfile_ToInternal(t *testing.T) {
|
||||
_, _, err := dp.toInternal(ctx, TestUpdTime, errColl)
|
||||
testutil.AssertErrorMsg(t, "blocking mode: bad custom ipv6: unexpected slice size", err)
|
||||
})
|
||||
|
||||
t.Run("nil_blocking_mode", func(t *testing.T) {
|
||||
dp := NewTestDNSProfile(t)
|
||||
dp.BlockingMode = nil
|
||||
|
||||
got, gotDevices, err := dp.toInternal(ctx, TestUpdTime, errColl)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, got)
|
||||
|
||||
wantProf := newProfile(t)
|
||||
wantProf.BlockingMode = &dnsmsg.BlockingModeNullIP{}
|
||||
|
||||
assert.Equal(t, wantProf, got)
|
||||
assert.Equal(t, newDevices(t), gotDevices)
|
||||
})
|
||||
|
||||
t.Run("nil_access", func(t *testing.T) {
|
||||
dp := NewTestDNSProfile(t)
|
||||
dp.Access = nil
|
||||
|
||||
got, _, err := dp.toInternal(ctx, TestUpdTime, errColl)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, got)
|
||||
|
||||
assert.Equal(t, got.ID, testProfileID)
|
||||
assert.IsType(t, access.EmptyProfile{}, got.Access)
|
||||
})
|
||||
|
||||
t.Run("access_disabled", func(t *testing.T) {
|
||||
dp := NewTestDNSProfile(t)
|
||||
dp.Access = &AccessSettings{
|
||||
Enabled: false,
|
||||
}
|
||||
|
||||
// newDNSProfileWithBadData returns a new instance of *DNSProfile with bad data
|
||||
// for tests.
|
||||
got, _, err := dp.toInternal(ctx, TestUpdTime, errColl)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, got)
|
||||
|
||||
assert.Equal(t, got.ID, testProfileID)
|
||||
assert.IsType(t, access.EmptyProfile{}, got.Access)
|
||||
})
|
||||
}
|
||||
|
||||
// newDNSProfileWithBadData returns a new instance of *DNSProfile with bad
|
||||
// devices data for tests.
|
||||
func newDNSProfileWithBadData(tb testing.TB) (dp *DNSProfile) {
|
||||
tb.Helper()
|
||||
|
||||
dayRange := &DayRange{
|
||||
Start: durationpb.New(0),
|
||||
End: durationpb.New(59 * time.Minute),
|
||||
}
|
||||
|
||||
devices := []*DeviceSettings{{
|
||||
Id: "118ffe93",
|
||||
Name: "118ffe93-name",
|
||||
FilteringEnabled: false,
|
||||
LinkedIp: ipToBytes(tb, netip.MustParseAddr("1.1.1.1")),
|
||||
DedicatedIps: [][]byte{ipToBytes(tb, netip.MustParseAddr("1.1.1.2"))},
|
||||
}, {
|
||||
Id: "b9e1a762",
|
||||
Name: "b9e1a762-name",
|
||||
FilteringEnabled: true,
|
||||
LinkedIp: ipToBytes(tb, netip.MustParseAddr("2.2.2.2")),
|
||||
DedicatedIps: nil,
|
||||
}, {
|
||||
invalidDevices := []*DeviceSettings{{
|
||||
Id: "invalid-too-long-device-id",
|
||||
Name: "device_name",
|
||||
FilteringEnabled: true,
|
||||
@ -179,45 +199,10 @@ func newDNSProfileWithBadData(tb testing.TB) (dp *DNSProfile) {
|
||||
DedicatedIps: [][]byte{[]byte("1")},
|
||||
}}
|
||||
|
||||
return &DNSProfile{
|
||||
DnsId: string(testProfileID),
|
||||
FilteringEnabled: true,
|
||||
QueryLogEnabled: true,
|
||||
Deleted: false,
|
||||
SafeBrowsing: &SafeBrowsingSettings{
|
||||
Enabled: true,
|
||||
BlockDangerousDomains: true,
|
||||
BlockNrd: false,
|
||||
},
|
||||
Parental: &ParentalSettings{
|
||||
Enabled: false,
|
||||
BlockAdult: false,
|
||||
GeneralSafeSearch: false,
|
||||
YoutubeSafeSearch: false,
|
||||
BlockedServices: []string{"youtube", "inv_blocked_svc\r"},
|
||||
Schedule: &ScheduleSettings{
|
||||
Tmz: "GMT",
|
||||
WeeklyRange: &WeeklyRange{
|
||||
Sun: nil,
|
||||
Mon: dayRange,
|
||||
Tue: dayRange,
|
||||
Wed: dayRange,
|
||||
Thu: dayRange,
|
||||
Fri: dayRange,
|
||||
Sat: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
RuleLists: &RuleListsSettings{
|
||||
Enabled: true,
|
||||
Ids: []string{"1", "inv_filter_id\r"},
|
||||
},
|
||||
Devices: devices,
|
||||
CustomRules: []string{"||example.org^"},
|
||||
FilteredResponseTtl: durationpb.New(10 * time.Second),
|
||||
BlockPrivateRelay: true,
|
||||
BlockFirefoxCanary: true,
|
||||
}
|
||||
dp = NewTestDNSProfile(tb)
|
||||
dp.Devices = append(dp.Devices, invalidDevices...)
|
||||
|
||||
return dp
|
||||
}
|
||||
|
||||
// NewTestDNSProfile returns a new instance of *DNSProfile for tests.
|
||||
@ -281,12 +266,27 @@ func NewTestDNSProfile(tb testing.TB) (dp *DNSProfile) {
|
||||
FilteredResponseTtl: durationpb.New(10 * time.Second),
|
||||
BlockPrivateRelay: true,
|
||||
BlockFirefoxCanary: true,
|
||||
IpLogEnabled: true,
|
||||
BlockingMode: &DNSProfile_BlockingModeCustomIp{
|
||||
BlockingModeCustomIp: &BlockingModeCustomIP{
|
||||
Ipv4: ipToBytes(tb, netip.MustParseAddr("1.2.3.4")),
|
||||
Ipv6: ipToBytes(tb, netip.MustParseAddr("1234::cdef")),
|
||||
},
|
||||
},
|
||||
Access: &AccessSettings{
|
||||
AllowlistCidr: []*CidrRange{{
|
||||
Address: netip.MustParseAddr("1.1.1.0").AsSlice(),
|
||||
Prefix: 24,
|
||||
}},
|
||||
BlocklistCidr: []*CidrRange{{
|
||||
Address: netip.MustParseAddr("2.2.2.0").AsSlice(),
|
||||
Prefix: 24,
|
||||
}},
|
||||
AllowlistAsn: []uint32{1},
|
||||
BlocklistAsn: []uint32{2},
|
||||
BlocklistDomainRules: []string{"block.test"},
|
||||
Enabled: true,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -328,13 +328,19 @@ func newProfile(tb testing.TB) (p *agd.Profile) {
|
||||
BlockNewlyRegisteredDomains: false,
|
||||
}
|
||||
|
||||
wantBlockingMode := dnsmsg.BlockingModeCodec{
|
||||
Mode: &dnsmsg.BlockingModeCustomIP{
|
||||
wantBlockingMode := &dnsmsg.BlockingModeCustomIP{
|
||||
IPv4: netip.MustParseAddr("1.2.3.4"),
|
||||
IPv6: netip.MustParseAddr("1234::cdef"),
|
||||
},
|
||||
}
|
||||
|
||||
wantAccess := access.NewDefaultProfile(&access.ProfileConfig{
|
||||
AllowedNets: []netip.Prefix{netip.MustParsePrefix("1.1.1.0/24")},
|
||||
BlockedNets: []netip.Prefix{netip.MustParsePrefix("2.2.2.0/24")},
|
||||
AllowedASN: []geoip.ASN{1},
|
||||
BlockedASN: []geoip.ASN{2},
|
||||
BlocklistDomainRules: []string{"block.test"},
|
||||
})
|
||||
|
||||
return &agd.Profile{
|
||||
Parental: wantParental,
|
||||
BlockingMode: wantBlockingMode,
|
||||
@ -348,12 +354,14 @@ func newProfile(tb testing.TB) (p *agd.Profile) {
|
||||
CustomRules: []agd.FilterRuleText{"||example.org^"},
|
||||
FilteredResponseTTL: 10 * time.Second,
|
||||
SafeBrowsing: wantSafeBrowsing,
|
||||
Access: wantAccess,
|
||||
RuleListsEnabled: true,
|
||||
FilteringEnabled: true,
|
||||
QueryLogEnabled: true,
|
||||
Deleted: false,
|
||||
BlockPrivateRelay: true,
|
||||
BlockFirefoxCanary: true,
|
||||
IPLogEnabled: true,
|
||||
}
|
||||
}
|
||||
|
||||
@ -390,30 +398,30 @@ func TestSyncTimeFromTrailer(t *testing.T) {
|
||||
milliseconds := strconv.FormatInt(TestUpdTime.UnixMilli(), 10)
|
||||
|
||||
testCases := []struct {
|
||||
in metadata.MD
|
||||
wantError string
|
||||
want time.Time
|
||||
name string
|
||||
in metadata.MD
|
||||
}{{
|
||||
in: metadata.MD{},
|
||||
wantError: "empty value",
|
||||
want: time.Time{},
|
||||
name: "no_key",
|
||||
in: metadata.MD{},
|
||||
}, {
|
||||
in: metadata.MD{"sync_time": []string{}},
|
||||
wantError: "empty value",
|
||||
want: time.Time{},
|
||||
name: "empty_key",
|
||||
in: metadata.MD{"sync_time": []string{}},
|
||||
}, {
|
||||
in: metadata.MD{"sync_time": []string{""}},
|
||||
wantError: `invalid value: strconv.ParseInt: parsing "": invalid syntax`,
|
||||
want: time.Time{},
|
||||
name: "empty_value",
|
||||
in: metadata.MD{"sync_time": []string{""}},
|
||||
}, {
|
||||
in: metadata.MD{"sync_time": []string{milliseconds}},
|
||||
wantError: "",
|
||||
want: TestUpdTime,
|
||||
name: "success",
|
||||
in: metadata.MD{"sync_time": []string{milliseconds}},
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/geoip"
|
||||
)
|
||||
|
||||
// Common Constants, Types, And Utilities
|
||||
@ -15,8 +16,8 @@ type Recorder interface {
|
||||
Record(
|
||||
ctx context.Context,
|
||||
id agd.DeviceID,
|
||||
ctry agd.Country,
|
||||
asn agd.ASN,
|
||||
ctry geoip.Country,
|
||||
asn geoip.ASN,
|
||||
start time.Time,
|
||||
proto agd.Protocol,
|
||||
)
|
||||
@ -32,8 +33,8 @@ type EmptyRecorder struct{}
|
||||
func (EmptyRecorder) Record(
|
||||
_ context.Context,
|
||||
_ agd.DeviceID,
|
||||
_ agd.Country,
|
||||
_ agd.ASN,
|
||||
_ geoip.Country,
|
||||
_ geoip.ASN,
|
||||
_ time.Time,
|
||||
_ agd.Protocol,
|
||||
) {
|
||||
@ -51,10 +52,10 @@ type Record struct {
|
||||
Time time.Time
|
||||
|
||||
// Country is the detected country of the client's IP address, if any.
|
||||
Country agd.Country
|
||||
Country geoip.Country
|
||||
|
||||
// ASN is the detected ASN of the client's IP address, if any.
|
||||
ASN agd.ASN
|
||||
ASN geoip.ASN
|
||||
|
||||
// Queries is the total number of Queries the device has performed since the
|
||||
// most recent sync. This value is an int32 to be in sync with the business
|
||||
|
@ -7,6 +7,8 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agdservice"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/geoip"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/metrics"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
)
|
||||
@ -51,8 +53,8 @@ var _ Recorder = (*RuntimeRecorder)(nil)
|
||||
func (r *RuntimeRecorder) Record(
|
||||
ctx context.Context,
|
||||
id agd.DeviceID,
|
||||
ctry agd.Country,
|
||||
asn agd.ASN,
|
||||
ctry geoip.Country,
|
||||
asn geoip.ASN,
|
||||
start time.Time,
|
||||
proto agd.Protocol,
|
||||
) {
|
||||
@ -80,10 +82,10 @@ func (r *RuntimeRecorder) Record(
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ agd.Refresher = (*RuntimeRecorder)(nil)
|
||||
var _ agdservice.Refresher = (*RuntimeRecorder)(nil)
|
||||
|
||||
// Refresh implements the agd.Refresher interface for *RuntimeRecorder. It
|
||||
// uploads the currently available data and resets it.
|
||||
// Refresh implements the [agdserivce.Refresher] interface for *RuntimeRecorder.
|
||||
// It uploads the currently available data and resets it.
|
||||
func (r *RuntimeRecorder) Refresh(ctx context.Context) (err error) {
|
||||
records := r.resetRecords()
|
||||
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agdtest"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/billstat"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/geoip"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/testutil"
|
||||
"github.com/stretchr/testify/assert"
|
||||
@ -22,8 +23,8 @@ type sig = struct{}
|
||||
const (
|
||||
devID = "dev1234"
|
||||
proto = agd.ProtoDoH
|
||||
clientCtry = agd.CountryAD
|
||||
clientASN agd.ASN = 42
|
||||
clientCtry = geoip.CountryAD
|
||||
clientASN geoip.ASN = 42
|
||||
)
|
||||
|
||||
func TestRuntimeRecorder_success(t *testing.T) {
|
||||
|
@ -6,7 +6,7 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// Common timeout for tests
|
||||
// testTimeout is a common timeout for tests.
|
||||
const testTimeout = 1 * time.Second
|
||||
|
||||
// Common addresses for tests.
|
||||
@ -24,5 +24,4 @@ var (
|
||||
// Common subnets for tests.
|
||||
var (
|
||||
testSubnetIPv4 = netip.MustParsePrefix("1.2.3.0/24")
|
||||
testSubnetIPv6 = netip.MustParsePrefix("1234:5678::/64")
|
||||
)
|
||||
|
@ -3,6 +3,7 @@ package bindtodevice_test
|
||||
import (
|
||||
"net/netip"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/bindtodevice"
|
||||
"github.com/AdguardTeam/golibs/testutil"
|
||||
@ -12,6 +13,9 @@ func TestMain(m *testing.M) {
|
||||
testutil.DiscardLogOutput(m)
|
||||
}
|
||||
|
||||
// testTimeout is a common timeout for tests.
|
||||
const testTimeout = 1 * time.Second
|
||||
|
||||
// Common interface listener IDs for tests
|
||||
const (
|
||||
testID1 bindtodevice.ID = "id1"
|
||||
|
@ -5,8 +5,7 @@ package bindtodevice
|
||||
import (
|
||||
"fmt"
|
||||
"net/netip"
|
||||
|
||||
"golang.org/x/exp/slices"
|
||||
"slices"
|
||||
)
|
||||
|
||||
// connIndex is the data structure that contains the channel listeners and
|
||||
|
@ -4,10 +4,10 @@ package bindtodevice
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
"slices"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
func TestSubnetCompare(t *testing.T) {
|
||||
|
@ -8,24 +8,24 @@ import (
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agdsync"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/metrics"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/optlog"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/AdguardTeam/golibs/netutil"
|
||||
"github.com/AdguardTeam/golibs/syncutil"
|
||||
)
|
||||
|
||||
// interfaceListener contains information about a single interface listener.
|
||||
type interfaceListener struct {
|
||||
conns *connIndex
|
||||
listenConf *net.ListenConfig
|
||||
bodyPool *agdsync.TypedPool[[]byte]
|
||||
oobPool *agdsync.TypedPool[[]byte]
|
||||
bodyPool *syncutil.Pool[[]byte]
|
||||
oobPool *syncutil.Pool[[]byte]
|
||||
writeRequests chan *packetConnWriteReq
|
||||
done chan unit
|
||||
errColl agd.ErrorCollector
|
||||
errColl errcoll.Interface
|
||||
ifaceName string
|
||||
port uint16
|
||||
}
|
||||
@ -37,7 +37,7 @@ func (l *interfaceListener) listenTCP(errCh chan<- error) {
|
||||
defer log.OnPanic("interfaceListener.listenTCP")
|
||||
|
||||
ctx := context.Background()
|
||||
addrStr := netutil.JoinHostPort("0.0.0.0", int(l.port))
|
||||
addrStr := netutil.JoinHostPort("0.0.0.0", l.port)
|
||||
tcpListener, err := l.listenConf.Listen(ctx, "tcp", addrStr)
|
||||
|
||||
errCh <- err
|
||||
@ -62,7 +62,7 @@ func (l *interfaceListener) listenTCP(errCh chan<- error) {
|
||||
var conn net.Conn
|
||||
conn, err = tcpListener.Accept()
|
||||
if err != nil {
|
||||
agd.Collectf(ctx, l.errColl, "%s: accepting: %w", logPrefix, err)
|
||||
errcoll.Collectf(ctx, l.errColl, "%s: accepting: %w", logPrefix, err)
|
||||
|
||||
continue
|
||||
}
|
||||
@ -78,7 +78,7 @@ func (l *interfaceListener) processConn(conn net.Conn, logPrefix string) {
|
||||
raddr := conn.RemoteAddr()
|
||||
if lsnr := l.conns.listener(laddr.Addr()); lsnr != nil {
|
||||
if !lsnr.send(conn) {
|
||||
log.Info("%s: from raddr %s: channel for laddr %s is closed", logPrefix, raddr, laddr)
|
||||
optlog.Debug3("%s: from raddr %s: channel for laddr %s is closed", logPrefix, raddr, laddr)
|
||||
}
|
||||
|
||||
return
|
||||
@ -90,7 +90,7 @@ func (l *interfaceListener) processConn(conn net.Conn, logPrefix string) {
|
||||
|
||||
err := conn.Close()
|
||||
if err != nil {
|
||||
log.Debug("%s: from raddr %s: closing: %s", logPrefix, raddr, err)
|
||||
optlog.Debug3("%s: from raddr %s: closing: %s", logPrefix, raddr, err)
|
||||
}
|
||||
}
|
||||
|
||||
@ -101,7 +101,7 @@ func (l *interfaceListener) listenUDP(errCh chan<- error) {
|
||||
defer log.OnPanic("interfaceListener.listenUDP")
|
||||
|
||||
ctx := context.Background()
|
||||
addrStr := netutil.JoinHostPort("0.0.0.0", int(l.port))
|
||||
addrStr := netutil.JoinHostPort("0.0.0.0", l.port)
|
||||
packetConn, err := l.listenConf.ListenPacket(ctx, "udp", addrStr)
|
||||
if err != nil {
|
||||
errCh <- err
|
||||
@ -131,7 +131,7 @@ func (l *interfaceListener) listenUDP(errCh chan<- error) {
|
||||
|
||||
err = l.readUDP(udpConn, logPrefix)
|
||||
if err != nil {
|
||||
agd.Collectf(ctx, l.errColl, "%s: reading session: %w", logPrefix, err)
|
||||
errcoll.Collectf(ctx, l.errColl, "%s: reading session: %w", logPrefix, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -179,7 +179,7 @@ func (l *interfaceListener) readUDP(c *net.UDPConn, logPrefix string) (err error
|
||||
}
|
||||
|
||||
if !chanPacketConn.send(sess) {
|
||||
log.Info("%s: channel for laddr %s is closed", logPrefix, laddr)
|
||||
optlog.Debug2("%s: channel for laddr %s is closed", logPrefix, laddr)
|
||||
}
|
||||
|
||||
return nil
|
||||
@ -194,7 +194,7 @@ func (l *interfaceListener) writeUDP(c *net.UDPConn) {
|
||||
var req *packetConnWriteReq
|
||||
select {
|
||||
case <-l.done:
|
||||
log.Info("%s: done", logPrefix)
|
||||
optlog.Debug1("%s: done", logPrefix)
|
||||
|
||||
return
|
||||
case req = <-l.writeRequests:
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"context"
|
||||
"net"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agdnet"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/netext"
|
||||
)
|
||||
|
||||
@ -19,7 +20,7 @@ import (
|
||||
type ListenConfig struct {
|
||||
packetConn *chanPacketConn
|
||||
listener *chanListener
|
||||
addr string
|
||||
addr *agdnet.PrefixNetAddr
|
||||
}
|
||||
|
||||
// type check
|
||||
@ -44,8 +45,7 @@ func (lc *ListenConfig) ListenPacket(
|
||||
return lc.packetConn, nil
|
||||
}
|
||||
|
||||
// Addr returns the address on which lc accepts connections. See
|
||||
// [agdnet.FormatPrefixAddr] for the format.
|
||||
func (lc *ListenConfig) Addr() (addr string) {
|
||||
// Addr returns the address on which lc accepts connections. addr.Net is empty.
|
||||
func (lc *ListenConfig) Addr() (addr *agdnet.PrefixNetAddr) {
|
||||
return lc.addr
|
||||
}
|
||||
|
@ -14,7 +14,11 @@ import (
|
||||
func TestListenConfig(t *testing.T) {
|
||||
pc := newChanPacketConn(nil, testSubnetIPv4, nil, testLAddr)
|
||||
lsnr := newChanListener(nil, testSubnetIPv4, testLAddr)
|
||||
addr := agdnet.FormatPrefixAddr(testSubnetIPv4, 1234)
|
||||
addr := &agdnet.PrefixNetAddr{
|
||||
Prefix: testSubnetIPv4,
|
||||
Net: "",
|
||||
Port: 1234,
|
||||
}
|
||||
c := &ListenConfig{
|
||||
packetConn: pc,
|
||||
listener: lsnr,
|
||||
|
@ -4,9 +4,12 @@ package bindtodevice
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agdnet"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/netext"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
)
|
||||
|
||||
// ListenConfig is a [netext.ListenConfig] implementation that uses the
|
||||
@ -31,7 +34,10 @@ func (lc *ListenConfig) Listen(
|
||||
network string,
|
||||
address string,
|
||||
) (l net.Listener, err error) {
|
||||
return nil, errUnsupported
|
||||
return nil, fmt.Errorf(
|
||||
"bindtodevice: listen: %w; only supported on linux",
|
||||
errors.ErrUnsupported,
|
||||
)
|
||||
}
|
||||
|
||||
// ListenPacket implements the [netext.ListenConfig] interface for
|
||||
@ -43,13 +49,15 @@ func (lc *ListenConfig) ListenPacket(
|
||||
network string,
|
||||
address string,
|
||||
) (c net.PacketConn, err error) {
|
||||
return nil, errUnsupported
|
||||
return nil, fmt.Errorf(
|
||||
"bindtodevice: listenpacket: %w; only supported on linux",
|
||||
errors.ErrUnsupported,
|
||||
)
|
||||
}
|
||||
|
||||
// Addr returns the address on which lc accepts connections. See
|
||||
// [agdnet.FormatPrefixAddr] for the format.
|
||||
// Addr returns the address on which lc accepts connections.
|
||||
//
|
||||
// It is only supported on Linux.
|
||||
func (lc *ListenConfig) Addr() (addr string) {
|
||||
return ""
|
||||
func (lc *ListenConfig) Addr() (addr *agdnet.PrefixNetAddr) {
|
||||
return nil
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
package bindtodevice
|
||||
|
||||
import "github.com/AdguardTeam/AdGuardDNS/internal/agd"
|
||||
import "github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
|
||||
|
||||
// ManagerConfig is the configuration structure for [NewManager]. All fields
|
||||
// must be set.
|
||||
@ -11,7 +11,7 @@ type ManagerConfig struct {
|
||||
|
||||
// ErrColl is the error collector that is used to collect non-critical
|
||||
// errors.
|
||||
ErrColl agd.ErrorCollector
|
||||
ErrColl errcoll.Interface
|
||||
|
||||
// ChannelBufferSize is the size of the buffers of the channels used to
|
||||
// dispatch TCP connections and UDP sessions.
|
||||
|
@ -9,13 +9,14 @@ import (
|
||||
"net/netip"
|
||||
"sync"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agdnet"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agdsync"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agdservice"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/netext"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/AdguardTeam/golibs/mapsutil"
|
||||
"github.com/AdguardTeam/golibs/syncutil"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
@ -24,7 +25,7 @@ type Manager struct {
|
||||
interfaces InterfaceStorage
|
||||
closeOnce *sync.Once
|
||||
ifaceListeners map[ID]*interfaceListener
|
||||
errColl agd.ErrorCollector
|
||||
errColl errcoll.Interface
|
||||
done chan unit
|
||||
chanBufSize int
|
||||
}
|
||||
@ -110,16 +111,8 @@ func (m *Manager) newInterfaceListener(
|
||||
return &interfaceListener{
|
||||
conns: &connIndex{},
|
||||
listenConf: newListenConfig(ifaceName, ctrlConf),
|
||||
bodyPool: agdsync.NewTypedPool(func() (v *[]byte) {
|
||||
b := make([]byte, bodySize)
|
||||
|
||||
return &b
|
||||
}),
|
||||
oobPool: agdsync.NewTypedPool(func() (v *[]byte) {
|
||||
b := make([]byte, netext.IPDstOOBSize)
|
||||
|
||||
return &b
|
||||
}),
|
||||
bodyPool: syncutil.NewSlicePool[byte](bodySize),
|
||||
oobPool: syncutil.NewSlicePool[byte](netext.IPDstOOBSize),
|
||||
writeRequests: make(chan *packetConnWriteReq, m.chanBufSize),
|
||||
done: m.done,
|
||||
errColl: m.errColl,
|
||||
@ -154,10 +147,10 @@ func (m *Manager) ListenConfig(id ID, subnet netip.Prefix) (c *ListenConfig, err
|
||||
}
|
||||
|
||||
lsnrCh := make(chan net.Conn, m.chanBufSize)
|
||||
lsnr := newChanListener(lsnrCh, subnet, &prefixNetAddr{
|
||||
prefix: subnet,
|
||||
network: "tcp",
|
||||
port: l.port,
|
||||
lsnr := newChanListener(lsnrCh, subnet, &agdnet.PrefixNetAddr{
|
||||
Prefix: subnet,
|
||||
Net: "tcp",
|
||||
Port: l.port,
|
||||
})
|
||||
|
||||
err = l.conns.addListener(lsnr)
|
||||
@ -166,10 +159,10 @@ func (m *Manager) ListenConfig(id ID, subnet netip.Prefix) (c *ListenConfig, err
|
||||
}
|
||||
|
||||
sessCh := make(chan *packetSession, m.chanBufSize)
|
||||
pConn := newChanPacketConn(sessCh, subnet, l.writeRequests, &prefixNetAddr{
|
||||
prefix: subnet,
|
||||
network: "udp",
|
||||
port: l.port,
|
||||
pConn := newChanPacketConn(sessCh, subnet, l.writeRequests, &agdnet.PrefixNetAddr{
|
||||
Prefix: subnet,
|
||||
Net: "udp",
|
||||
Port: l.port,
|
||||
})
|
||||
|
||||
err = l.conns.addPacketConn(pConn)
|
||||
@ -182,7 +175,11 @@ func (m *Manager) ListenConfig(id ID, subnet netip.Prefix) (c *ListenConfig, err
|
||||
return &ListenConfig{
|
||||
packetConn: pConn,
|
||||
listener: lsnr,
|
||||
addr: agdnet.FormatPrefixAddr(subnet, l.port),
|
||||
addr: &agdnet.PrefixNetAddr{
|
||||
Prefix: subnet,
|
||||
Net: "",
|
||||
Port: l.port,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -214,13 +211,15 @@ func (m *Manager) validateIfaceSubnet(ifaceName string, subnet netip.Prefix) (er
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ agd.Service = (*Manager)(nil)
|
||||
var _ agdservice.Interface = (*Manager)(nil)
|
||||
|
||||
// Start implements the [agd.Service] interface for *Manager. If m is nil,
|
||||
// Start returns nil, since this feature is optional.
|
||||
// Start implements the [agdservice.Interface] interface for *Manager. If m is
|
||||
// nil, Start returns nil, since this feature is optional.
|
||||
//
|
||||
// TODO(a.garipov): Consider an interface solution.
|
||||
func (m *Manager) Start() (err error) {
|
||||
//
|
||||
// TODO(a.garipov): Use the context for cancelation.
|
||||
func (m *Manager) Start(_ context.Context) (err error) {
|
||||
if m == nil {
|
||||
return nil
|
||||
}
|
||||
@ -250,8 +249,8 @@ func (m *Manager) Start() (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Shutdown implements the [agd.Service] interface for *Manager. If m is nil,
|
||||
// Shutdown returns nil, since this feature is optional.
|
||||
// Shutdown implements the [agdservice.Interface] interface for *Manager. If m
|
||||
// is nil, Shutdown returns nil, since this feature is optional.
|
||||
//
|
||||
// TODO(a.garipov): Consider an interface solution.
|
||||
//
|
||||
|
@ -233,10 +233,10 @@ func TestManager(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, lc)
|
||||
|
||||
err = m.Start()
|
||||
err = m.Start(agdtest.ContextWithTimeout(t, testTimeout))
|
||||
require.NoError(t, err)
|
||||
testutil.CleanupAndRequireSuccess(t, func() (err error) {
|
||||
return m.Shutdown(context.Background())
|
||||
return m.Shutdown(agdtest.ContextWithTimeout(t, testTimeout))
|
||||
})
|
||||
|
||||
t.Run("tcp", func(t *testing.T) {
|
||||
|
@ -4,9 +4,10 @@ package bindtodevice
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/netip"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agdservice"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
)
|
||||
|
||||
@ -22,17 +23,11 @@ func NewManager(c *ManagerConfig) (m *Manager) {
|
||||
return &Manager{}
|
||||
}
|
||||
|
||||
// errUnsupported is returned from all [Manager] methods on OSs other than
|
||||
// Linux.
|
||||
//
|
||||
// TODO(a.garipov): Consider using [errors.ErrUnsupported] in Go 1.21.
|
||||
const errUnsupported errors.Error = "bindtodevice is only supported on linux"
|
||||
|
||||
// Add creates a new interface-listener record in m.
|
||||
//
|
||||
// It is only supported on Linux.
|
||||
func (m *Manager) Add(id ID, ifaceName string, port uint16, cc *ControlConfig) (err error) {
|
||||
return errUnsupported
|
||||
return fmt.Errorf("bindtodevice: add: %w; only supported on linux", errors.ErrUnsupported)
|
||||
}
|
||||
|
||||
// ListenConfig returns a new *ListenConfig that receives connections from the
|
||||
@ -41,26 +36,29 @@ func (m *Manager) Add(id ID, ifaceName string, port uint16, cc *ControlConfig) (
|
||||
//
|
||||
// It is only supported on Linux.
|
||||
func (m *Manager) ListenConfig(id ID, subnet netip.Prefix) (c *ListenConfig, err error) {
|
||||
return nil, errUnsupported
|
||||
return nil, fmt.Errorf(
|
||||
"bindtodevice: listenconfig: %w; only supported on linux",
|
||||
errors.ErrUnsupported,
|
||||
)
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ agd.Service = (*Manager)(nil)
|
||||
var _ agdservice.Interface = (*Manager)(nil)
|
||||
|
||||
// Start implements the [agd.Service] interface for *Manager. If m is nil,
|
||||
// Start returns nil, since this feature is optional.
|
||||
// Start implements the [agdservice.Interface] interface for *Manager. If m is
|
||||
// nil, Start returns nil, since this feature is optional.
|
||||
//
|
||||
// It is only supported on Linux.
|
||||
func (m *Manager) Start() (err error) {
|
||||
func (m *Manager) Start(_ context.Context) (err error) {
|
||||
if m == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return errUnsupported
|
||||
return fmt.Errorf("bindtodevice: starting: %w; only supported on linux", errors.ErrUnsupported)
|
||||
}
|
||||
|
||||
// Shutdown implements the [agd.Service] interface for *Manager. If m is nil,
|
||||
// Shutdown returns nil, since this feature is optional.
|
||||
// Shutdown implements the [agdservice.Interface] interface for *Manager. If m
|
||||
// is nil, Shutdown returns nil, since this feature is optional.
|
||||
//
|
||||
// It is only supported on Linux.
|
||||
func (m *Manager) Shutdown(_ context.Context) (err error) {
|
||||
@ -68,5 +66,8 @@ func (m *Manager) Shutdown(_ context.Context) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return errUnsupported
|
||||
return fmt.Errorf(
|
||||
"bindtodevice: shutting down: %w; only supported on linux",
|
||||
errors.ErrUnsupported,
|
||||
)
|
||||
}
|
||||
|
@ -1,33 +0,0 @@
|
||||
//go:build linux
|
||||
|
||||
package bindtodevice
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/netip"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agdnet"
|
||||
)
|
||||
|
||||
// prefixNetAddr is a wrapper around netip.Prefix that makes it a [net.Addr].
|
||||
//
|
||||
// TODO(a.garipov): Support port 0, which will probably require atomic
|
||||
// operations and assistance in [Manager.Start].
|
||||
type prefixNetAddr struct {
|
||||
prefix netip.Prefix
|
||||
network string
|
||||
port uint16
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ net.Addr = (*prefixNetAddr)(nil)
|
||||
|
||||
// String implements the [net.Addr] interface for *prefixNetAddr.
|
||||
//
|
||||
// See [agdnet.FormatPrefixAddr] for the format.
|
||||
func (addr *prefixNetAddr) String() (n string) {
|
||||
return agdnet.FormatPrefixAddr(addr.prefix, addr.port)
|
||||
}
|
||||
|
||||
// Network implements the [net.Addr] interface for *prefixNetAddr.
|
||||
func (addr *prefixNetAddr) Network() (n string) { return addr.network }
|
@ -10,6 +10,7 @@ import (
|
||||
"net"
|
||||
"net/netip"
|
||||
"os"
|
||||
"slices"
|
||||
"strings"
|
||||
"syscall"
|
||||
"testing"
|
||||
@ -21,7 +22,6 @@ import (
|
||||
"github.com/miekg/dns"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/exp/slices"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
@ -105,8 +105,8 @@ func TestListenControl(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
// SubtestListenControlTCP is a shared subtest that uses lc to dial a listener and
|
||||
// perform two-way communication using the resulting connection.
|
||||
// SubtestListenControlTCP is a shared subtest that uses lc to dial a listener
|
||||
// and perform two-way communication using the resulting connection.
|
||||
func SubtestListenControlTCP(
|
||||
t *testing.T,
|
||||
lc netext.ListenConfig,
|
||||
@ -120,7 +120,7 @@ func SubtestListenControlTCP(
|
||||
require.NoError(t, err)
|
||||
testutil.CleanupAndRequireSuccess(t, lsnr.Close)
|
||||
|
||||
// Make sure we can work with [prefixNetAddr] as well.
|
||||
// Make sure we can work with [agdnet.PrefixNetAddr] as well.
|
||||
addrStr, _, _ := strings.Cut(lsnr.Addr().String(), "/")
|
||||
addr, err := netip.ParseAddrPort(addrStr)
|
||||
require.NoError(t, err)
|
||||
@ -201,7 +201,7 @@ func SubtestListenControlUDP(
|
||||
require.NoError(t, err)
|
||||
testutil.CleanupAndRequireSuccess(t, packetConn.Close)
|
||||
|
||||
// Make sure we can work with [prefixNetAddr] as well.
|
||||
// Make sure we can work with [agdnet.PrefixNetAddr] as well.
|
||||
addrStr, _, _ := strings.Cut(packetConn.LocalAddr().String(), "/")
|
||||
addr, err := netip.ParseAddrPort(addrStr)
|
||||
require.NoError(t, err)
|
||||
@ -501,8 +501,7 @@ func BenchmarkReadPacketSession(b *testing.B) {
|
||||
Type: unix.IP_ORIGDSTADDR,
|
||||
}
|
||||
|
||||
// TODO(a.garipov): Use binary.NativeEndian in Go 1.21 here and below.
|
||||
err := binary.Write(oobBuf, binary.LittleEndian, ctrlMsgHdr)
|
||||
err := binary.Write(oobBuf, binary.NativeEndian, ctrlMsgHdr)
|
||||
require.NoError(b, err)
|
||||
|
||||
pktInfo := unix.Inet4Pktinfo{
|
||||
@ -510,7 +509,7 @@ func BenchmarkReadPacketSession(b *testing.B) {
|
||||
Addr: *(*[4]byte)(testRAddr.IP),
|
||||
}
|
||||
|
||||
err = binary.Write(oobBuf, binary.LittleEndian, pktInfo)
|
||||
err = binary.Write(oobBuf, binary.NativeEndian, pktInfo)
|
||||
require.NoError(b, err)
|
||||
|
||||
oobData := oobBuf.Bytes()
|
||||
|
@ -4,12 +4,11 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/backend"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agdservice"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/backendpb"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/billstat"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/profiledb"
|
||||
"github.com/AdguardTeam/golibs/netutil"
|
||||
"github.com/AdguardTeam/golibs/timeutil"
|
||||
@ -34,6 +33,10 @@ type backendConfig struct {
|
||||
// synchronization.
|
||||
FullRefreshIvl timeutil.Duration `yaml:"full_refresh_interval"`
|
||||
|
||||
// FullRefreshRetryIvl is the interval between two retries of full
|
||||
// synchronizations.
|
||||
FullRefreshRetryIvl timeutil.Duration `yaml:"full_refresh_retry_interval"`
|
||||
|
||||
// BillStatIvl defines how often AdGuard DNS sends the billing statistics to
|
||||
// the backend.
|
||||
BillStatIvl timeutil.Duration `yaml:"bill_stat_interval"`
|
||||
@ -50,6 +53,8 @@ func (c *backendConfig) validate() (err error) {
|
||||
return newMustBePositiveError("refresh_interval", c.RefreshIvl)
|
||||
case c.FullRefreshIvl.Duration <= 0:
|
||||
return newMustBePositiveError("full_refresh_interval", c.FullRefreshIvl)
|
||||
case c.FullRefreshRetryIvl.Duration <= 0:
|
||||
return newMustBePositiveError("full_refresh_retry_interval", c.FullRefreshRetryIvl)
|
||||
case c.BillStatIvl.Duration <= 0:
|
||||
return newMustBePositiveError("bill_stat_interval", c.BillStatIvl)
|
||||
default:
|
||||
@ -64,7 +69,7 @@ func setupBackend(
|
||||
conf *backendConfig,
|
||||
envs *environments,
|
||||
sigHdlr signalHandler,
|
||||
errColl agd.ErrorCollector,
|
||||
errColl errcoll.Interface,
|
||||
) (profDB *profiledb.Default, rec *billstat.RuntimeRecorder, err error) {
|
||||
rec, err = setupBillStat(conf, envs, sigHdlr, errColl)
|
||||
if err != nil {
|
||||
@ -87,7 +92,7 @@ func setupBillStat(
|
||||
conf *backendConfig,
|
||||
envs *environments,
|
||||
sigHdlr signalHandler,
|
||||
errColl agd.ErrorCollector,
|
||||
errColl errcoll.Interface,
|
||||
) (rec *billstat.RuntimeRecorder, err error) {
|
||||
apiURL := netutil.CloneURL(&envs.BillStatURL.URL)
|
||||
billStatUploader, err := setupBillStatUploader(apiURL, errColl)
|
||||
@ -102,7 +107,7 @@ func setupBillStat(
|
||||
refrIvl := conf.RefreshIvl.Duration
|
||||
timeout := conf.Timeout.Duration
|
||||
|
||||
billStatRefr := agd.NewRefreshWorker(&agd.RefreshWorkerConfig{
|
||||
billStatRefr := agdservice.NewRefreshWorker(&agdservice.RefreshWorkerConfig{
|
||||
Context: func() (ctx context.Context, cancel context.CancelFunc) {
|
||||
return context.WithTimeout(context.Background(), timeout)
|
||||
},
|
||||
@ -112,8 +117,9 @@ func setupBillStat(
|
||||
Interval: refrIvl,
|
||||
RefreshOnShutdown: true,
|
||||
RoutineLogsAreDebug: true,
|
||||
RandomizeStart: false,
|
||||
})
|
||||
err = billStatRefr.Start()
|
||||
err = billStatRefr.Start(context.Background())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("starting bill stat recorder refresher: %w", err)
|
||||
}
|
||||
@ -129,7 +135,7 @@ func setupProfDB(
|
||||
conf *backendConfig,
|
||||
envs *environments,
|
||||
sigHdlr signalHandler,
|
||||
errColl agd.ErrorCollector,
|
||||
errColl errcoll.Interface,
|
||||
) (profDB *profiledb.Default, err error) {
|
||||
apiURL := netutil.CloneURL(&envs.ProfilesURL.URL)
|
||||
profStrg, err := setupProfStorage(apiURL, errColl)
|
||||
@ -137,15 +143,21 @@ func setupProfDB(
|
||||
return nil, fmt.Errorf("creating profile storage: %w", err)
|
||||
}
|
||||
|
||||
profDB, err = profiledb.New(profStrg, conf.FullRefreshIvl.Duration, envs.ProfilesCachePath)
|
||||
timeout := conf.Timeout.Duration
|
||||
profDB, err = profiledb.New(&profiledb.Config{
|
||||
Storage: profStrg,
|
||||
FullSyncIvl: conf.FullRefreshIvl.Duration,
|
||||
FullSyncRetryIvl: conf.FullRefreshRetryIvl.Duration,
|
||||
InitialTimeout: timeout,
|
||||
CacheFilePath: envs.ProfilesCachePath,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating default profile database: %w", err)
|
||||
}
|
||||
|
||||
refrIvl := conf.RefreshIvl.Duration
|
||||
timeout := conf.Timeout.Duration
|
||||
|
||||
profDBRefr := agd.NewRefreshWorker(&agd.RefreshWorkerConfig{
|
||||
profDBRefr := agdservice.NewRefreshWorker(&agdservice.RefreshWorkerConfig{
|
||||
Context: func() (ctx context.Context, cancel context.CancelFunc) {
|
||||
return context.WithTimeout(context.Background(), timeout)
|
||||
},
|
||||
@ -155,8 +167,9 @@ func setupProfDB(
|
||||
Interval: refrIvl,
|
||||
RefreshOnShutdown: false,
|
||||
RoutineLogsAreDebug: true,
|
||||
RandomizeStart: true,
|
||||
})
|
||||
err = profDBRefr.Start()
|
||||
err = profDBRefr.Start(context.Background())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("starting default profile database refresher: %w", err)
|
||||
}
|
||||
@ -168,8 +181,6 @@ func setupProfDB(
|
||||
|
||||
// Backend API URL schemes.
|
||||
const (
|
||||
schemeHTTP = "http"
|
||||
schemeHTTPS = "https"
|
||||
schemeGRPC = "grpc"
|
||||
schemeGRPCS = "grpcs"
|
||||
)
|
||||
@ -178,42 +189,32 @@ const (
|
||||
// provided API URL.
|
||||
func setupProfStorage(
|
||||
apiURL *url.URL,
|
||||
errColl agd.ErrorCollector,
|
||||
errColl errcoll.Interface,
|
||||
) (s profiledb.Storage, err error) {
|
||||
switch apiURL.Scheme {
|
||||
case schemeGRPC, schemeGRPCS:
|
||||
scheme := apiURL.Scheme
|
||||
if scheme == schemeGRPC || scheme == schemeGRPCS {
|
||||
return backendpb.NewProfileStorage(&backendpb.ProfileStorageConfig{
|
||||
Endpoint: apiURL,
|
||||
ErrColl: errColl,
|
||||
})
|
||||
case schemeHTTP, schemeHTTPS:
|
||||
return backend.NewProfileStorage(&backend.ProfileStorageConfig{
|
||||
BaseEndpoint: apiURL,
|
||||
Now: time.Now,
|
||||
ErrColl: errColl,
|
||||
}), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid backend api url: %s", apiURL)
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("invalid backend api url: %s", apiURL)
|
||||
}
|
||||
|
||||
// setupBillStatUploader creates and returns a billstat uploader depending on
|
||||
// the provided API URL.
|
||||
func setupBillStatUploader(
|
||||
apiURL *url.URL,
|
||||
errColl agd.ErrorCollector,
|
||||
errColl errcoll.Interface,
|
||||
) (s billstat.Uploader, err error) {
|
||||
switch apiURL.Scheme {
|
||||
case schemeGRPC, schemeGRPCS:
|
||||
scheme := apiURL.Scheme
|
||||
if scheme == schemeGRPC || scheme == schemeGRPCS {
|
||||
return backendpb.NewBillStat(&backendpb.BillStatConfig{
|
||||
ErrColl: errColl,
|
||||
Endpoint: apiURL,
|
||||
})
|
||||
case schemeHTTP, schemeHTTPS:
|
||||
return backend.NewBillStat(&backend.BillStatConfig{
|
||||
BaseEndpoint: apiURL,
|
||||
}), nil
|
||||
default:
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("invalid backend api url: %s", apiURL)
|
||||
}
|
||||
}
|
||||
|
@ -6,9 +6,9 @@ import (
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/dnscheck"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/netutil"
|
||||
"github.com/AdguardTeam/golibs/timeutil"
|
||||
@ -44,7 +44,7 @@ type checkConfig struct {
|
||||
func (c *checkConfig) toInternal(
|
||||
envs *environments,
|
||||
messages *dnsmsg.Constructor,
|
||||
errColl agd.ErrorCollector,
|
||||
errColl errcoll.Interface,
|
||||
) (conf *dnscheck.ConsulConfig) {
|
||||
var kvURL, sessURL *url.URL
|
||||
if envs.ConsulDNSCheckKVURL != nil && envs.ConsulDNSCheckSessionURL != nil {
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/access"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agdnet"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agdservice"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/debugsvc"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/dnscheck"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
|
||||
@ -78,7 +79,7 @@ func Main() {
|
||||
//
|
||||
// See AGDNS-884.
|
||||
|
||||
geoIP, geoIPRefr := &geoip.File{}, &agd.RefreshWorker{}
|
||||
geoIP, geoIPRefr := &geoip.File{}, &agdservice.RefreshWorker{}
|
||||
geoIPErrCh := make(chan error, 1)
|
||||
|
||||
go setupGeoIP(geoIP, geoIPRefr, geoIPErrCh, c.GeoIP, envs, errColl)
|
||||
@ -93,11 +94,13 @@ func Main() {
|
||||
|
||||
// TODO(ameshkov): Consider making a separated max_size config for
|
||||
// safe-browsing and adult-blocking filters.
|
||||
maxFilterSize := int64(c.Filters.MaxSize.Bytes())
|
||||
maxFilterSize := c.Filters.MaxSize.Bytes()
|
||||
|
||||
cloner := dnsmsg.NewCloner(metrics.ClonerStat{})
|
||||
safeBrowsingHashes, safeBrowsingFilter, err := setupHashPrefixFilter(
|
||||
c.SafeBrowsing,
|
||||
filteringResolver,
|
||||
cloner,
|
||||
agd.FilterListIDSafeBrowsing,
|
||||
envs.SafeBrowsingURL,
|
||||
envs.FilterCachePath,
|
||||
@ -110,6 +113,7 @@ func Main() {
|
||||
adultBlockingHashes, adultBlockingFilter, err := setupHashPrefixFilter(
|
||||
c.AdultBlocking,
|
||||
filteringResolver,
|
||||
cloner,
|
||||
agd.FilterListIDAdultBlocking,
|
||||
envs.AdultBlockingURL,
|
||||
envs.FilterCachePath,
|
||||
@ -123,6 +127,7 @@ func Main() {
|
||||
// Reuse general safe browsing filter configuration.
|
||||
c.SafeBrowsing,
|
||||
filteringResolver,
|
||||
cloner,
|
||||
agd.FilterListIDNewRegDomains,
|
||||
envs.NewRegDomainsURL,
|
||||
envs.FilterCachePath,
|
||||
@ -137,6 +142,7 @@ func Main() {
|
||||
fltStrgConf := c.Filters.toInternal(
|
||||
errColl,
|
||||
filteringResolver,
|
||||
cloner,
|
||||
envs,
|
||||
safeBrowsingFilter,
|
||||
adultBlockingFilter,
|
||||
@ -150,30 +156,37 @@ func Main() {
|
||||
fltGroups, err := c.FilteringGroups.toInternal(fltStrg)
|
||||
check(err)
|
||||
|
||||
// Network interface listener
|
||||
// Access
|
||||
|
||||
accessGlobal, err := access.NewGlobal(
|
||||
c.Access.BlockedQuestionDomains,
|
||||
c.Access.BlockedClientSubnets,
|
||||
)
|
||||
check(err)
|
||||
|
||||
// Network interface listener and server groups
|
||||
|
||||
messages := dnsmsg.NewConstructor(
|
||||
cloner,
|
||||
&dnsmsg.BlockingModeNullIP{},
|
||||
c.Filters.ResponseTTL.Duration,
|
||||
)
|
||||
|
||||
btdCtrlConf, ctrlConf := c.Network.toInternal()
|
||||
|
||||
btdMgr, err := c.InterfaceListeners.toInternal(errColl, btdCtrlConf)
|
||||
check(err)
|
||||
|
||||
err = btdMgr.Start()
|
||||
srvGrps, err := c.ServerGroups.toInternal(messages, btdMgr, fltGroups, c.RateLimit, c.DNS)
|
||||
check(err)
|
||||
|
||||
// Start the bind-to-device manager here, now that no further calls to
|
||||
// btdMgr.ListenConfig are required.
|
||||
err = btdMgr.Start(context.Background())
|
||||
check(err)
|
||||
|
||||
sigHdlr.add(btdMgr)
|
||||
|
||||
// access
|
||||
|
||||
accessManager, err := access.New(c.Access.BlockedQuestionDomains, c.Access.BlockedClientSubnets)
|
||||
check(err)
|
||||
|
||||
// Server groups
|
||||
|
||||
messages := dnsmsg.NewConstructor(&dnsmsg.BlockingModeNullIP{}, c.Filters.ResponseTTL.Duration)
|
||||
|
||||
srvGrps, err := c.ServerGroups.toInternal(messages, btdMgr, fltGroups)
|
||||
check(err)
|
||||
|
||||
// TLS keys logging
|
||||
|
||||
if envs.SSLKeyLogFile != "" {
|
||||
@ -227,14 +240,13 @@ func Main() {
|
||||
webSvc := websvc.New(webConf)
|
||||
// The web service is considered critical, so its Start method panics
|
||||
// instead of returning an error.
|
||||
_ = webSvc.Start()
|
||||
_ = webSvc.Start(context.Background())
|
||||
|
||||
sigHdlr.add(webSvc)
|
||||
|
||||
// DNS service
|
||||
|
||||
fwdConf, err := c.Upstream.toInternal()
|
||||
check(err)
|
||||
fwdConf := c.Upstream.toInternal()
|
||||
|
||||
handler := forward.NewHandler(fwdConf)
|
||||
|
||||
@ -246,8 +258,11 @@ func Main() {
|
||||
}
|
||||
|
||||
dnsConf := &dnssvc.Config{
|
||||
AccessManager: accessManager,
|
||||
Messages: messages,
|
||||
Cloner: cloner,
|
||||
ControlConf: ctrlConf,
|
||||
ConnLimiter: connLimiter,
|
||||
AccessManager: accessGlobal,
|
||||
SafeBrowsing: hashprefix.NewMatcher(hashStorages),
|
||||
BillStat: billStatRec,
|
||||
ProfileDB: profDB,
|
||||
@ -261,9 +276,9 @@ func Main() {
|
||||
QueryLog: c.buildQueryLog(envs),
|
||||
RuleStat: ruleStat,
|
||||
RateLimit: rateLimiter,
|
||||
ConnLimiter: connLimiter,
|
||||
FilteringGroups: fltGroups,
|
||||
ServerGroups: srvGrps,
|
||||
HandleTimeout: c.DNS.HandleTimeout.Duration,
|
||||
CacheSize: c.Cache.Size,
|
||||
ECSCacheSize: c.Cache.ECSSize,
|
||||
CacheMinTTL: c.Cache.TTLOverride.Min.Duration,
|
||||
@ -271,7 +286,6 @@ func Main() {
|
||||
UseECSCache: c.Cache.Type == cacheTypeECS,
|
||||
ResearchMetrics: bool(envs.ResearchMetrics),
|
||||
ResearchLogs: bool(envs.ResearchLogs),
|
||||
ControlConf: ctrlConf,
|
||||
}
|
||||
|
||||
dnsSvc, err := dnssvc.New(dnsConf)
|
||||
@ -283,14 +297,14 @@ func Main() {
|
||||
check(err)
|
||||
|
||||
upstreamHealthcheckUpd := newUpstreamHealthcheck(handler, c.Upstream, errColl)
|
||||
err = upstreamHealthcheckUpd.Start()
|
||||
err = upstreamHealthcheckUpd.Start(context.Background())
|
||||
check(err)
|
||||
|
||||
sigHdlr.add(upstreamHealthcheckUpd)
|
||||
|
||||
// The DNS service is considered critical, so its Start method panics
|
||||
// instead of returning an error.
|
||||
_ = dnsSvc.Start()
|
||||
_ = dnsSvc.Start(context.Background())
|
||||
|
||||
sigHdlr.add(dnsSvc)
|
||||
|
||||
@ -300,7 +314,7 @@ func Main() {
|
||||
|
||||
// The debug HTTP service is considered critical, so its Start method panics
|
||||
// instead of returning an error.
|
||||
_ = debugSvc.Start()
|
||||
_ = debugSvc.Start(context.Background())
|
||||
|
||||
sigHdlr.add(debugSvc)
|
||||
|
||||
@ -320,7 +334,7 @@ func Main() {
|
||||
//
|
||||
// TODO(a.garipov): Consider making into a helper in package agd and using
|
||||
// everywhere.
|
||||
func collectPanics(errColl agd.ErrorCollector) {
|
||||
func collectPanics(errColl errcoll.Interface) {
|
||||
v := recover()
|
||||
if v == nil {
|
||||
return
|
||||
|
@ -33,6 +33,9 @@ type configuration struct {
|
||||
// DNSDB is the configuration of DNSDB buffer.
|
||||
DNSDB *dnsDBConfig `yaml:"dnsdb"`
|
||||
|
||||
// DNSDB is the configuration of common DNS settings.
|
||||
DNS *dnsConfig `yaml:"dns"`
|
||||
|
||||
// Backend is the AdGuard HTTP backend service configuration. See the
|
||||
// environments type for more backend parameters.
|
||||
Backend *backendConfig `yaml:"backend"`
|
||||
@ -123,6 +126,9 @@ func (c *configuration) validate() (err error) {
|
||||
}, {
|
||||
validate: c.DNSDB.validate,
|
||||
name: "dnsdb",
|
||||
}, {
|
||||
validate: c.DNS.validate,
|
||||
name: "dns",
|
||||
}, {
|
||||
validate: c.Backend.validate,
|
||||
name: "backend",
|
||||
|
@ -86,7 +86,7 @@ func connectivityCheck(c *dnssvc.Config, connCheck *connCheckConfig) (err error)
|
||||
func requireIPv6ConnCheck(serverGroups []*agd.ServerGroup) (ok bool) {
|
||||
for _, srvGrp := range serverGroups {
|
||||
for _, s := range srvGrp.Servers {
|
||||
if containsIPv6BindAddress(s.BindData) {
|
||||
if s.HasIPv6() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -94,15 +94,3 @@ func requireIPv6ConnCheck(serverGroups []*agd.ServerGroup) (ok bool) {
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// containsIPv6BindAddress returns true if provided bindData contains valid IPv6
|
||||
// address.
|
||||
func containsIPv6BindAddress(bindData []*agd.ServerBindData) (ok bool) {
|
||||
for _, bData := range bindData {
|
||||
if addr := bData.AddrPort; addr.Addr().Is6() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
@ -1,8 +1,10 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"fmt"
|
||||
"net/netip"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
|
||||
@ -11,7 +13,6 @@ import (
|
||||
"github.com/AdguardTeam/golibs/netutil"
|
||||
"github.com/AdguardTeam/golibs/stringutil"
|
||||
"github.com/miekg/dns"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
// Discovery Of Designated Resolvers (DDR) configuration
|
||||
@ -57,16 +58,8 @@ func ddrRecsToSVCBTmpls(
|
||||
tmpls = appendDDRSVCBTmpls(tmpls, msgs, r, target)
|
||||
}
|
||||
|
||||
// TODO(e.burkov): Use cmp.Compare when updated to go1.21.
|
||||
slices.SortStableFunc(tmpls, func(a, b *dns.SVCB) (res int) {
|
||||
switch x, y := a.Priority, b.Priority; {
|
||||
case x < y:
|
||||
return -1
|
||||
case x > y:
|
||||
return +1
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
return cmp.Compare(a.Priority, b.Priority)
|
||||
})
|
||||
|
||||
return targets, tmpls
|
||||
|
58
internal/cmd/dns.go
Normal file
58
internal/cmd/dns.go
Normal file
@ -0,0 +1,58 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/AdguardTeam/golibs/timeutil"
|
||||
"github.com/c2h5oh/datasize"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
// dnsConfig contains common DNS settings.
|
||||
type dnsConfig struct {
|
||||
// ReadTimeout defines the timeout for any read from a UDP connection or the
|
||||
// first read from a TCP/TLS connection. It currently doesn't affect
|
||||
// DNSCrypt, QUIC, or HTTPS.
|
||||
ReadTimeout timeutil.Duration `yaml:"read_timeout"`
|
||||
|
||||
// TCPIdleTimeout defines the timeout for consecutive reads from a TCP/TLS
|
||||
// connection. It currently doesn't affect DNSCrypt, QUIC, or HTTPS.
|
||||
TCPIdleTimeout timeutil.Duration `yaml:"tcp_idle_timeout"`
|
||||
|
||||
// WriteTimeout defines the timeout for writing to a UDP or TCP/TLS
|
||||
// connection. It currently doesn't affect DNSCrypt, QUIC, or HTTPS.
|
||||
WriteTimeout timeutil.Duration `yaml:"write_timeout"`
|
||||
|
||||
// HandleTimeout defines the timeout for the entire handling of a single
|
||||
// query.
|
||||
HandleTimeout timeutil.Duration `yaml:"handle_timeout"`
|
||||
|
||||
// MaxUDPResponseSize is the maximum size of DNS response over UDP protocol.
|
||||
MaxUDPResponseSize datasize.ByteSize `yaml:"max_udp_response_size"`
|
||||
}
|
||||
|
||||
// validate returns an error if the configuration is invalid.
|
||||
func (c *dnsConfig) validate() (err error) {
|
||||
switch {
|
||||
case c == nil:
|
||||
return errNilConfig
|
||||
case c.ReadTimeout.Duration <= 0:
|
||||
return newMustBePositiveError("read_timeout", c.ReadTimeout)
|
||||
case c.TCPIdleTimeout.Duration <= 0:
|
||||
return newMustBePositiveError("tcp_idle_timeout", c.TCPIdleTimeout)
|
||||
case c.WriteTimeout.Duration <= 0:
|
||||
return newMustBePositiveError("write_timeout", c.WriteTimeout)
|
||||
case c.HandleTimeout.Duration <= 0:
|
||||
return newMustBePositiveError("handle_timeout", c.HandleTimeout)
|
||||
case c.MaxUDPResponseSize.Bytes() == 0:
|
||||
return newMustBePositiveError("max_udp_response_size", c.MaxUDPResponseSize)
|
||||
case c.MaxUDPResponseSize.Bytes() > dns.MaxMsgSize:
|
||||
return fmt.Errorf(
|
||||
"max_udp_response_size must be less than %s, got %s",
|
||||
datasize.ByteSize(dns.MaxMsgSize),
|
||||
c.MaxUDPResponseSize,
|
||||
)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
@ -1,12 +1,10 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/dnsdb"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
|
||||
)
|
||||
|
||||
// DNSDB Configuration.
|
||||
|
||||
// dnsDBConfig is the configuration of the DNSDB module.
|
||||
type dnsDBConfig struct {
|
||||
// MaxSize is the maximum amount of records in the memory buffer.
|
||||
@ -29,7 +27,7 @@ func (c *dnsDBConfig) validate() (err error) {
|
||||
}
|
||||
|
||||
// toInternal builds and returns an anonymous statistics collector.
|
||||
func (c *dnsDBConfig) toInternal(errColl agd.ErrorCollector) (d dnsdb.Interface) {
|
||||
func (c *dnsDBConfig) toInternal(errColl errcoll.Interface) (d dnsdb.Interface) {
|
||||
if !c.Enabled {
|
||||
return dnsdb.Empty{}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
@ -8,7 +9,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agdhttp"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agdservice"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/debugsvc"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/dnsdb"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
|
||||
@ -16,6 +17,7 @@ import (
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/rulestat"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/AdguardTeam/golibs/netutil"
|
||||
"github.com/AdguardTeam/golibs/netutil/urlutil"
|
||||
"github.com/caarlos0/env/v7"
|
||||
"github.com/getsentry/sentry-go"
|
||||
)
|
||||
@ -24,20 +26,20 @@ import (
|
||||
|
||||
// environments represents the configuration that is kept in the environment.
|
||||
type environments struct {
|
||||
AdultBlockingURL *agdhttp.URL `env:"ADULT_BLOCKING_URL,notEmpty"`
|
||||
BillStatURL *agdhttp.URL `env:"BILLSTAT_URL,notEmpty"`
|
||||
BlockedServiceIndexURL *agdhttp.URL `env:"BLOCKED_SERVICE_INDEX_URL,notEmpty"`
|
||||
ConsulAllowlistURL *agdhttp.URL `env:"CONSUL_ALLOWLIST_URL,notEmpty"`
|
||||
ConsulDNSCheckKVURL *agdhttp.URL `env:"CONSUL_DNSCHECK_KV_URL"`
|
||||
ConsulDNSCheckSessionURL *agdhttp.URL `env:"CONSUL_DNSCHECK_SESSION_URL"`
|
||||
FilterIndexURL *agdhttp.URL `env:"FILTER_INDEX_URL,notEmpty"`
|
||||
GeneralSafeSearchURL *agdhttp.URL `env:"GENERAL_SAFE_SEARCH_URL,notEmpty"`
|
||||
LinkedIPTargetURL *agdhttp.URL `env:"LINKED_IP_TARGET_URL"`
|
||||
NewRegDomainsURL *agdhttp.URL `env:"NEW_REG_DOMAINS_URL,notEmpty"`
|
||||
ProfilesURL *agdhttp.URL `env:"PROFILES_URL,notEmpty"`
|
||||
RuleStatURL *agdhttp.URL `env:"RULESTAT_URL"`
|
||||
SafeBrowsingURL *agdhttp.URL `env:"SAFE_BROWSING_URL,notEmpty"`
|
||||
YoutubeSafeSearchURL *agdhttp.URL `env:"YOUTUBE_SAFE_SEARCH_URL,notEmpty"`
|
||||
AdultBlockingURL *urlutil.URL `env:"ADULT_BLOCKING_URL,notEmpty"`
|
||||
BillStatURL *urlutil.URL `env:"BILLSTAT_URL,notEmpty"`
|
||||
BlockedServiceIndexURL *urlutil.URL `env:"BLOCKED_SERVICE_INDEX_URL,notEmpty"`
|
||||
ConsulAllowlistURL *urlutil.URL `env:"CONSUL_ALLOWLIST_URL,notEmpty"`
|
||||
ConsulDNSCheckKVURL *urlutil.URL `env:"CONSUL_DNSCHECK_KV_URL"`
|
||||
ConsulDNSCheckSessionURL *urlutil.URL `env:"CONSUL_DNSCHECK_SESSION_URL"`
|
||||
FilterIndexURL *urlutil.URL `env:"FILTER_INDEX_URL,notEmpty"`
|
||||
GeneralSafeSearchURL *urlutil.URL `env:"GENERAL_SAFE_SEARCH_URL,notEmpty"`
|
||||
LinkedIPTargetURL *urlutil.URL `env:"LINKED_IP_TARGET_URL"`
|
||||
NewRegDomainsURL *urlutil.URL `env:"NEW_REG_DOMAINS_URL,notEmpty"`
|
||||
ProfilesURL *urlutil.URL `env:"PROFILES_URL,notEmpty"`
|
||||
RuleStatURL *urlutil.URL `env:"RULESTAT_URL"`
|
||||
SafeBrowsingURL *urlutil.URL `env:"SAFE_BROWSING_URL,notEmpty"`
|
||||
YoutubeSafeSearchURL *urlutil.URL `env:"YOUTUBE_SAFE_SEARCH_URL,notEmpty"`
|
||||
|
||||
ConfPath string `env:"CONFIG_PATH" envDefault:"./config.yaml"`
|
||||
FilterCachePath string `env:"FILTER_CACHE_PATH" envDefault:"./filters/"`
|
||||
@ -50,7 +52,7 @@ type environments struct {
|
||||
|
||||
ListenAddr net.IP `env:"LISTEN_ADDR" envDefault:"127.0.0.1"`
|
||||
|
||||
ListenPort int `env:"LISTEN_PORT" envDefault:"8181"`
|
||||
ListenPort uint16 `env:"LISTEN_PORT" envDefault:"8181"`
|
||||
|
||||
LogTimestamp strictBool `env:"LOG_TIMESTAMP" envDefault:"1"`
|
||||
LogVerbose strictBool `env:"VERBOSE" envDefault:"0"`
|
||||
@ -84,7 +86,7 @@ func (envs *environments) configureLogs() {
|
||||
}
|
||||
|
||||
// buildErrColl builds and returns an error collector from environment.
|
||||
func (envs *environments) buildErrColl() (errColl agd.ErrorCollector, err error) {
|
||||
func (envs *environments) buildErrColl() (errColl errcoll.Interface, err error) {
|
||||
dsn := envs.SentryDSN
|
||||
if dsn == "stderr" {
|
||||
return errcoll.NewWriterErrorCollector(os.Stderr), nil
|
||||
@ -157,7 +159,7 @@ func (envs *environments) debugConf(dnsDB dnsdb.Interface) (conf *debugsvc.Confi
|
||||
// registers its refresher in sigHdlr, if necessary.
|
||||
func (envs *environments) buildRuleStat(
|
||||
sigHdlr signalHandler,
|
||||
errColl agd.ErrorCollector,
|
||||
errColl errcoll.Interface,
|
||||
) (r rulestat.Interface, err error) {
|
||||
if envs.RuleStatURL == nil {
|
||||
log.Info("main: warning: not collecting rule stats")
|
||||
@ -169,7 +171,7 @@ func (envs *environments) buildRuleStat(
|
||||
URL: &envs.RuleStatURL.URL,
|
||||
})
|
||||
|
||||
refr := agd.NewRefreshWorker(&agd.RefreshWorkerConfig{
|
||||
refr := agdservice.NewRefreshWorker(&agdservice.RefreshWorkerConfig{
|
||||
Context: ctxWithDefaultTimeout,
|
||||
Refresher: httpRuleStat,
|
||||
ErrColl: errColl,
|
||||
@ -178,8 +180,9 @@ func (envs *environments) buildRuleStat(
|
||||
Interval: 10 * time.Minute,
|
||||
RefreshOnShutdown: true,
|
||||
RoutineLogsAreDebug: false,
|
||||
RandomizeStart: false,
|
||||
})
|
||||
err = refr.Start()
|
||||
err = refr.Start(context.Background())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("starting rulestat refresher: %w", err)
|
||||
}
|
||||
|
@ -5,8 +5,10 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agdnet"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agdservice"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/filter"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/filter/hashprefix"
|
||||
"github.com/AdguardTeam/golibs/netutil"
|
||||
@ -49,8 +51,9 @@ type filtersConfig struct {
|
||||
// toInternal converts c to the filter storage configuration for the DNS server.
|
||||
// cacheDir must exist. c is assumed to be valid.
|
||||
func (c *filtersConfig) toInternal(
|
||||
errColl agd.ErrorCollector,
|
||||
errColl errcoll.Interface,
|
||||
resolver agdnet.Resolver,
|
||||
cloner *dnsmsg.Cloner,
|
||||
envs *environments,
|
||||
safeBrowsing *hashprefix.Filter,
|
||||
adultBlocking *hashprefix.Filter,
|
||||
@ -67,6 +70,7 @@ func (c *filtersConfig) toInternal(
|
||||
Now: time.Now,
|
||||
ErrColl: errColl,
|
||||
Resolver: resolver,
|
||||
Cloner: cloner,
|
||||
CacheDir: envs.FilterCachePath,
|
||||
CustomFilterCacheSize: c.CustomFilterCacheSize,
|
||||
SafeSearchCacheSize: c.SafeSearchCacheSize,
|
||||
@ -75,7 +79,7 @@ func (c *filtersConfig) toInternal(
|
||||
RuleListCacheSize: c.RuleListCache.Size,
|
||||
RefreshIvl: c.RefreshIvl.Duration,
|
||||
UseRuleListCache: c.RuleListCache.Enabled,
|
||||
MaxRuleListSize: int64(c.MaxSize.Bytes()),
|
||||
MaxRuleListSize: c.MaxSize.Bytes(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -133,7 +137,7 @@ func (c *fltRuleListCache) validate() (err error) {
|
||||
func setupFilterStorage(
|
||||
conf *filter.DefaultStorageConfig,
|
||||
sigHdlr signalHandler,
|
||||
errColl agd.ErrorCollector,
|
||||
errColl errcoll.Interface,
|
||||
refreshTimeout time.Duration,
|
||||
) (strg *filter.DefaultStorage, err error) {
|
||||
strg, err = filter.NewDefaultStorage(conf)
|
||||
@ -141,7 +145,7 @@ func setupFilterStorage(
|
||||
return nil, fmt.Errorf("creating default filter storage: %w", err)
|
||||
}
|
||||
|
||||
refr := agd.NewRefreshWorker(&agd.RefreshWorkerConfig{
|
||||
refr := agdservice.NewRefreshWorker(&agdservice.RefreshWorkerConfig{
|
||||
Context: func() (ctx context.Context, cancel context.CancelFunc) {
|
||||
return context.WithTimeout(context.Background(), refreshTimeout)
|
||||
},
|
||||
@ -151,8 +155,9 @@ func setupFilterStorage(
|
||||
Interval: conf.RefreshIvl,
|
||||
RefreshOnShutdown: false,
|
||||
RoutineLogsAreDebug: false,
|
||||
RandomizeStart: false,
|
||||
})
|
||||
err = refr.Start()
|
||||
err = refr.Start(context.Background())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("starting default filter storage update: %w", err)
|
||||
}
|
||||
|
@ -1,9 +1,11 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agdservice"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/geoip"
|
||||
"github.com/AdguardTeam/golibs/timeutil"
|
||||
)
|
||||
@ -47,11 +49,11 @@ func (c *geoIPConfig) validate() (err error) {
|
||||
// refresher have been created successfully or an error if not.
|
||||
func setupGeoIP(
|
||||
geoIPPtr *geoip.File,
|
||||
refrPtr *agd.RefreshWorker,
|
||||
refrPtr *agdservice.RefreshWorker,
|
||||
errCh chan<- error,
|
||||
conf *geoIPConfig,
|
||||
envs *environments,
|
||||
errColl agd.ErrorCollector,
|
||||
errColl errcoll.Interface,
|
||||
) {
|
||||
geoIP, err := envs.geoIP(conf)
|
||||
if err != nil {
|
||||
@ -60,7 +62,7 @@ func setupGeoIP(
|
||||
return
|
||||
}
|
||||
|
||||
refr := agd.NewRefreshWorker(&agd.RefreshWorkerConfig{
|
||||
refr := agdservice.NewRefreshWorker(&agdservice.RefreshWorkerConfig{
|
||||
Context: ctxWithDefaultTimeout,
|
||||
Refresher: geoIP,
|
||||
ErrColl: errColl,
|
||||
@ -68,8 +70,9 @@ func setupGeoIP(
|
||||
Interval: conf.RefreshIvl.Duration,
|
||||
RefreshOnShutdown: false,
|
||||
RoutineLogsAreDebug: false,
|
||||
RandomizeStart: false,
|
||||
})
|
||||
err = refr.Start()
|
||||
err = refr.Start(context.Background())
|
||||
if err != nil {
|
||||
errCh <- fmt.Errorf("starting geoip refresher: %w", err)
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/bindtodevice"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/mapsutil"
|
||||
)
|
||||
@ -22,7 +22,7 @@ type interfaceListenersConfig struct {
|
||||
|
||||
// toInternal converts c to a bindtodevice.Manager. c is assumed to be valid.
|
||||
func (c *interfaceListenersConfig) toInternal(
|
||||
errColl agd.ErrorCollector,
|
||||
errColl errcoll.Interface,
|
||||
ctrlConf *bindtodevice.ControlConfig,
|
||||
) (m *bindtodevice.Manager, err error) {
|
||||
if c == nil {
|
||||
|
@ -1,14 +1,16 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agdnet"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agdservice"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/connlimiter"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/consul"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/ratelimit"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/metrics"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/timeutil"
|
||||
@ -32,22 +34,28 @@ type rateLimitConfig struct {
|
||||
// Rate limit options for IPv6 addresses.
|
||||
IPv6 *rateLimitOptions `yaml:"ipv6"`
|
||||
|
||||
// QUIC is the configuration of QUIC streams limiting.
|
||||
QUIC *ratelimitQUICConfig `yaml:"quic"`
|
||||
|
||||
// TCP is the configuration of TCP pipeline limiting.
|
||||
TCP *ratelimitTCPConfig `yaml:"tcp"`
|
||||
|
||||
// ResponseSizeEstimate is the size of the estimate of the size of one DNS
|
||||
// response for the purposes of rate limiting. Responses over this estimate
|
||||
// are counted as several responses.
|
||||
ResponseSizeEstimate datasize.ByteSize `yaml:"response_size_estimate"`
|
||||
|
||||
// BackOffCount helps with repeated offenders. It defines, how many times
|
||||
// BackoffCount helps with repeated offenders. It defines, how many times
|
||||
// a client hits the rate limit before being held in the back off.
|
||||
BackOffCount int `yaml:"back_off_count"`
|
||||
BackoffCount int `yaml:"backoff_count"`
|
||||
|
||||
// BackOffDuration is how much a client that has hit the rate limit too
|
||||
// BackoffDuration is how much a client that has hit the rate limit too
|
||||
// often stays in the back off.
|
||||
BackOffDuration timeutil.Duration `yaml:"back_off_duration"`
|
||||
BackoffDuration timeutil.Duration `yaml:"backoff_duration"`
|
||||
|
||||
// BackOffPeriod is the time during which to count the number of times
|
||||
// BackoffPeriod is the time during which to count the number of times
|
||||
// a client has hit the rate limit for a back off.
|
||||
BackOffPeriod timeutil.Duration `yaml:"back_off_period"`
|
||||
BackoffPeriod timeutil.Duration `yaml:"backoff_period"`
|
||||
|
||||
// RefuseANY, if true, makes the server refuse DNS * queries.
|
||||
RefuseANY bool `yaml:"refuse_any"`
|
||||
@ -87,17 +95,17 @@ func (o *rateLimitOptions) validate() (err error) {
|
||||
|
||||
// toInternal converts c to the rate limiting configuration for the DNS server.
|
||||
// c is assumed to be valid.
|
||||
func (c *rateLimitConfig) toInternal(al ratelimit.Allowlist) (conf *ratelimit.BackOffConfig) {
|
||||
return &ratelimit.BackOffConfig{
|
||||
func (c *rateLimitConfig) toInternal(al ratelimit.Allowlist) (conf *ratelimit.BackoffConfig) {
|
||||
return &ratelimit.BackoffConfig{
|
||||
Allowlist: al,
|
||||
ResponseSizeEstimate: int(c.ResponseSizeEstimate.Bytes()),
|
||||
Duration: c.BackOffDuration.Duration,
|
||||
Period: c.BackOffPeriod.Duration,
|
||||
Duration: c.BackoffDuration.Duration,
|
||||
Period: c.BackoffPeriod.Duration,
|
||||
IPv4RPS: c.IPv4.RPS,
|
||||
IPv4SubnetKeyLen: c.IPv4.SubnetKeyLen,
|
||||
IPv6RPS: c.IPv6.RPS,
|
||||
IPv6SubnetKeyLen: c.IPv6.SubnetKeyLen,
|
||||
Count: c.BackOffCount,
|
||||
Count: c.BackoffCount,
|
||||
RefuseANY: c.RefuseANY,
|
||||
}
|
||||
}
|
||||
@ -111,25 +119,15 @@ func (c *rateLimitConfig) validate() (err error) {
|
||||
return fmt.Errorf("allowlist: %w", errNilConfig)
|
||||
}
|
||||
|
||||
err = c.ConnectionLimit.validate()
|
||||
if err != nil {
|
||||
return fmt.Errorf("connection_limit: %w", err)
|
||||
}
|
||||
|
||||
err = c.IPv4.validate()
|
||||
if err != nil {
|
||||
return fmt.Errorf("ipv4: %w", err)
|
||||
}
|
||||
|
||||
err = c.IPv6.validate()
|
||||
if err != nil {
|
||||
return fmt.Errorf("ipv6: %w", err)
|
||||
}
|
||||
|
||||
return coalesceError(
|
||||
validatePositive("back_off_count", c.BackOffCount),
|
||||
validatePositive("back_off_duration", c.BackOffDuration),
|
||||
validatePositive("back_off_period", c.BackOffPeriod),
|
||||
validateProp("connection_limit", c.ConnectionLimit.validate),
|
||||
validateProp("ipv4", c.IPv4.validate),
|
||||
validateProp("ipv6", c.IPv6.validate),
|
||||
validateProp("quic", c.QUIC.validate),
|
||||
validateProp("tcp", c.TCP.validate),
|
||||
validatePositive("backoff_count", c.BackoffCount),
|
||||
validatePositive("backoff_duration", c.BackoffDuration),
|
||||
validatePositive("backoff_period", c.BackoffPeriod),
|
||||
validatePositive("response_size_estimate", c.ResponseSizeEstimate),
|
||||
validatePositive("allowlist.refresh_interval", c.Allowlist.RefreshIvl),
|
||||
)
|
||||
@ -141,8 +139,8 @@ func setupRateLimiter(
|
||||
conf *rateLimitConfig,
|
||||
consulAllowlist *url.URL,
|
||||
sigHdlr signalHandler,
|
||||
errColl agd.ErrorCollector,
|
||||
) (rateLimiter *ratelimit.BackOff, connLimiter *connlimiter.Limiter, err error) {
|
||||
errColl errcoll.Interface,
|
||||
) (rateLimiter *ratelimit.Backoff, connLimiter *connlimiter.Limiter, err error) {
|
||||
allowSubnets, err := agdnet.ParseSubnets(conf.Allowlist.List...)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("parsing allowlist subnets: %w", err)
|
||||
@ -154,7 +152,7 @@ func setupRateLimiter(
|
||||
return nil, nil, fmt.Errorf("creating allowlist refresher: %w", err)
|
||||
}
|
||||
|
||||
refr := agd.NewRefreshWorker(&agd.RefreshWorkerConfig{
|
||||
refr := agdservice.NewRefreshWorker(&agdservice.RefreshWorkerConfig{
|
||||
Context: ctxWithDefaultTimeout,
|
||||
Refresher: refresher,
|
||||
ErrColl: errColl,
|
||||
@ -162,15 +160,17 @@ func setupRateLimiter(
|
||||
Interval: conf.Allowlist.RefreshIvl.Duration,
|
||||
RefreshOnShutdown: false,
|
||||
RoutineLogsAreDebug: false,
|
||||
RandomizeStart: false,
|
||||
})
|
||||
err = refr.Start()
|
||||
|
||||
err = refr.Start(context.Background())
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("starting allowlist refresher: %w", err)
|
||||
}
|
||||
|
||||
sigHdlr.add(refr)
|
||||
|
||||
return ratelimit.NewBackOff(conf.toInternal(allowlist)), conf.ConnectionLimit.toInternal(), nil
|
||||
return ratelimit.NewBackoff(conf.toInternal(allowlist)), conf.ConnectionLimit.toInternal(), nil
|
||||
}
|
||||
|
||||
// connLimitConfig is the configuration structure for the stream-connection
|
||||
@ -229,3 +229,41 @@ func (c *connLimitConfig) validate() (err error) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// ratelimitTCPConfig is the configuration of TCP pipeline limiting.
|
||||
type ratelimitTCPConfig struct {
|
||||
// MaxPipelineCount is the maximum number of simultaneously processing TCP
|
||||
// messages per one connection.
|
||||
MaxPipelineCount uint `yaml:"max_pipeline_count"`
|
||||
|
||||
// Enabled, if true, enables TCP limiting.
|
||||
Enabled bool `yaml:"enabled"`
|
||||
}
|
||||
|
||||
// validate returns an error if the config is invalid.
|
||||
func (c *ratelimitTCPConfig) validate() (err error) {
|
||||
if c == nil {
|
||||
return errNilConfig
|
||||
}
|
||||
|
||||
return validatePositive("max_pipeline_count", c.MaxPipelineCount)
|
||||
}
|
||||
|
||||
// ratelimitQUICConfig is the configuration of QUIC streams limiting.
|
||||
type ratelimitQUICConfig struct {
|
||||
// MaxStreamsPerPeer is the maximum number of concurrent streams that a peer
|
||||
// is allowed to open.
|
||||
MaxStreamsPerPeer int `yaml:"max_streams_per_peer"`
|
||||
|
||||
// Enabled, if true, enables QUIC limiting.
|
||||
Enabled bool `yaml:"enabled"`
|
||||
}
|
||||
|
||||
// validate returns an error if the config is invalid.
|
||||
func (c *ratelimitQUICConfig) validate() (err error) {
|
||||
if c == nil {
|
||||
return errNilConfig
|
||||
}
|
||||
|
||||
return validatePositive("max_streams_per_peer", c.MaxStreamsPerPeer)
|
||||
}
|
||||
|
@ -1,15 +1,19 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agdhttp"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agdnet"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agdservice"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/filter/hashprefix"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/netutil"
|
||||
"github.com/AdguardTeam/golibs/netutil/urlutil"
|
||||
"github.com/AdguardTeam/golibs/timeutil"
|
||||
)
|
||||
|
||||
@ -37,12 +41,13 @@ type safeBrowsingConfig struct {
|
||||
// toInternal converts c to the safe browsing filter configuration for the
|
||||
// filter storage of the DNS server. c is assumed to be valid.
|
||||
func (c *safeBrowsingConfig) toInternal(
|
||||
errColl agd.ErrorCollector,
|
||||
errColl errcoll.Interface,
|
||||
resolver agdnet.Resolver,
|
||||
cloner *dnsmsg.Cloner,
|
||||
id agd.FilterListID,
|
||||
url *agdhttp.URL,
|
||||
url *urlutil.URL,
|
||||
cacheDir string,
|
||||
maxSize int64,
|
||||
maxSize uint64,
|
||||
) (fltConf *hashprefix.FilterConfig, err error) {
|
||||
hashes, err := hashprefix.NewStorage("")
|
||||
if err != nil {
|
||||
@ -50,6 +55,7 @@ func (c *safeBrowsingConfig) toInternal(
|
||||
}
|
||||
|
||||
return &hashprefix.FilterConfig{
|
||||
Cloner: cloner,
|
||||
Hashes: hashes,
|
||||
URL: netutil.CloneURL(&url.URL),
|
||||
ErrColl: errColl,
|
||||
@ -88,14 +94,15 @@ func (c *safeBrowsingConfig) validate() (err error) {
|
||||
func setupHashPrefixFilter(
|
||||
conf *safeBrowsingConfig,
|
||||
resolver *agdnet.CachingResolver,
|
||||
cloner *dnsmsg.Cloner,
|
||||
id agd.FilterListID,
|
||||
url *agdhttp.URL,
|
||||
url *urlutil.URL,
|
||||
cachePath string,
|
||||
maxSize int64,
|
||||
maxSize uint64,
|
||||
sigHdlr signalHandler,
|
||||
errColl agd.ErrorCollector,
|
||||
errColl errcoll.Interface,
|
||||
) (strg *hashprefix.Storage, flt *hashprefix.Filter, err error) {
|
||||
fltConf, err := conf.toInternal(errColl, resolver, id, url, cachePath, maxSize)
|
||||
fltConf, err := conf.toInternal(errColl, resolver, cloner, id, url, cachePath, maxSize)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("configuring hash prefix filter %s: %w", id, err)
|
||||
}
|
||||
@ -105,7 +112,7 @@ func setupHashPrefixFilter(
|
||||
return nil, nil, fmt.Errorf("creating hash prefix filter %s: %w", id, err)
|
||||
}
|
||||
|
||||
refr := agd.NewRefreshWorker(&agd.RefreshWorkerConfig{
|
||||
refr := agdservice.NewRefreshWorker(&agdservice.RefreshWorkerConfig{
|
||||
Context: ctxWithDefaultTimeout,
|
||||
Refresher: flt,
|
||||
ErrColl: errColl,
|
||||
@ -113,8 +120,9 @@ func setupHashPrefixFilter(
|
||||
Interval: fltConf.Staleness,
|
||||
RefreshOnShutdown: false,
|
||||
RoutineLogsAreDebug: false,
|
||||
RandomizeStart: false,
|
||||
})
|
||||
err = refr.Start()
|
||||
err = refr.Start(context.Background())
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("starting refresher for hash prefix filter %s: %w", id, err)
|
||||
}
|
||||
|
@ -11,13 +11,13 @@ import (
|
||||
"github.com/AdguardTeam/golibs/stringutil"
|
||||
)
|
||||
|
||||
// Server configuration
|
||||
|
||||
// toInternal returns the configuration of DNS servers for a single server
|
||||
// group. srvs is assumed to be valid.
|
||||
// group. srvs and other parts of the configuration are assumed to be valid.
|
||||
func (srvs servers) toInternal(
|
||||
tlsConfig *agd.TLS,
|
||||
btdMgr *bindtodevice.Manager,
|
||||
ratelimitConf *rateLimitConfig,
|
||||
dnsConf *dnsConfig,
|
||||
) (dnsSrvs []*agd.Server, err error) {
|
||||
dnsSrvs = make([]*agd.Server, 0, len(srvs))
|
||||
for _, srv := range srvs {
|
||||
@ -28,29 +28,41 @@ func (srvs servers) toInternal(
|
||||
}
|
||||
|
||||
name := agd.ServerName(srv.Name)
|
||||
switch p := srv.Protocol; p {
|
||||
case srvProtoDNS:
|
||||
dnsSrvs = append(dnsSrvs, &agd.Server{
|
||||
dnsSrv := &agd.Server{
|
||||
Name: name,
|
||||
BindData: bindData,
|
||||
Protocol: agd.ProtoDNS,
|
||||
ReadTimeout: dnsConf.ReadTimeout.Duration,
|
||||
WriteTimeout: dnsConf.WriteTimeout.Duration,
|
||||
LinkedIPEnabled: srv.LinkedIPEnabled,
|
||||
})
|
||||
case srvProtoDNSCrypt:
|
||||
Protocol: srv.Protocol.toInternal(),
|
||||
}
|
||||
|
||||
tcpConf := &agd.TCPConfig{
|
||||
IdleTimeout: dnsConf.TCPIdleTimeout.Duration,
|
||||
MaxPipelineCount: ratelimitConf.TCP.MaxPipelineCount,
|
||||
MaxPipelineEnabled: ratelimitConf.TCP.Enabled,
|
||||
}
|
||||
|
||||
switch dnsSrv.Protocol {
|
||||
case agd.ProtoDNS:
|
||||
dnsSrv.TCPConf = tcpConf
|
||||
dnsSrv.UDPConf = &agd.UDPConfig{
|
||||
MaxRespSize: uint16(dnsConf.MaxUDPResponseSize.Bytes()),
|
||||
}
|
||||
case agd.ProtoDNSCrypt:
|
||||
var dcConf *agd.DNSCryptConfig
|
||||
dcConf, err = srv.DNSCrypt.toInternal()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("server %q: dnscrypt: %w", srv.Name, err)
|
||||
}
|
||||
|
||||
dnsSrvs = append(dnsSrvs, &agd.Server{
|
||||
DNSCrypt: dcConf,
|
||||
Name: name,
|
||||
BindData: bindData,
|
||||
Protocol: agd.ProtoDNSCrypt,
|
||||
LinkedIPEnabled: srv.LinkedIPEnabled,
|
||||
})
|
||||
dnsSrv.DNSCrypt = dcConf
|
||||
default:
|
||||
dnsSrv.TCPConf = tcpConf
|
||||
dnsSrv.QUICConf = &agd.QUICConfig{
|
||||
MaxStreamsPerPeer: ratelimitConf.QUIC.MaxStreamsPerPeer,
|
||||
QUICLimitsEnabled: ratelimitConf.QUIC.Enabled,
|
||||
}
|
||||
|
||||
tlsConf := tlsConfig.Conf.Clone()
|
||||
|
||||
// Attach the functions that will count TLS handshake metrics.
|
||||
@ -62,14 +74,12 @@ func (srvs servers) toInternal(
|
||||
tlsConf.Certificates,
|
||||
)
|
||||
|
||||
dnsSrvs = append(dnsSrvs, &agd.Server{
|
||||
TLS: tlsConf,
|
||||
Name: name,
|
||||
BindData: bindData,
|
||||
Protocol: p.toInternal(),
|
||||
LinkedIPEnabled: srv.LinkedIPEnabled,
|
||||
})
|
||||
dnsSrv.TLS = tlsConf
|
||||
}
|
||||
|
||||
dnsSrv.SetBindData(bindData)
|
||||
|
||||
dnsSrvs = append(dnsSrvs, dnsSrv)
|
||||
}
|
||||
|
||||
return dnsSrvs, nil
|
||||
@ -128,9 +138,13 @@ func (p serverProto) needsTLS() (ok bool) {
|
||||
}
|
||||
|
||||
// toInternal returns the equivalent agd.Protocol value if there is one. If
|
||||
// there is no such value, it returns agd.ProtoInvalid.
|
||||
// there is no such value, it returns [agd.ProtoInvalid].
|
||||
func (p serverProto) toInternal() (sp agd.Protocol) {
|
||||
switch p {
|
||||
case srvProtoDNS:
|
||||
return agd.ProtoDNS
|
||||
case srvProtoDNSCrypt:
|
||||
return agd.ProtoDNSCrypt
|
||||
case srvProtoHTTPS:
|
||||
return agd.ProtoDoH
|
||||
case srvProtoQUIC:
|
||||
@ -215,7 +229,7 @@ func (s *server) bindData(
|
||||
|
||||
bindData = append(bindData, &agd.ServerBindData{
|
||||
ListenConfig: lc,
|
||||
Address: lc.Addr(),
|
||||
PrefixAddr: lc.Addr(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -10,18 +10,19 @@ import (
|
||||
"github.com/AdguardTeam/golibs/stringutil"
|
||||
)
|
||||
|
||||
// Server Group Configuration
|
||||
|
||||
// serverGroups are the DNS server groups. A valid instance of serverGroups has
|
||||
// no nil items.
|
||||
type serverGroups []*serverGroup
|
||||
|
||||
// toInternal returns the configuration for all server groups in the DNS
|
||||
// service. srvGrps is assumed to be valid.
|
||||
// service. srvGrps and other parts of the configuration are assumed to be
|
||||
// valid.
|
||||
func (srvGrps serverGroups) toInternal(
|
||||
messages *dnsmsg.Constructor,
|
||||
btdMgr *bindtodevice.Manager,
|
||||
fltGrps map[agd.FilteringGroupID]*agd.FilteringGroup,
|
||||
ratelimitConf *rateLimitConfig,
|
||||
dnsConf *dnsConfig,
|
||||
) (svcSrvGrps []*agd.ServerGroup, err error) {
|
||||
svcSrvGrps = make([]*agd.ServerGroup, len(srvGrps))
|
||||
for i, g := range srvGrps {
|
||||
@ -44,7 +45,7 @@ func (srvGrps serverGroups) toInternal(
|
||||
FilteringGroup: fltGrpID,
|
||||
}
|
||||
|
||||
svcSrvGrps[i].Servers, err = g.Servers.toInternal(tlsConf, btdMgr)
|
||||
svcSrvGrps[i].Servers, err = g.Servers.toInternal(tlsConf, btdMgr, ratelimitConf, dnsConf)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("server group %q: %w", g.Name, err)
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ import (
|
||||
"os/signal"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agdservice"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
@ -17,7 +17,7 @@ type signalHandler struct {
|
||||
|
||||
// services are the services that are shut down before application
|
||||
// exiting.
|
||||
services []agd.Service
|
||||
services []agdservice.Interface
|
||||
}
|
||||
|
||||
// newSignalHandler returns a new signalHandler that shuts down services.
|
||||
@ -32,7 +32,7 @@ func newSignalHandler() (h signalHandler) {
|
||||
}
|
||||
|
||||
// add adds a service to the signal handler.
|
||||
func (h *signalHandler) add(s agd.Service) {
|
||||
func (h *signalHandler) add(s agdservice.Interface) {
|
||||
h.services = append(h.services, s)
|
||||
}
|
||||
|
||||
|
@ -7,15 +7,17 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agdservice"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/metrics"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/stringutil"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
// TLS Configuration And Utilities
|
||||
@ -217,9 +219,6 @@ func newTicketRotator(grps []*agd.ServerGroup) (tr *ticketRotator, err error) {
|
||||
return tr, nil
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ agd.Refresher = (*ticketRotator)(nil)
|
||||
|
||||
// sessTickLen is the length of a single TLS session ticket key in bytes.
|
||||
//
|
||||
// NOTE: Unlike Nginx, Go's crypto/tls doesn't use the random bytes from the
|
||||
@ -227,7 +226,10 @@ var _ agd.Refresher = (*ticketRotator)(nil)
|
||||
// 48 bytes of the hashed data as the key name, the AES key, and the HMAC key.
|
||||
const sessTickLen = 32
|
||||
|
||||
// Refresh implements the agd.Refresher interface for *ticketRotator.
|
||||
// type check
|
||||
var _ agdservice.Refresher = (*ticketRotator)(nil)
|
||||
|
||||
// Refresh implements the [agdservice.Refresher] interface for *ticketRotator.
|
||||
func (r *ticketRotator) Refresh(_ context.Context) (err error) {
|
||||
for conf, files := range r.confs {
|
||||
keys := make([][sessTickLen]byte, 0, len(files))
|
||||
@ -299,14 +301,14 @@ func enableTLSKeyLogging(grps []*agd.ServerGroup, keyLogFileName string) (err er
|
||||
func setupTicketRotator(
|
||||
srvGrps []*agd.ServerGroup,
|
||||
sigHdlr signalHandler,
|
||||
errColl agd.ErrorCollector,
|
||||
errColl errcoll.Interface,
|
||||
) (err error) {
|
||||
tickRot, err := newTicketRotator(srvGrps)
|
||||
if err != nil {
|
||||
return fmt.Errorf("setting up ticket rotator: %w", err)
|
||||
}
|
||||
|
||||
refr := agd.NewRefreshWorker(&agd.RefreshWorkerConfig{
|
||||
refr := agdservice.NewRefreshWorker(&agdservice.RefreshWorkerConfig{
|
||||
Context: ctxWithDefaultTimeout,
|
||||
Refresher: tickRot,
|
||||
ErrColl: errColl,
|
||||
@ -315,8 +317,9 @@ func setupTicketRotator(
|
||||
Interval: 1 * time.Minute,
|
||||
RefreshOnShutdown: false,
|
||||
RoutineLogsAreDebug: true,
|
||||
RandomizeStart: false,
|
||||
})
|
||||
err = refr.Start()
|
||||
err = refr.Start(context.Background())
|
||||
if err != nil {
|
||||
return fmt.Errorf("starting ticket rotator refresh: %w", err)
|
||||
}
|
||||
|
@ -8,41 +8,39 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agdservice"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/forward"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/prometheus"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/timeutil"
|
||||
)
|
||||
|
||||
// DNS upstream configuration
|
||||
|
||||
// upstreamConfig module configuration
|
||||
// upstreamConfig is the upstream module configuration.
|
||||
type upstreamConfig struct {
|
||||
// Healthcheck contains the upstream healthcheck configuration.
|
||||
Healthcheck *upstreamHealthcheckConfig `yaml:"healthcheck"`
|
||||
|
||||
// Server is the upstream url of the server we're using to forward DNS
|
||||
// queries. It starts with tcp://, udp://, or with an IP address.
|
||||
Server string `yaml:"server"`
|
||||
// Fallback is the configuration for the upstream fallback servers.
|
||||
Fallback *upstreamFallbackConfig `yaml:"fallback"`
|
||||
|
||||
// FallbackServers is a list of the DNS servers we're using to fallback to
|
||||
// when the upstream server fails to respond
|
||||
FallbackServers []netip.AddrPort `yaml:"fallback"`
|
||||
|
||||
// Timeout is the timeout for DNS requests to the upstreams.
|
||||
Timeout timeutil.Duration `yaml:"timeout"`
|
||||
// Servers is a list of the upstream servers configurations we use to
|
||||
// forward DNS queries.
|
||||
Servers []*upstreamServerConfig `yaml:"servers"`
|
||||
}
|
||||
|
||||
// toInternal converts c to the data storage configuration for the DNS server.
|
||||
func (c *upstreamConfig) toInternal() (fwdConf *forward.HandlerConfig, err error) {
|
||||
network, addrPort, err := splitUpstreamURL(c.Server)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// c is assumed to be valid.
|
||||
func (c *upstreamConfig) toInternal() (fwdConf *forward.HandlerConfig) {
|
||||
upstreams := c.Servers
|
||||
fallbacks := c.Fallback.Servers
|
||||
|
||||
fallbacks := c.FallbackServers
|
||||
metricsListener := prometheus.NewForwardMetricsListener(len(fallbacks) + 1)
|
||||
upsConfs := toUpstreamConfigs(upstreams)
|
||||
fallbackConfs := toUpstreamConfigs(fallbacks)
|
||||
|
||||
metricsListener := prometheus.NewForwardMetricsListener(len(upstreams) + len(fallbacks))
|
||||
|
||||
var hcInit time.Duration
|
||||
if c.Healthcheck.Enabled {
|
||||
@ -50,17 +48,15 @@ func (c *upstreamConfig) toInternal() (fwdConf *forward.HandlerConfig, err error
|
||||
}
|
||||
|
||||
fwdConf = &forward.HandlerConfig{
|
||||
Address: addrPort,
|
||||
Network: network,
|
||||
MetricsListener: metricsListener,
|
||||
HealthcheckDomainTmpl: c.Healthcheck.DomainTmpl,
|
||||
FallbackAddresses: c.FallbackServers,
|
||||
Timeout: c.Timeout.Duration,
|
||||
UpstreamsAddresses: upsConfs,
|
||||
FallbackAddresses: fallbackConfs,
|
||||
HealthcheckBackoffDuration: c.Healthcheck.BackoffDuration.Duration,
|
||||
HealthcheckInitDuration: hcInit,
|
||||
}
|
||||
|
||||
return fwdConf, nil
|
||||
return fwdConf
|
||||
}
|
||||
|
||||
// validate returns an error if the upstream configuration is invalid.
|
||||
@ -68,20 +64,20 @@ func (c *upstreamConfig) validate() (err error) {
|
||||
switch {
|
||||
case c == nil:
|
||||
return errNilConfig
|
||||
case c.Server == "":
|
||||
return errors.Error("no server")
|
||||
case len(c.FallbackServers) == 0:
|
||||
return errors.Error("no fallback")
|
||||
case c.Timeout.Duration <= 0:
|
||||
return newMustBePositiveError("timeout", c.Timeout)
|
||||
case len(c.Servers) == 0:
|
||||
return errors.Error("no servers")
|
||||
}
|
||||
|
||||
err = validateAddrs(c.FallbackServers)
|
||||
if err != nil {
|
||||
return fmt.Errorf("fallback: %w", err)
|
||||
for i, s := range c.Servers {
|
||||
if err = s.validate(); err != nil {
|
||||
return fmt.Errorf("servers: at index %d: %w", i, err)
|
||||
}
|
||||
}
|
||||
|
||||
return errors.Annotate(c.Healthcheck.validate(), "healthcheck: %w")
|
||||
return coalesceError(
|
||||
validateProp("fallback", c.Fallback.validate),
|
||||
validateProp("healthcheck", c.Healthcheck.validate),
|
||||
)
|
||||
}
|
||||
|
||||
// splitUpstreamURL separates server url to net protocol and port address.
|
||||
@ -163,13 +159,13 @@ func (c *upstreamHealthcheckConfig) validate() (err error) {
|
||||
func newUpstreamHealthcheck(
|
||||
handler *forward.Handler,
|
||||
conf *upstreamConfig,
|
||||
errColl agd.ErrorCollector,
|
||||
) (refr agd.Service) {
|
||||
errColl errcoll.Interface,
|
||||
) (refr agdservice.Interface) {
|
||||
if !conf.Healthcheck.Enabled {
|
||||
return agd.EmptyService{}
|
||||
return agdservice.Empty{}
|
||||
}
|
||||
|
||||
return agd.NewRefreshWorker(&agd.RefreshWorkerConfig{
|
||||
return agdservice.NewRefreshWorker(&agdservice.RefreshWorkerConfig{
|
||||
Context: func() (ctx context.Context, cancel context.CancelFunc) {
|
||||
return context.WithTimeout(
|
||||
context.Background(),
|
||||
@ -182,5 +178,76 @@ func newUpstreamHealthcheck(
|
||||
Interval: conf.Healthcheck.Interval.Duration,
|
||||
RefreshOnShutdown: false,
|
||||
RoutineLogsAreDebug: true,
|
||||
RandomizeStart: false,
|
||||
})
|
||||
}
|
||||
|
||||
// upstreamFallbackConfig is the configuration for the upstream fallback
|
||||
// servers.
|
||||
type upstreamFallbackConfig struct {
|
||||
// Servers is a list of the upstream servers configurations we use to
|
||||
// fallback when the upstream servers fail to respond.
|
||||
Servers []*upstreamServerConfig `yaml:"servers"`
|
||||
}
|
||||
|
||||
// validate returns an error if the upstream fallback configuration is invalid.
|
||||
func (c *upstreamFallbackConfig) validate() (err error) {
|
||||
switch {
|
||||
case c == nil:
|
||||
return errNilConfig
|
||||
case len(c.Servers) == 0:
|
||||
return errors.Error("no servers")
|
||||
}
|
||||
|
||||
for i, s := range c.Servers {
|
||||
if err = s.validate(); err != nil {
|
||||
return fmt.Errorf("servers: at index %d: %w", i, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// upstreamServerConfig is the configuration for the upstream server.
|
||||
type upstreamServerConfig struct {
|
||||
// Address is the url of the DNS server in the `[scheme://]ip:port`
|
||||
// format.
|
||||
Address string `yaml:"address"`
|
||||
|
||||
// Timeout is the timeout for DNS requests.
|
||||
Timeout timeutil.Duration `yaml:"timeout"`
|
||||
}
|
||||
|
||||
// validate returns an error if the upstream server configuration is invalid.
|
||||
func (c *upstreamServerConfig) validate() (err error) {
|
||||
switch {
|
||||
case c == nil:
|
||||
return errNilConfig
|
||||
case c.Timeout.Duration <= 0:
|
||||
return newMustBePositiveError("timeout", c.Timeout)
|
||||
}
|
||||
|
||||
_, _, err = splitUpstreamURL(c.Address)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid addr: %s", c.Address)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// toUpstreamConfigs converts confs to the list of upstream configurations.
|
||||
// confs must be valid.
|
||||
func toUpstreamConfigs(confs []*upstreamServerConfig) (upsConfs []*forward.UpstreamPlainConfig) {
|
||||
upsConfs = make([]*forward.UpstreamPlainConfig, 0, len(confs))
|
||||
for _, c := range confs {
|
||||
net, addrPort, _ := splitUpstreamURL(c.Address)
|
||||
|
||||
upsConfs = append(upsConfs, &forward.UpstreamPlainConfig{
|
||||
Network: net,
|
||||
Address: addrPort,
|
||||
Timeout: c.Timeout.Duration,
|
||||
})
|
||||
}
|
||||
|
||||
return upsConfs
|
||||
}
|
||||
|
@ -19,6 +19,17 @@ func validatePositive[T numberOrDuration](prop string, v T) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateProp returns an error wrapped with prop name if the given validator
|
||||
// func returns an error.
|
||||
func validateProp(prop string, validator func() error) (err error) {
|
||||
err = validator()
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", prop, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// netipAddr is the type constraint for the types from [netip], which we can
|
||||
// validate using [validateAddrs].
|
||||
type netipAddr interface {
|
||||
|
@ -9,12 +9,12 @@ import (
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agdhttp"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/websvc"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/httphdr"
|
||||
"github.com/AdguardTeam/golibs/netutil"
|
||||
"github.com/AdguardTeam/golibs/netutil/urlutil"
|
||||
"github.com/AdguardTeam/golibs/timeutil"
|
||||
)
|
||||
|
||||
@ -33,7 +33,7 @@ type webConfig struct {
|
||||
|
||||
// RootRedirectURL is the URL to which non-DNS and non-Debug HTTP requests
|
||||
// are redirected. If not set, a 404 page is shown.
|
||||
RootRedirectURL *agdhttp.URL `yaml:"root_redirect_url"`
|
||||
RootRedirectURL *urlutil.URL `yaml:"root_redirect_url"`
|
||||
|
||||
// StaticContent is the content that is served statically at the given
|
||||
// paths. If not set, no static content is shown.
|
||||
@ -60,7 +60,7 @@ type webConfig struct {
|
||||
func (c *webConfig) toInternal(
|
||||
envs *environments,
|
||||
dnsCk http.Handler,
|
||||
errColl agd.ErrorCollector,
|
||||
errColl errcoll.Interface,
|
||||
) (conf *websvc.Config, err error) {
|
||||
if c == nil {
|
||||
return nil, nil
|
||||
@ -179,7 +179,7 @@ type linkedIPServer struct {
|
||||
// toInternal converts s to a linkedIP server configuration. s is assumed to be
|
||||
// valid.
|
||||
func (s *linkedIPServer) toInternal(
|
||||
targetURL *agdhttp.URL,
|
||||
targetURL *urlutil.URL,
|
||||
) (srv *websvc.LinkedIPServer, err error) {
|
||||
if s == nil {
|
||||
return nil, nil
|
||||
|
@ -18,9 +18,9 @@ import (
|
||||
type limitConn struct {
|
||||
net.Conn
|
||||
|
||||
serverInfo *dnsserver.ServerInfo
|
||||
decrement func()
|
||||
start time.Time
|
||||
serverInfo dnsserver.ServerInfo
|
||||
isClosed atomic.Bool
|
||||
}
|
||||
|
||||
|
@ -53,7 +53,7 @@ func New(c *Config) (l *Limiter, err error) {
|
||||
|
||||
// Limit wraps lsnr to control the number of active connections. srvInfo is
|
||||
// used for logging and metrics.
|
||||
func (l *Limiter) Limit(lsnr net.Listener, srvInfo dnsserver.ServerInfo) (limited net.Listener) {
|
||||
func (l *Limiter) Limit(lsnr net.Listener, srvInfo *dnsserver.ServerInfo) (limited net.Listener) {
|
||||
name, addr := srvInfo.Name, srvInfo.Addr
|
||||
proto := srvInfo.Proto.String()
|
||||
|
||||
|
@ -23,7 +23,7 @@ func TestMain(m *testing.M) {
|
||||
const testTimeout = 1 * time.Second
|
||||
|
||||
// testServerInfo is the common server information for tests.
|
||||
var testServerInfo = dnsserver.ServerInfo{
|
||||
var testServerInfo = &dnsserver.ServerInfo{
|
||||
Name: "test_server",
|
||||
Addr: "127.0.0.1:0",
|
||||
Proto: agd.ProtoDoT,
|
||||
|
@ -20,6 +20,10 @@ import (
|
||||
type limitListener struct {
|
||||
net.Listener
|
||||
|
||||
// serverInfo is used for logging and metrics in both the listener itself
|
||||
// and in its conns. It's never nil.
|
||||
serverInfo *dnsserver.ServerInfo
|
||||
|
||||
// counterCond is the condition variable that protects counter and isClosed
|
||||
// through its locker, as well as signals when connections can be accepted
|
||||
// again or when the listener has been closed.
|
||||
@ -35,10 +39,6 @@ type limitListener struct {
|
||||
// waiting for an accept.
|
||||
waitingHist prometheus.Observer
|
||||
|
||||
// serverInfo is used for logging and metrics in both the listener itself
|
||||
// and in its conns.
|
||||
serverInfo dnsserver.ServerInfo
|
||||
|
||||
// isClosed shows whether this listener has been closed.
|
||||
isClosed bool
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user