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:
@@ -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)
|
||||
|
@@ -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}
|
||||
}
|
||||
|
@@ -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, "`, `"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user