2022-06-21 00:27:05 +08:00
|
|
|
package powerdns
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2022-08-15 08:46:56 +08:00
|
|
|
"net/http"
|
|
|
|
"strings"
|
|
|
|
|
2022-06-21 00:27:05 +08:00
|
|
|
"github.com/StackExchange/dnscontrol/v3/models"
|
|
|
|
"github.com/StackExchange/dnscontrol/v3/pkg/diff"
|
2022-12-12 04:02:58 +08:00
|
|
|
"github.com/StackExchange/dnscontrol/v3/pkg/diff2"
|
2022-06-21 00:27:05 +08:00
|
|
|
"github.com/mittwald/go-powerdns/apis/zones"
|
|
|
|
"github.com/mittwald/go-powerdns/pdnshttp"
|
|
|
|
)
|
|
|
|
|
|
|
|
// GetNameservers returns the nameservers for a domain.
|
|
|
|
func (dsp *powerdnsProvider) GetNameservers(string) ([]*models.Nameserver, error) {
|
|
|
|
var r []string
|
|
|
|
for _, j := range dsp.nameservers {
|
|
|
|
r = append(r, j.Name)
|
|
|
|
}
|
|
|
|
return models.ToNameservers(r)
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetZoneRecords gets the records of a zone and returns them in RecordConfig format.
|
|
|
|
func (dsp *powerdnsProvider) GetZoneRecords(domain string) (models.Records, error) {
|
|
|
|
zone, err := dsp.client.Zones().GetZone(context.Background(), dsp.ServerName, domain)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
curRecords := models.Records{}
|
|
|
|
// loop over grouped records by type, called RRSet
|
|
|
|
for _, rrset := range zone.ResourceRecordSets {
|
|
|
|
if rrset.Type == "SOA" {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
// loop over single records of this group and create records
|
|
|
|
for _, pdnsRecord := range rrset.Records {
|
|
|
|
r, err := toRecordConfig(domain, pdnsRecord, rrset.TTL, rrset.Name, rrset.Type)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
curRecords = append(curRecords, r)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return curRecords, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetDomainCorrections returns a list of corrections to update a domain.
|
|
|
|
func (dsp *powerdnsProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
|
|
|
|
|
|
|
|
// get current zone records
|
|
|
|
curRecords, err := dsp.GetZoneRecords(dc.Name)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// post-process records
|
|
|
|
if err := dc.Punycode(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
models.PostProcessRecords(curRecords)
|
|
|
|
|
2023-01-14 02:00:49 +08:00
|
|
|
// create record diff by group
|
|
|
|
var keysToUpdate map[models.RecordKey][]string
|
|
|
|
if !diff2.EnableDiff2 {
|
|
|
|
keysToUpdate, err = (diff.New(dc)).ChangedGroups(curRecords)
|
|
|
|
} else {
|
|
|
|
keysToUpdate, err = (diff.NewCompat(dc)).ChangedGroups(curRecords)
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
desiredRecords := dc.Records.GroupedByKey()
|
|
|
|
|
|
|
|
var cuCorrections []*models.Correction
|
|
|
|
var dCorrections []*models.Correction
|
|
|
|
|
|
|
|
// add create/update and delete corrections separately
|
|
|
|
for label, msgs := range keysToUpdate {
|
|
|
|
labelName := label.NameFQDN + "."
|
|
|
|
labelType := label.Type
|
|
|
|
msgJoined := strings.Join(msgs, "\n ")
|
|
|
|
|
|
|
|
if _, ok := desiredRecords[label]; !ok {
|
|
|
|
// no record found so delete it
|
|
|
|
dCorrections = append(dCorrections, &models.Correction{
|
|
|
|
Msg: msgJoined,
|
|
|
|
F: func() error {
|
|
|
|
return dsp.client.Zones().RemoveRecordSetFromZone(context.Background(), dsp.ServerName, dc.Name, labelName, labelType)
|
|
|
|
},
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
// record found so create or update it
|
|
|
|
ttl := desiredRecords[label][0].TTL
|
|
|
|
var records []zones.Record
|
|
|
|
for _, recordContent := range desiredRecords[label] {
|
|
|
|
records = append(records, zones.Record{
|
|
|
|
Content: recordContent.GetTargetCombined(),
|
2022-12-12 04:02:58 +08:00
|
|
|
})
|
|
|
|
}
|
2023-01-14 02:00:49 +08:00
|
|
|
cuCorrections = append(cuCorrections, &models.Correction{
|
|
|
|
Msg: msgJoined,
|
|
|
|
F: func() error {
|
|
|
|
return dsp.client.Zones().AddRecordSetToZone(context.Background(), dsp.ServerName, dc.Name, zones.ResourceRecordSet{
|
|
|
|
Name: labelName,
|
|
|
|
Type: labelType,
|
|
|
|
TTL: int(ttl),
|
|
|
|
Records: records,
|
|
|
|
ChangeType: zones.ChangeTypeReplace,
|
|
|
|
})
|
|
|
|
},
|
|
|
|
})
|
2022-06-21 00:27:05 +08:00
|
|
|
}
|
2023-01-14 02:00:49 +08:00
|
|
|
}
|
2022-06-21 00:27:05 +08:00
|
|
|
|
2023-01-14 02:00:49 +08:00
|
|
|
// append corrections in the right order
|
|
|
|
// delete corrections must be run first to avoid correlations with existing RR
|
|
|
|
var corrections []*models.Correction
|
|
|
|
corrections = append(corrections, dCorrections...)
|
|
|
|
corrections = append(corrections, cuCorrections...)
|
2022-12-12 04:02:58 +08:00
|
|
|
|
2023-01-14 02:00:49 +08:00
|
|
|
// DNSSec corrections
|
|
|
|
dnssecCorrections, err := dsp.getDNSSECCorrections(dc)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2022-06-21 00:27:05 +08:00
|
|
|
}
|
2023-01-14 02:00:49 +08:00
|
|
|
corrections = append(corrections, dnssecCorrections...)
|
2022-06-21 00:27:05 +08:00
|
|
|
|
|
|
|
return corrections, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// EnsureDomainExists adds a domain to the DNS service if it does not exist
|
|
|
|
func (dsp *powerdnsProvider) EnsureDomainExists(domain string) error {
|
|
|
|
if _, err := dsp.client.Zones().GetZone(context.Background(), dsp.ServerName, domain+"."); err != nil {
|
|
|
|
if e, ok := err.(pdnshttp.ErrUnexpectedStatus); ok {
|
|
|
|
if e.StatusCode != http.StatusNotFound {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else { // domain seems to be there
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err := dsp.client.Zones().CreateZone(context.Background(), dsp.ServerName, zones.Zone{
|
|
|
|
Name: domain + ".",
|
|
|
|
Type: zones.ZoneTypeZone,
|
|
|
|
DNSSec: dsp.DNSSecOnCreate,
|
|
|
|
Nameservers: dsp.DefaultNS,
|
|
|
|
})
|
|
|
|
return err
|
|
|
|
}
|