2018-02-16 01:02:50 +08:00
|
|
|
package models
|
|
|
|
|
|
|
|
import (
|
2018-02-28 06:36:47 +08:00
|
|
|
"fmt"
|
2018-02-16 01:02:50 +08:00
|
|
|
"net"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/miekg/dns"
|
|
|
|
)
|
|
|
|
|
2021-03-05 07:58:23 +08:00
|
|
|
/* .target is kind of a mess.
|
2018-02-16 01:02:50 +08:00
|
|
|
For simple rtypes it is the record's value. (i.e. for an A record
|
|
|
|
it is the IP address).
|
|
|
|
For complex rtypes (like an MX record has a preference and a value)
|
|
|
|
it might be a space-delimited string with all the parameters, or it
|
|
|
|
might just be the hostname.
|
|
|
|
|
|
|
|
This was a bad design decision that I regret. Eventually we will eliminate this
|
|
|
|
field and replace it with setters/getters. The setters/getters are below
|
|
|
|
so that it is easy to do things the right way in preparation.
|
|
|
|
*/
|
|
|
|
|
|
|
|
// GetTargetField returns the target. There may be other fields (for example
|
|
|
|
// an MX record also has a .MxPreference field.
|
|
|
|
func (rc *RecordConfig) GetTargetField() string {
|
2021-03-05 07:58:23 +08:00
|
|
|
return rc.target
|
2018-02-16 01:02:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// // GetTargetSingle returns the target for types that have a single value target
|
|
|
|
// // and panics for all others.
|
|
|
|
// func (rc *RecordConfig) GetTargetSingle() string {
|
|
|
|
// if rc.Type == "MX" || rc.Type == "SRV" || rc.Type == "CAA" || rc.Type == "TLSA" || rc.Type == "TXT" {
|
|
|
|
// panic("TargetSingle called on a type with a multi-parameter rtype.")
|
|
|
|
// }
|
2021-03-05 07:58:23 +08:00
|
|
|
// return rc.target
|
2018-02-16 01:02:50 +08:00
|
|
|
// }
|
|
|
|
|
2021-03-05 07:58:23 +08:00
|
|
|
// GetTargetIP returns the net.IP stored in .target.
|
2018-02-16 01:02:50 +08:00
|
|
|
func (rc *RecordConfig) GetTargetIP() net.IP {
|
|
|
|
if rc.Type != "A" && rc.Type != "AAAA" {
|
Switch to Go 1.13 error wrapping (#604)
* Replaced errors.Wrap with fmt.Errorf (#589)
* Find: errors\.Wrap\(([^,]+),\s+(["`][^"`]*)(["`])\)
Replace: fmt.Errorf($2: %w$3, $1)
* Replaced errors.Wrapf with fmt.Errorf (#589)
* Find: errors\.Wrapf\(([^,]+),\s+(["`][^"`]*)(["`])\)
Replace: fmt.Errorf($2: %w$3, $1)
* Find: errors\.Wrapf\(([^,]+),\s+(["`][^"`]*)(["`])(,[^)]+)\)
* Replace: fmt.Errorf($2: %w$3$4, $1)
* Replaced errors.Errorf with fmt.Errorf (#589)
* Find: errors\.Errorf
Replace: fmt.Errorf
* Cleaned up remaining imports
* Cleanup
* Regenerate provider support matrix
This was broken by #533 ... and it's now the third time this has been missed.
2020-01-29 00:06:56 +08:00
|
|
|
panic(fmt.Errorf("GetTargetIP called on an inappropriate rtype (%s)", rc.Type))
|
2018-02-16 01:02:50 +08:00
|
|
|
}
|
2021-03-05 07:58:23 +08:00
|
|
|
return net.ParseIP(rc.target)
|
2018-02-16 01:02:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// GetTargetCombined returns a string with the various fields combined.
|
|
|
|
// For example, an MX record might output `10 mx10.example.tld`.
|
|
|
|
func (rc *RecordConfig) GetTargetCombined() string {
|
2019-06-20 20:34:36 +08:00
|
|
|
// Pseudo records:
|
2018-02-16 01:02:50 +08:00
|
|
|
if _, ok := dns.StringToType[rc.Type]; !ok {
|
2019-06-20 20:34:36 +08:00
|
|
|
switch rc.Type { // #rtype_variations
|
|
|
|
case "R53_ALIAS":
|
|
|
|
// Differentiate between multiple R53_ALIASs on the same label.
|
2021-03-05 07:58:23 +08:00
|
|
|
return fmt.Sprintf("%s atype=%s zone_id=%s", rc.target, rc.R53Alias["type"], rc.R53Alias["zone_id"])
|
2020-03-03 00:25:42 +08:00
|
|
|
case "AZURE_ALIAS":
|
|
|
|
// Differentiate between multiple AZURE_ALIASs on the same label.
|
2021-03-05 07:58:23 +08:00
|
|
|
return fmt.Sprintf("%s atype=%s", rc.target, rc.AzureAlias["type"])
|
2019-06-20 20:34:36 +08:00
|
|
|
default:
|
|
|
|
// Just return the target.
|
2021-03-05 07:58:23 +08:00
|
|
|
return rc.target
|
2019-06-20 20:34:36 +08:00
|
|
|
}
|
2018-02-16 01:02:50 +08:00
|
|
|
}
|
|
|
|
|
2021-06-25 06:26:21 +08:00
|
|
|
if rc.Type == "SOA" {
|
|
|
|
return fmt.Sprintf("%s %v %d %d %d %d %d", rc.target, rc.SoaMbox, rc.SoaSerial, rc.SoaRefresh, rc.SoaRetry, rc.SoaExpire, rc.SoaMinttl)
|
|
|
|
}
|
|
|
|
|
2021-03-08 02:19:22 +08:00
|
|
|
return rc.zoneFileQuoted()
|
|
|
|
}
|
|
|
|
|
|
|
|
// zoneFileQuoted returns the rData as would be quoted in a zonefile.
|
|
|
|
func (rc *RecordConfig) zoneFileQuoted() string {
|
2018-02-16 01:02:50 +08:00
|
|
|
// We cheat by converting to a dns.RR and use the String() function.
|
|
|
|
// This combines all the data for us, and even does proper quoting.
|
|
|
|
// Sadly String() always includes a header, which we must strip out.
|
|
|
|
// TODO(tlim): Request the dns project add a function that returns
|
|
|
|
// the string without the header.
|
2021-06-25 06:26:21 +08:00
|
|
|
if rc.Type == "NAPTR" && rc.GetTargetField() == "" {
|
|
|
|
rc.SetTarget(".")
|
|
|
|
}
|
2018-02-16 01:02:50 +08:00
|
|
|
rr := rc.ToRR()
|
|
|
|
header := rr.Header().String()
|
|
|
|
full := rr.String()
|
|
|
|
if !strings.HasPrefix(full, header) {
|
|
|
|
panic("assertion failed. dns.Hdr.String() behavior has changed in an incompatible way")
|
|
|
|
}
|
|
|
|
return full[len(header):]
|
|
|
|
}
|
|
|
|
|
2018-02-28 06:36:47 +08:00
|
|
|
// GetTargetSortable returns a string that is sortable.
|
|
|
|
func (rc *RecordConfig) GetTargetSortable() string {
|
|
|
|
return rc.GetTargetDebug()
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetTargetDebug returns a string with the various fields spelled out.
|
|
|
|
func (rc *RecordConfig) GetTargetDebug() string {
|
2021-03-05 07:58:23 +08:00
|
|
|
content := fmt.Sprintf("%s %s %s %d", rc.Type, rc.NameFQDN, rc.target, rc.TTL)
|
2018-02-28 06:36:47 +08:00
|
|
|
switch rc.Type { // #rtype_variations
|
2021-06-22 22:24:49 +08:00
|
|
|
case "A", "AAAA", "CNAME", "NS", "PTR", "TXT", "AKAMAICDN":
|
2018-02-28 06:36:47 +08:00
|
|
|
// Nothing special.
|
2020-05-30 22:40:21 +08:00
|
|
|
case "DS":
|
|
|
|
content += fmt.Sprintf(" ds_algorithm=%d ds_keytag=%d ds_digesttype=%d ds_digest=%s", rc.DsAlgorithm, rc.DsKeyTag, rc.DsDigestType, rc.DsDigest)
|
2019-03-28 22:40:13 +08:00
|
|
|
case "NAPTR":
|
2019-03-29 19:01:52 +08:00
|
|
|
content += fmt.Sprintf(" naptrorder=%d naptrpreference=%d naptrflags=%s naptrservice=%s naptrregexp=%s", rc.NaptrOrder, rc.NaptrPreference, rc.NaptrFlags, rc.NaptrService, rc.NaptrRegexp)
|
2018-02-28 06:36:47 +08:00
|
|
|
case "MX":
|
|
|
|
content += fmt.Sprintf(" pref=%d", rc.MxPreference)
|
|
|
|
case "SOA":
|
2021-03-05 07:58:23 +08:00
|
|
|
content = fmt.Sprintf("%s ns=%v mbox=%v serial=%v refresh=%v retry=%v expire=%v minttl=%v", rc.Type, rc.target, rc.SoaMbox, rc.SoaSerial, rc.SoaRefresh, rc.SoaRetry, rc.SoaExpire, rc.SoaMinttl)
|
2018-02-28 06:36:47 +08:00
|
|
|
case "SRV":
|
|
|
|
content += fmt.Sprintf(" srvpriority=%d srvweight=%d srvport=%d", rc.SrvPriority, rc.SrvWeight, rc.SrvPort)
|
2019-01-29 06:26:20 +08:00
|
|
|
case "SSHFP":
|
|
|
|
content += fmt.Sprintf(" sshfpalgorithm=%d sshfpfingerprint=%d", rc.SshfpAlgorithm, rc.SshfpFingerprint)
|
2018-02-28 06:36:47 +08:00
|
|
|
case "TLSA":
|
|
|
|
content += fmt.Sprintf(" tlsausage=%d tlsaselector=%d tlsamatchingtype=%d", rc.TlsaUsage, rc.TlsaSelector, rc.TlsaMatchingType)
|
|
|
|
case "CAA":
|
|
|
|
content += fmt.Sprintf(" caatag=%s caaflag=%d", rc.CaaTag, rc.CaaFlag)
|
|
|
|
case "R53_ALIAS":
|
|
|
|
content += fmt.Sprintf(" type=%s zone_id=%s", rc.R53Alias["type"], rc.R53Alias["zone_id"])
|
2020-03-03 00:25:42 +08:00
|
|
|
case "AZURE_ALIAS":
|
|
|
|
content += fmt.Sprintf(" type=%s", rc.AzureAlias["type"])
|
2018-02-28 06:36:47 +08:00
|
|
|
default:
|
Switch to Go 1.13 error wrapping (#604)
* Replaced errors.Wrap with fmt.Errorf (#589)
* Find: errors\.Wrap\(([^,]+),\s+(["`][^"`]*)(["`])\)
Replace: fmt.Errorf($2: %w$3, $1)
* Replaced errors.Wrapf with fmt.Errorf (#589)
* Find: errors\.Wrapf\(([^,]+),\s+(["`][^"`]*)(["`])\)
Replace: fmt.Errorf($2: %w$3, $1)
* Find: errors\.Wrapf\(([^,]+),\s+(["`][^"`]*)(["`])(,[^)]+)\)
* Replace: fmt.Errorf($2: %w$3$4, $1)
* Replaced errors.Errorf with fmt.Errorf (#589)
* Find: errors\.Errorf
Replace: fmt.Errorf
* Cleaned up remaining imports
* Cleanup
* Regenerate provider support matrix
This was broken by #533 ... and it's now the third time this has been missed.
2020-01-29 00:06:56 +08:00
|
|
|
panic(fmt.Errorf("rc.String rtype %v unimplemented", rc.Type))
|
2018-02-28 06:36:47 +08:00
|
|
|
// We panic so that we quickly find any switch statements
|
|
|
|
// that have not been updated for a new RR type.
|
|
|
|
}
|
|
|
|
for k, v := range rc.Metadata {
|
|
|
|
content += fmt.Sprintf(" %s=%s", k, v)
|
|
|
|
}
|
|
|
|
return content
|
|
|
|
}
|
2018-02-16 01:02:50 +08:00
|
|
|
|
2018-02-28 06:36:47 +08:00
|
|
|
// SetTarget sets the target, assuming that the rtype is appropriate.
|
|
|
|
func (rc *RecordConfig) SetTarget(target string) error {
|
2021-03-05 07:58:23 +08:00
|
|
|
rc.target = target
|
2018-02-16 01:02:50 +08:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetTargetIP sets the target to an IP, verifying this is an appropriate rtype.
|
|
|
|
func (rc *RecordConfig) SetTargetIP(ip net.IP) error {
|
|
|
|
// TODO(tlim): Verify the rtype is appropriate for an IP.
|
2018-03-20 05:18:58 +08:00
|
|
|
rc.SetTarget(ip.String())
|
2018-02-16 01:02:50 +08:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-03-20 05:18:58 +08:00
|
|
|
// // SetTargetFQDN sets the target to a string, verifying this is an appropriate rtype.
|
2018-02-16 01:02:50 +08:00
|
|
|
// func (rc *RecordConfig) SetTargetFQDN(target string) error {
|
|
|
|
// // TODO(tlim): Verify the rtype is appropriate for an hostname.
|
|
|
|
// rc.Target = target
|
|
|
|
// return nil
|
|
|
|
// }
|