2017-03-19 09:58:47 +08:00
|
|
|
package dnsimple
|
|
|
|
|
|
|
|
import (
|
2018-10-14 12:30:58 +08:00
|
|
|
"context"
|
2017-03-19 09:58:47 +08:00
|
|
|
"encoding/json"
|
2022-09-23 23:55:09 +08:00
|
|
|
"errors"
|
2017-03-19 09:58:47 +08:00
|
|
|
"fmt"
|
2024-01-03 04:54:13 +08:00
|
|
|
"os"
|
2017-03-19 09:58:47 +08:00
|
|
|
"sort"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
|
2023-05-21 01:21:45 +08:00
|
|
|
"github.com/StackExchange/dnscontrol/v4/models"
|
|
|
|
"github.com/StackExchange/dnscontrol/v4/pkg/diff"
|
|
|
|
"github.com/StackExchange/dnscontrol/v4/pkg/printer"
|
2024-01-03 04:54:13 +08:00
|
|
|
"github.com/StackExchange/dnscontrol/v4/pkg/txtutil"
|
2023-05-21 01:21:45 +08:00
|
|
|
"github.com/StackExchange/dnscontrol/v4/providers"
|
2022-08-15 08:46:56 +08:00
|
|
|
dnsimpleapi "github.com/dnsimple/dnsimple-go/dnsimple"
|
|
|
|
"golang.org/x/oauth2"
|
2017-03-19 09:58:47 +08:00
|
|
|
)
|
|
|
|
|
2018-01-05 08:19:35 +08:00
|
|
|
var features = providers.DocumentationNotes{
|
2022-03-03 00:19:15 +08:00
|
|
|
providers.CanAutoDNSSEC: providers.Can(),
|
|
|
|
providers.CanGetZones: providers.Can(),
|
2018-01-05 08:19:35 +08:00
|
|
|
providers.CanUseAlias: providers.Can(),
|
|
|
|
providers.CanUseCAA: providers.Can(),
|
2022-07-09 13:12:46 +08:00
|
|
|
providers.CanUseDS: providers.Cannot(),
|
|
|
|
providers.CanUseDSForChildren: providers.Cannot(),
|
2023-03-17 02:04:20 +08:00
|
|
|
providers.CanUseLOC: providers.Cannot(),
|
2022-03-03 00:19:15 +08:00
|
|
|
providers.CanUseNAPTR: providers.Can(),
|
2018-01-05 08:19:35 +08:00
|
|
|
providers.CanUsePTR: providers.Can(),
|
|
|
|
providers.CanUseSRV: providers.Can(),
|
2022-03-03 00:19:15 +08:00
|
|
|
providers.CanUseSSHFP: providers.Can(),
|
2018-01-05 08:19:35 +08:00
|
|
|
providers.CanUseTLSA: providers.Cannot(),
|
2017-09-15 04:13:17 +08:00
|
|
|
providers.DocCreateDomains: providers.Cannot(),
|
2018-01-05 08:19:35 +08:00
|
|
|
providers.DocDualHost: providers.Cannot("DNSimple does not allow sufficient control over the apex NS records"),
|
2017-09-15 04:13:17 +08:00
|
|
|
providers.DocOfficiallySupported: providers.Cannot(),
|
|
|
|
}
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
providers.RegisterRegistrarType("DNSIMPLE", newReg)
|
2021-03-08 02:19:22 +08:00
|
|
|
fns := providers.DspFuncs{
|
2021-05-05 02:15:31 +08:00
|
|
|
Initializer: newDsp,
|
2021-03-09 09:14:30 +08:00
|
|
|
RecordAuditor: AuditRecords,
|
2021-03-08 02:19:22 +08:00
|
|
|
}
|
|
|
|
providers.RegisterDomainServiceProviderType("DNSIMPLE", fns, features)
|
2017-09-15 04:13:17 +08:00
|
|
|
}
|
|
|
|
|
2017-03-19 09:58:47 +08:00
|
|
|
const stateRegistered = "registered"
|
|
|
|
|
|
|
|
var defaultNameServerNames = []string{
|
|
|
|
"ns1.dnsimple.com",
|
2023-11-02 22:22:27 +08:00
|
|
|
"ns2.dnsimple-edge.net",
|
2017-03-19 09:58:47 +08:00
|
|
|
"ns3.dnsimple.com",
|
2022-11-05 02:54:48 +08:00
|
|
|
"ns4.dnsimple-edge.org",
|
|
|
|
}
|
|
|
|
|
|
|
|
var nameServerSuffixes = []string{
|
|
|
|
".dnsimple.com.",
|
|
|
|
".dnsimple-edge.org.",
|
|
|
|
".dnsimple-edge.net.",
|
|
|
|
".dnsimple-edge.io.",
|
|
|
|
".dnsimple-edge.com.",
|
2017-03-19 09:58:47 +08:00
|
|
|
}
|
|
|
|
|
2020-10-26 21:25:30 +08:00
|
|
|
// dnsimpleProvider is the handle for this provider.
|
|
|
|
type dnsimpleProvider struct {
|
2017-03-19 09:58:47 +08:00
|
|
|
AccountToken string // The account access token
|
|
|
|
BaseURL string // An alternate base URI
|
2018-01-10 01:53:16 +08:00
|
|
|
accountID string // Account id cache
|
2017-03-19 09:58:47 +08:00
|
|
|
}
|
|
|
|
|
2018-01-10 01:53:16 +08:00
|
|
|
// GetNameservers returns the name servers for a domain.
|
2022-07-12 02:52:18 +08:00
|
|
|
func (c *dnsimpleProvider) GetNameservers(_ string) ([]*models.Nameserver, error) {
|
2020-03-01 23:33:24 +08:00
|
|
|
return models.ToNameservers(defaultNameServerNames)
|
2017-03-19 09:58:47 +08:00
|
|
|
}
|
|
|
|
|
2020-02-18 21:59:18 +08:00
|
|
|
// GetZoneRecords gets the records of a zone and returns them in RecordConfig format.
|
2023-05-03 01:04:59 +08:00
|
|
|
func (c *dnsimpleProvider) GetZoneRecords(domain string, meta map[string]string) (models.Records, error) {
|
2020-06-18 21:37:57 +08:00
|
|
|
records, err := c.getRecords(domain)
|
2017-03-19 09:58:47 +08:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-02-21 03:52:19 +08:00
|
|
|
var cleanedRecords models.Records
|
2017-03-19 09:58:47 +08:00
|
|
|
for _, r := range records {
|
2020-02-21 03:52:19 +08:00
|
|
|
if r.Type == "SOA" {
|
2017-03-19 09:58:47 +08:00
|
|
|
continue
|
|
|
|
}
|
2022-07-22 21:36:28 +08:00
|
|
|
|
2017-03-19 09:58:47 +08:00
|
|
|
if r.Name == "" {
|
|
|
|
r.Name = "@"
|
|
|
|
}
|
2022-07-22 21:36:28 +08:00
|
|
|
|
2022-03-29 03:28:40 +08:00
|
|
|
if r.Type == "CNAME" || r.Type == "MX" || r.Type == "ALIAS" || r.Type == "NS" {
|
2017-03-19 09:58:47 +08:00
|
|
|
r.Content += "."
|
|
|
|
}
|
2022-07-22 21:36:28 +08:00
|
|
|
|
2020-02-21 03:52:19 +08:00
|
|
|
// DNSimple adds TXT records that mirror the alias records.
|
|
|
|
// They manage them on ALIAS updates, so pretend they don't exist
|
2024-01-03 04:54:13 +08:00
|
|
|
if r.Type == "TXT" && strings.HasPrefix(r.Content, `"ALIAS for `) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
// This second check is the same of before, but it exists for compatibility purpose.
|
|
|
|
// Until Nov 2023 DNSimple did not normalize TXT records, and they used to store TXT records without quotes.
|
|
|
|
//
|
|
|
|
// This is a backward-compatible function to facilitate the TXT transition.
|
|
|
|
if r.Type == "TXT" && strings.HasPrefix(r.Content, `ALIAS for `) {
|
2017-10-19 03:51:44 +08:00
|
|
|
continue
|
|
|
|
}
|
2022-07-22 21:36:28 +08:00
|
|
|
|
2017-03-19 09:58:47 +08:00
|
|
|
rec := &models.RecordConfig{
|
2018-02-16 01:02:50 +08:00
|
|
|
TTL: uint32(r.TTL),
|
|
|
|
Original: r,
|
2017-03-19 09:58:47 +08:00
|
|
|
}
|
2020-02-21 03:52:19 +08:00
|
|
|
rec.SetLabel(r.Name, domain)
|
2022-07-22 21:36:28 +08:00
|
|
|
|
|
|
|
var err error
|
2018-02-16 01:02:50 +08:00
|
|
|
switch rtype := r.Type; rtype {
|
2020-02-22 20:09:31 +08:00
|
|
|
case "DNSKEY", "CDNSKEY", "CDS":
|
|
|
|
continue
|
2018-02-28 05:50:34 +08:00
|
|
|
case "ALIAS", "URL":
|
2018-02-27 22:38:39 +08:00
|
|
|
rec.Type = r.Type
|
2022-07-22 21:36:28 +08:00
|
|
|
err = rec.SetTarget(r.Content)
|
2020-05-30 22:40:21 +08:00
|
|
|
case "DS":
|
2022-07-22 21:36:28 +08:00
|
|
|
err = rec.SetTargetDSString(r.Content)
|
2018-02-16 01:02:50 +08:00
|
|
|
case "MX":
|
2022-07-22 21:36:28 +08:00
|
|
|
err = rec.SetTargetMX(uint16(r.Priority), r.Content)
|
2018-10-14 12:30:13 +08:00
|
|
|
case "SRV":
|
2022-07-22 21:36:28 +08:00
|
|
|
err = rec.SetTargetSRVPriorityString(uint16(r.Priority), r.Content)
|
|
|
|
case "TXT":
|
2024-01-03 04:54:13 +08:00
|
|
|
// This is a backward-compatible function to facilitate the TXT transition.
|
|
|
|
if isQuotedTXT(r.Content) {
|
|
|
|
err = rec.PopulateFromStringFunc(r.Type, r.Content, domain, txtutil.ParseQuoted)
|
|
|
|
} else {
|
|
|
|
err = rec.SetTargetTXT(fmt.Sprintf("legacy: %s", r.Content))
|
|
|
|
}
|
2018-02-16 01:02:50 +08:00
|
|
|
default:
|
2022-07-22 21:36:28 +08:00
|
|
|
err = rec.PopulateFromString(r.Type, r.Content, domain)
|
2017-10-19 03:51:44 +08:00
|
|
|
}
|
2022-07-22 21:36:28 +08:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("unparsable record received from dnsimple: %w", err)
|
|
|
|
}
|
|
|
|
|
2020-02-21 03:52:19 +08:00
|
|
|
cleanedRecords = append(cleanedRecords, rec)
|
2017-03-19 09:58:47 +08:00
|
|
|
}
|
2020-02-21 03:52:19 +08:00
|
|
|
|
2023-04-15 03:22:23 +08:00
|
|
|
// Apex NS are immutable via API
|
|
|
|
cleanedRecords = removeApexNS(cleanedRecords)
|
|
|
|
|
2020-02-21 03:52:19 +08:00
|
|
|
return cleanedRecords, nil
|
|
|
|
}
|
|
|
|
|
2023-04-15 03:22:23 +08:00
|
|
|
func (c *dnsimpleProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, actual models.Records) ([]*models.Correction, error) {
|
|
|
|
removeOtherApexNS(dc)
|
2020-02-21 03:52:19 +08:00
|
|
|
|
2020-02-22 20:09:31 +08:00
|
|
|
dnssecFixes, err := c.getDNSSECCorrections(dc)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2023-10-23 01:56:13 +08:00
|
|
|
toReport, create, del, modify, err := diff.NewCompat(dc).IncrementalDiff(actual)
|
2023-01-04 00:58:21 +08:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2023-10-23 01:56:13 +08:00
|
|
|
// Start corrections with the reports
|
|
|
|
corrections := diff.GenerateMessageCorrections(toReport)
|
|
|
|
// Next dnsSec fixes
|
|
|
|
corrections = append(corrections, dnssecFixes...)
|
2022-12-12 04:02:58 +08:00
|
|
|
|
2023-01-04 00:58:21 +08:00
|
|
|
for _, del := range del {
|
|
|
|
rec := del.Existing.Original.(dnsimpleapi.ZoneRecord)
|
|
|
|
corrections = append(corrections, &models.Correction{
|
|
|
|
Msg: del.String(),
|
|
|
|
F: c.deleteRecordFunc(rec.ID, dc.Name),
|
|
|
|
})
|
|
|
|
}
|
2022-12-12 04:02:58 +08:00
|
|
|
|
2023-01-04 00:58:21 +08:00
|
|
|
for _, cre := range create {
|
|
|
|
rec := cre.Desired
|
|
|
|
corrections = append(corrections, &models.Correction{
|
|
|
|
Msg: cre.String(),
|
|
|
|
F: c.createRecordFunc(rec, dc.Name),
|
|
|
|
})
|
2017-03-19 09:58:47 +08:00
|
|
|
}
|
|
|
|
|
2023-01-04 00:58:21 +08:00
|
|
|
for _, mod := range modify {
|
|
|
|
old := mod.Existing.Original.(dnsimpleapi.ZoneRecord)
|
|
|
|
rec := mod.Desired
|
|
|
|
corrections = append(corrections, &models.Correction{
|
|
|
|
Msg: mod.String(),
|
|
|
|
F: c.updateRecordFunc(&old, rec, dc.Name),
|
|
|
|
})
|
|
|
|
}
|
2022-12-12 04:02:58 +08:00
|
|
|
|
2017-03-19 09:58:47 +08:00
|
|
|
return corrections, nil
|
|
|
|
}
|
|
|
|
|
2022-03-29 03:28:40 +08:00
|
|
|
func removeApexNS(records models.Records) models.Records {
|
|
|
|
var filtered models.Records
|
2020-02-21 03:52:19 +08:00
|
|
|
for _, r := range records {
|
2022-03-29 03:28:40 +08:00
|
|
|
if r.Type == "NS" && r.Name == "@" {
|
|
|
|
continue
|
2020-02-21 03:52:19 +08:00
|
|
|
}
|
2022-03-29 03:28:40 +08:00
|
|
|
filtered = append(filtered, r)
|
2020-02-21 03:52:19 +08:00
|
|
|
}
|
2022-03-29 03:28:40 +08:00
|
|
|
return filtered
|
2020-02-21 03:52:19 +08:00
|
|
|
}
|
|
|
|
|
2018-01-10 01:53:16 +08:00
|
|
|
// GetRegistrarCorrections returns corrections that update a domain's registrar.
|
2020-10-26 21:25:30 +08:00
|
|
|
func (c *dnsimpleProvider) GetRegistrarCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
|
2022-07-12 02:52:18 +08:00
|
|
|
var corrections []*models.Correction
|
2017-03-19 09:58:47 +08:00
|
|
|
|
|
|
|
nameServers, err := c.getNameservers(dc.Name)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2018-08-13 22:41:55 +08:00
|
|
|
sort.Strings(nameServers)
|
2017-03-19 09:58:47 +08:00
|
|
|
|
|
|
|
actual := strings.Join(nameServers, ",")
|
|
|
|
|
2022-07-12 02:52:18 +08:00
|
|
|
var expectedSet []string
|
2017-03-19 09:58:47 +08:00
|
|
|
for _, ns := range dc.Nameservers {
|
|
|
|
expectedSet = append(expectedSet, ns.Name)
|
|
|
|
}
|
|
|
|
sort.Strings(expectedSet)
|
|
|
|
expected := strings.Join(expectedSet, ",")
|
|
|
|
|
|
|
|
if actual != expected {
|
|
|
|
return []*models.Correction{
|
|
|
|
{
|
|
|
|
Msg: fmt.Sprintf("Update nameservers %s -> %s", actual, expected),
|
|
|
|
F: c.updateNameserversFunc(expectedSet, dc.Name),
|
|
|
|
},
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return corrections, nil
|
|
|
|
}
|
|
|
|
|
2020-02-22 20:09:31 +08:00
|
|
|
// getDNSSECCorrections returns corrections that update a domain's DNSSEC state.
|
2020-10-26 21:25:30 +08:00
|
|
|
func (c *dnsimpleProvider) getDNSSECCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
|
2020-02-22 20:09:31 +08:00
|
|
|
enabled, err := c.getDnssec(dc.Name)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-09-28 04:37:42 +08:00
|
|
|
if enabled && dc.AutoDNSSEC == "off" {
|
2020-02-22 20:09:31 +08:00
|
|
|
return []*models.Correction{
|
|
|
|
{
|
|
|
|
Msg: "Disable DNSSEC",
|
|
|
|
F: func() error { _, err := c.disableDnssec(dc.Name); return err },
|
|
|
|
},
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2020-09-28 04:37:42 +08:00
|
|
|
if !enabled && dc.AutoDNSSEC == "on" {
|
2020-02-22 20:09:31 +08:00
|
|
|
return []*models.Correction{
|
|
|
|
{
|
|
|
|
Msg: "Enable DNSSEC",
|
|
|
|
F: func() error { _, err := c.enableDnssec(dc.Name); return err },
|
|
|
|
},
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return []*models.Correction{}, nil
|
|
|
|
}
|
|
|
|
|
2017-03-19 09:58:47 +08:00
|
|
|
// DNSimple calls
|
|
|
|
|
2024-01-03 04:54:13 +08:00
|
|
|
// Initializes a new DNSimple API client.
|
|
|
|
//
|
|
|
|
// - if BaseURL is present, the provided BaseURL is used. Useful to switch to DNSimple sandbox site. It defaults to production otherwise.
|
|
|
|
// - if "DNSIMPLE_DEBUG_HTTP" is set to "1", it enables the API client logging.
|
2020-10-26 21:25:30 +08:00
|
|
|
func (c *dnsimpleProvider) getClient() *dnsimpleapi.Client {
|
2018-10-14 12:30:58 +08:00
|
|
|
ts := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: c.AccountToken})
|
|
|
|
tc := oauth2.NewClient(context.Background(), ts)
|
|
|
|
|
|
|
|
// new client
|
|
|
|
client := dnsimpleapi.NewClient(tc)
|
2020-05-14 04:37:49 +08:00
|
|
|
client.SetUserAgent("DNSControl")
|
2018-10-14 12:30:58 +08:00
|
|
|
|
2017-03-19 09:58:47 +08:00
|
|
|
if c.BaseURL != "" {
|
|
|
|
client.BaseURL = c.BaseURL
|
|
|
|
}
|
2024-01-03 04:54:13 +08:00
|
|
|
if os.Getenv("DNSIMPLE_DEBUG_HTTP") == "1" {
|
|
|
|
client.Debug = true
|
|
|
|
}
|
2017-03-19 09:58:47 +08:00
|
|
|
return client
|
|
|
|
}
|
|
|
|
|
2020-10-26 21:25:30 +08:00
|
|
|
func (c *dnsimpleProvider) getAccountID() (string, error) {
|
2018-01-10 01:53:16 +08:00
|
|
|
if c.accountID == "" {
|
2017-03-19 09:58:47 +08:00
|
|
|
client := c.getClient()
|
2020-05-14 04:37:49 +08:00
|
|
|
whoamiResponse, err := client.Identity.Whoami(context.Background())
|
2017-03-19 09:58:47 +08:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
if whoamiResponse.Data.User != nil && whoamiResponse.Data.Account == nil {
|
Switch to Go 1.13 error wrapping (#604)
* Replaced errors.Wrap with fmt.Errorf (#589)
* Find: errors\.Wrap\(([^,]+),\s+(["`][^"`]*)(["`])\)
Replace: fmt.Errorf($2: %w$3, $1)
* Replaced errors.Wrapf with fmt.Errorf (#589)
* Find: errors\.Wrapf\(([^,]+),\s+(["`][^"`]*)(["`])\)
Replace: fmt.Errorf($2: %w$3, $1)
* Find: errors\.Wrapf\(([^,]+),\s+(["`][^"`]*)(["`])(,[^)]+)\)
* Replace: fmt.Errorf($2: %w$3$4, $1)
* Replaced errors.Errorf with fmt.Errorf (#589)
* Find: errors\.Errorf
Replace: fmt.Errorf
* Cleaned up remaining imports
* Cleanup
* Regenerate provider support matrix
This was broken by #533 ... and it's now the third time this has been missed.
2020-01-29 00:06:56 +08:00
|
|
|
return "", fmt.Errorf("DNSimple token appears to be a user token. Please supply an account token")
|
2017-03-19 09:58:47 +08:00
|
|
|
}
|
2018-10-14 12:30:58 +08:00
|
|
|
c.accountID = strconv.FormatInt(whoamiResponse.Data.Account.ID, 10)
|
2017-03-19 09:58:47 +08:00
|
|
|
}
|
2018-01-10 01:53:16 +08:00
|
|
|
return c.accountID, nil
|
2017-03-19 09:58:47 +08:00
|
|
|
}
|
|
|
|
|
2020-10-26 21:25:30 +08:00
|
|
|
func (c *dnsimpleProvider) getRecords(domainName string) ([]dnsimpleapi.ZoneRecord, error) {
|
2017-03-19 09:58:47 +08:00
|
|
|
client := c.getClient()
|
|
|
|
|
2018-01-10 01:53:16 +08:00
|
|
|
accountID, err := c.getAccountID()
|
2017-03-19 09:58:47 +08:00
|
|
|
if err != nil {
|
2022-09-23 23:55:09 +08:00
|
|
|
var errorResponse *dnsimpleapi.ErrorResponse
|
|
|
|
if errors.As(err, &errorResponse) {
|
|
|
|
return nil, compileAttributeErrors(errorResponse)
|
|
|
|
}
|
2017-03-19 09:58:47 +08:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2017-10-19 01:53:44 +08:00
|
|
|
opts := &dnsimpleapi.ZoneRecordListOptions{}
|
2022-07-12 02:52:18 +08:00
|
|
|
var recs []dnsimpleapi.ZoneRecord
|
2020-05-14 04:37:49 +08:00
|
|
|
page := 1
|
2017-10-19 01:53:44 +08:00
|
|
|
for {
|
2020-05-14 04:37:49 +08:00
|
|
|
opts.Page = &page
|
|
|
|
recordsResponse, err := client.Zones.ListRecords(context.Background(), accountID, domainName, opts)
|
2017-10-19 01:53:44 +08:00
|
|
|
if err != nil {
|
2022-09-23 23:55:09 +08:00
|
|
|
var errorResponse *dnsimpleapi.ErrorResponse
|
|
|
|
if errors.As(err, &errorResponse) {
|
|
|
|
return nil, compileAttributeErrors(errorResponse)
|
|
|
|
}
|
2017-10-19 01:53:44 +08:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
recs = append(recs, recordsResponse.Data...)
|
|
|
|
pg := recordsResponse.Pagination
|
|
|
|
if pg.CurrentPage == pg.TotalPages {
|
|
|
|
break
|
|
|
|
}
|
2020-05-14 04:37:49 +08:00
|
|
|
page++
|
2017-03-19 09:58:47 +08:00
|
|
|
}
|
|
|
|
|
2017-10-19 01:53:44 +08:00
|
|
|
return recs, nil
|
2017-03-19 09:58:47 +08:00
|
|
|
}
|
|
|
|
|
2020-10-26 21:25:30 +08:00
|
|
|
func (c *dnsimpleProvider) getDnssec(domainName string) (bool, error) {
|
2020-02-22 20:09:31 +08:00
|
|
|
var (
|
|
|
|
client *dnsimpleapi.Client
|
|
|
|
accountID string
|
|
|
|
err error
|
|
|
|
)
|
|
|
|
client = c.getClient()
|
|
|
|
if accountID, err = c.getAccountID(); err != nil {
|
2022-09-23 23:55:09 +08:00
|
|
|
var errorResponse *dnsimpleapi.ErrorResponse
|
|
|
|
if errors.As(err, &errorResponse) {
|
|
|
|
return false, compileAttributeErrors(errorResponse)
|
|
|
|
}
|
2020-02-22 20:09:31 +08:00
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
|
2020-05-14 04:37:49 +08:00
|
|
|
dnssecResponse, err := client.Domains.GetDnssec(context.Background(), accountID, domainName)
|
2020-02-22 20:09:31 +08:00
|
|
|
if err != nil {
|
2022-09-23 23:55:09 +08:00
|
|
|
var errorResponse *dnsimpleapi.ErrorResponse
|
|
|
|
if errors.As(err, &errorResponse) {
|
|
|
|
return false, compileAttributeErrors(errorResponse)
|
|
|
|
}
|
2020-02-22 20:09:31 +08:00
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
if dnssecResponse.Data == nil {
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
return dnssecResponse.Data.Enabled, nil
|
|
|
|
}
|
|
|
|
|
2020-10-26 21:25:30 +08:00
|
|
|
func (c *dnsimpleProvider) enableDnssec(domainName string) (bool, error) {
|
2020-02-22 20:09:31 +08:00
|
|
|
var (
|
|
|
|
client *dnsimpleapi.Client
|
|
|
|
accountID string
|
|
|
|
err error
|
|
|
|
)
|
|
|
|
client = c.getClient()
|
|
|
|
if accountID, err = c.getAccountID(); err != nil {
|
2022-09-23 23:55:09 +08:00
|
|
|
var errorResponse *dnsimpleapi.ErrorResponse
|
|
|
|
if errors.As(err, &errorResponse) {
|
|
|
|
return false, compileAttributeErrors(errorResponse)
|
|
|
|
}
|
2020-02-22 20:09:31 +08:00
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
|
2020-05-14 04:37:49 +08:00
|
|
|
dnssecResponse, err := client.Domains.EnableDnssec(context.Background(), accountID, domainName)
|
2020-02-22 20:09:31 +08:00
|
|
|
if err != nil {
|
2022-09-23 23:55:09 +08:00
|
|
|
var errorResponse *dnsimpleapi.ErrorResponse
|
|
|
|
if errors.As(err, &errorResponse) {
|
|
|
|
return false, compileAttributeErrors(errorResponse)
|
|
|
|
}
|
2020-02-22 20:09:31 +08:00
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
if dnssecResponse.Data == nil {
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
return dnssecResponse.Data.Enabled, nil
|
|
|
|
}
|
|
|
|
|
2020-10-26 21:25:30 +08:00
|
|
|
func (c *dnsimpleProvider) disableDnssec(domainName string) (bool, error) {
|
2020-02-22 20:09:31 +08:00
|
|
|
var (
|
|
|
|
client *dnsimpleapi.Client
|
|
|
|
accountID string
|
|
|
|
err error
|
|
|
|
)
|
|
|
|
client = c.getClient()
|
|
|
|
if accountID, err = c.getAccountID(); err != nil {
|
2022-09-23 23:55:09 +08:00
|
|
|
var errorResponse *dnsimpleapi.ErrorResponse
|
|
|
|
if errors.As(err, &errorResponse) {
|
|
|
|
return false, compileAttributeErrors(errorResponse)
|
|
|
|
}
|
2020-02-22 20:09:31 +08:00
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
|
2020-05-14 04:37:49 +08:00
|
|
|
dnssecResponse, err := client.Domains.DisableDnssec(context.Background(), accountID, domainName)
|
2020-02-22 20:09:31 +08:00
|
|
|
if err != nil {
|
2022-09-23 23:55:09 +08:00
|
|
|
var errorResponse *dnsimpleapi.ErrorResponse
|
|
|
|
if errors.As(err, &errorResponse) {
|
|
|
|
return false, compileAttributeErrors(errorResponse)
|
|
|
|
}
|
2020-02-22 20:09:31 +08:00
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
if dnssecResponse.Data == nil {
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
return dnssecResponse.Data.Enabled, nil
|
|
|
|
}
|
|
|
|
|
2017-03-19 09:58:47 +08:00
|
|
|
// Returns the name server names that should be used. If the domain is registered
|
|
|
|
// then this method will return the delegation name servers. If this domain
|
|
|
|
// is hosted only, then it will return the default DNSimple name servers.
|
2020-10-26 21:25:30 +08:00
|
|
|
func (c *dnsimpleProvider) getNameservers(domainName string) ([]string, error) {
|
2017-03-19 09:58:47 +08:00
|
|
|
client := c.getClient()
|
|
|
|
|
2018-01-10 01:53:16 +08:00
|
|
|
accountID, err := c.getAccountID()
|
2017-03-19 09:58:47 +08:00
|
|
|
if err != nil {
|
2022-09-23 23:55:09 +08:00
|
|
|
var errorResponse *dnsimpleapi.ErrorResponse
|
|
|
|
if errors.As(err, &errorResponse) {
|
|
|
|
return nil, compileAttributeErrors(errorResponse)
|
|
|
|
}
|
2017-03-19 09:58:47 +08:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-05-14 04:37:49 +08:00
|
|
|
domainResponse, err := client.Domains.GetDomain(context.Background(), accountID, domainName)
|
2017-03-19 09:58:47 +08:00
|
|
|
if err != nil {
|
2022-09-23 23:55:09 +08:00
|
|
|
var errorResponse *dnsimpleapi.ErrorResponse
|
|
|
|
if errors.As(err, &errorResponse) {
|
|
|
|
return nil, compileAttributeErrors(errorResponse)
|
|
|
|
}
|
2017-03-19 09:58:47 +08:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if domainResponse.Data.State == stateRegistered {
|
|
|
|
|
2020-05-14 04:37:49 +08:00
|
|
|
delegationResponse, err := client.Registrar.GetDomainDelegation(context.Background(), accountID, domainName)
|
2017-03-19 09:58:47 +08:00
|
|
|
if err != nil {
|
2022-09-23 23:55:09 +08:00
|
|
|
var errorResponse *dnsimpleapi.ErrorResponse
|
|
|
|
if errors.As(err, &errorResponse) {
|
|
|
|
return nil, compileAttributeErrors(errorResponse)
|
|
|
|
}
|
2017-03-19 09:58:47 +08:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return *delegationResponse.Data, nil
|
|
|
|
}
|
2018-01-10 01:53:16 +08:00
|
|
|
return defaultNameServerNames, nil
|
2017-03-19 09:58:47 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Returns a function that can be invoked to change the delegation of the domain to the given name server names.
|
2020-10-26 21:25:30 +08:00
|
|
|
func (c *dnsimpleProvider) updateNameserversFunc(nameServerNames []string, domainName string) func() error {
|
2017-03-19 09:58:47 +08:00
|
|
|
return func() error {
|
|
|
|
client := c.getClient()
|
|
|
|
|
2018-01-10 01:53:16 +08:00
|
|
|
accountID, err := c.getAccountID()
|
2017-03-19 09:58:47 +08:00
|
|
|
if err != nil {
|
2022-09-23 23:55:09 +08:00
|
|
|
var errorResponse *dnsimpleapi.ErrorResponse
|
|
|
|
if errors.As(err, &errorResponse) {
|
|
|
|
return compileAttributeErrors(errorResponse)
|
|
|
|
}
|
2017-03-19 09:58:47 +08:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
nameServers := dnsimpleapi.Delegation(nameServerNames)
|
|
|
|
|
2020-05-14 04:37:49 +08:00
|
|
|
_, err = client.Registrar.ChangeDomainDelegation(context.Background(), accountID, domainName, &nameServers)
|
2017-03-19 09:58:47 +08:00
|
|
|
if err != nil {
|
2022-09-23 23:55:09 +08:00
|
|
|
var errorResponse *dnsimpleapi.ErrorResponse
|
|
|
|
if errors.As(err, &errorResponse) {
|
|
|
|
return compileAttributeErrors(errorResponse)
|
|
|
|
}
|
2017-03-19 09:58:47 +08:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns a function that can be invoked to create a record in a zone.
|
2020-10-26 21:25:30 +08:00
|
|
|
func (c *dnsimpleProvider) createRecordFunc(rc *models.RecordConfig, domainName string) func() error {
|
2017-03-19 09:58:47 +08:00
|
|
|
return func() error {
|
|
|
|
client := c.getClient()
|
|
|
|
|
2018-01-10 01:53:16 +08:00
|
|
|
accountID, err := c.getAccountID()
|
2017-03-19 09:58:47 +08:00
|
|
|
if err != nil {
|
2022-09-23 23:55:09 +08:00
|
|
|
var errorResponse *dnsimpleapi.ErrorResponse
|
|
|
|
if errors.As(err, &errorResponse) {
|
|
|
|
return compileAttributeErrors(errorResponse)
|
|
|
|
}
|
2017-03-19 09:58:47 +08:00
|
|
|
return err
|
|
|
|
}
|
2020-05-14 04:37:49 +08:00
|
|
|
record := dnsimpleapi.ZoneRecordAttributes{
|
|
|
|
Name: dnsimpleapi.String(rc.GetLabel()),
|
2017-03-19 09:58:47 +08:00
|
|
|
Type: rc.Type,
|
2018-10-14 12:30:13 +08:00
|
|
|
Content: getTargetRecordContent(rc),
|
2017-03-19 09:58:47 +08:00
|
|
|
TTL: int(rc.TTL),
|
2018-10-14 12:30:13 +08:00
|
|
|
Priority: getTargetRecordPriority(rc),
|
2017-03-19 09:58:47 +08:00
|
|
|
}
|
2020-05-14 04:37:49 +08:00
|
|
|
_, err = client.Zones.CreateRecord(context.Background(), accountID, domainName, record)
|
2017-03-19 09:58:47 +08:00
|
|
|
if err != nil {
|
2022-09-23 23:55:09 +08:00
|
|
|
var errorResponse *dnsimpleapi.ErrorResponse
|
|
|
|
if errors.As(err, &errorResponse) {
|
|
|
|
return compileAttributeErrors(errorResponse)
|
|
|
|
}
|
2017-03-19 09:58:47 +08:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns a function that can be invoked to delete a record in a zone.
|
2020-10-26 21:25:30 +08:00
|
|
|
func (c *dnsimpleProvider) deleteRecordFunc(recordID int64, domainName string) func() error {
|
2017-03-19 09:58:47 +08:00
|
|
|
return func() error {
|
|
|
|
client := c.getClient()
|
|
|
|
|
2018-01-10 01:53:16 +08:00
|
|
|
accountID, err := c.getAccountID()
|
2017-03-19 09:58:47 +08:00
|
|
|
if err != nil {
|
2022-09-23 23:55:09 +08:00
|
|
|
var errorResponse *dnsimpleapi.ErrorResponse
|
|
|
|
if errors.As(err, &errorResponse) {
|
|
|
|
return compileAttributeErrors(errorResponse)
|
|
|
|
}
|
2017-03-19 09:58:47 +08:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-05-14 04:37:49 +08:00
|
|
|
_, err = client.Zones.DeleteRecord(context.Background(), accountID, domainName, recordID)
|
2017-03-19 09:58:47 +08:00
|
|
|
if err != nil {
|
2022-09-23 23:55:09 +08:00
|
|
|
var errorResponse *dnsimpleapi.ErrorResponse
|
|
|
|
if errors.As(err, &errorResponse) {
|
|
|
|
return compileAttributeErrors(errorResponse)
|
|
|
|
}
|
2017-03-19 09:58:47 +08:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns a function that can be invoked to update a record in a zone.
|
2020-10-26 21:25:30 +08:00
|
|
|
func (c *dnsimpleProvider) updateRecordFunc(old *dnsimpleapi.ZoneRecord, rc *models.RecordConfig, domainName string) func() error {
|
2017-03-19 09:58:47 +08:00
|
|
|
return func() error {
|
|
|
|
client := c.getClient()
|
|
|
|
|
2018-01-10 01:53:16 +08:00
|
|
|
accountID, err := c.getAccountID()
|
2017-03-19 09:58:47 +08:00
|
|
|
if err != nil {
|
2022-09-23 23:55:09 +08:00
|
|
|
var errorResponse *dnsimpleapi.ErrorResponse
|
|
|
|
if errors.As(err, &errorResponse) {
|
|
|
|
return compileAttributeErrors(errorResponse)
|
|
|
|
}
|
2017-03-19 09:58:47 +08:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-05-14 04:37:49 +08:00
|
|
|
record := dnsimpleapi.ZoneRecordAttributes{
|
|
|
|
Name: dnsimpleapi.String(rc.GetLabel()),
|
2017-03-19 09:58:47 +08:00
|
|
|
Type: rc.Type,
|
2018-10-14 12:30:13 +08:00
|
|
|
Content: getTargetRecordContent(rc),
|
2017-03-19 09:58:47 +08:00
|
|
|
TTL: int(rc.TTL),
|
2018-10-14 12:30:13 +08:00
|
|
|
Priority: getTargetRecordPriority(rc),
|
2017-03-19 09:58:47 +08:00
|
|
|
}
|
|
|
|
|
2020-05-14 04:37:49 +08:00
|
|
|
_, err = client.Zones.UpdateRecord(context.Background(), accountID, domainName, old.ID, record)
|
2017-03-19 09:58:47 +08:00
|
|
|
if err != nil {
|
2022-09-23 23:55:09 +08:00
|
|
|
var errorResponse *dnsimpleapi.ErrorResponse
|
|
|
|
if errors.As(err, &errorResponse) {
|
|
|
|
return compileAttributeErrors(errorResponse)
|
|
|
|
}
|
2017-03-19 09:58:47 +08:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-21 04:07:34 +08:00
|
|
|
// ListZones returns all the zones in an account
|
2020-10-26 21:25:30 +08:00
|
|
|
func (c *dnsimpleProvider) ListZones() ([]string, error) {
|
2020-02-21 03:52:19 +08:00
|
|
|
client := c.getClient()
|
|
|
|
accountID, err := c.getAccountID()
|
|
|
|
if err != nil {
|
2022-09-23 23:55:09 +08:00
|
|
|
var errorResponse *dnsimpleapi.ErrorResponse
|
|
|
|
if errors.As(err, &errorResponse) {
|
|
|
|
return nil, compileAttributeErrors(errorResponse)
|
|
|
|
}
|
2020-02-21 03:52:19 +08:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
var zones []string
|
|
|
|
opts := &dnsimpleapi.ZoneListOptions{}
|
2020-05-14 04:37:49 +08:00
|
|
|
page := 1
|
2020-02-21 03:52:19 +08:00
|
|
|
for {
|
2020-05-14 04:37:49 +08:00
|
|
|
opts.Page = &page
|
|
|
|
zonesResponse, err := client.Zones.ListZones(context.Background(), accountID, opts)
|
2020-02-21 03:52:19 +08:00
|
|
|
if err != nil {
|
2022-09-23 23:55:09 +08:00
|
|
|
var errorResponse *dnsimpleapi.ErrorResponse
|
|
|
|
if errors.As(err, &errorResponse) {
|
|
|
|
return nil, compileAttributeErrors(errorResponse)
|
|
|
|
}
|
2020-02-21 03:52:19 +08:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
for _, zone := range zonesResponse.Data {
|
|
|
|
zones = append(zones, zone.Name)
|
|
|
|
}
|
|
|
|
pg := zonesResponse.Pagination
|
|
|
|
if pg.CurrentPage == pg.TotalPages {
|
|
|
|
break
|
|
|
|
}
|
2020-05-14 04:37:49 +08:00
|
|
|
page++
|
2020-02-21 03:52:19 +08:00
|
|
|
}
|
|
|
|
return zones, nil
|
|
|
|
}
|
|
|
|
|
2017-03-19 09:58:47 +08:00
|
|
|
// constructors
|
|
|
|
|
|
|
|
func newReg(conf map[string]string) (providers.Registrar, error) {
|
|
|
|
return newProvider(conf, nil)
|
|
|
|
}
|
|
|
|
|
|
|
|
func newDsp(conf map[string]string, metadata json.RawMessage) (providers.DNSServiceProvider, error) {
|
|
|
|
return newProvider(conf, metadata)
|
|
|
|
}
|
|
|
|
|
2022-07-12 02:52:18 +08:00
|
|
|
func newProvider(m map[string]string, _ json.RawMessage) (*dnsimpleProvider, error) {
|
2020-10-26 21:25:30 +08:00
|
|
|
api := &dnsimpleProvider{}
|
2017-03-19 09:58:47 +08:00
|
|
|
api.AccountToken = m["token"]
|
|
|
|
if api.AccountToken == "" {
|
Switch to Go 1.13 error wrapping (#604)
* Replaced errors.Wrap with fmt.Errorf (#589)
* Find: errors\.Wrap\(([^,]+),\s+(["`][^"`]*)(["`])\)
Replace: fmt.Errorf($2: %w$3, $1)
* Replaced errors.Wrapf with fmt.Errorf (#589)
* Find: errors\.Wrapf\(([^,]+),\s+(["`][^"`]*)(["`])\)
Replace: fmt.Errorf($2: %w$3, $1)
* Find: errors\.Wrapf\(([^,]+),\s+(["`][^"`]*)(["`])(,[^)]+)\)
* Replace: fmt.Errorf($2: %w$3$4, $1)
* Replaced errors.Errorf with fmt.Errorf (#589)
* Find: errors\.Errorf
Replace: fmt.Errorf
* Cleaned up remaining imports
* Cleanup
* Regenerate provider support matrix
This was broken by #533 ... and it's now the third time this has been missed.
2020-01-29 00:06:56 +08:00
|
|
|
return nil, fmt.Errorf("missing DNSimple token")
|
2017-03-19 09:58:47 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if m["baseurl"] != "" {
|
|
|
|
api.BaseURL = m["baseurl"]
|
|
|
|
}
|
|
|
|
|
|
|
|
return api, nil
|
|
|
|
}
|
|
|
|
|
2024-01-03 04:54:13 +08:00
|
|
|
// utilities
|
|
|
|
|
|
|
|
// Removes all non-dnsimple NS records from our desired state.
|
|
|
|
// If any are found, print a warning.
|
2022-03-29 03:28:40 +08:00
|
|
|
func removeOtherApexNS(dc *models.DomainConfig) {
|
2017-03-19 09:58:47 +08:00
|
|
|
newList := make([]*models.RecordConfig, 0, len(dc.Records))
|
|
|
|
for _, rec := range dc.Records {
|
2017-03-21 11:28:43 +08:00
|
|
|
if rec.Type == "NS" {
|
|
|
|
// apex NS inside dnsimple are expected.
|
2022-03-29 03:28:40 +08:00
|
|
|
// We ignore them, warning as needed.
|
|
|
|
// Child delegations are supported so we allow non-apex NS records.
|
|
|
|
if rec.GetLabelFQDN() == dc.Name {
|
2022-11-05 02:54:48 +08:00
|
|
|
if !isDnsimpleNameServerDomain(rec.GetTargetField()) {
|
2022-06-18 21:01:02 +08:00
|
|
|
printer.Printf("Warning: dnsimple.com does not allow NS records to be modified. %s will not be added.\n", rec.GetTargetField())
|
2022-03-29 03:28:40 +08:00
|
|
|
}
|
2017-03-21 11:28:43 +08:00
|
|
|
continue
|
2017-03-19 09:58:47 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
newList = append(newList, rec)
|
|
|
|
}
|
|
|
|
dc.Records = newList
|
|
|
|
}
|
2018-10-14 12:30:13 +08:00
|
|
|
|
2024-01-03 04:54:13 +08:00
|
|
|
// Returns the correct combined content for all special record types, Target for everything else
|
2018-10-14 12:30:13 +08:00
|
|
|
// Using RecordConfig.GetTargetCombined returns priority in the string, which we do not allow
|
|
|
|
func getTargetRecordContent(rc *models.RecordConfig) string {
|
|
|
|
switch rtype := rc.Type; rtype {
|
|
|
|
case "CAA":
|
|
|
|
return rc.GetTargetCombined()
|
2020-05-30 22:40:21 +08:00
|
|
|
case "DS":
|
|
|
|
return fmt.Sprintf("%d %d %d %s", rc.DsKeyTag, rc.DsAlgorithm, rc.DsDigestType, rc.DsDigest)
|
2022-03-03 00:19:15 +08:00
|
|
|
case "NAPTR":
|
2022-07-12 02:52:18 +08:00
|
|
|
return fmt.Sprintf(`%d %d "%s" "%s" "%s" %s`,
|
|
|
|
rc.NaptrOrder, rc.NaptrPreference, rc.NaptrFlags, rc.NaptrService, rc.NaptrRegexp,
|
2022-03-03 00:19:15 +08:00
|
|
|
rc.GetTargetField())
|
|
|
|
case "SSHFP":
|
|
|
|
return fmt.Sprintf("%d %d %s", rc.SshfpAlgorithm, rc.SshfpFingerprint, rc.GetTargetField())
|
2018-10-14 12:30:13 +08:00
|
|
|
case "SRV":
|
|
|
|
return fmt.Sprintf("%d %d %s", rc.SrvWeight, rc.SrvPort, rc.GetTargetField())
|
2020-02-22 02:23:30 +08:00
|
|
|
case "TXT":
|
2024-01-03 04:54:13 +08:00
|
|
|
return rc.GetTargetCombinedFunc(txtutil.EncodeQuoted)
|
2018-10-14 12:30:13 +08:00
|
|
|
default:
|
|
|
|
return rc.GetTargetField()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-03 04:54:13 +08:00
|
|
|
// Returns the correct priority for the record type, 0 for records without priority
|
2018-10-14 12:30:13 +08:00
|
|
|
func getTargetRecordPriority(rc *models.RecordConfig) int {
|
|
|
|
switch rtype := rc.Type; rtype {
|
|
|
|
case "MX":
|
|
|
|
return int(rc.MxPreference)
|
|
|
|
case "SRV":
|
|
|
|
return int(rc.SrvPriority)
|
2020-03-01 22:36:55 +08:00
|
|
|
case "NAPTR":
|
|
|
|
// Neither order nor preference
|
|
|
|
return 0
|
2018-10-14 12:30:13 +08:00
|
|
|
default:
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
}
|
2022-09-23 23:55:09 +08:00
|
|
|
|
2022-11-05 02:54:48 +08:00
|
|
|
// Compile the error messages returned by DNSimple's API into a single error message
|
2022-09-23 23:55:09 +08:00
|
|
|
func compileAttributeErrors(err *dnsimpleapi.ErrorResponse) error {
|
|
|
|
message := fmt.Sprintf("%d %s", err.HTTPResponse.StatusCode, err.Message)
|
|
|
|
for field, errors := range err.AttributeErrors {
|
|
|
|
e := strings.Join(errors, "& ")
|
|
|
|
message += fmt.Sprintf(": %s %s", field, e)
|
|
|
|
}
|
|
|
|
return fmt.Errorf(message)
|
|
|
|
}
|
2022-11-05 02:54:48 +08:00
|
|
|
|
|
|
|
// Return true if the string ends in one of DNSimple's name server domains
|
|
|
|
// False if anything else
|
|
|
|
func isDnsimpleNameServerDomain(name string) bool {
|
|
|
|
for _, i := range nameServerSuffixes {
|
|
|
|
if strings.HasSuffix(name, i) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
2024-01-03 04:54:13 +08:00
|
|
|
|
|
|
|
// Tests if the content is encoded, performing a naive check on the presence of quotes
|
|
|
|
// at the beginning and end of the string.
|
|
|
|
//
|
|
|
|
// This is a backward-compatible function to facilitate the TXT transition.
|
|
|
|
func isQuotedTXT(content string) bool {
|
|
|
|
return content[0:1] == `"` && content[len(content)-1:] == `"`
|
|
|
|
}
|