diff --git a/go.mod b/go.mod index 83bbda4..9263432 100644 --- a/go.mod +++ b/go.mod @@ -5,4 +5,6 @@ go 1.12 require ( 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 c396562..c003e37 100644 --- a/go.sum +++ b/go.sum @@ -1,22 +1,19 @@ -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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/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/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= -github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190609082536-301114b31cce h1:CQakrGkKbydnUmt7cFIlmQ4lNQiqdTPt6xzXij4nYCc= -golang.org/x/sys v0.0.0-20190609082536-301114b31cce/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk= +gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= +gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE= +gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw= diff --git a/main.go b/main.go index 2b58c23..2563b5d 100644 --- a/main.go +++ b/main.go @@ -6,18 +6,31 @@ import ( "fmt" log "github.com/sirupsen/logrus" "github.com/urfave/cli" + "gopkg.in/gomail.v2" "io/ioutil" "net/http" - "net/smtp" "os" "path" + "regexp" ) type Configuration struct { - Username string `json:"name"` - Email string `json:"email"` - GandiKey string `json:"gandikey"` - Domains []string `json:"domains"` + Username string `json:"name"` + Email string `json:"email"` + GandiKey string `json:"gandikey"` + Domains []string `json:"domains"` + RouterLogin string `json:"router_login"` + RouterPassword string `json:"router_password"` + RouterStatusUrl string `json:"router_status_url"` + WanIPRegex string `json:"wan_ip_regex"` + MailSettings *struct { + Sender string `json:"sender"` + To []string `json:"to"` + SmtpHost string `json:"smtp_host"` + SmtpLogin string `json:"smtp_login"` + SmtpPassword string `json:"smtp_password"` + SmtpPort int `json:"smtp_port"` + } `json:"mail_settings"` } type DnsResponse struct { @@ -28,22 +41,23 @@ type DnsResponse struct { } var ( - configPath string + configuration *Configuration + configPath string ) func main() { app := cli.NewApp() app.Name = "dnsupdater" - app.Version = "0.0.1" app.Usage = "Automatically update dns" + app.Before = before app.Action = start app.Flags = []cli.Flag{ cli.StringFlag{ Name: "config, C", Usage: "path to config file", - Value: "/config/conf.json", + Value: "/config", EnvVar: "CONFIG_PATH", }, } @@ -53,26 +67,35 @@ func main() { } } -func start(c *cli.Context) { - if err := update(c); err != nil { - log.Errorln(err) - SendStatusMail(fmt.Sprintf("Error during update process : %s", err)) - } -} - -func update(c *cli.Context) error { - configPath = c.String("config") +func before(c *cli.Context) error { + log.SetLevel(log.InfoLevel) + configPath = path.Join(c.String("config"), "conf.json") if confPathExists, _ := exists(configPath); !confPathExists { - configPath = "./data" + return fmt.Errorf("config file does not exist : %s", configPath) } - file, _ := os.Open(path.Join(configPath, "conf.json")) + file, _ := os.Open(configPath) defer file.Close() decoder := json.NewDecoder(file) - configuration := Configuration{} - err := decoder.Decode(&configuration) + configuration = &Configuration{} + err := decoder.Decode(configuration) + if err != nil { + return err + } + return nil +} +func start(c *cli.Context) error { + SendStatusMail("test") + if err := update(); err != nil { + SendStatusMail(fmt.Sprintf("Error during update process : %s", err)) + return err + } + return nil +} + +func update() error { newIp, err := GetOutboundIP() if err != nil { return err @@ -80,7 +103,7 @@ func update(c *cli.Context) error { updatedDomains := "" for _, domain := range configuration.Domains { - currentIp, err := GetCurrentIp(configuration, domain) + currentIp, err := GetCurrentIp(*configuration, domain) if err != nil { return err } @@ -89,9 +112,9 @@ func update(c *cli.Context) error { continue } - fmt.Printf("%s -> Ip has changed %s -> %s\n", domain, currentIp, newIp) + log.Infoln("%s -> Ip has changed %s -> %s\n", domain, currentIp, newIp) - err = SetCurrentIp(newIp, domain, configuration) + err = SetCurrentIp(newIp, domain, *configuration) if err != nil { return err } @@ -108,33 +131,30 @@ Dns updated successfully for domains } func SendStatusMail(messageText string) { - // user we are authorizing as - from := "laurent@lehouerou.net" - // use we are sending email to - to := "laurent@lehouerou.net" + settings := configuration.MailSettings + if settings == nil{ + log.Warnln("no smtp settings defined > status mail not sent") + return + } - // server we are authorized to send email through - host := "smtp.fastmail.com" + d := gomail.NewDialer(settings.SmtpHost, settings.SmtpPort, settings.SmtpLogin, settings.SmtpPassword) + s, err := d.Dial() + if err != nil { + log.Errorln("error while dialing smtp server : %v", err) + } - // Create the authentication for the SendMail() - // using PlainText, but other authentication methods are encouraged - auth := smtp.PlainAuth("", from, "c9bd8fb8l4bhs2f8", host) + m := gomail.NewMessage() + for _, r := range settings.To { + m.SetHeader("From", settings.Sender) + m.SetAddressHeader("To", r, "") + m.SetHeader("Subject", "DnsUpdater Status") + m.SetBody("text/plain", messageText) - // NOTE: Using the backtick here ` works like a heredoc, which is why all the - // rest of the lines are forced to the beginning of the line, otherwise the - // formatting is wrong for the RFC 822 style - message := fmt.Sprintf(`To: "Laurent Le Houerou" -From: "Laurent Le Houerou" -Subject: DnsUpdater Status - -%s -`, messageText) - - if err := smtp.SendMail(host+":587", auth, from, []string{to}, []byte(message)); err != nil { - fmt.Println("Error SendMail: ", err) - } else { - fmt.Println("Email Sent!") + if err := gomail.Send(s, m); err != nil { + log.Warnln("could not send email to %q: %v", r, err) + } + m.Reset() } } @@ -191,11 +211,17 @@ func SetCurrentIp(newip string, domain string, configuration Configuration) erro } func GetOutboundIP() (string, error) { - url := "https://api.ipify.org" + url := configuration.RouterStatusUrl + if url == "" { + url = "http://192.168.1.1/Status_Internet.live.asp" + } req, err := http.NewRequest("GET", url, nil) if err != nil { return "", err } + if configuration.RouterLogin != "" && configuration.RouterPassword != "" { + req.SetBasicAuth(configuration.RouterLogin, configuration.RouterPassword) + } res, err := http.DefaultClient.Do(req) if err != nil { return "", err @@ -205,7 +231,18 @@ func GetOutboundIP() (string, error) { if err != nil { return "", err } - return string(body), nil + + log.Debugln(string(body)) + wanipregex := configuration.WanIPRegex + if wanipregex == "" { + wanipregex = "wan_ipaddr::([0-9.]*)}" + } + r := regexp.MustCompile(wanipregex) + matches := r.FindStringSubmatch(string(body)) + if len(matches) < 2 { + return "", fmt.Errorf("unable to find WAN IP with regex %s in %s", wanipregex, string(body)) + } + return matches[1], nil } func exists(path string) (bool, error) {