128 lines
3.4 KiB
Go
128 lines
3.4 KiB
Go
package main
|
|
|
|
/*
|
|
* https://github.com/joeig/go-powerdns
|
|
* https://github.com/xlzd/gotp
|
|
* https://github.com/dspinhirne/netaddr-go => https://pkg.go.dev/github.com/dspinhirne/netaddr-go
|
|
* https://github.com/go-yaml/yaml/tree/v2.4.0 =>
|
|
*/
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"github.com/dspinhirne/netaddr-go"
|
|
"github.com/joeig/go-powerdns/v3"
|
|
"github.com/xlzd/gotp"
|
|
"gopkg.in/yaml.v3"
|
|
"log"
|
|
"net"
|
|
"net/http"
|
|
"os"
|
|
"strings"
|
|
)
|
|
|
|
type conf struct {
|
|
TotpToken string `yaml:"totp_secret"`
|
|
PdnsApiToken string `yaml:"pdns_api_token"`
|
|
Zone string `yaml:"zone"`
|
|
Records []string `yaml:"records"`
|
|
}
|
|
|
|
func (c *conf) getConf() *conf {
|
|
yamlFile, err := os.ReadFile("config.yaml")
|
|
if err != nil {
|
|
log.Printf("yamlFile.Get err #%v ", err)
|
|
}
|
|
err = yaml.Unmarshal(yamlFile, c)
|
|
if err != nil {
|
|
log.Fatalf("Unmarshal: %v", err)
|
|
}
|
|
return c
|
|
}
|
|
|
|
func main() {
|
|
f, err := os.OpenFile("/tmp/testlogfile", os.O_RDWR | os.O_CREATE | os.O_APPEND, 0666)
|
|
if err != nil {
|
|
log.Fatalf("error opening file: %v", err)
|
|
}
|
|
defer f.Close()
|
|
|
|
log.SetOutput(f)
|
|
|
|
var c conf
|
|
c.getConf()
|
|
|
|
ctx := context.Background()
|
|
pdns := powerdns.New("http://localhost:8081", "localhost", powerdns.WithAPIKey(c.PdnsApiToken))
|
|
|
|
http.HandleFunc("/update", func(w http.ResponseWriter, r *http.Request) {
|
|
token := r.URL.Query().Get("token")
|
|
if token == gotp.NewDefaultTOTP(c.TotpToken).Now() {
|
|
ip, _, err := net.SplitHostPort(r.RemoteAddr)
|
|
if err != nil {
|
|
log.Fatalf("Err %v ", err)
|
|
}
|
|
userIP := net.ParseIP(ip)
|
|
if userIP == nil {
|
|
log.Fatalf("Err %v ", err)
|
|
}
|
|
//fmt.Fprintf(w, "IP: %q \n", ip)
|
|
//fmt.Fprintf(w, "IP: %q \n", ip)
|
|
log.Println(fmt.Sprintf("TOTP : %q", gotp.NewDefaultTOTP(c.TotpToken).Now()))
|
|
log.Println(fmt.Sprintf("Token: %q", token))
|
|
|
|
ipv6, _ := netaddr.ParseIPv6(ip)
|
|
new_prefix := ipv6.NetId()
|
|
|
|
updated := false
|
|
for _, record_to_update := range c.Records {
|
|
records, err := pdns.Records.Get(ctx, c.Zone, record_to_update, powerdns.RRTypePtr(powerdns.RRTypeAAAA))
|
|
if err != nil {
|
|
log.Fatalf("%v", err)
|
|
}
|
|
for _, r := range records {
|
|
if strings.Index(*r.Name, record_to_update) == 0 {
|
|
ipv6, err := netaddr.ParseIPv6(*r.Records[0].Content)
|
|
if err != nil {
|
|
log.Fatalf("IPv6 parse err %v ", err)
|
|
}
|
|
hostId := ipv6.HostId()
|
|
new_ipv6 := netaddr.NewIPv6(new_prefix, hostId)
|
|
log.Println(fmt.Sprintf("Host: %q -> %q needs update: %t", ipv6, new_ipv6, ipv6.Long() != new_ipv6.Long()))
|
|
if ipv6.Long() != new_ipv6.Long() {
|
|
fmt.Fprintf(w, "Update Host %q to %q\n", *r.Name, ip)
|
|
updated = true
|
|
err = pdns.Records.Change(ctx, c.Zone, record_to_update+"."+c.Zone, powerdns.RRTypeAAAA, 30, []string{new_ipv6.Long()})
|
|
if err != nil {
|
|
log.Fatalf("Update err %v ", err)
|
|
}
|
|
} else {
|
|
fmt.Fprintf(w, "No update for host %q\n", *r.Name)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if updated {
|
|
// increase serial
|
|
zone, err := pdns.Zones.Get(ctx, c.Zone)
|
|
if err != nil {
|
|
log.Fatalf("%v", err)
|
|
}
|
|
serial := *zone.Serial
|
|
zoneChangeSet := &powerdns.Zone{
|
|
Serial: powerdns.Uint32(serial + 1),
|
|
}
|
|
|
|
if err := pdns.Zones.Change(ctx, c.Zone, zoneChangeSet); err != nil {
|
|
log.Fatalf("%v", err)
|
|
}
|
|
|
|
pdns.Zones.Notify(ctx, c.Zone)
|
|
log.Println("Updated...")
|
|
}
|
|
}
|
|
})
|
|
|
|
log.Println("Started...")
|
|
http.ListenAndServe(":8080", nil)
|
|
}
|