aboutsummaryrefslogtreecommitdiffstats
path: root/epan/dissectors/packet-dns.c
diff options
context:
space:
mode:
authorPeter Wu <peter@lekensteyn.nl>2020-10-23 22:45:55 +0200
committerPeter Wu <peter@lekensteyn.nl>2020-10-24 11:21:44 +0000
commitcad17858685e94cb763c492cbae400c30ab686dd (patch)
treee03034c7e472b2ae45e14f22dc94162c68fc707e /epan/dissectors/packet-dns.c
parent00c09b8696fe2ea550af02f06cf81889eeab43a8 (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.c60
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