mirror of
https://github.com/StackExchange/dnscontrol.git
synced 2024-05-11 05:55:12 +00:00
move txt encode/decode to txtutil
This commit is contained in:
@ -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", `!^.*$@#%^&()([][{}{<></:;-_=+\`)),
|
||||
//tc("Create TXT with frequently escaped characters", txt("fooex", `!^.*$@#%^&()([][{}{<></:;-_=+\`)),
|
||||
//clear(),
|
||||
),
|
||||
|
||||
|
@ -4,6 +4,7 @@ package models
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
@ -97,6 +98,7 @@ func RRtoRC(rr dns.RR, origin string) (RecordConfig, error) {
|
||||
case *dns.TLSA:
|
||||
err = rc.SetTargetTLSA(v.Usage, v.Selector, v.MatchingType, v.Certificate)
|
||||
case *dns.TXT:
|
||||
fmt.Fprintf(os.Stdout, "DEBUG: RRtoRC TXT inboundv=%v\n", v.Txt)
|
||||
err = rc.SetTargetTXTs(v.Txt)
|
||||
default:
|
||||
return *rc, fmt.Errorf("rrToRecord: Unimplemented zone record type=%s (%v)", rc.Type, rr)
|
||||
|
@ -4,9 +4,11 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/StackExchange/dnscontrol/v4/pkg/txtutil"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/miekg/dns"
|
||||
"github.com/miekg/dns/dnsutil"
|
||||
@ -314,13 +316,18 @@ func (rc *RecordConfig) GetLabelFQDN() string {
|
||||
// ToDiffable returns a string that is comparable by a differ.
|
||||
// extraMaps: a list of maps that should be included in the comparison.
|
||||
// NB(tlim): This will be deprecated when pkg/diff is replaced by pkg/diff2.
|
||||
// Use // ToComparableNoTTL() instead.
|
||||
// Use ToComparableNoTTL() instead.
|
||||
func (rc *RecordConfig) ToDiffable(extraMaps ...map[string]string) string {
|
||||
var content string
|
||||
switch rc.Type {
|
||||
case "SOA":
|
||||
content = fmt.Sprintf("%s %v %d %d %d %d ttl=%d", rc.target, rc.SoaMbox, rc.SoaRefresh, rc.SoaRetry, rc.SoaExpire, rc.SoaMinttl, rc.TTL)
|
||||
// SoaSerial is not used in comparison
|
||||
case "TXT":
|
||||
fmt.Fprintf(os.Stdout, "DEBUG: XXXXXXXXXXXXXXXX\n")
|
||||
t := rc.GetTargetField()
|
||||
te := txtutil.EncodeQuoted(t)
|
||||
content = fmt.Sprintf("%v ttl=%d", te, rc.TTL)
|
||||
default:
|
||||
content = fmt.Sprintf("%v ttl=%d", rc.GetTargetCombined(), rc.TTL)
|
||||
}
|
||||
@ -352,6 +359,10 @@ func (rc *RecordConfig) ToDiffable(extraMaps ...map[string]string) string {
|
||||
// pseudo-records like ANAME or R53_ALIAS
|
||||
// This replaces ToDiff()
|
||||
func (rc *RecordConfig) ToComparableNoTTL() string {
|
||||
if rc.Type == "TXT" {
|
||||
fmt.Fprintf(os.Stdout, "DEBUG: ToComNoTTL txts=%s q=%q\n", rc.target, rc.target)
|
||||
return txtutil.EncodeQuoted(rc.target)
|
||||
}
|
||||
return rc.GetTargetCombined()
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,8 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/StackExchange/dnscontrol/v4/models"
|
||||
"github.com/StackExchange/dnscontrol/v4/pkg/printer"
|
||||
"github.com/StackExchange/dnscontrol/v4/pkg/txtutil"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
@ -140,7 +142,10 @@ func (z *ZoneGenData) generateZoneFileHelper(w io.Writer) error {
|
||||
// the remaining line
|
||||
var target string
|
||||
if typeStr == "TXT" {
|
||||
target = txtToNative(rr.GetTargetTXTChunked255())
|
||||
t := rr.GetTargetField()
|
||||
target = txtutil.EncodeQuoted(t)
|
||||
printer.Printf("DEBUG: pretty txt outbounds=%s\n", t)
|
||||
printer.Printf("DEBUG: pretty txt encodedv=%v\n", target)
|
||||
} else {
|
||||
target = rr.GetTargetCombined()
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
//go:generate stringer -type=State
|
||||
|
||||
package gcloud
|
||||
package txtutil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@ -19,6 +19,14 @@ const (
|
||||
StateWantSpaceOrQuote // expect open quote after `" `
|
||||
)
|
||||
|
||||
func ParseQuoted(s string) (string, error) {
|
||||
return txtDecode(s)
|
||||
}
|
||||
|
||||
func EncodeQuoted(t string) string {
|
||||
return txtEncode(ToChunks(t))
|
||||
}
|
||||
|
||||
func isRemaining(s string, i, r int) bool {
|
||||
return (len(s) - 1 - i) > r
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package route53
|
||||
package txtutil
|
||||
|
||||
import (
|
||||
"strings"
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
|
@ -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]]
|
||||
}
|
@ -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
|
||||
}
|
Reference in New Issue
Block a user