feat: DNS lookup instead of HTTP HEAD request (#1)
This commit is contained in:
parent
0553bf07e5
commit
6fead37859
2
Makefile
2
Makefile
|
@ -25,7 +25,7 @@ deploy-image-release:
|
|||
ifndef DOCKER
|
||||
@echo "docker is required!"
|
||||
endif
|
||||
@echo "Pushing image ddnsclient:$(RELEASE_VERSION) to docker hub..."
|
||||
@echo "Pushing image with tag to docker hub..."
|
||||
@docker push ddnsclient:$(RELEASE_VERSION)
|
||||
@echo "Image pushed!"
|
||||
|
||||
|
|
|
@ -16,12 +16,13 @@ var (
|
|||
Checkout the documentation for parameters in the yaml config file.
|
||||
`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if err := ddnsclient.Start(logger); err != nil {
|
||||
if err := ddnsclient.Start(logger, config); err != nil {
|
||||
logrus.Error(err)
|
||||
}
|
||||
},
|
||||
}
|
||||
logger = logrus.StandardLogger()
|
||||
config ddnsclient.ClientConfig
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
@ -34,6 +35,9 @@ func init() {
|
|||
}
|
||||
|
||||
utils.LoadConfig()
|
||||
if err := viper.Unmarshal(&config); err != nil {
|
||||
logger.WithError(err).Fatalln("failed to map yaml config file into ClientConfig struct")
|
||||
}
|
||||
|
||||
utils.SetupLogger(logger)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
package ddnsclient
|
||||
|
||||
type ClientConfig struct {
|
||||
Logger Logger `mapstructure:"logger"`
|
||||
Providers Providers `mapstructure:"providers"`
|
||||
Watcher Watcher `mapstructure:"watcher"`
|
||||
UpdateTime int `mapstructure:"update-time"`
|
||||
WebIP string `mapstructure:"web-ip"`
|
||||
}
|
||||
|
||||
type Logger struct {
|
||||
Level string `mapstructure:"level"`
|
||||
DisableTimestamp bool `mapstructure:"disable-timestamp"`
|
||||
DisableColor bool `mapstructure:"disable-color"`
|
||||
}
|
||||
|
||||
type Providers struct {
|
||||
Ovh Ovh `mapstructure:"ovh,omitempty"`
|
||||
}
|
||||
|
||||
type Ovh struct {
|
||||
URL string `mapstructure:"url"`
|
||||
Username string `mapstructure:"username"`
|
||||
Password string `mapstructure:"password"`
|
||||
}
|
||||
|
||||
type Watcher struct {
|
||||
Domain string `mapstructure:"domain"`
|
||||
Subdomains []string `mapstructure:"subdomains"`
|
||||
}
|
6
main.go
6
main.go
|
@ -13,7 +13,7 @@ import (
|
|||
)
|
||||
|
||||
// Start create a new instance of ddns-client
|
||||
func Start(logger logrus.FieldLogger) error {
|
||||
func Start(logger logrus.FieldLogger, config ClientConfig) error {
|
||||
log := logger.WithFields(logrus.Fields{
|
||||
"pkg": "ddnsclient",
|
||||
"component": "root",
|
||||
|
@ -40,13 +40,13 @@ func Start(logger logrus.FieldLogger) error {
|
|||
defer close(sigc)
|
||||
|
||||
log.Infoln("Start watching periodically for changes!")
|
||||
t := time.NewTicker(viper.GetDuration("update-time")*time.Second)
|
||||
t := time.NewTicker(viper.GetDuration("update-time") * time.Second)
|
||||
go w.Run(t, chClose, chErr)
|
||||
|
||||
for {
|
||||
select {
|
||||
case err := <-chErr:
|
||||
log.WithError(err).Errorln("An error occured while running the watcher. Retrying in the next tick")
|
||||
log.Errorln(err.Error())
|
||||
continue
|
||||
case <-sigc:
|
||||
log.Infoln("Interrupt signal received. Stopping watcher...")
|
||||
|
|
|
@ -46,7 +46,6 @@ func (ovh *ovh) UpdateIP(subdomain, ip string) error {
|
|||
// * create GET request
|
||||
req, err := http.NewRequest("GET", newURL, nil)
|
||||
if err != nil {
|
||||
logger.WithError(err).WithField("request-type", "GET").Errorln(utils.ErrCreateNewRequest.Error())
|
||||
return utils.ErrCreateNewRequest
|
||||
}
|
||||
req.SetBasicAuth(ovh.ovhConfig["username"].(string), ovh.ovhConfig["password"].(string))
|
||||
|
@ -54,19 +53,16 @@ func (ovh *ovh) UpdateIP(subdomain, ip string) error {
|
|||
// * perform GET request
|
||||
logger.WithFields(logrus.Fields{
|
||||
"subdomain": subdomain,
|
||||
"new-ip": ip,
|
||||
"new-ip": ip,
|
||||
}).Debugln("calling OVH DynHost to update subdomain IP")
|
||||
c := new(http.Client)
|
||||
resp, err := c.Do(req)
|
||||
if err != nil {
|
||||
logger.WithError(err).Errorln(utils.ErrUpdateRequest.Error())
|
||||
return utils.ErrUpdateRequest
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
logger.WithField("status-code", resp.StatusCode).Errorln(utils.ErrWrongStatusCode.Error())
|
||||
return utils.ErrWrongStatusCode
|
||||
}
|
||||
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -2,9 +2,6 @@ package subdomain
|
|||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
h "net/http"
|
||||
"net/http/httptrace"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
@ -45,54 +42,24 @@ func NewSubdomain(logger logrus.FieldLogger, subdomainAddr string) (Subdomain, e
|
|||
|
||||
// RetrieveSubdomainIP will retrieve the subdomain IP with a HEAD request
|
||||
func (sd *subdomain) retrieveSubdomainIP() error {
|
||||
var remoteAddr string
|
||||
logger := sd.logger.WithField("component", "retrieve-subdomain-ip")
|
||||
|
||||
// * create HEAD request
|
||||
req, err := http.NewRequest("HEAD", "https://"+sd.subdomainAddr, nil)
|
||||
ips, err := net.LookupIP(sd.subdomainAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// * create a trace to get server remote address
|
||||
trace := &httptrace.ClientTrace{
|
||||
GotConn: func(gci httptrace.GotConnInfo) {
|
||||
remoteAddr = gci.Conn.RemoteAddr().String()
|
||||
},
|
||||
}
|
||||
req = req.WithContext(httptrace.WithClientTrace(req.Context(), trace))
|
||||
|
||||
// * create a client to perform the request
|
||||
client := new(h.Client)
|
||||
client.Timeout = 5 * time.Second
|
||||
|
||||
// * perform the request
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
// todo: ignoring errors is bad. Implement a solution to scrape 100% of the time remote addr
|
||||
logger.WithError(err).WithFields(logrus.Fields{
|
||||
"subdomain": sd.subdomainAddr,
|
||||
}).Errorln(utils.ErrHeadRemoteIP.Error())
|
||||
sd.ip = ""
|
||||
return nil
|
||||
}
|
||||
if resp.StatusCode != 200 && remoteAddr == "" {
|
||||
logger.WithFields(logrus.Fields{
|
||||
"status-code": resp.StatusCode,
|
||||
"subdomain": sd.subdomainAddr,
|
||||
}).Errorln(utils.ErrWrongStatusCode.Error())
|
||||
return utils.ErrWrongStatusCode
|
||||
if len(ips) != 1 {
|
||||
return utils.ErrIpLenght
|
||||
}
|
||||
|
||||
// * check if remote address contains a port
|
||||
if strings.Contains(remoteAddr, ":") {
|
||||
remoteAddr, _, err = net.SplitHostPort(remoteAddr)
|
||||
ip := ips[0].String()
|
||||
if strings.Contains(ip, ":") {
|
||||
ip, _, err = net.SplitHostPort(ip)
|
||||
if err != nil {
|
||||
logger.WithError(err).WithField("remote-address", remoteAddr).Errorln(utils.ErrSplitAddr.Error())
|
||||
return utils.ErrSplitAddr
|
||||
}
|
||||
}
|
||||
|
||||
sd.ip = remoteAddr
|
||||
sd.ip = ip
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -13,6 +13,18 @@ func (sb *subdomain) SubIsPending(sbs PendingSubdomains) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// FindSubdomain returns a subdomain found in the pending map of subdomain.
|
||||
// If not found, it returns nil.
|
||||
func (sb *subdomain) FindSubdomain(sbs PendingSubdomains) Subdomain {
|
||||
for _, sub := range sbs {
|
||||
if sub == sb {
|
||||
return sb
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CheckPendingSubdomains check if any pending subdomains are waiting to be restored.
|
||||
// If so, it/they will be returned as a slice.
|
||||
// If not, it returns nil.
|
||||
|
@ -31,18 +43,6 @@ func CheckPendingSubdomains(sbs PendingSubdomains, now time.Time) PendingSubdoma
|
|||
return delSbs
|
||||
}
|
||||
|
||||
// FindSubdomain returns a subdomain found in the pending map of subdomain.
|
||||
// If not found, it returns nil.
|
||||
func (sb *subdomain) FindSubdomain(sbs PendingSubdomains) Subdomain {
|
||||
for _, sub := range sbs {
|
||||
if sub == sb {
|
||||
return sb
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func DeletePendingSubdomains(delSbs PendingSubdomains, pending PendingSubdomains) PendingSubdomains {
|
||||
for t := range delSbs {
|
||||
delete(pending, t)
|
||||
|
|
|
@ -17,9 +17,9 @@ var (
|
|||
// ErrNilHTTP ...
|
||||
ErrNilHTTP = errors.New("http is mandatory")
|
||||
// ErrWrongStatusCode is thrown when the response status code isn't a 200
|
||||
ErrWrongStatusCode = errors.New("response sent an non 200 status code")
|
||||
ErrWrongStatusCode = errors.New("web-ip returns a non 200 status code")
|
||||
// ErrGetServerIP is thrown when HTTP can't contact the web-ip service
|
||||
ErrGetServerIP = errors.New("failed to fetch server IP")
|
||||
ErrGetServerIP = errors.New("HTTP error")
|
||||
// ErrParseHTTPBody is thrown when the HTTP service can't parse the body response
|
||||
ErrParseHTTPBody = errors.New("can't parse response body")
|
||||
// ErrHeadRemoteIP ...
|
||||
|
@ -30,6 +30,8 @@ var (
|
|||
ErrCreateNewRequest = errors.New("can't create http request")
|
||||
// ErrUpdateRequest ...
|
||||
ErrUpdateRequest = errors.New("failed to set new IP address")
|
||||
// ErrIpLength ...
|
||||
ErrIpLenght = errors.New("zero or more than 1 ips have been found")
|
||||
)
|
||||
|
||||
type (
|
||||
|
|
|
@ -65,29 +65,18 @@ func AggregateSubdomains(subdomains []string, domain string) []string {
|
|||
|
||||
// RetrieveServerIP will use the defined web-ip service to get the server public address and save it to the struct
|
||||
func RetrieveServerIP(webIP string) (string, error) {
|
||||
logger := logrus.WithFields(logrus.Fields{
|
||||
"pkg": "utils",
|
||||
"component": "server-ip",
|
||||
})
|
||||
|
||||
// * retrieve client's server IP
|
||||
resp, err := http.Get(webIP)
|
||||
if err != nil {
|
||||
logger.WithError(err).WithField("web-ip", webIP).Errorln(ErrGetServerIP.Error())
|
||||
return "", ErrGetServerIP
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
logger.WithError(err).WithFields(logrus.Fields{
|
||||
"web-ip": webIP,
|
||||
"statuc-code": resp.StatusCode,
|
||||
}).Errorln(ErrWrongStatusCode.Error())
|
||||
return "", ErrWrongStatusCode
|
||||
}
|
||||
|
||||
// * get ip from body
|
||||
d, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
logger.WithError(err).WithField("web-ip", webIP).Errorln(ErrParseHTTPBody.Error())
|
||||
return "", ErrParseHTTPBody
|
||||
}
|
||||
|
||||
|
|
|
@ -94,11 +94,16 @@ func (w *watcher) Run(t *time.Ticker, chClose chan struct{}, chErr chan error) {
|
|||
|
||||
func (w *watcher) runDDNSCheck() error {
|
||||
logger := w.logger.WithField("component", "runDDNSCheck")
|
||||
|
||||
logger.Infoln("Starting DDNS check...")
|
||||
|
||||
srvIP, err := utils.RetrieveServerIP(w.webIP)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Debugln("Checking server IP...")
|
||||
|
||||
srvIP = "109.14.53.74" // tmp
|
||||
|
||||
for _, sb := range w.subdomains {
|
||||
if sb.SubIsPending(w.pendingSubdomains) {
|
||||
|
|
Loading…
Reference in New Issue