From 6448fc5a8eb70769fe045c801cc51c65b3b5d5db Mon Sep 17 00:00:00 2001 From: Tom Limoncelli Date: Sun, 12 Nov 2023 14:26:27 -0500 Subject: [PATCH] move txt encode/decode to txtutil --- integrationTest/integration_test.go | 8 +- models/dnsrr.go | 2 + models/record.go | 13 +- pkg/prettyzone/prettyzone.go | 7 +- {providers/gcloud => pkg/txtutil}/txtcode.go | 10 +- .../route53 => pkg/txtutil}/txtcode_test.go | 2 +- providers/gcloud/gcloudProvider.go | 7 +- providers/gcloud/txtcode_test.go | 81 ------------ providers/route53/route53Provider.go | 5 +- providers/route53/state_string.go | 28 ----- providers/route53/txtcode.go | 118 ------------------ 11 files changed, 41 insertions(+), 240 deletions(-) rename {providers/gcloud => pkg/txtutil}/txtcode.go (94%) rename {providers/route53 => pkg/txtutil}/txtcode_test.go (99%) delete mode 100644 providers/gcloud/txtcode_test.go delete mode 100644 providers/route53/state_string.go delete mode 100644 providers/route53/txtcode.go diff --git a/integrationTest/integration_test.go b/integrationTest/integration_test.go index 3e1dd1228..fafb5a7bd 100644 --- a/integrationTest/integration_test.go +++ b/integrationTest/integration_test.go @@ -1074,7 +1074,7 @@ func makeTests(t *testing.T) []*TestGroup { // update the AuditRecords(). // Commented this one out. Nobody supports this or needs it. - tc("a 0-byte TXT", txt("foo0", "")), + //tc("a 0-byte TXT", txt("foo0", "")), tc("a 254-byte TXT", txt("foo254", strings.Repeat("B", 254))), tc("a 255-byte TXT", txt("foo255", strings.Repeat("C", 255))), @@ -1088,17 +1088,17 @@ func makeTests(t *testing.T) []*TestGroup { tc("TXT with 1 backtick", txt("foobt", "blah`blah")), tc("TXT with 1 double-quotes", txt("foodq", `quo"te`)), tc("TXT with 2 double-quotes", txt("foodqs", `q"uo"te`)), - tc("TXT with 1 backslash", txt("fooosbs", `back\slash`)), + tc("TXT with 1 backslash", txt("fooosbs", `backs\lash`)), tc("TXT interior ws", txt("foosp", "with spaces")), tc("TXT trailing ws", txt("foows1", "with space at end ")), - //tc("Create a TXT/SPF", txt("foo", "v=spf1 ip4:99.99.99.99 -all")), + tc("Create a TXT/SPF", txt("foo", "v=spf1 ip4:99.99.99.99 -all")), // This was added because Vultr syntax-checks TXT records with SPF contents. //clear(), // TODO(tlim): Re-add this when we fix the RFC1035 escaped-quotes issue. - tc("Create TXT with frequently escaped characters", txt("fooex", `!^.*$@#%^&()([][{}{<> r } diff --git a/providers/route53/txtcode_test.go b/pkg/txtutil/txtcode_test.go similarity index 99% rename from providers/route53/txtcode_test.go rename to pkg/txtutil/txtcode_test.go index a001cb370..016a16086 100644 --- a/providers/route53/txtcode_test.go +++ b/pkg/txtutil/txtcode_test.go @@ -1,4 +1,4 @@ -package route53 +package txtutil import ( "strings" diff --git a/providers/gcloud/gcloudProvider.go b/providers/gcloud/gcloudProvider.go index 84a96217c..d0835ba77 100644 --- a/providers/gcloud/gcloudProvider.go +++ b/providers/gcloud/gcloudProvider.go @@ -12,6 +12,7 @@ import ( "github.com/StackExchange/dnscontrol/v4/models" "github.com/StackExchange/dnscontrol/v4/pkg/diff" "github.com/StackExchange/dnscontrol/v4/pkg/printer" + "github.com/StackExchange/dnscontrol/v4/pkg/txtutil" "github.com/StackExchange/dnscontrol/v4/providers" gauth "golang.org/x/oauth2/google" gdns "google.golang.org/api/dns/v1" @@ -310,10 +311,10 @@ func (g *gcloudProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, exis //chunks := txtutil.ToChunks(r.GetTargetField()) //printer.Printf("DEBUG: gcloud txt chunks=%+v\n", chunks) //newRRs.Rrdatas = append(newRRs.Rrdatas, chunks[0:]...) - t := r.GetTargetTXTChunked255() + t := r.GetTargetField() printer.Printf("DEBUG: gcloud outboundv=%v\n", t) //tc := txtutil.RFC1035ChunkedAndQuoted(t) - tc := txtEncode(t) + tc := txtutil.EncodeQuoted(t) printer.Printf("DEBUG: gcloud encodedv=%v\n", tc) newRRs.Rrdatas = append(newRRs.Rrdatas, tc) } else { @@ -421,7 +422,7 @@ func nativeToRecord(set *gdns.ResourceRecordSet, rec, origin string) (*models.Re //t := r //te := models.ParseQuotedTxt(rec) t := rec - td, err := txtDecode(t) + td, err := txtutil.ParseQuoted(t) if err != nil { return nil, fmt.Errorf("gcloud TXT parse error: %w", err) } diff --git a/providers/gcloud/txtcode_test.go b/providers/gcloud/txtcode_test.go deleted file mode 100644 index 76179e9b0..000000000 --- a/providers/gcloud/txtcode_test.go +++ /dev/null @@ -1,81 +0,0 @@ -package gcloud - -import ( - "strings" - "testing" -) - -func r(s string, c int) string { return strings.Repeat(s, c) } - -func TestTxtDecode(t *testing.T) { - tests := []struct { - data string - expected []string - }{ - {``, []string{``}}, - {`""`, []string{``}}, - {`foo`, []string{`foo`}}, - {`"foo"`, []string{`foo`}}, - {`"foo bar"`, []string{`foo bar`}}, - {`foo bar`, []string{`foo`, `bar`}}, - {`"aaa" "bbb"`, []string{`aaa`, `bbb`}}, - {`"a\"a" "bbb"`, []string{`a"a`, `bbb`}}, - // Seen in live traffic: - {"\"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\"", - []string{r("B", 254)}}, - {"\"CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\"", - []string{r("C", 255)}}, - {"\"DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD\" \"D\"", - []string{r("D", 255), "D"}}, - {"\"EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\" \"EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\"", - []string{r("E", 255), r("E", 255)}}, - {"\"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF\" \"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF\" \"F\"", - []string{r("F", 255), r("F", 255), "F"}}, - {"\"GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG\" \"GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG\" \"GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG\"", - []string{r("G", 255), r("G", 255), r("G", 255)}}, - {"\"HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH\" \"HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH\" \"HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH\" \"H\"", - []string{r("H", 255), r("H", 255), r("H", 255), "H"}}, - {"\"quo'te\"", []string{`quo'te`}}, - {"\"blah`blah\"", []string{"blah`blah"}}, - {"\"quo\\\"te\"", []string{`quo"te`}}, - {"\"q\\\"uo\\\"te\"", []string{`q"uo"te`}}, - {"\"backs\\\\lash\"", []string{`backs\lash`}}, - } - for i, test := range tests { - got, err := txtDecode(test.data) - if err != nil { - t.Error(err) - } - - want := strings.Join(test.expected, "") - if got != want { - t.Errorf("%v: expected TxtStrings=(%q) got (%q)", i, want, got) - } - } -} - -func TestTxtEncode(t *testing.T) { - tests := []struct { - data []string - expected string - }{ - {[]string{``}, `""`}, - {[]string{`foo`}, `"foo"`}, - {[]string{`aaa`, `bbb`}, `"aaa" "bbb"`}, - {[]string{`ccc`, `ddd`, `eee`}, `"ccc" "ddd" "eee"`}, - {[]string{`a"a`, `bbb`}, `"a\"a" "bbb"`}, - {[]string{`quo'te`}, "\"quo'te\""}, - {[]string{"blah`blah"}, "\"blah`blah\""}, - {[]string{`quo"te`}, "\"quo\\\"te\""}, - {[]string{`q"uo"te`}, "\"q\\\"uo\\\"te\""}, - {[]string{`backs\lash`}, "\"backs\\\\lash\""}, - } - for i, test := range tests { - got := txtEncode(test.data) - - want := test.expected - if got != want { - t.Errorf("%v: expected TxtStrings=v(%v) got (%v)", i, want, got) - } - } -} diff --git a/providers/route53/route53Provider.go b/providers/route53/route53Provider.go index 1ed719a53..6c34d0f5c 100644 --- a/providers/route53/route53Provider.go +++ b/providers/route53/route53Provider.go @@ -14,6 +14,7 @@ import ( "github.com/StackExchange/dnscontrol/v4/models" "github.com/StackExchange/dnscontrol/v4/pkg/diff2" "github.com/StackExchange/dnscontrol/v4/pkg/printer" + "github.com/StackExchange/dnscontrol/v4/pkg/txtutil" "github.com/StackExchange/dnscontrol/v4/providers" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/config" @@ -356,7 +357,7 @@ func (r *route53Provider) GetZoneRecordsCorrections(dc *models.DomainConfig, exi // t := `"` + strings.Join(ts, `" "`) + `"` // printer.Printf("DEBUG: txt outboundv=%v\n", t) - t := txtEncode(r.GetTargetTXTChunked255()) + t := txtutil.EncodeQuoted(r.GetTargetField()) //printer.Printf("XXXXXXXXX %v\n", t) rr = r53Types.ResourceRecord{ @@ -523,7 +524,7 @@ func nativeToRecords(set r53Types.ResourceRecordSet, origin string) ([]*models.R //err = rc.SetTargetTXTs(dt) var t string - t, err = txtDecode(val) + t, err = txtutil.ParseQuoted(val) if err == nil { err = rc.SetTargetTXT(t) } diff --git a/providers/route53/state_string.go b/providers/route53/state_string.go deleted file mode 100644 index 97bc49339..000000000 --- a/providers/route53/state_string.go +++ /dev/null @@ -1,28 +0,0 @@ -// Code generated by "stringer -type=State"; DO NOT EDIT. - -package route53 - -import "strconv" - -func _() { - // An "invalid array index" compiler error signifies that the constant values have changed. - // Re-run the stringer command to generate them again. - var x [1]struct{} - _ = x[StateStart-0] - _ = x[StateQuoted-1] - _ = x[StateBackslash-2] - _ = x[StateQuotedBackslash-3] - _ = x[StateWantSpace-4] - _ = x[StateWantSpaceOrQuote-5] -} - -const _State_name = "StateStartStateQuotedStateBackslashStateQuotedBackslashStateWantSpaceStateWantSpaceOrQuote" - -var _State_index = [...]uint8{0, 10, 21, 35, 55, 69, 90} - -func (i State) String() string { - if i < 0 || i >= State(len(_State_index)-1) { - return "State(" + strconv.FormatInt(int64(i), 10) + ")" - } - return _State_name[_State_index[i]:_State_index[i+1]] -} diff --git a/providers/route53/txtcode.go b/providers/route53/txtcode.go deleted file mode 100644 index 2f3fc593b..000000000 --- a/providers/route53/txtcode.go +++ /dev/null @@ -1,118 +0,0 @@ -//go:generate stringer -type=State - -package route53 - -import ( - "bytes" - "fmt" - "strings" -) - -type State int - -const ( - StateStart State = iota // Normal text - StateQuoted // Quoted text - StateBackslash // last char was backslash - StateQuotedBackslash // last char was backlash in a quoted string - StateWantSpace // expect space after closing quote - StateWantSpaceOrQuote // expect open quote after `" ` -) - -func isRemaining(s string, i, r int) bool { - return (len(s) - 1 - i) > r -} - -// txtDecode decodes TXT strings received from ROUTE53. -func txtDecode(s string) (string, error) { - // Parse according to RFC1035 zonefile specifications. - // "foo" -> one string: `foo`` - // "foo" "bar" -> two strings: `foo` and `bar` - // quotes and backslashes are escaped using \ - - //printer.Printf("DEBUG: route53 txt inboundv=%v\n", s) - - b := &bytes.Buffer{} - state := StateStart - for i, c := range s { - - //printer.Printf("DEBUG: state=%v rune=%v\n", state, string(c)) - - switch state { - - case StateStart: - - if c == '"' { - state = StateQuoted - } else if c == ' ' { - state = StateQuoted - } else if c == '\\' { - if isRemaining(s, i, 1) { - state = StateBackslash - } else { - return "", fmt.Errorf("txtDecode string ends with backslash q(%q)", s) - } - } else { - b.WriteRune(c) - } - - case StateBackslash: - b.WriteRune(c) - state = StateStart - - case StateQuoted: - - if c == '\\' { - if isRemaining(s, i, 1) { - state = StateQuotedBackslash - } else { - return "", fmt.Errorf("txtDecode quoted string ends with backslash q(%q)", s) - } - } else if c == '"' { - state = StateWantSpace - } else { - b.WriteRune(c) - } - - case StateQuotedBackslash: - b.WriteRune(c) - state = StateQuoted - - case StateWantSpace: - if c == ' ' { - state = StateWantSpaceOrQuote - } else { - return "", fmt.Errorf("txtDecode expected whitespace after close quote q(%q)", s) - } - - case StateWantSpaceOrQuote: - if c == ' ' { - state = StateWantSpaceOrQuote - } else if c == '"' { - state = StateQuoted - } else { - state = StateStart - b.WriteRune(c) - } - - } - } - - r := b.String() - //printer.Printf("DEBUG: route53 txt decodedv=%v\n", r) - return r, nil -} - -// txtEncode encodes TXT strings as expected by ROUTE53. -func txtEncode(ts []string) string { - //printer.Printf("DEBUG: route53 txt outboundv=%v\n", ts) - - for i := range ts { - ts[i] = strings.ReplaceAll(ts[i], `\`, `\\`) - ts[i] = strings.ReplaceAll(ts[i], `"`, `\"`) - } - t := `"` + strings.Join(ts, `" "`) + `"` - - //printer.Printf("DEBUG: route53 txt encodedv=%v\n", t) - return t -}