Files
ddns-updater/main.go

129 lines
3.1 KiB
Go
Raw Normal View History

2023-04-28 14:57:21 +02:00
package main
import (
"context"
"log"
"net"
"os/signal"
"syscall"
"time"
"github.com/cloudflare/cloudflare-go"
2023-04-28 23:09:40 +02:00
"github.com/mkelcik/cloudflare-ddns-update/internal"
"github.com/mkelcik/cloudflare-ddns-update/public_resolvers"
2023-04-28 14:57:21 +02:00
)
type PublicIpResolver interface {
ResolvePublicIp(ctx context.Context) (net.IP, error)
}
2023-05-01 10:01:43 +02:00
func getResolver(resolverName string) (PublicIpResolver, string) {
2023-04-28 14:57:21 +02:00
switch resolverName {
// HERE add another resolver if needed
2023-05-01 10:01:43 +02:00
case public_resolvers.V4IdentMeTag:
return public_resolvers.NewV4IdentMeDefault(), public_resolvers.V4IdentMeTag
2023-04-28 14:57:21 +02:00
case public_resolvers.IfConfigMeTag:
fallthrough
default:
2023-05-01 10:01:43 +02:00
return public_resolvers.NewDefaultIfConfigMe(), public_resolvers.IfConfigMeTag
2023-04-28 14:57:21 +02:00
}
}
func main() {
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
defer stop()
config := internal.NewConfig()
if err := config.Validate(); err != nil {
log.Fatalln(err)
}
api, err := cloudflare.NewWithAPIToken(config.ApiToken)
if err != nil {
log.Fatal(err)
}
// Fetch user details on the account
zoneID, err := api.ZoneIDByName(config.CloudflareZone)
if err != nil {
log.Fatal(err)
}
2023-04-30 08:46:25 +02:00
// public ip resolver
2023-05-01 10:01:43 +02:00
publicIpResolver, resolverTag := getResolver(config.PublicIpResolverTag)
2023-04-30 08:46:25 +02:00
checkFunc := func() {
currentPublicIP, err := publicIpResolver.ResolvePublicIp(ctx)
if err != nil {
log.Fatal(err)
}
2023-05-01 10:01:43 +02:00
log.Printf("Current public ip `%s` (%s)", currentPublicIP, resolverTag)
2023-04-30 08:46:25 +02:00
dns, err := allDNSRecords(ctx, api, cloudflare.ZoneIdentifier(zoneID))
if err != nil {
log.Fatal(err)
}
for _, dnsRecord := range dns {
if internal.Contains(config.DnsRecordsToCheck, dnsRecord.Name) {
log.Printf("Checking record `%s` with current value `%s` ...", dnsRecord.Name, dnsRecord.Content)
if currentPublicIP.String() == dnsRecord.Content {
log.Println("OK")
continue // no update needed
2023-04-30 08:25:33 +02:00
}
2023-04-30 08:46:25 +02:00
update := cloudflare.UpdateDNSRecordParams{
ID: dnsRecord.ID,
Content: currentPublicIP.String(),
}
2023-04-30 08:25:33 +02:00
2023-04-30 08:46:25 +02:00
if config.OnChangeComment != "" {
update.Comment = config.OnChangeComment
2023-04-29 12:45:21 +02:00
}
2023-04-30 08:46:25 +02:00
if _, err := api.UpdateDNSRecord(ctx, cloudflare.ZoneIdentifier(zoneID), update); err != nil {
log.Printf("error updating dns record: %s", err)
} else {
log.Printf("Updated to `%s`", currentPublicIP)
2023-04-29 12:45:21 +02:00
}
2023-04-30 08:46:25 +02:00
}
}
}
log.Printf("checking ...")
checkFunc()
log.Println("waiting for check tick ...")
ticker := time.NewTicker(config.CheckInterval)
defer ticker.Stop()
for {
select {
case <-ticker.C:
log.Println("tick received checking ...")
checkFunc()
2023-04-29 12:45:21 +02:00
case <-ctx.Done():
break
2023-04-28 14:57:21 +02:00
}
}
}
func allDNSRecords(ctx context.Context, api *cloudflare.API, rc *cloudflare.ResourceContainer) ([]cloudflare.DNSRecord, error) {
out := make([]cloudflare.DNSRecord, 0, 100)
params := cloudflare.ListDNSRecordsParams{
ResultInfo: cloudflare.ResultInfo{Page: 1},
}
for {
page, res, err := api.ListDNSRecords(ctx, rc, params)
if err != nil {
return nil, err
}
out = append(out, page...)
if res.Page >= res.TotalPages {
break
}
params.Page++
}
return out, nil
}