diff --git a/build/generate/featureMatrix.go b/build/generate/featureMatrix.go index 48c593b84..6297c3f90 100644 --- a/build/generate/featureMatrix.go +++ b/build/generate/featureMatrix.go @@ -80,12 +80,14 @@ func matrixData() *FeatureMatrix { DomainModifierAlias = "[`ALIAS`](language-reference/domain-modifiers/ALIAS.md)" DomainModifierCaa = "[`CAA`](language-reference/domain-modifiers/CAA.md)" DomainModifierDnssec = "[`AUTODNSSEC`](language-reference/domain-modifiers/AUTODNSSEC_ON.md)" + DomainModifierHttps = "[`HTTPS`](language-reference/domain-modifiers/HTTPS.md)" DomainModifierLoc = "[`LOC`](language-reference/domain-modifiers/LOC.md)" DomainModifierNaptr = "[`NAPTR`](language-reference/domain-modifiers/NAPTR.md)" DomainModifierPtr = "[`PTR`](language-reference/domain-modifiers/PTR.md)" DomainModifierSoa = "[`SOA`](language-reference/domain-modifiers/SOA.md)" DomainModifierSrv = "[`SRV`](language-reference/domain-modifiers/SRV.md)" DomainModifierSshfp = "[`SSHFP`](language-reference/domain-modifiers/SSHFP.md)" + DomainModifierSvcb = "[`SVCB`](language-reference/domain-modifiers/SVCB.md)" DomainModifierTlsa = "[`TLSA`](language-reference/domain-modifiers/TLSA.md)" DomainModifierDs = "[`DS`](language-reference/domain-modifiers/DS.md)" DomainModifierDhcid = "[`DHCID`](language-reference/domain-modifiers/DHCID.md)" @@ -106,12 +108,14 @@ func matrixData() *FeatureMatrix { DomainModifierAlias, DomainModifierCaa, DomainModifierDnssec, + DomainModifierHttps, DomainModifierLoc, DomainModifierNaptr, DomainModifierPtr, DomainModifierSoa, DomainModifierSrv, DomainModifierSshfp, + DomainModifierSvcb, DomainModifierTlsa, DomainModifierDs, DomainModifierDhcid, @@ -207,6 +211,10 @@ func matrixData() *FeatureMatrix { DomainModifierDnskey, providers.CanUseDNSKEY, ) + setCapability( + DomainModifierHttps, + providers.CanUseHTTPS, + ) setCapability( DomainModifierLoc, providers.CanUseLOC, @@ -231,6 +239,10 @@ func matrixData() *FeatureMatrix { DomainModifierSshfp, providers.CanUseSSHFP, ) + setCapability( + DomainModifierSvcb, + providers.CanUseSVCB, + ) setCapability( DomainModifierTlsa, providers.CanUseTLSA, diff --git a/commands/getZones.go b/commands/getZones.go index 0b5860193..1adbb0837 100644 --- a/commands/getZones.go +++ b/commands/getZones.go @@ -357,6 +357,8 @@ func formatDsl(rec *models.RecordConfig, defaultTTL uint32) string { target = fmt.Sprintf(`"%s", "%s", %d, %d, %d, %d, %d`, rec.GetTargetField(), rec.SoaMbox, rec.SoaSerial, rec.SoaRefresh, rec.SoaRetry, rec.SoaExpire, rec.SoaMinttl) case "SRV": target = fmt.Sprintf(`%d, %d, %d, "%s"`, rec.SrvPriority, rec.SrvWeight, rec.SrvPort, rec.GetTargetField()) + case "SVCB", "HTTPS": + target = fmt.Sprintf(`%d, "%s", "%s"`, rec.SvcPriority, rec.GetTargetField(), rec.SvcParams) case "TLSA": target = fmt.Sprintf(`%d, %d, %d, "%s"`, rec.TlsaUsage, rec.TlsaSelector, rec.TlsaMatchingType, rec.GetTargetField()) case "TXT": diff --git a/documentation/SUMMARY.md b/documentation/SUMMARY.md index 653ce865e..c3dd24e63 100644 --- a/documentation/SUMMARY.md +++ b/documentation/SUMMARY.md @@ -46,6 +46,7 @@ * [DefaultTTL](language-reference/domain-modifiers/DefaultTTL.md) * [DnsProvider](language-reference/domain-modifiers/DnsProvider.md) * [FRAME](language-reference/domain-modifiers/FRAME.md) + * [HTTPS](language-reference/domain-modifiers/HTTPS.md) * [IGNORE](language-reference/domain-modifiers/IGNORE.md) * [IGNORE_NAME](language-reference/domain-modifiers/IGNORE_NAME.md) * [IGNORE_TARGET](language-reference/domain-modifiers/IGNORE_TARGET.md) @@ -69,6 +70,7 @@ * [SPF_BUILDER](language-reference/domain-modifiers/SPF_BUILDER.md) * [SRV](language-reference/domain-modifiers/SRV.md) * [SSHFP](language-reference/domain-modifiers/SSHFP.md) + * [SVCB](language-reference/domain-modifiers/SVCB.md) * [TLSA](language-reference/domain-modifiers/TLSA.md) * [TXT](language-reference/domain-modifiers/TXT.md) * [URL](language-reference/domain-modifiers/URL.md) diff --git a/documentation/language-reference/domain-modifiers/HTTPS.md b/documentation/language-reference/domain-modifiers/HTTPS.md new file mode 100644 index 000000000..5fe96d6bb --- /dev/null +++ b/documentation/language-reference/domain-modifiers/HTTPS.md @@ -0,0 +1,32 @@ +--- +name: HTTPS +parameters: + - name + - priority + - target + - params + - modifiers... +parameter_types: + name: string + priority: number + target: string + params: string + "modifiers...": RecordModifier[] +--- + +HTTPS adds an HTTPS record to a domain. The name should be the relative label for the record. Use `@` for the domain apex. The HTTPS record is a special form of the SVCB resource record. + +The priority must be a positive number, the address should be an ip address, either a string, or a numeric value obtained via [IP](../top-level-functions/IP.md). + +The params may be configured to specify the `alpn`, `ipv4hint`, `ipv6hint`, `ech` or `port` setting. Several params may be joined by a space. Not existing params may be specified as an empty string `""` + +Modifiers can be any number of [record modifiers](https://docs.dnscontrol.org/language-reference/record-modifiers) or JSON objects, which will be merged into the record's metadata. + +{% code title="dnsconfig.js" %} +```javascript +D("example.com", REG_MY_PROVIDER, DnsProvider(DSP_MY_PROVIDER), + HTTPS("@", 1, ".", "ipv4hint=123.123.123.123 alpn=h3,h2 port=443"), + HTTPS("@", 1, "test.com", "") +); +``` +{% endcode %} diff --git a/documentation/language-reference/domain-modifiers/SVCB.md b/documentation/language-reference/domain-modifiers/SVCB.md new file mode 100644 index 000000000..8aa6a4acc --- /dev/null +++ b/documentation/language-reference/domain-modifiers/SVCB.md @@ -0,0 +1,31 @@ +--- +name: SVCB +parameters: + - name + - priority + - target + - params + - modifiers... +parameter_types: + name: string + priority: number + target: string + params: string + "modifiers...": RecordModifier[] +--- + +SVCB adds an SVCB record to a domain. The name should be the relative label for the record. Use `@` for the domain apex. + +The priority must be a positive number, the address should be an ip address, either a string, or a numeric value obtained via [IP](../top-level-functions/IP.md). + +The params may be configured to specify the `alpn`, `ipv4hint`, `ipv6hint`, `ech` or `port` setting. Several params may be joined by a space. Not existing params may be specified as an empty string `""` + +Modifiers can be any number of [record modifiers](https://docs.dnscontrol.org/language-reference/record-modifiers) or JSON objects, which will be merged into the record's metadata. + +{% code title="dnsconfig.js" %} +```javascript +D("example.com", REG_MY_PROVIDER, DnsProvider(DSP_MY_PROVIDER), + SVCB("@", 1, ".", "ipv4hint=123.123.123.123 alpn=h3,h2 port=443") +); +``` +{% endcode %} diff --git a/integrationTest/integration_test.go b/integrationTest/integration_test.go index 14f3f8fca..5d1ceb727 100644 --- a/integrationTest/integration_test.go +++ b/integrationTest/integration_test.go @@ -528,6 +528,13 @@ func dnskey(name string, flags uint16, protocol, algorithm uint8, publicKey stri return r } +func https(name string, priority uint16, target string, params string) *models.RecordConfig { + r := makeRec(name, target, "HTTPS") + r.SvcPriority = priority + r.SvcParams = params + return r +} + func ignoreName(labelSpec string) *models.RecordConfig { return ignore(labelSpec, "*", "*") } @@ -620,6 +627,13 @@ func sshfp(name string, algorithm uint8, fingerprint uint8, target string) *mode return r } +func svcb(name string, priority uint16, target string, params string) *models.RecordConfig { + r := makeRec(name, target, "SVCB") + r.SvcPriority = priority + r.SvcParams = params + return r +} + func ovhdkim(name, target string) *models.RecordConfig { return makeOvhNativeRecord(name, target, "DKIM") } @@ -1016,6 +1030,23 @@ func makeTests() []*TestGroup { tc("Change back to CNAME", cname("foo", "google2.com.")), ), + testgroup("HTTPS", + tc("Create a HTTPS record", https("@", 1, "test.com.", "port=80")), + tc("Change HTTPS priority", https("@", 2, "test.com.", "port=80")), + tc("Change HTTPS target", https("@", 2, ".", "port=80")), + tc("Change HTTPS params", https("@", 2, ".", "port=99")), + tc("Change HTTPS params-empty", https("@", 2, ".", "")), + tc("Change HTTPS all", https("@", 3, "example.com.", "port=100")), + ), + + testgroup("SVCB", + tc("Create a HTTPS record", https("@", 1, "test.com.", "port=80")), + tc("Change HTTPS priority", https("@", 2, "test.com.", "port=80")), + tc("Change HTTPS target", https("@", 2, ".", "port=80")), + tc("Change HTTPS params", https("@", 2, ".", "port=99")), + tc("Change HTTPS params-empty", https("@", 2, ".", "")), + tc("Change HTTPS all", https("@", 3, "example.com.", "port=100")), + ), //// Test edge cases from various types. // Narrative: Every DNS record type has some weird edge-case that diff --git a/models/dnsrr.go b/models/dnsrr.go index e41128031..7c7c4f9c5 100644 --- a/models/dnsrr.go +++ b/models/dnsrr.go @@ -54,6 +54,8 @@ func helperRRtoRC(rr dns.RR, origin string, fixBug bool) (RecordConfig, error) { err = rc.SetTargetDS(v.KeyTag, v.Algorithm, v.DigestType, v.Digest) case *dns.DNSKEY: err = rc.SetTargetDNSKEY(v.Flags, v.Protocol, v.Algorithm, v.PublicKey) + case *dns.HTTPS: + err = rc.SetTargetSVCB(v.Priority, v.Target, v.Value) case *dns.LOC: err = rc.SetTargetLOC(v.Version, v.Latitude, v.Longitude, v.Altitude, v.Size, v.HorizPre, v.VertPre) case *dns.MX: @@ -70,6 +72,8 @@ func helperRRtoRC(rr dns.RR, origin string, fixBug bool) (RecordConfig, error) { err = rc.SetTargetSRV(v.Priority, v.Weight, v.Port, v.Target) case *dns.SSHFP: err = rc.SetTargetSSHFP(v.Algorithm, v.Type, v.FingerPrint) + case *dns.SVCB: + err = rc.SetTargetSVCB(v.Priority, v.Target, v.Value) case *dns.TLSA: err = rc.SetTargetTLSA(v.Usage, v.Selector, v.MatchingType, v.Certificate) case *dns.TXT: diff --git a/models/domain.go b/models/domain.go index 6e29f5728..e4e6d38d4 100644 --- a/models/domain.go +++ b/models/domain.go @@ -141,7 +141,7 @@ func (dc *DomainConfig) Punycode() error { rec.SetTarget(t) case "CF_REDIRECT", "CF_TEMP_REDIRECT", "CF_WORKER_ROUTE": rec.SetTarget(rec.GetTargetField()) - case "A", "AAAA", "CAA", "DHCID", "DNSKEY", "DS", "LOC", "NAPTR", "SOA", "SSHFP", "TXT", "TLSA", "AZURE_ALIAS": + case "A", "AAAA", "CAA", "DHCID", "DNSKEY", "DS", "HTTPS", "LOC", "NAPTR", "SOA", "SSHFP", "SVCB", "TXT", "TLSA", "AZURE_ALIAS": // Nothing to do. default: return fmt.Errorf("Punycode rtype %v unimplemented", rec.Type) diff --git a/models/record.go b/models/record.go index 46c19b031..9d329858b 100644 --- a/models/record.go +++ b/models/record.go @@ -22,6 +22,7 @@ import ( // ANAME // Technically not an official rtype yet. // CAA // CNAME +// HTTPS // LOC // MX // NAPTR @@ -30,6 +31,7 @@ import ( // SOA // SRV // SSHFP +// SVCB // TLSA // TXT // Pseudo-Types: (alphabetical) @@ -128,6 +130,8 @@ type RecordConfig struct { SoaRetry uint32 `json:"soaretry,omitempty"` SoaExpire uint32 `json:"soaexpire,omitempty"` SoaMinttl uint32 `json:"soaminttl,omitempty"` + SvcPriority uint16 `json:"svcpriority,omitempty"` + SvcParams string `json:"svcparams,omitempty"` TlsaUsage uint8 `json:"tlsausage,omitempty"` TlsaSelector uint8 `json:"tlsaselector,omitempty"` TlsaMatchingType uint8 `json:"tlsamatchingtype,omitempty"` @@ -200,6 +204,8 @@ func (rc *RecordConfig) UnmarshalJSON(b []byte) error { SoaRetry uint32 `json:"soaretry,omitempty"` SoaExpire uint32 `json:"soaexpire,omitempty"` SoaMinttl uint32 `json:"soaminttl,omitempty"` + SvcPriority uint16 `json:"svcpriority,omitempty"` + SvcParams string `json:"svcparams,omitempty"` TlsaUsage uint8 `json:"tlsausage,omitempty"` TlsaSelector uint8 `json:"tlsaselector,omitempty"` TlsaMatchingType uint8 `json:"tlsamatchingtype,omitempty"` @@ -381,6 +387,10 @@ func (rc *RecordConfig) ToRR() dns.RR { rr.(*dns.DNSKEY).Protocol = rc.DnskeyProtocol rr.(*dns.DNSKEY).Algorithm = rc.DnskeyAlgorithm rr.(*dns.DNSKEY).PublicKey = rc.DnskeyPublicKey + case dns.TypeHTTPS: + rr.(*dns.HTTPS).Priority = rc.SvcPriority + rr.(*dns.HTTPS).Target = rc.GetTargetField() + rr.(*dns.HTTPS).Value = rc.GetSVCBValue() case dns.TypeLOC: // fmt.Printf("ToRR long: %d, lat:%d, sz: %d, hz:%d, vt:%d\n", rc.LocLongitude, rc.LocLatitude, rc.LocSize, rc.LocHorizPre, rc.LocVertPre) // fmt.Printf("ToRR rc: %+v\n", *rc) @@ -424,6 +434,10 @@ func (rc *RecordConfig) ToRR() dns.RR { rr.(*dns.SSHFP).Algorithm = rc.SshfpAlgorithm rr.(*dns.SSHFP).Type = rc.SshfpFingerprint rr.(*dns.SSHFP).FingerPrint = rc.GetTargetField() + case dns.TypeSVCB: + rr.(*dns.SVCB).Priority = rc.SvcPriority + rr.(*dns.SVCB).Target = rc.GetTargetField() + rr.(*dns.SVCB).Value = rc.GetSVCBValue() case dns.TypeTLSA: rr.(*dns.TLSA).Usage = rc.TlsaUsage rr.(*dns.TLSA).MatchingType = rc.TlsaMatchingType @@ -483,6 +497,20 @@ func (rc *RecordConfig) Key() RecordKey { return RecordKey{rc.NameFQDN, t} } +func (rc *RecordConfig) GetSVCBValue() []dns.SVCBKeyValue { + record, err := dns.NewRR(fmt.Sprintf("%s %s %d %s %s", rc.NameFQDN, rc.Type, rc.SvcPriority, rc.target, rc.SvcParams)) + if err != nil { + log.Fatalf("could not parse SVCB record: %s", err) + } + switch r := record.(type) { + case *dns.HTTPS: + return r.Value + case *dns.SVCB: + return r.Value + } + return nil +} + // Records is a list of *RecordConfig. type Records []*RecordConfig @@ -580,7 +608,7 @@ func CanonicalizeTargets(recs []*RecordConfig, origin string) { case "ALIAS", "ANAME", "CNAME", "DNAME", "DS", "DNSKEY", "MX", "NS", "NAPTR", "PTR", "SRV": // Target is a hostname that might be a shortname. Turn it into a FQDN. r.target = dnsutil.AddOrigin(r.target, originFQDN) - case "A", "AKAMAICDN", "CAA", "DHCID", "CF_REDIRECT", "CF_TEMP_REDIRECT", "CF_WORKER_ROUTE", "IMPORT_TRANSFORM", "LOC", "SSHFP", "TLSA", "TXT": + case "A", "AKAMAICDN", "CAA", "DHCID", "CF_REDIRECT", "CF_TEMP_REDIRECT", "CF_WORKER_ROUTE", "HTTPS", "IMPORT_TRANSFORM", "LOC", "SSHFP", "SVCB", "TLSA", "TXT": // Do nothing. case "SOA": if r.target != "DEFAULT_NOT_SET." { diff --git a/models/t_parse.go b/models/t_parse.go index c7f50f62b..10e2a952d 100644 --- a/models/t_parse.go +++ b/models/t_parse.go @@ -105,6 +105,8 @@ func (rc *RecordConfig) PopulateFromStringFunc(rtype, contents, origin string, t return rc.SetTargetSRVString(contents) case "SSHFP": return rc.SetTargetSSHFPString(contents) + case "SVCB", "HTTPS": + return rc.SetTargetSVCBString(origin, contents) case "TLSA": return rc.SetTargetTLSAString(contents) default: diff --git a/models/t_svcb.go b/models/t_svcb.go new file mode 100644 index 000000000..fd8b82cbb --- /dev/null +++ b/models/t_svcb.go @@ -0,0 +1,44 @@ +package models + +import ( + "fmt" + "strings" + + "github.com/miekg/dns" +) + +// SetTargetSVCB sets the SVCB fields. +func (rc *RecordConfig) SetTargetSVCB(priority uint16, target string, params []dns.SVCBKeyValue) error { + rc.SvcPriority = priority + rc.SetTarget(target) + paramsStr := []string{} + for _, kv := range params { + paramsStr = append(paramsStr, fmt.Sprintf("%s=%s", kv.Key(), kv.String())) + } + rc.SvcParams = strings.Join(paramsStr, " ") + if rc.Type == "" { + rc.Type = "SVCB" + } + if rc.Type != "SVCB" && rc.Type != "HTTPS" { + panic("assertion failed: SetTargetSVCB called when .Type is not SVCB or HTTPS") + } + return nil +} + +// SetTargetSVCBString is like SetTargetSVCB but accepts one big string and the origin so parsing can be done using miekg/dns. +func (rc *RecordConfig) SetTargetSVCBString(origin, contents string) error { + if rc.Type == "" { + rc.Type = "SVCB" + } + record, err := dns.NewRR(fmt.Sprintf("%s. %s %s", origin, rc.Type, contents)) + if err != nil { + return fmt.Errorf("could not parse SVCB record: %s", err) + } + switch r := record.(type) { + case *dns.HTTPS: + return rc.SetTargetSVCB(r.Priority, r.Target, r.Value) + case *dns.SVCB: + return rc.SetTargetSVCB(r.Priority, r.Target, r.Value) + } + return nil +} diff --git a/models/target.go b/models/target.go index ecb82237f..62fbedc17 100644 --- a/models/target.go +++ b/models/target.go @@ -131,6 +131,9 @@ func (rc *RecordConfig) GetTargetDebug() string { content += fmt.Sprintf(" srvpriority=%d srvweight=%d srvport=%d", rc.SrvPriority, rc.SrvWeight, rc.SrvPort) case "SSHFP": content += fmt.Sprintf(" sshfpalgorithm=%d sshfpfingerprint=%d", rc.SshfpAlgorithm, rc.SshfpFingerprint) + case "SVCB", "HTTPS": + // HTTPS is only a special subform of the SVCB Record + content += fmt.Sprintf(" priority=%d params=%v", rc.SvcPriority, rc.SvcParams) case "TLSA": content += fmt.Sprintf(" tlsausage=%d tlsaselector=%d tlsamatchingtype=%d", rc.TlsaUsage, rc.TlsaSelector, rc.TlsaMatchingType) default: diff --git a/pkg/js/helpers.js b/pkg/js/helpers.js index bebaf4d34..1ecb348ef 100644 --- a/pkg/js/helpers.js +++ b/pkg/js/helpers.js @@ -449,6 +449,22 @@ var DNSKEY = recordBuilder('DNSKEY', { }, }); +// name, priority, target, params +var HTTPS = recordBuilder('HTTPS', { + args: [ + ['name', _.isString], + ['priority', _.isNumber], + ['target', _.isString], + ['params', _.isString], + ], + transform: function (record, args, modifiers) { + record.name = args.name; + record.svcpriority = args.priority; + record.target = args.target; + record.svcparams = args.params; + }, +}); + // PTR(name,target, recordModifiers...) var PTR = recordBuilder('PTR'); @@ -530,6 +546,22 @@ var SSHFP = recordBuilder('SSHFP', { }, }); +// name, priority, target, params +var SVCB = recordBuilder('SVCB', { + args: [ + ['name', _.isString], + ['priority', _.isNumber], + ['target', _.isString], + ['params', _.isString], + ], + transform: function (record, args, modifiers) { + record.name = args.name; + record.svcpriority = args.priority; + record.target = args.target; + record.svcparams = args.params; + }, +}); + // name, usage, selector, matchingtype, certificate var TLSA = recordBuilder('TLSA', { args: [ diff --git a/pkg/js/parse_tests/047-SVCB.js b/pkg/js/parse_tests/047-SVCB.js new file mode 100644 index 000000000..1a69a5de0 --- /dev/null +++ b/pkg/js/parse_tests/047-SVCB.js @@ -0,0 +1,4 @@ +D("foo.com","none", + SVCB("@", 1, ".", ""), + HTTPS("@", 2, ".", 'alpn="h3,h2" port=443 ipv4hint=123.123.123.123 ipv6hint=dead::beaf') +); diff --git a/pkg/js/parse_tests/047-SVCB.json b/pkg/js/parse_tests/047-SVCB.json new file mode 100644 index 000000000..a3aa5c293 --- /dev/null +++ b/pkg/js/parse_tests/047-SVCB.json @@ -0,0 +1,27 @@ +{ + "registrars": [], + "dns_providers": [], + "domains": [ + { + "name": "foo.com", + "registrar": "none", + "dnsProviders": {}, + "records": [ + { + "type": "SVCB", + "name": "@", + "target": ".", + "svcpriority": 1 + }, + + { + "type": "HTTPS", + "name": "@", + "svcparams": "alpn=\"h3,h2\" port=443 ipv4hint=123.123.123.123 ipv6hint=dead::beaf", + "svcpriority": 2, + "target": "." + } + ] + } + ] +} diff --git a/pkg/normalize/validate.go b/pkg/normalize/validate.go index cbb120642..acff2857f 100644 --- a/pkg/normalize/validate.go +++ b/pkg/normalize/validate.go @@ -63,6 +63,7 @@ func validateRecordTypes(rec *models.RecordConfig, domain string, pTypes []strin "DNAME": true, "DS": true, "DNSKEY": true, + "HTTPS": true, "IMPORT_TRANSFORM": false, "LOC": true, "MX": true, @@ -72,6 +73,7 @@ func validateRecordTypes(rec *models.RecordConfig, domain string, pTypes []strin "SOA": true, "SRV": true, "SSHFP": true, + "SVCB": true, "TLSA": true, "TXT": true, } @@ -224,7 +226,7 @@ func checkTargets(rec *models.RecordConfig, domain string) (errs []error) { } case "SRV": check(checkTarget(target)) - case "CAA", "DHCID", "DNSKEY", "DS", "IMPORT_TRANSFORM", "SSHFP", "TLSA", "TXT": + case "CAA", "DHCID", "DNSKEY", "DS", "HTTPS", "IMPORT_TRANSFORM", "SSHFP", "SVCB", "TLSA", "TXT": default: if rec.Metadata["orig_custom_type"] != "" { // it is a valid custom type. We perform no validation on target @@ -685,6 +687,7 @@ var providerCapabilityChecks = []pairTypeCapability{ capabilityCheck("DHCID", providers.CanUseDHCID), capabilityCheck("DNAME", providers.CanUseDNAME), capabilityCheck("DNSKEY", providers.CanUseDNSKEY), + capabilityCheck("HTTPS", providers.CanUseHTTPS), capabilityCheck("LOC", providers.CanUseLOC), capabilityCheck("NAPTR", providers.CanUseNAPTR), capabilityCheck("PTR", providers.CanUsePTR), @@ -692,6 +695,7 @@ var providerCapabilityChecks = []pairTypeCapability{ capabilityCheck("SOA", providers.CanUseSOA), capabilityCheck("SRV", providers.CanUseSRV), capabilityCheck("SSHFP", providers.CanUseSSHFP), + capabilityCheck("SVCB", providers.CanUseSVCB), capabilityCheck("TLSA", providers.CanUseTLSA), // DS needs special record-level checks diff --git a/pkg/prettyzone/prettyzone_test.go b/pkg/prettyzone/prettyzone_test.go index 1e27c757d..4c98cacf8 100644 --- a/pkg/prettyzone/prettyzone_test.go +++ b/pkg/prettyzone/prettyzone_test.go @@ -341,6 +341,8 @@ func TestWriteZoneFileEach(t *testing.T) { d = append(d, mustNewRR(`dname.bosun.org. 300 IN DNAME example.com.`)) d = append(d, mustNewRR(`dnssec.bosun.org. 300 IN DS 31334 13 2 94cc505ebc36b1f4e051268b820efb230f1572d445e833bb5bf7380d6c2cbc0a`)) d = append(d, mustNewRR(`dnssec.bosun.org. 300 IN DNSKEY 257 3 13 rNR701yiOPHfqDP53GnsHZdlsRqI7O1ksk60rnFILZVk7Z4eTBd1U49oSkTNVNox9tb7N15N2hboXoMEyFFzcw==`)) + d = append(d, mustNewRR(`bosun.org. 300 IN HTTPS 1 . alpn="h3,h2"`)) + d = append(d, mustNewRR(`bosun.org. 300 IN SVCB 1 . alpn="h3,h2"`)) buf := &bytes.Buffer{} writeZoneFileRR(buf, d, "bosun.org") if buf.String() != testdataZFEach { @@ -359,6 +361,8 @@ var testdataZFEach = `$TTL 300 IN TXT "my text" IN CAA 0 issue "letsencrypt.org" IN DHCID AAIBY2/AuCccgoJbsaxcQc9TUapptP69lOjxfNuVAA2kjEA= + IN HTTPS 1 . alpn="h3,h2" + IN SVCB 1 . alpn="h3,h2" 4.5 IN PTR y.bosun.org. _443._tcp IN TLSA 3 1 1 abcdef0 dname IN DNAME example.com. diff --git a/pkg/prettyzone/sorting.go b/pkg/prettyzone/sorting.go index 57bda8983..6ae20f071 100644 --- a/pkg/prettyzone/sorting.go +++ b/pkg/prettyzone/sorting.go @@ -80,6 +80,12 @@ func (z *ZoneGenData) Less(i, j int) bool { if pa != pb { return pa < pb } + case "SVCB", "HTTPS": + // sort by priority. If they are equal, sort by record. + if a.SvcPriority == b.SvcPriority { + return a.GetTargetField() < b.GetTargetField() + } + return a.SvcPriority < b.SvcPriority case "PTR": //ta2, tb2 := a.(*dns.PTR), b.(*dns.PTR) pa, pb := a.GetTargetField(), b.GetTargetField() diff --git a/providers/axfrddns/axfrddnsProvider.go b/providers/axfrddns/axfrddnsProvider.go index 7ba283976..41a90915a 100644 --- a/providers/axfrddns/axfrddnsProvider.go +++ b/providers/axfrddns/axfrddnsProvider.go @@ -44,11 +44,13 @@ var features = providers.DocumentationNotes{ providers.CanConcur: providers.Cannot(), providers.CanUseCAA: providers.Can(), providers.CanUseDHCID: providers.Can(), + providers.CanUseHTTPS: providers.Can(), providers.CanUseLOC: providers.Unimplemented(), providers.CanUseNAPTR: providers.Can(), providers.CanUsePTR: providers.Can(), providers.CanUseSRV: providers.Can(), providers.CanUseSSHFP: providers.Can(), + providers.CanUseSVCB: providers.Can(), providers.CanUseTLSA: providers.Can(), providers.DocCreateDomains: providers.Cannot(), providers.DocDualHost: providers.Cannot(), diff --git a/providers/bind/bindProvider.go b/providers/bind/bindProvider.go index a1d31cb8d..753fcbacd 100644 --- a/providers/bind/bindProvider.go +++ b/providers/bind/bindProvider.go @@ -40,12 +40,14 @@ var features = providers.DocumentationNotes{ providers.CanUseDNAME: providers.Can(), providers.CanUseDS: providers.Can(), providers.CanUseDNSKEY: providers.Can(), + providers.CanUseHTTPS: providers.Can(), providers.CanUseLOC: providers.Can(), providers.CanUseNAPTR: providers.Can(), providers.CanUsePTR: providers.Can(), providers.CanUseSOA: providers.Can(), providers.CanUseSRV: providers.Can(), providers.CanUseSSHFP: providers.Can(), + providers.CanUseSVCB: providers.Can(), providers.CanUseTLSA: providers.Can(), providers.DocCreateDomains: providers.Can("Driver just maintains list of zone files. It should automatically add missing ones."), providers.DocDualHost: providers.Can(), diff --git a/providers/capabilities.go b/providers/capabilities.go index 83c6af69f..853c1271c 100644 --- a/providers/capabilities.go +++ b/providers/capabilities.go @@ -54,6 +54,9 @@ const ( // only for children records, not at the root of the zone. CanUseDSForChildren + // CanUseHTTPS indicates the provider can handle HTTPS records + CanUseHTTPS + // CanUseLOC indicates whether service provider handles LOC records CanUseLOC @@ -75,6 +78,9 @@ const ( // CanUseSSHFP indicates the provider can handle SSHFP records CanUseSSHFP + // CanUseSVCB indicates the provider can handle SVCB records + CanUseSVCB + // CanUseTLSA indicates the provider can handle TLSA records CanUseTLSA diff --git a/providers/capability_string.go b/providers/capability_string.go index 3c7612a97..b41ba039f 100644 --- a/providers/capability_string.go +++ b/providers/capability_string.go @@ -19,23 +19,25 @@ func _() { _ = x[CanUseDNAME-8] _ = x[CanUseDS-9] _ = x[CanUseDSForChildren-10] - _ = x[CanUseLOC-11] - _ = x[CanUseNAPTR-12] - _ = x[CanUsePTR-13] - _ = x[CanUseRoute53Alias-14] - _ = x[CanUseSOA-15] - _ = x[CanUseSRV-16] - _ = x[CanUseSSHFP-17] - _ = x[CanUseTLSA-18] - _ = x[CanUseDNSKEY-19] - _ = x[DocCreateDomains-20] - _ = x[DocDualHost-21] - _ = x[DocOfficiallySupported-22] + _ = x[CanUseHTTPS-11] + _ = x[CanUseLOC-12] + _ = x[CanUseNAPTR-13] + _ = x[CanUsePTR-14] + _ = x[CanUseRoute53Alias-15] + _ = x[CanUseSOA-16] + _ = x[CanUseSRV-17] + _ = x[CanUseSSHFP-18] + _ = x[CanUseSVCB-19] + _ = x[CanUseTLSA-20] + _ = x[CanUseDNSKEY-21] + _ = x[DocCreateDomains-22] + _ = x[DocDualHost-23] + _ = x[DocOfficiallySupported-24] } -const _Capability_name = "CanAutoDNSSECCanConcurCanGetZonesCanUseAKAMAICDNCanUseAliasCanUseAzureAliasCanUseCAACanUseDHCIDCanUseDNAMECanUseDSCanUseDSForChildrenCanUseLOCCanUseNAPTRCanUsePTRCanUseRoute53AliasCanUseSOACanUseSRVCanUseSSHFPCanUseTLSACanUseDNSKEYDocCreateDomainsDocDualHostDocOfficiallySupported" +const _Capability_name = "CanAutoDNSSECCanConcurCanGetZonesCanUseAKAMAICDNCanUseAliasCanUseAzureAliasCanUseCAACanUseDHCIDCanUseDNAMECanUseDSCanUseDSForChildrenCanUseHTTPSCanUseLOCCanUseNAPTRCanUsePTRCanUseRoute53AliasCanUseSOACanUseSRVCanUseSSHFPCanUseSVCBCanUseTLSACanUseDNSKEYDocCreateDomainsDocDualHostDocOfficiallySupported" -var _Capability_index = [...]uint16{0, 13, 22, 33, 48, 59, 75, 84, 95, 106, 114, 133, 142, 153, 162, 180, 189, 198, 209, 219, 231, 247, 258, 280} +var _Capability_index = [...]uint16{0, 13, 22, 33, 48, 59, 75, 84, 95, 106, 114, 133, 144, 153, 164, 173, 191, 200, 209, 220, 230, 240, 252, 268, 279, 301} func (i Capability) String() string { if i >= Capability(len(_Capability_index)-1) {