mirror of
https://github.com/StackExchange/dnscontrol.git
synced 2024-05-11 05:55:12 +00:00
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
|
|
}
|