Files
he-dydns/internal/updater/updater.go
T

89 lines
2.3 KiB
Go

// Package updater runs dynamic DNS update passes across all configured entries.
package updater
import (
"fmt"
"log"
"net"
"git.aehoo.net/alphard/he-dydns/internal/config"
"git.aehoo.net/alphard/he-dydns/internal/hedns"
"git.aehoo.net/alphard/he-dydns/internal/network"
)
type cacheKey struct {
hostname string
recordType config.RecordType
}
// Updater runs update passes and remembers the last address successfully
// published for each entry, so unchanged addresses aren't re-sent upstream.
type Updater struct {
last map[cacheKey]net.IP
}
// New returns an Updater with no cached state; its first Run always
// publishes every entry that has a usable address.
func New() *Updater {
return &Updater{last: make(map[cacheKey]net.IP)}
}
// Run discovers the current address(es) and updates entries whose address
// changed since the last successful update. It returns an error only when
// address discovery itself fails outright; per-entry update failures are
// logged and aggregated but do not stop other entries from being attempted.
func (u *Updater) Run(cfg *config.Config) error {
addrs, err := network.Discover(cfg.Interface)
if err != nil {
return fmt.Errorf("discovering address: %w", err)
}
if addrs.Empty() {
log.Printf("no address found (interface=%q); skipping update", cfg.Interface)
return nil
}
if addrs.IPv4 != nil {
log.Printf("discovered IPv4 address %s", addrs.IPv4)
}
if addrs.IPv6 != nil {
log.Printf("discovered IPv6 address %s", addrs.IPv6)
}
var failures int
for _, entry := range cfg.Entries {
var ip net.IP
switch entry.RecordType {
case config.RecordTypeAAAA:
ip = addrs.IPv6
default:
ip = addrs.IPv4
}
if ip == nil {
log.Printf("no %s address available for %s; skipping", entry.RecordType, entry.Hostname)
continue
}
key := cacheKey{hostname: entry.Hostname, recordType: entry.RecordType}
if last, ok := u.last[key]; ok && last.Equal(ip) {
log.Printf("%s (%s) unchanged at %s; skipping update", entry.Hostname, entry.RecordType, ip)
continue
}
if err := hedns.Update(entry.Hostname, entry.Token, ip); err != nil {
log.Printf("error: %v", err)
failures++
continue
}
log.Printf("updated %s (%s) -> %s", entry.Hostname, entry.RecordType, ip)
u.last[key] = ip
}
if failures > 0 {
return fmt.Errorf("%d update(s) failed", failures)
}
return nil
}