1
0
mirror of https://github.com/StackExchange/dnscontrol.git synced 2024-05-11 05:55:12 +00:00

REFACTOR: Opinion: TXT records are one long string (#2631)

Co-authored-by: Costas Drogos <costas.drogos@gmail.com>
Co-authored-by: imlonghao <git@imlonghao.com>
Co-authored-by: Jeffrey Cafferata <jeffrey@jcid.nl>
Co-authored-by: Vincent Hagen <blackshadev@users.noreply.github.com>
This commit is contained in:
Tom Limoncelli
2023-12-04 17:45:25 -05:00
committed by GitHub
parent 88d26c3ea2
commit cbccbbeb8d
71 changed files with 882 additions and 747 deletions

View File

@@ -13,6 +13,8 @@ func AuditRecords(records []*models.RecordConfig) []error {
a.Add("TXT", rejectif.TxtIsEmpty) // Last verified 2021-10-01
a.Add("TXT", rejectif.TxtHasDoubleQuotes) // Last verified 2023-11-30
a.Add("SRV", rejectif.SrvHasNullTarget) // Last verified 2020-12-28
return a.Audit(records)

View File

@@ -3,7 +3,6 @@ package hexonet
import (
"bytes"
"fmt"
"regexp"
"strconv"
"strings"
@@ -129,10 +128,6 @@ func toRecord(r *HXRecord, origin string) *models.RecordConfig {
rc.SetLabelFromFQDN(fqdn, origin)
switch rtype := r.Type; rtype {
case "TXT":
if err := rc.SetTargetTXTs(decodeTxt(r.Answer)); err != nil {
panic(fmt.Errorf("unparsable TXT record received from hexonet api: %w", err))
}
case "MX":
if err := rc.SetTargetMX(uint16(r.Priority), r.Answer); err != nil {
panic(fmt.Errorf("unparsable MX record received from hexonet api: %w", err))
@@ -142,7 +137,7 @@ func toRecord(r *HXRecord, origin string) *models.RecordConfig {
panic(fmt.Errorf("unparsable SRV record received from hexonet api: %w", err))
}
default: // "A", "AAAA", "ANAME", "CNAME", "NS"
if err := rc.PopulateFromString(rtype, r.Answer, r.Fqdn); err != nil {
if err := rc.PopulateFromStringFunc(rtype, r.Answer, r.Fqdn, txtutil.ParseQuoted); err != nil {
panic(fmt.Errorf("unparsable record received from hexonet api: %w", err))
}
}
@@ -242,7 +237,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.GetTargetTXTSegmented())
record.Answer = txtutil.EncodeQuoted(rc.GetTargetTXTJoined())
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')")
@@ -266,31 +261,3 @@ func (n *HXClient) createRecordString(rc *models.RecordConfig, domain string) (s
func (n *HXClient) deleteRecordString(record *HXRecord, domain string) string {
return record.Raw
}
// encodeTxt encodes []string for sending in the CREATE/MODIFY API:
func encodeTxt(txts []string) string {
var r []string
for _, txt := range txts {
n := `"` + strings.Replace(txt, `"`, `\"`, -1) + `"`
r = append(r, n)
}
return strings.Join(r, " ")
}
// finds a string surrounded by quotes that might contain an escaped quote character.
var quotedStringRegexp = regexp.MustCompile(`"((?:[^"\\]|\\.)*)"`)
// decodeTxt decodes the TXT record as received from hexonet api and
// returns the list of strings.
func decodeTxt(s string) []string {
if len(s) >= 2 && s[0] == '"' && s[len(s)-1] == '"' {
txtStrings := []string{}
for _, t := range quotedStringRegexp.FindAllStringSubmatch(s, -1) {
txtString := strings.Replace(t[1], `\"`, `"`, -1)
txtStrings = append(txtStrings, txtString)
}
return txtStrings
}
return []string{s}
}

View File

@@ -1,51 +0,0 @@
package hexonet
import (
"strings"
"testing"
)
var txtData = []struct {
decoded []string
encoded string
}{
{[]string{`simple`}, `"simple"`},
{[]string{`changed`}, `"changed"`},
{[]string{`with spaces`}, `"with spaces"`},
{[]string{`with whitespace`}, `"with whitespace"`},
{[]string{"one", "two"}, `"one" "two"`},
{[]string{"eh", "bee", "cee"}, `"eh" "bee" "cee"`},
{[]string{"o\"ne", "tw\"o"}, `"o\"ne" "tw\"o"`},
{[]string{"dimple"}, `"dimple"`},
{[]string{"fun", "two"}, `"fun" "two"`},
{[]string{"eh", "bzz", "cee"}, `"eh" "bzz" "cee"`},
}
func TestEncodeTxt(t *testing.T) {
// Test encoded the lists of strings into a string:
for i, test := range txtData {
enc := encodeTxt(test.decoded)
if enc != test.encoded {
t.Errorf("%v: txt\n data: []string{%v}\nexpected: %q\n got: %q",
i, "`"+strings.Join(test.decoded, "`, `")+"`", test.encoded, enc)
}
}
}
func TestDecodeTxt(t *testing.T) {
// Test decoded a string into the list of strings:
for i, test := range txtData {
data := test.encoded
got := decodeTxt(data)
wanted := test.decoded
if len(got) != len(wanted) {
t.Errorf("%v: txt\n decode: %v\nexpected: %q\n got: %q\n", i, data, strings.Join(wanted, "`, `"), strings.Join(got, "`, `"))
} else {
for j := range got {
if got[j] != wanted[j] {
t.Errorf("%v: txt\n decode: %v\nexpected: %q\n got: %q\n", i, data, strings.Join(wanted, "`, `"), strings.Join(got, "`, `"))
}
}
}
}
}