PERFORMANCE: Refactor auditrecords.go to loop only once #1570 (#1658)

* stash

* Use rejectif idea

* rename

* wip!

* Convert old systems to new

* fixup!

* fix typo
This commit is contained in:
Tom Limoncelli 2022-08-11 17:24:47 -04:00 committed by GitHub
parent 443b99aa59
commit 31723ad146
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
52 changed files with 497 additions and 569 deletions

View file

@ -1047,7 +1047,7 @@ func makeTests(t *testing.T) []*TestGroup {
tc("CAA change flag", caa("@", "issuewild", 128, "example.com")),
),
testgroup("CAA with ;",
requires(providers.CanUseCAA), not("DIGITALOCEAN"),
requires(providers.CanUseCAA),
// Test support of ";" as a value
tc("CAA many records", caa("@", "issuewild", 0, ";")),
),

View file

@ -485,8 +485,10 @@ func ValidateAndNormalizeConfig(config *models.DNSConfig) (errs []error) {
// be performed.
continue
}
if err := providers.AuditRecords(provider.ProviderBase.ProviderType, domain.Records); err != nil {
errs = append(errs, fmt.Errorf("%s rejects domain %s: %w", provider.ProviderBase.ProviderType, domain.Name, err))
if es := providers.AuditRecords(provider.ProviderBase.ProviderType, domain.Records); len(es) != 0 {
for _, e := range es {
errs = append(errs, fmt.Errorf("%s rejects domain %s: %w", provider.ProviderBase.ProviderType, domain.Name, e))
}
}
}
}

View file

@ -153,6 +153,7 @@ func (z *ZoneGenData) generateZoneFileHelper(w io.Writer) error {
return nil
}
// FormatLine formats a zonefile line.
func FormatLine(lengths []int, fields []string) string {
c := 0
result := ""

View file

@ -411,7 +411,7 @@ zt.mup IN A 1.2.3.14
zap IN A 1.2.3.15
`
// func formatLine
// func FormatLine
func TestFormatLine(t *testing.T) {
tests := []struct {

View file

@ -1,159 +0,0 @@
package recordaudit
import (
"fmt"
"strings"
"github.com/StackExchange/dnscontrol/v3/models"
)
// Keep these in alphabetical order.
// TxtNoBackticks audits TXT records for strings that contain backticks.
func TxtNoBackticks(records []*models.RecordConfig) error {
for _, rc := range records {
if rc.HasFormatIdenticalToTXT() {
for _, txt := range rc.TxtStrings {
if strings.Contains(txt, "`") {
return fmt.Errorf("txtstring contains backtick")
}
}
}
}
return nil
}
// TxtNoSingleQuotes audits TXT records for strings that contain single-quotes.
func TxtNoSingleQuotes(records []*models.RecordConfig) error {
for _, rc := range records {
if rc.HasFormatIdenticalToTXT() {
for _, txt := range rc.TxtStrings {
if strings.Contains(txt, "'") {
return fmt.Errorf("txtstring contains single-quotes")
}
}
}
}
return nil
}
// TxtNoDoubleQuotes audits TXT records for strings that contain doublequotes.
func TxtNoDoubleQuotes(records []*models.RecordConfig) error {
for _, rc := range records {
if rc.HasFormatIdenticalToTXT() {
for _, txt := range rc.TxtStrings {
if strings.Contains(txt, `"`) {
return fmt.Errorf("txtstring contains doublequotes")
}
}
}
}
return nil
}
// TxtNoStringsExactlyLen255 audits TXT records for strings exactly 255 octets long.
// This is rare; you probably want to use TxtNoLongStrings() instead.
func TxtNoStringsExactlyLen255(records []*models.RecordConfig) error {
for _, rc := range records {
if rc.HasFormatIdenticalToTXT() { // TXT and similar:
for _, txt := range rc.TxtStrings {
if len(txt) == 255 {
return fmt.Errorf("txtstring length is 255")
}
}
}
}
return nil
}
// TxtNoStringsLen256orLonger audits TXT records for strings that are >255 octets.
func TxtNoStringsLen256orLonger(records []*models.RecordConfig) error {
for _, rc := range records {
if rc.HasFormatIdenticalToTXT() { // TXT and similar:
for _, txt := range rc.TxtStrings {
if len(txt) > 255 {
return fmt.Errorf("%q txtstring length > 255", rc.GetLabel())
}
}
}
}
return nil
}
// TxtNoMultipleStrings audits TXT records for multiple strings
func TxtNoMultipleStrings(records []*models.RecordConfig) error {
for _, rc := range records {
if rc.HasFormatIdenticalToTXT() { // TXT and similar:
if len(rc.TxtStrings) > 1 {
return fmt.Errorf("multiple strings in one txt")
}
}
}
return nil
}
// TxtNoTrailingSpace audits TXT records for strings that end with space.
func TxtNoTrailingSpace(records []*models.RecordConfig) error {
for _, rc := range records {
if rc.HasFormatIdenticalToTXT() { // TXT and similar:
for _, txt := range rc.TxtStrings {
if txt != "" && txt[ultimate(txt)] == ' ' {
return fmt.Errorf("txtstring ends with space")
}
}
}
}
return nil
}
// TxtNotEmpty audits TXT records for empty strings.
func TxtNotEmpty(records []*models.RecordConfig) error {
for _, rc := range records {
if rc.HasFormatIdenticalToTXT() { // TXT and similar:
// There must be strings.
if len(rc.TxtStrings) == 0 {
return fmt.Errorf("txt with no strings")
}
// Each string must be non-empty.
for _, txt := range rc.TxtStrings {
if len(txt) == 0 {
return fmt.Errorf("txtstring is empty")
}
}
}
}
return nil
}
// TxtNoUnpairedDoubleQuotes audits TXT records for strings that contain unpaired doublequotes.
func TxtNoUnpairedDoubleQuotes(records []*models.RecordConfig) error {
for _, rc := range records {
if rc.HasFormatIdenticalToTXT() {
for _, txt := range rc.TxtStrings {
if strings.Count(txt, `"`)%2 == 1 {
return fmt.Errorf("txtstring contains unpaired doublequotes")
}
}
}
}
return nil
}

46
pkg/rejectif/audit.go Normal file
View file

@ -0,0 +1,46 @@
package rejectif
import (
"github.com/StackExchange/dnscontrol/v3/models"
)
// Auditor stores a list of checks to be executed during Audit().
type Auditor struct {
checksFor map[string][]checker
}
type checker = func(*models.RecordConfig) error
// Add registers a function to call on each record of a given type.
func (aud *Auditor) Add(rtype string, fn checker) {
if aud.checksFor == nil {
aud.checksFor = map[string][]checker{}
}
aud.checksFor[rtype] = append(aud.checksFor[rtype], fn)
// SPF records get any checkers that TXT records do.
if rtype == "TXT" {
aud.Add("SPF", fn)
}
}
// Audit performs the audit. For each record it calls each function in
// the list of checks.
func (aud *Auditor) Audit(records models.Records) (errs []error) {
// No checks? Exit early.
if aud.checksFor == nil {
return nil
}
// For each record, call the checks for that type, gather errors.
for _, rc := range records {
for _, f := range aud.checksFor[rc.Type] {
e := f(rc)
if e != nil {
errs = append(errs, e)
}
}
}
return errs
}

18
pkg/rejectif/caa.go Normal file
View file

@ -0,0 +1,18 @@
package rejectif
import (
"fmt"
"strings"
"github.com/StackExchange/dnscontrol/v3/models"
)
// Keep these in alphabetical order.
// CaaTargetHasSemicolon audits CAA records for issues that contain semicolons.
func CaaTargetHasSemicolon(rc *models.RecordConfig) error {
if strings.Contains(rc.GetTargetField(), ";") {
return fmt.Errorf("caa target contains semicolon")
}
return nil
}

104
pkg/rejectif/txt.go Normal file
View file

@ -0,0 +1,104 @@
package rejectif
import (
"fmt"
"strings"
"github.com/StackExchange/dnscontrol/v3/models"
)
// Keep these in alphabetical order.
// TxtHasBackticks audits TXT records for strings that contain backticks.
func TxtHasBackticks(rc *models.RecordConfig) error {
for _, txt := range rc.TxtStrings {
if strings.Contains(txt, "`") {
return fmt.Errorf("txtstring contains backtick")
}
}
return nil
}
// TxtHasSingleQuotes audits TXT records for strings that contain single-quotes.
func TxtHasSingleQuotes(rc *models.RecordConfig) error {
for _, txt := range rc.TxtStrings {
if strings.Contains(txt, "'") {
return fmt.Errorf("txtstring contains single-quotes")
}
}
return nil
}
// TxtHasDoubleQuotes audits TXT records for strings that contain doublequotes.
func TxtHasDoubleQuotes(rc *models.RecordConfig) error {
for _, txt := range rc.TxtStrings {
if strings.Contains(txt, `"`) {
return fmt.Errorf("txtstring contains doublequotes")
}
}
return nil
}
// TxtIsExactlyLen255 audits TXT records for strings exactly 255 octets long.
// This is rare; you probably want to use TxtNoStringsLen256orLonger() instead.
func TxtIsExactlyLen255(rc *models.RecordConfig) error {
for _, txt := range rc.TxtStrings {
if len(txt) == 255 {
return fmt.Errorf("txtstring length is 255")
}
}
return nil
}
// TxtHasSegmentLen256orLonger audits TXT records for strings that are >255 octets.
func TxtHasSegmentLen256orLonger(rc *models.RecordConfig) error {
for _, txt := range rc.TxtStrings {
if len(txt) > 255 {
return fmt.Errorf("%q txtstring length > 255", rc.GetLabel())
}
}
return nil
}
// TxtHasMultipleSegments audits TXT records for multiple strings
func TxtHasMultipleSegments(rc *models.RecordConfig) error {
if len(rc.TxtStrings) > 1 {
return fmt.Errorf("multiple strings in one txt")
}
return nil
}
// TxtHasTrailingSpace audits TXT records for strings that end with space.
func TxtHasTrailingSpace(rc *models.RecordConfig) error {
for _, txt := range rc.TxtStrings {
if txt != "" && txt[ultimate(txt)] == ' ' {
return fmt.Errorf("txtstring ends with space")
}
}
return nil
}
// TxtIsEmpty audits TXT records for empty strings.
func TxtIsEmpty(rc *models.RecordConfig) error {
// There must be strings.
if len(rc.TxtStrings) == 0 {
return fmt.Errorf("txt with no strings")
}
// Each string must be non-empty.
for _, txt := range rc.TxtStrings {
if len(txt) == 0 {
return fmt.Errorf("txtstring is empty")
}
}
return nil
}
// TxtHasUnpairedDoubleQuotes audits TXT records for strings that contain unpaired doublequotes.
func TxtHasUnpairedDoubleQuotes(rc *models.RecordConfig) error {
for _, txt := range rc.TxtStrings {
if strings.Count(txt, `"`)%2 == 1 {
return fmt.Errorf("txtstring contains unpaired doublequotes")
}
}
return nil
}

View file

@ -1,4 +1,4 @@
package recordaudit
package rejectif
/*
I proposed that Go add something like "len()" that returns the highest

View file

@ -2,7 +2,9 @@ package akamaiedgedns
import "github.com/StackExchange/dnscontrol/v3/models"
// AuditRecords returns an error if any records are not supportable by this provider.
func AuditRecords(records []*models.RecordConfig) error {
// AuditRecords returns a list of errors corresponding to the records
// that aren't supported by this provider. If all records are
// supported, an empty list is returned.
func AuditRecords(records []*models.RecordConfig) []error {
return nil
}

View file

@ -12,6 +12,7 @@ import (
"github.com/StackExchange/dnscontrol/v3/models"
)
// ZoneListFilter describes a JSON list filter.
type ZoneListFilter struct {
Key string `json:"key"`
Value string `json:"value"`
@ -20,6 +21,7 @@ type ZoneListFilter struct {
Filter []*ZoneListFilter `json:"filters,omitempty"`
}
// ZoneListRequest describes a JSON zone list request.
type ZoneListRequest struct {
Filter []*ZoneListFilter `json:"filters"`
}
@ -27,11 +29,11 @@ type ZoneListRequest struct {
func (api *autoDnsProvider) request(method string, requestPath string, data interface{}) ([]byte, error) {
client := &http.Client{}
requestUrl := api.baseURL
requestUrl.Path = api.baseURL.Path + requestPath
requestURL := api.baseURL
requestURL.Path = api.baseURL.Path + requestPath
request := &http.Request{
URL: &requestUrl,
URL: &requestURL,
Header: api.defaultHeaders,
Method: method,
}
@ -50,7 +52,7 @@ func (api *autoDnsProvider) request(method string, requestPath string, data inte
responseText, _ := ioutil.ReadAll(response.Body)
if response.StatusCode != 200 {
return nil, errors.New("Request to " + requestUrl.Path + " failed: " + string(responseText))
return nil, errors.New("Request to " + requestURL.Path + " failed: " + string(responseText))
}
return responseText, nil

View file

@ -2,8 +2,9 @@ package autodns
import "github.com/StackExchange/dnscontrol/v3/models"
// AuditRecords returns an error if any records are not
// supportable by this provider.
func AuditRecords(records []*models.RecordConfig) error {
// AuditRecords returns a list of errors corresponding to the records
// that aren't supported by this provider. If all records are
// supported, an empty list is returned.
func AuditRecords(records []*models.RecordConfig) []error {
return nil
}

View file

@ -1,11 +1,10 @@
package axfrddns
import (
"github.com/StackExchange/dnscontrol/v3/models"
)
import "github.com/StackExchange/dnscontrol/v3/models"
// AuditRecords returns an error if any records are not
// supportable by this provider.
func AuditRecords(records []*models.RecordConfig) error {
// AuditRecords returns a list of errors corresponding to the records
// that aren't supported by this provider. If all records are
// supported, an empty list is returned.
func AuditRecords(records []*models.RecordConfig) []error {
return nil
}

View file

@ -1,11 +1,10 @@
package azuredns
import (
"github.com/StackExchange/dnscontrol/v3/models"
)
import "github.com/StackExchange/dnscontrol/v3/models"
// AuditRecords returns an error if any records are not
// supportable by this provider.
func AuditRecords(records []*models.RecordConfig) error {
// AuditRecords returns a list of errors corresponding to the records
// that aren't supported by this provider. If all records are
// supported, an empty list is returned.
func AuditRecords(records []*models.RecordConfig) []error {
return nil
}

View file

@ -1,11 +1,10 @@
package bind
import (
"github.com/StackExchange/dnscontrol/v3/models"
)
import "github.com/StackExchange/dnscontrol/v3/models"
// AuditRecords returns an error if any records are not
// supportable by this provider.
func AuditRecords(records []*models.RecordConfig) error {
// AuditRecords returns a list of errors corresponding to the records
// that aren't supported by this provider. If all records are
// supported, an empty list is returned.
func AuditRecords(records []*models.RecordConfig) []error {
return nil
}

View file

@ -2,24 +2,20 @@ package cloudflare
import (
"github.com/StackExchange/dnscontrol/v3/models"
"github.com/StackExchange/dnscontrol/v3/pkg/recordaudit"
"github.com/StackExchange/dnscontrol/v3/pkg/rejectif"
)
// AuditRecords returns an error if any records are not
// supportable by this provider.
func AuditRecords(records []*models.RecordConfig) error {
// AuditRecords returns a list of errors corresponding to the records
// that aren't supported by this provider. If all records are
// supported, an empty list is returned.
func AuditRecords(records []*models.RecordConfig) []error {
a := rejectif.Auditor{}
if err := recordaudit.TxtNoMultipleStrings(records); err != nil {
return err
} // Still needed as of 2022-06-18
a.Add("TXT", rejectif.TxtHasMultipleSegments) // Last verified 2022-06-18
if err := recordaudit.TxtNoTrailingSpace(records); err != nil {
return err
} // Still needed as of 2022-06-18
a.Add("TXT", rejectif.TxtHasTrailingSpace) // Last verified 2022-06-18
if err := recordaudit.TxtNotEmpty(records); err != nil {
return err
} // Still needed as of 2022-06-18
a.Add("TXT", rejectif.TxtIsEmpty) // Last verified 2022-06-18
return nil
return a.Audit(records)
}

View file

@ -2,36 +2,24 @@ package cloudns
import (
"github.com/StackExchange/dnscontrol/v3/models"
"github.com/StackExchange/dnscontrol/v3/pkg/recordaudit"
"github.com/StackExchange/dnscontrol/v3/pkg/rejectif"
)
// AuditRecords returns an error if any records are not
// supportable by this provider.
func AuditRecords(records []*models.RecordConfig) error {
// AuditRecords returns a list of errors corresponding to the records
// that aren't supported by this provider. If all records are
// supported, an empty list is returned.
func AuditRecords(records []*models.RecordConfig) []error {
a := rejectif.Auditor{}
if err := recordaudit.TxtNoBackticks(records); err != nil {
return err
}
// Still needed as of 2021-03-01
a.Add("TXT", rejectif.TxtHasBackticks) // Last verified 2021-03-01
if err := recordaudit.TxtNotEmpty(records); err != nil {
return err
}
// Still needed as of 2021-03-01
a.Add("TXT", rejectif.TxtIsEmpty) // Last verified 2021-03-01
if err := recordaudit.TxtNoTrailingSpace(records); err != nil {
return err
}
// Still needed as of 2021-03-01
a.Add("TXT", rejectif.TxtHasTrailingSpace) // Last verified 2021-03-01
if err := recordaudit.TxtNoDoubleQuotes(records); err != nil {
return err
}
// Still needed as of 2021-03-11
a.Add("TXT", rejectif.TxtHasDoubleQuotes) // Last verified 2021-03-01
if err := recordaudit.TxtNoMultipleStrings(records); err != nil {
return err
}
a.Add("TXT", rejectif.TxtHasMultipleSegments) // Last verified 2021-03-01
return nil
return a.Audit(records)
}

View file

@ -2,39 +2,34 @@ package cscglobal
import (
"github.com/StackExchange/dnscontrol/v3/models"
"github.com/StackExchange/dnscontrol/v3/pkg/recordaudit"
"github.com/StackExchange/dnscontrol/v3/pkg/rejectif"
)
// AuditRecords returns an error if any records are not
// supportable by this provider.
func AuditRecords(records []*models.RecordConfig) error {
// AuditRecords returns a list of errors corresponding to the records
// that aren't supported by this provider. If all records are
// supported, an empty list is returned.
func AuditRecords(records []*models.RecordConfig) []error {
a := rejectif.Auditor{}
// Each test should be encapsulated in a function that can be tested
// individually. If the test is of general use, add it to the
// recordaudit module.
a.Add("TXT", rejectif.TxtHasMultipleSegments) // Last verified 2022-06-10
// Each test should document the last time we verified the test was
// still needed. Sometimes companies change their API.
a.Add("TXT", rejectif.TxtHasTrailingSpace) // Last verified 2022-06-10
if err := recordaudit.TxtNoDoubleQuotes(records); err != nil {
return err
} // Needed as of 2022-08-08
a.Add("TXT", rejectif.TxtIsEmpty) // Last verified 2022-06-10
// if err := recordaudit.TxtNoStringsLen256orLonger(records); err != nil {
// return err
// } // Needed as of 2022-06-10
a.Add("TXT", rejectif.TxtHasDoubleQuotes) // Last verified 2022-08-08
if err := recordaudit.TxtNoMultipleStrings(records); err != nil {
return err
} // Needed as of 2022-06-10
if err := recordaudit.TxtNoTrailingSpace(records); err != nil {
return err
} // Needed as of 2022-06-10
if err := recordaudit.TxtNotEmpty(records); err != nil {
return err
} // Needed as of 2022-06-10
return nil
return a.Audit(records)
}
/* How To Write Providers:
Each test should be encapsulated in a function that can be tested
individually. If the test is of general use, add it to the
rejectif module.
The "Last verified" comment logs the last time we verified this
test was needed. Sometimes companies change their API. Once a year,
try removing tests one at a time to verify they are still needed.
*/

View file

@ -4,8 +4,9 @@ import (
"github.com/StackExchange/dnscontrol/v3/models"
)
// AuditRecords returns an error if any records are not
// supportable by this provider.
func AuditRecords(records []*models.RecordConfig) error {
// AuditRecords returns a list of errors corresponding to the records
// that aren't supported by this provider. If all records are
// supported, an empty list is returned.
func AuditRecords(records []*models.RecordConfig) []error {
return nil
}

View file

@ -2,37 +2,32 @@ package digitalocean
import (
"fmt"
"github.com/StackExchange/dnscontrol/v3/models"
"github.com/StackExchange/dnscontrol/v3/pkg/recordaudit"
"github.com/StackExchange/dnscontrol/v3/pkg/rejectif"
)
// AuditRecords returns an error if any records are not
// supportable by this provider.
func AuditRecords(records []*models.RecordConfig) error {
// AuditRecords returns a list of errors corresponding to the records
// that aren't supported by this provider. If all records are
// supported, an empty list is returned.
func AuditRecords(records []*models.RecordConfig) []error {
a := rejectif.Auditor{}
// TODO(tlim): Audit CAA records.
// "Semicolons not supported in issue/issuewild fields.", "https://www.digitalocean.com/docs/networking/dns/how-to/create-caa-records"),
// Users are warned about these limits in docs/_providers/digitalocean.md
a.Add("TXT", MaxLengthDO) // Last verified 2021-03-01
if err := MaxLengthDO(records); err != nil {
return err
}
// Still needed as of 2021-03-01
a.Add("CAA", rejectif.CaaTargetHasSemicolon) // Last verified 2021-03-01
a.Add("TXT", rejectif.TxtHasDoubleQuotes) // Last verified 2021-03-01
// Double-quotes not permitted in TXT strings. I have a hunch that
// this is due to a broken parser on the DO side.
if err := recordaudit.TxtNoDoubleQuotes(records); err != nil {
return err
}
// Still needed as of 2021-03-01
return nil
return a.Audit(records)
}
// MaxLengthDO returns and error if the strings are longer than
// MaxLengthDO returns and error if the string is longer than
// permitted by DigitalOcean. Sadly their length limit is
// undocumented. This is a guess.
func MaxLengthDO(records []*models.RecordConfig) error {
func MaxLengthDO(rc *models.RecordConfig) error {
// The total length of all strings can't be longer than 512; and in
// reality must be shorter due to sloppy validation checks.
// https://github.com/StackExchange/dnscontrol/issues/370
@ -47,14 +42,10 @@ func MaxLengthDO(records []*models.RecordConfig) error {
// In other words, they're doing the checking on the API protocol
// encoded data instead of on on the resulting TXT record. Sigh.
for _, rc := range records {
if rc.HasFormatIdenticalToTXT() { // TXT and similar:
if len(rc.GetTargetField()) > 509 {
return fmt.Errorf("encoded txt too long")
}
}
if len(rc.GetTargetField()) > 509 {
return fmt.Errorf("encoded txt too long")
}
// FIXME(tlim): Try replacing GetTargetField() with (2 + (3*len(rc.TxtStrings) - 1))
return nil
}

View file

@ -2,28 +2,23 @@ package dnsimple
import (
"github.com/StackExchange/dnscontrol/v3/models"
"github.com/StackExchange/dnscontrol/v3/pkg/recordaudit"
"github.com/StackExchange/dnscontrol/v3/pkg/rejectif"
)
// AuditRecords returns an error if any records are not
// supportable by this provider.
func AuditRecords(records []*models.RecordConfig) error {
// AuditRecords returns a list of errors corresponding to the records
// that aren't supported by this provider. If all records are
// supported, an empty list is returned.
func AuditRecords(records []*models.RecordConfig) []error {
a := rejectif.Auditor{}
a.Add("TXT", rejectif.TxtHasMultipleSegments) // Last verified 2022-07
//TODO(onlyhavecans) I think we can support multiple strings.
if err := recordaudit.TxtNoMultipleStrings(records); err != nil {
return err
}
if err := recordaudit.TxtNoTrailingSpace(records); err != nil {
return err
} // as of 2022-07
a.Add("TXT", rejectif.TxtHasTrailingSpace) // Last verified 2022-07
if err := recordaudit.TxtNotEmpty(records); err != nil {
return err
} // as of 2022-07
a.Add("TXT", rejectif.TxtIsEmpty) // Last verified 2022-07
if err := recordaudit.TxtNoUnpairedDoubleQuotes(records); err != nil {
return err
} // as of 2022-07
a.Add("TXT", rejectif.TxtHasUnpairedDoubleQuotes) // Last verified 2022-07
return nil
return a.Audit(records)
}

View file

@ -2,16 +2,16 @@ package dnsmadeeasy
import (
"github.com/StackExchange/dnscontrol/v3/models"
"github.com/StackExchange/dnscontrol/v3/pkg/recordaudit"
"github.com/StackExchange/dnscontrol/v3/pkg/rejectif"
)
// AuditRecords returns an error if any records are not
// supportable by this provider.
func AuditRecords(records []*models.RecordConfig) error {
if err := recordaudit.TxtNoDoubleQuotes(records); err != nil {
return err
}
// Still needed as of 2021-03-11
// AuditRecords returns a list of errors corresponding to the records
// that aren't supported by this provider. If all records are
// supported, an empty list is returned.
func AuditRecords(records []*models.RecordConfig) []error {
a := rejectif.Auditor{}
return nil
a.Add("TXT", rejectif.TxtHasDoubleQuotes) // Last verified 2021-03-11
return a.Audit(records)
}

View file

@ -1,11 +1,10 @@
package doh
import (
"github.com/StackExchange/dnscontrol/v3/models"
)
import "github.com/StackExchange/dnscontrol/v3/models"
// AuditRecords returns an error if any records are not
// supportable by this provider.
func AuditRecords(records []*models.RecordConfig) error {
// AuditRecords returns a list of errors corresponding to the records
// that aren't supported by this provider. If all records are
// supported, an empty list is returned.
func AuditRecords(records []*models.RecordConfig) []error {
return nil
}

View file

@ -0,0 +1,10 @@
package domainnameshop
import "github.com/StackExchange/dnscontrol/v3/models"
// AuditRecords returns a list of errors corresponding to the records
// that aren't supported by this provider. If all records are
// supported, an empty list is returned.
func AuditRecords(records []*models.RecordConfig) []error {
return nil
}

View file

@ -4,7 +4,6 @@ import (
"encoding/json"
"fmt"
"github.com/StackExchange/dnscontrol/v3/models"
"github.com/StackExchange/dnscontrol/v3/providers"
)
@ -46,7 +45,7 @@ var features = providers.DocumentationNotes{
func init() {
fns := providers.DspFuncs{
Initializer: newDomainNameShopProvider,
RecordAuditor: auditRecords,
RecordAuditor: AuditRecords,
}
providers.RegisterDomainServiceProviderType("DOMAINNAMESHOP", fns, features)
@ -69,10 +68,6 @@ func newDomainNameShopProvider(conf map[string]string, metadata json.RawMessage)
return api, nil
}
func auditRecords(records []*models.RecordConfig) error {
return nil
}
type domainResponse struct {
ID int `json:"id"`
Domain string `json:"domain"`

View file

@ -1,11 +1,10 @@
package exoscale
import (
"github.com/StackExchange/dnscontrol/v3/models"
)
import "github.com/StackExchange/dnscontrol/v3/models"
// AuditRecords returns an error if any records are not
// supportable by this provider.
func AuditRecords(records []*models.RecordConfig) error {
// AuditRecords returns a list of errors corresponding to the records
// that aren't supported by this provider. If all records are
// supported, an empty list is returned.
func AuditRecords(records []*models.RecordConfig) []error {
return nil
}

View file

@ -1,11 +1,10 @@
package gandiv5
import (
"github.com/StackExchange/dnscontrol/v3/models"
)
import "github.com/StackExchange/dnscontrol/v3/models"
// AuditRecords returns an error if any records are not
// supportable by this provider.
func AuditRecords(records []*models.RecordConfig) error {
// AuditRecords returns a list of errors corresponding to the records
// that aren't supported by this provider. If all records are
// supported, an empty list is returned.
func AuditRecords(records []*models.RecordConfig) []error {
return nil
}

View file

@ -1,11 +1,10 @@
package gcloud
import (
"github.com/StackExchange/dnscontrol/v3/models"
)
import "github.com/StackExchange/dnscontrol/v3/models"
// AuditRecords returns an error if any records are not
// supportable by this provider.
func AuditRecords(records []*models.RecordConfig) error {
// AuditRecords returns a list of errors corresponding to the records
// that aren't supported by this provider. If all records are
// supported, an empty list is returned.
func AuditRecords(records []*models.RecordConfig) []error {
return nil
}

View file

@ -1,11 +1,10 @@
package hedns
import (
"github.com/StackExchange/dnscontrol/v3/models"
)
import "github.com/StackExchange/dnscontrol/v3/models"
// AuditRecords returns an error if any records are not
// supportable by this provider.
func AuditRecords(records []*models.RecordConfig) error {
// AuditRecords returns a list of errors corresponding to the records
// that aren't supported by this provider. If all records are
// supported, an empty list is returned.
func AuditRecords(records []*models.RecordConfig) []error {
return nil
}

View file

@ -1,11 +1,10 @@
package hetzner
import (
"github.com/StackExchange/dnscontrol/v3/models"
)
import "github.com/StackExchange/dnscontrol/v3/models"
// AuditRecords returns an error if any records are not
// supportable by this provider.
func AuditRecords(records []*models.RecordConfig) error {
// AuditRecords returns a list of errors corresponding to the records
// that aren't supported by this provider. If all records are
// supported, an empty list is returned.
func AuditRecords(records []*models.RecordConfig) []error {
return nil
}

View file

@ -2,17 +2,16 @@ package hexonet
import (
"github.com/StackExchange/dnscontrol/v3/models"
"github.com/StackExchange/dnscontrol/v3/pkg/recordaudit"
"github.com/StackExchange/dnscontrol/v3/pkg/rejectif"
)
// AuditRecords returns an error if any records are not
// supportable by this provider.
func AuditRecords(records []*models.RecordConfig) error {
// AuditRecords returns a list of errors corresponding to the records
// that aren't supported by this provider. If all records are
// supported, an empty list is returned.
func AuditRecords(records []*models.RecordConfig) []error {
a := rejectif.Auditor{}
if err := recordaudit.TxtNotEmpty(records); err != nil {
return err
}
// Still needed as of 2021-10-01
a.Add("TXT", rejectif.TxtIsEmpty) // Last verified 2021-10-01
return nil
return a.Audit(records)
}

View file

@ -1,11 +1,10 @@
package hostingde
import (
"github.com/StackExchange/dnscontrol/v3/models"
)
import "github.com/StackExchange/dnscontrol/v3/models"
// AuditRecords returns an error if any records are not
// supportable by this provider.
func AuditRecords(records []*models.RecordConfig) error {
// AuditRecords returns a list of errors corresponding to the records
// that aren't supported by this provider. If all records are
// supported, an empty list is returned.
func AuditRecords(records []*models.RecordConfig) []error {
return nil
}

View file

@ -1,11 +1,10 @@
package internetbs
import (
"github.com/StackExchange/dnscontrol/v3/models"
)
import "github.com/StackExchange/dnscontrol/v3/models"
// AuditRecords returns an error if any records are not
// supportable by this provider.
func AuditRecords(records []*models.RecordConfig) error {
// AuditRecords returns a list of errors corresponding to the records
// that aren't supported by this provider. If all records are
// supported, an empty list is returned.
func AuditRecords(records []*models.RecordConfig) []error {
return nil
}

View file

@ -2,32 +2,22 @@ package inwx
import (
"github.com/StackExchange/dnscontrol/v3/models"
"github.com/StackExchange/dnscontrol/v3/pkg/recordaudit"
"github.com/StackExchange/dnscontrol/v3/pkg/rejectif"
)
// AuditRecords returns an error if any records are not
// supportable by this provider.
func AuditRecords(records []*models.RecordConfig) error {
// AuditRecords returns a list of errors corresponding to the records
// that aren't supported by this provider. If all records are
// supported, an empty list is returned.
func AuditRecords(records []*models.RecordConfig) []error {
a := rejectif.Auditor{}
if err := recordaudit.TxtNoBackticks(records); err != nil {
return err
}
// Still needed as of 2021-03-01
a.Add("TXT", rejectif.TxtHasBackticks) // Last verified 2021-03-01
if err := recordaudit.TxtNoStringsExactlyLen255(records); err != nil {
return err
}
// Still needed as of 2021-03-01
a.Add("TXT", rejectif.TxtIsExactlyLen255) // Last verified 2021-03-01
if err := recordaudit.TxtNoTrailingSpace(records); err != nil {
return err
}
// Still needed as of 2021-03-01
a.Add("TXT", rejectif.TxtHasTrailingSpace) // Last verified 2021-03-01
if err := recordaudit.TxtNotEmpty(records); err != nil {
return err
}
// Still needed as of 2021-03-01
a.Add("TXT", rejectif.TxtIsEmpty) // Last verified 2021-03-01
return nil
return a.Audit(records)
}

View file

@ -1,11 +1,10 @@
package linode
import (
"github.com/StackExchange/dnscontrol/v3/models"
)
import "github.com/StackExchange/dnscontrol/v3/models"
// AuditRecords returns an error if any records are not
// supportable by this provider.
func AuditRecords(records []*models.RecordConfig) error {
// AuditRecords returns a list of errors corresponding to the records
// that aren't supported by this provider. If all records are
// supported, an empty list is returned.
func AuditRecords(records []*models.RecordConfig) []error {
return nil
}

View file

@ -2,41 +2,26 @@ package msdns
import (
"github.com/StackExchange/dnscontrol/v3/models"
"github.com/StackExchange/dnscontrol/v3/pkg/recordaudit"
"github.com/StackExchange/dnscontrol/v3/pkg/rejectif"
)
// AuditRecords returns an error if any records are not
// supportable by this provider.
func AuditRecords(records []*models.RecordConfig) error {
// AuditRecords returns a list of errors corresponding to the records
// that aren't supported by this provider. If all records are
// supported, an empty list is returned.
func AuditRecords(records []*models.RecordConfig) []error {
a := rejectif.Auditor{}
if err := recordaudit.TxtNoMultipleStrings(records); err != nil {
return err
}
// Still needed as of 2021-03-01
a.Add("TXT", rejectif.TxtHasBackticks) // Last verified 2021-03-01
if err := recordaudit.TxtNoStringsLen256orLonger(records); err != nil {
return err
}
a.Add("TXT", rejectif.TxtHasDoubleQuotes) // Last verified 2021-03-01
if err := recordaudit.TxtNotEmpty(records); err != nil {
return err
}
// Still needed as of 2021-03-01
a.Add("TXT", rejectif.TxtHasMultipleSegments) // Last verified 2021-03-01
if err := recordaudit.TxtNoBackticks(records); err != nil {
return err
}
// Still needed as of 2021-03-01
a.Add("TXT", rejectif.TxtHasSingleQuotes) // Last verified 2021-03-01
if err := recordaudit.TxtNoDoubleQuotes(records); err != nil {
return err
}
// Still needed as of 2021-03-01
a.Add("TXT", rejectif.TxtHasSegmentLen256orLonger)
if err := recordaudit.TxtNoSingleQuotes(records); err != nil {
return err
}
// Still needed as of 2021-03-01
a.Add("TXT", rejectif.TxtIsEmpty) // Last verified 2021-03-01
return nil
return a.Audit(records)
}

View file

@ -1,11 +1,10 @@
package namecheap
import (
"github.com/StackExchange/dnscontrol/v3/models"
)
import "github.com/StackExchange/dnscontrol/v3/models"
// AuditRecords returns an error if any records are not
// supportable by this provider.
func AuditRecords(records []*models.RecordConfig) error {
// AuditRecords returns a list of errors corresponding to the records
// that aren't supported by this provider. If all records are
// supported, an empty list is returned.
func AuditRecords(records []*models.RecordConfig) []error {
return nil
}

View file

@ -5,49 +5,41 @@ import (
"strings"
"github.com/StackExchange/dnscontrol/v3/models"
"github.com/StackExchange/dnscontrol/v3/pkg/recordaudit"
"github.com/StackExchange/dnscontrol/v3/pkg/rejectif"
)
// AuditRecords returns an error if any records are not
// supportable by this provider.
func AuditRecords(records []*models.RecordConfig) error {
// AuditRecords returns a list of errors corresponding to the records
// that aren't supported by this provider. If all records are
// supported, an empty list is returned.
func AuditRecords(records []*models.RecordConfig) []error {
a := rejectif.Auditor{}
if err := MaxLengthNDC(records); err != nil {
return err
}
// Still needed as of 2021-03-01
a.Add("TXT", MaxLengthNDC) // Last verified 2021-03-01
if err := recordaudit.TxtNotEmpty(records); err != nil {
return err
}
// Still needed as of 2021-03-01
a.Add("TXT", rejectif.TxtIsEmpty) // Last verified 2021-03-01
return nil
return a.Audit(records)
}
// MaxLengthNDC returns and error if the sum of the strings
// are longer than permitted by NDC. Sadly their
// length limit is undocumented. This seems to work.
func MaxLengthNDC(records []*models.RecordConfig) error {
for _, rc := range records {
func MaxLengthNDC(rc *models.RecordConfig) error {
if len(rc.TxtStrings) == 0 {
return nil
}
if rc.HasFormatIdenticalToTXT() { // TXT and similar:
// Sum the length of the segments:
sum := 0
for _, segment := range rc.TxtStrings {
sum += len(segment) // The length of each segment
sum += strings.Count(segment, `"`) // Add 1 for any char to be escaped
}
// Add the overhead of quoting them:
n := len(rc.TxtStrings)
if n > 0 {
sum += 2 + 3*(n-1) // Start and end double-quotes, plus `" "` between each segment.
}
if sum > 512 {
return fmt.Errorf("encoded txt too long")
}
}
sum := 2 // Count the start and end quote.
// Add the length of each segment.
for _, segment := range rc.TxtStrings {
sum += len(segment) // The length of each segment
sum += strings.Count(segment, `"`) // Add 1 for any char to be escaped
}
// Add 3 (quote space quote) for each interior join.
sum += 3 * (len(rc.TxtStrings) - 1)
if sum > 512 {
return fmt.Errorf("encoded txt too long")
}
return nil
}

View file

@ -2,12 +2,16 @@ package netcup
import (
"github.com/StackExchange/dnscontrol/v3/models"
"github.com/StackExchange/dnscontrol/v3/pkg/recordaudit"
"github.com/StackExchange/dnscontrol/v3/pkg/rejectif"
)
// AuditRecords returns an error if any records are not
// supportable by this provider.
func AuditRecords(records []*models.RecordConfig) error {
return recordaudit.TxtNotEmpty(records)
// Still needed as of 2021-03-01
// AuditRecords returns a list of errors corresponding to the records
// that aren't supported by this provider. If all records are
// supported, an empty list is returned.
func AuditRecords(records []*models.RecordConfig) []error {
a := rejectif.Auditor{}
a.Add("TXT", rejectif.TxtIsEmpty) // Last verified 2021-03-01
return a.Audit(records)
}

View file

@ -2,17 +2,16 @@ package ns1
import (
"github.com/StackExchange/dnscontrol/v3/models"
"github.com/StackExchange/dnscontrol/v3/pkg/recordaudit"
"github.com/StackExchange/dnscontrol/v3/pkg/rejectif"
)
// AuditRecords returns an error if any records are not
// supportable by this provider.
func AuditRecords(records []*models.RecordConfig) error {
// AuditRecords returns a list of errors corresponding to the records
// that aren't supported by this provider. If all records are
// supported, an empty list is returned.
func AuditRecords(records []*models.RecordConfig) []error {
a := rejectif.Auditor{}
if err := recordaudit.TxtNoMultipleStrings(records); err != nil {
return err
}
// Still needed as of 2021-03-01
a.Add("TXT", rejectif.TxtHasMultipleSegments)
return nil
return a.Audit(records)
}

View file

@ -1,11 +1,10 @@
package octodns
import (
"github.com/StackExchange/dnscontrol/v3/models"
)
import "github.com/StackExchange/dnscontrol/v3/models"
// AuditRecords returns an error if any records are not
// supportable by this provider.
func AuditRecords(records []*models.RecordConfig) error {
// AuditRecords returns a list of errors corresponding to the records
// that aren't supported by this provider. If all records are
// supported, an empty list is returned.
func AuditRecords(records []*models.RecordConfig) []error {
return nil
}

View file

@ -1,11 +1,10 @@
package opensrs
import (
"github.com/StackExchange/dnscontrol/v3/models"
)
import "github.com/StackExchange/dnscontrol/v3/models"
// AuditRecords returns an error if any records are not
// supportable by this provider.
func AuditRecords(records []*models.RecordConfig) error {
// AuditRecords returns a list of errors corresponding to the records
// that aren't supported by this provider. If all records are
// supported, an empty list is returned.
func AuditRecords(records []*models.RecordConfig) []error {
return nil
}

View file

@ -1,11 +1,10 @@
package oracle
import (
"github.com/StackExchange/dnscontrol/v3/models"
)
import "github.com/StackExchange/dnscontrol/v3/models"
// AuditRecords returns an error if any records are not
// supportable by this provider.
func AuditRecords(records []*models.RecordConfig) error {
// AuditRecords returns a list of errors corresponding to the records
// that aren't supported by this provider. If all records are
// supported, an empty list is returned.
func AuditRecords(records []*models.RecordConfig) []error {
return nil
}

View file

@ -1,11 +1,10 @@
package ovh
import (
"github.com/StackExchange/dnscontrol/v3/models"
)
import "github.com/StackExchange/dnscontrol/v3/models"
// AuditRecords returns an error if any records are not
// supportable by this provider.
func AuditRecords(records []*models.RecordConfig) error {
// AuditRecords returns a list of errors corresponding to the records
// that aren't supported by this provider. If all records are
// supported, an empty list is returned.
func AuditRecords(records []*models.RecordConfig) []error {
return nil
}

View file

@ -1,11 +1,10 @@
package packetframe
import (
"github.com/StackExchange/dnscontrol/v3/models"
)
import "github.com/StackExchange/dnscontrol/v3/models"
// AuditRecords returns an error if any records are not
// supportable by this provider.
func AuditRecords(records []*models.RecordConfig) error {
// AuditRecords returns a list of errors corresponding to the records
// that aren't supported by this provider. If all records are
// supported, an empty list is returned.
func AuditRecords(records []*models.RecordConfig) []error {
return nil
}

View file

@ -1,11 +1,10 @@
package powerdns
import (
"github.com/StackExchange/dnscontrol/v3/models"
)
import "github.com/StackExchange/dnscontrol/v3/models"
// AuditRecords returns an error if any records are not
// supportable by this provider.
func AuditRecords(records []*models.RecordConfig) error {
// AuditRecords returns a list of errors corresponding to the records
// that aren't supported by this provider. If all records are
// supported, an empty list is returned.
func AuditRecords(records []*models.RecordConfig) []error {
return nil
}

View file

@ -41,9 +41,9 @@ var RegistrarTypes = map[string]RegistrarInitializer{}
type DspInitializer func(map[string]string, json.RawMessage) (DNSServiceProvider, error)
// RecordAuditor is a function that verifies that all the records
// are supportable by this provider. It returns an error related to
// the first record that this provider can not support.
type RecordAuditor func([]*models.RecordConfig) error
// are supportable by this provider. It returns a list of errors
// detailing records that this provider can not support.
type RecordAuditor func([]*models.RecordConfig) []error
// DspFuncs lists functions registered with a provider.
type DspFuncs struct {
@ -132,13 +132,13 @@ func beCompatible(n string, config map[string]string) (string, error) {
}
// AuditRecords calls the RecordAudit function for a provider.
func AuditRecords(dType string, rcs models.Records) error {
func AuditRecords(dType string, rcs models.Records) []error {
p, ok := DNSProviderTypes[dType]
if !ok {
return fmt.Errorf("unknown DNS service provider type: %q", dType)
return []error{fmt.Errorf("unknown DNS service provider type: %q", dType)}
}
if p.RecordAuditor == nil {
return fmt.Errorf("DNS service provider type %q has no RecordAuditor", dType)
return []error{fmt.Errorf("DNS service provider type %q has no RecordAuditor", dType)}
}
return p.RecordAuditor(rcs)
}

View file

@ -1,11 +1,10 @@
package route53
import (
"github.com/StackExchange/dnscontrol/v3/models"
)
import "github.com/StackExchange/dnscontrol/v3/models"
// AuditRecords returns an error if any records are not
// supportable by this provider.
func AuditRecords(records []*models.RecordConfig) error {
// AuditRecords returns a list of errors corresponding to the records
// that aren't supported by this provider. If all records are
// supported, an empty list is returned.
func AuditRecords(records []*models.RecordConfig) []error {
return nil
}

View file

@ -2,24 +2,20 @@ package rwth
import (
"github.com/StackExchange/dnscontrol/v3/models"
"github.com/StackExchange/dnscontrol/v3/pkg/recordaudit"
"github.com/StackExchange/dnscontrol/v3/pkg/rejectif"
)
// AuditRecords returns an error if any records are not
// supportable by this provider.
func AuditRecords(records []*models.RecordConfig) error {
// AuditRecords returns a list of errors corresponding to the records
// that aren't supported by this provider. If all records are
// supported, an empty list is returned.
func AuditRecords(records []*models.RecordConfig) []error {
a := rejectif.Auditor{}
if err := recordaudit.TxtNoMultipleStrings(records); err != nil {
return err
}
a.Add("TXT", rejectif.TxtHasMultipleSegments)
if err := recordaudit.TxtNoTrailingSpace(records); err != nil {
return err
}
a.Add("TXT", rejectif.TxtHasTrailingSpace)
if err := recordaudit.TxtNotEmpty(records); err != nil {
return err
}
a.Add("TXT", rejectif.TxtIsEmpty)
return nil
return a.Audit(records)
}

View file

@ -1,11 +1,10 @@
package softlayer
import (
"github.com/StackExchange/dnscontrol/v3/models"
)
import "github.com/StackExchange/dnscontrol/v3/models"
// AuditRecords returns an error if any records are not
// supportable by this provider.
func AuditRecords(records []*models.RecordConfig) error {
// AuditRecords returns a list of errors corresponding to the records
// that aren't supported by this provider. If all records are
// supported, an empty list is returned.
func AuditRecords(records []*models.RecordConfig) []error {
return nil
}

View file

@ -1,11 +1,10 @@
package transip
import (
"github.com/StackExchange/dnscontrol/v3/models"
)
import "github.com/StackExchange/dnscontrol/v3/models"
// AuditRecords returns an error if any records are not
// supportable by this provider.
func AuditRecords(records []*models.RecordConfig) error {
// AuditRecords returns a list of errors corresponding to the records
// that aren't supported by this provider. If all records are
// supported, an empty list is returned.
func AuditRecords(records []*models.RecordConfig) []error {
return nil
}

View file

@ -2,23 +2,20 @@ package vultr
import (
"github.com/StackExchange/dnscontrol/v3/models"
"github.com/StackExchange/dnscontrol/v3/pkg/recordaudit"
"github.com/StackExchange/dnscontrol/v3/pkg/rejectif"
)
// AuditRecords returns an error if any records are not
// supportable by this provider.
func AuditRecords(records []*models.RecordConfig) error {
// AuditRecords returns a list of errors corresponding to the records
// that aren't supported by this provider. If all records are
// supported, an empty list is returned.
func AuditRecords(records []*models.RecordConfig) []error {
a := rejectif.Auditor{}
// TODO(tlim) Needs investigation. Could be a dnscontrol issue or
a.Add("TXT", rejectif.TxtHasDoubleQuotes) // Last verified 2021-03-02
// Needs investigation. Could be a dnscontrol issue or
// the provider doesn't support double quotes.
if err := recordaudit.TxtNoDoubleQuotes(records); err != nil {
return err
}
// Still needed as of 2021-03-02
if err := recordaudit.TxtNoMultipleStrings(records); err != nil {
return err
}
a.Add("TXT", rejectif.TxtHasMultipleSegments)
return nil
return a.Audit(records)
}