github.com/teknogeek/dnscontrol@v0.2.8/models/target.go (about)

     1  package models
     2  
     3  import (
     4  	"fmt"
     5  	"net"
     6  	"strings"
     7  
     8  	"github.com/miekg/dns"
     9  	"github.com/pkg/errors"
    10  )
    11  
    12  /* .Target is kind of a mess.
    13  For simple rtypes it is the record's value. (i.e. for an A record
    14  	it is the IP address).
    15  For complex rtypes (like an MX record has a preference and a value)
    16  	it might be a space-delimited string with all the parameters, or it
    17  	might just be the hostname.
    18  
    19  This was a bad design decision that I regret. Eventually we will eliminate this
    20  field and replace it with setters/getters.  The setters/getters are below
    21  so that it is easy to do things the right way in preparation.
    22  */
    23  
    24  // GetTargetField returns the target. There may be other fields (for example
    25  // an MX record also has a .MxPreference field.
    26  func (rc *RecordConfig) GetTargetField() string {
    27  	return rc.Target
    28  }
    29  
    30  // // GetTargetSingle returns the target for types that have a single value target
    31  // // and panics for all others.
    32  // func (rc *RecordConfig) GetTargetSingle() string {
    33  // 	if rc.Type == "MX" || rc.Type == "SRV" || rc.Type == "CAA" || rc.Type == "TLSA" || rc.Type == "TXT" {
    34  // 		panic("TargetSingle called on a type with a multi-parameter rtype.")
    35  // 	}
    36  // 	return rc.Target
    37  // }
    38  
    39  // GetTargetIP returns the net.IP stored in Target.
    40  func (rc *RecordConfig) GetTargetIP() net.IP {
    41  	if rc.Type != "A" && rc.Type != "AAAA" {
    42  		panic(errors.Errorf("GetTargetIP called on an inappropriate rtype (%s)", rc.Type))
    43  	}
    44  	return net.ParseIP(rc.Target)
    45  }
    46  
    47  // GetTargetCombined returns a string with the various fields combined.
    48  // For example, an MX record might output `10 mx10.example.tld`.
    49  func (rc *RecordConfig) GetTargetCombined() string {
    50  	// If this is a pseudo record, just return the target.
    51  	if _, ok := dns.StringToType[rc.Type]; !ok {
    52  		return rc.Target
    53  	}
    54  
    55  	// We cheat by converting to a dns.RR and use the String() function.
    56  	// This combines all the data for us, and even does proper quoting.
    57  	// Sadly String() always includes a header, which we must strip out.
    58  	// TODO(tlim): Request the dns project add a function that returns
    59  	// the string without the header.
    60  	rr := rc.ToRR()
    61  	header := rr.Header().String()
    62  	full := rr.String()
    63  	if !strings.HasPrefix(full, header) {
    64  		panic("assertion failed. dns.Hdr.String() behavior has changed in an incompatible way")
    65  	}
    66  	return full[len(header):]
    67  }
    68  
    69  // GetTargetSortable returns a string that is sortable.
    70  func (rc *RecordConfig) GetTargetSortable() string {
    71  	return rc.GetTargetDebug()
    72  }
    73  
    74  // GetTargetDebug returns a string with the various fields spelled out.
    75  func (rc *RecordConfig) GetTargetDebug() string {
    76  	content := fmt.Sprintf("%s %s %s %d", rc.Type, rc.NameFQDN, rc.Target, rc.TTL)
    77  	switch rc.Type { // #rtype_variations
    78  	case "A", "AAAA", "CNAME", "NS", "PTR", "TXT":
    79  		// Nothing special.
    80  	case "MX":
    81  		content += fmt.Sprintf(" pref=%d", rc.MxPreference)
    82  	case "SOA":
    83  		content = fmt.Sprintf("%s %s %s %d", rc.Type, rc.Name, rc.Target, rc.TTL)
    84  	case "SRV":
    85  		content += fmt.Sprintf(" srvpriority=%d srvweight=%d srvport=%d", rc.SrvPriority, rc.SrvWeight, rc.SrvPort)
    86  	case "TLSA":
    87  		content += fmt.Sprintf(" tlsausage=%d tlsaselector=%d tlsamatchingtype=%d", rc.TlsaUsage, rc.TlsaSelector, rc.TlsaMatchingType)
    88  	case "CAA":
    89  		content += fmt.Sprintf(" caatag=%s caaflag=%d", rc.CaaTag, rc.CaaFlag)
    90  	case "R53_ALIAS":
    91  		content += fmt.Sprintf(" type=%s zone_id=%s", rc.R53Alias["type"], rc.R53Alias["zone_id"])
    92  	default:
    93  		panic(errors.Errorf("rc.String rtype %v unimplemented", rc.Type))
    94  		// We panic so that we quickly find any switch statements
    95  		// that have not been updated for a new RR type.
    96  	}
    97  	for k, v := range rc.Metadata {
    98  		content += fmt.Sprintf(" %s=%s", k, v)
    99  	}
   100  	return content
   101  }
   102  
   103  // SetTarget sets the target, assuming that the rtype is appropriate.
   104  func (rc *RecordConfig) SetTarget(target string) error {
   105  	rc.Target = target
   106  	return nil
   107  }
   108  
   109  // SetTargetIP sets the target to an IP, verifying this is an appropriate rtype.
   110  func (rc *RecordConfig) SetTargetIP(ip net.IP) error {
   111  	// TODO(tlim): Verify the rtype is appropriate for an IP.
   112  	rc.SetTarget(ip.String())
   113  	return nil
   114  }
   115  
   116  // // SetTargetFQDN sets the target to a string, verifying this is an appropriate rtype.
   117  // func (rc *RecordConfig) SetTargetFQDN(target string) error {
   118  // 	// TODO(tlim): Verify the rtype is appropriate for an hostname.
   119  // 	rc.Target = target
   120  // 	return nil
   121  // }