178 lines
5.6 KiB
Go
178 lines
5.6 KiB
Go
|
package client
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"io"
|
||
|
"net/http"
|
||
|
"os"
|
||
|
"path/filepath"
|
||
|
"strconv"
|
||
|
"strings"
|
||
|
"time"
|
||
|
"velvettear/godyn/config"
|
||
|
"velvettear/godyn/log"
|
||
|
"velvettear/godyn/notifications"
|
||
|
)
|
||
|
|
||
|
var wanIp string
|
||
|
var ipFile string
|
||
|
|
||
|
// exported function(s)
|
||
|
func Run() {
|
||
|
timestamp := time.Now().UnixMilli()
|
||
|
log.Debug("starting godyn in client mode...")
|
||
|
if !config.HasValidDyndnsConfig() {
|
||
|
log.Fatal("encountered an error starting godyn in client mode", "missing parameters for the dyndns provider")
|
||
|
}
|
||
|
interval := strconv.Itoa(config.Interval())
|
||
|
notifications.Send(notifications.Notification{
|
||
|
Title: "godyn started",
|
||
|
Message: "interval: " + interval + " seconds\n" +
|
||
|
"ip-provider: " + config.IpProviderUrl(),
|
||
|
Emoji: "white_circle",
|
||
|
})
|
||
|
readIpFile()
|
||
|
for config.Interval() > 0 {
|
||
|
log.Info("waiting " + interval + " seconds before next remote/wan ip query and dyndns update...")
|
||
|
time.Sleep(time.Duration(config.Interval()) * time.Second)
|
||
|
updateDyndns()
|
||
|
}
|
||
|
log.DebugTimed("client mode finished", timestamp)
|
||
|
}
|
||
|
|
||
|
// unexported function(s)
|
||
|
func readIpFile() {
|
||
|
ipFile = filepath.Join(os.TempDir(), ".ipfile")
|
||
|
log.Debug("using file '" + ipFile + "' to store ip address")
|
||
|
timestamp := time.Now().UnixMilli()
|
||
|
bytes, error := os.ReadFile(ipFile)
|
||
|
if error != nil {
|
||
|
if errors.Is(error, os.ErrNotExist) {
|
||
|
return
|
||
|
}
|
||
|
log.ErrorTimed("encountered an error reading from the '.ipfile'", timestamp, "location: '"+ipFile+"'", error.Error())
|
||
|
}
|
||
|
wanIp = string(bytes)
|
||
|
log.DebugTimed("read latest remote/wan ip from '.ipfile'", timestamp, "value: '"+wanIp+"'", "location: '"+ipFile+"'")
|
||
|
}
|
||
|
|
||
|
func writeIpFile(value string) {
|
||
|
timestamp := time.Now().UnixMilli()
|
||
|
error := os.WriteFile(ipFile, []byte(value), 0644)
|
||
|
if error != nil {
|
||
|
log.ErrorTimed("encountered an error writing the '.ipfile'", timestamp, "value: '"+value+"'", "location: '"+ipFile+"'", error.Error())
|
||
|
return
|
||
|
}
|
||
|
log.DebugTimed("wrote latest remote/wan ip to '.ipfile'", timestamp, "value: '"+wanIp+"'", "location: '"+ipFile+"'")
|
||
|
}
|
||
|
|
||
|
func getWANIp() (string, error) {
|
||
|
timestamp := time.Now().UnixMilli()
|
||
|
var ip string
|
||
|
providerUrl := config.IpProviderUrl()
|
||
|
response, error := http.Get(providerUrl)
|
||
|
if error != nil {
|
||
|
return ip, error
|
||
|
}
|
||
|
body, error := io.ReadAll(response.Body)
|
||
|
if error != nil {
|
||
|
return ip, error
|
||
|
}
|
||
|
ip = strings.TrimSpace(string(body))
|
||
|
log.InfoTimed("got remote/wan ip '"+ip+"'", timestamp, "ip provider: '"+providerUrl+"'")
|
||
|
return ip, nil
|
||
|
}
|
||
|
|
||
|
func buildDyndnsdUrl(wanIp string) string {
|
||
|
return config.DyndnsUrl() + "?hostname=" + config.DyndnsHostname() + "&myip=" + wanIp
|
||
|
}
|
||
|
|
||
|
func updateDyndns() {
|
||
|
timestamp := time.Now().UnixMilli()
|
||
|
tmpIp, error := getWANIp()
|
||
|
if error != nil {
|
||
|
log.ErrorTimed("encountered an error getting the remote/wan ip", timestamp, "ip provider: '"+config.IpProviderUrl()+"'", error.Error())
|
||
|
}
|
||
|
if len(tmpIp) <= 0 {
|
||
|
log.ErrorTimed("will not update dyndns because remote/wan ip is empty", timestamp)
|
||
|
}
|
||
|
if tmpIp == wanIp {
|
||
|
log.InfoTimed("nothing to do - remote/wan ip has not changed", timestamp, "ip: "+wanIp)
|
||
|
return
|
||
|
}
|
||
|
dyndnsUrl := buildDyndnsdUrl(tmpIp)
|
||
|
request, error := http.NewRequest("GET", dyndnsUrl, nil)
|
||
|
if error != nil {
|
||
|
msg := "encountered an error creating the http request to update dyndns"
|
||
|
log.ErrorTimed(msg, timestamp, error.Error())
|
||
|
notifications.Send(notifications.Notification{
|
||
|
Title: "godyn",
|
||
|
Message: msg + "\n" + error.Error(),
|
||
|
Priority: notifications.PRIORITY_MAX,
|
||
|
Emoji: "red_circle",
|
||
|
})
|
||
|
return
|
||
|
}
|
||
|
request.SetBasicAuth(config.DyndnsUsername(), config.DyndnsPassword())
|
||
|
response, error := http.DefaultClient.Do(request)
|
||
|
if error != nil {
|
||
|
msg := "encountered an error updating dyndns"
|
||
|
log.ErrorTimed(msg, timestamp, "url: '"+dyndnsUrl+"'", error.Error())
|
||
|
notifications.Send(notifications.Notification{
|
||
|
Title: "godyn",
|
||
|
Message: msg + "\n" + error.Error(),
|
||
|
Priority: notifications.PRIORITY_MAX,
|
||
|
Emoji: "red_circle",
|
||
|
})
|
||
|
return
|
||
|
}
|
||
|
bytes, error := io.ReadAll(response.Body)
|
||
|
if error != nil {
|
||
|
msg := "encountered an error reading the response from the dyndns provider"
|
||
|
log.ErrorTimed(msg, timestamp, error.Error())
|
||
|
notifications.Send(notifications.Notification{
|
||
|
Title: "godyn",
|
||
|
Message: msg + "\n" + error.Error(),
|
||
|
Priority: notifications.PRIORITY_MAX,
|
||
|
Emoji: "red_circle",
|
||
|
})
|
||
|
return
|
||
|
}
|
||
|
body := strings.ToLower(strings.TrimSpace(string(bytes)))
|
||
|
if !strings.Contains(body, tmpIp) {
|
||
|
statuscode := strconv.Itoa(response.StatusCode)
|
||
|
msg := "probably encountered an error updating dyndns"
|
||
|
log.WarningTimed(msg, timestamp, "status code: "+statuscode+"'", "response: '"+body+"'")
|
||
|
notifications.Send(notifications.Notification{
|
||
|
|
||
|
Message: msg + "\n" +
|
||
|
"status code: " + statuscode + "\n" +
|
||
|
"response: '" + body + "'",
|
||
|
Priority: notifications.PRIORITY_MAX,
|
||
|
Emoji: "red_circle",
|
||
|
})
|
||
|
return
|
||
|
}
|
||
|
notification := notifications.Notification{}
|
||
|
var msg string
|
||
|
if strings.Contains(body, "abuse") {
|
||
|
msg = "dyndns was not updated because remote/wan ip did not change and the dyndns provider reported an abusive update"
|
||
|
log.WarningTimed(msg, timestamp, "response: '"+body+"'")
|
||
|
notification.Priority = notifications.PRIORITY_HIGH
|
||
|
notification.Emoji = "orange_circle"
|
||
|
} else if strings.Contains(body, "nochg") {
|
||
|
msg = "dyndns was not updated because remote/wan ip did not change"
|
||
|
log.WarningTimed(msg, timestamp, "response: '"+body+"'")
|
||
|
} else {
|
||
|
msg = "dyndns was updated successfully"
|
||
|
log.InfoTimed(msg, timestamp, "response: '"+strings.TrimSpace(string(bytes))+"'")
|
||
|
msg += "\n" + "ip-address: " + wanIp
|
||
|
notification.Priority = notifications.PRIORITY_MAX
|
||
|
notification.Emoji = "green_circle"
|
||
|
}
|
||
|
notification.Message = msg
|
||
|
notifications.Send(notification)
|
||
|
wanIp = tmpIp
|
||
|
writeIpFile(wanIp)
|
||
|
}
|