diff --git a/docs/_functions/domain/SOA.md b/docs/_functions/domain/SOA.md new file mode 100644 index 000000000..6b31a06c9 --- /dev/null +++ b/docs/_functions/domain/SOA.md @@ -0,0 +1,30 @@ +--- +name: SOA +parameters: + - name + - ns + - mbox + - refresh + - retry + - expire + - minttl + - modifiers... +--- + +`SOA` adds an `SOA` record to a domain. The name should be `@`. ns and mbox are strings. The other fields are unsigned 32-bit ints. + +{% include startExample.html %} +{% highlight js %} + +D("example.com", REG_THIRDPARTY, DnsProvider("DNS_BIND"), + SOA("@", "ns3.example.org.", "hostmaster.example.org.", 3600, 600, 604800, 1440), +); + +{%endhighlight%} +{% include endExample.html %} + + +## Notes: + +* The serial number is managed automatically. It isn't even a field in `SOA()`. +* Most providers automatically generate SOA records. They will ignore any `SOA()` statements. diff --git a/integrationTest/integration_test.go b/integrationTest/integration_test.go index 50122da9f..8ca87b777 100644 --- a/integrationTest/integration_test.go +++ b/integrationTest/integration_test.go @@ -411,6 +411,12 @@ func ds(name string, keyTag uint16, algorithm, digestType uint8, digest string) return r } +func soa(name string, ns, mbox string, serial, refresh, retry, expire, minttl uint32) *models.RecordConfig { + r := makeRec(name, "", "SOA") + r.SetTargetSOA(ns, mbox, serial, refresh, retry, expire, minttl) + return r +} + func srv(name string, priority, weight, port uint16, target string) *models.RecordConfig { r := makeRec(name, target, "SRV") r.SetTargetSRV(priority, weight, port, target) @@ -1031,6 +1037,19 @@ func makeTests(t *testing.T) []*TestGroup { tc("Modify PTR record", ptr("4", "bar.com.")), ), + // SOA + testgroup("SOA", requires(providers.CanUseSOA), + tc("Create SOA record", soa("@", "kim.ns.cloudflare.com.", "dns.cloudflare.com.", 2037190000, 10000, 2400, 604800, 3600)), + tc("Modify SOA ns ", soa("@", "mmm.ns.cloudflare.com.", "dns.cloudflare.com.", 2037190000, 10000, 2400, 604800, 3600)), + tc("Modify SOA mbox ", soa("@", "mmm.ns.cloudflare.com.", "eee.cloudflare.com.", 2037190000, 10000, 2400, 604800, 3600)), + tc("Modify SOA refres", soa("@", "mmm.ns.cloudflare.com.", "eee.cloudflare.com.", 2037190000, 10001, 2400, 604800, 3600)), + tc("Modify SOA retry ", soa("@", "mmm.ns.cloudflare.com.", "eee.cloudflare.com.", 2037190000, 10001, 2401, 604800, 3600)), + tc("Modify SOA expire", soa("@", "mmm.ns.cloudflare.com.", "eee.cloudflare.com.", 2037190000, 10001, 2401, 604801, 3600)), + tc("Modify SOA minttl", soa("@", "mmm.ns.cloudflare.com.", "eee.cloudflare.com.", 2037190000, 10001, 2401, 604801, 3601)), + clear(), + tc("Create SOA record", soa("@", "kim.ns.cloudflare.com.", "dns.cloudflare.com.", 2037190000, 10000, 2400, 604800, 3600)), + ), + testgroup("SRV", requires(providers.CanUseSRV), not("ACTIVEDIRECTORY_PS"), tc("SRV record", srv("_sip._tcp", 5, 6, 7, "foo.com.")), tc("Second SRV record, same prio", srv("_sip._tcp", 5, 6, 7, "foo.com."), srv("_sip._tcp", 5, 60, 70, "foo2.com.")), diff --git a/pkg/js/helpers.js b/pkg/js/helpers.js index 2d90e1598..ec3c8763e 100644 --- a/pkg/js/helpers.js +++ b/pkg/js/helpers.js @@ -357,7 +357,7 @@ var NAPTR = recordBuilder('NAPTR', { }, }); -// SOA(name,ns,mbox,serial,refresh,retry,expire,minimum, recordModifiers...) +// SOA(name,ns,mbox,refresh,retry,expire,minimum, recordModifiers...) var SOA = recordBuilder('SOA', { args: [ ['name', _.isString], diff --git a/providers/bind/bindProvider.go b/providers/bind/bindProvider.go index 9ab979d92..08b9f853c 100644 --- a/providers/bind/bindProvider.go +++ b/providers/bind/bindProvider.go @@ -33,19 +33,20 @@ import ( ) var features = providers.DocumentationNotes{ + providers.CanAutoDNSSEC: providers.Can("Just writes out a comment indicating DNSSEC was requested"), + providers.CanGetZones: providers.Can(), providers.CanUseCAA: providers.Can(), providers.CanUseDS: providers.Can(), - providers.CanUsePTR: providers.Can(), providers.CanUseNAPTR: providers.Can(), + providers.CanUsePTR: providers.Can(), + providers.CanUseSOA: providers.Can(), providers.CanUseSRV: providers.Can(), providers.CanUseSSHFP: providers.Can(), providers.CanUseTLSA: providers.Can(), - providers.CanAutoDNSSEC: providers.Can("Just writes out a comment indicating DNSSEC was requested"), 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(), - providers.CanGetZones: providers.Can(), } func initBind(config map[string]string, providermeta json.RawMessage) (providers.DNSServiceProvider, error) { @@ -84,8 +85,8 @@ func init() { providers.RegisterDomainServiceProviderType("BIND", fns, features) } -// SoaInfo contains the parts of the default SOA settings. -type SoaInfo struct { +// SoaDefaults contains the parts of the default SOA settings. +type SoaDefaults struct { Ns string `json:"master"` Mbox string `json:"mbox"` Serial uint32 `json:"serial"` @@ -96,14 +97,14 @@ type SoaInfo struct { TTL uint32 `json:"ttl,omitempty"` } -func (s SoaInfo) String() string { +func (s SoaDefaults) String() string { return fmt.Sprintf("%s %s %d %d %d %d %d %d", s.Ns, s.Mbox, s.Serial, s.Refresh, s.Retry, s.Expire, s.Minttl, s.TTL) } // bindProvider is the provider handle for the bindProvider driver. type bindProvider struct { - DefaultNS []string `json:"default_ns"` - DefaultSoa SoaInfo `json:"default_soa"` + DefaultNS []string `json:"default_ns"` + DefaultSoa SoaDefaults `json:"default_soa"` nameservers []*models.Nameserver directory string filenameformat string diff --git a/providers/bind/soa.go b/providers/bind/soa.go index 053097382..5c35c92c6 100644 --- a/providers/bind/soa.go +++ b/providers/bind/soa.go @@ -4,14 +4,14 @@ import ( "github.com/StackExchange/dnscontrol/v3/models" ) -func makeSoa(origin string, defSoa *SoaInfo, existing, desired *models.RecordConfig) (*models.RecordConfig, uint32) { +func makeSoa(origin string, defSoa *SoaDefaults, existing, desired *models.RecordConfig) (*models.RecordConfig, uint32) { // Create a SOA record. Take data from desired, existing, default, // or hardcoded defaults. soaRec := models.RecordConfig{} soaRec.SetLabel("@", origin) if defSoa == nil { - defSoa = &SoaInfo{} + defSoa = &SoaDefaults{} } if existing == nil { existing = &models.RecordConfig{} diff --git a/providers/bind/soa_test.go b/providers/bind/soa_test.go index 11fe63d01..d1147a540 100644 --- a/providers/bind/soa_test.go +++ b/providers/bind/soa_test.go @@ -16,7 +16,7 @@ func mkRC(target string, rec *models.RecordConfig) *models.RecordConfig { func Test_makeSoa(t *testing.T) { origin := "example.com" var tests = []struct { - def *SoaInfo + def *SoaDefaults existing *models.RecordConfig desired *models.RecordConfig expectedSoa *models.RecordConfig @@ -24,7 +24,7 @@ func Test_makeSoa(t *testing.T) { }{ { // If everything is blank, the hard-coded defaults should kick in. - &SoaInfo{"", "", 0, 0, 0, 0, 0, models.DefaultTTL}, + &SoaDefaults{"", "", 0, 0, 0, 0, 0, models.DefaultTTL}, mkRC("", &models.RecordConfig{SoaMbox: "", SoaSerial: 0, SoaRefresh: 0, SoaRetry: 0, SoaExpire: 0, SoaMinttl: 0}), mkRC("", &models.RecordConfig{SoaMbox: "", SoaSerial: 0, SoaRefresh: 0, SoaRetry: 0, SoaExpire: 0, SoaMinttl: 0}), mkRC("DEFAULT_NOT_SET.", &models.RecordConfig{SoaMbox: "DEFAULT_NOT_SET.", SoaSerial: 1, SoaRefresh: 3600, SoaRetry: 600, SoaExpire: 604800, SoaMinttl: 1440}), @@ -32,7 +32,7 @@ func Test_makeSoa(t *testing.T) { }, { // If everything is filled, leave the desired values in place. - &SoaInfo{"ns.example.com", "root.example.com", 1, 2, 3, 4, 5, models.DefaultTTL}, + &SoaDefaults{"ns.example.com", "root.example.com", 1, 2, 3, 4, 5, models.DefaultTTL}, mkRC("a", &models.RecordConfig{SoaMbox: "aa", SoaSerial: 10, SoaRefresh: 11, SoaRetry: 12, SoaExpire: 13, SoaMinttl: 14}), mkRC("b", &models.RecordConfig{SoaMbox: "bb", SoaSerial: 15, SoaRefresh: 16, SoaRetry: 17, SoaExpire: 18, SoaMinttl: 19}), mkRC("b", &models.RecordConfig{SoaMbox: "bb", SoaSerial: 15, SoaRefresh: 16, SoaRetry: 17, SoaExpire: 18, SoaMinttl: 19}), @@ -40,7 +40,7 @@ func Test_makeSoa(t *testing.T) { }, { // Test incrementing serial. - &SoaInfo{"ns.example.com", "root.example.com", 1, 2, 3, 4, 5, models.DefaultTTL}, + &SoaDefaults{"ns.example.com", "root.example.com", 1, 2, 3, 4, 5, models.DefaultTTL}, mkRC("a", &models.RecordConfig{SoaMbox: "aa", SoaSerial: 2019022301, SoaRefresh: 11, SoaRetry: 12, SoaExpire: 13, SoaMinttl: 14}), mkRC("b", &models.RecordConfig{SoaMbox: "bb", SoaSerial: 0, SoaRefresh: 16, SoaRetry: 17, SoaExpire: 18, SoaMinttl: 19}), mkRC("b", &models.RecordConfig{SoaMbox: "bb", SoaSerial: 2019022301, SoaRefresh: 16, SoaRetry: 17, SoaExpire: 18, SoaMinttl: 19}), @@ -48,7 +48,7 @@ func Test_makeSoa(t *testing.T) { }, { // Test incrementing serial_2. - &SoaInfo{"ns.example.com", "root.example.com", 1, 2, 3, 4, 5, models.DefaultTTL}, + &SoaDefaults{"ns.example.com", "root.example.com", 1, 2, 3, 4, 5, models.DefaultTTL}, mkRC("a", &models.RecordConfig{SoaMbox: "aa", SoaSerial: 0, SoaRefresh: 11, SoaRetry: 12, SoaExpire: 13, SoaMinttl: 14}), mkRC("b", &models.RecordConfig{SoaMbox: "bb", SoaSerial: 2019022304, SoaRefresh: 16, SoaRetry: 17, SoaExpire: 18, SoaMinttl: 19}), mkRC("b", &models.RecordConfig{SoaMbox: "bb", SoaSerial: 2019022304, SoaRefresh: 16, SoaRetry: 17, SoaExpire: 18, SoaMinttl: 19}), @@ -56,7 +56,7 @@ func Test_makeSoa(t *testing.T) { }, { // If there are gaps in existing or desired, fill in as appropriate. - &SoaInfo{"ns.example.com", "root.example.com", 1, 2, 3, 4, 5, models.DefaultTTL}, + &SoaDefaults{"ns.example.com", "root.example.com", 1, 2, 3, 4, 5, models.DefaultTTL}, mkRC("", &models.RecordConfig{SoaMbox: "aa", SoaSerial: 0, SoaRefresh: 11, SoaRetry: 0, SoaExpire: 13, SoaMinttl: 0}), mkRC("b", &models.RecordConfig{SoaMbox: "", SoaSerial: 15, SoaRefresh: 0, SoaRetry: 17, SoaExpire: 0, SoaMinttl: 19}), mkRC("b", &models.RecordConfig{SoaMbox: "aa", SoaSerial: 15, SoaRefresh: 11, SoaRetry: 17, SoaExpire: 13, SoaMinttl: 19}), @@ -64,7 +64,7 @@ func Test_makeSoa(t *testing.T) { }, { // Gaps + existing==nil - &SoaInfo{"ns.example.com", "root.example.com", 1, 2, 3, 4, 5, models.DefaultTTL}, + &SoaDefaults{"ns.example.com", "root.example.com", 1, 2, 3, 4, 5, models.DefaultTTL}, nil, mkRC("b", &models.RecordConfig{SoaMbox: "", SoaSerial: 15, SoaRefresh: 0, SoaRetry: 17, SoaExpire: 0, SoaMinttl: 19}), mkRC("b", &models.RecordConfig{SoaMbox: "root.example.com", SoaSerial: 15, SoaRefresh: 2, SoaRetry: 17, SoaExpire: 4, SoaMinttl: 19}), @@ -73,7 +73,7 @@ func Test_makeSoa(t *testing.T) { { // Gaps + desired==nil // NB(tom): In the code as of 2020-02-23, desired will never be nil. - &SoaInfo{"ns.example.com", "root.example.com", 1, 2, 3, 4, 5, models.DefaultTTL}, + &SoaDefaults{"ns.example.com", "root.example.com", 1, 2, 3, 4, 5, models.DefaultTTL}, mkRC("", &models.RecordConfig{SoaMbox: "aa", SoaSerial: 0, SoaRefresh: 11, SoaRetry: 0, SoaExpire: 13, SoaMinttl: 0}), nil, mkRC("ns.example.com", &models.RecordConfig{SoaMbox: "aa", SoaSerial: 1, SoaRefresh: 11, SoaRetry: 3, SoaExpire: 13, SoaMinttl: 5}),