From e63ab9a14d57dc8373b61719d6aad726594aaf30 Mon Sep 17 00:00:00 2001 From: Fijxu Date: Sun, 26 Jan 2025 18:06:15 -0300 Subject: [PATCH] feat: option to add own IP api, and better error checks --- cmd/simple-ddns-client/main.go | 70 ++++++++++++---------------------- config.json.example | 1 + go.mod | 2 +- internal/utils.go | 49 ++++++++++++++++++++++++ 4 files changed, 76 insertions(+), 46 deletions(-) create mode 100644 internal/utils.go diff --git a/cmd/simple-ddns-client/main.go b/cmd/simple-ddns-client/main.go index 755d1b5..df5f2f5 100644 --- a/cmd/simple-ddns-client/main.go +++ b/cmd/simple-ddns-client/main.go @@ -18,18 +18,17 @@ this program. If not, see . package main import ( - "bytes" "context" "encoding/json" "flag" "fmt" - "io" "log" "net" - "net/http" "os" "strings" "time" + + utils "git.nadeko.net/fijxu/simple-ddns-client/internal" ) type Porkbun struct { @@ -53,10 +52,10 @@ type Config struct { UpdateInterval int `json:"updateInterval"` Provider string `json:"provider"` DnsServer string `json:"dnsServer"` + PublicIpApi string `json:"publicIpApi"` Porkbun Porkbun `json:"porkbun"` } -var client *http.Client var config *Config // https://stackoverflow.com/questions/59889882/specifying-dns-server-for-lookup-in-go @@ -80,22 +79,32 @@ var resolver = &net.Resolver{ // TODO: Use UPNP, NAT-PMP or with PCP func getIpAddress() string { - req := doGetRequest("https://api.ipify.org") + req, err := utils.DoGetRequest(config.PublicIpApi) + if err != nil { + log.Print("Failed to retrieve public ip address from " + config.PublicIpApi + ": " + err.Error()) + return "" + } return string(req) } func (p *Porkbun) updateIp() { dnsIp, err := resolver.LookupHost(context.Background(), p.Domain) if err != nil { - log.Print("Failed to retrieve IP address for domain " + p.Domain) + log.Print("Failed to retrieve IP address for domain " + p.Domain + ": " + err.Error()) + return } - - log.Print("Current IP of the record " + p.Domain + " is " + dnsIp[0]) + if len(dnsIp) > 1 { + log.Fatal("The record '" + p.Domain + "' can't contain more than 1 IP address!") + } + if string(dnsIp[0]) == p.Data.Ip { + log.Print("No need to update the IP address of domain '" + p.Domain + "'. IP is already " + p.Data.Ip) + return + } + log.Print("Current IP of the record '" + p.Domain + "' is " + dnsIp[0]) p.Data.Ip = getIpAddress() - - if string(dnsIp[0]) == p.Data.Ip { - log.Print("No need to update the IP address of domain " + p.Domain + ". IP is already " + p.Data.Ip) + if p.Data.Ip == "" { + log.Print("Failed to retrieve current public IP address") return } @@ -111,7 +120,11 @@ func (p *Porkbun) updateIp() { data, _ := json.Marshal(&p.Data) queryUrl := domain + "/" + p.Type + "/" + subDomain - res := doPostRequest("https://api.porkbun.com/api/json/v3/dns/editByNameType/"+queryUrl, data) + res, err := utils.DoPostRequest("https://api.porkbun.com/api/json/v3/dns/editByNameType/"+queryUrl, data) + if err != nil { + log.Print("Failed to update public IP address: " + err.Error()) + return + } // Porkbun returns `{"status":"SUCCESS"}` if the IP address has been // updated successfully @@ -145,40 +158,7 @@ func loadConfig(configPath string) *Config { return config } -func doGetRequest(url string) []byte { - request, err := http.NewRequest("GET", url, nil) - if err != nil { - log.Fatal(err.Error()) - } - res, err := client.Do(request) - if err != nil { - log.Fatal(err.Error()) - } - defer res.Body.Close() - - body, err := io.ReadAll(res.Body) - - return body -} - -func doPostRequest(url string, data []byte) []byte { - request, err := http.NewRequest("POST", url, bytes.NewBuffer(data)) - if err != nil { - log.Fatal(err.Error()) - } - res, err := client.Do(request) - if err != nil { - log.Fatal(err.Error()) - } - defer res.Body.Close() - - body, err := io.ReadAll(res.Body) - - return body -} - func init() { - client = &http.Client{} var configPath string flag.StringVar(&configPath, "c", "/etc/simple-ddns-client/config.json", "config.json path") diff --git a/config.json.example b/config.json.example index 47431d2..43576b5 100644 --- a/config.json.example +++ b/config.json.example @@ -2,6 +2,7 @@ "updateInterval": 60, "dnsServer": "1.1.1.1:53", "provider": "porkbun", + "publicIpApi": "https://api.ipify.org", "porkbun": { "apiKey": "pk1_xxx", "secretApiKey": "sk1_xxx", diff --git a/go.mod b/go.mod index 3145931..9d40255 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,3 @@ -module git.nadeko.net/simple-ddns-client +module git.nadeko.net/fijxu/simple-ddns-client go 1.23.4 \ No newline at end of file diff --git a/internal/utils.go b/internal/utils.go new file mode 100644 index 0000000..f1dee42 --- /dev/null +++ b/internal/utils.go @@ -0,0 +1,49 @@ +package utils + +import ( + "bytes" + "io" + "net/http" +) + +var client = &http.Client{} + +func DoGetRequest(url string) ([]byte, error) { + request, err := http.NewRequest("GET", url, nil) + if err != nil { + return nil, err + } + + res, err := client.Do(request) + if err != nil { + return nil, err + } + defer res.Body.Close() + + body, err := io.ReadAll(res.Body) + if err != nil { + return nil, err + } + + return body, nil +} + +func DoPostRequest(url string, data []byte) ([]byte, error) { + request, err := http.NewRequest("POST", url, bytes.NewBuffer(data)) + if err != nil { + return nil, err + } + + res, err := client.Do(request) + if err != nil { + return nil, err + } + defer res.Body.Close() + + body, err := io.ReadAll(res.Body) + if err != nil { + return nil, err + } + + return body, nil +}