From b52c860a7b1d3f57abc442bb8c1257f228d49536 Mon Sep 17 00:00:00 2001 From: Mel Date: Thu, 20 Aug 2026 06:18:05 +0200 Subject: ddnnss Signed-off-by: Mel --- main.go | 118 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 main.go (limited to 'main.go') diff --git a/main.go b/main.go new file mode 100644 index 0000000..cc99e9e --- /dev/null +++ b/main.go @@ -0,0 +1,118 @@ +// dynamic domain no-name system service +// +// a simple dynamic dns record updating utility which fetches your +// ip address from a source, and lets a provider know of the fresh +// address. +package main + +import ( + "flag" + "fmt" + "log/slog" + "os" + + "git.rnrd.eu/ddnnss/provider" + "git.rnrd.eu/ddnnss/source" +) + +const version = "1.0.0" + +func main() { + configPath := flag.String("config", "/etc/ddnnss.json", "config file") + showVersion := flag.Bool("version", false, "print the version and exit") + flag.Parse() + + if *showVersion { + fmt.Println("ddnnss", version) + return + } + + slog.Info("ddnnss!", slog.String("version", version)) + + cfg, err := LoadConfig(*configPath) + if err != nil { + slog.Error("could not load config", slog.String("error", err.Error())) + os.Exit(1) + } + + providers := map[string]provider.Provider{} + sources := map[string]source.Source{} + + slog.Info("starting new cycle", slog.Int("records", len(cfg.Records))) + + failed := false + for _, r := range cfg.Records { + fqdn := r.Name + "." + r.Zone + log := slog.With("record", fqdn, "type", r.Type) + + log.Info( + "ensuring record", + slog.String("provider", r.Provider), + slog.String("source", r.Source), + ) + + p, ok := providers[r.Provider] + if !ok { + p, err = provider.New(log, cfg.Providers[r.Provider]) + if err != nil { + log.Error("provider setup failed", "provider", r.Provider, "error", err) + failed = true + continue + } + providers[r.Provider] = p + } + + s, ok := sources[r.Source] + if !ok { + s, err = source.New(log, cfg.Sources[r.Source]) + if err != nil { + log.Error("source setup failed", "source", r.Source, "error", err) + failed = true + continue + } + sources[r.Source] = s + } + + family := source.IPv4 + if r.Type == provider.TypeAAAA { + family = source.IPv6 + } + if !s.Supports(family) { + log.Error("source cannot provide the needed address family", + "source", r.Source, "family", family) + failed = true + continue + } + + addr, err := s.Lookup(family) + if err != nil { + log.Error("address lookup failed", "source", r.Source, "error", err) + failed = true + continue + } + + prev, changed, err := p.Ensure(r.Zone, r.Name, r.Type, addr) + switch { + case err != nil: + log.Error("update failed", "error", err) + failed = true + case changed && prev == "": + log.Info("record created", "addr", addr) + case changed: + log.Info("record updated", "from", prev, "to", addr) + default: + log.Info("record unchanged", "addr", addr) + } + } + + slog.Info( + "cycle finished", + slog.Int("records", len(cfg.Records)), + slog.Bool("failed", failed), + ) + + if failed { + slog.Error("not all records were ensured successfully") + os.Exit(1) + } +} -- cgit v1.3.1