mirror of
https://github.com/StackExchange/dnscontrol.git
synced 2025-01-12 02:17:43 +08:00
4eb6fdface
The TTL values permitted may be different for each account and for each domain. Therefore we perform this query once per domain. * Fetch ClouDNS allowed TTL values from API (Fix #1078) * Add get available TTL values comment
248 lines
6.5 KiB
Go
248 lines
6.5 KiB
Go
package cloudns
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"net/http"
|
|
"strconv"
|
|
"time"
|
|
)
|
|
|
|
// Api layer for ClouDNS
|
|
type cloudnsProvider struct {
|
|
domainIndex map[string]string
|
|
nameserversNames []string
|
|
creds struct {
|
|
id string
|
|
password string
|
|
subid 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"`
|
|
DsKeyTag string `json:"key_tag,omitempty"`
|
|
DsAlgorithm string `json:"dsalgorithm,omitempty"`
|
|
DsDigestType string `json:"digest_type,omitempty"`
|
|
DsDigest string `json:"dsdigest,omitempty"`
|
|
}
|
|
|
|
type recordResponse map[string]domainRecord
|
|
|
|
var allowedTTLValues = []uint32{}
|
|
|
|
func (c *cloudnsProvider) fetchAvailableNameservers() error {
|
|
c.nameserversNames = nil
|
|
|
|
var bodyString, err = c.get("/dns/available-name-servers.json", requestParams{})
|
|
if err != nil {
|
|
return fmt.Errorf("failed 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 *cloudnsProvider) fetchAvailableTTLValues(domain string) error {
|
|
allowedTTLValues = nil
|
|
params := requestParams{
|
|
"domain-name": domain,
|
|
}
|
|
|
|
var bodyString, err = c.get("/dns/get-available-ttl.json", params)
|
|
if err != nil {
|
|
return fmt.Errorf("failed fetching available TTL values list from ClouDNS: %s", err)
|
|
}
|
|
|
|
json.Unmarshal(bodyString, &allowedTTLValues)
|
|
return nil
|
|
}
|
|
|
|
func (c *cloudnsProvider) 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 fmt.Errorf("failed 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 *cloudnsProvider) createDomain(domain string) error {
|
|
params := requestParams{
|
|
"domain-name": domain,
|
|
"zone-type": "master",
|
|
}
|
|
if _, err := c.get("/dns/register.json", params); err != nil {
|
|
return fmt.Errorf("failed create domain (ClouDNS): %s", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *cloudnsProvider) createRecord(domainID string, rec requestParams) error {
|
|
rec["domain-name"] = domainID
|
|
if _, err := c.get("/dns/add-record.json", rec); err != nil { // here we add record
|
|
return fmt.Errorf("failed create record (ClouDNS): %s", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *cloudnsProvider) 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 fmt.Errorf("failed delete record (ClouDNS): %s", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *cloudnsProvider) 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 fmt.Errorf("failed update (ClouDNS): %s", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *cloudnsProvider) getRecords(id string) ([]domainRecord, error) {
|
|
params := requestParams{"domain-name": id}
|
|
|
|
var bodyString, err = c.get("/dns/records.json", params)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed 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 *cloudnsProvider) 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: Support 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)
|
|
q.Add("sub-auth-id", c.creds.subid)
|
|
|
|
for pName, pValue := range params {
|
|
q.Add(pName, pValue)
|
|
}
|
|
|
|
req.URL.RawQuery = q.Encode()
|
|
|
|
// ClouDNS has a rate limit (not documented) of 10 request/second
|
|
// so we do a very primitive rate-limiting here - delay every request for 100ms - so max. 10 requests/second ...
|
|
time.Sleep(100 * time.Millisecond)
|
|
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 err == nil {
|
|
if errResp.Status == "Failed" {
|
|
return bodyString, fmt.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]
|
|
}
|