github.com/philhug/dnscontrol@v0.2.4-0.20180625181521-921fa9849001/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 // }