mirror of
https://github.com/StackExchange/dnscontrol.git
synced 2024-05-11 05:55:12 +00:00
NEW REGISTRAR: Dynadot (DYNADOT) (#2753)
Co-authored-by: Tom Limoncelli <tlimoncelli@stackoverflow.com>
This commit is contained in:
135
providers/dynadot/api.go
Normal file
135
providers/dynadot/api.go
Normal file
@@ -0,0 +1,135 @@
|
||||
package dynadot
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// API layer for Dynadot
|
||||
|
||||
type dynadotProvider struct {
|
||||
key string
|
||||
}
|
||||
|
||||
type requestParams map[string]string
|
||||
|
||||
type header struct {
|
||||
SuccessCode int `xml:"SuccessCode"`
|
||||
Status string `xml:"Status"`
|
||||
Error string `xml:"Error,omitempty"`
|
||||
}
|
||||
|
||||
type addNsResponse struct {
|
||||
XMLName xml.Name `xml:"AddNsResponse"`
|
||||
AddNsHeader header `xml:"AddNsHeader"`
|
||||
}
|
||||
|
||||
type setNsResponse struct {
|
||||
XMLName xml.Name `xml:"SetNsResponse"`
|
||||
SetNsHeader header `xml:"SetNsHeader"`
|
||||
}
|
||||
|
||||
type getNsResponse struct {
|
||||
XMLName xml.Name `xml:"GetNsResponse"`
|
||||
NsContent nsContent `xml:"NsContent"`
|
||||
GetNsHeader header `xml:"GetNsHeader"`
|
||||
}
|
||||
|
||||
type nsContent struct {
|
||||
Host []string `xml:"Host"`
|
||||
NsName string `xml:"NsName"`
|
||||
}
|
||||
|
||||
func (c *dynadotProvider) getNameservers(domain string) ([]string, error) {
|
||||
var bodyString, err = c.get("get_ns", requestParams{"domain": domain})
|
||||
if err != nil {
|
||||
return []string{}, fmt.Errorf("failed NS list (Dynadot): %s", err)
|
||||
}
|
||||
var ns getNsResponse
|
||||
xml.Unmarshal(bodyString, &ns)
|
||||
|
||||
if ns.GetNsHeader.SuccessCode != 0 {
|
||||
return []string{}, fmt.Errorf("failed NS list (Dynadot): %s", ns.GetNsHeader.Error)
|
||||
}
|
||||
|
||||
hosts := []string{}
|
||||
hosts = append(hosts, ns.NsContent.Host...)
|
||||
return hosts, nil
|
||||
}
|
||||
|
||||
func (c *dynadotProvider) updateNameservers(ns []string, domain string) error {
|
||||
if len(ns) > 13 {
|
||||
return fmt.Errorf("failed NS update (Dynadot): only up to 13 nameservers are supported")
|
||||
}
|
||||
|
||||
// Nameservers must first be added to the Dynadot account
|
||||
for _, host := range ns {
|
||||
b, err := c.get("add_ns", requestParams{"host": host})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed NS add (Dynadot): %s", err)
|
||||
}
|
||||
var resp addNsResponse
|
||||
err = xml.Unmarshal(b, &resp)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed NS add (Dynadot): %s", err)
|
||||
}
|
||||
|
||||
if resp.AddNsHeader.SuccessCode != 0 {
|
||||
// No apparent way to get all existing entries on an account, so filter
|
||||
if strings.Contains(resp.AddNsHeader.Error, "already exists") {
|
||||
continue
|
||||
}
|
||||
return fmt.Errorf("failed NS add (Dynadot): %s", resp.AddNsHeader.Error)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
rec := requestParams{}
|
||||
rec["domain"] = domain
|
||||
// supported prams: ns0 - ns12
|
||||
for i, h := range ns {
|
||||
rec[fmt.Sprintf("%s%d", "ns", i)] = h
|
||||
}
|
||||
|
||||
b, err := c.get("set_ns", rec)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed NS set (Dynadot): %s", err)
|
||||
}
|
||||
|
||||
var resp setNsResponse
|
||||
err = xml.Unmarshal(b, &resp)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed NS add (Dynadot): %s", err)
|
||||
}
|
||||
|
||||
if resp.SetNsHeader.SuccessCode != 0 {
|
||||
return fmt.Errorf("failed NS add (Dynadot): %s", resp.SetNsHeader.Error)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *dynadotProvider) get(command string, params requestParams) ([]byte, error) {
|
||||
client := &http.Client{}
|
||||
req, _ := http.NewRequest("GET", "https://api.dynadot.com/api3.xml", nil)
|
||||
q := req.URL.Query()
|
||||
|
||||
q.Add("key", c.key)
|
||||
q.Add("command", command)
|
||||
|
||||
for pName, pValue := range params {
|
||||
q.Add(pName, pValue)
|
||||
}
|
||||
|
||||
req.URL.RawQuery = q.Encode()
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
|
||||
return io.ReadAll(resp.Body)
|
||||
}
|
62
providers/dynadot/dynadotProvider.go
Normal file
62
providers/dynadot/dynadotProvider.go
Normal file
@@ -0,0 +1,62 @@
|
||||
package dynadot
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/StackExchange/dnscontrol/v4/models"
|
||||
"github.com/StackExchange/dnscontrol/v4/providers"
|
||||
)
|
||||
|
||||
/*
|
||||
|
||||
Dynadot Registrator:
|
||||
|
||||
Info required in `creds.json`:
|
||||
- key API Key
|
||||
|
||||
*/
|
||||
|
||||
func init() {
|
||||
providers.RegisterRegistrarType("DYNADOT", newDynadot)
|
||||
}
|
||||
|
||||
func newDynadot(m map[string]string) (providers.Registrar, error) {
|
||||
d := &dynadotProvider{}
|
||||
|
||||
d.key = m["key"]
|
||||
if d.key == "" {
|
||||
return nil, fmt.Errorf("missing Dynadot key")
|
||||
}
|
||||
|
||||
return d, nil
|
||||
}
|
||||
|
||||
func (c *dynadotProvider) GetRegistrarCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
|
||||
nss, err := c.getNameservers(dc.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
foundNameservers := strings.Join(nss, ",")
|
||||
|
||||
expected := []string{}
|
||||
for _, ns := range dc.Nameservers {
|
||||
name := strings.TrimRight(ns.Name, ".")
|
||||
expected = append(expected, name)
|
||||
}
|
||||
sort.Strings(expected)
|
||||
expectedNameservers := strings.Join(expected, ",")
|
||||
|
||||
if foundNameservers != expectedNameservers {
|
||||
return []*models.Correction{
|
||||
{
|
||||
Msg: fmt.Sprintf("Update nameservers (%s) -> (%s)", foundNameservers, expectedNameservers),
|
||||
F: func() error {
|
||||
return c.updateNameservers(expected, dc.Name)
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
Reference in New Issue
Block a user