diff --git a/models/record.go b/models/record.go index b7ab5455f..26eb48a6c 100644 --- a/models/record.go +++ b/models/record.go @@ -295,6 +295,8 @@ func (rc *RecordConfig) ToRR() dns.RR { rr.(*dns.TLSA).MatchingType = rc.TlsaMatchingType rr.(*dns.TLSA).Selector = rc.TlsaSelector rr.(*dns.TLSA).Certificate = rc.GetTargetField() + case dns.TypeSPF: + rr.(*dns.SPF).Txt = rc.TxtStrings case dns.TypeTXT: rr.(*dns.TXT).Txt = rc.TxtStrings default: diff --git a/models/t_parse.go b/models/t_parse.go index bf0b4da07..5f6d324b2 100644 --- a/models/t_parse.go +++ b/models/t_parse.go @@ -47,7 +47,7 @@ func (r *RecordConfig) PopulateFromString(rtype, contents, origin string) error return r.SetTargetSSHFPString(contents) case "TLSA": return r.SetTargetTLSAString(contents) - case "TXT": + case "SPF", "TXT": return r.SetTargetTXTString(contents) default: return fmt.Errorf("unknown rtype (%s) when parsing (%s) domain=(%s)", diff --git a/models/t_txt.go b/models/t_txt.go index 6911838c3..73bcfbda9 100644 --- a/models/t_txt.go +++ b/models/t_txt.go @@ -5,6 +5,18 @@ import ( "strings" ) +// HasFormatIdenticalToTXT returns if a RecordConfig has a format which is +// identical to TXT, such as SPF. For more details, read +// https://tools.ietf.org/html/rfc4408#section-3.1.1 +func (rc *RecordConfig) HasFormatIdenticalToTXT() bool { + switch rc.Type { + case "SPF", "TXT": + return true + default: + return false + } +} + // SetTargetTXT sets the TXT fields when there is 1 string. func (rc *RecordConfig) SetTargetTXT(s string) error { rc.SetTarget(s) @@ -12,8 +24,8 @@ func (rc *RecordConfig) SetTargetTXT(s string) error { if rc.Type == "" { rc.Type = "TXT" } - if rc.Type != "TXT" { - panic("assertion failed: SetTargetTXT called when .Type is not TXT") + if !rc.HasFormatIdenticalToTXT() { + panic("assertion failed: SetTargetTXT called when .Type is not identical to TXT") } return nil } @@ -25,8 +37,8 @@ func (rc *RecordConfig) SetTargetTXTs(s []string) error { if rc.Type == "" { rc.Type = "TXT" } - if rc.Type != "TXT" { - panic("assertion failed: SetTargetTXT called when .Type is not TXT") + if !rc.HasFormatIdenticalToTXT() { + panic("assertion failed: SetTargetTXT called when .Type is not identical to TXT") } return nil } @@ -68,8 +80,8 @@ func splitChunks(buf string, lim int) []string { // ValidateTXT returns an error if the txt record is invalid. // Verifies the Target and TxtStrings are less than 255 bytes each. func ValidateTXT(rc *RecordConfig) error { - if rc.Type != "TXT" { - return fmt.Errorf("rc.Type=%q, expecting TXT", rc.Type) + if !rc.HasFormatIdenticalToTXT() { + return fmt.Errorf("rc.Type=%q, expecting something identical to TXT", rc.Type) } for i := range rc.TxtStrings { l := len(rc.TxtStrings[i]) diff --git a/models/target.go b/models/target.go index a9ddcbc0b..b8c73de96 100644 --- a/models/target.go +++ b/models/target.go @@ -23,7 +23,7 @@ so that it is easy to do things the right way in preparation. // GetTargetField returns the target. There may be other fields (for example // an MX record also has a .MxPreference field. func (rc *RecordConfig) GetTargetField() string { - if rc.Type == "TXT" { + if rc.HasFormatIdenticalToTXT() { return strings.Join(rc.TxtStrings, "") } return rc.Target diff --git a/pkg/normalize/validate.go b/pkg/normalize/validate.go index 641cb1385..a3309cd30 100644 --- a/pkg/normalize/validate.go +++ b/pkg/normalize/validate.go @@ -351,7 +351,7 @@ func ValidateAndNormalizeConfig(config *models.DNSConfig) (errs []error) { // Split TXT targets that are >255 bytes (if permitted) for _, domain := range config.Domains { for _, rec := range domain.Records { - if rec.Type == "TXT" { + if rec.HasFormatIdenticalToTXT() { if txtAlgo, ok := rec.Metadata["txtSplitAlgorithm"]; ok { rec.TxtNormalize(txtAlgo) } @@ -371,12 +371,12 @@ func ValidateAndNormalizeConfig(config *models.DNSConfig) (errs []error) { } // Validate TXT records. for _, rec := range domain.Records { - if rec.Type == "TXT" { + if rec.HasFormatIdenticalToTXT() { // If TXTMulti is required, all providers must support that feature. if len(rec.TxtStrings) > 1 && len(txtMultiDissenters) > 0 { errs = append(errs, - fmt.Errorf("TXT records with multiple strings not supported by %s (label=%q domain=%v)", - strings.Join(txtMultiDissenters, ","), rec.GetLabel(), domain.Name)) + fmt.Errorf("%s records with multiple strings not supported by %s (label=%q domain=%v)", + rec.Type, strings.Join(txtMultiDissenters, ","), rec.GetLabel(), domain.Name)) } // Validate the record: if err := models.ValidateTXT(rec); err != nil { diff --git a/providers/octodns/octoyaml/read.go b/providers/octodns/octoyaml/read.go index 51b2dfdf5..672f6e7a7 100644 --- a/providers/octodns/octoyaml/read.go +++ b/providers/octodns/octoyaml/read.go @@ -236,7 +236,7 @@ func newRecordConfig(rname, rtype, target string, ttl uint32, origin string) *mo } rc.SetLabel(rname, origin) switch rtype { - case "TXT": + case "SPF", "TXT": rc.SetTargetTXT(target) default: rc.SetTarget(target)