github.com/teknogeek/dnscontrol/v2@v2.10.1-0.20200227202244-ae299b55ba42/providers/cloudns/api.go (about) 1 package cloudns 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io/ioutil" 7 "net/http" 8 "strconv" 9 ) 10 11 // Api layer for CloDNS 12 type api struct { 13 domainIndex map[string]string 14 nameserversNames []string 15 creds struct { 16 id string 17 password string 18 } 19 } 20 21 type requestParams map[string]string 22 23 type errorResponse struct { 24 Status string `json:"status"` 25 Description string `json:"statusDescription"` 26 } 27 28 type nameserverRecord struct { 29 Type string `json:"type"` 30 Name string `json:"name"` 31 } 32 33 type nameserverResponse []nameserverRecord 34 35 type zoneRecord struct { 36 Name string `json:"name"` 37 Type string `json:"type"` 38 Status string `json:"status"` 39 Zone string `json:"zone"` 40 } 41 42 type zoneResponse []zoneRecord 43 44 type domainRecord struct { 45 ID string `json:"id"` 46 Type string `json:"type"` 47 Host string `json:"host"` 48 Target string `json:"record"` 49 Priority string `json:"priority"` 50 Weight string `json:"weight"` 51 Port string `json:"port"` 52 Service string `json:"service"` 53 Protocol string `json:"protocol"` 54 TTL string `json:"ttl"` 55 Status int8 `json:"status"` 56 CaaFlag string `json:"caa_flag,omitempty"` 57 CaaTag string `json:"caa_type,omitempty"` 58 CaaValue string `json:"caa_value,omitempty"` 59 TlsaUsage string `json:"tlsa_usage,omitempty"` 60 TlsaSelector string `json:"tlsa_selector,omitempty"` 61 TlsaMatchingType string `json:"tlsa_matching_type,omitempty"` 62 SshfpAlgorithm string `json:"algorithm,omitempty"` 63 SshfpFingerprint string `json:"fp_type,omitempty"` 64 } 65 66 type recordResponse map[string]domainRecord 67 68 var allowedTTLValues = []uint32{ 69 60, // 1 minute 70 300, // 5 minutes 71 900, // 15 minutes 72 1800, // 30 minutes 73 3600, // 1 hour 74 21600, // 6 hours 75 43200, // 12 hours 76 86400, // 1 day 77 172800, // 2 days 78 259200, // 3 days 79 604800, // 1 week 80 1209600, // 2 weeks 81 2419200, // 4 weeks 82 } 83 84 func (c *api) fetchAvailableNameservers() error { 85 c.nameserversNames = nil 86 87 var bodyString, err = c.get("/dns/available-name-servers.json", requestParams{}) 88 if err != nil { 89 return fmt.Errorf("Error fetching available nameservers list from ClouDNS: %s", err) 90 } 91 92 var nr nameserverResponse 93 json.Unmarshal(bodyString, &nr) 94 95 for _, nameserver := range nr { 96 if nameserver.Type == "premium" { 97 c.nameserversNames = append(c.nameserversNames, nameserver.Name) 98 } 99 100 } 101 return nil 102 } 103 104 func (c *api) fetchDomainList() error { 105 c.domainIndex = map[string]string{} 106 rowsPerPage := 100 107 page := 1 108 for { 109 var dr zoneResponse 110 params := requestParams{ 111 "page": strconv.Itoa(page), 112 "rows-per-page": strconv.Itoa(rowsPerPage), 113 } 114 endpoint := "/dns/list-zones.json" 115 var bodyString, err = c.get(endpoint, params) 116 if err != nil { 117 return fmt.Errorf("Error fetching domain list from ClouDNS: %s", err) 118 } 119 json.Unmarshal(bodyString, &dr) 120 121 for _, domain := range dr { 122 c.domainIndex[domain.Name] = domain.Name 123 } 124 if len(dr) < rowsPerPage { 125 break 126 } 127 page++ 128 } 129 return nil 130 } 131 132 func (c *api) createDomain(domain string) error { 133 params := requestParams{ 134 "domain-name": domain, 135 "zone-type": "master", 136 } 137 if _, err := c.get("/dns/register.json", params); err != nil { 138 return fmt.Errorf("Error create domain ClouDNS: %s", err) 139 } 140 return nil 141 } 142 143 func (c *api) createRecord(domainID string, rec requestParams) error { 144 rec["domain-name"] = domainID 145 if _, err := c.get("/dns/add-record.json", rec); err != nil { 146 return fmt.Errorf("Error create record ClouDNS: %s", err) 147 } 148 return nil 149 } 150 151 func (c *api) deleteRecord(domainID string, recordID string) error { 152 params := requestParams{ 153 "domain-name": domainID, 154 "record-id": recordID, 155 } 156 if _, err := c.get("/dns/delete-record.json", params); err != nil { 157 return fmt.Errorf("Error delete record ClouDNS: %s", err) 158 } 159 return nil 160 } 161 162 func (c *api) modifyRecord(domainID string, recordID string, rec requestParams) error { 163 rec["domain-name"] = domainID 164 rec["record-id"] = recordID 165 if _, err := c.get("/dns/mod-record.json", rec); err != nil { 166 return fmt.Errorf("Error create update ClouDNS: %s", err) 167 } 168 return nil 169 } 170 171 func (c *api) getRecords(id string) ([]domainRecord, error) { 172 params := requestParams{"domain-name": id} 173 174 var bodyString, err = c.get("/dns/records.json", params) 175 if err != nil { 176 return nil, fmt.Errorf("Error fetching record list from ClouDNS: %s", err) 177 } 178 179 var dr recordResponse 180 json.Unmarshal(bodyString, &dr) 181 182 var records []domainRecord 183 for _, rec := range dr { 184 records = append(records, rec) 185 } 186 return records, nil 187 } 188 189 func (c *api) get(endpoint string, params requestParams) ([]byte, error) { 190 client := &http.Client{} 191 req, _ := http.NewRequest("GET", "https://api.cloudns.net"+endpoint, nil) 192 q := req.URL.Query() 193 194 //TODO: Suport sub-auth-id / sub-auth-user https://asia.cloudns.net/wiki/article/42/ 195 // Add auth params 196 q.Add("auth-id", c.creds.id) 197 q.Add("auth-password", c.creds.password) 198 199 for pName, pValue := range params { 200 q.Add(pName, pValue) 201 } 202 203 req.URL.RawQuery = q.Encode() 204 205 resp, err := client.Do(req) 206 if err != nil { 207 return []byte{}, err 208 } 209 210 bodyString, _ := ioutil.ReadAll(resp.Body) 211 212 // Got error from API ? 213 var errResp errorResponse 214 err = json.Unmarshal(bodyString, &errResp) 215 if errResp.Status == "Failed" { 216 return bodyString, fmt.Errorf("ClouDNS API error: %s URL:%s%s ", errResp.Description, req.Host, req.URL.RequestURI()) 217 } 218 219 return bodyString, nil 220 } 221 222 func fixTTL(ttl uint32) uint32 { 223 // if the TTL is larger than the largest allowed value, return the largest allowed value 224 if ttl > allowedTTLValues[len(allowedTTLValues)-1] { 225 return allowedTTLValues[len(allowedTTLValues)-1] 226 } 227 228 for _, v := range allowedTTLValues { 229 if v >= ttl { 230 return v 231 } 232 } 233 234 return allowedTTLValues[0] 235 }