Sync v2.0.1

This commit is contained in:
Andrey Meshkov 2022-09-07 20:18:00 +03:00
parent b6a98906a5
commit 328512e850
26 changed files with 374 additions and 76 deletions

View File

@ -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

View File

@ -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'

View File

@ -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
## <a href="#filtering_groups" id="filtering_groups" name="filtering_groups">Filtering Groups</a>
@ -530,6 +533,7 @@ The items of the `filtering_groups` array have the following properties:
* <a href="#fg-*-id" id="fg-*-id" name="fg-*-id">`id`</a>:
The unique ID of this filtering group.
**Example:** `default`.
* <a href="#fg-*-rule_lists" id="fg-*-rule_lists" name="fg-*-rule_lists">`rule_lists`</a>:
@ -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
## <a href="#additional_metrics_info" id="additional_metrics_info" name="additional_metrics_info">Additional Metrics Info</a>
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

2
go.mod
View File

@ -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

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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 {

View File

@ -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,
}

View File

@ -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) {

View File

@ -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)

View File

@ -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
}

View File

@ -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)
})
}
}

View File

@ -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{}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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
)

View File

@ -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=

View File

@ -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"

View File

@ -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

View File

@ -215,6 +215,8 @@ exit_on_output gofumpt --extra -e -l .
"$GO" vet ./... "$dnssrvmod"
govulncheck ./... "$dnssrvmod"
gocyclo --over 10 .
ineffassign ./... "$dnssrvmod"

View File

@ -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\