WIP
This commit is contained in:
parent
0ed762638a
commit
05c98a9adb
14
README.md
14
README.md
|
@ -1,5 +1,17 @@
|
||||||
# DDNS Client
|
# DDNS Client
|
||||||
|
|
||||||
|
## How to install DDNS-CLIENT
|
||||||
|
|
||||||
|
Simply run the command `go get github.com/datahearth/ddnsclient`
|
||||||
|
|
||||||
|
## Run the client
|
||||||
|
|
||||||
|
You have 2 options to run the DDNS client.
|
||||||
|
You can either run it as a docker container:
|
||||||
|
`PENDING...`
|
||||||
|
Or as binary executable:
|
||||||
|
`PENDING...`
|
||||||
|
|
||||||
## Supported providers
|
## Supported providers
|
||||||
|
|
||||||
- OVH (list of available [regions](https://github.com/ovh/php-ovh/#supported-apis))
|
- OVH (list of available [regions](https://github.com/ovh/php-ovh/#supported-apis). TO BE CHECKED HOW TO SHIT WORKS)
|
25
main.go
25
main.go
|
@ -1,31 +1,30 @@
|
||||||
package ddnsclient
|
package ddnsclient
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/datahearth/ddnsclient/pkg/http"
|
"time"
|
||||||
|
|
||||||
"github.com/datahearth/ddnsclient/pkg/providers/ovh"
|
"github.com/datahearth/ddnsclient/pkg/providers/ovh"
|
||||||
|
"github.com/datahearth/ddnsclient/pkg/watcher"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Start create a new instance of ddns-client
|
// Start create a new instance of ddns-client
|
||||||
func Start(logger logrus.FieldLogger) error {
|
func Start(logger logrus.FieldLogger) error {
|
||||||
ddnsHTTP, err := http.NewHTTP(logger)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
ovh, err := ovh.NewOVH(logger)
|
ovh, err := ovh.NewOVH(logger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
check := make(chan bool)
|
|
||||||
|
w, err := watcher.NewWatcher(logger, ovh, viper.GetString("web-ip"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
c := make(chan bool)
|
c := make(chan bool)
|
||||||
|
go w.Run(c)
|
||||||
for {
|
for {
|
||||||
select {
|
|
||||||
case <-check:
|
|
||||||
|
|
||||||
case <-c:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
132
pkg/http/main.go
132
pkg/http/main.go
|
@ -1,132 +0,0 @@
|
||||||
package http
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io/ioutil"
|
|
||||||
"net"
|
|
||||||
h "net/http"
|
|
||||||
|
|
||||||
"github.com/datahearth/ddnsclient/pkg/utils"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
)
|
|
||||||
|
|
||||||
// HTTP is the base interface to interact with websites
|
|
||||||
type HTTP interface {
|
|
||||||
CheckIPAddr(addr string) (bool, error)
|
|
||||||
GetServerIP() (string, error)
|
|
||||||
GetSubdomainIP(addr string) (string, error)
|
|
||||||
RetrieveSubdomainIP(addr string) error
|
|
||||||
RetrieveServerIP() error
|
|
||||||
}
|
|
||||||
|
|
||||||
type http struct {
|
|
||||||
logger logrus.FieldLogger
|
|
||||||
serverIP string
|
|
||||||
subdomainIP string
|
|
||||||
webIP string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewHTTP instanciate a new http implementation
|
|
||||||
func NewHTTP(logger logrus.FieldLogger) (HTTP, error) {
|
|
||||||
if logger == nil {
|
|
||||||
return nil, utils.ErrNilLogger
|
|
||||||
}
|
|
||||||
|
|
||||||
logger = logger.WithField("package", "http")
|
|
||||||
webIP := viper.GetString("web-ip")
|
|
||||||
|
|
||||||
if webIP == "" {
|
|
||||||
logger.Infoln("web-ip field not set in config. Using default")
|
|
||||||
webIP = "http://dynamicdns.park-your-domain.com/getip"
|
|
||||||
}
|
|
||||||
|
|
||||||
return &http{
|
|
||||||
logger: logger,
|
|
||||||
webIP: webIP,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// RetrieveServerIP will use the defined web-ip service to get the server public address and save it to the struct
|
|
||||||
func (http *http) RetrieveServerIP() error {
|
|
||||||
resp, err := h.Get(http.webIP)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if resp.StatusCode != 200 {
|
|
||||||
return utils.ErrWrongStatusCode
|
|
||||||
}
|
|
||||||
d, err := ioutil.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
http.serverIP = string(d)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetServerIP will return the IP addr of the server.
|
|
||||||
// If you haven't triggered the function before manually or with CheckIPAddr,
|
|
||||||
// it'll trigger the RetrieveServerIP function and save it. Then, it'll return the IP
|
|
||||||
func (http *http) GetServerIP() (string, error) {
|
|
||||||
if http.serverIP == "" {
|
|
||||||
if err := http.RetrieveServerIP(); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return http.serverIP, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// RetrieveSubdomainIP will retrieve the subdomain IP with a HEAD request then save it to the struct
|
|
||||||
func (http *http) RetrieveSubdomainIP(addr string) error {
|
|
||||||
resp, err := h.Head(addr)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if resp.StatusCode != 200 {
|
|
||||||
return utils.ErrWrongStatusCode
|
|
||||||
}
|
|
||||||
|
|
||||||
h, _, err := net.SplitHostPort(resp.Request.RemoteAddr)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
http.subdomainIP = h
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetSubdomainIP will return the IP addr of the subdomain.
|
|
||||||
// If you haven't triggered the function before manually or with CheckIPAddr,
|
|
||||||
// it'll trigger the RetrieveSubdomainIP function and save it. Then, it'll return the IP
|
|
||||||
func (http *http) GetSubdomainIP(addr string) (string, error) {
|
|
||||||
if http.subdomainIP == "" {
|
|
||||||
if err := http.RetrieveSubdomainIP(addr); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return http.subdomainIP, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CheckIPAddr will get both ip addr (subdomain from addr and server) and compare it.
|
|
||||||
// If one of them is missing, it'll retrieve it and then save it
|
|
||||||
func (http *http) CheckIPAddr(addr string) (bool, error) {
|
|
||||||
srvIP, err := http.GetServerIP()
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
subIP, err := http.GetSubdomainIP(addr)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if srvIP != subIP {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return true, nil
|
|
||||||
}
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
package subdomain
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
h "net/http"
|
||||||
|
|
||||||
|
"github.com/datahearth/ddnsclient/pkg/utils"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HTTP is the base interface to interact with websites
|
||||||
|
type Subdomain interface {
|
||||||
|
CheckIPAddr(srvIP string) (bool, error)
|
||||||
|
GetSubdomainIP() string
|
||||||
|
retrieveSubdomainIP() error
|
||||||
|
}
|
||||||
|
|
||||||
|
type subdomain struct {
|
||||||
|
logger logrus.FieldLogger
|
||||||
|
subdomainAddr string
|
||||||
|
ip string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSubdomain instanciate a new http implementation
|
||||||
|
func NewSubdomain(logger logrus.FieldLogger, subdomainAddr string) (Subdomain, error) {
|
||||||
|
if logger == nil {
|
||||||
|
return nil, utils.ErrNilLogger
|
||||||
|
}
|
||||||
|
|
||||||
|
logger = logger.WithField("package", "http")
|
||||||
|
|
||||||
|
return &subdomain{
|
||||||
|
logger: logger,
|
||||||
|
subdomainAddr: subdomainAddr,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RetrieveSubdomainIP will retrieve the subdomain IP with a HEAD request
|
||||||
|
func (sd *subdomain) retrieveSubdomainIP() error {
|
||||||
|
resp, err := h.Head(sd.subdomainAddr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if resp.StatusCode != 200 {
|
||||||
|
return utils.ErrWrongStatusCode
|
||||||
|
}
|
||||||
|
|
||||||
|
h, _, err := net.SplitHostPort(resp.Request.RemoteAddr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
sd.ip = h
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckIPAddr will compare the srvIP passed in parameter and the subIP retrieved from the head request
|
||||||
|
func (sd *subdomain) CheckIPAddr(srvIP string) (bool, error) {
|
||||||
|
if err := sd.retrieveSubdomainIP(); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if srvIP != sd.ip {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sd *subdomain) GetSubdomainIP() string {
|
||||||
|
return sd.ip
|
||||||
|
}
|
|
@ -1,10 +0,0 @@
|
||||||
package utils
|
|
||||||
|
|
||||||
func AggregateSubdomains(subdomains []string, domain string) []string {
|
|
||||||
agdSub := make([]string, len(subdomains))
|
|
||||||
for _, sd := range subdomains {
|
|
||||||
agdSub = append(agdSub, sd+"."+domain)
|
|
||||||
}
|
|
||||||
|
|
||||||
return agdSub
|
|
||||||
}
|
|
|
@ -1,6 +1,9 @@
|
||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
@ -50,3 +53,32 @@ func SetupLogger(logger *logrus.Logger) {
|
||||||
DisableTimestamp: timestamp,
|
DisableTimestamp: timestamp,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func AggregateSubdomains(subdomains []string, domain string) []string {
|
||||||
|
agdSub := make([]string, len(subdomains))
|
||||||
|
for _, sd := range subdomains {
|
||||||
|
agdSub = append(agdSub, sd+"."+domain)
|
||||||
|
}
|
||||||
|
|
||||||
|
return agdSub
|
||||||
|
}
|
||||||
|
|
||||||
|
// RetrieveServerIP will use the defined web-ip service to get the server public address and save it to the struct
|
||||||
|
func RetrieveServerIP(webIP) (string, error) {
|
||||||
|
// * retrieve client's server IP
|
||||||
|
resp, err := http.Get(webIP)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if resp.StatusCode != 200 {
|
||||||
|
return "", ErrWrongStatusCode
|
||||||
|
}
|
||||||
|
|
||||||
|
// * get ip from body
|
||||||
|
d, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(d), nil
|
||||||
|
}
|
|
@ -1,47 +1,76 @@
|
||||||
package watcher
|
package watcher
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/datahearth/ddnsclient/pkg/http"
|
|
||||||
"github.com/datahearth/ddnsclient/pkg/providers"
|
"github.com/datahearth/ddnsclient/pkg/providers"
|
||||||
|
"github.com/datahearth/ddnsclient/pkg/subdomain"
|
||||||
"github.com/datahearth/ddnsclient/pkg/utils"
|
"github.com/datahearth/ddnsclient/pkg/utils"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Watcher interface {
|
type Watcher interface {
|
||||||
Run() error
|
Run(chan bool) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type watcher struct {
|
type watcher struct {
|
||||||
logger logrus.FieldLogger
|
logger logrus.FieldLogger
|
||||||
provider providers.Provider
|
provider providers.Provider
|
||||||
http http.HTTP
|
subdomains []subdomain.Subdomain
|
||||||
subdomains []string
|
|
||||||
domain string
|
domain string
|
||||||
|
webIP string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewWatcher(logger logrus.FieldLogger, provider providers.Provider, http http.HTTP) (Watcher, error) {
|
func NewWatcher(logger logrus.FieldLogger, provider providers.Provider, webIP string) (Watcher, error) {
|
||||||
if logger == nil {
|
if logger == nil {
|
||||||
return nil, utils.ErrNilLogger
|
return nil, utils.ErrNilLogger
|
||||||
}
|
}
|
||||||
if provider == nil {
|
if provider == nil {
|
||||||
return nil, ErrNilProvider
|
return nil, ErrNilProvider
|
||||||
}
|
}
|
||||||
if http == nil {
|
if webIP == "" {
|
||||||
return nil, ErrNilHTTP
|
webIP = "http://dynamicdns.park-your-domain.com/getip"
|
||||||
}
|
}
|
||||||
domain := viper.GetStringMap("watcher")["domain"].(string)
|
domain := viper.GetStringMap("watcher")["domain"].(string)
|
||||||
subdomains := utils.AggregateSubdomains(viper.GetStringMap("watcher")["subdomains"].([]string), domain)
|
sbs := utils.AggregateSubdomains(viper.GetStringMap("watcher")["subdomains"].([]string), domain)
|
||||||
|
subdomains := make([]subdomain.Subdomain, len(sbs))
|
||||||
|
for _, sd := range sbs {
|
||||||
|
sub, err := subdomain.NewSubdomain(logger, sd)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
subdomains = append(subdomains, sub)
|
||||||
|
}
|
||||||
|
|
||||||
return &watcher{
|
return &watcher{
|
||||||
logger: logger,
|
logger: logger,
|
||||||
provider: provider,
|
provider: provider,
|
||||||
http: http,
|
|
||||||
domain: domain,
|
domain: domain,
|
||||||
subdomains: subdomains,
|
subdomains: subdomains,
|
||||||
|
webIP: webIP,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *watcher) Run() error {
|
func (w *watcher) Run(close chan bool) error {
|
||||||
return nil
|
for {
|
||||||
|
select {
|
||||||
|
case <-close:
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
srvIP, err := utils.RetrieveServerIP(w.webIP)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, sd := range w.subdomains {
|
||||||
|
ok, err := sd.CheckIPAddr(srvIP)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
w.provider.UpdateIP(sd.GetSubdomainIP(), srvIP)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue