ddnsclient/pkg/watcher/watcher.go

172 lines
3.7 KiB
Go
Raw Normal View History

2021-03-14 16:36:24 +01:00
package watcher
import (
2022-09-02 21:40:23 +02:00
"encoding/json"
"fmt"
2021-05-27 12:33:02 +02:00
"io/ioutil"
2022-09-02 21:40:23 +02:00
"net"
2021-05-27 12:33:02 +02:00
"net/http"
2022-09-02 21:40:23 +02:00
"strings"
2021-03-15 19:13:11 +01:00
"time"
2021-05-17 13:15:39 +02:00
"github.com/datahearth/ddnsclient/pkg/provider"
2021-03-14 16:36:24 +01:00
"github.com/datahearth/ddnsclient/pkg/utils"
"github.com/sirupsen/logrus"
)
type Watcher interface {
2021-03-15 19:13:11 +01:00
Run(*time.Ticker, chan struct{}, chan error)
2021-05-27 12:33:02 +02:00
runDDNSCheck() error
retrieveServerIP() (string, error)
2021-03-14 16:36:24 +01:00
}
type watcher struct {
2021-05-17 13:15:39 +02:00
logger logrus.FieldLogger
providers []provider.Provider
firstRun bool
webIP string
providerName string
2021-03-14 16:36:24 +01:00
}
2021-05-27 12:33:02 +02:00
// NewWatcher creates a new instance of the `Watcher` interface
2021-05-17 13:15:39 +02:00
func NewWatcher(logger logrus.FieldLogger, w *utils.Watcher, webIP string) (Watcher, error) {
2021-03-14 16:36:24 +01:00
if logger == nil {
return nil, utils.ErrNilLogger
}
2021-05-17 13:15:39 +02:00
if w == nil {
return nil, utils.ErrNilWatcher
2021-03-14 16:36:24 +01:00
}
2021-03-15 08:31:47 +01:00
if webIP == "" {
2021-05-17 13:15:39 +02:00
webIP = utils.DefaultURLs["webIP"]
2021-03-14 16:36:24 +01:00
}
2021-03-17 08:14:06 +01:00
2021-05-17 13:15:39 +02:00
providers := []provider.Provider{}
for _, c := range w.Config {
p, err := provider.NewProvider(logger, c, w.URL, w.Name)
2021-03-15 08:31:47 +01:00
if err != nil {
return nil, err
}
2021-05-17 13:15:39 +02:00
providers = append(providers, p)
2021-03-15 08:31:47 +01:00
}
2021-05-17 13:15:39 +02:00
logger = logger.WithField("pkg", "watcher")
2021-03-14 16:36:24 +01:00
return &watcher{
2021-05-17 13:15:39 +02:00
logger: logger,
providers: providers,
webIP: webIP,
firstRun: true,
providerName: w.Name,
2021-03-14 16:36:24 +01:00
}, nil
}
2021-05-27 12:33:02 +02:00
// Run will trigger a subdomain update every X seconds (defined in config `update-time`)
2021-03-15 19:13:11 +01:00
func (w *watcher) Run(t *time.Ticker, chClose chan struct{}, chErr chan error) {
2021-03-17 08:14:06 +01:00
logger := w.logger.WithField("component", "Run")
if w.firstRun {
if err := w.runDDNSCheck(); err != nil {
chErr <- err
}
w.firstRun = false
}
2021-03-15 19:13:11 +01:00
2021-03-15 08:31:47 +01:00
for {
select {
2021-03-15 19:13:11 +01:00
case <-chClose:
t.Stop()
2021-05-17 13:15:39 +02:00
logger.WithField("provider", w.providerName).Infoln("Close watcher channel triggered. Ticker stopped")
2021-03-15 19:13:11 +01:00
return
case <-t.C:
2021-03-17 08:14:06 +01:00
if err := w.runDDNSCheck(); err != nil {
2021-03-15 19:13:11 +01:00
chErr <- err
2021-03-15 08:31:47 +01:00
}
2021-03-17 08:14:06 +01:00
}
}
}
func (w *watcher) runDDNSCheck() error {
logger := w.logger.WithField("component", "runDDNSCheck")
logger.Infof("Starting [%s] DDNS check...\n", w.providerName)
2021-05-17 13:15:39 +02:00
logger.Debugln("Checking server IP...")
2021-05-27 12:33:02 +02:00
srvIP, err := w.retrieveServerIP()
2021-03-17 08:14:06 +01:00
if err != nil {
return err
}
2021-05-17 13:15:39 +02:00
for _, p := range w.providers {
if err := p.UpdateSubdomains(srvIP); err != nil {
2021-03-17 08:14:06 +01:00
return err
}
}
logger.Infof("[%s] DDNS check finished\n", w.providerName)
2021-03-17 08:14:06 +01:00
return nil
}
2021-05-27 12:33:02 +02:00
func (w *watcher) retrieveServerIP() (string, error) {
2022-09-02 21:40:23 +02:00
rsp, err := http.Get(w.webIP)
2021-05-27 12:33:02 +02:00
if err != nil {
2022-09-02 21:40:23 +02:00
return "", fmt.Errorf("%v: %v", utils.ErrGetServerIP, err)
2021-05-27 12:33:02 +02:00
}
2022-09-02 21:40:23 +02:00
if rsp.StatusCode != 200 {
return "", fmt.Errorf("%v: %v", utils.ErrWrongStatusCode, rsp.Status)
2021-05-27 12:33:02 +02:00
}
2022-09-02 21:40:23 +02:00
b, err := ioutil.ReadAll(rsp.Body)
2021-05-27 12:33:02 +02:00
if err != nil {
2022-09-02 21:40:23 +02:00
return "", fmt.Errorf("%v: %v", utils.ErrParseHTTPBody, err)
2021-05-27 12:33:02 +02:00
}
2022-09-02 21:40:23 +02:00
var ip string
2023-01-31 20:16:41 +01:00
if strings.Contains(rsp.Header.Get("content-type"), "application/json") {
2022-09-02 21:40:23 +02:00
body := make(map[string]interface{})
if err := json.Unmarshal(b, &body); err != nil {
return "", fmt.Errorf("failed to unmarshal JSON body: %v", err)
}
if v, ok := body["ip"]; ok {
if v, ok := v.(string); ok {
ip = v
} else {
return "", fmt.Errorf("\"ip\" field found in JSON response but is not a string value")
}
} else {
for _, v := range body {
if v, ok := v.(string); ok {
if parsedIP := net.ParseIP(v); parsedIP != nil {
ip = parsedIP.String()
}
}
}
if ip == "" {
return "", fmt.Errorf("no field found with a valid IPv4 address in JSON response")
}
}
} else {
body := string(b)
if strings.Count(body, "\n") > 0 {
for _, l := range strings.Split(body, "\n") {
if parsedIP := net.ParseIP(l); parsedIP != nil {
ip = parsedIP.String()
break
}
}
if ip == "" {
return "", fmt.Errorf("no field found with a valid IPv4 address in body response")
}
} else {
if body == "" {
return "", fmt.Errorf("body is empty")
}
ip = body
}
}
return ip, nil
2021-05-27 12:33:02 +02:00
}