Merge pull request 'Fix DNS Zone matching for PowerDNS API integration' (#8) from bug-1 into main

Reviewed-on: https://codeberg.org/lauralani/ipam/pulls/8
This commit is contained in:
Adora Laura Kalb 2023-04-01 14:36:55 +00:00
commit 34e4e6fc3e
3 changed files with 370 additions and 371 deletions

3
.gitignore vendored
View file

@ -23,5 +23,6 @@ go.work
# build output # build output
bin/ bin/
# vs code stuff # IDE stuff
.vscode/ .vscode/
.idea/

View file

@ -5,96 +5,96 @@ Copyright © 2023 Laura Kalb <dev@lauka.net>
package cmd package cmd
import ( import (
"fmt" "fmt"
"net/netip" "net/netip"
"os" "os"
"os/user" "os/user"
"time" "time"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
var ipaddCmd = &cobra.Command{ var ipaddCmd = &cobra.Command{
Use: "add ipaddress [hostname]", Use: "add ipaddress [hostname]",
Short: "Add new IP address", Short: "Add new IP address",
Long: `Add new IP address`, Long: `Add new IP address`,
Aliases: []string{"a"}, Aliases: []string{"a"},
Args: cobra.RangeArgs(1, 2), Args: cobra.RangeArgs(1, 2),
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
var ipaddress, hostname string var ipaddress, hostname string
if len(args) == 1 { if len(args) == 1 {
ipaddress = args[0] ipaddress = args[0]
hostname = "" hostname = ""
} else { } else {
ipaddress = args[0] ipaddress = args[0]
hostname = args[1] hostname = args[1]
} }
ip, parseerr := netip.ParseAddr(ipaddress) ip, parseerr := netip.ParseAddr(ipaddress)
// Exit if parsed value is no valid IP // Exit if parsed value is no valid IP
if parseerr != nil { if parseerr != nil {
fmt.Println("[ERROR]", parseerr) fmt.Println("[ERROR]", parseerr)
os.Exit(1) os.Exit(1)
} }
// Exit if parsed value is an IPv6 Address // Exit if parsed value is an IPv6 Address
// TODO: Implement IPv6 support // TODO: Implement IPv6 support
//if !ip.Is4() { //if !ip.Is4() {
// fmt.Printf("[ERROR] IPv6 is not yet supported!\n") // fmt.Printf("[ERROR] IPv6 is not yet supported!\n")
// os.Exit(1) // os.Exit(1)
//} //}
subnet, subnetexists := FindBestSubnet(ip) subnet, subnetexists := FindBestSubnet(ip)
if !subnetexists { if !subnetexists {
fmt.Printf("[ERROR] Found no suitable subnet for IP %v\n", ipaddress) fmt.Printf("[ERROR] Found no suitable subnet for IP %v\n", ipaddress)
fmt.Printf("[ERROR] Maybe you need to add it first?\n") fmt.Printf("[ERROR] Maybe you need to add it first?\n")
os.Exit(1) os.Exit(1)
} }
if subnet.HasIP(ip) { if subnet.HasIP(ip) {
fmt.Printf("[ERROR] IP %v already exists in subnet %v\n", ip.String(), subnet.Subnet.String()) fmt.Printf("[ERROR] IP %v already exists in subnet %v\n", ip.String(), subnet.Subnet.String())
os.Exit(1) os.Exit(1)
} }
currentuser, _ := user.Current() currentuser, _ := user.Current()
timestamp := time.Now() timestamp := time.Now()
subnet.Addresses = append(subnet.Addresses, Address{ip, hostname, timestamp, currentuser.Username}) subnet.Addresses = append(subnet.Addresses, Address{ip, hostname, timestamp, currentuser.Username})
subnet.ChangedBy = currentuser.Username subnet.ChangedBy = currentuser.Username
subnet.ChangedAt = timestamp subnet.ChangedAt = timestamp
writeerr := subnet.WriteSubnet() writeerr := subnet.WriteSubnet()
if writeerr != nil { if writeerr != nil {
fmt.Println("[ERROR]", writeerr) fmt.Println("[ERROR]", writeerr)
os.Exit(1) os.Exit(1)
} }
if hostname == "" { if hostname == "" {
fmt.Printf("added ip:\nip: %v\n", ipaddress) fmt.Printf("added ip:\nip: %v\n", ipaddress)
} else { } else {
fmt.Printf("added ip:\nip: %v\nhostname: %v\n", ipaddress, hostname) fmt.Printf("added ip:\nip: %v\nhostname: %v\n", ipaddress, hostname)
dnserr := AddDNSFqdn(hostname, ip) dnserr := AddDNSFqdn(hostname, ip)
if dnserr != nil { if dnserr != nil {
fmt.Println("[ERROR]", writeerr) fmt.Println("[ERROR]", dnserr)
os.Exit(1) os.Exit(1)
} }
} }
}, },
} }
func init() { func init() {
ipCmd.AddCommand(ipaddCmd) ipCmd.AddCommand(ipaddCmd)
// Here you will define your flags and configuration settings. // Here you will define your flags and configuration settings.
// Cobra supports Persistent Flags which will work for this command // Cobra supports Persistent Flags which will work for this command
// and all subcommands, e.g.: // and all subcommands, e.g.:
// addCmd.PersistentFlags().String("foo", "", "A help for foo") // addCmd.PersistentFlags().String("foo", "", "A help for foo")
// Cobra supports local flags which will only run when this command // Cobra supports local flags which will only run when this command
// is called directly, e.g.: // is called directly, e.g.:
// addCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") // addCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
} }

View file

@ -5,32 +5,32 @@ Copyright © 2023 Laura Kalb <dev@lauka.net>
package cmd package cmd
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
"net/netip" "net/netip"
"strings" "strings"
"github.com/spf13/viper" "github.com/spf13/viper"
) )
type DNSZone struct { type DNSZone struct {
ID string `json:"id"` ID string `json:"id"`
Name string `json:"name"` Name string `json:"name"`
Kind string `json:"kind"` Kind string `json:"kind"`
RRsets []DNSRecordSet `json:"rrsets"` RRsets []DNSRecordSet `json:"rrsets"`
Metadata map[string]string `json:"metadata"` Metadata map[string]string `json:"metadata"`
DNSSEC bool `json:"dnssec"` DNSSEC bool `json:"dnssec"`
NSEC3Param string `json:"nsec3param,omitempty"` NSEC3Param string `json:"nsec3param,omitempty"`
Account string `json:"account,omitempty"` Account string `json:"account,omitempty"`
Serial int `json:"serial"` Serial int `json:"serial"`
} }
type Patch struct { type Patch struct {
Rrsets []DNSRecordSet `json:"rrsets"` Rrsets []DNSRecordSet `json:"rrsets"`
} }
// GetRecord checks if a given Record already exists in the DNSRecordSet list. // GetRecord checks if a given Record already exists in the DNSRecordSet list.
@ -38,106 +38,102 @@ type Patch struct {
// Returns the DNSRecordSet and true if record exists, empty // Returns the DNSRecordSet and true if record exists, empty
// DNSRecordSet and false if not. // DNSRecordSet and false if not.
func (z DNSZone) GetRecord(fqdn string, rtype string, rcontent string) (DNSRecordSet, bool) { func (z DNSZone) GetRecord(fqdn string, rtype string, rcontent string) (DNSRecordSet, bool) {
if !strings.HasSuffix(fqdn, ".") { if !strings.HasSuffix(fqdn, ".") {
fqdn = fqdn + "." fqdn = fqdn + "."
} }
if (rtype == "PTR") && !strings.HasSuffix(rcontent, ".") { if (rtype == "PTR") && !strings.HasSuffix(rcontent, ".") {
rcontent = rcontent + "." rcontent = rcontent + "."
} }
for _, recordset := range z.RRsets { for _, recordset := range z.RRsets {
if recordset.Name == fqdn && recordset.Type == rtype { if recordset.Name == fqdn && recordset.Type == rtype {
for _, record := range recordset.Records { for _, record := range recordset.Records {
if record.Content == rcontent { if record.Content == rcontent {
return recordset, true return recordset, true
} }
} }
} }
} }
return DNSRecordSet{}, false return DNSRecordSet{}, false
} }
// SendPATCH sends a PATCH API request for DNSZone z. Returns error or nil // SendPATCH sends a PATCH API request for DNSZone z. Returns error or nil
// //
// Example args for "test.example.com IN A 127.0.0.1" // Example args for "test.example.com IN A 127.0.0.1"
// //
// z.Name = "example.com." // z.Name = "example.com."
// record = "test" // record = "test"
// value = "127.0.0.1" // value = "127.0.0.1"
// recordtype = "A" // recordtype = "A"
// changetype = "REPLACE" // changetype = "REPLACE"
func (z DNSZone) SendPATCH(record string, value string, recordtype string, changetype string) error { func (z DNSZone) SendPATCH(record string, value string, recordtype string, changetype string) error {
pdnsendpoint := viper.GetString("powerdnsendpoint") pdnsendpoint := viper.GetString("powerdnsendpoint")
pdnsapikey := viper.GetString("powerdnsapikey") pdnsapikey := viper.GetString("powerdnsapikey")
debug, _ := rootCmd.Flags().GetBool("debug") debug, _ := rootCmd.Flags().GetBool("debug")
if !viper.GetBool("powerdnsenabled") { if !viper.GetBool("powerdnsenabled") {
return nil return nil
} }
url := pdnsendpoint + "/api/v1/servers/localhost/zones/" + z.Name url := pdnsendpoint + "/api/v1/servers/localhost/zones/" + z.Name
if debug { if debug {
fmt.Println("[DEBUG] PowerDNS URL: " + url) fmt.Println("[DEBUG] PowerDNS URL: " + url)
} }
rset := DNSRecordSet{} rset := DNSRecordSet{}
rset.Changetype = changetype rset.Changetype = changetype
rset.Name = strings.Join([]string{record, z.Name}, ".") rset.Name = strings.Join([]string{record, z.Name}, ".")
rset.TTL = 3600 rset.TTL = 3600
rset.Type = recordtype rset.Type = recordtype
rec := DNSRecord{} rec := DNSRecord{}
if recordtype == "PTR" { if recordtype == "PTR" {
rec.Content = value + "." rec.Content = value + "."
} else { } else {
rec.Content = value rec.Content = value
} }
rset.Records = append(rset.Records, rec) rset.Records = append(rset.Records, rec)
patch := Patch{} patch := Patch{}
patch.Rrsets = append(patch.Rrsets, rset) patch.Rrsets = append(patch.Rrsets, rset)
payload, marsherr := json.Marshal(patch) payload, marsherr := json.Marshal(patch)
if marsherr != nil { if marsherr != nil {
return marsherr return marsherr
} }
req, reqerr := http.NewRequest("PATCH", url, bytes.NewBuffer(payload)) req, reqerr := http.NewRequest("PATCH", url, bytes.NewBuffer(payload))
if reqerr != nil { if reqerr != nil {
return reqerr return reqerr
} }
req.Header.Add("X-API-Key", pdnsapikey) req.Header.Add("X-API-Key", pdnsapikey)
req.Header.Add("Content-Type", "application/json") req.Header.Add("Content-Type", "application/json")
client := &http.Client{} client := &http.Client{}
resp, resperr := client.Do(req) resp, _ := client.Do(req)
if resperr != nil { if resp.StatusCode != 204 {
return resperr defer resp.Body.Close()
}
if resp.StatusCode != 204 { body, readerr := io.ReadAll(resp.Body)
defer resp.Body.Close() if readerr != nil {
fmt.Println(readerr)
body, readerr := io.ReadAll(resp.Body) }
if readerr != nil { return fmt.Errorf("[HTTP ERROR] %v: %q", resp.Status, string(body))
fmt.Println(readerr) }
} return nil
return errors.New("HTTP Error: " + resp.Status + "\n" + string(body))
}
return nil
} }
type DNSRecordSet struct { type DNSRecordSet struct {
Name string `json:"name"` Name string `json:"name"`
Type string `json:"type"` Type string `json:"type"`
TTL int `json:"ttl"` TTL int `json:"ttl"`
Records []DNSRecord `json:"records"` Records []DNSRecord `json:"records"`
Changetype string `json:"changetype,omitempty"` Changetype string `json:"changetype,omitempty"`
} }
type DNSRecord struct { type DNSRecord struct {
Content string `json:"content"` Content string `json:"content"`
Disabled bool `json:"disabled,omitempty"` Disabled bool `json:"disabled,omitempty"`
SetPTR bool `json:"set-ptr,omitempty"` SetPTR bool `json:"set-ptr,omitempty"`
} }
// GetDNSZone retrieves the corresponding DNSZone for string zone // GetDNSZone retrieves the corresponding DNSZone for string zone
@ -146,45 +142,45 @@ type DNSRecord struct {
// Returns the DNSZone and nil if successful, empty DNSZone and an // Returns the DNSZone and nil if successful, empty DNSZone and an
// error otherwise // error otherwise
func GetDNSZone(zone string) (DNSZone, error) { func GetDNSZone(zone string) (DNSZone, error) {
if !strings.HasSuffix(zone, ".") { if !strings.HasSuffix(zone, ".") {
zone = zone + "." zone = zone + "."
} }
pdnsendpoint := viper.GetString("powerdnsendpoint") pdnsendpoint := viper.GetString("powerdnsendpoint")
pdnsapikey := viper.GetString("powerdnsapikey") pdnsapikey := viper.GetString("powerdnsapikey")
url := pdnsendpoint + "/api/v1/servers/localhost/zones/" + zone url := pdnsendpoint + "/api/v1/servers/localhost/zones/" + zone
req, reqerr := http.NewRequest("GET", url, nil) req, reqerr := http.NewRequest("GET", url, nil)
if reqerr != nil { if reqerr != nil {
fmt.Println(reqerr) fmt.Println(reqerr)
return DNSZone{}, reqerr return DNSZone{}, reqerr
} }
req.Header.Add("X-API-Key", pdnsapikey) req.Header.Add("X-API-Key", pdnsapikey)
req.Header.Add("Accept", "application/json") req.Header.Add("Accept", "application/json")
client := &http.Client{} client := &http.Client{}
resp, resperr := client.Do(req) resp, resperr := client.Do(req)
if resperr != nil { if resperr != nil {
fmt.Println(resperr) fmt.Println(resperr)
return DNSZone{}, resperr return DNSZone{}, resperr
} }
defer resp.Body.Close() defer resp.Body.Close()
body, readerr := io.ReadAll(resp.Body) body, readerr := io.ReadAll(resp.Body)
if readerr != nil { if readerr != nil {
fmt.Println(readerr) fmt.Println(readerr)
return DNSZone{}, readerr return DNSZone{}, readerr
} }
var zoneobj DNSZone var zoneobj DNSZone
marsherr := json.Unmarshal(body, &zoneobj) marsherr := json.Unmarshal(body, &zoneobj)
if marsherr != nil { if marsherr != nil {
fmt.Println(marsherr) fmt.Println(marsherr)
return DNSZone{}, marsherr return DNSZone{}, marsherr
} }
return zoneobj, nil return zoneobj, nil
} }
// GetBestDNSZone requests a list of all zones from PowerDNS // GetBestDNSZone requests a list of all zones from PowerDNS
@ -193,70 +189,70 @@ func GetDNSZone(zone string) (DNSZone, error) {
// Returns the found DNSZone and nil if a suitable zone was // Returns the found DNSZone and nil if a suitable zone was
// found, an empty DNSZone object and an error if not. // found, an empty DNSZone object and an error if not.
func GetBestDNSZone(fqdn string) (DNSZone, error) { func GetBestDNSZone(fqdn string) (DNSZone, error) {
pdnsendpoint := viper.GetString("powerdnsendpoint") pdnsendpoint := viper.GetString("powerdnsendpoint")
pdnsapikey := viper.GetString("powerdnsapikey") pdnsapikey := viper.GetString("powerdnsapikey")
fqdn = fqdn + "." fqdn = fqdn + "."
if !viper.GetBool("powerdnsenabled") { if !viper.GetBool("powerdnsenabled") {
return DNSZone{}, errors.New("PowerDNS integration not enabled") return DNSZone{}, errors.New("PowerDNS integration not enabled")
} }
url := pdnsendpoint + "/api/v1/servers/localhost/zones" url := pdnsendpoint + "/api/v1/servers/localhost/zones"
req, reqerr := http.NewRequest("GET", url, nil) req, reqerr := http.NewRequest("GET", url, nil)
if reqerr != nil { if reqerr != nil {
fmt.Println(reqerr) fmt.Println(reqerr)
return DNSZone{}, reqerr return DNSZone{}, reqerr
} }
req.Header.Add("X-API-Key", pdnsapikey) req.Header.Add("X-API-Key", pdnsapikey)
client := &http.Client{} client := &http.Client{}
resp, resperr := client.Do(req) resp, resperr := client.Do(req)
if resperr != nil { if resperr != nil {
fmt.Println(resperr) fmt.Println(resperr)
return DNSZone{}, resperr return DNSZone{}, resperr
} }
defer resp.Body.Close() defer resp.Body.Close()
body, readerr := io.ReadAll(resp.Body) body, readerr := io.ReadAll(resp.Body)
if readerr != nil { if readerr != nil {
fmt.Println(readerr) fmt.Println(readerr)
return DNSZone{}, readerr return DNSZone{}, readerr
} }
var zones []DNSZone var zones []DNSZone
marsherr := json.Unmarshal(body, &zones) marsherr := json.Unmarshal(body, &zones)
if marsherr != nil { if marsherr != nil {
fmt.Println(marsherr) fmt.Println(marsherr)
return DNSZone{}, marsherr return DNSZone{}, marsherr
} }
var bestmatch DNSZone var bestmatch DNSZone
var matchfound bool = false var matchfound bool = false
for _, zone := range zones { for _, zone := range zones {
if strings.HasSuffix(fqdn, zone.Name) { if strings.HasSuffix(fqdn, "."+zone.Name) {
if !matchfound { if !matchfound {
matchfound = true matchfound = true
bestmatch = zone bestmatch = zone
} }
if matchfound && len(zone.Name) > len(bestmatch.Name) { if matchfound && len(zone.Name) > len(bestmatch.Name) {
bestmatch = zone bestmatch = zone
} }
} }
} }
if !matchfound { if !matchfound {
return DNSZone{}, errors.New("No suitable zone found for " + fqdn) return DNSZone{}, errors.New("No suitable zone found for " + fqdn)
} }
zone, geterr := GetDNSZone(bestmatch.ID) zone, geterr := GetDNSZone(bestmatch.ID)
if geterr != nil { if geterr != nil {
return DNSZone{}, geterr return DNSZone{}, geterr
} }
return zone, nil return zone, nil
} }
// AddDNSFqdn tries to create forward and reverse lookup records // AddDNSFqdn tries to create forward and reverse lookup records
@ -265,74 +261,76 @@ func GetBestDNSZone(fqdn string) (DNSZone, error) {
// //
// Returns nil on success, error otherwise // Returns nil on success, error otherwise
func AddDNSFqdn(fqdn string, addr netip.Addr) error { func AddDNSFqdn(fqdn string, addr netip.Addr) error {
debug, _ := rootCmd.Flags().GetBool("debug") debug, _ := rootCmd.Flags().GetBool("debug")
if !viper.GetBool("powerdnsenabled") { if !viper.GetBool("powerdnsenabled") {
if debug { if debug {
fmt.Println("[INFO] PowerDNS integration disabled, skipping DNS operations.") fmt.Println("[INFO] PowerDNS integration disabled, skipping DNS operations.")
} }
return nil return nil
} }
var recordtype string var recordtype string
if addr.Is4() { if addr.Is4() {
recordtype = "A" recordtype = "A"
} else if addr.Is6() { } else if addr.Is6() {
recordtype = "AAAA" recordtype = "AAAA"
} else { } else {
return errors.New(addr.String() + " is not a valid IP address") return errors.New(addr.String() + " is not a valid IP address")
} }
fzone, fzoneerr := GetBestDNSZone(fqdn) fzone, fzoneerr := GetBestDNSZone(fqdn)
if fzoneerr != nil { if fzoneerr != nil {
fmt.Printf("[DNS] No suitable zone found for %v, skipping DNS op\n", fqdn) fmt.Printf("[DNS] No suitable zone found for %v, skipping DNS op\n", fqdn)
} else { } else {
_, frecordexists := fzone.GetRecord(fqdn, recordtype, addr.String()) _, frecordexists := fzone.GetRecord(fqdn, recordtype, addr.String())
if frecordexists { if frecordexists {
fmt.Printf("[DNS] DNS Record for %v already exists, no need to change DNS.\n", fqdn) fmt.Printf("[DNS] DNS Record for %v already exists, no need to change DNS.\n", fqdn)
} else { } else {
fpatcherr := fzone.SendPATCH(strings.Replace(fqdn+".", "."+fzone.Name, "", 1), addr.String(), recordtype, "REPLACE") var dotfqdn, dotfzone string = fqdn + ".", "." + fzone.Name
if fpatcherr != nil { record := strings.Replace(dotfqdn, dotfzone, "", 1)
return fpatcherr fpatcherr := fzone.SendPATCH(record, addr.String(), recordtype, "REPLACE")
} if fpatcherr != nil {
fmt.Printf("[DNS] + %v IN %v %v\n", fqdn, recordtype, addr.String()) return fpatcherr
} }
} fmt.Printf("[DNS] + %v IN %v %v\n", fqdn, recordtype, addr.String())
}
}
baseip := addr.StringExpanded() baseip := addr.StringExpanded()
var rfqdn string var rfqdn string
if addr.Is4() { if addr.Is4() {
a := strings.Split(baseip, ".") a := strings.Split(baseip, ".")
b := reverse(a) b := reverse(a)
rfqdn = strings.Join(b, ".") + ".in-addr.arpa" rfqdn = strings.Join(b, ".") + ".in-addr.arpa"
} else if addr.Is6() { } else if addr.Is6() {
a := strings.Replace(baseip, ":", "", -1) a := strings.Replace(baseip, ":", "", -1)
b := strings.Split(a, "") b := strings.Split(a, "")
c := reverse(b) c := reverse(b)
rfqdn = strings.Join(c, ".") + ".ip6.arpa" rfqdn = strings.Join(c, ".") + ".ip6.arpa"
} }
rzone, rzoneerr := GetBestDNSZone(rfqdn) rzone, rzoneerr := GetBestDNSZone(rfqdn)
if rzoneerr != nil { if rzoneerr != nil {
fmt.Printf("[DNS] No suitable zone found for %v, skipping DNS op\n", rfqdn) fmt.Printf("[DNS] No suitable zone found for %v, skipping DNS op\n", rfqdn)
} else { } else {
_, rrecordexists := rzone.GetRecord(rfqdn, "PTR", fqdn) _, rrecordexists := rzone.GetRecord(rfqdn, "PTR", fqdn)
rhost := strings.Replace(rfqdn+".", "."+rzone.Name, "", 1) rhost := strings.Replace(rfqdn+".", "."+rzone.Name, "", 1)
if rrecordexists { if rrecordexists {
fmt.Printf("[DNS] Reverse DNS Record for %v already exists, no need to change DNS.\n", addr.String()) fmt.Printf("[DNS] Reverse DNS Record for %v already exists, no need to change DNS.\n", addr.String())
} else { } else {
rpatcherr := rzone.SendPATCH(rhost, fqdn, "PTR", "REPLACE") rpatcherr := rzone.SendPATCH(rhost, fqdn, "PTR", "REPLACE")
if rpatcherr != nil { if rpatcherr != nil {
return rpatcherr return rpatcherr
} }
fmt.Printf("[DNS] + %v IN %v %v\n", rfqdn, "PTR", fqdn) fmt.Printf("[DNS] + %v IN %v %v\n", rfqdn, "PTR", fqdn)
} }
} }
return nil return nil
} }
// DeleteDNSFqdn tries to delete the corresponding record for // DeleteDNSFqdn tries to delete the corresponding record for
@ -340,70 +338,70 @@ func AddDNSFqdn(fqdn string, addr netip.Addr) error {
// //
// Returns nil on success, error otherwise // Returns nil on success, error otherwise
func DeleteDNSFqdn(fqdn string, addr netip.Addr) error { func DeleteDNSFqdn(fqdn string, addr netip.Addr) error {
debug, _ := rootCmd.Flags().GetBool("debug") debug, _ := rootCmd.Flags().GetBool("debug")
if !viper.GetBool("powerdnsenabled") { if !viper.GetBool("powerdnsenabled") {
if debug { if debug {
fmt.Println("[INFO] PowerDNS integration disabled, skipping DNS operations.") fmt.Println("[INFO] PowerDNS integration disabled, skipping DNS operations.")
} }
return nil return nil
} }
var recordtype string var recordtype string
if addr.Is4() { if addr.Is4() {
recordtype = "A" recordtype = "A"
} else if addr.Is6() { } else if addr.Is6() {
recordtype = "AAAA" recordtype = "AAAA"
} else { } else {
return errors.New(addr.String() + " is not a valid IP address") return errors.New(addr.String() + " is not a valid IP address")
} }
fzone, fzoneerr := GetBestDNSZone(fqdn) fzone, fzoneerr := GetBestDNSZone(fqdn)
if fzoneerr != nil { if fzoneerr != nil {
fmt.Printf("[DNS] No suitable zone found for %v, skipping DNS delete op\n", fqdn) fmt.Printf("[DNS] No suitable zone found for %v, skipping DNS delete op\n", fqdn)
} else { } else {
_, frecordexists := fzone.GetRecord(fqdn, recordtype, addr.String()) _, frecordexists := fzone.GetRecord(fqdn, recordtype, addr.String())
if !frecordexists { if !frecordexists {
fmt.Printf("[DNS] DNS Record for %v doesn't exists, no need to change DNS.\n", fqdn) fmt.Printf("[DNS] DNS Record for %v doesn't exists, no need to change DNS.\n", fqdn)
} else { } else {
fpatcherr := fzone.SendPATCH(strings.Replace(fqdn+".", "."+fzone.Name, "", 1), addr.String(), recordtype, "DELETE") fpatcherr := fzone.SendPATCH(strings.Replace(fqdn+".", "."+fzone.Name, "", 1), addr.String(), recordtype, "DELETE")
if fpatcherr != nil { if fpatcherr != nil {
return fpatcherr return fpatcherr
} }
fmt.Printf("[DNS] - %v IN %v %v\n", fqdn, recordtype, addr.String()) fmt.Printf("[DNS] - %v IN %v %v\n", fqdn, recordtype, addr.String())
} }
} }
baseip := addr.StringExpanded() baseip := addr.StringExpanded()
var rfqdn string var rfqdn string
if addr.Is4() { if addr.Is4() {
a := strings.Split(baseip, ".") a := strings.Split(baseip, ".")
b := reverse(a) b := reverse(a)
rfqdn = strings.Join(b, ".") + ".in-addr.arpa" rfqdn = strings.Join(b, ".") + ".in-addr.arpa"
} else if addr.Is6() { } else if addr.Is6() {
a := strings.Replace(baseip, ":", "", -1) a := strings.Replace(baseip, ":", "", -1)
b := strings.Split(a, "") b := strings.Split(a, "")
c := reverse(b) c := reverse(b)
rfqdn = strings.Join(c, ".") + ".ip6.arpa" rfqdn = strings.Join(c, ".") + ".ip6.arpa"
} }
rzone, rzoneerr := GetBestDNSZone(rfqdn) rzone, rzoneerr := GetBestDNSZone(rfqdn)
if rzoneerr != nil { if rzoneerr != nil {
fmt.Printf("[DNS] No suitable zone found for %v, skipping DNS delete op\n", rfqdn) fmt.Printf("[DNS] No suitable zone found for %v, skipping DNS delete op\n", rfqdn)
} else { } else {
_, rrecordexists := rzone.GetRecord(rfqdn, "PTR", fqdn) _, rrecordexists := rzone.GetRecord(rfqdn, "PTR", fqdn)
rhost := strings.Replace(rfqdn+".", "."+rzone.Name, "", 1) rhost := strings.Replace(rfqdn+".", "."+rzone.Name, "", 1)
if !rrecordexists { if !rrecordexists {
fmt.Printf("[DNS] Reverse DNS Record for %v doesn't exists, no need to change DNS.\n", addr.String()) fmt.Printf("[DNS] Reverse DNS Record for %v doesn't exists, no need to change DNS.\n", addr.String())
} else { } else {
rpatcherr := rzone.SendPATCH(rhost, fqdn, "PTR", "DELETE") rpatcherr := rzone.SendPATCH(rhost, fqdn, "PTR", "DELETE")
if rpatcherr != nil { if rpatcherr != nil {
return rpatcherr return rpatcherr
} }
fmt.Printf("[DNS] - %v IN %v %v\n", rfqdn, "PTR", fqdn) fmt.Printf("[DNS] - %v IN %v %v\n", rfqdn, "PTR", fqdn)
} }
} }
return nil return nil
} }