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

BUGFIX: CAA records may include quoted spaces #1374 (#1377)

This commit is contained in:
Tom Limoncelli
2022-01-25 09:57:20 -05:00
committed by GitHub
parent 263c6b7290
commit b73d37908c
4 changed files with 49 additions and 4 deletions

View File

@ -1085,6 +1085,11 @@ func makeTests(t *testing.T) []*TestGroup {
// Test support of ";" as a value // Test support of ";" as a value
tc("CAA many records", caa("@", "issuewild", 0, ";")), tc("CAA many records", caa("@", "issuewild", 0, ";")),
), ),
testgroup("Issue 1374",
requires(providers.CanUseCAA), not("DIGITALOCEAN"),
// Test support of ";" as a value
tc("CAA many records", caa("@", "issue", 0, "letsencrypt.org; validationmethods=dns-01; accounturi=https://acme-v02.api.letsencrypt.org/acme/acct/1234")),
),
testgroup("NAPTR", testgroup("NAPTR",
requires(providers.CanUseNAPTR), requires(providers.CanUseNAPTR),

View File

@ -1,6 +1,9 @@
package models package models
import "strings" import (
"encoding/csv"
"strings"
)
// IsQuoted returns true if the string starts and ends with a double quote. // IsQuoted returns true if the string starts and ends with a double quote.
func IsQuoted(s string) bool { func IsQuoted(s string) bool {
@ -35,3 +38,14 @@ func ParseQuotedTxt(s string) []string {
} }
return strings.Split(StripQuotes(s), `" "`) return strings.Split(StripQuotes(s), `" "`)
} }
// ParseQuotedFields is like strings.Fields except individual fields
// might be quoted using `"`.
func ParseQuotedFields(s string) ([]string, error) {
// Fields are space-separated but a field might be quoted. This is,
// essentially, a CSV where spaces are the field separator (not
// commas). Therefore, we use the CSV parser. See https://stackoverflow.com/a/47489846/71978
r := csv.NewReader(strings.NewReader(s))
r.Comma = ' ' // space
return r.Read()
}

View File

@ -71,9 +71,33 @@ func TestSetTxtParse(t *testing.T) {
t.Errorf("%v: expected TxtStrings=(%v) got (%v)", i, test.e2, ls) t.Errorf("%v: expected TxtStrings=(%v) got (%v)", i, test.e2, ls)
} }
for i := range ls { for i := range ls {
if len(ls[i]) != len(test.e2[i]) { if ls[i] != test.e2[i] {
t.Errorf("%v: expected TxtStrings=(%v) got (%v)", i, test.e2, ls) t.Errorf("%v: expected TxtStrings=(%v) got (%v)", i, test.e2, ls)
} }
} }
} }
} }
func TestParseQuotedFields(t *testing.T) {
tests := []struct {
d1 string
e1 []string
}{
{`1 2 3`, []string{`1`, `2`, `3`}},
{`1 "2" 3`, []string{`1`, `2`, `3`}},
{`1 2 "three 3"`, []string{`1`, `2`, `three 3`}},
{`0 issue "letsencrypt.org; validationmethods=dns-01; accounturi=https://acme-v02.api.letsencrypt.org/acme/acct/1234"`, []string{`0`, `issue`, `letsencrypt.org; validationmethods=dns-01; accounturi=https://acme-v02.api.letsencrypt.org/acme/acct/1234`}},
}
for i, test := range tests {
ls, _ := ParseQuotedFields(test.d1)
//fmt.Printf("%v: expected TxtStrings:\nWANT: %v\n GOT: %v\n", i, test.e1, ls)
if len(ls) != len(test.e1) {
t.Errorf("%v: expected TxtStrings=(%v) got (%v)", i, test.e1, ls)
}
for i := range ls {
if ls[i] != test.e1[i] {
t.Errorf("%v: expected TxtStrings=(%v) got (%v)", i, test.e1, ls)
}
}
}
}

View File

@ -3,7 +3,6 @@ package models
import ( import (
"fmt" "fmt"
"strconv" "strconv"
"strings"
) )
// SetTargetCAA sets the CAA fields. // SetTargetCAA sets the CAA fields.
@ -37,7 +36,10 @@ func (rc *RecordConfig) SetTargetCAAStrings(flag, tag, target string) error {
// SetTargetCAAString is like SetTargetCAA but accepts one big string. // SetTargetCAAString is like SetTargetCAA but accepts one big string.
// Ex: `0 issue "letsencrypt.org"` // Ex: `0 issue "letsencrypt.org"`
func (rc *RecordConfig) SetTargetCAAString(s string) error { func (rc *RecordConfig) SetTargetCAAString(s string) error {
part := strings.Fields(s) part, err := ParseQuotedFields(s)
if err != nil {
return err
}
if len(part) != 3 { if len(part) != 3 {
return fmt.Errorf("CAA value does not contain 3 fields: (%#v)", s) return fmt.Errorf("CAA value does not contain 3 fields: (%#v)", s)
} }