diff options
author | Peter Wu <peter@lekensteyn.nl> | 2020-10-23 22:45:55 +0200 |
---|---|---|
committer | Peter Wu <peter@lekensteyn.nl> | 2020-10-24 11:21:44 +0000 |
commit | cad17858685e94cb763c492cbae400c30ab686dd (patch) | |
tree | e03034c7e472b2ae45e14f22dc94162c68fc707e /epan/dissectors/packet-dns.c | |
parent | 00c09b8696fe2ea550af02f06cf81889eeab43a8 (diff) |
dns: detect DNS over UDP on non-standard ports using heuristics
While running a test suite of a DNS server, a lot of DNS messages on
non-standard ports were not recognized. Rather than manually discovering
and decoding every port using an iterative process of checking the
output of the `udp and not dns` filter, have some heuristics to detect
DNS messages automatically.
Enable these heuristics by default assuming that the checks are strong
enough, 8 bytes are essentially fixed to a low number of possibilities.
Should it cause issued, then the heuristics could be disabled (assuming
that non-standard DNS ports are uncommon) or strengthened.
Diffstat (limited to 'epan/dissectors/packet-dns.c')
-rw-r--r-- | epan/dissectors/packet-dns.c | 60 |
1 files changed, 60 insertions, 0 deletions
diff --git a/epan/dissectors/packet-dns.c b/epan/dissectors/packet-dns.c index f3ea805d10..e66b022bdd 100644 --- a/epan/dissectors/packet-dns.c +++ b/epan/dissectors/packet-dns.c @@ -4452,6 +4452,65 @@ dissect_dns(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data) } } +static gboolean +dissect_dns_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) +{ + /* + * Try hard to match DNS messages while avoiding false positives. Look for: + * + * - Non-empty DNS messages (more than just a header). + * - Flags: QR bit (0-Query, 1-Response); Opcode bits: Standard Query (0000) + * - Questions: 1 (for queries), or 0 or 1 (for responses like AXFR) + * - Answer RRs: 0 (for queries) or a low number (for responses) + * - Authority RRs: 0 (for queries) or a low number (for responses) + * - Additional RRs: assume a low number. + * + * Not implemented, but perhaps we could check for: + * - Require that the question and answer count cannot both be zero. Perhaps + * some protocols have large sequences of zero bytes, this check reduces the + * probability of matching such payloads. + * - Assume a valid QNAME in the question section. (Is there sufficient data + * for a valid name?) + * - Assume a common QTYPE and QCLASS (IN/CH). + * - Potentially implement heuristics for TCP by checking the length prefix? + */ + int offset = 0; + guint16 flags, quest, ans, auth, add; + /* + * max_ans=10 was sufficient for recognizing the majority of DNS messages from + * the rrdns test suite, but four "huge record" test cases have 100 answers. + * The max_auth and max_add numbers were picked arbitrarily. + */ + const guint16 max_ans = 100; + const guint16 max_auth = 10; + const guint16 max_add = 10; + + if (tvb_reported_length(tvb) <= DNS_HDRLEN) + return FALSE; + + flags = tvb_get_ntohs(tvb, offset + DNS_FLAGS); + if ((flags & F_OPCODE) != 0) + return FALSE; + + quest = tvb_get_ntohs(tvb, offset + DNS_QUEST); + ans = tvb_get_ntohs(tvb, offset + DNS_ANS); + auth = tvb_get_ntohs(tvb, offset + DNS_AUTH); + if (!(flags & F_RESPONSE)) { + if (quest != 1 || ans != 0 || auth != 0) + return FALSE; + } else { + if (quest > 1 || ans > max_ans || auth > max_auth) + return FALSE; + } + + add = tvb_get_ntohs(tvb, offset + DNS_ADD); + if (add > max_add) + return FALSE; + + dissect_dns(tvb, pinfo, tree, NULL); + return TRUE; +} + static void dns_stats_tree_init(stats_tree* st) { st_node_packets = stats_tree_create_node(st, st_str_packets, 0, STAT_DT_INT, TRUE); @@ -4566,6 +4625,7 @@ proto_reg_handoff_dns(void) dissector_add_uint_range_with_preference("tcp.port", DEFAULT_DNS_TCP_PORT_RANGE, dns_handle); dissector_add_uint_range_with_preference("udp.port", DEFAULT_DNS_PORT_RANGE, dns_handle); dissector_add_string("media_type", "application/dns-message", dns_handle); /* since draft-ietf-doh-dns-over-https-07 */ + heur_dissector_add("udp", dissect_dns_heur, "DNS over UDP", "dns_udp", proto_dns, HEURISTIC_ENABLE); } void |