118 lines
2.7 KiB
Go
118 lines
2.7 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"github.com/libdns/libdns"
|
|
"log"
|
|
"net"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/libdns/inwx"
|
|
"github.com/sethvargo/go-envconfig"
|
|
)
|
|
|
|
type Config struct {
|
|
Username string `env:"DYNDNS_USERNAME, required"`
|
|
Password string `env:"DYNDNS_PASSWORD, required"`
|
|
Zone string `env:"DYNDNS_ZONE, required"`
|
|
Record string `env:"DYNDNS_RECORD, required"`
|
|
Nameservers string `env:"DYNDNS_NAMESERVERS, required"`
|
|
}
|
|
|
|
func main() {
|
|
ctx := context.Background()
|
|
var cfg Config
|
|
if err := envconfig.Process(ctx, &cfg); err != nil {
|
|
log.Fatal("Error reading config: ", err)
|
|
}
|
|
|
|
ifaces, err := net.Interfaces()
|
|
if err != nil {
|
|
log.Fatal("Error getting interfaces: ", err)
|
|
}
|
|
var ownIp string
|
|
for _, iface := range ifaces {
|
|
addrs, err := iface.Addrs()
|
|
if err != nil {
|
|
log.Fatalf("Error getting addresses for %v: %s", iface, err)
|
|
}
|
|
for _, addr := range addrs {
|
|
if ipnet, ok := addr.(*net.IPNet); ok &&
|
|
ipnet.IP.To4() == nil &&
|
|
!ipnet.IP.IsPrivate() &&
|
|
!ipnet.IP.IsLinkLocalUnicast() &&
|
|
!ipnet.IP.IsLoopback() &&
|
|
ipnet.IP.IsGlobalUnicast() {
|
|
ownIp = ipnet.IP.String()
|
|
}
|
|
}
|
|
}
|
|
log.Print("Detected own IP: ", ownIp)
|
|
|
|
resolv := &net.Resolver{
|
|
PreferGo: true,
|
|
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
|
|
d := net.Dialer{
|
|
Timeout: time.Duration(10) * time.Second,
|
|
}
|
|
var conn net.Conn
|
|
var err error
|
|
for _, server := range strings.Split(cfg.Nameservers, ",") {
|
|
conn, err = d.DialContext(ctx, network, fmt.Sprintf("%s:53", server))
|
|
if err == nil {
|
|
return conn, err
|
|
} else {
|
|
log.Printf("Error resolving with NS \"%s\"", server)
|
|
}
|
|
}
|
|
return conn, err
|
|
},
|
|
}
|
|
|
|
target := fmt.Sprintf("%s.%s", cfg.Record, cfg.Zone)
|
|
resolvIps, err := resolv.LookupHost(ctx, target)
|
|
if err != nil {
|
|
log.Fatalf("Error resolving %s: %v", target, err)
|
|
}
|
|
if len(resolvIps) != 1 {
|
|
log.Fatalf("Detected %d IPs for \"%s\". Don't know what to do", len(resolvIps), target)
|
|
}
|
|
resolvIp := resolvIps[0]
|
|
log.Printf("Resolved IP: %s", resolvIp)
|
|
|
|
forceUpdate := len(os.Args) == 2 && os.Args[1] == "--force"
|
|
if forceUpdate {
|
|
log.Println("Detected force update")
|
|
}
|
|
|
|
if ownIp == resolvIp && !forceUpdate {
|
|
log.Println("Resolved IP is equal to own IP. Nothing to do.")
|
|
os.Exit(0)
|
|
}
|
|
|
|
provider := &inwx.Provider{
|
|
Username: cfg.Username,
|
|
Password: cfg.Password,
|
|
}
|
|
|
|
records, err := provider.SetRecords(context.Background(), cfg.Zone, []libdns.Record{
|
|
{
|
|
Type: "AAAA",
|
|
Name: cfg.Record,
|
|
Value: ownIp,
|
|
TTL: time.Duration(15) * time.Minute,
|
|
},
|
|
})
|
|
|
|
if err != nil {
|
|
log.Fatalf("Error updating DNS record \"%s\": %s", target, err)
|
|
}
|
|
|
|
for _, record := range records {
|
|
log.Printf("Set record: %v", record)
|
|
}
|
|
}
|