1package dns 2 3import ( 4 "errors" 5 "net" 6 "strconv" 7) 8 9const hexDigit = "0123456789abcdef" 10 11// Everything is assumed in ClassINET. 12 13// SetReply creates a reply message from a request message. 14func (dns *Msg) SetReply(request *Msg) *Msg { 15 dns.Id = request.Id 16 dns.Response = true 17 dns.Opcode = request.Opcode 18 if dns.Opcode == OpcodeQuery { 19 dns.RecursionDesired = request.RecursionDesired // Copy rd bit 20 dns.CheckingDisabled = request.CheckingDisabled // Copy cd bit 21 } 22 dns.Rcode = RcodeSuccess 23 if len(request.Question) > 0 { 24 dns.Question = make([]Question, 1) 25 dns.Question[0] = request.Question[0] 26 } 27 return dns 28} 29 30// SetQuestion creates a question message, it sets the Question 31// section, generates an Id and sets the RecursionDesired (RD) 32// bit to true. 33func (dns *Msg) SetQuestion(z string, t uint16) *Msg { 34 dns.Id = Id() 35 dns.RecursionDesired = true 36 dns.Question = make([]Question, 1) 37 dns.Question[0] = Question{z, t, ClassINET} 38 return dns 39} 40 41// SetNotify creates a notify message, it sets the Question 42// section, generates an Id and sets the Authoritative (AA) 43// bit to true. 44func (dns *Msg) SetNotify(z string) *Msg { 45 dns.Opcode = OpcodeNotify 46 dns.Authoritative = true 47 dns.Id = Id() 48 dns.Question = make([]Question, 1) 49 dns.Question[0] = Question{z, TypeSOA, ClassINET} 50 return dns 51} 52 53// SetRcode creates an error message suitable for the request. 54func (dns *Msg) SetRcode(request *Msg, rcode int) *Msg { 55 dns.SetReply(request) 56 dns.Rcode = rcode 57 return dns 58} 59 60// SetRcodeFormatError creates a message with FormError set. 61func (dns *Msg) SetRcodeFormatError(request *Msg) *Msg { 62 dns.Rcode = RcodeFormatError 63 dns.Opcode = OpcodeQuery 64 dns.Response = true 65 dns.Authoritative = false 66 dns.Id = request.Id 67 return dns 68} 69 70// SetUpdate makes the message a dynamic update message. It 71// sets the ZONE section to: z, TypeSOA, ClassINET. 72func (dns *Msg) SetUpdate(z string) *Msg { 73 dns.Id = Id() 74 dns.Response = false 75 dns.Opcode = OpcodeUpdate 76 dns.Compress = false // BIND9 cannot handle compression 77 dns.Question = make([]Question, 1) 78 dns.Question[0] = Question{z, TypeSOA, ClassINET} 79 return dns 80} 81 82// SetIxfr creates message for requesting an IXFR. 83func (dns *Msg) SetIxfr(z string, serial uint32, ns, mbox string) *Msg { 84 dns.Id = Id() 85 dns.Question = make([]Question, 1) 86 dns.Ns = make([]RR, 1) 87 s := new(SOA) 88 s.Hdr = RR_Header{z, TypeSOA, ClassINET, defaultTtl, 0} 89 s.Serial = serial 90 s.Ns = ns 91 s.Mbox = mbox 92 dns.Question[0] = Question{z, TypeIXFR, ClassINET} 93 dns.Ns[0] = s 94 return dns 95} 96 97// SetAxfr creates message for requesting an AXFR. 98func (dns *Msg) SetAxfr(z string) *Msg { 99 dns.Id = Id() 100 dns.Question = make([]Question, 1) 101 dns.Question[0] = Question{z, TypeAXFR, ClassINET} 102 return dns 103} 104 105// SetTsig appends a TSIG RR to the message. 106// This is only a skeleton TSIG RR that is added as the last RR in the 107// additional section. The Tsig is calculated when the message is being send. 108func (dns *Msg) SetTsig(z, algo string, fudge uint16, timesigned int64) *Msg { 109 t := new(TSIG) 110 t.Hdr = RR_Header{z, TypeTSIG, ClassANY, 0, 0} 111 t.Algorithm = algo 112 t.Fudge = fudge 113 t.TimeSigned = uint64(timesigned) 114 t.OrigId = dns.Id 115 dns.Extra = append(dns.Extra, t) 116 return dns 117} 118 119// SetEdns0 appends a EDNS0 OPT RR to the message. 120// TSIG should always the last RR in a message. 121func (dns *Msg) SetEdns0(udpsize uint16, do bool) *Msg { 122 e := new(OPT) 123 e.Hdr.Name = "." 124 e.Hdr.Rrtype = TypeOPT 125 e.SetUDPSize(udpsize) 126 if do { 127 e.SetDo() 128 } 129 dns.Extra = append(dns.Extra, e) 130 return dns 131} 132 133// IsTsig checks if the message has a TSIG record as the last record 134// in the additional section. It returns the TSIG record found or nil. 135func (dns *Msg) IsTsig() *TSIG { 136 if len(dns.Extra) > 0 { 137 if dns.Extra[len(dns.Extra)-1].Header().Rrtype == TypeTSIG { 138 return dns.Extra[len(dns.Extra)-1].(*TSIG) 139 } 140 } 141 return nil 142} 143 144// IsEdns0 checks if the message has a EDNS0 (OPT) record, any EDNS0 145// record in the additional section will do. It returns the OPT record 146// found or nil. 147func (dns *Msg) IsEdns0() *OPT { 148 // EDNS0 is at the end of the additional section, start there. 149 // We might want to change this to *only* look at the last two 150 // records. So we see TSIG and/or OPT - this a slightly bigger 151 // change though. 152 for i := len(dns.Extra) - 1; i >= 0; i-- { 153 if dns.Extra[i].Header().Rrtype == TypeOPT { 154 return dns.Extra[i].(*OPT) 155 } 156 } 157 return nil 158} 159 160// IsDomainName checks if s is a valid domain name, it returns the number of 161// labels and true, when a domain name is valid. Note that non fully qualified 162// domain name is considered valid, in this case the last label is counted in 163// the number of labels. When false is returned the number of labels is not 164// defined. Also note that this function is extremely liberal; almost any 165// string is a valid domain name as the DNS is 8 bit protocol. It checks if each 166// label fits in 63 characters, but there is no length check for the entire 167// string s. I.e. a domain name longer than 255 characters is considered valid. 168func IsDomainName(s string) (labels int, ok bool) { 169 _, labels, err := packDomainName(s, nil, 0, nil, false) 170 return labels, err == nil 171} 172 173// IsSubDomain checks if child is indeed a child of the parent. If child and parent 174// are the same domain true is returned as well. 175func IsSubDomain(parent, child string) bool { 176 // Entire child is contained in parent 177 return CompareDomainName(parent, child) == CountLabel(parent) 178} 179 180// IsMsg sanity checks buf and returns an error if it isn't a valid DNS packet. 181// The checking is performed on the binary payload. 182func IsMsg(buf []byte) error { 183 // Header 184 if len(buf) < 12 { 185 return errors.New("dns: bad message header") 186 } 187 // Header: Opcode 188 // TODO(miek): more checks here, e.g. check all header bits. 189 return nil 190} 191 192// IsFqdn checks if a domain name is fully qualified. 193func IsFqdn(s string) bool { 194 l := len(s) 195 if l == 0 { 196 return false 197 } 198 return s[l-1] == '.' 199} 200 201// IsRRset checks if a set of RRs is a valid RRset as defined by RFC 2181. 202// This means the RRs need to have the same type, name, and class. Returns true 203// if the RR set is valid, otherwise false. 204func IsRRset(rrset []RR) bool { 205 if len(rrset) == 0 { 206 return false 207 } 208 if len(rrset) == 1 { 209 return true 210 } 211 rrHeader := rrset[0].Header() 212 rrType := rrHeader.Rrtype 213 rrClass := rrHeader.Class 214 rrName := rrHeader.Name 215 216 for _, rr := range rrset[1:] { 217 curRRHeader := rr.Header() 218 if curRRHeader.Rrtype != rrType || curRRHeader.Class != rrClass || curRRHeader.Name != rrName { 219 // Mismatch between the records, so this is not a valid rrset for 220 //signing/verifying 221 return false 222 } 223 } 224 225 return true 226} 227 228// Fqdn return the fully qualified domain name from s. 229// If s is already fully qualified, it behaves as the identity function. 230func Fqdn(s string) string { 231 if IsFqdn(s) { 232 return s 233 } 234 return s + "." 235} 236 237// Copied from the official Go code. 238 239// ReverseAddr returns the in-addr.arpa. or ip6.arpa. hostname of the IP 240// address suitable for reverse DNS (PTR) record lookups or an error if it fails 241// to parse the IP address. 242func ReverseAddr(addr string) (arpa string, err error) { 243 ip := net.ParseIP(addr) 244 if ip == nil { 245 return "", &Error{err: "unrecognized address: " + addr} 246 } 247 if ip.To4() != nil { 248 return strconv.Itoa(int(ip[15])) + "." + strconv.Itoa(int(ip[14])) + "." + strconv.Itoa(int(ip[13])) + "." + 249 strconv.Itoa(int(ip[12])) + ".in-addr.arpa.", nil 250 } 251 // Must be IPv6 252 buf := make([]byte, 0, len(ip)*4+len("ip6.arpa.")) 253 // Add it, in reverse, to the buffer 254 for i := len(ip) - 1; i >= 0; i-- { 255 v := ip[i] 256 buf = append(buf, hexDigit[v&0xF]) 257 buf = append(buf, '.') 258 buf = append(buf, hexDigit[v>>4]) 259 buf = append(buf, '.') 260 } 261 // Append "ip6.arpa." and return (buf already has the final .) 262 buf = append(buf, "ip6.arpa."...) 263 return string(buf), nil 264} 265 266// String returns the string representation for the type t. 267func (t Type) String() string { 268 if t1, ok := TypeToString[uint16(t)]; ok { 269 return t1 270 } 271 return "TYPE" + strconv.Itoa(int(t)) 272} 273 274// String returns the string representation for the class c. 275func (c Class) String() string { 276 if s, ok := ClassToString[uint16(c)]; ok { 277 // Only emit mnemonics when they are unambiguous, specically ANY is in both. 278 if _, ok := StringToType[s]; !ok { 279 return s 280 } 281 } 282 return "CLASS" + strconv.Itoa(int(c)) 283} 284 285// String returns the string representation for the name n. 286func (n Name) String() string { 287 return sprintName(string(n)) 288} 289