163 lines
3.9 KiB
Go
163 lines
3.9 KiB
Go
package provider
|
|
|
|
import (
|
|
"fmt"
|
|
"io/ioutil"
|
|
"net"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/datahearth/ddnsclient/pkg/utils"
|
|
"github.com/sirupsen/logrus"
|
|
)
|
|
|
|
type Provider interface {
|
|
UpdateSubdomains(ip string) error
|
|
updateSubdomain(subdomain, ip string) error
|
|
retrieveSubdomainIP(addr string) (string, error)
|
|
checkResponse(body []byte, tokenBased bool, ip string) error
|
|
}
|
|
|
|
type provider struct {
|
|
logger logrus.FieldLogger
|
|
config utils.Config
|
|
name string
|
|
url string
|
|
}
|
|
|
|
// NewProvider creates a new instance of the `Provider` interface
|
|
func NewProvider(logger logrus.FieldLogger, config utils.Config, url, name string) (Provider, error) {
|
|
if logger == nil {
|
|
return nil, utils.ErrNilLogger
|
|
}
|
|
if name == "" {
|
|
return nil, utils.ErrInvalidName
|
|
}
|
|
if url == "" {
|
|
if utils.DefaultURLs[name] == "" {
|
|
return nil, utils.ErrInvalidURL
|
|
}
|
|
url = utils.DefaultURLs[name]
|
|
}
|
|
logger = logger.WithField("pkg", "providers")
|
|
|
|
return &provider{
|
|
config: config,
|
|
logger: logger,
|
|
name: name,
|
|
url: url,
|
|
}, nil
|
|
}
|
|
|
|
// UpdateSubdomains will watch for every defined subdomains if they need an update.
|
|
// If so, it'll trigger an update
|
|
func (p *provider) UpdateSubdomains(srvIP string) error {
|
|
for _, sb := range p.config.Subdomains {
|
|
ip, err := p.retrieveSubdomainIP(sb)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if ip == srvIP {
|
|
continue
|
|
}
|
|
|
|
p.logger.WithFields(logrus.Fields{
|
|
"component": "UpdateSubdomains",
|
|
"server-ip": srvIP,
|
|
"subdomain-address": ip,
|
|
"subdomain": sb,
|
|
}).Infoln("IP addresses doesn't match. Updating subdomain's ip...")
|
|
if err := p.updateSubdomain(sb, srvIP); err != nil {
|
|
p.logger.WithError(err).WithFields(logrus.Fields{
|
|
"component": "UpdateSubdomains",
|
|
"subdomain": sb,
|
|
"new-ip": srvIP,
|
|
}).Errorln("failed to update subdomain ip")
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (p *provider) updateSubdomain(subdomain, ip string) error {
|
|
tokenBased := p.config.Token != "" && (p.config.Username == "" && p.config.Password == "")
|
|
|
|
newURL := strings.ReplaceAll(p.url, "SUBDOMAIN", subdomain)
|
|
newURL = strings.ReplaceAll(newURL, "NEWIP", ip)
|
|
if tokenBased {
|
|
newURL = strings.ReplaceAll(newURL, "TOKEN", p.config.Token)
|
|
}
|
|
logger := p.logger.WithFields(logrus.Fields{
|
|
"component": "UpdateIP",
|
|
"updated-url": newURL,
|
|
"subdomain": subdomain,
|
|
"new-ip": ip,
|
|
})
|
|
|
|
req, err := http.NewRequest("GET", newURL, nil)
|
|
if err != nil {
|
|
return fmt.Errorf("%v: %v", utils.ErrCreateNewRequest, err)
|
|
}
|
|
if !tokenBased {
|
|
req.SetBasicAuth(p.config.Username, p.config.Password)
|
|
}
|
|
|
|
logger.Debugln("calling DDNS provider for subdomain update")
|
|
c := new(http.Client)
|
|
resp, err := c.Do(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if resp.ContentLength != 0 {
|
|
b, err := ioutil.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := p.checkResponse(b, tokenBased, ip); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if resp.StatusCode != 200 {
|
|
return utils.ErrWrongStatusCode
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (p *provider) retrieveSubdomainIP(addr string) (string, error) {
|
|
ips, err := net.LookupIP(addr)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
if len(ips) != 1 {
|
|
return "", fmt.Errorf("%v: %v", utils.ErrIpLenght, ips)
|
|
}
|
|
|
|
return ips[0].String(), nil
|
|
}
|
|
|
|
func (p *provider) checkResponse(body []byte, tokenBased bool, ip string) error {
|
|
var invalidResponse error
|
|
|
|
if tokenBased {
|
|
if !strings.Contains(string(body), "OK") {
|
|
if strings.Contains(string(body), "KO") {
|
|
invalidResponse = fmt.Errorf("invalid body response.\n Body response: %v", string(body))
|
|
} else {
|
|
invalidResponse = fmt.Errorf("unknown body response. Please fill a issue if you think this is an error.\n Body response: %v", string(body))
|
|
}
|
|
}
|
|
} else {
|
|
if !strings.Contains(string(body), "good "+ip) && !strings.Contains(string(body), "nochg "+ip) {
|
|
invalidResponse = fmt.Errorf("invalid body response.\n Body response: %v", string(body))
|
|
}
|
|
}
|
|
|
|
return invalidResponse
|
|
}
|