1
0
mirror of https://github.com/StackExchange/dnscontrol.git synced 2024-05-11 05:55:12 +00:00
This commit is contained in:
Tom Limoncelli
2023-06-21 18:41:01 -06:00
parent 10f06c2834
commit 1c2c207790
13 changed files with 63 additions and 98 deletions

View File

@ -427,7 +427,7 @@ func (rc *RecordConfig) ToRR() dns.RR {
rr.(*dns.SOA).Expire = rc.SoaExpire
rr.(*dns.SOA).Minttl = rc.SoaMinttl
case dns.TypeSPF:
rr.(*dns.SPF).Txt = rc.TxtStrings
rr.(*dns.SPF).Txt = []string{rc.target}
case dns.TypeSRV:
rr.(*dns.SRV).Priority = rc.SrvPriority
rr.(*dns.SRV).Weight = rc.SrvWeight
@ -443,7 +443,7 @@ func (rc *RecordConfig) ToRR() dns.RR {
rr.(*dns.TLSA).Selector = rc.TlsaSelector
rr.(*dns.TLSA).Certificate = rc.GetTargetField()
case dns.TypeTXT:
rr.(*dns.TXT).Txt = rc.TxtStrings
rr.(*dns.TXT).Txt = []string{rc.target}
default:
panic(fmt.Sprintf("ToRR: Unimplemented rtype %v", rc.Type))
// We panic so that we quickly find any switch statements

View File

@ -49,13 +49,13 @@ func (rc *RecordConfig) SetTargetTXT(s string) error {
// SetTargetTXTs sets the TXT fields when there are many strings.
// The individual strings are stored in .TxtStrings, and joined to make .Target.
func (rc *RecordConfig) SetTargetTXTs(s []string) error {
if rc.Type == "" {
rc.Type = "TXT"
} else if !rc.HasFormatIdenticalToTXT() {
panic("assertion failed: SetTargetTXTs called when .Type is not TXT or compatible type")
}
// if rc.Type == "" {
// rc.Type = "TXT"
// } else if !rc.HasFormatIdenticalToTXT() {
// panic("assertion failed: SetTargetTXTs called when .Type is not TXT or compatible type")
// }
rc.SetTarget(strings.Join(s))
rc.SetTargetTXT(strings.Join(s, ""))
return nil
}
@ -74,6 +74,7 @@ func (rc *RecordConfig) GetTargetTXTJoined() string {
// "foo bar" << 1 string
// "foo" "bar" << 2 strings
// foo << error. No quotes! Did you intend to use SetTargetTXT?
//
// Deprecated: GetTargetTXTJoined is deprecated. ...or should be.
func (rc *RecordConfig) SetTargetTXTfromRFC1035Quoted(s string) error {
if s != "" && s[0] != '"' {

View File

@ -21,7 +21,7 @@ Not the best design, but we're stuck with it until we re-do RecordConfig, possib
var debugWarnTxtField = false
// GetTargetField returns the target. There may be other fields (for example
// an MX record also has a .MxPreference field.
// an MX record also has a .MxPreference field; they are not included.
func (rc *RecordConfig) GetTargetField() string {
return rc.target
}

View File

@ -32,7 +32,7 @@ func flattenSPFs(cfg *models.DNSConfig) []error {
// flatten all spf records that have the "flatten" metadata
for _, txt := range txtRecords {
var rec *spflib.SPFRecord
txtTarget := strings.Join(txt.TxtStrings, "")
txtTarget := txt.GetTargetField()
if txt.Metadata["flatten"] != "" || txt.Metadata["split"] != "" {
if cache == nil {
cache, err = spflib.NewCache("spfcache.json")

View File

@ -11,30 +11,24 @@ import (
// 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")
}
if strings.Contains(rc.GetTargetField(), "`") {
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")
}
if strings.Contains(rc.GetTargetField(), "'") {
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")
}
if strings.Contains(rc.GetTargetField(), `"`) {
return fmt.Errorf("txtstring contains doublequotes")
}
return nil
}
@ -42,27 +36,23 @@ func TxtHasDoubleQuotes(rc *models.RecordConfig) error {
// 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")
}
if len(rc.GetTargetField()) == 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())
}
if len(rc.GetTargetField()) > 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 {
if len(rc.GetTargetField()) > 255 {
return fmt.Errorf("multiple strings in one txt")
}
return nil
@ -70,35 +60,25 @@ func TxtHasMultipleSegments(rc *models.RecordConfig) error {
// 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")
}
txt := rc.GetTargetField()
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")
}
if len(rc.GetTargetField()) == 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")
}
if strings.Count(rc.GetTargetField(), `"`)%2 == 1 {
return fmt.Errorf("txtstring contains unpaired doublequotes")
}
return nil
}

View File

@ -1,5 +1,23 @@
package txtutil
import "github.com/StackExchange/dnscontrol/v4/models"
// SplitSingleLongTxt finds TXT records with a single long string and splits it
// into 255-octet chunks. This is used by providers that, when a user specifies
// one long TXT string, split it into smaller strings behind the scenes.
// This should be called from GetZoneRecordsCorrections().
// Deprecated: SplitSingleLongTxt() is deprecated. No longer needed.
func SplitSingleLongTxt(records []*models.RecordConfig) {
// for _, rc := range records {
// if rc.HasFormatIdenticalToTXT() {
// s := rc.TxtStrings[0]
// if len(rc.TxtStrings) == 1 && len(s) > 255 {
// rc.SetTargetTXTs(splitChunks(s, 255))
// }
// }
// }
}
// ToChunks returns the string as chunks of 255-octet strings (the last string being the remainder).
func ToChunks(s string) []string {
return splitChunks(s, 255)

View File

@ -328,8 +328,7 @@ func (c *axfrddnsProvider) GetZoneRecords(domain string, meta map[string]string)
last := foundRecords[len(foundRecords)-1]
if last.Type == "TXT" &&
last.Name == dnssecDummyLabel &&
len(last.TxtStrings) == 1 &&
last.TxtStrings[0] == dnssecDummyTxt {
last.GetTargetField() == dnssecDummyTxt {
c.hasDnssecRecords = true
foundRecords = foundRecords[0:(len(foundRecords) - 1)]
}

View File

@ -653,11 +653,9 @@ func (a *azurednsProvider) recordToNative(recordKey models.RecordKey, recordConf
recordSet.Properties.TxtRecords = []*adns.TxtRecord{}
}
// Empty TXT record needs to have no value set in it's properties
if !(len(rec.TxtStrings) == 1 && rec.TxtStrings[0] == "") {
var txts []*string
for _, txt := range rec.TxtStrings {
txts = append(txts, to.StringPtr(txt))
}
tt := rec.GetTargetField()
if tt != "" {
txts := []*string{to.StringPtr(tt)}
recordSet.Properties.TxtRecords = append(recordSet.Properties.TxtRecords, &adns.TxtRecord{Value: txts})
}
case "MX":
@ -729,11 +727,9 @@ func (a *azurednsProvider) recordToNativeDiff2(recordKey models.RecordKey, recor
recordSet.Properties.TxtRecords = []*adns.TxtRecord{}
}
// Empty TXT record needs to have no value set in it's properties
if !(len(rec.TxtStrings) == 1 && rec.TxtStrings[0] == "") {
var txts []*string
for _, txt := range rec.TxtStrings {
txts = append(txts, to.StringPtr(txt))
}
tt := rec.GetTargetField()
if tt != "" {
txts := []*string{to.StringPtr(tt)}
recordSet.Properties.TxtRecords = append(recordSet.Properties.TxtRecords, &adns.TxtRecord{Value: txts})
}
case "MX":

View File

@ -135,14 +135,7 @@ func (client *providerClient) GetZoneRecordsCorrections(dc *models.DomainConfig,
}
func makePurge(domainname string, cor diff.Correlation) zoneResourceRecordEdit {
var existingTarget string
switch cor.Existing.Type {
case "TXT":
existingTarget = strings.Join(cor.Existing.TxtStrings, "")
default:
existingTarget = cor.Existing.GetTargetField()
}
existingTarget := cor.Existing.GetTargetField()
zer := zoneResourceRecordEdit{
Action: "PURGE",
@ -163,19 +156,11 @@ func makePurge(domainname string, cor diff.Correlation) zoneResourceRecordEdit {
func makeAdd(domainname string, cre diff.Correlation) zoneResourceRecordEdit {
rec := cre.Desired
var recTarget string
switch rec.Type {
case "TXT":
recTarget = strings.Join(rec.TxtStrings, "")
default:
recTarget = rec.GetTargetField()
}
zer := zoneResourceRecordEdit{
Action: "ADD",
RecordType: rec.Type,
NewKey: rec.Name,
NewValue: recTarget,
NewValue: rec.GetTargetField(),
NewTTL: rec.TTL,
}
@ -192,7 +177,7 @@ func makeAdd(domainname string, cre diff.Correlation) zoneResourceRecordEdit {
zer.NewWeight = rec.SrvWeight
zer.NewPort = rec.SrvPort
case "TXT":
zer.NewValue = strings.Join(rec.TxtStrings, "")
zer.NewValue = rec.GetTargetField()
default: // "A", "CNAME", "NS"
// Nothing to do.
}
@ -205,15 +190,8 @@ func makeEdit(domainname string, m diff.Correlation) zoneResourceRecordEdit {
// TODO: Assert that old.Type == rec.Type
// TODO: Assert that old.Name == rec.Name
var oldTarget, recTarget string
switch old.Type {
case "TXT":
oldTarget = strings.Join(old.TxtStrings, "")
recTarget = strings.Join(rec.TxtStrings, "")
default:
oldTarget = old.GetTargetField()
recTarget = rec.GetTargetField()
}
oldTarget := old.GetTargetField()
recTarget := rec.GetTargetField()
zer := zoneResourceRecordEdit{
Action: "EDIT",

View File

@ -28,13 +28,6 @@ func (api *domainNameShopProvider) GetZoneRecords(domain string, meta map[string
// GetZoneRecordsCorrections returns a list of corrections that will turn existing records into dc.Records.
func (api *domainNameShopProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, existingRecords models.Records) ([]*models.Correction, error) {
// Merge TXT strings to one string
for _, rc := range dc.Records {
if rc.HasFormatIdenticalToTXT() {
rc.SetTargetTXT(strings.Join(rc.TxtStrings, ""))
}
}
// Domainnameshop doesn't allow arbitrary TTLs they must be a multiple of 60.
for _, record := range dc.Records {
record.TTL = fixTTL(record.TTL)

View File

@ -63,7 +63,7 @@ func fromRecordConfig(in *models.RecordConfig, zone *zone) record {
ZoneID: zone.ID,
}
if r.Type == "TXT" && len(in.TxtStrings) == 1 {
if r.Type == "TXT" {
// HACK: HETZNER rejects values that fit into 255 bytes w/o quotes,
// but do not fit w/ added quotes (via GetTargetCombined()).
// Sending the raw, non-quoted value works for the comprehensive
@ -71,7 +71,7 @@ func fromRecordConfig(in *models.RecordConfig, zone *zone) record {
// The HETZNER validation does not provide helpful error messages.
// {"error":{"message":"422 Unprocessable Entity: missing: ; ","code":422}}
// Last checked: 2023-04-01
valueNotQuoted := in.TxtStrings[0]
valueNotQuoted := in.GetTargetField()
if len(valueNotQuoted) == 254 || len(valueNotQuoted) == 255 {
r.Value = valueNotQuoted
}

View File

@ -256,7 +256,7 @@ func (n *HXClient) createRecordString(rc *models.RecordConfig, domain string) (s
case "CAA":
record.Answer = fmt.Sprintf(`%v %s "%s"`, rc.CaaFlag, rc.CaaTag, record.Answer)
case "TXT":
record.Answer = encodeTxt(rc.TxtStrings)
record.Answer = encodeTxt([]string{rc.GetTargetField()})
case "SRV":
if rc.GetTargetField() == "." {
return "", fmt.Errorf("SRV records with empty targets are not supported (as of 2020-02-27, the API returns 'Invalid attribute value syntax')")

View File

@ -356,7 +356,7 @@ func toVultrRecord(dc *models.DomainConfig, rc *models.RecordConfig, vultrID str
// Vultr doesn't permit TXT strings to include double-quotes
// therefore, we don't have to escape interior double-quotes.
// Vultr's API requires the string to begin and end with double-quotes.
r.Data = `"` + strings.Join(rc.TxtStrings, "") + `"`
r.Data = `"` + rc.GetTargetField() + `"`
default:
}