diff --git a/OWNERS b/OWNERS
index f6a47203e..958f5ca2b 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,4 +1,3 @@
-# providers/activedir DEPRECATED
providers/akamaiedgedns @svernick
providers/autodns @arnoschoon
providers/axfrddns @hnrgrgr
@@ -22,7 +21,7 @@ providers/hetzner @das7pad
providers/hexonet @papakai
providers/hostingde @juliusrickert
providers/internetbs @pragmaton
-providers/inwx @svenpeter42
+providers/inwx @patschi
providers/msdns @tlimoncelli
providers/linode @koesie10
providers/namecheap @captncraig
diff --git a/README.md b/README.md
index 9b6e63f33..a88346bf8 100644
--- a/README.md
+++ b/README.md
@@ -78,7 +78,7 @@ You can think of it as a DNS compiler. The configuration files are
written in a DSL that looks a lot like JavaScript. It is compiled
to an intermediate representation (IR). Compiler back-ends use the
IR to update your DNS zones on services such as Route53, Cloudflare,
-and Gandi, or systems such as BIND and Active Directory.
+and Gandi, or systems such as BIND.
## An Example
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 000000000..e77616de7
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,16 @@
+# Security Policy
+
+DNSControl is a command-line tool and therefore has a different (limited) attack surface as compared to a web app or other system.
+
+## Supported Versions
+
+Only the most recent release is supported with security updates.
+
+When a major version is incremented, we'll support the previous major version for 6 months. For example, when v4.0 is released, we will support the most recent v3.x release for 6 months.
+
+## Reporting a Vulnerability
+
+To report a vulnerability please [create a new Github "issue"](https://github.com/StackExchange/dnscontrol/issues/new/choose).
+
+We will respond in a best-effort manner, usually within 1 week. We will communciate via the Github issue unless we need to communicate privately, in which case we'll arrange a way to communicate directly.
+
diff --git a/docs/_includes/matrix.html b/docs/_includes/matrix.html
index 629512956..2b84635f7 100644
--- a/docs/_includes/matrix.html
+++ b/docs/_includes/matrix.html
@@ -5,7 +5,6 @@
|
- ACTIVEDIRECTORY_PS |
AKAMAIEDGEDNS |
AUTODNS |
AXFRDDNS |
@@ -52,9 +51,6 @@
-
-
- |
|
@@ -217,9 +213,6 @@
|
-
-
- |
|
@@ -331,9 +324,6 @@
|
-
-
- |
|
@@ -442,9 +432,6 @@
|
-
-
- |
|
@@ -531,8 +518,8 @@
|
-
-
+ |
+
|
@@ -540,7 +527,6 @@
|
- |
|
@@ -611,9 +597,6 @@
-
-
- |
|
@@ -724,9 +707,6 @@
-
-
- |
|
@@ -835,7 +815,6 @@
- |
|
@@ -912,7 +891,6 @@
- |
|
@@ -963,9 +941,6 @@
-
-
- |
|
@@ -1082,7 +1057,6 @@
- |
|
@@ -1173,7 +1147,6 @@
- |
|
@@ -1336,7 +1309,6 @@
|
|
|
- |
|
@@ -1370,7 +1342,6 @@
|
|
|
- |
|
@@ -1420,7 +1391,6 @@
- |
|
@@ -1501,7 +1471,6 @@
- |
|
@@ -1544,12 +1513,10 @@
|
|
|
+ |
-
-
- |
|
@@ -1656,9 +1623,6 @@
-
-
- |
|
@@ -1785,9 +1749,6 @@
-
-
- |
|
@@ -1914,9 +1875,6 @@
-
-
- |
|
diff --git a/docs/_providers/activedir.md b/docs/_providers/activedir.md
deleted file mode 100644
index d0f72a37a..000000000
--- a/docs/_providers/activedir.md
+++ /dev/null
@@ -1,72 +0,0 @@
----
-name: ActiveDirectory_PS
-layout: default
-jsId: ACTIVEDIRECTORY_PS
-title: ActiveDirectory_PS Provider
----
-
-# WARNING:
-
-WARNING: This provider is deprecated and will eventually be removed.
-Please switch to MSDNS. It is more modern and reliable. The
-`creds.json` fields changed names; otherwise it should be an
-uneventful upgrade.
-
-# ActiveDirectory_PS Provider
-This provider updates an Microsoft Active Directory server DNS server. It interacts with AD via PowerShell commands that are generated and executed on the local machine. This means that DNSControl must be run on a Windows host. This driver automatically deactivates itself when run on non-Windows systems.
-
-# Running on Non-Windows systems
-For debugging and testing on non-Windows systems, a "fake PowerShell" mode can be used, which will activate the driver and simulate PowerShell as follows:
-
-- **Zone Input**: Normally when DNSControl needs to know the contents of an existing DNS zone, it generates a PowerShell command to gather such information and saves a copy in a file called `adzonedump.ZONE.json` (where "ZONE" is replaced with the zone name). When "fake PowerShell" mode is enabled, the PowerShell command is not run, but the `adzonedump.ZONE.json` file is read. You must generate this file ahead of time (often on a different machine, one that runs PowerShell).
-- **Zone Changes**: Normally when DNSControl needs to change DNS records, it executes PowerShell commands as required. When "fake PowerShell" mode is enabled, these commands are simply logged to a file `dns_update_commands.ps1` and the system assumes they executed.
-
-To activate this mode, set `"fakeps":"true"` inside your credentials file for the provider.
-
-## Configuration
-
-The `ActiveDirectory_PS` provider reads an `ADServer` setting from `creds.json` to know the name of the ActiveDirectory DNS Server to update.
-
-```js
-{
- "activedir": {
- "TYPE": "ACTIVEDIRECTORY_PS",
- "ADServer": "ny-dc01"
- }
-}
-```
-
-
-If you want to modify the "fake powershell" mode details, you can set them in the credentials file:
-
-```js
-{
- "activedir": {
- "TYPE": "ACTIVEDIRECTORY_PS",
- "ADServer": "ny-dc01",
- "fakeps": "true",
- "pslog": "powershell.log",
- "psout": "commandsToRun.ps1"
- }
-}
-```
-
-
-An example DNS configuration:
-
-```js
-var REG_NONE = NewRegistrar("none", "NONE")
-var DSP_ACTIVEDIRECTORY = NewDnsProvider("activedir", "ACTIVEDIRECTORY_PS");
-
-D("example.tld", REG_NONE, DnsProvider(DSP_ACTIVEDIRECTORY),
- A("test", "1.2.3.4")
-)
-```
-
-To generate a `adzonedump.ZONE.json` file, run `dnscontrol preview` on a Windows system then copy the appropriate file to the system you'll use in "fake powershell" mode.
-
-The `adzonedump.ZONE.json` files should be UTF-16LE encoded. If you hand-craft such a file on a non-Windows system, you may need to convert it from UTF-8 to UTF-16LE using:
-
- iconv -f UTF8 -t UTF-16LE adzonedump.FOO.json
-
-If you check these files into Git, you should mark them as "binary" in `.gitattributes`.
diff --git a/docs/_providers/msdns.md b/docs/_providers/msdns.md
index d47493d5a..686e49535 100644
--- a/docs/_providers/msdns.md
+++ b/docs/_providers/msdns.md
@@ -16,8 +16,6 @@ non-Windows systems.
DNSControl will use `New-PSSession` to execute the commands remotely if
`computername` is set in `creds.json` (see below).
-This provider will replace `ACTIVEDIRECTORY_PS` which is deprecated.
-
# Caveats
* Two systems updating a zone is never a good idea. If Windows Dynamic
@@ -65,37 +63,3 @@ D("example.tld", REG_NONE, DnsProvider(DSP_MSDNS),
A("test", "1.2.3.4")
)
```
-
-
-# Converting from `ACTIVEDIRECTORY_PS`
-
-If you were using the `ACTIVEDIRECTORY_PS` provider and are switching to `MSDNS`, make the following changes:
-
-1. In `dnsconfig.js`, change `ACTIVEDIRECTORY_PS` to `MSDNS` in any `NewDnsProvider()` calls.
-
-2. In `creds.json`: Since unused fields are quietly ignored, it is
- safe to list both the old and new options:
- a. Add a field "dnsserver" with the DNS server's name. (OPTIONAL if dnscontrol is run on the DNS server.)
- b. If the PowerShell commands need to be run on a different host using a `PSSession`, add `pssession: "remoteserver",` where `remoteserver` is the name of the server where the PowerShell commands should run.
- c. The MSDNS provider will quietly ignore `fakeps`, `pslog` and `psout`. Feel free to leave them in `creds.json` until you are sure you aren't going back to the old provider.
-
-During the transition your `creds.json` file might look like:
-
-```json
-{
- "msdns": {
- "ADServer": "ny-dc01", << Delete these after you have
- "fakeps": "true", << verified that MSDNS works
- "pslog": "log.txt", << properly.
- "psout": "out.txt",
- "dnsserver": "ny-dc01",
- "pssession": "mywindowshost"
- }
-}
-```
-
-3. Run `dnscontrol preview` to make sure the provider works as expected.
-
-4. If for any reason you need to revert, simply change `dnsconfig.js` to refer to `ACTIVEDIRECTORY_PS` again (or use `git` commands). If you are reverting because you found a bug, please [file an issue](https://github.com/StackExchange/dnscontrol/issues/new).
-
-5. Once you are confident in the new provider, remove `ADServer`, `fakeps`, `pslog`, `psout` from `creds.json`.
diff --git a/docs/provider-list.md b/docs/provider-list.md
index f12f30acd..4722da648 100644
--- a/docs/provider-list.md
+++ b/docs/provider-list.md
@@ -47,7 +47,6 @@ Official support means:
Providers in this category and their maintainers are:
-* `ACTIVEDIRECTORY_PS` @tlimoncelli
* `AZURE_DNS` @vatsalyagoel
* `BIND` @tlimoncelli
* `GCLOUD` @riyadhalnur
diff --git a/docs/writing-providers.md b/docs/writing-providers.md
index c6f504697..3977a5f82 100644
--- a/docs/writing-providers.md
+++ b/docs/writing-providers.md
@@ -92,7 +92,7 @@ Pick a similar provider as your base. Providers basically fall
into three general categories:
* **zone:** The API requires you to upload the entire zone every time. (BIND, NAMECHEAP).
-* **incremental-record:** The API lets you add/change/delete individual DNS records. (ACTIVEDIR, CLOUDFLARE, DNSIMPLE, NAMEDOTCOM, GCLOUD, HEXONET)
+* **incremental-record:** The API lets you add/change/delete individual DNS records. (CLOUDFLARE, DNSIMPLE, NAMEDOTCOM, GCLOUD, HEXONET)
* **incremental-label:** Like incremental-record, but if there are
multiple records on a label (for example, example www.example.com
has A and MX records), you have to replace all the records at that
diff --git a/integrationTest/integration_test.go b/integrationTest/integration_test.go
index 3466b46c1..6b0d60576 100644
--- a/integrationTest/integration_test.go
+++ b/integrationTest/integration_test.go
@@ -749,7 +749,6 @@ func makeTests(t *testing.T) []*TestGroup {
),
testgroup("MX",
- not("ACTIVEDIRECTORY_PS"), // Not implemented.
tc("MX record", mx("@", 5, "foo.com.")),
tc("Second MX record, same prio", mx("@", 5, "foo.com."), mx("@", 5, "foo2.com.")),
tc("3 MX", mx("@", 5, "foo.com."), mx("@", 5, "foo2.com."), mx("@", 15, "foo3.com.")),
@@ -1048,7 +1047,7 @@ func makeTests(t *testing.T) []*TestGroup {
tc("CAA change flag", caa("@", "issuewild", 128, "example.com")),
),
testgroup("CAA with ;",
- requires(providers.CanUseCAA), not("DIGITALOCEAN"),
+ requires(providers.CanUseCAA),
// Test support of ";" as a value
tc("CAA many records", caa("@", "issuewild", 0, ";")),
),
@@ -1077,7 +1076,7 @@ func makeTests(t *testing.T) []*TestGroup {
),
// ClouDNS provider can work with PTR records, but you need to create special type of zone
- testgroup("PTR", requires(providers.CanUsePTR), not("ACTIVEDIRECTORY_PS", "CLOUDNS"),
+ testgroup("PTR", requires(providers.CanUsePTR), not("CLOUDNS"),
tc("Create PTR record", ptr("4", "foo.com.")),
tc("Modify PTR record", ptr("4", "bar.com.")),
),
@@ -1094,7 +1093,7 @@ func makeTests(t *testing.T) []*TestGroup {
tc("Modify SOA minttl", soa("@", "mmm.ns.cloudflare.com.", "eee.cloudflare.com.", 2037190000, 10001, 2401, 604801, 3601)),
),
- testgroup("SRV", requires(providers.CanUseSRV), not("ACTIVEDIRECTORY_PS"),
+ testgroup("SRV", requires(providers.CanUseSRV),
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.")),
tc("3 SRV", srv("_sip._tcp", 5, 6, 7, "foo.com."), srv("_sip._tcp", 5, 60, 70, "foo2.com."), srv("_sip._tcp", 15, 65, 75, "foo3.com.")),
diff --git a/integrationTest/providers.json b/integrationTest/providers.json
index 2599cc259..9c1fd672e 100644
--- a/integrationTest/providers.json
+++ b/integrationTest/providers.json
@@ -1,8 +1,4 @@
{
- "ACTIVEDIRECTORY_PS": {
- "ADServer": "$AD_SERVER",
- "domain": "$AD_DOMAIN"
- },
"AUTODNS": {
"username": "$AUTODNS_USERNAME",
"password": "$AUTODNS_PASSWORD",
diff --git a/pkg/diff/diff.go b/pkg/diff/diff.go
index 2b6066393..c6d515cbe 100644
--- a/pkg/diff/diff.go
+++ b/pkg/diff/diff.go
@@ -53,36 +53,15 @@ type differ struct {
// get normalized content for record. target, ttl, mxprio, and specified metadata
func (d *differ) content(r *models.RecordConfig) string {
- // NB(tlim): This function will eventually be replaced by calling
- // r.GetTargetDiffable(). In the meanwhile, this function compares
- // its output with r.GetTargetDiffable() to make sure the same
- // results are generated. Once we have confidence, this function will go away.
- content := fmt.Sprintf("%v ttl=%d", r.GetTargetCombined(), r.TTL)
- if r.Type == "SOA" {
- content = fmt.Sprintf("%s %v %d %d %d %d ttl=%d", r.GetTargetField(), r.SoaMbox, r.SoaRefresh, r.SoaRetry, r.SoaExpire, r.SoaMinttl, r.TTL) // SoaSerial is not used in comparison
- }
+
+ // get the extra values maps to add to the comparison.
var allMaps []map[string]string
for _, f := range d.extraValues {
- // sort the extra values map keys to perform a deterministic
- // comparison since Golang maps iteration order is not guaranteed
valueMap := f(r)
allMaps = append(allMaps, valueMap)
- keys := make([]string, 0)
- for k := range valueMap {
- keys = append(keys, k)
- }
- sort.Strings(keys)
- for _, k := range keys {
- v := valueMap[k]
- content += fmt.Sprintf(" %s=%s", k, v)
- }
}
- control := r.ToDiffable(allMaps...)
- if control != content {
- fmt.Printf("CONTROL=%q CONTENT=%q\n", control, content)
- panic("OOPS! control != content")
- }
- return content
+
+ return r.ToDiffable(allMaps...)
}
func apexException(rec *models.RecordConfig) bool {
diff --git a/pkg/normalize/validate.go b/pkg/normalize/validate.go
index f3c1f6a5a..0d27e13b3 100644
--- a/pkg/normalize/validate.go
+++ b/pkg/normalize/validate.go
@@ -485,8 +485,10 @@ func ValidateAndNormalizeConfig(config *models.DNSConfig) (errs []error) {
// be performed.
continue
}
- if err := providers.AuditRecords(provider.ProviderBase.ProviderType, domain.Records); err != nil {
- errs = append(errs, fmt.Errorf("%s rejects domain %s: %w", provider.ProviderBase.ProviderType, domain.Name, err))
+ if es := providers.AuditRecords(provider.ProviderBase.ProviderType, domain.Records); len(es) != 0 {
+ for _, e := range es {
+ errs = append(errs, fmt.Errorf("%s rejects domain %s: %w", provider.ProviderBase.ProviderType, domain.Name, e))
+ }
}
}
}
diff --git a/pkg/prettyzone/prettyzone.go b/pkg/prettyzone/prettyzone.go
index 3fcc41399..0f6afb682 100644
--- a/pkg/prettyzone/prettyzone.go
+++ b/pkg/prettyzone/prettyzone.go
@@ -153,6 +153,7 @@ func (z *ZoneGenData) generateZoneFileHelper(w io.Writer) error {
return nil
}
+// FormatLine formats a zonefile line.
func FormatLine(lengths []int, fields []string) string {
c := 0
result := ""
diff --git a/pkg/prettyzone/prettyzone_test.go b/pkg/prettyzone/prettyzone_test.go
index 7f598ce07..e2cc9c1ad 100644
--- a/pkg/prettyzone/prettyzone_test.go
+++ b/pkg/prettyzone/prettyzone_test.go
@@ -411,7 +411,7 @@ zt.mup IN A 1.2.3.14
zap IN A 1.2.3.15
`
-// func formatLine
+// func FormatLine
func TestFormatLine(t *testing.T) {
tests := []struct {
diff --git a/pkg/recordaudit/txt.go b/pkg/recordaudit/txt.go
deleted file mode 100644
index d38783d6a..000000000
--- a/pkg/recordaudit/txt.go
+++ /dev/null
@@ -1,159 +0,0 @@
-package recordaudit
-
-import (
- "fmt"
- "strings"
-
- "github.com/StackExchange/dnscontrol/v3/models"
-)
-
-// Keep these in alphabetical order.
-
-// TxtNoBackticks audits TXT records for strings that contain backticks.
-func TxtNoBackticks(records []*models.RecordConfig) error {
- for _, rc := range records {
-
- if rc.HasFormatIdenticalToTXT() {
- for _, txt := range rc.TxtStrings {
- if strings.Contains(txt, "`") {
- return fmt.Errorf("txtstring contains backtick")
- }
- }
- }
-
- }
- return nil
-}
-
-// TxtNoSingleQuotes audits TXT records for strings that contain single-quotes.
-func TxtNoSingleQuotes(records []*models.RecordConfig) error {
- for _, rc := range records {
-
- if rc.HasFormatIdenticalToTXT() {
- for _, txt := range rc.TxtStrings {
- if strings.Contains(txt, "'") {
- return fmt.Errorf("txtstring contains single-quotes")
- }
- }
- }
-
- }
- return nil
-}
-
-// TxtNoDoubleQuotes audits TXT records for strings that contain doublequotes.
-func TxtNoDoubleQuotes(records []*models.RecordConfig) error {
- for _, rc := range records {
-
- if rc.HasFormatIdenticalToTXT() {
- for _, txt := range rc.TxtStrings {
- if strings.Contains(txt, `"`) {
- return fmt.Errorf("txtstring contains doublequotes")
- }
- }
- }
-
- }
- return nil
-}
-
-// TxtNoStringsExactlyLen255 audits TXT records for strings exactly 255 octets long.
-// This is rare; you probably want to use TxtNoLongStrings() instead.
-func TxtNoStringsExactlyLen255(records []*models.RecordConfig) error {
- for _, rc := range records {
-
- if rc.HasFormatIdenticalToTXT() { // TXT and similar:
- for _, txt := range rc.TxtStrings {
- if len(txt) == 255 {
- return fmt.Errorf("txtstring length is 255")
- }
- }
- }
-
- }
- return nil
-}
-
-// TxtNoStringsLen256orLonger audits TXT records for strings that are >255 octets.
-func TxtNoStringsLen256orLonger(records []*models.RecordConfig) error {
- for _, rc := range records {
-
- if rc.HasFormatIdenticalToTXT() { // TXT and similar:
- for _, txt := range rc.TxtStrings {
- if len(txt) > 255 {
- return fmt.Errorf("%q txtstring length > 255", rc.GetLabel())
- }
- }
- }
-
- }
-
- return nil
-}
-
-// TxtNoMultipleStrings audits TXT records for multiple strings
-func TxtNoMultipleStrings(records []*models.RecordConfig) error {
- for _, rc := range records {
-
- if rc.HasFormatIdenticalToTXT() { // TXT and similar:
- if len(rc.TxtStrings) > 1 {
- return fmt.Errorf("multiple strings in one txt")
- }
- }
-
- }
- return nil
-}
-
-// TxtNoTrailingSpace audits TXT records for strings that end with space.
-func TxtNoTrailingSpace(records []*models.RecordConfig) error {
- for _, rc := range records {
-
- if rc.HasFormatIdenticalToTXT() { // TXT and similar:
- for _, txt := range rc.TxtStrings {
- if txt != "" && txt[ultimate(txt)] == ' ' {
- return fmt.Errorf("txtstring ends with space")
- }
- }
- }
-
- }
- return nil
-}
-
-// TxtNotEmpty audits TXT records for empty strings.
-func TxtNotEmpty(records []*models.RecordConfig) error {
- for _, rc := range records {
-
- if rc.HasFormatIdenticalToTXT() { // TXT and similar:
- // There must be strings.
- if len(rc.TxtStrings) == 0 {
- return fmt.Errorf("txt with no strings")
- }
- // Each string must be non-empty.
- for _, txt := range rc.TxtStrings {
- if len(txt) == 0 {
- return fmt.Errorf("txtstring is empty")
- }
- }
- }
-
- }
- return nil
-}
-
-// TxtNoUnpairedDoubleQuotes audits TXT records for strings that contain unpaired doublequotes.
-func TxtNoUnpairedDoubleQuotes(records []*models.RecordConfig) error {
- for _, rc := range records {
-
- if rc.HasFormatIdenticalToTXT() {
- for _, txt := range rc.TxtStrings {
- if strings.Count(txt, `"`)%2 == 1 {
- return fmt.Errorf("txtstring contains unpaired doublequotes")
- }
- }
- }
-
- }
- return nil
-}
diff --git a/pkg/rejectif/audit.go b/pkg/rejectif/audit.go
new file mode 100644
index 000000000..7cf494db0
--- /dev/null
+++ b/pkg/rejectif/audit.go
@@ -0,0 +1,46 @@
+package rejectif
+
+import (
+ "github.com/StackExchange/dnscontrol/v3/models"
+)
+
+// Auditor stores a list of checks to be executed during Audit().
+type Auditor struct {
+ checksFor map[string][]checker
+}
+
+type checker = func(*models.RecordConfig) error
+
+// Add registers a function to call on each record of a given type.
+func (aud *Auditor) Add(rtype string, fn checker) {
+ if aud.checksFor == nil {
+ aud.checksFor = map[string][]checker{}
+ }
+ aud.checksFor[rtype] = append(aud.checksFor[rtype], fn)
+
+ // SPF records get any checkers that TXT records do.
+ if rtype == "TXT" {
+ aud.Add("SPF", fn)
+ }
+}
+
+// Audit performs the audit. For each record it calls each function in
+// the list of checks.
+func (aud *Auditor) Audit(records models.Records) (errs []error) {
+ // No checks? Exit early.
+ if aud.checksFor == nil {
+ return nil
+ }
+
+ // For each record, call the checks for that type, gather errors.
+ for _, rc := range records {
+ for _, f := range aud.checksFor[rc.Type] {
+ e := f(rc)
+ if e != nil {
+ errs = append(errs, e)
+ }
+ }
+ }
+
+ return errs
+}
diff --git a/pkg/rejectif/caa.go b/pkg/rejectif/caa.go
new file mode 100644
index 000000000..0f886997d
--- /dev/null
+++ b/pkg/rejectif/caa.go
@@ -0,0 +1,18 @@
+package rejectif
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/StackExchange/dnscontrol/v3/models"
+)
+
+// Keep these in alphabetical order.
+
+// CaaTargetHasSemicolon audits CAA records for issues that contain semicolons.
+func CaaTargetHasSemicolon(rc *models.RecordConfig) error {
+ if strings.Contains(rc.GetTargetField(), ";") {
+ return fmt.Errorf("caa target contains semicolon")
+ }
+ return nil
+}
diff --git a/pkg/rejectif/txt.go b/pkg/rejectif/txt.go
new file mode 100644
index 000000000..9c1d2dccc
--- /dev/null
+++ b/pkg/rejectif/txt.go
@@ -0,0 +1,104 @@
+package rejectif
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/StackExchange/dnscontrol/v3/models"
+)
+
+// Keep these in alphabetical order.
+
+// TxtHasBackticks audits TXT records for strings that contain backticks.
+func TxtHasBackticks(rc *models.RecordConfig) error {
+ for _, txt := range rc.TxtStrings {
+ if strings.Contains(txt, "`") {
+ return fmt.Errorf("txtstring contains backtick")
+ }
+ }
+ return nil
+}
+
+// TxtHasSingleQuotes audits TXT records for strings that contain single-quotes.
+func TxtHasSingleQuotes(rc *models.RecordConfig) error {
+ for _, txt := range rc.TxtStrings {
+ if strings.Contains(txt, "'") {
+ return fmt.Errorf("txtstring contains single-quotes")
+ }
+ }
+ return nil
+}
+
+// TxtHasDoubleQuotes audits TXT records for strings that contain doublequotes.
+func TxtHasDoubleQuotes(rc *models.RecordConfig) error {
+ for _, txt := range rc.TxtStrings {
+ if strings.Contains(txt, `"`) {
+ return fmt.Errorf("txtstring contains doublequotes")
+ }
+ }
+ return nil
+}
+
+// TxtIsExactlyLen255 audits TXT records for strings exactly 255 octets long.
+// This is rare; you probably want to use TxtNoStringsLen256orLonger() instead.
+func TxtIsExactlyLen255(rc *models.RecordConfig) error {
+ for _, txt := range rc.TxtStrings {
+ if len(txt) == 255 {
+ return fmt.Errorf("txtstring length is 255")
+ }
+ }
+ return nil
+}
+
+// TxtHasSegmentLen256orLonger audits TXT records for strings that are >255 octets.
+func TxtHasSegmentLen256orLonger(rc *models.RecordConfig) error {
+ for _, txt := range rc.TxtStrings {
+ if len(txt) > 255 {
+ return fmt.Errorf("%q txtstring length > 255", rc.GetLabel())
+ }
+ }
+ return nil
+}
+
+// TxtHasMultipleSegments audits TXT records for multiple strings
+func TxtHasMultipleSegments(rc *models.RecordConfig) error {
+ if len(rc.TxtStrings) > 1 {
+ return fmt.Errorf("multiple strings in one txt")
+ }
+ return nil
+}
+
+// TxtHasTrailingSpace audits TXT records for strings that end with space.
+func TxtHasTrailingSpace(rc *models.RecordConfig) error {
+ for _, txt := range rc.TxtStrings {
+ if txt != "" && txt[ultimate(txt)] == ' ' {
+ return fmt.Errorf("txtstring ends with space")
+ }
+ }
+ return nil
+}
+
+// TxtIsEmpty audits TXT records for empty strings.
+func TxtIsEmpty(rc *models.RecordConfig) error {
+ // There must be strings.
+ if len(rc.TxtStrings) == 0 {
+ return fmt.Errorf("txt with no strings")
+ }
+ // Each string must be non-empty.
+ for _, txt := range rc.TxtStrings {
+ if len(txt) == 0 {
+ return fmt.Errorf("txtstring is empty")
+ }
+ }
+ return nil
+}
+
+// TxtHasUnpairedDoubleQuotes audits TXT records for strings that contain unpaired doublequotes.
+func TxtHasUnpairedDoubleQuotes(rc *models.RecordConfig) error {
+ for _, txt := range rc.TxtStrings {
+ if strings.Count(txt, `"`)%2 == 1 {
+ return fmt.Errorf("txtstring contains unpaired doublequotes")
+ }
+ }
+ return nil
+}
diff --git a/pkg/recordaudit/ultimate.go b/pkg/rejectif/ultimate.go
similarity index 96%
rename from pkg/recordaudit/ultimate.go
rename to pkg/rejectif/ultimate.go
index 54fcdcde6..791afa470 100644
--- a/pkg/recordaudit/ultimate.go
+++ b/pkg/rejectif/ultimate.go
@@ -1,4 +1,4 @@
-package recordaudit
+package rejectif
/*
I proposed that Go add something like "len()" that returns the highest
diff --git a/providers/_all/all.go b/providers/_all/all.go
index c6c2fb67f..206bf006d 100644
--- a/providers/_all/all.go
+++ b/providers/_all/all.go
@@ -3,7 +3,6 @@ package all
import (
// Define all known providers here. They should each register themselves with the providers package via init function.
- _ "github.com/StackExchange/dnscontrol/v3/providers/activedir"
_ "github.com/StackExchange/dnscontrol/v3/providers/akamaiedgedns"
_ "github.com/StackExchange/dnscontrol/v3/providers/autodns"
_ "github.com/StackExchange/dnscontrol/v3/providers/axfrddns"
diff --git a/providers/activedir/activedirProvider.go b/providers/activedir/activedirProvider.go
deleted file mode 100644
index 4f6157118..000000000
--- a/providers/activedir/activedirProvider.go
+++ /dev/null
@@ -1,73 +0,0 @@
-package activedir
-
-import (
- "encoding/json"
- "fmt"
- "github.com/StackExchange/dnscontrol/v3/pkg/printer"
- "runtime"
-
- "github.com/StackExchange/dnscontrol/v3/providers"
-)
-
-// This is the struct that matches either (or both) of the Registrar and/or DNSProvider interfaces:
-type activedirProvider struct {
- adServer string
- fake bool
- psOut string
- psLog string
-}
-
-var features = providers.DocumentationNotes{
- providers.CanGetZones: providers.Unimplemented(),
- providers.CanUseAlias: 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() {
- fns := providers.DspFuncs{
- Initializer: newDNS,
- RecordAuditor: AuditRecords,
- }
- providers.RegisterDomainServiceProviderType("ACTIVEDIRECTORY_PS", fns, features)
-}
-
-func newDNS(config map[string]string, metadata json.RawMessage) (providers.DNSServiceProvider, error) {
- printer.Printf("WARNING: ACTIVEDIRECTORY_PS provider is being replaced by MSDNS. Please convert. Details in https://stackexchange.github.io/dnscontrol/providers/msdns\n")
-
- fake := false
- if fVal := config["fakeps"]; fVal == "true" {
- fake = true
- } else if fVal != "" && fVal != "false" {
- return nil, fmt.Errorf("fakeps value must be 'true' or 'false'")
- }
-
- psOut, psLog := config["psout"], config["pslog"]
- if psOut == "" {
- psOut = "dns_update_commands.ps1"
- }
- if psLog == "" {
- psLog = "powershell.log"
- }
-
- p := &activedirProvider{psLog: psLog, psOut: psOut, fake: fake}
- if fake {
- return p, nil
- }
- if runtime.GOOS == "windows" {
- srv := config["ADServer"]
- if srv == "" {
- return nil, fmt.Errorf("ADServer required for Active Directory provider")
- }
- p.adServer = srv
- return p, nil
- }
- printer.Printf("WARNING: PowerShell not available. Active Directory will not be updated.\n")
- return providers.None{}, nil
-}
diff --git a/providers/activedir/adzonedump.test2.json b/providers/activedir/adzonedump.test2.json
deleted file mode 100755
index e3796a285..000000000
Binary files a/providers/activedir/adzonedump.test2.json and /dev/null differ
diff --git a/providers/activedir/auditrecords.go b/providers/activedir/auditrecords.go
deleted file mode 100644
index 85fe206ff..000000000
--- a/providers/activedir/auditrecords.go
+++ /dev/null
@@ -1,11 +0,0 @@
-package activedir
-
-import (
- "github.com/StackExchange/dnscontrol/v3/models"
-)
-
-// AuditRecords returns an error if any records are not
-// supportable by this provider.
-func AuditRecords(records []*models.RecordConfig) error {
- return nil
-}
diff --git a/providers/activedir/doc.md b/providers/activedir/doc.md
deleted file mode 100644
index c13809cf3..000000000
--- a/providers/activedir/doc.md
+++ /dev/null
@@ -1,78 +0,0 @@
-### Active Directory
-
-This provider updates a DNS Zone in an Active Directory Integrated Zone.
-
-When run on Windows, AD is updated directly. The code generates
-PowerShell commands, executes them, and checks the results.
-It leaves behind a log file of the commands that were generated.
-
-When run on non-Windows, AD isn't updated because we can't execute
-PowerShell at this time. Instead of reading the existing zone data
-from AD, It learns what
-records are in the zone by reading
-`adzonedump.{ZONENAME}.json`, a file that must be created beforehand.
-It does not actually update AD, it generates a file with PowerShell
-commands that would do the updates, which you must execute afterwords.
-If the `adzonedump.{ZONENAME}.json` does not exist, the zone is quietly skipped.
-
-Not implemented:
-
-* Delete records. This provider will not delete any records. It will only add
-and change existing records. See "Note to future devs" below.
-* Update TTLs. It ignores TTLs.
-
-
-## required creds.json config
-
-No "creds.json" configuration is expected.
-
-## example dns config js:
-
-```js
-var REG_NONE = NewRegistrar('none', 'NONE')
-var DSP_ACTIVEDIRECTORY_DS = NewDSP("activedir", "ACTIVEDIRECTORY_PS");
-
-D('ds.stackexchange.com', REG_NONE,
- DSP_ACTIVEDIRECTORY_DS,
-)
-
-
- // records handled by another provider...
-);
-```
-
-## Special Windows stuff
-
-This provider needs to do 2 things:
-
-* Get a list of zone records:
- * powerShellDump: Runs a PS command that dumps the zone to JSON.
- * readZoneDump: Opens a adzonedump.$DOMAINNAME.json file and reads JSON out of it. If the file does not exist, this is considered an error and processing stops.
-
-* Update records:
- * powerShellExec: Execute PS commands that do the update.
- * powerShellRecord: Record the PS command that can be run later to do the updates. This file is -psout=dns_update_commands.ps1
-
-So what happens when? Well, that's complex. We want both Windows and Linux to be able to use -fakewindows
-for either debugging or (on Windows) actual use. However only Windows permits -fakewinows=false and actually executes
-the PS code. Here's which algorithm is used for each case:
-
- * If -fakewindows is used on any system: readZoneDump and powerShellRecord is used.
- * On Windows (without -fakewindows): powerShellDump and powerShellExec is used.
- * On Linux (wihtout -fakewindows): the provider loads as "NONE" and nothing happens.
-
-
-## Note to future devs
-
-### Why doesn't this provider delete records?
-
-Because at this time Stack doesn't fully control AD zones
-using dnscontrol. It only needs to add/change records.
-
-What should we do when it does need to delete them?
-
-Currently NO_PURGE is a no-op. I would change it to update
-domain metadata to flag that deletes should be enabled/disabled.
-Then generate the deletes only if this flag exists. To be paranoid,
-the func that does the deleting could check this flag to make sure
-that it really should be deleting something.
diff --git a/providers/activedir/domains.go b/providers/activedir/domains.go
deleted file mode 100644
index 20f3203fb..000000000
--- a/providers/activedir/domains.go
+++ /dev/null
@@ -1,358 +0,0 @@
-package activedir
-
-import (
- "encoding/json"
- "fmt"
- "os"
- "strings"
- "time"
-
- "github.com/StackExchange/dnscontrol/v3/models"
- "github.com/StackExchange/dnscontrol/v3/pkg/diff"
- "github.com/StackExchange/dnscontrol/v3/pkg/printer"
- "github.com/TomOnTime/utfutil"
-)
-
-const zoneDumpFilenamePrefix = "adzonedump"
-
-// RecordConfigJSON RecordConfig, reconfigured for JSON input/output.
-type RecordConfigJSON struct {
- Name string `json:"hostname"`
- Type string `json:"recordtype"`
- Data string `json:"recorddata"`
- TTL uint32 `json:"timetolive"`
-}
-
-func (c *activedirProvider) GetNameservers(string) ([]*models.Nameserver, error) {
- // TODO: If using AD for publicly hosted zones, probably pull these from config.
- return nil, nil
-}
-
-// list of types this provider supports.
-// until it is up to speed with all the built-in types.
-var supportedTypes = map[string]bool{
- "A": true,
- "AAAA": true,
- "CNAME": true,
- "NS": true,
-}
-
-// GetZoneRecords gets the records of a zone and returns them in RecordConfig format.
-func (c *activedirProvider) GetZoneRecords(domain string) (models.Records, error) {
- foundRecords, err := c.getExistingRecords(domain)
- if err != nil {
- return nil, fmt.Errorf("c.getExistingRecords(%q) failed: %v", domain, err)
- }
- return foundRecords, nil
-}
-
-// GetDomainCorrections gets existing records, diffs them against existing, and returns corrections.
-func (c *activedirProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
-
- dc.Filter(func(r *models.RecordConfig) bool {
- if r.Type == "NS" && r.Name == "@" {
- return false
- }
- if !supportedTypes[r.Type] {
- printer.Warnf("Active Directory only manages certain record types. Won't consider %s %s\n", r.Type, r.GetLabelFQDN())
- return false
- }
- return true
- })
-
- // Read foundRecords:
- foundRecords, err := c.getExistingRecords(dc.Name)
- if err != nil {
- return nil, fmt.Errorf("c.getExistingRecords(%v) failed: %v", dc.Name, err)
- }
-
- // Normalize
- models.PostProcessRecords(foundRecords)
-
- differ := diff.New(dc)
- _, creates, dels, modifications, err := differ.IncrementalDiff(foundRecords)
- if err != nil {
- return nil, err
- }
- // NOTE(tlim): This provider does not delete records. If
- // you need to delete a record, either delete it manually
- // or see providers/activedir/doc.md for implementation tips.
-
- // Generate changes.
- corrections := []*models.Correction{}
- for _, del := range dels {
- corrections = append(corrections, c.deleteRec(dc.Name, del))
- }
- for _, cre := range creates {
- corrections = append(corrections, c.createRec(dc.Name, cre)...)
- }
- for _, m := range modifications {
- corrections = append(corrections, c.modifyRec(dc.Name, m))
- }
- return corrections, nil
-
-}
-
-// zoneDumpFilename returns the filename to use to write or read
-// an activedirectory zone dump for a particular domain.
-func zoneDumpFilename(domainname string) string {
- return zoneDumpFilenamePrefix + "." + domainname + ".json"
-}
-
-// readZoneDump reads a pre-existing zone dump from adzonedump.*.json.
-func (c *activedirProvider) readZoneDump(domainname string) ([]byte, error) {
- // File not found is considered an error.
- dat, err := utfutil.ReadFile(zoneDumpFilename(domainname), utfutil.WINDOWS)
- if err != nil {
- printer.Printf("Powershell to generate zone dump:\n")
- printer.Printf("%v\n", c.generatePowerShellZoneDump(domainname))
- }
- return dat, err
-}
-
-// powerShellLogCommand logs to flagPsLog that a PowerShell command is going to be run.
-func (c *activedirProvider) logCommand(command string) error {
- return c.logHelper(fmt.Sprintf("# %s\r\n%s\r\n", time.Now().UTC(), strings.TrimSpace(command)))
-}
-
-// powerShellLogOutput logs to flagPsLog that a PowerShell command is going to be run.
-func (c *activedirProvider) logOutput(s string) error {
- return c.logHelper(fmt.Sprintf("OUTPUT: START\r\n%s\r\nOUTPUT: END\r\n", s))
-}
-
-// powerShellLogErr logs that a PowerShell command had an error.
-func (c *activedirProvider) logErr(e error) error {
- err := c.logHelper(fmt.Sprintf("ERROR: %v\r\r", e)) // Log error to powershell.log
- if err != nil {
- return err // Bubble up error created in logHelper
- }
- return e // Bubble up original error
-}
-
-func (c *activedirProvider) logHelper(s string) error {
- logfile, err := os.OpenFile(c.psLog, os.O_APPEND|os.O_RDWR|os.O_CREATE, 0660)
- if err != nil {
- return fmt.Errorf("error: Can not create/append to %#v: %v", c.psLog, err)
- }
- _, err = fmt.Fprintln(logfile, s)
- if err != nil {
- return fmt.Errorf("append to %#v failed: %v", c.psLog, err)
- }
- if logfile.Close() != nil {
- return fmt.Errorf("closing %#v failed: %v", c.psLog, err)
- }
- return nil
-}
-
-// powerShellRecord records that a PowerShell command should be executed later.
-func (c *activedirProvider) powerShellRecord(command string) error {
- recordfile, err := os.OpenFile(c.psOut, os.O_APPEND|os.O_RDWR|os.O_CREATE, 0660)
- if err != nil {
- return fmt.Errorf("can not create/append to %#v: %v", c.psOut, err)
- }
- _, err = recordfile.WriteString(command)
- if err != nil {
- return fmt.Errorf("append to %#v failed: %v", c.psOut, err)
- }
- return recordfile.Close()
-}
-
-func (c *activedirProvider) getExistingRecords(domainname string) ([]*models.RecordConfig, error) {
- // Get the JSON either from adzonedump or by running a PowerShell script.
- data, err := c.getRecords(domainname)
- if err != nil {
- return nil, fmt.Errorf("getRecords failed on %#v: %v", domainname, err)
- }
-
- var recs []*RecordConfigJSON
- jdata := string(data)
- // when there is only a single record, AD powershell does not
- // wrap it in an array as our types expect. This makes sure it is always an array.
- if strings.HasPrefix(strings.TrimSpace(jdata), "{") {
- jdata = "[" + jdata + "]"
- data = []byte(jdata)
- }
- err = json.Unmarshal(data, &recs)
- if err != nil {
- return nil, fmt.Errorf("json.Unmarshal failed on %#v: %v", domainname, err)
- }
-
- result := make([]*models.RecordConfig, 0, len(recs))
- unsupportedCounts := map[string]int{}
- for _, rec := range recs {
- t, supportedType := rec.unpackRecord(domainname)
- if !supportedType {
- unsupportedCounts[rec.Type]++
- }
- if t != nil {
- result = append(result, t)
- }
- }
- for t, count := range unsupportedCounts {
- printer.Warnf("%d records of type %s found in AD zone. These will be ignored.\n", count, t)
- }
-
- return result, nil
-}
-
-func (r *RecordConfigJSON) unpackRecord(origin string) (rc *models.RecordConfig, supported bool) {
- rc = &models.RecordConfig{
- Type: r.Type,
- TTL: r.TTL,
- }
- rc.SetLabel(r.Name, origin)
- switch rtype := rc.Type; rtype { // #rtype_variations
- case "A", "AAAA":
- rc.SetTarget(r.Data)
- case "CNAME":
- rc.SetTarget(strings.ToLower(r.Data))
- case "NS":
- // skip root NS
- if rc.Name == "@" {
- return nil, true
- }
- rc.SetTarget(strings.ToLower(r.Data))
- case "SOA":
- return nil, true
- default:
- return nil, false
- }
- return rc, true
-}
-
-// powerShellDump runs a PowerShell command to get a dump of all records in a DNS zone.
-func (c *activedirProvider) generatePowerShellZoneDump(domainname string) string {
- cmdTxt := `@("REPLACE_WITH_ZONE") | %{
-Get-DnsServerResourceRecord -ComputerName REPLACE_WITH_COMPUTER_NAME -ZoneName $_ | select hostname,recordtype,@{n="timestamp";e={$_.timestamp.tostring()}},@{n="timetolive";e={$_.timetolive.totalseconds}},@{n="recorddata";e={($_.recorddata.ipv4address,$_.recorddata.ipv6address,$_.recorddata.HostNameAlias,$_.recorddata.NameServer,"unsupported_record_type" -ne $null)[0]-as [string]}} | ConvertTo-Json > REPLACE_WITH_FILENAMEPREFIX.REPLACE_WITH_ZONE.json
-}`
- cmdTxt = strings.Replace(cmdTxt, "REPLACE_WITH_ZONE", domainname, -1)
- cmdTxt = strings.Replace(cmdTxt, "REPLACE_WITH_COMPUTER_NAME", c.adServer, -1)
- cmdTxt = strings.Replace(cmdTxt, "REPLACE_WITH_FILENAMEPREFIX", zoneDumpFilenamePrefix, -1)
- return cmdTxt
-}
-
-// generatePowerShellCreate generates PowerShell commands to ADD a record.
-func (c *activedirProvider) generatePowerShellCreate(domainname string, rec *models.RecordConfig) string {
- content := rec.GetTargetField()
- text := "\r\n" // Skip a line.
- funcSuffix := rec.Type
- if rec.Type == "NS" {
- funcSuffix = ""
- }
- text += fmt.Sprintf("Add-DnsServerResourceRecord%s", funcSuffix)
- text += fmt.Sprintf(` -ComputerName "%s"`, c.adServer)
- text += fmt.Sprintf(` -ZoneName "%s"`, domainname)
- text += fmt.Sprintf(` -Name "%s"`, rec.GetLabel())
- text += fmt.Sprintf(` -TimeToLive $(New-TimeSpan -Seconds %d)`, rec.TTL)
- switch rec.Type { // #rtype_variations
- case "CNAME":
- text += fmt.Sprintf(` -HostNameAlias "%s"`, content)
- case "A":
- text += fmt.Sprintf(` -IPv4Address "%s"`, content)
- case "NS":
- text += fmt.Sprintf(` -NS -NameServer "%s"`, content)
- default:
- panic(fmt.Errorf("generatePowerShellCreate() does not yet handle recType=%s recName=%#v content=%#v)",
- rec.Type, rec.GetLabel(), content))
- // We panic so that we quickly find any switch statements
- // that have not been updated for a new RR type.
- }
- text += "\r\n"
-
- return text
-}
-
-// generatePowerShellModify generates PowerShell commands to MODIFY a record.
-func (c *activedirProvider) generatePowerShellModify(domainname, recName, recType, oldContent, newContent string, oldTTL, newTTL uint32) string {
-
- var queryField, queryContent string
- queryContent = `"` + oldContent + `"`
-
- switch recType { // #rtype_variations
- case "A":
- queryField = "IPv4address"
- case "CNAME":
- queryField = "HostNameAlias"
- case "NS":
- queryField = "NameServer"
- default:
- panic(fmt.Errorf("generatePowerShellModify() does not yet handle recType=%s recName=%#v content=(%#v, %#v)", recType, recName, oldContent, newContent))
- // We panic so that we quickly find any switch statements
- // that have not been updated for a new RR type.
- }
-
- text := "\r\n" // Skip a line.
- text += fmt.Sprintf(`echo "MODIFY %s %s %s old=%s new=%s"`, recName, domainname, recType, oldContent, newContent)
- text += "\r\n"
-
- text += "$OldObj = Get-DnsServerResourceRecord"
- text += fmt.Sprintf(` -ComputerName "%s"`, c.adServer)
- text += fmt.Sprintf(` -ZoneName "%s"`, domainname)
- text += fmt.Sprintf(` -Name "%s"`, recName)
- text += fmt.Sprintf(` -RRType "%s"`, recType)
- text += fmt.Sprintf(" | Where-Object {$_.RecordData.%s -eq %s -and $_.HostName -eq \"%s\"}", queryField, queryContent, recName)
- text += "\r\n"
- text += `if($OldObj.Length -ne $null){ throw "Error, multiple results for Get-DnsServerResourceRecord" }`
- text += "\r\n"
-
- text += "$NewObj = $OldObj.Clone()"
- text += "\r\n"
-
- if oldContent != newContent {
- text += fmt.Sprintf(`$NewObj.RecordData.%s = "%s"`, queryField, newContent)
- text += "\r\n"
- }
-
- if oldTTL != newTTL {
- text += fmt.Sprintf(`$NewObj.TimeToLive = New-TimeSpan -Seconds %d`, newTTL)
- text += "\r\n"
- }
-
- text += "Set-DnsServerResourceRecord"
- text += fmt.Sprintf(` -ComputerName "%s"`, c.adServer)
- text += fmt.Sprintf(` -ZoneName "%s"`, domainname)
- text += ` -NewInputObject $NewObj -OldInputObject $OldObj`
- text += "\r\n"
-
- return text
-}
-
-func (c *activedirProvider) generatePowerShellDelete(domainname, recName, recType, content string) string {
- text := fmt.Sprintf(`echo "DELETE %s %s %s"`, recType, recName, content)
- text += "\r\n"
- text += `Remove-DnsServerResourceRecord -Force -ComputerName "%s" -ZoneName "%s" -Name "%s" -RRType "%s" -RecordData "%s"`
- text += "\r\n"
- return fmt.Sprintf(text, c.adServer, domainname, recName, recType, content)
-}
-
-func (c *activedirProvider) createRec(domainname string, cre diff.Correlation) []*models.Correction {
- rec := cre.Desired
- arr := []*models.Correction{
- {
- Msg: cre.String(),
- F: func() error {
- return c.powerShellDoCommand(c.generatePowerShellCreate(domainname, rec), true)
- }},
- }
- return arr
-}
-
-func (c *activedirProvider) modifyRec(domainname string, m diff.Correlation) *models.Correction {
- old, rec := m.Existing, m.Desired
- return &models.Correction{
- Msg: m.String(),
- F: func() error {
- return c.powerShellDoCommand(c.generatePowerShellModify(domainname, rec.GetLabel(), rec.Type, old.GetTargetField(), rec.GetTargetField(), old.TTL, rec.TTL), true)
- },
- }
-}
-
-func (c *activedirProvider) deleteRec(domainname string, cor diff.Correlation) *models.Correction {
- rec := cor.Existing
- return &models.Correction{
- Msg: cor.String(),
- F: func() error {
- return c.powerShellDoCommand(c.generatePowerShellDelete(domainname, rec.GetLabel(), rec.Type, rec.GetTargetField()), true)
- },
- }
-}
diff --git a/providers/activedir/domains_test.go b/providers/activedir/domains_test.go
deleted file mode 100644
index 8c54b28f5..000000000
--- a/providers/activedir/domains_test.go
+++ /dev/null
@@ -1,46 +0,0 @@
-package activedir
-
-import (
- "fmt"
- "testing"
-
- "github.com/StackExchange/dnscontrol/v3/models"
-)
-
-func makeRC(label, domain, target string, rc models.RecordConfig) *models.RecordConfig {
- rc.SetLabel(label, domain)
- rc.SetTarget(target)
- return &rc
-}
-
-func TestGetExistingRecords(t *testing.T) {
-
- cf := &activedirProvider{}
-
- cf.fake = true
- actual, err := cf.getExistingRecords("test2")
- if err != nil {
- t.Fatal(err)
- }
- expected := []*models.RecordConfig{
- makeRC("@", "test2", "10.166.2.11", models.RecordConfig{Type: "A", TTL: 600}),
- makeRC("_msdcs", "test2", "other_record", models.RecordConfig{Type: "NS", TTL: 300}),
- makeRC("co-devsearch02", "test2", "10.8.2.64", models.RecordConfig{Type: "A", TTL: 3600}),
- makeRC("co-devservice01", "test2", "10.8.2.48", models.RecordConfig{Type: "A", TTL: 1200}), // Downcased.
- makeRC("yum", "test2", "10.8.0.59", models.RecordConfig{Type: "A", TTL: 3600}),
- }
-
- actualS := ""
- for i, x := range actual {
- actualS += fmt.Sprintf("%d %v\n", i, x)
- }
-
- expectedS := ""
- for i, x := range expected {
- expectedS += fmt.Sprintf("%d %v\n", i, x)
- }
-
- if actualS != expectedS {
- t.Fatalf("got\n(%s)\nbut expected\n(%s)", actualS, expectedS)
- }
-}
diff --git a/providers/activedir/getzones_other.go b/providers/activedir/getzones_other.go
deleted file mode 100644
index 24a3b2dbe..000000000
--- a/providers/activedir/getzones_other.go
+++ /dev/null
@@ -1,18 +0,0 @@
-//go:build !windows
-// +build !windows
-
-package activedir
-
-func (c *activedirProvider) getRecords(domainname string) ([]byte, error) {
- if !c.fake {
- panic("Can not happen: PowerShell on non-windows")
- }
- return c.readZoneDump(domainname)
-}
-
-func (c *activedirProvider) powerShellDoCommand(command string, shouldLog bool) error {
- if !c.fake {
- panic("Can not happen: PowerShell on non-windows")
- }
- return c.powerShellRecord(command)
-}
diff --git a/providers/activedir/getzones_windows.go b/providers/activedir/getzones_windows.go
deleted file mode 100644
index c31be91e4..000000000
--- a/providers/activedir/getzones_windows.go
+++ /dev/null
@@ -1,86 +0,0 @@
-package activedir
-
-import (
- "fmt"
- "os/exec"
- "strconv"
- "strings"
- "sync"
-
- "github.com/StackExchange/dnscontrol/v3/pkg/printer"
-)
-
-var checkPS sync.Once
-var psAvailible = false
-
-func (c *activedirProvider) getRecords(domainname string) ([]byte, error) {
-
- // If we are using PowerShell, make sure it is enabled
- // and then run the PS1 command to generate the adzonedump file.
-
- if !c.fake {
- checkPS.Do(func() {
- psAvailible = c.isPowerShellReady()
- if !psAvailible {
- printer.Printf("\n\n\n")
- printer.Printf("***********************************************\n")
- printer.Printf("PowerShell DnsServer module not installed.\n")
- printer.Printf("See http://social.technet.microsoft.com/wiki/contents/articles/2202.remote-server-administration-tools-rsat-for-windows-client-and-windows-server-dsforum2wiki.aspx\n")
- printer.Printf("***********************************************\n")
- printer.Printf("\n\n\n")
- }
- })
- if !psAvailible {
- return nil, fmt.Errorf("powershell module DnsServer not installed")
- }
-
- _, err := c.powerShellExec(c.generatePowerShellZoneDump(domainname), true)
- if err != nil {
- return []byte{}, err
- }
- }
- // Return the contents of zone.*.json file instead.
- return c.readZoneDump(domainname)
-}
-
-func (c *activedirProvider) isPowerShellReady() bool {
- query, _ := c.powerShellExec(`(Get-Module -ListAvailable DnsServer) -ne $null`, true)
- q, err := strconv.ParseBool(strings.TrimSpace(string(query)))
- if err != nil {
- return false
- }
- return q
-}
-
-func (c *activedirProvider) powerShellDoCommand(command string, shouldLog bool) error {
- if c.fake {
- // If fake, just record the command.
- return c.powerShellRecord(command)
- }
- _, err := c.powerShellExec(command, shouldLog)
- return err
-}
-
-func (c *activedirProvider) powerShellExec(command string, shouldLog bool) ([]byte, error) {
- // log it.
- err := c.logCommand(command)
- if err != nil {
- return nil, err
- }
-
- // Run it.
- out, err := exec.Command("powershell", "-NoProfile", command).CombinedOutput()
- if err != nil {
- // If there was an error, log it.
- c.logErr(err)
- }
- if shouldLog {
- err = c.logOutput(string(out))
- if err != nil {
- return []byte{}, err
- }
- }
-
- // Return the result.
- return out, err
-}
diff --git a/providers/activedir/zone.testzone.json b/providers/activedir/zone.testzone.json
deleted file mode 100755
index 99ab33fbf..000000000
Binary files a/providers/activedir/zone.testzone.json and /dev/null differ
diff --git a/providers/akamaiedgedns/auditrecords.go b/providers/akamaiedgedns/auditrecords.go
index 4729c6d8f..5db57330d 100644
--- a/providers/akamaiedgedns/auditrecords.go
+++ b/providers/akamaiedgedns/auditrecords.go
@@ -2,7 +2,9 @@ package akamaiedgedns
import "github.com/StackExchange/dnscontrol/v3/models"
-// AuditRecords returns an error if any records are not supportable by this provider.
-func AuditRecords(records []*models.RecordConfig) error {
+// AuditRecords returns a list of errors corresponding to the records
+// that aren't supported by this provider. If all records are
+// supported, an empty list is returned.
+func AuditRecords(records []*models.RecordConfig) []error {
return nil
}
diff --git a/providers/autodns/api.go b/providers/autodns/api.go
index c6315ecf0..2b97c494c 100644
--- a/providers/autodns/api.go
+++ b/providers/autodns/api.go
@@ -12,6 +12,7 @@ import (
"github.com/StackExchange/dnscontrol/v3/models"
)
+// ZoneListFilter describes a JSON list filter.
type ZoneListFilter struct {
Key string `json:"key"`
Value string `json:"value"`
@@ -20,6 +21,7 @@ type ZoneListFilter struct {
Filter []*ZoneListFilter `json:"filters,omitempty"`
}
+// ZoneListRequest describes a JSON zone list request.
type ZoneListRequest struct {
Filter []*ZoneListFilter `json:"filters"`
}
@@ -27,11 +29,11 @@ type ZoneListRequest struct {
func (api *autoDnsProvider) request(method string, requestPath string, data interface{}) ([]byte, error) {
client := &http.Client{}
- requestUrl := api.baseURL
- requestUrl.Path = api.baseURL.Path + requestPath
+ requestURL := api.baseURL
+ requestURL.Path = api.baseURL.Path + requestPath
request := &http.Request{
- URL: &requestUrl,
+ URL: &requestURL,
Header: api.defaultHeaders,
Method: method,
}
@@ -50,7 +52,7 @@ func (api *autoDnsProvider) request(method string, requestPath string, data inte
responseText, _ := ioutil.ReadAll(response.Body)
if response.StatusCode != 200 {
- return nil, errors.New("Request to " + requestUrl.Path + " failed: " + string(responseText))
+ return nil, errors.New("Request to " + requestURL.Path + " failed: " + string(responseText))
}
return responseText, nil
diff --git a/providers/autodns/auditrecords.go b/providers/autodns/auditrecords.go
index 320cb526e..e114d1305 100644
--- a/providers/autodns/auditrecords.go
+++ b/providers/autodns/auditrecords.go
@@ -2,8 +2,9 @@ package autodns
import "github.com/StackExchange/dnscontrol/v3/models"
-// AuditRecords returns an error if any records are not
-// supportable by this provider.
-func AuditRecords(records []*models.RecordConfig) error {
+// AuditRecords returns a list of errors corresponding to the records
+// that aren't supported by this provider. If all records are
+// supported, an empty list is returned.
+func AuditRecords(records []*models.RecordConfig) []error {
return nil
}
diff --git a/providers/axfrddns/auditrecords.go b/providers/axfrddns/auditrecords.go
index 8232f277b..8b8b23a66 100644
--- a/providers/axfrddns/auditrecords.go
+++ b/providers/axfrddns/auditrecords.go
@@ -1,11 +1,10 @@
package axfrddns
-import (
- "github.com/StackExchange/dnscontrol/v3/models"
-)
+import "github.com/StackExchange/dnscontrol/v3/models"
-// AuditRecords returns an error if any records are not
-// supportable by this provider.
-func AuditRecords(records []*models.RecordConfig) error {
+// AuditRecords returns a list of errors corresponding to the records
+// that aren't supported by this provider. If all records are
+// supported, an empty list is returned.
+func AuditRecords(records []*models.RecordConfig) []error {
return nil
}
diff --git a/providers/azuredns/auditrecords.go b/providers/azuredns/auditrecords.go
index 46f946f2d..25a912470 100644
--- a/providers/azuredns/auditrecords.go
+++ b/providers/azuredns/auditrecords.go
@@ -1,11 +1,10 @@
package azuredns
-import (
- "github.com/StackExchange/dnscontrol/v3/models"
-)
+import "github.com/StackExchange/dnscontrol/v3/models"
-// AuditRecords returns an error if any records are not
-// supportable by this provider.
-func AuditRecords(records []*models.RecordConfig) error {
+// AuditRecords returns a list of errors corresponding to the records
+// that aren't supported by this provider. If all records are
+// supported, an empty list is returned.
+func AuditRecords(records []*models.RecordConfig) []error {
return nil
}
diff --git a/providers/bind/auditrecords.go b/providers/bind/auditrecords.go
index 3834ecfe6..1bbf39301 100644
--- a/providers/bind/auditrecords.go
+++ b/providers/bind/auditrecords.go
@@ -1,11 +1,10 @@
package bind
-import (
- "github.com/StackExchange/dnscontrol/v3/models"
-)
+import "github.com/StackExchange/dnscontrol/v3/models"
-// AuditRecords returns an error if any records are not
-// supportable by this provider.
-func AuditRecords(records []*models.RecordConfig) error {
+// AuditRecords returns a list of errors corresponding to the records
+// that aren't supported by this provider. If all records are
+// supported, an empty list is returned.
+func AuditRecords(records []*models.RecordConfig) []error {
return nil
}
diff --git a/providers/cloudflare/auditrecords.go b/providers/cloudflare/auditrecords.go
index 805e818fb..8feae2308 100644
--- a/providers/cloudflare/auditrecords.go
+++ b/providers/cloudflare/auditrecords.go
@@ -2,24 +2,20 @@ package cloudflare
import (
"github.com/StackExchange/dnscontrol/v3/models"
- "github.com/StackExchange/dnscontrol/v3/pkg/recordaudit"
+ "github.com/StackExchange/dnscontrol/v3/pkg/rejectif"
)
-// AuditRecords returns an error if any records are not
-// supportable by this provider.
-func AuditRecords(records []*models.RecordConfig) error {
+// AuditRecords returns a list of errors corresponding to the records
+// that aren't supported by this provider. If all records are
+// supported, an empty list is returned.
+func AuditRecords(records []*models.RecordConfig) []error {
+ a := rejectif.Auditor{}
- if err := recordaudit.TxtNoMultipleStrings(records); err != nil {
- return err
- } // Still needed as of 2022-06-18
+ a.Add("TXT", rejectif.TxtHasMultipleSegments) // Last verified 2022-06-18
- if err := recordaudit.TxtNoTrailingSpace(records); err != nil {
- return err
- } // Still needed as of 2022-06-18
+ a.Add("TXT", rejectif.TxtHasTrailingSpace) // Last verified 2022-06-18
- if err := recordaudit.TxtNotEmpty(records); err != nil {
- return err
- } // Still needed as of 2022-06-18
+ a.Add("TXT", rejectif.TxtIsEmpty) // Last verified 2022-06-18
- return nil
+ return a.Audit(records)
}
diff --git a/providers/cloudflare/rest.go b/providers/cloudflare/rest.go
index 7f8024a63..229087051 100644
--- a/providers/cloudflare/rest.go
+++ b/providers/cloudflare/rest.go
@@ -16,7 +16,7 @@ func (c *cloudflareProvider) fetchDomainList() error {
c.nameservers = map[string][]string{}
zones, err := c.cfClient.ListZones(context.Background())
if err != nil {
- return fmt.Errorf("failed fetching domain list from cloudflare: %s", err)
+ return fmt.Errorf("failed fetching domain list from cloudflare(%q): %s", c.cfClient.APIEmail, err)
}
for _, zone := range zones {
diff --git a/providers/cloudns/auditrecords.go b/providers/cloudns/auditrecords.go
index dd801e958..8c4b7c708 100644
--- a/providers/cloudns/auditrecords.go
+++ b/providers/cloudns/auditrecords.go
@@ -2,36 +2,24 @@ package cloudns
import (
"github.com/StackExchange/dnscontrol/v3/models"
- "github.com/StackExchange/dnscontrol/v3/pkg/recordaudit"
+ "github.com/StackExchange/dnscontrol/v3/pkg/rejectif"
)
-// AuditRecords returns an error if any records are not
-// supportable by this provider.
-func AuditRecords(records []*models.RecordConfig) error {
+// AuditRecords returns a list of errors corresponding to the records
+// that aren't supported by this provider. If all records are
+// supported, an empty list is returned.
+func AuditRecords(records []*models.RecordConfig) []error {
+ a := rejectif.Auditor{}
- if err := recordaudit.TxtNoBackticks(records); err != nil {
- return err
- }
- // Still needed as of 2021-03-01
+ a.Add("TXT", rejectif.TxtHasBackticks) // Last verified 2021-03-01
- if err := recordaudit.TxtNotEmpty(records); err != nil {
- return err
- }
- // Still needed as of 2021-03-01
+ a.Add("TXT", rejectif.TxtIsEmpty) // Last verified 2021-03-01
- if err := recordaudit.TxtNoTrailingSpace(records); err != nil {
- return err
- }
- // Still needed as of 2021-03-01
+ a.Add("TXT", rejectif.TxtHasTrailingSpace) // Last verified 2021-03-01
- if err := recordaudit.TxtNoDoubleQuotes(records); err != nil {
- return err
- }
- // Still needed as of 2021-03-11
+ a.Add("TXT", rejectif.TxtHasDoubleQuotes) // Last verified 2021-03-01
- if err := recordaudit.TxtNoMultipleStrings(records); err != nil {
- return err
- }
+ a.Add("TXT", rejectif.TxtHasMultipleSegments) // Last verified 2021-03-01
- return nil
+ return a.Audit(records)
}
diff --git a/providers/cscglobal/auditrecords.go b/providers/cscglobal/auditrecords.go
index 1cafa59df..c8a8d3b61 100644
--- a/providers/cscglobal/auditrecords.go
+++ b/providers/cscglobal/auditrecords.go
@@ -2,39 +2,34 @@ package cscglobal
import (
"github.com/StackExchange/dnscontrol/v3/models"
- "github.com/StackExchange/dnscontrol/v3/pkg/recordaudit"
+ "github.com/StackExchange/dnscontrol/v3/pkg/rejectif"
)
-// AuditRecords returns an error if any records are not
-// supportable by this provider.
-func AuditRecords(records []*models.RecordConfig) error {
+// AuditRecords returns a list of errors corresponding to the records
+// that aren't supported by this provider. If all records are
+// supported, an empty list is returned.
+func AuditRecords(records []*models.RecordConfig) []error {
+ a := rejectif.Auditor{}
- // Each test should be encapsulated in a function that can be tested
- // individually. If the test is of general use, add it to the
- // recordaudit module.
+ a.Add("TXT", rejectif.TxtHasMultipleSegments) // Last verified 2022-06-10
- // Each test should document the last time we verified the test was
- // still needed. Sometimes companies change their API.
+ a.Add("TXT", rejectif.TxtHasTrailingSpace) // Last verified 2022-06-10
- if err := recordaudit.TxtNoDoubleQuotes(records); err != nil {
- return err
- } // Needed as of 2022-08-08
+ a.Add("TXT", rejectif.TxtIsEmpty) // Last verified 2022-06-10
- // if err := recordaudit.TxtNoStringsLen256orLonger(records); err != nil {
- // return err
- // } // Needed as of 2022-06-10
+ a.Add("TXT", rejectif.TxtHasDoubleQuotes) // Last verified 2022-08-08
- if err := recordaudit.TxtNoMultipleStrings(records); err != nil {
- return err
- } // Needed as of 2022-06-10
-
- if err := recordaudit.TxtNoTrailingSpace(records); err != nil {
- return err
- } // Needed as of 2022-06-10
-
- if err := recordaudit.TxtNotEmpty(records); err != nil {
- return err
- } // Needed as of 2022-06-10
-
- return nil
+ return a.Audit(records)
}
+
+/* How To Write Providers:
+
+Each test should be encapsulated in a function that can be tested
+individually. If the test is of general use, add it to the
+rejectif module.
+
+The "Last verified" comment logs the last time we verified this
+test was needed. Sometimes companies change their API. Once a year,
+try removing tests one at a time to verify they are still needed.
+
+*/
diff --git a/providers/cscglobal/dns.go b/providers/cscglobal/dns.go
index 4adda69c0..846b04a4f 100644
--- a/providers/cscglobal/dns.go
+++ b/providers/cscglobal/dns.go
@@ -1,7 +1,6 @@
package cscglobal
import (
- "fmt"
"strings"
"github.com/StackExchange/dnscontrol/v3/models"
@@ -113,12 +112,6 @@ func PrepDesiredRecords(dc *models.DomainConfig) {
// GetDomainCorrections gets existing records, diffs them against existing, and returns corrections.
func (client *providerClient) GenerateDomainCorrections(dc *models.DomainConfig, foundRecords models.Records) ([]*models.Correction, error) {
- // Read foundRecords:
- foundRecords, err := client.GetZoneRecords(dc.Name)
- if err != nil {
- return nil, fmt.Errorf("c.GetDNSZoneRecords(%v) failed: %v", dc.Name, err)
- }
-
// Normalize
models.PostProcessRecords(foundRecords)
//txtutil.SplitSingleLongTxt(dc.Records) // Autosplit long TXT records
diff --git a/providers/desec/auditrecords.go b/providers/desec/auditrecords.go
index 91b77c59c..5ff25d863 100644
--- a/providers/desec/auditrecords.go
+++ b/providers/desec/auditrecords.go
@@ -4,8 +4,9 @@ import (
"github.com/StackExchange/dnscontrol/v3/models"
)
-// AuditRecords returns an error if any records are not
-// supportable by this provider.
-func AuditRecords(records []*models.RecordConfig) error {
+// AuditRecords returns a list of errors corresponding to the records
+// that aren't supported by this provider. If all records are
+// supported, an empty list is returned.
+func AuditRecords(records []*models.RecordConfig) []error {
return nil
}
diff --git a/providers/digitalocean/auditrecords.go b/providers/digitalocean/auditrecords.go
index b1d093024..9e700ae20 100644
--- a/providers/digitalocean/auditrecords.go
+++ b/providers/digitalocean/auditrecords.go
@@ -2,37 +2,32 @@ package digitalocean
import (
"fmt"
+
"github.com/StackExchange/dnscontrol/v3/models"
- "github.com/StackExchange/dnscontrol/v3/pkg/recordaudit"
+ "github.com/StackExchange/dnscontrol/v3/pkg/rejectif"
)
-// AuditRecords returns an error if any records are not
-// supportable by this provider.
-func AuditRecords(records []*models.RecordConfig) error {
+// AuditRecords returns a list of errors corresponding to the records
+// that aren't supported by this provider. If all records are
+// supported, an empty list is returned.
+func AuditRecords(records []*models.RecordConfig) []error {
+ a := rejectif.Auditor{}
- // TODO(tlim): Audit CAA records.
- // "Semicolons not supported in issue/issuewild fields.", "https://www.digitalocean.com/docs/networking/dns/how-to/create-caa-records"),
- // Users are warned about these limits in docs/_providers/digitalocean.md
+ a.Add("TXT", MaxLengthDO) // Last verified 2021-03-01
- if err := MaxLengthDO(records); err != nil {
- return err
- }
- // Still needed as of 2021-03-01
+ a.Add("CAA", rejectif.CaaTargetHasSemicolon) // Last verified 2021-03-01
+ a.Add("TXT", rejectif.TxtHasDoubleQuotes) // Last verified 2021-03-01
// Double-quotes not permitted in TXT strings. I have a hunch that
// this is due to a broken parser on the DO side.
- if err := recordaudit.TxtNoDoubleQuotes(records); err != nil {
- return err
- }
- // Still needed as of 2021-03-01
- return nil
+ return a.Audit(records)
}
-// MaxLengthDO returns and error if the strings are longer than
+// MaxLengthDO returns and error if the string is longer than
// permitted by DigitalOcean. Sadly their length limit is
// undocumented. This is a guess.
-func MaxLengthDO(records []*models.RecordConfig) error {
+func MaxLengthDO(rc *models.RecordConfig) error {
// The total length of all strings can't be longer than 512; and in
// reality must be shorter due to sloppy validation checks.
// https://github.com/StackExchange/dnscontrol/issues/370
@@ -47,14 +42,10 @@ func MaxLengthDO(records []*models.RecordConfig) error {
// In other words, they're doing the checking on the API protocol
// encoded data instead of on on the resulting TXT record. Sigh.
- for _, rc := range records {
-
- if rc.HasFormatIdenticalToTXT() { // TXT and similar:
- if len(rc.GetTargetField()) > 509 {
- return fmt.Errorf("encoded txt too long")
- }
- }
-
+ if len(rc.GetTargetField()) > 509 {
+ return fmt.Errorf("encoded txt too long")
}
+ // FIXME(tlim): Try replacing GetTargetField() with (2 + (3*len(rc.TxtStrings) - 1))
+
return nil
}
diff --git a/providers/dnsimple/auditrecords.go b/providers/dnsimple/auditrecords.go
index c10fa308b..029524b23 100644
--- a/providers/dnsimple/auditrecords.go
+++ b/providers/dnsimple/auditrecords.go
@@ -2,28 +2,23 @@ package dnsimple
import (
"github.com/StackExchange/dnscontrol/v3/models"
- "github.com/StackExchange/dnscontrol/v3/pkg/recordaudit"
+ "github.com/StackExchange/dnscontrol/v3/pkg/rejectif"
)
-// AuditRecords returns an error if any records are not
-// supportable by this provider.
-func AuditRecords(records []*models.RecordConfig) error {
+// AuditRecords returns a list of errors corresponding to the records
+// that aren't supported by this provider. If all records are
+// supported, an empty list is returned.
+func AuditRecords(records []*models.RecordConfig) []error {
+ a := rejectif.Auditor{}
+
+ a.Add("TXT", rejectif.TxtHasMultipleSegments) // Last verified 2022-07
//TODO(onlyhavecans) I think we can support multiple strings.
- if err := recordaudit.TxtNoMultipleStrings(records); err != nil {
- return err
- }
- if err := recordaudit.TxtNoTrailingSpace(records); err != nil {
- return err
- } // as of 2022-07
+ a.Add("TXT", rejectif.TxtHasTrailingSpace) // Last verified 2022-07
- if err := recordaudit.TxtNotEmpty(records); err != nil {
- return err
- } // as of 2022-07
+ a.Add("TXT", rejectif.TxtIsEmpty) // Last verified 2022-07
- if err := recordaudit.TxtNoUnpairedDoubleQuotes(records); err != nil {
- return err
- } // as of 2022-07
+ a.Add("TXT", rejectif.TxtHasUnpairedDoubleQuotes) // Last verified 2022-07
- return nil
+ return a.Audit(records)
}
diff --git a/providers/dnsmadeeasy/auditrecords.go b/providers/dnsmadeeasy/auditrecords.go
index 272e602ad..dafd1ad6c 100644
--- a/providers/dnsmadeeasy/auditrecords.go
+++ b/providers/dnsmadeeasy/auditrecords.go
@@ -2,16 +2,16 @@ package dnsmadeeasy
import (
"github.com/StackExchange/dnscontrol/v3/models"
- "github.com/StackExchange/dnscontrol/v3/pkg/recordaudit"
+ "github.com/StackExchange/dnscontrol/v3/pkg/rejectif"
)
-// AuditRecords returns an error if any records are not
-// supportable by this provider.
-func AuditRecords(records []*models.RecordConfig) error {
- if err := recordaudit.TxtNoDoubleQuotes(records); err != nil {
- return err
- }
- // Still needed as of 2021-03-11
+// AuditRecords returns a list of errors corresponding to the records
+// that aren't supported by this provider. If all records are
+// supported, an empty list is returned.
+func AuditRecords(records []*models.RecordConfig) []error {
+ a := rejectif.Auditor{}
- return nil
+ a.Add("TXT", rejectif.TxtHasDoubleQuotes) // Last verified 2021-03-11
+
+ return a.Audit(records)
}
diff --git a/providers/doh/auditrecords.go b/providers/doh/auditrecords.go
index 030da7bc5..12503c850 100644
--- a/providers/doh/auditrecords.go
+++ b/providers/doh/auditrecords.go
@@ -1,11 +1,10 @@
package doh
-import (
- "github.com/StackExchange/dnscontrol/v3/models"
-)
+import "github.com/StackExchange/dnscontrol/v3/models"
-// AuditRecords returns an error if any records are not
-// supportable by this provider.
-func AuditRecords(records []*models.RecordConfig) error {
+// AuditRecords returns a list of errors corresponding to the records
+// that aren't supported by this provider. If all records are
+// supported, an empty list is returned.
+func AuditRecords(records []*models.RecordConfig) []error {
return nil
}
diff --git a/providers/domainnameshop/auditrecords.go b/providers/domainnameshop/auditrecords.go
new file mode 100644
index 000000000..e3a9400ed
--- /dev/null
+++ b/providers/domainnameshop/auditrecords.go
@@ -0,0 +1,10 @@
+package domainnameshop
+
+import "github.com/StackExchange/dnscontrol/v3/models"
+
+// AuditRecords returns a list of errors corresponding to the records
+// that aren't supported by this provider. If all records are
+// supported, an empty list is returned.
+func AuditRecords(records []*models.RecordConfig) []error {
+ return nil
+}
diff --git a/providers/domainnameshop/domainnameshopProvider.go b/providers/domainnameshop/domainnameshopProvider.go
index 47e09bb99..1dc97d5d1 100644
--- a/providers/domainnameshop/domainnameshopProvider.go
+++ b/providers/domainnameshop/domainnameshopProvider.go
@@ -4,7 +4,6 @@ import (
"encoding/json"
"fmt"
- "github.com/StackExchange/dnscontrol/v3/models"
"github.com/StackExchange/dnscontrol/v3/providers"
)
@@ -46,7 +45,7 @@ var features = providers.DocumentationNotes{
func init() {
fns := providers.DspFuncs{
Initializer: newDomainNameShopProvider,
- RecordAuditor: auditRecords,
+ RecordAuditor: AuditRecords,
}
providers.RegisterDomainServiceProviderType("DOMAINNAMESHOP", fns, features)
@@ -69,10 +68,6 @@ func newDomainNameShopProvider(conf map[string]string, metadata json.RawMessage)
return api, nil
}
-func auditRecords(records []*models.RecordConfig) error {
- return nil
-}
-
type domainResponse struct {
ID int `json:"id"`
Domain string `json:"domain"`
diff --git a/providers/exoscale/auditrecords.go b/providers/exoscale/auditrecords.go
index c112e493d..a72cfa68a 100644
--- a/providers/exoscale/auditrecords.go
+++ b/providers/exoscale/auditrecords.go
@@ -1,11 +1,10 @@
package exoscale
-import (
- "github.com/StackExchange/dnscontrol/v3/models"
-)
+import "github.com/StackExchange/dnscontrol/v3/models"
-// AuditRecords returns an error if any records are not
-// supportable by this provider.
-func AuditRecords(records []*models.RecordConfig) error {
+// AuditRecords returns a list of errors corresponding to the records
+// that aren't supported by this provider. If all records are
+// supported, an empty list is returned.
+func AuditRecords(records []*models.RecordConfig) []error {
return nil
}
diff --git a/providers/gandiv5/auditrecords.go b/providers/gandiv5/auditrecords.go
index 90b195624..aea3da3fb 100644
--- a/providers/gandiv5/auditrecords.go
+++ b/providers/gandiv5/auditrecords.go
@@ -1,11 +1,10 @@
package gandiv5
-import (
- "github.com/StackExchange/dnscontrol/v3/models"
-)
+import "github.com/StackExchange/dnscontrol/v3/models"
-// AuditRecords returns an error if any records are not
-// supportable by this provider.
-func AuditRecords(records []*models.RecordConfig) error {
+// AuditRecords returns a list of errors corresponding to the records
+// that aren't supported by this provider. If all records are
+// supported, an empty list is returned.
+func AuditRecords(records []*models.RecordConfig) []error {
return nil
}
diff --git a/providers/gcloud/auditrecords.go b/providers/gcloud/auditrecords.go
index 891b914fc..36beace3a 100644
--- a/providers/gcloud/auditrecords.go
+++ b/providers/gcloud/auditrecords.go
@@ -1,11 +1,10 @@
package gcloud
-import (
- "github.com/StackExchange/dnscontrol/v3/models"
-)
+import "github.com/StackExchange/dnscontrol/v3/models"
-// AuditRecords returns an error if any records are not
-// supportable by this provider.
-func AuditRecords(records []*models.RecordConfig) error {
+// AuditRecords returns a list of errors corresponding to the records
+// that aren't supported by this provider. If all records are
+// supported, an empty list is returned.
+func AuditRecords(records []*models.RecordConfig) []error {
return nil
}
diff --git a/providers/hedns/auditrecords.go b/providers/hedns/auditrecords.go
index 800dff618..c567c8161 100644
--- a/providers/hedns/auditrecords.go
+++ b/providers/hedns/auditrecords.go
@@ -1,11 +1,10 @@
package hedns
-import (
- "github.com/StackExchange/dnscontrol/v3/models"
-)
+import "github.com/StackExchange/dnscontrol/v3/models"
-// AuditRecords returns an error if any records are not
-// supportable by this provider.
-func AuditRecords(records []*models.RecordConfig) error {
+// AuditRecords returns a list of errors corresponding to the records
+// that aren't supported by this provider. If all records are
+// supported, an empty list is returned.
+func AuditRecords(records []*models.RecordConfig) []error {
return nil
}
diff --git a/providers/hetzner/auditrecords.go b/providers/hetzner/auditrecords.go
index c7ace1249..c690cbd53 100644
--- a/providers/hetzner/auditrecords.go
+++ b/providers/hetzner/auditrecords.go
@@ -1,11 +1,10 @@
package hetzner
-import (
- "github.com/StackExchange/dnscontrol/v3/models"
-)
+import "github.com/StackExchange/dnscontrol/v3/models"
-// AuditRecords returns an error if any records are not
-// supportable by this provider.
-func AuditRecords(records []*models.RecordConfig) error {
+// AuditRecords returns a list of errors corresponding to the records
+// that aren't supported by this provider. If all records are
+// supported, an empty list is returned.
+func AuditRecords(records []*models.RecordConfig) []error {
return nil
}
diff --git a/providers/hexonet/auditrecords.go b/providers/hexonet/auditrecords.go
index 4d2a3eddd..0b79d1e27 100644
--- a/providers/hexonet/auditrecords.go
+++ b/providers/hexonet/auditrecords.go
@@ -2,17 +2,16 @@ package hexonet
import (
"github.com/StackExchange/dnscontrol/v3/models"
- "github.com/StackExchange/dnscontrol/v3/pkg/recordaudit"
+ "github.com/StackExchange/dnscontrol/v3/pkg/rejectif"
)
-// AuditRecords returns an error if any records are not
-// supportable by this provider.
-func AuditRecords(records []*models.RecordConfig) error {
+// AuditRecords returns a list of errors corresponding to the records
+// that aren't supported by this provider. If all records are
+// supported, an empty list is returned.
+func AuditRecords(records []*models.RecordConfig) []error {
+ a := rejectif.Auditor{}
- if err := recordaudit.TxtNotEmpty(records); err != nil {
- return err
- }
- // Still needed as of 2021-10-01
+ a.Add("TXT", rejectif.TxtIsEmpty) // Last verified 2021-10-01
- return nil
+ return a.Audit(records)
}
diff --git a/providers/hostingde/auditrecords.go b/providers/hostingde/auditrecords.go
index 91ef6c807..ca0adb5a1 100644
--- a/providers/hostingde/auditrecords.go
+++ b/providers/hostingde/auditrecords.go
@@ -1,11 +1,10 @@
package hostingde
-import (
- "github.com/StackExchange/dnscontrol/v3/models"
-)
+import "github.com/StackExchange/dnscontrol/v3/models"
-// AuditRecords returns an error if any records are not
-// supportable by this provider.
-func AuditRecords(records []*models.RecordConfig) error {
+// AuditRecords returns a list of errors corresponding to the records
+// that aren't supported by this provider. If all records are
+// supported, an empty list is returned.
+func AuditRecords(records []*models.RecordConfig) []error {
return nil
}
diff --git a/providers/internetbs/auditrecords.go b/providers/internetbs/auditrecords.go
index b48d32e7c..ab205a4ba 100644
--- a/providers/internetbs/auditrecords.go
+++ b/providers/internetbs/auditrecords.go
@@ -1,11 +1,10 @@
package internetbs
-import (
- "github.com/StackExchange/dnscontrol/v3/models"
-)
+import "github.com/StackExchange/dnscontrol/v3/models"
-// AuditRecords returns an error if any records are not
-// supportable by this provider.
-func AuditRecords(records []*models.RecordConfig) error {
+// AuditRecords returns a list of errors corresponding to the records
+// that aren't supported by this provider. If all records are
+// supported, an empty list is returned.
+func AuditRecords(records []*models.RecordConfig) []error {
return nil
}
diff --git a/providers/inwx/auditrecords.go b/providers/inwx/auditrecords.go
index 5ff053eb0..8b293385d 100644
--- a/providers/inwx/auditrecords.go
+++ b/providers/inwx/auditrecords.go
@@ -2,32 +2,22 @@ package inwx
import (
"github.com/StackExchange/dnscontrol/v3/models"
- "github.com/StackExchange/dnscontrol/v3/pkg/recordaudit"
+ "github.com/StackExchange/dnscontrol/v3/pkg/rejectif"
)
-// AuditRecords returns an error if any records are not
-// supportable by this provider.
-func AuditRecords(records []*models.RecordConfig) error {
+// AuditRecords returns a list of errors corresponding to the records
+// that aren't supported by this provider. If all records are
+// supported, an empty list is returned.
+func AuditRecords(records []*models.RecordConfig) []error {
+ a := rejectif.Auditor{}
- if err := recordaudit.TxtNoBackticks(records); err != nil {
- return err
- }
- // Still needed as of 2021-03-01
+ a.Add("TXT", rejectif.TxtHasBackticks) // Last verified 2021-03-01
- if err := recordaudit.TxtNoStringsExactlyLen255(records); err != nil {
- return err
- }
- // Still needed as of 2021-03-01
+ a.Add("TXT", rejectif.TxtIsExactlyLen255) // Last verified 2021-03-01
- if err := recordaudit.TxtNoTrailingSpace(records); err != nil {
- return err
- }
- // Still needed as of 2021-03-01
+ a.Add("TXT", rejectif.TxtHasTrailingSpace) // Last verified 2021-03-01
- if err := recordaudit.TxtNotEmpty(records); err != nil {
- return err
- }
- // Still needed as of 2021-03-01
+ a.Add("TXT", rejectif.TxtIsEmpty) // Last verified 2021-03-01
- return nil
+ return a.Audit(records)
}
diff --git a/providers/linode/auditrecords.go b/providers/linode/auditrecords.go
index b132e215f..0f386d2b1 100644
--- a/providers/linode/auditrecords.go
+++ b/providers/linode/auditrecords.go
@@ -1,11 +1,10 @@
package linode
-import (
- "github.com/StackExchange/dnscontrol/v3/models"
-)
+import "github.com/StackExchange/dnscontrol/v3/models"
-// AuditRecords returns an error if any records are not
-// supportable by this provider.
-func AuditRecords(records []*models.RecordConfig) error {
+// AuditRecords returns a list of errors corresponding to the records
+// that aren't supported by this provider. If all records are
+// supported, an empty list is returned.
+func AuditRecords(records []*models.RecordConfig) []error {
return nil
}
diff --git a/providers/msdns/auditrecords.go b/providers/msdns/auditrecords.go
index 2599597ec..fbe27a60f 100644
--- a/providers/msdns/auditrecords.go
+++ b/providers/msdns/auditrecords.go
@@ -2,41 +2,26 @@ package msdns
import (
"github.com/StackExchange/dnscontrol/v3/models"
- "github.com/StackExchange/dnscontrol/v3/pkg/recordaudit"
+ "github.com/StackExchange/dnscontrol/v3/pkg/rejectif"
)
-// AuditRecords returns an error if any records are not
-// supportable by this provider.
-func AuditRecords(records []*models.RecordConfig) error {
+// AuditRecords returns a list of errors corresponding to the records
+// that aren't supported by this provider. If all records are
+// supported, an empty list is returned.
+func AuditRecords(records []*models.RecordConfig) []error {
+ a := rejectif.Auditor{}
- if err := recordaudit.TxtNoMultipleStrings(records); err != nil {
- return err
- }
- // Still needed as of 2021-03-01
+ a.Add("TXT", rejectif.TxtHasBackticks) // Last verified 2021-03-01
- if err := recordaudit.TxtNoStringsLen256orLonger(records); err != nil {
- return err
- }
+ a.Add("TXT", rejectif.TxtHasDoubleQuotes) // Last verified 2021-03-01
- if err := recordaudit.TxtNotEmpty(records); err != nil {
- return err
- }
- // Still needed as of 2021-03-01
+ a.Add("TXT", rejectif.TxtHasMultipleSegments) // Last verified 2021-03-01
- if err := recordaudit.TxtNoBackticks(records); err != nil {
- return err
- }
- // Still needed as of 2021-03-01
+ a.Add("TXT", rejectif.TxtHasSingleQuotes) // Last verified 2021-03-01
- if err := recordaudit.TxtNoDoubleQuotes(records); err != nil {
- return err
- }
- // Still needed as of 2021-03-01
+ a.Add("TXT", rejectif.TxtHasSegmentLen256orLonger)
- if err := recordaudit.TxtNoSingleQuotes(records); err != nil {
- return err
- }
- // Still needed as of 2021-03-01
+ a.Add("TXT", rejectif.TxtIsEmpty) // Last verified 2021-03-01
- return nil
+ return a.Audit(records)
}
diff --git a/providers/msdns/corrections.go b/providers/msdns/corrections.go
index b9dfec7b5..b3c326b3c 100644
--- a/providers/msdns/corrections.go
+++ b/providers/msdns/corrections.go
@@ -1,20 +1,13 @@
package msdns
import (
- "fmt"
"github.com/StackExchange/dnscontrol/v3/models"
"github.com/StackExchange/dnscontrol/v3/pkg/diff"
"github.com/StackExchange/dnscontrol/v3/pkg/txtutil"
)
// GetDomainCorrections gets existing records, diffs them against existing, and returns corrections.
-func (client *msdnsProvider) GenerateDomainCorrections(dc *models.DomainConfig, existing models.Records) ([]*models.Correction, error) {
-
- // Read foundRecords:
- foundRecords, err := client.GetZoneRecords(dc.Name)
- if err != nil {
- return nil, fmt.Errorf("c.GetDNSZoneRecords(%v) failed: %v", dc.Name, err)
- }
+func (client *msdnsProvider) GenerateDomainCorrections(dc *models.DomainConfig, foundRecords models.Records) ([]*models.Correction, error) {
// Normalize
models.PostProcessRecords(foundRecords)
diff --git a/providers/namecheap/auditrecords.go b/providers/namecheap/auditrecords.go
index 1b9ffb9a0..940305865 100644
--- a/providers/namecheap/auditrecords.go
+++ b/providers/namecheap/auditrecords.go
@@ -1,11 +1,10 @@
package namecheap
-import (
- "github.com/StackExchange/dnscontrol/v3/models"
-)
+import "github.com/StackExchange/dnscontrol/v3/models"
-// AuditRecords returns an error if any records are not
-// supportable by this provider.
-func AuditRecords(records []*models.RecordConfig) error {
+// AuditRecords returns a list of errors corresponding to the records
+// that aren't supported by this provider. If all records are
+// supported, an empty list is returned.
+func AuditRecords(records []*models.RecordConfig) []error {
return nil
}
diff --git a/providers/namedotcom/auditrecords.go b/providers/namedotcom/auditrecords.go
index 3fcbe6667..dd20eebcd 100644
--- a/providers/namedotcom/auditrecords.go
+++ b/providers/namedotcom/auditrecords.go
@@ -5,49 +5,41 @@ import (
"strings"
"github.com/StackExchange/dnscontrol/v3/models"
- "github.com/StackExchange/dnscontrol/v3/pkg/recordaudit"
+ "github.com/StackExchange/dnscontrol/v3/pkg/rejectif"
)
-// AuditRecords returns an error if any records are not
-// supportable by this provider.
-func AuditRecords(records []*models.RecordConfig) error {
+// AuditRecords returns a list of errors corresponding to the records
+// that aren't supported by this provider. If all records are
+// supported, an empty list is returned.
+func AuditRecords(records []*models.RecordConfig) []error {
+ a := rejectif.Auditor{}
- if err := MaxLengthNDC(records); err != nil {
- return err
- }
- // Still needed as of 2021-03-01
+ a.Add("TXT", MaxLengthNDC) // Last verified 2021-03-01
- if err := recordaudit.TxtNotEmpty(records); err != nil {
- return err
- }
- // Still needed as of 2021-03-01
+ a.Add("TXT", rejectif.TxtIsEmpty) // Last verified 2021-03-01
- return nil
+ return a.Audit(records)
}
// MaxLengthNDC returns and error if the sum of the strings
// are longer than permitted by NDC. Sadly their
// length limit is undocumented. This seems to work.
-func MaxLengthNDC(records []*models.RecordConfig) error {
- for _, rc := range records {
+func MaxLengthNDC(rc *models.RecordConfig) error {
+ if len(rc.TxtStrings) == 0 {
+ return nil
+ }
- if rc.HasFormatIdenticalToTXT() { // TXT and similar:
- // Sum the length of the segments:
- sum := 0
- for _, segment := range rc.TxtStrings {
- sum += len(segment) // The length of each segment
- sum += strings.Count(segment, `"`) // Add 1 for any char to be escaped
- }
- // Add the overhead of quoting them:
- n := len(rc.TxtStrings)
- if n > 0 {
- sum += 2 + 3*(n-1) // Start and end double-quotes, plus `" "` between each segment.
- }
- if sum > 512 {
- return fmt.Errorf("encoded txt too long")
- }
- }
+ sum := 2 // Count the start and end quote.
+ // Add the length of each segment.
+ for _, segment := range rc.TxtStrings {
+ sum += len(segment) // The length of each segment
+ sum += strings.Count(segment, `"`) // Add 1 for any char to be escaped
+ }
+ // Add 3 (quote space quote) for each interior join.
+ sum += 3 * (len(rc.TxtStrings) - 1)
+ if sum > 512 {
+ return fmt.Errorf("encoded txt too long")
}
return nil
}
diff --git a/providers/netcup/auditrecords.go b/providers/netcup/auditrecords.go
index e93e0187b..2571b99cf 100644
--- a/providers/netcup/auditrecords.go
+++ b/providers/netcup/auditrecords.go
@@ -2,12 +2,16 @@ package netcup
import (
"github.com/StackExchange/dnscontrol/v3/models"
- "github.com/StackExchange/dnscontrol/v3/pkg/recordaudit"
+ "github.com/StackExchange/dnscontrol/v3/pkg/rejectif"
)
-// AuditRecords returns an error if any records are not
-// supportable by this provider.
-func AuditRecords(records []*models.RecordConfig) error {
- return recordaudit.TxtNotEmpty(records)
- // Still needed as of 2021-03-01
+// AuditRecords returns a list of errors corresponding to the records
+// that aren't supported by this provider. If all records are
+// supported, an empty list is returned.
+func AuditRecords(records []*models.RecordConfig) []error {
+ a := rejectif.Auditor{}
+
+ a.Add("TXT", rejectif.TxtIsEmpty) // Last verified 2021-03-01
+
+ return a.Audit(records)
}
diff --git a/providers/ns1/auditrecords.go b/providers/ns1/auditrecords.go
index cac395ab4..d302017f2 100644
--- a/providers/ns1/auditrecords.go
+++ b/providers/ns1/auditrecords.go
@@ -2,17 +2,16 @@ package ns1
import (
"github.com/StackExchange/dnscontrol/v3/models"
- "github.com/StackExchange/dnscontrol/v3/pkg/recordaudit"
+ "github.com/StackExchange/dnscontrol/v3/pkg/rejectif"
)
-// AuditRecords returns an error if any records are not
-// supportable by this provider.
-func AuditRecords(records []*models.RecordConfig) error {
+// AuditRecords returns a list of errors corresponding to the records
+// that aren't supported by this provider. If all records are
+// supported, an empty list is returned.
+func AuditRecords(records []*models.RecordConfig) []error {
+ a := rejectif.Auditor{}
- if err := recordaudit.TxtNoMultipleStrings(records); err != nil {
- return err
- }
- // Still needed as of 2021-03-01
+ a.Add("TXT", rejectif.TxtHasMultipleSegments)
- return nil
+ return a.Audit(records)
}
diff --git a/providers/octodns/auditrecords.go b/providers/octodns/auditrecords.go
index ab572f670..4dd078981 100644
--- a/providers/octodns/auditrecords.go
+++ b/providers/octodns/auditrecords.go
@@ -1,11 +1,10 @@
package octodns
-import (
- "github.com/StackExchange/dnscontrol/v3/models"
-)
+import "github.com/StackExchange/dnscontrol/v3/models"
-// AuditRecords returns an error if any records are not
-// supportable by this provider.
-func AuditRecords(records []*models.RecordConfig) error {
+// AuditRecords returns a list of errors corresponding to the records
+// that aren't supported by this provider. If all records are
+// supported, an empty list is returned.
+func AuditRecords(records []*models.RecordConfig) []error {
return nil
}
diff --git a/providers/opensrs/auditrecords.go b/providers/opensrs/auditrecords.go
index ef7bbf441..e26fd0465 100644
--- a/providers/opensrs/auditrecords.go
+++ b/providers/opensrs/auditrecords.go
@@ -1,11 +1,10 @@
package opensrs
-import (
- "github.com/StackExchange/dnscontrol/v3/models"
-)
+import "github.com/StackExchange/dnscontrol/v3/models"
-// AuditRecords returns an error if any records are not
-// supportable by this provider.
-func AuditRecords(records []*models.RecordConfig) error {
+// AuditRecords returns a list of errors corresponding to the records
+// that aren't supported by this provider. If all records are
+// supported, an empty list is returned.
+func AuditRecords(records []*models.RecordConfig) []error {
return nil
}
diff --git a/providers/oracle/auditrecords.go b/providers/oracle/auditrecords.go
index 697fa7184..3ad3e998b 100644
--- a/providers/oracle/auditrecords.go
+++ b/providers/oracle/auditrecords.go
@@ -1,11 +1,10 @@
package oracle
-import (
- "github.com/StackExchange/dnscontrol/v3/models"
-)
+import "github.com/StackExchange/dnscontrol/v3/models"
-// AuditRecords returns an error if any records are not
-// supportable by this provider.
-func AuditRecords(records []*models.RecordConfig) error {
+// AuditRecords returns a list of errors corresponding to the records
+// that aren't supported by this provider. If all records are
+// supported, an empty list is returned.
+func AuditRecords(records []*models.RecordConfig) []error {
return nil
}
diff --git a/providers/ovh/auditrecords.go b/providers/ovh/auditrecords.go
index c3ee7dd92..612694ad2 100644
--- a/providers/ovh/auditrecords.go
+++ b/providers/ovh/auditrecords.go
@@ -1,11 +1,10 @@
package ovh
-import (
- "github.com/StackExchange/dnscontrol/v3/models"
-)
+import "github.com/StackExchange/dnscontrol/v3/models"
-// AuditRecords returns an error if any records are not
-// supportable by this provider.
-func AuditRecords(records []*models.RecordConfig) error {
+// AuditRecords returns a list of errors corresponding to the records
+// that aren't supported by this provider. If all records are
+// supported, an empty list is returned.
+func AuditRecords(records []*models.RecordConfig) []error {
return nil
}
diff --git a/providers/packetframe/auditrecords.go b/providers/packetframe/auditrecords.go
index df0823c18..7d092f96a 100644
--- a/providers/packetframe/auditrecords.go
+++ b/providers/packetframe/auditrecords.go
@@ -1,11 +1,10 @@
package packetframe
-import (
- "github.com/StackExchange/dnscontrol/v3/models"
-)
+import "github.com/StackExchange/dnscontrol/v3/models"
-// AuditRecords returns an error if any records are not
-// supportable by this provider.
-func AuditRecords(records []*models.RecordConfig) error {
+// AuditRecords returns a list of errors corresponding to the records
+// that aren't supported by this provider. If all records are
+// supported, an empty list is returned.
+func AuditRecords(records []*models.RecordConfig) []error {
return nil
}
diff --git a/providers/powerdns/auditrecords.go b/providers/powerdns/auditrecords.go
index 4c350ac83..fc3b2fae4 100644
--- a/providers/powerdns/auditrecords.go
+++ b/providers/powerdns/auditrecords.go
@@ -1,11 +1,10 @@
package powerdns
-import (
- "github.com/StackExchange/dnscontrol/v3/models"
-)
+import "github.com/StackExchange/dnscontrol/v3/models"
-// AuditRecords returns an error if any records are not
-// supportable by this provider.
-func AuditRecords(records []*models.RecordConfig) error {
+// AuditRecords returns a list of errors corresponding to the records
+// that aren't supported by this provider. If all records are
+// supported, an empty list is returned.
+func AuditRecords(records []*models.RecordConfig) []error {
return nil
}
diff --git a/providers/providers.go b/providers/providers.go
index e30ae34f8..42d850748 100644
--- a/providers/providers.go
+++ b/providers/providers.go
@@ -41,9 +41,9 @@ var RegistrarTypes = map[string]RegistrarInitializer{}
type DspInitializer func(map[string]string, json.RawMessage) (DNSServiceProvider, error)
// RecordAuditor is a function that verifies that all the records
-// are supportable by this provider. It returns an error related to
-// the first record that this provider can not support.
-type RecordAuditor func([]*models.RecordConfig) error
+// are supportable by this provider. It returns a list of errors
+// detailing records that this provider can not support.
+type RecordAuditor func([]*models.RecordConfig) []error
// DspFuncs lists functions registered with a provider.
type DspFuncs struct {
@@ -132,13 +132,13 @@ func beCompatible(n string, config map[string]string) (string, error) {
}
// AuditRecords calls the RecordAudit function for a provider.
-func AuditRecords(dType string, rcs models.Records) error {
+func AuditRecords(dType string, rcs models.Records) []error {
p, ok := DNSProviderTypes[dType]
if !ok {
- return fmt.Errorf("unknown DNS service provider type: %q", dType)
+ return []error{fmt.Errorf("unknown DNS service provider type: %q", dType)}
}
if p.RecordAuditor == nil {
- return fmt.Errorf("DNS service provider type %q has no RecordAuditor", dType)
+ return []error{fmt.Errorf("DNS service provider type %q has no RecordAuditor", dType)}
}
return p.RecordAuditor(rcs)
}
diff --git a/providers/route53/auditrecords.go b/providers/route53/auditrecords.go
index ee3bc20bd..be1eb8839 100644
--- a/providers/route53/auditrecords.go
+++ b/providers/route53/auditrecords.go
@@ -1,11 +1,10 @@
package route53
-import (
- "github.com/StackExchange/dnscontrol/v3/models"
-)
+import "github.com/StackExchange/dnscontrol/v3/models"
-// AuditRecords returns an error if any records are not
-// supportable by this provider.
-func AuditRecords(records []*models.RecordConfig) error {
+// AuditRecords returns a list of errors corresponding to the records
+// that aren't supported by this provider. If all records are
+// supported, an empty list is returned.
+func AuditRecords(records []*models.RecordConfig) []error {
return nil
}
diff --git a/providers/rwth/auditrecords.go b/providers/rwth/auditrecords.go
index 6e83f9466..0ab2db054 100644
--- a/providers/rwth/auditrecords.go
+++ b/providers/rwth/auditrecords.go
@@ -2,24 +2,20 @@ package rwth
import (
"github.com/StackExchange/dnscontrol/v3/models"
- "github.com/StackExchange/dnscontrol/v3/pkg/recordaudit"
+ "github.com/StackExchange/dnscontrol/v3/pkg/rejectif"
)
-// AuditRecords returns an error if any records are not
-// supportable by this provider.
-func AuditRecords(records []*models.RecordConfig) error {
+// AuditRecords returns a list of errors corresponding to the records
+// that aren't supported by this provider. If all records are
+// supported, an empty list is returned.
+func AuditRecords(records []*models.RecordConfig) []error {
+ a := rejectif.Auditor{}
- if err := recordaudit.TxtNoMultipleStrings(records); err != nil {
- return err
- }
+ a.Add("TXT", rejectif.TxtHasMultipleSegments)
- if err := recordaudit.TxtNoTrailingSpace(records); err != nil {
- return err
- }
+ a.Add("TXT", rejectif.TxtHasTrailingSpace)
- if err := recordaudit.TxtNotEmpty(records); err != nil {
- return err
- }
+ a.Add("TXT", rejectif.TxtIsEmpty)
- return nil
+ return a.Audit(records)
}
diff --git a/providers/softlayer/auditrecords.go b/providers/softlayer/auditrecords.go
index 5af343d2c..8acbabb8f 100644
--- a/providers/softlayer/auditrecords.go
+++ b/providers/softlayer/auditrecords.go
@@ -1,11 +1,10 @@
package softlayer
-import (
- "github.com/StackExchange/dnscontrol/v3/models"
-)
+import "github.com/StackExchange/dnscontrol/v3/models"
-// AuditRecords returns an error if any records are not
-// supportable by this provider.
-func AuditRecords(records []*models.RecordConfig) error {
+// AuditRecords returns a list of errors corresponding to the records
+// that aren't supported by this provider. If all records are
+// supported, an empty list is returned.
+func AuditRecords(records []*models.RecordConfig) []error {
return nil
}
diff --git a/providers/transip/auditrecords.go b/providers/transip/auditrecords.go
index d79841f95..bd3052b43 100644
--- a/providers/transip/auditrecords.go
+++ b/providers/transip/auditrecords.go
@@ -1,11 +1,10 @@
package transip
-import (
- "github.com/StackExchange/dnscontrol/v3/models"
-)
+import "github.com/StackExchange/dnscontrol/v3/models"
-// AuditRecords returns an error if any records are not
-// supportable by this provider.
-func AuditRecords(records []*models.RecordConfig) error {
+// AuditRecords returns a list of errors corresponding to the records
+// that aren't supported by this provider. If all records are
+// supported, an empty list is returned.
+func AuditRecords(records []*models.RecordConfig) []error {
return nil
}
diff --git a/providers/vultr/auditrecords.go b/providers/vultr/auditrecords.go
index 4efe22900..82838c1de 100644
--- a/providers/vultr/auditrecords.go
+++ b/providers/vultr/auditrecords.go
@@ -2,23 +2,20 @@ package vultr
import (
"github.com/StackExchange/dnscontrol/v3/models"
- "github.com/StackExchange/dnscontrol/v3/pkg/recordaudit"
+ "github.com/StackExchange/dnscontrol/v3/pkg/rejectif"
)
-// AuditRecords returns an error if any records are not
-// supportable by this provider.
-func AuditRecords(records []*models.RecordConfig) error {
+// AuditRecords returns a list of errors corresponding to the records
+// that aren't supported by this provider. If all records are
+// supported, an empty list is returned.
+func AuditRecords(records []*models.RecordConfig) []error {
+ a := rejectif.Auditor{}
- // TODO(tlim) Needs investigation. Could be a dnscontrol issue or
+ a.Add("TXT", rejectif.TxtHasDoubleQuotes) // Last verified 2021-03-02
+ // Needs investigation. Could be a dnscontrol issue or
// the provider doesn't support double quotes.
- if err := recordaudit.TxtNoDoubleQuotes(records); err != nil {
- return err
- }
- // Still needed as of 2021-03-02
- if err := recordaudit.TxtNoMultipleStrings(records); err != nil {
- return err
- }
+ a.Add("TXT", rejectif.TxtHasMultipleSegments)
- return nil
+ return a.Audit(records)
}