From 0ee63f97a316d903c1759d531fb691819b5fecbb Mon Sep 17 00:00:00 2001 From: McStork Date: Sun, 13 Mar 2016 05:25:24 -0700 Subject: [PATCH] Packetbeat: add support for EDNS/DNSSEC * Add EDNS OPT meta-RR information in `dns.opt` field * Document EDNS fields * Do some EDNS checks (UDP packet size, OPT RR present in request and response) * Update vendor miekg/dns (EDNS fix) * Add DNSSEC RRs * Add new tests: begin `names_test.go` for RRs specific tests * Refactor rrToMapStr/rrToString to use only one switch case * Instantiate a logp.MakeDebug and use it throughout the dns package --- CHANGELOG.asciidoc | 1 + glide.yaml | 2 +- packetbeat/docs/fields.asciidoc | 24 ++ packetbeat/etc/fields.yml | 16 + packetbeat/protos/dns/README.md | 2 - packetbeat/protos/dns/dns.go | 341 ++++++++++++------ packetbeat/protos/dns/dns_tcp.go | 14 +- packetbeat/protos/dns/dns_test.go | 4 + packetbeat/protos/dns/dns_udp.go | 12 +- packetbeat/protos/dns/dns_udp_test.go | 113 +++++- packetbeat/protos/dns/errors.go | 29 +- packetbeat/protos/dns/names.go | 44 +++ packetbeat/protos/dns/names_test.go | 124 +++++++ .../tests/system/pcaps/dns_udp_edns_ds.pcap | Bin 0 -> 461 bytes packetbeat/tests/system/test_0032_dns.py | 26 ++ vendor/github.com/miekg/dns/.gitignore | 4 - vendor/github.com/miekg/dns/.travis.yml | 2 +- vendor/github.com/miekg/dns/README.md | 6 +- vendor/github.com/miekg/dns/client.go | 44 ++- vendor/github.com/miekg/dns/client_test.go | 48 +++ vendor/github.com/miekg/dns/defaults.go | 14 +- vendor/github.com/miekg/dns/dns_test.go | 5 +- vendor/github.com/miekg/dns/dnsutil/util.go | 79 ++++ .../github.com/miekg/dns/dnsutil/util_test.go | 130 +++++++ vendor/github.com/miekg/dns/doc.go | 2 +- vendor/github.com/miekg/dns/edns.go | 9 +- vendor/github.com/miekg/dns/edns_test.go | 26 +- vendor/github.com/miekg/dns/issue_test.go | 23 ++ vendor/github.com/miekg/dns/labels.go | 6 + vendor/github.com/miekg/dns/labels_test.go | 37 +- vendor/github.com/miekg/dns/msg.go | 5 + vendor/github.com/miekg/dns/parse_test.go | 12 +- vendor/github.com/miekg/dns/sanitize_test.go | 12 +- vendor/github.com/miekg/dns/tlsa.go | 2 +- vendor/github.com/miekg/dns/tsig.go | 4 +- vendor/github.com/miekg/dns/tsig_test.go | 37 ++ vendor/github.com/miekg/dns/types_generate.go | 15 +- vendor/github.com/miekg/dns/udp.go | 2 +- vendor/github.com/miekg/dns/udp_other.go | 2 +- vendor/github.com/miekg/dns/udp_plan9.go | 34 ++ vendor/github.com/miekg/dns/update.go | 88 +++-- vendor/github.com/miekg/dns/update_test.go | 60 +++ vendor/github.com/miekg/dns/xfr.go | 4 +- vendor/github.com/miekg/dns/zscan_rr.go | 128 +++---- 44 files changed, 1253 insertions(+), 339 deletions(-) create mode 100644 packetbeat/protos/dns/names_test.go create mode 100644 packetbeat/tests/system/pcaps/dns_udp_edns_ds.pcap delete mode 100644 vendor/github.com/miekg/dns/.gitignore create mode 100644 vendor/github.com/miekg/dns/dnsutil/util.go create mode 100644 vendor/github.com/miekg/dns/dnsutil/util_test.go create mode 100644 vendor/github.com/miekg/dns/issue_test.go create mode 100644 vendor/github.com/miekg/dns/tsig_test.go create mode 100644 vendor/github.com/miekg/dns/udp_plan9.go diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index 1b12e16d1b7..61d8ded62e4 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -185,6 +185,7 @@ https://github.com/elastic/beats/compare/v1.2.0...5.0.0-alpha1[View commits] - Change the DNS library used throughout the dns package to github.com/miekg/dns. {pull}803[803] - Add support for NFS v3 and v4. {pull}1231[1231] +- Add support for EDNS and DNSSEC. {pull}1292[1292] *Topbeat* diff --git a/glide.yaml b/glide.yaml index 1064994ab3d..704ec043d74 100644 --- a/glide.yaml +++ b/glide.yaml @@ -42,7 +42,7 @@ import: - package: github.com/go-ole/go-ole version: v1.2.0 - package: github.com/miekg/dns - version: 85b661b2a6fc95a5a83e66d7730c4bc0b6e9c99e + version: c9d1302d540edfb97d9ecbfe90b4fb515088630b - package: github.com/Shopify/sarama version: v1.8.0 - package: github.com/klauspost/crc32 diff --git a/packetbeat/docs/fields.asciidoc b/packetbeat/docs/fields.asciidoc index ef1f3af4fb5..4e271c767f7 100644 --- a/packetbeat/docs/fields.asciidoc +++ b/packetbeat/docs/fields.asciidoc @@ -638,6 +638,30 @@ The time interval in seconds that this resource record may be cached before it s The data describing the resource. The meaning of this data depends on the type and class of the resource record. +==== dns.opt.version + +example: 0 + +The EDNS version. + +==== dns.opt.do + +type: bool + +If set, the transaction uses DNSSEC. + +==== dns.opt.ext_rcode + +example: BADVERS + +Extended response code field. + +==== dns.opt.udp_size + +type: int + +Requestor's UDP payload size (in bytes). + === amqp Fields AMQP specific event fields. diff --git a/packetbeat/etc/fields.yml b/packetbeat/etc/fields.yml index 05f2b71d69e..261bc4c9666 100644 --- a/packetbeat/etc/fields.yml +++ b/packetbeat/etc/fields.yml @@ -644,6 +644,22 @@ trans_event: The data describing the resource. The meaning of this data depends on the type and class of the resource record. + - name: opt.version + description: The EDNS version. + example: "0" + + - name: opt.do + type: bool + description: If set, the transaction uses DNSSEC. + + - name: opt.ext_rcode + description: Extended response code field. + example: "BADVERS" + + - name: opt.udp_size + type: int + description: Requestor's UDP payload size (in bytes). + - name: amqp type: group description: AMQP specific event fields. diff --git a/packetbeat/protos/dns/README.md b/packetbeat/protos/dns/README.md index 9cc27c567b8..55f37571fff 100644 --- a/packetbeat/protos/dns/README.md +++ b/packetbeat/protos/dns/README.md @@ -35,8 +35,6 @@ When response error Notes are linked to the previous request, the transaction is **General** * Publish an event with Notes when a Query or a lone Response cannot be decoded. -* Add EDNS and DNSSEC support (consider using miekg/dns instead - of gopacket). * Consider adding ICMP support to - correlate ICMP type 3, code 4 (datagram too big) with DNS messages, - correlate ICMP type 3, code 13 (administratively prohibited) or diff --git a/packetbeat/protos/dns/dns.go b/packetbeat/protos/dns/dns.go index b50beb3ea21..c7358737588 100644 --- a/packetbeat/protos/dns/dns.go +++ b/packetbeat/protos/dns/dns.go @@ -8,8 +8,10 @@ package dns import ( + "bytes" "fmt" "net" + "sort" "strconv" "strings" "time" @@ -24,6 +26,10 @@ import ( "golang.org/x/net/publicsuffix" ) +var ( + debugf = logp.MakeDebug("dns") +) + const MaxDnsTupleRawSize = 16 + 16 + 2 + 2 + 4 + 1 // Constants used to associate the DNS QR flag with a meaningful value. @@ -275,35 +281,64 @@ func (dns *Dns) ConnectionTimeout() time.Duration { } func (dns *Dns) receivedDnsRequest(tuple *DnsTuple, msg *DnsMessage) { - logp.Debug("dns", "Processing query. %s", tuple.String()) + debugf("Processing query. %s", tuple.String()) trans := dns.deleteTransaction(tuple.Hashable()) if trans != nil { // This happens if a client puts multiple requests in flight // with the same ID. trans.Notes = append(trans.Notes, DuplicateQueryMsg.Error()) - logp.Debug("dns", "%s %s", DuplicateQueryMsg.Error(), tuple.String()) + debugf("%s %s", DuplicateQueryMsg.Error(), tuple.String()) dns.publishTransaction(trans) dns.deleteTransaction(trans.tuple.Hashable()) } trans = newTransaction(msg.Ts, *tuple, *msg.CmdlineTuple) + + if tuple.Transport == TransportUdp && (msg.Data.IsEdns0() != nil) && msg.Length > MaxDnsPacketSize { + trans.Notes = append(trans.Notes, UdpPacketTooLarge.Error()) + debugf("%s", UdpPacketTooLarge.Error()) + } + dns.transactions.Put(tuple.Hashable(), trans) trans.Request = msg } func (dns *Dns) receivedDnsResponse(tuple *DnsTuple, msg *DnsMessage) { - logp.Debug("dns", "Processing response. %s", tuple.String()) + debugf("Processing response. %s", tuple.String()) trans := dns.getTransaction(tuple.RevHashable()) if trans == nil { trans = newTransaction(msg.Ts, tuple.Reverse(), common.CmdlineTuple{ Src: msg.CmdlineTuple.Dst, Dst: msg.CmdlineTuple.Src}) trans.Notes = append(trans.Notes, OrphanedResponse.Error()) - logp.Debug("dns", "%s %s", OrphanedResponse.Error(), tuple.String()) + debugf("%s %s", OrphanedResponse.Error(), tuple.String()) } trans.Response = msg + + if tuple.Transport == TransportUdp { + respIsEdns := msg.Data.IsEdns0() != nil + if !respIsEdns && msg.Length > MaxDnsPacketSize { + trans.Notes = append(trans.Notes, UdpPacketTooLarge.ResponseError()) + debugf("%s", UdpPacketTooLarge.ResponseError()) + } + + request := trans.Request + if request != nil { + reqIsEdns := request.Data.IsEdns0() != nil + + switch { + case reqIsEdns && !respIsEdns: + trans.Notes = append(trans.Notes, RespEdnsNoSupport.Error()) + debugf("%s %s", RespEdnsNoSupport.Error(), tuple.String()) + case !reqIsEdns && respIsEdns: + trans.Notes = append(trans.Notes, RespEdnsUnexpected.Error()) + debugf("%s %s", RespEdnsUnexpected.Error(), tuple.String()) + } + } + } + dns.publishTransaction(trans) dns.deleteTransaction(trans.tuple.Hashable()) } @@ -313,7 +348,7 @@ func (dns *Dns) publishTransaction(t *DnsTransaction) { return } - logp.Debug("dns", "Publishing transaction. %s", t.tuple.String()) + debugf("Publishing transaction. %s", t.tuple.String()) event := common.MapStr{} event["@timestamp"] = common.Time(t.ts) @@ -385,7 +420,7 @@ func (dns *Dns) publishTransaction(t *DnsTransaction) { func (dns *Dns) expireTransaction(t *DnsTransaction) { t.Notes = append(t.Notes, NoResponse.Error()) - logp.Debug("dns", "%s %s", NoResponse.Error(), t.tuple.String()) + debugf("%s %s", NoResponse.Error(), t.tuple.String()) dns.publishTransaction(t) } @@ -419,167 +454,249 @@ func addDnsToMapStr(m common.MapStr, dns *mkdns.Msg, authority bool, additional } } + rrOPT := dns.IsEdns0() + if rrOPT != nil { + m["opt"] = optToMapStr(rrOPT) + } + m["answers_count"] = len(dns.Answer) if len(dns.Answer) > 0 { - m["answers"] = rrToMapStr(dns.Answer) + m["answers"] = rrsToMapStrs(dns.Answer) } m["authorities_count"] = len(dns.Ns) if authority && len(dns.Ns) > 0 { - m["authorities"] = rrToMapStr(dns.Ns) + m["authorities"] = rrsToMapStrs(dns.Ns) } - m["additionals_count"] = len(dns.Extra) + if rrOPT != nil { + m["additionals_count"] = len(dns.Extra) - 1 + } else { + m["additionals_count"] = len(dns.Extra) + } if additional && len(dns.Extra) > 0 { - m["additionals"] = rrToMapStr(dns.Extra) + rrsMapStrs := rrsToMapStrs(dns.Extra) + // We do not want OPT RR to appear in the 'additional' section, + // that's why rrsMapStrs could be empty even though len(dns.Extra) > 0 + if len(rrsMapStrs) > 0 { + m["additionals"] = rrsMapStrs + } + } + +} + +func optToMapStr(rrOPT *mkdns.OPT) common.MapStr { + optMapStr := common.MapStr{ + "do": rrOPT.Do(), // true if DNSSEC + "version": strconv.FormatUint(uint64(rrOPT.Version()), 10), + "udp_size": rrOPT.UDPSize(), + "ext_rcode": dnsResponseCodeToString(rrOPT.ExtendedRcode()), + } + for _, o := range rrOPT.Option { + switch o.(type) { + case *mkdns.EDNS0_DAU: + optMapStr["dau"] = o.String() + case *mkdns.EDNS0_DHU: + optMapStr["dhu"] = o.String() + case *mkdns.EDNS0_EXPIRE: + optMapStr["local"] = o.String() + case *mkdns.EDNS0_LLQ: + optMapStr["llq"] = o.String() + case *mkdns.EDNS0_LOCAL: + optMapStr["local"] = o.String() + case *mkdns.EDNS0_N3U: + optMapStr["n3u"] = o.String() + case *mkdns.EDNS0_NSID: + optMapStr["nsid"] = o.String() + case *mkdns.EDNS0_SUBNET: + var draft string + if o.(*mkdns.EDNS0_SUBNET).DraftOption { + draft = " draft" + } + optMapStr["subnet"] = o.String() + draft + case *mkdns.EDNS0_UL: + optMapStr["ul"] = o.String() + } } + return optMapStr } -// rrToMapStr converts an array of DNSResourceRecord's to an array of MapStr's. -func rrToMapStr(records []mkdns.RR) []common.MapStr { +// rrsToMapStr converts an array of RR's to an array of MapStr's. +func rrsToMapStrs(records []mkdns.RR) []common.MapStr { mapStrArray := make([]common.MapStr, len(records)) for i, rr := range records { rrHeader := rr.Header() - rrType := rrHeader.Rrtype - mapStr := common.MapStr{ - "name": rrHeader.Name, - "type": dnsTypeToString(rrType), - "class": dnsClassToString(rrHeader.Class), - "ttl": strconv.FormatInt(int64(rrHeader.Ttl), 10), + mapStr := rrToMapStr(rr) + if len(mapStr) == 0 { // OPT pseudo-RR returns an empty MapStr + resizeStrArray := make([]common.MapStr, len(mapStrArray)-1) + copy(resizeStrArray, mapStrArray) + mapStrArray = resizeStrArray + continue } + mapStr["name"] = rrHeader.Name + mapStr["type"] = dnsTypeToString(rrHeader.Rrtype) + mapStr["class"] = dnsClassToString(rrHeader.Class) + mapStr["ttl"] = strconv.FormatInt(int64(rrHeader.Ttl), 10) mapStrArray[i] = mapStr - - switch x := rr.(type) { - default: - // We don't have special handling for this type - logp.Debug("dns", "No special handling for RR type %s", dnsTypeToString(rrType)) - unsupportedRR := new(mkdns.RFC3597) - err := unsupportedRR.ToRFC3597(x) - if err == nil { - rData, err := hexStringToString(unsupportedRR.Rdata) - mapStr["data"] = rData - if err != nil { - logp.Debug("dns", "%s", err.Error()) - } - } else { - logp.Debug("dns", "Rdata for the unhandled RR type %s could not be fetched", dnsTypeToString(rrType)) - } - case *mkdns.A: - mapStr["data"] = x.A.String() - case *mkdns.AAAA: - mapStr["data"] = x.AAAA.String() - case *mkdns.CNAME: - mapStr["data"] = x.Target - case *mkdns.MX: - mapStr["preference"] = x.Preference - mapStr["data"] = x.Mx - case *mkdns.NS: - mapStr["data"] = x.Ns - case *mkdns.PTR: - mapStr["data"] = x.Ptr - case *mkdns.RFC3597: - // Miekg/dns lib doesn't handle this type - //TODO: write a test for this. - logp.Debug("dns", "Unknown RR type %s", dnsTypeToString(rrType)) - rData, err := hexStringToString(x.Rdata) - mapStr["data"] = rData - if err != nil { - logp.Debug("dns", "%s", err.Error()) - } - case *mkdns.SOA: - mapStr["rname"] = x.Mbox - mapStr["serial"] = x.Serial - mapStr["refresh"] = x.Refresh - mapStr["retry"] = x.Retry - mapStr["expire"] = x.Expire - mapStr["minimum"] = x.Minttl - mapStr["data"] = x.Ns - case *mkdns.SRV: - mapStr["priority"] = x.Priority - mapStr["weight"] = x.Weight - mapStr["port"] = x.Port - mapStr["data"] = x.Target - case *mkdns.TXT: - mapStr["data"] = strings.Join(x.Txt, " ") - } } - return mapStrArray } -// dnsQuestionToString converts a Question to a string. -func dnsQuestionToString(q mkdns.Question) string { - name := q.Name +// Convert all RDATA fields of a RR to a single string +// fields are ordered alphabetically with 'data' as the last element +// +// TODO An improvement would be to replace 'data' by the real field name +// It would require some changes in unit tests +func rrToString(rr mkdns.RR) string { + var st string + var keys []string + + mapStr := rrToMapStr(rr) + data, ok := mapStr["data"] + delete(mapStr, "data") + + for k, _ := range mapStr { + keys = append(keys, k) + } + sort.Strings(keys) + + var b bytes.Buffer + for _, k := range keys { + v := mapStr[k] + switch x := v.(type) { + case int: + fmt.Fprintf(&b, "%s %d, ", k, x) + case string: + fmt.Fprintf(&b, "%s %s, ", k, x) + } + } + if !ok { + st = strings.TrimSuffix(b.String(), ", ") + return st + } - return fmt.Sprintf("class %s, type %s, %s", dnsClassToString(q.Qclass), - dnsTypeToString(q.Qtype), name) + switch x := data.(type) { + case int: + fmt.Fprintf(&b, "%d", x) + case string: + fmt.Fprintf(&b, "%s", x) + } + return b.String() } -// dnsResourceRecordToString converts a RR to a string. -func dnsResourceRecordToString(rr mkdns.RR) string { - rrHeader := rr.Header() - rrType := rrHeader.Rrtype +func rrToMapStr(rr mkdns.RR) common.MapStr { + mapStr := common.MapStr{} + rrType := rr.Header().Rrtype - var data string switch x := rr.(type) { default: // We don't have special handling for this type - logp.Debug("dns", "No special handling for RR type %s", dnsTypeToString(rrType)) + debugf("No special handling for RR type %s", dnsTypeToString(rrType)) unsupportedRR := new(mkdns.RFC3597) err := unsupportedRR.ToRFC3597(x) if err == nil { rData, err := hexStringToString(unsupportedRR.Rdata) - data = rData + mapStr["data"] = rData if err != nil { - logp.Debug("dns", "%s", err.Error()) + debugf("%s", err.Error()) } } else { - logp.Debug("dns", "Rdata for the unhandled RR type %s could not be fetched", dnsTypeToString(rrType)) + debugf("Rdata for the unhandled RR type %s could not be fetched", dnsTypeToString(rrType)) } case *mkdns.A: - data = x.A.String() + mapStr["data"] = x.A.String() case *mkdns.AAAA: - data = x.AAAA.String() + mapStr["data"] = x.AAAA.String() case *mkdns.CNAME: - data = x.Target + mapStr["data"] = x.Target + case *mkdns.DNSKEY: + mapStr["flags"] = strconv.Itoa(int(x.Flags)) + mapStr["protocol"] = strconv.Itoa(int(x.Protocol)) + mapStr["algorithm"] = dnsAlgorithmToString(x.Algorithm) + mapStr["data"] = x.PublicKey + case *mkdns.DS: + mapStr["key_tag"] = strconv.Itoa(int(x.KeyTag)) + mapStr["algorithm"] = dnsAlgorithmToString(x.Algorithm) + mapStr["digest_type"] = dnsHashToString(x.DigestType) + mapStr["data"] = strings.ToUpper(x.Digest) case *mkdns.MX: - data = fmt.Sprintf("preference %d, %s", x.Preference, x.Mx) + mapStr["preference"] = x.Preference + mapStr["data"] = x.Mx case *mkdns.NS: - data = x.Ns + mapStr["data"] = x.Ns + case *mkdns.NSEC: + mapStr["type_bits"] = dnsTypeBitsMapToString(x.TypeBitMap) + mapStr["data"] = x.NextDomain + case *mkdns.NSEC3: + mapStr["hash"] = dnsHashToString(x.Hash) + mapStr["flags"] = strconv.Itoa(int(x.Flags)) + mapStr["iterations"] = strconv.Itoa(int(x.Iterations)) + mapStr["salt"] = dnsSaltToString(x.Salt) + mapStr["type_bits"] = dnsTypeBitsMapToString(x.TypeBitMap) + mapStr["data"] = x.NextDomain + case *mkdns.NSEC3PARAM: + mapStr["hash"] = dnsHashToString(x.Hash) + mapStr["flags"] = strconv.Itoa(int(x.Flags)) + mapStr["iterations"] = strconv.Itoa(int(x.Iterations)) + mapStr["data"] = dnsSaltToString(x.Salt) + case *mkdns.OPT: // EDNS [RFC6891] + // OPT pseudo-RR is managed in addDnsToMapStr function + return nil case *mkdns.PTR: - data = x.Ptr + mapStr["data"] = x.Ptr case *mkdns.RFC3597: // Miekg/dns lib doesn't handle this type - logp.Debug("dns", "Unknown RR type %s", dnsTypeToString(rrType)) + debugf("Unknown RR type %s", dnsTypeToString(rrType)) rData, err := hexStringToString(x.Rdata) - data = rData + mapStr["data"] = rData if err != nil { - logp.Debug("dns", "%s", err.Error()) + debugf("%s", err.Error()) } + case *mkdns.RRSIG: + mapStr["type_covered"] = dnsTypeToString(x.TypeCovered) + mapStr["algorithm"] = dnsAlgorithmToString(x.Algorithm) + mapStr["labels"] = strconv.Itoa(int(x.Labels)) + mapStr["original_ttl"] = strconv.FormatInt(int64(x.OrigTtl), 10) + mapStr["expiration"] = mkdns.TimeToString(x.Expiration) + mapStr["inception"] = mkdns.TimeToString(x.Inception) + mapStr["key_tag"] = strconv.Itoa(int(x.KeyTag)) + mapStr["signer_name"] = x.SignerName + mapStr["data"] = x.Signature case *mkdns.SOA: - data = fmt.Sprintf("mname %s, rname %s, serial %d, refresh %d, "+ - "retry %d, expire %d, minimum %d", x.Ns, x.Mbox, - x.Serial, x.Refresh, x.Retry, x.Expire, - x.Minttl) + mapStr["rname"] = x.Mbox + mapStr["serial"] = x.Serial + mapStr["refresh"] = x.Refresh + mapStr["retry"] = x.Retry + mapStr["expire"] = x.Expire + mapStr["minimum"] = x.Minttl + mapStr["data"] = x.Ns case *mkdns.SRV: - data = fmt.Sprintf("priority %d, weight %d, port %d, %s", x.Priority, - x.Weight, x.Port, x.Target) + mapStr["priority"] = x.Priority + mapStr["weight"] = x.Weight + mapStr["port"] = x.Port + mapStr["data"] = x.Target case *mkdns.TXT: - data = strings.Join(x.Txt, " ") + mapStr["data"] = strings.Join(x.Txt, " ") } - return fmt.Sprintf("%s: ttl %d, class %s, type %s, %s", rrHeader.Name, - int(rrHeader.Ttl), dnsClassToString(rrHeader.Class), - dnsTypeToString(rrType), data) + return mapStr +} + +// dnsQuestionToString converts a Question to a string. +func dnsQuestionToString(q mkdns.Question) string { + name := q.Name + + return fmt.Sprintf("class %s, type %s, %s", dnsClassToString(q.Qclass), + dnsTypeToString(q.Qtype), name) } -// dnsResourceRecordsToString converts an array of DNSResourceRecord's to a +// rrsToString converts an array of RR's to a // string. -func dnsResourceRecordsToString(r []mkdns.RR) string { +func rrsToString(r []mkdns.RR) string { var rrStrs []string for _, rr := range r { - rrStrs = append(rrStrs, dnsResourceRecordToString(rr)) + rrStrs = append(rrStrs, rrToString(rr)) } return strings.Join(rrStrs, "; ") } @@ -629,17 +746,17 @@ func dnsToString(dns *mkdns.Msg) string { if len(dns.Answer) > 0 { a = append(a, fmt.Sprintf("ANSWER %s", - dnsResourceRecordsToString(dns.Answer))) + rrsToString(dns.Answer))) } if len(dns.Ns) > 0 { a = append(a, fmt.Sprintf("AUTHORITY %s", - dnsResourceRecordsToString(dns.Ns))) + rrsToString(dns.Ns))) } if len(dns.Extra) > 0 { a = append(a, fmt.Sprintf("ADDITIONAL %s", - dnsResourceRecordsToString(dns.Extra))) + rrsToString(dns.Extra))) } return strings.Join(a, "; ") diff --git a/packetbeat/protos/dns/dns_tcp.go b/packetbeat/protos/dns/dns_tcp.go index 7bfddd6f67d..74df307477c 100644 --- a/packetbeat/protos/dns/dns_tcp.go +++ b/packetbeat/protos/dns/dns_tcp.go @@ -40,7 +40,7 @@ type dnsConnectionData struct { func (dns *Dns) Parse(pkt *protos.Packet, tcpTuple *common.TcpTuple, dir uint8, private protos.ProtocolData) protos.ProtocolData { defer logp.Recover("Dns ParseTcp") - logp.Debug("dns", "Parsing packet addressed with %s of length %d.", + debugf("Parsing packet addressed with %s of length %d.", pkt.Tuple.String(), len(pkt.Payload)) conn := ensureDnsConnection(private) @@ -85,7 +85,7 @@ func (dns *Dns) doParse(conn *dnsConnectionData, pkt *protos.Packet, tcpTuple *c stream.rawData = append(stream.rawData, payload...) if len(stream.rawData) > tcp.TCP_MAX_DATA_IN_STREAM { - logp.Debug("dns", "Stream data too large, dropping DNS stream") + debugf("Stream data too large, dropping DNS stream") conn.Data[dir] = nil return conn } @@ -95,7 +95,7 @@ func (dns *Dns) doParse(conn *dnsConnectionData, pkt *protos.Packet, tcpTuple *c if err != nil { if err == IncompleteMsg { - logp.Debug("dns", "Waiting for more raw data") + debugf("Waiting for more raw data") return conn } @@ -103,7 +103,7 @@ func (dns *Dns) doParse(conn *dnsConnectionData, pkt *protos.Packet, tcpTuple *c dns.publishResponseError(conn, err) } - logp.Debug("dns", "%s addresses %s, length %d", err.Error(), + debugf("%s addresses %s, length %d", err.Error(), tcpTuple.String(), len(stream.rawData)) // This means that malformed requests or responses are being sent... @@ -177,7 +177,7 @@ func (dns *Dns) ReceivedFin(tcpTuple *common.TcpTuple, dir uint8, private protos dns.publishResponseError(conn, err) } - logp.Debug("dns", "%s addresses %s, length %d", err.Error(), + debugf("%s addresses %s, length %d", err.Error(), tcpTuple.String(), len(stream.rawData)) return conn @@ -208,9 +208,9 @@ func (dns *Dns) GapInStream(tcpTuple *common.TcpTuple, dir uint8, nbytes int, pr dns.publishResponseError(conn, err) } - logp.Debug("dns", "%s addresses %s, length %d", err.Error(), + debugf("%s addresses %s, length %d", err.Error(), tcpTuple.String(), len(stream.rawData)) - logp.Debug("dns", "Dropping the stream %s", tcpTuple.String()) + debugf("Dropping the stream %s", tcpTuple.String()) // drop the stream because it is binary Data and it would be unexpected to have a decodable message later return private, true diff --git a/packetbeat/protos/dns/dns_test.go b/packetbeat/protos/dns/dns_test.go index 43c92ca5a0c..039338975ad 100644 --- a/packetbeat/protos/dns/dns_test.go +++ b/packetbeat/protos/dns/dns_test.go @@ -228,6 +228,10 @@ func assertFlags(t testing.TB, m common.MapStr, flags []string) { t.Fatalf("Unknown flag '%s' specified in test.", expected) case "aa": key = "dns.flags.authoritative" + case "ad": + key = "dns.flags.authentic_data" + case "cd": + key = "dns.flags.checking_disabled" case "ra": key = "dns.flags.recursion_available" case "rd": diff --git a/packetbeat/protos/dns/dns_udp.go b/packetbeat/protos/dns/dns_udp.go index 13d8ffaff06..72a2f6db41d 100644 --- a/packetbeat/protos/dns/dns_udp.go +++ b/packetbeat/protos/dns/dns_udp.go @@ -7,18 +7,22 @@ import ( "github.com/elastic/beats/packetbeat/protos" ) +// Only EDNS packets should have their size beyond this value +const MaxDnsPacketSize = (1 << 9) // 512 (bytes) + func (dns *Dns) ParseUdp(pkt *protos.Packet) { defer logp.Recover("Dns ParseUdp") + packetSize := len(pkt.Payload) - logp.Debug("dns", "Parsing packet addressed with %s of length %d.", - pkt.Tuple.String(), len(pkt.Payload)) + debugf("Parsing packet addressed with %s of length %d.", + pkt.Tuple.String(), packetSize) dnsPkt, err := decodeDnsData(TransportUdp, pkt.Payload) if err != nil { // This means that malformed requests or responses are being sent or // that someone is attempting to the DNS port for non-DNS traffic. Both // are issues that a monitoring system should report. - logp.Debug("dns", "%s", err.Error()) + debugf("%s", err.Error()) return } @@ -28,7 +32,7 @@ func (dns *Dns) ParseUdp(pkt *protos.Packet) { Tuple: pkt.Tuple, CmdlineTuple: procs.ProcWatcher.FindProcessesTuple(&pkt.Tuple), Data: dnsPkt, - Length: len(pkt.Payload), + Length: packetSize, } if dnsMsg.Data.Response { diff --git a/packetbeat/protos/dns/dns_udp_test.go b/packetbeat/protos/dns/dns_udp_test.go index 890fc71b470..9d513087066 100644 --- a/packetbeat/protos/dns/dns_udp_test.go +++ b/packetbeat/protos/dns/dns_udp_test.go @@ -10,7 +10,6 @@ // TODO: // * Add test validation for responsetime to make sure unit conversion // is being done correctly. -// * Add validation of special fields provided in MX, SOA, NS queries. // * Add test case to verify that Include_authorities and Include_additionals // are working. // * Add test case for Send_request and validate the stringified DNS message. @@ -29,9 +28,8 @@ import ( "github.com/elastic/beats/libbeat/common" - "github.com/stretchr/testify/assert" - mkdns "github.com/miekg/dns" + "github.com/stretchr/testify/assert" ) // Verify that the interface for UDP has been satisfied. @@ -46,6 +44,7 @@ var ( zoneIxfr, githubPtr, sophosTxt, + ednsSecA, } elasticA = DnsTestMessage{ @@ -194,6 +193,48 @@ var ( 0x6f, 0x73, 0x78, 0x6c, 0x03, 0x6e, 0x65, 0x74, 0x00, 0x00, 0x10, 0x00, 0x01, }, } + + ednsSecA = DnsTestMessage{ + id: 20498, + opcode: "QUERY", + flags: []string{"rd", "ad", "ra"}, + rcode: "NOERROR", + q_class: "IN", + q_type: "A", + q_name: "www.ietf.org.", + q_etld: "ietf.org.", + answers: []string{"64.170.98.30", "iDA8bJnrAEz3jgYnyFRm567a76qlv1V0CqxOSd/o9nvnN0GlZLaVoDmuXpaIaoypbGxwzwgK/LY6CV2k6SWKwicBmpENL26hwyjkFzPDW8kX3ibFhtfsOb8pYe7nBj326actp/7iG+DRuDmPnkYBja+wDYk61doTtkqZg57fn3iS97tjNPCC9C9knRAuDYUG+dVxalazSwYrpvY97dUC1H2spD0g4UdDyCbGA46mouZ4GPzNMewgf948qxrnU8pWPk3nQW5TgLVkGoWgco2owfLElBqf6rJ4LOswuhaw8IpTtmw3FsixxTLQvKOE5nftd1nMhQQd9CaHjoKNAUEz5Q=="}, + request: []byte{ + 0x50, 0x12, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x77, 0x77, 0x77, + 0x04, 0x69, 0x65, 0x74, 0x66, 0x03, 0x6f, 0x72, 0x67, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, + 0x29, 0x10, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + }, + response: []byte{ + 0x50, 0x12, 0x81, 0xa0, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x03, 0x77, 0x77, 0x77, + 0x04, 0x69, 0x65, 0x74, 0x66, 0x03, 0x6f, 0x72, 0x67, 0x00, 0x00, 0x01, 0x00, 0x01, 0xc0, 0x0c, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x04, 0xd8, 0x00, 0x04, 0x40, 0xaa, 0x62, 0x1e, 0xc0, 0x0c, + 0x00, 0x2e, 0x00, 0x01, 0x00, 0x00, 0x04, 0xd8, 0x01, 0x1c, 0x00, 0x01, 0x05, 0x03, 0x00, 0x00, + 0x07, 0x08, 0x52, 0xd0, 0x71, 0xc7, 0x50, 0xef, 0x30, 0xaf, 0x9e, 0x04, 0x04, 0x69, 0x65, 0x74, + 0x66, 0x03, 0x6f, 0x72, 0x67, 0x00, 0x88, 0x30, 0x3c, 0x6c, 0x99, 0xeb, 0x00, 0x4c, 0xf7, 0x8e, + 0x06, 0x27, 0xc8, 0x54, 0x66, 0xe7, 0xae, 0xda, 0xef, 0xaa, 0xa5, 0xbf, 0x55, 0x74, 0x0a, 0xac, + 0x4e, 0x49, 0xdf, 0xe8, 0xf6, 0x7b, 0xe7, 0x37, 0x41, 0xa5, 0x64, 0xb6, 0x95, 0xa0, 0x39, 0xae, + 0x5e, 0x96, 0x88, 0x6a, 0x8c, 0xa9, 0x6c, 0x6c, 0x70, 0xcf, 0x08, 0x0a, 0xfc, 0xb6, 0x3a, 0x09, + 0x5d, 0xa4, 0xe9, 0x25, 0x8a, 0xc2, 0x27, 0x01, 0x9a, 0x91, 0x0d, 0x2f, 0x6e, 0xa1, 0xc3, 0x28, + 0xe4, 0x17, 0x33, 0xc3, 0x5b, 0xc9, 0x17, 0xde, 0x26, 0xc5, 0x86, 0xd7, 0xec, 0x39, 0xbf, 0x29, + 0x61, 0xee, 0xe7, 0x06, 0x3d, 0xf6, 0xe9, 0xa7, 0x2d, 0xa7, 0xfe, 0xe2, 0x1b, 0xe0, 0xd1, 0xb8, + 0x39, 0x8f, 0x9e, 0x46, 0x01, 0x8d, 0xaf, 0xb0, 0x0d, 0x89, 0x3a, 0xd5, 0xda, 0x13, 0xb6, 0x4a, + 0x99, 0x83, 0x9e, 0xdf, 0x9f, 0x78, 0x92, 0xf7, 0xbb, 0x63, 0x34, 0xf0, 0x82, 0xf4, 0x2f, 0x64, + 0x9d, 0x10, 0x2e, 0x0d, 0x85, 0x06, 0xf9, 0xd5, 0x71, 0x6a, 0x56, 0xb3, 0x4b, 0x06, 0x2b, 0xa6, + 0xf6, 0x3d, 0xed, 0xd5, 0x02, 0xd4, 0x7d, 0xac, 0xa4, 0x3d, 0x20, 0xe1, 0x47, 0x43, 0xc8, 0x26, + 0xc6, 0x03, 0x8e, 0xa6, 0xa2, 0xe6, 0x78, 0x18, 0xfc, 0xcd, 0x31, 0xec, 0x20, 0x7f, 0xde, 0x3c, + 0xab, 0x1a, 0xe7, 0x53, 0xca, 0x56, 0x3e, 0x4d, 0xe7, 0x41, 0x6e, 0x53, 0x80, 0xb5, 0x64, 0x1a, + 0x85, 0xa0, 0x72, 0x8d, 0xa8, 0xc1, 0xf2, 0xc4, 0x94, 0x1a, 0x9f, 0xea, 0xb2, 0x78, 0x2c, 0xeb, + 0x30, 0xba, 0x16, 0xb0, 0xf0, 0x8a, 0x53, 0xb6, 0x6c, 0x37, 0x16, 0xc8, 0xb1, 0xc5, 0x32, 0xd0, + 0xbc, 0xa3, 0x84, 0xe6, 0x77, 0xed, 0x77, 0x59, 0xcc, 0x85, 0x04, 0x1d, 0xf4, 0x26, 0x87, 0x8e, + 0x82, 0x8d, 0x01, 0x41, 0x33, 0xe5, 0x00, 0x00, 0x29, 0x0f, 0xa0, 0x00, 0x00, 0x80, 0x00, 0x00, + 0x00, + }, + } ) // Verify that an empty packet is safely handled (no panics). @@ -332,6 +373,72 @@ func TestPublishTransaction_emptyDnsResponse(t *testing.T) { assert.Equal(t, common.ERROR_STATUS, mapValue(t, m, "status")) } +func TestPublishTransaction_edns(t *testing.T) { + dns := newDns(testing.Verbose()) + q := ednsSecA + packet := newPacket(forward, q.request) + dns.ParseUdp(packet) + assert.Equal(t, 1, dns.transactions.Size(), "There should be one transaction.") + packet = newPacket(reverse, q.response) + dns.ParseUdp(packet) + assert.Empty(t, dns.transactions.Size(), "There should be no transactions.") + + m := expectResult(t, dns) + assert.Equal(t, "udp", mapValue(t, m, "transport")) + assert.Equal(t, len(q.request), mapValue(t, m, "bytes_in")) + assert.Equal(t, len(q.response), mapValue(t, m, "bytes_out")) + assert.NotNil(t, mapValue(t, m, "responsetime")) + assert.Equal(t, common.OK_STATUS, mapValue(t, m, "status")) + assert.Nil(t, mapValue(t, m, "notes")) + assertMapStrData(t, m, q) +} + +// Verify that a non-edns answer to a edns query publishes Notes. +func TestPublishTransaction_respEdnsNoSupport(t *testing.T) { + dns := newDns(testing.Verbose()) + q := ednsSecA + q.response = q.response[:len(q.response)-11] // Remove OPT RR + + packet := newPacket(forward, q.request) + dns.ParseUdp(packet) + assert.Equal(t, 1, dns.transactions.Size(), "There should be one transaction.") + packet = newPacket(reverse, q.response) + dns.ParseUdp(packet) + assert.Empty(t, dns.transactions.Size(), "There should be no transactions.") + + m := expectResult(t, dns) + assert.Equal(t, "udp", mapValue(t, m, "transport")) + assert.Equal(t, len(q.request), mapValue(t, m, "bytes_in")) + assert.Equal(t, len(q.response), mapValue(t, m, "bytes_out")) + assert.NotNil(t, mapValue(t, m, "responsetime")) + assert.Equal(t, common.OK_STATUS, mapValue(t, m, "status")) + assert.Equal(t, RespEdnsNoSupport.Error(), mapValue(t, m, "notes")) + assertRequest(t, m, q) +} + +// Verify that a edns response to a non-edns query publishes Notes. +func TestPublishTransaction_respEdnsUnexpected(t *testing.T) { + dns := newDns(testing.Verbose()) + q := ednsSecA + q.request = q.request[:len(q.request)-11] // Remove OPT RR + + packet := newPacket(forward, q.request) + dns.ParseUdp(packet) + assert.Equal(t, 1, dns.transactions.Size(), "There should be one transaction.") + packet = newPacket(reverse, q.response) + dns.ParseUdp(packet) + assert.Empty(t, dns.transactions.Size(), "There should be no transactions.") + + m := expectResult(t, dns) + assert.Equal(t, "udp", mapValue(t, m, "transport")) + assert.Equal(t, len(q.request), mapValue(t, m, "bytes_in")) + assert.Equal(t, len(q.response), mapValue(t, m, "bytes_out")) + assert.NotNil(t, mapValue(t, m, "responsetime")) + assert.Equal(t, common.OK_STATUS, mapValue(t, m, "status")) + assert.Equal(t, RespEdnsUnexpected.Error(), mapValue(t, m, "notes")) + assertMapStrData(t, m, q) +} + // Benchmarks UDP parsing for the given test message. func benchmarkUdp(b *testing.B, q DnsTestMessage) { dns := newDns(false) diff --git a/packetbeat/protos/dns/errors.go b/packetbeat/protos/dns/errors.go index 735c99abafe..117034d980f 100644 --- a/packetbeat/protos/dns/errors.go +++ b/packetbeat/protos/dns/errors.go @@ -1,5 +1,9 @@ package dns +import ( + "fmt" +) + // All dns protocol errors are defined here. type Error interface { @@ -22,18 +26,25 @@ func (e *DNSError) ResponseError() string { return "Response: " + e.Error() } -// Messages +// Common var ( - NonDnsMsg = &DNSError{Err: "Message's data could not be decoded as DNS"} - ZeroLengthMsg = &DNSError{Err: "Message's length was set to zero"} - UnexpectedLengthMsg = &DNSError{Err: "Unexpected message data length"} - DuplicateQueryMsg = &DNSError{Err: "Another query with the same DNS ID from this client " + + NonDnsMsg = &DNSError{Err: "Message's data could not be decoded as DNS"} + DuplicateQueryMsg = &DNSError{Err: "Another query with the same DNS ID from this client " + "was received so this query was closed without receiving a response"} - IncompleteMsg = &DNSError{Err: "Message's data is incomplete"} - NoResponse = &DNSError{Err: "No response to this query was received"} + NoResponse = &DNSError{Err: "No response to this query was received"} + OrphanedResponse = &DNSError{Err: "Response: received without an associated Query"} ) -// TCP responses +// EDNS var ( - OrphanedResponse = &DNSError{Err: "Response: received without an associated Query"} + UdpPacketTooLarge = &DNSError{Err: fmt.Sprintf("Non-EDNS packet has size greater than %d", MaxDnsPacketSize)} + RespEdnsNoSupport = &DNSError{Err: "Responder does not support EDNS"} + RespEdnsUnexpected = &DNSError{Err: "Unexpected EDNS answer"} +) + +// TCP +var ( + ZeroLengthMsg = &DNSError{Err: "Message's length was set to zero"} + UnexpectedLengthMsg = &DNSError{Err: "Unexpected message data length"} + IncompleteMsg = &DNSError{Err: "Message's data is incomplete"} ) diff --git a/packetbeat/protos/dns/names.go b/packetbeat/protos/dns/names.go index aa2f517570a..b3e41935422 100644 --- a/packetbeat/protos/dns/names.go +++ b/packetbeat/protos/dns/names.go @@ -6,6 +6,7 @@ import ( "encoding/hex" "fmt" "strconv" + "strings" mkdns "github.com/miekg/dns" ) @@ -54,6 +55,49 @@ func dnsClassToString(c uint16) string { return s } +// dnsAlgorithmToString converts an algorithm value to a string. If the algorithm's +// string representation is unknown then the numeric value will be returned +// as a string. +func dnsAlgorithmToString(a uint8) string { + s, exists := mkdns.AlgorithmToString[uint8(a)] + if !exists { + return strconv.Itoa(int(a)) + } + return s +} + +// dnsHashToString converts a hash value to a string. If the hash's +// string representation is unknown then the numeric value will be returned +// as a string. +func dnsHashToString(h uint8) string { + s, exists := mkdns.HashToString[uint8(h)] + if !exists { + return strconv.Itoa(int(h)) + } + return s +} + +// dnsTypeBitsMapToString converts a map of type bits to a string. If the type's +// string representation is unknown then the numeric value will be returned +// as a string. +func dnsTypeBitsMapToString(t []uint16) string { + var s string + for i := 0; i < len(t); i++ { + s += dnsTypeToString(t[i]) + " " + } + return strings.TrimSuffix(s, " ") +} + +// saltToString converts a NSECX salt to uppercase and +// returns "-" when it is empty +// func copied from miekg/dns because unexported +func dnsSaltToString(s string) string { + if len(s) == 0 { + return "-" + } + return strings.ToUpper(s) +} + // hexStringToString converts an hexadeciaml string to string. Bytes // below 32 or above 126 are represented as an escaped base10 integer (\DDD). // Back slashes and quotes are escaped. Tabs, carriage returns, and line feeds diff --git a/packetbeat/protos/dns/names_test.go b/packetbeat/protos/dns/names_test.go new file mode 100644 index 00000000000..b5745d87391 --- /dev/null +++ b/packetbeat/protos/dns/names_test.go @@ -0,0 +1,124 @@ +// +build !integration + +// Unit tests and benchmarks for the dns package. +// This file contains tests for queries' RR type +// +// TODO: +// * Add validation of special fields provided in MX, SOA, NS... +// * Use struct DnsTestMsg fields question, answers, authorities,... for struct DnsTestMessage + +package dns + +import ( + "testing" + + "github.com/elastic/beats/libbeat/common" + + "github.com/stretchr/testify/assert" +) + +type DnsTestMsg struct { + rawData []byte + question common.MapStr + answers []common.MapStr + authorities []common.MapStr + additionals []common.MapStr + opt common.MapStr +} + +// DNS messages for testing. +var ( + // An array of all test messages. + dnsTestRRs = []DnsTestMsg{ + unhandledRR, + unknownRR, + opt, + } + + unhandledRR = DnsTestMsg{ // RR specified in a RFC but not implemented in the package dns + rawData: []byte{ + 0x21, 0x51, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x65, 0x6c, 0x61, + 0x73, 0x74, 0x69, 0x63, 0x02, 0x63, 0x6f, 0x00, 0x00, 0x1e, 0x00, 0x01, + }, + question: common.MapStr{ + "type": "NXT", + "name": "elastic.co.", + }, + } + + unknownRR = DnsTestMsg{ // RR unspecified in any known RFC + rawData: []byte{ + 0x21, 0x51, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x65, 0x6c, 0x61, + 0x73, 0x74, 0x69, 0x63, 0x02, 0x63, 0x6f, 0x00, 0xff, 0x00, 0x00, 0x01, + }, + question: common.MapStr{ + "type": "65280", + "name": "elastic.co.", + }, + } + + opt = DnsTestMsg{ + rawData: []byte{ + 0x50, 0x12, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x77, 0x77, 0x77, + 0x04, 0x69, 0x65, 0x74, 0x66, 0x03, 0x6f, 0x72, 0x67, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, + 0x29, 0x10, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + }, + question: common.MapStr{ + "type": "A", + "name": "www.ietf.org.", + }, + opt: common.MapStr{ + "version": "0", + "do": true, + }, + } +) + +// oracleRRs and rrs should be sorted in the same order +func assertRRs(t testing.TB, oracleRRs []common.MapStr, rrs []common.MapStr) { + assert.Equal(t, len(oracleRRs), len(rrs)) + for i, oracleRR := range oracleRRs { + rr := rrs[i] + for k, v := range oracleRR { + assert.NotNil(t, rr[k]) + assert.Equal(t, v, rr[k]) + } + } +} + +func assertDnsMessage(t testing.TB, q DnsTestMsg) { + dns, err := decodeDnsData(TransportUdp, q.rawData) + if err != nil { + t.Error("failed to decode dns data") + } + + mapStr := common.MapStr{} + addDnsToMapStr(mapStr, dns, true, true) + if q.question != nil { + for k, v := range q.question { + assert.NotNil(t, mapStr["question"].(common.MapStr)[k]) + assert.Equal(t, v, mapStr["question"].(common.MapStr)[k]) + } + } + if len(q.answers) > 0 { + assertRRs(t, q.answers, mapStr["answer"].([]common.MapStr)) + } + if len(q.authorities) > 0 { + assertRRs(t, q.authorities, mapStr["authorities"].([]common.MapStr)) + } + if len(q.additionals) > 0 { + assertRRs(t, q.additionals, mapStr["additionals"].([]common.MapStr)) + } + if q.opt != nil { + for k, v := range q.opt { + assert.NotNil(t, mapStr["opt"].(common.MapStr)[k]) + assert.Equal(t, v, mapStr["opt"].(common.MapStr)[k]) + } + } +} + +func TestAllRR(t *testing.T) { + for _, q := range dnsTestRRs { + assertDnsMessage(t, q) + } +} diff --git a/packetbeat/tests/system/pcaps/dns_udp_edns_ds.pcap b/packetbeat/tests/system/pcaps/dns_udp_edns_ds.pcap new file mode 100644 index 0000000000000000000000000000000000000000..7e16154ea33ac3a42d502ba6605cdceff76d52a3 GIT binary patch literal 461 zcmca|c+)~A1{MYw`2U}Qff2}=?EgPt=0rvYe;^x#876QGO+3gTb6~RZZw>}m1_sA- zQ49O5`9AAswLyJT9&$o9->Lq~c887_( zIql+lowIvSI&IL|pld!&^AR^tjULG1Kr^Q^XtOghFa|aR3ZG>O_`zSY*beM7Zj)l! z`r8J#_az>aTO8o29@-(k*mnO)pL_jVL!8=vEczTYQOVjZ)ZpmqrIrtG@7Z``&5^L) zt*gQh?dta`G|], zonename must be fully qualified SingleInflight bool // if true suppress multiple outstanding queries for the same Qname, Qtype and Qclass group singleflight @@ -39,14 +40,7 @@ type Client struct { // Exchange performs a synchronous UDP query. It sends the message m to the address // contained in a and waits for an reply. Exchange does not retry a failed query, nor // will it fall back to TCP in case of truncation. -// If you need to send a DNS message on an already existing connection, you can use the -// following: -// -// co := &dns.Conn{Conn: c} // c is your net.Conn -// co.WriteMsg(m) -// in, err := co.ReadMsg() -// co.Close() -// +// See client.Exchange for more information on setting larger buffer sizes. func Exchange(m *Msg, a string) (r *Msg, err error) { var co *Conn co, err = DialTimeout("udp", a, dnsTimeout) @@ -106,6 +100,10 @@ func ExchangeConn(c net.Conn, m *Msg) (r *Msg, err error) { // // Exchange does not retry a failed query, nor will it fall back to TCP in // case of truncation. +// It is up to the caller to create a message that allows for larger responses to be +// returned. Specifically this means adding an EDNS0 OPT RR that will advertise a larger +// buffer, see SetEdns0. Messsages without an OPT RR will fallback to the historic limit +// of 512 bytes. func (c *Client) Exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err error) { if !c.SingleInflight { return c.exchange(m, a) @@ -132,6 +130,9 @@ func (c *Client) Exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err erro } func (c *Client) dialTimeout() time.Duration { + if c.Timeout != 0 { + return c.Timeout + } if c.DialTimeout != 0 { return c.DialTimeout } @@ -173,6 +174,11 @@ func (c *Client) exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err erro } } + var deadline time.Time + if c.Timeout != 0 { + deadline = time.Now().Add(c.Timeout) + } + if tls { co, err = DialTimeoutWithTLS(network, a, c.TLSConfig, c.dialTimeout()) } else { @@ -195,12 +201,12 @@ func (c *Client) exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err erro } co.TsigSecret = c.TsigSecret - co.SetWriteDeadline(time.Now().Add(c.writeTimeout())) + co.SetWriteDeadline(deadlineOrTimeout(deadline, c.writeTimeout())) if err = co.WriteMsg(m); err != nil { return nil, 0, err } - co.SetReadDeadline(time.Now().Add(c.readTimeout())) + co.SetReadDeadline(deadlineOrTimeout(deadline, c.readTimeout())) r, err = co.ReadMsg() if err == nil && r.Id != m.Id { err = ErrId @@ -258,7 +264,7 @@ func (co *Conn) ReadMsgHeader(hdr *Header) ([]byte, error) { } p = make([]byte, l) n, err = tcpRead(r, p) - + co.rtt = time.Since(co.t) default: if co.UDPSize > MinMsgSize { p = make([]byte, co.UDPSize) @@ -266,6 +272,7 @@ func (co *Conn) ReadMsgHeader(hdr *Header) ([]byte, error) { p = make([]byte, MinMsgSize) } n, err = co.Read(p) + co.rtt = time.Since(co.t) } if err != nil { @@ -342,8 +349,6 @@ func (co *Conn) Read(p []byte) (n int, err error) { if err != nil { return n, err } - - co.rtt = time.Since(co.t) return n, err } @@ -438,3 +443,10 @@ func DialTimeoutWithTLS(network, address string, tlsConfig *tls.Config, timeout } return conn, nil } + +func deadlineOrTimeout(deadline time.Time, timeout time.Duration) time.Time { + if deadline.IsZero() { + return time.Now().Add(timeout) + } + return deadline +} diff --git a/vendor/github.com/miekg/dns/client_test.go b/vendor/github.com/miekg/dns/client_test.go index 09ec7037339..f3d229c4c14 100644 --- a/vendor/github.com/miekg/dns/client_test.go +++ b/vendor/github.com/miekg/dns/client_test.go @@ -419,3 +419,51 @@ func TestTruncatedMsg(t *testing.T) { t.Fail() } } + +func TestTimeout(t *testing.T) { + // Set up a dummy UDP server that won't respond + addr, err := net.ResolveUDPAddr("udp", "127.0.0.1:0") + if err != nil { + t.Fatalf("unable to resolve local udp address: %v", err) + } + conn, err := net.ListenUDP("udp", addr) + if err != nil { + t.Fatalf("unable to run test server: %v", err) + } + defer conn.Close() + addrstr := conn.LocalAddr().String() + + // Message to send + m := new(Msg) + m.SetQuestion("miek.nl.", TypeTXT) + + // Use a channel + timeout to ensure we don't get stuck if the + // Client Timeout is not working properly + done := make(chan struct{}) + + timeout := time.Millisecond + allowable := timeout + (10 * time.Millisecond) + abortAfter := timeout + (100 * time.Millisecond) + + start := time.Now() + + go func() { + c := &Client{Timeout: timeout} + _, _, err := c.Exchange(m, addrstr) + if err == nil { + t.Error("no timeout using Client") + } + done <- struct{}{} + }() + + select { + case <-done: + case <-time.After(abortAfter): + } + + length := time.Since(start) + + if length > allowable { + t.Errorf("exchange took longer (%v) than specified Timeout (%v)", length, timeout) + } +} diff --git a/vendor/github.com/miekg/dns/defaults.go b/vendor/github.com/miekg/dns/defaults.go index 63165b4fa9c..cf456165f43 100644 --- a/vendor/github.com/miekg/dns/defaults.go +++ b/vendor/github.com/miekg/dns/defaults.go @@ -142,9 +142,13 @@ func (dns *Msg) IsTsig() *TSIG { // record in the additional section will do. It returns the OPT record // found or nil. func (dns *Msg) IsEdns0() *OPT { - for _, r := range dns.Extra { - if r.Header().Rrtype == TypeOPT { - return r.(*OPT) + // EDNS0 is at the end of the additional section, start there. + // We might want to change this to *only* look at the last two + // records. So we see TSIG and/or OPT - this a slightly bigger + // change though. + for i := len(dns.Extra) - 1; i >= 0; i-- { + if dns.Extra[i].Header().Rrtype == TypeOPT { + return dns.Extra[i].(*OPT) } } return nil @@ -163,8 +167,8 @@ func IsDomainName(s string) (labels int, ok bool) { return labels, err == nil } -// IsSubDomain checks if child is indeed a child of the parent. Both child and -// parent are *not* downcased before doing the comparison. +// IsSubDomain checks if child is indeed a child of the parent. If child and parent +// are the same domain true is returned as well. func IsSubDomain(parent, child string) bool { // Entire child is contained in parent return CompareDomainName(parent, child) == CountLabel(parent) diff --git a/vendor/github.com/miekg/dns/dns_test.go b/vendor/github.com/miekg/dns/dns_test.go index 58fab4ca7b9..20b33f0cbd8 100644 --- a/vendor/github.com/miekg/dns/dns_test.go +++ b/vendor/github.com/miekg/dns/dns_test.go @@ -99,8 +99,9 @@ func TestPackUnpack3(t *testing.T) { func TestBailiwick(t *testing.T) { yes := map[string]string{ - "miek.nl": "ns.miek.nl", - ".": "miek.nl", + "miek1.nl": "miek1.nl", + "miek.nl": "ns.miek.nl", + ".": "miek.nl", } for parent, child := range yes { if !IsSubDomain(parent, child) { diff --git a/vendor/github.com/miekg/dns/dnsutil/util.go b/vendor/github.com/miekg/dns/dnsutil/util.go new file mode 100644 index 00000000000..9ed03f2969c --- /dev/null +++ b/vendor/github.com/miekg/dns/dnsutil/util.go @@ -0,0 +1,79 @@ +// Package dnsutil contains higher-level methods useful with the dns +// package. While package dns implements the DNS protocols itself, +// these functions are related but not directly required for protocol +// processing. They are often useful in preparing input/output of the +// functions in package dns. +package dnsutil + +import ( + "strings" + + "github.com/miekg/dns" +) + +// AddDomain adds origin to s if s is not already a FQDN. +// Note that the result may not be a FQDN. If origin does not end +// with a ".", the result won't either. +// This implements the zonefile convention (specified in RFC 1035, +// Section "5.1. Format") that "@" represents the +// apex (bare) domain. i.e. AddOrigin("@", "foo.com.") returns "foo.com.". +func AddOrigin(s, origin string) string { + // ("foo.", "origin.") -> "foo." (already a FQDN) + // ("foo", "origin.") -> "foo.origin." + // ("foo"), "origin" -> "foo.origin" + // ("@", "origin.") -> "origin." (@ represents the apex (bare) domain) + // ("", "origin.") -> "origin." (not obvious) + // ("foo", "") -> "foo" (not obvious) + + if dns.IsFqdn(s) { + return s // s is already a FQDN, no need to mess with it. + } + if len(origin) == 0 { + return s // Nothing to append. + } + if s == "@" || len(s) == 0 { + return origin // Expand apex. + } + + if origin == "." { + return s + origin // AddOrigin(s, ".") is an expensive way to add a ".". + } + + return s + "." + origin // The simple case. +} + +// TrimDomainName trims origin from s if s is a subdomain. +// This function will never return "", but returns "@" instead (@ represents the apex (bare) domain). +func TrimDomainName(s, origin string) string { + // An apex (bare) domain is always returned as "@". + // If the return value ends in a ".", the domain was not the suffix. + // origin can end in "." or not. Either way the results should be the same. + + if len(s) == 0 { + return "@" // Return the apex (@) rather than "". + } + // Someone is using TrimDomainName(s, ".") to remove a dot if it exists. + if origin == "." { + return strings.TrimSuffix(s, origin) + } + + // Dude, you aren't even if the right subdomain! + if !dns.IsSubDomain(origin, s) { + return s + } + + slabels := dns.Split(s) + olabels := dns.Split(origin) + m := dns.CompareDomainName(s, origin) + if len(olabels) == m { + if len(olabels) == len(slabels) { + return "@" // origin == s + } + if (s[0] == '.') && (len(slabels) == (len(olabels) + 1)) { + return "@" // TrimDomainName(".foo.", "foo.") + } + } + + // Return the first (len-m) labels: + return s[:slabels[len(slabels)-m]-1] +} diff --git a/vendor/github.com/miekg/dns/dnsutil/util_test.go b/vendor/github.com/miekg/dns/dnsutil/util_test.go new file mode 100644 index 00000000000..0f1ecec8e08 --- /dev/null +++ b/vendor/github.com/miekg/dns/dnsutil/util_test.go @@ -0,0 +1,130 @@ +package dnsutil + +import "testing" + +func TestAddOrigin(t *testing.T) { + var tests = []struct{ e1, e2, expected string }{ + {"@", "example.com", "example.com"}, + {"foo", "example.com", "foo.example.com"}, + {"foo.", "example.com", "foo."}, + {"@", "example.com.", "example.com."}, + {"foo", "example.com.", "foo.example.com."}, + {"foo.", "example.com.", "foo."}, + // Oddball tests: + // In general origin should not be "" or "." but at least + // these tests verify we don't crash and will keep results + // from changing unexpectedly. + {"*.", "", "*."}, + {"@", "", "@"}, + {"foobar", "", "foobar"}, + {"foobar.", "", "foobar."}, + {"*.", ".", "*."}, + {"@", ".", "."}, + {"foobar", ".", "foobar."}, + {"foobar.", ".", "foobar."}, + } + for _, test := range tests { + actual := AddOrigin(test.e1, test.e2) + if test.expected != actual { + t.Errorf("AddOrigin(%#v, %#v) expected %#v, go %#v\n", test.e1, test.e2, test.expected, actual) + } + } +} + +func TestTrimDomainName(t *testing.T) { + + // Basic tests. + // Try trimming "example.com" and "example.com." from typical use cases. + var tests_examplecom = []struct{ experiment, expected string }{ + {"foo.example.com", "foo"}, + {"foo.example.com.", "foo"}, + {".foo.example.com", ".foo"}, + {".foo.example.com.", ".foo"}, + {"*.example.com", "*"}, + {"example.com", "@"}, + {"example.com.", "@"}, + {"com.", "com."}, + {"foo.", "foo."}, + {"serverfault.com.", "serverfault.com."}, + {"serverfault.com", "serverfault.com"}, + {".foo.ronco.com", ".foo.ronco.com"}, + {".foo.ronco.com.", ".foo.ronco.com."}, + } + for _, dom := range []string{"example.com", "example.com."} { + for i, test := range tests_examplecom { + actual := TrimDomainName(test.experiment, dom) + if test.expected != actual { + t.Errorf("%d TrimDomainName(%#v, %#v): expected (%v) got (%v)\n", i, test.experiment, dom, test.expected, actual) + } + } + } + + // Paranoid tests. + // These test shouldn't be needed but I was weary of off-by-one errors. + // In theory, these can't happen because there are no single-letter TLDs, + // but it is good to exercize the code this way. + var tests = []struct{ experiment, expected string }{ + {"", "@"}, + {".", "."}, + {"a.b.c.d.e.f.", "a.b.c.d.e"}, + {"b.c.d.e.f.", "b.c.d.e"}, + {"c.d.e.f.", "c.d.e"}, + {"d.e.f.", "d.e"}, + {"e.f.", "e"}, + {"f.", "@"}, + {".a.b.c.d.e.f.", ".a.b.c.d.e"}, + {".b.c.d.e.f.", ".b.c.d.e"}, + {".c.d.e.f.", ".c.d.e"}, + {".d.e.f.", ".d.e"}, + {".e.f.", ".e"}, + {".f.", "@"}, + {"a.b.c.d.e.f", "a.b.c.d.e"}, + {"a.b.c.d.e.", "a.b.c.d.e."}, + {"a.b.c.d.e", "a.b.c.d.e"}, + {"a.b.c.d.", "a.b.c.d."}, + {"a.b.c.d", "a.b.c.d"}, + {"a.b.c.", "a.b.c."}, + {"a.b.c", "a.b.c"}, + {"a.b.", "a.b."}, + {"a.b", "a.b"}, + {"a.", "a."}, + {"a", "a"}, + {".a.b.c.d.e.f", ".a.b.c.d.e"}, + {".a.b.c.d.e.", ".a.b.c.d.e."}, + {".a.b.c.d.e", ".a.b.c.d.e"}, + {".a.b.c.d.", ".a.b.c.d."}, + {".a.b.c.d", ".a.b.c.d"}, + {".a.b.c.", ".a.b.c."}, + {".a.b.c", ".a.b.c"}, + {".a.b.", ".a.b."}, + {".a.b", ".a.b"}, + {".a.", ".a."}, + {".a", ".a"}, + } + for _, dom := range []string{"f", "f."} { + for i, test := range tests { + actual := TrimDomainName(test.experiment, dom) + if test.expected != actual { + t.Errorf("%d TrimDomainName(%#v, %#v): expected (%v) got (%v)\n", i, test.experiment, dom, test.expected, actual) + } + } + } + + // Test cases for bugs found in the wild. + // These test cases provide both origin, s, and the expected result. + // If you find a bug in the while, this is probably the easiest place + // to add it as a test case. + var tests_wild = []struct{ e1, e2, expected string }{ + {"mathoverflow.net.", ".", "mathoverflow.net"}, + {"mathoverflow.net", ".", "mathoverflow.net"}, + {"", ".", "@"}, + {"@", ".", "@"}, + } + for i, test := range tests_wild { + actual := TrimDomainName(test.e1, test.e2) + if test.expected != actual { + t.Errorf("%d TrimDomainName(%#v, %#v): expected (%v) got (%v)\n", i, test.e1, test.e2, test.expected, actual) + } + } + +} diff --git a/vendor/github.com/miekg/dns/doc.go b/vendor/github.com/miekg/dns/doc.go index e20f8a4453d..f3555e43399 100644 --- a/vendor/github.com/miekg/dns/doc.go +++ b/vendor/github.com/miekg/dns/doc.go @@ -101,7 +101,7 @@ uses public key cryptography to sign resource records. The public keys are stored in DNSKEY records and the signatures in RRSIG records. Requesting DNSSEC information for a zone is done by adding the DO (DNSSEC OK) bit -to an request. +to a request. m := new(dns.Msg) m.SetEdns0(4096, true) diff --git a/vendor/github.com/miekg/dns/edns.go b/vendor/github.com/miekg/dns/edns.go index 0c47f6ea5a4..867c810b3a2 100644 --- a/vendor/github.com/miekg/dns/edns.go +++ b/vendor/github.com/miekg/dns/edns.go @@ -96,13 +96,16 @@ func (rr *OPT) SetVersion(v uint8) { } // ExtendedRcode returns the EDNS extended RCODE field (the upper 8 bits of the TTL). -func (rr *OPT) ExtendedRcode() uint8 { - return uint8((rr.Hdr.Ttl & 0xFF000000) >> 24) +func (rr *OPT) ExtendedRcode() int { + return int((rr.Hdr.Ttl&0xFF000000)>>24) + 15 } // SetExtendedRcode sets the EDNS extended RCODE field. func (rr *OPT) SetExtendedRcode(v uint8) { - rr.Hdr.Ttl = rr.Hdr.Ttl&0x00FFFFFF | (uint32(v) << 24) + if v < RcodeBadVers { // Smaller than 16.. Use the 4 bits you have! + return + } + rr.Hdr.Ttl = rr.Hdr.Ttl&0x00FFFFFF | (uint32(v-15) << 24) } // UDPSize returns the UDP buffer size. diff --git a/vendor/github.com/miekg/dns/edns_test.go b/vendor/github.com/miekg/dns/edns_test.go index 8ee82ab4264..5fd75abb455 100644 --- a/vendor/github.com/miekg/dns/edns_test.go +++ b/vendor/github.com/miekg/dns/edns_test.go @@ -8,41 +8,25 @@ func TestOPTTtl(t *testing.T) { e.Hdr.Rrtype = TypeOPT if e.Do() { - t.Fail() + t.Errorf("DO bit should be zero") } e.SetDo() if !e.Do() { - t.Fail() + t.Errorf("DO bit should be non-zero") } - oldTtl := e.Hdr.Ttl - if e.Version() != 0 { - t.Fail() + t.Errorf("version should be non-zero") } e.SetVersion(42) if e.Version() != 42 { - t.Fail() - } - - e.SetVersion(0) - if e.Hdr.Ttl != oldTtl { - t.Fail() - } - - if e.ExtendedRcode() != 0 { - t.Fail() + t.Errorf("set 42, expected %d, got %d", 42, e.Version()) } e.SetExtendedRcode(42) if e.ExtendedRcode() != 42 { - t.Fail() - } - - e.SetExtendedRcode(0) - if e.Hdr.Ttl != oldTtl { - t.Fail() + t.Errorf("set 42, expected %d, got %d", 42-15, e.ExtendedRcode()) } } diff --git a/vendor/github.com/miekg/dns/issue_test.go b/vendor/github.com/miekg/dns/issue_test.go new file mode 100644 index 00000000000..3025fc98cb1 --- /dev/null +++ b/vendor/github.com/miekg/dns/issue_test.go @@ -0,0 +1,23 @@ +package dns + +// Tests that solve that an specific issue. + +import "testing" + +func TestTCPRtt(t *testing.T) { + m := new(Msg) + m.RecursionDesired = true + m.SetQuestion("example.org.", TypeA) + + c := &Client{} + for _, proto := range []string{"udp", "tcp"} { + c.Net = proto + _, rtt, err := c.Exchange(m, "8.8.4.4:53") + if err != nil { + t.Fatal(err) + } + if rtt == 0 { + t.Fatalf("expecting non zero rtt %s, got zero", c.Net) + } + } +} diff --git a/vendor/github.com/miekg/dns/labels.go b/vendor/github.com/miekg/dns/labels.go index 3944dd0632f..fca5c7dd2d1 100644 --- a/vendor/github.com/miekg/dns/labels.go +++ b/vendor/github.com/miekg/dns/labels.go @@ -4,9 +4,11 @@ package dns // SplitDomainName splits a name string into it's labels. // www.miek.nl. returns []string{"www", "miek", "nl"} +// .www.miek.nl. returns []string{"", "www", "miek", "nl"}, // The root label (.) returns nil. Note that using // strings.Split(s) will work in most cases, but does not handle // escaped dots (\.) for instance. +// s must be a syntactically valid domain name, see IsDomainName. func SplitDomainName(s string) (labels []string) { if len(s) == 0 { return nil @@ -45,6 +47,8 @@ func SplitDomainName(s string) (labels []string) { // // www.miek.nl. and miek.nl. have two labels in common: miek and nl // www.miek.nl. and www.bla.nl. have one label in common: nl +// +// s1 and s2 must be syntactically valid domain names. func CompareDomainName(s1, s2 string) (n int) { s1 = Fqdn(s1) s2 = Fqdn(s2) @@ -85,6 +89,7 @@ func CompareDomainName(s1, s2 string) (n int) { } // CountLabel counts the the number of labels in the string s. +// s must be a syntactically valid domain name. func CountLabel(s string) (labels int) { if s == "." { return @@ -103,6 +108,7 @@ func CountLabel(s string) (labels int) { // Split splits a name s into its label indexes. // www.miek.nl. returns []int{0, 4, 9}, www.miek.nl also returns []int{0, 4, 9}. // The root name (.) returns nil. Also see SplitDomainName. +// s must be a syntactically valid domain name. func Split(s string) []int { if s == "." { return nil diff --git a/vendor/github.com/miekg/dns/labels_test.go b/vendor/github.com/miekg/dns/labels_test.go index 2a3f3d05d9f..50f31163730 100644 --- a/vendor/github.com/miekg/dns/labels_test.go +++ b/vendor/github.com/miekg/dns/labels_test.go @@ -1,8 +1,6 @@ package dns -import ( - "testing" -) +import "testing" func TestCompareDomainName(t *testing.T) { s1 := "www.miek.nl." @@ -61,9 +59,9 @@ func TestSplit(t *testing.T) { func TestSplit2(t *testing.T) { splitter := map[string][]int{ - "www.miek.nl.": []int{0, 4, 9}, - "www.miek.nl": []int{0, 4, 9}, - "nl": []int{0}, + "www.miek.nl.": {0, 4, 9}, + "www.miek.nl": {0, 4, 9}, + "nl": {0}, } for s, i := range splitter { x := Split(s) @@ -125,13 +123,14 @@ func TestCountLabel(t *testing.T) { func TestSplitDomainName(t *testing.T) { labels := map[string][]string{ - "miek.nl": []string{"miek", "nl"}, + "miek.nl": {"miek", "nl"}, ".": nil, - "www.miek.nl.": []string{"www", "miek", "nl"}, - "www.miek.nl": []string{"www", "miek", "nl"}, - "www..miek.nl": []string{"www", "", "miek", "nl"}, - `www\.miek.nl`: []string{`www\.miek`, "nl"}, - `www\\.miek.nl`: []string{`www\\`, "miek", "nl"}, + "www.miek.nl.": {"www", "miek", "nl"}, + "www.miek.nl": {"www", "miek", "nl"}, + "www..miek.nl": {"www", "", "miek", "nl"}, + `www\.miek.nl`: {`www\.miek`, "nl"}, + `www\\.miek.nl`: {`www\\`, "miek", "nl"}, + ".www.miek.nl.": {"", "www", "miek", "nl"}, } domainLoop: for domain, splits := range labels { @@ -155,13 +154,13 @@ func TestIsDomainName(t *testing.T) { lab int } names := map[string]*ret{ - "..": &ret{false, 1}, - "@.": &ret{true, 1}, - "www.example.com": &ret{true, 3}, - "www.e%ample.com": &ret{true, 3}, - "www.example.com.": &ret{true, 3}, - "mi\\k.nl.": &ret{true, 2}, - "mi\\k.nl": &ret{true, 2}, + "..": {false, 1}, + "@.": {true, 1}, + "www.example.com": {true, 3}, + "www.e%ample.com": {true, 3}, + "www.example.com.": {true, 3}, + "mi\\k.nl.": {true, 2}, + "mi\\k.nl": {true, 2}, } for d, ok := range names { l, k := IsDomainName(d) diff --git a/vendor/github.com/miekg/dns/msg.go b/vendor/github.com/miekg/dns/msg.go index 692ad64cf04..7274800fc01 100644 --- a/vendor/github.com/miekg/dns/msg.go +++ b/vendor/github.com/miekg/dns/msg.go @@ -1896,6 +1896,11 @@ func Copy(r RR) RR { return r1 } +// Len returns the length (in octets) of the uncompressed RR in wire format. +func Len(r RR) int { + return r.len() +} + // Copy returns a new *Msg which is a deep-copy of dns. func (dns *Msg) Copy() *Msg { return dns.CopyTo(new(Msg)) diff --git a/vendor/github.com/miekg/dns/parse_test.go b/vendor/github.com/miekg/dns/parse_test.go index 0cbcd3ebfcd..a700f644412 100644 --- a/vendor/github.com/miekg/dns/parse_test.go +++ b/vendor/github.com/miekg/dns/parse_test.go @@ -768,7 +768,7 @@ func TestRfc1982(t *testing.T) { } func TestEmpty(t *testing.T) { - for _ = range ParseZone(strings.NewReader(""), "", "") { + for range ParseZone(strings.NewReader(""), "", "") { t.Errorf("should be empty") } } @@ -1208,11 +1208,11 @@ func TestNewPrivateKey(t *testing.T) { t.Skip("skipping test in short mode.") } algorithms := []algorithm{ - algorithm{ECDSAP256SHA256, 256}, - algorithm{ECDSAP384SHA384, 384}, - algorithm{RSASHA1, 1024}, - algorithm{RSASHA256, 2048}, - algorithm{DSA, 1024}, + {ECDSAP256SHA256, 256}, + {ECDSAP384SHA384, 384}, + {RSASHA1, 1024}, + {RSASHA256, 2048}, + {DSA, 1024}, } for _, algo := range algorithms { diff --git a/vendor/github.com/miekg/dns/sanitize_test.go b/vendor/github.com/miekg/dns/sanitize_test.go index 7933572791c..22d8e87987a 100644 --- a/vendor/github.com/miekg/dns/sanitize_test.go +++ b/vendor/github.com/miekg/dns/sanitize_test.go @@ -9,24 +9,24 @@ func TestDedup(t *testing.T) { newRR(t, "mIek.nl. IN A 127.0.0.1"), newRR(t, "mieK.nl. IN A 127.0.0.1"), newRR(t, "miek.Nl. IN A 127.0.0.1"), - }: []string{"mIek.nl.\t3600\tIN\tA\t127.0.0.1"}, + }: {"mIek.nl.\t3600\tIN\tA\t127.0.0.1"}, [...]RR{ newRR(t, "miEk.nl. 2000 IN A 127.0.0.1"), newRR(t, "mieK.Nl. 1000 IN A 127.0.0.1"), newRR(t, "Miek.nL. 500 IN A 127.0.0.1"), - }: []string{"miEk.nl.\t500\tIN\tA\t127.0.0.1"}, + }: {"miEk.nl.\t500\tIN\tA\t127.0.0.1"}, [...]RR{ newRR(t, "miek.nl. IN A 127.0.0.1"), newRR(t, "miek.nl. CH A 127.0.0.1"), newRR(t, "miek.nl. IN A 127.0.0.1"), - }: []string{"miek.nl.\t3600\tIN\tA\t127.0.0.1", + }: {"miek.nl.\t3600\tIN\tA\t127.0.0.1", "miek.nl.\t3600\tCH\tA\t127.0.0.1", }, [...]RR{ newRR(t, "miek.nl. CH A 127.0.0.1"), newRR(t, "miek.nl. IN A 127.0.0.1"), newRR(t, "miek.de. IN A 127.0.0.1"), - }: []string{"miek.nl.\t3600\tCH\tA\t127.0.0.1", + }: {"miek.nl.\t3600\tCH\tA\t127.0.0.1", "miek.nl.\t3600\tIN\tA\t127.0.0.1", "miek.de.\t3600\tIN\tA\t127.0.0.1", }, @@ -34,7 +34,7 @@ func TestDedup(t *testing.T) { newRR(t, "miek.de. IN A 127.0.0.1"), newRR(t, "miek.nl. 200 IN A 127.0.0.1"), newRR(t, "miek.nl. 300 IN A 127.0.0.1"), - }: []string{"miek.de.\t3600\tIN\tA\t127.0.0.1", + }: {"miek.de.\t3600\tIN\tA\t127.0.0.1", "miek.nl.\t200\tIN\tA\t127.0.0.1", }, } @@ -57,7 +57,7 @@ func BenchmarkDedup(b *testing.B) { } m := make(map[string]RR) for i := 0; i < b.N; i++ { - Dedup(rrs,m ) + Dedup(rrs, m) } } diff --git a/vendor/github.com/miekg/dns/tlsa.go b/vendor/github.com/miekg/dns/tlsa.go index f027787df30..0a550dc6cbf 100644 --- a/vendor/github.com/miekg/dns/tlsa.go +++ b/vendor/github.com/miekg/dns/tlsa.go @@ -82,5 +82,5 @@ func TLSAName(name, service, network string) (string, error) { if e != nil { return "", e } - return "_" + strconv.Itoa(p) + "_" + network + "." + name, nil + return "_" + strconv.Itoa(p) + "._" + network + "." + name, nil } diff --git a/vendor/github.com/miekg/dns/tsig.go b/vendor/github.com/miekg/dns/tsig.go index 32616d2d44c..c3374e19407 100644 --- a/vendor/github.com/miekg/dns/tsig.go +++ b/vendor/github.com/miekg/dns/tsig.go @@ -112,7 +112,7 @@ func TsigGenerate(m *Msg, secret, requestMAC string, timersOnly bool) ([]byte, s t := new(TSIG) var h hash.Hash - switch rr.Algorithm { + switch strings.ToLower(rr.Algorithm) { case HmacMD5: h = hmac.New(md5.New, []byte(rawsecret)) case HmacSHA1: @@ -178,7 +178,7 @@ func TsigVerify(msg []byte, secret, requestMAC string, timersOnly bool) error { } var h hash.Hash - switch tsig.Algorithm { + switch strings.ToLower(tsig.Algorithm) { case HmacMD5: h = hmac.New(md5.New, rawsecret) case HmacSHA1: diff --git a/vendor/github.com/miekg/dns/tsig_test.go b/vendor/github.com/miekg/dns/tsig_test.go new file mode 100644 index 00000000000..48b9988b662 --- /dev/null +++ b/vendor/github.com/miekg/dns/tsig_test.go @@ -0,0 +1,37 @@ +package dns + +import ( + "testing" + "time" +) + +func newTsig(algo string) *Msg { + m := new(Msg) + m.SetQuestion("example.org.", TypeA) + m.SetTsig("example.", algo, 300, time.Now().Unix()) + return m +} + +func TestTsig(t *testing.T) { + m := newTsig(HmacMD5) + buf, _, err := TsigGenerate(m, "pRZgBrBvI4NAHZYhxmhs/Q==", "", false) + if err != nil { + t.Fatal(err) + } + err = TsigVerify(buf, "pRZgBrBvI4NAHZYhxmhs/Q==", "", false) + if err != nil { + t.Fatal(err) + } +} + +func TestTsigCase(t *testing.T) { + m := newTsig("HmAc-mD5.sig-ALg.rEg.int.") // HmacMD5 + buf, _, err := TsigGenerate(m, "pRZgBrBvI4NAHZYhxmhs/Q==", "", false) + if err != nil { + t.Fatal(err) + } + err = TsigVerify(buf, "pRZgBrBvI4NAHZYhxmhs/Q==", "", false) + if err != nil { + t.Fatal(err) + } +} diff --git a/vendor/github.com/miekg/dns/types_generate.go b/vendor/github.com/miekg/dns/types_generate.go index 53690141a10..63bfda0e7ed 100644 --- a/vendor/github.com/miekg/dns/types_generate.go +++ b/vendor/github.com/miekg/dns/types_generate.go @@ -20,11 +20,11 @@ import ( ) var skipLen = map[string]struct{}{ - "NSEC": struct{}{}, - "NSEC3": struct{}{}, - "OPT": struct{}{}, - "WKS": struct{}{}, - "IPSECKEY": struct{}{}, + "NSEC": {}, + "NSEC3": {}, + "OPT": {}, + "WKS": {}, + "IPSECKEY": {}, } var packageHdr = ` @@ -229,7 +229,10 @@ func main() { if sl, ok := st.Field(i).Type().(*types.Slice); ok { t := sl.Underlying().String() t = strings.TrimPrefix(t, "[]") - t = strings.TrimPrefix(t, "github.com/miekg/dns.") + if strings.Contains(t, ".") { + splits := strings.Split(t, ".") + t = splits[len(splits)-1] + } fmt.Fprintf(b, "%s := make([]%s, len(rr.%s)); copy(%s, rr.%s)\n", f, t, f, f, f) fields = append(fields, f) diff --git a/vendor/github.com/miekg/dns/udp.go b/vendor/github.com/miekg/dns/udp.go index fc86563744e..c79c6c88371 100644 --- a/vendor/github.com/miekg/dns/udp.go +++ b/vendor/github.com/miekg/dns/udp.go @@ -1,4 +1,4 @@ -// +build !windows +// +build !windows,!plan9 package dns diff --git a/vendor/github.com/miekg/dns/udp_other.go b/vendor/github.com/miekg/dns/udp_other.go index c38dd3e7f0e..d40732441b0 100644 --- a/vendor/github.com/miekg/dns/udp_other.go +++ b/vendor/github.com/miekg/dns/udp_other.go @@ -1,4 +1,4 @@ -// +build !linux +// +build !linux,!plan9 package dns diff --git a/vendor/github.com/miekg/dns/udp_plan9.go b/vendor/github.com/miekg/dns/udp_plan9.go new file mode 100644 index 00000000000..b794deeba0f --- /dev/null +++ b/vendor/github.com/miekg/dns/udp_plan9.go @@ -0,0 +1,34 @@ +package dns + +import ( + "net" +) + +func setUDPSocketOptions(conn *net.UDPConn) error { return nil } + +// SessionUDP holds the remote address and the associated +// out-of-band data. +type SessionUDP struct { + raddr *net.UDPAddr + context []byte +} + +// RemoteAddr returns the remote network address. +func (s *SessionUDP) RemoteAddr() net.Addr { return s.raddr } + +// ReadFromSessionUDP acts just like net.UDPConn.ReadFrom(), but returns a session object instead of a +// net.UDPAddr. +func ReadFromSessionUDP(conn *net.UDPConn, b []byte) (int, *SessionUDP, error) { + oob := make([]byte, 40) + n, oobn, _, raddr, err := conn.ReadMsgUDP(b, oob) + if err != nil { + return n, nil, err + } + return n, &SessionUDP{raddr, oob[:oobn]}, err +} + +// WriteToSessionUDP acts just like net.UDPConn.WritetTo(), but uses a *SessionUDP instead of a net.Addr. +func WriteToSessionUDP(conn *net.UDPConn, b []byte, session *SessionUDP) (int, error) { + n, _, err := conn.WriteMsgUDP(b, session.context, session.raddr) + return n, err +} diff --git a/vendor/github.com/miekg/dns/update.go b/vendor/github.com/miekg/dns/update.go index 3539987ccb0..e90c5c968ec 100644 --- a/vendor/github.com/miekg/dns/update.go +++ b/vendor/github.com/miekg/dns/update.go @@ -3,18 +3,22 @@ package dns // NameUsed sets the RRs in the prereq section to // "Name is in use" RRs. RFC 2136 section 2.4.4. func (u *Msg) NameUsed(rr []RR) { - u.Answer = make([]RR, len(rr)) - for i, r := range rr { - u.Answer[i] = &ANY{Hdr: RR_Header{Name: r.Header().Name, Ttl: 0, Rrtype: TypeANY, Class: ClassANY}} + if u.Answer == nil { + u.Answer = make([]RR, 0, len(rr)) + } + for _, r := range rr { + u.Answer = append(u.Answer, &ANY{Hdr: RR_Header{Name: r.Header().Name, Ttl: 0, Rrtype: TypeANY, Class: ClassANY}}) } } // NameNotUsed sets the RRs in the prereq section to // "Name is in not use" RRs. RFC 2136 section 2.4.5. func (u *Msg) NameNotUsed(rr []RR) { - u.Answer = make([]RR, len(rr)) - for i, r := range rr { - u.Answer[i] = &ANY{Hdr: RR_Header{Name: r.Header().Name, Ttl: 0, Rrtype: TypeANY, Class: ClassNONE}} + if u.Answer == nil { + u.Answer = make([]RR, 0, len(rr)) + } + for _, r := range rr { + u.Answer = append(u.Answer, &ANY{Hdr: RR_Header{Name: r.Header().Name, Ttl: 0, Rrtype: TypeANY, Class: ClassNONE}}) } } @@ -24,34 +28,34 @@ func (u *Msg) Used(rr []RR) { if len(u.Question) == 0 { panic("dns: empty question section") } - u.Answer = make([]RR, len(rr)) - for i, r := range rr { - u.Answer[i] = r - u.Answer[i].Header().Class = u.Question[0].Qclass + if u.Answer == nil { + u.Answer = make([]RR, 0, len(rr)) + } + for _, r := range rr { + r.Header().Class = u.Question[0].Qclass + u.Answer = append(u.Answer, r) } } // RRsetUsed sets the RRs in the prereq section to // "RRset exists (value independent -- no rdata)" RRs. RFC 2136 section 2.4.1. func (u *Msg) RRsetUsed(rr []RR) { - u.Answer = make([]RR, len(rr)) - for i, r := range rr { - u.Answer[i] = r - u.Answer[i].Header().Class = ClassANY - u.Answer[i].Header().Ttl = 0 - u.Answer[i].Header().Rdlength = 0 + if u.Answer == nil { + u.Answer = make([]RR, 0, len(rr)) + } + for _, r := range rr { + u.Answer = append(u.Answer, &ANY{Hdr: RR_Header{Name: r.Header().Name, Ttl: 0, Rrtype: r.Header().Rrtype, Class: ClassANY}}) } } // RRsetNotUsed sets the RRs in the prereq section to // "RRset does not exist" RRs. RFC 2136 section 2.4.3. func (u *Msg) RRsetNotUsed(rr []RR) { - u.Answer = make([]RR, len(rr)) - for i, r := range rr { - u.Answer[i] = r - u.Answer[i].Header().Class = ClassNONE - u.Answer[i].Header().Rdlength = 0 - u.Answer[i].Header().Ttl = 0 + if u.Answer == nil { + u.Answer = make([]RR, 0, len(rr)) + } + for _, r := range rr { + u.Answer = append(u.Answer, &ANY{Hdr: RR_Header{Name: r.Header().Name, Ttl: 0, Rrtype: r.Header().Rrtype, Class: ClassNONE}}) } } @@ -60,35 +64,43 @@ func (u *Msg) Insert(rr []RR) { if len(u.Question) == 0 { panic("dns: empty question section") } - u.Ns = make([]RR, len(rr)) - for i, r := range rr { - u.Ns[i] = r - u.Ns[i].Header().Class = u.Question[0].Qclass + if u.Ns == nil { + u.Ns = make([]RR, 0, len(rr)) + } + for _, r := range rr { + r.Header().Class = u.Question[0].Qclass + u.Ns = append(u.Ns, r) } } // RemoveRRset creates a dynamic update packet that deletes an RRset, see RFC 2136 section 2.5.2. func (u *Msg) RemoveRRset(rr []RR) { - u.Ns = make([]RR, len(rr)) - for i, r := range rr { - u.Ns[i] = &ANY{Hdr: RR_Header{Name: r.Header().Name, Ttl: 0, Rrtype: r.Header().Rrtype, Class: ClassANY}} + if u.Ns == nil { + u.Ns = make([]RR, 0, len(rr)) + } + for _, r := range rr { + u.Ns = append(u.Ns, &ANY{Hdr: RR_Header{Name: r.Header().Name, Ttl: 0, Rrtype: r.Header().Rrtype, Class: ClassANY}}) } } // RemoveName creates a dynamic update packet that deletes all RRsets of a name, see RFC 2136 section 2.5.3 func (u *Msg) RemoveName(rr []RR) { - u.Ns = make([]RR, len(rr)) - for i, r := range rr { - u.Ns[i] = &ANY{Hdr: RR_Header{Name: r.Header().Name, Ttl: 0, Rrtype: TypeANY, Class: ClassANY}} + if u.Ns == nil { + u.Ns = make([]RR, 0, len(rr)) + } + for _, r := range rr { + u.Ns = append(u.Ns, &ANY{Hdr: RR_Header{Name: r.Header().Name, Ttl: 0, Rrtype: TypeANY, Class: ClassANY}}) } } -// Remove creates a dynamic update packet deletes RR from the RRSset, see RFC 2136 section 2.5.4 +// Remove creates a dynamic update packet deletes RR from a RRSset, see RFC 2136 section 2.5.4 func (u *Msg) Remove(rr []RR) { - u.Ns = make([]RR, len(rr)) - for i, r := range rr { - u.Ns[i] = r - u.Ns[i].Header().Class = ClassNONE - u.Ns[i].Header().Ttl = 0 + if u.Ns == nil { + u.Ns = make([]RR, 0, len(rr)) + } + for _, r := range rr { + r.Header().Class = ClassNONE + r.Header().Ttl = 0 + u.Ns = append(u.Ns, r) } } diff --git a/vendor/github.com/miekg/dns/update_test.go b/vendor/github.com/miekg/dns/update_test.go index 1c840309758..43ef61628e9 100644 --- a/vendor/github.com/miekg/dns/update_test.go +++ b/vendor/github.com/miekg/dns/update_test.go @@ -83,3 +83,63 @@ func TestRemoveRRset(t *testing.T) { t.Errorf("actual msg:\n%v", tmp) } } + +func TestPreReqAndRemovals(t *testing.T) { + // Build a list of multiple prereqs and then somes removes followed by an insert. + // We should be able to add multiple prereqs and updates. + m := new(Msg) + m.SetUpdate("example.org.") + m.Id = 1234 + + // Use a full set of RRs each time, so we are sure the rdata is stripped. + rr_name1, _ := NewRR("name_used. 3600 IN A 127.0.0.1") + rr_name2, _ := NewRR("name_not_used. 3600 IN A 127.0.0.1") + rr_remove1, _ := NewRR("remove1. 3600 IN A 127.0.0.1") + rr_remove2, _ := NewRR("remove2. 3600 IN A 127.0.0.1") + rr_remove3, _ := NewRR("remove3. 3600 IN A 127.0.0.1") + rr_insert, _ := NewRR("insert. 3600 IN A 127.0.0.1") + rr_rrset1, _ := NewRR("rrset_used1. 3600 IN A 127.0.0.1") + rr_rrset2, _ := NewRR("rrset_used2. 3600 IN A 127.0.0.1") + rr_rrset3, _ := NewRR("rrset_not_used. 3600 IN A 127.0.0.1") + + // Handle the prereqs. + m.NameUsed([]RR{rr_name1}) + m.NameNotUsed([]RR{rr_name2}) + m.RRsetUsed([]RR{rr_rrset1}) + m.Used([]RR{rr_rrset2}) + m.RRsetNotUsed([]RR{rr_rrset3}) + + // and now the updates. + m.RemoveName([]RR{rr_remove1}) + m.RemoveRRset([]RR{rr_remove2}) + m.Remove([]RR{rr_remove3}) + m.Insert([]RR{rr_insert}) + + // This test function isn't a Example function because we print these RR with tabs at the + // end and the Example function trim these, thus they never match. + // TODO(miek): don't print these tabs and make this into an Example function. + expect := `;; opcode: UPDATE, status: NOERROR, id: 1234 +;; flags:; QUERY: 1, ANSWER: 5, AUTHORITY: 4, ADDITIONAL: 0 + +;; QUESTION SECTION: +;example.org. IN SOA + +;; ANSWER SECTION: +name_used. 0 ANY ANY +name_not_used. 0 NONE ANY +rrset_used1. 0 ANY A +rrset_used2. 3600 IN A 127.0.0.1 +rrset_not_used. 0 NONE A + +;; AUTHORITY SECTION: +remove1. 0 ANY ANY +remove2. 0 ANY A +remove3. 0 NONE A 127.0.0.1 +insert. 3600 IN A 127.0.0.1 +` + + if m.String() != expect { + t.Errorf("expected msg:\n%s", expect) + t.Errorf("actual msg:\n%v", m.String()) + } +} diff --git a/vendor/github.com/miekg/dns/xfr.go b/vendor/github.com/miekg/dns/xfr.go index 7d3a67b8e45..7346deffbb7 100644 --- a/vendor/github.com/miekg/dns/xfr.go +++ b/vendor/github.com/miekg/dns/xfr.go @@ -162,8 +162,8 @@ func (t *Transfer) inIxfr(id uint16, c chan *Envelope) { // // ch := make(chan *dns.Envelope) // tr := new(dns.Transfer) -// tr.Out(w, r, ch) -// c <- &dns.Envelope{RR: []dns.RR{soa, rr1, rr2, rr3, soa}} +// go tr.Out(w, r, ch) +// ch <- &dns.Envelope{RR: []dns.RR{soa, rr1, rr2, rr3, soa}} // close(ch) // w.Hijack() // // w.Close() // Client closes connection diff --git a/vendor/github.com/miekg/dns/zscan_rr.go b/vendor/github.com/miekg/dns/zscan_rr.go index a2db008fa98..ead9614d13b 100644 --- a/vendor/github.com/miekg/dns/zscan_rr.go +++ b/vendor/github.com/miekg/dns/zscan_rr.go @@ -2203,68 +2203,68 @@ func setCAA(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { } var typeToparserFunc = map[uint16]parserFunc{ - TypeAAAA: parserFunc{setAAAA, false}, - TypeAFSDB: parserFunc{setAFSDB, false}, - TypeA: parserFunc{setA, false}, - TypeCAA: parserFunc{setCAA, true}, - TypeCDS: parserFunc{setCDS, true}, - TypeCDNSKEY: parserFunc{setCDNSKEY, true}, - TypeCERT: parserFunc{setCERT, true}, - TypeCNAME: parserFunc{setCNAME, false}, - TypeDHCID: parserFunc{setDHCID, true}, - TypeDLV: parserFunc{setDLV, true}, - TypeDNAME: parserFunc{setDNAME, false}, - TypeKEY: parserFunc{setKEY, true}, - TypeDNSKEY: parserFunc{setDNSKEY, true}, - TypeDS: parserFunc{setDS, true}, - TypeEID: parserFunc{setEID, true}, - TypeEUI48: parserFunc{setEUI48, false}, - TypeEUI64: parserFunc{setEUI64, false}, - TypeGID: parserFunc{setGID, false}, - TypeGPOS: parserFunc{setGPOS, false}, - TypeHINFO: parserFunc{setHINFO, true}, - TypeHIP: parserFunc{setHIP, true}, - TypeIPSECKEY: parserFunc{setIPSECKEY, true}, - TypeKX: parserFunc{setKX, false}, - TypeL32: parserFunc{setL32, false}, - TypeL64: parserFunc{setL64, false}, - TypeLOC: parserFunc{setLOC, true}, - TypeLP: parserFunc{setLP, false}, - TypeMB: parserFunc{setMB, false}, - TypeMD: parserFunc{setMD, false}, - TypeMF: parserFunc{setMF, false}, - TypeMG: parserFunc{setMG, false}, - TypeMINFO: parserFunc{setMINFO, false}, - TypeMR: parserFunc{setMR, false}, - TypeMX: parserFunc{setMX, false}, - TypeNAPTR: parserFunc{setNAPTR, false}, - TypeNID: parserFunc{setNID, false}, - TypeNIMLOC: parserFunc{setNIMLOC, true}, - TypeNINFO: parserFunc{setNINFO, true}, - TypeNSAPPTR: parserFunc{setNSAPPTR, false}, - TypeNSEC3PARAM: parserFunc{setNSEC3PARAM, false}, - TypeNSEC3: parserFunc{setNSEC3, true}, - TypeNSEC: parserFunc{setNSEC, true}, - TypeNS: parserFunc{setNS, false}, - TypeOPENPGPKEY: parserFunc{setOPENPGPKEY, true}, - TypePTR: parserFunc{setPTR, false}, - TypePX: parserFunc{setPX, false}, - TypeSIG: parserFunc{setSIG, true}, - TypeRKEY: parserFunc{setRKEY, true}, - TypeRP: parserFunc{setRP, false}, - TypeRRSIG: parserFunc{setRRSIG, true}, - TypeRT: parserFunc{setRT, false}, - TypeSOA: parserFunc{setSOA, false}, - TypeSPF: parserFunc{setSPF, true}, - TypeSRV: parserFunc{setSRV, false}, - TypeSSHFP: parserFunc{setSSHFP, true}, - TypeTALINK: parserFunc{setTALINK, false}, - TypeTA: parserFunc{setTA, true}, - TypeTLSA: parserFunc{setTLSA, true}, - TypeTXT: parserFunc{setTXT, true}, - TypeUID: parserFunc{setUID, false}, - TypeUINFO: parserFunc{setUINFO, true}, - TypeURI: parserFunc{setURI, true}, - TypeWKS: parserFunc{setWKS, true}, - TypeX25: parserFunc{setX25, false}, + TypeAAAA: {setAAAA, false}, + TypeAFSDB: {setAFSDB, false}, + TypeA: {setA, false}, + TypeCAA: {setCAA, true}, + TypeCDS: {setCDS, true}, + TypeCDNSKEY: {setCDNSKEY, true}, + TypeCERT: {setCERT, true}, + TypeCNAME: {setCNAME, false}, + TypeDHCID: {setDHCID, true}, + TypeDLV: {setDLV, true}, + TypeDNAME: {setDNAME, false}, + TypeKEY: {setKEY, true}, + TypeDNSKEY: {setDNSKEY, true}, + TypeDS: {setDS, true}, + TypeEID: {setEID, true}, + TypeEUI48: {setEUI48, false}, + TypeEUI64: {setEUI64, false}, + TypeGID: {setGID, false}, + TypeGPOS: {setGPOS, false}, + TypeHINFO: {setHINFO, true}, + TypeHIP: {setHIP, true}, + TypeIPSECKEY: {setIPSECKEY, true}, + TypeKX: {setKX, false}, + TypeL32: {setL32, false}, + TypeL64: {setL64, false}, + TypeLOC: {setLOC, true}, + TypeLP: {setLP, false}, + TypeMB: {setMB, false}, + TypeMD: {setMD, false}, + TypeMF: {setMF, false}, + TypeMG: {setMG, false}, + TypeMINFO: {setMINFO, false}, + TypeMR: {setMR, false}, + TypeMX: {setMX, false}, + TypeNAPTR: {setNAPTR, false}, + TypeNID: {setNID, false}, + TypeNIMLOC: {setNIMLOC, true}, + TypeNINFO: {setNINFO, true}, + TypeNSAPPTR: {setNSAPPTR, false}, + TypeNSEC3PARAM: {setNSEC3PARAM, false}, + TypeNSEC3: {setNSEC3, true}, + TypeNSEC: {setNSEC, true}, + TypeNS: {setNS, false}, + TypeOPENPGPKEY: {setOPENPGPKEY, true}, + TypePTR: {setPTR, false}, + TypePX: {setPX, false}, + TypeSIG: {setSIG, true}, + TypeRKEY: {setRKEY, true}, + TypeRP: {setRP, false}, + TypeRRSIG: {setRRSIG, true}, + TypeRT: {setRT, false}, + TypeSOA: {setSOA, false}, + TypeSPF: {setSPF, true}, + TypeSRV: {setSRV, false}, + TypeSSHFP: {setSSHFP, true}, + TypeTALINK: {setTALINK, false}, + TypeTA: {setTA, true}, + TypeTLSA: {setTLSA, true}, + TypeTXT: {setTXT, true}, + TypeUID: {setUID, false}, + TypeUINFO: {setUINFO, true}, + TypeURI: {setURI, true}, + TypeWKS: {setWKS, true}, + TypeX25: {setX25, false}, }