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\