diff --git a/CHANGELOG.md b/CHANGELOG.md
index ac46984..50e1511 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,6 +11,29 @@ The format is **not** based on [Keep a Changelog][kec], since the project
+## AGDNS-1032 / Build 363
+
+* The new optional field `static_content.*.allow_origin` has been added:
+
+ ```yaml
+ static_content:
+ '/favicon.ico':
+ allow_origin: '*'
+ ```
+
+
+
+## AGDNS-898 / Build 359
+
+* The new optional object `additional_metrics_info` has been added:
+
+ ```yaml
+ additional_metrics_info:
+ test_key: 'test_value'
+ ```
+
+
+
## AGDNS-986 / Build 346
* The new object `upstream.healthcheck` now contains all healthcheck-related
diff --git a/config.dist.yml b/config.dist.yml
index 7822456..f6b7c1c 100644
--- a/config.dist.yml
+++ b/config.dist.yml
@@ -156,6 +156,7 @@ web:
# servers. Paths must not cross the ones used by the DNS-over-HTTPS server.
static_content:
'/favicon.ico':
+ allow_origin: '*'
content_type: 'image/x-icon'
content: ''
# If not defined, AdGuard DNS will respond with a 404 page to all such
@@ -326,3 +327,7 @@ server_groups:
connectivity_check:
probe_ipv4: '8.8.8.8:53'
probe_ipv6: '[2001:4860:4860::8888]:53'
+
+# Additional information to be exposed through metrics.
+additional_metrics_info:
+ test_key: 'test_value'
diff --git a/doc/configuration.md b/doc/configuration.md
index ca9facc..ebaffa9 100644
--- a/doc/configuration.md
+++ b/doc/configuration.md
@@ -24,6 +24,7 @@ configuration file with comments.
* [DDR](#server_groups-*-ddr)
* [Servers](#server_groups-*-servers-*)
* [Connectivity Check](#connectivity-check)
+ * [Additional Metrics Info](#additional_metrics_info)
[dist]: ../config.dist.yml
[env]: environment.md
@@ -521,6 +522,8 @@ The `filters` object has the following properties:
**Example:** `5m`.
+[env-blocked_services]: environment.md#BLOCKED_SERVICE_INDEX_URL
+
## Filtering Groups
@@ -530,6 +533,7 @@ The items of the `filtering_groups` array have the following properties:
* `id`:
The unique ID of this filtering group.
+
**Example:** `default`.
* `rule_lists`:
@@ -814,5 +818,30 @@ The `connectivity_check` object has the following properties:
**Example:** `[2001:4860:4860::8888]:53`.
-[env-backend]: environment.md#BACKEND_ENDPOINT
-[env-blocked_services]: environment.md#BLOCKED_SERVICE_INDEX_URL
+
+
+## Additional Metrics Info
+
+The `additional_metrics_info` object is a map of strings with extra information
+which is exposed by `dns_app_additional_info` metric.
+
+Map keys must match reqular expression `^[a-zA-Z_][a-zA-Z0-9_]*$`. See
+[Prometheus documentation on valid labels][prom-label].
+
+**Property example:**
+
+```yaml
+'additional_metrics_info':
+ 'info_key_1': 'info_value_1'
+ 'info_key_2': 'info_value_2'
+```
+
+The Prometheus metrics key is `additional_info`. For example:
+
+```none
+# HELP dns_app_additional_info A metric with a constant '1' value labeled by additional info provided in configuration
+# TYPE dns_app_additional_info gauge
+dns_app_additional_info{info_key_1="info_value_1",info_key_2="info_value_2"} 1
+```
+
+[prom-label]: https://pkg.go.dev/github.com/prometheus/common/model#LabelNameRE
diff --git a/go.mod b/go.mod
index 06cfe37..a52341e 100644
--- a/go.mod
+++ b/go.mod
@@ -17,6 +17,7 @@ require (
github.com/patrickmn/go-cache v2.1.1-0.20191004192108-46f407853014+incompatible
github.com/prometheus/client_golang v1.13.0
github.com/prometheus/client_model v0.2.0
+ github.com/prometheus/common v0.37.0
github.com/stretchr/testify v1.8.0
go.etcd.io/bbolt v1.3.6
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e
@@ -48,7 +49,6 @@ require (
github.com/onsi/ginkgo v1.16.5 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
- github.com/prometheus/common v0.37.0 // indirect
github.com/prometheus/procfs v0.8.0 // indirect
golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8 // indirect
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
diff --git a/internal/agd/server.go b/internal/agd/server.go
index ef88f2b..c27c97a 100644
--- a/internal/agd/server.go
+++ b/internal/agd/server.go
@@ -14,7 +14,9 @@ import (
// ServerGroup is a group of DNS servers all of which use the same filtering
// settings.
type ServerGroup struct {
- // TLS are the TLS settings for this server group, if any.
+ // TLS are the TLS settings for this server group. If Servers contains at
+ // least one server with a non-plain protocol (see [Protocol.IsPlain]), TLS
+ // must not be nil.
TLS *TLS
// DDR is the configuration for the server group's Discovery Of Designated
diff --git a/internal/cmd/additional.go b/internal/cmd/additional.go
new file mode 100644
index 0000000..5e3359c
--- /dev/null
+++ b/internal/cmd/additional.go
@@ -0,0 +1,30 @@
+package cmd
+
+import (
+ "fmt"
+
+ "github.com/prometheus/common/model"
+ "golang.org/x/exp/maps"
+ "golang.org/x/exp/slices"
+)
+
+// additionalInfo is a extra info configuration.
+type additionalInfo map[string]string
+
+// validateAdditionalInfo return an error is the section is invalid.
+func (c additionalInfo) validate() (err error) {
+ if c == nil {
+ return nil
+ }
+
+ keys := maps.Keys(c)
+ slices.Sort(keys)
+
+ for _, k := range keys {
+ if !model.LabelName(k).IsValid() {
+ return fmt.Errorf("prometheus labels must match %s, got %q", model.LabelNameRE, k)
+ }
+ }
+
+ return nil
+}
diff --git a/internal/cmd/cmd.go b/internal/cmd/cmd.go
index a0046e3..3f717f2 100644
--- a/internal/cmd/cmd.go
+++ b/internal/cmd/cmd.go
@@ -64,6 +64,10 @@ func Main() {
err = c.validate()
fatalOnError(err)
+ // Additional Metrics
+
+ metrics.SetAdditionalInfo(c.AdditionalMetricsInfo)
+
// GeoIP Database
// We start GeoIP initialization early in a dedicated routine cause it
diff --git a/internal/cmd/config.go b/internal/cmd/config.go
index ca15b51..7c44312 100644
--- a/internal/cmd/config.go
+++ b/internal/cmd/config.go
@@ -66,6 +66,9 @@ type configuration struct {
// ConnectivityCheck is the connectivity check configuration.
ConnectivityCheck *connCheckConfig `yaml:"connectivity_check"`
+ // AdditionalMetricsInfo is extra information, which is exposed by metrics.
+ AdditionalMetricsInfo additionalInfo `yaml:"additional_metrics_info"`
+
// FilteringGroups are the predefined filtering configurations that are used
// for different server groups.
FilteringGroups filteringGroups `yaml:"filtering_groups"`
@@ -142,6 +145,9 @@ func (c *configuration) validate() (err error) {
}, {
validate: c.ConnectivityCheck.validate,
name: "connectivity_check",
+ }, {
+ validate: c.AdditionalMetricsInfo.validate,
+ name: "additional_metrics_info",
}}
for _, v := range validators {
diff --git a/internal/cmd/websvc.go b/internal/cmd/websvc.go
index 187aed4..c72a2ba 100644
--- a/internal/cmd/websvc.go
+++ b/internal/cmd/websvc.go
@@ -386,6 +386,9 @@ func (sc staticContent) validate() (err error) {
// staticFile is a single file in a static content mapping.
type staticFile struct {
+ // AllowOrigin is the value for the HTTP Access-Control-Allow-Origin header.
+ AllowOrigin string `yaml:"allow_origin"`
+
// ContentType is the value for the HTTP Content-Type header.
ContentType string `yaml:"content_type"`
@@ -397,6 +400,7 @@ type staticFile struct {
// assumed to be valid.
func (f *staticFile) toInternal() (file *websvc.StaticFile, err error) {
file = &websvc.StaticFile{
+ AllowOrigin: f.AllowOrigin,
ContentType: f.ContentType,
}
diff --git a/internal/dnsserver/protocol.go b/internal/dnsserver/protocol.go
index d8e84fb..828910c 100644
--- a/internal/dnsserver/protocol.go
+++ b/internal/dnsserver/protocol.go
@@ -77,6 +77,11 @@ func (p Protocol) ALPN() (alpn []string) {
}
}
+// IsPlain returns true if the protocol is a plain DNS over TCP or UDP.
+func (p Protocol) IsPlain() (ok bool) {
+ return p == ProtoDNSTCP || p == ProtoDNSUDP
+}
+
// IsStdEncrypted returns true if the protocol is one of the standard encrypted
// DNS protocol as defined by an RFC.
func (p Protocol) IsStdEncrypted() (ok bool) {
diff --git a/internal/dnsserver/serverdns.go b/internal/dnsserver/serverdns.go
index c876b8e..4e422c9 100644
--- a/internal/dnsserver/serverdns.go
+++ b/internal/dnsserver/serverdns.go
@@ -77,13 +77,11 @@ type ServerDNS struct {
// type check
var _ Server = (*ServerDNS)(nil)
-// NewServerDNS creates a new ServerDNS instance.
+// NewServerDNS creates a new ServerDNS instance. conf.Proto must be either
+// [ProtoDNSTCP] or [ProtoDNSUDP].
func NewServerDNS(conf ConfigDNS) (s *ServerDNS) {
- switch conf.Proto {
- case ProtoDNSTCP, ProtoDNSUDP:
- // Go on.
- default:
- panic(fmt.Errorf("invalid proto %v in NewServerDNS", conf.Proto))
+ if !conf.Proto.IsPlain() {
+ panic(fmt.Errorf("invalid proto %s in NewServerDNS", conf.Proto))
}
return newServerDNS(conf)
diff --git a/internal/dnssvc/deviceid.go b/internal/dnssvc/deviceid.go
index e49f6b8..bc4862d 100644
--- a/internal/dnssvc/deviceid.go
+++ b/internal/dnssvc/deviceid.go
@@ -13,6 +13,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
"github.com/AdguardTeam/AdGuardDNS/internal/optlog"
"github.com/AdguardTeam/golibs/errors"
+ "github.com/miekg/dns"
)
// Device ID Extraction
@@ -163,3 +164,39 @@ func deviceIDFromContext(
return id, nil
}
+
+// dnsmasqCPEIDOption is the identifier of dnsmasq EDNS0 option
+// `EDNS0_OPTION_NOMCPEID`.
+//
+// See: https://github.com/PowerDNS/dnsmasq/blob/master/src/dns-protocol.h.
+const dnsmasqCPEIDOption uint16 = 65074
+
+// deviceIDFromEDNS extracts the device ID from EDNS0 option of plain DNS
+// request. This method works with dnsmasq option `--add-cpe-id`, which adds
+// an identifying string to DNS queries through [dnsmasqCPEIDOption] option as
+// a non-standard support of Nominum servers.
+//
+// Requests of this kind could be emulated with `+ednsopt` option of `dig`
+// utility.
+// TODO(a.garipov): Add test documentation.
+func deviceIDFromEDNS(req *dns.Msg) (id agd.DeviceID, err error) {
+ option := req.IsEdns0()
+ if option == nil {
+ return "", nil
+ }
+
+ for _, opt := range option.Option {
+ if opt.Option() != dnsmasqCPEIDOption {
+ continue
+ }
+
+ o, ok := opt.(*dns.EDNS0_LOCAL)
+ if !ok {
+ continue
+ }
+
+ return agd.NewDeviceID(string(o.Data))
+ }
+
+ return "", nil
+}
diff --git a/internal/dnssvc/deviceid_internal_test.go b/internal/dnssvc/deviceid_internal_test.go
index 1a3f136..7ccfb21 100644
--- a/internal/dnssvc/deviceid_internal_test.go
+++ b/internal/dnssvc/deviceid_internal_test.go
@@ -6,8 +6,11 @@ import (
"testing"
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
+ "github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver"
+ "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/dnsservertest"
"github.com/AdguardTeam/golibs/testutil"
+ "github.com/miekg/dns"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -204,3 +207,66 @@ func TestService_Wrap_deviceIDHTTPS(t *testing.T) {
assert.Equal(t, agd.DeviceID(want), deviceID)
})
}
+
+func TestService_Wrap_deviceIDFromEDNS(t *testing.T) {
+ testCases := []struct {
+ name string
+ opt dns.EDNS0
+ wantDeviceID agd.DeviceID
+ wantErrMsg string
+ }{{
+ name: "no_device_id",
+ wantDeviceID: "",
+ wantErrMsg: "",
+ }, {
+ name: "wrong_edns",
+ opt: &dns.EDNS0_LOCAL{
+ Code: dnsmasqCPEIDOption - 1,
+ Data: []byte("devid"),
+ },
+ wantDeviceID: "",
+ wantErrMsg: "",
+ }, {
+ name: "no_device_id",
+ opt: &dns.EDNS0_LOCAL{
+ Code: dnsmasqCPEIDOption,
+ Data: []byte{},
+ },
+ wantDeviceID: "",
+ wantErrMsg: `bad device id "": too short: got 0 bytes, min 1`,
+ }, {
+ name: "bad_device_id",
+ opt: &dns.EDNS0_LOCAL{
+ Code: dnsmasqCPEIDOption,
+ Data: []byte("toolongdeviceid"),
+ },
+ wantDeviceID: "",
+ wantErrMsg: `bad device id "toolongdeviceid": too long: got 15 bytes, max 8`,
+ }, {
+ name: "device_id",
+ opt: &dns.EDNS0_LOCAL{
+ Code: dnsmasqCPEIDOption,
+ Data: []byte("devid"),
+ },
+ wantDeviceID: "devid",
+ wantErrMsg: "",
+ }}
+
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ msg := dnsservertest.NewReq("example.com.", dns.TypeA, dns.ClassINET)
+ if tc.opt != nil {
+ msg.SetEdns0(dnsmsg.DefaultEDNSUDPSize, true)
+ extra := &dns.OPT{
+ Hdr: dns.RR_Header{Name: ".", Rrtype: dns.TypeOPT},
+ Option: []dns.EDNS0{tc.opt},
+ }
+ msg.Extra = append(msg.Extra, extra)
+ }
+
+ deviceID, err := deviceIDFromEDNS(msg)
+ assert.Equal(t, tc.wantDeviceID, deviceID)
+ testutil.AssertErrorMsg(t, tc.wantErrMsg, err)
+ })
+ }
+}
diff --git a/internal/dnssvc/initmw.go b/internal/dnssvc/initmw.go
index 00279e5..bf6dbd2 100644
--- a/internal/dnssvc/initmw.go
+++ b/internal/dnssvc/initmw.go
@@ -172,7 +172,7 @@ func (mw *initMw) newRequestInfo(
}
// Add the profile information, if any.
- err = mw.addProfile(ctx, ri)
+ err = mw.addProfile(ctx, ri, req)
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return nil, err
@@ -245,15 +245,23 @@ func (mw *initMw) locationData(ctx context.Context, ip netip.Addr, typ string) (
// addProfile adds profile and device information, if any, to the request
// information.
-func (mw *initMw) addProfile(ctx context.Context, ri *agd.RequestInfo) (err error) {
+func (mw *initMw) addProfile(ctx context.Context, ri *agd.RequestInfo, req *dns.Msg) (err error) {
defer func() { err = errors.Annotate(err, "getting profile from req: %w") }()
var id agd.DeviceID
- if tlsConf := mw.srvGrp.TLS; tlsConf != nil {
- id, err = deviceIDFromContext(ctx, mw.srv.Protocol, tlsConf.DeviceIDWildcards)
- if err != nil {
- return err
- }
+ if p := mw.srv.Protocol; p.IsStdEncrypted() {
+ // Assume that mw.srvGrp.TLS is non-nil if p.IsStdEncrypted() is true.
+ wildcards := mw.srvGrp.TLS.DeviceIDWildcards
+ id, err = deviceIDFromContext(ctx, mw.srv.Protocol, wildcards)
+ } else if p.IsPlain() {
+ id, err = deviceIDFromEDNS(req)
+ } else {
+ // No DeviceID for DNSCrypt yet.
+ return nil
+ }
+
+ if err != nil {
+ return err
}
optlog.Debug2("init mw: got device id %q and ip %s", id, ri.RemoteIP)
@@ -300,7 +308,7 @@ func (mw *initMw) profile(
optlog.Debug1("init mw: not matching by ip for server %s", mw.srv.Name)
return nil, nil, "", agd.ProfileNotFoundError{}
- } else if p != agd.ProtoDNSUDP && p != agd.ProtoDNSTCP {
+ } else if !p.IsPlain() {
optlog.Debug1("init mw: not matching by ip for proto %v", p)
return nil, nil, "", agd.ProfileNotFoundError{}
diff --git a/internal/filter/hashstorage.go b/internal/filter/hashstorage.go
index 2aefc37..e0f0891 100644
--- a/internal/filter/hashstorage.go
+++ b/internal/filter/hashstorage.go
@@ -1,6 +1,7 @@
package filter
import (
+ "bufio"
"context"
"crypto/sha256"
"encoding/hex"
@@ -290,28 +291,11 @@ func (hs *HashStorage) refresh(ctx context.Context, acceptStale bool) (err error
return hs.refr.refresh(ctx, acceptStale)
}
-// splitHosts splits a text made of hostnames, empty strings, and comments and
-// returns only the strings containing hostnames.
-//
-// TODO(a.garipov): Consider additional validation to make sure that it
-// actually is a list of hosts and NOT a list of filtering rules.
-func splitHosts(text string) (hosts []string) {
- hosts = strings.Split(text, "\n")
- hosts = stringutil.FilterOut(hosts, func(s string) (ok bool) {
- return len(s) == 0 || s[0] == '#'
- })
-
- return hosts
-}
-
-// resetHosts resets the hosts in the index. err is always nil, as it is only
-// there for compatibility with refreshFromFileOrURL.
+// resetHosts resets the hosts in the index.
func (hs *HashStorage) resetHosts(hostsStr string) (err error) {
hs.mu.Lock()
defer hs.mu.Unlock()
- hosts := splitHosts(hostsStr)
-
// Delete all elements without allocating a new map to safe space and
// performance.
//
@@ -320,17 +304,31 @@ func (hs *HashStorage) resetHosts(hostsStr string) (err error) {
delete(hs.hashSuffixes, hp)
}
- for _, h := range hosts {
- sum := sha256.Sum256([]byte(h))
+ var n int
+ s := bufio.NewScanner(strings.NewReader(hostsStr))
+ for s.Scan() {
+ host := s.Text()
+ if len(host) == 0 || host[0] == '#' {
+ continue
+ }
+
+ sum := sha256.Sum256([]byte(host))
hp := hashPrefix{sum[0], sum[1]}
// TODO(a.garipov): Convert to array directly when proposal
// golang/go#46505 is implemented in Go 1.20.
suf := *(*hashSuffix)(sum[hashPrefixLen:])
hs.hashSuffixes[hp] = append(hs.hashSuffixes[hp], suf)
+
+ n++
}
- log.Info("filter %s: reset %d hosts", hs.id(), len(hosts))
+ err = s.Err()
+ if err != nil {
+ return fmt.Errorf("scanning hosts: %w", err)
+ }
+
+ log.Info("filter %s: reset %d hosts", hs.id(), n)
return nil
}
diff --git a/internal/filter/hashstorage_internal_test.go b/internal/filter/hashstorage_internal_test.go
index cb0e021..db61dce 100644
--- a/internal/filter/hashstorage_internal_test.go
+++ b/internal/filter/hashstorage_internal_test.go
@@ -50,3 +50,42 @@ func BenchmarkHashStorage_Hashes(b *testing.B) {
})
}
}
+
+func BenchmarkHashStorage_resetHosts(b *testing.B) {
+ const N = 1_000
+
+ var hosts []string
+ for i := 0; i < N; i++ {
+ hosts = append(hosts, fmt.Sprintf("%d.porn.example.com", i))
+ }
+
+ // Don't use a constructor, since we don't need the whole contents of the
+ // storage.
+ //
+ // TODO(a.garipov): Think of a better way to do this.
+ hs := &HashStorage{
+ mu: &sync.RWMutex{},
+ hashSuffixes: map[hashPrefix][]hashSuffix{},
+ refr: &refreshableFilter{id: "test_filter"},
+ }
+
+ // Reset them once to fill the initial map.
+ err := hs.resetHosts(strings.Join(hosts, "\n"))
+ require.NoError(b, err)
+
+ b.ReportAllocs()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ err = hs.resetHosts(strings.Join(hosts, "\n"))
+ }
+
+ require.NoError(b, err)
+
+ // Most recent result, on a ThinkPad X13 with a Ryzen Pro 7 CPU:
+ //
+ // goos: linux
+ // goarch: amd64
+ // pkg: github.com/AdguardTeam/AdGuardDNS/internal/filter
+ // cpu: AMD Ryzen 7 PRO 4750U with Radeon Graphics
+ // BenchmarkHashStorage_resetHosts-16 1404 829505 ns/op 58289 B/op 1011 allocs/op
+}
diff --git a/internal/filter/storage_test.go b/internal/filter/storage_test.go
index b6b984b..fa7da7a 100644
--- a/internal/filter/storage_test.go
+++ b/internal/filter/storage_test.go
@@ -392,9 +392,10 @@ func BenchmarkStorage_NewDefaultStorage(b *testing.B) {
assert.NotNil(b, defaultStorageSink)
assert.NoError(b, errSink)
- // Recent result on MBP 15
- // goos: darwin
- // goarch: amd64
- // cpu: Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz
- // BenchmarkStorage_NewDefaultStorage/success-12 3238 344513 ns/op 198096 B/op 952 allocs/op
+ // Recent result on MBP 15:
+ //
+ // goos: darwin
+ // goarch: amd64
+ // cpu: Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz
+ // BenchmarkStorage_NewDefaultStorage/success-12 3238 344513 ns/op 198096 B/op 952 allocs/op
}
diff --git a/internal/geoip/file_test.go b/internal/geoip/file_test.go
index c4c566b..bfd7c96 100644
--- a/internal/geoip/file_test.go
+++ b/internal/geoip/file_test.go
@@ -184,9 +184,10 @@ func BenchmarkNewFile(b *testing.B) {
assert.NotNil(b, fileSink)
assert.NoError(b, errSink)
- // Recent result on MBP 15
- // goos: darwin
- // goarch: amd64
- // cpu: Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz
- // BenchmarkNewFile-12 2192 532262 ns/op 180929 B/op 5980 allocs/op
+ // Recent result on MBP 15:
+ //
+ // goos: darwin
+ // goarch: amd64
+ // cpu: Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz
+ // BenchmarkNewFile-12 2192 532262 ns/op 180929 B/op 5980 allocs/op
}
diff --git a/internal/metrics/metrics.go b/internal/metrics/metrics.go
index d198080..0d163c9 100644
--- a/internal/metrics/metrics.go
+++ b/internal/metrics/metrics.go
@@ -69,3 +69,24 @@ func BoolString(cond bool) (s string) {
return "0"
}
+
+// SetAdditionalInfo adds a gauge with extra info labels. If info is nil,
+// SetAdditionalInfo does nothing.
+func SetAdditionalInfo(info map[string]string) {
+ if info == nil {
+ return
+ }
+
+ gauge := promauto.NewGauge(
+ prometheus.GaugeOpts{
+ Name: "additional_info",
+ Namespace: namespace,
+ Subsystem: subsystemApplication,
+ Help: `A metric with a constant '1' value labeled by additional ` +
+ `info provided in configuration`,
+ ConstLabels: info,
+ },
+ )
+
+ gauge.Set(1)
+}
diff --git a/internal/querylog/fs_test.go b/internal/querylog/fs_test.go
index 1b67928..bcc152f 100644
--- a/internal/querylog/fs_test.go
+++ b/internal/querylog/fs_test.go
@@ -114,9 +114,9 @@ func BenchmarkFileSystem_Write_file(b *testing.B) {
// Most recent result, on a ThinkPad X13 with a Ryzen Pro 7 CPU:
//
- // goos: linux
- // goarch: amd64
- // pkg: github.com/AdguardTeam/AdGuardDNS/internal/querylog
- // cpu: AMD Ryzen 7 PRO 4750U with Radeon Graphics
- // BenchmarkFileSystem_Write_file-16 244162 5000 ns/op 200 B/op 3 allocs/op
+ // goos: linux
+ // goarch: amd64
+ // pkg: github.com/AdguardTeam/AdGuardDNS/internal/querylog
+ // cpu: AMD Ryzen 7 PRO 4750U with Radeon Graphics
+ // BenchmarkFileSystem_Write_file-16 244162 5000 ns/op 200 B/op 3 allocs/op
}
diff --git a/internal/tools/go.mod b/internal/tools/go.mod
index f2605fc..955f271 100644
--- a/internal/tools/go.mod
+++ b/internal/tools/go.mod
@@ -10,10 +10,11 @@ require (
github.com/kyoh86/looppointer v0.1.8-0.20220224024524-f953a93c424a
github.com/securego/gosec/v2 v2.13.1
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616
- golang.org/x/tools v0.1.12
+ golang.org/x/tools v0.1.13-0.20220803210227-8b9a1fbdf5c3
+ golang.org/x/vuln v0.0.0-20220902211423-27dd78d2ca39
honnef.co/go/tools v0.3.3
mvdan.cc/gofumpt v0.3.1
- mvdan.cc/unparam v0.0.0-20220706161116-678bad134442
+ mvdan.cc/unparam v0.0.0-20220831102321-2fc90a84c7ec
)
require (
@@ -21,13 +22,14 @@ require (
github.com/client9/misspell v0.3.4 // indirect
github.com/google/go-cmp v0.5.8 // indirect
github.com/google/uuid v1.3.0 // indirect
- github.com/gookit/color v1.5.1 // indirect
+ github.com/gookit/color v1.5.2 // indirect
github.com/kyoh86/nolint v0.0.1 // indirect
github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354 // indirect
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect
- golang.org/x/exp/typeparams v0.0.0-20220722155223-a9213eeb770e // indirect
+ golang.org/x/exp v0.0.0-20220827204233-334a2380cb91 // indirect
+ golang.org/x/exp/typeparams v0.0.0-20220827204233-334a2380cb91 // indirect
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde // indirect
- golang.org/x/sys v0.0.0-20220818161305-2296e01440c6 // indirect
+ golang.org/x/sys v0.0.0-20220906135438-9e1f76180b77 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)
diff --git a/internal/tools/go.sum b/internal/tools/go.sum
index 8c1d4a4..72e5309 100644
--- a/internal/tools/go.sum
+++ b/internal/tools/go.sum
@@ -2,19 +2,22 @@ github.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0
github.com/BurntSushi/toml v1.2.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
-github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/frankban/quicktest v1.14.2 h1:SPb1KFFmM+ybpEjPUhCCkZOM5xlovT5UbrMvWnXyBns=
github.com/fzipp/gocyclo v0.6.0 h1:lsblElZG7d3ALtGMx9fmxeTKZaLLpU8mET09yN4BBLo=
github.com/fzipp/gocyclo v0.6.0/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlyaLkLoA=
github.com/golangci/misspell v0.3.5 h1:pLzmVdl3VxTOncgzHcvLOKirdvcx/TydsClUQXTehjo=
github.com/golangci/misspell v0.3.5/go.mod h1:dEbvlSfYbMQDtrpRMQU675gSDLDNa8sCPPChZ7PhiVA=
+github.com/google/go-cmdtest v0.4.0 h1:ToXh6W5spLp3npJV92tk6d5hIpUPYEzHLkD+rncbyhI=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA=
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/gookit/color v1.5.1 h1:Vjg2VEcdHpwq+oY63s/ksHrgJYCTo0bwWvmmYWdE9fQ=
-github.com/gookit/color v1.5.1/go.mod h1:wZFzea4X8qN6vHOSP2apMb4/+w/orMznEzYsIHPaqKM=
+github.com/gookit/color v1.5.2 h1:uLnfXcaFjlrDnQDT+NCBcfhrXqYTx/rcCa6xn01Y8yI=
+github.com/gookit/color v1.5.2/go.mod h1:w8h4bGiHeeBpvQVePTutdbERIUf3oJE5lZ8HM0UgXyg=
github.com/gordonklaus/ineffassign v0.0.0-20210914165742-4cc7213b9bc8 h1:PVRE9d4AQKmbelZ7emNig1+NT27DUmKZn5qXxfio54U=
github.com/gordonklaus/ineffassign v0.0.0-20210914165742-4cc7213b9bc8/go.mod h1:Qcp2HIAYhR7mNUVSIxZww3Guk4it82ghYcEXIAk+QT0=
github.com/kisielk/errcheck v1.6.2 h1:uGQ9xI8/pgc9iOoCe7kWQgRE6SBTrCGmTSf0LrEtY7c=
@@ -36,9 +39,11 @@ github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XF
github.com/securego/gosec/v2 v2.13.1 h1:7mU32qn2dyC81MH9L2kefnQyRMUarfDER3iQyMHcjYM=
github.com/securego/gosec/v2 v2.13.1/go.mod h1:EO1sImBMBWFjOTFzMWfTRrZW6M15gm60ljzrmy/wtHo=
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/testify v1.1.4/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
-github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s=
-github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
+github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
+github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8=
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@@ -48,8 +53,10 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/exp/typeparams v0.0.0-20220722155223-a9213eeb770e h1:7Xs2YCOpMlNqSQSmrrnhlzBXIE/bpMecZplbLePTJvE=
-golang.org/x/exp/typeparams v0.0.0-20220722155223-a9213eeb770e/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
+golang.org/x/exp v0.0.0-20220827204233-334a2380cb91 h1:tnebWN09GYg9OLPss1KXj8txwZc6X6uMr6VFdcGNbHw=
+golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
+golang.org/x/exp/typeparams v0.0.0-20220827204233-334a2380cb91 h1:Ic/qN6TEifvObMGQy72k0n1LlJr7DjWWEi+MOsDOiSk=
+golang.org/x/exp/typeparams v0.0.0-20220827204233-334a2380cb91/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug=
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
@@ -81,8 +88,8 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220224003255-dbe011f71a99/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220818161305-2296e01440c6 h1:Sx/u41w+OwrInGdEckYmEuU5gHoGSL4QbDz3S9s6j4U=
-golang.org/x/sys v0.0.0-20220818161305-2296e01440c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220906135438-9e1f76180b77 h1:C1tElbkWrsSkn3IRl1GCW/gETw1TywWIPgwZtXTZbYg=
+golang.org/x/sys v0.0.0-20220906135438-9e1f76180b77/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=
@@ -96,8 +103,10 @@ golang.org/x/tools v0.0.0-20201007032633-0806396f153e/go.mod h1:z6u4i615ZeAfBE4X
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
-golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
-golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
+golang.org/x/tools v0.1.13-0.20220803210227-8b9a1fbdf5c3 h1:aE4T3aJwdCNz+s35ScSQYUzeGu7BOLDHZ1bBHVurqqY=
+golang.org/x/tools v0.1.13-0.20220803210227-8b9a1fbdf5c3/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
+golang.org/x/vuln v0.0.0-20220902211423-27dd78d2ca39 h1:501+NfNjDh4IT4HOzdeezTOFD7njtY49aXJN1oY3E1s=
+golang.org/x/vuln v0.0.0-20220902211423-27dd78d2ca39/go.mod h1:7tDfEDtOLlzHQRi4Yzfg5seVBSvouUIjyPzBx4q5CxQ=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -106,11 +115,12 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogR
gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8=
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=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.3.3 h1:oDx7VAwstgpYpb3wv0oxiZlxY+foCpRAwY7Vk6XpAgA=
honnef.co/go/tools v0.3.3/go.mod h1:jzwdWgg7Jdq75wlfblQxO4neNaFFSvgc1tD5Wv8U0Yw=
mvdan.cc/gofumpt v0.3.1 h1:avhhrOmv0IuvQVK7fvwV91oFSGAk5/6Po8GXTzICeu8=
mvdan.cc/gofumpt v0.3.1/go.mod h1:w3ymliuxvzVx8DAutBnVyDqYb1Niy/yCJt/lk821YCE=
-mvdan.cc/unparam v0.0.0-20220706161116-678bad134442 h1:seuXWbRB1qPrS3NQnHmFKLJLtskWyueeIzmLXghMGgk=
-mvdan.cc/unparam v0.0.0-20220706161116-678bad134442/go.mod h1:F/Cxw/6mVrNKqrR2YjFf5CaW0Bw4RL8RfbEf4GRggJk=
+mvdan.cc/unparam v0.0.0-20220831102321-2fc90a84c7ec h1:uyA9l4gQQUHSm9zPgTzarWmsjIw7s7hAldLwVxLlu1Y=
+mvdan.cc/unparam v0.0.0-20220831102321-2fc90a84c7ec/go.mod h1:EAphbHIduKNGVweyBBwWQd24rSnLy4DsjlpxRFYE498=
diff --git a/internal/tools/tools.go b/internal/tools/tools.go
index 7d875da..230d6c4 100644
--- a/internal/tools/tools.go
+++ b/internal/tools/tools.go
@@ -10,9 +10,9 @@ import (
_ "github.com/kisielk/errcheck"
_ "github.com/kyoh86/looppointer"
_ "github.com/securego/gosec/v2/cmd/gosec"
- _ "golang.org/x/lint/golint"
_ "golang.org/x/tools/go/analysis/passes/nilness/cmd/nilness"
_ "golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow"
+ _ "golang.org/x/vuln/cmd/govulncheck"
_ "honnef.co/go/tools/cmd/staticcheck"
_ "mvdan.cc/gofumpt"
_ "mvdan.cc/unparam"
diff --git a/internal/websvc/handler.go b/internal/websvc/handler.go
index 05d90ca..92798f8 100644
--- a/internal/websvc/handler.go
+++ b/internal/websvc/handler.go
@@ -140,6 +140,9 @@ func (sc StaticContent) serveHTTP(w http.ResponseWriter, r *http.Request) (serve
return false
}
+ if f.AllowOrigin != "" {
+ w.Header().Set(agdhttp.HdrNameAccessControlAllowOrigin, f.ContentType)
+ }
w.Header().Set(agdhttp.HdrNameContentType, f.ContentType)
w.WriteHeader(http.StatusOK)
@@ -153,6 +156,9 @@ func (sc StaticContent) serveHTTP(w http.ResponseWriter, r *http.Request) (serve
// StaticFile is a single file in a StaticFS.
type StaticFile struct {
+ // AllowOrigin is the value for the HTTP Access-Control-Allow-Origin header.
+ AllowOrigin string
+
// ContentType is the value for the HTTP Content-Type header.
ContentType string
diff --git a/scripts/make/go-lint.sh b/scripts/make/go-lint.sh
index af24709..c1a0424 100644
--- a/scripts/make/go-lint.sh
+++ b/scripts/make/go-lint.sh
@@ -215,6 +215,8 @@ exit_on_output gofumpt --extra -e -l .
"$GO" vet ./... "$dnssrvmod"
+govulncheck ./... "$dnssrvmod"
+
gocyclo --over 10 .
ineffassign ./... "$dnssrvmod"
diff --git a/scripts/make/go-tools.sh b/scripts/make/go-tools.sh
index e6e793c..edbe026 100644
--- a/scripts/make/go-tools.sh
+++ b/scripts/make/go-tools.sh
@@ -30,22 +30,23 @@ readonly go
# prevent the "cannot install cross-compiled binaries when GOBIN is set" error.
env\
GOARCH=""\
- GOOS=""\
GOBIN="${PWD}/bin"\
+ GOOS=""\
GOWORK='off'\
"$go" install\
--modfile=./internal/tools/go.mod\
- $v_flags $x_flags\
+ $v_flags\
+ $x_flags\
github.com/fzipp/gocyclo/cmd/gocyclo\
github.com/golangci/misspell/cmd/misspell\
github.com/gordonklaus/ineffassign\
github.com/kisielk/errcheck\
github.com/kyoh86/looppointer/cmd/looppointer\
github.com/securego/gosec/v2/cmd/gosec\
- golang.org/x/lint/golint\
golang.org/x/tools/go/analysis/passes/fieldalignment/cmd/fieldalignment\
golang.org/x/tools/go/analysis/passes/nilness/cmd/nilness\
golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow\
+ golang.org/x/vuln/cmd/govulncheck\
honnef.co/go/tools/cmd/staticcheck\
mvdan.cc/gofumpt\
mvdan.cc/unparam\