1
0
mirror of https://github.com/StackExchange/dnscontrol.git synced 2024-05-11 05:55:12 +00:00

"Target" RecordConfig should not be exported (#1061)

* Unexport RecordConfig.Target
* Fix tests
* HEDNS: Fix usage of target field to resolve TXT handling (#1067)

Co-authored-by: Robert Blenkinsopp <robert@blenkinsopp.net>
This commit is contained in:
Tom Limoncelli
2021-03-04 18:58:23 -05:00
committed by GitHub
parent 3e5e976766
commit 21e85e6528
22 changed files with 454 additions and 219 deletions

View File

@@ -1,13 +1,16 @@
package models
import (
"encoding/json"
"fmt"
"log"
"sort"
"strings"
"github.com/jinzhu/copier"
"github.com/miekg/dns"
"github.com/miekg/dns/dnsutil"
"github.com/qdm12/reprint"
)
// RecordConfig stores a DNS record.
@@ -54,7 +57,7 @@ import (
// NOTE: Eventually we will unexport Name/NameFQDN. Please start using
// the setters (SetLabel/SetLabelFromFQDN) and getters (GetLabel/GetLabelFQDN).
// as they will always work.
// Target:
// target:
// This is the host or IP address of the record, with
// the other related parameters (weight, priority, etc.) stored in individual
// fields.
@@ -69,13 +72,16 @@ import (
// rec.Label() == "@" // Is this record at the apex?
//
type RecordConfig struct {
Type string `json:"type"` // All caps rtype name.
Name string `json:"name"` // The short name. See above.
SubDomain string `json:"subdomain,omitempty"`
NameFQDN string `json:"-"` // Must end with ".$origin". See above.
Target string `json:"target"` // If a name, must end with "."
TTL uint32 `json:"ttl,omitempty"`
Metadata map[string]string `json:"meta,omitempty"`
Type string `json:"type"` // All caps rtype name.
Name string `json:"name"` // The short name. See above.
SubDomain string `json:"subdomain,omitempty"`
NameFQDN string `json:"-"` // Must end with ".$origin". See above.
target string // If a name, must end with "."
TTL uint32 `json:"ttl,omitempty"`
Metadata map[string]string `json:"meta,omitempty"`
Original interface{} `json:"-"` // Store pointer to provider-specific record object. Used in diffing.
// If you add a field to this struct, also add it to the list on MarshalJSON.
MxPreference uint16 `json:"mxpreference,omitempty"`
SrvPriority uint16 `json:"srvpriority,omitempty"`
SrvWeight uint16 `json:"srvweight,omitempty"`
@@ -105,14 +111,100 @@ type RecordConfig struct {
TxtStrings []string `json:"txtstrings,omitempty"` // TxtStrings stores all strings (including the first). Target stores only the first one.
R53Alias map[string]string `json:"r53_alias,omitempty"`
AzureAlias map[string]string `json:"azure_alias,omitempty"`
}
Original interface{} `json:"-"` // Store pointer to provider-specific record object. Used in diffing.
// MarshalJSON marshals RecordConfig.
func (rc *RecordConfig) MarshalJSON() ([]byte, error) {
recj := &struct {
RecordConfig
Target string `json:"target"`
}{
RecordConfig: *rc,
Target: rc.GetTargetField(),
}
j, err := json.Marshal(*recj)
if err != nil {
return nil, err
}
return j, nil
}
// UnmarshalJSON unmarshals RecordConfig.
func (rc *RecordConfig) UnmarshalJSON(b []byte) error {
recj := &struct {
Target string `json:"target"`
Type string `json:"type"` // All caps rtype name.
Name string `json:"name"` // The short name. See above.
SubDomain string `json:"subdomain,omitempty"`
NameFQDN string `json:"-"` // Must end with ".$origin". See above.
target string // If a name, must end with "."
TTL uint32 `json:"ttl,omitempty"`
Metadata map[string]string `json:"meta,omitempty"`
Original interface{} `json:"-"` // Store pointer to provider-specific record object. Used in diffing.
MxPreference uint16 `json:"mxpreference,omitempty"`
SrvPriority uint16 `json:"srvpriority,omitempty"`
SrvWeight uint16 `json:"srvweight,omitempty"`
SrvPort uint16 `json:"srvport,omitempty"`
CaaTag string `json:"caatag,omitempty"`
CaaFlag uint8 `json:"caaflag,omitempty"`
DsKeyTag uint16 `json:"dskeytag,omitempty"`
DsAlgorithm uint8 `json:"dsalgorithm,omitempty"`
DsDigestType uint8 `json:"dsdigesttype,omitempty"`
DsDigest string `json:"dsdigest,omitempty"`
NaptrOrder uint16 `json:"naptrorder,omitempty"`
NaptrPreference uint16 `json:"naptrpreference,omitempty"`
NaptrFlags string `json:"naptrflags,omitempty"`
NaptrService string `json:"naptrservice,omitempty"`
NaptrRegexp string `json:"naptrregexp,omitempty"`
SshfpAlgorithm uint8 `json:"sshfpalgorithm,omitempty"`
SshfpFingerprint uint8 `json:"sshfpfingerprint,omitempty"`
SoaMbox string `json:"soambox,omitempty"`
SoaSerial uint32 `json:"soaserial,omitempty"`
SoaRefresh uint32 `json:"soarefresh,omitempty"`
SoaRetry uint32 `json:"soaretry,omitempty"`
SoaExpire uint32 `json:"soaexpire,omitempty"`
SoaMinttl uint32 `json:"soaminttl,omitempty"`
TlsaUsage uint8 `json:"tlsausage,omitempty"`
TlsaSelector uint8 `json:"tlsaselector,omitempty"`
TlsaMatchingType uint8 `json:"tlsamatchingtype,omitempty"`
TxtStrings []string `json:"txtstrings,omitempty"` // TxtStrings stores all strings (including the first). Target stores only the first one.
R53Alias map[string]string `json:"r53_alias,omitempty"`
AzureAlias map[string]string `json:"azure_alias,omitempty"`
// NB(tlim): If anyone can figure out how to do this without listing all
// the fields, please let us know!
}{}
if err := json.Unmarshal(b, &recj); err != nil {
return err
}
// Copy the exported fields.
copier.CopyWithOption(&rc, &recj, copier.Option{IgnoreEmpty: true, DeepCopy: true})
// Set each unexported field.
rc.SetTarget(recj.Target)
// Some sanity checks:
if recj.Type != rc.Type {
panic("DEBUG: TYPE NOT COPIED\n")
}
if recj.Type == "" {
panic("DEBUG: TYPE BLANK\n")
}
if recj.Name != rc.Name {
panic("DEBUG: NAME NOT COPIED\n")
}
return nil
}
// Copy returns a deep copy of a RecordConfig.
func (rc *RecordConfig) Copy() (*RecordConfig, error) {
newR := &RecordConfig{}
err := copyObj(rc, newR)
// Copy the exported fields.
err := reprint.FromTo(rc, newR) // Deep copy
// Set each unexported field.
newR.target = rc.target
return newR, err
}
@@ -129,7 +221,9 @@ func (rc *RecordConfig) SetLabel(short, origin string) {
panic(fmt.Errorf("origin (%s) is not supposed to end with a dot", origin))
}
if strings.HasSuffix(short, ".") {
panic(fmt.Errorf("short (%s) is not supposed to end with a dot", origin))
if short != "**current-domain**" {
panic(fmt.Errorf("short (%s) is not supposed to end with a dot", origin))
}
}
// TODO(tlim): We should add more validation here or in a separate validation
@@ -198,7 +292,7 @@ func (rc *RecordConfig) GetLabelFQDN() string {
func (rc *RecordConfig) ToDiffable(extraMaps ...map[string]string) string {
content := fmt.Sprintf("%v ttl=%d", rc.GetTargetCombined(), rc.TTL)
if rc.Type == "SOA" {
content = fmt.Sprintf("%s %v %d %d %d %d ttl=%d", rc.Target, rc.SoaMbox, rc.SoaRefresh, rc.SoaRetry, rc.SoaExpire, rc.SoaMinttl, rc.TTL)
content = fmt.Sprintf("%s %v %d %d %d %d ttl=%d", rc.target, rc.SoaMbox, rc.SoaRefresh, rc.SoaRetry, rc.SoaExpire, rc.SoaMinttl, rc.TTL)
// SoaSerial is not used in comparison
}
for _, valueMap := range extraMaps {
@@ -406,13 +500,13 @@ func downcase(recs []*RecordConfig) {
switch r.Type { // #rtype_variations
case "ANAME", "CNAME", "DS", "MX", "NS", "PTR", "NAPTR", "SRV":
// These record types have a target that is case insensitive, so we downcase it.
r.Target = strings.ToLower(r.Target)
r.target = strings.ToLower(r.target)
case "A", "AAAA", "ALIAS", "CAA", "IMPORT_TRANSFORM", "TLSA", "TXT", "SSHFP", "CF_REDIRECT", "CF_TEMP_REDIRECT":
// These record types have a target that is case sensitive, or is an IP address. We leave them alone.
// Do nothing.
case "SOA":
if r.Target != "DEFAULT_NOT_SET." {
r.Target = strings.ToLower(r.Target) // .Target stores the Ns
if r.target != "DEFAULT_NOT_SET." {
r.target = strings.ToLower(r.target) // .target stores the Ns
}
if r.SoaMbox != "DEFAULT_NOT_SET." {
r.SoaMbox = strings.ToLower(r.SoaMbox)