mirror of
https://github.com/StackExchange/dnscontrol.git
synced 2024-05-11 05:55:12 +00:00
NEW PROVIDER: ClouDNS (#578)
* ClouDNS: first version of provider * ClouDNS: documentation * ClouDNS: code cleanup * ClouDNS: GetNameservers now uses ClouDNS API to fetch NS servers list * ClouDNS: CAA support * ClouDNS: TLSA support * ClouDNS: tests credentials now use variables instead of hardcoded values * ClouDNS: SSHFP support * ClouDNS: export only necessary methods
This commit is contained in:
committed by
Tom Limoncelli
parent
16d0043cce
commit
253cd07154
235
providers/cloudns/api.go
Normal file
235
providers/cloudns/api.go
Normal file
@@ -0,0 +1,235 @@
|
||||
package cloudns
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/pkg/errors"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Api layer for CloDNS
|
||||
type api struct {
|
||||
domainIndex map[string]string
|
||||
nameserversNames []string
|
||||
creds struct {
|
||||
id string
|
||||
password string
|
||||
}
|
||||
}
|
||||
|
||||
type requestParams map[string]string
|
||||
|
||||
type errorResponse struct {
|
||||
Status string `json:"status"`
|
||||
Description string `json:"statusDescription"`
|
||||
}
|
||||
|
||||
type nameserverRecord struct {
|
||||
Type string `json:"type"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type nameserverResponse []nameserverRecord
|
||||
|
||||
type zoneRecord struct {
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Status string `json:"status"`
|
||||
Zone string `json:"zone"`
|
||||
}
|
||||
|
||||
type zoneResponse []zoneRecord
|
||||
|
||||
type domainRecord struct {
|
||||
ID string `json:"id"`
|
||||
Type string `json:"type"`
|
||||
Host string `json:"host"`
|
||||
Target string `json:"record"`
|
||||
Priority string `json:"priority"`
|
||||
Weight string `json:"weight"`
|
||||
Port string `json:"port"`
|
||||
Service string `json:"service"`
|
||||
Protocol string `json:"protocol"`
|
||||
TTL string `json:"ttl"`
|
||||
Status int8 `json:"status"`
|
||||
CaaFlag string `json:"caa_flag,omitempty"`
|
||||
CaaTag string `json:"caa_type,omitempty"`
|
||||
CaaValue string `json:"caa_value,omitempty"`
|
||||
TlsaUsage string `json:"tlsa_usage,omitempty"`
|
||||
TlsaSelector string `json:"tlsa_selector,omitempty"`
|
||||
TlsaMatchingType string `json:"tlsa_matching_type,omitempty"`
|
||||
SshfpAlgorithm string `json:"algorithm,omitempty"`
|
||||
SshfpFingerprint string `json:"fp_type,omitempty"`
|
||||
}
|
||||
|
||||
type recordResponse map[string]domainRecord
|
||||
|
||||
var allowedTTLValues = []uint32{
|
||||
60, // 1 minute
|
||||
300, // 5 minutes
|
||||
900, // 15 minutes
|
||||
1800, // 30 minutes
|
||||
3600, // 1 hour
|
||||
21600, // 6 hours
|
||||
43200, // 12 hours
|
||||
86400, // 1 day
|
||||
172800, // 2 days
|
||||
259200, // 3 days
|
||||
604800, // 1 week
|
||||
1209600, // 2 weeks
|
||||
2419200, // 4 weeks
|
||||
}
|
||||
|
||||
func (c *api) fetchAvailableNameservers() error {
|
||||
c.nameserversNames = nil
|
||||
|
||||
var bodyString, err = c.get("/dns/available-name-servers.json", requestParams{})
|
||||
if err != nil {
|
||||
return errors.Errorf("Error fetching available nameservers list from ClouDNS: %s", err)
|
||||
}
|
||||
|
||||
var nr nameserverResponse
|
||||
json.Unmarshal(bodyString, &nr)
|
||||
|
||||
for _, nameserver := range nr {
|
||||
if nameserver.Type == "premium" {
|
||||
c.nameserversNames = append(c.nameserversNames, nameserver.Name)
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *api) fetchDomainList() error {
|
||||
c.domainIndex = map[string]string{}
|
||||
rowsPerPage := 100
|
||||
page := 1
|
||||
for {
|
||||
var dr zoneResponse
|
||||
params := requestParams{
|
||||
"page": strconv.Itoa(page),
|
||||
"rows-per-page": strconv.Itoa(rowsPerPage),
|
||||
}
|
||||
endpoint := "/dns/list-zones.json"
|
||||
var bodyString, err = c.get(endpoint, params)
|
||||
if err != nil {
|
||||
return errors.Errorf("Error fetching domain list from ClouDNS: %s", err)
|
||||
}
|
||||
json.Unmarshal(bodyString, &dr)
|
||||
|
||||
for _, domain := range dr {
|
||||
c.domainIndex[domain.Name] = domain.Name
|
||||
}
|
||||
if len(dr) < rowsPerPage {
|
||||
break
|
||||
}
|
||||
page++
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *api) createDomain(domain string) error {
|
||||
params := requestParams{
|
||||
"domain-name": domain,
|
||||
"zone-type": "master",
|
||||
}
|
||||
if _, err := c.get("/dns/register.json", params); err != nil {
|
||||
return errors.Errorf("Error create domain ClouDNS: %s", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *api) createRecord(domainID string, rec requestParams) error {
|
||||
rec["domain-name"] = domainID
|
||||
if _, err := c.get("/dns/add-record.json", rec); err != nil {
|
||||
return errors.Errorf("Error create record ClouDNS: %s", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *api) deleteRecord(domainID string, recordID string) error {
|
||||
params := requestParams{
|
||||
"domain-name": domainID,
|
||||
"record-id": recordID,
|
||||
}
|
||||
if _, err := c.get("/dns/delete-record.json", params); err != nil {
|
||||
return errors.Errorf("Error delete record ClouDNS: %s", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *api) modifyRecord(domainID string, recordID string, rec requestParams) error {
|
||||
rec["domain-name"] = domainID
|
||||
rec["record-id"] = recordID
|
||||
if _, err := c.get("/dns/mod-record.json", rec); err != nil {
|
||||
return errors.Errorf("Error create update ClouDNS: %s", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *api) getRecords(id string) ([]domainRecord, error) {
|
||||
params := requestParams{"domain-name": id}
|
||||
|
||||
var bodyString, err = c.get("/dns/records.json", params)
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("Error fetching record list from ClouDNS: %s", err)
|
||||
}
|
||||
|
||||
var dr recordResponse
|
||||
json.Unmarshal(bodyString, &dr)
|
||||
|
||||
var records []domainRecord
|
||||
for _, rec := range dr {
|
||||
records = append(records, rec)
|
||||
}
|
||||
return records, nil
|
||||
}
|
||||
|
||||
func (c *api) get(endpoint string, params requestParams) ([]byte, error) {
|
||||
client := &http.Client{}
|
||||
req, _ := http.NewRequest("GET", "https://api.cloudns.net"+endpoint, nil)
|
||||
q := req.URL.Query()
|
||||
|
||||
//TODO: Suport sub-auth-id / sub-auth-user https://asia.cloudns.net/wiki/article/42/
|
||||
// Add auth params
|
||||
q.Add("auth-id", c.creds.id)
|
||||
q.Add("auth-password", c.creds.password)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
bodyString, _ := ioutil.ReadAll(resp.Body)
|
||||
|
||||
// Got error from API ?
|
||||
var errResp errorResponse
|
||||
err = json.Unmarshal(bodyString, &errResp)
|
||||
if errResp.Status == "Failed" {
|
||||
return bodyString, errors.Errorf("ClouDNS API error: %s URL:%s%s ", errResp.Description, req.Host, req.URL.RequestURI())
|
||||
}
|
||||
|
||||
return bodyString, nil
|
||||
}
|
||||
|
||||
func fixTTL(ttl uint32) uint32 {
|
||||
// if the TTL is larger than the largest allowed value, return the largest allowed value
|
||||
if ttl > allowedTTLValues[len(allowedTTLValues)-1] {
|
||||
return allowedTTLValues[len(allowedTTLValues)-1]
|
||||
}
|
||||
|
||||
for _, v := range allowedTTLValues {
|
||||
if v >= ttl {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
return allowedTTLValues[0]
|
||||
}
|
||||
Reference in New Issue
Block a user