github.com/philhug/dnscontrol@v0.2.4-0.20180625181521-921fa9849001/providers/ovh/protocol.go (about) 1 package ovh 2 3 import ( 4 "fmt" 5 6 "github.com/StackExchange/dnscontrol/models" 7 "github.com/miekg/dns/dnsutil" 8 "github.com/pkg/errors" 9 ) 10 11 // Void an empty structure. 12 type Void struct { 13 } 14 15 // fetchDomainList gets list of zones for account 16 func (c *ovhProvider) fetchZones() error { 17 if c.zones != nil { 18 return nil 19 } 20 c.zones = map[string]bool{} 21 22 var response []string 23 24 err := c.client.Call("GET", "/domain/zone", nil, &response) 25 26 if err != nil { 27 return err 28 } 29 30 for _, d := range response { 31 c.zones[d] = true 32 } 33 return nil 34 } 35 36 // Zone describes the attributes of a DNS zone. 37 type Zone struct { 38 LastUpdate string `json:"lastUpdate,omitempty"` 39 HasDNSAnycast bool `json:"hasDNSAnycast,omitempty"` 40 NameServers []string `json:"nameServers"` 41 DNSSecSupported bool `json:"dnssecSupported"` 42 } 43 44 // get info about a zone. 45 func (c *ovhProvider) fetchZone(fqdn string) (*Zone, error) { 46 var response Zone 47 48 err := c.client.Call("GET", "/domain/zone/"+fqdn, nil, &response) 49 if err != nil { 50 return nil, err 51 } 52 53 return &response, nil 54 } 55 56 // Record describes a DNS record. 57 type Record struct { 58 Target string `json:"target,omitempty"` 59 Zone string `json:"zone,omitempty"` 60 TTL uint32 `json:"ttl,omitempty"` 61 FieldType string `json:"fieldType,omitempty"` 62 ID int64 `json:"id,omitempty"` 63 SubDomain string `json:"subDomain,omitempty"` 64 } 65 66 type records struct { 67 recordsID []int 68 } 69 70 func (c *ovhProvider) fetchRecords(fqdn string) ([]*Record, error) { 71 var recordIds []int 72 73 err := c.client.Call("GET", "/domain/zone/"+fqdn+"/record", nil, &recordIds) 74 if err != nil { 75 return nil, err 76 } 77 78 records := make([]*Record, len(recordIds)) 79 for i, id := range recordIds { 80 r, err := c.fecthRecord(fqdn, id) 81 if err != nil { 82 return nil, err 83 } 84 records[i] = r 85 } 86 87 return records, nil 88 } 89 90 func (c *ovhProvider) fecthRecord(fqdn string, id int) (*Record, error) { 91 var response Record 92 93 err := c.client.Call("GET", fmt.Sprintf("/domain/zone/%s/record/%d", fqdn, id), nil, &response) 94 if err != nil { 95 return nil, err 96 } 97 return &response, nil 98 } 99 100 // Returns a function that can be invoked to delete a record in a zone. 101 func (c *ovhProvider) deleteRecordFunc(id int64, fqdn string) func() error { 102 return func() error { 103 err := c.client.Call("DELETE", fmt.Sprintf("/domain/zone/%s/record/%d", fqdn, id), nil, nil) 104 if err != nil { 105 return err 106 } 107 return nil 108 } 109 } 110 111 // Returns a function that can be invoked to create a record in a zone. 112 func (c *ovhProvider) createRecordFunc(rc *models.RecordConfig, fqdn string) func() error { 113 return func() error { 114 record := Record{ 115 SubDomain: dnsutil.TrimDomainName(rc.GetLabelFQDN(), fqdn), 116 FieldType: rc.Type, 117 Target: rc.GetTargetCombined(), 118 TTL: rc.TTL, 119 } 120 if record.SubDomain == "@" { 121 record.SubDomain = "" 122 } 123 var response Record 124 err := c.client.Call("POST", fmt.Sprintf("/domain/zone/%s/record", fqdn), &record, &response) 125 return err 126 } 127 } 128 129 // Returns a function that can be invoked to update a record in a zone. 130 func (c *ovhProvider) updateRecordFunc(old *Record, rc *models.RecordConfig, fqdn string) func() error { 131 return func() error { 132 record := Record{ 133 SubDomain: rc.GetLabel(), 134 FieldType: rc.Type, 135 Target: rc.GetTargetCombined(), 136 TTL: rc.TTL, 137 Zone: fqdn, 138 ID: old.ID, 139 } 140 if record.SubDomain == "@" { 141 record.SubDomain = "" 142 } 143 144 return c.client.Call("PUT", fmt.Sprintf("/domain/zone/%s/record/%d", fqdn, old.ID), &record, &Void{}) 145 } 146 } 147 148 func (c *ovhProvider) refreshZone(fqdn string) error { 149 return c.client.Call("POST", fmt.Sprintf("/domain/zone/%s/refresh", fqdn), nil, &Void{}) 150 } 151 152 // fetch the NS OVH attributed to this zone (which is distinct from fetchRealNS which 153 // get the exact NS stored at the registrar 154 func (c *ovhProvider) fetchNS(fqdn string) ([]string, error) { 155 zone, err := c.fetchZone(fqdn) 156 if err != nil { 157 return nil, err 158 } 159 160 return zone.NameServers, nil 161 } 162 163 // CurrentNameServer stores information about nameservers. 164 type CurrentNameServer struct { 165 ToDelete bool `json:"toDelete,omitempty"` 166 IP string `json:"ip,omitempty"` 167 IsUsed bool `json:"isUsed,omitempty"` 168 ID int `json:"id,omitempty"` 169 Host string `json:"host,omitempty"` 170 } 171 172 // Retrieve the NS currently being deployed to the registrar 173 func (c *ovhProvider) fetchRegistrarNS(fqdn string) ([]string, error) { 174 var nameServersID []int 175 err := c.client.Call("GET", "/domain/"+fqdn+"/nameServer", nil, &nameServersID) 176 if err != nil { 177 return nil, err 178 } 179 180 var nameServers []string 181 for _, id := range nameServersID { 182 var ns CurrentNameServer 183 err = c.client.Call("GET", fmt.Sprintf("/domain/%s/nameServer/%d", fqdn, id), nil, &ns) 184 if err != nil { 185 return nil, err 186 } 187 188 // skip NS that we asked for deletion 189 if ns.ToDelete { 190 continue 191 } 192 nameServers = append(nameServers, ns.Host) 193 } 194 195 return nameServers, nil 196 } 197 198 // DomainNS describes a domain's NS in ovh's protocol. 199 type DomainNS struct { 200 Host string `json:"host,omitempty"` 201 IP string `json:"ip,omitempty"` 202 } 203 204 // UpdateNS describes a list of nameservers in ovh's protocol. 205 type UpdateNS struct { 206 NameServers []DomainNS `json:"nameServers"` 207 } 208 209 // Task describes a task in ovh's protocol. 210 type Task struct { 211 Function string `json:"function,omitempty"` 212 Status string `json:"status,omitempty"` 213 CanAccelerate bool `json:"canAccelerate,omitempty"` 214 LastUpdate string `json:"lastUpdate,omitempty"` 215 CreationDate string `json:"creationDate,omitempty"` 216 Comment string `json:"comment,omitempty"` 217 TodoDate string `json:"todoDate,omitempty"` 218 ID int64 `json:"id,omitempty"` 219 CanCancel bool `json:"canCancel,omitempty"` 220 DoneDate string `json:"doneDate,omitempty"` 221 CanRelaunch bool `json:"canRelaunch,omitempty"` 222 } 223 224 // Domain describes a domain in ovh's protocol. 225 type Domain struct { 226 NameServerType string `json:"nameServerType,omitempty"` 227 TransferLockStatus string `json:"transferLockStatus,omitempty"` 228 } 229 230 func (c *ovhProvider) updateNS(fqdn string, ns []string) error { 231 // we first need to make sure we can edit the NS 232 // by default zones are in "hosted" mode meaning they default 233 // to OVH default NS. In this mode, the NS can't be updated. 234 domain := Domain{NameServerType: "external"} 235 err := c.client.Call("PUT", fmt.Sprintf("/domain/%s", fqdn), &domain, &Void{}) 236 if err != nil { 237 return err 238 } 239 240 var newNs []DomainNS 241 for _, n := range ns { 242 newNs = append(newNs, DomainNS{ 243 Host: n, 244 }) 245 } 246 247 update := UpdateNS{ 248 NameServers: newNs, 249 } 250 var task Task 251 err = c.client.Call("POST", fmt.Sprintf("/domain/%s/nameServers/update", fqdn), &update, &task) 252 if err != nil { 253 return err 254 } 255 256 if task.Status == "error" { 257 return errors.Errorf("API error while updating ns for %s: %s", fqdn, task.Comment) 258 } 259 260 // we don't wait for the task execution. One of the reason is that 261 // NS modification can take time in the registrar, the other is that every task 262 // in OVH is usually executed a few minutes after they have been registered. 263 // We count on the fact that `GetNameservers` uses the registrar API to get 264 // a coherent view (including pending modifications) of the registered NS. 265 266 return nil 267 }