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

Add support for TXT records with multiple strings (BIND, ROUTE53) (#293)

* BIND: Support TXT records with multiple strings (#289)
* ROUTE53: Add support for TXT records with multiple strings (#292)
This commit is contained in:
Tom Limoncelli
2018-01-04 19:19:35 -05:00
committed by GitHub
parent d051f51a59
commit de88bfe8b7
32 changed files with 489 additions and 184 deletions

View File

@@ -9,10 +9,14 @@ parameters:
TXT adds an TXT record To a domain. The name should be the relative
label for the record. Use `@` for the domain apex.
The contents is a single string. While DNS permits multiple
strings in TXT records, that is not supported at this time.
The contents is either a single or multiple strings. To
specify multiple strings, include them in an array.
The string is a JavaScript string (quoted using single or double
TXT records with multiple strings are only supported by some
providers. DNSControl will produce a validation error if the
provider does not support multiple strings.
Each string is a JavaScript string (quoted using single or double
quotes). The (somewhat complex) quoting rules of the DNS protocol
will be done for you.
@@ -24,6 +28,9 @@ Modifers can be any number of [record modifiers](#record-modifiers) or json obje
D("example.com", REGISTRAR, ....,
TXT('@', '598611146-3338560'),
TXT('listserve', 'google-site-verification=12345'),
TXT('multiple', ['one', 'two', 'three']), // Multiple strings
TXT('quoted', 'any "quotes" and escapes? ugh; no worries!'),
TXT('_domainkey', 't=y; o=-;') // Escapes are done for you automatically.
);
{%endhighlight%}

View File

@@ -112,7 +112,7 @@ func runTests(t *testing.T, prv providers.DNSServiceProvider, domainName string,
}
dom.Records = append(dom.Records, &rc)
}
models.Downcase(dom.Records)
models.PostProcessRecords(dom.Records)
dom2, _ := dom.Copy()
// get corrections for first time
corrections, err := prv.GetDomainCorrections(dom)
@@ -237,7 +237,17 @@ func srv(name string, priority, weight, port uint16, target string) *rec {
}
func txt(name, target string) *rec {
return makeRec(name, target, "TXT")
// FYI: This must match the algorithm in pkg/js/helpers.js TXT.
r := makeRec(name, target, "TXT")
r.TxtStrings = []string{target}
return r
}
func txtmulti(name string, target []string) *rec {
// FYI: This must match the algorithm in pkg/js/helpers.js TXT.
r := makeRec(name, target[0], "TXT")
r.TxtStrings = target
return r
}
func caa(name string, tag string, flag uint8, target string) *rec {
@@ -427,15 +437,42 @@ func makeTests(t *testing.T) []*TestCase {
)
}
// Case
// TXT (single)
tests = append(tests, tc("Empty"),
// TXT
tc("Empty"),
tc("Create a TXT", txt("foo", "simple")),
tc("Change a TXT", txt("foo", "changed")),
tc("Empty"),
tc("Create a TXT with spaces", txt("foo", "with spaces")),
tc("Change a TXT with spaces", txt("foo", "with whitespace")),
tc("Create 1 TXT as array", txtmulti("foo", []string{"simple"})),
)
// TXTMulti
if !providers.ProviderHasCabability(*providerToRun, providers.CanUseTXTMulti) {
t.Log("Skipping TXTMulti Tests because provider does not support them")
} else {
tests = append(tests,
tc("Empty"),
tc("Create TXTMulti 1",
txtmulti("foo1", []string{"simple"}),
),
tc("Create TXTMulti 2",
txtmulti("foo1", []string{"simple"}),
txtmulti("foo2", []string{"one", "two"}),
),
tc("Create TXTMulti 3",
txtmulti("foo1", []string{"simple"}),
txtmulti("foo2", []string{"one", "two"}),
txtmulti("foo3", []string{"eh", "bee", "cee"}),
),
tc("Change TXTMulti",
txtmulti("foo1", []string{"dimple"}),
txtmulti("foo2", []string{"fun", "two"}),
txtmulti("foo3", []string{"eh", "bzz", "cee"}),
),
)
}
return tests
}

View File

@@ -1,4 +1,4 @@
$TTL 300
@ IN SOA DEFAULT_NOT_SET. DEFAULT_NOT_SET. 2017091830 3600 600 604800 1440
@ IN SOA DEFAULT_NOT_SET. DEFAULT_NOT_SET. 2018010281 3600 600 604800 1440
IN NS ns1.otherdomain.tld.
IN NS ns2.otherdomain.tld.

View File

@@ -101,6 +101,7 @@ type RecordConfig struct {
TlsaUsage uint8 `json:"tlsausage,omitempty"`
TlsaSelector uint8 `json:"tlsaselector,omitempty"`
TlsaMatchingType uint8 `json:"tlsamatchingtype,omitempty"`
TxtStrings []string `json:"txtstrings,omitempty"` // TxtStrings stores all strings (including the first). Target stores only the first one.
CombinedTarget bool `json:"-"`
@@ -247,7 +248,7 @@ func (rc *RecordConfig) ToRR() dns.RR {
rr.(*dns.TLSA).Selector = rc.TlsaSelector
rr.(*dns.TLSA).Certificate = rc.Target
case dns.TypeTXT:
rr.(*dns.TXT).Txt = []string{rc.Target}
rr.(*dns.TXT).Txt = rc.TxtStrings
default:
panic(fmt.Sprintf("ToRR: Unimplemented rtype %v", rc.Type))
// We panic so that we quickly find any switch statements
@@ -275,6 +276,12 @@ func (r Records) Grouped() map[RecordKey]Records {
return groups
}
// PostProcessRecords does any post-processing of the downloaded DNS records.
func PostProcessRecords(recs []*RecordConfig) {
Downcase(recs)
fixTxt(recs)
}
// Downcase converts all labels and targets to lowercase in a list of RecordConfig.
func Downcase(recs []*RecordConfig) {
for _, r := range recs {
@@ -292,6 +299,17 @@ func Downcase(recs []*RecordConfig) {
return
}
// fixTxt fixes TXT records generated by providers that do not understand CanUseTXTMulti.
func fixTxt(recs []*RecordConfig) {
for _, r := range recs {
if r.Type == "TXT" {
if len(r.TxtStrings) == 0 {
r.TxtStrings = []string{r.Target}
}
}
}
}
type RecordKey struct {
Name string
Type string
@@ -453,7 +471,6 @@ func (dc *DomainConfig) CombineCAAs() {
panic(pm)
}
rec.Target = rec.Content()
fmt.Printf("DEBUG: NEW TARGET: %v\n", rec.Target)
rec.CombinedTarget = true
}
}

57
models/txt.go Normal file
View File

@@ -0,0 +1,57 @@
package models
import "strings"
// SetTxt sets the value of a TXT record to s.
func (rc *RecordConfig) SetTxt(s string) {
rc.Target = s
rc.TxtStrings = []string{s}
}
// SetTxts sets the value of a TXT record to the list of strings s.
func (rc *RecordConfig) SetTxts(s []string) {
rc.Target = s[0]
rc.TxtStrings = s
}
// SetTxtParse sets the value of TXT record if the list of strings is combined into one string.
// `foo` -> []string{"foo"}
// `"foo"` -> []string{"foo"}
// `"foo" "bar"` -> []string{"foo" "bar"}
func (rc *RecordConfig) SetTxtParse(s string) {
rc.SetTxts(ParseQuotedTxt(s))
}
// IsQuoted returns true if the string starts and ends with a double quote.
func IsQuoted(s string) bool {
if s == "" {
return false
}
if len(s) < 2 {
return false
}
if s[0] == '"' && s[len(s)-1] == s[0] {
return true
}
return false
}
// StripQuotes returns the string with the starting and ending quotes removed.
func StripQuotes(s string) string {
if IsQuoted(s) {
return s[1 : len(s)-1]
}
return s
}
// ParseQuotedTxt returns the individual strings of a combined quoted string.
// `foo` -> []string{"foo"}
// `"foo"` -> []string{"foo"}
// `"foo" "bar"` -> []string{"foo" "bar"}
// NOTE: it is assumed there is exactly one space between the quotes.
func ParseQuotedTxt(s string) []string {
if !IsQuoted(s) {
return []string{s}
}
return strings.Split(StripQuotes(s), `" "`)
}

71
models/txt_test.go Normal file
View File

@@ -0,0 +1,71 @@
package models
import (
"testing"
)
func TestIsQuoted(t *testing.T) {
tests := []struct {
d1 string
e1 bool
}{
{``, false},
{`foo`, false},
{`""`, true},
{`"a"`, true},
{`"bb"`, true},
{`"ccc"`, true},
{`"aaa" "bbb"`, true},
}
for i, test := range tests {
r := IsQuoted(test.d1)
if r != test.e1 {
t.Errorf("%v: expected (%v) got (%v)", i, test.e1, r)
}
}
}
func TestStripQuotes(t *testing.T) {
tests := []struct {
d1 string
e1 string
}{
{``, ``},
{`a`, `a`},
{`bb`, `bb`},
{`ccc`, `ccc`},
{`dddd`, `dddd`},
{`"A"`, `A`},
{`"BB"`, `BB`},
{`"CCC"`, `CCC`},
{`"DDDD"`, `DDDD`},
{`"EEEEE"`, `EEEEE`},
{`"aaa" "bbb"`, `aaa" "bbb`},
}
for i, test := range tests {
r := StripQuotes(test.d1)
if r != test.e1 {
t.Errorf("%v: expected (%v) got (%v)", i, test.e1, r)
}
}
}
func TestSetTxtParse(t *testing.T) {
tests := []struct {
d1 string
e1 string
e2 []string
}{
{``, ``, []string{``}},
{`foo`, `foo`, []string{`foo`}},
{`"foo"`, `foo`, []string{`foo`}},
{`"aaa" "bbb"`, `aaa`, []string{`aaa`, `bbb`}},
}
for i, test := range tests {
x := &RecordConfig{Type: "TXT"}
x.SetTxtParse(test.d1)
if x.Target != test.e1 {
t.Errorf("%v: expected Target=(%v) got (%v)", i, test.e1, x.Target)
}
}
}

View File

@@ -220,8 +220,32 @@ var TLSA = recordBuilder('TLSA', {
},
});
function isStringOrArray(x) {
return _.isString(x) || _.isArray(x);
}
// TXT(name,target, recordModifiers...)
var TXT = recordBuilder('TXT');
var TXT = recordBuilder("TXT", {
args: [["name", _.isString], ["target", isStringOrArray]],
transform: function(record, args, modifiers) {
record.name = args.name;
// Store the strings twice:
// .target is the first string
// .txtstrings is the individual strings.
// NOTE: If there are more than 1 string, providers should only access
// .txtstrings, thus it doesn't matter what we store in .target.
// However, by storing the first string there, it improves backwards
// compatibility when the len(array) == 1 and (intentionally) breaks
// broken providers early in the integration tests.
if (_.isString(args.target)) {
record.target = args.target;
record.txtstrings = [args.target];
} else {
record.target = args.target[0]
record.txtstrings = args.target;
}
}
});
// MX(name,priority,target, recordModifiers...)
var MX = recordBuilder('MX', {

View File

@@ -11,9 +11,10 @@
"type": "TXT",
"name": "_dmarc",
"target": "v=DMARC1\\; p=reject\\; sp=reject\\; pct=100\\; rua=mailto:xx...@yyyy.com\\; ruf=mailto:xx...@yyyy.com\\; fo=1",
"ttl": 300
"ttl": 300,
"txtstrings":["v=DMARC1\\; p=reject\\; sp=reject\\; pct=100\\; rua=mailto:xx...@yyyy.com\\; ruf=mailto:xx...@yyyy.com\\; fo=1"]
}
]
}
]
}
}

View File

@@ -0,0 +1,6 @@
D("foo.com","none"
, TXT("@","simple")
, TXT("@",["one"])
, TXT("@",["bonie", "clyde"])
, TXT("@",["straw", "wood", "brick"])
);

View File

@@ -0,0 +1,37 @@
{
"registrars": [],
"dns_providers": [],
"domains": [
{
"name": "foo.com",
"registrar": "none",
"dnsProviders": {},
"records": [
{
"type": "TXT",
"name": "@",
"target": "simple",
"txtstrings": [ "simple" ]
},
{
"type": "TXT",
"name": "@",
"target": "one",
"txtstrings": [ "one" ]
},
{
"type": "TXT",
"name": "@",
"target": "bonie",
"txtstrings": [ "bonie", "clyde" ]
},
{
"type": "TXT",
"name": "@",
"target": "straw",
"txtstrings": [ "straw", "wood", "brick" ]
}
]
}
]
}

View File

@@ -192,83 +192,88 @@ var _escData = map[string]*_escFile{
"/helpers.js": {
local: "pkg/js/helpers.js",
size: 16222,
size: 17122,
modtime: 0,
compressed: `
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
`,
},

View File

@@ -35,7 +35,7 @@ func flattenSPFs(cfg *models.DNSConfig) []error {
}
if flatten, ok := txt.Metadata["flatten"]; ok && strings.HasPrefix(txt.Target, "v=spf1") {
rec = rec.Flatten(flatten)
txt.Target = rec.TXT()
txt.SetTxt(rec.TXT())
}
// now split if needed
if split, ok := txt.Metadata["split"]; ok {
@@ -46,10 +46,10 @@ func flattenSPFs(cfg *models.DNSConfig) []error {
recs := rec.TXTSplit(split + "." + domain.Name)
for k, v := range recs {
if k == "@" {
txt.Target = v
txt.SetTxt(v)
} else {
cp, _ := txt.Copy()
cp.Target = v
cp.SetTxt(v)
cp.NameFQDN = k
cp.Name = dnsutil.TrimDomainName(k, domain.Name)
domain.Records = append(domain.Records, cp)

View File

@@ -252,6 +252,7 @@ func NormalizeAndValidateConfig(config *models.DNSConfig) (errs []error) {
for _, domain := range config.Domains {
pTypes := []string{}
txtMultiDissenters := []string{}
for p := range domain.DNSProviders {
pType, ok := ptypeMap[p]
if !ok {
@@ -264,6 +265,11 @@ func NormalizeAndValidateConfig(config *models.DNSConfig) (errs []error) {
if domain.KeepUnknown && providers.ProviderHasCabability(pType, providers.CantUseNOPURGE) {
errs = append(errs, fmt.Errorf("%s uses NO_PURGE which is not supported by %s(%s)", domain.Name, p, pType))
}
// Record if any providers do not support TXTMulti:
if !providers.ProviderHasCabability(pType, providers.CanUseTXTMulti) {
txtMultiDissenters = append(txtMultiDissenters, p)
}
}
// Normalize Nameservers.
@@ -272,7 +278,7 @@ func NormalizeAndValidateConfig(config *models.DNSConfig) (errs []error) {
ns.Name = strings.TrimRight(ns.Name, ".")
}
// Normalize Records.
models.Downcase(domain.Records)
models.PostProcessRecords(domain.Records)
for _, rec := range domain.Records {
if rec.TTL == 0 {
rec.TTL = models.DefaultTTL
@@ -315,6 +321,12 @@ func NormalizeAndValidateConfig(config *models.DNSConfig) (errs []error) {
errs = append(errs, fmt.Errorf("TLSA MatchingType %d is invalid in record %s (domain %s)",
rec.TlsaMatchingType, rec.Name, domain.Name))
}
} else if rec.Type == "TXT" && len(txtMultiDissenters) != 0 && len(rec.TxtStrings) > 1 {
// There are providers that don't support TXTMulti yet there is
// a TXT record with multiple strings:
errs = append(errs,
fmt.Errorf("TXT records with multiple strings (label %v domain: %v) not supported by %s",
rec.Name, domain.Name, strings.Join(txtMultiDissenters, ",")))
}
// Populate FQDN:

View File

@@ -16,20 +16,20 @@ type adProvider struct {
psLog string
}
var docNotes = providers.DocumentationNotes{
providers.DocDualHost: providers.Cannot("This driver does not manage NS records, so should not be used for dual-host scenarios"),
providers.DocCreateDomains: providers.Cannot("AD depends on the zone already existing on the dns server"),
providers.DocOfficiallySupported: providers.Can(),
var features = providers.DocumentationNotes{
providers.CanUseAlias: providers.Cannot(),
providers.CanUseSRV: providers.Cannot(),
providers.CanUsePTR: providers.Cannot(),
providers.CanUseCAA: providers.Cannot(),
providers.CanUsePTR: providers.Cannot(),
providers.CanUseSRV: providers.Cannot(),
providers.DocCreateDomains: providers.Cannot("AD depends on the zone already existing on the dns server"),
providers.DocDualHost: providers.Cannot("This driver does not manage NS records, so should not be used for dual-host scenarios"),
providers.DocOfficiallySupported: providers.Can(),
}
// Register with the dnscontrol system.
// This establishes the name (all caps), and the function to call to initialize it.
func init() {
providers.RegisterDomainServiceProviderType("ACTIVEDIRECTORY_PS", newDNS, docNotes)
providers.RegisterDomainServiceProviderType("ACTIVEDIRECTORY_PS", newDNS, features)
}
func newDNS(config map[string]string, metadata json.RawMessage) (providers.DNSServiceProvider, error) {

View File

@@ -47,7 +47,7 @@ func (c *adProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Co
}
// Normalize
models.Downcase(foundRecords)
models.PostProcessRecords(foundRecords)
differ := diff.New(dc)
_, creates, dels, modifications := differ.IncrementalDiff(foundRecords)

View File

@@ -30,9 +30,15 @@ import (
"github.com/StackExchange/dnscontrol/providers/diff"
)
var docNotes = providers.DocumentationNotes{
providers.DocDualHost: providers.Can(),
var features = providers.DocumentationNotes{
providers.CanUseCAA: providers.Can(),
providers.CanUsePTR: providers.Can(),
providers.CanUseSRV: providers.Can(),
providers.CanUseTLSA: providers.Can(),
providers.CanUseTXTMulti: providers.Can(),
providers.CantUseNOPURGE: providers.Cannot(),
providers.DocCreateDomains: providers.Can("Driver just maintains list of zone files. It should automatically add missing ones."),
providers.DocDualHost: providers.Can(),
providers.DocOfficiallySupported: providers.Can(),
}
@@ -56,13 +62,7 @@ func initBind(config map[string]string, providermeta json.RawMessage) (providers
}
func init() {
providers.RegisterDomainServiceProviderType("BIND", initBind,
providers.CanUsePTR,
providers.CanUseSRV,
providers.CanUseCAA,
providers.CanUseTLSA,
providers.CantUseNOPURGE,
docNotes)
providers.RegisterDomainServiceProviderType("BIND", initBind, features)
}
type SoaInfo struct {
@@ -144,6 +144,7 @@ func rrToRecord(rr dns.RR, origin string, replaceSerial uint32) (models.RecordCo
rc.Target = v.Certificate
case *dns.TXT:
rc.Target = strings.Join(v.Txt, " ")
rc.TxtStrings = v.Txt
default:
log.Fatalf("rrToRecord: Unimplemented zone record type=%s (%v)\n", rc.Type, rr)
}
@@ -243,7 +244,7 @@ func (c *Bind) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correcti
}
// Normalize
models.Downcase(foundRecords)
models.PostProcessRecords(foundRecords)
differ := diff.New(dc)
_, create, del, mod := differ.IncrementalDiff(foundRecords)

View File

@@ -19,6 +19,9 @@ const (
// CanUseSRV indicates the provider can handle SRV records
CanUseSRV
// CanUseTXTMulti indicates the provider can handle TXT records with multiple strings
CanUseTXTMulti
// CanUseCAA indicates the provider can handle CAA records
CanUseCAA
@@ -66,12 +69,12 @@ type ProviderMetadata interface{}
var Notes = map[string]DocumentationNotes{}
func unwrapProviderCapabilities(pName string, meta []ProviderMetadata) {
if providerCapabilities[pName] == nil {
providerCapabilities[pName] = map[Capability]bool{}
}
for _, pm := range meta {
switch x := pm.(type) {
case Capability:
if providerCapabilities[pName] == nil {
providerCapabilities[pName] = map[Capability]bool{}
}
providerCapabilities[pName][x] = true
case DocumentationNotes:
if Notes[pName] == nil {
@@ -79,6 +82,7 @@ func unwrapProviderCapabilities(pName string, meta []ProviderMetadata) {
}
for k, v := range x {
Notes[pName][k] = v
providerCapabilities[pName][k] = v.HasFeature
}
default:
log.Fatalf("Unrecognized ProviderMetadata type: %T", pm)

View File

@@ -33,15 +33,17 @@ Domain level metadata available:
- ip_conversions
*/
var docNotes = providers.DocumentationNotes{
providers.DocDualHost: providers.Cannot("Cloudflare will not work well in situations where it is not the only DNS server"),
providers.DocCreateDomains: providers.Can(),
providers.DocOfficiallySupported: providers.Can(),
var features = providers.DocumentationNotes{
providers.CanUseAlias: providers.Can("CF automatically flattens CNAME records into A records dynamically"),
providers.CanUseCAA: providers.Can(),
providers.CanUseSRV: providers.Can(),
providers.DocCreateDomains: providers.Can(),
providers.DocDualHost: providers.Cannot("Cloudflare will not work well in situations where it is not the only DNS server"),
providers.DocOfficiallySupported: providers.Can(),
}
func init() {
providers.RegisterDomainServiceProviderType("CLOUDFLAREAPI", newCloudflare, providers.CanUseSRV, providers.CanUseAlias, providers.CanUseCAA, docNotes)
providers.RegisterDomainServiceProviderType("CLOUDFLAREAPI", newCloudflare, features)
providers.RegisterCustomRecordType("CF_REDIRECT", "CLOUDFLAREAPI", "")
providers.RegisterCustomRecordType("CF_TEMP_REDIRECT", "CLOUDFLAREAPI", "")
}
@@ -122,7 +124,7 @@ func (c *CloudflareApi) GetDomainCorrections(dc *models.DomainConfig) ([]*models
checkNSModifications(dc)
// Normalize
models.Downcase(records)
models.PostProcessRecords(records)
differ := diff.New(dc, getProxyMetadata)
_, create, del, mod := differ.IncrementalDiff(records)

View File

@@ -60,13 +60,14 @@ func NewDo(m map[string]string, metadata json.RawMessage) (providers.DNSServiceP
return api, nil
}
var docNotes = providers.DocumentationNotes{
var features = providers.DocumentationNotes{
providers.DocCreateDomains: providers.Can(),
providers.DocOfficiallySupported: providers.Cannot(),
providers.CanUseSRV: providers.Can(),
}
func init() {
providers.RegisterDomainServiceProviderType("DIGITALOCEAN", NewDo, providers.CanUseSRV, docNotes)
providers.RegisterDomainServiceProviderType("DIGITALOCEAN", NewDo, features)
}
func (api *DoApi) EnsureDomainExists(domain string) error {
@@ -102,7 +103,7 @@ func (api *DoApi) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Corre
}
// Normalize
models.Downcase(existingRecords)
models.PostProcessRecords(existingRecords)
differ := diff.New(dc)
_, create, delete, modify := differ.IncrementalDiff(existingRecords)

View File

@@ -15,16 +15,20 @@ import (
dnsimpleapi "github.com/dnsimple/dnsimple-go/dnsimple"
)
var docNotes = providers.DocumentationNotes{
providers.DocDualHost: providers.Cannot("DNSimple does not allow sufficient control over the apex NS records"),
providers.DocCreateDomains: providers.Cannot(),
providers.DocOfficiallySupported: providers.Cannot(),
var features = providers.DocumentationNotes{
providers.CanUseAlias: providers.Can(),
providers.CanUseCAA: providers.Can(),
providers.CanUsePTR: providers.Can(),
providers.CanUseSRV: providers.Can(),
providers.CanUseTLSA: providers.Cannot(),
providers.DocCreateDomains: providers.Cannot(),
providers.DocDualHost: providers.Cannot("DNSimple does not allow sufficient control over the apex NS records"),
providers.DocOfficiallySupported: providers.Cannot(),
}
func init() {
providers.RegisterRegistrarType("DNSIMPLE", newReg)
providers.RegisterDomainServiceProviderType("DNSIMPLE", newDsp, providers.CanUsePTR, providers.CanUseAlias, providers.CanUseCAA, providers.CanUseSRV, docNotes)
providers.RegisterDomainServiceProviderType("DNSIMPLE", newDsp, features)
}
const stateRegistered = "registered"
@@ -92,7 +96,7 @@ func (c *DnsimpleApi) GetDomainCorrections(dc *models.DomainConfig) ([]*models.C
})
// Normalize
models.Downcase(actual)
models.PostProcessRecords(actual)
differ := diff.New(dc)
_, create, delete, modify := differ.IncrementalDiff(actual)

View File

@@ -26,19 +26,17 @@ Info required in `creds.json`:
*/
var docNotes = providers.DocumentationNotes{
var features = providers.DocumentationNotes{
providers.CanUseCAA: providers.Can(),
providers.CanUsePTR: providers.Can(),
providers.CanUseSRV: providers.Can(),
providers.CantUseNOPURGE: providers.Cannot(),
providers.DocCreateDomains: providers.Cannot("Can only manage domains registered through their service"),
providers.DocOfficiallySupported: providers.Cannot(),
}
func init() {
providers.RegisterDomainServiceProviderType("GANDI", newDsp,
providers.CanUseCAA,
providers.CanUsePTR,
providers.CanUseSRV,
providers.CantUseNOPURGE,
docNotes,
)
providers.RegisterDomainServiceProviderType("GANDI", newDsp, features)
providers.RegisterRegistrarType("GANDI", newReg)
}
@@ -121,7 +119,7 @@ func (c *GandiApi) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Corr
dc.Records = recordsToKeep
// Normalize
models.Downcase(foundRecords)
models.PostProcessRecords(foundRecords)
differ := diff.New(dc)
_, create, del, mod := differ.IncrementalDiff(foundRecords)

View File

@@ -192,8 +192,10 @@ func convert(r *gandirecord.RecordInfo, origin string) *models.RecordConfig {
TTL: uint32(r.Ttl),
}
switch r.Type {
case "A", "AAAA", "NS", "CNAME", "PTR", "TXT":
case "A", "AAAA", "NS", "CNAME", "PTR":
// no-op
case "TXT":
rc.SetTxtParse(r.Value)
case "CAA":
var err error
rc.CaaTag, rc.CaaFlag, rc.Target, err = models.SplitCombinedCaaValue(r.Value)

View File

@@ -14,14 +14,17 @@ import (
"github.com/StackExchange/dnscontrol/providers/diff"
)
var docNotes = providers.DocumentationNotes{
providers.DocDualHost: providers.Can(),
var features = providers.DocumentationNotes{
providers.DocCreateDomains: providers.Can(),
providers.DocDualHost: providers.Can(),
providers.DocOfficiallySupported: providers.Can(),
providers.CanUsePTR: providers.Can(),
providers.CanUseSRV: providers.Can(),
providers.CanUseCAA: providers.Can(),
}
func init() {
providers.RegisterDomainServiceProviderType("GCLOUD", New, providers.CanUsePTR, providers.CanUseSRV, providers.CanUseCAA, docNotes)
providers.RegisterDomainServiceProviderType("GCLOUD", New, features)
}
type gcloud struct {
@@ -137,7 +140,7 @@ func (g *gcloud) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correc
}
// Normalize
models.Downcase(existingRecords)
models.PostProcessRecords(existingRecords)
// first collect keys that have changed
differ := diff.New(dc)

View File

@@ -13,9 +13,10 @@ import (
"net/url"
"golang.org/x/oauth2"
"regexp"
"strings"
"golang.org/x/oauth2"
)
/*
@@ -84,14 +85,14 @@ func NewLinode(m map[string]string, metadata json.RawMessage) (providers.DNSServ
return api, nil
}
var docNotes = providers.DocumentationNotes{
providers.DocOfficiallySupported: providers.Cannot(),
var features = providers.DocumentationNotes{
providers.DocDualHost: providers.Cannot(),
providers.DocOfficiallySupported: providers.Cannot(),
}
func init() {
// SRV support is in this provider, but Linode doesn't seem to support it properly
providers.RegisterDomainServiceProviderType("LINODE", NewLinode, docNotes)
providers.RegisterDomainServiceProviderType("LINODE", NewLinode, features)
}
func (api *LinodeApi) GetNameservers(domain string) ([]*models.Nameserver, error) {
@@ -138,7 +139,7 @@ func (api *LinodeApi) GetDomainCorrections(dc *models.DomainConfig) ([]*models.C
}
// Normalize
models.Downcase(existingRecords)
models.PostProcessRecords(existingRecords)
// Linode doesn't allow selecting an arbitrary TTL, only a set of predefined values
// We need to make sure we don't change it every time if it is as close as it's going to get

View File

@@ -25,20 +25,21 @@ type Namecheap struct {
client *nc.Client
}
var docNotes = providers.DocumentationNotes{
providers.DocCreateDomains: providers.Cannot("Requires domain registered through their service"),
providers.DocOfficiallySupported: providers.Cannot(),
providers.DocDualHost: providers.Cannot("Doesn't allow control of apex NS records"),
var features = providers.DocumentationNotes{
providers.CanUseAlias: providers.Cannot(),
providers.CanUseCAA: providers.Cannot(),
providers.CanUseSRV: providers.Cannot("The namecheap web console allows you to make SRV records, but their api does not let you read or set them"),
providers.CanUsePTR: providers.Cannot(),
providers.CanUseSRV: providers.Cannot("The namecheap web console allows you to make SRV records, but their api does not let you read or set them"),
providers.CanUseTLSA: providers.Cannot(),
providers.CantUseNOPURGE: providers.Cannot(),
providers.DocCreateDomains: providers.Cannot("Requires domain registered through their service"),
providers.DocDualHost: providers.Cannot("Doesn't allow control of apex NS records"),
providers.DocOfficiallySupported: providers.Cannot(),
}
func init() {
providers.RegisterRegistrarType("NAMECHEAP", newReg)
providers.RegisterDomainServiceProviderType("NAMECHEAP", newDsp, providers.CantUseNOPURGE, docNotes)
providers.RegisterDomainServiceProviderType("NAMECHEAP", newDsp, features)
providers.RegisterCustomRecordType("URL", "NAMECHEAP", "")
providers.RegisterCustomRecordType("URL301", "NAMECHEAP", "")
providers.RegisterCustomRecordType("FRAME", "NAMECHEAP", "")
@@ -156,7 +157,7 @@ func (n *Namecheap) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Cor
}
// Normalize
models.Downcase(actual)
models.PostProcessRecords(actual)
differ := diff.New(dc)
_, create, delete, modify := differ.IncrementalDiff(actual)

View File

@@ -19,11 +19,13 @@ type nameDotCom struct {
APIKey string `json:"apikey"`
}
var docNotes = providers.DocumentationNotes{
providers.DocDualHost: providers.Cannot("Apex NS records not editable"),
providers.DocCreateDomains: providers.Cannot("New domains require registration"),
providers.DocOfficiallySupported: providers.Can(),
var features = providers.DocumentationNotes{
providers.CanUseAlias: providers.Can(),
providers.CanUsePTR: providers.Cannot("PTR records are not supported (See Link)", "https://www.name.com/support/articles/205188508-Reverse-DNS-records"),
providers.CanUseSRV: providers.Can(),
providers.DocCreateDomains: providers.Cannot("New domains require registration"),
providers.DocDualHost: providers.Cannot("Apex NS records not editable"),
providers.DocOfficiallySupported: providers.Can(),
}
func newReg(conf map[string]string) (providers.Registrar, error) {
@@ -48,7 +50,7 @@ func newProvider(conf map[string]string) (*nameDotCom, error) {
func init() {
providers.RegisterRegistrarType("NAMEDOTCOM", newReg)
providers.RegisterDomainServiceProviderType("NAMEDOTCOM", newDsp, providers.CanUseAlias, providers.CanUseSRV, docNotes)
providers.RegisterDomainServiceProviderType("NAMEDOTCOM", newDsp, features)
}
///

View File

@@ -38,7 +38,7 @@ func (n *nameDotCom) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Co
checkNSModifications(dc)
// Normalize
models.Downcase(actual)
models.PostProcessRecords(actual)
differ := diff.New(dc)
_, create, del, mod := differ.IncrementalDiff(actual)

View File

@@ -67,7 +67,7 @@ func (n *nsone) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correct
desiredGrouped := dc.Records.Grouped()
// Normalize
models.Downcase(found)
models.PostProcessRecords(found)
differ := diff.New(dc)
changedGroups := differ.ChangedGroups(found)

View File

@@ -3,13 +3,14 @@ package ovh
import (
"encoding/json"
"fmt"
"sort"
"strings"
"github.com/StackExchange/dnscontrol/models"
"github.com/StackExchange/dnscontrol/providers"
"github.com/StackExchange/dnscontrol/providers/diff"
"github.com/miekg/dns/dnsutil"
"github.com/xlucas/go-ovh/ovh"
"sort"
"strings"
)
type ovhProvider struct {
@@ -17,14 +18,15 @@ type ovhProvider struct {
zones map[string]bool
}
var docNotes = providers.DocumentationNotes{
providers.DocDualHost: providers.Can(),
providers.DocCreateDomains: providers.Cannot("New domains require registration"),
providers.DocOfficiallySupported: providers.Cannot(),
var features = providers.DocumentationNotes{
providers.CanUseAlias: providers.Cannot(),
providers.CanUseTLSA: providers.Can(),
providers.CanUseCAA: providers.Cannot(),
providers.CanUsePTR: providers.Cannot(),
providers.CanUseSRV: providers.Can(),
providers.CanUseTLSA: providers.Can(),
providers.DocCreateDomains: providers.Cannot("New domains require registration"),
providers.DocDualHost: providers.Can(),
providers.DocOfficiallySupported: providers.Cannot(),
}
func newOVH(m map[string]string, metadata json.RawMessage) (*ovhProvider, error) {
@@ -50,7 +52,7 @@ func newReg(conf map[string]string) (providers.Registrar, error) {
func init() {
providers.RegisterRegistrarType("OVH", newReg)
providers.RegisterDomainServiceProviderType("OVH", newDsp, providers.CanUseSRV, providers.CanUseTLSA, docNotes)
providers.RegisterDomainServiceProviderType("OVH", newDsp, features)
}
func (c *ovhProvider) GetNameservers(domain string) ([]*models.Nameserver, error) {
@@ -124,7 +126,7 @@ func (c *ovhProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.C
}
// Normalize
models.Downcase(actual)
models.PostProcessRecords(actual)
differ := diff.New(dc)
_, create, delete, modify := differ.IncrementalDiff(actual)

View File

@@ -55,15 +55,19 @@ func newRoute53(m map[string]string, metadata json.RawMessage) (*route53Provider
return api, nil
}
var docNotes = providers.DocumentationNotes{
providers.DocDualHost: providers.Can(),
providers.DocCreateDomains: providers.Can(),
providers.DocOfficiallySupported: providers.Can(),
var features = providers.DocumentationNotes{
providers.CanUseAlias: providers.Cannot("R53 does not provide a generic ALIAS functionality. They do have 'ALIAS' CNAME types to point at various AWS infrastructure, but dnscontrol has not implemented those."),
providers.DocCreateDomains: providers.Can(),
providers.DocDualHost: providers.Can(),
providers.DocOfficiallySupported: providers.Can(),
providers.CanUsePTR: providers.Can(),
providers.CanUseSRV: providers.Can(),
providers.CanUseTXTMulti: providers.Can(),
providers.CanUseCAA: providers.Can(),
}
func init() {
providers.RegisterDomainServiceProviderType("ROUTE53", newRoute53Dsp, providers.CanUsePTR, providers.CanUseSRV, providers.CanUseCAA, docNotes)
providers.RegisterDomainServiceProviderType("ROUTE53", newRoute53Dsp, features)
providers.RegisterRegistrarType("ROUTE53", newRoute53Reg)
}
@@ -170,7 +174,7 @@ func (r *route53Provider) GetDomainCorrections(dc *models.DomainConfig) ([]*mode
}
// Normalize
models.Downcase(existingRecords)
models.PostProcessRecords(existingRecords)
//diff
differ := diff.New(dc)

View File

@@ -21,8 +21,12 @@ type SoftLayer struct {
Session *session.Session
}
var features = providers.DocumentationNotes{
providers.CanUseSRV: providers.Can(),
}
func init() {
providers.RegisterDomainServiceProviderType("SOFTLAYER", newReg, providers.CanUseSRV)
providers.RegisterDomainServiceProviderType("SOFTLAYER", newReg, features)
}
func newReg(conf map[string]string, _ json.RawMessage) (providers.DNSServiceProvider, error) {
@@ -164,7 +168,7 @@ func (s *SoftLayer) getExistingRecords(domain *datatypes.Dns_Domain) ([]*models.
}
// Normalize
models.Downcase(actual)
models.PostProcessRecords(actual)
return actual, nil
}

View File

@@ -24,16 +24,18 @@ Info required in `creds.json`:
*/
var docNotes = providers.DocumentationNotes{
var features = providers.DocumentationNotes{
providers.CanUseAlias: providers.Cannot(),
providers.CanUseCAA: providers.Can(),
providers.CanUsePTR: providers.Cannot(),
providers.CanUseSRV: providers.Can(),
providers.CanUseTLSA: providers.Cannot(),
providers.DocCreateDomains: providers.Can(),
providers.DocOfficiallySupported: providers.Cannot(),
providers.CanUseAlias: providers.Cannot(),
providers.CanUseTLSA: providers.Cannot(),
providers.CanUsePTR: providers.Cannot(),
}
func init() {
providers.RegisterDomainServiceProviderType("VULTR", NewVultr, providers.CanUseSRV, providers.CanUseCAA, docNotes)
providers.RegisterDomainServiceProviderType("VULTR", NewVultr, features)
}
// VultrApi represents the Vultr DNSServiceProvider
@@ -98,7 +100,7 @@ func (api *VultrApi) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Co
}
// Normalize
models.Downcase(curRecords)
models.PostProcessRecords(curRecords)
differ := diff.New(dc)
_, create, delete, modify := differ.IncrementalDiff(curRecords)