feat: option to add own IP api, and better error checks
All checks were successful
golangci-lint / lint (push) Successful in 38s

This commit is contained in:
Fijxu 2025-01-26 18:06:15 -03:00
parent e937474aa8
commit e63ab9a14d
Signed by: Fijxu
GPG key ID: 32C1DDF333EDA6A4
4 changed files with 76 additions and 46 deletions

View file

@ -18,18 +18,17 @@ this program. If not, see <https://www.gnu.org/licenses/>.
package main package main
import ( import (
"bytes"
"context" "context"
"encoding/json" "encoding/json"
"flag" "flag"
"fmt" "fmt"
"io"
"log" "log"
"net" "net"
"net/http"
"os" "os"
"strings" "strings"
"time" "time"
utils "git.nadeko.net/fijxu/simple-ddns-client/internal"
) )
type Porkbun struct { type Porkbun struct {
@ -53,10 +52,10 @@ type Config struct {
UpdateInterval int `json:"updateInterval"` UpdateInterval int `json:"updateInterval"`
Provider string `json:"provider"` Provider string `json:"provider"`
DnsServer string `json:"dnsServer"` DnsServer string `json:"dnsServer"`
PublicIpApi string `json:"publicIpApi"`
Porkbun Porkbun `json:"porkbun"` Porkbun Porkbun `json:"porkbun"`
} }
var client *http.Client
var config *Config var config *Config
// https://stackoverflow.com/questions/59889882/specifying-dns-server-for-lookup-in-go // 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 // TODO: Use UPNP, NAT-PMP or with PCP
func getIpAddress() string { 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) return string(req)
} }
func (p *Porkbun) updateIp() { func (p *Porkbun) updateIp() {
dnsIp, err := resolver.LookupHost(context.Background(), p.Domain) dnsIp, err := resolver.LookupHost(context.Background(), p.Domain)
if err != nil { 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
} }
if len(dnsIp) > 1 {
log.Print("Current IP of the record " + p.Domain + " is " + dnsIp[0]) 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() p.Data.Ip = getIpAddress()
if p.Data.Ip == "" {
if string(dnsIp[0]) == p.Data.Ip { log.Print("Failed to retrieve current public IP address")
log.Print("No need to update the IP address of domain " + p.Domain + ". IP is already " + p.Data.Ip)
return return
} }
@ -111,7 +120,11 @@ func (p *Porkbun) updateIp() {
data, _ := json.Marshal(&p.Data) data, _ := json.Marshal(&p.Data)
queryUrl := domain + "/" + p.Type + "/" + subDomain 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 // Porkbun returns `{"status":"SUCCESS"}` if the IP address has been
// updated successfully // updated successfully
@ -145,40 +158,7 @@ func loadConfig(configPath string) *Config {
return 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() { func init() {
client = &http.Client{}
var configPath string var configPath string
flag.StringVar(&configPath, "c", "/etc/simple-ddns-client/config.json", "config.json path") flag.StringVar(&configPath, "c", "/etc/simple-ddns-client/config.json", "config.json path")

View file

@ -2,6 +2,7 @@
"updateInterval": 60, "updateInterval": 60,
"dnsServer": "1.1.1.1:53", "dnsServer": "1.1.1.1:53",
"provider": "porkbun", "provider": "porkbun",
"publicIpApi": "https://api.ipify.org",
"porkbun": { "porkbun": {
"apiKey": "pk1_xxx", "apiKey": "pk1_xxx",
"secretApiKey": "sk1_xxx", "secretApiKey": "sk1_xxx",

2
go.mod
View file

@ -1,3 +1,3 @@
module git.nadeko.net/simple-ddns-client module git.nadeko.net/fijxu/simple-ddns-client
go 1.23.4 go 1.23.4

49
internal/utils.go Normal file
View file

@ -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
}