mirror of
https://github.com/StackExchange/dnscontrol.git
synced 2024-05-11 05:55:12 +00:00
DNSIMPLE: do not support unpaired double quotes in TXT (#1610)
* DNSIMPLE: do not support unpaired double quotes in TXT DNSimple supports multiple double-quotes strings in a TXT record, but does not correctly support unpaired or escaped double-quotes currently. IE the following are valid ``` asdf "asdf" "asdf" "asdf" !@#$ %^&*()([][{}{<></'`:;-_=+\ ``` however `as\"df` and `as"df` are not This removes the extra string processing in getTargetRecordPriority as all tests pass without it and currently only double-quotes cause problems in our TXT validations. I added another test to prove additional quoting is not needed. We can remove it if undesired. Also applied small lint changes to make my editor happy. * Use backticks to prevent escaping * Set TXT target record content to GetTargetRFC1035Quoted() Co-authored-by: Tom Limoncelli <tlimoncelli@stackoverflow.com>
This commit is contained in:
@ -905,6 +905,8 @@ func makeTests(t *testing.T) []*TestGroup {
|
|||||||
tc("Create TXT with double-quote", txt("foodq", `quo"te`)),
|
tc("Create TXT with double-quote", txt("foodq", `quo"te`)),
|
||||||
clear(),
|
clear(),
|
||||||
tc("Create TXT with ws at end", txt("foows1", "with space at end ")),
|
tc("Create TXT with ws at end", txt("foows1", "with space at end ")),
|
||||||
|
clear(),
|
||||||
|
tc("Create TXT with frequently escaped characters", txt("fooex", `!^.*$@#%^&()([][{}{<></:;-_=+\`)),
|
||||||
),
|
),
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -2,10 +2,14 @@ package dnsimple
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/StackExchange/dnscontrol/v3/models"
|
"github.com/StackExchange/dnscontrol/v3/models"
|
||||||
|
"github.com/StackExchange/dnscontrol/v3/pkg/recordaudit"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AuditRecords returns an error if any records are not
|
// AuditRecords returns an error if any records are not
|
||||||
// supportable by this provider.
|
// supportable by this provider.
|
||||||
func AuditRecords(records []*models.RecordConfig) error {
|
func AuditRecords(records []*models.RecordConfig) error {
|
||||||
|
if err := recordaudit.TxtNoDoubleQuotes(records); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -61,7 +61,7 @@ type dnsimpleProvider struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetNameservers returns the name servers for a domain.
|
// GetNameservers returns the name servers for a domain.
|
||||||
func (c *dnsimpleProvider) GetNameservers(domainName string) ([]*models.Nameserver, error) {
|
func (c *dnsimpleProvider) GetNameservers(_ string) ([]*models.Nameserver, error) {
|
||||||
return models.ToNameservers(defaultNameServerNames)
|
return models.ToNameservers(defaultNameServerNames)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,7 +130,7 @@ func (c *dnsimpleProvider) GetZoneRecords(domain string) (models.Records, error)
|
|||||||
|
|
||||||
// GetDomainCorrections returns corrections that update a domain.
|
// GetDomainCorrections returns corrections that update a domain.
|
||||||
func (c *dnsimpleProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
|
func (c *dnsimpleProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
|
||||||
corrections := []*models.Correction{}
|
var corrections []*models.Correction
|
||||||
err := dc.Punycode()
|
err := dc.Punycode()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -201,7 +201,7 @@ func removeApexNS(records models.Records) models.Records {
|
|||||||
|
|
||||||
// GetRegistrarCorrections returns corrections that update a domain's registrar.
|
// GetRegistrarCorrections returns corrections that update a domain's registrar.
|
||||||
func (c *dnsimpleProvider) GetRegistrarCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
|
func (c *dnsimpleProvider) GetRegistrarCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
|
||||||
corrections := []*models.Correction{}
|
var corrections []*models.Correction
|
||||||
|
|
||||||
nameServers, err := c.getNameservers(dc.Name)
|
nameServers, err := c.getNameservers(dc.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -211,7 +211,7 @@ func (c *dnsimpleProvider) GetRegistrarCorrections(dc *models.DomainConfig) ([]*
|
|||||||
|
|
||||||
actual := strings.Join(nameServers, ",")
|
actual := strings.Join(nameServers, ",")
|
||||||
|
|
||||||
expectedSet := []string{}
|
var expectedSet []string
|
||||||
for _, ns := range dc.Nameservers {
|
for _, ns := range dc.Nameservers {
|
||||||
expectedSet = append(expectedSet, ns.Name)
|
expectedSet = append(expectedSet, ns.Name)
|
||||||
}
|
}
|
||||||
@ -298,7 +298,7 @@ func (c *dnsimpleProvider) getRecords(domainName string) ([]dnsimpleapi.ZoneReco
|
|||||||
}
|
}
|
||||||
|
|
||||||
opts := &dnsimpleapi.ZoneRecordListOptions{}
|
opts := &dnsimpleapi.ZoneRecordListOptions{}
|
||||||
recs := []dnsimpleapi.ZoneRecord{}
|
var recs []dnsimpleapi.ZoneRecord
|
||||||
page := 1
|
page := 1
|
||||||
for {
|
for {
|
||||||
opts.Page = &page
|
opts.Page = &page
|
||||||
@ -540,7 +540,7 @@ func newDsp(conf map[string]string, metadata json.RawMessage) (providers.DNSServ
|
|||||||
return newProvider(conf, metadata)
|
return newProvider(conf, metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newProvider(m map[string]string, metadata json.RawMessage) (*dnsimpleProvider, error) {
|
func newProvider(m map[string]string, _ json.RawMessage) (*dnsimpleProvider, error) {
|
||||||
api := &dnsimpleProvider{}
|
api := &dnsimpleProvider{}
|
||||||
api.AccountToken = m["token"]
|
api.AccountToken = m["token"]
|
||||||
if api.AccountToken == "" {
|
if api.AccountToken == "" {
|
||||||
@ -584,21 +584,15 @@ func getTargetRecordContent(rc *models.RecordConfig) string {
|
|||||||
case "DS":
|
case "DS":
|
||||||
return fmt.Sprintf("%d %d %d %s", rc.DsKeyTag, rc.DsAlgorithm, rc.DsDigestType, rc.DsDigest)
|
return fmt.Sprintf("%d %d %d %s", rc.DsKeyTag, rc.DsAlgorithm, rc.DsDigestType, rc.DsDigest)
|
||||||
case "NAPTR":
|
case "NAPTR":
|
||||||
return fmt.Sprintf("%d %d %s %s %s %s",
|
return fmt.Sprintf(`%d %d "%s" "%s" "%s" %s`,
|
||||||
rc.NaptrOrder, rc.NaptrPreference,
|
rc.NaptrOrder, rc.NaptrPreference, rc.NaptrFlags, rc.NaptrService, rc.NaptrRegexp,
|
||||||
quoteDNSString(rc.NaptrFlags), quoteDNSString(rc.NaptrService),
|
|
||||||
quoteDNSString(rc.NaptrRegexp),
|
|
||||||
rc.GetTargetField())
|
rc.GetTargetField())
|
||||||
case "SSHFP":
|
case "SSHFP":
|
||||||
return fmt.Sprintf("%d %d %s", rc.SshfpAlgorithm, rc.SshfpFingerprint, rc.GetTargetField())
|
return fmt.Sprintf("%d %d %s", rc.SshfpAlgorithm, rc.SshfpFingerprint, rc.GetTargetField())
|
||||||
case "SRV":
|
case "SRV":
|
||||||
return fmt.Sprintf("%d %d %s", rc.SrvWeight, rc.SrvPort, rc.GetTargetField())
|
return fmt.Sprintf("%d %d %s", rc.SrvWeight, rc.SrvPort, rc.GetTargetField())
|
||||||
case "TXT":
|
case "TXT":
|
||||||
quoted := make([]string, len(rc.TxtStrings))
|
return rc.GetTargetRFC1035Quoted()
|
||||||
for i := range rc.TxtStrings {
|
|
||||||
quoted[i] = quoteDNSString(rc.TxtStrings[i])
|
|
||||||
}
|
|
||||||
return strings.Join(quoted, " ")
|
|
||||||
default:
|
default:
|
||||||
return rc.GetTargetField()
|
return rc.GetTargetField()
|
||||||
}
|
}
|
||||||
@ -618,21 +612,3 @@ func getTargetRecordPriority(rc *models.RecordConfig) int {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return a DNS string appropriately escaped for DNSimple.
|
|
||||||
// Should include the surrounding quotes.
|
|
||||||
//
|
|
||||||
// Warning: the DNSimple API is severely underdocumented in this area.
|
|
||||||
// I know that it takes multiple quoted strings just fine, and constructs the
|
|
||||||
// DNS multiple quoted items.
|
|
||||||
// I'm not 100% on the escaping, but since it's a JSON API, JSON escaping seems
|
|
||||||
// reasonable.
|
|
||||||
// I do know that DNSimple have their own checks, so anything too crazy will
|
|
||||||
// get a "400 Validation failed" HTTP response.
|
|
||||||
func quoteDNSString(unquoted string) string {
|
|
||||||
b, err := json.Marshal(unquoted)
|
|
||||||
if err != nil {
|
|
||||||
panic(fmt.Errorf("unable to marshal to JSON: %q", unquoted))
|
|
||||||
}
|
|
||||||
return string(b)
|
|
||||||
}
|
|
||||||
|
Reference in New Issue
Block a user