mirror of
https://github.com/StackExchange/dnscontrol.git
synced 2024-05-11 05:55:12 +00:00
* OVH DNS Provider (#143) This adds the OVH Provider along with its documentation. Unfortunately we can't set this DNS provider to support `CanUsePTR`, because OVH only supports setting PTR target on the Arpa zone. * OVH Registrar provider (#143) This implements OVH as a registrar provider. Note that NS modifications are done in a "best effort" mode, as the provider doesn't wait for the modifications to be fully applied (the operation that can take a long time). * Allow support for dual providers scenarios Since OVH released their APIv6, it is now possible to update zone apex NS records, opening the door to complete dual providers scenarii. This change implements apex NS management in an OVH zone.
158 lines
3.9 KiB
Go
158 lines
3.9 KiB
Go
package ovh
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/sha1"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"log"
|
|
"net/http"
|
|
"strconv"
|
|
"time"
|
|
)
|
|
|
|
// OVH endpoints list
|
|
const (
|
|
ENDPOINT_CA_OVHCOM = "https://ca.api.ovh.com/1.0"
|
|
ENDPOINT_CA_KIMSUFI = "https://ca.api.kimsufi.com/1.0"
|
|
ENDPOINT_CA_RUNABOVE = "https://api.runabove.com/1.0"
|
|
ENDPOINT_CA_SOYOUSTART = "https://ca.api.soyoustart.com/1.0"
|
|
ENDPOINT_EU_OVHCOM = "https://eu.api.ovh.com/1.0"
|
|
ENDPOINT_EU_KIMSUFI = "https://eu.api.kimsufi.com/1.0"
|
|
ENDPOINT_EU_RUNABOVE = "https://api.runabove.com/1.0"
|
|
ENDPOINT_EU_SOYOUSTART = "https://eu.api.soyoustart.com/1.0"
|
|
)
|
|
|
|
// Client helps interacting with OVH API endpoints.
|
|
type Client struct {
|
|
AppKey string
|
|
AppSecret string
|
|
ConsumerKey string
|
|
Endpoint string
|
|
TimeShift time.Duration
|
|
Debug bool
|
|
}
|
|
|
|
// NewClient builds up a new client link to the specified endpoint
|
|
// with given authentication information and no timeshift.
|
|
func NewClient(endpoint, ak, as, ck string, debug bool) *Client {
|
|
return &Client{
|
|
AppKey: ak,
|
|
AppSecret: as,
|
|
ConsumerKey: ck,
|
|
Endpoint: endpoint,
|
|
TimeShift: 0,
|
|
Debug: debug,
|
|
}
|
|
}
|
|
|
|
func computeSignature(appSecret, consumerKey, method, url string, body []byte, timestamp int64) string {
|
|
hasher := sha1.New()
|
|
pattern := fmt.Sprintf("%s+%s+%s+%s+%s+%d",
|
|
appSecret,
|
|
consumerKey,
|
|
method,
|
|
url,
|
|
body,
|
|
timestamp)
|
|
hasher.Write([]byte(pattern))
|
|
return fmt.Sprintf("$1$%x", hasher.Sum(nil))
|
|
}
|
|
|
|
func sendRequest(appKey, consumerKey, signature string, timestamp int64, method, url string, body []byte) ([]byte, error) {
|
|
req, err := http.NewRequest(method, url, bytes.NewBuffer(body))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
req.Header.Set("Content-Type", "application/json")
|
|
req.Header.Add("X-Ovh-Application", appKey)
|
|
req.Header.Add("X-Ovh-Consumer", consumerKey)
|
|
req.Header.Add("X-Ovh-Signature", signature)
|
|
req.Header.Add("X-Ovh-Timestamp", fmt.Sprintf("%d", timestamp))
|
|
|
|
httpClient := &http.Client{}
|
|
resp, err := httpClient.Do(req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
outBytes, err := ioutil.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode >= 300 {
|
|
return nil, fmt.Errorf("Unexpected HTTP return code (%s : %s).", resp.Status, outBytes)
|
|
}
|
|
|
|
return outBytes, err
|
|
}
|
|
|
|
// PollTimeshift calculates the difference between
|
|
// local and remote system time through a call to
|
|
// the API. It may be useful to call this function
|
|
// to avoid signatures to be rejected due to
|
|
// timeshift or network delay.
|
|
func (c *Client) PollTimeshift() error {
|
|
sysTime := time.Now()
|
|
resp, err := http.Get(c.Endpoint + "/auth/time")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
outPayload, err := ioutil.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
apiTime, err := strconv.ParseInt(string(outPayload), 10, 64)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
c.TimeShift = time.Unix(apiTime, 0).Sub(sysTime)
|
|
return err
|
|
}
|
|
|
|
// Call sends a request to the OVH API and returns response content.
|
|
// Input and output json processing will leverage json
|
|
// marshalling/unmarshalling of the specified interfaces.
|
|
func (c *Client) Call(method, path string, in interface{}, out interface{}) error {
|
|
var (
|
|
inBytes, outBytes []byte
|
|
err error
|
|
)
|
|
if in != nil {
|
|
inBytes, err = json.Marshal(in)
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
url := c.Endpoint + path
|
|
timestamp := time.Now().Add(c.TimeShift).Unix()
|
|
signature := computeSignature(c.AppSecret, c.ConsumerKey, method, url, inBytes, timestamp)
|
|
|
|
if c.Debug {
|
|
log.Printf("Method = %s", method)
|
|
log.Printf("URL = %s", url)
|
|
log.Printf("Timestamp = %d", timestamp)
|
|
log.Printf("Signature = %s", signature)
|
|
log.Printf("Body = %s", inBytes)
|
|
}
|
|
|
|
outBytes, err = sendRequest(c.AppKey, c.ConsumerKey, signature, timestamp, method, url, inBytes)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = json.Unmarshal(outBytes, &out)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *Client) SetDebug(debug bool) {
|
|
c.Debug = debug
|
|
}
|