diff --git a/go.mod b/go.mod index 2d418b5..f46ff12 100644 --- a/go.mod +++ b/go.mod @@ -1,10 +1,13 @@ module gogs.lehouerou.net/Laurent/dnsupdater -go 1.12 +go 1.13 require ( + github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927 // indirect + github.com/dghubble/sling v1.3.0 github.com/matryer/try v0.0.0-20161228173917-9ac251b645a2 github.com/sirupsen/logrus v1.4.2 github.com/urfave/cli v1.20.0 + gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df ) diff --git a/go.sum b/go.sum index b99b2a2..9db8d3c 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,11 @@ +github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927 h1:SKI1/fuSdodxmNNyVBR8d7X/HuLnRpvvFO0AgyQk764= +github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927/go.mod h1:h/aW8ynjgkuj+NQRlZcDbAbM1ORAbXjXX77sX7T289U= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dghubble/sling v1.3.0 h1:pZHjCJq4zJvc6qVQ5wN1jo5oNZlNE0+8T/h0XeXBUKU= +github.com/dghubble/sling v1.3.0/go.mod h1:XXShWaBWKzNLhu2OxikSNFrlsvowtz4kyRuXUG7oQKY= +github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= +github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/matryer/try v0.0.0-20161228173917-9ac251b645a2 h1:JAEbJn3j/FrhdWA9jW8B5ajsLIjeuEHLi8xE4fk997o= diff --git a/main.go b/main.go index 147a6ba..0023dc0 100644 --- a/main.go +++ b/main.go @@ -1,19 +1,20 @@ package main import ( - "bytes" "encoding/json" "fmt" - "github.com/matryer/try" - log "github.com/sirupsen/logrus" - "github.com/urfave/cli" - "gopkg.in/gomail.v2" "io/ioutil" "net/http" "os" "path" "regexp" "time" + + "github.com/dghubble/sling" + "github.com/matryer/try" + log "github.com/sirupsen/logrus" + "github.com/urfave/cli" + "gopkg.in/gomail.v2" ) type Configuration struct { @@ -43,12 +44,14 @@ type DnsResponse struct { } var ( - configuration *Configuration + configuration Configuration configPath string + base *sling.Sling ) const ( - GandiARecordUrl = "https://dns.api.gandi.net/api/v5/domains/%s/records/@/A" + GandiApiUrl = "https://dns.api.gandi.net/api/v5/" + GandiARecordUrl = "domains/%s/records/@/A" ) func main() { @@ -83,15 +86,21 @@ func before(c *cli.Context) error { } defer file.Close() decoder := json.NewDecoder(file) - configuration = &Configuration{} - err = decoder.Decode(configuration) + configuration = Configuration{} + err = decoder.Decode(&configuration) if err != nil { return err } + + base = sling.New().Client(&http.Client{ + Timeout: 30 * time.Second, + }).Base(GandiApiUrl). + Set("X-Api-Key", configuration.GandiKey) + return nil } -func start(c *cli.Context) error { +func start(*cli.Context) error { if err := update(); err != nil { SendStatusMail(fmt.Sprintf("Error during update process : %s", err)) return err @@ -102,25 +111,25 @@ func start(c *cli.Context) error { func update() error { newIp, err := GetOutboundIP() if err != nil { - return err + return fmt.Errorf("getting outbound ip: %v", err) } updatedDomains := "" for _, domain := range configuration.Domains { - currentIp, err := GetCurrentIp(*configuration, domain) + currentIp, err := GetCurrentIp(domain) if err != nil { - return err + return fmt.Errorf("getting current ip: %v", err) } if currentIp == newIp { continue } - log.Infoln(fmt.Sprintf("%s -> Ip has changed %s -> %s", domain, currentIp, newIp)) + log.Infof(fmt.Sprintf("%s -> Ip has changed %s -> %s", domain, currentIp, newIp)) - err = SetCurrentIp(newIp, domain, *configuration) + err = SetCurrentIp(newIp, domain) if err != nil { - return err + return fmt.Errorf("setting current ip: %v", err) } updatedDomains += fmt.Sprintf("\t- %s\n", domain) } @@ -138,14 +147,14 @@ func SendStatusMail(messageText string) { settings := configuration.MailSettings if settings == nil { - log.Warnln("no smtp settings defined > status mail not sent") + log.Warnf("no smtp settings defined > status mail not sent") return } d := gomail.NewDialer(settings.SmtpHost, settings.SmtpPort, settings.SmtpLogin, settings.SmtpPassword) s, err := d.Dial() if err != nil { - log.Errorln(fmt.Sprintf("error while dialing smtp server : %v", err)) + log.Errorf(fmt.Sprintf("error while dialing smtp server : %v", err)) return } @@ -157,38 +166,31 @@ func SendStatusMail(messageText string) { m.SetBody("text/plain", messageText) if err := gomail.Send(s, m); err != nil { - log.Warnln(fmt.Sprintf("could not send email to %q: %v", r, err)) + log.Warnf(fmt.Sprintf("could not send email to %q: %v", r, err)) } m.Reset() } } -func GetCurrentIp(configuration Configuration, domain string) (string, error) { +func GetCurrentIp(domain string) (string, error) { var value string err := try.Do(func(attempt int) (bool, error) { var err error - value, err = func(configuration Configuration, domain string) (string, error) { - url := fmt.Sprintf(GandiARecordUrl, domain) - req, err := http.NewRequest("GET", url, nil) - if err != nil { - return "", err - } - req.Header.Set("X-Api-Key", configuration.GandiKey) - client := &http.Client{} - resp, err := client.Do(req) - if err != nil { - return "", err - } - defer resp.Body.Close() - - decoder := json.NewDecoder(resp.Body) + value, err = func(domain string) (string, error) { dnsresponse := DnsResponse{} - err = decoder.Decode(&dnsresponse) + resp, err := base.New().Get(fmt.Sprintf(GandiARecordUrl, domain)). + ReceiveSuccess(&dnsresponse) if err != nil { - return "", err + return "", fmt.Errorf("requesting gandi dns info: %v", err) + } + if resp.StatusCode < 200 || resp.StatusCode > 299 { + return "", fmt.Errorf("requesting gandi dns info, no 2xx http status code: %d", resp.StatusCode) + } + if len(dnsresponse.Values) == 0 { + return "", fmt.Errorf("no values in dnsresponse") } return dnsresponse.Values[0], nil - }(configuration, domain) + }(domain) if err != nil { log.Warnln(fmt.Sprintf("Error while getting current ip from gandi : %v", err)) time.Sleep(30 * time.Second) @@ -201,31 +203,21 @@ func GetCurrentIp(configuration Configuration, domain string) (string, error) { return value, nil } -func SetCurrentIp(newip string, domain string, configuration Configuration) error { - url := fmt.Sprintf(GandiARecordUrl, domain) - log.Infoln("URL:>", url) - - var str = fmt.Sprintf("{\"rrset_ttl\": %d,\"rrset_values\": [\"%s\"]}", 600, newip) - log.Infoln("json:", str) - var jsonStr = []byte(str) - req, err := http.NewRequest("PUT", url, bytes.NewBuffer(jsonStr)) +func SetCurrentIp(newip string, domain string) error { + resp, err := base.New().Put(fmt.Sprintf(GandiARecordUrl, domain)). + BodyJSON(struct { + TTL int `json:"rrset_ttl"` + Values []string `json:"rrset_values"` + }{ + TTL: 600, + Values: []string{newip}, + }).ReceiveSuccess(nil) if err != nil { - return err + return fmt.Errorf("setting gandi dns info: %v", err) } - req.Header.Set("X-Api-Key", configuration.GandiKey) - req.Header.Set("Content-Type", "application/json") - - client := &http.Client{} - resp, err := client.Do(req) - if err != nil { - return err + if resp.StatusCode < 200 || resp.StatusCode > 299 { + return fmt.Errorf("setting gandi dns info, no 2xx http status code: %d", resp.StatusCode) } - defer resp.Body.Close() - - log.Debugln("response Status:", resp.Status) - log.Debugln("response Headers:", resp.Header) - body, _ := ioutil.ReadAll(resp.Body) - log.Debugln("response Body:", string(body)) return nil } @@ -236,22 +228,21 @@ func GetOutboundIP() (string, error) { } req, err := http.NewRequest("GET", url, nil) if err != nil { - return "", err + return "", fmt.Errorf("creating http request: %v", err) } if configuration.RouterLogin != "" && configuration.RouterPassword != "" { req.SetBasicAuth(configuration.RouterLogin, configuration.RouterPassword) } res, err := http.DefaultClient.Do(req) if err != nil { - return "", err + return "", fmt.Errorf("executing http request: %v", err) } defer res.Body.Close() body, err := ioutil.ReadAll(res.Body) if err != nil { - return "", err + return "", fmt.Errorf("reading response body: %v", err) } - log.Debugln(string(body)) wanipregex := configuration.WanIPRegex if wanipregex == "" { wanipregex = "wan_ipaddr::([0-9.]*)}"