mirror of
https://github.com/StackExchange/dnscontrol.git
synced 2024-05-11 05:55:12 +00:00
TXT records are now handled different. 1. The raw input from dnsconfig.js is passed all the way to the provider. The provider can determine if it can or can't handle such records (auditrecords.go) and processes them internally as such. 2. The CanUseTXTMulti capability is no longer needed. * DSPs now register a table of functions * Use audits for txt record variations * unit tests pass. integration fails. * fix deepcopy problem * rename to AuditRecordSupport * Reduce use of TXTMulti * Remove CanUseTXTMulti * fix Test Skip * fix DO * fix vultr * fix NDC * msdns fixes * Fix powerdns and cloudflare * HEDNS: Fix usage of target field to resolve TXT handling (#1067) * Fix HEXONET Co-authored-by: Robert Blenkinsopp <robert@blenkinsopp.net> Co-authored-by: Jakob Ackermann <das7pad@outlook.com>
187 lines
5.3 KiB
Go
187 lines
5.3 KiB
Go
package octodns
|
|
|
|
/*
|
|
|
|
octodns -
|
|
Generate zonefiles suitiable for OctoDNS.
|
|
|
|
The zonefiles are read and written to the directory octoconfig
|
|
|
|
If the old octoconfig files are readable, we read them to determine
|
|
if an update is actually needed.
|
|
|
|
The YAML input and output code is extremely complicated because
|
|
the format does not fit well with a statically typed language.
|
|
The YAML format changes drastically if the label has single
|
|
or multiple rtypes associated with it, and if there is a single
|
|
or multiple rtype data.
|
|
|
|
*/
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/StackExchange/dnscontrol/v3/models"
|
|
"github.com/StackExchange/dnscontrol/v3/pkg/diff"
|
|
"github.com/StackExchange/dnscontrol/v3/pkg/txtutil"
|
|
"github.com/StackExchange/dnscontrol/v3/providers"
|
|
"github.com/StackExchange/dnscontrol/v3/providers/octodns/octoyaml"
|
|
)
|
|
|
|
var features = providers.DocumentationNotes{
|
|
//providers.CanUseCAA: providers.Can(),
|
|
providers.CanUsePTR: providers.Can(),
|
|
providers.CanUseSRV: providers.Can(),
|
|
providers.DocCreateDomains: providers.Cannot("Driver just maintains list of OctoDNS config files. You must manually create the master config files that refer these."),
|
|
providers.DocDualHost: providers.Cannot("Research is needed."),
|
|
providers.CanGetZones: providers.Unimplemented(),
|
|
}
|
|
|
|
func initProvider(config map[string]string, providermeta json.RawMessage) (providers.DNSServiceProvider, error) {
|
|
// config -- the key/values from creds.json
|
|
// meta -- the json blob from NewReq('name', 'TYPE', meta)
|
|
api := &octodnsProvider{
|
|
directory: config["directory"],
|
|
}
|
|
if api.directory == "" {
|
|
api.directory = "config"
|
|
}
|
|
if len(providermeta) != 0 {
|
|
err := json.Unmarshal(providermeta, api)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
//api.nameservers = models.StringsToNameservers(api.DefaultNS)
|
|
return api, nil
|
|
}
|
|
|
|
func init() {
|
|
fns := providers.DspFuncs{
|
|
Initializer: initProvider,
|
|
AuditRecordsor: AuditRecords,
|
|
}
|
|
providers.RegisterDomainServiceProviderType("OCTODNS", fns, features)
|
|
}
|
|
|
|
// octodnsProvider is the provider handle for the OctoDNS driver.
|
|
type octodnsProvider struct {
|
|
//DefaultNS []string `json:"default_ns"`
|
|
//DefaultSoa SoaInfo `json:"default_soa"`
|
|
//nameservers []*models.Nameserver
|
|
directory string
|
|
}
|
|
|
|
// GetNameservers returns the nameservers for a domain.
|
|
func (c *octodnsProvider) GetNameservers(string) ([]*models.Nameserver, error) {
|
|
return nil, nil
|
|
}
|
|
|
|
// GetZoneRecords gets the records of a zone and returns them in RecordConfig format.
|
|
func (c *octodnsProvider) GetZoneRecords(domain string) (models.Records, error) {
|
|
return nil, fmt.Errorf("not implemented")
|
|
// This enables the get-zones subcommand.
|
|
// Implement this by extracting the code from GetDomainCorrections into
|
|
// a single function. For most providers this should be relatively easy.
|
|
}
|
|
|
|
// GetDomainCorrections returns a list of corrections to update a domain.
|
|
func (c *octodnsProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
|
|
dc.Punycode()
|
|
// Phase 1: Copy everything to []*models.RecordConfig:
|
|
// expectedRecords < dc.Records[i]
|
|
// foundRecords < zonefile
|
|
//
|
|
// Phase 2: Do any manipulations:
|
|
// add NS
|
|
// manipulate SOA
|
|
//
|
|
// Phase 3: Convert to []diff.Records and compare:
|
|
// expectedDiffRecords < expectedRecords
|
|
// foundDiffRecords < foundRecords
|
|
// diff.Inc...(foundDiffRecords, expectedDiffRecords )
|
|
|
|
// Read foundRecords:
|
|
var foundRecords models.Records
|
|
zoneFileFound := true
|
|
zoneFileName := filepath.Join(c.directory, strings.Replace(strings.ToLower(dc.Name), "/", "_", -1)+".yaml")
|
|
foundFH, err := os.Open(zoneFileName)
|
|
if err != nil {
|
|
if os.IsNotExist(err) {
|
|
zoneFileFound = false
|
|
} else {
|
|
return nil, fmt.Errorf("can't open %s: %w", zoneFileName, err)
|
|
}
|
|
} else {
|
|
foundRecords, err = octoyaml.ReadYaml(foundFH, dc.Name)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("can not get corrections: %w", err)
|
|
}
|
|
}
|
|
|
|
// Normalize
|
|
models.PostProcessRecords(foundRecords)
|
|
txtutil.SplitSingleLongTxt(dc.Records) // Autosplit long TXT records
|
|
|
|
differ := diff.New(dc)
|
|
_, create, del, mod, err := differ.IncrementalDiff(foundRecords)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
buf := &bytes.Buffer{}
|
|
// Print a list of changes. Generate an actual change that is the zone
|
|
changes := false
|
|
for _, i := range create {
|
|
changes = true
|
|
fmt.Fprintln(buf, i)
|
|
}
|
|
for _, i := range del {
|
|
changes = true
|
|
fmt.Fprintln(buf, i)
|
|
}
|
|
for _, i := range mod {
|
|
changes = true
|
|
fmt.Fprintln(buf, i)
|
|
}
|
|
msg := fmt.Sprintf("GENERATE_CONFIGFILE: %s", dc.Name)
|
|
if zoneFileFound {
|
|
msg += "\n"
|
|
msg += buf.String()
|
|
} else {
|
|
msg += fmt.Sprintf(" (%d records)\n", len(create))
|
|
}
|
|
corrections := []*models.Correction{}
|
|
if changes {
|
|
corrections = append(corrections,
|
|
&models.Correction{
|
|
Msg: msg,
|
|
F: func() error {
|
|
fmt.Printf("CREATING CONFIGFILE: %v\n", zoneFileName)
|
|
zf, err := os.Create(zoneFileName)
|
|
if err != nil {
|
|
log.Fatalf("Could not create zonefile: %v", err)
|
|
}
|
|
//err = WriteZoneFile(zf, dc.Records, dc.Name)
|
|
err = octoyaml.WriteYaml(zf, dc.Records, dc.Name)
|
|
if err != nil {
|
|
log.Fatalf("WriteZoneFile error: %v\n", err)
|
|
}
|
|
err = zf.Close()
|
|
if err != nil {
|
|
log.Fatalf("Closing: %v", err)
|
|
}
|
|
return nil
|
|
},
|
|
})
|
|
}
|
|
|
|
return corrections, nil
|
|
}
|