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

Add D_EXTEND (#885) (thanks to @ad8-bdl!)

* fix get-zones code block indentation

* extend D_EXTEND to handle subdomains

* fix targets: make absolute incl. subdomain where necessary

* clarify subdomain target test (not IP && not fqdn)

* Add parse_tests for D and D_EXTEND

* _getDomainObject: examine all domains

* human readable form

* consistent test IP addresses

* Improve docs and formatting

* propagate subdomain to canonicalisation

* en-US spelling

* rm extraneous console.log

* ignore subdomain for CF_REDIRECT

* clarify D_EXTEND doc re. CF_REDIRECT

* rm extraneous linebreak

* _getDomainObject: examine all domains

* human readable form

* consistent test IP addresses

* propagate subdomain to canonicalisation

* en-US spelling

* rm extraneous console.log

* ignore subdomain for CF_REDIRECT

* clarify D_EXTEND doc re. CF_REDIRECT

* rm extraneous linebreak

* GANDI_V5: Use github.com/go-gandi/go-gandi, not github.com/tiramiseb/go-gandi (#883)

* DOCUMENTATION: Fix error in CNAME.md (#877)

The current example `CNAME("def", "test.subdomain"), // def.example.com -> test.subdomain.example.com` is invalid (correctly raises a validation error, "ERROR: in CNAME def.example.com: target (test.subdomain) must end with a (.)")

* typos, fmt; example syntax fixes and real output

* formatting; re-add lost comment

* RecordConfig subdomain should be nullable

* providers/cscglobal/api.go: Fix fmt string

* More tests and docs

* go generate

Co-authored-by: Ben L <47653825+ad8-bdl@users.noreply.github.com>
This commit is contained in:
Tom Limoncelli
2020-10-07 14:27:33 -04:00
committed by GitHub
parent 7d2016a970
commit b275286dae
35 changed files with 978 additions and 158 deletions

View File

@@ -5,26 +5,83 @@ parameters:
- modifiers...
---
`D_EXTEND` adds records (and metadata) to a domain. The domain must have previously been defined by `D()`. `D_EXTEND()` behaves the same as `D()` in all other ways: The first argument is the domain name. See the documentation of `D` for further details.
`D_EXTEND` adds records (and metadata) to a domain previously defined
by `D()`. It can also be used to add subdomain records (and metadata)
to a previously defined domain.
The first argument is a domain name. If it exactly matches a
previously defined domain, `D_EXTEND()` behaves the same as `D()`,
simply adding records as if they had been specified in the original
`D()`.
If the domain name does not match an existing domain, but could be a
(non-delegated) subdomain of an existing domain, the new records (and
metadata) are added with the subdomain part appended to all record
names (labels), and targets (as appropriate). See the examples below.
Matching the domain name to previously-defined domains is done using a
`longest match` algorithm. If `domain.tld` and `sub.domain.tld` are
defined as separate domains via separate `D()` statements, then
`D_EXTEND('sub.sub.domain.tld', ...)` would match `sub.domain.tld`,
not `domain.tld`.
Some operators only act on an apex domain (e.g.
`CF_REDIRECT` and `CF_TEMP_REDIRECT`). Using them
in a `D_EXTEND` subdomain may not be what you expect.
Example:
{% include startExample.html %}
{% highlight js %}
D('domain.tld', REG, DnsProvider(DNS),
A('@', "127.0.0.1")
)
D_EXTEND('domain.tld',
A('@', "127.0.0.2")
)
D("domain.tld", REG, DnsProvider(DNS),
A("@", "127.0.0.1"), // domain.tld
A("www", "127.0.0.2"), // www.domain.tld
CNAME("a", "b") // a.domain.tld -> b.domain.tld
);
D_EXTEND("domain.tld",
A("aaa", "127.0.0.3"), // aaa.domain.tld
CNAME("c", "d") // c.domain.tld -> d.domain.tld
);
D_EXTEND("sub.domain.tld",
A("bbb", "127.0.0.4"), // bbb.sub.domain.tld
A("ccc", "127.0.0.5"), // ccc.sub.domain.tld
CNAME("e", "f") // e.sub.domain.tld -> f.sub.domain.tld
);
D_EXTEND("sub.sub.domain.tld",
A("ddd", "127.0.0.6"), // ddd.sub.sub.domain.tld
CNAME("g", "h") // g.sub.sub.domain.tld -> h.sub.sub.domain.tld
);
D_EXTEND("sub.domain.tld",
A("@", "127.0.0.7"), // sub.domain.tld
CNAME("i", "j") // i.sub.domain.tld -> j.sub.domain.tld
);
{%endhighlight%}
This will end up in following modifications:
This will end up in the following modifications:
```
******************** Domain: domain.tld
----- Getting nameservers from: registrar
----- DNS Provider: registrar...3 corrections
#1: CREATE A domain.tld 127.0.0.1 ttl=43200
#2: CREATE A domain.tld 127.0.0.2 ttl=43200
#3: REFRESH zone domain.tld
----- Getting nameservers from: cloudflare
----- DNS Provider: cloudflare...7 corrections
#1: CREATE A aaa.domain.tld 127.0.0.3
#2: CREATE A bbb.sub.domain.tld 127.0.0.4
#3: CREATE A ccc.sub.domain.tld 127.0.0.5
#4: CREATE A ddd.sub.sub.domain.tld 127.0.0.6
#5: CREATE A sub.domain.tld 127.0.0.7
#6: CREATE A www.domain.tld 127.0.0.2
#7: CREATE A domain.tld 127.0.0.1
#8: CREATE CNAME a.domain.tld b.domain.tld.
#9: CREATE CNAME c.domain.tld d.domain.tld.
#10: CREATE CNAME e.sub.domain.tld f.sub.domain.tld.
#11: CREATE CNAME g.sub.sub.domain.tld h.sub.sub.domain.tld.
#12: CREATE CNAME i.sub.domain.tld j.sub.domain.tld.
```
{% include endExample.html %}
ProTips: `D_EXTEND()` permits you to create very complex and
sophisticated configurations, but you shouldn't. Be nice to the next
person that edits the file, who may not be as expert as yourself.
Enhance readability by putting any `D_EXTEND()` statements immediately
after the original `D()`, like in above example. Avoid the temptation
to obscure the addition of records to existing domains with randomly
placed `D_EXTEND()` statements. Don't build up a domain using loops of
`D_EXTEND()` statements. You'll be glad you didn't.

View File

@@ -49,7 +49,7 @@ import (
// the intended FQDN is "foo.com.foo.com." (which may look odd)
// NameFQDN:
// This is the FQDN version of Name.
// It should never have a trailiing ".".
// It should never have a trailing ".".
// NOTE: Eventually we will unexport Name/NameFQDN. Please start using
// the setters (SetLabel/SetLabelFromFQDN) and getters (GetLabel/GetLabelFQDN).
// as they will always work.
@@ -59,6 +59,10 @@ import (
// fields.
// NOTE: Eventually we will unexport Target. Please start using the
// setters (SetTarget*) and getters (GetTarget*) as they will always work.
// SubDomain:
// This is the subdomain path, if any, imported from the configuration. If
// present at the time of canonicalization it is inserted between the
// Name and origin when constructing a canonical (FQDN) target.
//
// Idioms:
// rec.Label() == "@" // Is this record at the apex?
@@ -66,6 +70,7 @@ import (
type RecordConfig struct {
Type string `json:"type"` // All caps rtype name.
Name string `json:"name"` // The short name. See above.
SubDomain string `json:"subdomain,omitempty"`
NameFQDN string `json:"-"` // Must end with ".$origin". See above.
Target string `json:"target"` // If a name, must end with "."
TTL uint32 `json:"ttl,omitempty"`

View File

@@ -0,0 +1,92 @@
{
"dns_providers": [
{
"name": "Cloudflare",
"type": "CLOUDFLAREAPI"
}
],
"domains": [
{
"dnsProviders": {
"Cloudflare": -1
},
"name": "domain.tld",
"records": [
{
"name": "@",
"target": "127.0.0.1",
"type": "A"
},
{
"name": "www",
"target": "127.0.0.2",
"type": "A"
},
{
"name": "a",
"target": "b",
"type": "CNAME"
},
{
"name": "aaa",
"target": "127.0.0.3",
"type": "A"
},
{
"name": "c",
"target": "d",
"type": "CNAME"
},
{
"name": "bbb.sub",
"subdomain": "sub",
"target": "127.0.0.4",
"type": "A"
},
{
"name": "ccc.sub",
"subdomain": "sub",
"target": "127.0.0.5",
"type": "A"
},
{
"name": "e.sub",
"subdomain": "sub",
"target": "f",
"type": "CNAME"
},
{
"name": "ddd.sub.sub",
"subdomain": "sub.sub",
"target": "127.0.0.6",
"type": "A"
},
{
"name": "g.sub.sub",
"subdomain": "sub.sub",
"target": "h",
"type": "CNAME"
},
{
"name": "sub",
"subdomain": "sub",
"target": "127.0.0.7",
"type": "A"
},
{
"name": "i.sub",
"subdomain": "sub",
"target": "j",
"type": "CNAME"
}
],
"registrar": "Third-Party"
}
],
"registrars": [
{
"name": "Third-Party",
"type": "NONE"
}
]
}

View File

@@ -53,6 +53,7 @@ function NewDnsProvider(name, type, meta) {
function newDomain(name, registrar) {
return {
name: name,
subdomain: '',
registrar: registrar,
meta: {},
records: [],
@@ -102,12 +103,13 @@ function D(name, registrar) {
conf.domain_names.push(name);
}
// DU(name): Update an already added DNS Domain with D().
// D_EXTEND(name): Update a DNS Domain already added with D(), or subdomain thereof
function D_EXTEND(name) {
var domain = _getDomainObject(name);
if (domain == null) {
throw name + ' was not declared yet and therefore cannot be updated. Use D() before.';
}
domain.obj.subdomain = name.substr(0, name.length-domain.obj.name.length - 1);
for (var i = 0; i < defaultArgs.length; i++) {
processDargs(defaultArgs[i], domain.obj);
}
@@ -119,13 +121,19 @@ function D_EXTEND(name) {
}
// _getDomainObject(name): This is a small helper function to get the domain JS object returned.
// returns the domain object defined for the given name or subdomain thereof
function _getDomainObject(name) {
domain = null;
domain_len = 0;
for(var i = 0; i < conf.domains.length; i++) {
if (conf.domains[i]['name'] == name) {
return {'id': i, 'obj': conf.domains[i]};
if (name.substr(-conf.domains[i]['name'].length) == conf.domains[i]['name']) {
if (conf.domains[i]['name'].length > domain_len) {
domain_len = conf.domains[i]['name'].length;
domain = {'id': i, 'obj': conf.domains[i]};
}
}
return null;
}
return domain;
}
// DEFAULTS provides a set of default arguments to apply to all future domains.
@@ -660,6 +668,17 @@ function recordBuilder(type, opts) {
opts.applyModifier(record, modifiers);
opts.transform(record, parsedArgs, modifiers);
// Handle D_EXTEND() with subdomains.
if (d.subdomain && record.type != 'CF_REDIRECT' &&
record.type != 'CF_TEMP_REDIRECT') {
record.subdomain = d.subdomain;
if (record.name == '@') {
record.name = d.subdomain;
} else {
record.name += '.' + d.subdomain;
}
}
d.records.push(record);
return record;
};

View File

@@ -1,6 +1,7 @@
var REG = NewRegistrar("Third-Party","NONE");
var CF = NewDnsProvider("Cloudflare", "CLOUDFLAREAPI")
var CF = NewDnsProvider("Cloudflare", "CLOUDFLAREAPI");
D("foo.com",REG,DnsProvider(CF),
A("@","1.2.3.4")
);

View File

@@ -0,0 +1,22 @@
var REG = NewRegistrar("Third-Party", "NONE");
var CF = NewDnsProvider("Cloudflare", "CLOUDFLAREAPI");
// Zone and subdomain Zone:
D("foo.com", REG, DnsProvider(CF),
A("@", "10.1.1.1"),
A("www", "10.2.2.2")
);
D("bar.foo.com", REG, DnsProvider(CF),
A("@", "10.3.3.3"),
A("www", "10.4.4.4")
);
// Zone that gets extended
D("foo.edu", REG, DnsProvider(CF),
A("@", "10.5.5.5"),
A("www", "10.6.6.6")
);
D_EXTEND("foo.edu",
A("more1", "10.7.7.7"),
A("more2", "10.8.8.8")
);

View File

@@ -0,0 +1,83 @@
{
"registrars": [
{
"name": "Third-Party",
"type": "NONE"
}
],
"dns_providers": [
{
"name": "Cloudflare",
"type": "CLOUDFLAREAPI"
}
],
"domains": [
{
"name": "foo.com",
"registrar": "Third-Party",
"dnsProviders": {
"Cloudflare": -1
},
"records": [
{
"type": "A",
"name": "@",
"target": "10.1.1.1"
},
{
"type": "A",
"name": "www",
"target": "10.2.2.2"
}
]
},
{
"name": "bar.foo.com",
"registrar": "Third-Party",
"dnsProviders": {
"Cloudflare": -1
},
"records": [
{
"type": "A",
"name": "@",
"target": "10.3.3.3"
},
{
"type": "A",
"name": "www",
"target": "10.4.4.4"
}
]
},
{
"name": "foo.edu",
"registrar": "Third-Party",
"dnsProviders": {
"Cloudflare": -1
},
"records": [
{
"type": "A",
"name": "@",
"target": "10.5.5.5"
},
{
"type": "A",
"name": "www",
"target": "10.6.6.6"
},
{
"type": "A",
"name": "more1",
"target": "10.7.7.7"
},
{
"type": "A",
"name": "more2",
"target": "10.8.8.8"
}
]
}
]
}

View File

@@ -0,0 +1,84 @@
var REG = NewRegistrar("Third-Party", "NONE");
var CF = NewDnsProvider("Cloudflare", "CLOUDFLAREAPI");
// Zone that gets extended by subdomain
D("foo.net", REG, DnsProvider(CF),
A("@", "10.1.1.1"),
A("www", "10.2.2.2")
);
D_EXTEND("bar.foo.net",
A("@", "10.3.3.3"),
A("www", "10.4.4.4")
);
// Zone and subdomain zone, each get extended.
D("foo.tld", REG, DnsProvider(CF),
A("@", "20.5.5.5"),
A("www", "20.6.6.6")
);
D("bar.foo.tld", REG, DnsProvider(CF),
A("@", "30.7.7.7"),
A("www", "30.8.8.8")
);
D_EXTEND("bar.foo.tld",
A("a", "30.9.9.9")
);
D_EXTEND("foo.tld",
A("a", "20.10.10.10")
);
// Zone and subdomain zone, each get extended by a subdomain.
D("foo.help", REG, DnsProvider(CF),
A("@", "40.12.12.12"),
A("www", "40.12.12.12")
);
D("bar.foo.help", REG, DnsProvider(CF),
A("@", "50.13.13.13"),
A("www", "50.14.14.14")
);
D_EXTEND("zip.bar.foo.help",
A("@", "50.15.15.15"),
A("www", "50.16.16.16")
);
D_EXTEND("morty.foo.help",
A("@", "40.17.17.17"),
A("www", "40.18.18.18")
);
// Zone extended by a subdomain and sub-subdomain.
D("foo.here", REG, DnsProvider(CF),
A("@", "60.19.19.19"),
A("www", "60.20.20.20")
);
D_EXTEND("bar.foo.here",
A("@", "60.21.21.21"),
A("www", "60.22.22.22")
);
D_EXTEND("baz.bar.foo.here",
A("@", "60.23.23.23"),
A("www", "60.24.24.24")
);
// Zone extended by a sub-subdomain.
D_EXTEND("a.long.path.of.sub.domains.foo.net",
A("@", "10.25.25.25"),
A("www", "10.26.26.26")
);
// Zone extended by a subdomain, with absolute and relative CNAME targets
D("example.tld", REG, DnsProvider(CF));
D_EXTEND("sub.example.tld",
CNAME("a", "b"), // a.sub.example.tld -> b.sub.example.tld
CNAME("b", "@"), // a.sub.example.tld -> sub.example.tld
CNAME("c", "sub.example.tld."), // a.sub.example.tld -> sub.example.tld
//CNAME("d", "x.y"), // Error. Contains dot but doesn't end with dot.
CNAME("e", "otherdomain.tld.") // a.sub.example.tld -> otherdomain.tld
// Also test for MX, NS, ANAME, SRV.
// Not sure if PTR needs any special treatment. Haven't thought about it much.
);
D("foo.com", REG, DnsProvider(CF));
D_EXTEND("sub.foo.com",
CF_REDIRECT("test.foo.com","https://goo.com/$1"),
CF_TEMP_REDIRECT("test.foo.com","https://goo.com/$1")
);

View File

@@ -0,0 +1,264 @@
{
"registrars": [
{
"name": "Third-Party",
"type": "NONE"
}
],
"dns_providers": [
{
"name": "Cloudflare",
"type": "CLOUDFLAREAPI"
}
],
"domains": [
{
"name": "foo.net",
"registrar": "Third-Party",
"dnsProviders": {
"Cloudflare": -1
},
"records": [
{
"type": "A",
"name": "@",
"target": "10.1.1.1"
},
{
"type": "A",
"name": "www",
"target": "10.2.2.2"
},
{
"type": "A",
"name": "bar",
"subdomain": "bar",
"target": "10.3.3.3"
},
{
"type": "A",
"name": "www.bar",
"subdomain": "bar",
"target": "10.4.4.4"
},
{
"type": "A",
"name": "a.long.path.of.sub.domains",
"subdomain": "a.long.path.of.sub.domains",
"target": "10.25.25.25"
},
{
"type": "A",
"name": "www.a.long.path.of.sub.domains",
"subdomain": "a.long.path.of.sub.domains",
"target": "10.26.26.26"
}
]
},
{
"name": "foo.tld",
"registrar": "Third-Party",
"dnsProviders": {
"Cloudflare": -1
},
"records": [
{
"type": "A",
"name": "@",
"target": "20.5.5.5"
},
{
"type": "A",
"name": "www",
"target": "20.6.6.6"
},
{
"type": "A",
"name": "a",
"target": "20.10.10.10"
}
]
},
{
"name": "bar.foo.tld",
"registrar": "Third-Party",
"dnsProviders": {
"Cloudflare": -1
},
"records": [
{
"type": "A",
"name": "@",
"target": "30.7.7.7"
},
{
"type": "A",
"name": "www",
"target": "30.8.8.8"
},
{
"type": "A",
"name": "a",
"target": "30.9.9.9"
}
]
},
{
"name": "foo.help",
"registrar": "Third-Party",
"dnsProviders": {
"Cloudflare": -1
},
"records": [
{
"type": "A",
"name": "@",
"target": "40.12.12.12"
},
{
"type": "A",
"name": "www",
"target": "40.12.12.12"
},
{
"type": "A",
"name": "morty",
"subdomain": "morty",
"target": "40.17.17.17"
},
{
"type": "A",
"name": "www.morty",
"subdomain": "morty",
"target": "40.18.18.18"
}
]
},
{
"name": "bar.foo.help",
"registrar": "Third-Party",
"dnsProviders": {
"Cloudflare": -1
},
"records": [
{
"type": "A",
"name": "@",
"target": "50.13.13.13"
},
{
"type": "A",
"name": "www",
"target": "50.14.14.14"
},
{
"type": "A",
"name": "zip",
"subdomain": "zip",
"target": "50.15.15.15"
},
{
"type": "A",
"name": "www.zip",
"subdomain": "zip",
"target": "50.16.16.16"
}
]
},
{
"name": "foo.here",
"registrar": "Third-Party",
"dnsProviders": {
"Cloudflare": -1
},
"records": [
{
"type": "A",
"name": "@",
"target": "60.19.19.19"
},
{
"type": "A",
"name": "www",
"target": "60.20.20.20"
},
{
"type": "A",
"name": "bar",
"subdomain": "bar",
"target": "60.21.21.21"
},
{
"type": "A",
"name": "www.bar",
"subdomain": "bar",
"target": "60.22.22.22"
},
{
"type": "A",
"name": "baz.bar",
"subdomain": "baz.bar",
"target": "60.23.23.23"
},
{
"type": "A",
"name": "www.baz.bar",
"subdomain": "baz.bar",
"target": "60.24.24.24"
}
]
},
{
"name": "example.tld",
"registrar": "Third-Party",
"dnsProviders": {
"Cloudflare": -1
},
"records": [
{
"type": "CNAME",
"name": "a.sub",
"subdomain": "sub",
"target": "b"
},
{
"type": "CNAME",
"name": "b.sub",
"subdomain": "sub",
"target": "@"
},
{
"type": "CNAME",
"name": "c.sub",
"subdomain": "sub",
"target": "sub.example.tld."
},
{
"type": "CNAME",
"name": "e.sub",
"subdomain": "sub",
"target": "otherdomain.tld."
}
]
},
{
"name": "foo.com",
"registrar": "Third-Party",
"dnsProviders": {
"Cloudflare": -1
},
"records": [
{
"type": "CF_REDIRECT",
"name": "@",
"target": "test.foo.com,https://goo.com/$1"
},
{
"type": "CF_TEMP_REDIRECT",
"name": "@",
"target": "test.foo.com,https://goo.com/$1"
}
]
}
]
}

View File

@@ -0,0 +1,27 @@
var REG = NewRegistrar("Third-Party", "NONE");
var DNS = NewDnsProvider("Cloudflare", "CLOUDFLAREAPI");
// The example from docs/_functions/global/D_EXTEND.md
D("domain.tld", REG, DnsProvider(DNS),
A("@", "127.0.0.1"), // domain.tld
A("www", "127.0.0.2"), // www.domain.tld
CNAME("a", "b") // a.domain.tld -> b.domain.tld
);
D_EXTEND("domain.tld",
A("aaa", "127.0.0.3"), // aaa.domain.tld
CNAME("c", "d") // c.domain.tld -> d.domain.tld
);
D_EXTEND("sub.domain.tld",
A("bbb", "127.0.0.4"), // bbb.sub.domain.tld
A("ccc", "127.0.0.5"), // ccc.sub.domain.tld
CNAME("e", "f") // e.sub.domain.tld -> f.sub.domain.tld
);
D_EXTEND("sub.sub.domain.tld",
A("ddd", "127.0.0.6"), // ddd.sub.sub.domain.tld
CNAME("g", "h") // g.sub.domain.tld -> h.sub.domain.tld
);
D_EXTEND("sub.domain.tld",
A("@", "127.0.0.7"), // sub.domain.tld
CNAME("i", "j") // i.sub.domain.tld -> j.sub.domain.tld
);

View File

@@ -0,0 +1,67 @@
{
"registrars":
[
{"name":"Third-Party","type":"NONE"}
],
"dns_providers":[{"name":"Cloudflare","type":"CLOUDFLAREAPI"}],
"domains":
[
{
"name":"domain.tld",
"registrar":"Third-Party",
"dnsProviders": {"Cloudflare":-1},
"records":
[
{
"type":"A",
"name":"@",
"target":"127.0.0.1"
},
{
"type":"A",
"name":"www",
"target":"127.0.0.2"
},
{
"type":"CNAME",
"name":"a",
"target":"b"
},
{
"type":"A",
"name":"aaa",
"target":"127.0.0.3"
},
{
"type":"CNAME",
"name":"c",
"target":"d"
},
{
"type":"A",
"name":"bbb.sub",
"subdomain":"sub",
"target":"127.0.0.4"
},
{
"type":"A","name":"ccc.sub","subdomain":"sub","target":"127.0.0.5"
},
{
"type":"CNAME","name":"e.sub","subdomain":"sub","target":"f"
},
{
"type":"A","name":"ddd.sub.sub","subdomain":"sub.sub","target":"127.0.0.6"
},
{
"type":"CNAME","name":"g.sub.sub","subdomain":"sub.sub","target":"h"
},
{
"type":"A","name":"sub","subdomain":"sub","target":"127.0.0.7"
},
{
"type":"CNAME","name":"i.sub","subdomain":"sub","target":"j"
}
]
}
]
}

View File

@@ -0,0 +1,45 @@
var REG = NewRegistrar("Third-Party", "NONE");
var DNS = NewDnsProvider("Cloudflare", "CLOUDFLAREAPI");
// Test the name matching algorithm
D("domain.tld", REG, DnsProvider(DNS),
A("@", "127.0.0.1"),
A("a", "127.0.0.2"),
CNAME("b", "c")
);
D("sub.domain.tld", REG, DnsProvider(DNS),
A("@", "127.0.1.1"),
A("aa", "127.0.1.2"),
CNAME("bb", "cc")
);
// Should match domain.tld
D_EXTEND("domain.tld",
A("@", "127.0.0.3"),
A("d", "127.0.0.4"),
CNAME("e", "f")
);
// Should match domain.tld
D_EXTEND("ub.domain.tld",
A("@", "127.0.0.5"),
A("g", "127.0.0.6"),
CNAME("h", "i")
);
// Should match sub.domain.tld
D_EXTEND("sub.domain.tld",
A("@", "127.0.1.3"),
A("dd", "127.0.1.4"),
CNAME("ee", "ff")
);
// Should match domain.tld
D_EXTEND("ssub.domain.tld",
A("@", "127.0.0.7"),
A("j", "127.0.0.8"),
CNAME("k", "l")
);

View File

@@ -0,0 +1,46 @@
{
"registrars":
[
{"name":"Third-Party","type":"NONE"}
],
"dns_providers":
[
{"name":"Cloudflare","type":"CLOUDFLAREAPI"}],
"domains":
[
{
"name":"domain.tld",
"registrar":"Third-Party",
"dnsProviders":{"Cloudflare":-1},
"records":
[
{"type":"A","name":"@","target":"127.0.0.1"},
{"type":"A","name":"a","target":"127.0.0.2"},
{"type":"CNAME","name":"b","target":"c"},
{"type":"A","name":"@","target":"127.0.0.3"},
{"type":"A","name":"d","target":"127.0.0.4"},
{"type":"CNAME","name":"e","target":"f"},
{"type":"A","name":"ub","subdomain":"ub","target":"127.0.0.5"},
{"type":"A","name":"g.ub","subdomain":"ub","target":"127.0.0.6"},
{"type":"CNAME","name":"h.ub","subdomain":"ub","target":"i"},
{"type":"A","name":"@","subdomain":"ssub","target":"127.0.0.7"},
{"type":"A","name":"j","subdomain":"ssub","target":"127.0.0.8"},
{"type":"CNAME","name":"k","subdomain":"ssub","target":"l"}
]
},
{
"name":"sub.domain.tld",
"registrar":"Third-Party",
"dnsProviders":{"Cloudflare":-1},
"records":
[
{"type":"A","name":"@","target":"127.0.1.1"},
{"type":"A","name":"aa","target":"127.0.1.2"},
{"type":"CNAME","name":"bb","target":"cc"},
{"type":"A","name":"@","target":"127.0.1.3"},
{"type":"A","name":"dd","target":"127.0.1.4"},
{"type":"CNAME","name":"ee","target":"ff"}
]
}
]
}

View File

@@ -212,124 +212,128 @@ var _escData = map[string]*_escFile{
"/helpers.js": {
name: "helpers.js",
local: "pkg/js/helpers.js",
size: 26563,
size: 27416,
modtime: 0,
compressed: `
H4sIAAAAAAAC/+x9aXcbN7Lod/2Kis6bNGm3W4tjzxwqnDeMlozeaDsUlfGMni4HYoMk7G6gL4AWzSTK
b78HWzd6o2SdLF+uPjhsoFAoFAq1AAUkyAUGITmZyeBga2tnB07nsGY54JhIkEsiYE4SHOqyNBcSeE7h
PwsGC0wxRxL/ByQDnN7jWIMrFKoFEApyiUGwnM8wzFiMIx8/4hiWGD2QZA0xvs8XC0IXpkMFG+rG229i
/LAN8wQtYEWSRLXnGMUlYRATjmcyWQOhQqoqNodcGFwYWC6zXAKbq5YVqiP4F8uDJAEhSZIAxYp+1jK6
ezxnHKv2iuwZS1PNGAyzJaILLKKtrQfEYcboHIbw0xYAAMcLIiRHXAzg9i7UZTEV04yzBxLjSjFLEaGN
gilFKbaljwemixjPUZ7IEV8IGMLt3cHW1jynM0kYBUKJJCghP+Je3xJRoaiLqg2UtVL3eGCIbJDyqCd3
jGXOqQBEAXGO1mo2LA5YLclsCSvMsaUEcxyDYDBXY8u5mjOeU0lSze3LFYVieHOmOJxmSJJ7khC5VmIg
GBXAOJA5CJZiiNEaRIZnBCWQcTbDQsvBiuVJDPeq1//OCcdxVLJtgeUho3OyyDmOjwyhBQO5HozmY+TP
ih5sgeICr8aOsT1VH4JcZziEFEvkUJE59FRp35sO9Q3DIQTno4ub0VlgOPuo/1XTzfFCTR8onAMoMQ88
/AP9r5sVTWk5y1GWi2WP40X/wB+PwtQYwhEVV1YEnhwEm5teh4p4dv8Rz2QAX38NAcmmM0YfMBeEUREo
FeC3V3/qO6rCwVBNb4rkVMpeS32/zphYZC9hTEXMDW9ikT3FG4pXRi4sWwr21qSkHKJHVnPpDcqfYYUp
A/jp0YefMR431+lVuUx9cLscJ5OzAeyGFUoE5g+NZU0WlHEc+0qmXiURX2BZXfk+X+wCO0J8IXppaFe5
Y4oyAowDRrMlpCwmc4J5qASISCACUBRFBZzFOIAZShIFsCJyafE5IK1MBq5TxZ6cC/KAk7WDMHKopp0v
sO6GSqY5GyOJCvmdRkSc2B57ab8imj07BitvgBOBi0YjRUGthRpiT0nkRy3qfpX6q7Lo9uNdwaWDAu6x
ra9LPZZaZ9MIf5aYxpbKSA0thLRKraddlpytIPjnaHxxevH9wPZcTIbRPjkVeZYxLnE8gABeV8h3S71W
HIBZD80GljCzhszgjFU4MmunXDoDOOQYSQwIji6uLcIIbgTWljVDHKVYYi4ACbcWANFYkS889X3UtSi1
mjAjHm5YwobMYhoJDGH3AAh86xu4KMF0IZcHQF6/9iekMr0e/C2pT/Rjs5t90w3iizzFVHZ2ouBTGJaA
t+TuoJ2EtLVXJVMNCxYRGuPPl3PNkD58NRzCm71+Q3pULbyGQC3ZGM8SpAx2yriaJUSB0RmuWC2vH6dg
fYKaZGgYTYNzII5uzPcAbrJYSwgFlCifbw0ojnHsCYxRFEe9vi8R0+MPk+OLIzuyFmmYLrA07e0ys/07
ZjnAIdA8STYwZYUEUCZLzqyx1EIql5hrpxFmiCqIewy5Hk1sZPyo17duZRR0SsivKogRu//YLYx7v6Ew
Nnr2heTWwpD4DoZegwOl0RMsAwHsAfMVJ9JoBqPlIyss7VM5gImKDpSZAZEqm7LESYZ56U1K5d8bJ97O
9v+7tqitOa84ie3dWL7MGa9PWWUZdLCytiwVJ28DhTe406Ln9VD6BMbRCEgcDICE2vcKBlBD89gwLhUH
J0+SYq0dn4xuzibXYL0izTAstc9upKicacUzlGXJWv9IEpjnMueOfyJS+I6VtddGXLISuYrbYJZgxAHR
NWQcPxCWC3hASY6F6tBfvrZV4YM3A42uhfKk9PorSSseX4z7VYs1mZz1HvoDuLZiMpmc6U6NvTIWySPb
gHtusrLi11KFNL2HihV/gKEOt+liwo5yjrQf8lBZIHamHPIe99vzSMoEhvBw0OaUtWD2VGCK5GyJFR8f
Iv27t/Nfvf8fv+73bkW6jFd0ffd/+/9nx9OFRYsuZfjgzINSc0jNKYlVLIc8cioqLqdEwhACETR6ud2/
8zuwkGVlJQyAofISBD6lsmi/52ZRDTbXIYIYwF4I6QDe74awHMDb97u7LijIb4NYrTbIoyW8gv1viuKV
LY7hFfy5KKVe6dvdonjtF79/ZymAV0PIb9UY7ioBxkOx+AqXvSJobuE5gSt1lL9K/La/kdTFlaUTlRFG
p/Cl6BM+HI1OErTo6cVdi5BKgdbLpyLVZkHNENJbPT8PjXbwu9nZgcPRaHo4Pp2cHo7OlAdJJJmhRBXr
HSK9R+LDaOkpadqDb7+FP/fNLpcf7267qPACpXg7hN2+gqDikOVUa8NdSDGiAmJGAwm5wMB4sYehtZoX
aUV+Y7UsHHaLRDVHSeJPZyP2ts1bAm+HWMfeOY3xnFAcBz4zCxB4s/clM+xFl7eKDCXWFldtIkaGTJKF
dubObVQhoijq63kYwdDWfZeTRI0sGAWW96PR6DkYRqM2JKNRiefsdHRtEJlodQMyBdqCTRUX6P59Mz6e
ekjtdsKTuMt2LT2UlUFo+a0cpAHcFry3hj+Ecv16AfltoMgIQqNckcSjH3OORwlBYrLOcBVSk9qGyf5H
ckTFnPF0UF+OoSYrLALEluWpPV/t/QkvyPMATPcOxHwdVFweL7q1bZAazRSp4fSbHk8dxDLjruhjnXlk
NILgdiTaMpgNowIJNN2mcOux72+xtvO/qurUGL/y1bCurPLSrEKUCNyyOm+DURCCEfMQgsOL0flxcFfE
a7YzE7AVm67v3lbF1gqsEd8usS1aNYW2qPq1RHb87u1vLrDi95JY/u7tZnktAF4urQWKL5NVKwz/vrw4
7v3IKJ6SuF8KcKOqyz7XAxWfB5uG74/c9qEHb38/NfTaqG2rgfvRMuyqA9Imbb/y8uyVslvdFBsFYa1A
r+BqmVnN9cIm3PmHesnkw6RedDUZ14uur04aReMf6kUXo2rTDu2i6/ue7+Us7SLUcN2a5bDNcOthlrvD
k8ujy55MSNofwKkEsXSHNIgC5tyc6uh+XHSxq5yuvf2/RC9TSGjRXan7+eOU0AwhiRalElo8oaZ839gQ
6Lq/yNN7zFuorKyCpsct6i53qU+0zD7PydKgLTOvpd753c5IfcJrJUqAkgXjRC7TEGKywMIYLfPToD1q
Wqjto+vtl5om07GtNwyr1BcEdYMY6qyN2whTJeN3lKlYmHE6IPPVAlYM10EWBS3A5cAddFnSCV4F/QIT
7Enh1WT8PBm8moybEqj0nUWklZ9BxXiMeZhxPMcc0xkO9UoIVRhHZvq0An/OnuxQI2x2aZXsC2VUk9Yt
WyXN3TB6MN092FF2A5jhb1Kof6znRlEmueaTA9Mf7XAlwxxwWdLewmhFC6w/2uEsHx2k/WyHNSx1oObr
ZcvhevyDkeGME7VY1+EKk8VShhnj8kmRvR7/0BRY7Si8UFwdFd3SaMjbINGMb6j9o2VN8Ac3xFJ+zHcb
rBmsgzRfrTgZL6DU7xfKwvXfT66MNJS2VFvRJ9w03bBFEFTxi0XhGdZzTugC84wTumHK/2CXTIjlPPsC
06jhvYEVmqMs+iKnzk2u8ZVygRY4BIETPJOMh2ZTnNCFcZZmmEsyJzMksZ7Yydl1iwOuSl88rZqC7tly
lHVD+BR/4UIHnfTnjUUn6wlAsG3gt4uzn99z5yARSHPFQemPVjDHndJImO9WYJ9RroFf9gIlUSYJWp5e
cpPN8rm2A+BFxp/78PPPUCa+fC4iwcmHyfNcscmHSYsUqkD2pZtKTjpq4/h9NINStdLkPmB7mCJArsgM
D3wYADcjRGjQOeFC2gZ1wM/SIbLAhMbkgcQ5SlwXUbXNxeXkeACnc5NkoLNoy4SMPdsoLM4chIusGU3W
gGYzLEQnESHIZS6ASIgZFjSQSs9IzGG1RBJWatSqK0LdEGu0/Z2t8APmIdyvNahLwvU5YOgOdYJWqqjE
Au7R7NMK8bhGWTXfc7XEJqE4wbSn08H6MBzCns646BEqMVVTjZJk3Yd7jtGnGrp7zj5h6nEGI67Thi3j
JV7YY0uJhfT4XjtZ85ZZ1wbg5l1FH7AUgCHcetB3z9smbOvodvfu6b5aCWvsJZ5/qHmZTy358w/NFX/+
4Tf0K/9ozzD93BZadLiGz3LnLp55onXRsm9/cV2GuefH18fjH44rYbO3F1wD8DdI64kU8NUQWpLDghJF
qV0yKYBRXBhkfYatOqgmOj1xFOmfpupMDT/XFx77tePIkpBpV96GR6tNJ4zaeDH9LY7UfwIqplImA3iI
JLPI+vXN6zIFuhDZqUT3CfZSaif6hOg2YSud1rAki+UA9kOgePUdEngAb+9CMNXfuOp3uvr0agDv7+4c
Ip0bu70Hv8A+/AJv4ZcD+AZ+gXfwC8Av8H67yKJICMVPJd7U6N2UPEZU9FuDr+SQKSBNLgyBZJH+WT2P
0UV1vVtN0jUgdRh9NG5RT6MUZQYuLKWQtDWp5E6l+zGTPdI/aIA99qOPjNBeEAa12lb97RPj0Bqya423
mr8sj9SMF1xSHw0+qcInOaWBOnhluyi4pb7/UH5ZgjyOafKfxzOltIZwW1CVRQlb9UPwCtSS6Rfrya4c
Tzz1crDXKtjKjgB+gaDftvANtAU6gKBwoU+/v7gcm011TyX7peWaj3HGsQrt4lCnjhioqdJZfl9ecTWh
tlFR79Cr6jgPrGnnyuWBSgpvRStb7JPR+PvjSa9hgNqqQ+AT75LMM+mwNxWspci0y0oHlVPwgUFctRya
yPOry/FkOhmPLq5PLsfnRvkmWpsb9VQkVWurW4dv2uA6RN35uQ0aXQRKawemG/NbyqTq8/ya3kzwt+AJ
18SQ0nR2sESW/FJ96wPe0ngZ16Y+wn6zQ53FaKBl0tzvvxl/f9zzxMUUFBIQR//AOLuhnyhbUUWAOa+1
/sDltNG+KOtEIXleYBjdTC6PLq6vjw81MZinREocu5xVxPFAVWxvAxwxfTqp+b42sSGWUkU6PS+fT2eU
bTO6DQDHVLHE68Mm+hHhLr1o2PlcYSfiKeBiiCXM9PLCjTOOUC7ZNKZC4BkMNQ1qlK2tTk66m83nXe1c
mxmjgin7zxbmmHy7uHzika+vEjiVFsGpNOe7K0BA2RuWRQBXCVZ6Xmm7ypiA8Rq5EUy8nEmis5RT9AkD
ZXYlzLQUisjkhKdY6C0bnZMcE4GyDCu3hAJyCc0c694j5QNZJfrq1Ra8gr+VZG/Bq53KHcLCPe+ZVSgk
4rKSesviTjdKAxc5zJ3py/rqi8tbrqQse7pSAflEj/VqM5d97o2K0mPRN2zgJ+PAPpp6D7YNhmVSRLrr
u9vdOxg5D19pFR/e8WVYbbJ3B5eZidBdogbjm9oVegZOvLx9k4NeSUt32djwyrFqokSgM68NCS9XHEZ0
XSpNIxj32MOlOiQ4tvc17MVjS1DkpS6kuUT2ksKCPGDqk9XJGjUYJzstwyzpkkxjNjir4le1P2ZHWGF3
sqN+ayfOLhPR++nRQISedBXWqSUiL+NsZYfKMPBlxsj6NQbSMHyJHrA32OK+j2F9vaXC7SYKEHUXN9Sa
8i6O2czYtp2Q7qje95CN5d243dNmQJ036bd7poP77N0jz8P15qMiTS1z0jkbbUFdAdyljip3glgMw7KJ
jugagM3blyzud0UQKYtdmnhL7NB+W3IDup0dMBeKZSm1elHZHbHWRvpqAos9RfT1196OeKWqs2c7GA9J
5bZzBcdBK4bH1tLiNqjnm+kp7uZXO4F2M+d4PL4cD8C5Q5VrokELym55NNGdFYC6C1/fENB3OGJ7u+en
x+pGQKkR7GsH/sw0dqm+Lc2NLarPicJZNDsjOjOlaNMYog56y1hX4vSJcFeBNDZfDTeayG3wC/Xo10yH
tsevG60CpzXtSwaicQXXKXyfDa2ISgvaa8NRZVMLgn4ElzRZw8bGmwjQ70CI3Kj44KDlppu/Mb1VWclJ
ohR+0c3WJkVW50arIrOScaRsBtFW1ZOMygaVgzapiV1XIT0hLXE6bvy1upvq28Sclr6RftYibzGBRSJr
Bfvt3l1LOuuzRashYsEGoGrHu3cb8RVbwXZkerMTkaQx65v0ir5fWuiK2zoBKgb1DtC7ZaZQKe0y0yIs
z7lZ6Kdgdt8trFG1cXejfANET8awZUq9Fy8adc0HJYpWMhlUrnNVQR5rhrvppra4EwfNJoVRK8DL2as2
rb4gELndeft0SYsHYPlm6jzOVrafngjZUBybaKcXu2sS1asTKo7yNuHIHMpDXaodwxCQEHmKgWQKHcdC
RIWTQezRaM2XbHEjG35jxWX0H4OZVaSgbfbbHh4x6IrdsK1nyIE7v6o8JVKVKMvs9lc+YjwjMYZ7JHAM
KpxRpDr4N0WY4977EOYafxneqABNfVWSOnTTy9Y3PhRs5Z0PDetSoU9P4PxDidlMmZ5HN84tz9kTrc97
VP3iJy1JapzhdpOw4QGS8iESjmftQcPGF0Je7O3qwXf6uc/wctMu/3ajd9v0bH2vtvbAyReCdfq8jV2q
hsUqdq3OO99KCcJ2C2tfTGmvDXrXn0iWEbr4qh80IJ44xHjcateP1eeLOJ65LUySQfmGUmFlBMw5S2Ep
ZTbY2RESzT6xB8znCVtFM5buoJ2/7O2++/M3uzt7+3vv3+8qTA8EuQYf0QMSM04yGaF7lkvdJiH3HPH1
zn1CMit30VKm3lb/VS9mle2wWL/2ICORJUT2gsh5wTs7kHEsJcH8jdner1y+0X+v49vduz68gv137/vw
GlTB3l2/VrLfKHl716+97OROkfLUP/Gleapv1xaXa1uuBwVB/YkV75xY4WtpQ/O08ZCV0fvwJ0Vny87g
W6Vz/qpVz5s3lSu+ikY4R3IZzRPGuCZ6R4+2FCOFvVegV2wIogBeQ9yybxgX93wSlsfzBHEM+iYWFgOT
CoIlcjvbQlPppSoVR+r6FsjJ9Gp8+eFf08uTE32Pa1agnGacfV4PIGDzeQCP+pWPK1Wk92LvExzXUVx0
YqBVBJi2tT+5OTvrwjDPk6SC4/UYkWSR0xKX2ft/455O8lmg9/8t7Xb7mc3nxhxSSYqXMaqnAIMqefa1
i05OTW27kmMtvdJmp13dXDzZC3Wd3FCidAdKrq/P2kdWdHJzcfrD8fh6dHZ9fdY2lNyhEiKpjqTaCX12
HxdPdWGGoeX55npyeR7C1fjyh9Oj4zFcXx0fnp6cHsL4+PByfASTf10dX3taYepuEZYrYYzNI5O/8l1C
3aC4exeEQV/rHXuv1w58fHx0Oj4+bMmk9Co3JFiZ5zeDcNO4qveWsJCE6jDtWa1+35NJ+5roawhCpcrM
aWVJcfUc0bJwcnx+tZmPFYj/ZWYnM2/GZ03+3YzPlPm29W9391pB3u7uOaiTces1QV3s8teur06m392c
nqkVK9EnLMqNfq15M8SlGOjTP/0TmM6IVe2cr9+TDO4xfGTKhpsYI4Cgr7V6gu5xYpofXVybz+K5lYyT
FPG1hyuCXqkj/xbow1yOVgP4p07C7ZmXTTWWvvGzGddHEzlFiXnm1DliHp3OlGiKdDym6JEkxZoUFZOZ
tFTMgXHrvPukmMfHtI8S2jdvy5dhNJHav7J4cZolSBrcKI6JPYtzr+sZbs30s3yxP96pyOZ/is2g5wmS
EtMBjCAhQvqvu5r2FsAaT+VaLjGK9wYwSpl+hxe27/P5HHPgjKXb5vhOp/rpSLFIFiYSp8ULwtkcZkv9
Ao5i1Gd5jj5fkx+xGVeKPpM0T0GQH3EZjU4+TAqG/WAO7RUxsP/unTk64ljoI2MKaZ5IkiVlTrc39v13
74K+Zxw8sWwxBkahG3n8+WfwPss96v2WREpf2IudXSQhwUhI2AecYL2V1HA6bY9W8Pyd9aLYVwSNhhyt
VKxXfnw1HEIQNFGpuiEEU45WIpsX6Iw1M7vzOj9xiQu58OTK2DuzI5KZfX4HrXwq79BOrR0snSho/0nN
ZHGUqrrTJLj9Pstem2MV9AvE5cqrLjUXZpzOnayqZUOEZjwWOs3Kvf0MyOvd26VAqxpSx1ZDksVbctYW
lPu/u5XX+4oGwxp8S4Lczo7ZdkdxXNCi2GFpdA+s0kDqi/RpJtf1qwcloe0zXoWRMmk99TQB6OTDpMQV
2rkJzXNoRfP+s88/NyDtPxkfezPrQlo1r/pB6DlR82r8eqMU1czVJ841q86OBi/mxsFUlkAVhdZ4VRxF
cQWPLulAVKq5KqayvEBVFh3UWPH9ZkGuLr46N2oz35gcrV7KOc+6pr0x3U9iKpMpK3sb/jNgm3yDjcb9
cDTaYNQJi/HcNJ0xKtFMKiWUlBu8PWZzWErw6cw+RDaA7xhLMKL65AbTWD9ojvUtT6tgCMfxjoOPlKgq
G17sK1Wu8nlvYnA8zwWOG90LkeMBnFmNezhyb6yb6D1hK/OmvYbzUYva03LQM3bf5O5bMXG21HhMGseK
JPEARhZz2d9MjVl3oiBmiMdtvRUpa9Hm/jx76011p719vvWrCbihuNDS5lOpQ8ooDvrVYrgNDoK7gzYU
asw1NLqoHZWpcugKfAX1blgFdV/VGvfh559L6CpwbSu6qHKmZziE3Q1gdiSbqn1M5li7xaHxV2jToVFz
jqnka1VkKGe8FLCXehf1qVFrs/6QkVdVLNvmK0ZaPR2ORlX1FOhmQQgekrDy3qBvozpeOHo+6n7zkfBW
Ae53HFeEkHguhS8F5iAjwdQcYDyTQoWgpFB93ZK7fv9gq2tJfAFhnmC9nDgtO2EdrU9k3ZBca8uO4Ogf
p+fuemLxRP1f9999A/driSvvjf/j9LyHePFA1myZ00/WGO+/e1e+PjruvDPjho84bxkyvB6WSMvRj92h
Mo9EQma4R0IF64FWzwHGbohFTuGKo0y/gcw4LBJ23+vrn95D+pAwpE3WnCTYBKUjUfrhBQ96hML3rK94
RCiwXP+vTyRnCSC6XqF1qJ8HVu1stnRxUdXl9QlEiVy/mS3x7JONFC+YxANHGBH2QhnV8S9XYWpOYzbT
x4E4rr/nHME109nCRIcOa0UTW1HgRHyK/ERJrYmmtpdik8ee0+/fwRC2P4rtA3uuOcNKvWhKCJ0leYwh
+igce9xM608YatrNSXmP5kkSlpj9V+a9k0SDp+Mo0dLa00Adub66butx638CAAD//34OENjDZwAA
H4sIAAAAAAAC/+x9a3cbN7Lgd/2Kis5eN2m3Ww/Hnnuo4exw9MhoR69D0rmeq9VyIDZIwu7XAmjRTKL8
9j14NtAPStaZJF/WHxI2UCgUCoV6AAUoKBkGximZ8+BoZ2dvD84XsMlLwDHhwFeEwYIkOJRlack40DKD
fy1zWOIMU8Txv4DngNN7HEtwgUK0AJIBX2FgeUnnGOZ5jCMXP6IYVhg9kGQDMb4vl0uSLVWHAjaUjXff
xvhhFxYJWsKaJIloTzGKK8IgJhTPebIBkjEuqvIFlEzhwpCXvCg55AvR0qM6gn/mZZAkwDhJEsiwoD9v
Gd09XuQUi/aC7HmeppIxGOYrlC0xi3Z2HhCFeZ4tYAg/7wAAULwkjFNE2QBu70JZFmdsVtD8gcTYK85T
RLJGwSxDKdalj0eqixgvUJnwEV0yGMLt3dHOzqLM5pzkGZCMcIIS8hPu9TURHkVdVG2hrJW6xyNFZIOU
Rzm5Y8xLmjFAGSBK0UbMhsYB6xWZr2CNKdaUYIpjYDksxNhKKuaMlhknqeT29ToDO7xFLjicFoiTe5IQ
vhFiwPKMQU6BLIDlKYYYbYAVeE5QAgXN55hJOVjnZRLDvej1/5aE4jiq2LbE/DjPFmRZUhyfKEItA6kc
jORj5M6KHKxFcYXXY8PYnqgPgW8KHEKKOTKoyAJ6orTvTIf4huEQgsvR1cfRRaA4+yj/K6ab4qWYPhA4
B1BhHjj4B/K/ZlYkpdUsR0XJVj2Kl/0jdzwCU2MIJxm70SLw5CDyhep1KIjP7z/jOQ/g1SsISDGb59kD
pozkGQuECnDbi3/iO/LhYCimN0V8xnmvpb5fZ0zMipcwxhNzxZuYFU/xJsNrJReaLZa9NSmphuiQZctY
ea8kaABBEDZX5KD6GXq8GsDPjy78PKdxc/neVKvXBderdDq9GMB+6BHIMH1orHayzHKKY1f31Ks4okvM
fYXgskuvuxNEl6yXhnrxG14J25BTwGi+gjSPyYJgGgq5IhwIAxRFkYXTGAcwR0kiANaErzQ+AyR1zMB0
KthTUkYecLIxEEo8hTTQJZbdZDyXnI0RR1asZxFhZ7rHXtr3JLanx6DFEHDCsG00EhTUWogh9oSgfpYr
wK0S/3wW3X6+s1w6snCPbX1dy7HUOptF+CvHWaypjMTQQkh9ah2ls6L5GoL/Go2vzq9+GOie7WQopVRm
rCyKnHIcDyCANx75RgPUigM4MQJeq9GEqaWlBqeMxYlaUtWKGsAxxYhjQHByNdEII/jIsDS4BaIoxRxT
BoiZtQAoiwX5zNHqJ11rVWoPNeLhlpWtyLTTSGAI+0dA4M+u3YsSnC356gjImzfuhHjT68DfkvpEPza7
OVTdILosU5zxzk4EfArDCvCW3B21k5C29ipkqmHYIpLF+Ov1QjKkD98Nh/D2oN+QHlELbyAQSzbG8wQJ
O57mVMwSyiDP5tgzZk4/Ru+6BDXJkDCSBuNXnMxOP01Pr9TE9gfwsYjrcgIoEa7hBlAc41hpi5NePxQe
glW/Qo4ozheOrHiY2+RktsRcdaEXoKbMsNEADiErk2QLu9aIQZbzimcbzKX4SqKElwlzlAmIewylHGGs
pP+k19d+aORxVi+t/P5zVA1xKHsUBYzT3n6oPpUgvXVaOMXwFg5+c6kXnXZL/sFvKPmNnl2JvNUwJL6D
odPgSJiPBPOAQf6A6ZoSrtSQMimRlsx26RjAVEQowqYBS4UBW+GkwLTyaLmIMVQgoeftf000au1SCEdV
xjvKpXYgNViMFyTDseSjqF2SB6xcmCdkvp1mzeRKisokOXJDkgRnUibM5NXFxFvnHdMnFowrnm+9qSB3
t4GoDe50+75YVh0gdbtaU2kt2OAvzlDqzRsD3Y7rqKOx8EwDEgcDIKH0joNBHdOj3/axbvVdt1K1smrw
9Gz08WI6Ae3HSvHCXEZZas1V60JIGCqKZCN/JAksSl5SI0NMytapcMSkf8XzCrmItGGeYEQBZRsoKH4g
ecngASUlZqJD19bqVjZqaoaGXWrlybXu6h1pE9xF3/edien0ovfQH8BEL6rp9EJ2qkyEchYcshW4E9gI
B2vCRRDae/AcrAcYyg2SbDnNT0qKpIv44KkTPVcGeY+67WnEeQJDeDhq85dbMDs2KEV8vsKCjw+R/N3b
+z+9/x2/6fduWbqK19nm7n/2/8eeY4xsiy5r9GAst7AzSMwpiUX0jRxyPBtTZoTDEAIWNHq5PfQWoYas
Kr3ADYbCgWP4POO2/YGZRTHYUgZ1bAAHIaQD+LAfwmoA7z7s75swrrwN4kBo6TJawWs4/N4Wr3VxDK/h
T7Y0c0rf7dvijVv84b2mAF4PobwVY7jzQsIHu/hsNOUJmll4RuAqPe2uErftbyR1sbd0oir46xS+FH3B
x6PRWYKWPbm4azFtJdBy+XhSrRbUHCG5OffLUGkHt5u9PTgejWbH4/Pp+fHoQjj3hJM5SkSx3NOTu1ou
jJSeiqYD+POf4U99tS/p7lDsmjj+CqV4N4T9voDI2HFeZlIb7kOKUcYgzrOAQ8mkRTS7TlKrOUFw5DYW
y8Jg10hEc5Qk7nQ2dkt085atEoNY7paUmTbagctMCwJvD75lhp3A/1aQIcRa46pNxEiRSYpQz9ylDvhY
FEV9OQ8jGOq6v5UkESMLRoHm/Wg0eg6G0agNyWhU4bk4H00UIrWRsAWZAG3BJootuv/+OD6dOUj1BtCT
uKt2LT1UlUGo+S3cyQHcWt5rXyCEav06eyW3gSAjCJVyRRyPfiopHiUEsemmwD6kJLUNk/4fpyhji5ym
g/pyDCVZoY3dW5an9AWlr8yc+NsBUN0bEPV15DlrzsaDboPEaGZIDKdfd6KaIJoZd7aPTeGQ0difaEci
LYPa4rNI4LGxXRLuPPbdTfF2/vuqTozxO1cNy0qfl2oVooThltV5G4yCEJSYhxAcX40uT4M7G0rrzlQs
bbfJ37/zxVYLrBLfLrG1rZpCa6v+XSI7fv/uNxdY9ntJLH3/bru8WoCXS6tF8W2yqoXhv6+vTns/5Rme
kbhfCXCjqss+10Mslwfbhu+OXPchB69/PzX02qh1q4H50TJs3wFpk7Z/8/LsVbLr71eOnH14VSBXsF+m
VnO9sAl3+aleMv00rRfdTMf1osnNWaNo/GO96GrkN+3QLrK+7/hextIuQwnXrVmO2wy3HGa1cT+9Prnu
8YSk/QGcc2Arc6yGMsCUqnM42Y+JLvaF03Vw+J/RyxQSWnZXyn7+OCU0R4ijZaWElk+oKdc3VgSa7q/K
9B7TFiq9VdD0uFnd5a70iZTZ5zlZErRl5qXUG7/bGKkveCNECVCyzCnhqzSEmCwxU0ZL/VRoT5oWavdk
svtS06Q61vWKYV69JagbRFGnbdxWGJ+M31GmYqbGaYDUVwuYHa6BtAUtwNXADXRV0gnug36DCXak8GY6
fp4M3kzHTQkU+k4jkspPocppjGlYULzAFGdzHMqVEIowjszlQRL+WjzZoUTY7FIr2RfKqCStW7Yqmrth
5GC6e9Cj7AZQw9+mUP9Yzy1DBaeSTwZMfrTDVQwzwFVJewulFTWw/GiH03w0kPqzHVax1ICqr5cth8n4
RyXDBSVisW7CNSbLFQ+LnPInRXYy/rEpsNJReKG4Giq6pVGRt0Wic7ql9o+WNUYfzBAr+VHfbbBqsAZS
fbXizKmFEr9fKAuTv5/dKGmobKm0ok+4abJhiyCI4heLwjOs54JkS0wLSrItU/4Hu2SMrRbFN5hGCe8M
zGqOquibnDozucpXKhla4hAYTvCc5zRUm+IkWypnaY4pJwsyRxzLiZ1eTFoccFH64mmVFHTPlqGsG8Kl
+BsXOshjS2csMr2SAYJdBb9rz35+z52DhCHJFQMlP1rBDHcqI6G+W4FdRpkGbtkLlESV1ql5ek1VotHX
2g6AExl/7cMvv0CVk/TVRoLTT9PnuWLTT9MWKRSB7Es3lYx01Mbx+2gGoWq5SkvB+jCFAV+TOR64MABm
Rog6ZV8QyrhuUAf8yg0iDUyymDyQuESJ6SLy21xdT08HcL5Qx/Ay77nKlTnQjUJ75sBMZJ1nyQbQfI4Z
6yQiBL4qGRAOcY5ZFnChZzimsF4hDmsxatEVycwQa7T9PV/jB0xDuN9IUJM27XJA0R3K3LlUUIkZ3KP5
lzWicY0yP0N3vcIqBTzBWU9m6snT/AOZ8tIjGceZmGqUJJs+3FOMvtTQ3dP8C84czmBEZaK3ZjzHS31s
yTHjDt9rJ2vOMuvaANy+q+gCVgIwhFsH+u5524RtHd3u3z3dVythjb3Ey081L/OpJX/5qbniLz/9hn7l
H+0Zpl/bQosO1/BZ7tzVM0+0rlr27a8mVZh7eTo5Hf946oXNzl5wDcDdIK0nUsB3Q2jJ2wsqFJV2KTiD
PMPWIMszbJmhE3zDUaR7miozNdzsbHjs144jK0JmXXkbDq060zNq48XstzhS/xkyNuM8GcBDxHONrF/f
vK6S1q3Izji6T7CT7TyVJ0S3Sb6WaQ0rslwN4DCEDK//hhgewLu7EFT196b6vaw+vxnAh7s7g0imLe8e
wK9wCL/CO/j1CL6HX+E9/ArwK3zYtVkUCcnwU4k3NXq3pdoREf3W4L2MOwEkyYUhkCKSP/3zGFnUlqxV
+SoKpC0jy6CeRSkqFFxYSSFpa+Km85fpYZzzHuk307Ue+9HnnGS9IAxqta362yXGoFVkb8/mcngkZtxy
SXw0+CQKn+SUBOrgle7Cckt8/6H80gQ5HJPkP49nQmkN4dZSVURJvu6H4BSIJdO360mvHEc85XLQF2Hy
tR4B/ApBv23hK2gNdASBdaHPf7i6HqtNdUclu6XVmo9xQbEI7eJQpo4oqJnQWW5fTrGf69yoqHfoVHWc
B9a0s3evw8uu9rSyxj4djX84nfYaBqitOgQ6da41PZMOfYlEW4pCuqzZwDsFHyjEvuWQRF7eXI+ns+l4
dDU5ux5fKuWbSG2u1JPNd5dWtw7ftMF1iLrzcxs0ugiE1g5UN+o354nv8/w7vZngr8ETrolJE607O5gj
TX6lvuUBb2W8lGtTH2G/2aHMYlTQPGnu938c/3Dac8RFFVgJiKN/YFx8zL5k+ToTBKjzWu0PXM8a7W1Z
JwpOS4th9HF6fXI1mZweS2IwTQnnODY5q4jigajY3QU4yeXppOT7RsWGmHMR6fScfD6ZUbabZ7sAcJoJ
ljh96EQ/wsx9JAm7WAjshD0FbIdYwcyur8w44wiVPJ/FGWN4DkNJgxhla6uzs+5mi0VXO9NmnmcsF/Y/
X6pj8l17L8ghX97yMCotgnOuznfXgCDL3+ZFBHCTYKHnhbbzxgQ5rZEbwdTJmSQySzlFXzBkuV4JcymF
LFIZ9ClmcstG5iTHhKGiwMItyQCZhGaKZe+R8IG0En39egdew18rsnfg9Z5369O65z21ChlHlHupt3nc
6UZJYJvD3Jm+LG8lmbxlL2XZ0ZUCyCV6LFebuod1r1SUHIu8/AQ/Kwf2UdU7sG0wecFZJLu+u92/g5Hx
8IVWceENX4Z+k4M7uC5UhG4SNXK6rZ3VM3Dm3HJQOeheWrrJxobXhlVTIQKdeW2IObniMMo2ldJUgnGP
HVyiQ4JjfWFGXxXXBEVO6kJacqSvdKibEw5ZnawRgzGy0zLMii6eS8wKpy9+vv1RO8ICu5Ed8Vs6cXqZ
sN7PjwoidKTLWqeWiLyKs4UdqsLAlxkj7dcoSMXwFXrAzmDt1SvF+npLgdtMFCB7f0WsKedOn86MbdsJ
6Y7qXQ9ZWd6t2z1tBtR4k267Zzq4z949cjxcZz48aWqZk87ZaAvqLHCXOvJuUOUxDKsmMqJrADYvxuZx
vyuCSPPYpIm3xA7tF1m3oNvbA3UFnFdSKxeV3hFrbSSvJuSxo4hevXJ2xL2qzp71YBwk3v10D8dRK4bH
1lJ7UdfxzeQUd/OrnUC9mXM6Hl+PB2DcIe8Gb9CCslseVXSnBaDuwtc3BOQdjljf7vn50d8IqDSCfp/C
nZnGLtWfK3Njr3v5QxY4bbMLIjNTbJvGEGXQW8W6HKdPhLsCpLH5qrjRRK6DX6hHv2o6pD1+02gVGK2p
355gjdvRRuG7bGhFVFnQXhsOn00tCPoRXGfJBrY23kaAfLmDlUrFB/Uda8FQd2N6x1vJSSIUvu1mZ5si
q3OjVZFpyTgRNoNIq+pIhrdBZaBVamLXxVFHSCuc1bXBgzZJEjaxzCrfSD5EUraYQJvI6mG/PbhrSWd9
tmg1RCzYAuR3vH+3FZ/dCtYjk5udiCSNWd+mV+RtXKsrbusEiBjUOUDvlhmrUtplpkVYnnOz0E3B7L5b
WKNq6+5G9WqLnIxhy5Q6b5Q06ppvfdhWPBl417l8kMea4W66qS3uxFGziTVqFryaPb9p3bv7O8riBFcX
6fvq7r29f+wczNkr88519VevrBslBP27IQTHZ7Px6cn5+PR4GsCrVx0qvNFmenp5UzXc4vq5d+Wdr6PW
Reu5oHIvptuquM7qVsRbDbyL580QgiiAN0+gqy1C//2NyByg6PeAWpw0LdqqzhF+b4fwiagaxbEKSHux
ucni324Roa6zT0oWUJ27Z9J3DwExVqYYSCHQUcxYZP1Aok+va+5+i6ffcO09r959YWnuLdS2Bdr2mo9C
Zzcsd56xVM0Ro/cQj7/oNbPb38iJ8ZzEGO4RwzGIiFOQauDf2kjUvJbD1BqsIlARQ4svL+9GNr1ufSFH
wHqv5EhYk61+fgaXnyrMasrkPJpx7jj+OGt9HMcPXZ409qmKV9qt9pbne6pnfCiet8d1W9/XeXFAIgff
GYo8IxBJu0KQrQFIM/hwA4/a80DfCNaptRobiQ2nwm4sXna+NBSE7U6Qfm+ovTboTb6QoiDZ8rt+0IDo
P+elhaZ+9N8Eo3hudplJAdXDZNYRYLCgeQorzovB3h7jaP4lf8B0keTraJ6ne2jvPw/23//p+/29g8OD
Dx/2BaYHgkyDz+gBsTklBY/QfV5y2SYh9xTRzd59Qgotd9GKp85pzE0vzr0dy1g+X8IjViSE94LIBCp7
e1BQzDnB9K06gfHuR8l/b+Lb/bs+vIbD9x/68AZEwcFdv1Zy2Ch5d9evPZdmDvrK1D2Uz8pUXoC2959b
bnAFQf2BIucoX+BraZOVaeN1OKX34T8EnS2bt++EzvmLVD1v33q3sAWNcIn4KlokeU4l0XtytJUYCew9
i16wQZvnlq3d2F7FSvIyXiSIYpCX5TAbqGwdzJE5fGCSSiebzGY9yIs6Z7Ob8fWnf86uz87kVbu5RTkr
aP51M4AgXywCeJTP1tyIIrldfp/guI7iqhND5iPAWVv7s48XF10YFmWSeDjejBFJlmVW4VLHM2/Nw2Mu
C+QRjaZdnxDki4Uyhxkn9vES/6Bm4JOnHyTp5NRMt6s41tJr1uy0q5urJ3vJTCcfMyJ0B0omk4v2kdlO
Pl6d/3g6nowuJpOLtqGUBhVjiT8Sv5Ps2X1cPdWFGoaU54+T6fVlCDfj6x/PT07HMLk5PT4/Oz+G8enx
9fgEpv+8OZ04WmFmLnpWK2GM1cut/+brnrKBvR4ZhEFf6h199VoP3MQILTffnMijOwdOvWkbhNvG5V8t
w4yTTEbSz2r1+x4e6yd630AQClWmDpQriv2jXs1CL9Zq5aMfjf1/ZnYx8+P4osm/j+MLYb51/bv9g1aQ
d/sHBups3HqTUxabFMPJzdnsbx/PL8SK5egLZtVZjNS8BaKcDeQBrfwJuUxaFu2Mr9/jOdxj+JzLh8dk
jBFA0JdaPUH3OFHNT64m6tO+iFNQkiK6cXBF0Kt05F8Ded5O0XoA/yXzpHvquWCJpa/87JzK06MyQ4l6
O9g4Yg6dxpRIimQ8JujhJMWSFBGTqcxhTOUjaVLNuKSoB/qkjxLqh6Srx3skkdK/0nhxWiSIK9wojok+
LjVvUypuzeWjlrE73hkrFv8Rq0EvEsQ5zgYwgoQw7j6ZrNprAG08hWu5wig+GMAozeXj1rB7Xy4WmALN
83RXnbDKbEwZKdp8bsJxap/lLhYwX8lHigSjvvJL9HVCfsJqXCn6StIyBUZ+wlU0Ov00tQz7UeVVCGLg
8P17dbpHMZOn+hmkZcJJkVRp987YD9+/D/qOcXDEssUYKIWu5PGXX8D5rI4RDltyXV1ht5vviEOCEeNw
CDjBcrev4XTqHrXguYcftthVBI2GFK1FrFd9fDccQhA0UYm6IQQzitasWFh0ypqpAxSZQrrCVi4cuVL2
Tu2IFOooxkALn8o5VxVrB3MjCtJ/EjNpT7tFd5IEsyWr2avT4IK+RVytPH+pmTDjfGFkVSwbwiTjMZOZ
cOZBdUBO784uBVrXkBq2KpI03oqzuqDaot/3nqO0DYY1+JYcxr09dTKC4tjSItihaTTPE2cBl28dpAXf
1G+HVIS2z7gPw3nSujupAtDpp2mFK9RzE6oX62zz/rOPqLcg7T8ZHzsza0JaMa/ylfUFEfOq/HqlFMXM
1SfONPNnR4LbuTEw3hLwUUiN5+OwxR4eWdKBqFJzPqaq3KKqio5qrPhhuyD7i6/OjdrMNyZHqpdqzouu
aW9M95OYqnxXb2/Dfaltm2+w1bgfj0ZbjDrJY7xQTed5xtGcCyWUVBu8vVynGVXgs7l+K24Af8vzBKNM
Hq7hLJZ/JQDLi7hawRCK4z0DHwlRFTbc7it5ty2dZ0soXpQMx43uGSvxAC60xj0emT9coKL3JF+rPxQh
4VzUrPb6H/SU3VfXK7SYGFuqPCaJY02SeAAjjbnqby7GLDsREHNE47bebFZhtL0/x946U91pb59v/WoC
rii2Wlp9CnWY5RkO+n4x3AZHwd1RGwox5hoaWdSOSlUZdBafpd4My1L3Xa1xH375pYL2gWtb0bbKmJ7h
EPa3gOmRbKt2ManMgxaHxl2hTYdGzDnOON2IIkV5TisBe6l3UZ8asTbrb005VXbZNh+akurpeDTy1VMg
mwUhOEhC70lI10Z1PEL1fNT95hP7rQLc7ziuCCFxXApXCtRBRoIzdYDxTAoFgopC8XVL7vr9o52uJfEN
hDmC9XLipOyEdbQukXVDMpGWHcHJP84vzQ1S+wce/nL4/nu433Dsvdb/j/PLHqL2DbP5qsy+aGN8+P59
9UDsuPNakxk+orRlyPBmWCGtRj825/40YgmZ4x4JBawD6p8DjM0QbdrnmqJCPuqdU1gm+X2vL386f4YC
khxJk7UgCVZB6YhVfrjlQY9k8EPeFzwiGeSl/HtCnOYJoGyzRptQvuAs2umEdnuX2KReMpQRvnk7X+H5
Fx0pXuUcDwxhhOk7f5mMf6kIU8sszufyOBDH9QfKI5jkMqGbyNBhI2jK1xlQwr5Ebi6r1EQz3Yvd5NGp
FId3MITdz2z3SJ9rzrFQL5ISks2TMsYQfWaGPWam5ScMJe0qmaGXlUkSVpjd1+qdk0SFp+MoUdPak0Ad
6diybudx5/8FAAD//7FJb88YawAA
`,
},
}

View File

@@ -304,7 +304,11 @@ func ValidateAndNormalizeConfig(config *models.DNSConfig) (errs []error) {
// These record types have a target that is a hostname.
// We normalize them to a FQDN so there is less variation to handle. If a
// provider API requires a shortname, the provider must do the shortening.
rec.SetTarget(dnsutil.AddOrigin(rec.GetTargetField(), domain.Name+"."))
origin := domain.Name + "."
if len(rec.SubDomain) > 0 {
origin = rec.SubDomain + "." + origin
}
rec.SetTarget(dnsutil.AddOrigin(rec.GetTargetField(), origin))
} else if rec.Type == "A" || rec.Type == "AAAA" {
rec.SetTarget(net.ParseIP(rec.GetTargetField()).String())
} else if rec.Type == "PTR" {

View File

@@ -158,7 +158,7 @@ func (c *api) get(endpoint string) ([]byte, error) {
if resp.StatusCode == 400 {
// 400, error message is in the body as plain text
return nil, fmt.Errorf("CSC Global API error: %w URL: %s%s",
return nil, fmt.Errorf("CSC Global API error: %s URL: %s%s",
bodyString,
req.Host, req.URL.RequestURI())
}